Combine photo nav + sets
This commit is contained in:
parent
e0a83415b0
commit
db77448a63
@ -1,6 +1,6 @@
|
|||||||
import { Photo, PhotoDateRange } from '@/photo';
|
import { Photo, PhotoDateRange } from '@/photo';
|
||||||
import { pathForCameraShare } from '@/site/paths';
|
import { pathForCameraShare } from '@/site/paths';
|
||||||
import PhotoSetHeader from '@/photo/PhotoSetHeader';
|
import PhotoHeader from '@/photo/PhotoHeader';
|
||||||
import { Camera, cameraFromPhoto } from '.';
|
import { Camera, cameraFromPhoto } from '.';
|
||||||
import PhotoCamera from './PhotoCamera';
|
import PhotoCamera from './PhotoCamera';
|
||||||
import { descriptionForCameraPhotos } from './meta';
|
import { descriptionForCameraPhotos } from './meta';
|
||||||
@ -22,7 +22,8 @@ export default function CameraHeader({
|
|||||||
}) {
|
}) {
|
||||||
const camera = cameraFromPhoto(photos[0], cameraProp);
|
const camera = cameraFromPhoto(photos[0], cameraProp);
|
||||||
return (
|
return (
|
||||||
<PhotoSetHeader
|
<PhotoHeader
|
||||||
|
camera={camera}
|
||||||
entity={<PhotoCamera {...{ camera }} contrast="high" hideAppleIcon />}
|
entity={<PhotoCamera {...{ camera }} contrast="high" hideAppleIcon />}
|
||||||
entityVerb="Photo"
|
entityVerb="Photo"
|
||||||
entityDescription={
|
entityDescription={
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { Photo, PhotoDateRange } from '@/photo';
|
import { Photo, PhotoDateRange } from '@/photo';
|
||||||
import { descriptionForFocalLengthPhotos } from '.';
|
import { descriptionForFocalLengthPhotos } from '.';
|
||||||
import { pathForFocalLengthShare } from '@/site/paths';
|
import { pathForFocalLengthShare } from '@/site/paths';
|
||||||
import PhotoSetHeader from '@/photo/PhotoSetHeader';
|
import PhotoHeader from '@/photo/PhotoHeader';
|
||||||
import PhotoFocalLength from './PhotoFocalLength';
|
import PhotoFocalLength from './PhotoFocalLength';
|
||||||
|
|
||||||
export default function FocalLengthHeader({
|
export default function FocalLengthHeader({
|
||||||
@ -20,7 +20,8 @@ export default function FocalLengthHeader({
|
|||||||
dateRange?: PhotoDateRange
|
dateRange?: PhotoDateRange
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<PhotoSetHeader
|
<PhotoHeader
|
||||||
|
focal={focal}
|
||||||
entity={<PhotoFocalLength focal={focal} contrast="high" />}
|
entity={<PhotoFocalLength focal={focal} contrast="high" />}
|
||||||
entityDescription={descriptionForFocalLengthPhotos(
|
entityDescription={descriptionForFocalLengthPhotos(
|
||||||
photos,
|
photos,
|
||||||
|
|||||||
@ -10,11 +10,9 @@ import {
|
|||||||
import SiteGrid from '@/components/SiteGrid';
|
import SiteGrid from '@/components/SiteGrid';
|
||||||
import Spinner from '@/components/Spinner';
|
import Spinner from '@/components/Spinner';
|
||||||
import { getPhotosCachedAction, getPhotosAction } from '@/photo/actions';
|
import { getPhotosCachedAction, getPhotosAction } from '@/photo/actions';
|
||||||
import { Photo } from '.';
|
import { Photo, PhotoSetAttributes } from '.';
|
||||||
import { clsx } from 'clsx/lite';
|
import { clsx } from 'clsx/lite';
|
||||||
import { useAppState } from '@/state/AppState';
|
import { useAppState } from '@/state/AppState';
|
||||||
import { Camera } from '@/camera';
|
|
||||||
import { FilmSimulation } from '@/simulation';
|
|
||||||
import { GetPhotosOptions } from './db';
|
import { GetPhotosOptions } from './db';
|
||||||
|
|
||||||
export type RevalidatePhoto = (
|
export type RevalidatePhoto = (
|
||||||
@ -38,9 +36,6 @@ export default function InfinitePhotoScroll({
|
|||||||
initialOffset: number
|
initialOffset: number
|
||||||
itemsPerPage: number
|
itemsPerPage: number
|
||||||
sortBy?: GetPhotosOptions['sortBy']
|
sortBy?: GetPhotosOptions['sortBy']
|
||||||
tag?: string
|
|
||||||
camera?: Camera
|
|
||||||
simulation?: FilmSimulation
|
|
||||||
cacheKey: string
|
cacheKey: string
|
||||||
wrapMoreButtonInGrid?: boolean
|
wrapMoreButtonInGrid?: boolean
|
||||||
useCachedPhotos?: boolean
|
useCachedPhotos?: boolean
|
||||||
@ -50,7 +45,7 @@ export default function InfinitePhotoScroll({
|
|||||||
onLastPhotoVisible: () => void
|
onLastPhotoVisible: () => void
|
||||||
revalidatePhoto?: RevalidatePhoto
|
revalidatePhoto?: RevalidatePhoto
|
||||||
}) => ReactNode
|
}) => ReactNode
|
||||||
}) {
|
} & PhotoSetAttributes) {
|
||||||
const { swrTimestamp, isUserSignedIn } = useAppState();
|
const { swrTimestamp, isUserSignedIn } = useAppState();
|
||||||
|
|
||||||
const key = `${swrTimestamp}-${cacheKey}`;
|
const key = `${swrTimestamp}-${cacheKey}`;
|
||||||
|
|||||||
@ -1,17 +1,15 @@
|
|||||||
import AnimateItems from '@/components/AnimateItems';
|
import AnimateItems from '@/components/AnimateItems';
|
||||||
import { Photo, PhotoDateRange } from '.';
|
import { Photo, PhotoDateRange, PhotoSetAttributes } 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 PhotoNav from './PhotoNav';
|
|
||||||
import TagHeader from '@/tag/TagHeader';
|
import TagHeader from '@/tag/TagHeader';
|
||||||
import { Camera } from '@/camera';
|
|
||||||
import CameraHeader from '@/camera/CameraHeader';
|
import CameraHeader from '@/camera/CameraHeader';
|
||||||
import { FilmSimulation } from '@/simulation';
|
|
||||||
import FilmSimulationHeader from '@/simulation/FilmSimulationHeader';
|
import FilmSimulationHeader from '@/simulation/FilmSimulationHeader';
|
||||||
import { TAG_HIDDEN } from '@/tag';
|
import { TAG_HIDDEN } from '@/tag';
|
||||||
import HiddenHeader from '@/tag/HiddenHeader';
|
import HiddenHeader from '@/tag/HiddenHeader';
|
||||||
import FocalLengthHeader from '@/focal/FocalLengthHeader';
|
import FocalLengthHeader from '@/focal/FocalLengthHeader';
|
||||||
|
import PhotoHeader from './PhotoHeader';
|
||||||
|
|
||||||
export default function PhotoDetailPage({
|
export default function PhotoDetailPage({
|
||||||
photo,
|
photo,
|
||||||
@ -30,22 +28,16 @@ export default function PhotoDetailPage({
|
|||||||
photo: Photo
|
photo: Photo
|
||||||
photos: Photo[]
|
photos: Photo[]
|
||||||
photosGrid?: Photo[]
|
photosGrid?: Photo[]
|
||||||
tag?: string
|
|
||||||
camera?: Camera
|
|
||||||
simulation?: FilmSimulation
|
|
||||||
focal?: number
|
|
||||||
indexNumber?: number
|
indexNumber?: number
|
||||||
count?: number
|
count?: number
|
||||||
dateRange?: PhotoDateRange
|
dateRange?: PhotoDateRange
|
||||||
shouldShare?: boolean
|
shouldShare?: boolean
|
||||||
includeFavoriteInAdminMenu?: boolean
|
includeFavoriteInAdminMenu?: boolean
|
||||||
}) {
|
} & PhotoSetAttributes) {
|
||||||
return (
|
let customHeader: JSX.Element | undefined;
|
||||||
<div>
|
|
||||||
{tag &&
|
if (tag) {
|
||||||
<SiteGrid
|
customHeader = tag === TAG_HIDDEN
|
||||||
className="mt-4 mb-8"
|
|
||||||
contentMain={tag === TAG_HIDDEN
|
|
||||||
? <HiddenHeader
|
? <HiddenHeader
|
||||||
photos={photos}
|
photos={photos}
|
||||||
selectedPhoto={photo}
|
selectedPhoto={photo}
|
||||||
@ -60,64 +52,44 @@ export default function PhotoDetailPage({
|
|||||||
indexNumber={indexNumber}
|
indexNumber={indexNumber}
|
||||||
count={count}
|
count={count}
|
||||||
dateRange={dateRange}
|
dateRange={dateRange}
|
||||||
/>}
|
/>;
|
||||||
/>}
|
} else if (camera) {
|
||||||
{camera &&
|
customHeader = <CameraHeader
|
||||||
<SiteGrid
|
|
||||||
className="mt-4 mb-8"
|
|
||||||
contentMain={
|
|
||||||
<CameraHeader
|
|
||||||
camera={camera}
|
camera={camera}
|
||||||
photos={photos}
|
photos={photos}
|
||||||
selectedPhoto={photo}
|
selectedPhoto={photo}
|
||||||
indexNumber={indexNumber}
|
indexNumber={indexNumber}
|
||||||
count={count}
|
count={count}
|
||||||
dateRange={dateRange}
|
dateRange={dateRange}
|
||||||
/>}
|
/>;
|
||||||
/>}
|
} else if (simulation) {
|
||||||
{simulation &&
|
customHeader = <FilmSimulationHeader
|
||||||
<SiteGrid
|
|
||||||
className="mt-4 mb-8"
|
|
||||||
contentMain={
|
|
||||||
<FilmSimulationHeader
|
|
||||||
simulation={simulation}
|
simulation={simulation}
|
||||||
photos={photos}
|
photos={photos}
|
||||||
selectedPhoto={photo}
|
selectedPhoto={photo}
|
||||||
indexNumber={indexNumber}
|
indexNumber={indexNumber}
|
||||||
count={count}
|
count={count}
|
||||||
dateRange={dateRange}
|
dateRange={dateRange}
|
||||||
/>}
|
/>;
|
||||||
/>}
|
} else if (focal) {
|
||||||
{focal &&
|
customHeader = <FocalLengthHeader
|
||||||
<SiteGrid
|
|
||||||
className="mt-4 mb-8"
|
|
||||||
contentMain={
|
|
||||||
<FocalLengthHeader
|
|
||||||
focal={focal}
|
focal={focal}
|
||||||
photos={photos}
|
photos={photos}
|
||||||
selectedPhoto={photo}
|
selectedPhoto={photo}
|
||||||
indexNumber={indexNumber}
|
indexNumber={indexNumber}
|
||||||
count={count}
|
count={count}
|
||||||
dateRange={dateRange}
|
dateRange={dateRange}
|
||||||
/>}
|
/>;
|
||||||
/>}
|
}
|
||||||
<AnimateItems
|
|
||||||
animateOnFirstLoadOnly
|
return (
|
||||||
items={[
|
<div>
|
||||||
<SiteGrid
|
<SiteGrid
|
||||||
key="photo-nav"
|
className="mt-2 mb-6 sm:mb-8"
|
||||||
className="mb-4"
|
contentMain={customHeader ?? <PhotoHeader
|
||||||
contentMain={<PhotoNav {...{
|
selectedPhoto={photo}
|
||||||
photo,
|
photos={photos}
|
||||||
photos,
|
/>}
|
||||||
className: 'border-t pt-4 border-gray-100 dark:border-gray-900',
|
|
||||||
tag,
|
|
||||||
camera,
|
|
||||||
simulation,
|
|
||||||
focal,
|
|
||||||
}} />}
|
|
||||||
/>,
|
|
||||||
]}
|
|
||||||
/>
|
/>
|
||||||
<AnimateItems
|
<AnimateItems
|
||||||
className="md:mb-8"
|
className="md:mb-8"
|
||||||
@ -129,7 +101,7 @@ export default function PhotoDetailPage({
|
|||||||
primaryTag={tag}
|
primaryTag={tag}
|
||||||
priority
|
priority
|
||||||
prefetchRelatedLinks
|
prefetchRelatedLinks
|
||||||
showTitle={false}
|
showTitle={Boolean(customHeader)}
|
||||||
showCamera={!camera}
|
showCamera={!camera}
|
||||||
showSimulation={!simulation}
|
showSimulation={!simulation}
|
||||||
shouldShare={shouldShare}
|
shouldShare={shouldShare}
|
||||||
|
|||||||
@ -1,11 +1,9 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { Photo } from '.';
|
import { Photo, PhotoSetAttributes } from '.';
|
||||||
import PhotoMedium from './PhotoMedium';
|
import PhotoMedium from './PhotoMedium';
|
||||||
import { clsx } from 'clsx/lite';
|
import { clsx } from 'clsx/lite';
|
||||||
import AnimateItems from '@/components/AnimateItems';
|
import AnimateItems from '@/components/AnimateItems';
|
||||||
import { Camera } from '@/camera';
|
|
||||||
import { FilmSimulation } from '@/simulation';
|
|
||||||
import { GRID_ASPECT_RATIO, HIGH_DENSITY_GRID } from '@/site/config';
|
import { GRID_ASPECT_RATIO, HIGH_DENSITY_GRID } from '@/site/config';
|
||||||
import { useAppState } from '@/state/AppState';
|
import { useAppState } from '@/state/AppState';
|
||||||
import SelectTileOverlay from '@/components/SelectTileOverlay';
|
import SelectTileOverlay from '@/components/SelectTileOverlay';
|
||||||
@ -31,10 +29,6 @@ export default function PhotoGrid({
|
|||||||
}: {
|
}: {
|
||||||
photos: Photo[]
|
photos: Photo[]
|
||||||
selectedPhoto?: Photo
|
selectedPhoto?: Photo
|
||||||
tag?: string
|
|
||||||
camera?: Camera
|
|
||||||
simulation?: FilmSimulation
|
|
||||||
focal?: number
|
|
||||||
photoPriority?: boolean
|
photoPriority?: boolean
|
||||||
fast?: boolean
|
fast?: boolean
|
||||||
animate?: boolean
|
animate?: boolean
|
||||||
@ -46,7 +40,7 @@ export default function PhotoGrid({
|
|||||||
canSelect?: boolean
|
canSelect?: boolean
|
||||||
onLastPhotoVisible?: () => void
|
onLastPhotoVisible?: () => void
|
||||||
onAnimationComplete?: () => void
|
onAnimationComplete?: () => void
|
||||||
}) {
|
} & PhotoSetAttributes) {
|
||||||
const {
|
const {
|
||||||
isUserSignedIn,
|
isUserSignedIn,
|
||||||
selectedPhotoIds,
|
selectedPhotoIds,
|
||||||
|
|||||||
@ -38,7 +38,7 @@ export default function PhotoGridContainer({
|
|||||||
return (
|
return (
|
||||||
<SiteGrid
|
<SiteGrid
|
||||||
contentMain={<div className={clsx(
|
contentMain={<div className={clsx(
|
||||||
header && 'space-y-8 mt-4',
|
header && 'space-y-8 mt-2',
|
||||||
)}>
|
)}>
|
||||||
{header &&
|
{header &&
|
||||||
<AnimateItems
|
<AnimateItems
|
||||||
|
|||||||
123
src/photo/PhotoHeader.tsx
Normal file
123
src/photo/PhotoHeader.tsx
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
import { clsx } from 'clsx/lite';
|
||||||
|
import {
|
||||||
|
Photo,
|
||||||
|
PhotoDateRange,
|
||||||
|
PhotoSetAttributes,
|
||||||
|
dateRangeForPhotos,
|
||||||
|
} from '.';
|
||||||
|
import ShareButton from '@/components/ShareButton';
|
||||||
|
import AnimateItems from '@/components/AnimateItems';
|
||||||
|
import { ReactNode } from 'react';
|
||||||
|
import {
|
||||||
|
HIGH_DENSITY_GRID,
|
||||||
|
SHOW_PHOTO_TITLE_FALLBACK_TEXT,
|
||||||
|
} from '@/site/config';
|
||||||
|
import DivDebugBaselineGrid from '@/components/DivDebugBaselineGrid';
|
||||||
|
import PhotoPrevNext from './PhotoPrevNext';
|
||||||
|
import PhotoLink from './PhotoLink';
|
||||||
|
|
||||||
|
export default function PhotoHeader({
|
||||||
|
tag,
|
||||||
|
camera,
|
||||||
|
simulation,
|
||||||
|
focal,
|
||||||
|
photos,
|
||||||
|
selectedPhoto,
|
||||||
|
entity,
|
||||||
|
entityVerb,
|
||||||
|
entityDescription,
|
||||||
|
sharePath,
|
||||||
|
indexNumber,
|
||||||
|
count,
|
||||||
|
dateRange,
|
||||||
|
}: {
|
||||||
|
photos: Photo[]
|
||||||
|
selectedPhoto?: Photo
|
||||||
|
entity?: ReactNode
|
||||||
|
entityVerb?: string
|
||||||
|
entityDescription?: string
|
||||||
|
sharePath?: string
|
||||||
|
indexNumber?: number
|
||||||
|
count?: number
|
||||||
|
dateRange?: PhotoDateRange
|
||||||
|
} & PhotoSetAttributes) {
|
||||||
|
const { start, end } = dateRangeForPhotos(photos, dateRange);
|
||||||
|
|
||||||
|
const selectedPhotoIndex = selectedPhoto
|
||||||
|
? photos.findIndex(photo => photo.id === selectedPhoto.id)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const renderPrevNext = () =>
|
||||||
|
<PhotoPrevNext {...{
|
||||||
|
photo: selectedPhoto,
|
||||||
|
photos,
|
||||||
|
tag,
|
||||||
|
camera,
|
||||||
|
simulation,
|
||||||
|
focal,
|
||||||
|
}} />;
|
||||||
|
|
||||||
|
const renderDateRange = () =>
|
||||||
|
<span className="text-dim uppercase text-right">
|
||||||
|
{start === end
|
||||||
|
? start
|
||||||
|
: <>{end}<br />– {start}</>}
|
||||||
|
</span>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AnimateItems
|
||||||
|
type="bottom"
|
||||||
|
distanceOffset={10}
|
||||||
|
animateOnFirstLoadOnly
|
||||||
|
items={[<DivDebugBaselineGrid
|
||||||
|
key="PhotosHeader"
|
||||||
|
className={clsx(
|
||||||
|
'grid gap-0.5 sm:gap-1 items-start grid-cols-2',
|
||||||
|
HIGH_DENSITY_GRID
|
||||||
|
? 'sm:grid-cols-4 lg:grid-cols-5'
|
||||||
|
: 'sm:grid-cols-4 md:grid-cols-3 lg:grid-cols-4',
|
||||||
|
)}>
|
||||||
|
<span className={clsx(
|
||||||
|
'inline-flex uppercase',
|
||||||
|
HIGH_DENSITY_GRID && 'sm:col-span-2',
|
||||||
|
)}>
|
||||||
|
{entity ?? (
|
||||||
|
(selectedPhoto?.title || SHOW_PHOTO_TITLE_FALLBACK_TEXT)
|
||||||
|
? <PhotoLink
|
||||||
|
photo={selectedPhoto}
|
||||||
|
className="uppercase font-bold"
|
||||||
|
/>
|
||||||
|
: <>X of X</>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
<span className={clsx(
|
||||||
|
'hidden sm:block',
|
||||||
|
'inline-flex gap-2 self-start',
|
||||||
|
'uppercase text-dim',
|
||||||
|
HIGH_DENSITY_GRID
|
||||||
|
? 'lg:col-span-2'
|
||||||
|
: 'sm:col-span-2 md:col-span-1 lg:col-span-2',
|
||||||
|
)}>
|
||||||
|
{entity && <>
|
||||||
|
{selectedPhotoIndex !== undefined
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
? `${entityVerb ? `${entityVerb} ` : ''}${indexNumber || (selectedPhotoIndex + 1)} of ${count ?? photos.length}`
|
||||||
|
: entityDescription}
|
||||||
|
{selectedPhotoIndex === undefined && sharePath &&
|
||||||
|
<ShareButton
|
||||||
|
className="translate-y-[1.5px]"
|
||||||
|
path={sharePath}
|
||||||
|
dim
|
||||||
|
/>}
|
||||||
|
</>}
|
||||||
|
</span>
|
||||||
|
<div className="flex justify-end">
|
||||||
|
{selectedPhoto
|
||||||
|
? renderPrevNext()
|
||||||
|
: renderDateRange()}
|
||||||
|
</div>
|
||||||
|
</DivDebugBaselineGrid>,
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,13 +1,11 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
import { Photo, titleForPhoto } from '@/photo';
|
import { Photo, PhotoSetAttributes, titleForPhoto } from '@/photo';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { AnimationConfig } from '../components/AnimateItems';
|
import { AnimationConfig } from '../components/AnimateItems';
|
||||||
import { useAppState } from '@/state/AppState';
|
import { useAppState } from '@/state/AppState';
|
||||||
import { pathForPhoto } from '@/site/paths';
|
import { pathForPhoto } from '@/site/paths';
|
||||||
import { Camera } from '@/camera';
|
|
||||||
import { FilmSimulation } from '@/simulation';
|
|
||||||
import { clsx } from 'clsx/lite';
|
import { clsx } from 'clsx/lite';
|
||||||
|
|
||||||
export default function PhotoLink({
|
export default function PhotoLink({
|
||||||
@ -23,16 +21,12 @@ export default function PhotoLink({
|
|||||||
children,
|
children,
|
||||||
}: {
|
}: {
|
||||||
photo?: Photo
|
photo?: Photo
|
||||||
tag?: string
|
|
||||||
camera?: Camera
|
|
||||||
simulation?: FilmSimulation
|
|
||||||
focal?: number
|
|
||||||
scroll?: boolean
|
scroll?: boolean
|
||||||
prefetch?: boolean
|
prefetch?: boolean
|
||||||
nextPhotoAnimation?: AnimationConfig
|
nextPhotoAnimation?: AnimationConfig
|
||||||
className?: string
|
className?: string
|
||||||
children?: ReactNode
|
children?: ReactNode
|
||||||
}) {
|
} & PhotoSetAttributes) {
|
||||||
const { setNextPhotoAnimation } = useAppState();
|
const { setNextPhotoAnimation } = useAppState();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,12 +1,15 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { Photo, altTextForPhoto, doesPhotoNeedBlurCompatibility } from '.';
|
import {
|
||||||
|
Photo,
|
||||||
|
PhotoSetAttributes,
|
||||||
|
altTextForPhoto,
|
||||||
|
doesPhotoNeedBlurCompatibility,
|
||||||
|
} from '.';
|
||||||
import ImageMedium from '@/components/image/ImageMedium';
|
import ImageMedium from '@/components/image/ImageMedium';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { clsx } from 'clsx/lite';
|
import { clsx } from 'clsx/lite';
|
||||||
import { pathForPhoto } from '@/site/paths';
|
import { pathForPhoto } from '@/site/paths';
|
||||||
import { Camera } from '@/camera';
|
|
||||||
import { FilmSimulation } from '@/simulation';
|
|
||||||
import { SHOULD_PREFETCH_ALL_LINKS } from '@/site/config';
|
import { SHOULD_PREFETCH_ALL_LINKS } from '@/site/config';
|
||||||
import { useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
import useOnVisible from '@/utility/useOnVisible';
|
import useOnVisible from '@/utility/useOnVisible';
|
||||||
@ -24,16 +27,12 @@ export default function PhotoMedium({
|
|||||||
onVisible,
|
onVisible,
|
||||||
}: {
|
}: {
|
||||||
photo: Photo
|
photo: Photo
|
||||||
tag?: string
|
|
||||||
camera?: Camera
|
|
||||||
simulation?: FilmSimulation
|
|
||||||
focal?: number
|
|
||||||
selected?: boolean
|
selected?: boolean
|
||||||
priority?: boolean
|
priority?: boolean
|
||||||
prefetch?: boolean
|
prefetch?: boolean
|
||||||
className?: string
|
className?: string
|
||||||
onVisible?: () => void
|
onVisible?: () => void
|
||||||
}) {
|
} & PhotoSetAttributes) {
|
||||||
const ref = useRef<HTMLAnchorElement>(null);
|
const ref = useRef<HTMLAnchorElement>(null);
|
||||||
|
|
||||||
useOnVisible(ref, onVisible);
|
useOnVisible(ref, onVisible);
|
||||||
|
|||||||
@ -1,16 +1,17 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { Photo, getNextPhoto, getPreviousPhoto } from '@/photo';
|
import {
|
||||||
|
Photo,
|
||||||
|
PhotoSetAttributes,
|
||||||
|
getNextPhoto,
|
||||||
|
getPreviousPhoto,
|
||||||
|
} from '@/photo';
|
||||||
import PhotoLink from './PhotoLink';
|
import PhotoLink from './PhotoLink';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { pathForPhoto } from '@/site/paths';
|
import { pathForPhoto } from '@/site/paths';
|
||||||
import { useAppState } from '@/state/AppState';
|
import { useAppState } from '@/state/AppState';
|
||||||
import { AnimationConfig } from '@/components/AnimateItems';
|
import { AnimationConfig } from '@/components/AnimateItems';
|
||||||
import { Camera } from '@/camera';
|
|
||||||
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';
|
import { clsx } from 'clsx/lite';
|
||||||
|
|
||||||
const LISTENER_KEYUP = 'keyup';
|
const LISTENER_KEYUP = 'keyup';
|
||||||
@ -18,25 +19,19 @@ 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 PhotoNav({
|
export default function PhotoPrevNext({
|
||||||
photo,
|
photo,
|
||||||
photos,
|
photos = [],
|
||||||
className,
|
className,
|
||||||
tag,
|
tag,
|
||||||
camera,
|
camera,
|
||||||
simulation,
|
simulation,
|
||||||
focal,
|
focal,
|
||||||
prefetch,
|
|
||||||
}: {
|
}: {
|
||||||
photo: Photo
|
photo?: Photo
|
||||||
photos: Photo[]
|
photos?: Photo[]
|
||||||
className?: string
|
className?: string
|
||||||
tag?: string
|
} & PhotoSetAttributes) {
|
||||||
camera?: Camera
|
|
||||||
simulation?: FilmSimulation
|
|
||||||
focal?: number
|
|
||||||
prefetch?: boolean
|
|
||||||
}) {
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -44,8 +39,8 @@ export default function PhotoNav({
|
|||||||
shouldRespondToKeyboardCommands,
|
shouldRespondToKeyboardCommands,
|
||||||
} = useAppState();
|
} = useAppState();
|
||||||
|
|
||||||
const previousPhoto = getPreviousPhoto(photo, photos);
|
const previousPhoto = photo ? getPreviousPhoto(photo, photos) : undefined;
|
||||||
const nextPhoto = getNextPhoto(photo, photos);
|
const nextPhoto = photo ? getNextPhoto(photo, photos) : undefined;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (shouldRespondToKeyboardCommands) {
|
if (shouldRespondToKeyboardCommands) {
|
||||||
@ -105,8 +100,10 @@ export default function PhotoNav({
|
|||||||
'flex items-center',
|
'flex items-center',
|
||||||
className,
|
className,
|
||||||
)}>
|
)}>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
<PhotoLink
|
<PhotoLink
|
||||||
photo={previousPhoto}
|
photo={previousPhoto}
|
||||||
|
className="select-none"
|
||||||
nextPhotoAnimation={ANIMATION_RIGHT}
|
nextPhotoAnimation={ANIMATION_RIGHT}
|
||||||
tag={tag}
|
tag={tag}
|
||||||
camera={camera}
|
camera={camera}
|
||||||
@ -116,25 +113,13 @@ export default function PhotoNav({
|
|||||||
prefetch
|
prefetch
|
||||||
>
|
>
|
||||||
<span className="group inline-flex gap-1 items-center">
|
<span className="group inline-flex gap-1 items-center">
|
||||||
<BiChevronLeft
|
|
||||||
className={clsx(
|
|
||||||
'text-[1.25rem] transition-transform',
|
|
||||||
'group-hover:-translate-x-1',
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
PREV
|
PREV
|
||||||
</span>
|
</span>
|
||||||
</PhotoLink>
|
</PhotoLink>
|
||||||
<div className="grow text-center">
|
<span className="text-extra-extra-dim">/</span>
|
||||||
{(photo.title || SHOW_PHOTO_TITLE_FALLBACK_TEXT) &&
|
|
||||||
<PhotoLink
|
|
||||||
photo={photo}
|
|
||||||
className="uppercase font-bold"
|
|
||||||
prefetch={prefetch}
|
|
||||||
/>}
|
|
||||||
</div>
|
|
||||||
<PhotoLink
|
<PhotoLink
|
||||||
photo={nextPhoto}
|
photo={nextPhoto}
|
||||||
|
className="select-none"
|
||||||
nextPhotoAnimation={ANIMATION_LEFT}
|
nextPhotoAnimation={ANIMATION_LEFT}
|
||||||
tag={tag}
|
tag={tag}
|
||||||
camera={camera}
|
camera={camera}
|
||||||
@ -145,14 +130,9 @@ export default function PhotoNav({
|
|||||||
>
|
>
|
||||||
<span className="group inline-flex gap-1 items-center">
|
<span className="group inline-flex gap-1 items-center">
|
||||||
NEXT
|
NEXT
|
||||||
<BiChevronRight
|
|
||||||
className={clsx(
|
|
||||||
'text-[1.25rem] transition-transform',
|
|
||||||
'group-hover:translate-x-1',
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</span>
|
</span>
|
||||||
</PhotoLink>
|
</PhotoLink>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -1,85 +0,0 @@
|
|||||||
import { clsx } from 'clsx/lite';
|
|
||||||
import { Photo, PhotoDateRange, dateRangeForPhotos } from '.';
|
|
||||||
import ShareButton from '@/components/ShareButton';
|
|
||||||
import AnimateItems from '@/components/AnimateItems';
|
|
||||||
import { ReactNode } from 'react';
|
|
||||||
import { HIGH_DENSITY_GRID } from '@/site/config';
|
|
||||||
import DivDebugBaselineGrid from '@/components/DivDebugBaselineGrid';
|
|
||||||
|
|
||||||
export default function PhotoSetHeader({
|
|
||||||
entity,
|
|
||||||
entityVerb,
|
|
||||||
entityDescription,
|
|
||||||
photos,
|
|
||||||
selectedPhoto,
|
|
||||||
sharePath,
|
|
||||||
indexNumber,
|
|
||||||
count,
|
|
||||||
dateRange,
|
|
||||||
}: {
|
|
||||||
entity: ReactNode
|
|
||||||
entityVerb?: string
|
|
||||||
entityDescription: string
|
|
||||||
photos: Photo[]
|
|
||||||
selectedPhoto?: Photo
|
|
||||||
sharePath?: string
|
|
||||||
indexNumber?: number
|
|
||||||
count?: number
|
|
||||||
dateRange?: PhotoDateRange
|
|
||||||
}) {
|
|
||||||
const { start, end } = dateRangeForPhotos(photos, dateRange);
|
|
||||||
|
|
||||||
const selectedPhotoIndex = selectedPhoto
|
|
||||||
? photos.findIndex(photo => photo.id === selectedPhoto.id)
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<AnimateItems
|
|
||||||
type="bottom"
|
|
||||||
distanceOffset={10}
|
|
||||||
animateOnFirstLoadOnly
|
|
||||||
items={[<DivDebugBaselineGrid
|
|
||||||
key="PhotosHeader"
|
|
||||||
className={clsx(
|
|
||||||
'grid gap-0.5 sm:gap-1 items-start',
|
|
||||||
HIGH_DENSITY_GRID
|
|
||||||
? 'xs:grid-cols-2 sm:grid-cols-4 lg:grid-cols-5'
|
|
||||||
: 'xs:grid-cols-2 sm:grid-cols-4 md:grid-cols-3 lg:grid-cols-4',
|
|
||||||
)}>
|
|
||||||
<span className={clsx(
|
|
||||||
'inline-flex uppercase',
|
|
||||||
HIGH_DENSITY_GRID && 'sm:col-span-2',
|
|
||||||
)}>
|
|
||||||
{entity}
|
|
||||||
</span>
|
|
||||||
<span className={clsx(
|
|
||||||
'inline-flex gap-2 self-start',
|
|
||||||
'uppercase text-dim',
|
|
||||||
HIGH_DENSITY_GRID
|
|
||||||
? 'lg:col-span-2'
|
|
||||||
: 'sm:col-span-2 md:col-span-1 lg:col-span-2',
|
|
||||||
)}>
|
|
||||||
{selectedPhotoIndex !== undefined
|
|
||||||
// eslint-disable-next-line max-len
|
|
||||||
? `${entityVerb ? `${entityVerb} ` : ''}${indexNumber || (selectedPhotoIndex + 1)} of ${count ?? photos.length}`
|
|
||||||
: entityDescription}
|
|
||||||
{selectedPhotoIndex === undefined && sharePath &&
|
|
||||||
<ShareButton
|
|
||||||
className="translate-y-[1.5px]"
|
|
||||||
path={sharePath}
|
|
||||||
dim
|
|
||||||
/>}
|
|
||||||
</span>
|
|
||||||
<span className={clsx(
|
|
||||||
'hidden sm:inline-block',
|
|
||||||
'text-right uppercase',
|
|
||||||
'text-dim',
|
|
||||||
)}>
|
|
||||||
{start === end
|
|
||||||
? start
|
|
||||||
: <>{end}<br />– {start}</>}
|
|
||||||
</span>
|
|
||||||
</DivDebugBaselineGrid>]}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,17 +1,11 @@
|
|||||||
import PhotoOGTile from '@/photo/PhotoOGTile';
|
import PhotoOGTile from '@/photo/PhotoOGTile';
|
||||||
import { absolutePathForPhoto, pathForPhoto } from '@/site/paths';
|
import { absolutePathForPhoto, pathForPhoto } from '@/site/paths';
|
||||||
import { Photo } from '.';
|
import { Photo, PhotoSetAttributes } from '.';
|
||||||
import ShareModal from '@/components/ShareModal';
|
import ShareModal from '@/components/ShareModal';
|
||||||
import { Camera } from '@/camera';
|
|
||||||
import { FilmSimulation } from '@/simulation';
|
|
||||||
|
|
||||||
export default function PhotoShareModal(props: {
|
export default function PhotoShareModal(props: {
|
||||||
photo: Photo
|
photo: Photo
|
||||||
tag?: string
|
} & PhotoSetAttributes) {
|
||||||
camera?: Camera
|
|
||||||
simulation?: FilmSimulation
|
|
||||||
focal?: number
|
|
||||||
}) {
|
|
||||||
return (
|
return (
|
||||||
<ShareModal
|
<ShareModal
|
||||||
pathShare={absolutePathForPhoto(props)}
|
pathShare={absolutePathForPhoto(props)}
|
||||||
|
|||||||
@ -1,4 +1,9 @@
|
|||||||
import { Photo, altTextForPhoto, doesPhotoNeedBlurCompatibility } from '.';
|
import {
|
||||||
|
Photo,
|
||||||
|
PhotoSetAttributes,
|
||||||
|
altTextForPhoto,
|
||||||
|
doesPhotoNeedBlurCompatibility,
|
||||||
|
} from '.';
|
||||||
import ImageSmall from '@/components/image/ImageSmall';
|
import ImageSmall from '@/components/image/ImageSmall';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { clsx } from 'clsx/lite';
|
import { clsx } from 'clsx/lite';
|
||||||
@ -6,8 +11,6 @@ import { pathForPhoto } from '@/site/paths';
|
|||||||
import { SHOULD_PREFETCH_ALL_LINKS } from '@/site/config';
|
import { SHOULD_PREFETCH_ALL_LINKS } from '@/site/config';
|
||||||
import { useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
import useOnVisible from '@/utility/useOnVisible';
|
import useOnVisible from '@/utility/useOnVisible';
|
||||||
import { Camera } from '@/camera';
|
|
||||||
import { FilmSimulation } from '@/simulation';
|
|
||||||
|
|
||||||
export default function PhotoSmall({
|
export default function PhotoSmall({
|
||||||
photo,
|
photo,
|
||||||
@ -21,15 +24,11 @@ export default function PhotoSmall({
|
|||||||
onVisible,
|
onVisible,
|
||||||
}: {
|
}: {
|
||||||
photo: Photo
|
photo: Photo
|
||||||
tag?: string
|
|
||||||
camera?: Camera
|
|
||||||
simulation?: FilmSimulation
|
|
||||||
focal?: number
|
|
||||||
selected?: boolean
|
selected?: boolean
|
||||||
className?: string
|
className?: string
|
||||||
prefetch?: boolean
|
prefetch?: boolean
|
||||||
onVisible?: () => void
|
onVisible?: () => void
|
||||||
}) {
|
} & PhotoSetAttributes) {
|
||||||
const ref = useRef<HTMLAnchorElement>(null);
|
const ref = useRef<HTMLAnchorElement>(null);
|
||||||
|
|
||||||
useOnVisible(ref, onVisible);
|
useOnVisible(ref, onVisible);
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
import { Camera } from '@/camera';
|
|
||||||
import { Lens } from '@/lens';
|
|
||||||
import { FilmSimulation } from '@/simulation';
|
|
||||||
import { PRIORITY_ORDER_ENABLED } from '@/site/config';
|
import { PRIORITY_ORDER_ENABLED } from '@/site/config';
|
||||||
import { parameterize } from '@/utility/string';
|
import { parameterize } from '@/utility/string';
|
||||||
|
import { PhotoSetAttributes } from '..';
|
||||||
|
|
||||||
export const GENERATE_STATIC_PARAMS_LIMIT = 1000;
|
export const GENERATE_STATIC_PARAMS_LIMIT = 1000;
|
||||||
export const PHOTO_DEFAULT_LIMIT = 100;
|
export const PHOTO_DEFAULT_LIMIT = 100;
|
||||||
@ -12,16 +10,11 @@ export type GetPhotosOptions = {
|
|||||||
limit?: number
|
limit?: number
|
||||||
offset?: number
|
offset?: number
|
||||||
query?: string
|
query?: string
|
||||||
tag?: string
|
|
||||||
camera?: Camera
|
|
||||||
lens?: Lens
|
|
||||||
simulation?: FilmSimulation
|
|
||||||
focal?: number
|
|
||||||
takenBefore?: Date
|
takenBefore?: Date
|
||||||
takenAfterInclusive?: Date
|
takenAfterInclusive?: Date
|
||||||
updatedBefore?: Date
|
updatedBefore?: Date
|
||||||
hidden?: 'exclude' | 'include' | 'only'
|
hidden?: 'exclude' | 'include' | 'only'
|
||||||
};
|
} & PhotoSetAttributes;
|
||||||
|
|
||||||
export const areOptionsSensitive = (options: GetPhotosOptions) =>
|
export const areOptionsSensitive = (options: GetPhotosOptions) =>
|
||||||
options.hidden === 'include' || options.hidden === 'only';
|
options.hidden === 'include' || options.hidden === 'only';
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
|
import { Camera } from '@/camera';
|
||||||
import { formatFocalLength } from '@/focal';
|
import { formatFocalLength } from '@/focal';
|
||||||
|
import { Lens } from '@/lens';
|
||||||
import { getNextImageUrlForRequest } from '@/services/next-image';
|
import { getNextImageUrlForRequest } from '@/services/next-image';
|
||||||
import { FilmSimulation } from '@/simulation';
|
import { FilmSimulation } from '@/simulation';
|
||||||
import { HIGH_DENSITY_GRID, SHOW_EXIF_DATA } from '@/site/config';
|
import { HIGH_DENSITY_GRID, SHOW_EXIF_DATA } from '@/site/config';
|
||||||
@ -99,6 +101,14 @@ export interface Photo extends PhotoDb {
|
|||||||
takenAtNaiveFormatted: string
|
takenAtNaiveFormatted: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PhotoSetAttributes {
|
||||||
|
tag?: string
|
||||||
|
camera?: Camera
|
||||||
|
simulation?: FilmSimulation
|
||||||
|
focal?: number
|
||||||
|
lens?: Lens // Unimplemented as a set
|
||||||
|
}
|
||||||
|
|
||||||
export const parsePhotoFromDb = (photoDbRaw: PhotoDb): Photo => {
|
export const parsePhotoFromDb = (photoDbRaw: PhotoDb): Photo => {
|
||||||
const photoDb = camelcaseKeys(
|
const photoDb = camelcaseKeys(
|
||||||
photoDbRaw as unknown as Record<string, unknown>
|
photoDbRaw as unknown as Record<string, unknown>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { Photo, PhotoDateRange } from '@/photo';
|
import { Photo, PhotoDateRange } from '@/photo';
|
||||||
import { FilmSimulation, descriptionForFilmSimulationPhotos } from '.';
|
import { FilmSimulation, descriptionForFilmSimulationPhotos } from '.';
|
||||||
import { pathForFilmSimulationShare } from '@/site/paths';
|
import { pathForFilmSimulationShare } from '@/site/paths';
|
||||||
import PhotoSetHeader from '@/photo/PhotoSetHeader';
|
import PhotoHeader from '@/photo/PhotoHeader';
|
||||||
import PhotoFilmSimulation from
|
import PhotoFilmSimulation from
|
||||||
'@/simulation/PhotoFilmSimulation';
|
'@/simulation/PhotoFilmSimulation';
|
||||||
|
|
||||||
@ -21,7 +21,8 @@ export default function FilmSimulationHeader({
|
|||||||
dateRange?: PhotoDateRange
|
dateRange?: PhotoDateRange
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<PhotoSetHeader
|
<PhotoHeader
|
||||||
|
simulation={simulation}
|
||||||
entity={<PhotoFilmSimulation {...{ simulation }} />}
|
entity={<PhotoFilmSimulation {...{ simulation }} />}
|
||||||
entityVerb="Photo"
|
entityVerb="Photo"
|
||||||
entityDescription={descriptionForFilmSimulationPhotos(
|
entityDescription={descriptionForFilmSimulationPhotos(
|
||||||
|
|||||||
@ -142,6 +142,10 @@
|
|||||||
@apply
|
@apply
|
||||||
text-gray-400/80 dark:text-gray-400/50
|
text-gray-400/80 dark:text-gray-400/50
|
||||||
}
|
}
|
||||||
|
.text-extra-extra-dim {
|
||||||
|
@apply
|
||||||
|
text-gray-200 dark:text-gray-800
|
||||||
|
}
|
||||||
.text-icon {
|
.text-icon {
|
||||||
@apply
|
@apply
|
||||||
text-gray-800 dark:text-gray-200
|
text-gray-800 dark:text-gray-200
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Photo } from '@/photo';
|
import { Photo, PhotoSetAttributes } from '@/photo';
|
||||||
import { BASE_URL, GRID_HOMEPAGE_ENABLED } from './config';
|
import { BASE_URL, GRID_HOMEPAGE_ENABLED } from './config';
|
||||||
import { Camera } from '@/camera';
|
import { Camera } from '@/camera';
|
||||||
import { FilmSimulation } from '@/simulation';
|
import { FilmSimulation } from '@/simulation';
|
||||||
@ -75,13 +75,7 @@ export const PATHS_TO_CACHE = [
|
|||||||
...PATHS_ADMIN,
|
...PATHS_ADMIN,
|
||||||
];
|
];
|
||||||
|
|
||||||
interface PhotoPathParams {
|
type PhotoPathParams = { photo: PhotoOrPhotoId } & PhotoSetAttributes;
|
||||||
photo: PhotoOrPhotoId
|
|
||||||
tag?: string
|
|
||||||
camera?: Camera
|
|
||||||
simulation?: FilmSimulation
|
|
||||||
focal?: number
|
|
||||||
}
|
|
||||||
|
|
||||||
// Absolute paths
|
// Absolute paths
|
||||||
export const ABSOLUTE_PATH_FOR_HOME_IMAGE = `${BASE_URL}/home-image`;
|
export const ABSOLUTE_PATH_FOR_HOME_IMAGE = `${BASE_URL}/home-image`;
|
||||||
@ -280,11 +274,7 @@ export const isPathProtected = (pathname?: string) =>
|
|||||||
|
|
||||||
export const getPathComponents = (pathname = ''): {
|
export const getPathComponents = (pathname = ''): {
|
||||||
photoId?: string
|
photoId?: string
|
||||||
tag?: string
|
} & PhotoSetAttributes => {
|
||||||
camera?: Camera
|
|
||||||
simulation?: FilmSimulation
|
|
||||||
focal?: number
|
|
||||||
} => {
|
|
||||||
const photoIdFromPhoto = pathname.match(
|
const photoIdFromPhoto = pathname.match(
|
||||||
new RegExp(`^${PREFIX_PHOTO}/([^/]+)`))?.[1];
|
new RegExp(`^${PREFIX_PHOTO}/([^/]+)`))?.[1];
|
||||||
const photoIdFromTag = pathname.match(
|
const photoIdFromTag = pathname.match(
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { Photo, photoQuantityText } from '@/photo';
|
import { Photo, photoQuantityText } from '@/photo';
|
||||||
import PhotoSetHeader from '@/photo/PhotoSetHeader';
|
import PhotoHeader from '@/photo/PhotoHeader';
|
||||||
import HiddenTag from './HiddenTag';
|
import HiddenTag from './HiddenTag';
|
||||||
|
|
||||||
export default function HiddenHeader({
|
export default function HiddenHeader({
|
||||||
@ -14,7 +14,7 @@ export default function HiddenHeader({
|
|||||||
count: number
|
count: number
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<PhotoSetHeader
|
<PhotoHeader
|
||||||
key="HiddenHeader"
|
key="HiddenHeader"
|
||||||
entity={<HiddenTag contrast="high" />}
|
entity={<HiddenTag contrast="high" />}
|
||||||
entityDescription={photoQuantityText(count, false)}
|
entityDescription={photoQuantityText(count, false)}
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { Photo, PhotoDateRange } from '@/photo';
|
|||||||
import PhotoTag from './PhotoTag';
|
import PhotoTag from './PhotoTag';
|
||||||
import { descriptionForTaggedPhotos, isTagFavs } from '.';
|
import { descriptionForTaggedPhotos, isTagFavs } from '.';
|
||||||
import { pathForTagShare } from '@/site/paths';
|
import { pathForTagShare } from '@/site/paths';
|
||||||
import PhotoSetHeader from '@/photo/PhotoSetHeader';
|
import PhotoHeader from '@/photo/PhotoHeader';
|
||||||
import FavsTag from './FavsTag';
|
import FavsTag from './FavsTag';
|
||||||
|
|
||||||
export default function TagHeader({
|
export default function TagHeader({
|
||||||
@ -21,7 +21,8 @@ export default function TagHeader({
|
|||||||
dateRange?: PhotoDateRange
|
dateRange?: PhotoDateRange
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<PhotoSetHeader
|
<PhotoHeader
|
||||||
|
tag={tag}
|
||||||
entity={isTagFavs(tag)
|
entity={isTagFavs(tag)
|
||||||
? <FavsTag contrast="high" />
|
? <FavsTag contrast="high" />
|
||||||
: <PhotoTag tag={tag} contrast="high" />}
|
: <PhotoTag tag={tag} contrast="high" />}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user