From 72900966982985621b605c6ec33c8a3db1105be0 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Tue, 26 Mar 2024 12:13:51 -0500 Subject: [PATCH 01/15] Enforce baseline grid --- src/app/admin/baseline/page.tsx | 193 ++++++++++++++++++++++ src/camera/PhotoCamera.tsx | 6 +- src/components/Badge.tsx | 7 +- src/components/EntityLink.tsx | 97 ----------- src/components/primitives/EntityLink.tsx | 89 ++++++++++ src/components/primitives/Icon.tsx | 35 ++++ src/components/primitives/LabeledIcon.tsx | 60 +++++++ src/photo/PhotoLarge.tsx | 12 +- src/simulation/PhotoFilmSimulation.tsx | 4 +- src/site/FooterClient.tsx | 2 +- src/site/SiteChecklistClient.tsx | 2 +- src/site/globals.css | 22 ++- src/tag/FavsTag.tsx | 6 +- src/tag/PhotoTag.tsx | 6 +- src/tag/PhotoTags.tsx | 12 +- tailwind.config.js | 14 +- 16 files changed, 437 insertions(+), 130 deletions(-) create mode 100644 src/app/admin/baseline/page.tsx delete mode 100644 src/components/EntityLink.tsx create mode 100644 src/components/primitives/EntityLink.tsx create mode 100644 src/components/primitives/Icon.tsx create mode 100644 src/components/primitives/LabeledIcon.tsx diff --git a/src/app/admin/baseline/page.tsx b/src/app/admin/baseline/page.tsx new file mode 100644 index 00000000..0dbec5f6 --- /dev/null +++ b/src/app/admin/baseline/page.tsx @@ -0,0 +1,193 @@ +'use client'; + +import FieldSetWithStatus from '@/components/FieldSetWithStatus'; +import EntityLink from '@/components/primitives/EntityLink'; +import LabeledIcon from '@/components/primitives/LabeledIcon'; +import PhotoFilmSimulationIcon from '@/simulation/PhotoFilmSimulationIcon'; +import { clsx } from 'clsx/lite'; +import { useState } from 'react'; +import { FaCamera, FaHandSparkles, FaUserAltSlash } from 'react-icons/fa'; +import { IoMdCamera } from 'react-icons/io'; +import { IoImageSharp } from 'react-icons/io5'; + +const DEBUG_LINES = new Array(22).fill(null); + +export default function ComponentsPage() { + const [_debugGrid, setDebugGrid] = useState('true'); + const [_debugComponents, setDebugComponents] = useState('false'); + + const debugGrid = _debugGrid === 'true'; + const debugComponents = _debugComponents === 'true'; + + return ( + <> +

+
+ Baseline Grid: + + 13px / 18px + 14px / 20px + +
+
*]:inline-flex [&>*]:gap-1 [&_input]:-translate-y-0.5', + )}> + + +
+

+
+
+
+ } + debug={debugComponents} + > + Camera
Line two +
+
+
+ } debug={debugComponents}> + Image + +
+
+ } debug={debugComponents}> + Image + +
+
+ } debug={debugComponents}> + Image + +
+
+ } debug={debugComponents}> + Image + +
+
+ } debug={debugComponents}> + Image + +
+
+ } debug={debugComponents}> + Image + +
+
+ } debug={debugComponents}> + Image + +
+
+ } debug={debugComponents}> + Image + +
+
+ } debug={debugComponents}> + Image + +
+
+ } debug={debugComponents}> + Image + +
+
+ } + label="Image" + debug={debugComponents} + /> +
+
+ } + label="Image" + badged + debug={debugComponents} + /> +
+
+ } + debug={debugComponents} + > + Canon Mark III + +
+
+ } + label="Astia/Soft" + type="icon-last" + iconWide + badged + debug={debugComponents} + /> +
+
+ } debug={debugComponents}> + Image + +
+
+ } debug={debugComponents}> + Image + +
+
+ } + label="Astia/Soft" + type="icon-last" + badged + debug={debugComponents} + /> +
+
+ } debug={debugComponents}> + Image + +
+
+ } debug={debugComponents}> + Image + +
+
+
*]:bg-gray-800', + '[&>*]:flex', + )}> + {DEBUG_LINES.map((_, i) => +
+ Line {(i + 1).toString().padStart(2, '0')} +
+ )} +
+
+ + ); +} diff --git a/src/camera/PhotoCamera.tsx b/src/camera/PhotoCamera.tsx index 1f9eb84c..e6e8511a 100644 --- a/src/camera/PhotoCamera.tsx +++ b/src/camera/PhotoCamera.tsx @@ -2,7 +2,9 @@ import { AiFillApple } from 'react-icons/ai'; import { pathForCamera } from '@/site/paths'; import { IoMdCamera } from 'react-icons/io'; import { Camera, formatCameraText } from '.'; -import EntityLink, { EntityLinkExternalProps } from '@/components/EntityLink'; +import EntityLink, { + EntityLinkExternalProps, +} from '@/components/primitives/EntityLink'; export default function PhotoCamera({ camera, @@ -31,7 +33,7 @@ export default function PhotoCamera({ /> : } type={showAppleIcon && isCameraApple ? 'icon-first' : type} badged={badged} diff --git a/src/components/Badge.tsx b/src/components/Badge.tsx index 8a942928..e3fa1b6d 100644 --- a/src/components/Badge.tsx +++ b/src/components/Badge.tsx @@ -27,8 +27,9 @@ export default function Badge({ ); case 'small': return clsx( - 'px-[0.3rem] py-1 rounded-[0.25rem]', - 'text-[0.7rem] font-medium', + 'px-[4px] py-[2.5px]', + 'md:px-[4.5px] md:py-[3px]', + 'text-[0.7rem] font-medium rounded-[0.25rem]', highContrast ? 'text-invert bg-invert' : 'text-medium bg-gray-300/30 dark:bg-gray-700/50', @@ -43,7 +44,7 @@ export default function Badge({ }; return ( <> - - {labelSmall ?? label} - - - {label} - - ; - - const classForContrast = () => { - switch (contrast) { - case 'low': - return 'text-dim'; - case 'high': - return 'text-main'; - default: - return 'text-medium'; - } - }; - - return ( - - - {type !== 'icon-only' && <> - {badged - ? - - {renderLabel()} - - - : - {renderLabel()} - } - } - {icon && type !== 'text-only' && - - {icon} - } - - {hoverEntity !== undefined && - - {hoverEntity} - } - - ); -} diff --git a/src/components/primitives/EntityLink.tsx b/src/components/primitives/EntityLink.tsx new file mode 100644 index 00000000..9ccd22fc --- /dev/null +++ b/src/components/primitives/EntityLink.tsx @@ -0,0 +1,89 @@ +import { ReactNode } from 'react'; +import LabeledIcon, { LabeledIconType } from './LabeledIcon'; +import Badge from '../Badge'; +import { clsx } from 'clsx/lite'; + +export interface EntityLinkExternalProps { + type?: LabeledIconType + badged?: boolean + contrast?: 'low' | 'medium' | 'high' +} + +export default function EntityLink({ + icon, + label, + labelSmall, + iconWide, + type, + badged, + contrast = 'medium', + href, + prefetch, + title, + hoverEntity, + debug, +}: { + icon: ReactNode + label: ReactNode + labelSmall?: ReactNode + iconWide?: boolean + href?: string + prefetch?: boolean + title?: string + hoverEntity?: ReactNode + debug?: boolean +} & EntityLinkExternalProps) { + const classForContrast = () => { + switch (contrast) { + case 'low': + return 'text-dim'; + case 'high': + return 'text-main'; + default: + return 'text-medium'; + } + }; + + const renderLabel = () => <> + + {labelSmall ?? label} + + + {label} + + ; + + return ( + + + {badged + ? + {renderLabel()} + + : renderLabel()} + + {hoverEntity !== undefined && + + {hoverEntity} + } + + ); +} diff --git a/src/components/primitives/Icon.tsx b/src/components/primitives/Icon.tsx new file mode 100644 index 00000000..57adbe06 --- /dev/null +++ b/src/components/primitives/Icon.tsx @@ -0,0 +1,35 @@ +import { ReactNode } from 'react'; +import { clsx } from 'clsx/lite'; +import Spinner from '../Spinner'; + +export default function Icon({ + children, + className, + iconClassName, + wide, + loading, + debug, +}: { + children: ReactNode + className?: string + iconClassName?: string + wide?: boolean + loading?: boolean + debug?: boolean, +}) { + return ( + + {loading + ? + : + {children} + } + + ); +} diff --git a/src/components/primitives/LabeledIcon.tsx b/src/components/primitives/LabeledIcon.tsx new file mode 100644 index 00000000..ca249680 --- /dev/null +++ b/src/components/primitives/LabeledIcon.tsx @@ -0,0 +1,60 @@ +import { ComponentProps, ReactNode } from 'react'; +import Icon from './Icon'; +import { clsx } from 'clsx/lite'; +import Link from 'next/link'; + +export type LabeledIconType = + 'icon-first' | + 'icon-last' | + 'icon-only' | + 'text-only'; + +export default function LabeledIcon({ + icon, + type = 'icon-first', + className: classNameProp, + children, + iconWide, + href, + prefetch, + debug, +}: { + icon?: ReactNode, + type?: LabeledIconType, + className?: string, + children: ReactNode, + iconWide?:boolean, + debug?: boolean, +} & Partial>) { + const className = clsx( + 'inline-flex gap-x-1 md:gap-x-1.5', + classNameProp, + debug && 'border border-green-500 m-[-1px]', + ); + + const renderContent = () => <> + {icon && type !== 'text-only' && + + {icon} + } + {children && type !== 'icon-only' && + + {children} + } + ; + + return href + ? + {renderContent()} + + :
+ {renderContent()} +
; +} diff --git a/src/photo/PhotoLarge.tsx b/src/photo/PhotoLarge.tsx index c54b0088..376315fe 100644 --- a/src/photo/PhotoLarge.tsx +++ b/src/photo/PhotoLarge.tsx @@ -65,11 +65,11 @@ export default function PhotoLarge({ contentSide={
{/* Meta */}
@@ -88,7 +88,7 @@ export default function PhotoLarge({
-
+
{photo.caption &&
{photo.caption} @@ -106,7 +106,7 @@ export default function PhotoLarge({
{/* EXIF Data */} -
+
{showExifContent && <>
    @@ -134,8 +134,8 @@ export default function PhotoLarge({ />} }
    -
    +
    {isPathAdmin(pathname) ? <> {userEmail === undefined && diff --git a/src/site/SiteChecklistClient.tsx b/src/site/SiteChecklistClient.tsx index d140f0ef..b17daa95 100644 --- a/src/site/SiteChecklistClient.tsx +++ b/src/site/SiteChecklistClient.tsx @@ -138,7 +138,7 @@ export default function SiteChecklistClient({
    ; return ( -
    +
    } diff --git a/src/site/globals.css b/src/site/globals.css index 9803be2a..fefddffb 100644 --- a/src/site/globals.css +++ b/src/site/globals.css @@ -116,7 +116,7 @@ hover:text-gray-600 hover:dark:text-gray-400 } - /* Common Utilities: Text */ + /* Utilities: Text */ .text-main { @apply text-gray-900 dark:text-gray-100 @@ -145,7 +145,7 @@ @apply text-red-500 dark:text-red-400 } - /* Common Utilities: Background */ + /* Utilities: Background */ .bg-main { @apply bg-white dark:bg-black @@ -159,4 +159,22 @@ @apply bg-gray-900 dark:bg-gray-100 } + /* Utilities: Baseline Grid */ + .space-y-baseline { + @apply + space-y-[1.125rem] md:space-y-[1.25rem] + } + .gap-y-baseline { + @apply + gap-y-[1.125rem] md:gap-y-[1.25rem] + } + .gap-baseline { + @apply + gap-[1.125rem] md:gap-[1.25rem] + } + .debug-baseline-grid { + @apply + bg-[repeating-linear-gradient(to_bottom,#222,#222_1px,transparent_1px,transparent_1.125rem)] + md:bg-[repeating-linear-gradient(to_bottom,#222,#222_1px,transparent_1px,transparent_1.25rem)] + } } diff --git a/src/tag/FavsTag.tsx b/src/tag/FavsTag.tsx index 5496a276..65fc5d8e 100644 --- a/src/tag/FavsTag.tsx +++ b/src/tag/FavsTag.tsx @@ -1,8 +1,10 @@ import { FaStar } from 'react-icons/fa'; -import EntityLink, { EntityLinkExternalProps } from '@/components/EntityLink'; import { TAG_FAVS } from '.'; import { pathForTag } from '@/site/paths'; import { clsx } from 'clsx/lite'; +import EntityLink, { + EntityLinkExternalProps, +} from '@/components/primitives/EntityLink'; export default function FavsTag({ type, @@ -30,7 +32,7 @@ export default function FavsTag({ size={12} className={clsx( 'text-amber-500', - 'translate-x-[-1px] translate-y-[3.5px]', + 'translate-x-[-1px] translate-y-[-0.5px]', )} />} type={type} diff --git a/src/tag/PhotoTag.tsx b/src/tag/PhotoTag.tsx index 56fab6a6..0c1e4546 100644 --- a/src/tag/PhotoTag.tsx +++ b/src/tag/PhotoTag.tsx @@ -1,7 +1,9 @@ import { pathForTag } from '@/site/paths'; import { FaTag } from 'react-icons/fa'; import { formatTag } from '.'; -import EntityLink, { EntityLinkExternalProps } from '@/components/EntityLink'; +import EntityLink, { + EntityLinkExternalProps, +} from '@/components/primitives/EntityLink'; export default function PhotoTag({ tag, @@ -19,7 +21,7 @@ export default function PhotoTag({ href={pathForTag(tag)} icon={} type={type} badged={badged} diff --git a/src/tag/PhotoTags.tsx b/src/tag/PhotoTags.tsx index ef48121e..6eec2af3 100644 --- a/src/tag/PhotoTags.tsx +++ b/src/tag/PhotoTags.tsx @@ -1,7 +1,7 @@ import PhotoTag from '@/tag/PhotoTag'; import { isTagFavs } from '.'; import FavsTag from './FavsTag'; -import { EntityLinkExternalProps } from '@/components/EntityLink'; +import { EntityLinkExternalProps } from '@/components/primitives/EntityLink'; export default function PhotoTags({ tags, @@ -10,13 +10,13 @@ export default function PhotoTags({ tags: string[] } & EntityLinkExternalProps) { return ( -
    +
    {tags.map(tag => -
    + <> {isTagFavs(tag) - ? - : } -
    )} + ? + : } + )}
    ); } diff --git a/tailwind.config.js b/tailwind.config.js index 9816930c..b76c7276 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -12,13 +12,13 @@ module.exports = { ...defaultTheme.screens, }, fontSize: { - 'xs': '0.75rem', - 'sm': ['0.825rem', '1.15rem'], - 'base': ['0.875rem', '1.275rem'], - 'lg': ['0.925rem', '1.05rem'], - 'xl': '1rem', - '2xl': '1.1rem', - '3xl': ['1.3rem', '1.7rem'], + 'xs': ['0.75rem', '1rem'], // 12px on 16px + 'sm': ['0.8125rem', '1.125rem'], // 13px on 18px [Default: mobile] + 'base': ['0.875rem', '1.25rem'], // 14px on 20px [Default: desktop] + 'lg': ['1rem', '1.25rem'], // 16px on 20px + 'xl': ['1.125rem', '1.25rem'], // 18px on 20px + '2xl': ['1.25rem', '1.25rem'], // 20px on 20px + '3xl': ['1.5rem', '1.5rem'], // 24px on 24px }, extend: { fontFamily: { From bdd83170bc4311f69722680132e7c62e47c9de6a Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Tue, 26 Mar 2024 12:50:49 -0500 Subject: [PATCH 02/15] Fix debug baseline grid in light mode --- src/site/globals.css | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/site/globals.css b/src/site/globals.css index fefddffb..b4481406 100644 --- a/src/site/globals.css +++ b/src/site/globals.css @@ -174,7 +174,9 @@ } .debug-baseline-grid { @apply - bg-[repeating-linear-gradient(to_bottom,#222,#222_1px,transparent_1px,transparent_1.125rem)] - md:bg-[repeating-linear-gradient(to_bottom,#222,#222_1px,transparent_1px,transparent_1.25rem)] + bg-[repeating-linear-gradient(to_bottom,#eee,#eee_1px,transparent_1px,transparent_1.125rem)] + md:bg-[repeating-linear-gradient(to_bottom,#eee,#eee_1px,transparent_1px,transparent_1.25rem)] + dark:bg-[repeating-linear-gradient(to_bottom,#222,#222_1px,transparent_1px,transparent_1.125rem)] + dark:md:bg-[repeating-linear-gradient(to_bottom,#222,#222_1px,transparent_1px,transparent_1.25rem)] } } From 9b9818f17c909d871cb653739484c669452c42e1 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Tue, 26 Mar 2024 12:55:28 -0500 Subject: [PATCH 03/15] Fix photo header contrast --- src/camera/CameraHeader.tsx | 2 +- src/simulation/PhotoFilmSimulation.tsx | 3 ++- src/tag/TagHeader.tsx | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/camera/CameraHeader.tsx b/src/camera/CameraHeader.tsx index 657c0e5a..35f7b9ef 100644 --- a/src/camera/CameraHeader.tsx +++ b/src/camera/CameraHeader.tsx @@ -21,7 +21,7 @@ export default function CameraHeader({ const camera = cameraFromPhoto(photos[0], cameraProp); return ( } + entity={} entityVerb="Photo" entityDescription={ descriptionForCameraPhotos(photos, undefined, count, dateRange)} diff --git a/src/simulation/PhotoFilmSimulation.tsx b/src/simulation/PhotoFilmSimulation.tsx index 7a185350..3ae380c7 100644 --- a/src/simulation/PhotoFilmSimulation.tsx +++ b/src/simulation/PhotoFilmSimulation.tsx @@ -25,13 +25,14 @@ export default function PhotoFilmSimulation({ href={pathForFilmSimulation(simulation)} icon={} title={`Film Simulation: ${large}`} type={type} badged={badged} contrast={contrast} hoverEntity={countOnHover} + iconWide /> ); } diff --git a/src/tag/TagHeader.tsx b/src/tag/TagHeader.tsx index f320de7d..fe97f856 100644 --- a/src/tag/TagHeader.tsx +++ b/src/tag/TagHeader.tsx @@ -22,7 +22,7 @@ export default function TagHeader({ - : } + : } entityVerb="Tagged" entityDescription={descriptionForTaggedPhotos(photos, undefined, count)} photos={photos} From 9b1b94175546b10f94dd55358bc8c2baa7637b06 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Tue, 26 Mar 2024 16:33:16 -0500 Subject: [PATCH 04/15] Turn off baseline grid --- src/photo/PhotoLarge.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/photo/PhotoLarge.tsx b/src/photo/PhotoLarge.tsx index 376315fe..72dadd64 100644 --- a/src/photo/PhotoLarge.tsx +++ b/src/photo/PhotoLarge.tsx @@ -69,7 +69,6 @@ export default function PhotoLarge({ 'grid grid-cols-2 md:grid-cols-1', 'gap-x-0.5 sm:gap-x-1 gap-y-baseline', 'pb-6', - 'debug-baseline-grid', )}> {/* Meta */}
    From fa53e7ece37752e9ca59367d28db323701aa17a9 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Tue, 26 Mar 2024 23:46:13 -0500 Subject: [PATCH 05/15] Refine baseline type sizes --- src/app/admin/baseline/page.tsx | 2 +- src/components/Badge.tsx | 6 +++--- src/components/OGTile.tsx | 3 +-- src/components/ShareModal.tsx | 2 +- src/simulation/PhotoFilmSimulation.tsx | 5 +---- src/site/NavClient.tsx | 1 - src/site/globals.css | 16 ++++++++++------ tailwind.config.js | 14 +++++++------- 8 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src/app/admin/baseline/page.tsx b/src/app/admin/baseline/page.tsx index 0dbec5f6..60383037 100644 --- a/src/app/admin/baseline/page.tsx +++ b/src/app/admin/baseline/page.tsx @@ -52,7 +52,7 @@ export default function ComponentsPage() {
    diff --git a/src/components/Badge.tsx b/src/components/Badge.tsx index e3fa1b6d..02124a22 100644 --- a/src/components/Badge.tsx +++ b/src/components/Badge.tsx @@ -27,8 +27,8 @@ export default function Badge({ ); case 'small': return clsx( - 'px-[4px] py-[2.5px]', - 'md:px-[4.5px] md:py-[3px]', + 'h-max-baseline', + 'px-[5px] py-[2.75px]', 'text-[0.7rem] font-medium rounded-[0.25rem]', highContrast ? 'text-invert bg-invert' @@ -44,7 +44,7 @@ export default function Badge({ }; return ( }
    diff --git a/src/simulation/PhotoFilmSimulation.tsx b/src/simulation/PhotoFilmSimulation.tsx index 3ae380c7..ddcfc819 100644 --- a/src/simulation/PhotoFilmSimulation.tsx +++ b/src/simulation/PhotoFilmSimulation.tsx @@ -23,10 +23,7 @@ export default function PhotoFilmSimulation({ label={medium} labelSmall={small} href={pathForFilmSimulation(simulation)} - icon={} + icon={} title={`Film Simulation: ${large}`} type={type} badged={badged} diff --git a/src/site/NavClient.tsx b/src/site/NavClient.tsx index 9ff3f75c..a34d4350 100644 --- a/src/site/NavClient.tsx +++ b/src/site/NavClient.tsx @@ -57,7 +57,6 @@ export default function NavClient({ className={clsx( 'flex items-center', 'w-full min-h-[4rem]', - 'leading-none', )}>
    Date: Wed, 27 Mar 2024 00:03:48 -0500 Subject: [PATCH 06/15] Provide keys explicitly in --- src/tag/PhotoTags.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tag/PhotoTags.tsx b/src/tag/PhotoTags.tsx index 6eec2af3..e9273fee 100644 --- a/src/tag/PhotoTags.tsx +++ b/src/tag/PhotoTags.tsx @@ -14,8 +14,8 @@ export default function PhotoTags({ {tags.map(tag => <> {isTagFavs(tag) - ? - : } + ? + : } )}
    ); From cd4445d880f39fb62f28b6dbf51cb929ddeeac49 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Wed, 27 Mar 2024 00:18:29 -0500 Subject: [PATCH 07/15] Improve responsive date handling --- src/app/admin/photos/page.tsx | 3 ++- src/components/ResponsiveDate.tsx | 20 ++++++++++++++++++++ src/photo/PhotoDate.tsx | 17 +++++++++++++++++ src/site/CommandK.tsx | 11 ++--------- 4 files changed, 41 insertions(+), 10 deletions(-) create mode 100644 src/components/ResponsiveDate.tsx create mode 100644 src/photo/PhotoDate.tsx diff --git a/src/app/admin/photos/page.tsx b/src/app/admin/photos/page.tsx index b59a38f8..cb8f78bf 100644 --- a/src/app/admin/photos/page.tsx +++ b/src/app/admin/photos/page.tsx @@ -33,6 +33,7 @@ import { PRO_MODE_ENABLED } from '@/site/config'; import SubmitButtonWithStatus from '@/components/SubmitButtonWithStatus'; import IconGrSync from '@/site/IconGrSync'; import { getStoragePhotoUrlsNoStore } from '@/services/storage/cache'; +import PhotoDate from '@/photo/PhotoDate'; const DEBUG_PHOTO_BLOBS = false; @@ -103,7 +104,7 @@ export default async function AdminPhotosPage({ 'lg:w-[50%] uppercase', 'text-dim', )}> - {photo.takenAtNaive} +
    + {/* Mobile */} + + {formatDate(date, true)} + + {/* Desktop */} + + {formatDate(date)} + + + ); +} diff --git a/src/photo/PhotoDate.tsx b/src/photo/PhotoDate.tsx new file mode 100644 index 00000000..c2a9ea24 --- /dev/null +++ b/src/photo/PhotoDate.tsx @@ -0,0 +1,17 @@ +import ResponsiveDate from '@/components/ResponsiveDate'; +import { Photo } from '.'; +import { useMemo } from 'react'; + +export default function PhotoDate({ + photo: { takenAtNaive }, +}: { + photo: Photo +}) { + const date = useMemo(() => { + const date = new Date(takenAtNaive); + return isNaN(date.getTime()) ? new Date() : date; + }, [takenAtNaive]); + return ( + + ); +} diff --git a/src/site/CommandK.tsx b/src/site/CommandK.tsx index 523207bf..b006f7b6 100644 --- a/src/site/CommandK.tsx +++ b/src/site/CommandK.tsx @@ -21,7 +21,6 @@ import { authCached } from '@/auth/cache'; import { getPhotos } from '@/services/vercel-postgres'; import { getKeywordsForPhoto, photoQuantityText, titleForPhoto } from '@/photo'; import PhotoTiny from '@/photo/PhotoTiny'; -import { formatDate } from '@/utility/date'; import { formatCount, formatCountDescriptive } from '@/utility/string'; import { BiLockAlt, BiSolidUser } from 'react-icons/bi'; import { sortTagsObject } from '@/tag'; @@ -31,6 +30,7 @@ import { TbPhoto } from 'react-icons/tb'; import { IoMdCamera } from 'react-icons/io'; import { HiDocumentText } from 'react-icons/hi'; import { signOutAction } from '@/auth/actions'; +import PhotoDate from '@/photo/PhotoDate'; export default async function CommandK() { const [ @@ -147,14 +147,7 @@ export default async function CommandK() { items: photos.map(photo => ({ label: titleForPhoto(photo), keywords: getKeywordsForPhoto(photo), - annotation: <> - - {formatDate(photo.takenAt)} - - - {formatDate(photo.takenAt, true)} - - , + annotation: , accessory: , path: pathForPhoto(photo), })), From 5b98d8edf8dedb774dd8141ed19c152b422ab2ed Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Wed, 27 Mar 2024 00:21:08 -0500 Subject: [PATCH 08/15] Refine /grid sidebar spacing --- src/components/HeaderList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/HeaderList.tsx b/src/components/HeaderList.tsx index 9abc7f98..31f80d33 100644 --- a/src/components/HeaderList.tsx +++ b/src/components/HeaderList.tsx @@ -17,7 +17,7 @@ export default function HeaderList({ Date: Wed, 27 Mar 2024 11:21:19 -0500 Subject: [PATCH 09/15] Add leading 0 to dates --- src/components/LocalDate.tsx | 11 ----------- src/utility/date.ts | 4 ++-- 2 files changed, 2 insertions(+), 13 deletions(-) delete mode 100644 src/components/LocalDate.tsx diff --git a/src/components/LocalDate.tsx b/src/components/LocalDate.tsx deleted file mode 100644 index 2f2fd88f..00000000 --- a/src/components/LocalDate.tsx +++ /dev/null @@ -1,11 +0,0 @@ -'use client'; - -import { formatDate } from '@/utility/date'; - -export default function LocalDate({ date }: { date: Date }) { - return ( - <> - {formatDate(date)} - - ); -}; diff --git a/src/utility/date.ts b/src/utility/date.ts index 7e6159be..df214bfe 100644 --- a/src/utility/date.ts +++ b/src/utility/date.ts @@ -1,13 +1,13 @@ import { format, parseISO, parse } from 'date-fns'; const DATE_STRING_FORMAT_SHORT = 'dd MMM yyyy'; -const DATE_STRING_FORMAT = 'd MMM yyyy h:mma'; +const DATE_STRING_FORMAT = 'dd MMM yyyy h:mma'; const DATE_STRING_FORMAT_POSTGRES = 'yyyy-MM-dd HH:mm:ss'; type AmbiguousTimestamp = number | string; export const formatDate = (date: Date, short?: boolean) => - format(date, short? DATE_STRING_FORMAT_SHORT : DATE_STRING_FORMAT); + format(date, short ? DATE_STRING_FORMAT_SHORT : DATE_STRING_FORMAT); export const formatDateFromPostgresString = (date: string, short?: boolean) => formatDate(parse(date, DATE_STRING_FORMAT_POSTGRES, new Date()), short); From edbc6421f73f69eab72cf5ec9ed5486b82c53f77 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Wed, 27 Mar 2024 11:28:49 -0500 Subject: [PATCH 10/15] Fix Apple icon positioning --- src/camera/PhotoCamera.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/camera/PhotoCamera.tsx b/src/camera/PhotoCamera.tsx index e6e8511a..66e21d06 100644 --- a/src/camera/PhotoCamera.tsx +++ b/src/camera/PhotoCamera.tsx @@ -28,7 +28,7 @@ export default function PhotoCamera({ icon={showAppleIcon ? : Date: Wed, 27 Mar 2024 13:10:38 -0500 Subject: [PATCH 11/15] Add admin toggle to debug site-wide baseline grid --- src/app/admin/baseline/page.tsx | 340 +++++++++--------- src/components/CommandKClient.tsx | 26 +- src/components/DebugBaselineGridContainer.tsx | 24 ++ src/photo/PhotoLarge.tsx | 6 +- src/site/CommandK.tsx | 11 +- src/site/config.ts | 1 + src/site/paths.ts | 1 + src/state/AppStateProvider.tsx | 13 +- src/state/index.ts | 2 + 9 files changed, 241 insertions(+), 183 deletions(-) create mode 100644 src/components/DebugBaselineGridContainer.tsx diff --git a/src/app/admin/baseline/page.tsx b/src/app/admin/baseline/page.tsx index 60383037..99a18ce4 100644 --- a/src/app/admin/baseline/page.tsx +++ b/src/app/admin/baseline/page.tsx @@ -1,9 +1,13 @@ 'use client'; +import DebugBaselineGridContainer from + '@/components/DebugBaselineGridContainer'; import FieldSetWithStatus from '@/components/FieldSetWithStatus'; +import SiteGrid from '@/components/SiteGrid'; import EntityLink from '@/components/primitives/EntityLink'; import LabeledIcon from '@/components/primitives/LabeledIcon'; import PhotoFilmSimulationIcon from '@/simulation/PhotoFilmSimulationIcon'; +import { useAppState } from '@/state'; import { clsx } from 'clsx/lite'; import { useState } from 'react'; import { FaCamera, FaHandSparkles, FaUserAltSlash } from 'react-icons/fa'; @@ -13,181 +17,179 @@ import { IoImageSharp } from 'react-icons/io5'; const DEBUG_LINES = new Array(22).fill(null); export default function ComponentsPage() { - const [_debugGrid, setDebugGrid] = useState('true'); - const [_debugComponents, setDebugComponents] = useState('false'); + const { + shouldShowBaselineGrid, + setShouldShowBaselineGrid, + } = useAppState(); - const debugGrid = _debugGrid === 'true'; - const debugComponents = _debugComponents === 'true'; + const [debugComponents, setDebugComponents] = useState(false); return ( - <> -

    -
    - Baseline Grid: - - 13px / 18px - 14px / 20px - -
    -
    *]:inline-flex [&>*]:gap-1 [&_input]:-translate-y-0.5', - )}> - - -
    -

    -
    -
    -
    - } - debug={debugComponents} - > - Camera
    Line two -
    + +

    +
    + Baseline Grid: + + 13.5px / 19px + 14px / 20px +
    -
    - } debug={debugComponents}> - Image - -
    -
    - } debug={debugComponents}> - Image - -
    -
    - } debug={debugComponents}> - Image - -
    -
    - } debug={debugComponents}> - Image - -
    -
    - } debug={debugComponents}> - Image - -
    -
    - } debug={debugComponents}> - Image - -
    -
    - } debug={debugComponents}> - Image - -
    -
    - } debug={debugComponents}> - Image - -
    -
    - } debug={debugComponents}> - Image - -
    -
    - } debug={debugComponents}> - Image - -
    -
    - } - label="Image" - debug={debugComponents} +
    *]:inline-flex [&>*]:gap-1 [&_input]:-translate-y-0.5', + )}> + setShouldShowBaselineGrid?.(e === 'true')} + /> + setDebugComponents(e === 'true')} />
    -
    - } - label="Image" - badged - debug={debugComponents} - /> -
    -
    - } - debug={debugComponents} - > - Canon Mark III - -
    -
    - } - label="Astia/Soft" - type="icon-last" - iconWide - badged - debug={debugComponents} - /> -
    -
    - } debug={debugComponents}> - Image - -
    -
    - } debug={debugComponents}> - Image - -
    -
    - } - label="Astia/Soft" - type="icon-last" - badged - debug={debugComponents} - /> -
    -
    - } debug={debugComponents}> - Image - -
    -
    - } debug={debugComponents}> - Image - -
    -
    -
    *]:bg-gray-800', - '[&>*]:flex', - )}> - {DEBUG_LINES.map((_, i) => -
    - Line {(i + 1).toString().padStart(2, '0')} +

    + +
    +
    + } + debug={debugComponents} + > + Camera
    Line two +
    - )} -
    -
    - +
    + } debug={debugComponents}> + Image + +
    +
    + } debug={debugComponents}> + Image + +
    +
    + } debug={debugComponents}> + Image + +
    +
    + } debug={debugComponents}> + Image + +
    +
    + } debug={debugComponents}> + Image + +
    +
    + } debug={debugComponents}> + Image + +
    +
    + } debug={debugComponents}> + Image + +
    +
    + } debug={debugComponents}> + Image + +
    +
    + } debug={debugComponents}> + Image + +
    +
    + } debug={debugComponents}> + Image + +
    +
    + } + label="Image" + debug={debugComponents} + /> +
    +
    + } + label="Image" + badged + debug={debugComponents} + /> +
    +
    + } + debug={debugComponents} + > + Canon Mark III + +
    +
    + } + label="Astia/Soft" + type="icon-last" + iconWide + badged + debug={debugComponents} + /> +
    +
    + } debug={debugComponents}> + Image + +
    +
    + } debug={debugComponents}> + Image + +
    +
    + } + label="Astia/Soft" + type="icon-last" + badged + debug={debugComponents} + /> +
    +
    + } debug={debugComponents}> + Image + +
    +
    + } debug={debugComponents}> + Image + +
    +
    +
    *]:bg-gray-800', + '[&>*]:flex', + )}> + {DEBUG_LINES.map((_, i) => +
    + Line {(i + 1).toString().padStart(2, '0')} +
    + )} +
    + + } + /> ); } diff --git a/src/components/CommandKClient.tsx b/src/components/CommandKClient.tsx index eb9a103d..f36e69f9 100644 --- a/src/components/CommandKClient.tsx +++ b/src/components/CommandKClient.tsx @@ -18,6 +18,8 @@ import { useTheme } from 'next-themes'; import { BiDesktop, BiMoon, BiSun } from 'react-icons/bi'; import { IoInvertModeSharp } from 'react-icons/io5'; import { useAppState } from '@/state'; +import { ADMIN_DEBUG_TOOLS_ENABLED } from '@/site/config'; +import { RiToolsFill } from 'react-icons/ri'; const LISTENER_KEYDOWN = 'keydown'; const MINIMUM_QUERY_LENGTH = 2; @@ -38,17 +40,18 @@ export type CommandKSection = { export default function CommandKClient({ onQueryChange, - sections = [], + serverSections = [], footer, }: { onQueryChange?: (query: string) => Promise - sections?: CommandKSection[] + serverSections?: CommandKSection[] footer?: string }) { const { isCommandKOpen: isOpen, setIsCommandKOpen: setIsOpen, setShouldRespondToKeyboardCommands, + setShouldShowBaselineGrid, } = useAppState(); const isOpenRef = useRef(isOpen); @@ -132,7 +135,7 @@ export default function CommandKClient({ } }, [isOpen, setShouldRespondToKeyboardCommands]); - const sectionTheme: CommandKSection = { + const clientSections: CommandKSection[] = [{ heading: 'Theme', accessory: , action: () => setTheme('dark'), }], - }; + }]; + + if (ADMIN_DEBUG_TOOLS_ENABLED) { + clientSections.push({ + heading: 'Debug Tools', + accessory: , + items: [{ + label: 'Toggle Baseline Grid', + action: () => setShouldShowBaselineGrid?.(prev => !prev), + }], + }); + } return ( {queriedSections - .concat(sections) - .concat(sectionTheme) + .concat(serverSections) + .concat(clientSections) .filter(({ items }) => items.length > 0) .map(({ heading, accessory, items }) => + {children} +
    + ); +} diff --git a/src/photo/PhotoLarge.tsx b/src/photo/PhotoLarge.tsx index 72dadd64..4184e78f 100644 --- a/src/photo/PhotoLarge.tsx +++ b/src/photo/PhotoLarge.tsx @@ -18,6 +18,8 @@ import PhotoFilmSimulation from '@/simulation/PhotoFilmSimulation'; import { sortTags } from '@/tag'; import AdminPhotoMenu from '@/admin/AdminPhotoMenu'; import { Suspense } from 'react'; +import DebugBaselineGridContainer from + '@/components/DebugBaselineGridContainer'; export default function PhotoLarge({ photo, @@ -63,7 +65,7 @@ export default function PhotoLarge({ priority={priority} />} contentSide={ -
    -
    } + } /> ); }; diff --git a/src/site/CommandK.tsx b/src/site/CommandK.tsx index b006f7b6..ecf17f08 100644 --- a/src/site/CommandK.tsx +++ b/src/site/CommandK.tsx @@ -6,6 +6,7 @@ import { getUniqueTagsCached, } from '@/photo/cache'; import { + PATH_ADMIN_BASELINE, PATH_ADMIN_CONFIGURATION, PATH_ADMIN_PHOTOS, PATH_ADMIN_TAGS, @@ -31,6 +32,7 @@ import { IoMdCamera } from 'react-icons/io'; import { HiDocumentText } from 'react-icons/hi'; import { signOutAction } from '@/auth/actions'; import PhotoDate from '@/photo/PhotoDate'; +import { ADMIN_DEBUG_TOOLS_ENABLED } from './config'; export default async function CommandK() { const [ @@ -129,8 +131,15 @@ export default async function CommandK() { }], }; + if (isAdminLoggedIn && ADMIN_DEBUG_TOOLS_ENABLED) { + SECTION_ADMIN.items.push({ + label: 'Baseline Overview', + path: PATH_ADMIN_BASELINE, + }); + } + return (); - const [shouldRespondToKeyboardCommands, setShouldRespondToKeyboardCommands] = useState(true); - - const [isCommandKOpen, setIsCommandKOpen] = useState(false); + const [isCommandKOpen, setIsCommandKOpen] = + useState(false); + const [shouldShowBaselineGrid, setShouldShowBaselineGrid] = + useState(false); useEffect(() => { setHasLoaded?.(true); @@ -38,6 +39,8 @@ export default function AppStateProvider({ setShouldRespondToKeyboardCommands, isCommandKOpen, setIsCommandKOpen, + shouldShowBaselineGrid, + setShouldShowBaselineGrid, clearNextPhotoAnimation: () => setNextPhotoAnimation?.(undefined), }} > diff --git a/src/state/index.ts b/src/state/index.ts index 8c303280..18b08869 100644 --- a/src/state/index.ts +++ b/src/state/index.ts @@ -11,6 +11,8 @@ export interface AppStateContext { setShouldRespondToKeyboardCommands?: Dispatch> isCommandKOpen?: boolean setIsCommandKOpen?: Dispatch> + shouldShowBaselineGrid?: boolean + setShouldShowBaselineGrid?: Dispatch> clearNextPhotoAnimation?: () => void } From d36a1c5536d962ab18ddb784a064e189c785e8c1 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Wed, 27 Mar 2024 13:22:31 -0500 Subject: [PATCH 12/15] Fix debug tools cmd-k interaction --- src/components/CommandKClient.tsx | 5 +++-- src/site/CommandK.tsx | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/CommandKClient.tsx b/src/components/CommandKClient.tsx index f36e69f9..d16be976 100644 --- a/src/components/CommandKClient.tsx +++ b/src/components/CommandKClient.tsx @@ -18,7 +18,6 @@ import { useTheme } from 'next-themes'; import { BiDesktop, BiMoon, BiSun } from 'react-icons/bi'; import { IoInvertModeSharp } from 'react-icons/io5'; import { useAppState } from '@/state'; -import { ADMIN_DEBUG_TOOLS_ENABLED } from '@/site/config'; import { RiToolsFill } from 'react-icons/ri'; const LISTENER_KEYDOWN = 'keydown'; @@ -41,10 +40,12 @@ export type CommandKSection = { export default function CommandKClient({ onQueryChange, serverSections = [], + showDebugTools, footer, }: { onQueryChange?: (query: string) => Promise serverSections?: CommandKSection[] + showDebugTools?: boolean footer?: string }) { const { @@ -156,7 +157,7 @@ export default function CommandKClient({ }], }]; - if (ADMIN_DEBUG_TOOLS_ENABLED) { + if (showDebugTools) { clientSections.push({ heading: 'Debug Tools', accessory: , diff --git a/src/site/CommandK.tsx b/src/site/CommandK.tsx index ecf17f08..7a44a423 100644 --- a/src/site/CommandK.tsx +++ b/src/site/CommandK.tsx @@ -163,6 +163,7 @@ export default async function CommandK() { }] : []; }} + showDebugTools={isAdminLoggedIn && ADMIN_DEBUG_TOOLS_ENABLED} footer={photoQuantityText(count, false)} />; } From 7a9c839a2084bf601396c2adc4010c5b1da16d5a Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Wed, 27 Mar 2024 17:59:53 -0500 Subject: [PATCH 13/15] Add baseline grid debugging to photo set headers --- src/app/admin/baseline/page.tsx | 7 +++---- ...gBaselineGridContainer.tsx => DivDebugBaselineGrid.tsx} | 2 +- src/photo/PhotoLarge.tsx | 7 +++---- src/photo/PhotoSetHeader.tsx | 5 +++-- 4 files changed, 10 insertions(+), 11 deletions(-) rename src/components/{DebugBaselineGridContainer.tsx => DivDebugBaselineGrid.tsx} (88%) diff --git a/src/app/admin/baseline/page.tsx b/src/app/admin/baseline/page.tsx index 99a18ce4..181682d9 100644 --- a/src/app/admin/baseline/page.tsx +++ b/src/app/admin/baseline/page.tsx @@ -1,7 +1,6 @@ 'use client'; -import DebugBaselineGridContainer from - '@/components/DebugBaselineGridContainer'; +import DivDebugBaselineGrid from '@/components/DivDebugBaselineGrid'; import FieldSetWithStatus from '@/components/FieldSetWithStatus'; import SiteGrid from '@/components/SiteGrid'; import EntityLink from '@/components/primitives/EntityLink'; @@ -55,7 +54,7 @@ export default function ComponentsPage() { />
    - +
    )}
    - + } /> ); diff --git a/src/components/DebugBaselineGridContainer.tsx b/src/components/DivDebugBaselineGrid.tsx similarity index 88% rename from src/components/DebugBaselineGridContainer.tsx rename to src/components/DivDebugBaselineGrid.tsx index 8d474c48..2724d6d7 100644 --- a/src/components/DebugBaselineGridContainer.tsx +++ b/src/components/DivDebugBaselineGrid.tsx @@ -4,7 +4,7 @@ import { useAppState } from '@/state'; import { clsx } from 'clsx/lite'; import { ReactNode } from 'react'; -export default function DebugBaselineGridContainer({ +export default function DivDebugBaselineGrid({ children, className, }: { diff --git a/src/photo/PhotoLarge.tsx b/src/photo/PhotoLarge.tsx index 4184e78f..6b76868d 100644 --- a/src/photo/PhotoLarge.tsx +++ b/src/photo/PhotoLarge.tsx @@ -18,8 +18,7 @@ import PhotoFilmSimulation from '@/simulation/PhotoFilmSimulation'; import { sortTags } from '@/tag'; import AdminPhotoMenu from '@/admin/AdminPhotoMenu'; import { Suspense } from 'react'; -import DebugBaselineGridContainer from - '@/components/DebugBaselineGridContainer'; +import DivDebugBaselineGrid from '@/components/DivDebugBaselineGrid'; export default function PhotoLarge({ photo, @@ -65,7 +64,7 @@ export default function PhotoLarge({ priority={priority} />} contentSide={ -
    - } + } /> ); }; diff --git a/src/photo/PhotoSetHeader.tsx b/src/photo/PhotoSetHeader.tsx index 163a8a9f..7fe89136 100644 --- a/src/photo/PhotoSetHeader.tsx +++ b/src/photo/PhotoSetHeader.tsx @@ -4,6 +4,7 @@ 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, @@ -35,7 +36,7 @@ export default function PhotoSetHeader({ type="bottom" distanceOffset={10} animateOnFirstLoadOnly - items={[
    {end}
    – {start}} -
    ]} + ]} /> ); } From ef0b652c97a524ebe28d26ad9101fab351c1a3fb Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Thu, 28 Mar 2024 18:52:09 -0500 Subject: [PATCH 14/15] Fix AI Button intercepting implicit form submits --- src/photo/ai/AiButton.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/photo/ai/AiButton.tsx b/src/photo/ai/AiButton.tsx index b7892312..b88d1fb0 100644 --- a/src/photo/ai/AiButton.tsx +++ b/src/photo/ai/AiButton.tsx @@ -41,6 +41,7 @@ export default function AiButton({ return (