gridsmith-ui 0.22.0 → 0.24.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/Breadcrumbs-CGS8GKl-.d.ts +33 -0
- package/dist/chat.js +1 -1
- package/dist/chunk-2PWGJ4PK.js +45 -0
- package/dist/chunk-2PWGJ4PK.js.map +1 -0
- package/dist/chunk-2YSEME33.js +118 -0
- package/dist/chunk-2YSEME33.js.map +1 -0
- package/dist/chunk-3BRQRKDO.js +72 -0
- package/dist/chunk-3BRQRKDO.js.map +1 -0
- package/dist/{chunk-2RV23OBK.js → chunk-6XAU44U4.js} +25 -10
- package/dist/chunk-6XAU44U4.js.map +1 -0
- package/dist/{chunk-RT7RYGC3.js → chunk-JXANCXK6.js} +4 -39
- package/dist/chunk-JXANCXK6.js.map +1 -0
- package/dist/{chunk-AAYCQXZQ.js → chunk-MV7JXWXK.js} +630 -411
- package/dist/chunk-MV7JXWXK.js.map +1 -0
- package/dist/{chunk-EYMGLK4M.js → chunk-SVP2IG4S.js} +2 -2
- package/dist/{chunk-EYMGLK4M.js.map → chunk-SVP2IG4S.js.map} +1 -1
- package/dist/chunk-WOVYHFUD.js +50 -0
- package/dist/chunk-WOVYHFUD.js.map +1 -0
- package/dist/chunk-XVB55M3N.js +42 -0
- package/dist/chunk-XVB55M3N.js.map +1 -0
- package/dist/data-grid.js +5 -3
- package/dist/data-grid.js.map +1 -1
- package/dist/editor.js +11 -3
- package/dist/editor.js.map +1 -1
- package/dist/index.d.ts +227 -45
- package/dist/index.js +843 -586
- package/dist/index.js.map +1 -1
- package/dist/kanban.js +23 -4
- package/dist/kanban.js.map +1 -1
- package/dist/resizable.d.ts +17 -2
- package/dist/resizable.js +102 -24
- package/dist/resizable.js.map +1 -1
- package/dist/roots.css +19 -4
- package/dist/tokens.css +20 -3
- package/dist/virtualized.js +5 -2
- package/dist/virtualized.js.map +1 -1
- package/dist-static/tokens.css +20 -3
- package/package.json +1 -1
- package/dist/chunk-2RV23OBK.js.map +0 -1
- package/dist/chunk-AAYCQXZQ.js.map +0 -1
- package/dist/chunk-RG4KUCRS.js +0 -9
- package/dist/chunk-RG4KUCRS.js.map +0 -1
- package/dist/chunk-RT7RYGC3.js.map +0 -1
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { PreferencesContext, Button } from './chunk-7TOJ4G7S.js';
|
|
2
2
|
import { useBreakpoint, readBreakpointPx } from './chunk-CRQ6KCRD.js';
|
|
3
|
-
import { FormChangeCtx
|
|
4
|
-
import {
|
|
3
|
+
import { FormChangeCtx } from './chunk-XVB55M3N.js';
|
|
4
|
+
import { SurfaceBoundary } from './chunk-2PWGJ4PK.js';
|
|
5
|
+
import { usePopupFocus, useOverlayLayer, useReturnFocus, useFocusTrap, focusInitial, useNonModalDialog, OVERLAY_CLOSE_ATTR } from './chunk-JXANCXK6.js';
|
|
6
|
+
import { usePosition, useTextOverflow, Tooltip } from './chunk-CXMV7SXJ.js';
|
|
5
7
|
import { useLatest } from './chunk-WH2FUFAC.js';
|
|
6
8
|
import { Icon } from './chunk-4LDYR7QP.js';
|
|
7
9
|
import { cn, lockBodyScroll, unlockBodyScroll, OVERLAY_SURFACE, GLASS_OVERLAY } from './chunk-M2QRNBDQ.js';
|
|
8
|
-
import { createContext, version, forwardRef, useState, useCallback, useRef, useEffect, isValidElement, useMemo, useId,
|
|
10
|
+
import { createContext, version, forwardRef, useState, useCallback, useRef, useEffect, isValidElement, useContext, useMemo, useId, useLayoutEffect } from 'react';
|
|
9
11
|
import { createPortal } from 'react-dom';
|
|
10
12
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
11
13
|
|
|
@@ -54,6 +56,126 @@ function useCloseSettle(open, elRef, onSettled) {
|
|
|
54
56
|
};
|
|
55
57
|
}, [open, elRef, cb]);
|
|
56
58
|
}
|
|
59
|
+
var STEP = 16;
|
|
60
|
+
var BIG_STEP = 64;
|
|
61
|
+
function clamp(v, min, max) {
|
|
62
|
+
return Math.min(Math.max(v, min), max);
|
|
63
|
+
}
|
|
64
|
+
function ResizeEdge({ edge, width, min, max, defaultWidth, onResize, label, onDraggingChange, className }) {
|
|
65
|
+
const [dragging, setDragging] = useState(false);
|
|
66
|
+
const start = useRef(null);
|
|
67
|
+
const latestOnResize = useLatest(onResize);
|
|
68
|
+
const direction = edge === "left" ? -1 : 1;
|
|
69
|
+
const onPointerDown = useCallback(
|
|
70
|
+
(e) => {
|
|
71
|
+
if (e.button !== 0) return;
|
|
72
|
+
e.preventDefault();
|
|
73
|
+
start.current = { x: e.clientX, width };
|
|
74
|
+
setDragging(true);
|
|
75
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
76
|
+
},
|
|
77
|
+
[width]
|
|
78
|
+
);
|
|
79
|
+
const onPointerMove = useCallback(
|
|
80
|
+
(e) => {
|
|
81
|
+
if (!start.current) return;
|
|
82
|
+
const next = clamp(start.current.width + direction * (e.clientX - start.current.x), min, max());
|
|
83
|
+
latestOnResize.current(next);
|
|
84
|
+
},
|
|
85
|
+
[direction, min, max, latestOnResize]
|
|
86
|
+
);
|
|
87
|
+
const endDrag = useCallback(() => {
|
|
88
|
+
start.current = null;
|
|
89
|
+
setDragging(false);
|
|
90
|
+
}, []);
|
|
91
|
+
const latestOnDraggingChange = useLatest(onDraggingChange);
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
latestOnDraggingChange.current?.(dragging);
|
|
94
|
+
}, [dragging, latestOnDraggingChange]);
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
if (!dragging) return;
|
|
97
|
+
const onKey = (e) => {
|
|
98
|
+
if (e.key !== "Escape" || !start.current) return;
|
|
99
|
+
latestOnResize.current(start.current.width);
|
|
100
|
+
endDrag();
|
|
101
|
+
};
|
|
102
|
+
document.addEventListener("keydown", onKey);
|
|
103
|
+
return () => document.removeEventListener("keydown", onKey);
|
|
104
|
+
}, [dragging, endDrag, latestOnResize]);
|
|
105
|
+
const onKeyDown = (e) => {
|
|
106
|
+
const step = e.shiftKey ? BIG_STEP : STEP;
|
|
107
|
+
let next = null;
|
|
108
|
+
if (e.key === "ArrowLeft") next = width - direction * step;
|
|
109
|
+
else if (e.key === "ArrowRight") next = width + direction * step;
|
|
110
|
+
else if (e.key === "Home") next = min;
|
|
111
|
+
else if (e.key === "End") next = max();
|
|
112
|
+
else if (e.key === "Enter") next = defaultWidth;
|
|
113
|
+
if (next === null) return;
|
|
114
|
+
e.preventDefault();
|
|
115
|
+
onResize(clamp(next, min, max()));
|
|
116
|
+
};
|
|
117
|
+
return /* @__PURE__ */ jsx(
|
|
118
|
+
"div",
|
|
119
|
+
{
|
|
120
|
+
role: "separator",
|
|
121
|
+
tabIndex: 0,
|
|
122
|
+
"aria-orientation": "vertical",
|
|
123
|
+
"aria-label": label,
|
|
124
|
+
"aria-valuemin": min,
|
|
125
|
+
"aria-valuemax": max(),
|
|
126
|
+
"aria-valuenow": Math.round(width),
|
|
127
|
+
"data-resize-edge": edge,
|
|
128
|
+
"data-dragging": dragging || void 0,
|
|
129
|
+
onPointerDown,
|
|
130
|
+
onPointerMove,
|
|
131
|
+
onPointerUp: endDrag,
|
|
132
|
+
onPointerCancel: endDrag,
|
|
133
|
+
onDoubleClick: () => onResize(clamp(defaultWidth, min, max())),
|
|
134
|
+
onKeyDown,
|
|
135
|
+
className: cn(
|
|
136
|
+
// A narrow absolutely-positioned strip over the edge — never a flex child that takes width.
|
|
137
|
+
"group/edge absolute top-0 bottom-0 z-10 w-ds-2 cursor-col-resize touch-none select-none outline-none",
|
|
138
|
+
edge === "left" ? "-left-ds-1" : "-right-ds-1",
|
|
139
|
+
className
|
|
140
|
+
),
|
|
141
|
+
children: /* @__PURE__ */ jsx(
|
|
142
|
+
"div",
|
|
143
|
+
{
|
|
144
|
+
"aria-hidden": "true",
|
|
145
|
+
className: cn(
|
|
146
|
+
"absolute top-0 bottom-0 left-1/2 w-px -translate-x-1/2 transition-[width,background-color] duration-[var(--ds-transition-speed)]",
|
|
147
|
+
"group-hover/edge:w-[3px] group-hover/edge:bg-[var(--ds-accent-primary)]",
|
|
148
|
+
"group-focus-visible/edge:w-[3px] group-focus-visible/edge:bg-[var(--ds-accent-primary)]",
|
|
149
|
+
dragging && "w-[3px] bg-[var(--ds-accent-primary)]"
|
|
150
|
+
)
|
|
151
|
+
}
|
|
152
|
+
)
|
|
153
|
+
}
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
var remembered = /* @__PURE__ */ new Map();
|
|
157
|
+
function useRememberedWidth(key) {
|
|
158
|
+
const [width, setWidth] = useState(() => key ? remembered.get(key) ?? null : null);
|
|
159
|
+
const remember = useCallback(
|
|
160
|
+
(w) => {
|
|
161
|
+
setWidth(w);
|
|
162
|
+
if (!key) return;
|
|
163
|
+
if (w === null) remembered.delete(key);
|
|
164
|
+
else remembered.set(key, w);
|
|
165
|
+
},
|
|
166
|
+
[key]
|
|
167
|
+
);
|
|
168
|
+
return [width, remember];
|
|
169
|
+
}
|
|
170
|
+
function pxOf(length) {
|
|
171
|
+
if (!length) return null;
|
|
172
|
+
const m = /^(\d+(?:\.\d+)?)px$/.exec(length.trim());
|
|
173
|
+
return m ? parseFloat(m[1]) : null;
|
|
174
|
+
}
|
|
175
|
+
function defaultMaxWidth(container) {
|
|
176
|
+
const base = container instanceof HTMLElement ? container.clientWidth : typeof window !== "undefined" ? window.innerWidth : 1200;
|
|
177
|
+
return Math.round(base * 0.6);
|
|
178
|
+
}
|
|
57
179
|
var OVERLAY_ROOT_ATTR = "data-overlay-root";
|
|
58
180
|
function controlsOf(root) {
|
|
59
181
|
if (!root) return "";
|
|
@@ -177,371 +299,69 @@ function useDirtyGuard(dirty, onClose, onDiscard) {
|
|
|
177
299
|
const then = pending.current;
|
|
178
300
|
pending.current = void 0;
|
|
179
301
|
runAfter(then);
|
|
180
|
-
}, [onClose, onDiscard, runAfter]);
|
|
181
|
-
return { confirming, requestClose, keep, discard };
|
|
182
|
-
}
|
|
183
|
-
function DiscardConfirm({ open, onKeep, onDiscard }) {
|
|
184
|
-
const keepRef = useRef(null);
|
|
185
|
-
const returnTo = useRef(null);
|
|
186
|
-
const overlay = useRef(null);
|
|
187
|
-
useEffect(() => {
|
|
188
|
-
if (!open) return;
|
|
189
|
-
const active = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
190
|
-
overlay.current = keepRef.current?.closest("[role='dialog'], [role='alertdialog']") ?? null;
|
|
191
|
-
returnTo.current = active && active !== document.body && overlay.current?.contains(active) ? active : null;
|
|
192
|
-
keepRef.current?.focus();
|
|
193
|
-
return () => {
|
|
194
|
-
if (document.activeElement !== document.body) return;
|
|
195
|
-
const back = returnTo.current;
|
|
196
|
-
if (back && back.isConnected) {
|
|
197
|
-
back.focus();
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
const first = overlay.current?.querySelector("button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex='-1'])");
|
|
201
|
-
(first ?? overlay.current)?.focus();
|
|
202
|
-
};
|
|
203
|
-
}, [open]);
|
|
204
|
-
if (!open) return null;
|
|
205
|
-
return /* @__PURE__ */ jsxs(
|
|
206
|
-
"div",
|
|
207
|
-
{
|
|
208
|
-
role: "group",
|
|
209
|
-
"aria-label": "Unsaved changes",
|
|
210
|
-
"data-discard-confirm": "",
|
|
211
|
-
className: "shrink-0 flex flex-wrap items-center justify-between gap-ds-3 border-t-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)] bg-[var(--ds-status-warning-bg)]",
|
|
212
|
-
style: { padding: "var(--ds-space-3) var(--ds-space-5)" },
|
|
213
|
-
children: [
|
|
214
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--ds-text-primary)]", children: "You have unsaved changes." }),
|
|
215
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-2", children: [
|
|
216
|
-
/* @__PURE__ */ jsx(Button, { type: "button", ref: keepRef, variant: "secondary", size: "sm", onClick: onKeep, children: "Keep editing" }),
|
|
217
|
-
/* @__PURE__ */ jsx(Button, { type: "button", variant: "destructive", size: "sm", onClick: onDiscard, children: "Discard changes" })
|
|
218
|
-
] })
|
|
219
|
-
]
|
|
220
|
-
}
|
|
221
|
-
);
|
|
222
|
-
}
|
|
223
|
-
function Sheet({ open, onClose: onCloseProp, title, children, footer, confirm, dirty, onDiscard, snapPoints = [0.5, 0.92], container, flush, keepMounted = false, id, labelledBy }) {
|
|
224
|
-
const standalone = confirm === void 0;
|
|
225
|
-
const { marked, mark } = useOverlayMark(open);
|
|
226
|
-
const guards = standalone || dirty !== void 0;
|
|
227
|
-
const guard = useDirtyGuard(standalone ? dirty ?? marked : dirty, onCloseProp, onDiscard);
|
|
228
|
-
const onClose = guard.requestClose;
|
|
229
|
-
const titleId = useId();
|
|
230
|
-
const panelRef = useRef(null);
|
|
231
|
-
const [mounted, setMounted] = useState(open);
|
|
232
|
-
const [hidden, setHidden] = useState(!open);
|
|
233
|
-
const [snapIdx, setSnapIdx] = useState(0);
|
|
234
|
-
const [dragOffset, setDragOffset] = useState(0);
|
|
235
|
-
const [dragging, setDragging] = useState(false);
|
|
236
|
-
const dragStartY = useRef(0);
|
|
237
|
-
const startHeight = useRef(0);
|
|
238
|
-
const onCloseRef = useLatest(onClose);
|
|
239
|
-
const [prevOpen, setPrevOpen] = useState(open);
|
|
240
|
-
if (open !== prevOpen) {
|
|
241
|
-
setPrevOpen(open);
|
|
242
|
-
if (open) {
|
|
243
|
-
setMounted(true);
|
|
244
|
-
setHidden(false);
|
|
245
|
-
setSnapIdx(0);
|
|
246
|
-
setDragOffset(0);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
if (open && !mounted) setMounted(true);
|
|
250
|
-
if (open && hidden) setHidden(false);
|
|
251
|
-
const viewportHeight = useCallback(
|
|
252
|
-
() => container instanceof HTMLElement ? container.clientHeight : window.innerHeight,
|
|
253
|
-
[container]
|
|
254
|
-
);
|
|
255
|
-
useCloseSettle(open, panelRef, () => {
|
|
256
|
-
if (keepMounted) setHidden(true);
|
|
257
|
-
else setMounted(false);
|
|
258
|
-
});
|
|
259
|
-
useReturnFocus(open, panelRef);
|
|
260
|
-
useEffect(() => {
|
|
261
|
-
if (!open || !mounted) return;
|
|
262
|
-
const raf = requestAnimationFrame(() => focusInitial(panelRef.current));
|
|
263
|
-
return () => cancelAnimationFrame(raf);
|
|
264
|
-
}, [open, mounted]);
|
|
265
|
-
const isTopLayer = useOverlayLayer(open);
|
|
266
|
-
useFocusTrap(panelRef, open, isTopLayer);
|
|
267
|
-
useEffect(() => {
|
|
268
|
-
if (!open) return;
|
|
269
|
-
const onKeyDown = (e) => {
|
|
270
|
-
if (e.key !== "Escape" || e.defaultPrevented || !isTopLayer()) return;
|
|
271
|
-
e.preventDefault();
|
|
272
|
-
onCloseRef.current();
|
|
273
|
-
};
|
|
274
|
-
document.addEventListener("keydown", onKeyDown);
|
|
275
|
-
if (!container) lockBodyScroll();
|
|
276
|
-
return () => {
|
|
277
|
-
document.removeEventListener("keydown", onKeyDown);
|
|
278
|
-
if (!container) unlockBodyScroll();
|
|
279
|
-
};
|
|
280
|
-
}, [open, container, onCloseRef, isTopLayer]);
|
|
281
|
-
const handlePointerDown = useCallback((e) => {
|
|
282
|
-
setDragging(true);
|
|
283
|
-
dragStartY.current = e.clientY;
|
|
284
|
-
startHeight.current = (snapPoints[snapIdx] ?? snapPoints[0]) * viewportHeight();
|
|
285
|
-
setDragOffset(0);
|
|
286
|
-
e.target.setPointerCapture(e.pointerId);
|
|
287
|
-
}, [snapIdx, snapPoints, viewportHeight]);
|
|
288
|
-
const handlePointerMove = useCallback((e) => {
|
|
289
|
-
if (!dragging) return;
|
|
290
|
-
const delta = dragStartY.current - e.clientY;
|
|
291
|
-
setDragOffset(delta);
|
|
292
|
-
}, [dragging]);
|
|
293
|
-
const handlePointerUp = useCallback(() => {
|
|
294
|
-
if (!dragging) return;
|
|
295
|
-
setDragging(false);
|
|
296
|
-
const currentHeightPx = startHeight.current + dragOffset;
|
|
297
|
-
const currentFraction = currentHeightPx / viewportHeight();
|
|
298
|
-
if (currentFraction < snapPoints[0] * 0.6) {
|
|
299
|
-
onClose();
|
|
300
|
-
setDragOffset(0);
|
|
301
|
-
return;
|
|
302
|
-
}
|
|
303
|
-
let bestIdx = 0;
|
|
304
|
-
let bestDist = Math.abs(currentFraction - snapPoints[0]);
|
|
305
|
-
for (let i = 1; i < snapPoints.length; i++) {
|
|
306
|
-
const dist = Math.abs(currentFraction - snapPoints[i]);
|
|
307
|
-
if (dist < bestDist) {
|
|
308
|
-
bestDist = dist;
|
|
309
|
-
bestIdx = i;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
setSnapIdx(bestIdx);
|
|
313
|
-
setDragOffset(0);
|
|
314
|
-
}, [dragging, dragOffset, snapPoints, onClose, viewportHeight]);
|
|
315
|
-
if (!mounted) return null;
|
|
316
|
-
const baseHeight = (snapPoints[snapIdx] ?? snapPoints[0]) * 100;
|
|
317
|
-
const heightUnit = container ? "%" : "vh";
|
|
318
|
-
const dragPx = dragging ? dragOffset : 0;
|
|
319
|
-
const positionClass = container ? "absolute" : "fixed";
|
|
320
|
-
return createPortal(
|
|
321
|
-
/* @__PURE__ */ jsxs(
|
|
322
|
-
"div",
|
|
323
|
-
{
|
|
324
|
-
hidden: keepMounted && hidden ? true : void 0,
|
|
325
|
-
className: cn(
|
|
326
|
-
`${positionClass} inset-0 z-[var(--ds-z-modal)] transition-opacity duration-[var(--ds-transition-speed)]`,
|
|
327
|
-
open ? "pointer-events-auto" : "pointer-events-none"
|
|
328
|
-
),
|
|
329
|
-
children: [
|
|
330
|
-
/* @__PURE__ */ jsx(
|
|
331
|
-
"div",
|
|
332
|
-
{
|
|
333
|
-
className: cn(
|
|
334
|
-
"absolute inset-0 bg-black/50 transition-opacity duration-[var(--ds-transition-speed)]",
|
|
335
|
-
open ? "opacity-100" : "opacity-0"
|
|
336
|
-
),
|
|
337
|
-
onClick: onClose
|
|
338
|
-
}
|
|
339
|
-
),
|
|
340
|
-
/* @__PURE__ */ jsxs(
|
|
341
|
-
"div",
|
|
342
|
-
{
|
|
343
|
-
ref: panelRef,
|
|
344
|
-
id,
|
|
345
|
-
role: "dialog",
|
|
346
|
-
"data-gs": "sheet",
|
|
347
|
-
...{ [OVERLAY_ROOT_ATTR]: "" },
|
|
348
|
-
"aria-modal": "true",
|
|
349
|
-
"aria-labelledby": title ? titleId : labelledBy,
|
|
350
|
-
"data-ds-sheet": "",
|
|
351
|
-
className: cn(
|
|
352
|
-
`${positionClass} bottom-0 left-0 right-0 z-10 flex flex-col`,
|
|
353
|
-
OVERLAY_SURFACE,
|
|
354
|
-
"text-[var(--ds-text-primary)]",
|
|
355
|
-
GLASS_OVERLAY,
|
|
356
|
-
"border-t-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
|
|
357
|
-
"rounded-t-[var(--ds-radius-xl)]",
|
|
358
|
-
!dragging && "transition-[transform,height] duration-[var(--ds-transition-speed)] ease-[var(--ds-transition-ease)]",
|
|
359
|
-
open ? "translate-y-0" : "translate-y-full"
|
|
360
|
-
),
|
|
361
|
-
style: {
|
|
362
|
-
containerType: "inline-size",
|
|
363
|
-
height: dragPx ? `calc(${baseHeight}${heightUnit} + ${dragPx}px)` : `${baseHeight}${heightUnit}`
|
|
364
|
-
},
|
|
365
|
-
children: [
|
|
366
|
-
/* @__PURE__ */ jsx(
|
|
367
|
-
"div",
|
|
368
|
-
{
|
|
369
|
-
className: "flex justify-center pt-ds-2 pb-ds-1 cursor-grab active:cursor-grabbing touch-none",
|
|
370
|
-
onPointerDown: handlePointerDown,
|
|
371
|
-
onPointerMove: handlePointerMove,
|
|
372
|
-
onPointerUp: handlePointerUp,
|
|
373
|
-
onPointerCancel: handlePointerUp,
|
|
374
|
-
children: /* @__PURE__ */ jsx("div", { className: "w-10 h-1 rounded-[var(--ds-radius-full)] bg-[var(--ds-border-default)]" })
|
|
375
|
-
}
|
|
376
|
-
),
|
|
377
|
-
title && /* @__PURE__ */ jsxs(
|
|
378
|
-
"div",
|
|
379
|
-
{
|
|
380
|
-
className: "flex items-center justify-between shrink-0 border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
|
|
381
|
-
style: { padding: "var(--ds-space-2) var(--ds-space-5)" },
|
|
382
|
-
children: [
|
|
383
|
-
/* @__PURE__ */ jsx("h2", { id: titleId, className: "font-[family-name:var(--ds-font-display)] text-lg font-[var(--ds-weight-heading)] text-[var(--ds-text-primary)] truncate", children: title }),
|
|
384
|
-
/* @__PURE__ */ jsx(
|
|
385
|
-
"button",
|
|
386
|
-
{
|
|
387
|
-
type: "button",
|
|
388
|
-
onClick: onClose,
|
|
389
|
-
...{ [OVERLAY_CLOSE_ATTR]: "" },
|
|
390
|
-
className: "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer",
|
|
391
|
-
"aria-label": "Close",
|
|
392
|
-
children: /* @__PURE__ */ jsx(Icon, { name: "close", size: 18 })
|
|
393
|
-
}
|
|
394
|
-
)
|
|
395
|
-
]
|
|
396
|
-
}
|
|
397
|
-
),
|
|
398
|
-
/* @__PURE__ */ jsx(
|
|
399
|
-
"div",
|
|
400
|
-
{
|
|
401
|
-
className: "flex-1 overflow-y-auto",
|
|
402
|
-
style: flush ? void 0 : { padding: "var(--ds-space-5)" },
|
|
403
|
-
children: guards ? /* @__PURE__ */ jsx(OverlayCloseCtx, { value: guard.requestClose, children: standalone ? /* @__PURE__ */ jsx(OverlayMarkScope, { mark, children }) : children }) : children
|
|
404
|
-
}
|
|
405
|
-
),
|
|
406
|
-
confirm,
|
|
407
|
-
guards && /* @__PURE__ */ jsx(DiscardConfirm, { open: guard.confirming, onKeep: guard.keep, onDiscard: guard.discard }),
|
|
408
|
-
footer && /* @__PURE__ */ jsx(
|
|
409
|
-
"div",
|
|
410
|
-
{
|
|
411
|
-
className: "shrink-0 flex items-center justify-end gap-ds-2 border-t-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
|
|
412
|
-
style: { padding: "var(--ds-space-3) var(--ds-space-5)" },
|
|
413
|
-
children: footer
|
|
414
|
-
}
|
|
415
|
-
)
|
|
416
|
-
]
|
|
417
|
-
}
|
|
418
|
-
)
|
|
419
|
-
]
|
|
420
|
-
}
|
|
421
|
-
),
|
|
422
|
-
container ?? document.body
|
|
423
|
-
);
|
|
424
|
-
}
|
|
425
|
-
var STEP = 16;
|
|
426
|
-
var BIG_STEP = 64;
|
|
427
|
-
function clamp(v, min, max) {
|
|
428
|
-
return Math.min(Math.max(v, min), max);
|
|
429
|
-
}
|
|
430
|
-
function ResizeEdge({ edge, width, min, max, defaultWidth, onResize, label, onDraggingChange, className }) {
|
|
431
|
-
const [dragging, setDragging] = useState(false);
|
|
432
|
-
const start = useRef(null);
|
|
433
|
-
const latestOnResize = useLatest(onResize);
|
|
434
|
-
const direction = edge === "left" ? -1 : 1;
|
|
435
|
-
const onPointerDown = useCallback(
|
|
436
|
-
(e) => {
|
|
437
|
-
if (e.button !== 0) return;
|
|
438
|
-
e.preventDefault();
|
|
439
|
-
start.current = { x: e.clientX, width };
|
|
440
|
-
setDragging(true);
|
|
441
|
-
e.currentTarget.setPointerCapture(e.pointerId);
|
|
442
|
-
},
|
|
443
|
-
[width]
|
|
444
|
-
);
|
|
445
|
-
const onPointerMove = useCallback(
|
|
446
|
-
(e) => {
|
|
447
|
-
if (!start.current) return;
|
|
448
|
-
const next = clamp(start.current.width + direction * (e.clientX - start.current.x), min, max());
|
|
449
|
-
latestOnResize.current(next);
|
|
450
|
-
},
|
|
451
|
-
[direction, min, max, latestOnResize]
|
|
452
|
-
);
|
|
453
|
-
const endDrag = useCallback(() => {
|
|
454
|
-
start.current = null;
|
|
455
|
-
setDragging(false);
|
|
456
|
-
}, []);
|
|
457
|
-
const latestOnDraggingChange = useLatest(onDraggingChange);
|
|
458
|
-
useEffect(() => {
|
|
459
|
-
latestOnDraggingChange.current?.(dragging);
|
|
460
|
-
}, [dragging, latestOnDraggingChange]);
|
|
302
|
+
}, [onClose, onDiscard, runAfter]);
|
|
303
|
+
return { confirming, requestClose, keep, discard };
|
|
304
|
+
}
|
|
305
|
+
function DiscardConfirm({ open, onKeep, onDiscard }) {
|
|
306
|
+
const keepRef = useRef(null);
|
|
307
|
+
const returnTo = useRef(null);
|
|
308
|
+
const overlay = useRef(null);
|
|
461
309
|
useEffect(() => {
|
|
462
|
-
if (!
|
|
463
|
-
const
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
310
|
+
if (!open) return;
|
|
311
|
+
const active = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
312
|
+
overlay.current = keepRef.current?.closest("[role='dialog'], [role='alertdialog']") ?? null;
|
|
313
|
+
returnTo.current = active && active !== document.body && overlay.current?.contains(active) ? active : null;
|
|
314
|
+
keepRef.current?.focus();
|
|
315
|
+
return () => {
|
|
316
|
+
if (document.activeElement !== document.body) return;
|
|
317
|
+
const back = returnTo.current;
|
|
318
|
+
if (back && back.isConnected) {
|
|
319
|
+
back.focus();
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
const first = overlay.current?.querySelector("button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex='-1'])");
|
|
323
|
+
(first ?? overlay.current)?.focus();
|
|
467
324
|
};
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
const onKeyDown = (e) => {
|
|
472
|
-
const step = e.shiftKey ? BIG_STEP : STEP;
|
|
473
|
-
let next = null;
|
|
474
|
-
if (e.key === "ArrowLeft") next = width - direction * step;
|
|
475
|
-
else if (e.key === "ArrowRight") next = width + direction * step;
|
|
476
|
-
else if (e.key === "Home") next = min;
|
|
477
|
-
else if (e.key === "End") next = max();
|
|
478
|
-
else if (e.key === "Enter") next = defaultWidth;
|
|
479
|
-
if (next === null) return;
|
|
480
|
-
e.preventDefault();
|
|
481
|
-
onResize(clamp(next, min, max()));
|
|
482
|
-
};
|
|
483
|
-
return /* @__PURE__ */ jsx(
|
|
325
|
+
}, [open]);
|
|
326
|
+
if (!open) return null;
|
|
327
|
+
return /* @__PURE__ */ jsxs(
|
|
484
328
|
"div",
|
|
485
329
|
{
|
|
486
|
-
role: "
|
|
487
|
-
|
|
488
|
-
"
|
|
489
|
-
"
|
|
490
|
-
"
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
onPointerCancel: endDrag,
|
|
499
|
-
onDoubleClick: () => onResize(clamp(defaultWidth, min, max())),
|
|
500
|
-
onKeyDown,
|
|
501
|
-
className: cn(
|
|
502
|
-
// A narrow absolutely-positioned strip over the edge — never a flex child that takes width.
|
|
503
|
-
"group/edge absolute top-0 bottom-0 z-10 w-ds-2 cursor-col-resize touch-none select-none outline-none",
|
|
504
|
-
edge === "left" ? "-left-ds-1" : "-right-ds-1",
|
|
505
|
-
className
|
|
506
|
-
),
|
|
507
|
-
children: /* @__PURE__ */ jsx(
|
|
508
|
-
"div",
|
|
509
|
-
{
|
|
510
|
-
"aria-hidden": "true",
|
|
511
|
-
className: cn(
|
|
512
|
-
"absolute top-0 bottom-0 left-1/2 w-px -translate-x-1/2 transition-[width,background-color] duration-[var(--ds-transition-speed)]",
|
|
513
|
-
"group-hover/edge:w-[3px] group-hover/edge:bg-[var(--ds-accent-primary)]",
|
|
514
|
-
"group-focus-visible/edge:w-[3px] group-focus-visible/edge:bg-[var(--ds-accent-primary)]",
|
|
515
|
-
dragging && "w-[3px] bg-[var(--ds-accent-primary)]"
|
|
516
|
-
)
|
|
517
|
-
}
|
|
518
|
-
)
|
|
330
|
+
role: "group",
|
|
331
|
+
"aria-label": "Unsaved changes",
|
|
332
|
+
"data-discard-confirm": "",
|
|
333
|
+
className: "shrink-0 flex flex-wrap items-center justify-between gap-ds-3 border-t-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)] bg-[var(--ds-status-warning-bg)]",
|
|
334
|
+
style: { padding: "var(--ds-space-3) var(--ds-page-gutter)" },
|
|
335
|
+
children: [
|
|
336
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--ds-text-primary)]", children: "You have unsaved changes." }),
|
|
337
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-2", children: [
|
|
338
|
+
/* @__PURE__ */ jsx(Button, { type: "button", ref: keepRef, variant: "secondary", size: "sm", onClick: onKeep, children: "Keep editing" }),
|
|
339
|
+
/* @__PURE__ */ jsx(Button, { type: "button", variant: "destructive", size: "sm", onClick: onDiscard, children: "Discard changes" })
|
|
340
|
+
] })
|
|
341
|
+
]
|
|
519
342
|
}
|
|
520
343
|
);
|
|
521
344
|
}
|
|
522
|
-
var
|
|
523
|
-
function
|
|
524
|
-
const
|
|
525
|
-
|
|
526
|
-
(
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
}
|
|
541
|
-
function defaultMaxWidth(container) {
|
|
542
|
-
const base = container instanceof HTMLElement ? container.clientWidth : typeof window !== "undefined" ? window.innerWidth : 1200;
|
|
543
|
-
return Math.round(base * 0.6);
|
|
345
|
+
var DrawerWidthCtx = createContext(null);
|
|
346
|
+
function useScrollbarInset() {
|
|
347
|
+
const observer = useRef(null);
|
|
348
|
+
return useCallback((el) => {
|
|
349
|
+
observer.current?.disconnect();
|
|
350
|
+
observer.current = null;
|
|
351
|
+
if (!el) return;
|
|
352
|
+
const measure = () => {
|
|
353
|
+
const style = getComputedStyle(el);
|
|
354
|
+
const borders = (parseFloat(style.borderLeftWidth) || 0) + (parseFloat(style.borderRightWidth) || 0);
|
|
355
|
+
el.style.setProperty("--gs-scrollbar", `${Math.max(0, el.offsetWidth - el.clientWidth - borders)}px`);
|
|
356
|
+
};
|
|
357
|
+
measure();
|
|
358
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
359
|
+
const o = new ResizeObserver(measure);
|
|
360
|
+
o.observe(el);
|
|
361
|
+
observer.current = o;
|
|
362
|
+
}, []);
|
|
544
363
|
}
|
|
364
|
+
var insetLessScrollbar = (gutter) => `max(0px, calc(${gutter} - var(--gs-scrollbar, 0px)))`;
|
|
545
365
|
var DrawerFooterCtx = createContext(null);
|
|
546
366
|
var slideClasses = {
|
|
547
367
|
right: {
|
|
@@ -566,7 +386,8 @@ var borderByPosition = {
|
|
|
566
386
|
bottom: "border-t-[length:var(--ds-border-width)] border-t-[var(--ds-border-default)]"
|
|
567
387
|
};
|
|
568
388
|
function Drawer(props) {
|
|
569
|
-
const { position = "right", mobile,
|
|
389
|
+
const { position = "right", mobile, open, onClose, footer, dirty, onDiscard } = props;
|
|
390
|
+
const shellWidth = useContext(DrawerWidthCtx) ?? void 0;
|
|
570
391
|
const isPhone = useBreakpoint("sm");
|
|
571
392
|
const mobileMode = mobile ?? (position === "left" ? "panel" : "sheet");
|
|
572
393
|
const { marked, mark } = useOverlayMark(open);
|
|
@@ -589,15 +410,16 @@ function Drawer(props) {
|
|
|
589
410
|
() => ({ registerActions, unregisterActions }),
|
|
590
411
|
[registerActions, unregisterActions]
|
|
591
412
|
);
|
|
592
|
-
|
|
593
|
-
return /* @__PURE__ */ jsx(DrawerFooterCtx, { value: footerCtx, children: /* @__PURE__ */ jsx(OverlayMarkScope, { mark, children: /* @__PURE__ */ jsx(OverlayCloseCtx, { value: guard.requestClose, children: /* @__PURE__ */ jsx(Sheet, { open, onClose: guard.requestClose, title, footer: effectiveFooter, confirm, container, flush, snapPoints, keepMounted, id, labelledBy, children }) }) }) });
|
|
594
|
-
}
|
|
595
|
-
return /* @__PURE__ */ jsx(DrawerFooterCtx, { value: footerCtx, children: /* @__PURE__ */ jsx(OverlayMarkScope, { mark, children: /* @__PURE__ */ jsx(OverlayCloseCtx, { value: guard.requestClose, children: /* @__PURE__ */ jsx(DrawerPanel, { ...props, footer: effectiveFooter, onClose: guard.requestClose, confirm }) }) }) });
|
|
413
|
+
return /* @__PURE__ */ jsx(DrawerFooterCtx, { value: footerCtx, children: /* @__PURE__ */ jsx(OverlayMarkScope, { mark, children: /* @__PURE__ */ jsx(OverlayCloseCtx, { value: guard.requestClose, children: /* @__PURE__ */ jsx(DrawerPanel, { ...props, width: props.width ?? shellWidth, form: isPhone && mobileMode === "sheet" ? "sheet" : "panel", footer: effectiveFooter, onClose: guard.requestClose, confirm }) }) }) });
|
|
596
414
|
}
|
|
597
|
-
|
|
415
|
+
var INSET = "var(--ds-page-gutter)";
|
|
416
|
+
var DEFAULT_SNAPS = [0.5, 0.92];
|
|
417
|
+
function DrawerPanel({ form, open, onClose, position = "right", width = "400px", title, children, footer, container, flush, lightBackdrop, topOffset, modal = true, keepMounted = false, id, labelledBy, resizable = false, maxWidth, snapPoints = DEFAULT_SNAPS, confirm }) {
|
|
418
|
+
const sheet = form === "sheet";
|
|
598
419
|
const nonModal = !modal;
|
|
420
|
+
const blocking = !nonModal || sheet;
|
|
599
421
|
const [chosen, remember] = useRememberedWidth(id ?? labelledBy);
|
|
600
|
-
const canResize = nonModal && resizable && position !== "bottom";
|
|
422
|
+
const canResize = !sheet && nonModal && resizable && position !== "bottom";
|
|
601
423
|
const effectiveWidth = canResize && chosen !== null ? `${chosen}px` : width;
|
|
602
424
|
const minPx = pxOf(width) ?? 320;
|
|
603
425
|
const maxPx = () => pxOf(maxWidth) ?? defaultMaxWidth(container instanceof Element ? container : null);
|
|
@@ -607,40 +429,104 @@ function DrawerPanel({ open, onClose, position = "right", width = "400px", title
|
|
|
607
429
|
const panelRef = useRef(null);
|
|
608
430
|
const [mounted, setMounted] = useState(open);
|
|
609
431
|
const [hidden, setHidden] = useState(!open);
|
|
432
|
+
const [snapIdx, setSnapIdx] = useState(0);
|
|
433
|
+
const [dragOffset, setDragOffset] = useState(0);
|
|
434
|
+
const [dragging, setDragging] = useState(false);
|
|
435
|
+
const dragStartY = useRef(0);
|
|
436
|
+
const startHeight = useRef(0);
|
|
610
437
|
const onCloseRef = useLatest(onClose);
|
|
438
|
+
const [prevOpen, setPrevOpen] = useState(open);
|
|
439
|
+
if (open !== prevOpen) {
|
|
440
|
+
setPrevOpen(open);
|
|
441
|
+
if (open) {
|
|
442
|
+
setSnapIdx(0);
|
|
443
|
+
setDragOffset(0);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
611
446
|
if (open && !mounted) setMounted(true);
|
|
612
447
|
if (open && hidden) setHidden(false);
|
|
613
448
|
const isTopLayer = useOverlayLayer(open);
|
|
614
449
|
useEffect(() => {
|
|
615
|
-
if (!open ||
|
|
450
|
+
if (!open || !blocking) return;
|
|
616
451
|
const onKeyDown = (e) => {
|
|
617
452
|
if (e.key !== "Escape" || e.defaultPrevented || !isTopLayer()) return;
|
|
618
453
|
e.preventDefault();
|
|
619
454
|
onCloseRef.current();
|
|
620
455
|
};
|
|
621
456
|
document.addEventListener("keydown", onKeyDown);
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
457
|
+
return () => document.removeEventListener("keydown", onKeyDown);
|
|
458
|
+
}, [open, blocking, onCloseRef, isTopLayer]);
|
|
459
|
+
useEffect(() => {
|
|
460
|
+
if (!open || !blocking || container) return;
|
|
461
|
+
lockBodyScroll();
|
|
462
|
+
return () => unlockBodyScroll();
|
|
463
|
+
}, [open, blocking, container]);
|
|
628
464
|
useReturnFocus(open && !nonModal, panelRef);
|
|
629
|
-
useFocusTrap(panelRef, open &&
|
|
465
|
+
useFocusTrap(panelRef, open && blocking && (sheet || !topOffset), isTopLayer);
|
|
630
466
|
useEffect(() => {
|
|
631
467
|
if (!open || !mounted || nonModal) return;
|
|
632
468
|
const raf = requestAnimationFrame(() => focusInitial(panelRef.current));
|
|
633
469
|
return () => cancelAnimationFrame(raf);
|
|
634
470
|
}, [open, mounted, nonModal]);
|
|
635
471
|
useNonModalDialog(panelRef, open && mounted && nonModal, onClose, isTopLayer);
|
|
472
|
+
const wasBlocking = useRef(blocking);
|
|
473
|
+
useEffect(() => {
|
|
474
|
+
const was = wasBlocking.current;
|
|
475
|
+
wasBlocking.current = blocking;
|
|
476
|
+
if (!open || !mounted || !blocking || was) return;
|
|
477
|
+
const panel = panelRef.current;
|
|
478
|
+
if (panel && !panel.contains(document.activeElement)) focusInitial(panel);
|
|
479
|
+
}, [open, mounted, blocking]);
|
|
480
|
+
const bodyRef = useScrollbarInset();
|
|
636
481
|
useCloseSettle(open, panelRef, () => {
|
|
637
482
|
if (keepMounted) setHidden(true);
|
|
638
483
|
else setMounted(false);
|
|
639
484
|
});
|
|
485
|
+
const viewportHeight = useCallback(
|
|
486
|
+
() => container instanceof HTMLElement ? container.clientHeight : window.innerHeight,
|
|
487
|
+
[container]
|
|
488
|
+
);
|
|
489
|
+
const handlePointerDown = useCallback((e) => {
|
|
490
|
+
setDragging(true);
|
|
491
|
+
dragStartY.current = e.clientY;
|
|
492
|
+
startHeight.current = (snapPoints[snapIdx] ?? snapPoints[0]) * viewportHeight();
|
|
493
|
+
setDragOffset(0);
|
|
494
|
+
e.target.setPointerCapture(e.pointerId);
|
|
495
|
+
}, [snapIdx, snapPoints, viewportHeight]);
|
|
496
|
+
const handlePointerMove = useCallback((e) => {
|
|
497
|
+
if (!dragging) return;
|
|
498
|
+
setDragOffset(dragStartY.current - e.clientY);
|
|
499
|
+
}, [dragging]);
|
|
500
|
+
const handlePointerUp = useCallback(() => {
|
|
501
|
+
if (!dragging) return;
|
|
502
|
+
setDragging(false);
|
|
503
|
+
const currentFraction = (startHeight.current + dragOffset) / viewportHeight();
|
|
504
|
+
if (currentFraction < snapPoints[0] * 0.6) {
|
|
505
|
+
onClose();
|
|
506
|
+
setDragOffset(0);
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
let bestIdx = 0;
|
|
510
|
+
let bestDist = Math.abs(currentFraction - snapPoints[0]);
|
|
511
|
+
for (let i = 1; i < snapPoints.length; i++) {
|
|
512
|
+
const dist = Math.abs(currentFraction - snapPoints[i]);
|
|
513
|
+
if (dist < bestDist) {
|
|
514
|
+
bestDist = dist;
|
|
515
|
+
bestIdx = i;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
setSnapIdx(bestIdx);
|
|
519
|
+
setDragOffset(0);
|
|
520
|
+
}, [dragging, dragOffset, snapPoints, onClose, viewportHeight]);
|
|
640
521
|
if (!mounted) return null;
|
|
641
522
|
const slide = slideClasses[position];
|
|
642
523
|
const isHorizontal = position === "left" || position === "right";
|
|
643
524
|
const positionClass = container ? "absolute" : "fixed";
|
|
525
|
+
const offset = sheet ? void 0 : topOffset;
|
|
526
|
+
const baseHeight = (snapPoints[snapIdx] ?? snapPoints[0]) * 100;
|
|
527
|
+
const heightUnit = container ? "%" : "vh";
|
|
528
|
+
const dragPx = dragging ? dragOffset : 0;
|
|
529
|
+
const heading = "font-[family-name:var(--ds-font-display)] text-lg font-[var(--ds-weight-heading)] text-[var(--ds-text-primary)] truncate";
|
|
644
530
|
return createPortal(
|
|
645
531
|
/* @__PURE__ */ jsxs(
|
|
646
532
|
"div",
|
|
@@ -650,17 +536,17 @@ function DrawerPanel({ open, onClose, position = "right", width = "400px", title
|
|
|
650
536
|
`${positionClass} transition-opacity duration-[var(--ds-transition-speed)]`,
|
|
651
537
|
// A non-modal panel never intercepts the page: the wrapper is inert to the
|
|
652
538
|
// pointer and only the panel itself accepts events.
|
|
653
|
-
|
|
654
|
-
|
|
539
|
+
blocking ? cn("z-[var(--ds-z-modal)]", open ? "pointer-events-auto" : "pointer-events-none") : "z-[var(--ds-z-overlay)] pointer-events-none",
|
|
540
|
+
offset ? "left-0 right-0 bottom-0" : "inset-0"
|
|
655
541
|
),
|
|
656
|
-
style:
|
|
542
|
+
style: offset ? { top: offset } : void 0,
|
|
657
543
|
children: [
|
|
658
|
-
|
|
544
|
+
blocking && /* @__PURE__ */ jsx(
|
|
659
545
|
"div",
|
|
660
546
|
{
|
|
661
547
|
className: cn(
|
|
662
548
|
"absolute inset-0 transition-opacity duration-[var(--ds-transition-speed)]",
|
|
663
|
-
lightBackdrop ? "bg-black/15" : cn("bg-black/50", "[backdrop-filter:blur(calc(var(--ds-glass-blur)*0.3))] [-webkit-backdrop-filter:blur(calc(var(--ds-glass-blur)*0.3))]"),
|
|
549
|
+
sheet ? "bg-black/50" : lightBackdrop ? "bg-black/15" : cn("bg-black/50", "[backdrop-filter:blur(calc(var(--ds-glass-blur)*0.3))] [-webkit-backdrop-filter:blur(calc(var(--ds-glass-blur)*0.3))]"),
|
|
664
550
|
open ? "opacity-100" : "opacity-0"
|
|
665
551
|
),
|
|
666
552
|
onClick: onClose
|
|
@@ -672,12 +558,18 @@ function DrawerPanel({ open, onClose, position = "right", width = "400px", title
|
|
|
672
558
|
ref: panelRef,
|
|
673
559
|
id,
|
|
674
560
|
role: "dialog",
|
|
675
|
-
"data-gs": "drawer",
|
|
561
|
+
"data-gs": sheet ? "sheet" : "drawer",
|
|
562
|
+
"data-ds-sheet": sheet ? "" : void 0,
|
|
676
563
|
...{ [OVERLAY_ROOT_ATTR]: "" },
|
|
677
|
-
"aria-modal":
|
|
564
|
+
"aria-modal": blocking ? "true" : void 0,
|
|
678
565
|
"aria-labelledby": title ? titleId : labelledBy,
|
|
679
|
-
style: {
|
|
566
|
+
style: sheet ? {
|
|
567
|
+
containerType: "inline-size",
|
|
568
|
+
"--ds-form-max": "none",
|
|
569
|
+
height: dragPx ? `calc(${baseHeight}${heightUnit} + ${dragPx}px)` : `${baseHeight}${heightUnit}`
|
|
570
|
+
} : {
|
|
680
571
|
containerType: "inline-size",
|
|
572
|
+
"--ds-form-max": "none",
|
|
681
573
|
width: isHorizontal ? effectiveWidth : void 0,
|
|
682
574
|
maxWidth: isHorizontal ? "100%" : void 0,
|
|
683
575
|
height: isMobile ? "100%" : void 0,
|
|
@@ -685,23 +577,42 @@ function DrawerPanel({ open, onClose, position = "right", width = "400px", title
|
|
|
685
577
|
},
|
|
686
578
|
className: cn(
|
|
687
579
|
`${positionClass} z-10 flex flex-col`,
|
|
688
|
-
|
|
580
|
+
!blocking && "pointer-events-auto",
|
|
689
581
|
OVERLAY_SURFACE,
|
|
690
582
|
"text-[var(--ds-text-primary)]",
|
|
691
583
|
GLASS_OVERLAY,
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
584
|
+
sheet ? cn(
|
|
585
|
+
"bottom-0 left-0 right-0",
|
|
586
|
+
"border-t-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
|
|
587
|
+
"rounded-t-[var(--ds-radius-xl)]",
|
|
588
|
+
!dragging && "transition-[transform,height] duration-[var(--ds-transition-speed)] ease-[var(--ds-transition-ease)]",
|
|
589
|
+
open ? "translate-y-0" : "translate-y-full"
|
|
590
|
+
) : cn(
|
|
591
|
+
// flush drops the edge border for children that draw their own (the mobile
|
|
592
|
+
// menu's Sidebar); a non-modal panel has no backdrop to separate it from
|
|
593
|
+
// the page, so it always keeps the border.
|
|
594
|
+
(!flush || nonModal) && borderByPosition[position],
|
|
595
|
+
"transition-transform duration-[var(--ds-transition-speed)] ease-[var(--ds-transition-ease)]",
|
|
596
|
+
isHorizontal && position === "left" && "rounded-r-[var(--ds-radius-lg)] overflow-hidden",
|
|
597
|
+
isHorizontal && position === "right" && "rounded-l-[var(--ds-radius-lg)] overflow-hidden",
|
|
598
|
+
!isHorizontal && "rounded-t-[var(--ds-radius-lg)] overflow-hidden",
|
|
599
|
+
(lightBackdrop || nonModal) && "shadow-[var(--ds-shadow-overlay)]",
|
|
600
|
+
slide.panel,
|
|
601
|
+
open ? slide.open : slide.closed
|
|
602
|
+
)
|
|
703
603
|
),
|
|
704
604
|
children: [
|
|
605
|
+
sheet && /* @__PURE__ */ jsx(
|
|
606
|
+
"div",
|
|
607
|
+
{
|
|
608
|
+
className: "flex justify-center pt-ds-2 pb-ds-1 cursor-grab active:cursor-grabbing touch-none",
|
|
609
|
+
onPointerDown: handlePointerDown,
|
|
610
|
+
onPointerMove: handlePointerMove,
|
|
611
|
+
onPointerUp: handlePointerUp,
|
|
612
|
+
onPointerCancel: handlePointerUp,
|
|
613
|
+
children: /* @__PURE__ */ jsx("div", { className: "w-10 h-1 rounded-[var(--ds-radius-full)] bg-[var(--ds-border-default)]" })
|
|
614
|
+
}
|
|
615
|
+
),
|
|
705
616
|
canResize && open && /* @__PURE__ */ jsx(
|
|
706
617
|
ResizeEdge,
|
|
707
618
|
{
|
|
@@ -717,13 +628,13 @@ function DrawerPanel({ open, onClose, position = "right", width = "400px", title
|
|
|
717
628
|
title && /* @__PURE__ */ jsxs(
|
|
718
629
|
"div",
|
|
719
630
|
{
|
|
720
|
-
className: "flex items-center justify-between shrink-0 border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
|
|
721
|
-
style: {
|
|
722
|
-
padding:
|
|
631
|
+
className: "flex items-center justify-between gap-ds-3 shrink-0 border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
|
|
632
|
+
style: sheet ? { padding: `var(--ds-space-2) ${INSET}` } : {
|
|
633
|
+
padding: `var(--ds-space-3) ${INSET}`,
|
|
723
634
|
minHeight: "calc(var(--ds-space-unit) * 6 + 1.75rem + 2px + var(--ds-border-width))"
|
|
724
635
|
},
|
|
725
636
|
children: [
|
|
726
|
-
titleTruncated ? /* @__PURE__ */ jsx(Tooltip, { content: title, children: /* @__PURE__ */ jsx("h2", { ref: titleOverflowRef, id: titleId, className:
|
|
637
|
+
!sheet && titleTruncated ? /* @__PURE__ */ jsx(Tooltip, { content: title, children: /* @__PURE__ */ jsx("h2", { ref: titleOverflowRef, id: titleId, className: heading, children: title }) }) : /* @__PURE__ */ jsx("h2", { ref: titleOverflowRef, id: titleId, className: heading, children: title }),
|
|
727
638
|
/* @__PURE__ */ jsx(
|
|
728
639
|
"button",
|
|
729
640
|
{
|
|
@@ -741,9 +652,10 @@ function DrawerPanel({ open, onClose, position = "right", width = "400px", title
|
|
|
741
652
|
/* @__PURE__ */ jsx(
|
|
742
653
|
"div",
|
|
743
654
|
{
|
|
655
|
+
ref: bodyRef,
|
|
744
656
|
className: "flex-1 overflow-y-auto",
|
|
745
|
-
style: flush ? void 0 : { padding:
|
|
746
|
-
children
|
|
657
|
+
style: flush ? void 0 : { padding: `var(--ds-space-5) ${insetLessScrollbar(INSET)} var(--ds-space-5) ${INSET}` },
|
|
658
|
+
children: /* @__PURE__ */ jsx(SurfaceBoundary, { children })
|
|
747
659
|
}
|
|
748
660
|
),
|
|
749
661
|
confirm,
|
|
@@ -751,7 +663,7 @@ function DrawerPanel({ open, onClose, position = "right", width = "400px", title
|
|
|
751
663
|
"div",
|
|
752
664
|
{
|
|
753
665
|
className: "shrink-0 flex items-center justify-end gap-ds-2 border-t-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
|
|
754
|
-
style: { padding:
|
|
666
|
+
style: { padding: `var(--ds-space-3) ${INSET}` },
|
|
755
667
|
children: footer
|
|
756
668
|
}
|
|
757
669
|
)
|
|
@@ -819,7 +731,9 @@ var AppShell = forwardRef(function AppShell2({
|
|
|
819
731
|
preferences,
|
|
820
732
|
currentPath,
|
|
821
733
|
homePath = "/",
|
|
822
|
-
|
|
734
|
+
navigate,
|
|
735
|
+
focus,
|
|
736
|
+
drawerWidth = "520px"
|
|
823
737
|
}, ref) {
|
|
824
738
|
if (REACT_VERSION_PROBLEM) throw new Error(REACT_VERSION_PROBLEM);
|
|
825
739
|
const [sidebarOpen, setSidebarOpen] = useState(false);
|
|
@@ -845,6 +759,16 @@ var AppShell = forwardRef(function AppShell2({
|
|
|
845
759
|
setUnsavedCount((n) => n - 1);
|
|
846
760
|
};
|
|
847
761
|
}, []);
|
|
762
|
+
const [leftCount, setLeftCount] = useState(0);
|
|
763
|
+
const markFlowLeft = useCallback(() => {
|
|
764
|
+
setLeftCount((n) => n + 1);
|
|
765
|
+
let released = false;
|
|
766
|
+
return () => {
|
|
767
|
+
if (released) return;
|
|
768
|
+
released = true;
|
|
769
|
+
setLeftCount((n) => n - 1);
|
|
770
|
+
};
|
|
771
|
+
}, []);
|
|
848
772
|
const showMobileDrawer = hasMobileMenu && !(isSmallMobile && hasMobileNav);
|
|
849
773
|
const [shellEl, setShellEl] = useState(null);
|
|
850
774
|
const [chrome, setChrome] = useState({});
|
|
@@ -878,15 +802,20 @@ var AppShell = forwardRef(function AppShell2({
|
|
|
878
802
|
const hasLauncher = !!launcher && !isSmallMobile;
|
|
879
803
|
const launcherProps = isValidElement(launcher) ? launcher.props : null;
|
|
880
804
|
const launcherCorner = !hasLauncher ? null : launcherProps?.variant === "ribbon" ? null : launcherProps?.edge === "bottom-left" ? "bottom-left" : launcherProps?.edge === "right" || launcherProps?.edge === "left" || launcherProps?.edge === "bottom" ? null : "bottom-right";
|
|
881
|
-
return /* @__PURE__ */ jsx(PreferencesContext, { value: preferences ?? null, children: /* @__PURE__ */ jsx(
|
|
805
|
+
return /* @__PURE__ */ jsx(PreferencesContext, { value: preferences ?? null, children: /* @__PURE__ */ jsx(DrawerWidthCtx, { value: drawerWidth, children: /* @__PURE__ */ jsx(
|
|
882
806
|
AppShellContext,
|
|
883
807
|
{
|
|
884
|
-
value: { hasSidebar, hasMobileMenu, hasMobileNav, sidebarOpen, setSidebarOpen, isMobile, isTablet, sidebarCollapsed, setSidebarCollapsed: setUserCollapsed, asideOpen, setAsideOpen, asideSide, asideFloating, setAsideFloating, container: shellEl, theme: theme ?? null, isHome, navbarFloating, sidebarFloating, registerChrome, hasLauncher, launcherCorner, hasFooter, focus: focus ?? null, hasUnsavedChanges: unsavedCount > 0, addUnsavedChanges },
|
|
808
|
+
value: { hasSidebar, hasMobileMenu, hasMobileNav, sidebarOpen, setSidebarOpen, isMobile, isTablet, sidebarCollapsed, setSidebarCollapsed: setUserCollapsed, asideOpen, setAsideOpen, asideSide, asideFloating, setAsideFloating, container: shellEl, theme: theme ?? null, isHome, navbarFloating, sidebarFloating, registerChrome, hasLauncher, launcherCorner, hasFooter, focus: focus ?? null, hasUnsavedChanges: unsavedCount > 0, addUnsavedChanges, drawerWidth, navigate: navigate ?? null, flowLeft: leftCount > 0, markFlowLeft },
|
|
885
809
|
children: /* @__PURE__ */ jsxs("div", { ref: (el) => {
|
|
886
810
|
setShellEl(el);
|
|
887
811
|
if (typeof ref === "function") ref(el);
|
|
888
812
|
else if (ref) ref.current = el;
|
|
889
|
-
}, className: cn(
|
|
813
|
+
}, className: cn(
|
|
814
|
+
// Sprint 25.16 A42: the dynamic viewport where the browser has it, so nothing hides under a phone browser's
|
|
815
|
+
// toolbar (100vh is the height with the toolbar hidden)
|
|
816
|
+
"relative h-screen supports-[height:100dvh]:h-dvh flex flex-col bg-[var(--ds-bg-canvas)] overflow-hidden",
|
|
817
|
+
className
|
|
818
|
+
), children: [
|
|
890
819
|
skipLink && /* @__PURE__ */ jsx(SkipLink, { href: `#${mainId}` }),
|
|
891
820
|
layout === "sidebar-header" && navbar,
|
|
892
821
|
layout === "multi-column" && navbar,
|
|
@@ -901,6 +830,7 @@ var AppShell = forwardRef(function AppShell2({
|
|
|
901
830
|
{
|
|
902
831
|
ref: mainRef,
|
|
903
832
|
id: mainId,
|
|
833
|
+
"data-shell-main": "",
|
|
904
834
|
tabIndex: -1,
|
|
905
835
|
className: cn(
|
|
906
836
|
"flex-1 min-h-0 focus:outline-none",
|
|
@@ -909,7 +839,7 @@ var AppShell = forwardRef(function AppShell2({
|
|
|
909
839
|
),
|
|
910
840
|
style: { containerType: "inline-size" },
|
|
911
841
|
children: scrollable ? footer ? /* @__PURE__ */ jsxs("div", { className: "min-h-full flex flex-col", children: [
|
|
912
|
-
/* @__PURE__ */ jsx("div", { className: "flex-1", children }),
|
|
842
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1", "data-shell-content": "", children }),
|
|
913
843
|
footer
|
|
914
844
|
] }) : children : /* @__PURE__ */ jsx("div", { className: "absolute inset-0", children })
|
|
915
845
|
}
|
|
@@ -935,7 +865,296 @@ var AppShell = forwardRef(function AppShell2({
|
|
|
935
865
|
!isSmallMobile && launcher
|
|
936
866
|
] })
|
|
937
867
|
}
|
|
938
|
-
) });
|
|
868
|
+
) }) });
|
|
869
|
+
});
|
|
870
|
+
function useCrumbFollow() {
|
|
871
|
+
const navigate = useAppShell()?.navigate ?? null;
|
|
872
|
+
return useCallback((e, item) => {
|
|
873
|
+
if (item.href && (e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey)) return;
|
|
874
|
+
if (item.onClick) {
|
|
875
|
+
e.preventDefault();
|
|
876
|
+
item.onClick();
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
879
|
+
if (!item.href) {
|
|
880
|
+
e.preventDefault();
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
if (!navigate || e.defaultPrevented || !onThisSite(item.href)) return;
|
|
884
|
+
e.preventDefault();
|
|
885
|
+
navigate(item.href);
|
|
886
|
+
}, [navigate]);
|
|
887
|
+
}
|
|
888
|
+
function onThisSite(href) {
|
|
889
|
+
if (typeof window === "undefined") return false;
|
|
890
|
+
try {
|
|
891
|
+
return new URL(href, window.location.href).origin === window.location.origin;
|
|
892
|
+
} catch {
|
|
893
|
+
return false;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
var crumbGoes = (item) => !!(item.href || item.onClick);
|
|
897
|
+
function SeparatorIcon({ type, size }) {
|
|
898
|
+
const iconSize = size === "sm" ? 10 : 12;
|
|
899
|
+
if (type === "chevron") return /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: iconSize });
|
|
900
|
+
if (type === "arrow") return /* @__PURE__ */ jsx(Icon, { name: "arrow-right", size: iconSize });
|
|
901
|
+
if (type === "dot") return /* @__PURE__ */ jsx("span", { children: "\xB7" });
|
|
902
|
+
return /* @__PURE__ */ jsx("span", { children: "/" });
|
|
903
|
+
}
|
|
904
|
+
var Breadcrumbs = forwardRef(function Breadcrumbs2({
|
|
905
|
+
items,
|
|
906
|
+
separator = "chevron",
|
|
907
|
+
variant = "inline",
|
|
908
|
+
itemVariant,
|
|
909
|
+
size = "md",
|
|
910
|
+
showIcons = false,
|
|
911
|
+
maxItems,
|
|
912
|
+
bleed = false,
|
|
913
|
+
contentMaxWidth,
|
|
914
|
+
className,
|
|
915
|
+
container,
|
|
916
|
+
"aria-label": label = "Breadcrumb"
|
|
917
|
+
}, ref) {
|
|
918
|
+
const [dropdownOpen, setDropdownOpen] = useState(false);
|
|
919
|
+
const follow = useCrumbFollow();
|
|
920
|
+
const [activeIdx, setActiveIdx] = useState(0);
|
|
921
|
+
const ellipsisBtnRef = useRef(null);
|
|
922
|
+
const dropdownRef = useRef(null);
|
|
923
|
+
const closeDropdown = useCallback(() => setDropdownOpen(false), []);
|
|
924
|
+
const { isTop, returnFocus } = usePopupFocus(dropdownOpen, { trigger: ellipsisBtnRef, panel: dropdownRef, close: closeDropdown, tabExits: "any" });
|
|
925
|
+
const pos = usePosition(ellipsisBtnRef, dropdownRef, dropdownOpen, { side: "bottom", align: "start", offset: 4, flip: true });
|
|
926
|
+
const effectiveMaxItems = maxItems ? Math.max(maxItems, 2) : void 0;
|
|
927
|
+
const shouldCollapse = effectiveMaxItems && items.length > effectiveMaxItems;
|
|
928
|
+
const collapsedItems = shouldCollapse ? items.slice(1, -(effectiveMaxItems - 1)) : [];
|
|
929
|
+
const visibleItems = shouldCollapse ? [...items.slice(0, 1), null, ...items.slice(-(effectiveMaxItems - 1))] : items;
|
|
930
|
+
useEffect(() => {
|
|
931
|
+
if (!dropdownOpen) return;
|
|
932
|
+
const handler = (e) => {
|
|
933
|
+
const target = e.target;
|
|
934
|
+
if (ellipsisBtnRef.current?.contains(target) || dropdownRef.current?.contains(target)) return;
|
|
935
|
+
setDropdownOpen(false);
|
|
936
|
+
};
|
|
937
|
+
const onKeyDown = (e) => {
|
|
938
|
+
if (e.key !== "Escape" || e.defaultPrevented || !isTop()) return;
|
|
939
|
+
e.preventDefault();
|
|
940
|
+
returnFocus();
|
|
941
|
+
setDropdownOpen(false);
|
|
942
|
+
};
|
|
943
|
+
document.addEventListener("mousedown", handler);
|
|
944
|
+
document.addEventListener("keydown", onKeyDown);
|
|
945
|
+
return () => {
|
|
946
|
+
document.removeEventListener("mousedown", handler);
|
|
947
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
948
|
+
};
|
|
949
|
+
}, [dropdownOpen, isTop, returnFocus]);
|
|
950
|
+
useEffect(() => {
|
|
951
|
+
if (!dropdownOpen) return;
|
|
952
|
+
const id = requestAnimationFrame(() => dropdownRef.current?.querySelector(`[data-index="${activeIdx}"]`)?.focus());
|
|
953
|
+
return () => cancelAnimationFrame(id);
|
|
954
|
+
}, [dropdownOpen, activeIdx]);
|
|
955
|
+
function onMenuKeyDown(e) {
|
|
956
|
+
const last = collapsedItems.length - 1;
|
|
957
|
+
const next = e.key === "ArrowDown" ? activeIdx >= last ? 0 : activeIdx + 1 : e.key === "ArrowUp" ? activeIdx <= 0 ? last : activeIdx - 1 : e.key === "Home" ? 0 : e.key === "End" ? last : null;
|
|
958
|
+
if (next === null) return;
|
|
959
|
+
e.preventDefault();
|
|
960
|
+
setActiveIdx(next);
|
|
961
|
+
}
|
|
962
|
+
const textClass = size === "sm" ? "text-xs" : "text-sm";
|
|
963
|
+
const gapClass = size === "sm" ? "gap-ds-1" : "gap-ds-1.5";
|
|
964
|
+
const iconSize = size === "sm" ? 12 : 14;
|
|
965
|
+
const isContainedBleed = variant === "contained" && bleed;
|
|
966
|
+
const wrapperClasses = cn(
|
|
967
|
+
variant === "contained" && !bleed && cn(
|
|
968
|
+
"bg-[var(--ds-bg-muted)] rounded-[var(--ds-radius-md)]",
|
|
969
|
+
size === "sm" ? "py-ds-1 px-ds-2" : "py-ds-1.5 px-ds-3"
|
|
970
|
+
),
|
|
971
|
+
!isContainedBleed && className
|
|
972
|
+
);
|
|
973
|
+
function renderItem(item, _index, isLast) {
|
|
974
|
+
const isPill = itemVariant === "pill" || variant === "pill" && !itemVariant;
|
|
975
|
+
const insideContained = variant === "contained";
|
|
976
|
+
const pillClasses = isPill ? cn(
|
|
977
|
+
"rounded-[var(--ds-radius-full)]",
|
|
978
|
+
insideContained ? "bg-[var(--ds-bg-surface)]" : "bg-[var(--ds-bg-muted)]",
|
|
979
|
+
size === "sm" ? "px-ds-2 py-[2px]" : "px-ds-2.5 py-ds-0.5"
|
|
980
|
+
) : "";
|
|
981
|
+
const content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
982
|
+
showIcons && item.icon && /* @__PURE__ */ jsx(Icon, { name: item.icon, size: iconSize, className: "shrink-0" }),
|
|
983
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: item.label })
|
|
984
|
+
] });
|
|
985
|
+
if (!crumbGoes(item)) {
|
|
986
|
+
return /* @__PURE__ */ jsx(
|
|
987
|
+
"span",
|
|
988
|
+
{
|
|
989
|
+
"aria-current": isLast ? "page" : void 0,
|
|
990
|
+
className: cn(
|
|
991
|
+
"flex items-center",
|
|
992
|
+
isLast ? "font-medium text-[var(--ds-text-primary)]" : "text-[var(--ds-text-secondary)]",
|
|
993
|
+
gapClass,
|
|
994
|
+
pillClasses
|
|
995
|
+
),
|
|
996
|
+
children: content
|
|
997
|
+
}
|
|
998
|
+
);
|
|
999
|
+
}
|
|
1000
|
+
return /* @__PURE__ */ jsx(
|
|
1001
|
+
"a",
|
|
1002
|
+
{
|
|
1003
|
+
href: item.href ?? "#",
|
|
1004
|
+
onClick: (e) => follow(e, item),
|
|
1005
|
+
className: cn(
|
|
1006
|
+
"text-[var(--ds-text-secondary)] flex items-center",
|
|
1007
|
+
"hover:text-[var(--ds-text-primary)]",
|
|
1008
|
+
"transition-colors duration-[var(--ds-transition-speed)]",
|
|
1009
|
+
gapClass,
|
|
1010
|
+
pillClasses
|
|
1011
|
+
),
|
|
1012
|
+
children: content
|
|
1013
|
+
}
|
|
1014
|
+
);
|
|
1015
|
+
}
|
|
1016
|
+
const listContent = /* @__PURE__ */ jsx("ol", { className: cn("flex items-center flex-wrap", gapClass, textClass), children: visibleItems.map((item, i) => {
|
|
1017
|
+
if (item === null) {
|
|
1018
|
+
return /* @__PURE__ */ jsxs("li", { className: cn("flex items-center", gapClass), children: [
|
|
1019
|
+
i > 0 && /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "text-[var(--ds-text-secondary)] select-none flex items-center", children: /* @__PURE__ */ jsx(SeparatorIcon, { type: separator, size }) }),
|
|
1020
|
+
/* @__PURE__ */ jsx(
|
|
1021
|
+
"button",
|
|
1022
|
+
{
|
|
1023
|
+
type: "button",
|
|
1024
|
+
ref: ellipsisBtnRef,
|
|
1025
|
+
onClick: () => {
|
|
1026
|
+
if (!dropdownOpen) setActiveIdx(0);
|
|
1027
|
+
setDropdownOpen(!dropdownOpen);
|
|
1028
|
+
},
|
|
1029
|
+
"aria-label": "Show hidden breadcrumbs",
|
|
1030
|
+
"aria-expanded": dropdownOpen,
|
|
1031
|
+
"aria-haspopup": "menu",
|
|
1032
|
+
className: cn(
|
|
1033
|
+
"text-[var(--ds-text-secondary)] hover:text-[var(--ds-text-primary)]",
|
|
1034
|
+
"transition-colors duration-[var(--ds-transition-speed)] cursor-pointer",
|
|
1035
|
+
"flex items-center",
|
|
1036
|
+
(itemVariant === "pill" || variant === "pill" && !itemVariant) && cn(
|
|
1037
|
+
"rounded-[var(--ds-radius-full)]",
|
|
1038
|
+
variant === "contained" ? "bg-[var(--ds-bg-surface)]" : "bg-[var(--ds-bg-muted)]",
|
|
1039
|
+
size === "sm" ? "px-ds-2 py-[2px]" : "px-ds-2.5 py-ds-0.5"
|
|
1040
|
+
)
|
|
1041
|
+
),
|
|
1042
|
+
children: "\xB7\xB7\xB7"
|
|
1043
|
+
}
|
|
1044
|
+
)
|
|
1045
|
+
] }, "__ellipsis");
|
|
1046
|
+
}
|
|
1047
|
+
const isLast = i === visibleItems.length - 1;
|
|
1048
|
+
return /* @__PURE__ */ jsxs("li", { className: cn("flex items-center", gapClass), children: [
|
|
1049
|
+
i > 0 && /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "text-[var(--ds-text-secondary)] select-none flex items-center", children: /* @__PURE__ */ jsx(SeparatorIcon, { type: separator, size }) }),
|
|
1050
|
+
renderItem(item, i, isLast)
|
|
1051
|
+
] }, i);
|
|
1052
|
+
}) });
|
|
1053
|
+
const dropdown = dropdownOpen && collapsedItems.length > 0 && createPortal(
|
|
1054
|
+
/* @__PURE__ */ jsx(
|
|
1055
|
+
"div",
|
|
1056
|
+
{
|
|
1057
|
+
ref: dropdownRef,
|
|
1058
|
+
role: "menu",
|
|
1059
|
+
"aria-label": "Hidden breadcrumbs",
|
|
1060
|
+
onKeyDown: onMenuKeyDown,
|
|
1061
|
+
className: cn(
|
|
1062
|
+
"fixed z-[var(--ds-z-popover)] min-w-[160px] max-h-60 overflow-auto",
|
|
1063
|
+
"bg-[var(--ds-bg-surface-elevated)]",
|
|
1064
|
+
"[backdrop-filter:var(--ds-glass-filter)] [-webkit-backdrop-filter:var(--ds-glass-filter)]",
|
|
1065
|
+
"border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
|
|
1066
|
+
"rounded-[var(--ds-radius-md)]",
|
|
1067
|
+
"shadow-[var(--ds-shadow-dropdown),var(--ds-glass-inset)]",
|
|
1068
|
+
"py-ds-1"
|
|
1069
|
+
),
|
|
1070
|
+
style: { top: pos.top, left: pos.left },
|
|
1071
|
+
children: collapsedItems.map((ci, idx) => {
|
|
1072
|
+
const itemClass = cn(
|
|
1073
|
+
"flex items-center w-full text-left text-sm cursor-pointer",
|
|
1074
|
+
"px-ds-3 py-ds-1.5 transition-colors",
|
|
1075
|
+
"text-[var(--ds-text-secondary)]",
|
|
1076
|
+
"hover:bg-[var(--ds-bg-muted)] hover:text-[var(--ds-text-primary)]",
|
|
1077
|
+
"focus-visible:outline-none focus-visible:bg-[var(--ds-bg-muted)] focus-visible:text-[var(--ds-text-primary)] focus-visible:shadow-[inset_var(--ds-shadow-focus)]",
|
|
1078
|
+
gapClass
|
|
1079
|
+
);
|
|
1080
|
+
const label2 = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1081
|
+
showIcons && ci.icon && /* @__PURE__ */ jsx(Icon, { name: ci.icon, size: iconSize, className: "shrink-0" }),
|
|
1082
|
+
ci.label
|
|
1083
|
+
] });
|
|
1084
|
+
return ci.href && !ci.onClick ? /* @__PURE__ */ jsx(
|
|
1085
|
+
"a",
|
|
1086
|
+
{
|
|
1087
|
+
"data-index": idx,
|
|
1088
|
+
role: "menuitem",
|
|
1089
|
+
href: ci.href,
|
|
1090
|
+
tabIndex: idx === activeIdx ? 0 : -1,
|
|
1091
|
+
className: itemClass,
|
|
1092
|
+
onClick: (e) => {
|
|
1093
|
+
setDropdownOpen(false);
|
|
1094
|
+
follow(e, ci);
|
|
1095
|
+
},
|
|
1096
|
+
onKeyDown: (e) => {
|
|
1097
|
+
if (e.key === " ") {
|
|
1098
|
+
e.preventDefault();
|
|
1099
|
+
e.currentTarget.click();
|
|
1100
|
+
}
|
|
1101
|
+
},
|
|
1102
|
+
children: label2
|
|
1103
|
+
},
|
|
1104
|
+
idx
|
|
1105
|
+
) : /* @__PURE__ */ jsx(
|
|
1106
|
+
"button",
|
|
1107
|
+
{
|
|
1108
|
+
type: "button",
|
|
1109
|
+
"data-index": idx,
|
|
1110
|
+
role: "menuitem",
|
|
1111
|
+
tabIndex: idx === activeIdx ? 0 : -1,
|
|
1112
|
+
"aria-disabled": !crumbGoes(ci) || void 0,
|
|
1113
|
+
className: itemClass,
|
|
1114
|
+
onClick: () => {
|
|
1115
|
+
if (!ci.onClick) return;
|
|
1116
|
+
setDropdownOpen(false);
|
|
1117
|
+
ci.onClick();
|
|
1118
|
+
},
|
|
1119
|
+
children: label2
|
|
1120
|
+
},
|
|
1121
|
+
idx
|
|
1122
|
+
);
|
|
1123
|
+
})
|
|
1124
|
+
}
|
|
1125
|
+
),
|
|
1126
|
+
container ?? document.body
|
|
1127
|
+
);
|
|
1128
|
+
if (isContainedBleed) {
|
|
1129
|
+
return /* @__PURE__ */ jsxs(
|
|
1130
|
+
"nav",
|
|
1131
|
+
{
|
|
1132
|
+
ref,
|
|
1133
|
+
"aria-label": label,
|
|
1134
|
+
className: cn(
|
|
1135
|
+
"w-full bg-[var(--ds-bg-muted)]",
|
|
1136
|
+
// Hairline under the band: it sits within ~1.2:1 of both canvas and a tinted header, so the edge needs a line.
|
|
1137
|
+
"border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
|
|
1138
|
+
className
|
|
1139
|
+
),
|
|
1140
|
+
children: [
|
|
1141
|
+
/* @__PURE__ */ jsx(
|
|
1142
|
+
"div",
|
|
1143
|
+
{
|
|
1144
|
+
className: cn("w-full mx-auto px-ds-6", size === "sm" ? "py-ds-1" : "py-ds-1.5"),
|
|
1145
|
+
style: { maxWidth: contentMaxWidth ?? "var(--ds-bp-xl)" },
|
|
1146
|
+
children: listContent
|
|
1147
|
+
}
|
|
1148
|
+
),
|
|
1149
|
+
dropdown
|
|
1150
|
+
]
|
|
1151
|
+
}
|
|
1152
|
+
);
|
|
1153
|
+
}
|
|
1154
|
+
return /* @__PURE__ */ jsxs("nav", { ref, "aria-label": label, className: wrapperClasses, children: [
|
|
1155
|
+
listContent,
|
|
1156
|
+
dropdown
|
|
1157
|
+
] });
|
|
939
1158
|
});
|
|
940
1159
|
function useCompact(ref, breakpoint, band = 0) {
|
|
941
1160
|
const px = typeof breakpoint === "number" ? breakpoint : readBreakpointPx(breakpoint);
|
|
@@ -964,6 +1183,6 @@ function useElementWidth(ref) {
|
|
|
964
1183
|
return width;
|
|
965
1184
|
}
|
|
966
1185
|
|
|
967
|
-
export { AppShell, DiscardConfirm, Drawer, DrawerFooterCtx, OVERLAY_ROOT_ATTR, OverlayCloseCtx, OverlayMarkCtx, OverlayMarkScope, REACT_VERSION_PROBLEM, ResizeEdge,
|
|
968
|
-
//# sourceMappingURL=chunk-
|
|
969
|
-
//# sourceMappingURL=chunk-
|
|
1186
|
+
export { AppShell, Breadcrumbs, DiscardConfirm, Drawer, DrawerFooterCtx, DrawerPanel, OVERLAY_ROOT_ATTR, OverlayCloseCtx, OverlayMarkCtx, OverlayMarkScope, REACT_VERSION_PROBLEM, ResizeEdge, SkipLink, crumbGoes, defaultMaxWidth, insetLessScrollbar, pxOf, useAppShell, useBelow, useCloseSettle, useCompact, useCrumbFollow, useDirtyGuard, useElementWidth, useOverlayMark, useRememberedWidth, useScrollbarInset, useUnsavedChanges };
|
|
1187
|
+
//# sourceMappingURL=chunk-MV7JXWXK.js.map
|
|
1188
|
+
//# sourceMappingURL=chunk-MV7JXWXK.js.map
|