gridsmith-ui 0.15.0 → 0.17.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/dist/charts.d.ts +20 -1
- package/dist/charts.js +64 -28
- package/dist/charts.js.map +1 -1
- package/dist/chat.js +3 -3
- package/dist/{chunk-X4CNZFWC.js → chunk-4LDYR7QP.js} +7 -7
- package/dist/chunk-4LDYR7QP.js.map +1 -0
- package/dist/{chunk-ATONRIEB.js → chunk-J43HCK7Q.js} +36 -13
- package/dist/chunk-J43HCK7Q.js.map +1 -0
- package/dist/{chunk-XCSTU6IJ.js → chunk-PVTGMCVR.js} +3 -3
- package/dist/{chunk-XCSTU6IJ.js.map → chunk-PVTGMCVR.js.map} +1 -1
- package/dist/{chunk-LU3YRNES.js → chunk-XGSQOMEY.js} +4 -4
- package/dist/{chunk-LU3YRNES.js.map → chunk-XGSQOMEY.js.map} +1 -1
- package/dist/{chunk-VFAKME7Q.js → chunk-YQABRUWD.js} +156 -40
- package/dist/chunk-YQABRUWD.js.map +1 -0
- package/dist/{chunk-M3AARGLI.js → chunk-Z63OSN64.js} +9 -8
- package/dist/chunk-Z63OSN64.js.map +1 -0
- package/dist/data-grid.js +1 -1
- package/dist/editor.js +11 -5
- package/dist/editor.js.map +1 -1
- package/dist/{feather-B65FI6CY.js → feather-2JFSFLXN.js} +3 -2
- package/dist/feather-2JFSFLXN.js.map +1 -0
- package/dist/{heroicons-DIR7PXCR.js → heroicons-4AQ4YBVG.js} +3 -2
- package/dist/heroicons-4AQ4YBVG.js.map +1 -0
- package/dist/icons.d.ts +1 -1
- package/dist/icons.js +1 -1
- package/dist/index.d.ts +528 -6
- package/dist/index.js +2145 -506
- package/dist/index.js.map +1 -1
- package/dist/kanban.d.ts +11 -1
- package/dist/kanban.js +225 -113
- package/dist/kanban.js.map +1 -1
- package/dist/{lucide-JH2R3TFV.js → lucide-RZC2FEMS.js} +3 -2
- package/dist/lucide-RZC2FEMS.js.map +1 -0
- package/dist/{phosphor-HCVUMVT5.js → phosphor-CEN25FLM.js} +3 -2
- package/dist/phosphor-CEN25FLM.js.map +1 -0
- package/dist/resizable.d.ts +11 -1
- package/dist/resizable.js +79 -21
- package/dist/resizable.js.map +1 -1
- package/dist/roots.css +20 -8
- package/dist/{tabler-HHIXSM73.js → tabler-YXXC7Y6B.js} +3 -2
- package/dist/tabler-YXXC7Y6B.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-ATONRIEB.js.map +0 -1
- package/dist/chunk-M3AARGLI.js.map +0 -1
- package/dist/chunk-VFAKME7Q.js.map +0 -1
- package/dist/chunk-X4CNZFWC.js.map +0 -1
- package/dist/feather-B65FI6CY.js.map +0 -1
- package/dist/heroicons-DIR7PXCR.js.map +0 -1
- package/dist/lucide-JH2R3TFV.js.map +0 -1
- package/dist/phosphor-HCVUMVT5.js.map +0 -1
- package/dist/tabler-HHIXSM73.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,25 +1,26 @@
|
|
|
1
|
-
import { useAppShell, ControlSizeContext,
|
|
2
|
-
export { AppShell, Button, ButtonSizeContext, ControlSizeContext, Drawer, Sheet, SkipLink, Spinner, useAppShell } from './chunk-
|
|
3
|
-
import { Skeleton } from './chunk-
|
|
4
|
-
export { Skeleton } from './chunk-
|
|
5
|
-
import { usePopupFocus,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
import { Button, useAppShell, ControlSizeContext, useDirtyGuard, OverlayCloseCtx, DiscardConfirm, Spinner, DrawerFooterCtx, useCompact, useElementWidth, Sheet, useCloseSettle, Drawer, useRememberedWidth, pxOf, ResizeEdge, defaultMaxWidth } from './chunk-J43HCK7Q.js';
|
|
2
|
+
export { AppShell, Button, ButtonSizeContext, ControlSizeContext, Drawer, Sheet, SkipLink, Spinner, useAppShell, useUnsavedChanges } from './chunk-J43HCK7Q.js';
|
|
3
|
+
import { Skeleton } from './chunk-Z63OSN64.js';
|
|
4
|
+
export { Skeleton } from './chunk-Z63OSN64.js';
|
|
5
|
+
import { FOCUSABLE, usePopupFocus, useOverlayLayer, useFocusTrap, useFocusBeforeOpen, rememberFocus, focusInitial, OVERLAY_CLOSE_ATTR, focusAfterRemoval, rovingTabStop, useGridKeyboard, useNonModalDialog, usePressMarks, useReturnFocus, useCloseOnFocusOut } from './chunk-YQABRUWD.js';
|
|
6
|
+
export { useGridKeyboard } from './chunk-YQABRUWD.js';
|
|
7
|
+
import { Link } from './chunk-XGSQOMEY.js';
|
|
8
|
+
export { CodeBlock, Divider, Link } from './chunk-XGSQOMEY.js';
|
|
9
|
+
import { Badge } from './chunk-PVTGMCVR.js';
|
|
10
|
+
export { Badge } from './chunk-PVTGMCVR.js';
|
|
10
11
|
import { inferColumnKinds, CardValue } from './chunk-OENJCGTJ.js';
|
|
11
12
|
import { SplitPaneCtx, useShellPreferences, TableCardCtx, PREFERENCE_HOUSE_DEFAULTS, preferenceActionKey } from './chunk-TKVTZWBN.js';
|
|
12
13
|
export { PreferencesContext, preferenceActionKey, usePreferences } from './chunk-TKVTZWBN.js';
|
|
13
|
-
import { useBreakpoint } from './chunk-CRQ6KCRD.js';
|
|
14
|
+
import { useBreakpoint, readBreakpointPx } from './chunk-CRQ6KCRD.js';
|
|
14
15
|
export { BreakpointWidthProvider, useBreakpoint } from './chunk-CRQ6KCRD.js';
|
|
15
16
|
import { useTextOverflow, Tooltip, usePosition } from './chunk-EMCSARQO.js';
|
|
16
17
|
export { Tooltip, useTextOverflow } from './chunk-EMCSARQO.js';
|
|
17
18
|
import { mergeRefs, Slot } from './chunk-7Q46JXSK.js';
|
|
18
19
|
import { useLatest } from './chunk-WH2FUFAC.js';
|
|
19
|
-
import { Icon } from './chunk-
|
|
20
|
-
import { cn, GLASS_SURFACE, ACTIVE_RING, GLASS_BACKDROP, GLASS_DROPDOWN, ERROR_ACTIVE_RING, ERROR_FOCUS_RING, FOCUS_RING, ERROR_HAS_FOCUS_RING, HAS_FOCUS_RING, ERROR_FOCUS_WITHIN_RING, FOCUS_WITHIN_RING
|
|
21
|
-
import { createContext, forwardRef, useState,
|
|
22
|
-
import { createPortal } from 'react-dom';
|
|
20
|
+
import { Icon } from './chunk-4LDYR7QP.js';
|
|
21
|
+
import { cn, GLASS_SURFACE, ACTIVE_RING, GLASS_BACKDROP, GLASS_DROPDOWN, lockBodyScroll, unlockBodyScroll, GLASS_OVERLAY, ERROR_ACTIVE_RING, ERROR_FOCUS_RING, FOCUS_RING, ERROR_HAS_FOCUS_RING, HAS_FOCUS_RING, ERROR_FOCUS_WITHIN_RING, FOCUS_WITHIN_RING } from './chunk-4KVFH5KC.js';
|
|
22
|
+
import { createContext, forwardRef, useState, useRef, useCallback, useLayoutEffect, useMemo, useContext, useId, memo, useEffect, useImperativeHandle, Children, isValidElement, cloneElement, Fragment as Fragment$1, useSyncExternalStore } from 'react';
|
|
23
|
+
import { createPortal, flushSync } from 'react-dom';
|
|
23
24
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
24
25
|
|
|
25
26
|
function isDivider(entry) {
|
|
@@ -44,10 +45,10 @@ function DropdownMenu({ trigger, items, align = "end", className, container }) {
|
|
|
44
45
|
useEffect(() => {
|
|
45
46
|
if (!open) return;
|
|
46
47
|
const onKeyDown = (e) => {
|
|
47
|
-
if (e.key
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
if (e.key !== "Escape" || e.defaultPrevented || !isTopLayer()) return;
|
|
49
|
+
e.preventDefault();
|
|
50
|
+
returnFocus();
|
|
51
|
+
setOpen(false);
|
|
51
52
|
};
|
|
52
53
|
const onClick = (e) => {
|
|
53
54
|
const target = e.target;
|
|
@@ -523,7 +524,7 @@ function Dropdown({
|
|
|
523
524
|
const els = optionButtons();
|
|
524
525
|
const current = els.findIndex((el) => el === document.activeElement);
|
|
525
526
|
if (e.key === "Escape") {
|
|
526
|
-
if (!isTopLayer()) return;
|
|
527
|
+
if (e.defaultPrevented || !isTopLayer()) return;
|
|
527
528
|
e.preventDefault();
|
|
528
529
|
returnFocus();
|
|
529
530
|
setOpen(false);
|
|
@@ -585,14 +586,14 @@ function Dropdown({
|
|
|
585
586
|
};
|
|
586
587
|
const chipsRef = useRef(null);
|
|
587
588
|
const pendingChipFocus = useRef(null);
|
|
588
|
-
const chipCount = selectedValues.length;
|
|
589
589
|
useLayoutEffect(() => {
|
|
590
|
-
const
|
|
591
|
-
if (
|
|
590
|
+
const pending = pendingChipFocus.current;
|
|
591
|
+
if (pending === null) return;
|
|
592
592
|
pendingChipFocus.current = null;
|
|
593
|
+
if (selectedValues.includes(pending.value)) return;
|
|
593
594
|
const chips = Array.from(chipsRef.current?.querySelectorAll("[data-chip-remove]") ?? []);
|
|
594
|
-
focusAfterRemoval(chips, index, triggerRef.current);
|
|
595
|
-
}
|
|
595
|
+
focusAfterRemoval(chips, pending.index, triggerRef.current);
|
|
596
|
+
});
|
|
596
597
|
const displayLabel = multiple ? selectedValues.length > 0 ? `${selectedValues.length} selected` : placeholder : options.find((o) => o.id === selectedValues[0])?.label ?? placeholder;
|
|
597
598
|
const { ref: overflowRef, isTruncated } = useTextOverflow({ lines: 1, content: displayLabel });
|
|
598
599
|
return /* @__PURE__ */ jsxs("div", { ref, className: cn("relative", className), children: [
|
|
@@ -705,7 +706,7 @@ function Dropdown({
|
|
|
705
706
|
{
|
|
706
707
|
"data-chip-remove": "",
|
|
707
708
|
onClick: () => {
|
|
708
|
-
pendingChipFocus.current = i;
|
|
709
|
+
pendingChipFocus.current = { index: i, value: v };
|
|
709
710
|
handleSelect(v);
|
|
710
711
|
},
|
|
711
712
|
className: "hover:text-[var(--ds-text-primary)] cursor-pointer",
|
|
@@ -845,9 +846,14 @@ function Combobox({ value, onChange, options, placeholder = "Search\u2026", disa
|
|
|
845
846
|
} else if (e.key === "Enter" && open && filtered[highlightIdx]) {
|
|
846
847
|
e.preventDefault();
|
|
847
848
|
handleSelect(filtered[highlightIdx].value);
|
|
848
|
-
} else if (e.key === "Tab" && open
|
|
849
|
-
onChange(filtered[highlightIdx].value);
|
|
850
|
-
|
|
849
|
+
} else if (e.key === "Tab" && open) {
|
|
850
|
+
if (moved && filtered[highlightIdx]) onChange(filtered[highlightIdx].value);
|
|
851
|
+
flushSync(() => {
|
|
852
|
+
setOpen(false);
|
|
853
|
+
setQuery("");
|
|
854
|
+
});
|
|
855
|
+
} else if (e.key === "Escape" && open) {
|
|
856
|
+
e.preventDefault();
|
|
851
857
|
setOpen(false);
|
|
852
858
|
setQuery("");
|
|
853
859
|
}
|
|
@@ -1306,12 +1312,12 @@ function parseTypedDate(text, dayFirst = localeDayFirst()) {
|
|
|
1306
1312
|
}
|
|
1307
1313
|
return null;
|
|
1308
1314
|
}
|
|
1309
|
-
function DatePicker({ value, onChange, placeholder = "Pick a date", disabled, className, ref, container, error = false, label, id, size: sizeProp, min, max, range }) {
|
|
1315
|
+
function DatePicker({ value, onChange, placeholder = "Pick a date", disabled, className, ref, container, error = false, label, id, size: sizeProp, min, max, range: range2 }) {
|
|
1310
1316
|
const contextSize = useContext(ControlSizeContext);
|
|
1311
1317
|
const size = sizeProp ?? contextSize ?? "md";
|
|
1312
1318
|
const [open, setOpen] = useState(false);
|
|
1313
1319
|
const [hover, setHover] = useState(null);
|
|
1314
|
-
const [viewDate, setViewDate] = useState(() => value ??
|
|
1320
|
+
const [viewDate, setViewDate] = useState(() => value ?? range2?.start ?? range2?.end ?? /* @__PURE__ */ new Date());
|
|
1315
1321
|
const triggerRef = useRef(null);
|
|
1316
1322
|
const buttonRef = useRef(null);
|
|
1317
1323
|
const panelRef = useRef(null);
|
|
@@ -1326,12 +1332,12 @@ function DatePicker({ value, onChange, placeholder = "Pick a date", disabled, cl
|
|
|
1326
1332
|
const today = useMemo(() => /* @__PURE__ */ new Date(), []);
|
|
1327
1333
|
const startOf = (d) => new Date(d.getFullYear(), d.getMonth(), d.getDate());
|
|
1328
1334
|
const isDisabledDay = (d) => !!min && d < startOf(min) || !!max && d > startOf(max);
|
|
1329
|
-
const [focusDate, setFocusDate] = useState(() => startOf(value ??
|
|
1335
|
+
const [focusDate, setFocusDate] = useState(() => startOf(value ?? range2?.start ?? range2?.end ?? /* @__PURE__ */ new Date()));
|
|
1330
1336
|
const [prevOpen, setPrevOpen] = useState(open);
|
|
1331
1337
|
if (open !== prevOpen) {
|
|
1332
1338
|
setPrevOpen(open);
|
|
1333
1339
|
if (open) {
|
|
1334
|
-
const start = startOf(value ??
|
|
1340
|
+
const start = startOf(value ?? range2?.start ?? range2?.end ?? today);
|
|
1335
1341
|
setFocusDate(start);
|
|
1336
1342
|
setViewDate(new Date(start.getFullYear(), start.getMonth(), 1));
|
|
1337
1343
|
}
|
|
@@ -1362,8 +1368,9 @@ function DatePicker({ value, onChange, placeholder = "Pick a date", disabled, cl
|
|
|
1362
1368
|
const raf = requestAnimationFrame(() => focusActiveDay.current());
|
|
1363
1369
|
return () => cancelAnimationFrame(raf);
|
|
1364
1370
|
}, [open, focusActiveDay]);
|
|
1365
|
-
const
|
|
1371
|
+
const choose = (d) => {
|
|
1366
1372
|
returnFocus();
|
|
1373
|
+
onChange(d);
|
|
1367
1374
|
setOpen(false);
|
|
1368
1375
|
};
|
|
1369
1376
|
const dayFirst = useMemo(() => localeDayFirst(), []);
|
|
@@ -1388,10 +1395,10 @@ function DatePicker({ value, onChange, placeholder = "Pick a date", disabled, cl
|
|
|
1388
1395
|
setOpen(false);
|
|
1389
1396
|
};
|
|
1390
1397
|
const keyHandler = (e) => {
|
|
1391
|
-
if (e.key
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1398
|
+
if (e.key !== "Escape" || e.defaultPrevented || !isTopLayer()) return;
|
|
1399
|
+
e.preventDefault();
|
|
1400
|
+
returnFocus();
|
|
1401
|
+
setOpen(false);
|
|
1395
1402
|
};
|
|
1396
1403
|
document.addEventListener("mousedown", handler);
|
|
1397
1404
|
document.addEventListener("keydown", keyHandler);
|
|
@@ -1485,11 +1492,10 @@ function DatePicker({ value, onChange, placeholder = "Pick a date", disabled, cl
|
|
|
1485
1492
|
const selectDay = (day) => {
|
|
1486
1493
|
const d = new Date(year, month, day);
|
|
1487
1494
|
if (isDisabledDay(d)) return;
|
|
1488
|
-
|
|
1489
|
-
closeAndRefocus();
|
|
1495
|
+
choose(d);
|
|
1490
1496
|
};
|
|
1491
|
-
const rangeA =
|
|
1492
|
-
const rangeB =
|
|
1497
|
+
const rangeA = range2?.start ?? null;
|
|
1498
|
+
const rangeB = range2?.end ?? null;
|
|
1493
1499
|
const otherEnd = value ? rangeA && !isSameDay(rangeA, value) ? rangeA : rangeB : rangeA ?? rangeB;
|
|
1494
1500
|
const thisEnd = value ?? (otherEnd && hover ? hover : null);
|
|
1495
1501
|
const [bandStart, bandEnd] = otherEnd && thisEnd ? otherEnd <= thisEnd ? [otherEnd, thisEnd] : [thisEnd, otherEnd] : [null, null];
|
|
@@ -1676,8 +1682,7 @@ function DatePicker({ value, onChange, placeholder = "Pick a date", disabled, cl
|
|
|
1676
1682
|
onClick: () => {
|
|
1677
1683
|
const now = /* @__PURE__ */ new Date();
|
|
1678
1684
|
setViewDate(now);
|
|
1679
|
-
|
|
1680
|
-
closeAndRefocus();
|
|
1685
|
+
choose(now);
|
|
1681
1686
|
},
|
|
1682
1687
|
children: "Today"
|
|
1683
1688
|
}
|
|
@@ -1687,10 +1692,7 @@ function DatePicker({ value, onChange, placeholder = "Pick a date", disabled, cl
|
|
|
1687
1692
|
{
|
|
1688
1693
|
variant: "ghost",
|
|
1689
1694
|
size: "sm",
|
|
1690
|
-
onClick: () =>
|
|
1691
|
-
onChange(null);
|
|
1692
|
-
closeAndRefocus();
|
|
1693
|
-
},
|
|
1695
|
+
onClick: () => choose(null),
|
|
1694
1696
|
children: "Clear"
|
|
1695
1697
|
}
|
|
1696
1698
|
)
|
|
@@ -1962,6 +1964,10 @@ function DateRangePicker({
|
|
|
1962
1964
|
keyOf: dayKey,
|
|
1963
1965
|
move: (from, e) => moveDay(from, e.key, e.shiftKey, weekStartsOn)
|
|
1964
1966
|
});
|
|
1967
|
+
const pageMonths = (n) => {
|
|
1968
|
+
setViewDate(addMonths(viewDate, n));
|
|
1969
|
+
setFocusDate(addMonthsClamped(focusDate, n));
|
|
1970
|
+
};
|
|
1965
1971
|
const { isTop: isTopLayer, returnFocus } = usePopupFocus(open, { trigger: triggerRef, panel: panelRef, close: () => setOpen(false), tabExits: "edges" });
|
|
1966
1972
|
const focusActiveDay = useLatest(grid.focusActive);
|
|
1967
1973
|
useEffect(() => {
|
|
@@ -1977,10 +1983,10 @@ function DateRangePicker({
|
|
|
1977
1983
|
setOpen(false);
|
|
1978
1984
|
};
|
|
1979
1985
|
const onKey = (e) => {
|
|
1980
|
-
if (e.key
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1986
|
+
if (e.key !== "Escape" || e.defaultPrevented || !isTopLayer()) return;
|
|
1987
|
+
e.preventDefault();
|
|
1988
|
+
returnFocus();
|
|
1989
|
+
setOpen(false);
|
|
1984
1990
|
};
|
|
1985
1991
|
document.addEventListener("mousedown", onMouse);
|
|
1986
1992
|
document.addEventListener("keydown", onKey);
|
|
@@ -2071,7 +2077,7 @@ function DateRangePicker({
|
|
|
2071
2077
|
}, children: p.label }, p.label)) }),
|
|
2072
2078
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
|
|
2073
2079
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between bg-[var(--ds-bg-muted)] rounded-tr-[var(--ds-radius-md)] px-ds-3 py-ds-2", children: [
|
|
2074
|
-
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon-sm", onClick: () =>
|
|
2080
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon-sm", onClick: () => pageMonths(-1), "aria-label": "Previous month", children: /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: 14, className: "rotate-180" }) }),
|
|
2075
2081
|
/* @__PURE__ */ jsxs("span", { className: "text-sm font-medium text-[var(--ds-text-primary)]", children: [
|
|
2076
2082
|
MONTHS2[months[0].getMonth()],
|
|
2077
2083
|
" ",
|
|
@@ -2081,7 +2087,7 @@ function DateRangePicker({
|
|
|
2081
2087
|
" ",
|
|
2082
2088
|
months[1].getFullYear()
|
|
2083
2089
|
] }),
|
|
2084
|
-
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon-sm", onClick: () =>
|
|
2090
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon-sm", onClick: () => pageMonths(1), "aria-label": "Next month", children: /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: 14 }) })
|
|
2085
2091
|
] }),
|
|
2086
2092
|
/* @__PURE__ */ jsx("div", { ref: gridRef, onKeyDown: grid.onKeyDown, className: "flex gap-ds-4 px-ds-3 py-ds-2", onMouseLeave: () => setHover(null), children: months.map((m) => /* @__PURE__ */ jsxs("div", { role: "grid", "aria-label": `${MONTHS2[m.getMonth()]} ${m.getFullYear()}`, className: "w-[224px] flex flex-col gap-ds-0.5", children: [
|
|
2087
2093
|
/* @__PURE__ */ jsx("div", { role: "row", className: "grid grid-cols-7 gap-ds-0.5 mb-ds-0.5", children: dayNames.map((d) => /* @__PURE__ */ jsx("div", { role: "columnheader", className: "text-center text-2xs font-medium text-[var(--ds-text-muted)] py-ds-1", children: d }, d)) }),
|
|
@@ -2135,6 +2141,96 @@ function DateRangePicker({
|
|
|
2135
2141
|
)
|
|
2136
2142
|
] });
|
|
2137
2143
|
}
|
|
2144
|
+
|
|
2145
|
+
// ../../showcase/src/lib/file-accept.ts
|
|
2146
|
+
var MIME_NAMES = {
|
|
2147
|
+
"text/plain": "TXT",
|
|
2148
|
+
"audio/mpeg": "MP3",
|
|
2149
|
+
"video/quicktime": "MOV",
|
|
2150
|
+
"application/msword": "DOC",
|
|
2151
|
+
"application/vnd.ms-excel": "XLS",
|
|
2152
|
+
"application/vnd.ms-powerpoint": "PPT",
|
|
2153
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": "DOCX",
|
|
2154
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "XLSX",
|
|
2155
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation": "PPTX"
|
|
2156
|
+
};
|
|
2157
|
+
var EXT_NAMES = { jpeg: "JPG", tif: "TIFF", htm: "HTML", yml: "YAML" };
|
|
2158
|
+
var FAMILIES = {
|
|
2159
|
+
image: { one: "an image", many: "images" },
|
|
2160
|
+
video: { one: "a video", many: "videos" },
|
|
2161
|
+
audio: { one: "an audio file", many: "audio files" },
|
|
2162
|
+
text: { one: "a text file", many: "text files" }
|
|
2163
|
+
};
|
|
2164
|
+
var READ_AS_WORD = /* @__PURE__ */ new Set(["FLAC", "HEIC", "HEIF", "LOG", "MIDI", "MOV", "RAR", "RAW"]);
|
|
2165
|
+
var extName = (ext) => EXT_NAMES[ext] ?? ext.toUpperCase();
|
|
2166
|
+
function mimeName(mime) {
|
|
2167
|
+
if (MIME_NAMES[mime]) return MIME_NAMES[mime];
|
|
2168
|
+
const subtype = mime.slice(mime.indexOf("/") + 1).replace(/\+.*$/, "").replace(/^(?:x-|vnd\.)/, "");
|
|
2169
|
+
return extName(subtype.slice(subtype.lastIndexOf(".") + 1));
|
|
2170
|
+
}
|
|
2171
|
+
function parseAccept(accept) {
|
|
2172
|
+
return (accept ?? "").split(",").map((t) => t.trim().toLowerCase()).filter(Boolean).map((t) => {
|
|
2173
|
+
if (t === "*" || t === "*/*") return { kind: "any" };
|
|
2174
|
+
if (t.startsWith(".")) return { kind: "format", name: extName(t.slice(1)), suffix: t };
|
|
2175
|
+
if (t.endsWith("/*")) return { kind: "family", family: t.slice(0, -2) };
|
|
2176
|
+
return { kind: "format", name: mimeName(t), mime: t };
|
|
2177
|
+
});
|
|
2178
|
+
}
|
|
2179
|
+
function fileFormat(file) {
|
|
2180
|
+
const dot = file.name.lastIndexOf(".");
|
|
2181
|
+
return dot > 0 ? extName(file.name.slice(dot + 1).toLowerCase()) : null;
|
|
2182
|
+
}
|
|
2183
|
+
function matches(file, tokens) {
|
|
2184
|
+
if (tokens.length === 0 || tokens.some((t) => t.kind === "any")) return true;
|
|
2185
|
+
const name = file.name.toLowerCase();
|
|
2186
|
+
const format2 = fileFormat(file);
|
|
2187
|
+
const type = file.type.toLowerCase();
|
|
2188
|
+
return tokens.some((t) => {
|
|
2189
|
+
if (t.kind === "family") return type === "" || type.startsWith(`${t.family}/`);
|
|
2190
|
+
if (t.kind !== "format") return false;
|
|
2191
|
+
return t.name === format2 || t.suffix != null && name.endsWith(t.suffix) || t.mime != null && t.mime === type;
|
|
2192
|
+
});
|
|
2193
|
+
}
|
|
2194
|
+
var joinOr = (items) => items.length < 2 ? items.join("") : `${items.slice(0, -1).join(", ")} or ${items[items.length - 1]}`;
|
|
2195
|
+
var article = (name) => {
|
|
2196
|
+
const asWord = name.replace(/[^A-Z]/g, "").length >= 5 || READ_AS_WORD.has(name);
|
|
2197
|
+
return (asWord ? /^[AEIOU]/ : /^[AEFHILMNORSX]/).test(name) ? "an" : "a";
|
|
2198
|
+
};
|
|
2199
|
+
function typeWords(accept) {
|
|
2200
|
+
const tokens = parseAccept(accept);
|
|
2201
|
+
if (tokens.length === 0 || tokens.some((t) => t.kind === "any" || t.kind === "family" && !FAMILIES[t.family])) return null;
|
|
2202
|
+
const families = [...new Set(tokens.flatMap((t) => t.kind === "family" ? [t.family] : []))].map((f) => FAMILIES[f]);
|
|
2203
|
+
const formats2 = [...new Set(tokens.flatMap((t) => t.kind === "format" ? [t.name] : []))];
|
|
2204
|
+
const many = `${joinOr([...families.map((f) => f.many), ...formats2])}${formats2.length ? " files" : ""}`;
|
|
2205
|
+
const one = families.length === 0 ? `${article(formats2[0])} ${joinOr(formats2)}` : joinOr([...families.map((f) => f.one), ...formats2.map((f) => `${article(f)} ${f}`)]);
|
|
2206
|
+
return { one, many };
|
|
2207
|
+
}
|
|
2208
|
+
function sizeWords(bytes) {
|
|
2209
|
+
const units = [["GB", 1024 ** 3], ["MB", 1024 ** 2], ["KB", 1024]];
|
|
2210
|
+
for (const [unit, size] of units) if (bytes >= size) return `${Math.round(bytes / size * 10) / 10} ${unit}`;
|
|
2211
|
+
return `${bytes} bytes`;
|
|
2212
|
+
}
|
|
2213
|
+
var capitalise = (s) => s.charAt(0).toUpperCase() + s.slice(1);
|
|
2214
|
+
function fileConstraints(accept, maxSize, multiple) {
|
|
2215
|
+
const types = typeWords(accept)?.many;
|
|
2216
|
+
const size = maxSize != null ? `up to ${sizeWords(maxSize)}${multiple ? " each" : ""}` : null;
|
|
2217
|
+
const line = [types, size].filter(Boolean).join(" ");
|
|
2218
|
+
return line ? capitalise(line) : null;
|
|
2219
|
+
}
|
|
2220
|
+
function checkFiles(files, { accept, maxSize, multiple }) {
|
|
2221
|
+
if (!multiple && files.length > 1) return { accepted: [], message: "You can only select one file" };
|
|
2222
|
+
const tokens = parseAccept(accept);
|
|
2223
|
+
const types = typeWords(accept);
|
|
2224
|
+
const accepted = [];
|
|
2225
|
+
const reasons = [];
|
|
2226
|
+
for (const file of files) {
|
|
2227
|
+
const subject = multiple ? file.name : "The selected file";
|
|
2228
|
+
if (!matches(file, tokens)) reasons.push(types ? `${subject} must be ${types.one}` : `${subject} is not a file type that can be added here`);
|
|
2229
|
+
else if (maxSize != null && file.size > maxSize) reasons.push(`${subject} must be smaller than ${sizeWords(maxSize)}`);
|
|
2230
|
+
else accepted.push(file);
|
|
2231
|
+
}
|
|
2232
|
+
return { accepted, message: reasons.length ? reasons.join("; ") : null };
|
|
2233
|
+
}
|
|
2138
2234
|
var variantFills = {
|
|
2139
2235
|
default: "bg-[var(--ds-accent-primary)] [background-image:var(--ds-gradient)]",
|
|
2140
2236
|
success: "bg-[var(--ds-status-success)]",
|
|
@@ -2182,6 +2278,66 @@ function Progress({ value, max = 100, variant = "default", size = "md", indeterm
|
|
|
2182
2278
|
] })
|
|
2183
2279
|
] });
|
|
2184
2280
|
}
|
|
2281
|
+
function InlineFieldError({ message, id, className }) {
|
|
2282
|
+
return /* @__PURE__ */ jsxs(
|
|
2283
|
+
"p",
|
|
2284
|
+
{
|
|
2285
|
+
id,
|
|
2286
|
+
role: "alert",
|
|
2287
|
+
className: cn(
|
|
2288
|
+
"flex items-center gap-ds-1.5 text-xs text-[var(--ds-status-error)]",
|
|
2289
|
+
"mt-ds-1.5",
|
|
2290
|
+
className
|
|
2291
|
+
),
|
|
2292
|
+
children: [
|
|
2293
|
+
/* @__PURE__ */ jsx(Icon, { name: "error", size: 14, className: "shrink-0" }),
|
|
2294
|
+
/* @__PURE__ */ jsx("span", { children: message })
|
|
2295
|
+
]
|
|
2296
|
+
}
|
|
2297
|
+
);
|
|
2298
|
+
}
|
|
2299
|
+
var FieldContext = createContext(null);
|
|
2300
|
+
function useField() {
|
|
2301
|
+
return useContext(FieldContext);
|
|
2302
|
+
}
|
|
2303
|
+
function Field({ label, children, hint, error, required, labelAction, id: idProp, className }) {
|
|
2304
|
+
const generated = useId();
|
|
2305
|
+
const id = idProp ?? `field-${generated}`;
|
|
2306
|
+
const hintId = `${id}-hint`;
|
|
2307
|
+
const errorId = `${id}-error`;
|
|
2308
|
+
const hasError = typeof error === "string" && error.length > 0;
|
|
2309
|
+
const describedBy = [hint ? hintId : null, hasError ? errorId : null].filter(Boolean).join(" ") || void 0;
|
|
2310
|
+
const labelId = `${id}-label`;
|
|
2311
|
+
const fieldContext = useMemo(() => ({ controlId: id, labelId, describedBy }), [id, labelId, describedBy]);
|
|
2312
|
+
const controlProps = {
|
|
2313
|
+
id,
|
|
2314
|
+
"aria-describedby": describedBy,
|
|
2315
|
+
...hasError ? { "aria-invalid": true, error: true } : {},
|
|
2316
|
+
...required ? { "aria-required": true, required: true } : {}
|
|
2317
|
+
};
|
|
2318
|
+
let control;
|
|
2319
|
+
if (typeof children === "function") {
|
|
2320
|
+
control = children(controlProps);
|
|
2321
|
+
} else {
|
|
2322
|
+
const child = Children.only(children);
|
|
2323
|
+
if (!isValidElement(child)) throw new Error("Field expects a single form control as its child");
|
|
2324
|
+
const isNative = typeof child.type === "string";
|
|
2325
|
+
const { error: errorFlag, ...ariaOnly } = controlProps;
|
|
2326
|
+
control = cloneElement(child, isNative ? ariaOnly : { ...ariaOnly, ...errorFlag ? { error: true } : {} });
|
|
2327
|
+
}
|
|
2328
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-ds-1.5", className), children: [
|
|
2329
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-ds-2", children: [
|
|
2330
|
+
/* @__PURE__ */ jsxs(Label, { id: labelId, htmlFor: id, children: [
|
|
2331
|
+
label,
|
|
2332
|
+
required && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "ml-ds-0.5 text-[var(--ds-status-error)]", children: "*" })
|
|
2333
|
+
] }),
|
|
2334
|
+
labelAction
|
|
2335
|
+
] }),
|
|
2336
|
+
/* @__PURE__ */ jsx(FieldContext.Provider, { value: fieldContext, children: control }),
|
|
2337
|
+
hint && /* @__PURE__ */ jsx("p", { id: hintId, className: "text-xs text-[var(--ds-text-muted)]", children: hint }),
|
|
2338
|
+
hasError && /* @__PURE__ */ jsx(InlineFieldError, { id: errorId, message: error, className: "mt-0" })
|
|
2339
|
+
] });
|
|
2340
|
+
}
|
|
2185
2341
|
function TruncFileName({ children, className }) {
|
|
2186
2342
|
const { ref, isTruncated } = useTextOverflow({ lines: 1 });
|
|
2187
2343
|
const el = /* @__PURE__ */ jsx("p", { ref, className: cn("truncate", className), children });
|
|
@@ -2193,37 +2349,57 @@ function formatSize(bytes) {
|
|
|
2193
2349
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
2194
2350
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
2195
2351
|
}
|
|
2196
|
-
function FileUpload({ onFiles, accept, multiple = true, maxSize, className, error = false, label }) {
|
|
2352
|
+
function FileUpload({ onFiles, accept, multiple = true, maxSize, className, error = false, label, id }) {
|
|
2197
2353
|
const [files, setFiles] = useState([]);
|
|
2198
2354
|
const [dragOver, setDragOver] = useState(false);
|
|
2199
|
-
const
|
|
2355
|
+
const [rejected, setRejected] = useState(null);
|
|
2200
2356
|
const inputRef = useRef(null);
|
|
2357
|
+
const nextEntryId = useRef(0);
|
|
2358
|
+
const timers = useRef(/* @__PURE__ */ new Map());
|
|
2359
|
+
useEffect(() => {
|
|
2360
|
+
const running = timers.current;
|
|
2361
|
+
return () => running.forEach(clearInterval);
|
|
2362
|
+
}, []);
|
|
2363
|
+
const field = useField();
|
|
2364
|
+
const ownId = useId();
|
|
2365
|
+
const inputId = id ?? `${ownId}-input`;
|
|
2366
|
+
const constraintsId = `${ownId}-constraints`;
|
|
2367
|
+
const rejectedId = `${ownId}-rejected`;
|
|
2368
|
+
const showLabel = label != null && !field;
|
|
2369
|
+
const constraints = fileConstraints(accept, maxSize, multiple);
|
|
2370
|
+
const invalid = error || rejected != null;
|
|
2371
|
+
const describedBy = [constraints ? constraintsId : null, field?.describedBy, rejected ? rejectedId : null].filter(Boolean).join(" ") || void 0;
|
|
2372
|
+
const stopTimer = (id2) => {
|
|
2373
|
+
clearInterval(timers.current.get(id2));
|
|
2374
|
+
timers.current.delete(id2);
|
|
2375
|
+
};
|
|
2201
2376
|
const addFiles = useCallback(
|
|
2202
2377
|
(fileList) => {
|
|
2203
|
-
const
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2378
|
+
const { accepted, message } = checkFiles(Array.from(fileList), { accept, maxSize, multiple });
|
|
2379
|
+
setRejected(message);
|
|
2380
|
+
if (accepted.length === 0) return;
|
|
2381
|
+
const entries = accepted.map((file) => ({ id: nextEntryId.current++, file, progress: 0, status: "uploading" }));
|
|
2382
|
+
if (!multiple) {
|
|
2383
|
+
timers.current.forEach(clearInterval);
|
|
2384
|
+
timers.current.clear();
|
|
2385
|
+
}
|
|
2386
|
+
setFiles((prev) => multiple ? [...prev, ...entries] : entries);
|
|
2387
|
+
onFiles(accepted);
|
|
2388
|
+
for (const { id: id2 } of entries) {
|
|
2209
2389
|
let progress = 0;
|
|
2210
|
-
const
|
|
2211
|
-
progress
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
clearInterval(
|
|
2215
|
-
|
|
2216
|
-
(prev) => prev.map((f, fi) => fi === prev.length - entries.length + i ? { ...f, progress: 100, status: "complete" } : f)
|
|
2217
|
-
);
|
|
2218
|
-
} else {
|
|
2219
|
-
setFiles(
|
|
2220
|
-
(prev) => prev.map((f, fi) => fi === prev.length - entries.length + i ? { ...f, progress } : f)
|
|
2221
|
-
);
|
|
2390
|
+
const timer = setInterval(() => {
|
|
2391
|
+
progress = Math.min(100, progress + Math.random() * 30 + 10);
|
|
2392
|
+
const done = progress >= 100;
|
|
2393
|
+
if (done) {
|
|
2394
|
+
clearInterval(timer);
|
|
2395
|
+
timers.current.delete(id2);
|
|
2222
2396
|
}
|
|
2397
|
+
setFiles((prev) => prev.map((f) => f.id === id2 ? { ...f, progress, status: done ? "complete" : "uploading" } : f));
|
|
2223
2398
|
}, 300);
|
|
2224
|
-
|
|
2399
|
+
timers.current.set(id2, timer);
|
|
2400
|
+
}
|
|
2225
2401
|
},
|
|
2226
|
-
[maxSize, onFiles]
|
|
2402
|
+
[accept, maxSize, multiple, onFiles]
|
|
2227
2403
|
);
|
|
2228
2404
|
const handleDrop = useCallback(
|
|
2229
2405
|
(e) => {
|
|
@@ -2233,63 +2409,66 @@ function FileUpload({ onFiles, accept, multiple = true, maxSize, className, erro
|
|
|
2233
2409
|
},
|
|
2234
2410
|
[addFiles]
|
|
2235
2411
|
);
|
|
2236
|
-
const removeFile = (
|
|
2237
|
-
|
|
2412
|
+
const removeFile = (id2) => {
|
|
2413
|
+
stopTimer(id2);
|
|
2414
|
+
setFiles((prev) => prev.filter((f) => f.id !== id2));
|
|
2238
2415
|
};
|
|
2239
2416
|
return /* @__PURE__ */ jsxs("div", { className: cn("space-y-ds-3", className), children: [
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
"
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2417
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-ds-1.5", children: [
|
|
2418
|
+
showLabel && /* @__PURE__ */ jsx(Label, { htmlFor: inputId, children: label }),
|
|
2419
|
+
/* @__PURE__ */ jsxs(
|
|
2420
|
+
"div",
|
|
2421
|
+
{
|
|
2422
|
+
onDragOver: (e) => {
|
|
2423
|
+
e.preventDefault();
|
|
2424
|
+
setDragOver(true);
|
|
2425
|
+
},
|
|
2426
|
+
onDragLeave: () => setDragOver(false),
|
|
2427
|
+
onDrop: handleDrop,
|
|
2428
|
+
onClick: () => inputRef.current?.click(),
|
|
2429
|
+
className: cn(
|
|
2430
|
+
"flex flex-col items-center justify-center gap-ds-2 cursor-pointer group",
|
|
2431
|
+
"border-2 border-dashed rounded-[var(--ds-radius-md)]",
|
|
2432
|
+
"transition-colors",
|
|
2433
|
+
// The real file input is visually hidden inside; Tab lands on it and
|
|
2434
|
+
// Enter/Space open the picker natively, so the zone shows its focus.
|
|
2435
|
+
invalid ? ERROR_HAS_FOCUS_RING : HAS_FOCUS_RING,
|
|
2436
|
+
dragOver ? `${ACTIVE_RING} bg-[var(--ds-accent-whisper)]` : invalid ? "border-[var(--ds-status-error)]" : "border-[var(--ds-border-default)] hover:border-[var(--ds-accent-primary)] hover:bg-[var(--ds-accent-whisper)]"
|
|
2437
|
+
),
|
|
2438
|
+
style: { padding: "var(--ds-space-8) var(--ds-space-4)" },
|
|
2439
|
+
children: [
|
|
2440
|
+
/* @__PURE__ */ jsx("div", { className: cn(
|
|
2441
|
+
"flex items-center justify-center w-10 h-10 rounded-[var(--ds-radius-full)] transition-colors",
|
|
2442
|
+
"bg-[var(--ds-bg-muted)] group-hover:bg-[var(--ds-accent-subtle)]"
|
|
2443
|
+
), children: /* @__PURE__ */ jsx(Icon, { name: "plus", size: 20, className: "text-[var(--ds-text-muted)] group-hover:text-[var(--ds-accent-primary)] transition-colors" }) }),
|
|
2444
|
+
/* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
2445
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-[var(--ds-text-primary)]", children: multiple ? "Drop files here or browse" : "Drop a file here or browse" }),
|
|
2446
|
+
constraints && /* @__PURE__ */ jsx("p", { id: constraintsId, className: "text-xs text-[var(--ds-text-muted)] mt-ds-1", children: constraints })
|
|
2447
|
+
] }),
|
|
2448
|
+
/* @__PURE__ */ jsx(
|
|
2449
|
+
"input",
|
|
2450
|
+
{
|
|
2451
|
+
ref: inputRef,
|
|
2452
|
+
id: inputId,
|
|
2453
|
+
type: "file",
|
|
2454
|
+
accept,
|
|
2455
|
+
multiple,
|
|
2456
|
+
className: "sr-only",
|
|
2457
|
+
"aria-labelledby": field ? field.labelId : void 0,
|
|
2458
|
+
"aria-describedby": describedBy,
|
|
2459
|
+
"aria-invalid": invalid || void 0,
|
|
2460
|
+
onChange: (e) => {
|
|
2461
|
+
if (e.target.files?.length) addFiles(e.target.files);
|
|
2462
|
+
e.target.value = "";
|
|
2463
|
+
}
|
|
2286
2464
|
}
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2465
|
+
)
|
|
2466
|
+
]
|
|
2467
|
+
}
|
|
2468
|
+
),
|
|
2469
|
+
rejected && /* @__PURE__ */ jsx(InlineFieldError, { id: rejectedId, message: rejected, className: "mt-0" })
|
|
2470
|
+
] }),
|
|
2471
|
+
files.length > 0 && /* @__PURE__ */ jsx("ul", { className: "space-y-ds-2", children: files.map((entry) => /* @__PURE__ */ jsxs(
|
|
2293
2472
|
"li",
|
|
2294
2473
|
{
|
|
2295
2474
|
className: cn(
|
|
@@ -2306,10 +2485,10 @@ function FileUpload({ onFiles, accept, multiple = true, maxSize, className, erro
|
|
|
2306
2485
|
entry.status === "uploading" && /* @__PURE__ */ jsx(Progress, { value: entry.progress, size: "sm", className: "mt-ds-1" })
|
|
2307
2486
|
] }),
|
|
2308
2487
|
entry.status === "complete" && /* @__PURE__ */ jsx(Icon, { name: "check", size: 16, className: "text-[var(--ds-status-success)] shrink-0" }),
|
|
2309
|
-
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon-sm", onClick: () => removeFile(
|
|
2488
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon-sm", onClick: () => removeFile(entry.id), "aria-label": "Remove file", children: /* @__PURE__ */ jsx(Icon, { name: "close", size: 14 }) })
|
|
2310
2489
|
]
|
|
2311
2490
|
},
|
|
2312
|
-
|
|
2491
|
+
entry.id
|
|
2313
2492
|
)) })
|
|
2314
2493
|
] });
|
|
2315
2494
|
}
|
|
@@ -2324,9 +2503,17 @@ function SliderRange({
|
|
|
2324
2503
|
className,
|
|
2325
2504
|
ref,
|
|
2326
2505
|
error = false,
|
|
2327
|
-
label
|
|
2506
|
+
label,
|
|
2507
|
+
"aria-label": ariaLabel,
|
|
2508
|
+
"aria-describedby": describedByProp,
|
|
2328
2509
|
trackStyle
|
|
2329
2510
|
}) {
|
|
2511
|
+
const field = useField();
|
|
2512
|
+
const ownId = useId();
|
|
2513
|
+
const showLabel = label != null && !field;
|
|
2514
|
+
const nameId = field ? field.labelId : showLabel ? `${ownId}-label` : void 0;
|
|
2515
|
+
const describedBy = [...new Set([describedByProp, field?.describedBy].join(" ").split(" ").filter(Boolean))].join(" ") || void 0;
|
|
2516
|
+
const thumbName = (part) => nameId ? { "aria-labelledby": part ? `${nameId} ${ownId}-${part}` : nameId } : { "aria-label": ariaLabel ? part ? `${ariaLabel} ${part}` : ariaLabel : void 0 };
|
|
2330
2517
|
const isRange = Array.isArray(value);
|
|
2331
2518
|
const low = isRange ? value[0] : value;
|
|
2332
2519
|
const high = isRange ? value[1] : value;
|
|
@@ -2382,7 +2569,14 @@ function SliderRange({
|
|
|
2382
2569
|
[disabled, min, max, handleChange]
|
|
2383
2570
|
);
|
|
2384
2571
|
return /* @__PURE__ */ jsxs("div", { ref, className: cn("space-y-ds-1", disabled && "opacity-disabled", className), children: [
|
|
2385
|
-
showValue && /* @__PURE__ */
|
|
2572
|
+
(showLabel || showValue) && /* @__PURE__ */ jsxs("div", { className: cn("flex items-center gap-ds-2", showLabel ? "justify-between" : "justify-end"), children: [
|
|
2573
|
+
showLabel && /* @__PURE__ */ jsx("span", { id: `${ownId}-label`, className: "text-sm font-medium leading-none text-[var(--ds-text-secondary)]", children: label }),
|
|
2574
|
+
showValue && /* @__PURE__ */ jsx("span", { className: "text-xs font-mono tabular-nums text-[var(--ds-text-muted)]", children: isRange ? `${low} \u2013 ${high}` : low })
|
|
2575
|
+
] }),
|
|
2576
|
+
isRange && nameId && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2577
|
+
/* @__PURE__ */ jsx("span", { id: `${ownId}-minimum`, hidden: true, children: "minimum" }),
|
|
2578
|
+
/* @__PURE__ */ jsx("span", { id: `${ownId}-maximum`, hidden: true, children: "maximum" })
|
|
2579
|
+
] }),
|
|
2386
2580
|
/* @__PURE__ */ jsxs(
|
|
2387
2581
|
"div",
|
|
2388
2582
|
{
|
|
@@ -2405,7 +2599,8 @@ function SliderRange({
|
|
|
2405
2599
|
{
|
|
2406
2600
|
role: "slider",
|
|
2407
2601
|
tabIndex: disabled ? -1 : 0,
|
|
2408
|
-
"
|
|
2602
|
+
...thumbName("minimum"),
|
|
2603
|
+
"aria-describedby": describedBy,
|
|
2409
2604
|
"aria-valuemin": min,
|
|
2410
2605
|
"aria-valuemax": high,
|
|
2411
2606
|
"aria-valuenow": low,
|
|
@@ -2429,7 +2624,8 @@ function SliderRange({
|
|
|
2429
2624
|
{
|
|
2430
2625
|
role: "slider",
|
|
2431
2626
|
tabIndex: disabled ? -1 : 0,
|
|
2432
|
-
|
|
2627
|
+
...thumbName(isRange ? "maximum" : void 0),
|
|
2628
|
+
"aria-describedby": describedBy,
|
|
2433
2629
|
"aria-valuemin": isRange ? low : min,
|
|
2434
2630
|
"aria-valuemax": max,
|
|
2435
2631
|
"aria-valuenow": high,
|
|
@@ -2605,10 +2801,10 @@ function TimePicker({
|
|
|
2605
2801
|
setOpen(false);
|
|
2606
2802
|
};
|
|
2607
2803
|
const keyHandler = (e) => {
|
|
2608
|
-
if (e.key
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2804
|
+
if (e.key !== "Escape" || e.defaultPrevented || !isTopLayer()) return;
|
|
2805
|
+
e.preventDefault();
|
|
2806
|
+
returnFocus();
|
|
2807
|
+
setOpen(false);
|
|
2612
2808
|
};
|
|
2613
2809
|
document.addEventListener("mousedown", handler);
|
|
2614
2810
|
document.addEventListener("keydown", keyHandler);
|
|
@@ -3246,14 +3442,14 @@ function TagInput({
|
|
|
3246
3442
|
);
|
|
3247
3443
|
const containerRef = useRef(null);
|
|
3248
3444
|
const pendingFocus = useRef(null);
|
|
3249
|
-
const tagCount = value.length;
|
|
3250
3445
|
useLayoutEffect(() => {
|
|
3251
|
-
const
|
|
3252
|
-
if (
|
|
3446
|
+
const pending = pendingFocus.current;
|
|
3447
|
+
if (pending === null) return;
|
|
3253
3448
|
pendingFocus.current = null;
|
|
3449
|
+
if (value.includes(pending.tag)) return;
|
|
3254
3450
|
const buttons = Array.from(containerRef.current?.querySelectorAll("[data-tag-remove]") ?? []);
|
|
3255
|
-
focusAfterRemoval(buttons, index, inputRef.current);
|
|
3256
|
-
}
|
|
3451
|
+
focusAfterRemoval(buttons, pending.index, inputRef.current);
|
|
3452
|
+
});
|
|
3257
3453
|
const handleKeyDown = useCallback(
|
|
3258
3454
|
(e) => {
|
|
3259
3455
|
if ((e.key === "Enter" || e.key === ",") && input.trim()) {
|
|
@@ -3308,7 +3504,7 @@ function TagInput({
|
|
|
3308
3504
|
Tag,
|
|
3309
3505
|
{
|
|
3310
3506
|
onRemove: disabled ? void 0 : () => {
|
|
3311
|
-
pendingFocus.current = i;
|
|
3507
|
+
pendingFocus.current = { index: i, tag };
|
|
3312
3508
|
removeTag(tag);
|
|
3313
3509
|
},
|
|
3314
3510
|
disabled,
|
|
@@ -3837,60 +4033,6 @@ function ColorPicker({
|
|
|
3837
4033
|
}
|
|
3838
4034
|
);
|
|
3839
4035
|
}
|
|
3840
|
-
function InlineFieldError({ message, id, className }) {
|
|
3841
|
-
return /* @__PURE__ */ jsxs(
|
|
3842
|
-
"p",
|
|
3843
|
-
{
|
|
3844
|
-
id,
|
|
3845
|
-
role: "alert",
|
|
3846
|
-
className: cn(
|
|
3847
|
-
"flex items-center gap-ds-1.5 text-xs text-[var(--ds-status-error)]",
|
|
3848
|
-
"mt-ds-1.5",
|
|
3849
|
-
className
|
|
3850
|
-
),
|
|
3851
|
-
children: [
|
|
3852
|
-
/* @__PURE__ */ jsx(Icon, { name: "error", size: 14, className: "shrink-0" }),
|
|
3853
|
-
/* @__PURE__ */ jsx("span", { children: message })
|
|
3854
|
-
]
|
|
3855
|
-
}
|
|
3856
|
-
);
|
|
3857
|
-
}
|
|
3858
|
-
function Field({ label, children, hint, error, required, labelAction, id: idProp, className }) {
|
|
3859
|
-
const generated = useId();
|
|
3860
|
-
const id = idProp ?? `field-${generated}`;
|
|
3861
|
-
const hintId = `${id}-hint`;
|
|
3862
|
-
const errorId = `${id}-error`;
|
|
3863
|
-
const hasError = typeof error === "string" && error.length > 0;
|
|
3864
|
-
const describedBy = [hint ? hintId : null, hasError ? errorId : null].filter(Boolean).join(" ") || void 0;
|
|
3865
|
-
const controlProps = {
|
|
3866
|
-
id,
|
|
3867
|
-
"aria-describedby": describedBy,
|
|
3868
|
-
...hasError ? { "aria-invalid": true, error: true } : {},
|
|
3869
|
-
...required ? { "aria-required": true, required: true } : {}
|
|
3870
|
-
};
|
|
3871
|
-
let control;
|
|
3872
|
-
if (typeof children === "function") {
|
|
3873
|
-
control = children(controlProps);
|
|
3874
|
-
} else {
|
|
3875
|
-
const child = Children.only(children);
|
|
3876
|
-
if (!isValidElement(child)) throw new Error("Field expects a single form control as its child");
|
|
3877
|
-
const isNative = typeof child.type === "string";
|
|
3878
|
-
const { error: errorFlag, ...ariaOnly } = controlProps;
|
|
3879
|
-
control = cloneElement(child, isNative ? ariaOnly : { ...ariaOnly, ...errorFlag ? { error: true } : {} });
|
|
3880
|
-
}
|
|
3881
|
-
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-ds-1.5", className), children: [
|
|
3882
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-ds-2", children: [
|
|
3883
|
-
/* @__PURE__ */ jsxs(Label, { htmlFor: id, children: [
|
|
3884
|
-
label,
|
|
3885
|
-
required && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "ml-ds-0.5 text-[var(--ds-status-error)]", children: "*" })
|
|
3886
|
-
] }),
|
|
3887
|
-
labelAction
|
|
3888
|
-
] }),
|
|
3889
|
-
control,
|
|
3890
|
-
hint && /* @__PURE__ */ jsx("p", { id: hintId, className: "text-xs text-[var(--ds-text-muted)]", children: hint }),
|
|
3891
|
-
hasError && /* @__PURE__ */ jsx(InlineFieldError, { id: errorId, message: error, className: "mt-0" })
|
|
3892
|
-
] });
|
|
3893
|
-
}
|
|
3894
4036
|
var variantStyles2 = {
|
|
3895
4037
|
info: "border-[var(--ds-accent-primary)] bg-[var(--ds-accent-subtle)]",
|
|
3896
4038
|
success: "border-[var(--ds-status-success)] bg-[var(--ds-status-success-bg)]",
|
|
@@ -4566,7 +4708,6 @@ function MentionInput({
|
|
|
4566
4708
|
const value = controlledValue ?? internalValue;
|
|
4567
4709
|
const [query, setQuery] = useState("");
|
|
4568
4710
|
const [showPopup, setShowPopup] = useState(false);
|
|
4569
|
-
useOverlayLayer(showPopup);
|
|
4570
4711
|
const [activeIndex, setActiveIndex] = useState(0);
|
|
4571
4712
|
const [triggerStart, setTriggerStart] = useState(-1);
|
|
4572
4713
|
const [popupPos, setPopupPos] = useState({ top: 0, left: 0 });
|
|
@@ -4576,6 +4717,7 @@ function MentionInput({
|
|
|
4576
4717
|
const filtered = suggestions.filter(
|
|
4577
4718
|
(s) => s.label.toLowerCase().includes(query.toLowerCase()) || s.description?.toLowerCase().includes(query.toLowerCase())
|
|
4578
4719
|
);
|
|
4720
|
+
useOverlayLayer(showPopup && filtered.length > 0);
|
|
4579
4721
|
const updateValue = useCallback(
|
|
4580
4722
|
(v) => {
|
|
4581
4723
|
if (controlledValue === void 0) setInternalValue(v);
|
|
@@ -4654,6 +4796,7 @@ function MentionInput({
|
|
|
4654
4796
|
e.preventDefault();
|
|
4655
4797
|
insertMention(filtered[activeIndex]);
|
|
4656
4798
|
} else if (e.key === "Escape") {
|
|
4799
|
+
e.preventDefault();
|
|
4657
4800
|
setShowPopup(false);
|
|
4658
4801
|
}
|
|
4659
4802
|
};
|
|
@@ -4757,6 +4900,8 @@ function MentionInput({
|
|
|
4757
4900
|
] });
|
|
4758
4901
|
}
|
|
4759
4902
|
var CardCtx = createContext(false);
|
|
4903
|
+
var CardActionCtx = createContext(null);
|
|
4904
|
+
var CARD_ACTION_ATTR = "data-card-action";
|
|
4760
4905
|
var CardSurfaceCtx = createContext(false);
|
|
4761
4906
|
var variantStyles3 = {
|
|
4762
4907
|
default: `bg-[var(--ds-bg-surface)] border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)] ${GLASS_SURFACE}`,
|
|
@@ -4876,11 +5021,33 @@ var Card = forwardRef(function Card2({
|
|
|
4876
5021
|
const isSelectable = selectable || !!selectType;
|
|
4877
5022
|
const isToggle = selectType === "toggle";
|
|
4878
5023
|
const hasSelectType = !!selectType;
|
|
5024
|
+
const isAction = !isSelectable && variant === "interactive" && !!onClick;
|
|
5025
|
+
const rootRef = useRef(null);
|
|
5026
|
+
const setRoot = useCallback((node) => {
|
|
5027
|
+
rootRef.current = node;
|
|
5028
|
+
if (typeof ref === "function") ref(node);
|
|
5029
|
+
else if (ref) ref.current = node;
|
|
5030
|
+
}, [ref]);
|
|
5031
|
+
const [inner, setInner] = useState({ controls: false, titleId: null });
|
|
5032
|
+
useLayoutEffect(() => {
|
|
5033
|
+
const root = rootRef.current;
|
|
5034
|
+
if (!root || !isAction && !checkable) return;
|
|
5035
|
+
const controls = Array.from(root.querySelectorAll(FOCUSABLE)).some((el) => !el.hasAttribute(CARD_ACTION_ATTR));
|
|
5036
|
+
const titleId = root.querySelector("[data-card-title]")?.id || null;
|
|
5037
|
+
setInner((cur) => cur.controls === controls && cur.titleId === titleId ? cur : { controls, titleId });
|
|
5038
|
+
}, [isAction, checkable, loading, children]);
|
|
5039
|
+
const holdsControls = isAction && inner.controls;
|
|
5040
|
+
const action = useMemo(() => holdsControls ? { activate: () => rootRef.current?.click(), disabled } : null, [holdsControls, disabled]);
|
|
4879
5041
|
const handleClick = isSelectable && !disabled ? (e) => {
|
|
4880
5042
|
onSelectedChange?.(!selected);
|
|
4881
5043
|
onClick?.(e);
|
|
4882
|
-
} : onClick;
|
|
4883
|
-
const
|
|
5044
|
+
} : isAction && disabled ? void 0 : onClick;
|
|
5045
|
+
const handleCardClick = (e) => {
|
|
5046
|
+
if (disabled || !(e.target instanceof Element) || !e.currentTarget.contains(e.target)) return;
|
|
5047
|
+
const control = e.target.closest(FOCUSABLE);
|
|
5048
|
+
if (control && control !== e.currentTarget && e.currentTarget.contains(control)) return;
|
|
5049
|
+
onClick?.(e);
|
|
5050
|
+
};
|
|
4884
5051
|
const handleKeyDown = isSelectable && !disabled ? (e) => {
|
|
4885
5052
|
if (e.key === "Enter" || e.key === " ") {
|
|
4886
5053
|
e.preventDefault();
|
|
@@ -4903,7 +5070,8 @@ var Card = forwardRef(function Card2({
|
|
|
4903
5070
|
e.stopPropagation();
|
|
4904
5071
|
onSelectedChange?.(!selected);
|
|
4905
5072
|
};
|
|
4906
|
-
const
|
|
5073
|
+
const isButton = isSelectable || isAction && !holdsControls;
|
|
5074
|
+
const role = selectType === "radio" ? "radio" : selectType === "checkbox" ? "checkbox" : selectType === "toggle" ? "switch" : isButton ? "button" : void 0;
|
|
4907
5075
|
const ariaAttr = selectType === "radio" || selectType === "checkbox" || selectType === "toggle" ? { "aria-checked": !!selected } : isSelectable ? { "aria-pressed": !!selected } : {};
|
|
4908
5076
|
const showRing = isSelectable && !isToggle && !hasSelectType && selected;
|
|
4909
5077
|
const indicator = selectType === "radio" ? /* @__PURE__ */ jsx(RadioIndicator, { checked: !!selected, disabled }) : selectType === "checkbox" ? /* @__PURE__ */ jsx(CheckboxIndicator, { checked: !!selected, indeterminate, disabled }) : selectType === "toggle" ? /* @__PURE__ */ jsx(ToggleIndicator, { checked: !!selected, disabled }) : null;
|
|
@@ -4911,11 +5079,11 @@ var Card = forwardRef(function Card2({
|
|
|
4911
5079
|
return /* @__PURE__ */ jsxs(
|
|
4912
5080
|
"div",
|
|
4913
5081
|
{
|
|
4914
|
-
ref,
|
|
5082
|
+
ref: setRoot,
|
|
4915
5083
|
"data-card": "",
|
|
4916
5084
|
role,
|
|
4917
|
-
tabIndex: isSelectable ||
|
|
4918
|
-
"aria-disabled":
|
|
5085
|
+
tabIndex: isSelectable || isButton && !disabled ? 0 : void 0,
|
|
5086
|
+
"aria-disabled": isButton && disabled ? true : void 0,
|
|
4919
5087
|
...name && selectType === "radio" ? { "data-radio-group": name } : {},
|
|
4920
5088
|
style: variant === "interactive" ? { ...INTERACTIVE_SCALE_CAP, ...style } : style,
|
|
4921
5089
|
className: cn(
|
|
@@ -4923,15 +5091,15 @@ var Card = forwardRef(function Card2({
|
|
|
4923
5091
|
variantStyles3[variant],
|
|
4924
5092
|
isSelectable && !disabled && "cursor-pointer select-none transition-all duration-[var(--ds-transition-speed)] ease-[var(--ds-transition-ease)]",
|
|
4925
5093
|
isSelectable && !disabled && !selected && "hover:border-[var(--ds-accent-primary)]",
|
|
4926
|
-
|
|
5094
|
+
isButton && "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
|
|
4927
5095
|
(showRing || hasSelectType && selected) && ACTIVE_RING,
|
|
4928
5096
|
hasSelectType && selected && "bg-[var(--ds-accent-subtle)]",
|
|
4929
5097
|
disabled && "opacity-disabled cursor-not-allowed",
|
|
4930
5098
|
className
|
|
4931
5099
|
),
|
|
4932
|
-
onClick: handleClick,
|
|
4933
|
-
onKeyDown: handleKeyDown,
|
|
4934
|
-
onKeyUp: handleKeyUp,
|
|
5100
|
+
onClick: holdsControls ? handleCardClick : handleClick,
|
|
5101
|
+
onKeyDown: holdsControls ? void 0 : handleKeyDown,
|
|
5102
|
+
onKeyUp: holdsControls ? void 0 : handleKeyUp,
|
|
4935
5103
|
...ariaAttr,
|
|
4936
5104
|
...props,
|
|
4937
5105
|
children: [
|
|
@@ -4944,6 +5112,7 @@ var Card = forwardRef(function Card2({
|
|
|
4944
5112
|
onClick: handleCheckableClick,
|
|
4945
5113
|
role: "checkbox",
|
|
4946
5114
|
"aria-checked": !!selected,
|
|
5115
|
+
...inner.titleId ? { "aria-labelledby": inner.titleId } : { "aria-label": "Select" },
|
|
4947
5116
|
tabIndex: 0,
|
|
4948
5117
|
onKeyDown: (e) => {
|
|
4949
5118
|
if (e.key === "Enter" || e.key === " ") {
|
|
@@ -4955,7 +5124,7 @@ var Card = forwardRef(function Card2({
|
|
|
4955
5124
|
children: /* @__PURE__ */ jsx(CheckboxIndicator, { checked: !!selected })
|
|
4956
5125
|
}
|
|
4957
5126
|
),
|
|
4958
|
-
/* @__PURE__ */ jsx(CardSurfaceCtx, { value: true, children: /* @__PURE__ */ jsx(CardCtx, { value: checkable, children: loading ? (
|
|
5127
|
+
/* @__PURE__ */ jsx(CardSurfaceCtx, { value: true, children: /* @__PURE__ */ jsx(CardActionCtx, { value: action, children: /* @__PURE__ */ jsx(CardCtx, { value: checkable, children: loading ? (
|
|
4959
5128
|
// Round 15 (the owner's 52.1): a placeholder stands where the content will stand — inside the card's
|
|
4960
5129
|
// content inset (CardContent's padding), a title-height line then two body lines — never flush against the border.
|
|
4961
5130
|
/* @__PURE__ */ jsx("div", { "data-card-loading": "", "aria-busy": "true", className: cn("pr-ds-5 py-ds-4", checkable ? "pl-ds-10" : "pl-ds-5"), children: /* @__PURE__ */ jsxs("div", { className: "space-y-ds-3", children: [
|
|
@@ -4963,7 +5132,7 @@ var Card = forwardRef(function Card2({
|
|
|
4963
5132
|
/* @__PURE__ */ jsx(Skeleton, { variant: "custom", className: "h-3 w-full" }),
|
|
4964
5133
|
/* @__PURE__ */ jsx(Skeleton, { variant: "custom", className: "h-3 w-2/3" })
|
|
4965
5134
|
] }) })
|
|
4966
|
-
) : children }) })
|
|
5135
|
+
) : children }) }) })
|
|
4967
5136
|
]
|
|
4968
5137
|
}
|
|
4969
5138
|
);
|
|
@@ -4981,6 +5150,7 @@ var CardHeader = forwardRef(function CardHeader2({ className, children, actions,
|
|
|
4981
5150
|
"button",
|
|
4982
5151
|
{
|
|
4983
5152
|
type: "button",
|
|
5153
|
+
"aria-label": "More actions",
|
|
4984
5154
|
className: cn(
|
|
4985
5155
|
"inline-flex items-center justify-center h-8 w-8 rounded-[var(--ds-radius-md)]",
|
|
4986
5156
|
"text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] hover:bg-[var(--ds-bg-muted)]",
|
|
@@ -5017,20 +5187,42 @@ var cardLineClampClasses = {
|
|
|
5017
5187
|
5: "line-clamp-5",
|
|
5018
5188
|
6: "line-clamp-6"
|
|
5019
5189
|
};
|
|
5020
|
-
var CardTitle = forwardRef(function CardTitle2({ className, children, lines = 2, ...props }, forwardedRef) {
|
|
5190
|
+
var CardTitle = forwardRef(function CardTitle2({ className, children, lines = 2, id, ...props }, forwardedRef) {
|
|
5191
|
+
const action = useContext(CardActionCtx);
|
|
5192
|
+
const generatedId = useId();
|
|
5021
5193
|
const { ref: overflowRef, isTruncated } = useTextOverflow({ lines, disabled: lines === 0 });
|
|
5022
5194
|
const lineClampClass = lines > 0 ? cardLineClampClasses[lines] || `line-clamp-[${lines}]` : "";
|
|
5023
5195
|
const heading = /* @__PURE__ */ jsx(
|
|
5024
5196
|
"h3",
|
|
5025
5197
|
{
|
|
5026
5198
|
ref: (node) => {
|
|
5027
|
-
overflowRef(lines > 0 ? node : null);
|
|
5199
|
+
if (!action) overflowRef(lines > 0 ? node : null);
|
|
5028
5200
|
if (typeof forwardedRef === "function") forwardedRef(node);
|
|
5029
5201
|
else if (forwardedRef) forwardedRef.current = node;
|
|
5030
5202
|
},
|
|
5031
|
-
|
|
5203
|
+
id: id ?? generatedId,
|
|
5204
|
+
"data-card-title": "",
|
|
5205
|
+
className: cn("font-[family-name:var(--ds-font-display)] text-lg font-[var(--ds-weight-heading)] text-[var(--ds-text-primary)]", !action && lines > 0 && lineClampClass, className),
|
|
5032
5206
|
...props,
|
|
5033
|
-
children
|
|
5207
|
+
children: action ? (
|
|
5208
|
+
// The card's action when the card holds controls of its own (Sprint 25.7): a real button, which a click
|
|
5209
|
+
// elsewhere on the card also runs. Its ::after spans the card to draw the card's focus ring, taking no clicks.
|
|
5210
|
+
// The clamp moves inside, since a button lays out its own lines.
|
|
5211
|
+
/* @__PURE__ */ jsx(
|
|
5212
|
+
"button",
|
|
5213
|
+
{
|
|
5214
|
+
type: "button",
|
|
5215
|
+
...{ [CARD_ACTION_ATTR]: "" },
|
|
5216
|
+
disabled: action.disabled,
|
|
5217
|
+
onClick: action.activate,
|
|
5218
|
+
className: cn(
|
|
5219
|
+
"block w-full text-left cursor-pointer [text-overflow:inherit] focus-visible:outline-none disabled:cursor-not-allowed",
|
|
5220
|
+
"after:absolute after:inset-0 after:rounded-[var(--ds-radius-md)] after:pointer-events-none focus-visible:after:shadow-[var(--ds-shadow-focus)]"
|
|
5221
|
+
),
|
|
5222
|
+
children: /* @__PURE__ */ jsx("span", { ref: (node) => overflowRef(lines > 0 ? node : null), className: cn("block [text-overflow:inherit]", lines > 0 && lineClampClass), children })
|
|
5223
|
+
}
|
|
5224
|
+
)
|
|
5225
|
+
) : children
|
|
5034
5226
|
}
|
|
5035
5227
|
);
|
|
5036
5228
|
if (!isTruncated || lines === 0) return heading;
|
|
@@ -6047,10 +6239,10 @@ function OverflowPopover({
|
|
|
6047
6239
|
if (!open) return;
|
|
6048
6240
|
const raf = requestAnimationFrame(() => focusInitial(floatingRef.current));
|
|
6049
6241
|
const onKey = (e) => {
|
|
6050
|
-
if (e.key
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
|
|
6242
|
+
if (e.key !== "Escape" || e.defaultPrevented || !isTop()) return;
|
|
6243
|
+
e.preventDefault();
|
|
6244
|
+
returnFocus();
|
|
6245
|
+
setOpen(false);
|
|
6054
6246
|
};
|
|
6055
6247
|
const onDown = (e) => {
|
|
6056
6248
|
if (!triggerRef.current?.contains(e.target)) setOpen(false);
|
|
@@ -6705,6 +6897,9 @@ function CompactDayCell({
|
|
|
6705
6897
|
const today = isCurrentMonth && isToday(day);
|
|
6706
6898
|
const selected = isCurrentMonth && selectedDate && isSameDay2(day, selectedDate);
|
|
6707
6899
|
const hasEvents = dayEvents.length > 0;
|
|
6900
|
+
const shown = open && hasEvents;
|
|
6901
|
+
const isTop = useOverlayLayer(shown);
|
|
6902
|
+
useCloseOnFocusOut(shown, cellRef, floatingRef, () => setOpen(false), isTop);
|
|
6708
6903
|
const eventColors = dayEvents.slice(0, 3).map((e) => categories[e.category ?? ""]?.color ?? "var(--ds-accent-primary)");
|
|
6709
6904
|
const handleMouseEnter = () => {
|
|
6710
6905
|
if (!hasEvents) return;
|
|
@@ -6723,7 +6918,9 @@ function CompactDayCell({
|
|
|
6723
6918
|
useEffect(() => {
|
|
6724
6919
|
if (!open) return;
|
|
6725
6920
|
const onKey = (e) => {
|
|
6726
|
-
if (e.key
|
|
6921
|
+
if (e.key !== "Escape" || e.defaultPrevented || !isTop()) return;
|
|
6922
|
+
e.preventDefault();
|
|
6923
|
+
setOpen(false);
|
|
6727
6924
|
};
|
|
6728
6925
|
const onClick = (e) => {
|
|
6729
6926
|
const t = e.target;
|
|
@@ -6736,7 +6933,7 @@ function CompactDayCell({
|
|
|
6736
6933
|
document.removeEventListener("keydown", onKey);
|
|
6737
6934
|
document.removeEventListener("mousedown", onClick);
|
|
6738
6935
|
};
|
|
6739
|
-
}, [open]);
|
|
6936
|
+
}, [open, isTop]);
|
|
6740
6937
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
6741
6938
|
/* @__PURE__ */ jsxs(
|
|
6742
6939
|
"button",
|
|
@@ -6908,10 +7105,10 @@ function MonthDropdown({
|
|
|
6908
7105
|
(panel?.querySelector("[data-current]") ?? panel?.querySelector("button"))?.focus();
|
|
6909
7106
|
});
|
|
6910
7107
|
const onKey = (e) => {
|
|
6911
|
-
if (e.key
|
|
6912
|
-
|
|
6913
|
-
|
|
6914
|
-
|
|
7108
|
+
if (e.key !== "Escape" || e.defaultPrevented || !isTop()) return;
|
|
7109
|
+
e.preventDefault();
|
|
7110
|
+
returnFocus();
|
|
7111
|
+
setOpen(false);
|
|
6915
7112
|
};
|
|
6916
7113
|
const onClick = (e) => {
|
|
6917
7114
|
const t = e.target;
|
|
@@ -7042,10 +7239,10 @@ function YearDropdown({
|
|
|
7042
7239
|
(panel?.querySelector("[data-current]") ?? panel?.querySelector("button"))?.focus();
|
|
7043
7240
|
});
|
|
7044
7241
|
const onKey = (e) => {
|
|
7045
|
-
if (e.key
|
|
7046
|
-
|
|
7047
|
-
|
|
7048
|
-
|
|
7242
|
+
if (e.key !== "Escape" || e.defaultPrevented || !isTop()) return;
|
|
7243
|
+
e.preventDefault();
|
|
7244
|
+
returnFocus();
|
|
7245
|
+
setOpen(false);
|
|
7049
7246
|
};
|
|
7050
7247
|
const onClick = (e) => {
|
|
7051
7248
|
const t = e.target;
|
|
@@ -9127,9 +9324,1234 @@ var StackedList = forwardRef(function StackedList2({
|
|
|
9127
9324
|
}
|
|
9128
9325
|
);
|
|
9129
9326
|
});
|
|
9130
|
-
var
|
|
9131
|
-
var
|
|
9132
|
-
|
|
9327
|
+
var TIME_ZOOMS = ["day", "week", "month", "quarter"];
|
|
9328
|
+
var DAY_MS = 864e5;
|
|
9329
|
+
var DATE_ONLY = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
9330
|
+
var utcDay = (y, m, d) => Math.round(Date.UTC(y, m, d) / DAY_MS);
|
|
9331
|
+
function dayOfDate(d) {
|
|
9332
|
+
if (Number.isNaN(d.getTime())) return NaN;
|
|
9333
|
+
const ms = ((d.getHours() * 60 + d.getMinutes()) * 60 + d.getSeconds()) * 1e3 + d.getMilliseconds();
|
|
9334
|
+
return utcDay(d.getFullYear(), d.getMonth(), d.getDate()) + ms / DAY_MS;
|
|
9335
|
+
}
|
|
9336
|
+
function dayOf(value) {
|
|
9337
|
+
if (value instanceof Date) return dayOfDate(value);
|
|
9338
|
+
const m = DATE_ONLY.exec(value.trim());
|
|
9339
|
+
if (!m) return dayOfDate(new Date(value));
|
|
9340
|
+
const [y, mo, d] = [Number(m[1]), Number(m[2]) - 1, Number(m[3])];
|
|
9341
|
+
const day = utcDay(y, mo, d);
|
|
9342
|
+
const back = new Date(day * DAY_MS);
|
|
9343
|
+
return back.getUTCFullYear() === y && back.getUTCMonth() === mo && back.getUTCDate() === d ? day : NaN;
|
|
9344
|
+
}
|
|
9345
|
+
function dateOfDay(day) {
|
|
9346
|
+
const whole = Math.floor(day);
|
|
9347
|
+
const u = new Date(whole * DAY_MS);
|
|
9348
|
+
return new Date(u.getUTCFullYear(), u.getUTCMonth(), u.getUTCDate(), 0, 0, 0, Math.round((day - whole) * DAY_MS));
|
|
9349
|
+
}
|
|
9350
|
+
function endDayOf(value) {
|
|
9351
|
+
const day = dayOf(value);
|
|
9352
|
+
const wholeDay2 = typeof value === "string" ? DATE_ONLY.test(value.trim()) : Number.isInteger(day);
|
|
9353
|
+
return wholeDay2 ? day + 1 : day;
|
|
9354
|
+
}
|
|
9355
|
+
var MINOR = { day: "day", week: "week", month: "month", quarter: "quarter" };
|
|
9356
|
+
var MAJOR = { day: "month", week: "month", month: "year", quarter: "year" };
|
|
9357
|
+
var weekday = (day) => (day % 7 + 7 + 4) % 7;
|
|
9358
|
+
function periodStart(unit, day, weekStartsOn) {
|
|
9359
|
+
const d = Math.floor(day);
|
|
9360
|
+
if (unit === "day") return d;
|
|
9361
|
+
if (unit === "week") return d - (weekday(d) - weekStartsOn + 7) % 7;
|
|
9362
|
+
const u = new Date(d * DAY_MS);
|
|
9363
|
+
const y = u.getUTCFullYear();
|
|
9364
|
+
const m = u.getUTCMonth();
|
|
9365
|
+
if (unit === "month") return utcDay(y, m, 1);
|
|
9366
|
+
if (unit === "quarter") return utcDay(y, m - m % 3, 1);
|
|
9367
|
+
return utcDay(y, 0, 1);
|
|
9368
|
+
}
|
|
9369
|
+
function periodAfter(unit, start) {
|
|
9370
|
+
if (unit === "day") return start + 1;
|
|
9371
|
+
if (unit === "week") return start + 7;
|
|
9372
|
+
const u = new Date(start * DAY_MS);
|
|
9373
|
+
const y = u.getUTCFullYear();
|
|
9374
|
+
const m = u.getUTCMonth();
|
|
9375
|
+
if (unit === "month") return utcDay(y, m + 1, 1);
|
|
9376
|
+
if (unit === "quarter") return utcDay(y, m + 3, 1);
|
|
9377
|
+
return utcDay(y + 1, 0, 1);
|
|
9378
|
+
}
|
|
9379
|
+
var formats = /* @__PURE__ */ new Map();
|
|
9380
|
+
function formatter(locale, options) {
|
|
9381
|
+
const key = `${locale ?? ""}|${JSON.stringify(options)}`;
|
|
9382
|
+
let f = formats.get(key);
|
|
9383
|
+
if (!f) {
|
|
9384
|
+
f = new Intl.DateTimeFormat(locale, { ...options, timeZone: "UTC" });
|
|
9385
|
+
formats.set(key, f);
|
|
9386
|
+
}
|
|
9387
|
+
return f;
|
|
9388
|
+
}
|
|
9389
|
+
function format(day, locale, options) {
|
|
9390
|
+
return formatter(locale, options).format(new Date(day * DAY_MS));
|
|
9391
|
+
}
|
|
9392
|
+
var pad22 = (n) => String(n).padStart(2, "0");
|
|
9393
|
+
function keyOf(unit, day) {
|
|
9394
|
+
const u = new Date(day * DAY_MS);
|
|
9395
|
+
const y = u.getUTCFullYear();
|
|
9396
|
+
const m = u.getUTCMonth();
|
|
9397
|
+
if (unit === "year") return String(y);
|
|
9398
|
+
if (unit === "quarter") return `${y}-Q${Math.floor(m / 3) + 1}`;
|
|
9399
|
+
if (unit === "month") return `${y}-${pad22(m + 1)}`;
|
|
9400
|
+
return `${y}-${pad22(m + 1)}-${pad22(u.getUTCDate())}`;
|
|
9401
|
+
}
|
|
9402
|
+
function labelsOf(unit, role, day, locale) {
|
|
9403
|
+
const f = (options) => format(day, locale, options);
|
|
9404
|
+
let forms;
|
|
9405
|
+
if (unit === "day") forms = [f({ weekday: "short", day: "numeric" }), f({ day: "numeric" })];
|
|
9406
|
+
else if (unit === "week") forms = [f({ day: "numeric", month: "short" }), f({ day: "numeric" })];
|
|
9407
|
+
else if (unit === "month") {
|
|
9408
|
+
forms = role === "major" ? [f({ month: "long", year: "numeric" }), f({ month: "short", year: "numeric" }), f({ month: "short" }), f({ month: "narrow" })] : [f({ month: "long" }), f({ month: "short" }), f({ month: "narrow" })];
|
|
9409
|
+
} else if (unit === "quarter") forms = [`Q${Math.floor(new Date(day * DAY_MS).getUTCMonth() / 3) + 1}`];
|
|
9410
|
+
else forms = [f({ year: "numeric" }), f({ year: "2-digit" })];
|
|
9411
|
+
return [...new Set(forms)];
|
|
9412
|
+
}
|
|
9413
|
+
var DENSITY = { day: 56, week: 16, month: 4, quarter: 1.5 };
|
|
9414
|
+
var todays = /* @__PURE__ */ new WeakMap();
|
|
9415
|
+
function calendarDay(day) {
|
|
9416
|
+
const d = Math.floor(day);
|
|
9417
|
+
return { day: d, weekday: weekday(d), key: keyOf("day", d) };
|
|
9418
|
+
}
|
|
9419
|
+
function ticks(unit, role, s) {
|
|
9420
|
+
const out = [];
|
|
9421
|
+
for (let p = periodStart(unit, s.startDay, s.weekStartsOn); p < s.endDay; p = periodAfter(unit, p)) {
|
|
9422
|
+
const from = Math.max(p, s.startDay);
|
|
9423
|
+
const to = Math.min(periodAfter(unit, p), s.endDay);
|
|
9424
|
+
out.push({ key: keyOf(unit, p), day: from, x: (from - s.startDay) * s.pxPerDay, width: (to - from) * s.pxPerDay, weekday: weekday(from), labels: labelsOf(unit, role, p, s.locale) });
|
|
9425
|
+
}
|
|
9426
|
+
return out;
|
|
9427
|
+
}
|
|
9428
|
+
function build(s) {
|
|
9429
|
+
const { startDay, pxPerDay } = s;
|
|
9430
|
+
const x = (value) => (startIndex(value) - startDay) * pxPerDay;
|
|
9431
|
+
const inRange = (d) => d >= startDay && d < s.endDay;
|
|
9432
|
+
const todayDay = Math.floor(s.todayValue);
|
|
9433
|
+
const scale = {
|
|
9434
|
+
zoom: s.zoom,
|
|
9435
|
+
startDay,
|
|
9436
|
+
endDay: s.endDay,
|
|
9437
|
+
pxPerDay,
|
|
9438
|
+
width: (s.endDay - startDay) * pxPerDay,
|
|
9439
|
+
x,
|
|
9440
|
+
span: (start, end = start) => {
|
|
9441
|
+
const x0 = x(start);
|
|
9442
|
+
return { x: x0, width: Math.max(0, (endIndex(end) - startDay) * pxPerDay - x0) };
|
|
9443
|
+
},
|
|
9444
|
+
dayAt: (px) => startDay + px / pxPerDay,
|
|
9445
|
+
major: ticks(MAJOR[s.zoom], "major", s),
|
|
9446
|
+
minor: ticks(MINOR[s.zoom], "minor", s),
|
|
9447
|
+
today: inRange(todayDay) ? todayDay : null,
|
|
9448
|
+
now: inRange(s.todayValue) ? s.todayValue : null,
|
|
9449
|
+
weekStartsOn: s.weekStartsOn,
|
|
9450
|
+
locale: s.locale
|
|
9451
|
+
};
|
|
9452
|
+
todays.set(scale, s.todayValue);
|
|
9453
|
+
return scale;
|
|
9454
|
+
}
|
|
9455
|
+
var toMinute = (day) => Math.round(day * 1440) / 1440;
|
|
9456
|
+
var startIndex = (value) => typeof value === "number" ? value : dayOf(value);
|
|
9457
|
+
var endIndex = (value) => typeof value === "number" ? value : endDayOf(value);
|
|
9458
|
+
function scaleOver(from, to, zoom, todayValue, weekStartsOn, locale, pxPerDay, pad3) {
|
|
9459
|
+
const fallback = Number.isFinite(todayValue) ? Math.floor(todayValue) : 0;
|
|
9460
|
+
const a = Number.isFinite(from) ? from : fallback;
|
|
9461
|
+
const b = Number.isFinite(to) ? to : Math.floor(a) + 1;
|
|
9462
|
+
const lo = Math.floor(Math.min(a, b));
|
|
9463
|
+
const hi = Math.max(Math.ceil(Math.max(a, b)), lo + 1);
|
|
9464
|
+
const unit = MINOR[zoom];
|
|
9465
|
+
let startDay = periodStart(unit, lo, weekStartsOn);
|
|
9466
|
+
let endDay = periodAfter(unit, periodStart(unit, hi - 1, weekStartsOn));
|
|
9467
|
+
for (let i = 0; i < pad3; i++) {
|
|
9468
|
+
startDay = periodStart(unit, startDay - 1, weekStartsOn);
|
|
9469
|
+
endDay = periodAfter(unit, endDay);
|
|
9470
|
+
}
|
|
9471
|
+
return build({ zoom, startDay, endDay, pxPerDay: pxPerDay > 0 ? pxPerDay : DENSITY[zoom], todayValue, weekStartsOn, locale });
|
|
9472
|
+
}
|
|
9473
|
+
function extendTimeScale(scale, width) {
|
|
9474
|
+
if (!(width > scale.width) || !(scale.pxPerDay > 0)) return scale;
|
|
9475
|
+
const unit = MINOR[scale.zoom];
|
|
9476
|
+
const last = scale.startDay + Math.ceil(width / scale.pxPerDay) - 1;
|
|
9477
|
+
const endDay = periodAfter(unit, periodStart(unit, last, scale.weekStartsOn));
|
|
9478
|
+
const todayValue = todays.get(scale) ?? scale.now ?? scale.today ?? NaN;
|
|
9479
|
+
return build({ zoom: scale.zoom, startDay: scale.startDay, endDay, pxPerDay: scale.pxPerDay, todayValue, weekStartsOn: scale.weekStartsOn, locale: scale.locale });
|
|
9480
|
+
}
|
|
9481
|
+
function useTimeScale({ start, end, zoom, today, weekStartsOn = 1, locale, pxPerDay, pad: pad3 = 0 }) {
|
|
9482
|
+
const [mounted] = useState(() => toMinute(dayOf(/* @__PURE__ */ new Date())));
|
|
9483
|
+
const from = startIndex(start);
|
|
9484
|
+
const to = endIndex(end);
|
|
9485
|
+
const todayValue = today === void 0 ? mounted : toMinute(dayOf(today));
|
|
9486
|
+
const density = pxPerDay?.[zoom] ?? DENSITY[zoom];
|
|
9487
|
+
return useMemo(() => scaleOver(from, to, zoom, todayValue, weekStartsOn, locale, density, pad3), [from, to, zoom, todayValue, weekStartsOn, locale, density, pad3]);
|
|
9488
|
+
}
|
|
9489
|
+
function timeExtent(ranges) {
|
|
9490
|
+
let start = Infinity;
|
|
9491
|
+
let end = -Infinity;
|
|
9492
|
+
for (const r of ranges) {
|
|
9493
|
+
const s = dayOf(r.start);
|
|
9494
|
+
const e = endDayOf(r.end ?? r.start);
|
|
9495
|
+
if (Number.isFinite(s)) start = Math.min(start, s);
|
|
9496
|
+
if (Number.isFinite(e)) end = Math.max(end, e);
|
|
9497
|
+
}
|
|
9498
|
+
return Number.isFinite(start) && Number.isFinite(end) ? { start, end: Math.max(start, end) } : { start: NaN, end: NaN };
|
|
9499
|
+
}
|
|
9500
|
+
var DAY_WORDS = { day: "numeric", month: "long" };
|
|
9501
|
+
var TIME_WORDS = { hour: "2-digit", minute: "2-digit" };
|
|
9502
|
+
var wholeDay = (value) => typeof value === "string" ? DATE_ONLY.test(value.trim()) : Number.isInteger(value instanceof Date ? dayOf(value) : value);
|
|
9503
|
+
var SEPARATOR = /\s*[\u2010-\u2015\u2212~\uff5e\u301c-]\s*/;
|
|
9504
|
+
var english = (f) => new Intl.Locale(f.resolvedOptions().locale).language === "en";
|
|
9505
|
+
function range(f, a, b, to) {
|
|
9506
|
+
if (typeof f.formatRangeToParts !== "function") return to === void 0 ? f.formatRange(a, b) : `${f.format(a)}${to}${f.format(b)}`;
|
|
9507
|
+
const parts = f.formatRangeToParts(a, b);
|
|
9508
|
+
const lastOfStart = parts.map((p) => p.source).lastIndexOf("startRange");
|
|
9509
|
+
const firstOfEnd = parts.findIndex((p) => p.source === "endRange");
|
|
9510
|
+
if (lastOfStart < 0 || firstOfEnd < 0) return f.format(a);
|
|
9511
|
+
if (to === void 0) return parts.map((p) => p.value).join("");
|
|
9512
|
+
let joined = false;
|
|
9513
|
+
const out = parts.map((p, i) => {
|
|
9514
|
+
if (joined || i <= lastOfStart || i >= firstOfEnd || !SEPARATOR.test(p.value)) return p.value;
|
|
9515
|
+
joined = true;
|
|
9516
|
+
return p.value.replace(SEPARATOR, to);
|
|
9517
|
+
});
|
|
9518
|
+
if (!joined) out.splice(lastOfStart + 1, 0, to);
|
|
9519
|
+
return out.join("");
|
|
9520
|
+
}
|
|
9521
|
+
function formatDateRange(start, end = start, { locale, to, day = DAY_WORDS, time = TIME_WORDS } = {}) {
|
|
9522
|
+
const from = startIndex(start);
|
|
9523
|
+
const stop = endIndex(end);
|
|
9524
|
+
if (!Number.isFinite(from) || !Number.isFinite(stop)) return "";
|
|
9525
|
+
const timed = !wholeDay(start) || !wholeDay(end);
|
|
9526
|
+
const first = timed ? from : Math.floor(from);
|
|
9527
|
+
const last = timed ? Math.max(from, stop) : Math.max(first, Math.ceil(stop) - 1);
|
|
9528
|
+
const a = new Date(first * DAY_MS);
|
|
9529
|
+
const b = new Date(last * DAY_MS);
|
|
9530
|
+
const years = a.getUTCFullYear() !== b.getUTCFullYear() && day.year === void 0;
|
|
9531
|
+
const f = formatter(locale, { ...day, ...years ? { year: "numeric" } : {}, ...timed ? time : {} });
|
|
9532
|
+
return first === last ? f.format(a) : range(f, a, b, to ?? (english(f) ? " to " : void 0));
|
|
9533
|
+
}
|
|
9534
|
+
function createScrollStore() {
|
|
9535
|
+
let current = { scrollLeft: 0, visibleWidth: 0 };
|
|
9536
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
9537
|
+
return {
|
|
9538
|
+
get: () => current,
|
|
9539
|
+
set(next) {
|
|
9540
|
+
if (next.scrollLeft === current.scrollLeft && next.visibleWidth === current.visibleWidth) return;
|
|
9541
|
+
current = next;
|
|
9542
|
+
listeners.forEach((l) => l());
|
|
9543
|
+
},
|
|
9544
|
+
subscribe(listener) {
|
|
9545
|
+
listeners.add(listener);
|
|
9546
|
+
return () => {
|
|
9547
|
+
listeners.delete(listener);
|
|
9548
|
+
};
|
|
9549
|
+
}
|
|
9550
|
+
};
|
|
9551
|
+
}
|
|
9552
|
+
var TimeViewportCtx = createContext(null);
|
|
9553
|
+
var TimeViewportScrollCtx = createContext(null);
|
|
9554
|
+
var UNMEASURED = { scrollLeft: 0, visibleWidth: 0 };
|
|
9555
|
+
var noSubscription = () => () => {
|
|
9556
|
+
};
|
|
9557
|
+
var unmeasured = () => UNMEASURED;
|
|
9558
|
+
function useTimeViewportLayout() {
|
|
9559
|
+
const layout = useContext(TimeViewportCtx);
|
|
9560
|
+
if (!layout) throw new Error("A time-axis row or header must be inside a <TimeViewport>.");
|
|
9561
|
+
return layout;
|
|
9562
|
+
}
|
|
9563
|
+
function useTimeViewport() {
|
|
9564
|
+
const store = useContext(TimeViewportScrollCtx);
|
|
9565
|
+
const scroll = useSyncExternalStore(store?.subscribe ?? noSubscription, store?.get ?? unmeasured, unmeasured);
|
|
9566
|
+
return { ...useTimeViewportLayout(), ...scroll };
|
|
9567
|
+
}
|
|
9568
|
+
function fitLabels(boxes, measure, { gap = 8, align = "start", visible } = {}) {
|
|
9569
|
+
const out = boxes.map((b) => ({ key: b.key, x: b.x, width: 0, text: null }));
|
|
9570
|
+
const n = boxes.length;
|
|
9571
|
+
const levels = boxes.reduce((most, b) => Math.max(most, b.labels.length), 0);
|
|
9572
|
+
if (!n || !levels) return out;
|
|
9573
|
+
const widths = /* @__PURE__ */ new Map();
|
|
9574
|
+
const w = (text) => {
|
|
9575
|
+
let v = widths.get(text);
|
|
9576
|
+
if (v === void 0) {
|
|
9577
|
+
v = Math.max(0, measure(text) || 0);
|
|
9578
|
+
widths.set(text, v);
|
|
9579
|
+
}
|
|
9580
|
+
return v;
|
|
9581
|
+
};
|
|
9582
|
+
const order = boxes.map((_, i) => i).sort((a, b) => boxes[a].x - boxes[b].x || a - b);
|
|
9583
|
+
const rowEnd = order.reduce((end, i) => Math.max(end, boxes[i].x + Math.max(0, boxes[i].width)), -Infinity);
|
|
9584
|
+
const form = (b, level) => b.labels.length ? b.labels[Math.min(level, b.labels.length - 1)] : null;
|
|
9585
|
+
for (let step = 1; step <= n; step++) {
|
|
9586
|
+
const shown = [];
|
|
9587
|
+
for (let k = 0; k < n; k += step) shown.push(order[k]);
|
|
9588
|
+
const room = shown.map((i, s) => ({ start: boxes[i].x, end: s + 1 < shown.length ? boxes[shown[s + 1]].x : rowEnd }));
|
|
9589
|
+
const fits = (s, text) => text === null || w(text) + gap <= room[s].end - room[s].start;
|
|
9590
|
+
const decides = (s) => shown.length < 3 || s > 0 && s < shown.length - 1;
|
|
9591
|
+
let level = -1;
|
|
9592
|
+
for (let l = 0; l < levels && level < 0; l++) {
|
|
9593
|
+
if (shown.every((i, s) => !decides(s) || fits(s, form(boxes[i], l)))) level = l;
|
|
9594
|
+
}
|
|
9595
|
+
if (level < 0) continue;
|
|
9596
|
+
shown.forEach((i, s) => {
|
|
9597
|
+
const b = boxes[i];
|
|
9598
|
+
const lo = Math.max(room[s].start + gap / 2, visible ? visible.start + gap / 2 : -Infinity);
|
|
9599
|
+
const hi = Math.min(room[s].end - gap / 2, visible ? visible.end - gap / 2 : Infinity);
|
|
9600
|
+
let text = null;
|
|
9601
|
+
for (let l = level; l < Math.max(level + 1, b.labels.length) && text === null; l++) {
|
|
9602
|
+
const t = form(b, l);
|
|
9603
|
+
if (t !== null && fits(s, t) && w(t) <= hi - lo) text = t;
|
|
9604
|
+
}
|
|
9605
|
+
if (text === null) return;
|
|
9606
|
+
const tw = w(text);
|
|
9607
|
+
const from = visible ? Math.max(b.x, visible.start) : b.x;
|
|
9608
|
+
const to = Math.min(b.x + Math.max(0, b.width), visible ? visible.end : Infinity);
|
|
9609
|
+
const x = align === "center" ? (from + to) / 2 - tw / 2 : lo;
|
|
9610
|
+
out[i] = { key: b.key, x: Math.min(Math.max(x, lo), hi - tw), width: tw, text };
|
|
9611
|
+
});
|
|
9612
|
+
return out;
|
|
9613
|
+
}
|
|
9614
|
+
return out;
|
|
9615
|
+
}
|
|
9616
|
+
function fitLabel(labels, width, measure, { gap = 8 } = {}) {
|
|
9617
|
+
return labels.find((text) => Math.max(0, measure(text) || 0) + gap <= width) ?? null;
|
|
9618
|
+
}
|
|
9619
|
+
var context;
|
|
9620
|
+
function canvasContext() {
|
|
9621
|
+
if (context !== void 0) return context;
|
|
9622
|
+
context = null;
|
|
9623
|
+
if (typeof document === "undefined" || typeof navigator !== "undefined" && /jsdom/i.test(navigator.userAgent)) return context;
|
|
9624
|
+
try {
|
|
9625
|
+
context = document.createElement("canvas").getContext("2d");
|
|
9626
|
+
} catch {
|
|
9627
|
+
context = null;
|
|
9628
|
+
}
|
|
9629
|
+
return context;
|
|
9630
|
+
}
|
|
9631
|
+
function fontOf(el) {
|
|
9632
|
+
const cs = getComputedStyle(el);
|
|
9633
|
+
const size = parseFloat(cs.fontSize) || 12;
|
|
9634
|
+
return { font: `${cs.fontStyle || "normal"} ${cs.fontWeight || "400"} ${size}px ${cs.fontFamily || "sans-serif"}`, size, letterSpacing: cs.letterSpacing || "normal" };
|
|
9635
|
+
}
|
|
9636
|
+
function measurer(info) {
|
|
9637
|
+
const cache = /* @__PURE__ */ new Map();
|
|
9638
|
+
return (text) => {
|
|
9639
|
+
let v = cache.get(text);
|
|
9640
|
+
if (v !== void 0) return v;
|
|
9641
|
+
const ctx = info ? canvasContext() : null;
|
|
9642
|
+
if (ctx && info) {
|
|
9643
|
+
ctx.font = info.font;
|
|
9644
|
+
if ("letterSpacing" in ctx) ctx.letterSpacing = info.letterSpacing === "normal" ? "0px" : info.letterSpacing;
|
|
9645
|
+
v = ctx.measureText(text).width;
|
|
9646
|
+
} else {
|
|
9647
|
+
v = text.length * (info?.size ?? 12) * 0.6;
|
|
9648
|
+
}
|
|
9649
|
+
cache.set(text, v);
|
|
9650
|
+
return v;
|
|
9651
|
+
};
|
|
9652
|
+
}
|
|
9653
|
+
function useTextMeasure(ref) {
|
|
9654
|
+
const [info, setInfo] = useState(null);
|
|
9655
|
+
useLayoutEffect(() => {
|
|
9656
|
+
const el = ref.current;
|
|
9657
|
+
if (!el) return;
|
|
9658
|
+
let live = true;
|
|
9659
|
+
const read = (fresh) => {
|
|
9660
|
+
if (!live) return;
|
|
9661
|
+
const next = fontOf(el);
|
|
9662
|
+
setInfo((cur) => !fresh && cur && cur.font === next.font && cur.letterSpacing === next.letterSpacing ? cur : next);
|
|
9663
|
+
};
|
|
9664
|
+
read(false);
|
|
9665
|
+
const ro = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(() => read(false));
|
|
9666
|
+
ro?.observe(el);
|
|
9667
|
+
const fonts = typeof document === "undefined" ? void 0 : document.fonts;
|
|
9668
|
+
const loaded = () => read(true);
|
|
9669
|
+
fonts?.addEventListener?.("loadingdone", loaded);
|
|
9670
|
+
fonts?.ready?.then(loaded, () => {
|
|
9671
|
+
});
|
|
9672
|
+
return () => {
|
|
9673
|
+
live = false;
|
|
9674
|
+
ro?.disconnect();
|
|
9675
|
+
fonts?.removeEventListener?.("loadingdone", loaded);
|
|
9676
|
+
};
|
|
9677
|
+
}, [ref]);
|
|
9678
|
+
return useMemo(() => measurer(info), [info]);
|
|
9679
|
+
}
|
|
9680
|
+
var fontGeneration = 0;
|
|
9681
|
+
var fontListeners = /* @__PURE__ */ new Set();
|
|
9682
|
+
var fontsWatched = false;
|
|
9683
|
+
function watchFonts() {
|
|
9684
|
+
if (fontsWatched || typeof document === "undefined" || !document.fonts) return;
|
|
9685
|
+
fontsWatched = true;
|
|
9686
|
+
const moveOn = () => {
|
|
9687
|
+
fontGeneration++;
|
|
9688
|
+
shared.clear();
|
|
9689
|
+
fontListeners.forEach((l) => l());
|
|
9690
|
+
};
|
|
9691
|
+
document.fonts.addEventListener?.("loadingdone", moveOn);
|
|
9692
|
+
document.fonts.ready?.then(moveOn, () => {
|
|
9693
|
+
});
|
|
9694
|
+
}
|
|
9695
|
+
var shared = /* @__PURE__ */ new Map();
|
|
9696
|
+
function sharedMeasurer(info) {
|
|
9697
|
+
const key = `${fontGeneration}|${info.font}|${info.letterSpacing}`;
|
|
9698
|
+
let m = shared.get(key);
|
|
9699
|
+
if (!m) {
|
|
9700
|
+
m = measurer(info);
|
|
9701
|
+
shared.set(key, m);
|
|
9702
|
+
}
|
|
9703
|
+
return m;
|
|
9704
|
+
}
|
|
9705
|
+
var textWatchers = /* @__PURE__ */ new Map();
|
|
9706
|
+
var textObserver = null;
|
|
9707
|
+
function watchText(el, onResize) {
|
|
9708
|
+
if (typeof ResizeObserver === "undefined") return () => {
|
|
9709
|
+
};
|
|
9710
|
+
textObserver ??= new ResizeObserver((entries) => entries.forEach((e) => textWatchers.get(e.target)?.()));
|
|
9711
|
+
textWatchers.set(el, onResize);
|
|
9712
|
+
textObserver.observe(el);
|
|
9713
|
+
return () => {
|
|
9714
|
+
textWatchers.delete(el);
|
|
9715
|
+
textObserver?.unobserve(el);
|
|
9716
|
+
};
|
|
9717
|
+
}
|
|
9718
|
+
var NO_BORDERS = { start: 0, end: 0 };
|
|
9719
|
+
function useElementMetrics(ref, deps, text) {
|
|
9720
|
+
const [read, setRead] = useState({ info: null, borders: NO_BORDERS });
|
|
9721
|
+
const [generation, setGeneration] = useState(fontGeneration);
|
|
9722
|
+
useLayoutEffect(() => {
|
|
9723
|
+
const el = ref.current;
|
|
9724
|
+
if (!el) return;
|
|
9725
|
+
const readNow = () => {
|
|
9726
|
+
const cs = getComputedStyle(el);
|
|
9727
|
+
const next = fontOf(el);
|
|
9728
|
+
const start = parseFloat(cs.borderInlineStartWidth || cs.borderLeftWidth) || 0;
|
|
9729
|
+
const end = parseFloat(cs.borderInlineEndWidth || cs.borderRightWidth) || 0;
|
|
9730
|
+
setRead(
|
|
9731
|
+
(cur) => cur.info?.font === next.font && cur.info.letterSpacing === next.letterSpacing && cur.borders.start === start && cur.borders.end === end ? cur : { info: next, borders: { start, end } }
|
|
9732
|
+
);
|
|
9733
|
+
};
|
|
9734
|
+
readNow();
|
|
9735
|
+
const watched = text?.current;
|
|
9736
|
+
return watched ? watchText(watched, readNow) : void 0;
|
|
9737
|
+
}, deps);
|
|
9738
|
+
useLayoutEffect(() => {
|
|
9739
|
+
watchFonts();
|
|
9740
|
+
const listener = () => setGeneration(fontGeneration);
|
|
9741
|
+
fontListeners.add(listener);
|
|
9742
|
+
return () => {
|
|
9743
|
+
fontListeners.delete(listener);
|
|
9744
|
+
};
|
|
9745
|
+
}, []);
|
|
9746
|
+
const measure = useMemo(() => read.info ? sharedMeasurer(read.info) : measurer(null), [read.info, generation]);
|
|
9747
|
+
return { measure, borders: read.borders };
|
|
9748
|
+
}
|
|
9749
|
+
function useFittedLabels(boxes, ref, options = {}) {
|
|
9750
|
+
const measure = useTextMeasure(ref);
|
|
9751
|
+
const { gap, align } = options;
|
|
9752
|
+
const start = options.visible?.start;
|
|
9753
|
+
const end = options.visible?.end;
|
|
9754
|
+
return useMemo(
|
|
9755
|
+
() => fitLabels(boxes, measure, { gap, align, visible: start === void 0 || end === void 0 ? void 0 : { start, end } }),
|
|
9756
|
+
[boxes, measure, gap, align, start, end]
|
|
9757
|
+
);
|
|
9758
|
+
}
|
|
9759
|
+
var TODAY_INSET = 4;
|
|
9760
|
+
var TODAY_PAD_X = 6;
|
|
9761
|
+
function TimeScaleHeader({ todayLabel = "Today", className }) {
|
|
9762
|
+
const { scale, scrollLeft, visibleWidth, todayX: line } = useTimeViewport();
|
|
9763
|
+
const majorRef = useRef(null);
|
|
9764
|
+
const minorRef = useRef(null);
|
|
9765
|
+
const todayRef = useRef(null);
|
|
9766
|
+
const measured = visibleWidth > 0;
|
|
9767
|
+
const view = { start: scrollLeft, end: scrollLeft + visibleWidth };
|
|
9768
|
+
const major = useFittedLabels(scale.major, majorRef, measured ? { visible: view } : {});
|
|
9769
|
+
const minor = useFittedLabels(scale.minor, minorRef, measured ? { align: "center", visible: view } : { align: "center" });
|
|
9770
|
+
const measureToday = useTextMeasure(todayRef);
|
|
9771
|
+
const todayWidth = measureToday(todayLabel) + TODAY_PAD_X * 2;
|
|
9772
|
+
const todayShown = line !== null && (!measured || line >= view.start && line <= view.end);
|
|
9773
|
+
let todayX = line === null ? 0 : line - todayWidth / 2;
|
|
9774
|
+
if (measured) todayX = Math.max(view.start + TODAY_INSET, Math.min(todayX, view.end - TODAY_INSET - todayWidth));
|
|
9775
|
+
const underToday = (x, width) => todayShown && x < todayX + todayWidth + TODAY_INSET && x + width > todayX - TODAY_INSET;
|
|
9776
|
+
return /* @__PURE__ */ jsxs("div", { "aria-hidden": "true", "data-time-scale": "", className: cn("relative select-none text-xs", className), style: { width: scale.width }, children: [
|
|
9777
|
+
/* @__PURE__ */ jsxs("div", { "data-scale-row": "major", className: "relative h-7 font-medium text-[var(--ds-text-primary)]", children: [
|
|
9778
|
+
/* @__PURE__ */ jsx("span", { ref: majorRef, className: "invisible absolute whitespace-nowrap leading-none", children: "0" }),
|
|
9779
|
+
scale.major.slice(1).map((t) => /* @__PURE__ */ jsx("span", { className: "absolute inset-y-0 border-s-[length:1px] border-[var(--ds-border-subtle)]", style: { insetInlineStart: t.x } }, `line-${t.key}`)),
|
|
9780
|
+
major.map((l) => l.text && /* @__PURE__ */ jsx("span", { "data-scale-label": "", className: "absolute top-1/2 -translate-y-1/2 whitespace-nowrap leading-none", style: { insetInlineStart: l.x }, children: l.text }, l.key))
|
|
9781
|
+
] }),
|
|
9782
|
+
/* @__PURE__ */ jsxs("div", { "data-scale-row": "minor", className: "relative h-7 border-t-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)] text-[var(--ds-text-secondary)]", children: [
|
|
9783
|
+
/* @__PURE__ */ jsx("span", { ref: minorRef, className: "invisible absolute whitespace-nowrap leading-none tabular-nums", children: "0" }),
|
|
9784
|
+
scale.minor.slice(1).map((t) => /* @__PURE__ */ jsx("span", { className: "absolute inset-y-0 border-s-[length:1px] border-[var(--ds-border-subtle)]", style: { insetInlineStart: t.x } }, `line-${t.key}`)),
|
|
9785
|
+
minor.map((l) => l.text && !underToday(l.x, l.width) && /* @__PURE__ */ jsx("span", { "data-scale-label": "", className: "absolute top-1/2 -translate-y-1/2 whitespace-nowrap leading-none tabular-nums", style: { insetInlineStart: l.x }, children: l.text }, l.key)),
|
|
9786
|
+
/* @__PURE__ */ jsx(
|
|
9787
|
+
"span",
|
|
9788
|
+
{
|
|
9789
|
+
ref: todayRef,
|
|
9790
|
+
"data-scale-today": "",
|
|
9791
|
+
className: cn(
|
|
9792
|
+
"absolute top-1/2 -translate-y-1/2 whitespace-nowrap leading-none font-medium",
|
|
9793
|
+
// the transparent border draws the pill's outline in forced-colours mode
|
|
9794
|
+
"rounded-[var(--ds-radius-full)] py-ds-0.5 border-[length:1px] border-transparent bg-[var(--ds-accent-primary)] text-[var(--ds-text-on-primary)]",
|
|
9795
|
+
!todayShown && "invisible"
|
|
9796
|
+
),
|
|
9797
|
+
style: { insetInlineStart: todayX, paddingInline: TODAY_PAD_X },
|
|
9798
|
+
children: todayLabel
|
|
9799
|
+
}
|
|
9800
|
+
)
|
|
9801
|
+
] })
|
|
9802
|
+
] });
|
|
9803
|
+
}
|
|
9804
|
+
var ZOOM_NAMES = { day: "Days", week: "Weeks", month: "Months", quarter: "Quarters" };
|
|
9805
|
+
var SHADE_MIN = 4;
|
|
9806
|
+
var INSET = 8;
|
|
9807
|
+
var MIN_INSET = 2;
|
|
9808
|
+
var MIN_TITLE = 2 * INSET + 12;
|
|
9809
|
+
var PHONE_BELOW = 640;
|
|
9810
|
+
var MIN_LABEL = 112;
|
|
9811
|
+
var ROW_HEIGHT = 40;
|
|
9812
|
+
var MARGIN = 16;
|
|
9813
|
+
var WHEEL_STEP = 50;
|
|
9814
|
+
var WHEEL_PAUSE = 250;
|
|
9815
|
+
var FIELD = "input, select, textarea, [contenteditable='true']";
|
|
9816
|
+
var border = "border-[var(--ds-border-subtle)]";
|
|
9817
|
+
function writeScroll(el, visibleWidth) {
|
|
9818
|
+
el.style.setProperty("--time-scroll", String(el.scrollLeft));
|
|
9819
|
+
el.style.setProperty("--time-view-end", String(el.scrollLeft + visibleWidth));
|
|
9820
|
+
}
|
|
9821
|
+
var TimeViewport = forwardRef(function TimeViewport2({
|
|
9822
|
+
scale,
|
|
9823
|
+
onZoomChange,
|
|
9824
|
+
zooms = TIME_ZOOMS,
|
|
9825
|
+
labelWidth: labelWidthProp = 240,
|
|
9826
|
+
labelHeader,
|
|
9827
|
+
header,
|
|
9828
|
+
toolbar,
|
|
9829
|
+
initialDate,
|
|
9830
|
+
showToday = true,
|
|
9831
|
+
todayAt = "day",
|
|
9832
|
+
shadeDays,
|
|
9833
|
+
tabStop = true,
|
|
9834
|
+
children,
|
|
9835
|
+
"aria-label": ariaLabel,
|
|
9836
|
+
className
|
|
9837
|
+
}, ref) {
|
|
9838
|
+
const scrollerRef = useRef(null);
|
|
9839
|
+
const headerRef = useRef(null);
|
|
9840
|
+
const [frame, setFrame] = useState({ width: 0, header: 0 });
|
|
9841
|
+
useLayoutEffect(() => {
|
|
9842
|
+
const el = scrollerRef.current;
|
|
9843
|
+
if (!el) return;
|
|
9844
|
+
const read = () => {
|
|
9845
|
+
const width = el.clientWidth;
|
|
9846
|
+
const headerHeight = headerRef.current?.offsetHeight ?? 0;
|
|
9847
|
+
setFrame((cur) => cur.width === width && cur.header === headerHeight ? cur : { width, header: headerHeight });
|
|
9848
|
+
};
|
|
9849
|
+
read();
|
|
9850
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
9851
|
+
const ro = new ResizeObserver(read);
|
|
9852
|
+
ro.observe(el);
|
|
9853
|
+
if (headerRef.current) ro.observe(headerRef.current);
|
|
9854
|
+
return () => ro.disconnect();
|
|
9855
|
+
}, []);
|
|
9856
|
+
const phone = frame.width > 0 && frame.width < PHONE_BELOW;
|
|
9857
|
+
const labelWidth = phone ? Math.min(labelWidthProp, Math.max(MIN_LABEL, Math.round(frame.width * 0.36))) : labelWidthProp;
|
|
9858
|
+
const visibleWidth = Math.max(0, frame.width - labelWidth);
|
|
9859
|
+
const drawn = useMemo(() => extendTimeScale(scale, visibleWidth), [scale, visibleWidth]);
|
|
9860
|
+
const [store] = useState(createScrollStore);
|
|
9861
|
+
const lastLeft = useRef(0);
|
|
9862
|
+
const latest = useLatest({ drawn, visibleWidth, labelWidth, headerHeight: frame.header });
|
|
9863
|
+
const setLeft = useCallback((left) => {
|
|
9864
|
+
const el = scrollerRef.current;
|
|
9865
|
+
if (!el) return;
|
|
9866
|
+
el.scrollLeft = Math.max(0, left);
|
|
9867
|
+
lastLeft.current = el.scrollLeft;
|
|
9868
|
+
writeScroll(el, latest.current.visibleWidth);
|
|
9869
|
+
store.set({ scrollLeft: el.scrollLeft, visibleWidth: latest.current.visibleWidth });
|
|
9870
|
+
}, [store, latest]);
|
|
9871
|
+
const api = useMemo(() => {
|
|
9872
|
+
const ensureVisible = (x0, x1) => {
|
|
9873
|
+
const el = scrollerRef.current;
|
|
9874
|
+
if (!el) return;
|
|
9875
|
+
const vw = latest.current.visibleWidth;
|
|
9876
|
+
const left = el.scrollLeft;
|
|
9877
|
+
const lo = Math.min(x0, x1);
|
|
9878
|
+
const hi = Math.max(x0, x1);
|
|
9879
|
+
if (hi - lo > vw - 2 * MARGIN) {
|
|
9880
|
+
if (hi < left + MARGIN || lo > left + vw - MARGIN) setLeft(lo - MARGIN);
|
|
9881
|
+
} else if (lo < left + MARGIN) setLeft(lo - MARGIN);
|
|
9882
|
+
else if (hi > left + vw - MARGIN) setLeft(hi - vw + MARGIN);
|
|
9883
|
+
};
|
|
9884
|
+
return {
|
|
9885
|
+
scrollToDate(value, align = "start") {
|
|
9886
|
+
const { drawn: s, visibleWidth: vw } = latest.current;
|
|
9887
|
+
const x = s.x(value);
|
|
9888
|
+
setLeft(align === "center" ? x - vw / 2 : x - MARGIN);
|
|
9889
|
+
},
|
|
9890
|
+
page(direction) {
|
|
9891
|
+
const { drawn: s, visibleWidth: vw } = latest.current;
|
|
9892
|
+
const target = (scrollerRef.current?.scrollLeft ?? 0) + direction * vw;
|
|
9893
|
+
const tick = s.minor.reduce((best, t) => Math.abs(t.x - target) < Math.abs(best - target) ? t.x : best, Infinity);
|
|
9894
|
+
setLeft(Number.isFinite(tick) ? tick : target);
|
|
9895
|
+
},
|
|
9896
|
+
ensureVisible,
|
|
9897
|
+
reveal(target) {
|
|
9898
|
+
const el = scrollerRef.current;
|
|
9899
|
+
if (!el || !el.contains(target)) return;
|
|
9900
|
+
const { labelWidth: lw, headerHeight } = latest.current;
|
|
9901
|
+
const box = el.getBoundingClientRect();
|
|
9902
|
+
const r = target.getBoundingClientRect();
|
|
9903
|
+
const top = box.top + headerHeight;
|
|
9904
|
+
const bottom = box.top + el.clientHeight;
|
|
9905
|
+
if (r.top < top) el.scrollTop -= top - r.top;
|
|
9906
|
+
else if (r.bottom > bottom) el.scrollTop += Math.min(r.bottom - bottom, r.top - top);
|
|
9907
|
+
const x0 = r.left - box.left - lw + el.scrollLeft;
|
|
9908
|
+
ensureVisible(x0, x0 + r.width);
|
|
9909
|
+
}
|
|
9910
|
+
};
|
|
9911
|
+
}, [latest, setLeft]);
|
|
9912
|
+
useImperativeHandle(ref, () => api, [api]);
|
|
9913
|
+
const todayAtX = todayAt === "now" ? drawn.now === null ? null : drawn.x(drawn.now) : drawn.today === null ? null : drawn.x(drawn.today) + drawn.pxPerDay / 2;
|
|
9914
|
+
const todayX = showToday ? todayAtX : null;
|
|
9915
|
+
const layout = useMemo(() => ({ scale: drawn, labelWidth, showToday, todayX, compact: phone, ...api }), [drawn, labelWidth, showToday, todayX, phone, api]);
|
|
9916
|
+
const pending = useRef(null);
|
|
9917
|
+
const applied = useRef(null);
|
|
9918
|
+
useLayoutEffect(() => {
|
|
9919
|
+
const el = scrollerRef.current;
|
|
9920
|
+
if (!el || visibleWidth <= 0) return;
|
|
9921
|
+
writeScroll(el, visibleWidth);
|
|
9922
|
+
store.set({ scrollLeft: el.scrollLeft, visibleWidth });
|
|
9923
|
+
const prev = applied.current;
|
|
9924
|
+
applied.current = { startDay: drawn.startDay, pxPerDay: drawn.pxPerDay, visibleWidth };
|
|
9925
|
+
if (!prev) {
|
|
9926
|
+
const at = initialDate !== void 0 ? drawn.x(initialDate) : todayAtX;
|
|
9927
|
+
if (at !== null && Number.isFinite(at)) setLeft(at - visibleWidth / 2);
|
|
9928
|
+
return;
|
|
9929
|
+
}
|
|
9930
|
+
const rescaled = prev.startDay !== drawn.startDay || prev.pxPerDay !== drawn.pxPerDay;
|
|
9931
|
+
if (!rescaled && prev.visibleWidth === visibleWidth) return;
|
|
9932
|
+
const want = pending.current;
|
|
9933
|
+
pending.current = null;
|
|
9934
|
+
const anchor = want && want.zoom === drawn.zoom ? want : { day: prev.startDay + (lastLeft.current + prev.visibleWidth / 2) / prev.pxPerDay, at: visibleWidth / 2 };
|
|
9935
|
+
setLeft(drawn.x(anchor.day) - anchor.at);
|
|
9936
|
+
const focused = document.activeElement;
|
|
9937
|
+
if (focused instanceof HTMLElement && focused !== el && el.contains(focused)) api.reveal(focused);
|
|
9938
|
+
}, [drawn, visibleWidth, initialDate, todayAtX, setLeft, store, api]);
|
|
9939
|
+
const shaded = [];
|
|
9940
|
+
if (shadeDays && drawn.pxPerDay >= SHADE_MIN) {
|
|
9941
|
+
for (let d = drawn.startDay; d < drawn.endDay; d++) {
|
|
9942
|
+
if (!shadeDays(calendarDay(d))) continue;
|
|
9943
|
+
const last = shaded[shaded.length - 1];
|
|
9944
|
+
if (last && Math.abs(last.x + last.width - drawn.x(d)) < 0.5) last.width += drawn.pxPerDay;
|
|
9945
|
+
else shaded.push({ key: String(d), x: drawn.x(d), width: drawn.pxPerDay });
|
|
9946
|
+
}
|
|
9947
|
+
}
|
|
9948
|
+
const levels = TIME_ZOOMS.filter((z) => zooms.includes(z));
|
|
9949
|
+
const zoomTo = (direction) => {
|
|
9950
|
+
if (!onZoomChange) return null;
|
|
9951
|
+
const at = TIME_ZOOMS.indexOf(scale.zoom);
|
|
9952
|
+
const side = levels.filter((z) => direction < 0 ? TIME_ZOOMS.indexOf(z) < at : TIME_ZOOMS.indexOf(z) > at);
|
|
9953
|
+
return (direction < 0 ? side[side.length - 1] : side[0]) ?? null;
|
|
9954
|
+
};
|
|
9955
|
+
const focusedAt = (el) => {
|
|
9956
|
+
const f = document.activeElement;
|
|
9957
|
+
if (!(f instanceof HTMLElement) || f === el || !el.contains(f)) return void 0;
|
|
9958
|
+
const r = f.getBoundingClientRect();
|
|
9959
|
+
const x = (r.left + r.right) / 2 - el.getBoundingClientRect().left - labelWidth;
|
|
9960
|
+
return x >= 0 && x <= visibleWidth ? x : void 0;
|
|
9961
|
+
};
|
|
9962
|
+
const zoom = (direction, at) => {
|
|
9963
|
+
const next = zoomTo(direction);
|
|
9964
|
+
if (!next || !onZoomChange) return false;
|
|
9965
|
+
const el = scrollerRef.current;
|
|
9966
|
+
if (el && visibleWidth > 0) {
|
|
9967
|
+
const x = at ?? focusedAt(el) ?? visibleWidth / 2;
|
|
9968
|
+
pending.current = { zoom: next, day: drawn.dayAt(el.scrollLeft + x), at: x };
|
|
9969
|
+
}
|
|
9970
|
+
onZoomChange(next);
|
|
9971
|
+
return true;
|
|
9972
|
+
};
|
|
9973
|
+
const canZoomIn = zoomTo(-1) !== null;
|
|
9974
|
+
const canZoomOut = zoomTo(1) !== null;
|
|
9975
|
+
const panByPeriod = (direction) => {
|
|
9976
|
+
const left = scrollerRef.current?.scrollLeft ?? 0;
|
|
9977
|
+
const xs = drawn.minor.map((t) => t.x);
|
|
9978
|
+
const target = direction > 0 ? xs.find((x) => x > left + 1) ?? drawn.width : [...xs].reverse().find((x) => x < left - 1) ?? 0;
|
|
9979
|
+
setLeft(target);
|
|
9980
|
+
};
|
|
9981
|
+
const onKeyDown = (e) => {
|
|
9982
|
+
if (e.defaultPrevented || e.altKey || e.ctrlKey || e.metaKey) return;
|
|
9983
|
+
if (e.target instanceof HTMLElement && e.target.closest(FIELD)) return;
|
|
9984
|
+
if (e.key === "+" || e.key === "=") {
|
|
9985
|
+
if (zoom(-1)) e.preventDefault();
|
|
9986
|
+
} else if (e.key === "-" || e.key === "\u2212") {
|
|
9987
|
+
if (zoom(1)) e.preventDefault();
|
|
9988
|
+
} else if (e.shiftKey && (e.key === "ArrowLeft" || e.key === "ArrowRight")) {
|
|
9989
|
+
e.preventDefault();
|
|
9990
|
+
panByPeriod(e.key === "ArrowRight" ? 1 : -1);
|
|
9991
|
+
}
|
|
9992
|
+
};
|
|
9993
|
+
const wheel = useRef({ sum: 0, at: -Infinity });
|
|
9994
|
+
const onWheel = useLatest((e) => {
|
|
9995
|
+
if (!(e.ctrlKey || e.metaKey) || !onZoomChange) return;
|
|
9996
|
+
e.preventDefault();
|
|
9997
|
+
const w = wheel.current;
|
|
9998
|
+
if (e.timeStamp - w.at < WHEEL_PAUSE) return;
|
|
9999
|
+
w.sum += e.deltaY * (e.deltaMode === 1 ? 16 : e.deltaMode === 2 ? 400 : 1);
|
|
10000
|
+
if (Math.abs(w.sum) < WHEEL_STEP) return;
|
|
10001
|
+
const el = scrollerRef.current;
|
|
10002
|
+
const x = el ? e.clientX - el.getBoundingClientRect().left - labelWidth : NaN;
|
|
10003
|
+
zoom(w.sum < 0 ? -1 : 1, x >= 0 && x <= visibleWidth ? x : void 0);
|
|
10004
|
+
wheel.current = { sum: 0, at: e.timeStamp };
|
|
10005
|
+
});
|
|
10006
|
+
useEffect(() => {
|
|
10007
|
+
const el = scrollerRef.current;
|
|
10008
|
+
if (!el) return;
|
|
10009
|
+
const listener = (e) => onWheel.current(e);
|
|
10010
|
+
el.addEventListener("wheel", listener, { passive: false });
|
|
10011
|
+
return () => el.removeEventListener("wheel", listener);
|
|
10012
|
+
}, [onWheel]);
|
|
10013
|
+
const drag = useRef(null);
|
|
10014
|
+
const [dragging, setDragging] = useState(false);
|
|
10015
|
+
const onScaleDown = (e) => {
|
|
10016
|
+
if (e.button !== 0 || e.pointerType === "touch" || !scrollerRef.current) return;
|
|
10017
|
+
drag.current = { id: e.pointerId, x: e.clientX, left: scrollerRef.current.scrollLeft };
|
|
10018
|
+
e.currentTarget.setPointerCapture?.(e.pointerId);
|
|
10019
|
+
setDragging(true);
|
|
10020
|
+
};
|
|
10021
|
+
const onScaleMove = (e) => {
|
|
10022
|
+
const d = drag.current;
|
|
10023
|
+
if (d && d.id === e.pointerId) setLeft(d.left - (e.clientX - d.x));
|
|
10024
|
+
};
|
|
10025
|
+
const onScaleUp = (e) => {
|
|
10026
|
+
if (drag.current?.id !== e.pointerId) return;
|
|
10027
|
+
drag.current = null;
|
|
10028
|
+
setDragging(false);
|
|
10029
|
+
};
|
|
10030
|
+
const onScroll = () => {
|
|
10031
|
+
const el = scrollerRef.current;
|
|
10032
|
+
if (!el) return;
|
|
10033
|
+
lastLeft.current = el.scrollLeft;
|
|
10034
|
+
writeScroll(el, visibleWidth);
|
|
10035
|
+
store.set({ scrollLeft: el.scrollLeft, visibleWidth });
|
|
10036
|
+
};
|
|
10037
|
+
const headerNode = header === void 0 ? /* @__PURE__ */ jsx(TimeScaleHeader, {}) : header;
|
|
10038
|
+
const hasHeader = headerNode != null || labelHeader != null;
|
|
10039
|
+
const bar = toolbar === false ? null : /* @__PURE__ */ jsxs("div", { className: cn("flex shrink-0 flex-wrap items-center gap-ds-1 px-ds-2 py-ds-1.5 border-b-[length:var(--ds-border-width)]", border), children: [
|
|
10040
|
+
onZoomChange && levels.length > 1 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
10041
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "Zoom out", "aria-disabled": !canZoomOut || void 0, className: "aria-disabled:opacity-disabled aria-disabled:cursor-not-allowed", onClick: () => zoom(1), children: /* @__PURE__ */ jsx(Icon, { name: "minus", size: 14 }) }),
|
|
10042
|
+
/* @__PURE__ */ jsx("span", { "aria-live": "polite", className: "min-w-[4.5rem] text-center text-xs text-[var(--ds-text-secondary)]", children: ZOOM_NAMES[scale.zoom] }),
|
|
10043
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "Zoom in", "aria-disabled": !canZoomIn || void 0, className: "aria-disabled:opacity-disabled aria-disabled:cursor-not-allowed", onClick: () => zoom(-1), children: /* @__PURE__ */ jsx(Icon, { name: "plus", size: 14 }) })
|
|
10044
|
+
] }),
|
|
10045
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "Previous period", onClick: () => api.page(-1), children: /* @__PURE__ */ jsx(Icon, { name: "chevron-left", size: 14 }) }),
|
|
10046
|
+
/* @__PURE__ */ jsx(Button, { variant: "secondary", size: "sm", disabled: todayAtX === null, onClick: () => {
|
|
10047
|
+
if (todayAtX !== null) setLeft(todayAtX - visibleWidth / 2);
|
|
10048
|
+
}, children: "Today" }),
|
|
10049
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "Next period", onClick: () => api.page(1), children: /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: 14 }) }),
|
|
10050
|
+
toolbar
|
|
10051
|
+
] });
|
|
10052
|
+
return (
|
|
10053
|
+
// Inline-size containment: the rows' width never widens the frame (in a grid or flex parent it would, and the
|
|
10054
|
+
// wider frame would extend the scale again). The viewport takes the width it is given and scrolls inside it.
|
|
10055
|
+
/* @__PURE__ */ jsx("div", { "data-time-viewport": "", "data-compact": phone || void 0, className: cn("flex h-full min-h-0 w-full min-w-0 flex-col [contain:inline-size] bg-[var(--ds-bg-surface)]", className), onKeyDown, children: /* @__PURE__ */ jsx(TimeViewportCtx, { value: layout, children: /* @__PURE__ */ jsxs(TimeViewportScrollCtx, { value: store, children: [
|
|
10056
|
+
bar,
|
|
10057
|
+
/* @__PURE__ */ jsxs("div", { className: "relative min-h-0 flex-1", children: [
|
|
10058
|
+
/* @__PURE__ */ jsx(
|
|
10059
|
+
"div",
|
|
10060
|
+
{
|
|
10061
|
+
ref: scrollerRef,
|
|
10062
|
+
role: "region",
|
|
10063
|
+
"aria-label": ariaLabel,
|
|
10064
|
+
tabIndex: tabStop ? 0 : void 0,
|
|
10065
|
+
onScroll,
|
|
10066
|
+
className: "peer h-full overflow-auto isolate outline-none",
|
|
10067
|
+
style: { scrollPaddingInlineStart: labelWidth, scrollPaddingTop: frame.header },
|
|
10068
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex min-h-full flex-col", style: { width: labelWidth + drawn.width }, children: [
|
|
10069
|
+
hasHeader && /* @__PURE__ */ jsxs("div", { ref: headerRef, className: cn("sticky top-0 z-[3] flex shrink-0 bg-[var(--ds-bg-surface)] border-b-[length:var(--ds-border-width)]", border), children: [
|
|
10070
|
+
/* @__PURE__ */ jsx(
|
|
10071
|
+
"div",
|
|
10072
|
+
{
|
|
10073
|
+
className: cn("sticky start-0 z-[1] flex shrink-0 items-end px-ds-3 py-ds-1.5 bg-[var(--ds-bg-surface)] border-e-[length:var(--ds-border-width)] text-xs font-medium text-[var(--ds-text-secondary)]", border),
|
|
10074
|
+
style: { width: labelWidth },
|
|
10075
|
+
children: labelHeader
|
|
10076
|
+
}
|
|
10077
|
+
),
|
|
10078
|
+
/* @__PURE__ */ jsx(
|
|
10079
|
+
"div",
|
|
10080
|
+
{
|
|
10081
|
+
"data-time-scale-area": "",
|
|
10082
|
+
className: cn("shrink-0 select-none", dragging ? "cursor-grabbing" : "cursor-grab"),
|
|
10083
|
+
style: { width: drawn.width },
|
|
10084
|
+
onPointerDown: onScaleDown,
|
|
10085
|
+
onPointerMove: onScaleMove,
|
|
10086
|
+
onPointerUp: onScaleUp,
|
|
10087
|
+
onPointerCancel: onScaleUp,
|
|
10088
|
+
children: headerNode
|
|
10089
|
+
}
|
|
10090
|
+
)
|
|
10091
|
+
] }),
|
|
10092
|
+
/* @__PURE__ */ jsxs("div", { className: "relative flex flex-1 flex-col", children: [
|
|
10093
|
+
/* @__PURE__ */ jsxs("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-y-0", style: { insetInlineStart: labelWidth, width: drawn.width }, children: [
|
|
10094
|
+
shaded.map((b) => /* @__PURE__ */ jsx("span", { "data-shaded-days": "", className: "absolute inset-y-0 bg-[var(--ds-bg-muted)] opacity-70", style: { insetInlineStart: b.x, width: b.width } }, b.key)),
|
|
10095
|
+
drawn.minor.slice(1).map((t) => /* @__PURE__ */ jsx("span", { className: "absolute inset-y-0 border-s-[length:1px] border-[var(--ds-border-subtle)]", style: { insetInlineStart: t.x } }, t.key)),
|
|
10096
|
+
todayX !== null && /* @__PURE__ */ jsx("span", { "data-today-line": "", className: "absolute inset-y-0 -translate-x-1/2 border-s-2 border-[var(--ds-accent-primary)]", style: { insetInlineStart: todayX } })
|
|
10097
|
+
] }),
|
|
10098
|
+
children,
|
|
10099
|
+
/* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: cn("sticky start-0 z-[2] min-h-0 flex-1 bg-[var(--ds-bg-surface)] border-e-[length:var(--ds-border-width)]", border), style: { width: labelWidth } })
|
|
10100
|
+
] })
|
|
10101
|
+
] })
|
|
10102
|
+
}
|
|
10103
|
+
),
|
|
10104
|
+
/* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 peer-focus-visible:shadow-[inset_0_0_0_2px_var(--ds-border-focus)]" })
|
|
10105
|
+
] })
|
|
10106
|
+
] }) }) })
|
|
10107
|
+
);
|
|
10108
|
+
});
|
|
10109
|
+
function TimeRow({ label, children, height = ROW_HEIGHT, labelClassName, className, style, ...props }) {
|
|
10110
|
+
const { scale, labelWidth } = useTimeViewportLayout();
|
|
10111
|
+
return /* @__PURE__ */ jsxs("div", { ...props, className: cn("flex shrink-0", className), style: { height, ...style }, children: [
|
|
10112
|
+
/* @__PURE__ */ jsx(
|
|
10113
|
+
"div",
|
|
10114
|
+
{
|
|
10115
|
+
className: cn("sticky start-0 z-[2] flex shrink-0 items-center px-ds-3 bg-[var(--ds-bg-surface)] border-e-[length:var(--ds-border-width)] border-b-[length:var(--ds-border-width)] text-sm text-[var(--ds-text-primary)]", border, labelClassName),
|
|
10116
|
+
style: { width: labelWidth },
|
|
10117
|
+
children: /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1 truncate", children: label })
|
|
10118
|
+
}
|
|
10119
|
+
),
|
|
10120
|
+
/* @__PURE__ */ jsx("div", { className: cn("relative shrink-0 border-b-[length:var(--ds-border-width)]", border), style: { width: scale.width }, children })
|
|
10121
|
+
] });
|
|
10122
|
+
}
|
|
10123
|
+
function TimeBar({ start, end, minWidth = 0, labels, children, className, style, ...props }) {
|
|
10124
|
+
const { scale, labelWidth } = useTimeViewportLayout();
|
|
10125
|
+
const barRef = useRef(null);
|
|
10126
|
+
const textRef = useRef(null);
|
|
10127
|
+
const hasLabels = !!labels?.length;
|
|
10128
|
+
const { measure, borders } = useElementMetrics(barRef, [className, hasLabels], textRef);
|
|
10129
|
+
const { x, width } = scale.span(start, end ?? start);
|
|
10130
|
+
const drawnWidth = Math.max(width, minWidth);
|
|
10131
|
+
const inView = `(min(${x + drawnWidth - borders.end}, var(--time-view-end, 1e9)) - max(${x + borders.start}, var(--time-scroll, 0)))`;
|
|
10132
|
+
const sticky = { insetInlineStart: labelWidth, maxWidth: `min(100%, calc(${inView} * 1px))` };
|
|
10133
|
+
let label = null;
|
|
10134
|
+
if (labels && hasLabels) {
|
|
10135
|
+
const widths = labels.map(measure);
|
|
10136
|
+
const inset = Math.min(INSET, Math.max(MIN_INSET, (drawnWidth - borders.start - borders.end - Math.min(...widths)) / 2 - 0.5));
|
|
10137
|
+
const fits = (i) => `clamp(0, calc((${inView} - ${widths[i] + 2 * inset} + 0.25) * 1000), 1)`;
|
|
10138
|
+
const shows = (i) => i === 0 ? fits(0) : `calc(${[fits(i), ...labels.slice(0, i).map((_, j) => `(1 - ${fits(j)})`)].join(" * ")})`;
|
|
10139
|
+
const named = props["aria-label"] !== void 0 || props["aria-labelledby"] !== void 0;
|
|
10140
|
+
label = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
10141
|
+
/* @__PURE__ */ jsx("span", { "data-time-bar-label": "", "aria-hidden": "true", className: "sticky grid overflow-hidden whitespace-nowrap", style: { ...sticky, paddingInline: inset }, children: labels.map((text, i) => /* @__PURE__ */ jsx("span", { ref: i === 0 ? textRef : void 0, "data-time-bar-form": "", className: "justify-self-start [grid-area:1/1]", style: { opacity: shows(i) }, children: text }, i)) }),
|
|
10142
|
+
!named && /* @__PURE__ */ jsx("span", { className: "sr-only", children: labels[0] })
|
|
10143
|
+
] });
|
|
10144
|
+
} else if (children != null && children !== false) {
|
|
10145
|
+
label = /* @__PURE__ */ jsx(
|
|
10146
|
+
"span",
|
|
10147
|
+
{
|
|
10148
|
+
"data-time-bar-label": "",
|
|
10149
|
+
className: "sticky flex min-w-0 items-center gap-ds-1 overflow-hidden whitespace-nowrap",
|
|
10150
|
+
style: { ...sticky, paddingInline: INSET, opacity: `clamp(0, calc((${inView} - ${MIN_TITLE}) * 1000), 1)` },
|
|
10151
|
+
children: typeof children === "string" ? /* @__PURE__ */ jsx("span", { className: "truncate", children }) : children
|
|
10152
|
+
}
|
|
10153
|
+
);
|
|
10154
|
+
}
|
|
10155
|
+
return /* @__PURE__ */ jsx(
|
|
10156
|
+
"div",
|
|
10157
|
+
{
|
|
10158
|
+
...props,
|
|
10159
|
+
ref: barRef,
|
|
10160
|
+
className: cn("absolute top-ds-2 bottom-ds-2 flex items-center overflow-clip rounded-[var(--ds-radius-sm)] outline-none focus-visible:shadow-[var(--ds-shadow-focus)]", className),
|
|
10161
|
+
style: { insetInlineStart: x, width: drawnWidth, ...style },
|
|
10162
|
+
children: label
|
|
10163
|
+
}
|
|
10164
|
+
);
|
|
10165
|
+
}
|
|
10166
|
+
|
|
10167
|
+
// ../../showcase/src/lib/gantt-layout.ts
|
|
10168
|
+
var ROW_HEIGHT2 = 40;
|
|
10169
|
+
var BAR_HEIGHT = 24;
|
|
10170
|
+
var MIN_BAR = 6;
|
|
10171
|
+
var DIAMOND = 12;
|
|
10172
|
+
var TIP = DIAMOND * Math.SQRT2 / 2;
|
|
10173
|
+
var STUB = 8;
|
|
10174
|
+
var HEAD = 6;
|
|
10175
|
+
var BESIDE = STUB + 6;
|
|
10176
|
+
var groupKey = (id) => `group:${id}`;
|
|
10177
|
+
var itemKey = (id) => `item:${id}`;
|
|
10178
|
+
function ganttRows(items, groups, collapsed) {
|
|
10179
|
+
const known = new Set(groups.map((g) => g.id));
|
|
10180
|
+
const byGroup = /* @__PURE__ */ new Map();
|
|
10181
|
+
const loose = [];
|
|
10182
|
+
for (const item of items) {
|
|
10183
|
+
if (item.group === void 0 || !known.has(item.group)) loose.push(item);
|
|
10184
|
+
else byGroup.set(item.group, [...byGroup.get(item.group) ?? [], item]);
|
|
10185
|
+
}
|
|
10186
|
+
const shown = groups.filter((g) => byGroup.has(g.id));
|
|
10187
|
+
const topLevel = shown.length + loose.length;
|
|
10188
|
+
const rows = [];
|
|
10189
|
+
const push = (row) => rows.push({ ...row, index: rows.length, top: rows.length * ROW_HEIGHT2 });
|
|
10190
|
+
let pos = 0;
|
|
10191
|
+
for (const group of shown) {
|
|
10192
|
+
const children = byGroup.get(group.id);
|
|
10193
|
+
const key = groupKey(group.id);
|
|
10194
|
+
push({ key, kind: "group", level: 1, setSize: topLevel, posInSet: ++pos, group, children });
|
|
10195
|
+
if (collapsed.has(group.id)) continue;
|
|
10196
|
+
children.forEach((item, i) => push({ key: itemKey(item.id), kind: "item", level: 2, setSize: children.length, posInSet: i + 1, parentKey: key, item }));
|
|
10197
|
+
}
|
|
10198
|
+
for (const item of loose) push({ key: itemKey(item.id), kind: "item", level: 1, setSize: topLevel, posInSet: ++pos, item });
|
|
10199
|
+
return rows;
|
|
10200
|
+
}
|
|
10201
|
+
function itemShape(item, scale) {
|
|
10202
|
+
if (item.milestone) {
|
|
10203
|
+
const centre = scale.x((dayOf(item.start) + endDayOf(item.start)) / 2);
|
|
10204
|
+
return { kind: "milestone", x0: centre - TIP, x1: centre + TIP, in: centre - TIP, out: centre + TIP };
|
|
10205
|
+
}
|
|
10206
|
+
const { x, width } = scale.span(item.start, item.end ?? item.start);
|
|
10207
|
+
const x1 = x + Math.max(width, MIN_BAR);
|
|
10208
|
+
return { kind: "bar", x0: x, x1, in: x, out: x1 };
|
|
10209
|
+
}
|
|
10210
|
+
function groupExtent(items) {
|
|
10211
|
+
return timeExtent(items.map((i) => ({ start: i.start, end: i.milestone ? void 0 : i.end })));
|
|
10212
|
+
}
|
|
10213
|
+
var drawable = (shape) => !!shape && Number.isFinite(shape.x0) && Number.isFinite(shape.x1);
|
|
10214
|
+
function ganttArrows(items, rows, shapeOf, collapsed) {
|
|
10215
|
+
const rowOf = /* @__PURE__ */ new Map();
|
|
10216
|
+
const groupRow = /* @__PURE__ */ new Map();
|
|
10217
|
+
for (const row of rows) {
|
|
10218
|
+
if (row.item) rowOf.set(row.item.id, row);
|
|
10219
|
+
if (row.group) groupRow.set(row.group.id, row);
|
|
10220
|
+
}
|
|
10221
|
+
const drawnRow = (item) => rowOf.get(item.id) ?? (item.group !== void 0 && collapsed.has(item.group) ? groupRow.get(item.group) : void 0);
|
|
10222
|
+
const byId = new Map(items.map((i) => [i.id, i]));
|
|
10223
|
+
const arrows = [];
|
|
10224
|
+
for (const succ of items) {
|
|
10225
|
+
for (const id of new Set(succ.dependsOn ?? [])) {
|
|
10226
|
+
const pred = byId.get(id);
|
|
10227
|
+
if (!pred || pred === succ) continue;
|
|
10228
|
+
const from = drawnRow(pred);
|
|
10229
|
+
const to = drawnRow(succ);
|
|
10230
|
+
const a = shapeOf(pred);
|
|
10231
|
+
const b = shapeOf(succ);
|
|
10232
|
+
if (!from || !to || from === to || !drawable(a) || !drawable(b)) continue;
|
|
10233
|
+
const x1 = a.out;
|
|
10234
|
+
const y1 = from.top + ROW_HEIGHT2 / 2;
|
|
10235
|
+
const x2 = b.in;
|
|
10236
|
+
const y2 = to.top + ROW_HEIGHT2 / 2;
|
|
10237
|
+
const routed = x2 < x1 + STUB + HEAD;
|
|
10238
|
+
const edge = y2 > y1 ? from.top + ROW_HEIGHT2 : from.top;
|
|
10239
|
+
const d = routed ? `M${x1} ${y1}H${x1 + STUB}V${edge}H${x2 - STUB - HEAD}V${y2}H${x2}` : `M${x1} ${y1}H${x1 + STUB}V${y2}H${x2}`;
|
|
10240
|
+
const head = `M${x2} ${y2}L${x2 - HEAD} ${y2 - HEAD / 2}L${x2 - HEAD} ${y2 + HEAD / 2}Z`;
|
|
10241
|
+
arrows.push({ key: `${pred.id}>${succ.id}`, from: pred.id, to: succ.id, d, head, routed, x1, y1, x2, y2 });
|
|
10242
|
+
}
|
|
10243
|
+
}
|
|
10244
|
+
return arrows;
|
|
10245
|
+
}
|
|
10246
|
+
function labelPlace(shape, textWidth, scaleWidth, { pad: pad3 = 8, gap = BESIDE } = {}) {
|
|
10247
|
+
if (shape.kind === "bar" && textWidth + 2 * pad3 <= shape.x1 - shape.x0) return "inside";
|
|
10248
|
+
if (shape.x1 + gap + textWidth <= scaleWidth) return "after";
|
|
10249
|
+
return shape.x0 - gap - textWidth >= 0 ? "before" : "after";
|
|
10250
|
+
}
|
|
10251
|
+
var TONES = {
|
|
10252
|
+
neutral: { fill: "bg-[var(--ds-status-neutral)]", on: "text-[var(--ds-text-on-neutral)]" },
|
|
10253
|
+
info: { fill: "bg-[var(--ds-status-info)]", on: "text-[var(--ds-text-on-info)]" },
|
|
10254
|
+
success: { fill: "bg-[var(--ds-status-success)]", on: "text-[var(--ds-text-on-success)]", icon: "check" },
|
|
10255
|
+
warning: { fill: "bg-[var(--ds-status-warning)]", on: "text-[var(--ds-text-on-warning)]", icon: "warning" },
|
|
10256
|
+
error: { fill: "bg-[var(--ds-status-error)]", on: "text-[var(--ds-text-on-error)]", icon: "error" },
|
|
10257
|
+
accent: { fill: "bg-[var(--ds-accent-primary)]", on: "text-[var(--ds-text-on-primary)]" }
|
|
10258
|
+
};
|
|
10259
|
+
var ICON = 12;
|
|
10260
|
+
var ICON_GAP = 4;
|
|
10261
|
+
var LEVEL_INDENT = 16;
|
|
10262
|
+
var TARGET = 24;
|
|
10263
|
+
var KNOCKOUT = 2;
|
|
10264
|
+
var SELECTED = "outline-solid outline-2 outline-offset-2 outline-[var(--ds-text-primary)]";
|
|
10265
|
+
function GanttArrows({ arrows, width, height }) {
|
|
10266
|
+
const { labelWidth } = useTimeViewportLayout();
|
|
10267
|
+
return /* @__PURE__ */ jsx("svg", { "aria-hidden": "true", "data-gantt-arrows": "", className: "pointer-events-none absolute top-0 overflow-visible", style: { insetInlineStart: labelWidth, width, height }, children: arrows.map((a) => /* @__PURE__ */ jsxs("g", { "data-from": a.from, "data-to": a.to, "data-routed": a.routed || void 0, className: "text-[var(--ds-text-muted)]", children: [
|
|
10268
|
+
/* @__PURE__ */ jsx("path", { d: a.d, fill: "none", stroke: "currentColor", strokeWidth: 1.5 }),
|
|
10269
|
+
/* @__PURE__ */ jsx("path", { d: a.head, fill: "currentColor" })
|
|
10270
|
+
] }, a.key)) });
|
|
10271
|
+
}
|
|
10272
|
+
function Legend({ statuses }) {
|
|
10273
|
+
const entries = Object.entries(statuses);
|
|
10274
|
+
if (!entries.length) return null;
|
|
10275
|
+
return /* @__PURE__ */ jsx("ul", { "aria-label": "Status key", className: "ms-auto flex flex-wrap items-center gap-x-ds-3 gap-y-ds-1 ps-ds-2 text-xs text-[var(--ds-text-secondary)]", children: entries.map(([key, s]) => {
|
|
10276
|
+
const tone = TONES[s.tone] ?? TONES.neutral;
|
|
10277
|
+
return /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-ds-1.5", children: [
|
|
10278
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: cn("grid size-4 shrink-0 place-items-center rounded-[var(--ds-radius-sm)]", tone.fill, tone.on), children: tone.icon && /* @__PURE__ */ jsx(Icon, { name: tone.icon, size: 10 }) }),
|
|
10279
|
+
s.label
|
|
10280
|
+
] }, key);
|
|
10281
|
+
}) });
|
|
10282
|
+
}
|
|
10283
|
+
function Gantt({
|
|
10284
|
+
items,
|
|
10285
|
+
groups = [],
|
|
10286
|
+
selectedId,
|
|
10287
|
+
onSelect,
|
|
10288
|
+
statuses = {},
|
|
10289
|
+
zoom: zoomProp,
|
|
10290
|
+
defaultZoom = "week",
|
|
10291
|
+
onZoomChange,
|
|
10292
|
+
today,
|
|
10293
|
+
weekStartsOn,
|
|
10294
|
+
locale,
|
|
10295
|
+
renderLabel,
|
|
10296
|
+
labelHeader = "Task",
|
|
10297
|
+
toolbar,
|
|
10298
|
+
"aria-label": ariaLabel,
|
|
10299
|
+
className
|
|
10300
|
+
}) {
|
|
10301
|
+
const [zoomState, setZoomState] = useState(defaultZoom);
|
|
10302
|
+
const zoom = zoomProp ?? zoomState;
|
|
10303
|
+
const changeZoom = (next) => {
|
|
10304
|
+
if (zoomProp === void 0) setZoomState(next);
|
|
10305
|
+
onZoomChange?.(next);
|
|
10306
|
+
};
|
|
10307
|
+
const [selectedState, setSelectedState] = useState(null);
|
|
10308
|
+
const selected = selectedId !== void 0 ? selectedId : selectedState;
|
|
10309
|
+
const select = (item) => {
|
|
10310
|
+
if (selectedId === void 0) setSelectedState(item?.id ?? null);
|
|
10311
|
+
onSelect?.(item);
|
|
10312
|
+
};
|
|
10313
|
+
const [collapsed, setCollapsed] = useState(() => new Set(groups.filter((g) => g.defaultCollapsed).map((g) => g.id)));
|
|
10314
|
+
const extent = useMemo(() => timeExtent(items.map((i) => ({ start: i.start, end: i.milestone ? void 0 : i.end }))), [items]);
|
|
10315
|
+
const scale = useTimeScale({ start: extent.start, end: extent.end, zoom, today, weekStartsOn, locale, pad: 1 });
|
|
10316
|
+
const rows = useMemo(() => ganttRows(items, groups, collapsed), [items, groups, collapsed]);
|
|
10317
|
+
const shapes = useMemo(() => new Map(items.map((i) => [i.id, itemShape(i, scale)])), [items, scale]);
|
|
10318
|
+
const extents = useMemo(() => new Map(rows.filter((r) => r.children).map((r) => [r.group.id, groupExtent(r.children)])), [rows]);
|
|
10319
|
+
const arrows = useMemo(() => ganttArrows(items, rows, (i) => shapes.get(i.id), collapsed), [items, rows, shapes, collapsed]);
|
|
10320
|
+
const byKey = useMemo(() => new Map(rows.map((r) => [r.key, r])), [rows]);
|
|
10321
|
+
const names = useMemo(() => {
|
|
10322
|
+
const byId = new Map(items.map((i) => [i.id, i]));
|
|
10323
|
+
const list = new Intl.ListFormat(locale, { type: "conjunction" });
|
|
10324
|
+
const out = /* @__PURE__ */ new Map();
|
|
10325
|
+
for (const item of items) {
|
|
10326
|
+
const after = (item.dependsOn ?? []).flatMap((id) => byId.has(id) && id !== item.id ? [byId.get(id).title] : []);
|
|
10327
|
+
const status = item.status === void 0 ? void 0 : statuses[item.status];
|
|
10328
|
+
out.set(itemKey(item.id), [
|
|
10329
|
+
item.title,
|
|
10330
|
+
item.milestone ? "milestone" : null,
|
|
10331
|
+
formatDateRange(item.start, item.milestone ? item.start : item.end ?? item.start, { locale }),
|
|
10332
|
+
status?.label ?? null,
|
|
10333
|
+
after.length ? `after ${list.format(after)}` : null
|
|
10334
|
+
].filter((part) => part).join(", "));
|
|
10335
|
+
}
|
|
10336
|
+
for (const row of rows) {
|
|
10337
|
+
if (!row.group || !row.children) continue;
|
|
10338
|
+
const e = extents.get(row.group.id);
|
|
10339
|
+
const count = row.children.length === 1 ? "1 item" : `${row.children.length} items`;
|
|
10340
|
+
out.set(row.key, [row.group.title, formatDateRange(e.start, e.end, { locale }), count].filter((part) => part).join(", "));
|
|
10341
|
+
}
|
|
10342
|
+
return out;
|
|
10343
|
+
}, [items, rows, extents, statuses, locale]);
|
|
10344
|
+
const probeRef = useRef(null);
|
|
10345
|
+
const measure = useTextMeasure(probeRef);
|
|
10346
|
+
const treeRef = useRef(null);
|
|
10347
|
+
const viewportRef = useRef(null);
|
|
10348
|
+
const [activeState, setActive] = useState(null);
|
|
10349
|
+
const selectedKey = selected === null ? null : itemKey(selected);
|
|
10350
|
+
const active = activeState !== null && byKey.has(activeState) ? activeState : selectedKey !== null && byKey.has(selectedKey) ? selectedKey : rows[0]?.key ?? "";
|
|
10351
|
+
const toggle = (id) => {
|
|
10352
|
+
const key = groupKey(id);
|
|
10353
|
+
const closing = !collapsed.has(id);
|
|
10354
|
+
setCollapsed((cur) => {
|
|
10355
|
+
const next = new Set(cur);
|
|
10356
|
+
if (next.has(id)) next.delete(id);
|
|
10357
|
+
else next.add(id);
|
|
10358
|
+
return next;
|
|
10359
|
+
});
|
|
10360
|
+
if (closing && byKey.get(active)?.parentKey === key) {
|
|
10361
|
+
setActive(key);
|
|
10362
|
+
if (treeRef.current?.contains(document.activeElement)) grid.focus(key);
|
|
10363
|
+
}
|
|
10364
|
+
};
|
|
10365
|
+
const activate = (row) => {
|
|
10366
|
+
if (row.group) toggle(row.group.id);
|
|
10367
|
+
else if (row.item) select(row.item);
|
|
10368
|
+
};
|
|
10369
|
+
const grid = useGridKeyboard({
|
|
10370
|
+
container: treeRef,
|
|
10371
|
+
active,
|
|
10372
|
+
setActive,
|
|
10373
|
+
keyOf: (key) => key,
|
|
10374
|
+
move: (from, e) => {
|
|
10375
|
+
if (e.shiftKey) return null;
|
|
10376
|
+
const row = byKey.get(from);
|
|
10377
|
+
if (!row) return null;
|
|
10378
|
+
const next = rows[row.index + 1];
|
|
10379
|
+
switch (e.key) {
|
|
10380
|
+
case "ArrowDown":
|
|
10381
|
+
return next?.key ?? from;
|
|
10382
|
+
case "ArrowUp":
|
|
10383
|
+
return rows[row.index - 1]?.key ?? from;
|
|
10384
|
+
case "Home":
|
|
10385
|
+
return rows[0].key;
|
|
10386
|
+
case "End":
|
|
10387
|
+
return rows[rows.length - 1].key;
|
|
10388
|
+
case "ArrowRight":
|
|
10389
|
+
if (!row.group) return from;
|
|
10390
|
+
if (collapsed.has(row.group.id)) {
|
|
10391
|
+
toggle(row.group.id);
|
|
10392
|
+
return from;
|
|
10393
|
+
}
|
|
10394
|
+
return next?.parentKey === row.key ? next.key : from;
|
|
10395
|
+
case "ArrowLeft":
|
|
10396
|
+
if (row.group) {
|
|
10397
|
+
if (!collapsed.has(row.group.id)) toggle(row.group.id);
|
|
10398
|
+
return from;
|
|
10399
|
+
}
|
|
10400
|
+
return row.parentKey ?? from;
|
|
10401
|
+
default:
|
|
10402
|
+
return null;
|
|
10403
|
+
}
|
|
10404
|
+
},
|
|
10405
|
+
onActivate: (key) => {
|
|
10406
|
+
const row = byKey.get(key);
|
|
10407
|
+
if (row) activate(row);
|
|
10408
|
+
},
|
|
10409
|
+
typeahead: { items: rows.map((r) => r.key), labelOf: (key) => byKey.get(key)?.item?.title ?? byKey.get(key)?.group?.title ?? "" },
|
|
10410
|
+
scrollIntoView: (_key, el) => viewportRef.current?.reveal(el)
|
|
10411
|
+
});
|
|
10412
|
+
const onKeyDown = (e) => {
|
|
10413
|
+
if (e.key === "Escape" && selected !== null && !e.defaultPrevented && !e.altKey && !e.ctrlKey && !e.metaKey) {
|
|
10414
|
+
e.preventDefault();
|
|
10415
|
+
select(null);
|
|
10416
|
+
return;
|
|
10417
|
+
}
|
|
10418
|
+
grid.onKeyDown(e);
|
|
10419
|
+
};
|
|
10420
|
+
const onRowClick = (row) => {
|
|
10421
|
+
if (row.item) select(row.item.id === selected ? null : row.item);
|
|
10422
|
+
else if (row.group) toggle(row.group.id);
|
|
10423
|
+
setActive(row.key);
|
|
10424
|
+
grid.focus(row.key);
|
|
10425
|
+
};
|
|
10426
|
+
const treeitem = (row) => ({
|
|
10427
|
+
role: "treeitem",
|
|
10428
|
+
"aria-level": row.level,
|
|
10429
|
+
"aria-setsize": row.setSize,
|
|
10430
|
+
"aria-posinset": row.posInSet,
|
|
10431
|
+
"aria-label": names.get(row.key),
|
|
10432
|
+
"data-grid-key": row.key,
|
|
10433
|
+
tabIndex: grid.tabIndexOf(row.key),
|
|
10434
|
+
onFocus: () => setActive(row.key)
|
|
10435
|
+
});
|
|
10436
|
+
const renderItem = (row, item) => {
|
|
10437
|
+
const shape = shapes.get(item.id);
|
|
10438
|
+
if (!drawable(shape)) return null;
|
|
10439
|
+
const tone = TONES[item.status !== void 0 && statuses[item.status]?.tone || "neutral"] ?? TONES.neutral;
|
|
10440
|
+
const isSelected = item.id === selected;
|
|
10441
|
+
const textWidth = measure(item.title) + (tone.icon ? ICON + ICON_GAP : 0) + 2 * KNOCKOUT;
|
|
10442
|
+
const place = labelPlace(shape, textWidth, scale.width);
|
|
10443
|
+
const beside = place === "after" ? shape.x1 + BESIDE : shape.x0 - BESIDE - textWidth;
|
|
10444
|
+
const icon = tone.icon && /* @__PURE__ */ jsx(Icon, { name: tone.icon, size: ICON, className: "shrink-0" });
|
|
10445
|
+
const title = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
10446
|
+
icon,
|
|
10447
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: item.title })
|
|
10448
|
+
] });
|
|
10449
|
+
const shapeBox = shape.kind === "milestone" ? /* @__PURE__ */ jsx("div", { ...treeitem(row), "aria-selected": isSelected, className: cn("group absolute grid cursor-pointer place-items-center", "outline-none"), style: { insetInlineStart: (shape.x0 + shape.x1) / 2 - TARGET / 2, top: (ROW_HEIGHT2 - TARGET) / 2, width: TARGET, height: TARGET }, children: /* @__PURE__ */ jsx("span", { "data-gantt-shape": "milestone", "data-item": item.id, className: cn("block rotate-45 rounded-[2px] group-focus-visible:shadow-[var(--ds-shadow-focus)]", tone.fill, isSelected && SELECTED), style: { width: DIAMOND, height: DIAMOND } }) }) : (
|
|
10450
|
+
// TimeBar keeps the title in view beside the pinned column while the bar's start is scrolled away
|
|
10451
|
+
/* @__PURE__ */ jsx(
|
|
10452
|
+
TimeBar,
|
|
10453
|
+
{
|
|
10454
|
+
...treeitem(row),
|
|
10455
|
+
"aria-selected": isSelected,
|
|
10456
|
+
"data-gantt-shape": "bar",
|
|
10457
|
+
"data-item": item.id,
|
|
10458
|
+
start: item.start,
|
|
10459
|
+
end: item.end ?? item.start,
|
|
10460
|
+
minWidth: MIN_BAR,
|
|
10461
|
+
className: cn("cursor-pointer text-xs font-medium", tone.fill, tone.on, isSelected && SELECTED),
|
|
10462
|
+
style: { top: (ROW_HEIGHT2 - BAR_HEIGHT) / 2, bottom: "auto", height: BAR_HEIGHT },
|
|
10463
|
+
children: place === "inside" && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "flex min-w-0 items-center gap-ds-1 overflow-hidden", children: title })
|
|
10464
|
+
}
|
|
10465
|
+
)
|
|
10466
|
+
);
|
|
10467
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
10468
|
+
shapeBox,
|
|
10469
|
+
place !== "inside" && // Beside the bar, past the turn an arrow makes; its own background keeps lines that cross the row off the
|
|
10470
|
+
// text. Once it is partly under the pinned column or past the view's end it is hidden, never a fragment.
|
|
10471
|
+
/* @__PURE__ */ jsx(
|
|
10472
|
+
"span",
|
|
10473
|
+
{
|
|
10474
|
+
"aria-hidden": "true",
|
|
10475
|
+
className: "pointer-events-none absolute top-1/2 flex -translate-y-1/2 items-center gap-ds-1 whitespace-nowrap rounded-[var(--ds-radius-sm)] bg-[var(--ds-bg-surface)] text-xs font-medium leading-4 text-[var(--ds-text-primary)]",
|
|
10476
|
+
style: { insetInlineStart: beside, paddingInline: KNOCKOUT, opacity: `clamp(0, calc(min(${beside} - var(--time-scroll, 0), var(--time-view-end, 1e9) - ${beside + textWidth}) * 1000), 1)` },
|
|
10477
|
+
children: title
|
|
10478
|
+
}
|
|
10479
|
+
)
|
|
10480
|
+
] });
|
|
10481
|
+
};
|
|
10482
|
+
const renderGroup = (row, extent2) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
10483
|
+
/* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: "absolute inset-0 bg-[var(--ds-bg-muted)] opacity-60" }),
|
|
10484
|
+
extent2 && Number.isFinite(extent2.start) && // a 24 px target around the thin summary bar it draws
|
|
10485
|
+
/* @__PURE__ */ jsx(
|
|
10486
|
+
TimeBar,
|
|
10487
|
+
{
|
|
10488
|
+
...treeitem(row),
|
|
10489
|
+
"aria-expanded": !collapsed.has(row.group.id),
|
|
10490
|
+
"data-gantt-shape": "summary",
|
|
10491
|
+
start: extent2.start,
|
|
10492
|
+
end: extent2.end,
|
|
10493
|
+
minWidth: MIN_BAR,
|
|
10494
|
+
className: "cursor-pointer before:absolute before:inset-x-0 before:top-1/2 before:h-ds-2 before:-translate-y-1/2 before:rounded-[var(--ds-radius-full)] before:bg-[var(--ds-text-secondary)]",
|
|
10495
|
+
style: { top: (ROW_HEIGHT2 - TARGET) / 2, bottom: "auto", height: TARGET }
|
|
10496
|
+
}
|
|
10497
|
+
)
|
|
10498
|
+
] });
|
|
10499
|
+
const label = (row) => {
|
|
10500
|
+
if (row.group) {
|
|
10501
|
+
return /* @__PURE__ */ jsxs("span", { "aria-hidden": "true", className: "flex min-w-0 items-center gap-ds-1.5 font-medium", children: [
|
|
10502
|
+
/* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: 14, className: cn("shrink-0 transition-transform", !collapsed.has(row.group.id) && "rotate-90") }),
|
|
10503
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: row.group.title })
|
|
10504
|
+
] });
|
|
10505
|
+
}
|
|
10506
|
+
const item = row.item;
|
|
10507
|
+
return (
|
|
10508
|
+
// a container, so what renderLabel adds gives way to the title when the column is narrow (a phone)
|
|
10509
|
+
/* @__PURE__ */ jsxs("span", { "aria-hidden": "true", className: "@container flex min-w-0 items-center gap-ds-2", style: { paddingInlineStart: row.level === 2 ? LEVEL_INDENT : 0 }, children: [
|
|
10510
|
+
item.milestone && /* @__PURE__ */ jsx("span", { className: "size-2 shrink-0 rotate-45 rounded-[1px] bg-[var(--ds-text-secondary)]" }),
|
|
10511
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: item.title }),
|
|
10512
|
+
renderLabel && /* @__PURE__ */ jsx("span", { className: "flex shrink-0 items-center @max-[10rem]:hidden", children: renderLabel(item) })
|
|
10513
|
+
] })
|
|
10514
|
+
);
|
|
10515
|
+
};
|
|
10516
|
+
return /* @__PURE__ */ jsx(
|
|
10517
|
+
TimeViewport,
|
|
10518
|
+
{
|
|
10519
|
+
ref: viewportRef,
|
|
10520
|
+
scale,
|
|
10521
|
+
onZoomChange: changeZoom,
|
|
10522
|
+
labelHeader,
|
|
10523
|
+
toolbar: toolbar === false ? false : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
10524
|
+
toolbar,
|
|
10525
|
+
/* @__PURE__ */ jsx(Legend, { statuses })
|
|
10526
|
+
] }),
|
|
10527
|
+
tabStop: false,
|
|
10528
|
+
"aria-label": ariaLabel,
|
|
10529
|
+
className,
|
|
10530
|
+
children: /* @__PURE__ */ jsxs("div", { ref: treeRef, role: "tree", "aria-label": ariaLabel, className: "relative", onKeyDown, children: [
|
|
10531
|
+
/* @__PURE__ */ jsx("span", { ref: probeRef, "aria-hidden": "true", className: "pointer-events-none invisible absolute whitespace-nowrap text-xs font-medium", children: "0" }),
|
|
10532
|
+
/* @__PURE__ */ jsx(GanttArrows, { arrows, width: scale.width, height: rows.length * ROW_HEIGHT2 }),
|
|
10533
|
+
rows.map((row) => /* @__PURE__ */ jsx(
|
|
10534
|
+
TimeRow,
|
|
10535
|
+
{
|
|
10536
|
+
"data-row-key": row.key,
|
|
10537
|
+
label: label(row),
|
|
10538
|
+
className: "cursor-pointer",
|
|
10539
|
+
labelClassName: cn(
|
|
10540
|
+
row.group && "bg-[image:linear-gradient(var(--ds-bg-muted),var(--ds-bg-muted))]",
|
|
10541
|
+
row.item && row.item.id === selected && "bg-[image:linear-gradient(var(--ds-accent-subtle),var(--ds-accent-subtle))] text-[var(--ds-accent-subtle-text)]"
|
|
10542
|
+
),
|
|
10543
|
+
onClick: () => onRowClick(row),
|
|
10544
|
+
children: row.group ? renderGroup(row, extents.get(row.group.id)) : renderItem(row, row.item)
|
|
10545
|
+
},
|
|
10546
|
+
row.key
|
|
10547
|
+
))
|
|
10548
|
+
] })
|
|
10549
|
+
}
|
|
10550
|
+
);
|
|
10551
|
+
}
|
|
10552
|
+
var DEFAULT_OFFSET2 = "var(--ds-space-4)";
|
|
10553
|
+
var LAUNCHER_SIZES = {
|
|
10554
|
+
md: "var(--ds-space-10)",
|
|
9133
10555
|
lg: "var(--ds-space-12)"
|
|
9134
10556
|
};
|
|
9135
10557
|
function launcherSize(size = "lg") {
|
|
@@ -9877,10 +11299,10 @@ var Breadcrumbs = forwardRef(function Breadcrumbs2({
|
|
|
9877
11299
|
setDropdownOpen(false);
|
|
9878
11300
|
};
|
|
9879
11301
|
const onKeyDown = (e) => {
|
|
9880
|
-
if (e.key
|
|
9881
|
-
|
|
9882
|
-
|
|
9883
|
-
|
|
11302
|
+
if (e.key !== "Escape" || e.defaultPrevented || !isTop()) return;
|
|
11303
|
+
e.preventDefault();
|
|
11304
|
+
returnFocus();
|
|
11305
|
+
setDropdownOpen(false);
|
|
9884
11306
|
};
|
|
9885
11307
|
document.addEventListener("mousedown", handler);
|
|
9886
11308
|
document.addEventListener("keydown", onKeyDown);
|
|
@@ -10080,27 +11502,31 @@ function Popover({ open, onOpenChange, trigger, children, side = "bottom", align
|
|
|
10080
11502
|
setTriggerWidth(node?.offsetWidth);
|
|
10081
11503
|
}, []);
|
|
10082
11504
|
const pos = usePosition(triggerRef, floatingRef, open, { side, align, offset: 8, flip: true });
|
|
10083
|
-
const
|
|
11505
|
+
const onOpenChangeRef = useLatest(onOpenChange);
|
|
11506
|
+
const { props: pressMarks, marked } = usePressMarks();
|
|
11507
|
+
const { isTop: isTopLayer, returnFocus } = usePopupFocus(open, { trigger: triggerRef, panel: floatingRef, close: () => onOpenChangeRef.current(false), tabExits: "edges", marked });
|
|
10084
11508
|
useEffect(() => {
|
|
10085
11509
|
if (!open) return;
|
|
10086
11510
|
const onKeyDown = (e) => {
|
|
10087
|
-
if (e.key
|
|
10088
|
-
|
|
10089
|
-
|
|
10090
|
-
|
|
11511
|
+
if (e.key !== "Escape" || e.defaultPrevented || !isTopLayer()) return;
|
|
11512
|
+
const active = document.activeElement;
|
|
11513
|
+
if (active && active !== document.body && !triggerRef.current?.contains(active) && !floatingRef.current?.contains(active)) return;
|
|
11514
|
+
e.preventDefault();
|
|
11515
|
+
returnFocus();
|
|
11516
|
+
onOpenChangeRef.current(false);
|
|
10091
11517
|
};
|
|
10092
|
-
const
|
|
11518
|
+
const onPress = (e) => {
|
|
10093
11519
|
const target = e.target;
|
|
10094
|
-
if (triggerRef.current?.contains(target) || floatingRef.current?.contains(target)) return;
|
|
10095
|
-
|
|
11520
|
+
if (marked(e) || triggerRef.current?.contains(target) || floatingRef.current?.contains(target)) return;
|
|
11521
|
+
onOpenChangeRef.current(false);
|
|
10096
11522
|
};
|
|
10097
11523
|
document.addEventListener("keydown", onKeyDown);
|
|
10098
|
-
document.addEventListener("mousedown",
|
|
11524
|
+
document.addEventListener("mousedown", onPress);
|
|
10099
11525
|
return () => {
|
|
10100
11526
|
document.removeEventListener("keydown", onKeyDown);
|
|
10101
|
-
document.removeEventListener("mousedown",
|
|
11527
|
+
document.removeEventListener("mousedown", onPress);
|
|
10102
11528
|
};
|
|
10103
|
-
}, [open,
|
|
11529
|
+
}, [open, onOpenChangeRef, isTopLayer, returnFocus, marked]);
|
|
10104
11530
|
useEffect(() => {
|
|
10105
11531
|
if (open) focusInitial(floatingRef.current);
|
|
10106
11532
|
}, [open]);
|
|
@@ -10122,6 +11548,7 @@ function Popover({ open, onOpenChange, trigger, children, side = "bottom", align
|
|
|
10122
11548
|
{
|
|
10123
11549
|
ref: floatingRef,
|
|
10124
11550
|
"data-gs": "popover",
|
|
11551
|
+
...pressMarks,
|
|
10125
11552
|
onClick: (e) => e.stopPropagation(),
|
|
10126
11553
|
className: cn(
|
|
10127
11554
|
"fixed z-[var(--ds-z-popover)]",
|
|
@@ -10337,10 +11764,87 @@ function EllipsisJumper({ totalPages, onPageChange }) {
|
|
|
10337
11764
|
}
|
|
10338
11765
|
);
|
|
10339
11766
|
}
|
|
11767
|
+
var HeaderTabRowContext = createContext(null);
|
|
11768
|
+
function useHeaderTabRow() {
|
|
11769
|
+
return useContext(HeaderTabRowContext);
|
|
11770
|
+
}
|
|
11771
|
+
function useNarrowRow(row) {
|
|
11772
|
+
const [narrow, setNarrow] = useState(false);
|
|
11773
|
+
useLayoutEffect(() => {
|
|
11774
|
+
if (!row) return;
|
|
11775
|
+
const limit = readBreakpointPx("sm");
|
|
11776
|
+
const measure = () => setNarrow(row.clientWidth < limit);
|
|
11777
|
+
measure();
|
|
11778
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
11779
|
+
const ro = new ResizeObserver(measure);
|
|
11780
|
+
ro.observe(row);
|
|
11781
|
+
return () => ro.disconnect();
|
|
11782
|
+
}, [row]);
|
|
11783
|
+
return narrow;
|
|
11784
|
+
}
|
|
10340
11785
|
function Stepper({ steps, currentStep, onStepChange, orientation = "horizontal", className }) {
|
|
10341
11786
|
const isHorizontal = orientation === "horizontal";
|
|
11787
|
+
const tabRow = useHeaderTabRow();
|
|
11788
|
+
const narrow = useNarrowRow(tabRow?.row ?? null);
|
|
11789
|
+
const inHeader = isHorizontal && tabRow != null;
|
|
11790
|
+
if (inHeader && narrow) {
|
|
11791
|
+
const current = steps[currentStep];
|
|
11792
|
+
const where = `Step ${Math.min(currentStep + 1, steps.length)} of ${steps.length}`;
|
|
11793
|
+
return /* @__PURE__ */ jsxs("nav", { "data-gs": "stepper", "aria-label": "Progress", className: cn("mx-ds-1", className), children: [
|
|
11794
|
+
/* @__PURE__ */ jsxs("p", { className: cn("mb-ds-2 text-sm font-medium text-[var(--ds-text-secondary)]", tabRow.hasCaption && "sr-only"), children: [
|
|
11795
|
+
where,
|
|
11796
|
+
current ? /* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
|
|
11797
|
+
": ",
|
|
11798
|
+
current.label
|
|
11799
|
+
] }) : null
|
|
11800
|
+
] }),
|
|
11801
|
+
/* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: "flex gap-ds-1", children: steps.map((step, i) => /* @__PURE__ */ jsx(
|
|
11802
|
+
"div",
|
|
11803
|
+
{
|
|
11804
|
+
className: cn(
|
|
11805
|
+
"h-1.5 flex-1 rounded-[var(--ds-radius-full)] transition-colors",
|
|
11806
|
+
step.status === "error" ? "bg-[var(--ds-status-error)]" : i <= currentStep ? "bg-[var(--ds-accent-primary)]" : "bg-[var(--ds-border-subtle)]"
|
|
11807
|
+
)
|
|
11808
|
+
},
|
|
11809
|
+
i
|
|
11810
|
+
)) })
|
|
11811
|
+
] });
|
|
11812
|
+
}
|
|
11813
|
+
if (inHeader) {
|
|
11814
|
+
return /* @__PURE__ */ jsx("nav", { "data-gs": "stepper", "aria-label": "Progress", className: cn("flex items-start mx-ds-1", className), children: steps.map((step, i) => {
|
|
11815
|
+
const status = step.status ?? (i < currentStep ? "complete" : i === currentStep ? "current" : "upcoming");
|
|
11816
|
+
const isLast = i === steps.length - 1;
|
|
11817
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col items-start min-w-0", isLast ? "flex-none" : "flex-1"), children: [
|
|
11818
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center w-full", children: [
|
|
11819
|
+
/* @__PURE__ */ jsx(StepCircle, { status, index: i, onClick: onStepChange ? () => onStepChange(i) : void 0, clickable: !!onStepChange }),
|
|
11820
|
+
!isLast && /* @__PURE__ */ jsx(
|
|
11821
|
+
"div",
|
|
11822
|
+
{
|
|
11823
|
+
className: cn(
|
|
11824
|
+
"flex-1 h-0.5 mx-ds-2 transition-colors",
|
|
11825
|
+
i < currentStep ? "bg-[var(--ds-accent-primary)]" : "bg-[var(--ds-border-subtle)]"
|
|
11826
|
+
)
|
|
11827
|
+
}
|
|
11828
|
+
)
|
|
11829
|
+
] }),
|
|
11830
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-ds-2 pr-ds-2", children: [
|
|
11831
|
+
/* @__PURE__ */ jsx(
|
|
11832
|
+
"p",
|
|
11833
|
+
{
|
|
11834
|
+
className: cn(
|
|
11835
|
+
"text-xs font-medium",
|
|
11836
|
+
status === "current" ? "text-[var(--ds-accent-primary)]" : "text-[var(--ds-text-primary)]"
|
|
11837
|
+
),
|
|
11838
|
+
children: step.label
|
|
11839
|
+
}
|
|
11840
|
+
),
|
|
11841
|
+
step.description && /* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--ds-text-muted)] mt-ds-0.5", children: step.description })
|
|
11842
|
+
] })
|
|
11843
|
+
] }, i);
|
|
11844
|
+
}) });
|
|
11845
|
+
}
|
|
10342
11846
|
if (!isHorizontal) {
|
|
10343
|
-
return /* @__PURE__ */ jsx("nav", { "aria-label": "Progress", className: cn("flex flex-col", className), children: steps.map((step, i) => {
|
|
11847
|
+
return /* @__PURE__ */ jsx("nav", { "data-gs": "stepper", "aria-label": "Progress", className: cn("flex flex-col", className), children: steps.map((step, i) => {
|
|
10344
11848
|
const status = step.status ?? (i < currentStep ? "complete" : i === currentStep ? "current" : "upcoming");
|
|
10345
11849
|
const isLast = i === steps.length - 1;
|
|
10346
11850
|
return /* @__PURE__ */ jsxs("div", { className: "flex gap-ds-3", children: [
|
|
@@ -10375,6 +11879,7 @@ function Stepper({ steps, currentStep, onStepChange, orientation = "horizontal",
|
|
|
10375
11879
|
return /* @__PURE__ */ jsx(
|
|
10376
11880
|
"nav",
|
|
10377
11881
|
{
|
|
11882
|
+
"data-gs": "stepper",
|
|
10378
11883
|
"aria-label": "Progress",
|
|
10379
11884
|
className: cn("flex items-start", className),
|
|
10380
11885
|
children: steps.map((step, i) => {
|
|
@@ -10795,6 +12300,7 @@ function computeCompactFlyoutPos(anchorEl, itemsLength) {
|
|
|
10795
12300
|
return { top: Math.max(8, top), left: rect.right - (FLYOUT_BRIDGE_PX - 4) };
|
|
10796
12301
|
}
|
|
10797
12302
|
function CompactFlyout({
|
|
12303
|
+
id,
|
|
10798
12304
|
parentLabel,
|
|
10799
12305
|
items,
|
|
10800
12306
|
anchorEl,
|
|
@@ -10808,22 +12314,23 @@ function CompactFlyout({
|
|
|
10808
12314
|
const pos = useMemo(() => computeCompactFlyoutPos(anchorEl, items.length), [anchorEl, items.length]);
|
|
10809
12315
|
const anchorRef = useRef(anchorEl);
|
|
10810
12316
|
const panelRef = useRef(null);
|
|
12317
|
+
const headingId = useId();
|
|
10811
12318
|
const { isTop: isTopLayer, returnFocus } = usePopupFocus(true, { trigger: anchorRef, panel: panelRef, close: onClose, tabExits: "edges" });
|
|
10812
12319
|
useEffect(() => {
|
|
10813
12320
|
if (!focusIn) return;
|
|
10814
|
-
const raf = requestAnimationFrame(() => panelRef.current?.querySelector(
|
|
12321
|
+
const raf = requestAnimationFrame(() => panelRef.current?.querySelector("a[href]")?.focus());
|
|
10815
12322
|
return () => cancelAnimationFrame(raf);
|
|
10816
12323
|
}, [focusIn]);
|
|
10817
12324
|
useEffect(() => {
|
|
10818
12325
|
function handler(e) {
|
|
10819
|
-
if (!isTopLayer()) return;
|
|
12326
|
+
if (e.defaultPrevented || !isTopLayer()) return;
|
|
10820
12327
|
const inside = !!panelRef.current?.contains(document.activeElement);
|
|
10821
12328
|
if (e.key === "Escape" || e.key === "ArrowLeft" && inside) {
|
|
10822
12329
|
e.preventDefault();
|
|
10823
12330
|
if (inside) returnFocus();
|
|
10824
12331
|
onClose();
|
|
10825
12332
|
} else if (inside && (e.key === "ArrowDown" || e.key === "ArrowUp")) {
|
|
10826
|
-
const links = Array.from(panelRef.current?.querySelectorAll(
|
|
12333
|
+
const links = Array.from(panelRef.current?.querySelectorAll("a[href]") ?? []);
|
|
10827
12334
|
const at = links.indexOf(document.activeElement);
|
|
10828
12335
|
const next = links[(at + (e.key === "ArrowDown" ? 1 : -1) + links.length) % links.length];
|
|
10829
12336
|
if (next) {
|
|
@@ -10846,8 +12353,7 @@ function CompactFlyout({
|
|
|
10846
12353
|
"div",
|
|
10847
12354
|
{
|
|
10848
12355
|
ref: panelRef,
|
|
10849
|
-
|
|
10850
|
-
"aria-label": parentLabel,
|
|
12356
|
+
id,
|
|
10851
12357
|
className: cn(
|
|
10852
12358
|
"min-w-[160px] py-ds-1",
|
|
10853
12359
|
"bg-[var(--ds-sidebar-bg)] rounded-[var(--ds-radius-md)]",
|
|
@@ -10855,12 +12361,11 @@ function CompactFlyout({
|
|
|
10855
12361
|
"border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]"
|
|
10856
12362
|
),
|
|
10857
12363
|
children: [
|
|
10858
|
-
/* @__PURE__ */ jsx("p", { className: "px-ds-2 pt-ds-1 pb-ds-1.5 text-2xs font-semibold uppercase tracking-wider text-[var(--ds-sidebar-text-muted)]", children: parentLabel }),
|
|
10859
|
-
/* @__PURE__ */ jsx("
|
|
12364
|
+
/* @__PURE__ */ jsx("p", { id: headingId, className: "px-ds-2 pt-ds-1 pb-ds-1.5 text-2xs font-semibold uppercase tracking-wider text-[var(--ds-sidebar-text-muted)]", children: parentLabel }),
|
|
12365
|
+
/* @__PURE__ */ jsx("ul", { "aria-labelledby": headingId, className: "flex flex-col px-ds-1", style: { gap: "var(--ds-space-0\\.5)" }, children: items.map((child, i) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
|
|
10860
12366
|
"a",
|
|
10861
12367
|
{
|
|
10862
12368
|
href: child.href ?? "#",
|
|
10863
|
-
role: "menuitem",
|
|
10864
12369
|
onClick: (e) => {
|
|
10865
12370
|
e.preventDefault();
|
|
10866
12371
|
onItemClick?.(child, i);
|
|
@@ -10875,9 +12380,8 @@ function CompactFlyout({
|
|
|
10875
12380
|
/* @__PURE__ */ jsx(TruncLabel3, { children: child.label }),
|
|
10876
12381
|
child.badge && /* @__PURE__ */ jsx("span", { className: "shrink-0", children: child.badge })
|
|
10877
12382
|
]
|
|
10878
|
-
}
|
|
10879
|
-
|
|
10880
|
-
)) })
|
|
12383
|
+
}
|
|
12384
|
+
) }, child.label)) })
|
|
10881
12385
|
]
|
|
10882
12386
|
}
|
|
10883
12387
|
)
|
|
@@ -11224,6 +12728,7 @@ function CompactNavItem({
|
|
|
11224
12728
|
onToggleExpand
|
|
11225
12729
|
}) {
|
|
11226
12730
|
const [triggerEl, setTriggerEl] = useState(null);
|
|
12731
|
+
const flyoutId = useId();
|
|
11227
12732
|
const [keyboardOpen, setKeyboardOpen] = useState(false);
|
|
11228
12733
|
if (!isOpen && keyboardOpen) setKeyboardOpen(false);
|
|
11229
12734
|
const handleMouseEnter = useCallback(() => {
|
|
@@ -11255,8 +12760,8 @@ function CompactNavItem({
|
|
|
11255
12760
|
setKeyboardOpen(true);
|
|
11256
12761
|
onOpen(flyoutKey);
|
|
11257
12762
|
} : void 0,
|
|
11258
|
-
"aria-haspopup": !mobile && hasChildren ? "menu" : void 0,
|
|
11259
12763
|
"aria-expanded": hasChildren ? mobile ? expanded : isOpen : void 0,
|
|
12764
|
+
"aria-controls": !mobile && hasChildren && isOpen ? flyoutId : void 0,
|
|
11260
12765
|
className: cn(
|
|
11261
12766
|
"relative flex flex-col items-center justify-center gap-[3px] w-full py-ds-3 px-ds-1 rounded-[var(--ds-radius-md)] transition-colors cursor-pointer",
|
|
11262
12767
|
// Compact tiles have no edge to stroke: they read the fill and text
|
|
@@ -11291,6 +12796,7 @@ function CompactNavItem({
|
|
|
11291
12796
|
!mobile && isOpen && hasChildren && item.children && triggerEl && /* @__PURE__ */ jsx(
|
|
11292
12797
|
CompactFlyout,
|
|
11293
12798
|
{
|
|
12799
|
+
id: flyoutId,
|
|
11294
12800
|
parentLabel: item.label,
|
|
11295
12801
|
items: item.children,
|
|
11296
12802
|
anchorEl: triggerEl,
|
|
@@ -11766,6 +13272,35 @@ function BackButton({ onLight, onClick }) {
|
|
|
11766
13272
|
}
|
|
11767
13273
|
);
|
|
11768
13274
|
}
|
|
13275
|
+
function BackLink({ onClick, onLight }) {
|
|
13276
|
+
return /* @__PURE__ */ jsxs(
|
|
13277
|
+
"button",
|
|
13278
|
+
{
|
|
13279
|
+
type: "button",
|
|
13280
|
+
onClick,
|
|
13281
|
+
className: cn(
|
|
13282
|
+
"inline-flex items-center gap-ds-1 mb-ds-3 text-sm underline underline-offset-4 cursor-pointer",
|
|
13283
|
+
"rounded-[var(--ds-radius-sm)] focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
|
|
13284
|
+
onLight ? "text-[var(--ds-text-on-primary)]" : "text-[var(--ds-text-primary)] hover:text-[var(--ds-accent-primary)]"
|
|
13285
|
+
),
|
|
13286
|
+
children: [
|
|
13287
|
+
/* @__PURE__ */ jsx(Icon, { name: "chevron-left", size: 14 }),
|
|
13288
|
+
"Back"
|
|
13289
|
+
]
|
|
13290
|
+
}
|
|
13291
|
+
);
|
|
13292
|
+
}
|
|
13293
|
+
function Caption({ children, onLight, onTint }) {
|
|
13294
|
+
return /* @__PURE__ */ jsx("p", { className: cn(
|
|
13295
|
+
"mb-ds-1 text-sm font-medium",
|
|
13296
|
+
onLight ? "text-[var(--ds-text-on-primary)]/80" : onTint ? "text-[var(--ds-text-muted-on-tint)]" : "text-[var(--ds-text-secondary)]"
|
|
13297
|
+
), children });
|
|
13298
|
+
}
|
|
13299
|
+
function TabRow({ children, hasCaption, className, onBand }) {
|
|
13300
|
+
const [row, setRow] = useState(null);
|
|
13301
|
+
const value = useMemo(() => ({ row, hasCaption }), [row, hasCaption]);
|
|
13302
|
+
return /* @__PURE__ */ jsx("div", { ref: setRow, className: cn(className, onBand && "mx-0 rounded-[var(--ds-radius-md)] bg-[var(--ds-bg-surface)] px-ds-3 py-ds-2"), children: /* @__PURE__ */ jsx(HeaderTabRowContext.Provider, { value, children }) });
|
|
13303
|
+
}
|
|
11769
13304
|
var FLOATING_HEADER = cn(
|
|
11770
13305
|
"rounded-[var(--ds-radius-md)]",
|
|
11771
13306
|
"border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
|
|
@@ -11783,9 +13318,10 @@ function computeFloatingPadding(navbarFloating, sidebarFloating, overlapActive,
|
|
|
11783
13318
|
var PageHeader = forwardRef(function PageHeader2({
|
|
11784
13319
|
title,
|
|
11785
13320
|
description,
|
|
13321
|
+
caption,
|
|
11786
13322
|
badge,
|
|
11787
13323
|
breadcrumbs,
|
|
11788
|
-
onBack,
|
|
13324
|
+
onBack: onBackProp,
|
|
11789
13325
|
actions,
|
|
11790
13326
|
avatar,
|
|
11791
13327
|
filters,
|
|
@@ -11844,12 +13380,16 @@ var PageHeader = forwardRef(function PageHeader2({
|
|
|
11844
13380
|
const containedBreadcrumbs = containedBreadcrumbsProp ?? (container?.breadcrumbsInsideHeader && breadcrumbs?.length ? /* @__PURE__ */ jsx(Breadcrumbs, { variant: "contained", itemVariant: r.breadcrumbStyle, bleed: true, contentMaxWidth: gutterWidth, separator: bcSep, items: [...breadcrumbs, { label: title }] }) : void 0);
|
|
11845
13381
|
const noBottomMargin = noBottomMarginProp ?? container?.noBottomMargin;
|
|
11846
13382
|
const overlapActive = container?.overlap ?? false;
|
|
13383
|
+
const focusBack = shell?.focus ? onBackProp : void 0;
|
|
13384
|
+
const onBack = focusBack ? void 0 : onBackProp;
|
|
11847
13385
|
if (behavior === "snapping") {
|
|
11848
13386
|
return /* @__PURE__ */ jsx(
|
|
11849
13387
|
SnappingHeader,
|
|
11850
13388
|
{
|
|
11851
13389
|
title,
|
|
11852
13390
|
description,
|
|
13391
|
+
caption,
|
|
13392
|
+
focusBack,
|
|
11853
13393
|
badge,
|
|
11854
13394
|
breadcrumbs: inlineBreadcrumbs,
|
|
11855
13395
|
onBack,
|
|
@@ -11881,13 +13421,15 @@ var PageHeader = forwardRef(function PageHeader2({
|
|
|
11881
13421
|
}
|
|
11882
13422
|
let header;
|
|
11883
13423
|
if (variant === "minimal") {
|
|
11884
|
-
header = /* @__PURE__ */ jsx(MinimalHeader, { title, badge, breadcrumbs: inlineBreadcrumbs, actions, breadcrumbPill: bcPill, breadcrumbSeparator: bcSep, as: Heading, className, headerRef: floating ? void 0 : ref, isFloating: floating, contentMaxWidth: gutterWidth });
|
|
13424
|
+
header = /* @__PURE__ */ jsx(MinimalHeader, { title, caption, focusBack, badge, breadcrumbs: inlineBreadcrumbs, actions, tabs, breadcrumbPill: bcPill, breadcrumbSeparator: bcSep, as: Heading, className, headerRef: floating ? void 0 : ref, isFloating: floating, contentMaxWidth: gutterWidth });
|
|
11885
13425
|
} else if (variant === "tabs-integrated") {
|
|
11886
13426
|
header = /* @__PURE__ */ jsx(
|
|
11887
13427
|
TabsIntegratedHeader,
|
|
11888
13428
|
{
|
|
11889
13429
|
title,
|
|
11890
13430
|
description,
|
|
13431
|
+
caption,
|
|
13432
|
+
focusBack,
|
|
11891
13433
|
badge,
|
|
11892
13434
|
breadcrumbs: inlineBreadcrumbs,
|
|
11893
13435
|
onBack,
|
|
@@ -11910,6 +13452,9 @@ var PageHeader = forwardRef(function PageHeader2({
|
|
|
11910
13452
|
gutter: !insidePaddedColumn,
|
|
11911
13453
|
title,
|
|
11912
13454
|
description,
|
|
13455
|
+
caption,
|
|
13456
|
+
focusBack,
|
|
13457
|
+
tabs,
|
|
11913
13458
|
badge,
|
|
11914
13459
|
breadcrumbs: inlineBreadcrumbs,
|
|
11915
13460
|
onBack,
|
|
@@ -11935,6 +13480,9 @@ var PageHeader = forwardRef(function PageHeader2({
|
|
|
11935
13480
|
{
|
|
11936
13481
|
title,
|
|
11937
13482
|
description,
|
|
13483
|
+
caption,
|
|
13484
|
+
focusBack,
|
|
13485
|
+
tabs,
|
|
11938
13486
|
badge,
|
|
11939
13487
|
breadcrumbs: inlineBreadcrumbs,
|
|
11940
13488
|
onBack,
|
|
@@ -11975,6 +13523,9 @@ function floatingContentProps(isFloating, contentMaxWidth, extraClassName) {
|
|
|
11975
13523
|
function DefaultHeader({
|
|
11976
13524
|
title,
|
|
11977
13525
|
description,
|
|
13526
|
+
caption,
|
|
13527
|
+
focusBack,
|
|
13528
|
+
tabs,
|
|
11978
13529
|
badge,
|
|
11979
13530
|
breadcrumbs,
|
|
11980
13531
|
onBack,
|
|
@@ -11997,6 +13548,7 @@ function DefaultHeader({
|
|
|
11997
13548
|
return /* @__PURE__ */ jsxs(
|
|
11998
13549
|
"header",
|
|
11999
13550
|
{
|
|
13551
|
+
"data-gs": "page-header",
|
|
12000
13552
|
ref: headerRef,
|
|
12001
13553
|
className: cn(
|
|
12002
13554
|
!hasBanner && (hasBreadcrumbs ? "pt-ds-3 pb-ds-6" : "py-ds-6"),
|
|
@@ -12015,6 +13567,7 @@ function DefaultHeader({
|
|
|
12015
13567
|
children: [
|
|
12016
13568
|
hasBanner && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-[var(--ds-bg-canvas)]/85" }),
|
|
12017
13569
|
/* @__PURE__ */ jsxs("div", { className: inner.className, style: inner.style, children: [
|
|
13570
|
+
focusBack && /* @__PURE__ */ jsx(BackLink, { onClick: focusBack }),
|
|
12018
13571
|
hasBreadcrumbs && /* @__PURE__ */ jsx(Breadcrumbs3, { items: breadcrumbs, pill: breadcrumbPill, separator: breadcrumbSeparator }),
|
|
12019
13572
|
/* @__PURE__ */ jsxs("div", { className: cn(
|
|
12020
13573
|
"flex flex-col @sm:flex-row @sm:items-center justify-between gap-ds-4",
|
|
@@ -12024,6 +13577,7 @@ function DefaultHeader({
|
|
|
12024
13577
|
onBack && /* @__PURE__ */ jsx(BackButton, { onClick: onBack }),
|
|
12025
13578
|
avatar && /* @__PURE__ */ jsx("div", { className: "shrink-0", children: avatar }),
|
|
12026
13579
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
13580
|
+
caption && /* @__PURE__ */ jsx(Caption, { children: caption }),
|
|
12027
13581
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-2", children: [
|
|
12028
13582
|
/* @__PURE__ */ jsx(TruncHeading, { as: Heading, className: "font-[family-name:var(--ds-font-display)] text-3xl font-[var(--ds-weight-heading)] text-[var(--ds-text-primary)]", children: title }),
|
|
12029
13583
|
badge
|
|
@@ -12033,7 +13587,8 @@ function DefaultHeader({
|
|
|
12033
13587
|
] }),
|
|
12034
13588
|
actions && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-ds-2 shrink-0", children: actions })
|
|
12035
13589
|
] }),
|
|
12036
|
-
filters && /* @__PURE__ */ jsx(ControlSizeContext.Provider, { value: "sm", children: /* @__PURE__ */ jsx("div", { className: "flex items-center flex-wrap gap-ds-3 mt-ds-4", children: filters }) })
|
|
13590
|
+
filters && /* @__PURE__ */ jsx(ControlSizeContext.Provider, { value: "sm", children: /* @__PURE__ */ jsx("div", { className: "flex items-center flex-wrap gap-ds-3 mt-ds-4", children: filters }) }),
|
|
13591
|
+
tabs && /* @__PURE__ */ jsx(TabRow, { hasCaption: !!caption, className: "mt-ds-4 -mx-ds-1", children: tabs })
|
|
12037
13592
|
] })
|
|
12038
13593
|
]
|
|
12039
13594
|
}
|
|
@@ -12041,9 +13596,12 @@ function DefaultHeader({
|
|
|
12041
13596
|
}
|
|
12042
13597
|
function MinimalHeader({
|
|
12043
13598
|
title,
|
|
13599
|
+
caption,
|
|
13600
|
+
focusBack,
|
|
12044
13601
|
badge,
|
|
12045
13602
|
breadcrumbs,
|
|
12046
13603
|
actions,
|
|
13604
|
+
tabs,
|
|
12047
13605
|
breadcrumbPill,
|
|
12048
13606
|
breadcrumbSeparator,
|
|
12049
13607
|
as: Heading = "h1",
|
|
@@ -12054,20 +13612,26 @@ function MinimalHeader({
|
|
|
12054
13612
|
}) {
|
|
12055
13613
|
const hasBreadcrumbs = !!(breadcrumbs && breadcrumbs.length > 0);
|
|
12056
13614
|
const inner = floatingContentProps(isFloating, contentMaxWidth);
|
|
12057
|
-
return /* @__PURE__ */ jsx("header", { ref: headerRef, className: cn("py-ds-2", hasBreadcrumbs && "pt-ds-1", isFloating && FLOATING_HEADER, className), children: /* @__PURE__ */ jsxs("div", { className: inner.className, style: inner.style, children: [
|
|
13615
|
+
return /* @__PURE__ */ jsx("header", { "data-gs": "page-header", ref: headerRef, className: cn("py-ds-2", hasBreadcrumbs && "pt-ds-1", tabs && "pb-ds-4", isFloating && FLOATING_HEADER, className), children: /* @__PURE__ */ jsxs("div", { className: inner.className, style: inner.style, children: [
|
|
13616
|
+
focusBack && /* @__PURE__ */ jsx(BackLink, { onClick: focusBack }),
|
|
12058
13617
|
hasBreadcrumbs && /* @__PURE__ */ jsx(Breadcrumbs3, { items: breadcrumbs, pill: breadcrumbPill, separator: breadcrumbSeparator }),
|
|
13618
|
+
caption && /* @__PURE__ */ jsx(Caption, { children: caption }),
|
|
12059
13619
|
/* @__PURE__ */ jsxs("div", { className: cn("flex items-center justify-between gap-ds-4", hasBreadcrumbs && "mt-ds-0.5"), children: [
|
|
12060
13620
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-2 min-w-0", children: [
|
|
12061
13621
|
/* @__PURE__ */ jsx(TruncHeading, { as: Heading, lines: 1, className: "font-[family-name:var(--ds-font-display)] text-xl font-[var(--ds-weight-heading)] text-[var(--ds-text-primary)]", children: title }),
|
|
12062
13622
|
badge
|
|
12063
13623
|
] }),
|
|
12064
13624
|
actions && /* @__PURE__ */ jsx(ControlSizeContext.Provider, { value: "sm", children: /* @__PURE__ */ jsx("div", { className: "flex items-center gap-ds-2 shrink-0", children: actions }) })
|
|
12065
|
-
] })
|
|
13625
|
+
] }),
|
|
13626
|
+
tabs && /* @__PURE__ */ jsx(TabRow, { hasCaption: !!caption, className: "mt-ds-3 -mx-ds-1", children: tabs })
|
|
12066
13627
|
] }) });
|
|
12067
13628
|
}
|
|
12068
13629
|
function ColourfulHeader({
|
|
12069
13630
|
title,
|
|
12070
13631
|
description,
|
|
13632
|
+
caption,
|
|
13633
|
+
focusBack,
|
|
13634
|
+
tabs,
|
|
12071
13635
|
badge,
|
|
12072
13636
|
breadcrumbs,
|
|
12073
13637
|
onBack,
|
|
@@ -12092,6 +13656,7 @@ function ColourfulHeader({
|
|
|
12092
13656
|
return /* @__PURE__ */ jsxs(
|
|
12093
13657
|
"header",
|
|
12094
13658
|
{
|
|
13659
|
+
"data-gs": "page-header",
|
|
12095
13660
|
ref: headerRef,
|
|
12096
13661
|
className: cn(
|
|
12097
13662
|
"relative overflow-hidden w-full",
|
|
@@ -12135,6 +13700,7 @@ function ColourfulHeader({
|
|
|
12135
13700
|
...overlapActive ? { paddingBottom: `calc(var(--ds-space-6) + var(--ds-overlap, 0px))` } : {}
|
|
12136
13701
|
},
|
|
12137
13702
|
children: [
|
|
13703
|
+
focusBack && /* @__PURE__ */ jsx(BackLink, { onClick: focusBack, onLight: isBold }),
|
|
12138
13704
|
hasBreadcrumbs && /* @__PURE__ */ jsx(Breadcrumbs3, { items: breadcrumbs, onLight: isBold, onTint: !isBold, pill: breadcrumbPill, separator: breadcrumbSeparator }),
|
|
12139
13705
|
/* @__PURE__ */ jsxs("div", { className: cn(
|
|
12140
13706
|
"flex flex-col @sm:flex-row @sm:items-center justify-between gap-ds-4",
|
|
@@ -12144,6 +13710,7 @@ function ColourfulHeader({
|
|
|
12144
13710
|
onBack && /* @__PURE__ */ jsx(BackButton, { onClick: onBack, onLight: isBold }),
|
|
12145
13711
|
avatar && /* @__PURE__ */ jsx("div", { className: "shrink-0", children: avatar }),
|
|
12146
13712
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
13713
|
+
caption && /* @__PURE__ */ jsx(Caption, { onLight: isBold, onTint: !isBold, children: caption }),
|
|
12147
13714
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-2", children: [
|
|
12148
13715
|
/* @__PURE__ */ jsx(TruncHeading, { as: Heading, className: cn(
|
|
12149
13716
|
"font-[family-name:var(--ds-font-display)] text-3xl font-[var(--ds-weight-heading)]",
|
|
@@ -12159,7 +13726,8 @@ function ColourfulHeader({
|
|
|
12159
13726
|
] }),
|
|
12160
13727
|
actions && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-ds-2 shrink-0", children: actions })
|
|
12161
13728
|
] }),
|
|
12162
|
-
filters && /* @__PURE__ */ jsx(ControlSizeContext.Provider, { value: "sm", children: /* @__PURE__ */ jsx("div", { className: "flex items-center flex-wrap gap-ds-3 mt-ds-4", children: filters }) })
|
|
13729
|
+
filters && /* @__PURE__ */ jsx(ControlSizeContext.Provider, { value: "sm", children: /* @__PURE__ */ jsx("div", { className: "flex items-center flex-wrap gap-ds-3 mt-ds-4", children: filters }) }),
|
|
13730
|
+
tabs && /* @__PURE__ */ jsx(TabRow, { hasCaption: !!caption, className: "mt-ds-4 -mx-ds-1", onBand: isBold, children: tabs })
|
|
12163
13731
|
]
|
|
12164
13732
|
}
|
|
12165
13733
|
)
|
|
@@ -12170,6 +13738,8 @@ function ColourfulHeader({
|
|
|
12170
13738
|
function SnappingHeader({
|
|
12171
13739
|
title,
|
|
12172
13740
|
description,
|
|
13741
|
+
caption,
|
|
13742
|
+
focusBack,
|
|
12173
13743
|
badge,
|
|
12174
13744
|
breadcrumbs,
|
|
12175
13745
|
onBack,
|
|
@@ -12293,6 +13863,7 @@ function SnappingHeader({
|
|
|
12293
13863
|
const headerEl = /* @__PURE__ */ jsxs(
|
|
12294
13864
|
"header",
|
|
12295
13865
|
{
|
|
13866
|
+
"data-gs": "page-header",
|
|
12296
13867
|
ref: !isFloating ? mergedRef : void 0,
|
|
12297
13868
|
className: cn(
|
|
12298
13869
|
"transition-all duration-[var(--ds-transition-speed)] ease-[var(--ds-transition-ease)]",
|
|
@@ -12329,8 +13900,10 @@ function SnappingHeader({
|
|
|
12329
13900
|
onTint && !backgroundImage && /* @__PURE__ */ jsx(TintWash, {}),
|
|
12330
13901
|
(() => {
|
|
12331
13902
|
const hasBreadcrumbs = !!(breadcrumbs && breadcrumbs.length > 0);
|
|
12332
|
-
const innerContent = isMinimal && !collapsed ? /* @__PURE__ */ jsxs("div", { className: cn("py-ds-2", hasBreadcrumbs && !hideExpandedBreadcrumbs && "pt-ds-1"), children: [
|
|
13903
|
+
const innerContent = isMinimal && !collapsed ? /* @__PURE__ */ jsxs("div", { className: cn("py-ds-2", hasBreadcrumbs && !hideExpandedBreadcrumbs && "pt-ds-1", tabs && "pb-ds-4"), children: [
|
|
13904
|
+
focusBack && /* @__PURE__ */ jsx(BackLink, { onClick: focusBack }),
|
|
12333
13905
|
hasBreadcrumbs && !hideExpandedBreadcrumbs && /* @__PURE__ */ jsx(Breadcrumbs3, { items: breadcrumbs, pill: breadcrumbPill, separator: breadcrumbSeparator }),
|
|
13906
|
+
caption && /* @__PURE__ */ jsx(Caption, { children: caption }),
|
|
12334
13907
|
/* @__PURE__ */ jsxs("div", { className: cn("flex items-center justify-between gap-ds-4", hasBreadcrumbs && !hideExpandedBreadcrumbs && "mt-ds-0.5"), children: [
|
|
12335
13908
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-2 min-w-0", children: [
|
|
12336
13909
|
/* @__PURE__ */ jsx(TruncHeading, { as: Heading, className: "font-[family-name:var(--ds-font-display)] text-xl font-[var(--ds-weight-heading)] text-[var(--ds-text-primary)]", children: title }),
|
|
@@ -12340,7 +13913,7 @@ function SnappingHeader({
|
|
|
12340
13913
|
] }),
|
|
12341
13914
|
filters && /* @__PURE__ */ jsx(ControlSizeContext.Provider, { value: "sm", children: /* @__PURE__ */ jsx("div", { className: "flex items-center flex-wrap gap-ds-3 mt-ds-3", children: filters }) }),
|
|
12342
13915
|
expandedContent && /* @__PURE__ */ jsx("div", { className: "mt-ds-3", children: expandedContent }),
|
|
12343
|
-
tabs && /* @__PURE__ */ jsx(
|
|
13916
|
+
tabs && /* @__PURE__ */ jsx(TabRow, { hasCaption: !!caption, className: "mt-ds-3 -mx-ds-1", children: tabs })
|
|
12344
13917
|
] }) : /* @__PURE__ */ jsxs(
|
|
12345
13918
|
"div",
|
|
12346
13919
|
{
|
|
@@ -12354,6 +13927,7 @@ function SnappingHeader({
|
|
|
12354
13927
|
),
|
|
12355
13928
|
style: !collapsed && isColourful && overlapActive ? { paddingBottom: `calc(var(--ds-space-6) + var(--ds-overlap, 0px))` } : void 0,
|
|
12356
13929
|
children: [
|
|
13930
|
+
!collapsed && focusBack && /* @__PURE__ */ jsx(BackLink, { onClick: focusBack, onLight }),
|
|
12357
13931
|
!collapsed && hasBreadcrumbs && !hideExpandedBreadcrumbs && /* @__PURE__ */ jsx(Breadcrumbs3, { items: breadcrumbs, onLight, onTint, pill: breadcrumbPill, separator: breadcrumbSeparator }),
|
|
12358
13932
|
/* @__PURE__ */ jsxs("div", { className: cn(
|
|
12359
13933
|
"flex justify-between gap-ds-4",
|
|
@@ -12365,6 +13939,7 @@ function SnappingHeader({
|
|
|
12365
13939
|
onBack && !collapsed && /* @__PURE__ */ jsx(BackButton, { onClick: onBack, onLight }),
|
|
12366
13940
|
avatar && !collapsed && /* @__PURE__ */ jsx("div", { className: "shrink-0", children: avatar }),
|
|
12367
13941
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
13942
|
+
!collapsed && caption && /* @__PURE__ */ jsx(Caption, { onLight, onTint, children: caption }),
|
|
12368
13943
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-2", children: [
|
|
12369
13944
|
collapsed && hasBreadcrumbs && (() => {
|
|
12370
13945
|
const lastCrumb = breadcrumbs[breadcrumbs.length - 1];
|
|
@@ -12407,7 +13982,7 @@ function SnappingHeader({
|
|
|
12407
13982
|
] }),
|
|
12408
13983
|
!collapsed && filters && /* @__PURE__ */ jsx(ControlSizeContext.Provider, { value: "sm", children: /* @__PURE__ */ jsx("div", { className: "flex items-center flex-wrap gap-ds-3 mt-ds-4", children: filters }) }),
|
|
12409
13984
|
!collapsed && expandedContent && /* @__PURE__ */ jsx("div", { className: "mt-ds-4", children: expandedContent }),
|
|
12410
|
-
!collapsed && tabs && /* @__PURE__ */ jsx(
|
|
13985
|
+
!collapsed && tabs && /* @__PURE__ */ jsx(TabRow, { hasCaption: !!caption, className: "mt-ds-4 -mx-ds-1", onBand: isBoldColourful, children: tabs })
|
|
12411
13986
|
]
|
|
12412
13987
|
}
|
|
12413
13988
|
);
|
|
@@ -12455,6 +14030,8 @@ function SnappingHeader({
|
|
|
12455
14030
|
function TabsIntegratedHeader({
|
|
12456
14031
|
title,
|
|
12457
14032
|
description,
|
|
14033
|
+
caption,
|
|
14034
|
+
focusBack,
|
|
12458
14035
|
badge,
|
|
12459
14036
|
breadcrumbs,
|
|
12460
14037
|
onBack,
|
|
@@ -12471,7 +14048,8 @@ function TabsIntegratedHeader({
|
|
|
12471
14048
|
}) {
|
|
12472
14049
|
const hasBreadcrumbs = !!(breadcrumbs && breadcrumbs.length > 0);
|
|
12473
14050
|
const inner = floatingContentProps(isFloating, contentMaxWidth);
|
|
12474
|
-
return /* @__PURE__ */ jsx("header", { ref: headerRef, className: cn(hasBreadcrumbs ? "pt-ds-3" : "pt-ds-6", "pb-ds-4", isFloating && cn(FLOATING_HEADER, "overflow-hidden"), className), children: /* @__PURE__ */ jsxs("div", { className: inner.className, style: inner.style, children: [
|
|
14051
|
+
return /* @__PURE__ */ jsx("header", { "data-gs": "page-header", ref: headerRef, className: cn(hasBreadcrumbs ? "pt-ds-3" : "pt-ds-6", "pb-ds-4", isFloating && cn(FLOATING_HEADER, "overflow-hidden"), className), children: /* @__PURE__ */ jsxs("div", { className: inner.className, style: inner.style, children: [
|
|
14052
|
+
focusBack && /* @__PURE__ */ jsx(BackLink, { onClick: focusBack }),
|
|
12475
14053
|
hasBreadcrumbs && /* @__PURE__ */ jsx(Breadcrumbs3, { items: breadcrumbs, pill: breadcrumbPill, separator: breadcrumbSeparator }),
|
|
12476
14054
|
/* @__PURE__ */ jsxs("div", { className: cn(
|
|
12477
14055
|
"flex flex-col @sm:flex-row @sm:items-center justify-between gap-ds-4",
|
|
@@ -12481,6 +14059,7 @@ function TabsIntegratedHeader({
|
|
|
12481
14059
|
onBack && /* @__PURE__ */ jsx(BackButton, { onClick: onBack }),
|
|
12482
14060
|
avatar && /* @__PURE__ */ jsx("div", { className: "shrink-0", children: avatar }),
|
|
12483
14061
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
14062
|
+
caption && /* @__PURE__ */ jsx(Caption, { children: caption }),
|
|
12484
14063
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-2", children: [
|
|
12485
14064
|
/* @__PURE__ */ jsx(TruncHeading, { as: Heading, className: "font-[family-name:var(--ds-font-display)] text-3xl font-[var(--ds-weight-heading)] text-[var(--ds-text-primary)]", children: title }),
|
|
12486
14065
|
badge
|
|
@@ -12490,7 +14069,7 @@ function TabsIntegratedHeader({
|
|
|
12490
14069
|
] }),
|
|
12491
14070
|
actions && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-ds-2 shrink-0", children: actions })
|
|
12492
14071
|
] }),
|
|
12493
|
-
tabs && /* @__PURE__ */ jsx(
|
|
14072
|
+
tabs && /* @__PURE__ */ jsx(TabRow, { hasCaption: !!caption, className: "mt-ds-4 -mx-ds-1", children: tabs })
|
|
12494
14073
|
] }) });
|
|
12495
14074
|
}
|
|
12496
14075
|
var DEFAULT_BASE = 48;
|
|
@@ -12655,6 +14234,7 @@ function NavMenu({
|
|
|
12655
14234
|
const [open, setOpen] = useState(false);
|
|
12656
14235
|
const triggerRef = useRef(null);
|
|
12657
14236
|
const panelRef = useRef(null);
|
|
14237
|
+
const panelId = useId();
|
|
12658
14238
|
const [pos, setPos] = useState({ top: 0, left: 0, minWidth: 0 });
|
|
12659
14239
|
const close = useCallback(() => setOpen(false), []);
|
|
12660
14240
|
const { isTop: isTopLayer, returnFocus } = usePopupFocus(open, { trigger: triggerRef, panel: panelRef, close, tabExits: "edges" });
|
|
@@ -12680,10 +14260,10 @@ function NavMenu({
|
|
|
12680
14260
|
}
|
|
12681
14261
|
}
|
|
12682
14262
|
function handleKey(e) {
|
|
12683
|
-
if (e.key
|
|
12684
|
-
|
|
12685
|
-
|
|
12686
|
-
|
|
14263
|
+
if (e.key !== "Escape" || e.defaultPrevented || !isTopLayer()) return;
|
|
14264
|
+
e.preventDefault();
|
|
14265
|
+
returnFocus();
|
|
14266
|
+
close();
|
|
12687
14267
|
}
|
|
12688
14268
|
document.addEventListener("mousedown", handleClick);
|
|
12689
14269
|
document.addEventListener("keydown", handleKey);
|
|
@@ -12725,7 +14305,7 @@ function NavMenu({
|
|
|
12725
14305
|
ref: triggerRef,
|
|
12726
14306
|
onClick: () => setOpen(!open),
|
|
12727
14307
|
"aria-expanded": open,
|
|
12728
|
-
"aria-
|
|
14308
|
+
"aria-controls": open ? panelId : void 0,
|
|
12729
14309
|
className: triggerClasses,
|
|
12730
14310
|
children: [
|
|
12731
14311
|
label,
|
|
@@ -12745,7 +14325,7 @@ function NavMenu({
|
|
|
12745
14325
|
"div",
|
|
12746
14326
|
{
|
|
12747
14327
|
ref: panelRef,
|
|
12748
|
-
|
|
14328
|
+
id: panelId,
|
|
12749
14329
|
"data-gs": "menu",
|
|
12750
14330
|
className: cn(
|
|
12751
14331
|
"fixed z-[var(--ds-z-popover)] py-ds-1.5",
|
|
@@ -12754,7 +14334,7 @@ function NavMenu({
|
|
|
12754
14334
|
"border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]"
|
|
12755
14335
|
),
|
|
12756
14336
|
style: { top: pos.top, left: pos.left, minWidth: pos.minWidth },
|
|
12757
|
-
children: items.map((item, i) => {
|
|
14337
|
+
children: /* @__PURE__ */ jsx("ul", { children: items.map((item, i) => {
|
|
12758
14338
|
if (item.children?.length) {
|
|
12759
14339
|
return /* @__PURE__ */ jsx(
|
|
12760
14340
|
NavMenuGroup,
|
|
@@ -12780,7 +14360,7 @@ function NavMenu({
|
|
|
12780
14360
|
},
|
|
12781
14361
|
item.label
|
|
12782
14362
|
);
|
|
12783
|
-
})
|
|
14363
|
+
}) })
|
|
12784
14364
|
}
|
|
12785
14365
|
),
|
|
12786
14366
|
document.body
|
|
@@ -12791,11 +14371,10 @@ function NavMenuItemRow({
|
|
|
12791
14371
|
item,
|
|
12792
14372
|
onClick
|
|
12793
14373
|
}) {
|
|
12794
|
-
return /* @__PURE__ */ jsxs(
|
|
14374
|
+
return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
|
|
12795
14375
|
"a",
|
|
12796
14376
|
{
|
|
12797
14377
|
href: item.href ?? "#",
|
|
12798
|
-
role: "menuitem",
|
|
12799
14378
|
onClick: (e) => {
|
|
12800
14379
|
e.preventDefault();
|
|
12801
14380
|
onClick();
|
|
@@ -12821,14 +14400,15 @@ function NavMenuItemRow({
|
|
|
12821
14400
|
] })
|
|
12822
14401
|
]
|
|
12823
14402
|
}
|
|
12824
|
-
);
|
|
14403
|
+
) });
|
|
12825
14404
|
}
|
|
12826
14405
|
function NavMenuGroup({
|
|
12827
14406
|
group,
|
|
12828
14407
|
isFirst,
|
|
12829
14408
|
onItemClick
|
|
12830
14409
|
}) {
|
|
12831
|
-
|
|
14410
|
+
const headingId = useId();
|
|
14411
|
+
return /* @__PURE__ */ jsxs("li", { children: [
|
|
12832
14412
|
!isFirst && /* @__PURE__ */ jsx(
|
|
12833
14413
|
"div",
|
|
12834
14414
|
{
|
|
@@ -12836,15 +14416,15 @@ function NavMenuGroup({
|
|
|
12836
14416
|
style: { height: "var(--ds-divider-width, 1px)", marginTop: "var(--ds-space-1)", marginBottom: "var(--ds-space-1)" }
|
|
12837
14417
|
}
|
|
12838
14418
|
),
|
|
12839
|
-
/* @__PURE__ */ jsx("p", { className: "px-ds-3 pt-ds-1.5 pb-ds-1 text-2xs font-semibold uppercase tracking-wider text-[var(--ds-text-muted)]", children: group.label }),
|
|
12840
|
-
group.children.map((child) => /* @__PURE__ */ jsx(
|
|
14419
|
+
/* @__PURE__ */ jsx("p", { id: headingId, className: "px-ds-3 pt-ds-1.5 pb-ds-1 text-2xs font-semibold uppercase tracking-wider text-[var(--ds-text-muted)]", children: group.label }),
|
|
14420
|
+
/* @__PURE__ */ jsx("ul", { "aria-labelledby": headingId, children: group.children.map((child) => /* @__PURE__ */ jsx(
|
|
12841
14421
|
NavMenuItemRow,
|
|
12842
14422
|
{
|
|
12843
14423
|
item: child,
|
|
12844
14424
|
onClick: () => onItemClick(child)
|
|
12845
14425
|
},
|
|
12846
14426
|
child.label
|
|
12847
|
-
))
|
|
14427
|
+
)) })
|
|
12848
14428
|
] });
|
|
12849
14429
|
}
|
|
12850
14430
|
function resolveNavItemType(type) {
|
|
@@ -12883,30 +14463,198 @@ var NavItem = forwardRef(function NavItem2({ label, icon, active = false, href,
|
|
|
12883
14463
|
]
|
|
12884
14464
|
}
|
|
12885
14465
|
);
|
|
12886
|
-
});
|
|
12887
|
-
function getTypeClasses(type, active) {
|
|
12888
|
-
switch (type) {
|
|
12889
|
-
case "pill":
|
|
12890
|
-
return cn(
|
|
12891
|
-
"px-ds-2 py-ds-1 rounded-[var(--ds-radius-sm)]",
|
|
12892
|
-
active ? "bg-[var(--ds-accent-primary)] text-[var(--ds-text-on-primary)] font-[var(--ds-weight-heading)]" : "text-[var(--ds-text-secondary)] hover:text-[var(--ds-text-primary)] hover:bg-[var(--ds-bg-muted)]"
|
|
12893
|
-
);
|
|
12894
|
-
case "underline":
|
|
12895
|
-
return cn(
|
|
12896
|
-
"px-ds-2 py-ds-2 relative",
|
|
12897
|
-
active ? "text-[var(--ds-text-primary)] font-[var(--ds-weight-heading)]" : "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)]"
|
|
12898
|
-
);
|
|
12899
|
-
case "minimal":
|
|
12900
|
-
return cn(
|
|
12901
|
-
"px-ds-2 py-ds-1",
|
|
12902
|
-
active ? "text-[var(--ds-accent-primary)] font-medium" : "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-secondary)]"
|
|
12903
|
-
);
|
|
12904
|
-
case "tint":
|
|
12905
|
-
return cn(
|
|
12906
|
-
"px-ds-2 py-ds-1 rounded-[var(--ds-radius-sm)]",
|
|
12907
|
-
active ? "bg-[var(--ds-accent-subtle)] text-[var(--ds-accent-primary)] font-[var(--ds-weight-heading)]" : "text-[var(--ds-text-secondary)] hover:text-[var(--ds-text-primary)] hover:bg-[var(--ds-bg-muted)]"
|
|
12908
|
-
);
|
|
12909
|
-
}
|
|
14466
|
+
});
|
|
14467
|
+
function getTypeClasses(type, active) {
|
|
14468
|
+
switch (type) {
|
|
14469
|
+
case "pill":
|
|
14470
|
+
return cn(
|
|
14471
|
+
"px-ds-2 py-ds-1 rounded-[var(--ds-radius-sm)]",
|
|
14472
|
+
active ? "bg-[var(--ds-accent-primary)] text-[var(--ds-text-on-primary)] font-[var(--ds-weight-heading)]" : "text-[var(--ds-text-secondary)] hover:text-[var(--ds-text-primary)] hover:bg-[var(--ds-bg-muted)]"
|
|
14473
|
+
);
|
|
14474
|
+
case "underline":
|
|
14475
|
+
return cn(
|
|
14476
|
+
"px-ds-2 py-ds-2 relative",
|
|
14477
|
+
active ? "text-[var(--ds-text-primary)] font-[var(--ds-weight-heading)]" : "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)]"
|
|
14478
|
+
);
|
|
14479
|
+
case "minimal":
|
|
14480
|
+
return cn(
|
|
14481
|
+
"px-ds-2 py-ds-1",
|
|
14482
|
+
active ? "text-[var(--ds-accent-primary)] font-medium" : "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-secondary)]"
|
|
14483
|
+
);
|
|
14484
|
+
case "tint":
|
|
14485
|
+
return cn(
|
|
14486
|
+
"px-ds-2 py-ds-1 rounded-[var(--ds-radius-sm)]",
|
|
14487
|
+
active ? "bg-[var(--ds-accent-subtle)] text-[var(--ds-accent-primary)] font-[var(--ds-weight-heading)]" : "text-[var(--ds-text-secondary)] hover:text-[var(--ds-text-primary)] hover:bg-[var(--ds-bg-muted)]"
|
|
14488
|
+
);
|
|
14489
|
+
}
|
|
14490
|
+
}
|
|
14491
|
+
var DEFAULT_BUTTON_ATTR = "data-dialog-default-button";
|
|
14492
|
+
var sizeClasses = {
|
|
14493
|
+
// round 16 (the owner's item 5): the small dialog is 448 px, not 384 — a delete confirmation with a typed field was cramped
|
|
14494
|
+
sm: "max-w-md",
|
|
14495
|
+
md: "max-w-lg",
|
|
14496
|
+
lg: "max-w-2xl",
|
|
14497
|
+
xl: "max-w-4xl",
|
|
14498
|
+
full: "max-w-[calc(100vw-2rem)]"
|
|
14499
|
+
};
|
|
14500
|
+
function Dialog({ open, onClose: onCloseProp, title, description, children, footer, variant = "default", size = "md", alert = false, container, onSubmit, dirty, onDiscard }) {
|
|
14501
|
+
const guard = useDirtyGuard(dirty, onCloseProp, onDiscard);
|
|
14502
|
+
const onClose = guard.requestClose;
|
|
14503
|
+
const titleId = useId();
|
|
14504
|
+
const descriptionId = useId();
|
|
14505
|
+
const isMobile = useBreakpoint("sm");
|
|
14506
|
+
const overlayRef = useRef(null);
|
|
14507
|
+
const dialogRef = useRef(null);
|
|
14508
|
+
const opener = useRef(null);
|
|
14509
|
+
const defaultButtonRef = useRef(null);
|
|
14510
|
+
const onCloseRef = useLatest(onClose);
|
|
14511
|
+
const onSubmitRef = useLatest(onSubmit);
|
|
14512
|
+
const isTopLayer = useOverlayLayer(open);
|
|
14513
|
+
useFocusTrap(dialogRef, open, isTopLayer);
|
|
14514
|
+
const before = useFocusBeforeOpen(open);
|
|
14515
|
+
useEffect(() => {
|
|
14516
|
+
if (!open) return;
|
|
14517
|
+
const active = before.current;
|
|
14518
|
+
opener.current = active instanceof HTMLElement && active !== document.body && active.isConnected ? rememberFocus(active) : null;
|
|
14519
|
+
}, [open, before]);
|
|
14520
|
+
useEffect(() => {
|
|
14521
|
+
if (!open) return;
|
|
14522
|
+
requestAnimationFrame(() => {
|
|
14523
|
+
focusInitial(dialogRef.current, { destructive: variant === "destructive" });
|
|
14524
|
+
});
|
|
14525
|
+
}, [open, variant]);
|
|
14526
|
+
useEffect(() => {
|
|
14527
|
+
if (!open) return;
|
|
14528
|
+
const handler = (e) => {
|
|
14529
|
+
if (e.key !== "Escape" || e.defaultPrevented || !isTopLayer()) return;
|
|
14530
|
+
e.preventDefault();
|
|
14531
|
+
onCloseRef.current();
|
|
14532
|
+
};
|
|
14533
|
+
document.addEventListener("keydown", handler);
|
|
14534
|
+
return () => {
|
|
14535
|
+
document.removeEventListener("keydown", handler);
|
|
14536
|
+
};
|
|
14537
|
+
}, [open, onCloseRef, isTopLayer]);
|
|
14538
|
+
useEffect(() => {
|
|
14539
|
+
if (!open) return;
|
|
14540
|
+
lockBodyScroll();
|
|
14541
|
+
return () => {
|
|
14542
|
+
unlockBodyScroll();
|
|
14543
|
+
const r = opener.current;
|
|
14544
|
+
opener.current = null;
|
|
14545
|
+
const active = document.activeElement;
|
|
14546
|
+
if (!active || active === document.body || !active.isConnected) r?.restore();
|
|
14547
|
+
};
|
|
14548
|
+
}, [open]);
|
|
14549
|
+
if (!open) return null;
|
|
14550
|
+
return createPortal(
|
|
14551
|
+
/* @__PURE__ */ jsxs(
|
|
14552
|
+
"div",
|
|
14553
|
+
{
|
|
14554
|
+
ref: overlayRef,
|
|
14555
|
+
className: "fixed inset-0 z-[var(--ds-z-modal)] flex items-center justify-center p-ds-4",
|
|
14556
|
+
children: [
|
|
14557
|
+
/* @__PURE__ */ jsx(
|
|
14558
|
+
"div",
|
|
14559
|
+
{
|
|
14560
|
+
className: "fixed inset-0 bg-black/50 [backdrop-filter:blur(calc(var(--ds-glass-blur)*0.3))] [-webkit-backdrop-filter:blur(calc(var(--ds-glass-blur)*0.3))]",
|
|
14561
|
+
onClick: alert ? void 0 : onClose,
|
|
14562
|
+
"aria-hidden": "true"
|
|
14563
|
+
}
|
|
14564
|
+
),
|
|
14565
|
+
/* @__PURE__ */ jsxs(
|
|
14566
|
+
"form",
|
|
14567
|
+
{
|
|
14568
|
+
onSubmit: (e) => {
|
|
14569
|
+
e.preventDefault();
|
|
14570
|
+
if (variant === "destructive") return;
|
|
14571
|
+
const submitter = e.nativeEvent.submitter;
|
|
14572
|
+
const implicit = !submitter || submitter === defaultButtonRef.current;
|
|
14573
|
+
if (!implicit && submitter.getAttribute("type") !== "submit") return;
|
|
14574
|
+
if (implicit) {
|
|
14575
|
+
const explicit = e.currentTarget.querySelector(`button[type="submit"]:not([${DEFAULT_BUTTON_ATTR}])`);
|
|
14576
|
+
if (explicit?.disabled) return;
|
|
14577
|
+
}
|
|
14578
|
+
onSubmitRef.current?.();
|
|
14579
|
+
},
|
|
14580
|
+
className: "contents",
|
|
14581
|
+
children: [
|
|
14582
|
+
/* @__PURE__ */ jsx(
|
|
14583
|
+
"button",
|
|
14584
|
+
{
|
|
14585
|
+
ref: defaultButtonRef,
|
|
14586
|
+
type: "submit",
|
|
14587
|
+
tabIndex: -1,
|
|
14588
|
+
"aria-hidden": "true",
|
|
14589
|
+
className: "sr-only",
|
|
14590
|
+
...{ [DEFAULT_BUTTON_ATTR]: "" }
|
|
14591
|
+
}
|
|
14592
|
+
),
|
|
14593
|
+
/* @__PURE__ */ jsxs(
|
|
14594
|
+
"div",
|
|
14595
|
+
{
|
|
14596
|
+
ref: dialogRef,
|
|
14597
|
+
role: alert ? "alertdialog" : "dialog",
|
|
14598
|
+
"aria-modal": "true",
|
|
14599
|
+
"data-gs": "dialog",
|
|
14600
|
+
"aria-labelledby": titleId,
|
|
14601
|
+
"aria-describedby": description ? descriptionId : void 0,
|
|
14602
|
+
style: { containerType: "inline-size" },
|
|
14603
|
+
className: cn(
|
|
14604
|
+
"relative z-10 w-full",
|
|
14605
|
+
isMobile ? "max-w-none h-full" : sizeClasses[size],
|
|
14606
|
+
"bg-[var(--ds-bg-surface)] text-[var(--ds-text-primary)]",
|
|
14607
|
+
GLASS_OVERLAY,
|
|
14608
|
+
"border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
|
|
14609
|
+
isMobile ? "rounded-none" : "rounded-[var(--ds-radius-xl)]",
|
|
14610
|
+
"overflow-hidden",
|
|
14611
|
+
"animate-in fade-in-0 zoom-in-95"
|
|
14612
|
+
),
|
|
14613
|
+
children: [
|
|
14614
|
+
/* @__PURE__ */ jsx(
|
|
14615
|
+
"button",
|
|
14616
|
+
{
|
|
14617
|
+
type: "button",
|
|
14618
|
+
onClick: onClose,
|
|
14619
|
+
...{ [OVERLAY_CLOSE_ATTR]: "" },
|
|
14620
|
+
className: "absolute right-4 top-4 text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer",
|
|
14621
|
+
"aria-label": "Close",
|
|
14622
|
+
children: /* @__PURE__ */ jsx(Icon, { name: "close", size: 18 })
|
|
14623
|
+
}
|
|
14624
|
+
),
|
|
14625
|
+
/* @__PURE__ */ jsx("div", { className: "p-ds-6", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-ds-3", children: [
|
|
14626
|
+
variant === "destructive" && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center shrink-0 w-10 h-10 rounded-[var(--ds-radius-full)] bg-[var(--ds-status-error-bg)]", children: /* @__PURE__ */ jsx(Icon, { name: "warning", size: 20, className: "text-[var(--ds-status-error)]" }) }),
|
|
14627
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
14628
|
+
/* @__PURE__ */ jsx(
|
|
14629
|
+
"h2",
|
|
14630
|
+
{
|
|
14631
|
+
id: titleId,
|
|
14632
|
+
className: "font-[family-name:var(--ds-font-display)] text-lg font-[var(--ds-weight-heading)] leading-none tracking-tight",
|
|
14633
|
+
children: title
|
|
14634
|
+
}
|
|
14635
|
+
),
|
|
14636
|
+
description && /* @__PURE__ */ jsx("p", { id: descriptionId, className: "mt-ds-2 text-sm text-[var(--ds-text-muted)]", children: description }),
|
|
14637
|
+
children && /* @__PURE__ */ jsx("div", { className: "mt-ds-4", children: /* @__PURE__ */ jsx(OverlayCloseCtx, { value: onClose, children }) })
|
|
14638
|
+
] })
|
|
14639
|
+
] }) }),
|
|
14640
|
+
/* @__PURE__ */ jsx(DiscardConfirm, { open: guard.confirming, onKeep: guard.keep, onDiscard: guard.discard }),
|
|
14641
|
+
footer && /* @__PURE__ */ jsx("div", { className: cn(
|
|
14642
|
+
"flex justify-end gap-ds-2 p-ds-4",
|
|
14643
|
+
"border-t-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
|
|
14644
|
+
"bg-[var(--ds-bg-muted)]",
|
|
14645
|
+
"rounded-b-[var(--ds-radius-xl)]"
|
|
14646
|
+
), children: footer })
|
|
14647
|
+
]
|
|
14648
|
+
}
|
|
14649
|
+
)
|
|
14650
|
+
]
|
|
14651
|
+
}
|
|
14652
|
+
)
|
|
14653
|
+
]
|
|
14654
|
+
}
|
|
14655
|
+
),
|
|
14656
|
+
container ?? document.body
|
|
14657
|
+
);
|
|
12910
14658
|
}
|
|
12911
14659
|
function HamburgerButton({ onClick, open, className: cls }) {
|
|
12912
14660
|
return /* @__PURE__ */ jsx(
|
|
@@ -12963,9 +14711,11 @@ var Navbar = forwardRef(function Navbar2({
|
|
|
12963
14711
|
const [visibleCount, setVisibleCount] = useState(null);
|
|
12964
14712
|
const links = useMemo(() => flattenLinks(navLinks), [navLinks]);
|
|
12965
14713
|
const mobileHidesLinks = !!appShell?.isMobile && (appShell.hasMobileMenu || appShell.hasMobileNav);
|
|
14714
|
+
const focus = appShell?.focus ?? null;
|
|
14715
|
+
const [confirmExit, setConfirmExit] = useState(false);
|
|
12966
14716
|
useEffect(() => {
|
|
12967
14717
|
const header = headerRef.current;
|
|
12968
|
-
if (!header || mobileHidesLinks || links.length === 0 || typeof ResizeObserver === "undefined") return;
|
|
14718
|
+
if (!header || mobileHidesLinks || focus || links.length === 0 || typeof ResizeObserver === "undefined") return;
|
|
12969
14719
|
const measure = () => {
|
|
12970
14720
|
const nav = navRef.current;
|
|
12971
14721
|
if (!nav) return;
|
|
@@ -13002,7 +14752,7 @@ var Navbar = forwardRef(function Navbar2({
|
|
|
13002
14752
|
const ro = new ResizeObserver(measure);
|
|
13003
14753
|
ro.observe(header);
|
|
13004
14754
|
return () => ro.disconnect();
|
|
13005
|
-
}, [mobileHidesLinks, links]);
|
|
14755
|
+
}, [mobileHidesLinks, focus, links]);
|
|
13006
14756
|
const [topInset, setTopInset] = useState(null);
|
|
13007
14757
|
const shellEl = appShell?.container ?? null;
|
|
13008
14758
|
useLayoutEffect(() => {
|
|
@@ -13034,7 +14784,40 @@ var Navbar = forwardRef(function Navbar2({
|
|
|
13034
14784
|
}, [visibleCount, links]);
|
|
13035
14785
|
const showHamburger = appShell?.isMobile && appShell.hasMobileMenu && !(isSmallPhone && appShell.hasMobileNav) || !!onMenuClick;
|
|
13036
14786
|
const handleHamburger = () => onMenuClick ? onMenuClick() : appShell?.setSidebarOpen(!appShell.sidebarOpen);
|
|
13037
|
-
const hideNavLinks = mobileHidesLinks;
|
|
14787
|
+
const hideNavLinks = mobileHidesLinks || !!focus;
|
|
14788
|
+
const shownSearch = focus ? null : search;
|
|
14789
|
+
const requestExit = () => {
|
|
14790
|
+
if (!focus) return;
|
|
14791
|
+
if (focus.kind === "in-app" && appShell?.hasUnsavedChanges) {
|
|
14792
|
+
setConfirmExit(true);
|
|
14793
|
+
return;
|
|
14794
|
+
}
|
|
14795
|
+
focus.onExit?.();
|
|
14796
|
+
};
|
|
14797
|
+
const exitControl = focus ? /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "sm", onClick: requestExit, children: focus.exitLabel ?? (focus.kind === "before-app" ? "Save and log out" : "Exit") }) : null;
|
|
14798
|
+
const shownUserMenu = focus?.kind === "before-app" ? null : userMenu;
|
|
14799
|
+
const asking = confirmExit && focus?.kind === "in-app" && !!appShell?.hasUnsavedChanges;
|
|
14800
|
+
if (confirmExit && !asking) setConfirmExit(false);
|
|
14801
|
+
const exitDialog = focus?.kind === "in-app" ? /* @__PURE__ */ jsx(
|
|
14802
|
+
Dialog,
|
|
14803
|
+
{
|
|
14804
|
+
open: asking,
|
|
14805
|
+
onClose: () => setConfirmExit(false),
|
|
14806
|
+
title: "You have unsaved changes",
|
|
14807
|
+
description: "Exit now and your changes are lost.",
|
|
14808
|
+
variant: "destructive",
|
|
14809
|
+
size: "sm",
|
|
14810
|
+
alert: true,
|
|
14811
|
+
container: shellEl ?? void 0,
|
|
14812
|
+
footer: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
14813
|
+
/* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: () => setConfirmExit(false), children: "Keep editing" }),
|
|
14814
|
+
/* @__PURE__ */ jsx(Button, { variant: "destructive", onClick: () => {
|
|
14815
|
+
setConfirmExit(false);
|
|
14816
|
+
focus.onExit?.();
|
|
14817
|
+
}, children: "Discard changes" })
|
|
14818
|
+
] })
|
|
14819
|
+
}
|
|
14820
|
+
) : null;
|
|
13038
14821
|
const type = resolveNavItemType(typeProp);
|
|
13039
14822
|
const isMinimal = type === "minimal";
|
|
13040
14823
|
const isFloating = position === "floating";
|
|
@@ -13100,9 +14883,10 @@ var Navbar = forwardRef(function Navbar2({
|
|
|
13100
14883
|
] }),
|
|
13101
14884
|
navLinks && !hideNavLinks ? /* @__PURE__ */ jsx(NavbarTypeContext.Provider, { value: type, children: /* @__PURE__ */ jsx("nav", { ref: navRef, className: "flex items-center gap-ds-2 min-w-0 overflow-x-clip justify-center", "data-navbar-type": type, children: linkRow }) }) : /* @__PURE__ */ jsx("div", { "data-navbar-spacer": "" }),
|
|
13102
14885
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-4 justify-end", children: [
|
|
13103
|
-
|
|
14886
|
+
shownSearch && /* @__PURE__ */ jsx("div", { className: "flex items-center shrink-0", children: shownSearch }),
|
|
13104
14887
|
actions && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-ds-2 shrink-0", children: actions }),
|
|
13105
|
-
|
|
14888
|
+
exitControl && /* @__PURE__ */ jsx("div", { className: "flex items-center shrink-0", children: exitControl }),
|
|
14889
|
+
shownUserMenu && /* @__PURE__ */ jsx("div", { className: cn("flex items-center", resolvedSize === "sm" ? "min-w-0 max-w-[180px]" : "shrink-0"), children: shownUserMenu })
|
|
13106
14890
|
] })
|
|
13107
14891
|
]
|
|
13108
14892
|
}
|
|
@@ -13127,15 +14911,16 @@ var Navbar = forwardRef(function Navbar2({
|
|
|
13127
14911
|
align === "right" && /* @__PURE__ */ jsx("div", { "data-navbar-spacer": "", className: "flex-1" }),
|
|
13128
14912
|
navLinks && !hideNavLinks && /* @__PURE__ */ jsx(NavbarTypeContext.Provider, { value: type, children: /* @__PURE__ */ jsx("nav", { ref: navRef, className: cn("flex items-center gap-ds-2 min-w-0 overflow-x-clip", align === "left" && "flex-1"), "data-navbar-type": type, children: linkRow }) }),
|
|
13129
14913
|
(!navLinks || hideNavLinks) && /* @__PURE__ */ jsx("div", { "data-navbar-spacer": "", className: "flex-1" }),
|
|
13130
|
-
|
|
14914
|
+
shownSearch && /* @__PURE__ */ jsx("div", { className: "flex items-center shrink-0", children: shownSearch }),
|
|
13131
14915
|
actions && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-ds-2 shrink-0", children: actions }),
|
|
13132
|
-
|
|
14916
|
+
exitControl && /* @__PURE__ */ jsx("div", { className: "flex items-center shrink-0", children: exitControl }),
|
|
14917
|
+
shownUserMenu && /* @__PURE__ */ jsx("div", { className: cn("flex items-center", resolvedSize === "sm" ? "min-w-0 max-w-[180px]" : "shrink-0"), children: shownUserMenu })
|
|
13133
14918
|
]
|
|
13134
14919
|
}
|
|
13135
14920
|
);
|
|
13136
14921
|
}
|
|
13137
14922
|
if (isFloating) {
|
|
13138
|
-
return /* @__PURE__ */
|
|
14923
|
+
return /* @__PURE__ */ jsxs(
|
|
13139
14924
|
"div",
|
|
13140
14925
|
{
|
|
13141
14926
|
ref,
|
|
@@ -13145,11 +14930,17 @@ var Navbar = forwardRef(function Navbar2({
|
|
|
13145
14930
|
className
|
|
13146
14931
|
),
|
|
13147
14932
|
style: { padding: "var(--ds-space-3)" },
|
|
13148
|
-
children:
|
|
14933
|
+
children: [
|
|
14934
|
+
headerEl,
|
|
14935
|
+
exitDialog
|
|
14936
|
+
]
|
|
13149
14937
|
}
|
|
13150
14938
|
);
|
|
13151
14939
|
}
|
|
13152
|
-
return
|
|
14940
|
+
return exitDialog ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
14941
|
+
headerEl,
|
|
14942
|
+
exitDialog
|
|
14943
|
+
] }) : headerEl;
|
|
13153
14944
|
});
|
|
13154
14945
|
function MobileHeaderMenu({ items, type = "tint", onItemClick, className }) {
|
|
13155
14946
|
return /* @__PURE__ */ jsx("nav", { className: cn("flex flex-col", className), style: { padding: "var(--ds-space-4)" }, children: /* @__PURE__ */ jsx("div", { className: "flex flex-col", style: { gap: type === "underline" ? 0 : "var(--ds-space-1)" }, children: items.map((item, i) => /* @__PURE__ */ jsx(
|
|
@@ -13241,172 +15032,6 @@ function MobileMenuItem({
|
|
|
13241
15032
|
}
|
|
13242
15033
|
);
|
|
13243
15034
|
}
|
|
13244
|
-
var DEFAULT_BUTTON_ATTR = "data-dialog-default-button";
|
|
13245
|
-
var sizeClasses = {
|
|
13246
|
-
// round 16 (the owner's item 5): the small dialog is 448 px, not 384 — a delete confirmation with a typed field was cramped
|
|
13247
|
-
sm: "max-w-md",
|
|
13248
|
-
md: "max-w-lg",
|
|
13249
|
-
lg: "max-w-2xl",
|
|
13250
|
-
xl: "max-w-4xl",
|
|
13251
|
-
full: "max-w-[calc(100vw-2rem)]"
|
|
13252
|
-
};
|
|
13253
|
-
function Dialog({ open, onClose: onCloseProp, title, description, children, footer, variant = "default", size = "md", alert = false, container, onSubmit, dirty, onDiscard }) {
|
|
13254
|
-
const guard = useDirtyGuard(dirty, onCloseProp, onDiscard);
|
|
13255
|
-
const onClose = guard.requestClose;
|
|
13256
|
-
const titleId = useId();
|
|
13257
|
-
const descriptionId = useId();
|
|
13258
|
-
const isMobile = useBreakpoint("sm");
|
|
13259
|
-
const overlayRef = useRef(null);
|
|
13260
|
-
const dialogRef = useRef(null);
|
|
13261
|
-
const opener = useRef(null);
|
|
13262
|
-
const defaultButtonRef = useRef(null);
|
|
13263
|
-
const onCloseRef = useLatest(onClose);
|
|
13264
|
-
const onSubmitRef = useLatest(onSubmit);
|
|
13265
|
-
const isTopLayer = useOverlayLayer(open);
|
|
13266
|
-
useFocusTrap(dialogRef, open, isTopLayer);
|
|
13267
|
-
const before = useFocusBeforeOpen(open);
|
|
13268
|
-
useEffect(() => {
|
|
13269
|
-
if (!open) return;
|
|
13270
|
-
const active = before.current;
|
|
13271
|
-
opener.current = active instanceof HTMLElement && active !== document.body && active.isConnected ? rememberFocus(active) : null;
|
|
13272
|
-
}, [open, before]);
|
|
13273
|
-
useEffect(() => {
|
|
13274
|
-
if (!open) return;
|
|
13275
|
-
requestAnimationFrame(() => {
|
|
13276
|
-
focusInitial(dialogRef.current, { destructive: variant === "destructive" });
|
|
13277
|
-
});
|
|
13278
|
-
}, [open, variant]);
|
|
13279
|
-
useEffect(() => {
|
|
13280
|
-
if (!open) return;
|
|
13281
|
-
const handler = (e) => {
|
|
13282
|
-
if (e.key === "Escape" && !alert && isTopLayer()) onCloseRef.current();
|
|
13283
|
-
};
|
|
13284
|
-
document.addEventListener("keydown", handler);
|
|
13285
|
-
return () => {
|
|
13286
|
-
document.removeEventListener("keydown", handler);
|
|
13287
|
-
};
|
|
13288
|
-
}, [open, alert, onCloseRef, isTopLayer]);
|
|
13289
|
-
useEffect(() => {
|
|
13290
|
-
if (!open) return;
|
|
13291
|
-
lockBodyScroll();
|
|
13292
|
-
return () => {
|
|
13293
|
-
unlockBodyScroll();
|
|
13294
|
-
const r = opener.current;
|
|
13295
|
-
opener.current = null;
|
|
13296
|
-
const active = document.activeElement;
|
|
13297
|
-
if (!active || active === document.body || !active.isConnected) r?.restore();
|
|
13298
|
-
};
|
|
13299
|
-
}, [open]);
|
|
13300
|
-
if (!open) return null;
|
|
13301
|
-
return createPortal(
|
|
13302
|
-
/* @__PURE__ */ jsxs(
|
|
13303
|
-
"div",
|
|
13304
|
-
{
|
|
13305
|
-
ref: overlayRef,
|
|
13306
|
-
className: "fixed inset-0 z-[var(--ds-z-modal)] flex items-center justify-center p-ds-4",
|
|
13307
|
-
children: [
|
|
13308
|
-
/* @__PURE__ */ jsx(
|
|
13309
|
-
"div",
|
|
13310
|
-
{
|
|
13311
|
-
className: "fixed inset-0 bg-black/50 [backdrop-filter:blur(calc(var(--ds-glass-blur)*0.3))] [-webkit-backdrop-filter:blur(calc(var(--ds-glass-blur)*0.3))]",
|
|
13312
|
-
onClick: alert ? void 0 : onClose,
|
|
13313
|
-
"aria-hidden": "true"
|
|
13314
|
-
}
|
|
13315
|
-
),
|
|
13316
|
-
/* @__PURE__ */ jsxs(
|
|
13317
|
-
"form",
|
|
13318
|
-
{
|
|
13319
|
-
onSubmit: (e) => {
|
|
13320
|
-
e.preventDefault();
|
|
13321
|
-
if (variant === "destructive") return;
|
|
13322
|
-
const submitter = e.nativeEvent.submitter;
|
|
13323
|
-
const implicit = !submitter || submitter === defaultButtonRef.current;
|
|
13324
|
-
if (!implicit && submitter.getAttribute("type") !== "submit") return;
|
|
13325
|
-
if (implicit) {
|
|
13326
|
-
const explicit = e.currentTarget.querySelector(`button[type="submit"]:not([${DEFAULT_BUTTON_ATTR}])`);
|
|
13327
|
-
if (explicit?.disabled) return;
|
|
13328
|
-
}
|
|
13329
|
-
onSubmitRef.current?.();
|
|
13330
|
-
},
|
|
13331
|
-
className: "contents",
|
|
13332
|
-
children: [
|
|
13333
|
-
/* @__PURE__ */ jsx(
|
|
13334
|
-
"button",
|
|
13335
|
-
{
|
|
13336
|
-
ref: defaultButtonRef,
|
|
13337
|
-
type: "submit",
|
|
13338
|
-
tabIndex: -1,
|
|
13339
|
-
"aria-hidden": "true",
|
|
13340
|
-
className: "sr-only",
|
|
13341
|
-
...{ [DEFAULT_BUTTON_ATTR]: "" }
|
|
13342
|
-
}
|
|
13343
|
-
),
|
|
13344
|
-
/* @__PURE__ */ jsxs(
|
|
13345
|
-
"div",
|
|
13346
|
-
{
|
|
13347
|
-
ref: dialogRef,
|
|
13348
|
-
role: alert ? "alertdialog" : "dialog",
|
|
13349
|
-
"aria-modal": "true",
|
|
13350
|
-
"data-gs": "dialog",
|
|
13351
|
-
"aria-labelledby": titleId,
|
|
13352
|
-
"aria-describedby": description ? descriptionId : void 0,
|
|
13353
|
-
style: { containerType: "inline-size" },
|
|
13354
|
-
className: cn(
|
|
13355
|
-
"relative z-10 w-full",
|
|
13356
|
-
isMobile ? "max-w-none h-full" : sizeClasses[size],
|
|
13357
|
-
"bg-[var(--ds-bg-surface)] text-[var(--ds-text-primary)]",
|
|
13358
|
-
GLASS_OVERLAY,
|
|
13359
|
-
"border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
|
|
13360
|
-
isMobile ? "rounded-none" : "rounded-[var(--ds-radius-xl)]",
|
|
13361
|
-
"overflow-hidden",
|
|
13362
|
-
"animate-in fade-in-0 zoom-in-95"
|
|
13363
|
-
),
|
|
13364
|
-
children: [
|
|
13365
|
-
/* @__PURE__ */ jsx(
|
|
13366
|
-
"button",
|
|
13367
|
-
{
|
|
13368
|
-
type: "button",
|
|
13369
|
-
onClick: onClose,
|
|
13370
|
-
...{ [OVERLAY_CLOSE_ATTR]: "" },
|
|
13371
|
-
className: "absolute right-4 top-4 text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer",
|
|
13372
|
-
"aria-label": "Close",
|
|
13373
|
-
children: /* @__PURE__ */ jsx(Icon, { name: "close", size: 18 })
|
|
13374
|
-
}
|
|
13375
|
-
),
|
|
13376
|
-
/* @__PURE__ */ jsx("div", { className: "p-ds-6", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-ds-3", children: [
|
|
13377
|
-
variant === "destructive" && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center shrink-0 w-10 h-10 rounded-[var(--ds-radius-full)] bg-[var(--ds-status-error-bg)]", children: /* @__PURE__ */ jsx(Icon, { name: "warning", size: 20, className: "text-[var(--ds-status-error)]" }) }),
|
|
13378
|
-
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
13379
|
-
/* @__PURE__ */ jsx(
|
|
13380
|
-
"h2",
|
|
13381
|
-
{
|
|
13382
|
-
id: titleId,
|
|
13383
|
-
className: "font-[family-name:var(--ds-font-display)] text-lg font-[var(--ds-weight-heading)] leading-none tracking-tight",
|
|
13384
|
-
children: title
|
|
13385
|
-
}
|
|
13386
|
-
),
|
|
13387
|
-
description && /* @__PURE__ */ jsx("p", { id: descriptionId, className: "mt-ds-2 text-sm text-[var(--ds-text-muted)]", children: description }),
|
|
13388
|
-
children && /* @__PURE__ */ jsx("div", { className: "mt-ds-4", children: /* @__PURE__ */ jsx(OverlayCloseCtx, { value: onClose, children }) })
|
|
13389
|
-
] })
|
|
13390
|
-
] }) }),
|
|
13391
|
-
/* @__PURE__ */ jsx(DiscardConfirm, { open: guard.confirming, onKeep: guard.keep, onDiscard: guard.discard }),
|
|
13392
|
-
footer && /* @__PURE__ */ jsx("div", { className: cn(
|
|
13393
|
-
"flex justify-end gap-ds-2 p-ds-4",
|
|
13394
|
-
"border-t-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
|
|
13395
|
-
"bg-[var(--ds-bg-muted)]",
|
|
13396
|
-
"rounded-b-[var(--ds-radius-xl)]"
|
|
13397
|
-
), children: footer })
|
|
13398
|
-
]
|
|
13399
|
-
}
|
|
13400
|
-
)
|
|
13401
|
-
]
|
|
13402
|
-
}
|
|
13403
|
-
)
|
|
13404
|
-
]
|
|
13405
|
-
}
|
|
13406
|
-
),
|
|
13407
|
-
container ?? document.body
|
|
13408
|
-
);
|
|
13409
|
-
}
|
|
13410
15035
|
function CommandPalette({ open, onOpenChange, commands, placeholder = "Type a command\u2026", loading = false, container }) {
|
|
13411
15036
|
const [query, setQuery] = useState("");
|
|
13412
15037
|
const [selectedIdx, setSelectedIdx] = useState(0);
|
|
@@ -13443,14 +15068,17 @@ function CommandPalette({ open, onOpenChange, commands, placeholder = "Type a co
|
|
|
13443
15068
|
const isTopLayer = useOverlayLayer(open);
|
|
13444
15069
|
useFocusTrap(dialogRef, open, isTopLayer);
|
|
13445
15070
|
const returnFocus = useReturnFocus(open, dialogRef);
|
|
15071
|
+
const onOpenChangeRef = useLatest(onOpenChange);
|
|
13446
15072
|
useEffect(() => {
|
|
13447
15073
|
if (!open) return;
|
|
13448
15074
|
const handler = (e) => {
|
|
13449
|
-
if (e.key
|
|
15075
|
+
if (e.key !== "Escape" || e.defaultPrevented || !isTopLayer()) return;
|
|
15076
|
+
e.preventDefault();
|
|
15077
|
+
onOpenChangeRef.current(false);
|
|
13450
15078
|
};
|
|
13451
15079
|
document.addEventListener("keydown", handler);
|
|
13452
15080
|
return () => document.removeEventListener("keydown", handler);
|
|
13453
|
-
}, [open,
|
|
15081
|
+
}, [open, onOpenChangeRef, isTopLayer]);
|
|
13454
15082
|
const handleSelect = useCallback(
|
|
13455
15083
|
(cmd) => {
|
|
13456
15084
|
returnFocus();
|
|
@@ -13664,7 +15292,9 @@ function ContextMenu({ items, children, className, ref, container }) {
|
|
|
13664
15292
|
};
|
|
13665
15293
|
const keyHandler = (e) => {
|
|
13666
15294
|
if (e.key === "Escape") {
|
|
13667
|
-
if (isTopLayer())
|
|
15295
|
+
if (e.defaultPrevented || !isTopLayer()) return;
|
|
15296
|
+
e.preventDefault();
|
|
15297
|
+
setOpen(false);
|
|
13668
15298
|
return;
|
|
13669
15299
|
}
|
|
13670
15300
|
const actionable = items.filter((it) => !it.separator && !it.disabled);
|
|
@@ -13801,10 +15431,11 @@ function NotificationCenter({
|
|
|
13801
15431
|
const [internalFilter, setInternalFilter] = useState("all");
|
|
13802
15432
|
const isControlled = controlledOpen !== void 0;
|
|
13803
15433
|
const open = isControlled ? controlledOpen : internalOpen;
|
|
15434
|
+
const onOpenChangeRef = useLatest(onOpenChange);
|
|
13804
15435
|
const setOpen = useCallback((v) => {
|
|
13805
|
-
if (isControlled)
|
|
15436
|
+
if (isControlled) onOpenChangeRef.current?.(v);
|
|
13806
15437
|
else setInternalOpen(v);
|
|
13807
|
-
}, [isControlled,
|
|
15438
|
+
}, [isControlled, onOpenChangeRef]);
|
|
13808
15439
|
const triggerRef = useRef(null);
|
|
13809
15440
|
const panelRef = useRef(null);
|
|
13810
15441
|
const pos = usePosition(triggerRef, panelRef, open, { side: "bottom", align: "end", offset: 8, flip: true });
|
|
@@ -13821,10 +15452,10 @@ function NotificationCenter({
|
|
|
13821
15452
|
useEffect(() => {
|
|
13822
15453
|
if (!open) return;
|
|
13823
15454
|
const onKey = (e) => {
|
|
13824
|
-
if (e.key
|
|
13825
|
-
|
|
13826
|
-
|
|
13827
|
-
|
|
15455
|
+
if (e.key !== "Escape" || e.defaultPrevented || !isTopLayer()) return;
|
|
15456
|
+
e.preventDefault();
|
|
15457
|
+
returnFocus();
|
|
15458
|
+
setOpen(false);
|
|
13828
15459
|
};
|
|
13829
15460
|
const onClick = (e) => {
|
|
13830
15461
|
const target = e.target;
|
|
@@ -13946,6 +15577,8 @@ function NotificationCenter({
|
|
|
13946
15577
|
ref: triggerRef,
|
|
13947
15578
|
onClick: () => setOpen(!open),
|
|
13948
15579
|
"aria-label": `Notifications${unreadCount ? ` (${unreadCount} unread)` : ""}`,
|
|
15580
|
+
"aria-haspopup": "dialog",
|
|
15581
|
+
"aria-expanded": open,
|
|
13949
15582
|
className: cn(
|
|
13950
15583
|
"relative flex items-center justify-center w-9 h-9",
|
|
13951
15584
|
"rounded-[var(--ds-radius-md)]",
|
|
@@ -13964,6 +15597,8 @@ function NotificationCenter({
|
|
|
13964
15597
|
"div",
|
|
13965
15598
|
{
|
|
13966
15599
|
ref: panelRef,
|
|
15600
|
+
role: "dialog",
|
|
15601
|
+
"aria-label": "Notifications",
|
|
13967
15602
|
className: cn(
|
|
13968
15603
|
"fixed z-[var(--ds-z-popover)] w-[360px] max-h-[480px] flex flex-col",
|
|
13969
15604
|
"bg-[var(--ds-bg-surface-elevated)]",
|
|
@@ -14119,17 +15754,21 @@ function Tour({ steps, open, onClose, onComplete, container }) {
|
|
|
14119
15754
|
const raf = requestAnimationFrame(() => nextRef.current?.focus());
|
|
14120
15755
|
return () => cancelAnimationFrame(raf);
|
|
14121
15756
|
}, [open, current]);
|
|
15757
|
+
const onCloseRef = useLatest(onClose);
|
|
15758
|
+
const handleNextRef = useLatest(handleNext);
|
|
15759
|
+
const handlePrevRef = useLatest(handlePrev);
|
|
14122
15760
|
useEffect(() => {
|
|
14123
15761
|
if (!open) return;
|
|
14124
15762
|
const onKey = (e) => {
|
|
14125
|
-
if (!isTopLayer()) return;
|
|
14126
|
-
|
|
14127
|
-
|
|
14128
|
-
|
|
15763
|
+
if (e.defaultPrevented || !isTopLayer()) return;
|
|
15764
|
+
const act = e.key === "Escape" ? onCloseRef.current : e.key === "ArrowRight" ? handleNextRef.current : e.key === "ArrowLeft" ? handlePrevRef.current : null;
|
|
15765
|
+
if (!act) return;
|
|
15766
|
+
e.preventDefault();
|
|
15767
|
+
act();
|
|
14129
15768
|
};
|
|
14130
15769
|
document.addEventListener("keydown", onKey);
|
|
14131
15770
|
return () => document.removeEventListener("keydown", onKey);
|
|
14132
|
-
}, [open,
|
|
15771
|
+
}, [open, isTopLayer, onCloseRef, handleNextRef, handlePrevRef]);
|
|
14133
15772
|
if (!open || !step) return null;
|
|
14134
15773
|
const padding = 8;
|
|
14135
15774
|
const spotlightRadius = 8;
|
|
@@ -14522,7 +16161,7 @@ function usePagination(list, options = {}) {
|
|
|
14522
16161
|
const grows = style !== "pagination";
|
|
14523
16162
|
const start = grows ? 0 : (current - 1) * pageSize;
|
|
14524
16163
|
const items = list.slice(start, current * pageSize);
|
|
14525
|
-
const
|
|
16164
|
+
const range2 = items.length === 0 ? [0, 0] : [start + 1, start + items.length];
|
|
14526
16165
|
const hasMore = current < totalPages;
|
|
14527
16166
|
const setPage = (next) => setPageState(Math.min(Math.max(1, Math.floor(next)), totalPages));
|
|
14528
16167
|
const loadMore = () => setPageState((p) => Math.min(p + 1, totalPages));
|
|
@@ -14537,7 +16176,7 @@ function usePagination(list, options = {}) {
|
|
|
14537
16176
|
pageSize,
|
|
14538
16177
|
totalItems,
|
|
14539
16178
|
totalPages,
|
|
14540
|
-
range,
|
|
16179
|
+
range: range2,
|
|
14541
16180
|
shown: items.length,
|
|
14542
16181
|
hasMore,
|
|
14543
16182
|
loadMore,
|
|
@@ -14593,7 +16232,7 @@ function useDestructiveAction(options) {
|
|
|
14593
16232
|
}
|
|
14594
16233
|
setOpen(true);
|
|
14595
16234
|
};
|
|
14596
|
-
const
|
|
16235
|
+
const matches2 = !typedRequired || typed.trim().toLowerCase() === typedText.trim().toLowerCase();
|
|
14597
16236
|
const element = open ? /* @__PURE__ */ jsx(
|
|
14598
16237
|
Dialog,
|
|
14599
16238
|
{
|
|
@@ -14604,7 +16243,7 @@ function useDestructiveAction(options) {
|
|
|
14604
16243
|
variant: "destructive",
|
|
14605
16244
|
footer: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
14606
16245
|
/* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: close, disabled: busy, children: "Cancel" }),
|
|
14607
|
-
/* @__PURE__ */ jsx(Button, { variant: "destructive", onClick: () => void confirm(), disabled: !
|
|
16246
|
+
/* @__PURE__ */ jsx(Button, { variant: "destructive", onClick: () => void confirm(), disabled: !matches2 || busy, loading: busy, children: label })
|
|
14608
16247
|
] }),
|
|
14609
16248
|
children: typedRequired && /* @__PURE__ */ jsx(Field, { label: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
14610
16249
|
"Type ",
|
|
@@ -14754,6 +16393,6 @@ var TextOverflow = forwardRef(function TextOverflow2({
|
|
|
14754
16393
|
return /* @__PURE__ */ jsx(Tooltip, { content: resolvedTooltipContent, children: content });
|
|
14755
16394
|
});
|
|
14756
16395
|
|
|
14757
|
-
export { Accordion, ActionPanel, ActionPanelGroup, Alert, AsidePanel, Avatar, AvatarGroup, BottomNav, Breadcrumbs, BulkActionBar, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, ChatMessageList, ChatPanel, Checkbox, CheckboxGroup, CheckboxGroupItem, Collapsible, ColorPicker, Combobox, CommandPalette, ContextMenu, CornerPanel, DEFAULT_DATE_RANGE_PRESETS, DataList, DatePicker, DateRangePicker, DescriptionList, Dialog, Dock, Dropdown, DropdownMenu, EmptyState, EntityCell, Field, FileUpload, FormWrapper, HoverCard, InlineFieldError, Input, Kbd, Label, Launcher, MentionInput, MobileHeaderMenu, NavItem, NavMenu, Navbar, NotificationCenter, NumberInput, PageContainer, PageHeader, Pagination, PinInput, Popover, Progress, RadioGroup, Rating, ScrollArea, SectionHeading, SegmentedControl, SettingsLayout, Sidebar, SidebarNav, SliderRange, StackedList, StatsCard, StatusDot, Stepper, Table, TableToolbar, Tabs, Tag, TagInput, TextOverflow, Textarea, TimePicker, Timeline, Toast, ToastProvider, Toggle, Tour, TransferList, TreeView, useContentWidth, useDestructiveAction, useFormContext, usePagination, useSidebar, useToast, useToastOptional };
|
|
16396
|
+
export { Accordion, ActionPanel, ActionPanelGroup, Alert, AsidePanel, Avatar, AvatarGroup, BottomNav, Breadcrumbs, BulkActionBar, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, ChatMessageList, ChatPanel, Checkbox, CheckboxGroup, CheckboxGroupItem, Collapsible, ColorPicker, Combobox, CommandPalette, ContextMenu, CornerPanel, DEFAULT_DATE_RANGE_PRESETS, DataList, DatePicker, DateRangePicker, DescriptionList, Dialog, Dock, Dropdown, DropdownMenu, EmptyState, EntityCell, Field, FileUpload, FormWrapper, Gantt, HoverCard, InlineFieldError, Input, Kbd, Label, Launcher, MentionInput, MobileHeaderMenu, NavItem, NavMenu, Navbar, NotificationCenter, NumberInput, PageContainer, PageHeader, Pagination, PinInput, Popover, Progress, RadioGroup, Rating, ScrollArea, SectionHeading, SegmentedControl, SettingsLayout, Sidebar, SidebarNav, SliderRange, StackedList, StatsCard, StatusDot, Stepper, TIME_ZOOMS, Table, TableToolbar, Tabs, Tag, TagInput, TextOverflow, Textarea, TimeBar, TimePicker, TimeRow, TimeScaleHeader, TimeViewport, Timeline, Toast, ToastProvider, Toggle, Tour, TransferList, TreeView, dateOfDay, dayOf, endDayOf, fitLabel, fitLabels, formatDateRange, timeExtent, useContentWidth, useDestructiveAction, useFittedLabels, useFormContext, usePagination, useSidebar, useTextMeasure, useTimeScale, useTimeViewport, useTimeViewportLayout, useToast, useToastOptional };
|
|
14758
16397
|
//# sourceMappingURL=index.js.map
|
|
14759
16398
|
//# sourceMappingURL=index.js.map
|