{ "name": "relative-time-card", "type": "registry:ui", "files": [ { "path": "ui/relative-time-card.tsx", "type": "registry:ui", "content": "\"use client\";\n\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport * as React from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport {\n HoverCard,\n HoverCardContent,\n HoverCardTrigger,\n} from \"@/registry/bases/base/ui/hover-card\";\n\nfunction pluralize(n: number, word: string) {\n return `${n} ${word}${n === 1 ? \"\" : \"s\"}`;\n}\n\nfunction formatRelativeTime(date: Date): string {\n const now = new Date();\n const diff = now.getTime() - date.getTime();\n const isInFuture = diff < 0;\n const absDiff = Math.abs(diff);\n\n const seconds = Math.floor(absDiff / 1000);\n const minutes = Math.floor(seconds / 60);\n const hours = Math.floor(minutes / 60);\n const days = Math.floor(hours / 24);\n\n if (seconds < 5) return \"just now\";\n\n if (isInFuture) {\n if (seconds < 60) return `in ${pluralize(seconds, \"second\")}`;\n if (minutes < 60) return `in ${pluralize(minutes, \"minute\")}`;\n if (hours < 24) return `in ${pluralize(hours, \"hour\")}`;\n if (days < 7) return `in ${pluralize(days, \"day\")}`;\n return date.toLocaleDateString();\n }\n\n if (seconds < 60) return `${pluralize(seconds, \"second\")} ago`;\n if (minutes < 60)\n return `${pluralize(minutes, \"minute\")} ${pluralize(seconds % 60, \"second\")} ago`;\n if (hours < 24) return `${pluralize(hours, \"hour\")} ago`;\n if (days < 7) return `${pluralize(days, \"day\")} ago`;\n return date.toLocaleDateString();\n}\n\ninterface TimezoneCardProps extends React.ComponentProps<\"div\"> {\n date: Date;\n timezone?: string;\n}\n\nfunction TimezoneCard(props: TimezoneCardProps) {\n const { date, timezone, ...cardProps } = props;\n\n const locale = React.useMemo(\n () => Intl.DateTimeFormat().resolvedOptions().locale,\n [],\n );\n\n const timezoneName = React.useMemo(\n () =>\n timezone ??\n new Intl.DateTimeFormat(locale, { timeZoneName: \"shortOffset\" })\n .formatToParts(date)\n .find((part) => part.type === \"timeZoneName\")?.value,\n [date, timezone, locale],\n );\n\n const { formattedDate, formattedTime } = React.useMemo(\n () => ({\n formattedDate: new Intl.DateTimeFormat(locale, {\n month: \"long\",\n day: \"numeric\",\n year: \"numeric\",\n timeZone: timezone,\n }).format(date),\n formattedTime: new Intl.DateTimeFormat(locale, {\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n hour12: true,\n timeZone: timezone,\n }).format(date),\n }),\n [date, timezone, locale],\n );\n\n return (\n \n \n {timezoneName}\n \n
\n \n \n
\n \n );\n}\n\nconst triggerVariants = cva(\n \"inline-flex w-fit items-center justify-center text-foreground/70 text-sm transition-colors hover:text-foreground/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n {\n variants: {\n variant: {\n default: \"\",\n muted: \"text-foreground/50 hover:text-foreground/70\",\n ghost: \"hover:underline\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n },\n },\n);\n\ninterface RelativeTimeCardProps\n extends React.ComponentProps,\n React.ComponentProps,\n Pick<\n React.ComponentProps,\n \"align\" | \"side\" | \"alignOffset\" | \"sideOffset\"\n >,\n VariantProps {\n date: Date | string | number;\n timezones?: string[];\n updateInterval?: number;\n className?: string;\n children?: React.ReactNode;\n}\n\nfunction RelativeTimeCard(props: RelativeTimeCardProps) {\n const {\n date: dateProp,\n variant,\n timezones = [\"UTC\"],\n open,\n defaultOpen,\n onOpenChange,\n delay = 500,\n closeDelay = 300,\n align,\n side,\n alignOffset,\n sideOffset,\n updateInterval = 1000,\n render,\n children,\n className,\n ...triggerProps\n } = props;\n\n const date = React.useMemo(\n () => (dateProp instanceof Date ? dateProp : new Date(dateProp)),\n [dateProp],\n );\n\n const locale = React.useMemo(\n () => Intl.DateTimeFormat().resolvedOptions().locale,\n [],\n );\n\n const [formattedTime, setFormattedTime] = React.useState(() =>\n date.toLocaleDateString(),\n );\n\n React.useEffect(() => {\n setFormattedTime(formatRelativeTime(date));\n const timer = setInterval(() => {\n setFormattedTime(formatRelativeTime(date));\n }, updateInterval);\n\n return () => clearInterval(timer);\n }, [date, updateInterval]);\n\n return (\n \n }\n className={cn(triggerVariants({ variant, className }))}\n >\n {children ?? (\n \n )}\n \n \n \n {formattedTime}\n \n
\n {timezones.map((timezone) => (\n \n ))}\n \n
\n \n \n );\n}\n\nexport { RelativeTimeCard };\n", "target": "" } ], "registryDependencies": [ "hover-card" ], "dependencies": [ "@base-ui/react" ] }