Add placeholder client logic to date primitive
This commit is contained in:
parent
0b63cf76e7
commit
3d69e2d20c
@ -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>
|
||||
);
|
||||
|
||||
@ -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,
|
||||
}} />
|
||||
);
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user