gridsmith-ui 0.21.0 → 0.23.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.
Files changed (48) hide show
  1. package/dist/chat.js +3 -3
  2. package/dist/chunk-3BRQRKDO.js +72 -0
  3. package/dist/chunk-3BRQRKDO.js.map +1 -0
  4. package/dist/{chunk-GGVASDXT.js → chunk-6PL7FXYH.js} +284 -521
  5. package/dist/chunk-6PL7FXYH.js.map +1 -0
  6. package/dist/chunk-7TOJ4G7S.js +197 -0
  7. package/dist/chunk-7TOJ4G7S.js.map +1 -0
  8. package/dist/chunk-CKWITUSH.js +40 -0
  9. package/dist/chunk-CKWITUSH.js.map +1 -0
  10. package/dist/{chunk-ILIODA7W.js → chunk-CXMV7SXJ.js} +4 -3
  11. package/dist/chunk-CXMV7SXJ.js.map +1 -0
  12. package/dist/{chunk-EQDR57AM.js → chunk-EYMGLK4M.js} +3 -3
  13. package/dist/{chunk-EQDR57AM.js.map → chunk-EYMGLK4M.js.map} +1 -1
  14. package/dist/chunk-ICNWPNMM.js +1363 -0
  15. package/dist/chunk-ICNWPNMM.js.map +1 -0
  16. package/dist/chunk-IVCVNAXG.js +115 -0
  17. package/dist/chunk-IVCVNAXG.js.map +1 -0
  18. package/dist/{chunk-XYDIBW3J.js → chunk-SBTBMAIU.js} +4 -3
  19. package/dist/chunk-SBTBMAIU.js.map +1 -0
  20. package/dist/data-grid.d.ts +38 -4
  21. package/dist/data-grid.js +305 -264
  22. package/dist/data-grid.js.map +1 -1
  23. package/dist/editor.js +10 -3
  24. package/dist/editor.js.map +1 -1
  25. package/dist/index.d.ts +122 -146
  26. package/dist/index.js +1091 -1885
  27. package/dist/index.js.map +1 -1
  28. package/dist/kanban.js +4 -2
  29. package/dist/kanban.js.map +1 -1
  30. package/dist/resizable.d.ts +1 -1
  31. package/dist/resizable.js +26 -14
  32. package/dist/resizable.js.map +1 -1
  33. package/dist/roots.css +24 -4
  34. package/dist/{table-utils-GounRE6s.d.ts → table-utils-BkjNEWts.d.ts} +5 -1
  35. package/dist/tokens.css +20 -3
  36. package/dist/use-pagination-DiJo0aW5.d.ts +153 -0
  37. package/dist/virtualized.d.ts +11 -1
  38. package/dist/virtualized.js +69 -18
  39. package/dist/virtualized.js.map +1 -1
  40. package/dist-static/tokens.css +20 -3
  41. package/package.json +1 -1
  42. package/dist/chunk-GGVASDXT.js.map +0 -1
  43. package/dist/chunk-ILIODA7W.js.map +0 -1
  44. package/dist/chunk-JN37OVTP.js +0 -67
  45. package/dist/chunk-JN37OVTP.js.map +0 -1
  46. package/dist/chunk-TKVTZWBN.js +0 -50
  47. package/dist/chunk-TKVTZWBN.js.map +0 -1
  48. package/dist/chunk-XYDIBW3J.js.map +0 -1
@@ -1,161 +1,15 @@
1
- import { FormChangeCtx, useReturnFocus, focusInitial, useOverlayLayer, useFocusTrap, OVERLAY_CLOSE_ATTR, useNonModalDialog } from './chunk-RT7RYGC3.js';
2
- import { PreferencesContext } from './chunk-TKVTZWBN.js';
1
+ import { PreferencesContext, Button } from './chunk-7TOJ4G7S.js';
3
2
  import { useBreakpoint, readBreakpointPx } from './chunk-CRQ6KCRD.js';
4
- import { TooltipCtx, Tooltip, useTextOverflow } from './chunk-ILIODA7W.js';
3
+ import { FormChangeCtx, useOverlayLayer, useReturnFocus, useFocusTrap, focusInitial, useNonModalDialog, OVERLAY_CLOSE_ATTR } from './chunk-RT7RYGC3.js';
4
+ import { SurfaceBoundary } from './chunk-CKWITUSH.js';
5
+ import { useTextOverflow, Tooltip } from './chunk-CXMV7SXJ.js';
5
6
  import { useLatest } from './chunk-WH2FUFAC.js';
6
7
  import { Icon } from './chunk-4LDYR7QP.js';
7
8
  import { cn, lockBodyScroll, unlockBodyScroll, OVERLAY_SURFACE, GLASS_OVERLAY } from './chunk-M2QRNBDQ.js';
8
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
9
- import { createContext, forwardRef, useContext, version, useState, useCallback, useRef, useEffect, isValidElement, useMemo, useId, useLayoutEffect } from 'react';
9
+ import { createContext, version, forwardRef, useState, useCallback, useRef, useEffect, isValidElement, useContext, useMemo, useId, useLayoutEffect } from 'react';
10
10
  import { createPortal } from 'react-dom';
11
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
11
12
 
