Skip to content

Commit 57b5b68

Browse files
committed
feat: add relative time
1 parent 3baed19 commit 57b5b68

25 files changed

+863
-19
lines changed

docs/__registry__/index.tsx

+69-5
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,22 @@ export const Index: Record<string, any> = {
129129
subcategory: "",
130130
chunks: []
131131
},
132+
"relative-time-card": {
133+
name: "relative-time-card",
134+
description: "",
135+
type: "registry:ui",
136+
registryDependencies: ["hover-card"],
137+
files: [{
138+
path: "registry/default/ui/relative-time-card.tsx",
139+
type: "registry:ui",
140+
target: ""
141+
}],
142+
component: React.lazy(() => import("@/registry/default/ui/relative-time-card.tsx")),
143+
source: "",
144+
category: "",
145+
subcategory: "",
146+
chunks: []
147+
},
132148
"scroller": {
133149
name: "scroller",
134150
description: "",
@@ -653,17 +669,49 @@ export const Index: Record<string, any> = {
653669
subcategory: "",
654670
chunks: []
655671
},
656-
"sortable-demo": {
657-
name: "sortable-demo",
672+
"relative-time-card-demo": {
673+
name: "relative-time-card-demo",
658674
description: "",
659675
type: "registry:example",
660-
registryDependencies: undefined,
676+
registryDependencies: ["button","hover-card"],
661677
files: [{
662-
path: "registry/default/examples/sortable-demo.tsx",
678+
path: "registry/default/examples/relative-time-card-demo.tsx",
663679
type: "registry:example",
664680
target: ""
665681
}],
666-
component: React.lazy(() => import("@/registry/default/examples/sortable-demo.tsx")),
682+
component: React.lazy(() => import("@/registry/default/examples/relative-time-card-demo.tsx")),
683+
source: "",
684+
category: "",
685+
subcategory: "",
686+
chunks: []
687+
},
688+
"relative-time-card-variants-demo": {
689+
name: "relative-time-card-variants-demo",
690+
description: "",
691+
type: "registry:example",
692+
registryDependencies: ["button","hover-card"],
693+
files: [{
694+
path: "registry/default/examples/relative-time-card-variants-demo.tsx",
695+
type: "registry:example",
696+
target: ""
697+
}],
698+
component: React.lazy(() => import("@/registry/default/examples/relative-time-card-variants-demo.tsx")),
699+
source: "",
700+
category: "",
701+
subcategory: "",
702+
chunks: []
703+
},
704+
"relative-time-card-timezones-demo": {
705+
name: "relative-time-card-timezones-demo",
706+
description: "",
707+
type: "registry:example",
708+
registryDependencies: ["hover-card"],
709+
files: [{
710+
path: "registry/default/examples/relative-time-card-timezones-demo.tsx",
711+
type: "registry:example",
712+
target: ""
713+
}],
714+
component: React.lazy(() => import("@/registry/default/examples/relative-time-card-timezones-demo.tsx")),
667715
source: "",
668716
category: "",
669717
subcategory: "",
@@ -733,6 +781,22 @@ export const Index: Record<string, any> = {
733781
subcategory: "",
734782
chunks: []
735783
},
784+
"sortable-demo": {
785+
name: "sortable-demo",
786+
description: "",
787+
type: "registry:example",
788+
registryDependencies: undefined,
789+
files: [{
790+
path: "registry/default/examples/sortable-demo.tsx",
791+
type: "registry:example",
792+
target: ""
793+
}],
794+
component: React.lazy(() => import("@/registry/default/examples/sortable-demo.tsx")),
795+
source: "",
796+
category: "",
797+
subcategory: "",
798+
chunks: []
799+
},
736800
"sortable-dynamic-overlay-demo": {
737801
name: "sortable-dynamic-overlay-demo",
738802
description: "",

docs/app/(lobby)/pg/page.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,14 @@ import {
3535
ComboboxItem,
3636
ComboboxTrigger,
3737
} from "@/registry/default/ui/combobox";
38+
import { RelativeTimeCard } from "@/registry/default/ui/relative-time-card";
3839
import * as Mention from "@diceui/mention";
3940
import { ChevronDown } from "lucide-react";
4041

4142
export default function PlaygroundPage() {
4243
return (
4344
<Shell>
45+
<RelativeTimeCard date={new Date()}>Relative time</RelativeTimeCard>
4446
<ScrollerDemo />
4547
<Combobox className="w-[15rem]">
4648
<ComboboxAnchor>

docs/components/ui/hover-card.tsx

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
"use client";
2+
3+
import * as HoverCardPrimitive from "@radix-ui/react-hover-card";
4+
import * as React from "react";
5+
6+
import { cn } from "@/lib/utils";
7+
8+
const HoverCard = HoverCardPrimitive.Root;
9+
10+
const HoverCardTrigger = HoverCardPrimitive.Trigger;
11+
12+
const HoverCardContent = React.forwardRef<
13+
React.ElementRef<typeof HoverCardPrimitive.Content>,
14+
React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content>
15+
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
16+
<HoverCardPrimitive.Content
17+
ref={ref}
18+
align={align}
19+
sideOffset={sideOffset}
20+
className={cn(
21+
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=closed]:animate-out data-[state=open]:animate-in",
22+
className,
23+
)}
24+
{...props}
25+
/>
26+
));
27+
HoverCardContent.displayName = HoverCardPrimitive.Content.displayName;
28+
29+
export { HoverCard, HoverCardTrigger, HoverCardContent };

docs/content/docs/components/editable.mdx

+5
Original file line numberDiff line numberDiff line change
@@ -308,3 +308,8 @@ Button to cancel changes.
308308
},
309309
]}
310310
/>
311+
312+
313+
# Credits
314+
315+
- [John Phamous](https://x.com/JohnPhamous/status/1836091528911216803)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
---
2+
title: Relative Time Card
3+
description: A hover card that displays relative time relative to local time with timezone information.
4+
preview: true
5+
links:
6+
api: /docs/components/relative-time-card#api-reference
7+
---
8+
9+
<ComponentTabs name="relative-time-card-demo" />
10+
11+
## Installation
12+
13+
### CLI
14+
15+
```package-install
16+
npx shadcn@latest add "https://diceui.com/r/relative-time-card"
17+
```
18+
19+
### Manual
20+
21+
<Steps>
22+
<Step>
23+
Install the following dependencies:
24+
25+
```package-install
26+
@radix-ui/react-hover-card
27+
@radix-ui/react-slot
28+
```
29+
</Step>
30+
<Step>
31+
Copy and paste the following code into your project.
32+
33+
<ComponentSource name="relative-time-card" />
34+
</Step>
35+
</Steps>
36+
37+
38+
## Examples
39+
40+
### With Multiple Timezones
41+
42+
<ComponentTabs name="relative-time-card-timezones-demo" />
43+
44+
### With Variants
45+
46+
<ComponentTabs name="relative-time-card-variants-demo" />
47+
48+
## API Reference
49+
50+
### RelativeTimeCard
51+
52+
The main component that displays relative time with hover functionality.
53+
54+
<AutoTypeTable
55+
path="./types/docs/relative-time-card.ts"
56+
name="RelativeTimeCardProps"
57+
/>
58+
59+
<DataAttributesTable
60+
attributes={[
61+
{
62+
title: "[data-state]",
63+
description: "The state of the hover card",
64+
value: ["open", "closed"],
65+
},
66+
{
67+
title: "[data-side]",
68+
description: "The side the hover card appears on",
69+
value: ["top", "right", "bottom", "left"],
70+
},
71+
{
72+
title: "[data-align]",
73+
description: "The alignment of the hover card",
74+
value: ["start", "center", "end"],
75+
},
76+
]}
77+
/>
78+
79+
## Accessibility
80+
81+
### Keyboard Interactions
82+
83+
<KeyboardShortcutsTable
84+
shortcuts={[
85+
{
86+
keys: ["Tab"],
87+
description: "Opens/closes the relative time card.",
88+
},
89+
{
90+
keys: ["Enter"],
91+
description: "Opens the relative time card if closed.",
92+
},
93+
]}
94+
/>

docs/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"@radix-ui/react-collapsible": "^1.1.3",
2727
"@radix-ui/react-dialog": "^1.1.6",
2828
"@radix-ui/react-dropdown-menu": "^2.1.6",
29+
"@radix-ui/react-hover-card": "^1.1.6",
2930
"@radix-ui/react-label": "^2.1.2",
3031
"@radix-ui/react-popover": "^1.1.6",
3132
"@radix-ui/react-radio-group": "^1.2.3",

docs/public/r/index.json

+16
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,22 @@
110110
}
111111
]
112112
},
113+
{
114+
"name": "relative-time-card",
115+
"type": "registry:ui",
116+
"dependencies": [
117+
"@radix-ui/react-slot"
118+
],
119+
"registryDependencies": [
120+
"hover-card"
121+
],
122+
"files": [
123+
{
124+
"path": "ui/relative-time-card.tsx",
125+
"type": "registry:ui"
126+
}
127+
]
128+
},
113129
{
114130
"name": "scroller",
115131
"type": "registry:ui",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"name": "relative-time-card-demo",
3+
"type": "registry:example",
4+
"registryDependencies": [
5+
"button",
6+
"hover-card"
7+
],
8+
"files": [
9+
{
10+
"path": "examples/relative-time-card-demo.tsx",
11+
"content": "import { Button } from \"@/components/ui/button\";\nimport { RelativeTimeCard } from \"@/registry/default/ui/relative-time-card\";\nimport { Clock } from \"lucide-react\";\n\nexport default function RelativeTimeCardDemo() {\n const now = new Date();\n const fiveMinutesAgo = new Date(now.getTime() - 5 * 60 * 1000);\n const oneHourAgo = new Date(now.getTime() - 60 * 60 * 1000);\n const oneDayAgo = new Date(now.getTime() - 24 * 60 * 60 * 1000);\n\n return (\n <div className=\"flex flex-col gap-6\">\n <div className=\"flex flex-col gap-2\">\n <span className=\"text-muted-foreground text-sm\">Basic usage</span>\n <RelativeTimeCard date={fiveMinutesAgo} />\n </div>\n <div className=\"flex flex-col gap-2\">\n <span className=\"text-muted-foreground text-sm\">\n Different variants\n </span>\n <div className=\"flex items-center gap-4\">\n <RelativeTimeCard date={oneHourAgo} variant=\"default\" />\n <RelativeTimeCard date={oneHourAgo} variant=\"muted\" />\n <RelativeTimeCard date={oneHourAgo} variant=\"ghost\" />\n </div>\n </div>\n <div className=\"flex flex-col gap-2\">\n <span className=\"text-muted-foreground text-sm\">\n Multiple timezones\n </span>\n <RelativeTimeCard\n date={oneDayAgo}\n timezones={[\"America/New_York\", \"Europe/London\", \"Asia/Tokyo\"]}\n />\n </div>\n <div className=\"flex flex-col gap-2\">\n <span className=\"text-muted-foreground text-sm\">Custom trigger</span>\n <RelativeTimeCard date={now} asChild>\n <Button variant=\"outline\" size=\"sm\">\n <Clock />\n View time details\n </Button>\n </RelativeTimeCard>\n </div>\n <div className=\"flex flex-col gap-2\">\n <span className=\"text-muted-foreground text-sm\">\n Different positions\n </span>\n <div className=\"flex items-center gap-4\">\n <RelativeTimeCard date={now} side=\"top\" align=\"start\">\n Top Start\n </RelativeTimeCard>\n <RelativeTimeCard date={now} side=\"right\" align=\"center\">\n Right Center\n </RelativeTimeCard>\n <RelativeTimeCard date={now} side=\"bottom\" align=\"end\">\n Bottom End\n </RelativeTimeCard>\n </div>\n </div>\n </div>\n );\n}\n",
12+
"type": "registry:example",
13+
"target": ""
14+
}
15+
]
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "relative-time-card-timezones-demo",
3+
"type": "registry:example",
4+
"dependencies": [
5+
"@radix-ui/react-slot"
6+
],
7+
"registryDependencies": [
8+
"hover-card"
9+
],
10+
"files": [
11+
{
12+
"path": "examples/relative-time-card-timezones-demo.tsx",
13+
"content": "import { RelativeTimeCard } from \"@/registry/default/ui/relative-time-card\";\n\nexport default function RelativeTimeCardTimezonesDemo() {\n const now = new Date();\n\n return (\n <div className=\"flex flex-col gap-4\">\n <RelativeTimeCard\n date={now}\n timezones={[\n \"America/Los_Angeles\",\n \"America/New_York\",\n \"Europe/London\",\n \"Asia/Singapore\",\n \"Asia/Tokyo\",\n ]}\n />\n <RelativeTimeCard\n date={now}\n timezones={[\n \"America/Chicago\",\n \"Europe/Paris\",\n \"Asia/Dubai\",\n \"Australia/Sydney\",\n ]}\n />\n <RelativeTimeCard date={now} timezones={[\"UTC\"]} />\n </div>\n );\n}\n",
14+
"type": "registry:example",
15+
"target": ""
16+
}
17+
]
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"name": "relative-time-card-variants-demo",
3+
"type": "registry:example",
4+
"registryDependencies": [
5+
"button",
6+
"hover-card"
7+
],
8+
"files": [
9+
{
10+
"path": "examples/relative-time-card-variants-demo.tsx",
11+
"content": "import { Button } from \"@/components/ui/button\";\nimport { RelativeTimeCard } from \"@/registry/default/ui/relative-time-card\";\nimport { Clock } from \"lucide-react\";\n\nexport default function RelativeTimeCardVariantsDemo() {\n const now = new Date();\n\n return (\n <div className=\"flex flex-col gap-6\">\n <div className=\"flex flex-col gap-2\">\n <span className=\"text-muted-foreground text-sm\">Style variants</span>\n <div className=\"flex items-center gap-4\">\n <RelativeTimeCard date={now} variant=\"default\" />\n <RelativeTimeCard date={now} variant=\"muted\" />\n <RelativeTimeCard date={now} variant=\"ghost\" />\n </div>\n </div>\n <div className=\"flex flex-col gap-2\">\n <span className=\"text-muted-foreground text-sm\">Custom styling</span>\n <div className=\"flex items-center gap-4\">\n <RelativeTimeCard\n date={now}\n className=\"text-blue-500 hover:text-blue-700\"\n />\n <RelativeTimeCard\n date={now}\n className=\"font-semibold text-green-600 hover:text-green-800\"\n />\n <RelativeTimeCard\n date={now}\n className=\"text-purple-500 italic hover:text-purple-700\"\n />\n </div>\n </div>\n <div className=\"flex flex-col gap-2\">\n <span className=\"text-muted-foreground text-sm\">\n Hover card positions\n </span>\n <div className=\"flex items-center gap-4\">\n <RelativeTimeCard date={now} side=\"top\" align=\"start\" sideOffset={10}>\n Top aligned\n </RelativeTimeCard>\n <RelativeTimeCard\n date={now}\n side=\"right\"\n align=\"center\"\n sideOffset={10}\n >\n Right aligned\n </RelativeTimeCard>\n <RelativeTimeCard\n date={now}\n side=\"bottom\"\n align=\"end\"\n sideOffset={10}\n >\n Bottom aligned\n </RelativeTimeCard>\n </div>\n </div>\n <div className=\"flex flex-col gap-2\">\n <span className=\"text-muted-foreground text-sm\">Custom trigger</span>\n <div className=\"flex items-center gap-4\">\n <RelativeTimeCard date={now} asChild>\n <Button variant=\"outline\" size=\"sm\">\n <Clock />\n Time details\n </Button>\n </RelativeTimeCard>\n <RelativeTimeCard date={now}>\n <div className=\"flex items-center gap-2 text-emerald-600\">\n <span className=\"i-lucide-calendar h-4 w-4\" />\n <span>View date</span>\n </div>\n </RelativeTimeCard>\n </div>\n </div>\n </div>\n );\n}\n",
12+
"type": "registry:example",
13+
"target": ""
14+
}
15+
]
16+
}

0 commit comments

Comments
 (0)