Browse Source

feat(ui): show absolute timestamp tooltip on RelativeTime using shadcn Tooltip and date-fns

- Wrap relative time with Radix/Shadcn Tooltip
- Format absolute date as `yyyy-MM-dd HH:mm:ss OOOO` to include GMT offset
- Use <time> with tabIndex for keyboard accessibility and semantic dateTime
- Preserve existing relative time updates and layout
ding113 4 months ago
parent
commit
71b4aea1d9
1 changed files with 36 additions and 3 deletions
  1. 36 3
      src/components/ui/relative-time.tsx

+ 36 - 3
src/components/ui/relative-time.tsx

@@ -1,7 +1,9 @@
 "use client";
 
-import { useEffect, useState } from "react";
+import { useEffect, useMemo, useState } from "react";
 import { useLocale } from "next-intl";
+import { format as formatDate } from "date-fns";
+import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
 import { formatDateDistance } from "@/lib/utils/date-format";
 
 interface RelativeTimeProps {
@@ -32,6 +34,16 @@ export function RelativeTime({
   const [mounted, setMounted] = useState(false);
   const locale = useLocale();
 
+  // Precompute an absolute timestamp string for tooltip content. Include timezone display.
+  const absolute = useMemo(() => {
+    if (!date) return fallback;
+    const dateObj = typeof date === "string" ? new Date(date) : date;
+    if (isNaN(dateObj.getTime())) return fallback;
+    // date-fns does not fully support `z` for IANA abbreviations; use `OOOO` to show GMT offset.
+    // Example output: 2024-05-01 13:45:12 GMT+08:00
+    return formatDate(dateObj, "yyyy-MM-dd HH:mm:ss OOOO");
+  }, [date, fallback]);
+
   useEffect(() => {
     // 如果 date 为 null,直接显示 fallback
     if (!date) {
@@ -62,6 +74,27 @@ export function RelativeTime({
     return <span className={className}>{fallback}</span>;
   }
 
-  // 客户端挂载后显示相对时间
-  return <span className={className}>{timeAgo}</span>;
+  // 客户端挂载后显示相对时间,并在悬停/聚焦时展示绝对时间 Tooltip。
+  // 为了键盘可访问性,使触发元素可聚焦(tabIndex=0)。
+  return (
+    <Tooltip>
+      <TooltipTrigger asChild>
+        <time
+          className={className}
+          tabIndex={0}
+          // HTMLTimeElement `dateTime` attribute for semantics/SEO
+          dateTime={
+            typeof date === "string"
+              ? new Date(date).toISOString()
+              : date
+              ? date.toISOString()
+              : undefined
+          }
+        >
+          {timeAgo}
+        </time>
+      </TooltipTrigger>
+      <TooltipContent>{absolute}</TooltipContent>
+    </Tooltip>
+  );
 }