12
- var sizes = {
13
- sm: "h-4 w-4 border-[1.5px]",
14
- md: "h-6 w-6 border-2",
15
- lg: "h-8 w-8 border-[2.5px]"
16
- };
17
- function Spinner({ size = "md", color = "default", label = "Loading", className }) {
18
- return /* @__PURE__ */ jsx(
19
- "span",
20
- {
21
- role: "status",
22
- "aria-label": label,
23
- className: cn(
24
- "inline-block rounded-[var(--ds-radius-full)] animate-spin",
25
- color === "inherit" ? "border-current/30 border-t-current" : "border-[var(--ds-bg-emphasis)] border-t-[var(--ds-accent-primary)]",
26
- sizes[size],
27
- className
28
- )
29
- }
30
- );
31
- }
32
- var ControlSizeContext = createContext(null);
33
- var ButtonSizeContext = ControlSizeContext;
34
- var variantStyles = {
35
- default: "bg-[var(--ds-accent-primary)] [background-image:var(--ds-gradient)] [background-origin:border-box] text-[var(--ds-text-on-primary)] border-transparent hover:bg-[var(--ds-accent-primary-hover)] hover:brightness-110",
36
- secondary: "bg-[var(--ds-bg-surface)] text-[var(--ds-text-primary)] border-[var(--ds-border-default)] hover:bg-[var(--ds-bg-muted)]",
37
- tertiary: "bg-[var(--ds-bg-muted)] text-[var(--ds-text-primary)] border-transparent hover:brightness-95",
38
- outline: "bg-[var(--ds-input-bg)] text-[var(--ds-text-primary)] border-[var(--ds-border-default)] hover:bg-[var(--ds-bg-muted)]",
39
- ghost: "bg-transparent text-[var(--ds-text-primary)] border-transparent hover:bg-[var(--ds-bg-muted)] shadow-none",
40
- destructive: "bg-[var(--ds-status-error)] text-[var(--ds-text-on-error)] border-transparent hover:opacity-90",
41
- success: "bg-[var(--ds-status-success)] text-[var(--ds-text-on-success)] border-transparent hover:opacity-90",
42
- warning: "bg-[var(--ds-status-warning)] text-[var(--ds-text-on-warning)] border-transparent hover:opacity-90",
43
- link: "bg-transparent text-[var(--ds-accent-primary)] hover:text-[var(--ds-accent-primary-hover)] hover:underline"
44
- };
45
- var sizeStyles = {
46
- // xs / icon-xs: dense tool chrome (builder toolbars, inline row actions).
47
- xs: "h-ds-6 px-ds-3 py-0 text-xs",
48
- sm: "h-[var(--ds-control-height-sm)] px-ds-4 py-ds-1 text-xs",
49
- md: "h-[var(--ds-control-height-md)] px-ds-6 py-ds-2 text-sm",
50
- lg: "h-[var(--ds-control-height-lg)] px-ds-8 py-ds-3 text-base",
51
- "icon-xs": "h-ds-6 w-ds-6 p-0",
52
- "icon-sm": "h-[var(--ds-control-height-sm)] w-[var(--ds-control-height-sm)] p-0",
53
- icon: "h-[var(--ds-control-height-md)] w-[var(--ds-control-height-md)] p-0",
54
- "icon-lg": "h-[var(--ds-control-height-lg)] w-[var(--ds-control-height-lg)] p-0"
55
- };
56
- var linkSizeStyles = {
57
- xs: "text-xs",
58
- sm: "text-xs",
59
- md: "text-sm",
60
- lg: "text-base",
61
- "icon-xs": "text-xs",
62
- "icon-sm": "text-xs",
63
- icon: "text-sm",
64
- "icon-lg": "text-base"
65
- };
66
- var iconPaddingReduce = {
67
- xs: 2,
68
- sm: 2,
69
- md: 4,
70
- lg: 6
71
- };
72
- var HOVER_SCALE_CAP = { "--_btn-scale": "min(var(--ds-hover-scale), 1.02)" };
73
- var Button = forwardRef(function Button2({ variant = "default", size: sizeProp, iconLeft, iconRight, loading, className, children, disabled, style, onClick, ...props }, ref) {
74
- const contextSize = useContext(ControlSizeContext);
75
- const size = sizeProp ?? contextSize ?? "md";
76
- const isLink = variant === "link";
77
- const spinnerSize = size === "lg" || size === "icon-lg" ? "md" : "sm";
78
- let paddingStyle;
79
- const isIconSize = size === "icon" || size === "icon-xs" || size === "icon-sm" || size === "icon-lg";
80
- if (!isIconSize && !isLink && (iconLeft || iconRight)) {
81
- const reduce = iconPaddingReduce[size];
82
- paddingStyle = {};
83
- if (iconLeft) paddingStyle.paddingLeft = `calc(${getCssPadding(size)} - ${reduce}px)`;
84
- if (iconRight) paddingStyle.paddingRight = `calc(${getCssPadding(size)} - ${reduce}px)`;
85
- }
86
- const isLoading = loading && !disabled;
87
- const isDisabled = !!disabled;
88
- const handleClick = isLoading ? (e) => {
89
- e.preventDefault();
90
- } : onClick;
91
- const ariaLabel = props["aria-label"];
92
- const inTooltip = useContext(TooltipCtx);
93
- const btn = /* @__PURE__ */ jsxs(
94
- "button",
95
- {
96
- ref,
97
- "data-variant": variant,
98
- className: cn(
99
- "relative inline-flex items-center justify-center gap-ds-2 whitespace-nowrap font-medium",
100
- isLink ? cn(
101
- "px-0 py-ds-0.5 h-auto",
102
- linkSizeStyles[size],
103
- "transition-colors duration-[var(--ds-transition-speed)]",
104
- "focus-visible:outline-2 focus-visible:outline-[var(--ds-accent-primary)] focus-visible:outline-offset-2",
105
- "cursor-pointer",
106
- "active:text-[var(--ds-accent-primary-active)]"
107
- ) : cn(
108
- "border-[length:var(--ds-border-width)]",
109
- "rounded-[var(--ds-radius-md)]",
110
- "shadow-[var(--ds-shadow-button)]",
111
- "hover:scale-[var(--_btn-scale)] hover:-translate-y-[var(--ds-hover-lift)]",
112
- "active:shadow-[var(--ds-shadow-button-pressed)] active:translate-x-[var(--ds-press-depth)] active:translate-y-[var(--ds-press-depth)]",
113
- "[transition:var(--ds-transition-interactive)]",
114
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
115
- "cursor-pointer disabled:cursor-not-allowed disabled:opacity-disabled",
116
- sizeStyles[size]
117
- ),
118
- variantStyles[variant],
119
- isLoading && !isLink && "cursor-wait pointer-events-none",
120
- className
121
- ),
122
- disabled: isDisabled,
123
- "aria-busy": isLoading || void 0,
124
- "aria-disabled": isLoading && !disabled ? true : void 0,
125
- onClick: handleClick,
126
- style: isLink ? style : { ...HOVER_SCALE_CAP, ...paddingStyle, ...style },
127
- ...props,
128
- children: [
129
- loading && !isLink && /* @__PURE__ */ jsx("span", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsx(Spinner, { size: spinnerSize, color: "inherit" }) }),
130
- /* @__PURE__ */ jsxs("span", { className: cn("inline-flex items-center gap-ds-2 min-w-0", loading && !isLink && "invisible"), children: [
131
- iconLeft,
132
- children,
133
- iconRight
134
- ] })
135
- ]
136
- }
137
- );
138
- if (isIconSize && ariaLabel && !inTooltip) {
139
- return /* @__PURE__ */ jsx(Tooltip, { content: ariaLabel, children: btn });
140
- }
141
- return btn;
142
- });
143
- function getCssPadding(size) {
144
- switch (size) {
145
- case "xs":
146
- return "var(--ds-space-3)";
147
- case "sm":
148
- return "var(--ds-space-4)";
149
- case "md":
150
- return "var(--ds-space-6)";
151
- case "lg":
152
- return "var(--ds-space-8)";
153
- }
154
- }
155
- function triggerType(node) {
156
- if (!isValidElement(node)) return {};
157
- return (node.type === "button" || node.type === Button) && node.props.type === void 0 ? { type: "button" } : {};
158
- }
159
13
  function transitionMs(el) {
160
14
  const cs = getComputedStyle(el);
161
15
  const parse = (list) => list.split(",").map((raw) => {
@@ -201,6 +55,126 @@ function useCloseSettle(open, elRef, onSettled) {
201
55
  };
202
56
  }, [open, elRef, cb]);
203
57
  }
