Reintroduce recipe button next to films

This commit is contained in:
Sam Becker 2025-04-10 20:36:38 -05:00
parent ad1bb5e95c
commit 7d7b05c742
6 changed files with 41 additions and 21 deletions

View File

@ -80,7 +80,7 @@ export default function EntityLink({
'inline-flex items-center gap-2',
'max-w-full overflow-hidden select-none',
// Underline link text when action is hovered
'[&:has(.action:hover)_a>*>*>*]:underline',
'[&:has(.action:hover)_.text-content]:underline',
className,
)}
>
@ -112,15 +112,16 @@ export default function EntityLink({
? <Badge
type="small"
contrast={contrast}
className='translate-y-[-0.5px]'
className="translate-y-[-0.5px]"
uppercase
interactive
>
{renderLabel}
</Badge>
: <span className={clsx(
'text-content',
truncate && 'inline-flex max-w-full *:truncate',
'decoration-dotted underline-offset-[4px] underline-main',
'decoration-dotted underline-offset-[4px]',
'decoration-gray-300 dark:decoration-gray-600',
)}>
{renderLabel}

View File

@ -7,6 +7,8 @@ import EntityLink, {
import clsx from 'clsx/lite';
import { labelForFilm } from '.';
import { isStringFujifilmSimulation } from '@/platforms/fujifilm/simulation';
import PhotoRecipeOverlayButton from '@/recipe/PhotoRecipeOverlayButton';
import { ComponentProps } from 'react';
export default function PhotoFilm({
film,
@ -14,12 +16,15 @@ export default function PhotoFilm({
badged = true,
contrast = 'low',
countOnHover,
toggleRecipeOverlay,
isShowingRecipeOverlay,
...props
}: {
film: string
countOnHover?: number
recipe?: FujifilmRecipe
} & EntityLinkExternalProps) {
} & Partial<ComponentProps<typeof PhotoRecipeOverlayButton>>
& EntityLinkExternalProps) {
const { small, medium, large } = labelForFilm(film);
return (
@ -41,6 +46,11 @@ export default function PhotoFilm({
type={type}
badged={badged}
contrast={contrast}
action={toggleRecipeOverlay &&
<PhotoRecipeOverlayButton {...{
toggleRecipeOverlay,
isShowingRecipeOverlay,
}} />}
hoverEntity={countOnHover}
iconWide={isStringFujifilmSimulation(film)}
/>

View File

@ -105,6 +105,7 @@ export default function PhotoLarge({
const ref = useRef<HTMLDivElement>(null);
const refZoomControls = useRef<ZoomControlsRef>(null);
const refPhotoRecipe = useRef<HTMLDivElement>(null);
const refPhotoFilm = useRef<HTMLDivElement>(null);
const {
areZoomControlsShown,
@ -130,9 +131,12 @@ export default function PhotoLarge({
, []);
const refRecipe = useRef<HTMLDivElement>(null);
const refTriggers = useMemo(() => [refPhotoRecipe], []);
const refTriggers = useMemo(() => [
refPhotoRecipe,
refPhotoFilm,
], []);
const {
shouldShowRecipeOverlay,
isShowingRecipeOverlay,
toggleRecipeOverlay,
hideRecipeOverlay,
} = useRecipeOverlay({
@ -222,11 +226,11 @@ export default function PhotoLarge({
'flex items-center justify-center',
// Allow clicks to pass through to zoom controls
// when not showing recipe overlay
!(shouldShowRecipeOverlay || shouldDebugRecipeOverlays) &&
!(isShowingRecipeOverlay || shouldDebugRecipeOverlays) &&
'pointer-events-none',
)}>
<AnimatePresence>
{(shouldShowRecipeOverlay || shouldDebugRecipeOverlays) &&
{(isShowingRecipeOverlay || shouldDebugRecipeOverlays) &&
photo.recipeData &&
photo.film &&
<PhotoRecipeOverlay
@ -336,7 +340,7 @@ export default function PhotoLarge({
prefetch={prefetchRelatedLinks}
countOnHover={recipeCount}
toggleRecipeOverlay={toggleRecipeOverlay}
shouldShowRecipeOverlay={shouldShowRecipeOverlay}
isShowingRecipeOverlay={isShowingRecipeOverlay}
/>}
{showTagsContent &&
<PhotoTags
@ -398,9 +402,14 @@ export default function PhotoLarge({
</ul>
{showFilmContent && photo.film &&
<PhotoFilm
ref={refPhotoFilm}
film={photo.film}
prefetch={prefetchRelatedLinks}
countOnHover={filmCount}
{...photo.recipeData && !photo.recipeTitle && {
toggleRecipeOverlay,
isShowingRecipeOverlay,
}}
/>}
</>}
<div className={clsx(

View File

@ -13,7 +13,7 @@ export default function PhotoRecipe({
recipe,
countOnHover,
toggleRecipeOverlay,
shouldShowRecipeOverlay,
isShowingRecipeOverlay,
...props
}: {
recipe: string
@ -38,7 +38,7 @@ export default function PhotoRecipe({
action={toggleRecipeOverlay &&
<PhotoRecipeOverlayButton {...{
toggleRecipeOverlay,
shouldShowRecipeOverlay,
isShowingRecipeOverlay,
}} />}
hoverEntity={countOnHover}
/>

View File

@ -8,11 +8,11 @@ import { useRef } from 'react';
export default function PhotoRecipeOverlayButton({
className,
toggleRecipeOverlay,
shouldShowRecipeOverlay,
isShowingRecipeOverlay,
}: {
className?: string
toggleRecipeOverlay: () => void
shouldShowRecipeOverlay?: boolean
isShowingRecipeOverlay?: boolean
}) {
const ref = useRef<HTMLButtonElement>(null);
@ -37,7 +37,7 @@ export default function PhotoRecipeOverlayButton({
<FaPlus
className={clsx(
'transition-transform',
shouldShowRecipeOverlay && 'rotate-45',
isShowingRecipeOverlay && 'rotate-45',
)}
size={10}
/>

View File

@ -9,14 +9,14 @@ export default function useRecipeOverlay({
ref?: RefObject<HTMLElement | null>,
refTriggers?: RefObject<HTMLElement | null>[],
}) {
const [shouldShowRecipeOverlay, setShouldShowRecipeOverlay] = useState(false);
const [isShowingRecipeOverlay, setIsShowingRecipeOverlay] = useState(false);
const showRecipeOverlay =
useCallback(() => setShouldShowRecipeOverlay(true), []);
useCallback(() => setIsShowingRecipeOverlay(true), []);
const hideRecipeOverlay =
useCallback(() => setShouldShowRecipeOverlay(false), []);
useCallback(() => setIsShowingRecipeOverlay(false), []);
const toggleRecipeOverlay = useCallback(() =>
setShouldShowRecipeOverlay(current => !current),
setIsShowingRecipeOverlay(current => !current),
[]);
useClickInsideOutside({
@ -25,13 +25,13 @@ export default function useRecipeOverlay({
});
useEffect(() => {
if (shouldShowRecipeOverlay && !isElementEntirelyInViewport(ref?.current)) {
if (isShowingRecipeOverlay && !isElementEntirelyInViewport(ref?.current)) {
ref?.current?.scrollIntoView({ behavior: 'smooth' });
}
}, [ref, shouldShowRecipeOverlay]);
}, [ref, isShowingRecipeOverlay]);
return {
shouldShowRecipeOverlay,
isShowingRecipeOverlay,
showRecipeOverlay,
hideRecipeOverlay,
toggleRecipeOverlay,