Refine recipe overlay grid/behavior

This commit is contained in:
Sam Becker 2025-03-11 08:55:26 -05:00
parent 4b7bccc17c
commit 83651460f5
4 changed files with 66 additions and 65 deletions

View File

@ -32,7 +32,7 @@ export default function RecipeImageResponse({
recipe: photo.recipeData,
simulation: photo.filmSimulation!,
iso: photo.iso!.toString(),
})
}, true)
: [];
if (recipeLines && recipeLines.length > MAX_RECIPE_LINES) {

View File

@ -356,7 +356,11 @@ export default function PhotoLarge({
<button
ref={refRecipeButton}
title="Fujifilm Recipe"
onClick={toggleRecipeOverlay}
onClick={() => {
toggleRecipeOverlay();
// Avoid unexpected tooltip trigger
refRecipeButton.current?.blur();
}}
className={clsx(
'text-medium',
'border-medium rounded-md',

View File

@ -8,6 +8,7 @@ import { IoCloseCircle } from 'react-icons/io5';
import { motion } from 'framer-motion';
import {
addSign,
formatGrain,
formatNoiseReduction,
formatRecipe,
formatWhiteBalance,
@ -36,28 +37,24 @@ export default function PhotoRecipeOverlay({
clarity,
colorChromeEffect,
colorChromeFXBlue,
grainEffect,
bwAdjustment,
bwMagentaGreen,
} = recipe;
const whiteBalanceTypeFormatted = formatWhiteBalance(recipe);
const renderRow = (children: ReactNode) =>
<div className="flex gap-2 *:w-full *:grow">{children}</div>;
const renderDataSquare = (
value: ReactNode,
label?: string,
className?: string,
colSpan = 'col-span-4',
) => (
<div className={clsx(
'flex flex-col items-center justify-center gap-0.5 rounded-md min-w-0',
'flex flex-col items-center justify-center gap-0.5 min-w-0',
'rounded-md border',
'border-neutral-200/40',
'bg-neutral-100/30 hover:bg-neutral-100/50',
label && 'p-1',
className,
colSpan,
)}>
<div className="truncate max-w-full tracking-wide">
{typeof value === 'number' ? addSign(value) : value}
@ -109,55 +106,48 @@ export default function PhotoRecipeOverlay({
)}
/>
</div>
<div className="space-y-2">
{renderRow(<>
{renderDataSquare(`DR${dynamicRange.development}`)}
{renderDataSquare(iso)}
{renderDataSquare(exposure ?? '0ev')}
</>)}
{renderRow(<>
{renderDataSquare(
whiteBalanceTypeFormatted.toUpperCase(),
`R${addSign(whiteBalance?.red)} / B${addSign(whiteBalance?.blue)}`,
'basis-2/3',
)}
{renderDataSquare(
formatNoiseReduction(recipe),
'ISO NR',
'basis-1/3',
)}
</>)}
{renderRow(<>
{renderDataSquare(highlight, 'Highlight')}
{renderDataSquare(shadow, 'Shadow')}
</>)}
{renderRow(<>
{renderDataSquare(color, 'Color')}
{renderDataSquare(sharpness, 'Sharpness')}
{renderDataSquare(clarity, 'Clarity')}
</>)}
{renderRow(<>
{renderDataSquare(
colorChromeEffect?.toLocaleUpperCase() ?? 'N/A',
'Color Chrome',
)}
{renderDataSquare(
colorChromeFXBlue?.toLocaleUpperCase() ?? 'N/A',
'FX Blue',
)}
</>)}
{renderRow(<>
{renderDataSquare(
grainEffect.roughness.toLocaleUpperCase(),
grainEffect.size === 'large'
? 'Large Grain'
: grainEffect.size === 'small'
? 'Small Grain'
: 'Grain',
)}
{renderDataSquare(bwAdjustment ?? 0, 'BW ADJ')}
{renderDataSquare(bwMagentaGreen ?? 0, 'BW M/G')}
</>)}
<div className="grid grid-cols-12 gap-2">
{/* ROW */}
{renderDataSquare(`DR${dynamicRange.development}`)}
{renderDataSquare(iso)}
{renderDataSquare(exposure ?? '0ev')}
{/* ROW */}
{renderDataSquare(
whiteBalanceTypeFormatted.toUpperCase(),
`R${addSign(whiteBalance?.red)} / B${addSign(whiteBalance?.blue)}`,
'col-span-8',
)}
{renderDataSquare(
formatNoiseReduction(recipe),
'ISO NR',
'col-span-4',
)}
{/* ROW */}
{renderDataSquare(highlight, 'Highlight', 'col-span-6')}
{renderDataSquare(shadow, 'Shadow', 'col-span-6')}
{/* ROW */}
{renderDataSquare(color, 'Color')}
{renderDataSquare(sharpness, 'Sharpness')}
{renderDataSquare(clarity, 'Clarity')}
{/* ROW */}
{renderDataSquare(
colorChromeEffect?.toLocaleUpperCase() ?? 'N/A',
'Color Chrome',
'col-span-6',
)}
{renderDataSquare(
colorChromeFXBlue?.toLocaleUpperCase() ?? 'N/A',
'FX Blue',
'col-span-6',
)}
{/* ROW */}
{renderDataSquare(
formatGrain(recipe),
'grain',
'col-span-6',
)}
{renderDataSquare(bwAdjustment ?? 0, 'BW ADJ', 'col-span-3')}
{renderDataSquare(bwMagentaGreen ?? 0, 'BW M/G', 'col-span-3')}
</div>
</motion.div>
);

View File

@ -57,7 +57,9 @@ export const descriptionForRecipePhotos = (
export const generateRecipeText = ({
recipe,
simulation,
}: RecipeProps) => {
}: RecipeProps,
abbreviate?: boolean,
) => {
const lines = [
`${labelForFilmSimulation(simulation).small.toLocaleUpperCase()}`,
// eslint-disable-next-line max-len
@ -78,7 +80,7 @@ export const generateRecipeText = ({
lines.push(`FX BLUE ${recipe.colorChromeFXBlue.toLocaleUpperCase()}`);
}
if (recipe.grainEffect.roughness !== 'off') {
lines.push(`GRAIN ${formatGrain(recipe)}`);
lines.push(`GRAIN ${formatGrain(recipe, abbreviate)}`);
}
if (recipe.bwAdjustment || recipe.bwMagentaGreen) {
// eslint-disable-next-line max-len
@ -139,12 +141,17 @@ export const formatWhiteBalanceColor = ({
? `R${addSign(red)}/B${addSign(blue)}`
: '';
export const formatGrain = ({ grainEffect }: FujifilmRecipe) =>
grainEffect.roughness === 'off'
export const formatGrain = (
{ grainEffect }: FujifilmRecipe,
abbreviate?: boolean,
) => {
const size = abbreviate
? grainEffect.size === 'small' ? 'SM' : 'LG'
: grainEffect.size;
return grainEffect.roughness === 'off'
? 'OFF'
: grainEffect.roughness === 'weak'
? `WEAK/${grainEffect.size === 'small' ? 'SM' : 'LG'}`
: `STRONG/${grainEffect.size === 'small' ? 'SM' : 'LG'}`;
: `${grainEffect.roughness}/${size}`.toLocaleUpperCase();
};
export const formatNoiseReduction = ({
highISONoiseReduction,