58
+ var STEP = 16;
59
+ var BIG_STEP = 64;
60
+ function clamp(v, min, max) {
61
+ return Math.min(Math.max(v, min), max);
62
+ }
63
+ function ResizeEdge({ edge, width, min, max, defaultWidth, onResize, label, onDraggingChange, className }) {
64
+ const [dragging, setDragging] = useState(false);
65
+ const start = useRef(null);
66
+ const latestOnResize = useLatest(onResize);
67
+ const direction = edge === "left" ? -1 : 1;
68
+ const onPointerDown = useCallback(
69
+ (e) => {
70
+ if (e.button !== 0) return;
71
+ e.preventDefault();
72
+ start.current = { x: e.clientX, width };
73
+ setDragging(true);
74
+ e.currentTarget.setPointerCapture(e.pointerId);
75
+ },
76
+ [width]
77
+ );
78
+ const onPointerMove = useCallback(
79
+ (e) => {
80
+ if (!start.current) return;
81
+ const next = clamp(start.current.width + direction * (e.clientX - start.current.x), min, max());
82
+ latestOnResize.current(next);
83
+ },
84
+ [direction, min, max, latestOnResize]
85
+ );
86
+ const endDrag = useCallback(() => {
87
+ start.current = null;
88
+ setDragging(false);
89
+ }, []);
90
+ const latestOnDraggingChange = useLatest(onDraggingChange);
91
+ useEffect(() => {
92
+ latestOnDraggingChange.current?.(dragging);
93
+ }, [dragging, latestOnDraggingChange]);
94
+ useEffect(() => {
95
+ if (!dragging) return;
96
+ const onKey = (e) => {
97
+ if (e.key !== "Escape" || !start.current) return;
98
+ latestOnResize.current(start.current.width);
99
+ endDrag();
100
+ };
101
+ document.addEventListener("keydown", onKey);
102
+ return () => document.removeEventListener("keydown", onKey);
103
+ }, [dragging, endDrag, latestOnResize]);
104
+ const onKeyDown = (e) => {
105
+ const step = e.shiftKey ? BIG_STEP : STEP;
106
+ let next = null;
107
+ if (e.key === "ArrowLeft") next = width - direction * step;
108
+ else if (e.key === "ArrowRight") next = width + direction * step;
109
+ else if (e.key === "Home") next = min;
110
+ else if (e.key === "End") next = max();
111
+ else if (e.key === "Enter") next = defaultWidth;
112
+ if (next === null) return;
113
+ e.preventDefault();
114
+ onResize(clamp(next, min, max()));
115
+ };
116
+ return /* @__PURE__ */ jsx(
117
+ "div",
118
+ {
119
+ role: "separator",
120
+ tabIndex: 0,
121
+ "aria-orientation": "vertical",
122
+ "aria-label": label,
123
+ "aria-valuemin": min,
124
+ "aria-valuemax": max(),
125
+ "aria-valuenow": Math.round(width),
126
+ "data-resize-edge": edge,
127
+ "data-dragging": dragging || void 0,
128
+ onPointerDown,
129
+ onPointerMove,
130
+ onPointerUp: endDrag,
131
+ onPointerCancel: endDrag,
132
+ onDoubleClick: () => onResize(clamp(defaultWidth, min, max())),
133
+ onKeyDown,
134
+ className: cn(
135
+ // A narrow absolutely-positioned strip over the edge — never a flex child that takes width.
136
+ "group/edge absolute top-0 bottom-0 z-10 w-ds-2 cursor-col-resize touch-none select-none outline-none",
137
+ edge === "left" ? "-left-ds-1" : "-right-ds-1",
138
+ className
139
+ ),
140
+ children: /* @__PURE__ */ jsx(
141
+ "div",
142
+ {
143
+ "aria-hidden": "true",
144
+ className: cn(
145
+ "absolute top-0 bottom-0 left-1/2 w-px -translate-x-1/2 transition-[width,background-color] duration-[var(--ds-transition-speed)]",
146
+ "group-hover/edge:w-[3px] group-hover/edge:bg-[var(--ds-accent-primary)]",
147
+ "group-focus-visible/edge:w-[3px] group-focus-visible/edge:bg-[var(--ds-accent-primary)]",
148
+ dragging && "w-[3px] bg-[var(--ds-accent-primary)]"
149
+ )
150
+ }
151
+ )
152
+ }
153
+ );
154
+ }
155
+ var remembered = /* @__PURE__ */ new Map();
156
+ function useRememberedWidth(key) {
157
+ const [width, setWidth] = useState(() => key ? remembered.get(key) ?? null : null);
158
+ const remember = useCallback(
159
+ (w) => {
160
+ setWidth(w);
161
+ if (!key) return;
162
+ if (w === null) remembered.delete(key);
163
+ else remembered.set(key, w);
164
+ },
165
+ [key]
166
+ );
167
+ return [width, remember];
168
+ }
169
+ function pxOf(length) {
170
+ if (!length) return null;
171
+ const m = /^(\d+(?:\.\d+)?)px$/.exec(length.trim());
172
+ return m ? parseFloat(m[1]) : null;
173
+ }
174
+ function defaultMaxWidth(container) {
175
+ const base = container instanceof HTMLElement ? container.clientWidth : typeof window !== "undefined" ? window.innerWidth : 1200;
176
+ return Math.round(base * 0.6);
177
+ }
204
178
  var OVERLAY_ROOT_ATTR = "data-overlay-root";
205
179
  function controlsOf(root) {
206
180
  if (!root) return "";
@@ -356,7 +330,7 @@ function DiscardConfirm({ open, onKeep, onDiscard }) {
356
330
  "aria-label": "Unsaved changes",
357
331
  "data-discard-confirm": "",
358
332
  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)]",
359
- style: { padding: "var(--ds-space-3) var(--ds-space-5)" },
333
+ style: { padding: "var(--ds-space-3) var(--ds-page-gutter)" },
360
334
  children: [
361
335
  /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--ds-text-primary)]", children: "You have unsaved changes." }),
362
336
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-2", children: [
@@ -367,328 +341,26 @@ function DiscardConfirm({ open, onKeep, onDiscard }) {
367
341
  }
368
342
  );
369
343
  }
