@comitor/ui 1.11.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +35 -0
- package/README.md +23 -7
- package/dist/components/ui/action-bar-constants.d.ts +31 -0
- package/dist/components/ui/action-bar-constants.js +9 -0
- package/dist/components/ui/action-bar-constants.js.map +1 -0
- package/dist/components/ui/action-bar.d.ts +67 -0
- package/dist/components/ui/action-bar.js +522 -0
- package/dist/components/ui/action-bar.js.map +1 -0
- package/dist/components/ui/badge-overflow.d.ts +30 -0
- package/dist/components/ui/badge-overflow.js +179 -0
- package/dist/components/ui/badge-overflow.js.map +1 -0
- package/dist/components/ui/banner.d.ts +79 -0
- package/dist/components/ui/banner.js +518 -0
- package/dist/components/ui/banner.js.map +1 -0
- package/dist/components/ui/circular-progress.d.ts +50 -0
- package/dist/components/ui/circular-progress.js +266 -0
- package/dist/components/ui/circular-progress.js.map +1 -0
- package/dist/components/ui/cropper-constants.d.ts +34 -0
- package/dist/components/ui/cropper-constants.js +10 -0
- package/dist/components/ui/cropper-constants.js.map +1 -0
- package/dist/components/ui/cropper.d.ts +106 -0
- package/dist/components/ui/cropper.js +1269 -0
- package/dist/components/ui/cropper.js.map +1 -0
- package/dist/components/ui/drawer.d.ts +1 -1
- package/dist/components/ui/drawer.js +2 -2
- package/dist/components/ui/drawer.js.map +1 -1
- package/dist/components/ui/kanban-constants.d.ts +61 -0
- package/dist/components/ui/kanban-constants.js +21 -0
- package/dist/components/ui/kanban-constants.js.map +1 -0
- package/dist/components/ui/kanban.d.ts +72 -0
- package/dist/components/ui/kanban.js +787 -0
- package/dist/components/ui/kanban.js.map +1 -0
- package/dist/components/ui/key-value-constants.d.ts +33 -0
- package/dist/components/ui/key-value-constants.js +11 -0
- package/dist/components/ui/key-value-constants.js.map +1 -0
- package/dist/components/ui/key-value.d.ts +96 -0
- package/dist/components/ui/key-value.js +641 -0
- package/dist/components/ui/key-value.js.map +1 -0
- package/dist/components/ui/mask-input-constants.d.ts +99 -0
- package/dist/components/ui/mask-input-constants.js +601 -0
- package/dist/components/ui/mask-input-constants.js.map +1 -0
- package/dist/components/ui/mask-input.d.ts +36 -0
- package/dist/components/ui/mask-input.js +558 -0
- package/dist/components/ui/mask-input.js.map +1 -0
- package/dist/components/ui/mention.d.ts +31 -0
- package/dist/components/ui/mention.js +128 -0
- package/dist/components/ui/mention.js.map +1 -0
- package/dist/components/ui/phone-input-constants.d.ts +54 -0
- package/dist/components/ui/phone-input-constants.js +9 -0
- package/dist/components/ui/phone-input-constants.js.map +1 -0
- package/dist/components/ui/phone-input.d.ts +85 -0
- package/dist/components/ui/phone-input.js +743 -0
- package/dist/components/ui/phone-input.js.map +1 -0
- package/dist/components/ui/rating.d.ts +56 -0
- package/dist/components/ui/rating.js +646 -0
- package/dist/components/ui/rating.js.map +1 -0
- package/dist/components/ui/responsive-dialog.d.ts +35 -0
- package/dist/components/ui/responsive-dialog.js +193 -0
- package/dist/components/ui/responsive-dialog.js.map +1 -0
- package/dist/components/ui/scroll-spy.d.ts +42 -0
- package/dist/components/ui/scroll-spy.js +334 -0
- package/dist/components/ui/scroll-spy.js.map +1 -0
- package/dist/components/ui/stepper-constants.d.ts +29 -0
- package/dist/components/ui/stepper-constants.js +9 -0
- package/dist/components/ui/stepper-constants.js.map +1 -0
- package/dist/components/ui/stepper.d.ts +80 -0
- package/dist/components/ui/stepper.js +902 -0
- package/dist/components/ui/stepper.js.map +1 -0
- package/dist/components/ui/tags-input.d.ts +41 -0
- package/dist/components/ui/tags-input.js +127 -0
- package/dist/components/ui/tags-input.js.map +1 -0
- package/dist/components/ui/time-picker-constants.d.ts +48 -0
- package/dist/components/ui/time-picker-constants.js +14 -0
- package/dist/components/ui/time-picker-constants.js.map +1 -0
- package/dist/components/ui/time-picker.d.ts +115 -0
- package/dist/components/ui/time-picker.js +1620 -0
- package/dist/components/ui/time-picker.js.map +1 -0
- package/dist/components/ui/timeline.d.ts +32 -0
- package/dist/components/ui/timeline.js +559 -0
- package/dist/components/ui/timeline.js.map +1 -0
- package/dist/components/ui/tour-constants.d.ts +39 -0
- package/dist/components/ui/tour-constants.js +12 -0
- package/dist/components/ui/tour-constants.js.map +1 -0
- package/dist/components/ui/tour.d.ts +124 -0
- package/dist/components/ui/tour.js +1207 -0
- package/dist/components/ui/tour.js.map +1 -0
- package/dist/data-grid/data-grid-cell-variants.js +10 -6
- package/dist/data-grid/data-grid-cell-variants.js.map +1 -1
- package/dist/data-grid/data-grid-cell-wrapper.js +1 -1
- package/dist/data-grid/data-grid-cell-wrapper.js.map +1 -1
- package/dist/data-grid/data-grid-column-header.js +4 -2
- package/dist/data-grid/data-grid-column-header.js.map +1 -1
- package/dist/data-grid/data-grid-context-menu.js +5 -4
- package/dist/data-grid/data-grid-context-menu.js.map +1 -1
- package/dist/data-grid/data-grid-filter-menu.d.ts +12 -1
- package/dist/data-grid/data-grid-filter-menu.js +28 -17
- package/dist/data-grid/data-grid-filter-menu.js.map +1 -1
- package/dist/data-grid/data-grid-keyboard-shortcuts.d.ts +12 -1
- package/dist/data-grid/data-grid-keyboard-shortcuts.js +7 -5
- package/dist/data-grid/data-grid-keyboard-shortcuts.js.map +1 -1
- package/dist/data-grid/data-grid-locale.d.ts +24 -0
- package/dist/data-grid/data-grid-locale.js +25 -0
- package/dist/data-grid/data-grid-locale.js.map +1 -0
- package/dist/data-grid/data-grid-paste-dialog.js +3 -2
- package/dist/data-grid/data-grid-paste-dialog.js.map +1 -1
- package/dist/data-grid/data-grid-row-height-menu.d.ts +11 -1
- package/dist/data-grid/data-grid-row-height-menu.js +25 -28
- package/dist/data-grid/data-grid-row-height-menu.js.map +1 -1
- package/dist/data-grid/data-grid-row.js +1 -1
- package/dist/data-grid/data-grid-row.js.map +1 -1
- package/dist/data-grid/data-grid-search.js +6 -5
- package/dist/data-grid/data-grid-search.js.map +1 -1
- package/dist/data-grid/data-grid-select-column.js +3 -1
- package/dist/data-grid/data-grid-select-column.js.map +1 -1
- package/dist/data-grid/data-grid-sort-menu.d.ts +12 -1
- package/dist/data-grid/data-grid-sort-menu.js +25 -13
- package/dist/data-grid/data-grid-sort-menu.js.map +1 -1
- package/dist/data-grid/data-grid-view-menu.d.ts +12 -1
- package/dist/data-grid/data-grid-view-menu.js +12 -4
- package/dist/data-grid/data-grid-view-menu.js.map +1 -1
- package/dist/data-grid/data-grid.d.ts +14 -2
- package/dist/data-grid/data-grid.js +7 -4
- package/dist/data-grid/data-grid.js.map +1 -1
- package/dist/data-grid/filters.d.ts +17 -21
- package/dist/data-grid/filters.js +73 -52
- package/dist/data-grid/filters.js.map +1 -1
- package/dist/data-grid/index.d.ts +4 -1
- package/dist/data-grid/index.js +20 -2
- package/dist/data-grid/index.js.map +1 -1
- package/dist/data-grid/messages-en.d.ts +294 -0
- package/dist/data-grid/messages-en.js +279 -0
- package/dist/data-grid/messages-en.js.map +1 -0
- package/dist/data-grid/messages-vi.d.ts +12 -0
- package/dist/data-grid/messages-vi.js +279 -0
- package/dist/data-grid/messages-vi.js.map +1 -0
- package/dist/data-grid/messages.d.ts +549 -262
- package/dist/data-grid/messages.js +16 -275
- package/dist/data-grid/messages.js.map +1 -1
- package/dist/data-grid/sortable.js +1 -1
- package/dist/data-grid/sortable.js.map +1 -1
- package/dist/data-grid/use-data-grid-undo-redo.d.ts +13 -1
- package/dist/data-grid/use-data-grid-undo-redo.js +13 -10
- package/dist/data-grid/use-data-grid-undo-redo.js.map +1 -1
- package/dist/data-grid/use-data-grid.d.ts +281 -1
- package/dist/data-grid/use-data-grid.js +24 -19
- package/dist/data-grid/use-data-grid.js.map +1 -1
- package/dist/data-grid/utils.d.ts +9 -1
- package/dist/data-grid/utils.js +3 -2
- package/dist/data-grid/utils.js.map +1 -1
- package/dist/index.d.ts +31 -0
- package/dist/index.js +42 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/compose-refs.js.map +1 -0
- package/dist/lib/use-as-ref.js.map +1 -0
- package/dist/lib/use-is-mobile.d.ts +10 -0
- package/dist/lib/use-is-mobile.js +21 -0
- package/dist/lib/use-is-mobile.js.map +1 -0
- package/dist/lib/use-isomorphic-layout-effect.js.map +1 -0
- package/dist/lib/use-lazy-ref.js.map +1 -0
- package/dist/lib/visually-hidden-input.d.ts +12 -0
- package/dist/lib/visually-hidden-input.js +105 -0
- package/dist/lib/visually-hidden-input.js.map +1 -0
- package/package.json +5 -1
- package/styles.css +19 -0
- package/dist/data-grid/compose-refs.js.map +0 -1
- package/dist/data-grid/use-as-ref.js.map +0 -1
- package/dist/data-grid/use-isomorphic-layout-effect.js.map +0 -1
- package/dist/data-grid/use-lazy-ref.js.map +0 -1
- /package/dist/{data-grid → lib}/compose-refs.d.ts +0 -0
- /package/dist/{data-grid → lib}/compose-refs.js +0 -0
- /package/dist/{data-grid → lib}/use-as-ref.d.ts +0 -0
- /package/dist/{data-grid → lib}/use-as-ref.js +0 -0
- /package/dist/{data-grid → lib}/use-isomorphic-layout-effect.d.ts +0 -0
- /package/dist/{data-grid → lib}/use-isomorphic-layout-effect.js +0 -0
- /package/dist/{data-grid → lib}/use-lazy-ref.d.ts +0 -0
- /package/dist/{data-grid → lib}/use-lazy-ref.js +0 -0
|
@@ -0,0 +1,559 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useDirection } from "@radix-ui/react-direction";
|
|
4
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
5
|
+
import { cva } from "class-variance-authority";
|
|
6
|
+
import * as React from "react";
|
|
7
|
+
import { cn } from "../../lib/cn.js";
|
|
8
|
+
import { useComposedRefs } from "../../lib/compose-refs.js";
|
|
9
|
+
import { useIsomorphicLayoutEffect } from "../../lib/use-isomorphic-layout-effect.js";
|
|
10
|
+
import { useLazyRef } from "../../lib/use-lazy-ref.js";
|
|
11
|
+
const ROOT_NAME = "Timeline";
|
|
12
|
+
const ITEM_NAME = "TimelineItem";
|
|
13
|
+
const DOT_NAME = "TimelineDot";
|
|
14
|
+
const CONNECTOR_NAME = "TimelineConnector";
|
|
15
|
+
const CONTENT_NAME = "TimelineContent";
|
|
16
|
+
function getItemStatus(itemIndex, activeIndex) {
|
|
17
|
+
if (activeIndex === void 0 || itemIndex < 0) return "pending";
|
|
18
|
+
if (itemIndex < activeIndex) return "completed";
|
|
19
|
+
if (itemIndex === activeIndex) return "active";
|
|
20
|
+
return "pending";
|
|
21
|
+
}
|
|
22
|
+
function getSortedEntries(entries) {
|
|
23
|
+
return entries.sort((a, b) => {
|
|
24
|
+
const elementA = a[1].current;
|
|
25
|
+
const elementB = b[1].current;
|
|
26
|
+
if (!elementA || !elementB) return 0;
|
|
27
|
+
const position = elementA.compareDocumentPosition(elementB);
|
|
28
|
+
if (position & Node.DOCUMENT_POSITION_FOLLOWING) return -1;
|
|
29
|
+
if (position & Node.DOCUMENT_POSITION_PRECEDING) return 1;
|
|
30
|
+
return 0;
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
function useStore(selector) {
|
|
34
|
+
const store = React.useContext(StoreContext);
|
|
35
|
+
if (!store) {
|
|
36
|
+
throw new Error(`\`useStore\` ph\u1EA3i \u0111\u01B0\u1EE3c g\u1ECDi b\xEAn trong \`${ROOT_NAME}\``);
|
|
37
|
+
}
|
|
38
|
+
const getSnapshot = React.useCallback(() => selector(store), [store, selector]);
|
|
39
|
+
return React.useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);
|
|
40
|
+
}
|
|
41
|
+
const StoreContext = React.createContext(null);
|
|
42
|
+
function useStoreContext(consumerName) {
|
|
43
|
+
const context = React.useContext(StoreContext);
|
|
44
|
+
if (!context) {
|
|
45
|
+
throw new Error(`\`${consumerName}\` ph\u1EA3i n\u1EB1m b\xEAn trong \`${ROOT_NAME}\``);
|
|
46
|
+
}
|
|
47
|
+
return context;
|
|
48
|
+
}
|
|
49
|
+
const TimelineContext = React.createContext(null);
|
|
50
|
+
function useTimelineContext(consumerName) {
|
|
51
|
+
const context = React.useContext(TimelineContext);
|
|
52
|
+
if (!context) {
|
|
53
|
+
throw new Error(`\`${consumerName}\` ph\u1EA3i n\u1EB1m b\xEAn trong \`${ROOT_NAME}\``);
|
|
54
|
+
}
|
|
55
|
+
return context;
|
|
56
|
+
}
|
|
57
|
+
const timelineVariants = cva("relative flex [--timeline-connector-thickness:0.125rem] [--timeline-dot-size:0.875rem]", {
|
|
58
|
+
variants: {
|
|
59
|
+
orientation: {
|
|
60
|
+
vertical: "flex-col",
|
|
61
|
+
horizontal: "flex-row items-start"
|
|
62
|
+
},
|
|
63
|
+
variant: {
|
|
64
|
+
default: "",
|
|
65
|
+
alternate: ""
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
compoundVariants: [
|
|
69
|
+
{
|
|
70
|
+
orientation: "vertical",
|
|
71
|
+
variant: "default",
|
|
72
|
+
class: "gap-6"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
orientation: "horizontal",
|
|
76
|
+
variant: "default",
|
|
77
|
+
class: "gap-8"
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
orientation: "vertical",
|
|
81
|
+
variant: "alternate",
|
|
82
|
+
class: "relative w-full gap-3"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
orientation: "horizontal",
|
|
86
|
+
variant: "alternate",
|
|
87
|
+
class: "items-center gap-4"
|
|
88
|
+
}
|
|
89
|
+
],
|
|
90
|
+
defaultVariants: {
|
|
91
|
+
orientation: "vertical",
|
|
92
|
+
variant: "default"
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
function Timeline(props) {
|
|
96
|
+
const {
|
|
97
|
+
orientation = "vertical",
|
|
98
|
+
variant = "default",
|
|
99
|
+
dir: dirProp,
|
|
100
|
+
activeIndex,
|
|
101
|
+
asChild,
|
|
102
|
+
className,
|
|
103
|
+
...rootProps
|
|
104
|
+
} = props;
|
|
105
|
+
const dir = useDirection(dirProp);
|
|
106
|
+
const listenersRef = useLazyRef(() => /* @__PURE__ */ new Set());
|
|
107
|
+
const stateRef = useLazyRef(() => ({
|
|
108
|
+
items: /* @__PURE__ */ new Map()
|
|
109
|
+
}));
|
|
110
|
+
const store = React.useMemo(() => {
|
|
111
|
+
return {
|
|
112
|
+
subscribe: (cb) => {
|
|
113
|
+
listenersRef.current.add(cb);
|
|
114
|
+
return () => listenersRef.current.delete(cb);
|
|
115
|
+
},
|
|
116
|
+
getState: () => stateRef.current,
|
|
117
|
+
notify: () => {
|
|
118
|
+
for (const cb of listenersRef.current) {
|
|
119
|
+
cb();
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
onItemRegister: (id, ref) => {
|
|
123
|
+
stateRef.current.items.set(id, ref);
|
|
124
|
+
store.notify();
|
|
125
|
+
},
|
|
126
|
+
onItemUnregister: (id) => {
|
|
127
|
+
stateRef.current.items.delete(id);
|
|
128
|
+
store.notify();
|
|
129
|
+
},
|
|
130
|
+
getNextItemStatus: (id, activeIndex2) => {
|
|
131
|
+
const entries = Array.from(stateRef.current.items.entries());
|
|
132
|
+
const sortedEntries = getSortedEntries(entries);
|
|
133
|
+
const currentIndex = sortedEntries.findIndex(([key]) => key === id);
|
|
134
|
+
if (currentIndex === -1 || currentIndex === sortedEntries.length - 1) {
|
|
135
|
+
return void 0;
|
|
136
|
+
}
|
|
137
|
+
const nextItemIndex = currentIndex + 1;
|
|
138
|
+
return getItemStatus(nextItemIndex, activeIndex2);
|
|
139
|
+
},
|
|
140
|
+
getItemIndex: (id) => {
|
|
141
|
+
const entries = Array.from(stateRef.current.items.entries());
|
|
142
|
+
const sortedEntries = getSortedEntries(entries);
|
|
143
|
+
return sortedEntries.findIndex(([key]) => key === id);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
}, [listenersRef, stateRef]);
|
|
147
|
+
const contextValue = React.useMemo(
|
|
148
|
+
() => ({
|
|
149
|
+
dir,
|
|
150
|
+
orientation,
|
|
151
|
+
variant,
|
|
152
|
+
activeIndex
|
|
153
|
+
}),
|
|
154
|
+
[dir, orientation, variant, activeIndex]
|
|
155
|
+
);
|
|
156
|
+
const RootPrimitive = asChild ? Slot : "div";
|
|
157
|
+
return /* @__PURE__ */ jsx(StoreContext.Provider, { value: store, children: /* @__PURE__ */ jsx(TimelineContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(
|
|
158
|
+
RootPrimitive,
|
|
159
|
+
{
|
|
160
|
+
role: "list",
|
|
161
|
+
"data-slot": "timeline",
|
|
162
|
+
"data-orientation": orientation,
|
|
163
|
+
"data-variant": variant,
|
|
164
|
+
dir,
|
|
165
|
+
...rootProps,
|
|
166
|
+
className: cn(timelineVariants({ orientation, variant, className }))
|
|
167
|
+
}
|
|
168
|
+
) }) });
|
|
169
|
+
}
|
|
170
|
+
const TimelineItemContext = React.createContext(null);
|
|
171
|
+
function useTimelineItemContext(consumerName) {
|
|
172
|
+
const context = React.useContext(TimelineItemContext);
|
|
173
|
+
if (!context) {
|
|
174
|
+
throw new Error(`\`${consumerName}\` ph\u1EA3i n\u1EB1m b\xEAn trong \`${ITEM_NAME}\``);
|
|
175
|
+
}
|
|
176
|
+
return context;
|
|
177
|
+
}
|
|
178
|
+
const timelineItemVariants = cva("relative flex", {
|
|
179
|
+
variants: {
|
|
180
|
+
orientation: {
|
|
181
|
+
vertical: "",
|
|
182
|
+
horizontal: ""
|
|
183
|
+
},
|
|
184
|
+
variant: {
|
|
185
|
+
default: "",
|
|
186
|
+
alternate: ""
|
|
187
|
+
},
|
|
188
|
+
isAlternateRight: {
|
|
189
|
+
true: "",
|
|
190
|
+
false: ""
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
compoundVariants: [
|
|
194
|
+
{
|
|
195
|
+
orientation: "vertical",
|
|
196
|
+
variant: "default",
|
|
197
|
+
class: "gap-3 pb-8 last:pb-0"
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
orientation: "horizontal",
|
|
201
|
+
variant: "default",
|
|
202
|
+
class: "flex-col gap-3"
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
orientation: "vertical",
|
|
206
|
+
variant: "alternate",
|
|
207
|
+
isAlternateRight: false,
|
|
208
|
+
class: "w-1/2 gap-3 pr-6 pb-12 last:pb-0"
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
orientation: "vertical",
|
|
212
|
+
variant: "alternate",
|
|
213
|
+
isAlternateRight: true,
|
|
214
|
+
class: "ml-auto w-1/2 flex-row-reverse gap-3 pb-12 pl-6 last:pb-0"
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
orientation: "horizontal",
|
|
218
|
+
variant: "alternate",
|
|
219
|
+
class: "grid min-w-0 grid-rows-[1fr_auto_1fr] gap-3"
|
|
220
|
+
}
|
|
221
|
+
],
|
|
222
|
+
defaultVariants: {
|
|
223
|
+
orientation: "vertical",
|
|
224
|
+
variant: "default",
|
|
225
|
+
isAlternateRight: false
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
function TimelineItem(props) {
|
|
229
|
+
const { asChild, className, id, ref, ...itemProps } = props;
|
|
230
|
+
const { dir, orientation, variant, activeIndex } = useTimelineContext(ITEM_NAME);
|
|
231
|
+
const store = useStoreContext(ITEM_NAME);
|
|
232
|
+
const instanceId = React.useId();
|
|
233
|
+
const itemId = id ?? instanceId;
|
|
234
|
+
const itemRef = React.useRef(null);
|
|
235
|
+
const composedRef = useComposedRefs(ref, itemRef);
|
|
236
|
+
const itemIndex = useStore((state) => state.getItemIndex(itemId));
|
|
237
|
+
const status = React.useMemo(() => {
|
|
238
|
+
return getItemStatus(itemIndex, activeIndex);
|
|
239
|
+
}, [activeIndex, itemIndex]);
|
|
240
|
+
useIsomorphicLayoutEffect(() => {
|
|
241
|
+
store.onItemRegister(itemId, itemRef);
|
|
242
|
+
return () => {
|
|
243
|
+
store.onItemUnregister(itemId);
|
|
244
|
+
};
|
|
245
|
+
}, [itemId, store]);
|
|
246
|
+
const isAlternateRight = variant === "alternate" && itemIndex % 2 === 1;
|
|
247
|
+
const itemContextValue = React.useMemo(
|
|
248
|
+
() => ({ id: itemId, status, isAlternateRight }),
|
|
249
|
+
[itemId, status, isAlternateRight]
|
|
250
|
+
);
|
|
251
|
+
const ItemPrimitive = asChild ? Slot : "div";
|
|
252
|
+
return /* @__PURE__ */ jsx(TimelineItemContext.Provider, { value: itemContextValue, children: /* @__PURE__ */ jsx(
|
|
253
|
+
ItemPrimitive,
|
|
254
|
+
{
|
|
255
|
+
role: "listitem",
|
|
256
|
+
"aria-current": status === "active" ? "step" : void 0,
|
|
257
|
+
"data-slot": "timeline-item",
|
|
258
|
+
"data-status": status,
|
|
259
|
+
"data-orientation": orientation,
|
|
260
|
+
"data-alternate-right": isAlternateRight ? "" : void 0,
|
|
261
|
+
id: itemId,
|
|
262
|
+
dir,
|
|
263
|
+
...itemProps,
|
|
264
|
+
ref: composedRef,
|
|
265
|
+
className: cn(
|
|
266
|
+
timelineItemVariants({
|
|
267
|
+
orientation,
|
|
268
|
+
variant,
|
|
269
|
+
isAlternateRight,
|
|
270
|
+
className
|
|
271
|
+
})
|
|
272
|
+
)
|
|
273
|
+
}
|
|
274
|
+
) });
|
|
275
|
+
}
|
|
276
|
+
const timelineContentVariants = cva("flex-1", {
|
|
277
|
+
variants: {
|
|
278
|
+
orientation: {
|
|
279
|
+
vertical: "",
|
|
280
|
+
horizontal: ""
|
|
281
|
+
},
|
|
282
|
+
variant: {
|
|
283
|
+
default: "",
|
|
284
|
+
alternate: ""
|
|
285
|
+
},
|
|
286
|
+
isAlternateRight: {
|
|
287
|
+
true: "",
|
|
288
|
+
false: ""
|
|
289
|
+
}
|
|
290
|
+
},
|
|
291
|
+
compoundVariants: [
|
|
292
|
+
{
|
|
293
|
+
variant: "alternate",
|
|
294
|
+
orientation: "vertical",
|
|
295
|
+
isAlternateRight: false,
|
|
296
|
+
class: "text-right"
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
variant: "alternate",
|
|
300
|
+
orientation: "horizontal",
|
|
301
|
+
isAlternateRight: false,
|
|
302
|
+
class: "row-start-3 pt-2"
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
variant: "alternate",
|
|
306
|
+
orientation: "horizontal",
|
|
307
|
+
isAlternateRight: true,
|
|
308
|
+
class: "row-start-1 pb-2"
|
|
309
|
+
}
|
|
310
|
+
],
|
|
311
|
+
defaultVariants: {
|
|
312
|
+
orientation: "vertical",
|
|
313
|
+
variant: "default",
|
|
314
|
+
isAlternateRight: false
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
function TimelineContent(props) {
|
|
318
|
+
const { asChild, className, ...contentProps } = props;
|
|
319
|
+
const { variant, orientation } = useTimelineContext(CONTENT_NAME);
|
|
320
|
+
const { status, isAlternateRight } = useTimelineItemContext(CONTENT_NAME);
|
|
321
|
+
const ContentPrimitive = asChild ? Slot : "div";
|
|
322
|
+
return /* @__PURE__ */ jsx(
|
|
323
|
+
ContentPrimitive,
|
|
324
|
+
{
|
|
325
|
+
"data-slot": "timeline-content",
|
|
326
|
+
"data-status": status,
|
|
327
|
+
...contentProps,
|
|
328
|
+
className: cn(
|
|
329
|
+
timelineContentVariants({
|
|
330
|
+
orientation,
|
|
331
|
+
variant,
|
|
332
|
+
isAlternateRight,
|
|
333
|
+
className
|
|
334
|
+
})
|
|
335
|
+
)
|
|
336
|
+
}
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
const timelineDotVariants = cva(
|
|
340
|
+
"relative z-10 flex size-[var(--timeline-dot-size)] shrink-0 items-center justify-center rounded-full border-2 bg-background",
|
|
341
|
+
{
|
|
342
|
+
variants: {
|
|
343
|
+
status: {
|
|
344
|
+
completed: "border-primary-ink",
|
|
345
|
+
active: "border-primary-ink",
|
|
346
|
+
pending: "border-muted-foreground"
|
|
347
|
+
},
|
|
348
|
+
orientation: {
|
|
349
|
+
vertical: "",
|
|
350
|
+
horizontal: ""
|
|
351
|
+
},
|
|
352
|
+
variant: {
|
|
353
|
+
default: "",
|
|
354
|
+
alternate: ""
|
|
355
|
+
},
|
|
356
|
+
isAlternateRight: {
|
|
357
|
+
true: "",
|
|
358
|
+
false: ""
|
|
359
|
+
}
|
|
360
|
+
},
|
|
361
|
+
compoundVariants: [
|
|
362
|
+
{
|
|
363
|
+
variant: "alternate",
|
|
364
|
+
orientation: "vertical",
|
|
365
|
+
isAlternateRight: false,
|
|
366
|
+
class: "absolute -right-[calc(var(--timeline-dot-size)/2-var(--timeline-connector-thickness)/2)] bg-background"
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
variant: "alternate",
|
|
370
|
+
orientation: "vertical",
|
|
371
|
+
isAlternateRight: true,
|
|
372
|
+
class: "absolute -left-[calc(var(--timeline-dot-size)/2-var(--timeline-connector-thickness)/2)] bg-background"
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
variant: "alternate",
|
|
376
|
+
orientation: "horizontal",
|
|
377
|
+
class: "row-start-2 bg-background"
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
variant: "alternate",
|
|
381
|
+
status: "completed",
|
|
382
|
+
class: "bg-background"
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
variant: "alternate",
|
|
386
|
+
status: "active",
|
|
387
|
+
class: "bg-background"
|
|
388
|
+
}
|
|
389
|
+
],
|
|
390
|
+
defaultVariants: {
|
|
391
|
+
status: "pending",
|
|
392
|
+
orientation: "vertical",
|
|
393
|
+
variant: "default",
|
|
394
|
+
isAlternateRight: false
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
);
|
|
398
|
+
function TimelineDot(props) {
|
|
399
|
+
const { asChild, className, ...dotProps } = props;
|
|
400
|
+
const { orientation, variant } = useTimelineContext(DOT_NAME);
|
|
401
|
+
const { status, isAlternateRight } = useTimelineItemContext(DOT_NAME);
|
|
402
|
+
const DotPrimitive = asChild ? Slot : "div";
|
|
403
|
+
return /* @__PURE__ */ jsx(
|
|
404
|
+
DotPrimitive,
|
|
405
|
+
{
|
|
406
|
+
"data-slot": "timeline-dot",
|
|
407
|
+
"data-status": status,
|
|
408
|
+
"data-orientation": orientation,
|
|
409
|
+
...dotProps,
|
|
410
|
+
className: cn(
|
|
411
|
+
timelineDotVariants({
|
|
412
|
+
status,
|
|
413
|
+
orientation,
|
|
414
|
+
variant,
|
|
415
|
+
isAlternateRight,
|
|
416
|
+
className
|
|
417
|
+
})
|
|
418
|
+
)
|
|
419
|
+
}
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
const timelineConnectorVariants = cva("absolute z-0", {
|
|
423
|
+
variants: {
|
|
424
|
+
isCompleted: {
|
|
425
|
+
true: "bg-primary-ink",
|
|
426
|
+
false: "bg-border"
|
|
427
|
+
},
|
|
428
|
+
orientation: {
|
|
429
|
+
vertical: "",
|
|
430
|
+
horizontal: ""
|
|
431
|
+
},
|
|
432
|
+
variant: {
|
|
433
|
+
default: "",
|
|
434
|
+
alternate: ""
|
|
435
|
+
},
|
|
436
|
+
isAlternateRight: {
|
|
437
|
+
true: "",
|
|
438
|
+
false: ""
|
|
439
|
+
}
|
|
440
|
+
},
|
|
441
|
+
compoundVariants: [
|
|
442
|
+
{
|
|
443
|
+
orientation: "vertical",
|
|
444
|
+
variant: "default",
|
|
445
|
+
class: "start-[calc(var(--timeline-dot-size)/2-var(--timeline-connector-thickness)/2)] top-3 h-[calc(100%+0.5rem)] w-[var(--timeline-connector-thickness)]"
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
orientation: "horizontal",
|
|
449
|
+
variant: "default",
|
|
450
|
+
class: "start-3 top-[calc(var(--timeline-dot-size)/2-var(--timeline-connector-thickness)/2)] h-[var(--timeline-connector-thickness)] w-[calc(100%+0.5rem)]"
|
|
451
|
+
},
|
|
452
|
+
{
|
|
453
|
+
orientation: "vertical",
|
|
454
|
+
variant: "alternate",
|
|
455
|
+
isAlternateRight: false,
|
|
456
|
+
class: "top-2 -right-[calc(var(--timeline-connector-thickness)/2)] h-full w-[var(--timeline-connector-thickness)]"
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
orientation: "vertical",
|
|
460
|
+
variant: "alternate",
|
|
461
|
+
isAlternateRight: true,
|
|
462
|
+
class: "top-2 -left-[calc(var(--timeline-connector-thickness)/2)] h-full w-[var(--timeline-connector-thickness)]"
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
orientation: "horizontal",
|
|
466
|
+
variant: "alternate",
|
|
467
|
+
class: "top-[calc(var(--timeline-dot-size)/2-var(--timeline-connector-thickness)/2)] left-3 row-start-2 h-[var(--timeline-connector-thickness)] w-[calc(100%+0.5rem)]"
|
|
468
|
+
}
|
|
469
|
+
],
|
|
470
|
+
defaultVariants: {
|
|
471
|
+
isCompleted: false,
|
|
472
|
+
orientation: "vertical",
|
|
473
|
+
variant: "default",
|
|
474
|
+
isAlternateRight: false
|
|
475
|
+
}
|
|
476
|
+
});
|
|
477
|
+
function TimelineConnector(props) {
|
|
478
|
+
const { asChild, forceMount, className, ...connectorProps } = props;
|
|
479
|
+
const { orientation, variant, activeIndex } = useTimelineContext(CONNECTOR_NAME);
|
|
480
|
+
const { id, status, isAlternateRight } = useTimelineItemContext(CONNECTOR_NAME);
|
|
481
|
+
const nextItemStatus = useStore((state) => state.getNextItemStatus(id, activeIndex));
|
|
482
|
+
const isLastItem = nextItemStatus === void 0;
|
|
483
|
+
if (!forceMount && isLastItem) return null;
|
|
484
|
+
const isConnectorCompleted = nextItemStatus === "completed" || nextItemStatus === "active";
|
|
485
|
+
const ConnectorPrimitive = asChild ? Slot : "div";
|
|
486
|
+
return /* @__PURE__ */ jsx(
|
|
487
|
+
ConnectorPrimitive,
|
|
488
|
+
{
|
|
489
|
+
"aria-hidden": "true",
|
|
490
|
+
"data-slot": "timeline-connector",
|
|
491
|
+
"data-completed": isConnectorCompleted ? "" : void 0,
|
|
492
|
+
"data-status": status,
|
|
493
|
+
"data-orientation": orientation,
|
|
494
|
+
...connectorProps,
|
|
495
|
+
className: cn(
|
|
496
|
+
timelineConnectorVariants({
|
|
497
|
+
isCompleted: isConnectorCompleted,
|
|
498
|
+
orientation,
|
|
499
|
+
variant,
|
|
500
|
+
isAlternateRight,
|
|
501
|
+
className
|
|
502
|
+
})
|
|
503
|
+
)
|
|
504
|
+
}
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
function TimelineHeader(props) {
|
|
508
|
+
const { asChild, className, ...headerProps } = props;
|
|
509
|
+
const HeaderPrimitive = asChild ? Slot : "div";
|
|
510
|
+
return /* @__PURE__ */ jsx(HeaderPrimitive, { "data-slot": "timeline-header", ...headerProps, className: cn("flex flex-col gap-1", className) });
|
|
511
|
+
}
|
|
512
|
+
function TimelineTitle(props) {
|
|
513
|
+
const { asChild, className, ...titleProps } = props;
|
|
514
|
+
const TitlePrimitive = asChild ? Slot : "div";
|
|
515
|
+
return /* @__PURE__ */ jsx(
|
|
516
|
+
TitlePrimitive,
|
|
517
|
+
{
|
|
518
|
+
"data-slot": "timeline-title",
|
|
519
|
+
...titleProps,
|
|
520
|
+
className: cn("leading-none font-semibold", className)
|
|
521
|
+
}
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
function TimelineDescription(props) {
|
|
525
|
+
const { asChild, className, ...descriptionProps } = props;
|
|
526
|
+
const DescriptionPrimitive = asChild ? Slot : "div";
|
|
527
|
+
return /* @__PURE__ */ jsx(
|
|
528
|
+
DescriptionPrimitive,
|
|
529
|
+
{
|
|
530
|
+
"data-slot": "timeline-description",
|
|
531
|
+
...descriptionProps,
|
|
532
|
+
className: cn("text-sm text-muted-foreground", className)
|
|
533
|
+
}
|
|
534
|
+
);
|
|
535
|
+
}
|
|
536
|
+
function TimelineTime(props) {
|
|
537
|
+
const { asChild, className, ...timeProps } = props;
|
|
538
|
+
const TimePrimitive = asChild ? Slot : "time";
|
|
539
|
+
return /* @__PURE__ */ jsx(
|
|
540
|
+
TimePrimitive,
|
|
541
|
+
{
|
|
542
|
+
"data-slot": "timeline-time",
|
|
543
|
+
...timeProps,
|
|
544
|
+
className: cn("text-xs text-muted-foreground", className)
|
|
545
|
+
}
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
export {
|
|
549
|
+
Timeline,
|
|
550
|
+
TimelineConnector,
|
|
551
|
+
TimelineContent,
|
|
552
|
+
TimelineDescription,
|
|
553
|
+
TimelineDot,
|
|
554
|
+
TimelineHeader,
|
|
555
|
+
TimelineItem,
|
|
556
|
+
TimelineTime,
|
|
557
|
+
TimelineTitle
|
|
558
|
+
};
|
|
559
|
+
//# sourceMappingURL=timeline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/ui/timeline.tsx"],"sourcesContent":["\"use client\";\n\n/*\n * Port từ Dice UI (https://diceui.com/docs/components/timeline) — MIT, © 2024 Sadman Sakib.\n * Giữ nguyên cấu trúc thượng nguồn để còn đối chiếu ngược được; mọi chỗ đổi đều có chú thích.\n */\n\nimport { useDirection } from \"@radix-ui/react-direction\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva } from \"class-variance-authority\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\nimport { useComposedRefs } from \"../../lib/compose-refs.js\";\nimport { useIsomorphicLayoutEffect } from \"../../lib/use-isomorphic-layout-effect.js\";\nimport { useLazyRef } from \"../../lib/use-lazy-ref.js\";\n\ntype Direction = \"ltr\" | \"rtl\";\ntype Orientation = \"vertical\" | \"horizontal\";\ntype Variant = \"default\" | \"alternate\";\ntype Status = \"completed\" | \"active\" | \"pending\";\n\ninterface DivProps extends React.ComponentProps<\"div\"> {\n asChild?: boolean;\n}\n\ntype ItemElement = React.ComponentRef<typeof TimelineItem>;\n\nconst ROOT_NAME = \"Timeline\";\nconst ITEM_NAME = \"TimelineItem\";\nconst DOT_NAME = \"TimelineDot\";\nconst CONNECTOR_NAME = \"TimelineConnector\";\nconst CONTENT_NAME = \"TimelineContent\";\n\nfunction getItemStatus(itemIndex: number, activeIndex?: number): Status {\n /*\n * ⚠ Khác thượng nguồn: mục đăng ký chỉ số của mình trong layout effect, nên ở lượt render phía\n * server (và lượt đầu trước hydrate) MỌI mục đều mang chỉ số -1. Bản gốc so thẳng `-1 < activeIndex`\n * → mọi chấm cùng ra \"completed\" (tô gold) cho tới khi hydrate xong — một trạng thái SAI, không\n * chỉ là thiếu. Chưa biết vị trí thì coi là \"pending\": trung tính, và đúng với phần lớn mục.\n */\n if (activeIndex === undefined || itemIndex < 0) return \"pending\";\n if (itemIndex < activeIndex) return \"completed\";\n if (itemIndex === activeIndex) return \"active\";\n return \"pending\";\n}\n\nfunction getSortedEntries(entries: [string, React.RefObject<ItemElement | null>][]) {\n return entries.sort((a, b) => {\n const elementA = a[1].current;\n const elementB = b[1].current;\n if (!elementA || !elementB) return 0;\n const position = elementA.compareDocumentPosition(elementB);\n if (position & Node.DOCUMENT_POSITION_FOLLOWING) return -1;\n if (position & Node.DOCUMENT_POSITION_PRECEDING) return 1;\n return 0;\n });\n}\n\nfunction useStore<T>(selector: (store: Store) => T): T {\n const store = React.useContext(StoreContext);\n if (!store) {\n throw new Error(`\\`useStore\\` phải được gọi bên trong \\`${ROOT_NAME}\\``);\n }\n\n const getSnapshot = React.useCallback(() => selector(store), [store, selector]);\n\n return React.useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);\n}\n\ninterface StoreState {\n items: Map<string, React.RefObject<ItemElement | null>>;\n}\n\ninterface Store {\n subscribe: (callback: () => void) => () => void;\n getState: () => StoreState;\n notify: () => void;\n onItemRegister: (id: string, ref: React.RefObject<ItemElement | null>) => void;\n onItemUnregister: (id: string) => void;\n getNextItemStatus: (id: string, activeIndex?: number) => Status | undefined;\n getItemIndex: (id: string) => number;\n}\n\nconst StoreContext = React.createContext<Store | null>(null);\n\nfunction useStoreContext(consumerName: string) {\n const context = React.useContext(StoreContext);\n if (!context) {\n throw new Error(`\\`${consumerName}\\` phải nằm bên trong \\`${ROOT_NAME}\\``);\n }\n return context;\n}\n\ninterface TimelineContextValue {\n dir: Direction;\n orientation: Orientation;\n variant: Variant;\n activeIndex?: number;\n}\n\nconst TimelineContext = React.createContext<TimelineContextValue | null>(null);\n\nfunction useTimelineContext(consumerName: string) {\n const context = React.useContext(TimelineContext);\n if (!context) {\n throw new Error(`\\`${consumerName}\\` phải nằm bên trong \\`${ROOT_NAME}\\``);\n }\n return context;\n}\n\nconst timelineVariants = cva(\"relative flex [--timeline-connector-thickness:0.125rem] [--timeline-dot-size:0.875rem]\", {\n variants: {\n orientation: {\n vertical: \"flex-col\",\n horizontal: \"flex-row items-start\"\n },\n variant: {\n default: \"\",\n alternate: \"\"\n }\n },\n compoundVariants: [\n {\n orientation: \"vertical\",\n variant: \"default\",\n class: \"gap-6\"\n },\n {\n orientation: \"horizontal\",\n variant: \"default\",\n class: \"gap-8\"\n },\n {\n orientation: \"vertical\",\n variant: \"alternate\",\n class: \"relative w-full gap-3\"\n },\n {\n orientation: \"horizontal\",\n variant: \"alternate\",\n class: \"items-center gap-4\"\n }\n ],\n defaultVariants: {\n orientation: \"vertical\",\n variant: \"default\"\n }\n});\n\ninterface TimelineProps extends DivProps {\n dir?: Direction;\n orientation?: Orientation;\n variant?: Variant;\n activeIndex?: number;\n}\n\nfunction Timeline(props: TimelineProps) {\n const {\n orientation = \"vertical\",\n variant = \"default\",\n dir: dirProp,\n activeIndex,\n asChild,\n className,\n ...rootProps\n } = props;\n\n const dir = useDirection(dirProp);\n\n const listenersRef = useLazyRef(() => new Set<() => void>());\n const stateRef = useLazyRef<StoreState>(() => ({\n items: new Map()\n }));\n\n const store = React.useMemo<Store>(() => {\n return {\n subscribe: (cb) => {\n listenersRef.current.add(cb);\n return () => listenersRef.current.delete(cb);\n },\n getState: () => stateRef.current,\n notify: () => {\n for (const cb of listenersRef.current) {\n cb();\n }\n },\n onItemRegister: (id: string, ref: React.RefObject<ItemElement | null>) => {\n stateRef.current.items.set(id, ref);\n store.notify();\n },\n onItemUnregister: (id: string) => {\n stateRef.current.items.delete(id);\n store.notify();\n },\n getNextItemStatus: (id: string, activeIndex?: number) => {\n const entries = Array.from(stateRef.current.items.entries());\n const sortedEntries = getSortedEntries(entries);\n\n const currentIndex = sortedEntries.findIndex(([key]) => key === id);\n if (currentIndex === -1 || currentIndex === sortedEntries.length - 1) {\n return undefined;\n }\n\n const nextItemIndex = currentIndex + 1;\n return getItemStatus(nextItemIndex, activeIndex);\n },\n getItemIndex: (id: string) => {\n const entries = Array.from(stateRef.current.items.entries());\n const sortedEntries = getSortedEntries(entries);\n return sortedEntries.findIndex(([key]) => key === id);\n }\n };\n }, [listenersRef, stateRef]);\n\n const contextValue = React.useMemo<TimelineContextValue>(\n () => ({\n dir,\n orientation,\n variant,\n activeIndex\n }),\n [dir, orientation, variant, activeIndex]\n );\n\n const RootPrimitive = asChild ? Slot : \"div\";\n\n return (\n <StoreContext.Provider value={store}>\n <TimelineContext.Provider value={contextValue}>\n {/*\n * ⚠ Khác thượng nguồn: bỏ `aria-orientation` khỏi thẻ gốc. `role=\"list\"` không nhận thuộc\n * tính đó theo ARIA 1.2 (axe báo `aria-allowed-attr`); hướng vẫn có ở `data-orientation`.\n */}\n <RootPrimitive\n role=\"list\"\n data-slot=\"timeline\"\n data-orientation={orientation}\n data-variant={variant}\n dir={dir}\n {...rootProps}\n className={cn(timelineVariants({ orientation, variant, className }))}\n />\n </TimelineContext.Provider>\n </StoreContext.Provider>\n );\n}\n\ninterface TimelineItemContextValue {\n id: string;\n status: Status;\n isAlternateRight: boolean;\n}\n\nconst TimelineItemContext = React.createContext<TimelineItemContextValue | null>(null);\n\nfunction useTimelineItemContext(consumerName: string) {\n const context = React.useContext(TimelineItemContext);\n if (!context) {\n throw new Error(`\\`${consumerName}\\` phải nằm bên trong \\`${ITEM_NAME}\\``);\n }\n return context;\n}\n\nconst timelineItemVariants = cva(\"relative flex\", {\n variants: {\n orientation: {\n vertical: \"\",\n horizontal: \"\"\n },\n variant: {\n default: \"\",\n alternate: \"\"\n },\n isAlternateRight: {\n true: \"\",\n false: \"\"\n }\n },\n compoundVariants: [\n {\n orientation: \"vertical\",\n variant: \"default\",\n class: \"gap-3 pb-8 last:pb-0\"\n },\n {\n orientation: \"horizontal\",\n variant: \"default\",\n class: \"flex-col gap-3\"\n },\n {\n orientation: \"vertical\",\n variant: \"alternate\",\n isAlternateRight: false,\n class: \"w-1/2 gap-3 pr-6 pb-12 last:pb-0\"\n },\n {\n orientation: \"vertical\",\n variant: \"alternate\",\n isAlternateRight: true,\n class: \"ml-auto w-1/2 flex-row-reverse gap-3 pb-12 pl-6 last:pb-0\"\n },\n {\n orientation: \"horizontal\",\n variant: \"alternate\",\n class: \"grid min-w-0 grid-rows-[1fr_auto_1fr] gap-3\"\n }\n ],\n defaultVariants: {\n orientation: \"vertical\",\n variant: \"default\",\n isAlternateRight: false\n }\n});\n\nfunction TimelineItem(props: DivProps) {\n const { asChild, className, id, ref, ...itemProps } = props;\n\n const { dir, orientation, variant, activeIndex } = useTimelineContext(ITEM_NAME);\n const store = useStoreContext(ITEM_NAME);\n\n const instanceId = React.useId();\n const itemId = id ?? instanceId;\n const itemRef = React.useRef<ItemElement | null>(null);\n const composedRef = useComposedRefs(ref, itemRef);\n\n const itemIndex = useStore((state) => state.getItemIndex(itemId));\n\n const status = React.useMemo<Status>(() => {\n return getItemStatus(itemIndex, activeIndex);\n }, [activeIndex, itemIndex]);\n\n // ⚠ Khác thượng nguồn: phụ thuộc là `itemId` (thứ thật sự được dùng) thay cho `id` — cùng\n // nghĩa, vì `itemId = id ?? instanceId` và `instanceId` không đổi suốt vòng đời.\n useIsomorphicLayoutEffect(() => {\n store.onItemRegister(itemId, itemRef);\n return () => {\n store.onItemUnregister(itemId);\n };\n }, [itemId, store]);\n\n const isAlternateRight = variant === \"alternate\" && itemIndex % 2 === 1;\n\n const itemContextValue = React.useMemo<TimelineItemContextValue>(\n () => ({ id: itemId, status, isAlternateRight }),\n [itemId, status, isAlternateRight]\n );\n\n const ItemPrimitive = asChild ? Slot : \"div\";\n\n return (\n <TimelineItemContext.Provider value={itemContextValue}>\n <ItemPrimitive\n role=\"listitem\"\n aria-current={status === \"active\" ? \"step\" : undefined}\n data-slot=\"timeline-item\"\n data-status={status}\n data-orientation={orientation}\n data-alternate-right={isAlternateRight ? \"\" : undefined}\n id={itemId}\n dir={dir}\n {...itemProps}\n ref={composedRef}\n className={cn(\n timelineItemVariants({\n orientation,\n variant,\n isAlternateRight,\n className\n })\n )}\n />\n </TimelineItemContext.Provider>\n );\n}\n\nconst timelineContentVariants = cva(\"flex-1\", {\n variants: {\n orientation: {\n vertical: \"\",\n horizontal: \"\"\n },\n variant: {\n default: \"\",\n alternate: \"\"\n },\n isAlternateRight: {\n true: \"\",\n false: \"\"\n }\n },\n compoundVariants: [\n {\n variant: \"alternate\",\n orientation: \"vertical\",\n isAlternateRight: false,\n class: \"text-right\"\n },\n {\n variant: \"alternate\",\n orientation: \"horizontal\",\n isAlternateRight: false,\n class: \"row-start-3 pt-2\"\n },\n {\n variant: \"alternate\",\n orientation: \"horizontal\",\n isAlternateRight: true,\n class: \"row-start-1 pb-2\"\n }\n ],\n defaultVariants: {\n orientation: \"vertical\",\n variant: \"default\",\n isAlternateRight: false\n }\n});\n\nfunction TimelineContent(props: DivProps) {\n const { asChild, className, ...contentProps } = props;\n\n const { variant, orientation } = useTimelineContext(CONTENT_NAME);\n const { status, isAlternateRight } = useTimelineItemContext(CONTENT_NAME);\n\n const ContentPrimitive = asChild ? Slot : \"div\";\n\n return (\n <ContentPrimitive\n data-slot=\"timeline-content\"\n data-status={status}\n {...contentProps}\n className={cn(\n timelineContentVariants({\n orientation,\n variant,\n isAlternateRight,\n className\n })\n )}\n />\n );\n}\n\n/*\n * ⚠ Khác thượng nguồn — màu chấm đi qua token VAI TRÒ (docs/hop-dong-mau.md §1):\n * chấm là thành phần PHI VĂN BẢN mang nghĩa (đánh dấu từng mốc và trạng thái của nó) → viền chịu\n * 3:1 với nền trang.\n * - đã xong / đang diễn ra: `border-primary-ink` thay `border-primary` (token TÔ NỀN, ~1,86:1 với\n * nền trang; bậc ink đạt 6,51:1 light · 12,67:1 dark ở bảng tương phản cao).\n * - chờ: `border-muted-foreground` thay `border-border` (bậc TRANG TRÍ 1,16:1, và cổng bắt viền trang\n * trí khi phần tử có bo góc + cỡ cố định). Cùng lựa chọn với tone `neutral` của `StatusPill`\n * (`STATUS_TONES.neutral.borderColor`): 5,23:1 light · 5,70:1 dark.\n */\nconst timelineDotVariants = cva(\n \"relative z-10 flex size-[var(--timeline-dot-size)] shrink-0 items-center justify-center rounded-full border-2 bg-background\",\n {\n variants: {\n status: {\n completed: \"border-primary-ink\",\n active: \"border-primary-ink\",\n pending: \"border-muted-foreground\"\n },\n orientation: {\n vertical: \"\",\n horizontal: \"\"\n },\n variant: {\n default: \"\",\n alternate: \"\"\n },\n isAlternateRight: {\n true: \"\",\n false: \"\"\n }\n },\n compoundVariants: [\n {\n variant: \"alternate\",\n orientation: \"vertical\",\n isAlternateRight: false,\n class: \"absolute -right-[calc(var(--timeline-dot-size)/2-var(--timeline-connector-thickness)/2)] bg-background\"\n },\n {\n variant: \"alternate\",\n orientation: \"vertical\",\n isAlternateRight: true,\n class: \"absolute -left-[calc(var(--timeline-dot-size)/2-var(--timeline-connector-thickness)/2)] bg-background\"\n },\n {\n variant: \"alternate\",\n orientation: \"horizontal\",\n class: \"row-start-2 bg-background\"\n },\n {\n variant: \"alternate\",\n status: \"completed\",\n class: \"bg-background\"\n },\n {\n variant: \"alternate\",\n status: \"active\",\n class: \"bg-background\"\n }\n ],\n defaultVariants: {\n status: \"pending\",\n orientation: \"vertical\",\n variant: \"default\",\n isAlternateRight: false\n }\n }\n);\n\nfunction TimelineDot(props: DivProps) {\n const { asChild, className, ...dotProps } = props;\n\n const { orientation, variant } = useTimelineContext(DOT_NAME);\n const { status, isAlternateRight } = useTimelineItemContext(DOT_NAME);\n\n const DotPrimitive = asChild ? Slot : \"div\";\n\n return (\n <DotPrimitive\n data-slot=\"timeline-dot\"\n data-status={status}\n data-orientation={orientation}\n {...dotProps}\n className={cn(\n timelineDotVariants({\n status,\n orientation,\n variant,\n isAlternateRight,\n className\n })\n )}\n />\n );\n}\n\n/*\n * ⚠ Khác thượng nguồn: đoạn nối ĐÃ XONG mang nghĩa tiến độ → `bg-primary-ink` thay `bg-primary`\n * (token tô nền, ~1,86:1 với nền trang). Đoạn chưa tới giữ `bg-border` — đường kẻ trang trí.\n */\nconst timelineConnectorVariants = cva(\"absolute z-0\", {\n variants: {\n isCompleted: {\n true: \"bg-primary-ink\",\n false: \"bg-border\"\n },\n orientation: {\n vertical: \"\",\n horizontal: \"\"\n },\n variant: {\n default: \"\",\n alternate: \"\"\n },\n isAlternateRight: {\n true: \"\",\n false: \"\"\n }\n },\n compoundVariants: [\n {\n orientation: \"vertical\",\n variant: \"default\",\n class:\n \"start-[calc(var(--timeline-dot-size)/2-var(--timeline-connector-thickness)/2)] top-3 h-[calc(100%+0.5rem)] w-[var(--timeline-connector-thickness)]\"\n },\n {\n orientation: \"horizontal\",\n variant: \"default\",\n class:\n \"start-3 top-[calc(var(--timeline-dot-size)/2-var(--timeline-connector-thickness)/2)] h-[var(--timeline-connector-thickness)] w-[calc(100%+0.5rem)]\"\n },\n {\n orientation: \"vertical\",\n variant: \"alternate\",\n isAlternateRight: false,\n class: \"top-2 -right-[calc(var(--timeline-connector-thickness)/2)] h-full w-[var(--timeline-connector-thickness)]\"\n },\n {\n orientation: \"vertical\",\n variant: \"alternate\",\n isAlternateRight: true,\n class: \"top-2 -left-[calc(var(--timeline-connector-thickness)/2)] h-full w-[var(--timeline-connector-thickness)]\"\n },\n {\n orientation: \"horizontal\",\n variant: \"alternate\",\n class:\n \"top-[calc(var(--timeline-dot-size)/2-var(--timeline-connector-thickness)/2)] left-3 row-start-2 h-[var(--timeline-connector-thickness)] w-[calc(100%+0.5rem)]\"\n }\n ],\n defaultVariants: {\n isCompleted: false,\n orientation: \"vertical\",\n variant: \"default\",\n isAlternateRight: false\n }\n});\n\ninterface TimelineConnectorProps extends DivProps {\n forceMount?: boolean;\n}\n\nfunction TimelineConnector(props: TimelineConnectorProps) {\n const { asChild, forceMount, className, ...connectorProps } = props;\n\n const { orientation, variant, activeIndex } = useTimelineContext(CONNECTOR_NAME);\n const { id, status, isAlternateRight } = useTimelineItemContext(CONNECTOR_NAME);\n\n const nextItemStatus = useStore((state) => state.getNextItemStatus(id, activeIndex));\n\n const isLastItem = nextItemStatus === undefined;\n\n if (!forceMount && isLastItem) return null;\n\n const isConnectorCompleted = nextItemStatus === \"completed\" || nextItemStatus === \"active\";\n\n const ConnectorPrimitive = asChild ? Slot : \"div\";\n\n return (\n <ConnectorPrimitive\n aria-hidden=\"true\"\n data-slot=\"timeline-connector\"\n data-completed={isConnectorCompleted ? \"\" : undefined}\n data-status={status}\n data-orientation={orientation}\n {...connectorProps}\n className={cn(\n timelineConnectorVariants({\n isCompleted: isConnectorCompleted,\n orientation,\n variant,\n isAlternateRight,\n className\n })\n )}\n />\n );\n}\n\nfunction TimelineHeader(props: DivProps) {\n const { asChild, className, ...headerProps } = props;\n\n const HeaderPrimitive = asChild ? Slot : \"div\";\n\n return (\n <HeaderPrimitive data-slot=\"timeline-header\" {...headerProps} className={cn(\"flex flex-col gap-1\", className)} />\n );\n}\n\nfunction TimelineTitle(props: DivProps) {\n const { asChild, className, ...titleProps } = props;\n\n const TitlePrimitive = asChild ? Slot : \"div\";\n\n return (\n <TitlePrimitive\n data-slot=\"timeline-title\"\n {...titleProps}\n className={cn(\"leading-none font-semibold\", className)}\n />\n );\n}\n\nfunction TimelineDescription(props: DivProps) {\n const { asChild, className, ...descriptionProps } = props;\n\n const DescriptionPrimitive = asChild ? Slot : \"div\";\n\n return (\n <DescriptionPrimitive\n data-slot=\"timeline-description\"\n {...descriptionProps}\n className={cn(\"text-sm text-muted-foreground\", className)}\n />\n );\n}\n\ninterface TimelineTimeProps extends React.ComponentProps<\"time\"> {\n asChild?: boolean;\n}\n\n/** Mốc thời gian — gói KHÔNG tự định dạng ngày: app truyền chuỗi đã định dạng + `dateTime` máy đọc. */\nfunction TimelineTime(props: TimelineTimeProps) {\n const { asChild, className, ...timeProps } = props;\n\n const TimePrimitive = asChild ? Slot : \"time\";\n\n return (\n <TimePrimitive\n data-slot=\"timeline-time\"\n {...timeProps}\n className={cn(\"text-xs text-muted-foreground\", className)}\n />\n );\n}\n\nexport {\n Timeline,\n TimelineConnector,\n type TimelineConnectorProps,\n TimelineContent,\n TimelineDescription,\n TimelineDot,\n TimelineHeader,\n TimelineItem,\n type TimelineProps,\n TimelineTime,\n type TimelineTimeProps,\n TimelineTitle\n};\n"],"mappings":"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","names":["activeIndex"]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nhãn mặc định của `Tour`.
|
|
3
|
+
*
|
|
4
|
+
* Module **CỐ Ý không có `"use client"`**: mọi export của một file `"use client"` tới Server
|
|
5
|
+
* Component là client-reference proxy, đọc ra `undefined` mà KHÔNG throw — xem
|
|
6
|
+
* `src/chart/chart-colors.ts`. Barrel xuất module này TRỰC TIẾP; `tour.tsx` chỉ `import`.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Chuỗi hiển thị và chuỗi a11y của `Tour` — truyền `labels` cho `Tour` để ghi đè từng khoá.
|
|
10
|
+
*
|
|
11
|
+
* ⚠ Thượng nguồn (Dice UI) chôn cứng chuỗi tiếng Anh trong từng nút ("Previous", "Next",
|
|
12
|
+
* "Finish", "Skip", "Close tour") và bộ đếm `1 / 5`. Gói dùng chung cho MỌI app Comitor nên
|
|
13
|
+
* chuỗi cứng là ngõ cụt cho app đa ngôn ngữ.
|
|
14
|
+
*
|
|
15
|
+
* Từng chỗ vẫn ghi đè riêng được: `children` của `TourPrev`/`TourNext`/`TourSkip`/`TourClose`,
|
|
16
|
+
* `aria-label` của `TourClose`, `format` của `TourStepCounter`. `labels` là để đổi NGÔN NGỮ một
|
|
17
|
+
* lần cho cả tour.
|
|
18
|
+
*/
|
|
19
|
+
interface TourLabels {
|
|
20
|
+
/** `aria-label` của `TourClose` — nút chỉ có icon X, nên đây là tên DUY NHẤT của nó. */
|
|
21
|
+
close: string;
|
|
22
|
+
/** Chữ mặc định của `TourPrev` khi không có `children`. */
|
|
23
|
+
prev: string;
|
|
24
|
+
/** Chữ mặc định của `TourNext` ở mọi bước trừ bước cuối. */
|
|
25
|
+
next: string;
|
|
26
|
+
/** Chữ mặc định của `TourNext` ở bước CUỐI — bấm là kết thúc tour. */
|
|
27
|
+
finish: string;
|
|
28
|
+
/** Chữ mặc định của `TourSkip` khi không có `children`. */
|
|
29
|
+
skip: string;
|
|
30
|
+
/**
|
|
31
|
+
* Nội dung mặc định của `TourStepCounter`. `current` đếm từ 1.
|
|
32
|
+
* Là HÀM vì có số chèn vào giữa câu.
|
|
33
|
+
*/
|
|
34
|
+
stepCounter: (current: number, total: number) => string;
|
|
35
|
+
}
|
|
36
|
+
/** Nhãn tiếng Việt mặc định — mọi khoá đều ghi đè được qua prop `labels` của `Tour`. */
|
|
37
|
+
declare const DEFAULT_TOUR_LABELS: TourLabels;
|
|
38
|
+
|
|
39
|
+
export { DEFAULT_TOUR_LABELS, type TourLabels };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const DEFAULT_TOUR_LABELS = {
|
|
2
|
+
close: "\u0110\xF3ng h\u01B0\u1EDBng d\u1EABn",
|
|
3
|
+
prev: "Quay l\u1EA1i",
|
|
4
|
+
next: "Ti\u1EBFp theo",
|
|
5
|
+
finish: "Ho\xE0n t\u1EA5t",
|
|
6
|
+
skip: "B\u1ECF qua",
|
|
7
|
+
stepCounter: (current, total) => `B\u01B0\u1EDBc ${current}/${total}`
|
|
8
|
+
};
|
|
9
|
+
export {
|
|
10
|
+
DEFAULT_TOUR_LABELS
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=tour-constants.js.map
|