Skip to content

Commit

Permalink
feat: add figures and figure captions as options for content images
Browse files Browse the repository at this point in the history
  • Loading branch information
Aerilym committed Oct 10, 2024
1 parent f1492e2 commit c9a511d
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 41 deletions.
15 changes: 12 additions & 3 deletions apps/foundation/lib/sanity/sanity-components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,18 @@ export const components = {
marks,
block,
types: {
image: ({ value, isInline }) => (
<SanityImage value={value} isInline={isInline} client={client} className="my-4 md:my-6" />
),
image: async ({ value, isInline }) => {
const imageDictionary = await getTranslations('image');
return (
<SanityImage
value={value}
isInline={isInline}
client={client}
figureNumberTextTemplate={imageDictionary('figureLabelTemplate')}
className="my-4 md:my-6"
/>
);
},
button: (props) => (
<SanityButton {...props} client={client} postBaseUrl={SANITY_SCHEMA_URL.POST} />
),
Expand Down
3 changes: 3 additions & 0 deletions apps/foundation/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
82 changes: 51 additions & 31 deletions packages/sanity-cms/components/SanityImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -35,6 +36,7 @@ type SanityImageProps = {
isInline?: boolean;
cover?: boolean;
renderWithPriority?: boolean;
figureNumberTextTemplate?: string;
className?: string;
};
export const SanityImage = async ({
Expand All @@ -43,6 +45,7 @@ export const SanityImage = async ({
client,
cover,
renderWithPriority,
figureNumberTextTemplate = 'Figure {number}:',
className,
}: SanityImageProps) => {
let imageData = {
Expand Down Expand Up @@ -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 (
<Image
src={src}
alt={alt}
width={width}
height={height}
className={cn(
value.rounded && 'rounded-2xl',
/** Display alongside text if image appears inside a block text span */
isInline ? 'inline-block' : 'block',
cover && 'object-cover',
className
)}
style={{
/** Avoid jumping around with aspect-ratio CSS property */
aspectRatio: width / height,
}}
/**
* NOTE: Blur is REQUIRED for `blurDataURL` to work.
* https://nextjs.org/docs/app/api-reference/components/image#blurdataurl
*/
{...(base64
? {
blurDataURL: base64,
placeholder: 'blur',
}
: {
placeholder: 'empty',
})}
priority={priority}
loading={priority ? undefined : 'lazy'}
/>
<Comp>
<Image
src={src}
alt={alt}
width={width}
height={height}
className={cn(
value.rounded && 'rounded-2xl',
/** Display alongside text if image appears inside a block text span */
isInline ? 'inline-block' : 'block',
cover && 'object-cover',
className
)}
style={{
/** Avoid jumping around with aspect-ratio CSS property */
aspectRatio: width / height,
}}
/**
* NOTE: Blur is REQUIRED for `blurDataURL` to work.
* https://nextjs.org/docs/app/api-reference/components/image#blurdataurl
*/
{...(base64
? {
blurDataURL: base64,
placeholder: 'blur',
}
: {
placeholder: 'empty',
})}
priority={priority}
loading={priority ? undefined : 'lazy'}
/>
{hasCaption ? (
<figcaption className="mt-2 inline-flex gap-1 text-sm italic">
{figureNumber ? (
<strong>
{figureNumberTextTemplate?.replace('{number}', figureNumber.toString())}
</strong>
) : null}
<p>{value.caption}</p>
</figcaption>
) : null}
</Comp>
);
};
21 changes: 17 additions & 4 deletions packages/sanity-cms/components/SanityPortableText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
/**
Expand All @@ -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);
}
Expand Down
34 changes: 31 additions & 3 deletions packages/sanity-cms/schemas/fields/basic/image.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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: (
<p>
Calculate the figure number for the image. This will add a number to the start of the
caption. Eg:
<br />
<span>
<strong>Figure 2:</strong> YOUR CAPTION GOES HERE
</span>
<br />
This should be used in content that has multiple images.
</p>
),
hidden: ({ parent }) => !parent?.caption?.length,
}),
];

const imageFieldsWithAltAndOptionalCaption = [...imageFields, altField, ...captionFields];

export const imageFieldDefinition = {
name: 'image',
Expand All @@ -42,7 +70,7 @@ export const imageFieldDefinition = {
options: {
hotspot: true,
},
fields: imageFieldsWithAlt,
fields: imageFieldsWithAltAndOptionalCaption,
};

export const imageField = defineField(imageFieldDefinition);
Expand All @@ -52,5 +80,5 @@ export const imageFieldWithOutAltText = defineField({
fields: imageFields,
});

export type ImageFieldsSchemaType = SchemaFieldsType<typeof imageFieldsWithAlt>;
export type ImageFieldsSchemaType = SchemaFieldsType<typeof imageFieldsWithAltAndOptionalCaption>;
export type ImageFieldsSchemaTypeWithoutAltText = SchemaFieldsType<typeof imageFields>;

0 comments on commit c9a511d

Please sign in to comment.