370
- function Sheet({ open, onClose: onCloseProp, title, children, footer, confirm, dirty, onDiscard, snapPoints = [0.5, 0.92], container, flush, keepMounted = false, id, labelledBy }) {
371
- const standalone = confirm === void 0;
372
- const { marked, mark } = useOverlayMark(open);
373
- const guards = standalone || dirty !== void 0;
374
- const guard = useDirtyGuard(standalone ? dirty ?? marked : dirty, onCloseProp, onDiscard);
375
- const onClose = guard.requestClose;
376
- const titleId = useId();
377
- const panelRef = useRef(null);
378
- const [mounted, setMounted] = useState(open);
379
- const [hidden, setHidden] = useState(!open);
380
- const [snapIdx, setSnapIdx] = useState(0);
381
- const [dragOffset, setDragOffset] = useState(0);
382
- const [dragging, setDragging] = useState(false);
383
- const dragStartY = useRef(0);
384
- const startHeight = useRef(0);
385
- const onCloseRef = useLatest(onClose);
386
- const [prevOpen, setPrevOpen] = useState(open);
387
- if (open !== prevOpen) {
388
- setPrevOpen(open);
389
- if (open) {
390
- setMounted(true);
391
- setHidden(false);
392
- setSnapIdx(0);
393
- setDragOffset(0);
394
- }
395
- }
396
- if (open && !mounted) setMounted(true);
397
- if (open && hidden) setHidden(false);
398
- const viewportHeight = useCallback(
399
- () => container instanceof HTMLElement ? container.clientHeight : window.innerHeight,
400
- [container]
401
- );
402
- useCloseSettle(open, panelRef, () => {
403
- if (keepMounted) setHidden(true);
404
- else setMounted(false);
405
- });
406
- useReturnFocus(open, panelRef);
407
- useEffect(() => {
408
- if (!open || !mounted) return;
409
- const raf = requestAnimationFrame(() => focusInitial(panelRef.current));
410
- return () => cancelAnimationFrame(raf);
411
- }, [open, mounted]);
412
- const isTopLayer = useOverlayLayer(open);
413
- useFocusTrap(panelRef, open, isTopLayer);
414
- useEffect(() => {
415
- if (!open) return;
416
- const onKeyDown = (e) => {
417
- if (e.key !== "Escape" || e.defaultPrevented || !isTopLayer()) return;
418
- e.preventDefault();
419
- onCloseRef.current();
420
- };
421
- document.addEventListener("keydown", onKeyDown);
422
- if (!container) lockBodyScroll();
423
- return () => {
424
- document.removeEventListener("keydown", onKeyDown);
425
- if (!container) unlockBodyScroll();
344
+ var DrawerWidthCtx = createContext(null);
345
+ function useScrollbarInset() {
346
+ const observer = useRef(null);
347
+ return useCallback((el) => {
348
+ observer.current?.disconnect();
349
+ observer.current = null;
350
+ if (!el) return;
351
+ const measure = () => {
352
+ const style = getComputedStyle(el);
353
+ const borders = (parseFloat(style.borderLeftWidth) || 0) + (parseFloat(style.borderRightWidth) || 0);
354
+ el.style.setProperty("--gs-scrollbar", `${Math.max(0, el.offsetWidth - el.clientWidth - borders)}px`);
426
355
  };
427
- }, [open, container, onCloseRef, isTopLayer]);
428
- const handlePointerDown = useCallback((e) => {
429
- setDragging(true);
430
- dragStartY.current = e.clientY;
431
- startHeight.current = (snapPoints[snapIdx] ?? snapPoints[0]) * viewportHeight();
432
- setDragOffset(0);
433
- e.target.setPointerCapture(e.pointerId);
434
- }, [snapIdx, snapPoints, viewportHeight]);
435
- const handlePointerMove = useCallback((e) => {
436
- if (!dragging) return;
437
- const delta = dragStartY.current - e.clientY;
438
- setDragOffset(delta);
439
- }, [dragging]);
440
- const handlePointerUp = useCallback(() => {
441
- if (!dragging) return;
442
- setDragging(false);
443
- const currentHeightPx = startHeight.current + dragOffset;
444
- const currentFraction = currentHeightPx / viewportHeight();
445
- if (currentFraction < snapPoints[0] * 0.6) {
446
- onClose();
447
- setDragOffset(0);
448
- return;
449
- }
450
- let bestIdx = 0;
451
- let bestDist = Math.abs(currentFraction - snapPoints[0]);
452
- for (let i = 1; i < snapPoints.length; i++) {
453
- const dist = Math.abs(currentFraction - snapPoints[i]);
454
- if (dist < bestDist) {
455
- bestDist = dist;
456
- bestIdx = i;
457
- }
458
- }
459
- setSnapIdx(bestIdx);
460
- setDragOffset(0);
461
- }, [dragging, dragOffset, snapPoints, onClose, viewportHeight]);
462
- if (!mounted) return null;
463
- const baseHeight = (snapPoints[snapIdx] ?? snapPoints[0]) * 100;
464
- const heightUnit = container ? "%" : "vh";
465
- const dragPx = dragging ? dragOffset : 0;
466
- const positionClass = container ? "absolute" : "fixed";
467
- return createPortal(
468
- /* @__PURE__ */ jsxs(
469
- "div",
470
- {
471
- hidden: keepMounted && hidden ? true : void 0,
472
- className: cn(
473
- `${positionClass} inset-0 z-[var(--ds-z-modal)] transition-opacity duration-[var(--ds-transition-speed)]`,
474
- open ? "pointer-events-auto" : "pointer-events-none"
475
- ),
476
- children: [
477
- /* @__PURE__ */ jsx(
478
- "div",
479
- {
480
- className: cn(
481
- "absolute inset-0 bg-black/50 transition-opacity duration-[var(--ds-transition-speed)]",
482
- open ? "opacity-100" : "opacity-0"
483
- ),
484
- onClick: onClose
485
- }
486
- ),
487
- /* @__PURE__ */ jsxs(
488
- "div",
489
- {
490
- ref: panelRef,
491
- id,
492
- role: "dialog",
493
- "data-gs": "sheet",
494
- ...{ [OVERLAY_ROOT_ATTR]: "" },
495
- "aria-modal": "true",
496
- "aria-labelledby": title ? titleId : labelledBy,
497
- "data-ds-sheet": "",
498
- className: cn(
499
- `${positionClass} bottom-0 left-0 right-0 z-10 flex flex-col`,
500
- OVERLAY_SURFACE,
501
- "text-[var(--ds-text-primary)]",
502
- GLASS_OVERLAY,
503
- "border-t-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
504
- "rounded-t-[var(--ds-radius-xl)]",
505
- !dragging && "transition-[transform,height] duration-[var(--ds-transition-speed)] ease-[var(--ds-transition-ease)]",
506
- open ? "translate-y-0" : "translate-y-full"
507
- ),
508
- style: {
509
- containerType: "inline-size",
510
- height: dragPx ? `calc(${baseHeight}${heightUnit} + ${dragPx}px)` : `${baseHeight}${heightUnit}`
511
- },
512
- children: [
513
- /* @__PURE__ */ jsx(
514
- "div",
515
- {
516
- className: "flex justify-center pt-ds-2 pb-ds-1 cursor-grab active:cursor-grabbing touch-none",
517
- onPointerDown: handlePointerDown,
518
- onPointerMove: handlePointerMove,
519
- onPointerUp: handlePointerUp,
520
- onPointerCancel: handlePointerUp,
521
- children: /* @__PURE__ */ jsx("div", { className: "w-10 h-1 rounded-[var(--ds-radius-full)] bg-[var(--ds-border-default)]" })
522
- }
523
- ),
524
- title && /* @__PURE__ */ jsxs(
525
- "div",
526
- {
527
- className: "flex items-center justify-between shrink-0 border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
528
- style: { padding: "var(--ds-space-2) var(--ds-space-5)" },
529
- children: [
530
- /* @__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 }),
531
- /* @__PURE__ */ jsx(
532
- "button",
533
- {
534
- type: "button",
535
- onClick: onClose,
536
- ...{ [OVERLAY_CLOSE_ATTR]: "" },
537
- className: "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer",
538
- "aria-label": "Close",
539
- children: /* @__PURE__ */ jsx(Icon, { name: "close", size: 18 })
540
- }
541
- )
542
- ]
543
- }
544
- ),
545
- /* @__PURE__ */ jsx(
546
- "div",
547
- {
548
- className: "flex-1 overflow-y-auto",
549
- style: flush ? void 0 : { padding: "var(--ds-space-5)" },
550
- children: guards ? /* @__PURE__ */ jsx(OverlayCloseCtx, { value: guard.requestClose, children: standalone ? /* @__PURE__ */ jsx(OverlayMarkScope, { mark, children }) : children }) : children
551
- }
552
- ),
553
- confirm,
554
- guards && /* @__PURE__ */ jsx(DiscardConfirm, { open: guard.confirming, onKeep: guard.keep, onDiscard: guard.discard }),
555
- footer && /* @__PURE__ */ jsx(
556
- "div",
557
- {
558
- className: "shrink-0 flex items-center justify-end gap-ds-2 border-t-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
559
- style: { padding: "var(--ds-space-3) var(--ds-space-5)" },
560
- children: footer
561
- }
562
- )
563
- ]
564
- }
565
- )
566
- ]
567
- }
568
- ),
569
- container ?? document.body
570
- );
571
- }
572
- var STEP = 16;
573
- var BIG_STEP = 64;
574
- function clamp(v, min, max) {
575
- return Math.min(Math.max(v, min), max);
576
- }
577
- function ResizeEdge({ edge, width, min, max, defaultWidth, onResize, label, onDraggingChange, className }) {
578
- const [dragging, setDragging] = useState(false);
579
- const start = useRef(null);
580
- const latestOnResize = useLatest(onResize);
581
- const direction = edge === "left" ? -1 : 1;
582
- const onPointerDown = useCallback(
583
- (e) => {
584
- if (e.button !== 0) return;
585
- e.preventDefault();
586
- start.current = { x: e.clientX, width };
587
- setDragging(true);
588
- e.currentTarget.setPointerCapture(e.pointerId);
589
- },
590
- [width]
591
- );
592
- const onPointerMove = useCallback(
593
- (e) => {
594
- if (!start.current) return;
595
- const next = clamp(start.current.width + direction * (e.clientX - start.current.x), min, max());
596
- latestOnResize.current(next);
597
- },
598
- [direction, min, max, latestOnResize]
599
- );
600
- const endDrag = useCallback(() => {
601
- start.current = null;
602
- setDragging(false);
356
+ measure();
357
+ if (typeof ResizeObserver === "undefined") return;
358
+ const o = new ResizeObserver(measure);
359
+ o.observe(el);
360
+ observer.current = o;
603
361
  }, []);
604
- const latestOnDraggingChange = useLatest(onDraggingChange);
605
- useEffect(() => {
606
- latestOnDraggingChange.current?.(dragging);
607
- }, [dragging, latestOnDraggingChange]);
608
- useEffect(() => {
609
- if (!dragging) return;
610
- const onKey = (e) => {
611
- if (e.key !== "Escape" || !start.current) return;
612
- latestOnResize.current(start.current.width);
613
- endDrag();
614
- };
615
- document.addEventListener("keydown", onKey);
616
- return () => document.removeEventListener("keydown", onKey);
617
- }, [dragging, endDrag, latestOnResize]);
618
- const onKeyDown = (e) => {
619
- const step = e.shiftKey ? BIG_STEP : STEP;
620
- let next = null;
621
- if (e.key === "ArrowLeft") next = width - direction * step;
622
- else if (e.key === "ArrowRight") next = width + direction * step;
623
- else if (e.key === "Home") next = min;
624
- else if (e.key === "End") next = max();
625
- else if (e.key === "Enter") next = defaultWidth;
626
- if (next === null) return;
627
- e.preventDefault();
628
- onResize(clamp(next, min, max()));
629
- };
630
- return /* @__PURE__ */ jsx(
631
- "div",
632
- {
633
- role: "separator",
634
- tabIndex: 0,
635
- "aria-orientation": "vertical",
636
- "aria-label": label,
637
- "aria-valuemin": min,
638
- "aria-valuemax": max(),
639
- "aria-valuenow": Math.round(width),
640
- "data-resize-edge": edge,
641
- "data-dragging": dragging || void 0,
642
- onPointerDown,
643
- onPointerMove,
644
- onPointerUp: endDrag,
645
- onPointerCancel: endDrag,
646
- onDoubleClick: () => onResize(clamp(defaultWidth, min, max())),
647
- onKeyDown,
648
- className: cn(
649
- // A narrow absolutely-positioned strip over the edge — never a flex child that takes width.
650
- "group/edge absolute top-0 bottom-0 z-10 w-ds-2 cursor-col-resize touch-none select-none outline-none",
651
- edge === "left" ? "-left-ds-1" : "-right-ds-1",
652
- className
653
- ),
654
- children: /* @__PURE__ */ jsx(
655
- "div",
656
- {
657
- "aria-hidden": "true",
658
- className: cn(
659
- "absolute top-0 bottom-0 left-1/2 w-px -translate-x-1/2 transition-[width,background-color] duration-[var(--ds-transition-speed)]",
660
- "group-hover/edge:w-[3px] group-hover/edge:bg-[var(--ds-accent-primary)]",
661
- "group-focus-visible/edge:w-[3px] group-focus-visible/edge:bg-[var(--ds-accent-primary)]",
662
- dragging && "w-[3px] bg-[var(--ds-accent-primary)]"
663
- )
664
- }
665
- )
666
- }
667
- );
668
- }
669
- var remembered = /* @__PURE__ */ new Map();
670
- function useRememberedWidth(key) {
671
- const [width, setWidth] = useState(() => key ? remembered.get(key) ?? null : null);
672
- const remember = useCallback(
673
- (w) => {
674
- setWidth(w);
675
- if (!key) return;
676
- if (w === null) remembered.delete(key);
677
- else remembered.set(key, w);
678
- },
679
- [key]
680
- );
681
- return [width, remember];
682
- }
683
- function pxOf(length) {
684
- if (!length) return null;
685
- const m = /^(\d+(?:\.\d+)?)px$/.exec(length.trim());
686
- return m ? parseFloat(m[1]) : null;
687
- }
688
- function defaultMaxWidth(container) {
689
- const base = container instanceof HTMLElement ? container.clientWidth : typeof window !== "undefined" ? window.innerWidth : 1200;
690
- return Math.round(base * 0.6);
691
362
  }
363
+ var insetLessScrollbar = (gutter) => `max(0px, calc(${gutter} - var(--gs-scrollbar, 0px)))`;
692
364
  var DrawerFooterCtx = createContext(null);
693
365
  var slideClasses = {
694
366
  right: {
@@ -713,7 +385,8 @@ var borderByPosition = {
713
385
  bottom: "border-t-[length:var(--ds-border-width)] border-t-[var(--ds-border-default)]"
714
386
  };
715
387
  function Drawer(props) {
716
- const { position = "right", mobile, snapPoints, open, onClose, title, children, footer, container, flush, keepMounted, id, labelledBy, dirty, onDiscard } = props;
388
+ const { position = "right", mobile, open, onClose, footer, dirty, onDiscard } = props;
389
+ const shellWidth = useContext(DrawerWidthCtx) ?? void 0;
717
390
  const isPhone = useBreakpoint("sm");
718
391
  const mobileMode = mobile ?? (position === "left" ? "panel" : "sheet");
719
392
  const { marked, mark } = useOverlayMark(open);
@@ -736,15 +409,16 @@ function Drawer(props) {
736
409
  () => ({ registerActions, unregisterActions }),
737
410
  [registerActions, unregisterActions]
738
411
  );
739
- if (isPhone && mobileMode === "sheet") {
740
- 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 }) }) }) });
741
- }
742
- 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 }) }) }) });
412
+ 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 }) }) }) });
743
413
  }
