@astra-kit/ui 1.1.0 → 1.2.1
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/CHANGELOG.md +25 -0
- package/README.md +5 -3
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/dist/components/empty-state.d.ts +20 -0
- package/dist/components/empty-state.js +357 -0
- package/dist/components/metric-card.d.ts +22 -0
- package/dist/components/metric-card.js +378 -0
- package/dist/components/stepper.d.ts +39 -0
- package/dist/components/stepper.js +693 -0
- package/dist/components/timeline.d.ts +51 -0
- package/dist/components/timeline.js +1013 -0
- package/dist/components/usage-meter.d.ts +39 -0
- package/dist/components/usage-meter.js +1093 -0
- package/dist/constants/empty-state.d.ts +12 -0
- package/dist/constants/empty-state.js +12 -0
- package/dist/constants/metric-card.d.ts +12 -0
- package/dist/constants/metric-card.js +12 -0
- package/dist/constants/stepper.d.ts +52 -0
- package/dist/constants/stepper.js +23 -0
- package/dist/constants/timeline.d.ts +62 -0
- package/dist/constants/timeline.js +31 -0
- package/dist/constants/usage-meter.d.ts +100 -0
- package/dist/constants/usage-meter.js +38 -0
- package/dist/hooks/use-has-entered-view.d.ts +9 -0
- package/dist/hooks/use-has-entered-view.js +46 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/motion/arc-odometer.d.ts +53 -0
- package/dist/motion/arc-odometer.js +424 -0
- package/dist/motion/arc-text-swap.d.ts +22 -0
- package/dist/motion/arc-text-swap.js +142 -0
- package/dist/motion/arc-tokens.d.ts +1 -0
- package/dist/motion/arc-tokens.js +2 -1
- package/dist/styles.css +1 -1
- package/package.json +26 -1
|
@@ -0,0 +1,1013 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { c as _c } from "react/compiler-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* Arc's Timeline (https://uiarc.dev, MIT License, see THIRD_PARTY_NOTICES.md): follow what happened, newest first,
|
|
5
|
+
* grouped by day. Day labels stay pinned while their updates scroll, the connecting line draws itself as rows come into
|
|
6
|
+
* view, rows with details expand in place, and new updates slide in at the top while the rest glide down. Arrow keys
|
|
7
|
+
* move between expandable rows; Enter or Space expands one.
|
|
8
|
+
*/
|
|
9
|
+
import { createContext, useCallback, useContext, useEffect, useId, useMemo, useRef, useState } from "react";
|
|
10
|
+
import { AnimatePresence, motion } from "motion/react";
|
|
11
|
+
import { ChevronDown } from "lucide-react";
|
|
12
|
+
import { TIMELINE_AVATAR_SIZE_PX, TIMELINE_DEFAULT_HEADING_LEVEL, TIMELINE_DEFAULT_LOCALE, TIMELINE_DEFAULT_MESSAGES, TIMELINE_DEFAULT_TIME_ZONE, TIMELINE_FRESH_ROW_DELAY_S, TIMELINE_RELATIVE_WINDOW_HOURS, TIMELINE_REVEAL_AMOUNT, TIMELINE_REVEAL_QUEUE_LIMIT_S, TIMELINE_REVEAL_STEP_S } from "../constants/timeline.js";
|
|
13
|
+
import { useIsHydrated } from "../hooks/use-is-hydrated.js";
|
|
14
|
+
import { usePrefersReducedMotion } from "../hooks/use-prefers-reduced-motion.js";
|
|
15
|
+
import { cn } from "../lib/utils.js";
|
|
16
|
+
import { ArcTextSwap } from "../motion/arc-text-swap.js";
|
|
17
|
+
import { ARC_BLUR, ARC_DURATION, ARC_EASE, ARC_SPRING_MORPH, ARC_SPRING_SMOOTH, ARC_SPRING_SNAPPY } from "../motion/arc-tokens.js";
|
|
18
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
19
|
+
const MILLISECONDS_PER_MINUTE = 6e4;
|
|
20
|
+
const MINUTES_PER_HOUR = 60;
|
|
21
|
+
const MILLISECONDS_PER_HOUR = MILLISECONDS_PER_MINUTE * MINUTES_PER_HOUR;
|
|
22
|
+
/** Length of a `YYYY-MM-DD` day key, the start of an ISO timestamp. */
|
|
23
|
+
const DAY_KEY_LENGTH = 10;
|
|
24
|
+
const MILLISECONDS_PER_SECOND = 1e3;
|
|
25
|
+
const enterEase = [...ARC_EASE.enter];
|
|
26
|
+
const standardEase = [...ARC_EASE.standard];
|
|
27
|
+
/** Quick fades that replace travel with reduced motion. */
|
|
28
|
+
const REDUCED_FADE_S = .15;
|
|
29
|
+
const REDUCED_EXIT_S = .1;
|
|
30
|
+
/** Delays, in seconds, between a row's marker popping and its line drawing, and before new content fades in. */
|
|
31
|
+
const LINE_DRAW_DELAY_S = .1;
|
|
32
|
+
const CONTENT_FADE_DELAY_S = .05;
|
|
33
|
+
const DETAIL_FADE_DELAY_S = .06;
|
|
34
|
+
/** Visual durations, in seconds, of the marker pop, the line draw, a row leaving and details collapsing. */
|
|
35
|
+
const MARKER_POP_DURATION_S = .36;
|
|
36
|
+
const MARKER_POP_BOUNCE = .32;
|
|
37
|
+
const LINE_DRAW_DURATION_S = .34;
|
|
38
|
+
const ROW_EXIT_DURATION_S = .3;
|
|
39
|
+
const DETAIL_COLLAPSE_DURATION_S = .28;
|
|
40
|
+
/** Travel, in pixels, of a new row's content and of expanded details settling in. */
|
|
41
|
+
const FRESH_CONTENT_OFFSET_PX = -10;
|
|
42
|
+
const DETAIL_OFFSET_PX = -4;
|
|
43
|
+
/** Scale a marker pops from. */
|
|
44
|
+
const MARKER_ENTER_SCALE = .4;
|
|
45
|
+
const CHEVRON_SIZE_PX = 16;
|
|
46
|
+
const HALF_TURN_DEG = 180;
|
|
47
|
+
const RevealClockContext = createContext(null);
|
|
48
|
+
/**
|
|
49
|
+
* Keys a time by its calendar day in a time zone.
|
|
50
|
+
* @param time - Epoch milliseconds.
|
|
51
|
+
* @param timeZone - Time zone of the day groups.
|
|
52
|
+
* @returns The day as `YYYY-MM-DD`.
|
|
53
|
+
*/
|
|
54
|
+
function dayKey(time, timeZone) {
|
|
55
|
+
const parts = new Intl.DateTimeFormat("en-CA", {
|
|
56
|
+
timeZone,
|
|
57
|
+
year: "numeric",
|
|
58
|
+
month: "2-digit",
|
|
59
|
+
day: "2-digit"
|
|
60
|
+
}).formatToParts(new Date(time));
|
|
61
|
+
const get = (type) => parts.find((part) => part.type === type)?.value ?? "";
|
|
62
|
+
return `${get("year")}-${get("month")}-${get("day")}`;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The calendar day before a day key. It works on the date itself, so a daylight-saving change, where a day lasts 23 or
|
|
66
|
+
* 25 hours, never skips or repeats a day.
|
|
67
|
+
* @param day - A day as `YYYY-MM-DD`.
|
|
68
|
+
* @returns The previous day as `YYYY-MM-DD`.
|
|
69
|
+
*/
|
|
70
|
+
function previousDayKey(day) {
|
|
71
|
+
const [year, month, date] = day.split("-").map(Number);
|
|
72
|
+
return new Date(Date.UTC(year, month - 1, date - 1)).toISOString().slice(0, DAY_KEY_LENGTH);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Relative time of a recent event, short for the row and long for screen readers.
|
|
76
|
+
* @param time - When the event happened, in epoch milliseconds.
|
|
77
|
+
* @param now - Reference time, in epoch milliseconds.
|
|
78
|
+
* @param copy - Timeline copy.
|
|
79
|
+
* @returns Short and long relative times.
|
|
80
|
+
*/
|
|
81
|
+
function relativeTime(time, now, copy) {
|
|
82
|
+
const minutes = Math.max(0, Math.floor((now - time) / MILLISECONDS_PER_MINUTE));
|
|
83
|
+
if (minutes < 1) return {
|
|
84
|
+
short: copy.justNowShort,
|
|
85
|
+
long: copy.justNowLong
|
|
86
|
+
};
|
|
87
|
+
if (minutes < MINUTES_PER_HOUR) return {
|
|
88
|
+
short: copy.minutesShort(minutes),
|
|
89
|
+
long: copy.minutesLong(minutes)
|
|
90
|
+
};
|
|
91
|
+
const hours = Math.floor(minutes / MINUTES_PER_HOUR);
|
|
92
|
+
return {
|
|
93
|
+
short: copy.hoursShort(hours),
|
|
94
|
+
long: copy.hoursLong(hours)
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/** The day's update count rolls digit by digit in the direction it moved. */
|
|
98
|
+
function RollingCount(t0) {
|
|
99
|
+
const $ = _c(8);
|
|
100
|
+
const { value, reduceMotion } = t0;
|
|
101
|
+
let t1;
|
|
102
|
+
if ($[0] !== value) {
|
|
103
|
+
t1 = {
|
|
104
|
+
value,
|
|
105
|
+
direction: 1
|
|
106
|
+
};
|
|
107
|
+
$[0] = value;
|
|
108
|
+
$[1] = t1;
|
|
109
|
+
} else {
|
|
110
|
+
t1 = $[1];
|
|
111
|
+
}
|
|
112
|
+
const [state, setState] = useState(t1);
|
|
113
|
+
if (state.value !== value) {
|
|
114
|
+
setState({
|
|
115
|
+
value,
|
|
116
|
+
direction: value > state.value ? 1 : -1
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
const t2 = String(value);
|
|
120
|
+
let t3;
|
|
121
|
+
if ($[2] !== t2) {
|
|
122
|
+
t3 = [...t2];
|
|
123
|
+
$[2] = t2;
|
|
124
|
+
$[3] = t3;
|
|
125
|
+
} else {
|
|
126
|
+
t3 = $[3];
|
|
127
|
+
}
|
|
128
|
+
const characters = t3;
|
|
129
|
+
let t4;
|
|
130
|
+
if ($[4] !== characters || $[5] !== reduceMotion || $[6] !== state.direction) {
|
|
131
|
+
t4 = /* @__PURE__ */ _jsx("span", {
|
|
132
|
+
"aria-hidden": "true",
|
|
133
|
+
className: "inline-flex",
|
|
134
|
+
children: characters.map((character, index) => /* @__PURE__ */ _jsx("span", {
|
|
135
|
+
className: "relative inline-flex overflow-clip [overflow-clip-margin:.15em]",
|
|
136
|
+
children: /* @__PURE__ */ _jsx(ArcTextSwap, {
|
|
137
|
+
text: character,
|
|
138
|
+
reduceMotion,
|
|
139
|
+
direction: state.direction,
|
|
140
|
+
className: "inline-flex",
|
|
141
|
+
lineClassName: "inline-block whitespace-nowrap"
|
|
142
|
+
})
|
|
143
|
+
}, characters.length - index))
|
|
144
|
+
});
|
|
145
|
+
$[4] = characters;
|
|
146
|
+
$[5] = reduceMotion;
|
|
147
|
+
$[6] = state.direction;
|
|
148
|
+
$[7] = t4;
|
|
149
|
+
} else {
|
|
150
|
+
t4 = $[7];
|
|
151
|
+
}
|
|
152
|
+
return t4;
|
|
153
|
+
}
|
|
154
|
+
/** A day's section. A new day opens from nothing and pushes the older days down; it clips only while it grows. */
|
|
155
|
+
function DaySection(t0) {
|
|
156
|
+
const $ = _c(17);
|
|
157
|
+
const { labelId, isFresh, reduceMotion, children } = t0;
|
|
158
|
+
const [isEntering, setIsEntering] = useState(isFresh);
|
|
159
|
+
const t1 = isEntering && "overflow-clip";
|
|
160
|
+
let t2;
|
|
161
|
+
if ($[0] !== t1) {
|
|
162
|
+
t2 = cn("relative", t1);
|
|
163
|
+
$[0] = t1;
|
|
164
|
+
$[1] = t2;
|
|
165
|
+
} else {
|
|
166
|
+
t2 = $[1];
|
|
167
|
+
}
|
|
168
|
+
let t3;
|
|
169
|
+
if ($[2] !== isFresh) {
|
|
170
|
+
t3 = isFresh ? {
|
|
171
|
+
height: 0,
|
|
172
|
+
opacity: 0
|
|
173
|
+
} : false;
|
|
174
|
+
$[2] = isFresh;
|
|
175
|
+
$[3] = t3;
|
|
176
|
+
} else {
|
|
177
|
+
t3 = $[3];
|
|
178
|
+
}
|
|
179
|
+
let t4;
|
|
180
|
+
if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
|
|
181
|
+
t4 = {
|
|
182
|
+
height: "auto",
|
|
183
|
+
opacity: 1
|
|
184
|
+
};
|
|
185
|
+
$[4] = t4;
|
|
186
|
+
} else {
|
|
187
|
+
t4 = $[4];
|
|
188
|
+
}
|
|
189
|
+
const t5 = reduceMotion ? ARC_DURATION.instant : ARC_DURATION.exit;
|
|
190
|
+
let t6;
|
|
191
|
+
if ($[5] !== t5) {
|
|
192
|
+
t6 = {
|
|
193
|
+
height: 0,
|
|
194
|
+
opacity: 0,
|
|
195
|
+
transition: {
|
|
196
|
+
duration: t5,
|
|
197
|
+
ease: standardEase
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
$[5] = t5;
|
|
201
|
+
$[6] = t6;
|
|
202
|
+
} else {
|
|
203
|
+
t6 = $[6];
|
|
204
|
+
}
|
|
205
|
+
let t7;
|
|
206
|
+
if ($[7] !== reduceMotion) {
|
|
207
|
+
t7 = reduceMotion ? { duration: REDUCED_FADE_S } : {
|
|
208
|
+
height: ARC_SPRING_SMOOTH,
|
|
209
|
+
opacity: {
|
|
210
|
+
duration: ARC_DURATION.standard,
|
|
211
|
+
ease: enterEase
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
$[7] = reduceMotion;
|
|
215
|
+
$[8] = t7;
|
|
216
|
+
} else {
|
|
217
|
+
t7 = $[8];
|
|
218
|
+
}
|
|
219
|
+
let t8;
|
|
220
|
+
if ($[9] === Symbol.for("react.memo_cache_sentinel")) {
|
|
221
|
+
t8 = () => setIsEntering(false);
|
|
222
|
+
$[9] = t8;
|
|
223
|
+
} else {
|
|
224
|
+
t8 = $[9];
|
|
225
|
+
}
|
|
226
|
+
let t9;
|
|
227
|
+
if ($[10] !== children || $[11] !== labelId || $[12] !== t2 || $[13] !== t3 || $[14] !== t6 || $[15] !== t7) {
|
|
228
|
+
t9 = /* @__PURE__ */ _jsx(motion.section, {
|
|
229
|
+
"data-slot": "timeline-day",
|
|
230
|
+
"aria-labelledby": labelId,
|
|
231
|
+
className: t2,
|
|
232
|
+
initial: t3,
|
|
233
|
+
animate: t4,
|
|
234
|
+
exit: t6,
|
|
235
|
+
transition: t7,
|
|
236
|
+
onAnimationComplete: t8,
|
|
237
|
+
children
|
|
238
|
+
});
|
|
239
|
+
$[10] = children;
|
|
240
|
+
$[11] = labelId;
|
|
241
|
+
$[12] = t2;
|
|
242
|
+
$[13] = t3;
|
|
243
|
+
$[14] = t6;
|
|
244
|
+
$[15] = t7;
|
|
245
|
+
$[16] = t9;
|
|
246
|
+
} else {
|
|
247
|
+
t9 = $[16];
|
|
248
|
+
}
|
|
249
|
+
return t9;
|
|
250
|
+
}
|
|
251
|
+
/** Marker colours: a quiet node for system events, tinted by tone. Portraits sit on a hairline ring. */
|
|
252
|
+
const MARKER_TONE_CLASS_NAMES = {
|
|
253
|
+
neutral: "bg-surface",
|
|
254
|
+
success: "bg-[color-mix(in_oklab,var(--success)_12%,var(--surface))] text-success",
|
|
255
|
+
danger: "bg-[color-mix(in_oklab,var(--danger)_12%,var(--surface))] text-danger"
|
|
256
|
+
};
|
|
257
|
+
/** One update: a marker on the line, the copy and time, and details that expand in place. */
|
|
258
|
+
function TimelineRow(t0) {
|
|
259
|
+
const $ = _c(99);
|
|
260
|
+
const { row, isLast, isExpanded, onToggle, timeLabel, timeFull } = t0;
|
|
261
|
+
const clock = useContext(RevealClockContext);
|
|
262
|
+
const { reduceMotion } = clock;
|
|
263
|
+
const detailId = useId();
|
|
264
|
+
const [revealDelay, setRevealDelay] = useState(null);
|
|
265
|
+
let t1;
|
|
266
|
+
if ($[0] !== clock.fresh || $[1] !== row.id) {
|
|
267
|
+
t1 = clock.fresh.has(row.id);
|
|
268
|
+
$[0] = clock.fresh;
|
|
269
|
+
$[1] = row.id;
|
|
270
|
+
$[2] = t1;
|
|
271
|
+
} else {
|
|
272
|
+
t1 = $[2];
|
|
273
|
+
}
|
|
274
|
+
const isFresh = t1;
|
|
275
|
+
const [isEntering, setIsEntering] = useState(isFresh);
|
|
276
|
+
const isShown = revealDelay !== null;
|
|
277
|
+
const delay = (revealDelay ?? 0) + (isFresh ? TIMELINE_FRESH_ROW_DELAY_S : 0);
|
|
278
|
+
let t2;
|
|
279
|
+
if ($[3] !== delay || $[4] !== reduceMotion) {
|
|
280
|
+
t2 = reduceMotion ? {
|
|
281
|
+
duration: 0,
|
|
282
|
+
opacity: { duration: REDUCED_FADE_S }
|
|
283
|
+
} : {
|
|
284
|
+
...ARC_SPRING_MORPH,
|
|
285
|
+
visualDuration: MARKER_POP_DURATION_S,
|
|
286
|
+
bounce: MARKER_POP_BOUNCE,
|
|
287
|
+
delay,
|
|
288
|
+
opacity: {
|
|
289
|
+
duration: ARC_DURATION.fast,
|
|
290
|
+
ease: enterEase,
|
|
291
|
+
delay
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
$[3] = delay;
|
|
295
|
+
$[4] = reduceMotion;
|
|
296
|
+
$[5] = t2;
|
|
297
|
+
} else {
|
|
298
|
+
t2 = $[5];
|
|
299
|
+
}
|
|
300
|
+
const pop = t2;
|
|
301
|
+
let t3;
|
|
302
|
+
if ($[6] !== delay || $[7] !== reduceMotion) {
|
|
303
|
+
t3 = reduceMotion ? { duration: 0 } : {
|
|
304
|
+
...ARC_SPRING_SMOOTH,
|
|
305
|
+
visualDuration: LINE_DRAW_DURATION_S,
|
|
306
|
+
delay: delay + LINE_DRAW_DELAY_S
|
|
307
|
+
};
|
|
308
|
+
$[6] = delay;
|
|
309
|
+
$[7] = reduceMotion;
|
|
310
|
+
$[8] = t3;
|
|
311
|
+
} else {
|
|
312
|
+
t3 = $[8];
|
|
313
|
+
}
|
|
314
|
+
const draw = t3;
|
|
315
|
+
const tone = row.avatar ? undefined : row.tone ?? "neutral";
|
|
316
|
+
let t4;
|
|
317
|
+
if ($[9] !== row.actor) {
|
|
318
|
+
t4 = row.actor ? /* @__PURE__ */ _jsx("span", {
|
|
319
|
+
className: "font-medium text-foreground",
|
|
320
|
+
children: row.actor
|
|
321
|
+
}) : null;
|
|
322
|
+
$[9] = row.actor;
|
|
323
|
+
$[10] = t4;
|
|
324
|
+
} else {
|
|
325
|
+
t4 = $[10];
|
|
326
|
+
}
|
|
327
|
+
const t5 = row.actor ? " " : "";
|
|
328
|
+
let t6;
|
|
329
|
+
if ($[11] !== row.title || $[12] !== t4 || $[13] !== t5) {
|
|
330
|
+
t6 = /* @__PURE__ */ _jsxs("span", {
|
|
331
|
+
"data-slot": "timeline-title",
|
|
332
|
+
className: "text-sm leading-5 text-text-secondary",
|
|
333
|
+
children: [
|
|
334
|
+
t4,
|
|
335
|
+
t5,
|
|
336
|
+
row.title
|
|
337
|
+
]
|
|
338
|
+
});
|
|
339
|
+
$[11] = row.title;
|
|
340
|
+
$[12] = t4;
|
|
341
|
+
$[13] = t5;
|
|
342
|
+
$[14] = t6;
|
|
343
|
+
} else {
|
|
344
|
+
t6 = $[14];
|
|
345
|
+
}
|
|
346
|
+
let t7;
|
|
347
|
+
if ($[15] !== row.meta) {
|
|
348
|
+
t7 = row.meta ? /* @__PURE__ */ _jsx("span", {
|
|
349
|
+
className: "text-xs leading-(--leading-body) text-text-muted",
|
|
350
|
+
children: row.meta
|
|
351
|
+
}) : null;
|
|
352
|
+
$[15] = row.meta;
|
|
353
|
+
$[16] = t7;
|
|
354
|
+
} else {
|
|
355
|
+
t7 = $[16];
|
|
356
|
+
}
|
|
357
|
+
let t8;
|
|
358
|
+
if ($[17] !== t6 || $[18] !== t7) {
|
|
359
|
+
t8 = /* @__PURE__ */ _jsxs("span", {
|
|
360
|
+
className: "grid min-w-0 gap-0.5",
|
|
361
|
+
children: [t6, t7]
|
|
362
|
+
});
|
|
363
|
+
$[17] = t6;
|
|
364
|
+
$[18] = t7;
|
|
365
|
+
$[19] = t8;
|
|
366
|
+
} else {
|
|
367
|
+
t8 = $[19];
|
|
368
|
+
}
|
|
369
|
+
let t9;
|
|
370
|
+
if ($[20] !== row.time) {
|
|
371
|
+
t9 = new Date(row.time).toISOString();
|
|
372
|
+
$[20] = row.time;
|
|
373
|
+
$[21] = t9;
|
|
374
|
+
} else {
|
|
375
|
+
t9 = $[21];
|
|
376
|
+
}
|
|
377
|
+
let t10;
|
|
378
|
+
if ($[22] !== reduceMotion || $[23] !== timeLabel) {
|
|
379
|
+
t10 = /* @__PURE__ */ _jsx("span", {
|
|
380
|
+
"aria-hidden": "true",
|
|
381
|
+
children: /* @__PURE__ */ _jsx(ArcTextSwap, {
|
|
382
|
+
text: timeLabel,
|
|
383
|
+
reduceMotion,
|
|
384
|
+
className: "inline-flex",
|
|
385
|
+
lineClassName: "inline-block whitespace-nowrap"
|
|
386
|
+
})
|
|
387
|
+
});
|
|
388
|
+
$[22] = reduceMotion;
|
|
389
|
+
$[23] = timeLabel;
|
|
390
|
+
$[24] = t10;
|
|
391
|
+
} else {
|
|
392
|
+
t10 = $[24];
|
|
393
|
+
}
|
|
394
|
+
let t11;
|
|
395
|
+
if ($[25] !== timeFull) {
|
|
396
|
+
t11 = /* @__PURE__ */ _jsx("span", {
|
|
397
|
+
className: "sr-only",
|
|
398
|
+
children: timeFull
|
|
399
|
+
});
|
|
400
|
+
$[25] = timeFull;
|
|
401
|
+
$[26] = t11;
|
|
402
|
+
} else {
|
|
403
|
+
t11 = $[26];
|
|
404
|
+
}
|
|
405
|
+
let t12;
|
|
406
|
+
if ($[27] !== t10 || $[28] !== t11 || $[29] !== t9 || $[30] !== timeFull) {
|
|
407
|
+
t12 = /* @__PURE__ */ _jsxs("time", {
|
|
408
|
+
"data-slot": "timeline-time",
|
|
409
|
+
dateTime: t9,
|
|
410
|
+
title: timeFull,
|
|
411
|
+
className: "flex min-w-11 justify-end text-xs leading-5 whitespace-nowrap text-text-muted tabular-nums",
|
|
412
|
+
children: [t10, t11]
|
|
413
|
+
});
|
|
414
|
+
$[27] = t10;
|
|
415
|
+
$[28] = t11;
|
|
416
|
+
$[29] = t9;
|
|
417
|
+
$[30] = timeFull;
|
|
418
|
+
$[31] = t12;
|
|
419
|
+
} else {
|
|
420
|
+
t12 = $[31];
|
|
421
|
+
}
|
|
422
|
+
let t13;
|
|
423
|
+
if ($[32] !== t12 || $[33] !== t8) {
|
|
424
|
+
t13 = /* @__PURE__ */ _jsxs(_Fragment, { children: [t8, t12] });
|
|
425
|
+
$[32] = t12;
|
|
426
|
+
$[33] = t8;
|
|
427
|
+
$[34] = t13;
|
|
428
|
+
} else {
|
|
429
|
+
t13 = $[34];
|
|
430
|
+
}
|
|
431
|
+
const content = t13;
|
|
432
|
+
const t14 = isEntering && "overflow-clip";
|
|
433
|
+
let t15;
|
|
434
|
+
if ($[35] !== t14) {
|
|
435
|
+
t15 = cn("relative pl-[calc(var(--marker)+var(--space-2))]", t14);
|
|
436
|
+
$[35] = t14;
|
|
437
|
+
$[36] = t15;
|
|
438
|
+
} else {
|
|
439
|
+
t15 = $[36];
|
|
440
|
+
}
|
|
441
|
+
let t16;
|
|
442
|
+
if ($[37] !== isFresh) {
|
|
443
|
+
t16 = isFresh ? { height: 0 } : false;
|
|
444
|
+
$[37] = isFresh;
|
|
445
|
+
$[38] = t16;
|
|
446
|
+
} else {
|
|
447
|
+
t16 = $[38];
|
|
448
|
+
}
|
|
449
|
+
let t17;
|
|
450
|
+
if ($[39] === Symbol.for("react.memo_cache_sentinel")) {
|
|
451
|
+
t17 = { height: "auto" };
|
|
452
|
+
$[39] = t17;
|
|
453
|
+
} else {
|
|
454
|
+
t17 = $[39];
|
|
455
|
+
}
|
|
456
|
+
let t18;
|
|
457
|
+
let t19;
|
|
458
|
+
if ($[40] !== reduceMotion) {
|
|
459
|
+
t18 = {
|
|
460
|
+
height: 0,
|
|
461
|
+
opacity: 0,
|
|
462
|
+
transition: reduceMotion ? { duration: REDUCED_EXIT_S } : {
|
|
463
|
+
height: {
|
|
464
|
+
...ARC_SPRING_SMOOTH,
|
|
465
|
+
visualDuration: ROW_EXIT_DURATION_S
|
|
466
|
+
},
|
|
467
|
+
opacity: { duration: ARC_DURATION.instant }
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
t19 = reduceMotion ? { duration: 0 } : ARC_SPRING_SMOOTH;
|
|
471
|
+
$[40] = reduceMotion;
|
|
472
|
+
$[41] = t18;
|
|
473
|
+
$[42] = t19;
|
|
474
|
+
} else {
|
|
475
|
+
t18 = $[41];
|
|
476
|
+
t19 = $[42];
|
|
477
|
+
}
|
|
478
|
+
let t20;
|
|
479
|
+
if ($[43] === Symbol.for("react.memo_cache_sentinel")) {
|
|
480
|
+
t20 = () => setIsEntering(false);
|
|
481
|
+
$[43] = t20;
|
|
482
|
+
} else {
|
|
483
|
+
t20 = $[43];
|
|
484
|
+
}
|
|
485
|
+
let t21;
|
|
486
|
+
if ($[44] !== clock) {
|
|
487
|
+
t21 = () => setRevealDelay((current) => current ?? clock.schedule());
|
|
488
|
+
$[44] = clock;
|
|
489
|
+
$[45] = t21;
|
|
490
|
+
} else {
|
|
491
|
+
t21 = $[45];
|
|
492
|
+
}
|
|
493
|
+
let t22;
|
|
494
|
+
if ($[46] === Symbol.for("react.memo_cache_sentinel")) {
|
|
495
|
+
t22 = {
|
|
496
|
+
once: true,
|
|
497
|
+
amount: TIMELINE_REVEAL_AMOUNT
|
|
498
|
+
};
|
|
499
|
+
$[46] = t22;
|
|
500
|
+
} else {
|
|
501
|
+
t22 = $[46];
|
|
502
|
+
}
|
|
503
|
+
const t23 = tone && MARKER_TONE_CLASS_NAMES[tone];
|
|
504
|
+
let t24;
|
|
505
|
+
if ($[47] !== t23) {
|
|
506
|
+
t24 = cn("absolute top-(--marker-top) left-0 z-1 grid size-(--marker) place-items-center rounded-(--radius-pill) text-text-secondary after:pointer-events-none after:absolute after:inset-0 after:rounded-[inherit] after:border after:border-[color-mix(in_oklab,var(--foreground)_10%,transparent)] after:content-[''] [&_svg]:size-3.5 [&_svg]:stroke-2", t23);
|
|
507
|
+
$[47] = t23;
|
|
508
|
+
$[48] = t24;
|
|
509
|
+
} else {
|
|
510
|
+
t24 = $[48];
|
|
511
|
+
}
|
|
512
|
+
let t25;
|
|
513
|
+
if ($[49] === Symbol.for("react.memo_cache_sentinel")) {
|
|
514
|
+
t25 = {
|
|
515
|
+
scale: MARKER_ENTER_SCALE,
|
|
516
|
+
opacity: 0
|
|
517
|
+
};
|
|
518
|
+
$[49] = t25;
|
|
519
|
+
} else {
|
|
520
|
+
t25 = $[49];
|
|
521
|
+
}
|
|
522
|
+
let t26;
|
|
523
|
+
if ($[50] !== isShown) {
|
|
524
|
+
t26 = isShown ? {
|
|
525
|
+
scale: 1,
|
|
526
|
+
opacity: 1
|
|
527
|
+
} : undefined;
|
|
528
|
+
$[50] = isShown;
|
|
529
|
+
$[51] = t26;
|
|
530
|
+
} else {
|
|
531
|
+
t26 = $[51];
|
|
532
|
+
}
|
|
533
|
+
let t27;
|
|
534
|
+
if ($[52] !== row.avatar || $[53] !== row.icon) {
|
|
535
|
+
t27 = row.avatar ? /* @__PURE__ */ _jsx("img", {
|
|
536
|
+
src: row.avatar,
|
|
537
|
+
alt: "",
|
|
538
|
+
width: TIMELINE_AVATAR_SIZE_PX,
|
|
539
|
+
height: TIMELINE_AVATAR_SIZE_PX,
|
|
540
|
+
decoding: "async",
|
|
541
|
+
className: "block size-full rounded-[inherit] object-cover"
|
|
542
|
+
}) : row.icon;
|
|
543
|
+
$[52] = row.avatar;
|
|
544
|
+
$[53] = row.icon;
|
|
545
|
+
$[54] = t27;
|
|
546
|
+
} else {
|
|
547
|
+
t27 = $[54];
|
|
548
|
+
}
|
|
549
|
+
let t28;
|
|
550
|
+
if ($[55] !== pop || $[56] !== t24 || $[57] !== t26 || $[58] !== t27 || $[59] !== tone) {
|
|
551
|
+
t28 = /* @__PURE__ */ _jsx(motion.span, {
|
|
552
|
+
"data-slot": "timeline-marker",
|
|
553
|
+
"data-tone": tone,
|
|
554
|
+
"aria-hidden": "true",
|
|
555
|
+
className: t24,
|
|
556
|
+
initial: t25,
|
|
557
|
+
animate: t26,
|
|
558
|
+
transition: pop,
|
|
559
|
+
children: t27
|
|
560
|
+
});
|
|
561
|
+
$[55] = pop;
|
|
562
|
+
$[56] = t24;
|
|
563
|
+
$[57] = t26;
|
|
564
|
+
$[58] = t27;
|
|
565
|
+
$[59] = tone;
|
|
566
|
+
$[60] = t28;
|
|
567
|
+
} else {
|
|
568
|
+
t28 = $[60];
|
|
569
|
+
}
|
|
570
|
+
let t29;
|
|
571
|
+
if ($[61] !== draw || $[62] !== isLast || $[63] !== isShown) {
|
|
572
|
+
t29 = !isLast ? /* @__PURE__ */ _jsx(motion.span, {
|
|
573
|
+
"data-slot": "timeline-line",
|
|
574
|
+
"aria-hidden": "true",
|
|
575
|
+
className: "absolute top-[calc(var(--marker-top)+var(--marker)+var(--line-gap))] bottom-[calc(var(--line-gap)-var(--marker-top))] left-[calc(var(--marker)/2_-_.5px)] w-px origin-top bg-border-strong",
|
|
576
|
+
initial: { scaleY: 0 },
|
|
577
|
+
animate: isShown ? { scaleY: 1 } : undefined,
|
|
578
|
+
transition: draw
|
|
579
|
+
}) : null;
|
|
580
|
+
$[61] = draw;
|
|
581
|
+
$[62] = isLast;
|
|
582
|
+
$[63] = isShown;
|
|
583
|
+
$[64] = t29;
|
|
584
|
+
} else {
|
|
585
|
+
t29 = $[64];
|
|
586
|
+
}
|
|
587
|
+
let t30;
|
|
588
|
+
if ($[65] !== isFresh || $[66] !== reduceMotion) {
|
|
589
|
+
t30 = isFresh ? reduceMotion ? { opacity: 0 } : {
|
|
590
|
+
opacity: 0,
|
|
591
|
+
y: FRESH_CONTENT_OFFSET_PX,
|
|
592
|
+
filter: `blur(${ARC_BLUR.soft}px)`
|
|
593
|
+
} : false;
|
|
594
|
+
$[65] = isFresh;
|
|
595
|
+
$[66] = reduceMotion;
|
|
596
|
+
$[67] = t30;
|
|
597
|
+
} else {
|
|
598
|
+
t30 = $[67];
|
|
599
|
+
}
|
|
600
|
+
let t31;
|
|
601
|
+
if ($[68] === Symbol.for("react.memo_cache_sentinel")) {
|
|
602
|
+
t31 = {
|
|
603
|
+
opacity: 1,
|
|
604
|
+
y: 0,
|
|
605
|
+
filter: "blur(0px)"
|
|
606
|
+
};
|
|
607
|
+
$[68] = t31;
|
|
608
|
+
} else {
|
|
609
|
+
t31 = $[68];
|
|
610
|
+
}
|
|
611
|
+
let t32;
|
|
612
|
+
if ($[69] !== reduceMotion) {
|
|
613
|
+
t32 = reduceMotion ? { duration: REDUCED_FADE_S } : {
|
|
614
|
+
y: ARC_SPRING_SMOOTH,
|
|
615
|
+
opacity: {
|
|
616
|
+
duration: ARC_DURATION.standard,
|
|
617
|
+
ease: enterEase,
|
|
618
|
+
delay: CONTENT_FADE_DELAY_S
|
|
619
|
+
},
|
|
620
|
+
filter: {
|
|
621
|
+
duration: ARC_DURATION.standard,
|
|
622
|
+
ease: enterEase,
|
|
623
|
+
delay: CONTENT_FADE_DELAY_S
|
|
624
|
+
}
|
|
625
|
+
};
|
|
626
|
+
$[69] = reduceMotion;
|
|
627
|
+
$[70] = t32;
|
|
628
|
+
} else {
|
|
629
|
+
t32 = $[70];
|
|
630
|
+
}
|
|
631
|
+
let t33;
|
|
632
|
+
if ($[71] !== content || $[72] !== detailId || $[73] !== isExpanded || $[74] !== onToggle || $[75] !== reduceMotion || $[76] !== row.detail) {
|
|
633
|
+
t33 = row.detail ? /* @__PURE__ */ _jsxs("button", {
|
|
634
|
+
type: "button",
|
|
635
|
+
"data-timeline-trigger": "",
|
|
636
|
+
"aria-expanded": isExpanded,
|
|
637
|
+
"aria-controls": isExpanded ? detailId : undefined,
|
|
638
|
+
onClick: onToggle,
|
|
639
|
+
className: cn("grid w-full grid-cols-[minmax(0,1fr)_auto] items-start gap-x-(--space-3) rounded-(--radius-control) border-0 bg-transparent px-(--space-3) py-3 text-left [font:inherit] text-inherit [-webkit-tap-highlight-color:transparent] transition-[background-color] duration-(--duration-fast) ease-(--ease-standard) max-[420px]:gap-x-(--space-2) max-[420px]:px-(--space-2) motion-reduce:transition-none", "cursor-pointer grid-cols-[minmax(0,1fr)_auto_16px] hover-fine:bg-surface-muted active:bg-surface-muted"),
|
|
640
|
+
children: [content, /* @__PURE__ */ _jsx(motion.span, {
|
|
641
|
+
"aria-hidden": "true",
|
|
642
|
+
className: "grid h-5 place-items-center text-text-muted",
|
|
643
|
+
initial: false,
|
|
644
|
+
animate: { rotate: isExpanded ? HALF_TURN_DEG : 0 },
|
|
645
|
+
transition: reduceMotion ? { duration: 0 } : ARC_SPRING_SNAPPY,
|
|
646
|
+
children: /* @__PURE__ */ _jsx(ChevronDown, {
|
|
647
|
+
size: CHEVRON_SIZE_PX,
|
|
648
|
+
strokeWidth: 1.75
|
|
649
|
+
})
|
|
650
|
+
})]
|
|
651
|
+
}) : /* @__PURE__ */ _jsx("div", {
|
|
652
|
+
className: "grid w-full grid-cols-[minmax(0,1fr)_auto] items-start gap-x-(--space-3) rounded-(--radius-control) border-0 bg-transparent px-(--space-3) py-3 text-left [font:inherit] text-inherit [-webkit-tap-highlight-color:transparent] transition-[background-color] duration-(--duration-fast) ease-(--ease-standard) max-[420px]:gap-x-(--space-2) max-[420px]:px-(--space-2) motion-reduce:transition-none",
|
|
653
|
+
children: content
|
|
654
|
+
});
|
|
655
|
+
$[71] = content;
|
|
656
|
+
$[72] = detailId;
|
|
657
|
+
$[73] = isExpanded;
|
|
658
|
+
$[74] = onToggle;
|
|
659
|
+
$[75] = reduceMotion;
|
|
660
|
+
$[76] = row.detail;
|
|
661
|
+
$[77] = t33;
|
|
662
|
+
} else {
|
|
663
|
+
t33 = $[77];
|
|
664
|
+
}
|
|
665
|
+
let t34;
|
|
666
|
+
if ($[78] !== detailId || $[79] !== isExpanded || $[80] !== reduceMotion || $[81] !== row.detail) {
|
|
667
|
+
t34 = isExpanded && row.detail ? /* @__PURE__ */ _jsx(motion.div, {
|
|
668
|
+
id: detailId,
|
|
669
|
+
"data-slot": "timeline-detail",
|
|
670
|
+
className: "overflow-clip [overflow-clip-margin:4px]",
|
|
671
|
+
initial: { height: 0 },
|
|
672
|
+
animate: { height: "auto" },
|
|
673
|
+
exit: {
|
|
674
|
+
height: 0,
|
|
675
|
+
transition: reduceMotion ? { duration: 0 } : {
|
|
676
|
+
...ARC_SPRING_SMOOTH,
|
|
677
|
+
visualDuration: DETAIL_COLLAPSE_DURATION_S
|
|
678
|
+
}
|
|
679
|
+
},
|
|
680
|
+
transition: reduceMotion ? { duration: 0 } : ARC_SPRING_SMOOTH,
|
|
681
|
+
children: /* @__PURE__ */ _jsx(motion.div, {
|
|
682
|
+
className: "px-(--space-3) pb-(--space-4) text-sm leading-(--leading-body) text-text-secondary max-[420px]:px-(--space-2)",
|
|
683
|
+
initial: reduceMotion ? { opacity: 0 } : {
|
|
684
|
+
opacity: 0,
|
|
685
|
+
y: DETAIL_OFFSET_PX,
|
|
686
|
+
filter: `blur(${ARC_BLUR.soft}px)`
|
|
687
|
+
},
|
|
688
|
+
animate: {
|
|
689
|
+
opacity: 1,
|
|
690
|
+
y: 0,
|
|
691
|
+
filter: "blur(0px)"
|
|
692
|
+
},
|
|
693
|
+
exit: {
|
|
694
|
+
opacity: 0,
|
|
695
|
+
transition: {
|
|
696
|
+
duration: ARC_DURATION.instant,
|
|
697
|
+
ease: standardEase
|
|
698
|
+
}
|
|
699
|
+
},
|
|
700
|
+
transition: reduceMotion ? { duration: REDUCED_FADE_S } : {
|
|
701
|
+
duration: ARC_DURATION.standard,
|
|
702
|
+
ease: enterEase,
|
|
703
|
+
delay: DETAIL_FADE_DELAY_S
|
|
704
|
+
},
|
|
705
|
+
children: row.detail
|
|
706
|
+
})
|
|
707
|
+
}, "detail") : null;
|
|
708
|
+
$[78] = detailId;
|
|
709
|
+
$[79] = isExpanded;
|
|
710
|
+
$[80] = reduceMotion;
|
|
711
|
+
$[81] = row.detail;
|
|
712
|
+
$[82] = t34;
|
|
713
|
+
} else {
|
|
714
|
+
t34 = $[82];
|
|
715
|
+
}
|
|
716
|
+
let t35;
|
|
717
|
+
if ($[83] !== t34) {
|
|
718
|
+
t35 = /* @__PURE__ */ _jsx(AnimatePresence, {
|
|
719
|
+
initial: false,
|
|
720
|
+
children: t34
|
|
721
|
+
});
|
|
722
|
+
$[83] = t34;
|
|
723
|
+
$[84] = t35;
|
|
724
|
+
} else {
|
|
725
|
+
t35 = $[84];
|
|
726
|
+
}
|
|
727
|
+
let t36;
|
|
728
|
+
if ($[85] !== t30 || $[86] !== t32 || $[87] !== t33 || $[88] !== t35) {
|
|
729
|
+
t36 = /* @__PURE__ */ _jsxs(motion.div, {
|
|
730
|
+
className: "min-w-0",
|
|
731
|
+
initial: t30,
|
|
732
|
+
animate: t31,
|
|
733
|
+
transition: t32,
|
|
734
|
+
children: [t33, t35]
|
|
735
|
+
});
|
|
736
|
+
$[85] = t30;
|
|
737
|
+
$[86] = t32;
|
|
738
|
+
$[87] = t33;
|
|
739
|
+
$[88] = t35;
|
|
740
|
+
$[89] = t36;
|
|
741
|
+
} else {
|
|
742
|
+
t36 = $[89];
|
|
743
|
+
}
|
|
744
|
+
let t37;
|
|
745
|
+
if ($[90] !== t15 || $[91] !== t16 || $[92] !== t18 || $[93] !== t19 || $[94] !== t21 || $[95] !== t28 || $[96] !== t29 || $[97] !== t36) {
|
|
746
|
+
t37 = /* @__PURE__ */ _jsxs(motion.li, {
|
|
747
|
+
"data-slot": "timeline-item",
|
|
748
|
+
className: t15,
|
|
749
|
+
initial: t16,
|
|
750
|
+
animate: t17,
|
|
751
|
+
exit: t18,
|
|
752
|
+
transition: t19,
|
|
753
|
+
onAnimationComplete: t20,
|
|
754
|
+
onViewportEnter: t21,
|
|
755
|
+
viewport: t22,
|
|
756
|
+
children: [
|
|
757
|
+
t28,
|
|
758
|
+
t29,
|
|
759
|
+
t36
|
|
760
|
+
]
|
|
761
|
+
});
|
|
762
|
+
$[90] = t15;
|
|
763
|
+
$[91] = t16;
|
|
764
|
+
$[92] = t18;
|
|
765
|
+
$[93] = t19;
|
|
766
|
+
$[94] = t21;
|
|
767
|
+
$[95] = t28;
|
|
768
|
+
$[96] = t29;
|
|
769
|
+
$[97] = t36;
|
|
770
|
+
$[98] = t37;
|
|
771
|
+
} else {
|
|
772
|
+
t37 = $[98];
|
|
773
|
+
}
|
|
774
|
+
return t37;
|
|
775
|
+
}
|
|
776
|
+
/**
|
|
777
|
+
* A vertical activity feed grouped by day with pinned day labels and rows that expand in place.
|
|
778
|
+
* @param props - Events, reference time, accessible name, formatting, scrolling, copy and native div attributes.
|
|
779
|
+
* @returns The labelled feed region.
|
|
780
|
+
*/
|
|
781
|
+
function Timeline({ events, now, label, timeZone = TIMELINE_DEFAULT_TIME_ZONE, locale = TIMELINE_DEFAULT_LOCALE, maxHeight, scrollToNew = true, defaultExpanded = [], headingLevel = TIMELINE_DEFAULT_HEADING_LEVEL, messages, className, ...props }) {
|
|
782
|
+
const copy = {
|
|
783
|
+
...TIMELINE_DEFAULT_MESSAGES,
|
|
784
|
+
...messages
|
|
785
|
+
};
|
|
786
|
+
const prefersReducedMotion = usePrefersReducedMotion();
|
|
787
|
+
const isHydrated = useIsHydrated();
|
|
788
|
+
const reduceMotion = isHydrated && prefersReducedMotion;
|
|
789
|
+
const id = useId();
|
|
790
|
+
const scrollerRef = useRef(null);
|
|
791
|
+
const trackRef = useRef(null);
|
|
792
|
+
const [expanded, setExpanded] = useState(() => new Set(defaultExpanded));
|
|
793
|
+
const ids = events.map((event) => event.id).join("|");
|
|
794
|
+
const [known, setKnown] = useState(() => ({
|
|
795
|
+
ids,
|
|
796
|
+
set: new Set(events.map((event) => event.id)),
|
|
797
|
+
fresh: new Set(),
|
|
798
|
+
announcement: "",
|
|
799
|
+
announcementCount: 0
|
|
800
|
+
}));
|
|
801
|
+
if (known.ids !== ids) {
|
|
802
|
+
const added = events.filter((event) => !known.set.has(event.id));
|
|
803
|
+
setKnown({
|
|
804
|
+
ids,
|
|
805
|
+
set: new Set(events.map((event) => event.id)),
|
|
806
|
+
fresh: new Set([...known.fresh, ...added.map((event) => event.id)]),
|
|
807
|
+
announcement: added.length ? copy.newUpdates(added.map((event) => [event.actor, event.title].filter(Boolean).join(" ")).join(". ")) : known.announcement,
|
|
808
|
+
announcementCount: known.announcementCount + (added.length ? 1 : 0)
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
const scrolls = maxHeight !== undefined;
|
|
812
|
+
useEffect(() => {
|
|
813
|
+
const scroller = scrollerRef.current;
|
|
814
|
+
const track = trackRef.current;
|
|
815
|
+
if (!scrolls || !scroller || !track || typeof ResizeObserver === "undefined") return;
|
|
816
|
+
const update = () => {
|
|
817
|
+
if (scroller.scrollHeight - scroller.clientHeight - scroller.scrollTop > 2) scroller.dataset.more = "";
|
|
818
|
+
else delete scroller.dataset.more;
|
|
819
|
+
};
|
|
820
|
+
update();
|
|
821
|
+
scroller.addEventListener("scroll", update, { passive: true });
|
|
822
|
+
const observer = new ResizeObserver(update);
|
|
823
|
+
observer.observe(scroller);
|
|
824
|
+
observer.observe(track);
|
|
825
|
+
return () => {
|
|
826
|
+
scroller.removeEventListener("scroll", update);
|
|
827
|
+
observer.disconnect();
|
|
828
|
+
};
|
|
829
|
+
}, [scrolls]);
|
|
830
|
+
const newestId = events.reduce((latest, event) => !latest || new Date(event.at).getTime() > new Date(latest.at).getTime() ? event : latest, null)?.id;
|
|
831
|
+
const scrollOptions = useRef({
|
|
832
|
+
scrollToNew,
|
|
833
|
+
reduceMotion
|
|
834
|
+
});
|
|
835
|
+
useEffect(() => {
|
|
836
|
+
scrollOptions.current = {
|
|
837
|
+
scrollToNew,
|
|
838
|
+
reduceMotion
|
|
839
|
+
};
|
|
840
|
+
});
|
|
841
|
+
const shownNewestId = useRef(newestId);
|
|
842
|
+
useEffect(() => {
|
|
843
|
+
if (shownNewestId.current === newestId) return;
|
|
844
|
+
shownNewestId.current = newestId;
|
|
845
|
+
const scroller = scrollerRef.current;
|
|
846
|
+
if (!scrollOptions.current.scrollToNew || !scroller || scroller.scrollTop < 1) return;
|
|
847
|
+
scroller.scrollTo({
|
|
848
|
+
top: 0,
|
|
849
|
+
behavior: scrollOptions.current.reduceMotion ? "auto" : "smooth"
|
|
850
|
+
});
|
|
851
|
+
}, [newestId]);
|
|
852
|
+
const [initialDays] = useState(() => new Set(events.map((event) => dayKey(new Date(event.at).getTime(), timeZone))));
|
|
853
|
+
const { today: todayLabel, yesterday: yesterdayLabel } = copy;
|
|
854
|
+
const todayKey = dayKey(now, timeZone);
|
|
855
|
+
const groups = useMemo(() => {
|
|
856
|
+
const today = dayKey(now, timeZone);
|
|
857
|
+
const yesterday = previousDayKey(today);
|
|
858
|
+
const heading = new Intl.DateTimeFormat(locale, {
|
|
859
|
+
weekday: "long",
|
|
860
|
+
month: "long",
|
|
861
|
+
day: "numeric",
|
|
862
|
+
timeZone
|
|
863
|
+
});
|
|
864
|
+
const byDay = new Map();
|
|
865
|
+
events.map((event) => ({
|
|
866
|
+
...event,
|
|
867
|
+
time: new Date(event.at).getTime()
|
|
868
|
+
})).sort((first, second) => second.time - first.time).forEach((event) => {
|
|
869
|
+
const day = dayKey(event.time, timeZone);
|
|
870
|
+
byDay.set(day, [...byDay.get(day) ?? [], {
|
|
871
|
+
...event,
|
|
872
|
+
day
|
|
873
|
+
}]);
|
|
874
|
+
});
|
|
875
|
+
return [...byDay].map(([day, rows]) => ({
|
|
876
|
+
day,
|
|
877
|
+
rows,
|
|
878
|
+
label: day === today ? todayLabel : day === yesterday ? yesterdayLabel : heading.format(new Date(rows[0].time))
|
|
879
|
+
}));
|
|
880
|
+
}, [
|
|
881
|
+
events,
|
|
882
|
+
now,
|
|
883
|
+
timeZone,
|
|
884
|
+
locale,
|
|
885
|
+
todayLabel,
|
|
886
|
+
yesterdayLabel
|
|
887
|
+
]);
|
|
888
|
+
const formats = useMemo(() => ({
|
|
889
|
+
clock: new Intl.DateTimeFormat(locale, {
|
|
890
|
+
hour: "numeric",
|
|
891
|
+
minute: "2-digit",
|
|
892
|
+
timeZone
|
|
893
|
+
}),
|
|
894
|
+
full: new Intl.DateTimeFormat(locale, {
|
|
895
|
+
weekday: "long",
|
|
896
|
+
month: "long",
|
|
897
|
+
day: "numeric",
|
|
898
|
+
hour: "numeric",
|
|
899
|
+
minute: "2-digit",
|
|
900
|
+
timeZone
|
|
901
|
+
})
|
|
902
|
+
}), [locale, timeZone]);
|
|
903
|
+
const nextReveal = useRef(0);
|
|
904
|
+
const schedule = useCallback(() => {
|
|
905
|
+
if (reduceMotion) return 0;
|
|
906
|
+
const current = performance.now() / MILLISECONDS_PER_SECOND;
|
|
907
|
+
const start = Math.min(Math.max(current, nextReveal.current), current + TIMELINE_REVEAL_QUEUE_LIMIT_S);
|
|
908
|
+
nextReveal.current = start + TIMELINE_REVEAL_STEP_S;
|
|
909
|
+
return start - current;
|
|
910
|
+
}, [reduceMotion]);
|
|
911
|
+
const clock = useMemo(() => ({
|
|
912
|
+
schedule,
|
|
913
|
+
reduceMotion,
|
|
914
|
+
fresh: known.fresh
|
|
915
|
+
}), [
|
|
916
|
+
schedule,
|
|
917
|
+
reduceMotion,
|
|
918
|
+
known.fresh
|
|
919
|
+
]);
|
|
920
|
+
const toggle = (eventId) => {
|
|
921
|
+
setExpanded((current) => {
|
|
922
|
+
const next = new Set(current);
|
|
923
|
+
if (next.has(eventId)) next.delete(eventId);
|
|
924
|
+
else next.add(eventId);
|
|
925
|
+
return next;
|
|
926
|
+
});
|
|
927
|
+
};
|
|
928
|
+
const onKeyDown = (event) => {
|
|
929
|
+
if (![
|
|
930
|
+
"ArrowDown",
|
|
931
|
+
"ArrowUp",
|
|
932
|
+
"Home",
|
|
933
|
+
"End"
|
|
934
|
+
].includes(event.key)) return;
|
|
935
|
+
const triggers = [...scrollerRef.current?.querySelectorAll("[data-timeline-trigger]") ?? []];
|
|
936
|
+
const index = triggers.indexOf(document.activeElement);
|
|
937
|
+
if (index < 0) return;
|
|
938
|
+
event.preventDefault();
|
|
939
|
+
const next = event.key === "Home" ? 0 : event.key === "End" ? triggers.length - 1 : Math.min(Math.max(index + (event.key === "ArrowDown" ? 1 : -1), 0), triggers.length - 1);
|
|
940
|
+
triggers[next]?.focus();
|
|
941
|
+
};
|
|
942
|
+
return /* @__PURE__ */ _jsx(RevealClockContext.Provider, {
|
|
943
|
+
value: clock,
|
|
944
|
+
children: /* @__PURE__ */ _jsxs("div", {
|
|
945
|
+
"data-slot": "timeline",
|
|
946
|
+
role: "region",
|
|
947
|
+
"aria-label": label,
|
|
948
|
+
className: cn("relative grid min-w-0 text-foreground [--line-gap:4px] [--marker-top:8px] [--marker:28px]", className),
|
|
949
|
+
...props,
|
|
950
|
+
children: [/* @__PURE__ */ _jsx("div", {
|
|
951
|
+
ref: scrollerRef,
|
|
952
|
+
"data-slot": "timeline-scroller",
|
|
953
|
+
"data-scrolls": scrolls || undefined,
|
|
954
|
+
className: "min-w-0 [overflow-anchor:none] data-scrolls:max-h-(--timeline-height) data-scrolls:overflow-y-auto data-scrolls:overscroll-contain data-scrolls:[scrollbar-color:var(--border-strong)_transparent] data-scrolls:[scrollbar-width:thin] data-more:[mask-image:linear-gradient(to_bottom,#000_calc(100%_-_36px),transparent)]",
|
|
955
|
+
style: scrolls ? { "--timeline-height": typeof maxHeight === "number" ? `${maxHeight}px` : maxHeight } : undefined,
|
|
956
|
+
onKeyDown,
|
|
957
|
+
children: /* @__PURE__ */ _jsx("div", {
|
|
958
|
+
ref: trackRef,
|
|
959
|
+
children: /* @__PURE__ */ _jsx(AnimatePresence, { children: groups.map((group) => {
|
|
960
|
+
const labelId = `${id}-${group.day}`;
|
|
961
|
+
const noun = copy.updates(group.rows.length);
|
|
962
|
+
return /* @__PURE__ */ _jsxs(DaySection, {
|
|
963
|
+
labelId,
|
|
964
|
+
isFresh: !initialDays.has(group.day),
|
|
965
|
+
reduceMotion,
|
|
966
|
+
children: [/* @__PURE__ */ _jsxs("div", {
|
|
967
|
+
id: labelId,
|
|
968
|
+
role: "heading",
|
|
969
|
+
"aria-level": headingLevel,
|
|
970
|
+
className: "sticky top-0 z-2 flex items-baseline justify-between gap-(--space-3) bg-[var(--timeline-surface,var(--surface))] py-(--space-2) pr-(--space-3) text-sm leading-(--leading-body) font-medium text-foreground",
|
|
971
|
+
children: [
|
|
972
|
+
/* @__PURE__ */ _jsx("span", { children: group.label }),
|
|
973
|
+
/* @__PURE__ */ _jsxs("span", {
|
|
974
|
+
"aria-hidden": "true",
|
|
975
|
+
className: "inline-flex items-baseline text-xs font-normal whitespace-pre text-text-muted tabular-nums",
|
|
976
|
+
children: [/* @__PURE__ */ _jsx(RollingCount, {
|
|
977
|
+
value: group.rows.length,
|
|
978
|
+
reduceMotion
|
|
979
|
+
}), ` ${noun}`]
|
|
980
|
+
}),
|
|
981
|
+
/* @__PURE__ */ _jsx("span", {
|
|
982
|
+
className: "sr-only",
|
|
983
|
+
children: `, ${group.rows.length} ${noun}`
|
|
984
|
+
})
|
|
985
|
+
]
|
|
986
|
+
}), /* @__PURE__ */ _jsx("ol", {
|
|
987
|
+
className: "m-0 mb-(--space-3) list-none p-0",
|
|
988
|
+
children: /* @__PURE__ */ _jsx(AnimatePresence, { children: group.rows.map((row, index) => {
|
|
989
|
+
const isRecent = group.day === todayKey && now - row.time < TIMELINE_RELATIVE_WINDOW_HOURS * MILLISECONDS_PER_HOUR;
|
|
990
|
+
const full = formats.full.format(new Date(row.time));
|
|
991
|
+
const relative = isRecent ? relativeTime(row.time, now, copy) : null;
|
|
992
|
+
return /* @__PURE__ */ _jsx(TimelineRow, {
|
|
993
|
+
row,
|
|
994
|
+
isLast: index === group.rows.length - 1,
|
|
995
|
+
isExpanded: expanded.has(row.id),
|
|
996
|
+
onToggle: () => toggle(row.id),
|
|
997
|
+
timeLabel: relative ? relative.short : formats.clock.format(new Date(row.time)),
|
|
998
|
+
timeFull: relative ? `${relative.long}, ${full}` : full
|
|
999
|
+
}, row.id);
|
|
1000
|
+
}) })
|
|
1001
|
+
})]
|
|
1002
|
+
}, group.day);
|
|
1003
|
+
}) })
|
|
1004
|
+
})
|
|
1005
|
+
}), /* @__PURE__ */ _jsx("span", {
|
|
1006
|
+
className: "sr-only",
|
|
1007
|
+
role: "status",
|
|
1008
|
+
children: /* @__PURE__ */ _jsx("span", { children: known.announcement }, known.announcementCount)
|
|
1009
|
+
})]
|
|
1010
|
+
})
|
|
1011
|
+
});
|
|
1012
|
+
}
|
|
1013
|
+
export { Timeline };
|