Add placeholder client logic to date primitive

This commit is contained in:
Sam Becker 2025-01-12 13:26:38 -06:00
parent 0b63cf76e7
commit 3d69e2d20c
4 changed files with 72 additions and 24 deletions

View File

@ -1,40 +1,66 @@
'use client';
import { formatDate } from '@/utility/date';
import { clsx } from 'clsx/lite';
import { useEffect, useState } from 'react';
export default function ResponsiveDate({
date,
className,
titleLabel,
timezone: timezoneFromProps,
}: {
date: Date
className?: string
titleLabel?: string
timezone?: string | null
}) {
const [timezone, setTimezone] = useState(timezoneFromProps);
useEffect(() => {
if (timezoneFromProps === null) {
setTimezone(Intl.DateTimeFormat().resolvedOptions().timeZone);
}
}, [timezoneFromProps]);
const showPlaceholderContent = timezone === null;
const titleDateFormatted = formatDate(date).toLocaleUpperCase();
const title = titleLabel
? `${titleLabel}: ${formatDate(date).toLocaleUpperCase()}`
: formatDate(date).toLocaleUpperCase();
? `${titleLabel}: ${titleDateFormatted}`
: titleDateFormatted;
const contentClass = showPlaceholderContent && 'opacity-0 select-none';
return (
<span
title={title}
className={clsx(className, 'uppercase')}
title={showPlaceholderContent ? 'LOADING LOCAL TIME' : title}
className={clsx(
'uppercase rounded-md transition-colors',
showPlaceholderContent && 'bg-dim',
className,
)}
>
{/* Small */}
<span
className="xs:hidden"
className={clsx('xs:hidden', contentClass)}
aria-hidden
>
{formatDate(date, 'short')}
{formatDate(date, 'short', showPlaceholderContent)}
</span>
{/* Medium */}
<span
className="hidden xs:inline-block sm:hidden"
className={clsx('hidden xs:inline-block sm:hidden', contentClass)}
aria-hidden
>
{formatDate(date, 'medium')}
{formatDate(date, 'medium', showPlaceholderContent)}
</span>
{/* Large */}
<span className="hidden sm:inline-block">
{formatDate(date)}
<span
className={clsx('hidden sm:inline-block', contentClass)}
>
{formatDate(date, undefined, showPlaceholderContent)}
</span>
</span>
);

View File

@ -10,6 +10,7 @@ export default function PhotoDate({
photo: Photo
className?: string
dateType?: 'takenAt' | 'createdAt' | 'updatedAt'
timezone?: string | null
}) {
const date = useMemo(() => {
const date = new Date(dateType === 'takenAt'
@ -41,6 +42,7 @@ export default function PhotoDate({
date,
className,
titleLabel: getTitleLabel(),
timezone: null,
}} />
);
}

View File

@ -164,6 +164,10 @@
@apply
bg-white dark:bg-black
}
.bg-dim {
@apply
bg-gray-100 dark:bg-gray-900/75
}
.bg-content {
@apply
bg-white border-gray-200

View File

@ -1,25 +1,41 @@
import { format, parseISO, parse } from 'date-fns';
const DATE_STRING_FORMAT_TINY = 'dd MMM yy';
const DATE_STRING_FORMAT_SHORT = 'dd MMM yyyy';
const DATE_STRING_FORMAT_MEDIUM = 'dd MMM yy h:mma';
const DATE_STRING_FORMAT = 'dd MMM yyyy h:mma';
const DATE_STRING_FORMAT_POSTGRES = 'yyyy-MM-dd HH:mm:ss';
const DATE_STRING_FORMAT_TINY = 'dd MMM yy';
const DATE_STRING_FORMAT_TINY_PLACEHOLDER = '00 000 00';
const DATE_STRING_FORMAT_SHORT = 'dd MMM yyyy';
const DATE_STRING_FORMAT_SHORT_PLACEHOLDER = '00 000 0000';
const DATE_STRING_FORMAT_MEDIUM = 'dd MMM yy h:mma';
const DATE_STRING_FORMAT_MEDIUM_PLACEHOLDER = '00 000 00 00:0000';
const DATE_STRING_FORMAT_LONG = 'dd MMM yyyy h:mma';
const DATE_STRING_FORMAT_LONG_PLACEHOLDER = '00 000 0000 00:0000';
const DATE_STRING_FORMAT_POSTGRES = 'yyyy-MM-dd HH:mm:ss';
type AmbiguousTimestamp = number | string;
type Length = 'tiny' | 'short' | 'medium' | 'long';
export const formatDate = (date: Date, length: Length = 'long') => {
export const formatDate = (
date: Date,
length: Length = 'long',
showPlaceholder?: boolean,
) => {
switch (length) {
case 'tiny':
return format(date, DATE_STRING_FORMAT_TINY);
case 'short':
return format(date, DATE_STRING_FORMAT_SHORT);
case 'medium':
return format(date, DATE_STRING_FORMAT_MEDIUM);
default:
return format(date, DATE_STRING_FORMAT);
case 'tiny': return showPlaceholder
? DATE_STRING_FORMAT_TINY_PLACEHOLDER
: format(date, DATE_STRING_FORMAT_TINY);
case 'short': return showPlaceholder
? DATE_STRING_FORMAT_SHORT_PLACEHOLDER
: format(date, DATE_STRING_FORMAT_SHORT);
case 'medium': return showPlaceholder
? DATE_STRING_FORMAT_MEDIUM_PLACEHOLDER
: format(date, DATE_STRING_FORMAT_MEDIUM);
default: return showPlaceholder
? DATE_STRING_FORMAT_LONG_PLACEHOLDER
: format(date, DATE_STRING_FORMAT_LONG);
}
};