744
- function DrawerPanel({ open, onClose, position = "right", width = "400px", title, children, footer, container, flush, lightBackdrop, topOffset, modal = true, keepMounted = false, id, labelledBy, resizable = false, maxWidth, confirm }) {
414
+ var INSET = "var(--ds-page-gutter)";
415
+ var DEFAULT_SNAPS = [0.5, 0.92];
416
+ 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 }) {
417
+ const sheet = form === "sheet";
745
418
  const nonModal = !modal;
419
+ const blocking = !nonModal || sheet;
746
420
  const [chosen, remember] = useRememberedWidth(id ?? labelledBy);
747
- const canResize = nonModal && resizable && position !== "bottom";
421
+ const canResize = !sheet && nonModal && resizable && position !== "bottom";
748
422
  const effectiveWidth = canResize && chosen !== null ? `${chosen}px` : width;
749
423
  const minPx = pxOf(width) ?? 320;
750
424
  const maxPx = () => pxOf(maxWidth) ?? defaultMaxWidth(container instanceof Element ? container : null);
@@ -754,40 +428,104 @@ function DrawerPanel({ open, onClose, position = "right", width = "400px", title
754
428
  const panelRef = useRef(null);
755
429
  const [mounted, setMounted] = useState(open);
756
430
  const [hidden, setHidden] = useState(!open);
431
+ const [snapIdx, setSnapIdx] = useState(0);
432
+ const [dragOffset, setDragOffset] = useState(0);
433
+ const [dragging, setDragging] = useState(false);
434
+ const dragStartY = useRef(0);
435
+ const startHeight = useRef(0);
757
436
  const onCloseRef = useLatest(onClose);
437
+ const [prevOpen, setPrevOpen] = useState(open);
438
+ if (open !== prevOpen) {
439
+ setPrevOpen(open);
440
+ if (open) {
441
+ setSnapIdx(0);
442
+ setDragOffset(0);
443
+ }
444
+ }
758
445
  if (open && !mounted) setMounted(true);
759
446
  if (open && hidden) setHidden(false);
760
447
  const isTopLayer = useOverlayLayer(open);
761
448
  useEffect(() => {
762
- if (!open || nonModal) return;
449
+ if (!open || !blocking) return;
763
450
  const onKeyDown = (e) => {
764
451
  if (e.key !== "Escape" || e.defaultPrevented || !isTopLayer()) return;
765
452
  e.preventDefault();
766
453
  onCloseRef.current();
767
454
  };
768
455
  document.addEventListener("keydown", onKeyDown);
769
- if (!container) lockBodyScroll();
770
- return () => {
771
- document.removeEventListener("keydown", onKeyDown);
772
- if (!container) unlockBodyScroll();
773
- };
774
- }, [open, nonModal, container, onCloseRef, isTopLayer]);
456
+ return () => document.removeEventListener("keydown", onKeyDown);
457
+ }, [open, blocking, onCloseRef, isTopLayer]);
458
+ useEffect(() => {
459
+ if (!open || !blocking || container) return;
460
+ lockBodyScroll();
461
+ return () => unlockBodyScroll();
462
+ }, [open, blocking, container]);
775
463
  useReturnFocus(open && !nonModal, panelRef);
776
- useFocusTrap(panelRef, open && !topOffset && !nonModal, isTopLayer);
464
+ useFocusTrap(panelRef, open && blocking && (sheet || !topOffset), isTopLayer);
777
465
  useEffect(() => {
778
466
  if (!open || !mounted || nonModal) return;
779
467
  const raf = requestAnimationFrame(() => focusInitial(panelRef.current));
780
468
  return () => cancelAnimationFrame(raf);
781
469
  }, [open, mounted, nonModal]);
782
470
  useNonModalDialog(panelRef, open && mounted && nonModal, onClose, isTopLayer);
471
+ const wasBlocking = useRef(blocking);
472
+ useEffect(() => {
473
+ const was = wasBlocking.current;
474
+ wasBlocking.current = blocking;
475
+ if (!open || !mounted || !blocking || was) return;
476
+ const panel = panelRef.current;
477
+ if (panel && !panel.contains(document.activeElement)) focusInitial(panel);
478
+ }, [open, mounted, blocking]);
479
+ const bodyRef = useScrollbarInset();
783
480
  useCloseSettle(open, panelRef, () => {
784
481
  if (keepMounted) setHidden(true);
785
482
  else setMounted(false);
786
483
  });
484
+ const viewportHeight = useCallback(
485
+ () => container instanceof HTMLElement ? container.clientHeight : window.innerHeight,
486
+ [container]
487
+ );
488
+ const handlePointerDown = useCallback((e) => {
489
+ setDragging(true);
490
+ dragStartY.current = e.clientY;
491
+ startHeight.current = (snapPoints[snapIdx] ?? snapPoints[0]) * viewportHeight();
492
+ setDragOffset(0);
493
+ e.target.setPointerCapture(e.pointerId);
494
+ }, [snapIdx, snapPoints, viewportHeight]);
495
+ const handlePointerMove = useCallback((e) => {
496
+ if (!dragging) return;
497
+ setDragOffset(dragStartY.current - e.clientY);
498
+ }, [dragging]);
499
+ const handlePointerUp = useCallback(() => {
500
+ if (!dragging) return;
501
+ setDragging(false);
502
+ const currentFraction = (startHeight.current + dragOffset) / viewportHeight();
503
+ if (currentFraction < snapPoints[0] * 0.6) {
504
+ onClose();
505
+ setDragOffset(0);
506
+ return;
507
+ }
508
+ let bestIdx = 0;
509
+ let bestDist = Math.abs(currentFraction - snapPoints[0]);
510
+ for (let i = 1; i < snapPoints.length; i++) {
511
+ const dist = Math.abs(currentFraction - snapPoints[i]);
512
+ if (dist < bestDist) {
513
+ bestDist = dist;
514
+ bestIdx = i;
515
+ }
516
+ }
517
+ setSnapIdx(bestIdx);
518
+ setDragOffset(0);
519
+ }, [dragging, dragOffset, snapPoints, onClose, viewportHeight]);
787
520
  if (!mounted) return null;
788
521
  const slide = slideClasses[position];
789
522
  const isHorizontal = position === "left" || position === "right";
790
523
  const positionClass = container ? "absolute" : "fixed";
524
+ const offset = sheet ? void 0 : topOffset;
525
+ const baseHeight = (snapPoints[snapIdx] ?? snapPoints[0]) * 100;
526
+ const heightUnit = container ? "%" : "vh";
527
+ const dragPx = dragging ? dragOffset : 0;
528
+ const heading = "font-[family-name:var(--ds-font-display)] text-lg font-[var(--ds-weight-heading)] text-[var(--ds-text-primary)] truncate";
791
529
  return createPortal(
792
530
  /* @__PURE__ */ jsxs(
793
531
  "div",
@@ -797,17 +535,17 @@ function DrawerPanel({ open, onClose, position = "right", width = "400px", title
797
535
  `${positionClass} transition-opacity duration-[var(--ds-transition-speed)]`,
798
536
  // A non-modal panel never intercepts the page: the wrapper is inert to the
799
537
  // pointer and only the panel itself accepts events.
800
- nonModal ? "z-[var(--ds-z-overlay)] pointer-events-none" : cn("z-[var(--ds-z-modal)]", open ? "pointer-events-auto" : "pointer-events-none"),
801
- topOffset ? "left-0 right-0 bottom-0" : "inset-0"
538
+ blocking ? cn("z-[var(--ds-z-modal)]", open ? "pointer-events-auto" : "pointer-events-none") : "z-[var(--ds-z-overlay)] pointer-events-none",
539
+ offset ? "left-0 right-0 bottom-0" : "inset-0"
802
540
  ),
803
- style: topOffset ? { top: topOffset } : void 0,
541
+ style: offset ? { top: offset } : void 0,
804
542
  children: [
805
- !nonModal && /* @__PURE__ */ jsx(
543
+ blocking && /* @__PURE__ */ jsx(
806
544
  "div",
807
545
  {
808
546
  className: cn(
809
547
  "absolute inset-0 transition-opacity duration-[var(--ds-transition-speed)]",
810
- 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))]"),
548
+ 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))]"),
811
549
  open ? "opacity-100" : "opacity-0"
812
550
  ),
813
551
  onClick: onClose
@@ -819,11 +557,15 @@ function DrawerPanel({ open, onClose, position = "right", width = "400px", title
819
557
  ref: panelRef,
820
558
  id,
821
559
  role: "dialog",
822
- "data-gs": "drawer",
560
+ "data-gs": sheet ? "sheet" : "drawer",
561
+ "data-ds-sheet": sheet ? "" : void 0,
823
562
  ...{ [OVERLAY_ROOT_ATTR]: "" },
824
- "aria-modal": nonModal ? void 0 : "true",
563
+ "aria-modal": blocking ? "true" : void 0,
825
564
  "aria-labelledby": title ? titleId : labelledBy,
826
- style: {
565
+ style: sheet ? {
566
+ containerType: "inline-size",
567
+ height: dragPx ? `calc(${baseHeight}${heightUnit} + ${dragPx}px)` : `${baseHeight}${heightUnit}`
568
+ } : {
827
569
  containerType: "inline-size",
828
570
  width: isHorizontal ? effectiveWidth : void 0,
829
571
  maxWidth: isHorizontal ? "100%" : void 0,
@@ -832,23 +574,42 @@ function DrawerPanel({ open, onClose, position = "right", width = "400px", title
832
574
  },
833
575
  className: cn(
834
576
  `${positionClass} z-10 flex flex-col`,
835
- nonModal && "pointer-events-auto",
577
+ !blocking && "pointer-events-auto",
836
578
  OVERLAY_SURFACE,
837
579
  "text-[var(--ds-text-primary)]",
838
580
  GLASS_OVERLAY,
839
- // flush drops the edge border for children that draw their own (the mobile
840
- // menu's Sidebar); a non-modal panel has no backdrop to separate it from
841
- // the page, so it always keeps the border.
842
- (!flush || nonModal) && borderByPosition[position],
843
- "transition-transform duration-[var(--ds-transition-speed)] ease-[var(--ds-transition-ease)]",
844
- isHorizontal && position === "left" && "rounded-r-[var(--ds-radius-lg)] overflow-hidden",
845
- isHorizontal && position === "right" && "rounded-l-[var(--ds-radius-lg)] overflow-hidden",
846
- !isHorizontal && "rounded-t-[var(--ds-radius-lg)] overflow-hidden",
847
- (lightBackdrop || nonModal) && "shadow-[var(--ds-shadow-overlay)]",
848
- slide.panel,
849
- open ? slide.open : slide.closed
581
+ sheet ? cn(
582
+ "bottom-0 left-0 right-0",
583
+ "border-t-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
584
+ "rounded-t-[var(--ds-radius-xl)]",
585
+ !dragging && "transition-[transform,height] duration-[var(--ds-transition-speed)] ease-[var(--ds-transition-ease)]",
586
+ open ? "translate-y-0" : "translate-y-full"
587
+ ) : cn(
588
+ // flush drops the edge border for children that draw their own (the mobile
589
+ // menu's Sidebar); a non-modal panel has no backdrop to separate it from
590
+ // the page, so it always keeps the border.
591
+ (!flush || nonModal) && borderByPosition[position],
592
+ "transition-transform duration-[var(--ds-transition-speed)] ease-[var(--ds-transition-ease)]",
593
+ isHorizontal && position === "left" && "rounded-r-[var(--ds-radius-lg)] overflow-hidden",
594
+ isHorizontal && position === "right" && "rounded-l-[var(--ds-radius-lg)] overflow-hidden",
595
+ !isHorizontal && "rounded-t-[var(--ds-radius-lg)] overflow-hidden",
596
+ (lightBackdrop || nonModal) && "shadow-[var(--ds-shadow-overlay)]",
597
+ slide.panel,
598
+ open ? slide.open : slide.closed
599
+ )
850
600
  ),
851
601
  children: [
602
+ sheet && /* @__PURE__ */ jsx(
603
+ "div",
604
+ {
605
+ className: "flex justify-center pt-ds-2 pb-ds-1 cursor-grab active:cursor-grabbing touch-none",
606
+ onPointerDown: handlePointerDown,
607
+ onPointerMove: handlePointerMove,
608
+ onPointerUp: handlePointerUp,
609
+ onPointerCancel: handlePointerUp,
610
+ children: /* @__PURE__ */ jsx("div", { className: "w-10 h-1 rounded-[var(--ds-radius-full)] bg-[var(--ds-border-default)]" })
611
+ }
612
+ ),
852
613
  canResize && open && /* @__PURE__ */ jsx(
853
614
  ResizeEdge,
854
615
  {
@@ -864,13 +625,13 @@ function DrawerPanel({ open, onClose, position = "right", width = "400px", title
864
625
  title && /* @__PURE__ */ jsxs(
865
626
  "div",
866
627
  {
867
- className: "flex items-center justify-between shrink-0 border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
868
- style: {
869
- padding: "var(--ds-space-3) var(--ds-space-6)",
628
+ className: "flex items-center justify-between gap-ds-3 shrink-0 border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
629
+ style: sheet ? { padding: `var(--ds-space-2) ${INSET}` } : {
630
+ padding: `var(--ds-space-3) ${INSET}`,
870
631
  minHeight: "calc(var(--ds-space-unit) * 6 + 1.75rem + 2px + var(--ds-border-width))"
871
632
  },
872
633
  children: [
873
- titleTruncated ? /* @__PURE__ */ jsx(Tooltip, { content: title, children: /* @__PURE__ */ jsx("h2", { ref: titleOverflowRef, 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 }) }) : /* @__PURE__ */ jsx("h2", { ref: titleOverflowRef, 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 }),
634
+ !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 }),
874
635
  /* @__PURE__ */ jsx(
875
636
  "button",
876
637
  {
@@ -888,9 +649,10 @@ function DrawerPanel({ open, onClose, position = "right", width = "400px", title
888
649
  /* @__PURE__ */ jsx(
889
650
  "div",
890
651
  {
652
+ ref: bodyRef,
891
653
  className: "flex-1 overflow-y-auto",
892
- style: flush ? void 0 : { padding: "var(--ds-space-5)" },
893
- children
654
+ style: flush ? void 0 : { padding: `var(--ds-space-5) ${insetLessScrollbar(INSET)} var(--ds-space-5) ${INSET}` },
655
+ children: /* @__PURE__ */ jsx(SurfaceBoundary, { children })
894
656
  }
895
657
  ),
896
658
  confirm,
@@ -898,7 +660,7 @@ function DrawerPanel({ open, onClose, position = "right", width = "400px", title
898
660
  "div",
899
661
  {
900
662
  className: "shrink-0 flex items-center justify-end gap-ds-2 border-t-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
901
- style: { padding: "var(--ds-space-3) var(--ds-space-5)" },
663
+ style: { padding: `var(--ds-space-3) ${INSET}` },
902
664
  children: footer
903
665
  }
904
666
  )
@@ -966,7 +728,8 @@ var AppShell = forwardRef(function AppShell2({
966
728
  preferences,
967
729
  currentPath,
968
730
  homePath = "/",
969
- focus
731
+ focus,
732
+ drawerWidth = "520px"
970
733
  }, ref) {
971
734
  if (REACT_VERSION_PROBLEM) throw new Error(REACT_VERSION_PROBLEM);
972
735
  const [sidebarOpen, setSidebarOpen] = useState(false);
@@ -1025,10 +788,10 @@ var AppShell = forwardRef(function AppShell2({
1025
788
  const hasLauncher = !!launcher && !isSmallMobile;
1026
789
  const launcherProps = isValidElement(launcher) ? launcher.props : null;
1027
790
  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";
1028
- return /* @__PURE__ */ jsx(PreferencesContext, { value: preferences ?? null, children: /* @__PURE__ */ jsx(
791
+ return /* @__PURE__ */ jsx(PreferencesContext, { value: preferences ?? null, children: /* @__PURE__ */ jsx(DrawerWidthCtx, { value: drawerWidth, children: /* @__PURE__ */ jsx(
1029
792
  AppShellContext,
1030
793
  {
1031
- 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 },
794
+ 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 },
1032
795
  children: /* @__PURE__ */ jsxs("div", { ref: (el) => {
1033
796
  setShellEl(el);
1034
797
  if (typeof ref === "function") ref(el);
@@ -1082,7 +845,7 @@ var AppShell = forwardRef(function AppShell2({
1082
845
  !isSmallMobile && launcher
1083
846
  ] })
1084
847
  }
1085
- ) });
848
+ ) }) });
1086
849
  });
1087
850
  function useCompact(ref, breakpoint, band = 0) {
1088
851
  const px = typeof breakpoint === "number" ? breakpoint : readBreakpointPx(breakpoint);
@@ -1111,6 +874,6 @@ function useElementWidth(ref) {
1111
874
  return width;
1112
875
  }
1113
876
 
1114
- export { AppShell, Button, ButtonSizeContext, ControlSizeContext, DiscardConfirm, Drawer, DrawerFooterCtx, OVERLAY_ROOT_ATTR, OverlayCloseCtx, OverlayMarkCtx, OverlayMarkScope, REACT_VERSION_PROBLEM, ResizeEdge, Sheet, SkipLink, Spinner, defaultMaxWidth, pxOf, triggerType, useAppShell, useBelow, useCloseSettle, useCompact, useDirtyGuard, useElementWidth, useOverlayMark, useRememberedWidth, useUnsavedChanges };
1115
- //# sourceMappingURL=chunk-GGVASDXT.js.map
1116
- //# sourceMappingURL=chunk-GGVASDXT.js.map
877
+ export { AppShell, DiscardConfirm, Drawer, DrawerFooterCtx, DrawerPanel, OVERLAY_ROOT_ATTR, OverlayCloseCtx, OverlayMarkCtx, OverlayMarkScope, REACT_VERSION_PROBLEM, ResizeEdge, SkipLink, defaultMaxWidth, insetLessScrollbar, pxOf, useAppShell, useBelow, useCloseSettle, useCompact, useDirtyGuard, useElementWidth, useOverlayMark, useRememberedWidth, useScrollbarInset, useUnsavedChanges };
878
+ //# sourceMappingURL=chunk-6PL7FXYH.js.map
879
+ //# sourceMappingURL=chunk-6PL7FXYH.js.map