Vercel/src/image-response/RecipeImageResponse.tsx
2025-03-12 20:15:14 -05:00

129 lines
3.6 KiB
TypeScript

import type { Photo } from '../photo';
import ImageCaption from './components/ImageCaption';
import ImagePhotoGrid from './components/ImagePhotoGrid';
import ImageContainer from './components/ImageContainer';
import type { NextImageSize } from '@/platforms/next-image';
import { formatTag } from '@/tag';
import { TbChecklist } from 'react-icons/tb';
import { generateRecipeText, getPhotoWithRecipeFromPhotos } from '@/recipe';
import PhotoFilmSimulationIcon from '@/simulation/PhotoFilmSimulationIcon';
import { isStringFilmSimulation } from '@/platforms/fujifilm/simulation';
const MAX_RECIPE_LINES = 8;
export default function RecipeImageResponse({
recipe,
photos,
width,
height,
fontFamily,
smallText = true,
}: {
recipe: string,
photos: Photo[]
width: NextImageSize
height: number
fontFamily: string
smallText?: boolean
}) {
const {
recipeData,
filmSimulation,
} = getPhotoWithRecipeFromPhotos(photos) ?? {};
let recipeLines = recipeData && filmSimulation
? generateRecipeText({
recipe: recipeData,
simulation: filmSimulation,
}, true)
: [];
if (recipeLines && recipeLines.length > MAX_RECIPE_LINES) {
recipeLines = recipeLines.slice(0, MAX_RECIPE_LINES);
recipeLines[MAX_RECIPE_LINES - 1] = '•••';
}
return (
<ImageContainer solidBackground={photos.length === 0}>
<ImagePhotoGrid
{...{
photos,
width,
height,
gap: 0,
}}
/>
<div
tw="flex absolute inset-0"
style={{
background:
'linear-gradient(to right, rgba(0, 0, 0, .5) 30%, transparent 60%)',
}}
/>
<ImageCaption {...{
width,
height,
fontFamily,
legacyBottomAlignment: false,
gap: '0',
icon: <TbChecklist
size={height * .087}
style={{
transform: `translateY(${height * .003}px)`,
marginRight: height * .02,
}}
/>,
title: formatTag(recipe).toLocaleUpperCase(),
}}>
{recipeData &&
<div
// tw="opacity-70"
style={{
display: 'flex',
flexDirection: 'column',
...smallText ? {
paddingTop: height * .04,
lineHeight: 1.45,
letterSpacing: '0.04em',
fontSize: height * .06,
} : {
paddingTop: height * .02,
opacity: 0.7,
},
}}
>
{recipeLines.map(text => (
<div tw="flex" key={text}>
<div
tw="flex"
style={{
width: height * .143,
}}
/>
<div style={{
display: 'flex',
alignItems: 'center',
gap: height * .0325,
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
width: '100%',
flexGrow: 1,
}}>
{text}
{isStringFilmSimulation(text) && filmSimulation &&
<div tw="flex">
<PhotoFilmSimulationIcon
simulation={filmSimulation}
height={height * .06}
style={{ transform: `translateY(${-height * .001}px)`}}
/>
</div>}
</div>
</div>
))}
</div>}
</ImageCaption>
</ImageContainer>
);
}