Create navigation-based photo header
This commit is contained in:
parent
76c02ee015
commit
6be23effc7
@ -3,8 +3,7 @@ import { Photo, PhotoDateRange } from '.';
|
|||||||
import PhotoLarge from './PhotoLarge';
|
import PhotoLarge from './PhotoLarge';
|
||||||
import SiteGrid from '@/components/SiteGrid';
|
import SiteGrid from '@/components/SiteGrid';
|
||||||
import PhotoGrid from './PhotoGrid';
|
import PhotoGrid from './PhotoGrid';
|
||||||
import { clsx } from 'clsx/lite';
|
import PhotoNav from './PhotoNav';
|
||||||
import PhotoLinks from './PhotoLinks';
|
|
||||||
import TagHeader from '@/tag/TagHeader';
|
import TagHeader from '@/tag/TagHeader';
|
||||||
import { Camera } from '@/camera';
|
import { Camera } from '@/camera';
|
||||||
import CameraHeader from '@/camera/CameraHeader';
|
import CameraHeader from '@/camera/CameraHeader';
|
||||||
@ -102,6 +101,24 @@ export default function PhotoDetailPage({
|
|||||||
dateRange={dateRange}
|
dateRange={dateRange}
|
||||||
/>}
|
/>}
|
||||||
/>}
|
/>}
|
||||||
|
<AnimateItems
|
||||||
|
animateOnFirstLoadOnly
|
||||||
|
items={[
|
||||||
|
<SiteGrid
|
||||||
|
key="photo-nav"
|
||||||
|
className="mb-4"
|
||||||
|
contentMain={<PhotoNav {...{
|
||||||
|
photo,
|
||||||
|
photos,
|
||||||
|
className: 'border-t border-gray-100 pt-4',
|
||||||
|
tag,
|
||||||
|
camera,
|
||||||
|
simulation,
|
||||||
|
focal,
|
||||||
|
}} />}
|
||||||
|
/>,
|
||||||
|
]}
|
||||||
|
/>
|
||||||
<AnimateItems
|
<AnimateItems
|
||||||
className="md:mb-8"
|
className="md:mb-8"
|
||||||
animateFromAppState
|
animateFromAppState
|
||||||
@ -112,6 +129,7 @@ export default function PhotoDetailPage({
|
|||||||
primaryTag={tag}
|
primaryTag={tag}
|
||||||
priority
|
priority
|
||||||
prefetchRelatedLinks
|
prefetchRelatedLinks
|
||||||
|
showTitle={false}
|
||||||
showCamera={!camera}
|
showCamera={!camera}
|
||||||
showSimulation={!simulation}
|
showSimulation={!simulation}
|
||||||
shouldShare={shouldShare}
|
shouldShare={shouldShare}
|
||||||
@ -134,30 +152,6 @@ export default function PhotoDetailPage({
|
|||||||
focal={focal}
|
focal={focal}
|
||||||
animateOnFirstLoadOnly
|
animateOnFirstLoadOnly
|
||||||
/>}
|
/>}
|
||||||
contentSide={<AnimateItems
|
|
||||||
animateOnFirstLoadOnly
|
|
||||||
type="bottom"
|
|
||||||
items={[
|
|
||||||
<div
|
|
||||||
key="PhotoLinks"
|
|
||||||
className={clsx(
|
|
||||||
'grid grid-cols-2',
|
|
||||||
'gap-0.5 sm:gap-1',
|
|
||||||
'md:flex md:gap-4',
|
|
||||||
'user-select-none',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<PhotoLinks {...{
|
|
||||||
photo,
|
|
||||||
photos,
|
|
||||||
tag,
|
|
||||||
camera,
|
|
||||||
simulation,
|
|
||||||
focal,
|
|
||||||
}} />
|
|
||||||
</div>,
|
|
||||||
]}
|
|
||||||
/>}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -38,11 +38,13 @@ import { useAppState } from '@/state/AppState';
|
|||||||
|
|
||||||
export default function PhotoLarge({
|
export default function PhotoLarge({
|
||||||
photo,
|
photo,
|
||||||
|
className,
|
||||||
primaryTag,
|
primaryTag,
|
||||||
priority,
|
priority,
|
||||||
prefetch = SHOULD_PREFETCH_ALL_LINKS,
|
prefetch = SHOULD_PREFETCH_ALL_LINKS,
|
||||||
prefetchRelatedLinks = SHOULD_PREFETCH_ALL_LINKS,
|
prefetchRelatedLinks = SHOULD_PREFETCH_ALL_LINKS,
|
||||||
revalidatePhoto,
|
revalidatePhoto,
|
||||||
|
showTitle = true,
|
||||||
showCamera = true,
|
showCamera = true,
|
||||||
showSimulation = true,
|
showSimulation = true,
|
||||||
shouldShare = true,
|
shouldShare = true,
|
||||||
@ -55,11 +57,13 @@ export default function PhotoLarge({
|
|||||||
onVisible,
|
onVisible,
|
||||||
}: {
|
}: {
|
||||||
photo: Photo
|
photo: Photo
|
||||||
|
className?: string
|
||||||
primaryTag?: string
|
primaryTag?: string
|
||||||
priority?: boolean
|
priority?: boolean
|
||||||
prefetch?: boolean
|
prefetch?: boolean
|
||||||
prefetchRelatedLinks?: boolean
|
prefetchRelatedLinks?: boolean
|
||||||
revalidatePhoto?: RevalidatePhoto
|
revalidatePhoto?: RevalidatePhoto
|
||||||
|
showTitle?: boolean
|
||||||
showCamera?: boolean
|
showCamera?: boolean
|
||||||
showSimulation?: boolean
|
showSimulation?: boolean
|
||||||
shouldShare?: boolean
|
shouldShare?: boolean
|
||||||
@ -85,10 +89,14 @@ export default function PhotoLarge({
|
|||||||
|
|
||||||
const { arePhotosMatted, isUserSignedIn } = useAppState();
|
const { arePhotosMatted, isUserSignedIn } = useAppState();
|
||||||
|
|
||||||
|
const hasTitle = showTitle && (
|
||||||
|
Boolean(photo.title) ||
|
||||||
|
SHOW_PHOTO_TITLE_FALLBACK_TEXT
|
||||||
|
);
|
||||||
|
|
||||||
const hasTitleContent =
|
const hasTitleContent =
|
||||||
photo.title ||
|
hasTitle ||
|
||||||
SHOW_PHOTO_TITLE_FALLBACK_TEXT ||
|
Boolean(photo.caption);
|
||||||
photo.caption;
|
|
||||||
|
|
||||||
const hasMetaContent =
|
const hasMetaContent =
|
||||||
showCameraContent ||
|
showCameraContent ||
|
||||||
@ -102,6 +110,7 @@ export default function PhotoLarge({
|
|||||||
return (
|
return (
|
||||||
<SiteGrid
|
<SiteGrid
|
||||||
containerRef={ref}
|
containerRef={ref}
|
||||||
|
className={className}
|
||||||
contentMain={
|
contentMain={
|
||||||
<Link
|
<Link
|
||||||
href={pathForPhoto({ photo })}
|
href={pathForPhoto({ photo })}
|
||||||
@ -141,7 +150,7 @@ export default function PhotoLarge({
|
|||||||
{/* Meta */}
|
{/* Meta */}
|
||||||
<div className="pr-2 md:pr-0">
|
<div className="pr-2 md:pr-0">
|
||||||
<div className="md:relative flex gap-2 items-start">
|
<div className="md:relative flex gap-2 items-start">
|
||||||
{(photo.title || SHOW_PHOTO_TITLE_FALLBACK_TEXT) &&
|
{hasTitle &&
|
||||||
<PhotoLink
|
<PhotoLink
|
||||||
photo={photo}
|
photo={photo}
|
||||||
className="font-bold uppercase flex-grow"
|
className="font-bold uppercase flex-grow"
|
||||||
|
|||||||
@ -9,26 +9,33 @@ import { useAppState } from '@/state/AppState';
|
|||||||
import { AnimationConfig } from '@/components/AnimateItems';
|
import { AnimationConfig } from '@/components/AnimateItems';
|
||||||
import { Camera } from '@/camera';
|
import { Camera } from '@/camera';
|
||||||
import { FilmSimulation } from '@/simulation';
|
import { FilmSimulation } from '@/simulation';
|
||||||
|
import { SHOW_PHOTO_TITLE_FALLBACK_TEXT } from '@/site/config';
|
||||||
|
import { BiChevronLeft, BiChevronRight } from 'react-icons/bi';
|
||||||
|
import { clsx } from 'clsx/lite';
|
||||||
|
|
||||||
const LISTENER_KEYUP = 'keyup';
|
const LISTENER_KEYUP = 'keyup';
|
||||||
|
|
||||||
const ANIMATION_LEFT: AnimationConfig = { type: 'left', duration: 0.3 };
|
const ANIMATION_LEFT: AnimationConfig = { type: 'left', duration: 0.3 };
|
||||||
const ANIMATION_RIGHT: AnimationConfig = { type: 'right', duration: 0.3 };
|
const ANIMATION_RIGHT: AnimationConfig = { type: 'right', duration: 0.3 };
|
||||||
|
|
||||||
export default function PhotoLinks({
|
export default function PhotoNav({
|
||||||
photo,
|
photo,
|
||||||
photos,
|
photos,
|
||||||
|
className,
|
||||||
tag,
|
tag,
|
||||||
camera,
|
camera,
|
||||||
simulation,
|
simulation,
|
||||||
focal,
|
focal,
|
||||||
|
prefetch,
|
||||||
}: {
|
}: {
|
||||||
photo: Photo
|
photo: Photo
|
||||||
photos: Photo[]
|
photos: Photo[]
|
||||||
|
className?: string
|
||||||
tag?: string
|
tag?: string
|
||||||
camera?: Camera
|
camera?: Camera
|
||||||
simulation?: FilmSimulation
|
simulation?: FilmSimulation
|
||||||
focal?: number
|
focal?: number
|
||||||
|
prefetch?: boolean
|
||||||
}) {
|
}) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
@ -94,7 +101,10 @@ export default function PhotoLinks({
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div className={clsx(
|
||||||
|
'flex items-center',
|
||||||
|
className,
|
||||||
|
)}>
|
||||||
<PhotoLink
|
<PhotoLink
|
||||||
photo={previousPhoto}
|
photo={previousPhoto}
|
||||||
nextPhotoAnimation={ANIMATION_RIGHT}
|
nextPhotoAnimation={ANIMATION_RIGHT}
|
||||||
@ -105,8 +115,24 @@ export default function PhotoLinks({
|
|||||||
scroll={false}
|
scroll={false}
|
||||||
prefetch
|
prefetch
|
||||||
>
|
>
|
||||||
PREV
|
<span className="group inline-flex gap-1 items-center">
|
||||||
|
<BiChevronLeft
|
||||||
|
className={clsx(
|
||||||
|
'text-[1.25rem] transition-transform',
|
||||||
|
'group-hover:-translate-x-1',
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
PREV
|
||||||
|
</span>
|
||||||
</PhotoLink>
|
</PhotoLink>
|
||||||
|
<div className="grow text-center">
|
||||||
|
{(photo.title || SHOW_PHOTO_TITLE_FALLBACK_TEXT) &&
|
||||||
|
<PhotoLink
|
||||||
|
photo={photo}
|
||||||
|
className="uppercase font-bold"
|
||||||
|
prefetch={prefetch}
|
||||||
|
/>}
|
||||||
|
</div>
|
||||||
<PhotoLink
|
<PhotoLink
|
||||||
photo={nextPhoto}
|
photo={nextPhoto}
|
||||||
nextPhotoAnimation={ANIMATION_LEFT}
|
nextPhotoAnimation={ANIMATION_LEFT}
|
||||||
@ -117,8 +143,16 @@ export default function PhotoLinks({
|
|||||||
scroll={false}
|
scroll={false}
|
||||||
prefetch
|
prefetch
|
||||||
>
|
>
|
||||||
NEXT
|
<span className="group inline-flex gap-1 items-center">
|
||||||
|
NEXT
|
||||||
|
<BiChevronRight
|
||||||
|
className={clsx(
|
||||||
|
'text-[1.25rem] transition-transform',
|
||||||
|
'group-hover:translate-x-1',
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
</PhotoLink>
|
</PhotoLink>
|
||||||
</>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
Loading…
Reference in New Issue
Block a user