From c9a511d7aceb15952624d22ddfb3f8155de40fa4 Mon Sep 17 00:00:00 2001 From: Ryan Miller Date: Thu, 10 Oct 2024 11:04:51 +1100 Subject: [PATCH] feat: add figures and figure captions as options for content images --- .../lib/sanity/sanity-components.tsx | 15 +++- apps/foundation/locales/en.json | 3 + .../sanity-cms/components/SanityImage.tsx | 82 ++++++++++++------- .../components/SanityPortableText.tsx | 21 ++++- .../sanity-cms/schemas/fields/basic/image.tsx | 34 +++++++- 5 files changed, 114 insertions(+), 41 deletions(-) diff --git a/apps/foundation/lib/sanity/sanity-components.tsx b/apps/foundation/lib/sanity/sanity-components.tsx index f07d084a..c2fa369b 100644 --- a/apps/foundation/lib/sanity/sanity-components.tsx +++ b/apps/foundation/lib/sanity/sanity-components.tsx @@ -15,9 +15,18 @@ export const components = { marks, block, types: { - image: ({ value, isInline }) => ( - - ), + image: async ({ value, isInline }) => { + const imageDictionary = await getTranslations('image'); + return ( + + ); + }, button: (props) => ( ), diff --git a/apps/foundation/locales/en.json b/apps/foundation/locales/en.json index 1370ca3c..e92c7daf 100644 --- a/apps/foundation/locales/en.json +++ b/apps/foundation/locales/en.json @@ -6,6 +6,9 @@ "tile": { "scrollOrTap": "SCROLL/TAP" }, + "image": { + "figureLabelTemplate": "Figure '{number'}:" + }, "notFound": { "description": "Sorry! We couldn't find the page you were looking for.", "homeButton": "Return Home", diff --git a/packages/sanity-cms/components/SanityImage.tsx b/packages/sanity-cms/components/SanityImage.tsx index fbd3ca27..f2a77a8a 100644 --- a/packages/sanity-cms/components/SanityImage.tsx +++ b/packages/sanity-cms/components/SanityImage.tsx @@ -9,6 +9,7 @@ import type { } from '../schemas/fields/basic/image'; import { cn } from '@session/ui/lib/utils'; import { safeTry } from '@session/util-js/try'; +import { Fragment } from 'react'; export type SanityImageType = ImageFieldsSchemaType | ImageFieldsSchemaTypeWithoutAltText; @@ -35,6 +36,7 @@ type SanityImageProps = { isInline?: boolean; cover?: boolean; renderWithPriority?: boolean; + figureNumberTextTemplate?: string; className?: string; }; export const SanityImage = async ({ @@ -43,6 +45,7 @@ export const SanityImage = async ({ client, cover, renderWithPriority, + figureNumberTextTemplate = 'Figure {number}:', className, }: SanityImageProps) => { let imageData = { @@ -79,37 +82,54 @@ export const SanityImage = async ({ const priority = renderWithPriority ?? value.priority ?? false; + const hasCaption = 'caption' in value && value.caption?.length; + const figureNumber = 'figureNumber' in value ? value.figureNumber : null; + + const Comp = cover ? 'figure' : Fragment; + return ( - {alt} + + {alt} + {hasCaption ? ( +
+ {figureNumber ? ( + + {figureNumberTextTemplate?.replace('{number}', figureNumber.toString())} + + ) : null} +

{value.caption}

+
+ ) : null} +
); }; diff --git a/packages/sanity-cms/components/SanityPortableText.tsx b/packages/sanity-cms/components/SanityPortableText.tsx index afdd8f2e..cb7dc973 100644 --- a/packages/sanity-cms/components/SanityPortableText.tsx +++ b/packages/sanity-cms/components/SanityPortableText.tsx @@ -70,6 +70,8 @@ export function SanityPortableText({ value, className, ...props }: SanityPortabl logger.error('SanityPortableText: value is not an array'); return null; } + + let figureNumber = 1; for (const block of value) { if (block._type === 'block' && 'children' in block && block.children.length === 1) { /** @@ -86,10 +88,21 @@ export function SanityPortableText({ value, className, ...props }: SanityPortabl ) { continue; } - } else if (blocks.length < 3 && block._type === 'image') { - // Prioritize images in the first 5 blocks of the content - // @ts-expect-error - This is a workaround to make TS happy - block.priority = true; + } else if (block._type === 'image') { + if (blocks.length < 3) { + // Prioritize images in the first 5 blocks of the content + // @ts-expect-error - This is a workaround to make TS happy + block.priority = true; + } + if ( + 'caption' in block && + block.caption?.length && + 'calculateFigureNumber' in block && + block.calculateFigureNumber + ) { + block.figureNumber = figureNumber; + figureNumber++; + } } blocks.push(block); } diff --git a/packages/sanity-cms/schemas/fields/basic/image.tsx b/packages/sanity-cms/schemas/fields/basic/image.tsx index f0c18996..2f598a0c 100644 --- a/packages/sanity-cms/schemas/fields/basic/image.tsx +++ b/packages/sanity-cms/schemas/fields/basic/image.tsx @@ -32,7 +32,35 @@ const imageFields = [ }), ]; -const imageFieldsWithAlt = [...imageFields, altField]; +const captionFields = [ + defineField({ + name: 'caption', + type: 'string' as const, + title: 'Caption', + description: + 'The image caption, This should only be used if you want a visible figure caption below the image.', + }), + defineField({ + name: 'calculateFigureNumber', + type: 'boolean' as const, + title: 'Display calculated Figure Number', + description: ( +

+ Calculate the figure number for the image. This will add a number to the start of the + caption. Eg: +
+ + Figure 2: YOUR CAPTION GOES HERE + +
+ This should be used in content that has multiple images. +

+ ), + hidden: ({ parent }) => !parent?.caption?.length, + }), +]; + +const imageFieldsWithAltAndOptionalCaption = [...imageFields, altField, ...captionFields]; export const imageFieldDefinition = { name: 'image', @@ -42,7 +70,7 @@ export const imageFieldDefinition = { options: { hotspot: true, }, - fields: imageFieldsWithAlt, + fields: imageFieldsWithAltAndOptionalCaption, }; export const imageField = defineField(imageFieldDefinition); @@ -52,5 +80,5 @@ export const imageFieldWithOutAltText = defineField({ fields: imageFields, }); -export type ImageFieldsSchemaType = SchemaFieldsType; +export type ImageFieldsSchemaType = SchemaFieldsType; export type ImageFieldsSchemaTypeWithoutAltText = SchemaFieldsType;