Openstatus www.openstatus.dev
at main 89 lines 2.6 kB view raw
1"use client"; 2 3import { 4 HoverCard, 5 HoverCardContent, 6 HoverCardTrigger, 7} from "@/components/ui/hover-card"; 8import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard"; 9import { UTCDate } from "@date-fns/utc"; 10import { HoverCardPortal } from "@radix-ui/react-hover-card"; 11import { format, formatDistanceToNowStrict } from "date-fns"; 12import { Copy } from "lucide-react"; 13import { Check } from "lucide-react"; 14import type { ComponentPropsWithoutRef } from "react"; 15 16// TODO: move to TableCellDate? 17 18type HoverCardContentProps = ComponentPropsWithoutRef<typeof HoverCardContent>; 19 20interface HoverCardTimestampProps { 21 date: Date; 22 side?: HoverCardContentProps["side"]; 23 sideOffset?: HoverCardContentProps["sideOffset"]; 24 align?: HoverCardContentProps["align"]; 25 alignOffset?: HoverCardContentProps["alignOffset"]; 26 children?: React.ReactNode; 27} 28 29export function HoverCardTimestamp({ 30 date, 31 side = "right", 32 align = "start", 33 alignOffset = -4, 34 sideOffset, 35 children, 36}: HoverCardTimestampProps) { 37 const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; 38 39 return ( 40 <HoverCard openDelay={0} closeDelay={0}> 41 <HoverCardTrigger asChild>{children}</HoverCardTrigger> 42 <HoverCardPortal> 43 <HoverCardContent 44 className="z-10 w-auto p-2" 45 {...{ side, align, alignOffset, sideOffset }} 46 > 47 <dl className="flex flex-col gap-1"> 48 <Row value={String(date.getTime())} label="Timestamp" /> 49 <Row 50 value={format(new UTCDate(date), "LLL dd, y HH:mm:ss")} 51 label="UTC" 52 /> 53 <Row value={format(date, "LLL dd, y HH:mm:ss")} label={timezone} /> 54 <Row 55 value={formatDistanceToNowStrict(date, { addSuffix: true })} 56 label="Relative" 57 /> 58 </dl> 59 </HoverCardContent> 60 </HoverCardPortal> 61 </HoverCard> 62 ); 63} 64 65function Row({ value, label }: { value: string; label: string }) { 66 const { copy, isCopied } = useCopyToClipboard(); 67 68 return ( 69 <div 70 className="group flex items-center justify-between gap-4 text-sm" 71 onClick={(e) => { 72 e.stopPropagation(); 73 copy(value, {}); 74 }} 75 > 76 <dt className="text-muted-foreground">{label}</dt> 77 <dd className="flex items-center gap-1 truncate font-mono"> 78 <span className="invisible group-hover:visible"> 79 {!isCopied ? ( 80 <Copy className="h-3 w-3" /> 81 ) : ( 82 <Check className="h-3 w-3" /> 83 )} 84 </span> 85 {value} 86 </dd> 87 </div> 88 ); 89}