Skip to content

Commit

Permalink
Extract reusable Caption and MediaWrapper component and use it for St…
Browse files Browse the repository at this point in the history
…oryImage
  • Loading branch information
yvonnetangsu committed Feb 21, 2025
1 parent fde6f9e commit 79af62a
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 63 deletions.
35 changes: 35 additions & 0 deletions components/Media/Caption.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { cnb } from 'cnbuilder';
import { Container } from '@/components/Container';
import * as styles from './MediaWrapper.styles';

/**
* This is a caption component for images, looping video and embedded video
* that provides a shared set of layout and options.
*/

export type CaptionProps = React.HTMLAttributes<HTMLDivElement> & {
caption?: React.ReactNode;
isCaptionInset?: boolean;
captionBgColor?: styles.CaptionBgColorType;
};

export const Caption = ({
caption,
isCaptionInset,
captionBgColor = 'transparent',
className,
...props
}: CaptionProps) => {
return (
<Container
as="figcaption"
width={isCaptionInset ? 'site' : 'full'}
className={cnb(styles.captionWrapper, styles.captionBgColors[captionBgColor])}
{...props}
>
<div className={styles.caption(captionBgColor)}>
{caption}
</div>
</Container>
);
};
22 changes: 22 additions & 0 deletions components/Media/MediaWrapper.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { cnb } from 'cnbuilder';

export const captionBgColors = {
black: 'bg-gc-black',
'black-60': 'bg-black-true/60',
white: 'bg-white',
transparent: '',
};
export type CaptionBgColorType = keyof typeof captionBgColors;

export const root = (isFullHeight?: boolean) => cnb(isFullHeight ? 'h-full' : '');
export const animateWrapper = (isFullHeight?: boolean) => cnb(isFullHeight ? 'h-full' : '');
export const figure = (isFullHeight: boolean) => cnb(isFullHeight ? 'h-full' : '');
export const mediaWrapper = (isFullHeight: boolean, isParallax: boolean) => cnb('relative',
isFullHeight ? 'h-full' : '',
isParallax ? 'overflow-hidden' : '',
);
export const captionWrapper = 'mt-0';
export const caption = (captionBgColor: CaptionBgColorType) => cnb(
'*:*:leading-display max-w-prose-wide first:*:*:mt-0',
!!captionBgColor && captionBgColor !== 'transparent' ? 'px-1em py-08em' : 'pt-06em',
);
67 changes: 67 additions & 0 deletions components/Media/MediaWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { cnb } from 'cnbuilder';
import { AnimateInView, type AnimationType } from '@/components/Animate';
import { Caption } from './Caption';
import { WidthBox, type WidthType } from '@/components/WidthBox';
import { type PaddingType } from '@/utilities/datasource';
import { imageAspectRatios, type ImageAspectRatioType } from '@/utilities/datasource';
import * as styles from './MediaWrapper.styles';

/**
* This is a wrapper component for images and video components.
* that provides a shared set of layout and caption options.
*/

export type MediaWrapperProps = React.HTMLAttributes<HTMLDivElement> & {
caption?: React.ReactNode;
isCaptionInset?: boolean;
captionBgColor?: styles.CaptionBgColorType;
aspectRatio?: ImageAspectRatioType;
isFullHeight?: boolean;
isParallax?: boolean;
boundingWidth?: 'site' | 'full';
width?: WidthType;
pt?: PaddingType;
pb?: PaddingType;
animation?: AnimationType;
delay?: number;
};

export const MediaWrapper = ({
caption,
aspectRatio,
isFullHeight,
isParallax,
boundingWidth = 'full',
width,
pt,
pb,
isCaptionInset,
captionBgColor = 'transparent',
animation = 'none',
delay,
children,
className,
...props
}: MediaWrapperProps) => {
return (
<WidthBox
{...props}
boundingWidth={boundingWidth}
width={width}
pt={pt}
pb={pb}
className={cnb(styles.root(isFullHeight), className)}
>
<AnimateInView animation={animation} delay={delay} className={styles.animateWrapper(isFullHeight)}>
<figure className={styles.figure(isFullHeight)}>
<div className={cnb(imageAspectRatios[aspectRatio], styles.mediaWrapper(isFullHeight, isParallax))}>
{children}
</div>
{caption && (
<Caption caption={caption} isCaptionInset={isCaptionInset} captionBgColor={captionBgColor} />
)}
</figure>
</AnimateInView>
</WidthBox>
);
};
2 changes: 2 additions & 0 deletions components/Media/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './Caption';
export * from './MediaWrapper';
99 changes: 36 additions & 63 deletions components/StoryImage/StoryImage.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,17 @@
import { cnb } from 'cnbuilder';
import { AnimateInView, type AnimationType } from '@/components/Animate';
import { Container } from '@/components/Container';
import { type AnimationType } from '@/components/Animate';
import { MediaWrapper, type MediaWrapperProps } from '@/components/Media';
import { Parallax } from '@/components/Parallax';
import { WidthBox, type WidthType } from '@/components/WidthBox';
import { type PaddingType } from '@/utilities/datasource';
import { imageAspectRatios, type ImageAspectRatioType } from '@/utilities/datasource';
import { getProcessedImage } from '@/utilities/getProcessedImage';
import { getSbImageSize } from '@/utilities/getSbImageSize';
import * as styles from './StoryImage.styles';

