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, recipe: photo.recipeData,
simulation: photo.filmSimulation!, simulation: photo.filmSimulation!,
iso: photo.iso!.toString(), iso: photo.iso!.toString(),
}) }, true)
: []; : [];
if (recipeLines && recipeLines.length > MAX_RECIPE_LINES) { if (recipeLines && recipeLines.length > MAX_RECIPE_LINES) {

View File

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

View File

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

View File

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