export type StoryImageProps = React.HTMLAttributes<HTMLDivElement> & {
export type StoryImageProps = React.HTMLAttributes<HTMLDivElement> & MediaWrapperProps & {
imageSrc: string;
imageFocus?: string;
isLoadingEager?: boolean;
isParallax?: boolean;
alt?: string;
caption?: React.ReactNode;
isCaptionInset?: boolean;
captionBgColor?: styles.CaptionBgColorType;
aspectRatio?: ImageAspectRatioType;
isFullHeight?: boolean;
boundingWidth?: 'site' | 'full';
width?: WidthType;
spacingTop?: PaddingType;
spacingBottom?: PaddingType;
animation?: AnimationType;
Expand Down Expand Up @@ -61,62 +51,45 @@ export const StoryImage = ({
: parseInt(cropSize?.split('x')[1], 10);

return (
<WidthBox
<MediaWrapper
{...props}
boundingWidth={boundingWidth}
width={width}
pt={spacingTop}
pb={spacingBottom}
className={cnb(className, styles.root(isFullHeight))}
>
<AnimateInView animation={animation} delay={delay} className={styles.animateWrapper(isFullHeight)}>
<figure className={styles.figure(isFullHeight)}>
<div className={cnb(imageAspectRatios[aspectRatio], styles.imageWrapper(isFullHeight, isParallax))}>
{!!imageSrc && (
<Parallax offset={isParallax ? 60 : 0}>
<picture>
<source
srcSet={getProcessedImage(imageSrc, cropSize, imageFocus)}
media="(min-width: 1500px)"
/>
<source
srcSet={getProcessedImage(imageSrc, styles.imageCropsSmallDesktop[aspectRatio], imageFocus)}
media="(min-width: 992px)"
/>
<source
srcSet={getProcessedImage(imageSrc, styles.imageCropsTablet[aspectRatio], imageFocus)}
media="(min-width: 576px)"
/>
<source
srcSet={getProcessedImage(imageSrc, styles.imageCropsMobile[aspectRatio], imageFocus)}
media="(max-width: 575px)"
/>
<img
src={getProcessedImage(imageSrc, cropSize, imageFocus)}
loading={isLoadingEager ? 'eager' : 'lazy'}
width={cropWidth}
height={cropHeight}
alt={alt || ''}
className={styles.image(isParallax)}
/>
</picture>
</Parallax>
)}
{children}
</div>
{caption && (
<Container
as="figcaption"
width={isCaptionInset ? 'site' : 'full'}
className={cnb(styles.captionWrapper, styles.captionBgColors[captionBgColor])}
>
<div className={styles.caption(captionBgColor)}>
{caption}
</div>
</Container>
)}
</figure>
</AnimateInView>
</WidthBox>
{!!imageSrc && (
<Parallax offset={isParallax ? 60 : 0}>
<picture>
<source
srcSet={getProcessedImage(imageSrc, cropSize, imageFocus)}
media="(min-width: 1500px)"
/>
<source
srcSet={getProcessedImage(imageSrc, styles.imageCropsSmallDesktop[aspectRatio], imageFocus)}
media="(min-width: 992px)"
/>
<source
srcSet={getProcessedImage(imageSrc, styles.imageCropsTablet[aspectRatio], imageFocus)}
media="(min-width: 576px)"
/>
<source
srcSet={getProcessedImage(imageSrc, styles.imageCropsMobile[aspectRatio], imageFocus)}
media="(max-width: 575px)"
/>
<img
src={getProcessedImage(imageSrc, cropSize, imageFocus)}
loading={isLoadingEager ? 'eager' : 'lazy'}
width={cropWidth}
height={cropHeight}
alt={alt || ''}
className={styles.image(isParallax)}
/>
</picture>
</Parallax>
)}
{children}
</MediaWrapper>
);
};

0 comments on commit 79af62a

Please sign in to comment.