gridsmith-ui 0.9.0 → 0.9.2

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/index.js CHANGED
@@ -1,165 +1,26 @@
1
- import { ResizablePanelGroup, ResizablePanel, ResizableHandle } from './chunk-5B2ZFL2H.js';
1
+ import { useAppShell, ControlSizeContext, Button, Spinner, DrawerFooterCtx, useCompact, useElementWidth, Sheet, useCloseSettle, Drawer, useRememberedWidth, pxOf, ResizeEdge, useDirtyGuard, DiscardConfirm, defaultMaxWidth } from './chunk-Z37HO5NU.js';
2
+ export { AppShell, Button, ButtonSizeContext, ControlSizeContext, Drawer, Sheet, SkipLink, Spinner, useAppShell } from './chunk-Z37HO5NU.js';
2
3
  import { Skeleton } from './chunk-M3AARGLI.js';
3
4
  export { Skeleton } from './chunk-M3AARGLI.js';
4
- import { useOverlayLayer, useCloseOnFocusOut, useReturnFocus, focusInitial, useFocusTrap, OVERLAY_CLOSE_ATTR, useNonModalDialog, rovingTabStop } from './chunk-RLUK575N.js';
5
- import { Link } from './chunk-X27PSHOH.js';
6
- export { CodeBlock, Divider, Link } from './chunk-X27PSHOH.js';
7
- import { Badge } from './chunk-PHQWHAT3.js';
8
- export { Badge } from './chunk-PHQWHAT3.js';
9
- import { inferColumnKinds } from './chunk-PDZL4A6K.js';
10
- import { useBreakpoint, readBreakpointPx } from './chunk-CRQ6KCRD.js';
5
+ import { useOverlayLayer, useCloseOnFocusOut, focusInitial, rovingTabStop, OVERLAY_CLOSE_ATTR, useNonModalDialog, useFocusTrap } from './chunk-RLUK575N.js';
6
+ import { Link } from './chunk-ENBESZS2.js';
7
+ export { CodeBlock, Divider, Link } from './chunk-ENBESZS2.js';
8
+ import { Badge } from './chunk-OVVRMMCO.js';
9
+ export { Badge } from './chunk-OVVRMMCO.js';
10
+ import { inferColumnKinds, CardValue } from './chunk-FDIFQ5UF.js';
11
+ import { SplitPaneCtx, TableCardCtx } from './chunk-RG4KUCRS.js';
12
+ import { useBreakpoint } from './chunk-CRQ6KCRD.js';
11
13
  export { BreakpointWidthProvider, useBreakpoint } from './chunk-CRQ6KCRD.js';
12
- import { Tooltip, useTextOverflow, usePosition, Slot } from './chunk-RLNIRPOH.js';
13
- export { Tooltip, useTextOverflow } from './chunk-RLNIRPOH.js';
14
+ import { useTextOverflow, Tooltip, usePosition } from './chunk-CQADS7JZ.js';
15
+ export { Tooltip, useTextOverflow } from './chunk-CQADS7JZ.js';
16
+ import { mergeRefs, Slot } from './chunk-PKLVP5ZB.js';
14
17
  import { useLatest } from './chunk-WH2FUFAC.js';
15
18
  import { Icon } from './chunk-X4CNZFWC.js';
16
- import { cn, GLASS_SURFACE, ACTIVE_RING, GLASS_BACKDROP, GLASS_DROPDOWN, lockBodyScroll, unlockBodyScroll, GLASS_OVERLAY, ERROR_ACTIVE_RING, ERROR_FOCUS_RING, FOCUS_RING, ERROR_HAS_FOCUS_RING, HAS_FOCUS_RING, ERROR_FOCUS_WITHIN_RING, FOCUS_WITHIN_RING } from './chunk-4KVFH5KC.js';
17
- import { createContext, forwardRef, useContext, useState, memo, useCallback, useRef, useEffect, isValidElement, useLayoutEffect, Children, useId, cloneElement, useMemo, Fragment as Fragment$1, useSyncExternalStore } from 'react';
18
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
19
+ import { cn, GLASS_SURFACE, ACTIVE_RING, GLASS_BACKDROP, GLASS_DROPDOWN, ERROR_ACTIVE_RING, ERROR_FOCUS_RING, FOCUS_RING, ERROR_HAS_FOCUS_RING, HAS_FOCUS_RING, ERROR_FOCUS_WITHIN_RING, FOCUS_WITHIN_RING, lockBodyScroll, unlockBodyScroll, GLASS_OVERLAY } from './chunk-4KVFH5KC.js';
20
+ import { createContext, forwardRef, useState, useContext, memo, useEffect, useRef, useCallback, useLayoutEffect, Children, useMemo, useId, isValidElement, cloneElement, Fragment as Fragment$1, useSyncExternalStore } from 'react';
19
21
  import { createPortal } from 'react-dom';
22
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
20
23
 
21
- var sizes = {
22
- sm: "h-4 w-4 border-[1.5px]",
23
- md: "h-6 w-6 border-2",
24
- lg: "h-8 w-8 border-[2.5px]"
25
- };
26
- function Spinner({ size = "md", color = "default", label = "Loading", className }) {
27
- return /* @__PURE__ */ jsx(
28
- "span",
29
- {
30
- role: "status",
31
- "aria-label": label,
32
- className: cn(
33
- "inline-block rounded-[var(--ds-radius-full)] animate-spin",
34
- color === "inherit" ? "border-current/30 border-t-current" : "border-[var(--ds-bg-emphasis)] border-t-[var(--ds-accent-primary)]",
35
- sizes[size],
36
- className
37
- )
38
- }
39
- );
40
- }
41
- var ControlSizeContext = createContext(null);
42
- var ButtonSizeContext = ControlSizeContext;
43
- var variantStyles = {
44
- 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",
45
- secondary: "bg-[var(--ds-bg-surface)] text-[var(--ds-text-primary)] border-[var(--ds-border-default)] hover:bg-[var(--ds-bg-muted)]",
46
- tertiary: "bg-[var(--ds-bg-muted)] text-[var(--ds-text-primary)] border-transparent hover:brightness-95",
47
- outline: "bg-[var(--ds-input-bg)] text-[var(--ds-text-primary)] border-[var(--ds-border-default)] hover:bg-[var(--ds-bg-muted)]",
48
- ghost: "bg-transparent text-[var(--ds-text-primary)] border-transparent hover:bg-[var(--ds-bg-muted)] shadow-none",
49
- destructive: "bg-[var(--ds-status-error)] text-[var(--ds-text-on-error)] border-transparent hover:opacity-90",
50
- success: "bg-[var(--ds-status-success)] text-[var(--ds-text-on-success)] border-transparent hover:opacity-90",
51
- warning: "bg-[var(--ds-status-warning)] text-[var(--ds-text-on-warning)] border-transparent hover:opacity-90",
52
- link: "bg-transparent text-[var(--ds-accent-primary)] hover:text-[var(--ds-accent-primary-hover)] hover:underline"
53
- };
54
- var sizeStyles = {
55
- // xs / icon-xs: dense tool chrome (builder toolbars, inline row actions).
56
- xs: "h-ds-6 px-ds-3 py-0 text-xs",
57
- sm: "h-[var(--ds-control-height-sm)] px-ds-4 py-ds-1 text-xs",
58
- md: "h-[var(--ds-control-height-md)] px-ds-6 py-ds-2 text-sm",
59
- lg: "h-[var(--ds-control-height-lg)] px-ds-8 py-ds-3 text-base",
60
- "icon-xs": "h-ds-6 w-ds-6 p-0",
61
- "icon-sm": "h-[var(--ds-control-height-sm)] w-[var(--ds-control-height-sm)] p-0",
62
- icon: "h-[var(--ds-control-height-md)] w-[var(--ds-control-height-md)] p-0",
63
- "icon-lg": "h-[var(--ds-control-height-lg)] w-[var(--ds-control-height-lg)] p-0"
64
- };
65
- var linkSizeStyles = {
66
- xs: "text-xs",
67
- sm: "text-xs",
68
- md: "text-sm",
69
- lg: "text-base",
70
- "icon-xs": "text-xs",
71
- "icon-sm": "text-xs",
72
- icon: "text-sm",
73
- "icon-lg": "text-base"
74
- };
75
- var iconPaddingReduce = {
76
- xs: 2,
77
- sm: 2,
78
- md: 4,
79
- lg: 6
80
- };
81
- var HOVER_SCALE_CAP = { "--_btn-scale": "min(var(--ds-hover-scale), 1.02)" };
82
- var Button = forwardRef(function Button2({ variant = "default", size: sizeProp, iconLeft, iconRight, loading, className, children, disabled, style, onClick, ...props }, ref) {
83
- const contextSize = useContext(ControlSizeContext);
84
- const size = sizeProp ?? contextSize ?? "md";
85
- const isLink = variant === "link";
86
- const spinnerSize = size === "lg" || size === "icon-lg" ? "md" : "sm";
87
- let paddingStyle;
88
- const isIconSize = size === "icon" || size === "icon-xs" || size === "icon-sm" || size === "icon-lg";
89
- if (!isIconSize && !isLink && (iconLeft || iconRight)) {
90
- const reduce = iconPaddingReduce[size];
91
- paddingStyle = {};
92
- if (iconLeft) paddingStyle.paddingLeft = `calc(${getCssPadding(size)} - ${reduce}px)`;
93
- if (iconRight) paddingStyle.paddingRight = `calc(${getCssPadding(size)} - ${reduce}px)`;
94
- }
95
- const isLoading = loading && !disabled;
96
- const isDisabled = !!disabled;
97
- const handleClick = isLoading ? (e) => {
98
- e.preventDefault();
99
- } : onClick;
100
- const ariaLabel = props["aria-label"];
101
- const btn = /* @__PURE__ */ jsxs(
102
- "button",
103
- {
104
- ref,
105
- "data-variant": variant,
106
- className: cn(
107
- "relative inline-flex items-center justify-center gap-ds-2 whitespace-nowrap font-medium",
108
- isLink ? cn(
109
- "px-0 py-ds-0.5 h-auto",
110
- linkSizeStyles[size],
111
- "transition-colors duration-[var(--ds-transition-speed)]",
112
- "focus-visible:outline-2 focus-visible:outline-[var(--ds-accent-primary)] focus-visible:outline-offset-2",
113
- "cursor-pointer",
114
- "active:text-[var(--ds-accent-primary-active)]"
115
- ) : cn(
116
- "border-[length:var(--ds-border-width)]",
117
- "rounded-[var(--ds-radius-md)]",
118
- "shadow-[var(--ds-shadow-button)]",
119
- "hover:scale-[var(--_btn-scale)] hover:-translate-y-[var(--ds-hover-lift)]",
120
- "active:shadow-[var(--ds-shadow-button-pressed)] active:translate-x-[var(--ds-press-depth)] active:translate-y-[var(--ds-press-depth)]",
121
- "[transition:var(--ds-transition-interactive)]",
122
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
123
- "cursor-pointer disabled:cursor-not-allowed disabled:opacity-disabled",
124
- sizeStyles[size]
125
- ),
126
- variantStyles[variant],
127
- isLoading && !isLink && "cursor-wait pointer-events-none",
128
- className
129
- ),
130
- disabled: isDisabled,
131
- "aria-busy": isLoading || void 0,
132
- "aria-disabled": isLoading && !disabled ? true : void 0,
133
- onClick: handleClick,
134
- style: isLink ? style : { ...HOVER_SCALE_CAP, ...paddingStyle, ...style },
135
- ...props,
136
- children: [
137
- loading && !isLink && /* @__PURE__ */ jsx("span", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsx(Spinner, { size: spinnerSize, color: "inherit" }) }),
138
- /* @__PURE__ */ jsxs("span", { className: cn("inline-flex items-center gap-ds-2 min-w-0", loading && !isLink && "invisible"), children: [
139
- iconLeft,
140
- children,
141
- iconRight
142
- ] })
143
- ]
144
- }
145
- );
146
- if (isIconSize && ariaLabel) {
147
- return /* @__PURE__ */ jsx(Tooltip, { content: ariaLabel, children: btn });
148
- }
149
- return btn;
150
- });
151
- function getCssPadding(size) {
152
- switch (size) {
153
- case "xs":
154
- return "var(--ds-space-3)";
155
- case "sm":
156
- return "var(--ds-space-4)";
157
- case "md":
158
- return "var(--ds-space-6)";
159
- case "lg":
160
- return "var(--ds-space-8)";
161
- }
162
- }
163
24
  function isDivider(entry) {
164
25
  return "type" in entry && entry.type === "divider";
165
26
  }
@@ -235,6 +96,7 @@ function DropdownMenu({ trigger, items, align = "end", className, container }) {
235
96
  {
236
97
  ref: menuRef,
237
98
  role: "menu",
99
+ "data-gs": "menu",
238
100
  onKeyDown: handleMenuKeyDown,
239
101
  className: cn(
240
102
  "fixed z-[var(--ds-z-popover)] min-w-[180px] p-ds-1",
@@ -2445,7 +2307,7 @@ function TimePicker({
2445
2307
  )
2446
2308
  ] });
2447
2309
  }
2448
- var sizes2 = {
2310
+ var sizes = {
2449
2311
  sm: { box: "h-[var(--ds-control-height-sm)]", input: "text-xs self-stretch", btn: "w-7 text-xs" },
2450
2312
  md: { box: "h-[var(--ds-control-height-md)]", input: "text-sm self-stretch", btn: "w-9 text-sm" },
2451
2313
  lg: { box: "h-[var(--ds-control-height-lg)]", input: "text-base self-stretch", btn: "w-10 text-base" }
@@ -2479,18 +2341,18 @@ function NumberInput({
2479
2341
  setPrevSync({ value, focused });
2480
2342
  if (!focused) setDraft(String(value));
2481
2343
  }
2482
- const clamp2 = useCallback(
2344
+ const clamp = useCallback(
2483
2345
  (v) => Math.min(max, Math.max(min, v)),
2484
2346
  [min, max]
2485
2347
  );
2486
2348
  const valueRef = useLatest(value);
2487
2349
  const commitValue = useCallback((v) => {
2488
- const clamped = clamp2(v);
2350
+ const clamped = clamp(v);
2489
2351
  valueRef.current = clamped;
2490
2352
  setDraft(String(clamped));
2491
2353
  onChange(clamped);
2492
2354
  return clamped;
2493
- }, [onChange, clamp2, valueRef]);
2355
+ }, [onChange, clamp, valueRef]);
2494
2356
  const increment = useCallback(() => commitValue(valueRef.current + step), [commitValue, step, valueRef]);
2495
2357
  const decrement = useCallback(() => commitValue(valueRef.current - step), [commitValue, step, valueRef]);
2496
2358
  const startRepeat = useCallback(
@@ -2519,10 +2381,10 @@ function NumberInput({
2519
2381
  setDraft(raw);
2520
2382
  const parsed = parseFloat(raw);
2521
2383
  if (!isNaN(parsed) && isFinite(parsed)) {
2522
- onChange(clamp2(parsed));
2384
+ onChange(clamp(parsed));
2523
2385
  }
2524
2386
  },
2525
- [onChange, clamp2, decimals]
2387
+ [onChange, clamp, decimals]
2526
2388
  );
2527
2389
  const handleBlur = useCallback(() => {
2528
2390
  setFocused(false);
@@ -2552,7 +2414,7 @@ function NumberInput({
2552
2414
  },
2553
2415
  [increment, decrement]
2554
2416
  );
2555
- const s = sizes2[size];
2417
+ const s = sizes[size];
2556
2418
  const atMin = value <= min;
2557
2419
  const atMax = value >= max;
2558
2420
  return /* @__PURE__ */ jsxs(
@@ -2660,7 +2522,7 @@ function NumberInput({
2660
2522
  }
2661
2523
  );
2662
2524
  }
2663
- var variantStyles2 = {
2525
+ var variantStyles = {
2664
2526
  default: "bg-[var(--ds-bg-emphasis)] text-[var(--ds-text-primary)]",
2665
2527
  subtle: "bg-[var(--ds-bg-muted)] text-[var(--ds-text-secondary)]",
2666
2528
  success: "bg-[var(--ds-status-success-bg)] text-[var(--ds-status-success)] border-[length:var(--ds-border-width)] border-[var(--ds-status-success)]/[0.2]",
@@ -2668,7 +2530,7 @@ var variantStyles2 = {
2668
2530
  error: "bg-[var(--ds-status-error-bg)] text-[var(--ds-status-error)] border-[length:var(--ds-border-width)] border-[var(--ds-status-error)]/[0.2]",
2669
2531
  outline: "bg-transparent text-[var(--ds-text-primary)] border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]"
2670
2532
  };
2671
- var sizeStyles2 = {
2533
+ var sizeStyles = {
2672
2534
  sm: "px-ds-1 py-[calc(var(--ds-space-1)*0.5)] text-[length:var(--ds-font-size-xs)] gap-[calc(var(--ds-space-1)*0.5)]",
2673
2535
  md: "px-ds-2 py-[calc(var(--ds-space-1)*0.5)] text-[length:var(--ds-font-size-xs)] gap-ds-1"
2674
2536
  };
@@ -2718,7 +2580,7 @@ var Tag = forwardRef(function Tag2({
2718
2580
  e.stopPropagation();
2719
2581
  onRemove?.();
2720
2582
  };
2721
- const colorClasses = color ? getPaletteClasses(color, variant) : variantStyles2[variant];
2583
+ const colorClasses = color ? getPaletteClasses(color, variant) : variantStyles[variant];
2722
2584
  const tag = /* @__PURE__ */ jsxs(
2723
2585
  "span",
2724
2586
  {
@@ -2737,7 +2599,7 @@ var Tag = forwardRef(function Tag2({
2737
2599
  "inline-flex items-center font-medium max-w-full whitespace-nowrap",
2738
2600
  "rounded-[var(--ds-radius-sm)]",
2739
2601
  "transition-colors duration-[var(--ds-transition-speed)]",
2740
- sizeStyles2[size],
2602
+ sizeStyles[size],
2741
2603
  colorClasses,
2742
2604
  onClick && !disabled && "cursor-pointer select-none hover:opacity-80",
2743
2605
  disabled && "opacity-disabled cursor-not-allowed",
@@ -2883,7 +2745,7 @@ function TagInput({
2883
2745
  }
2884
2746
  );
2885
2747
  }
2886
- var sizes3 = {
2748
+ var sizes2 = {
2887
2749
  sm: "w-[var(--ds-control-height-sm)] h-[var(--ds-control-height-sm)] text-sm",
2888
2750
  md: "w-[var(--ds-control-height-md)] h-[var(--ds-control-height-md)] text-base",
2889
2751
  lg: "w-[var(--ds-control-height-lg)] h-[var(--ds-control-height-lg)] text-lg"
@@ -2986,7 +2848,7 @@ function PinInput({
2986
2848
  !error && !disabled && "hover:border-[var(--ds-input-border-hover)]",
2987
2849
  error ? ERROR_FOCUS_RING : FOCUS_RING,
2988
2850
  "placeholder:text-[var(--ds-text-muted)]",
2989
- sizes3[size],
2851
+ sizes2[size],
2990
2852
  disabled && "opacity-disabled cursor-not-allowed"
2991
2853
  )
2992
2854
  },
@@ -3429,7 +3291,7 @@ function Field({ label, children, hint, error, required, labelAction, id: idProp
3429
3291
  hasError && /* @__PURE__ */ jsx(InlineFieldError, { id: errorId, message: error, className: "mt-0" })
3430
3292
  ] });
3431
3293
  }
3432
- var variantStyles3 = {
3294
+ var variantStyles2 = {
3433
3295
  info: "border-[var(--ds-accent-primary)] bg-[var(--ds-accent-subtle)]",
3434
3296
  success: "border-[var(--ds-status-success)] bg-[var(--ds-status-success-bg)]",
3435
3297
  warning: "border-[var(--ds-status-warning)] bg-[var(--ds-status-warning-bg)]",
@@ -3474,7 +3336,7 @@ var Alert = forwardRef(function Alert2({
3474
3336
  "border-[length:var(--ds-border-width)]",
3475
3337
  "shadow-[var(--ds-shadow-sm)]",
3476
3338
  "text-sm text-[var(--ds-text-primary)]",
3477
- variantStyles3[variant],
3339
+ variantStyles2[variant],
3478
3340
  placementClasses,
3479
3341
  className
3480
3342
  ),
@@ -3570,974 +3432,56 @@ var Alert = forwardRef(function Alert2({
3570
3432
  }
3571
3433
  );
3572
3434
  });
3573
- var CardCtx = createContext(false);
3574
- var CardSurfaceCtx = createContext(false);
3575
- var variantStyles4 = {
3576
- default: `bg-[var(--ds-bg-surface)] border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)] ${GLASS_SURFACE}`,
3577
- featured: [
3578
- `bg-[var(--ds-bg-surface)] border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)] ${GLASS_SURFACE}`,
3579
- "[transition:var(--ds-transition-ambient)]",
3580
- "hover:scale-[var(--ds-ambient-hover-scale)] hover:-translate-y-[var(--ds-ambient-hover-lift)]"
3581
- ].join(" "),
3582
- interactive: [
3583
- `bg-[var(--ds-bg-surface)] cursor-pointer ${GLASS_SURFACE}`,
3584
- "border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
3585
- "[transition:var(--ds-transition-interactive)]",
3586
- "hover:border-[var(--ds-accent-primary)] hover:shadow-[var(--ds-shadow-card),var(--ds-glass-inset)] hover:bg-[var(--ds-bg-surface-elevated)]",
3587
- "hover:scale-[var(--_card-scale)] hover:-translate-y-[var(--ds-hover-lift)]"
3588
- ].join(" "),
3589
- outline: `bg-[var(--ds-bg-surface)] border-[length:var(--ds-border-width)] border-[var(--ds-border-default)] ${GLASS_SURFACE}`,
3590
- ghost: "bg-transparent border-none shadow-none"
3591
- };
3592
- var INTERACTIVE_SCALE_CAP = { "--_card-scale": "min(var(--ds-hover-scale), 1.04)" };
3593
- function RadioIndicator({ checked, disabled }) {
3594
- return /* @__PURE__ */ jsx(
3595
- "span",
3596
- {
3597
- className: cn(
3598
- "flex items-center justify-center shrink-0 rounded-[var(--ds-radius-full)]",
3599
- "h-[18px] w-[18px]",
3600
- "border-[length:var(--ds-border-width)] bg-[var(--ds-input-bg)]",
3601
- "transition-colors duration-[var(--ds-transition-speed)]",
3602
- disabled && "opacity-disabled",
3603
- checked ? "border-[var(--ds-check-border)]" : "border-[var(--ds-input-border)]"
3604
- ),
3605
- children: /* @__PURE__ */ jsx(
3606
- "span",
3607
- {
3608
- className: cn(
3609
- "h-2.5 w-2.5 rounded-[var(--ds-radius-full)] transition-transform duration-[var(--ds-transition-speed)]",
3610
- checked ? "bg-[var(--ds-accent-primary)] [background-image:var(--ds-gradient)] scale-100" : "scale-0"
3611
- )
3612
- }
3613
- )
3614
- }
3615
- );
3435
+ function stampFormId(node, formId) {
3436
+ return Children.map(node, (child) => {
3437
+ if (!isValidElement(child)) return child;
3438
+ if (child.type === Fragment$1) return stampFormId(child.props.children, formId);
3439
+ return cloneElement(child, { form: formId });
3440
+ });
3616
3441
  }
3617
- function CheckboxIndicator({ checked, indeterminate, disabled }) {
3618
- const filled = checked || indeterminate;
3619
- return /* @__PURE__ */ jsx(
3620
- "span",
3621
- {
3622
- className: cn(
3623
- "flex items-center justify-center shrink-0",
3624
- "h-[18px] w-[18px]",
3625
- "rounded-[calc(var(--ds-radius-sm)*0.6)]",
3626
- "border-[2px] transition-all duration-[var(--ds-transition-speed)]",
3627
- disabled && "opacity-disabled",
3628
- filled ? "bg-[var(--ds-check-bg)] [background-image:var(--ds-gradient)] border-[var(--ds-check-border)]" : "bg-[var(--ds-input-bg)] border-[var(--ds-input-border)]"
3629
- ),
3630
- children: /* @__PURE__ */ jsx(
3631
- "svg",
3632
- {
3633
- width: "12",
3634
- height: "12",
3635
- viewBox: "0 0 12 12",
3636
- fill: "none",
3637
- className: cn(
3638
- "text-[var(--ds-check-fg)] transition-opacity",
3639
- filled ? "opacity-100" : "opacity-0"
3640
- ),
3641
- children: indeterminate ? /* @__PURE__ */ jsx("path", { d: "M3 6H9", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round" }) : /* @__PURE__ */ jsx("path", { d: "M2.5 6L5 8.5L9.5 3.5", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round" })
3642
- }
3643
- )
3644
- }
3645
- );
3442
+ var FormCtx = createContext(null);
3443
+ function useFormContext() {
3444
+ const ctx = useContext(FormCtx);
3445
+ if (!ctx) throw new Error("useFormContext must be used within FormWrapper");
3446
+ return ctx;
3646
3447
  }
3647
- function ToggleIndicator({ checked, disabled }) {
3648
- return /* @__PURE__ */ jsx(
3649
- "span",
3650
- {
3651
- className: cn(
3652
- "relative inline-flex shrink-0 items-center overflow-hidden",
3653
- "border-[length:var(--ds-border-width)] rounded-[var(--ds-radius-full)]",
3654
- "transition-colors duration-[var(--ds-transition-speed)]",
3655
- disabled && "opacity-disabled",
3656
- checked ? "bg-[var(--ds-accent-primary)] border-transparent" : "bg-[var(--ds-bg-muted)] border-[var(--ds-border-default)]"
3657
- ),
3658
- style: { width: 44, height: 24 },
3659
- children: /* @__PURE__ */ jsx(
3660
- "span",
3661
- {
3662
- className: "pointer-events-none inline-block shrink-0 bg-[var(--ds-bg-surface)] rounded-[var(--ds-radius-full)] transition-transform",
3663
- style: {
3664
- width: 16,
3665
- height: 16,
3666
- transform: checked ? "translateX(calc(25px - var(--ds-border-width)))" : "translateX(calc(1px + var(--ds-border-width)))"
3667
- }
3668
- }
3669
- )
3670
- }
3671
- );
3448
+ function collectInvalidFields(form) {
3449
+ const out = [];
3450
+ for (const el of Array.from(form.querySelectorAll("[aria-invalid='true']"))) {
3451
+ const id = el.id || "";
3452
+ const label = id && form.querySelector(`label[for="${CSS.escape(id)}"]`)?.textContent?.replace(/\*\s*$/, "").trim() || el.getAttribute("aria-label") || el.closest("[data-field]")?.querySelector("label")?.textContent?.trim() || id || "A field";
3453
+ const described = (el.getAttribute("aria-describedby") ?? "").split(/\s+/).filter(Boolean);
3454
+ const message = described.map((d) => document.getElementById(d)?.textContent?.trim()).find((t) => t && /error|invalid|required|must|enter|choose|select|please/i.test(t)) ?? described.map((d) => document.getElementById(d)?.textContent?.trim()).filter(Boolean).pop() ?? "Needs attention";
3455
+ out.push({ id, label, message });
3456
+ }
3457
+ return out;
3672
3458
  }
3673
- var Card = forwardRef(function Card2({
3674
- variant = "default",
3675
- loading = false,
3676
- selected,
3677
- selectable,
3678
- selectType,
3679
- onSelectedChange,
3680
- name,
3681
- indeterminate,
3682
- disabled = false,
3683
- checkable = false,
3684
- className,
3685
- children,
3686
- onClick,
3687
- style,
3688
- ...props
3689
- }, ref) {
3690
- const isSelectable = selectable || !!selectType;
3691
- const isToggle = selectType === "toggle";
3692
- const hasSelectType = !!selectType;
3693
- const handleClick = isSelectable && !disabled ? (e) => {
3694
- onSelectedChange?.(!selected);
3695
- onClick?.(e);
3696
- } : onClick;
3697
- const handleKeyDown = isSelectable && !disabled ? (e) => {
3698
- if (e.key === "Enter" || e.key === " ") {
3699
- e.preventDefault();
3700
- onSelectedChange?.(!selected);
3701
- }
3702
- } : void 0;
3703
- const handleCheckableClick = (e) => {
3704
- e.stopPropagation();
3705
- onSelectedChange?.(!selected);
3706
- };
3707
- const role = selectType === "radio" ? "radio" : selectType === "checkbox" ? "checkbox" : selectType === "toggle" ? "switch" : isSelectable ? "button" : void 0;
3708
- const ariaAttr = selectType === "radio" || selectType === "checkbox" || selectType === "toggle" ? { "aria-checked": !!selected } : isSelectable ? { "aria-pressed": !!selected } : {};
3709
- const showRing = isSelectable && !isToggle && !hasSelectType && selected;
3710
- const indicator = selectType === "radio" ? /* @__PURE__ */ jsx(RadioIndicator, { checked: !!selected, disabled }) : selectType === "checkbox" ? /* @__PURE__ */ jsx(CheckboxIndicator, { checked: !!selected, indeterminate, disabled }) : selectType === "toggle" ? /* @__PURE__ */ jsx(ToggleIndicator, { checked: !!selected, disabled }) : null;
3711
- const defaultCheckBadge = !hasSelectType && !checkable && selected && !loading ? /* @__PURE__ */ jsx("div", { className: "absolute top-ds-3 right-ds-3 w-5 h-5 rounded-[var(--ds-radius-full)] bg-[var(--ds-accent-primary)] flex items-center justify-center", children: /* @__PURE__ */ jsx(Icon, { name: "check", size: 12, className: "text-[var(--ds-text-on-primary)]" }) }) : null;
3712
- return /* @__PURE__ */ jsxs(
3713
- "div",
3714
- {
3715
- ref,
3716
- "data-card": "",
3717
- role,
3718
- tabIndex: isSelectable ? 0 : void 0,
3719
- "aria-disabled": isSelectable && disabled ? true : void 0,
3720
- ...name && selectType === "radio" ? { "data-radio-group": name } : {},
3721
- style: variant === "interactive" ? { ...INTERACTIVE_SCALE_CAP, ...style } : style,
3722
- className: cn(
3723
- "rounded-[var(--ds-radius-md)] flex flex-col relative",
3724
- variantStyles4[variant],
3725
- isSelectable && !disabled && "cursor-pointer select-none transition-all duration-[var(--ds-transition-speed)] ease-[var(--ds-transition-ease)]",
3726
- isSelectable && !disabled && !selected && "hover:border-[var(--ds-accent-primary)]",
3727
- isSelectable && "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
3728
- (showRing || hasSelectType && selected) && ACTIVE_RING,
3729
- hasSelectType && selected && "bg-[var(--ds-accent-subtle)]",
3730
- disabled && "opacity-disabled cursor-not-allowed",
3731
- className
3732
- ),
3733
- onClick: handleClick,
3734
- onKeyDown: handleKeyDown,
3735
- ...ariaAttr,
3736
- ...props,
3737
- children: [
3738
- defaultCheckBadge,
3739
- indicator && /* @__PURE__ */ jsx("div", { className: "absolute top-ds-3 right-ds-3 z-[1]", children: indicator }),
3740
- checkable && /* @__PURE__ */ jsx(
3741
- "div",
3742
- {
3743
- className: "absolute top-ds-3 left-ds-3 z-[1] cursor-pointer",
3744
- onClick: handleCheckableClick,
3745
- role: "checkbox",
3746
- "aria-checked": !!selected,
3747
- tabIndex: 0,
3748
- onKeyDown: (e) => {
3749
- if (e.key === "Enter" || e.key === " ") {
3750
- e.preventDefault();
3751
- e.stopPropagation();
3752
- onSelectedChange?.(!selected);
3753
- }
3754
- },
3755
- children: /* @__PURE__ */ jsx(CheckboxIndicator, { checked: !!selected })
3756
- }
3757
- ),
3758
- /* @__PURE__ */ jsx(CardSurfaceCtx, { value: true, children: /* @__PURE__ */ jsx(CardCtx, { value: checkable, children: loading ? /* @__PURE__ */ jsxs("div", { className: "space-y-ds-3", children: [
3759
- /* @__PURE__ */ jsx(Skeleton, { variant: "custom", className: "h-4 w-1/3" }),
3760
- /* @__PURE__ */ jsx(Skeleton, { variant: "custom", className: "h-3 w-full" }),
3761
- /* @__PURE__ */ jsx(Skeleton, { variant: "custom", className: "h-3 w-2/3" })
3762
- ] }) : children }) })
3763
- ]
3764
- }
3765
- );
3766
- });
3767
- var CardHeader = forwardRef(function CardHeader2({ className, children, actions, icon, menuItems, ...props }, ref) {
3768
- const isCheckable = useContext(CardCtx);
3769
- const hasActions = actions || menuItems;
3770
- const pl = isCheckable ? "pl-ds-10" : "pl-ds-5";
3771
- const actionsBlock = hasActions ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-2 shrink-0", children: [
3772
- actions,
3773
- menuItems && /* @__PURE__ */ jsx(
3774
- DropdownMenu,
3775
- {
3776
- trigger: /* @__PURE__ */ jsx(
3777
- "button",
3778
- {
3779
- type: "button",
3780
- className: cn(
3781
- "inline-flex items-center justify-center h-8 w-8 rounded-[var(--ds-radius-md)]",
3782
- "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] hover:bg-[var(--ds-bg-muted)]",
3783
- "transition-colors duration-[var(--ds-transition-speed)]"
3784
- ),
3785
- onClick: (e) => e.stopPropagation(),
3786
- children: /* @__PURE__ */ jsx(Icon, { name: "more-horizontal", size: 16 })
3787
- }
3788
- ),
3789
- items: menuItems
3790
- }
3791
- )
3792
- ] }) : null;
3793
- const iconBlock = icon ? /* @__PURE__ */ jsx("span", { className: "shrink-0 h-10 w-10 rounded-[var(--ds-radius-md)] bg-[var(--ds-bg-muted)] flex items-center justify-center mb-ds-1", children: /* @__PURE__ */ jsx(Icon, { name: icon, size: 20, className: "text-[var(--ds-text-secondary)]" }) }) : null;
3794
- if (hasActions) {
3795
- return /* @__PURE__ */ jsx("div", { ref, className: cn(pl, "pr-ds-5 pt-ds-4 last:pb-ds-4", className), ...props, children: /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-ds-4", children: [
3796
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col space-y-ds-1.5 min-w-0", children: [
3797
- iconBlock,
3798
- children
3799
- ] }),
3800
- actionsBlock
3801
- ] }) });
3802
- }
3803
- return /* @__PURE__ */ jsxs("div", { ref, className: cn("flex flex-col space-y-ds-1.5 pr-ds-5 pt-ds-4 last:pb-ds-4", pl, className), ...props, children: [
3804
- iconBlock,
3805
- children
3806
- ] });
3807
- });
3808
- var cardLineClampClasses = {
3809
- 1: "truncate",
3810
- 2: "line-clamp-2",
3811
- 3: "line-clamp-3",
3812
- 4: "line-clamp-4",
3813
- 5: "line-clamp-5",
3814
- 6: "line-clamp-6"
3815
- };
3816
- var CardTitle = forwardRef(function CardTitle2({ className, children, lines = 2, ...props }, forwardedRef) {
3817
- const { ref: overflowRef, isTruncated } = useTextOverflow({ lines, disabled: lines === 0 });
3818
- const lineClampClass = lines > 0 ? cardLineClampClasses[lines] || `line-clamp-[${lines}]` : "";
3819
- const heading = /* @__PURE__ */ jsx(
3820
- "h3",
3821
- {
3822
- ref: (node) => {
3823
- overflowRef(lines > 0 ? node : null);
3824
- if (typeof forwardedRef === "function") forwardedRef(node);
3825
- else if (forwardedRef) forwardedRef.current = node;
3826
- },
3827
- className: cn("font-[family-name:var(--ds-font-display)] text-lg font-[var(--ds-weight-heading)] text-[var(--ds-text-primary)]", lines > 0 && lineClampClass, className),
3828
- ...props,
3829
- children
3830
- }
3831
- );
3832
- if (!isTruncated || lines === 0) return heading;
3833
- return /* @__PURE__ */ jsx(Tooltip, { content: children, children: heading });
3834
- });
3835
- var CardDescription = forwardRef(function CardDescription2({ className, children, lines = 2, ...props }, forwardedRef) {
3836
- const { ref: overflowRef, isTruncated } = useTextOverflow({ lines, disabled: lines === 0 });
3837
- const lineClampClass = lines > 0 ? cardLineClampClasses[lines] || `line-clamp-[${lines}]` : "";
3838
- const el = /* @__PURE__ */ jsx(
3839
- "p",
3840
- {
3841
- ref: (node) => {
3842
- if (lines > 0) overflowRef(node);
3843
- if (typeof forwardedRef === "function") forwardedRef(node);
3844
- else if (forwardedRef) forwardedRef.current = node;
3845
- },
3846
- className: cn("text-sm text-[var(--ds-text-muted)]", lines > 0 && lineClampClass, className),
3847
- ...props,
3848
- children
3849
- }
3850
- );
3851
- if (!isTruncated || lines === 0) return el;
3852
- return /* @__PURE__ */ jsx(Tooltip, { content: children, children: el });
3853
- });
3854
- var CardContent = forwardRef(function CardContent2({ className, children, ...props }, ref) {
3855
- const isCheckable = useContext(CardCtx);
3856
- return /* @__PURE__ */ jsx("div", { ref, className: cn("flex-1 pr-ds-5 py-ds-4", isCheckable ? "pl-ds-10" : "pl-ds-5", className), ...props, children });
3857
- });
3858
- var CardFooter = forwardRef(function CardFooter2({ className, children, ...props }, ref) {
3859
- const isCheckable = useContext(CardCtx);
3860
- return /* @__PURE__ */ jsx("div", { ref, className: cn("flex items-center pr-ds-5 pb-ds-4 pt-ds-2 mt-auto min-w-0", isCheckable ? "pl-ds-10" : "pl-ds-5", className), ...props, children });
3861
- });
3862
- function transitionMs(el) {
3863
- const cs = getComputedStyle(el);
3864
- const parse = (list) => list.split(",").map((raw) => {
3865
- const s = raw.trim();
3866
- if (!s) return 0;
3867
- const n = parseFloat(s);
3868
- if (Number.isNaN(n)) return 0;
3869
- return s.endsWith("ms") ? n : n * 1e3;
3870
- });
3871
- const durations = parse(cs.transitionDuration || "0s");
3872
- const delays = parse(cs.transitionDelay || "0s");
3873
- let max = 0;
3874
- durations.forEach((d, i) => {
3875
- const total = d + (delays[i] ?? delays[0] ?? 0);
3876
- if (total > max) max = total;
3877
- });
3878
- return max;
3879
- }
3880
- function useCloseSettle(open, elRef, onSettled) {
3881
- const cb = useLatest(onSettled);
3882
- useEffect(() => {
3883
- if (open) return;
3884
- const el = elRef.current;
3885
- const ms = el ? transitionMs(el) : 0;
3886
- let done = false;
3887
- const finish = () => {
3888
- if (done) return;
3889
- done = true;
3890
- cb.current();
3891
- };
3892
- if (!el || ms === 0) {
3893
- const t2 = setTimeout(finish, 0);
3894
- return () => clearTimeout(t2);
3895
- }
3896
- const onEnd = (e) => {
3897
- if (e.target === el) finish();
3898
- };
3899
- el.addEventListener("transitionend", onEnd);
3900
- const t = setTimeout(finish, ms + 50);
3901
- return () => {
3902
- el.removeEventListener("transitionend", onEnd);
3903
- clearTimeout(t);
3904
- };
3905
- }, [open, elRef, cb]);
3906
- }
3907
- var STEP = 16;
3908
- var BIG_STEP = 64;
3909
- function clamp(v, min, max) {
3910
- return Math.min(Math.max(v, min), max);
3911
- }
3912
- function ResizeEdge({ edge, width, min, max, defaultWidth, onResize, label, onDraggingChange, className }) {
3913
- const [dragging, setDragging] = useState(false);
3914
- const start = useRef(null);
3915
- const latestOnResize = useLatest(onResize);
3916
- const direction = edge === "left" ? -1 : 1;
3917
- const onPointerDown = useCallback(
3918
- (e) => {
3919
- if (e.button !== 0) return;
3920
- e.preventDefault();
3921
- start.current = { x: e.clientX, width };
3922
- setDragging(true);
3923
- e.currentTarget.setPointerCapture(e.pointerId);
3924
- },
3925
- [width]
3926
- );
3927
- const onPointerMove = useCallback(
3928
- (e) => {
3929
- if (!start.current) return;
3930
- const next = clamp(start.current.width + direction * (e.clientX - start.current.x), min, max());
3931
- latestOnResize.current(next);
3932
- },
3933
- [direction, min, max, latestOnResize]
3934
- );
3935
- const endDrag = useCallback(() => {
3936
- start.current = null;
3937
- setDragging(false);
3938
- }, []);
3939
- const latestOnDraggingChange = useLatest(onDraggingChange);
3940
- useEffect(() => {
3941
- latestOnDraggingChange.current?.(dragging);
3942
- }, [dragging, latestOnDraggingChange]);
3943
- useEffect(() => {
3944
- if (!dragging) return;
3945
- const onKey = (e) => {
3946
- if (e.key !== "Escape" || !start.current) return;
3947
- latestOnResize.current(start.current.width);
3948
- endDrag();
3949
- };
3950
- document.addEventListener("keydown", onKey);
3951
- return () => document.removeEventListener("keydown", onKey);
3952
- }, [dragging, endDrag, latestOnResize]);
3953
- const onKeyDown = (e) => {
3954
- const step = e.shiftKey ? BIG_STEP : STEP;
3955
- let next = null;
3956
- if (e.key === "ArrowLeft") next = width - direction * step;
3957
- else if (e.key === "ArrowRight") next = width + direction * step;
3958
- else if (e.key === "Home") next = min;
3959
- else if (e.key === "End") next = max();
3960
- else if (e.key === "Enter") next = defaultWidth;
3961
- if (next === null) return;
3962
- e.preventDefault();
3963
- onResize(clamp(next, min, max()));
3964
- };
3965
- return /* @__PURE__ */ jsx(
3966
- "div",
3967
- {
3968
- role: "separator",
3969
- tabIndex: 0,
3970
- "aria-orientation": "vertical",
3971
- "aria-label": label,
3972
- "aria-valuemin": min,
3973
- "aria-valuemax": max(),
3974
- "aria-valuenow": Math.round(width),
3975
- "data-resize-edge": edge,
3976
- "data-dragging": dragging || void 0,
3977
- onPointerDown,
3978
- onPointerMove,
3979
- onPointerUp: endDrag,
3980
- onPointerCancel: endDrag,
3981
- onDoubleClick: () => onResize(clamp(defaultWidth, min, max())),
3982
- onKeyDown,
3983
- className: cn(
3984
- // A narrow absolutely-positioned strip over the edge — never a flex child that takes width.
3985
- "group/edge absolute top-0 bottom-0 z-10 w-ds-2 cursor-col-resize touch-none select-none outline-none",
3986
- edge === "left" ? "-left-ds-1" : "-right-ds-1",
3987
- className
3988
- ),
3989
- children: /* @__PURE__ */ jsx(
3990
- "div",
3991
- {
3992
- "aria-hidden": "true",
3993
- className: cn(
3994
- "absolute top-0 bottom-0 left-1/2 w-px -translate-x-1/2 transition-[width,background-color] duration-[var(--ds-transition-speed)]",
3995
- "group-hover/edge:w-[3px] group-hover/edge:bg-[var(--ds-accent-primary)]",
3996
- "group-focus-visible/edge:w-[3px] group-focus-visible/edge:bg-[var(--ds-accent-primary)]",
3997
- dragging && "w-[3px] bg-[var(--ds-accent-primary)]"
3998
- )
3999
- }
4000
- )
4001
- }
4002
- );
4003
- }
4004
- var remembered = /* @__PURE__ */ new Map();
4005
- function useRememberedWidth(key) {
4006
- const [width, setWidth] = useState(() => key ? remembered.get(key) ?? null : null);
4007
- const remember = useCallback(
4008
- (w) => {
4009
- setWidth(w);
4010
- if (!key) return;
4011
- if (w === null) remembered.delete(key);
4012
- else remembered.set(key, w);
4013
- },
4014
- [key]
4015
- );
4016
- return [width, remember];
4017
- }
4018
- function pxOf(length) {
4019
- if (!length) return null;
4020
- const m = /^(\d+(?:\.\d+)?)px$/.exec(length.trim());
4021
- return m ? parseFloat(m[1]) : null;
4022
- }
4023
- function defaultMaxWidth(container) {
4024
- const base = container instanceof HTMLElement ? container.clientWidth : typeof window !== "undefined" ? window.innerWidth : 1200;
4025
- return Math.round(base * 0.6);
4026
- }
4027
- function useDirtyGuard(dirty, onClose, onDiscard) {
4028
- const [confirming, setConfirming] = useState(false);
4029
- useEffect(() => {
4030
- if (!dirty) setConfirming(false);
4031
- }, [dirty]);
4032
- const requestClose = useCallback(() => {
4033
- if (!dirty) {
4034
- onClose();
4035
- return;
4036
- }
4037
- setConfirming((c) => !c);
4038
- }, [dirty, onClose]);
4039
- const keep = useCallback(() => setConfirming(false), []);
4040
- const discard = useCallback(() => {
4041
- setConfirming(false);
4042
- onDiscard?.();
4043
- onClose();
4044
- }, [onClose, onDiscard]);
4045
- return { confirming, requestClose, keep, discard };
4046
- }
4047
- function DiscardConfirm({ open, onKeep, onDiscard }) {
4048
- const keepRef = useRef(null);
4049
- const returnTo = useRef(null);
4050
- const overlay = useRef(null);
4051
- useEffect(() => {
4052
- if (!open) return;
4053
- const active = document.activeElement instanceof HTMLElement ? document.activeElement : null;
4054
- overlay.current = keepRef.current?.closest("[role='dialog'], [role='alertdialog']") ?? null;
4055
- returnTo.current = active && active !== document.body && overlay.current?.contains(active) ? active : null;
4056
- keepRef.current?.focus();
4057
- return () => {
4058
- if (document.activeElement !== document.body) return;
4059
- const back = returnTo.current;
4060
- if (back && back.isConnected) {
4061
- back.focus();
4062
- return;
4063
- }
4064
- const first = overlay.current?.querySelector("button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex='-1'])");
4065
- (first ?? overlay.current)?.focus();
4066
- };
4067
- }, [open]);
4068
- if (!open) return null;
4069
- return /* @__PURE__ */ jsxs(
4070
- "div",
4071
- {
4072
- role: "group",
4073
- "aria-label": "Unsaved changes",
4074
- "data-discard-confirm": "",
4075
- 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)]",
4076
- style: { padding: "var(--ds-space-3) var(--ds-space-5)" },
4077
- children: [
4078
- /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--ds-text-primary)]", children: "You have unsaved changes." }),
4079
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-2", children: [
4080
- /* @__PURE__ */ jsx(Button, { ref: keepRef, variant: "secondary", size: "sm", onClick: onKeep, children: "Keep editing" }),
4081
- /* @__PURE__ */ jsx(Button, { variant: "destructive", size: "sm", onClick: onDiscard, children: "Discard changes" })
4082
- ] })
4083
- ]
4084
- }
4085
- );
4086
- }
4087
- function Sheet({ open, onClose, title, children, footer, confirm, snapPoints = [0.5, 0.92], container, flush, keepMounted = false, id, labelledBy }) {
4088
- const titleId = useId();
4089
- const panelRef = useRef(null);
4090
- const [mounted, setMounted] = useState(open);
4091
- const [hidden, setHidden] = useState(!open);
4092
- const [snapIdx, setSnapIdx] = useState(0);
4093
- const [dragOffset, setDragOffset] = useState(0);
4094
- const [dragging, setDragging] = useState(false);
4095
- const dragStartY = useRef(0);
4096
- const startHeight = useRef(0);
4097
- const onCloseRef = useLatest(onClose);
4098
- const [prevOpen, setPrevOpen] = useState(open);
4099
- if (open !== prevOpen) {
4100
- setPrevOpen(open);
4101
- if (open) {
4102
- setMounted(true);
4103
- setHidden(false);
4104
- setSnapIdx(0);
4105
- setDragOffset(0);
4106
- }
4107
- }
4108
- const viewportHeight = useCallback(
4109
- () => container instanceof HTMLElement ? container.clientHeight : window.innerHeight,
4110
- [container]
4111
- );
4112
- useCloseSettle(open, panelRef, () => {
4113
- if (keepMounted) setHidden(true);
4114
- else setMounted(false);
4115
- });
4116
- useReturnFocus(open);
4117
- useEffect(() => {
4118
- if (!open || !mounted) return;
4119
- const raf = requestAnimationFrame(() => focusInitial(panelRef.current));
4120
- return () => cancelAnimationFrame(raf);
4121
- }, [open, mounted]);
4122
- const isTopLayer = useOverlayLayer(open);
4123
- useFocusTrap(panelRef, open, isTopLayer);
4124
- useEffect(() => {
4125
- if (!open) return;
4126
- const onKeyDown = (e) => {
4127
- if (e.key === "Escape" && isTopLayer()) onCloseRef.current();
4128
- };
4129
- document.addEventListener("keydown", onKeyDown);
4130
- if (!container) lockBodyScroll();
4131
- return () => {
4132
- document.removeEventListener("keydown", onKeyDown);
4133
- if (!container) unlockBodyScroll();
4134
- };
4135
- }, [open, container, onCloseRef, isTopLayer]);
4136
- const handlePointerDown = useCallback((e) => {
4137
- setDragging(true);
4138
- dragStartY.current = e.clientY;
4139
- startHeight.current = (snapPoints[snapIdx] ?? snapPoints[0]) * viewportHeight();
4140
- setDragOffset(0);
4141
- e.target.setPointerCapture(e.pointerId);
4142
- }, [snapIdx, snapPoints, viewportHeight]);
4143
- const handlePointerMove = useCallback((e) => {
4144
- if (!dragging) return;
4145
- const delta = dragStartY.current - e.clientY;
4146
- setDragOffset(delta);
4147
- }, [dragging]);
4148
- const handlePointerUp = useCallback(() => {
4149
- if (!dragging) return;
4150
- setDragging(false);
4151
- const currentHeightPx = startHeight.current + dragOffset;
4152
- const currentFraction = currentHeightPx / viewportHeight();
4153
- if (currentFraction < snapPoints[0] * 0.6) {
4154
- onClose();
4155
- setDragOffset(0);
4156
- return;
4157
- }
4158
- let bestIdx = 0;
4159
- let bestDist = Math.abs(currentFraction - snapPoints[0]);
4160
- for (let i = 1; i < snapPoints.length; i++) {
4161
- const dist = Math.abs(currentFraction - snapPoints[i]);
4162
- if (dist < bestDist) {
4163
- bestDist = dist;
4164
- bestIdx = i;
4165
- }
4166
- }
4167
- setSnapIdx(bestIdx);
4168
- setDragOffset(0);
4169
- }, [dragging, dragOffset, snapPoints, onClose, viewportHeight]);
4170
- if (!mounted) return null;
4171
- const baseHeight = (snapPoints[snapIdx] ?? snapPoints[0]) * 100;
4172
- const heightUnit = container ? "%" : "vh";
4173
- const dragPx = dragging ? dragOffset : 0;
4174
- const positionClass = container ? "absolute" : "fixed";
4175
- return createPortal(
4176
- /* @__PURE__ */ jsxs(
4177
- "div",
4178
- {
4179
- hidden: keepMounted && hidden ? true : void 0,
4180
- className: cn(
4181
- `${positionClass} inset-0 z-[var(--ds-z-modal)] transition-opacity duration-[var(--ds-transition-speed)]`,
4182
- open ? "pointer-events-auto" : "pointer-events-none"
4183
- ),
4184
- children: [
4185
- /* @__PURE__ */ jsx(
4186
- "div",
4187
- {
4188
- className: cn(
4189
- "absolute inset-0 bg-black/50 transition-opacity duration-[var(--ds-transition-speed)]",
4190
- open ? "opacity-100" : "opacity-0"
4191
- ),
4192
- onClick: onClose
4193
- }
4194
- ),
4195
- /* @__PURE__ */ jsxs(
4196
- "div",
4197
- {
4198
- ref: panelRef,
4199
- id,
4200
- role: "dialog",
4201
- "aria-modal": "true",
4202
- "aria-labelledby": title ? titleId : labelledBy,
4203
- "data-ds-sheet": "",
4204
- className: cn(
4205
- `${positionClass} bottom-0 left-0 right-0 z-10 flex flex-col`,
4206
- "bg-[var(--ds-bg-surface)] text-[var(--ds-text-primary)]",
4207
- GLASS_OVERLAY,
4208
- "border-t-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
4209
- "rounded-t-[var(--ds-radius-xl)]",
4210
- !dragging && "transition-[transform,height] duration-[var(--ds-transition-speed)] ease-[var(--ds-transition-ease)]",
4211
- open ? "translate-y-0" : "translate-y-full"
4212
- ),
4213
- style: {
4214
- containerType: "inline-size",
4215
- height: dragPx ? `calc(${baseHeight}${heightUnit} + ${dragPx}px)` : `${baseHeight}${heightUnit}`
4216
- },
4217
- children: [
4218
- /* @__PURE__ */ jsx(
4219
- "div",
4220
- {
4221
- className: "flex justify-center pt-ds-2 pb-ds-1 cursor-grab active:cursor-grabbing touch-none",
4222
- onPointerDown: handlePointerDown,
4223
- onPointerMove: handlePointerMove,
4224
- onPointerUp: handlePointerUp,
4225
- onPointerCancel: handlePointerUp,
4226
- children: /* @__PURE__ */ jsx("div", { className: "w-10 h-1 rounded-[var(--ds-radius-full)] bg-[var(--ds-border-default)]" })
4227
- }
4228
- ),
4229
- title && /* @__PURE__ */ jsxs(
4230
- "div",
4231
- {
4232
- className: "flex items-center justify-between shrink-0 border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
4233
- style: { padding: "var(--ds-space-2) var(--ds-space-5)" },
4234
- children: [
4235
- /* @__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 }),
4236
- /* @__PURE__ */ jsx(
4237
- "button",
4238
- {
4239
- onClick: onClose,
4240
- ...{ [OVERLAY_CLOSE_ATTR]: "" },
4241
- className: "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer",
4242
- "aria-label": "Close",
4243
- children: /* @__PURE__ */ jsx(Icon, { name: "close", size: 18 })
4244
- }
4245
- )
4246
- ]
4247
- }
4248
- ),
4249
- /* @__PURE__ */ jsx(
4250
- "div",
4251
- {
4252
- className: "flex-1 overflow-y-auto",
4253
- style: flush ? void 0 : { padding: "var(--ds-space-5)" },
4254
- children
4255
- }
4256
- ),
4257
- confirm,
4258
- footer && /* @__PURE__ */ jsx(
4259
- "div",
4260
- {
4261
- className: "shrink-0 flex items-center justify-end gap-ds-2 border-t-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
4262
- style: { padding: "var(--ds-space-3) var(--ds-space-5)" },
4263
- children: footer
4264
- }
4265
- )
4266
- ]
4267
- }
4268
- )
4269
- ]
4270
- }
4271
- ),
4272
- container ?? document.body
4273
- );
4274
- }
4275
- var DrawerFooterCtx = createContext(null);
4276
- var slideClasses = {
4277
- right: {
4278
- panel: "top-0 right-0 h-full",
4279
- open: "translate-x-0",
4280
- closed: "translate-x-full"
4281
- },
4282
- left: {
4283
- panel: "top-0 left-0 h-full",
4284
- open: "translate-x-0",
4285
- closed: "-translate-x-full"
4286
- },
4287
- bottom: {
4288
- panel: "bottom-0 left-0 right-0",
4289
- open: "translate-y-0",
4290
- closed: "translate-y-full"
4291
- }
4292
- };
4293
- var borderByPosition = {
4294
- right: "border-l-[length:var(--ds-border-width)] border-l-[var(--ds-border-default)]",
4295
- left: "border-r-[length:var(--ds-border-width)] border-r-[var(--ds-border-default)]",
4296
- bottom: "border-t-[length:var(--ds-border-width)] border-t-[var(--ds-border-default)]"
4297
- };
4298
- function Drawer(props) {
4299
- const { position = "right", mobile, snapPoints, open, onClose, title, children, footer, container, flush, keepMounted, id, labelledBy, dirty, onDiscard } = props;
4300
- const isPhone = useBreakpoint("sm");
4301
- const mobileMode = mobile ?? (position === "left" ? "panel" : "sheet");
4302
- const guard = useDirtyGuard(dirty, onClose, onDiscard);
4303
- const confirm = /* @__PURE__ */ jsx(DiscardConfirm, { open: guard.confirming, onKeep: guard.keep, onDiscard: guard.discard });
4304
- const [actionsMap, setActionsMap] = useState(/* @__PURE__ */ new Map());
4305
- const registerActions = useCallback((formId, acts) => {
4306
- setActionsMap((prev) => new Map(prev).set(formId, acts));
4307
- }, []);
4308
- const unregisterActions = useCallback((formId) => {
4309
- setActionsMap((prev) => {
4310
- const next = new Map(prev);
4311
- next.delete(formId);
4312
- return next;
4313
- });
4314
- }, []);
4315
- const registeredActions = actionsMap.size > 0 ? /* @__PURE__ */ jsx(Fragment, { children: Array.from(actionsMap.values()) }) : null;
4316
- const effectiveFooter = footer ?? registeredActions;
4317
- const footerCtx = useMemo(
4318
- () => ({ registerActions, unregisterActions }),
4319
- [registerActions, unregisterActions]
4320
- );
4321
- if (isPhone && mobileMode === "sheet") {
4322
- return /* @__PURE__ */ jsx(DrawerFooterCtx, { value: footerCtx, children: /* @__PURE__ */ jsx(Sheet, { open, onClose: guard.requestClose, title, footer: effectiveFooter, confirm, container, flush, snapPoints, keepMounted, id, labelledBy, children }) });
4323
- }
4324
- return /* @__PURE__ */ jsx(DrawerFooterCtx, { value: footerCtx, children: /* @__PURE__ */ jsx(DrawerPanel, { ...props, footer: effectiveFooter, onClose: guard.requestClose, confirm }) });
4325
- }
4326
- 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 }) {
4327
- const nonModal = !modal;
4328
- const [chosen, remember] = useRememberedWidth(id ?? labelledBy);
4329
- const canResize = nonModal && resizable && position !== "bottom";
4330
- const effectiveWidth = canResize && chosen !== null ? `${chosen}px` : width;
4331
- const minPx = pxOf(width) ?? 320;
4332
- const maxPx = () => pxOf(maxWidth) ?? defaultMaxWidth(container instanceof Element ? container : null);
4333
- const titleId = useId();
4334
- const { ref: titleOverflowRef, isTruncated: titleTruncated } = useTextOverflow({ lines: 1 });
4335
- const isMobile = useBreakpoint("sm");
4336
- const panelRef = useRef(null);
4337
- const [mounted, setMounted] = useState(open);
4338
- const [hidden, setHidden] = useState(!open);
4339
- const onCloseRef = useLatest(onClose);
4340
- if (open && !mounted) setMounted(true);
4341
- if (open && hidden) setHidden(false);
4342
- const isTopLayer = useOverlayLayer(open);
4343
- useEffect(() => {
4344
- if (!open || nonModal) return;
4345
- const onKeyDown = (e) => {
4346
- if (e.key === "Escape" && isTopLayer()) onCloseRef.current();
4347
- };
4348
- document.addEventListener("keydown", onKeyDown);
4349
- if (!container) lockBodyScroll();
4350
- return () => {
4351
- document.removeEventListener("keydown", onKeyDown);
4352
- if (!container) unlockBodyScroll();
4353
- };
4354
- }, [open, nonModal, container, onCloseRef, isTopLayer]);
4355
- useReturnFocus(open && !nonModal);
4356
- useFocusTrap(panelRef, open && !topOffset && !nonModal, isTopLayer);
4357
- useEffect(() => {
4358
- if (!open || !mounted || nonModal) return;
4359
- const raf = requestAnimationFrame(() => focusInitial(panelRef.current));
4360
- return () => cancelAnimationFrame(raf);
4361
- }, [open, mounted, nonModal]);
4362
- useNonModalDialog(panelRef, open && mounted && nonModal, onClose, isTopLayer);
4363
- useCloseSettle(open, panelRef, () => {
4364
- if (keepMounted) setHidden(true);
4365
- else setMounted(false);
4366
- });
4367
- if (!mounted) return null;
4368
- const slide = slideClasses[position];
4369
- const isHorizontal = position === "left" || position === "right";
4370
- const positionClass = container ? "absolute" : "fixed";
4371
- return createPortal(
4372
- /* @__PURE__ */ jsxs(
4373
- "div",
4374
- {
4375
- hidden: keepMounted && hidden ? true : void 0,
4376
- className: cn(
4377
- `${positionClass} transition-opacity duration-[var(--ds-transition-speed)]`,
4378
- // A non-modal panel never intercepts the page: the wrapper is inert to the
4379
- // pointer and only the panel itself accepts events.
4380
- nonModal ? "z-[var(--ds-z-overlay)] pointer-events-none" : cn("z-[var(--ds-z-modal)]", open ? "pointer-events-auto" : "pointer-events-none"),
4381
- topOffset ? "left-0 right-0 bottom-0" : "inset-0"
4382
- ),
4383
- style: topOffset ? { top: topOffset } : void 0,
4384
- children: [
4385
- !nonModal && /* @__PURE__ */ jsx(
4386
- "div",
4387
- {
4388
- className: cn(
4389
- "absolute inset-0 transition-opacity duration-[var(--ds-transition-speed)]",
4390
- 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))]"),
4391
- open ? "opacity-100" : "opacity-0"
4392
- ),
4393
- onClick: onClose
4394
- }
4395
- ),
4396
- /* @__PURE__ */ jsxs(
4397
- "div",
4398
- {
4399
- ref: panelRef,
4400
- id,
4401
- role: "dialog",
4402
- "aria-modal": nonModal ? void 0 : "true",
4403
- "aria-labelledby": title ? titleId : labelledBy,
4404
- style: {
4405
- containerType: "inline-size",
4406
- width: isHorizontal ? effectiveWidth : void 0,
4407
- maxWidth: isHorizontal ? "100%" : void 0,
4408
- height: isMobile ? "100%" : void 0,
4409
- maxHeight: !isMobile && !isHorizontal ? "80vh" : void 0
4410
- },
4411
- className: cn(
4412
- `${positionClass} z-10 flex flex-col`,
4413
- nonModal && "pointer-events-auto",
4414
- "bg-[var(--ds-bg-surface)] text-[var(--ds-text-primary)]",
4415
- GLASS_OVERLAY,
4416
- // flush drops the edge border for children that draw their own (the mobile
4417
- // menu's Sidebar); a non-modal panel has no backdrop to separate it from
4418
- // the page, so it always keeps the border.
4419
- (!flush || nonModal) && borderByPosition[position],
4420
- "transition-transform duration-[var(--ds-transition-speed)] ease-[var(--ds-transition-ease)]",
4421
- isHorizontal && position === "left" && "rounded-r-[var(--ds-radius-lg)] overflow-hidden",
4422
- isHorizontal && position === "right" && "rounded-l-[var(--ds-radius-lg)] overflow-hidden",
4423
- !isHorizontal && "rounded-t-[var(--ds-radius-lg)] overflow-hidden",
4424
- (lightBackdrop || nonModal) && "shadow-[var(--ds-shadow-overlay)]",
4425
- slide.panel,
4426
- open ? slide.open : slide.closed
4427
- ),
4428
- children: [
4429
- canResize && open && /* @__PURE__ */ jsx(
4430
- ResizeEdge,
4431
- {
4432
- edge: position === "right" ? "left" : "right",
4433
- width: chosen ?? minPx,
4434
- min: minPx,
4435
- max: maxPx,
4436
- defaultWidth: minPx,
4437
- onResize: (w) => remember(w === minPx ? null : w),
4438
- label: "Resize panel"
4439
- }
4440
- ),
4441
- title && /* @__PURE__ */ jsxs(
4442
- "div",
4443
- {
4444
- className: "flex items-center justify-between shrink-0 border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
4445
- style: {
4446
- padding: "var(--ds-space-3) var(--ds-space-6)",
4447
- minHeight: "calc(var(--ds-space-unit) * 6 + 1.75rem + 2px + var(--ds-border-width))"
4448
- },
4449
- children: [
4450
- 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 }),
4451
- /* @__PURE__ */ jsx(
4452
- "button",
4453
- {
4454
- onClick: onClose,
4455
- ...{ [OVERLAY_CLOSE_ATTR]: "" },
4456
- className: "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer",
4457
- "aria-label": "Close",
4458
- children: /* @__PURE__ */ jsx(Icon, { name: "close", size: 18 })
4459
- }
4460
- )
4461
- ]
4462
- }
4463
- ),
4464
- /* @__PURE__ */ jsx(
4465
- "div",
4466
- {
4467
- className: "flex-1 overflow-y-auto",
4468
- style: flush ? void 0 : { padding: "var(--ds-space-5)" },
4469
- children
4470
- }
4471
- ),
4472
- confirm,
4473
- footer && /* @__PURE__ */ jsx(
4474
- "div",
4475
- {
4476
- className: "shrink-0 flex items-center justify-end gap-ds-2 border-t-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
4477
- style: { padding: "var(--ds-space-3) var(--ds-space-5)" },
4478
- children: footer
4479
- }
4480
- )
4481
- ]
4482
- }
4483
- )
4484
- ]
4485
- }
4486
- ),
4487
- container ?? document.body
4488
- );
4489
- }
4490
- function stampFormId(node, formId) {
4491
- return Children.map(node, (child) => {
4492
- if (!isValidElement(child)) return child;
4493
- if (child.type === Fragment$1) return stampFormId(child.props.children, formId);
4494
- return cloneElement(child, { form: formId });
4495
- });
4496
- }
4497
- var FormCtx = createContext(null);
4498
- function useFormContext() {
4499
- const ctx = useContext(FormCtx);
4500
- if (!ctx) throw new Error("useFormContext must be used within FormWrapper");
4501
- return ctx;
4502
- }
4503
- function collectInvalidFields(form) {
4504
- const out = [];
4505
- for (const el of Array.from(form.querySelectorAll("[aria-invalid='true']"))) {
4506
- const id = el.id || "";
4507
- const label = id && form.querySelector(`label[for="${CSS.escape(id)}"]`)?.textContent?.replace(/\*\s*$/, "").trim() || el.getAttribute("aria-label") || el.closest("[data-field]")?.querySelector("label")?.textContent?.trim() || id || "A field";
4508
- const described = (el.getAttribute("aria-describedby") ?? "").split(/\s+/).filter(Boolean);
4509
- const message = described.map((d) => document.getElementById(d)?.textContent?.trim()).find((t) => t && /error|invalid|required|must|enter|choose|select|please/i.test(t)) ?? described.map((d) => document.getElementById(d)?.textContent?.trim()).filter(Boolean).pop() ?? "Needs attention";
4510
- out.push({ id, label, message });
4511
- }
4512
- return out;
4513
- }
4514
- function FormWrapper({ onSubmit, validationMode = "onSubmit", children, actions, actionsAlign = "end", focusFirstError = true, errorSummary = true, className, ref }) {
4515
- const [errors, setErrors] = useState({});
4516
- const [submitted, setSubmitted] = useState(false);
4517
- const [submitCount, setSubmitCount] = useState(0);
4518
- const [summary, setSummary] = useState([]);
4519
- const errorsRef = useLatest(errors);
4520
- const formRef = useRef(null);
4521
- const setRefs = useCallback((node) => {
4522
- formRef.current = node;
4523
- if (typeof ref === "function") ref(node);
4524
- else if (ref) ref.current = node;
4525
- }, [ref]);
4526
- const inCard = useContext(CardSurfaceCtx);
4527
- const drawerFooter = useContext(DrawerFooterCtx);
4528
- const formId = useId();
4529
- useEffect(() => {
4530
- if (!drawerFooter || !actions) return;
4531
- drawerFooter.registerActions(formId, stampFormId(actions, formId));
4532
- return () => drawerFooter.unregisterActions(formId);
4533
- }, [drawerFooter, actions, formId]);
4534
- const pendingSubmit = useRef(false);
4535
- const pendingFocus = useRef(false);
4536
- const onSubmitRef = useLatest(onSubmit);
4537
- useEffect(() => {
4538
- if (pendingSubmit.current && submitted) {
4539
- pendingSubmit.current = false;
4540
- onSubmitRef.current(errorsRef.current);
3459
+ function FormWrapper({ onSubmit, validationMode = "onSubmit", children, actions, actionsAlign = "end", focusFirstError = true, errorSummary = true, className, ref }) {
3460
+ const [errors, setErrors] = useState({});
3461
+ const [submitted, setSubmitted] = useState(false);
3462
+ const [submitCount, setSubmitCount] = useState(0);
3463
+ const [summary, setSummary] = useState([]);
3464
+ const errorsRef = useLatest(errors);
3465
+ const formRef = useRef(null);
3466
+ const setRefs = useCallback((node) => {
3467
+ formRef.current = node;
3468
+ if (typeof ref === "function") ref(node);
3469
+ else if (ref) ref.current = node;
3470
+ }, [ref]);
3471
+ const drawerFooter = useContext(DrawerFooterCtx);
3472
+ const formId = useId();
3473
+ useEffect(() => {
3474
+ if (!drawerFooter || !actions) return;
3475
+ drawerFooter.registerActions(formId, stampFormId(actions, formId));
3476
+ return () => drawerFooter.unregisterActions(formId);
3477
+ }, [drawerFooter, actions, formId]);
3478
+ const pendingSubmit = useRef(false);
3479
+ const pendingFocus = useRef(false);
3480
+ const onSubmitRef = useLatest(onSubmit);
3481
+ useEffect(() => {
3482
+ if (pendingSubmit.current && submitted) {
3483
+ pendingSubmit.current = false;
3484
+ onSubmitRef.current(errorsRef.current);
4541
3485
  }
4542
3486
  }, [submitted, submitCount, errors, errorsRef, onSubmitRef]);
4543
3487
  const settle = useCallback(() => {
@@ -4603,27 +3547,8 @@ function FormWrapper({ onSubmit, validationMode = "onSubmit", children, actions,
4603
3547
  ": ",
4604
3548
  f.message
4605
3549
  ] }, f.id || f.label)) }) }) : null;
4606
- let actionsNode = null;
4607
- if (actions && !drawerFooter) {
4608
- if (inCard) {
4609
- actionsNode = /* @__PURE__ */ jsx(
4610
- "div",
4611
- {
4612
- "data-form-actions": "",
4613
- "data-form-actions-surface": "card",
4614
- className: cn(
4615
- "border-t-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
4616
- "flex flex-wrap items-center gap-ds-2 pt-ds-4 mt-ds-6",
4617
- actionsAlign === "end" ? "justify-end" : "justify-start"
4618
- ),
4619
- children: actions
4620
- }
4621
- );
4622
- } else {
4623
- actionsNode = /* @__PURE__ */ jsx("div", { "data-form-actions": "", className: cn("flex flex-wrap items-center gap-ds-2 pt-ds-4 mt-ds-2", actionsAlign === "end" ? "justify-end" : "justify-start"), children: actions });
4624
- }
4625
- }
4626
- return /* @__PURE__ */ jsx(FormCtx.Provider, { value: { errors, setError, clearErrors, validationMode, submitted }, children: /* @__PURE__ */ jsxs("form", { ref: setRefs, id: drawerFooter ? formId : void 0, onSubmit: handleSubmit, onFocusCapture: userActed, onInputCapture: userActed, className, noValidate: true, children: [
3550
+ const actionsNode = actions && !drawerFooter ? /* @__PURE__ */ jsx("div", { "data-form-actions": "", className: cn("flex flex-wrap items-center gap-ds-2 pt-ds-2", actionsAlign === "end" ? "justify-end" : "justify-start"), children: actions }) : null;
3551
+ return /* @__PURE__ */ jsx(FormCtx.Provider, { value: { errors, setError, clearErrors, validationMode, submitted }, children: /* @__PURE__ */ jsxs("form", { ref: setRefs, id: drawerFooter ? formId : void 0, onSubmit: handleSubmit, onFocusCapture: userActed, onInputCapture: userActed, className: cn("flex flex-col gap-ds-5", className), noValidate: true, children: [
4627
3552
  summaryNode && /* @__PURE__ */ jsx("div", { "data-form-error-summary": "", children: summaryNode }),
4628
3553
  children,
4629
3554
  actionsNode
@@ -4926,7 +3851,7 @@ var StatusDot = forwardRef(function StatusDot2({
4926
3851
  }
4927
3852
  );
4928
3853
  });
4929
- var sizeStyles3 = {
3854
+ var sizeStyles2 = {
4930
3855
  sm: "h-8 w-8 text-xs",
4931
3856
  md: "h-10 w-10 text-sm",
4932
3857
  lg: "h-12 w-12 text-base"
@@ -4958,12 +3883,13 @@ function Avatar({ initials, src, alt, size = "md", status, color = "auto", class
4958
3883
  const base = cn(
4959
3884
  "inline-flex items-center justify-center font-medium shrink-0",
4960
3885
  "rounded-[var(--ds-radius-full)]",
4961
- sizeStyles3[size],
3886
+ sizeStyles2[size],
4962
3887
  className
4963
3888
  );
4964
3889
  const avatarElement = src ? /* @__PURE__ */ jsx(
4965
3890
  "img",
4966
3891
  {
3892
+ "data-avatar": "",
4967
3893
  src,
4968
3894
  alt: alt ?? initials ?? "",
4969
3895
  className: cn(base, "object-cover")
@@ -4971,6 +3897,7 @@ function Avatar({ initials, src, alt, size = "md", status, color = "auto", class
4971
3897
  ) : /* @__PURE__ */ jsx(
4972
3898
  "div",
4973
3899
  {
3900
+ "data-avatar": "",
4974
3901
  className: cn(
4975
3902
  base,
4976
3903
  colorStyles(color, alt ?? initials ?? ""),
@@ -5178,34 +4105,326 @@ function MentionInput({
5178
4105
  ),
5179
4106
  onMouseDown: (e) => {
5180
4107
  e.preventDefault();
5181
- insertMention(s);
5182
- },
5183
- onMouseEnter: () => setActiveIndex(i),
5184
- children: [
5185
- /* @__PURE__ */ jsx(
5186
- Avatar,
5187
- {
5188
- src: s.avatar,
5189
- initials: s.label.charAt(0).toUpperCase(),
5190
- alt: s.label,
5191
- size: "sm",
5192
- className: "!h-6 !w-6 !text-2xs"
5193
- }
5194
- ),
5195
- /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
5196
- /* @__PURE__ */ jsx("div", { className: "font-medium text-[var(--ds-text-primary)] truncate", children: s.label }),
5197
- s.description && /* @__PURE__ */ jsx("div", { className: "text-xs text-[var(--ds-text-muted)] truncate", children: s.description })
5198
- ] })
5199
- ]
4108
+ insertMention(s);
4109
+ },
4110
+ onMouseEnter: () => setActiveIndex(i),
4111
+ children: [
4112
+ /* @__PURE__ */ jsx(
4113
+ Avatar,
4114
+ {
4115
+ src: s.avatar,
4116
+ initials: s.label.charAt(0).toUpperCase(),
4117
+ alt: s.label,
4118
+ size: "sm",
4119
+ className: "!h-6 !w-6 !text-2xs"
4120
+ }
4121
+ ),
4122
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
4123
+ /* @__PURE__ */ jsx("div", { className: "font-medium text-[var(--ds-text-primary)] truncate", children: s.label }),
4124
+ s.description && /* @__PURE__ */ jsx("div", { className: "text-xs text-[var(--ds-text-muted)] truncate", children: s.description })
4125
+ ] })
4126
+ ]
4127
+ },
4128
+ s.id
4129
+ ))
4130
+ }
4131
+ ),
4132
+ document.body
4133
+ )
4134
+ ] });
4135
+ }
4136
+ var CardCtx = createContext(false);
4137
+ var CardSurfaceCtx = createContext(false);
4138
+ var variantStyles3 = {
4139
+ default: `bg-[var(--ds-bg-surface)] border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)] ${GLASS_SURFACE}`,
4140
+ featured: [
4141
+ `bg-[var(--ds-bg-surface)] border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)] ${GLASS_SURFACE}`,
4142
+ "[transition:var(--ds-transition-ambient)]",
4143
+ "hover:scale-[var(--ds-ambient-hover-scale)] hover:-translate-y-[var(--ds-ambient-hover-lift)]"
4144
+ ].join(" "),
4145
+ interactive: [
4146
+ `bg-[var(--ds-bg-surface)] cursor-pointer ${GLASS_SURFACE}`,
4147
+ "border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
4148
+ "[transition:var(--ds-transition-interactive)]",
4149
+ "hover:border-[var(--ds-accent-primary)] hover:shadow-[var(--ds-shadow-card),var(--ds-glass-inset)] hover:bg-[var(--ds-bg-surface-elevated)]",
4150
+ "hover:scale-[var(--_card-scale)] hover:-translate-y-[var(--ds-hover-lift)]"
4151
+ ].join(" "),
4152
+ outline: `bg-[var(--ds-bg-surface)] border-[length:var(--ds-border-width)] border-[var(--ds-border-default)] ${GLASS_SURFACE}`,
4153
+ ghost: "bg-transparent border-none shadow-none"
4154
+ };
4155
+ var INTERACTIVE_SCALE_CAP = { "--_card-scale": "min(var(--ds-hover-scale), 1.04)" };
4156
+ function RadioIndicator({ checked, disabled }) {
4157
+ return /* @__PURE__ */ jsx(
4158
+ "span",
4159
+ {
4160
+ className: cn(
4161
+ "flex items-center justify-center shrink-0 rounded-[var(--ds-radius-full)]",
4162
+ "h-[18px] w-[18px]",
4163
+ "border-[length:var(--ds-border-width)] bg-[var(--ds-input-bg)]",
4164
+ "transition-colors duration-[var(--ds-transition-speed)]",
4165
+ disabled && "opacity-disabled",
4166
+ checked ? "border-[var(--ds-check-border)]" : "border-[var(--ds-input-border)]"
4167
+ ),
4168
+ children: /* @__PURE__ */ jsx(
4169
+ "span",
4170
+ {
4171
+ className: cn(
4172
+ "h-2.5 w-2.5 rounded-[var(--ds-radius-full)] transition-transform duration-[var(--ds-transition-speed)]",
4173
+ checked ? "bg-[var(--ds-accent-primary)] [background-image:var(--ds-gradient)] scale-100" : "scale-0"
4174
+ )
4175
+ }
4176
+ )
4177
+ }
4178
+ );
4179
+ }
4180
+ function CheckboxIndicator({ checked, indeterminate, disabled }) {
4181
+ const filled = checked || indeterminate;
4182
+ return /* @__PURE__ */ jsx(
4183
+ "span",
4184
+ {
4185
+ className: cn(
4186
+ "flex items-center justify-center shrink-0",
4187
+ "h-[18px] w-[18px]",
4188
+ "rounded-[calc(var(--ds-radius-sm)*0.6)]",
4189
+ "border-[2px] transition-all duration-[var(--ds-transition-speed)]",
4190
+ disabled && "opacity-disabled",
4191
+ filled ? "bg-[var(--ds-check-bg)] [background-image:var(--ds-gradient)] border-[var(--ds-check-border)]" : "bg-[var(--ds-input-bg)] border-[var(--ds-input-border)]"
4192
+ ),
4193
+ children: /* @__PURE__ */ jsx(
4194
+ "svg",
4195
+ {
4196
+ width: "12",
4197
+ height: "12",
4198
+ viewBox: "0 0 12 12",
4199
+ fill: "none",
4200
+ className: cn(
4201
+ "text-[var(--ds-check-fg)] transition-opacity",
4202
+ filled ? "opacity-100" : "opacity-0"
4203
+ ),
4204
+ children: indeterminate ? /* @__PURE__ */ jsx("path", { d: "M3 6H9", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round" }) : /* @__PURE__ */ jsx("path", { d: "M2.5 6L5 8.5L9.5 3.5", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round" })
4205
+ }
4206
+ )
4207
+ }
4208
+ );
4209
+ }
4210
+ function ToggleIndicator({ checked, disabled }) {
4211
+ return /* @__PURE__ */ jsx(
4212
+ "span",
4213
+ {
4214
+ className: cn(
4215
+ "relative inline-flex shrink-0 items-center overflow-hidden",
4216
+ "border-[length:var(--ds-border-width)] rounded-[var(--ds-radius-full)]",
4217
+ "transition-colors duration-[var(--ds-transition-speed)]",
4218
+ disabled && "opacity-disabled",
4219
+ checked ? "bg-[var(--ds-accent-primary)] border-transparent" : "bg-[var(--ds-bg-muted)] border-[var(--ds-border-default)]"
4220
+ ),
4221
+ style: { width: 44, height: 24 },
4222
+ children: /* @__PURE__ */ jsx(
4223
+ "span",
4224
+ {
4225
+ className: "pointer-events-none inline-block shrink-0 bg-[var(--ds-bg-surface)] rounded-[var(--ds-radius-full)] transition-transform",
4226
+ style: {
4227
+ width: 16,
4228
+ height: 16,
4229
+ transform: checked ? "translateX(calc(25px - var(--ds-border-width)))" : "translateX(calc(1px + var(--ds-border-width)))"
4230
+ }
4231
+ }
4232
+ )
4233
+ }
4234
+ );
4235
+ }
4236
+ var Card = forwardRef(function Card2({
4237
+ variant = "default",
4238
+ loading = false,
4239
+ selected,
4240
+ selectable,
4241
+ selectType,
4242
+ onSelectedChange,
4243
+ name,
4244
+ indeterminate,
4245
+ disabled = false,
4246
+ checkable = false,
4247
+ className,
4248
+ children,
4249
+ onClick,
4250
+ style,
4251
+ ...props
4252
+ }, ref) {
4253
+ const isSelectable = selectable || !!selectType;
4254
+ const isToggle = selectType === "toggle";
4255
+ const hasSelectType = !!selectType;
4256
+ const handleClick = isSelectable && !disabled ? (e) => {
4257
+ onSelectedChange?.(!selected);
4258
+ onClick?.(e);
4259
+ } : onClick;
4260
+ const handleKeyDown = isSelectable && !disabled ? (e) => {
4261
+ if (e.key === "Enter" || e.key === " ") {
4262
+ e.preventDefault();
4263
+ onSelectedChange?.(!selected);
4264
+ }
4265
+ } : void 0;
4266
+ const handleCheckableClick = (e) => {
4267
+ e.stopPropagation();
4268
+ onSelectedChange?.(!selected);
4269
+ };
4270
+ const role = selectType === "radio" ? "radio" : selectType === "checkbox" ? "checkbox" : selectType === "toggle" ? "switch" : isSelectable ? "button" : void 0;
4271
+ const ariaAttr = selectType === "radio" || selectType === "checkbox" || selectType === "toggle" ? { "aria-checked": !!selected } : isSelectable ? { "aria-pressed": !!selected } : {};
4272
+ const showRing = isSelectable && !isToggle && !hasSelectType && selected;
4273
+ const indicator = selectType === "radio" ? /* @__PURE__ */ jsx(RadioIndicator, { checked: !!selected, disabled }) : selectType === "checkbox" ? /* @__PURE__ */ jsx(CheckboxIndicator, { checked: !!selected, indeterminate, disabled }) : selectType === "toggle" ? /* @__PURE__ */ jsx(ToggleIndicator, { checked: !!selected, disabled }) : null;
4274
+ const defaultCheckBadge = !hasSelectType && !checkable && selected && !loading ? /* @__PURE__ */ jsx("div", { className: "absolute top-ds-3 right-ds-3 w-5 h-5 rounded-[var(--ds-radius-full)] bg-[var(--ds-accent-primary)] flex items-center justify-center", children: /* @__PURE__ */ jsx(Icon, { name: "check", size: 12, className: "text-[var(--ds-text-on-primary)]" }) }) : null;
4275
+ return /* @__PURE__ */ jsxs(
4276
+ "div",
4277
+ {
4278
+ ref,
4279
+ "data-card": "",
4280
+ role,
4281
+ tabIndex: isSelectable ? 0 : void 0,
4282
+ "aria-disabled": isSelectable && disabled ? true : void 0,
4283
+ ...name && selectType === "radio" ? { "data-radio-group": name } : {},
4284
+ style: variant === "interactive" ? { ...INTERACTIVE_SCALE_CAP, ...style } : style,
4285
+ className: cn(
4286
+ "rounded-[var(--ds-radius-md)] flex flex-col relative",
4287
+ variantStyles3[variant],
4288
+ isSelectable && !disabled && "cursor-pointer select-none transition-all duration-[var(--ds-transition-speed)] ease-[var(--ds-transition-ease)]",
4289
+ isSelectable && !disabled && !selected && "hover:border-[var(--ds-accent-primary)]",
4290
+ isSelectable && "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
4291
+ (showRing || hasSelectType && selected) && ACTIVE_RING,
4292
+ hasSelectType && selected && "bg-[var(--ds-accent-subtle)]",
4293
+ disabled && "opacity-disabled cursor-not-allowed",
4294
+ className
4295
+ ),
4296
+ onClick: handleClick,
4297
+ onKeyDown: handleKeyDown,
4298
+ ...ariaAttr,
4299
+ ...props,
4300
+ children: [
4301
+ defaultCheckBadge,
4302
+ indicator && /* @__PURE__ */ jsx("div", { className: "absolute top-ds-3 right-ds-3 z-[1]", children: indicator }),
4303
+ checkable && /* @__PURE__ */ jsx(
4304
+ "div",
4305
+ {
4306
+ className: "absolute top-ds-3 left-ds-3 z-[1] cursor-pointer",
4307
+ onClick: handleCheckableClick,
4308
+ role: "checkbox",
4309
+ "aria-checked": !!selected,
4310
+ tabIndex: 0,
4311
+ onKeyDown: (e) => {
4312
+ if (e.key === "Enter" || e.key === " ") {
4313
+ e.preventDefault();
4314
+ e.stopPropagation();
4315
+ onSelectedChange?.(!selected);
4316
+ }
5200
4317
  },
5201
- s.id
5202
- ))
5203
- }
5204
- ),
5205
- document.body
4318
+ children: /* @__PURE__ */ jsx(CheckboxIndicator, { checked: !!selected })
4319
+ }
4320
+ ),
4321
+ /* @__PURE__ */ jsx(CardSurfaceCtx, { value: true, children: /* @__PURE__ */ jsx(CardCtx, { value: checkable, children: loading ? /* @__PURE__ */ jsxs("div", { className: "space-y-ds-3", children: [
4322
+ /* @__PURE__ */ jsx(Skeleton, { variant: "custom", className: "h-4 w-1/3" }),
4323
+ /* @__PURE__ */ jsx(Skeleton, { variant: "custom", className: "h-3 w-full" }),
4324
+ /* @__PURE__ */ jsx(Skeleton, { variant: "custom", className: "h-3 w-2/3" })
4325
+ ] }) : children }) })
4326
+ ]
4327
+ }
4328
+ );
4329
+ });
4330
+ var CardHeader = forwardRef(function CardHeader2({ className, children, actions, icon, menuItems, ...props }, ref) {
4331
+ const isCheckable = useContext(CardCtx);
4332
+ const hasActions = actions || menuItems;
4333
+ const pl = isCheckable ? "pl-ds-10" : "pl-ds-5";
4334
+ const actionsBlock = hasActions ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-2 shrink-0", children: [
4335
+ actions,
4336
+ menuItems && /* @__PURE__ */ jsx(
4337
+ DropdownMenu,
4338
+ {
4339
+ trigger: /* @__PURE__ */ jsx(
4340
+ "button",
4341
+ {
4342
+ type: "button",
4343
+ className: cn(
4344
+ "inline-flex items-center justify-center h-8 w-8 rounded-[var(--ds-radius-md)]",
4345
+ "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] hover:bg-[var(--ds-bg-muted)]",
4346
+ "transition-colors duration-[var(--ds-transition-speed)]"
4347
+ ),
4348
+ onClick: (e) => e.stopPropagation(),
4349
+ children: /* @__PURE__ */ jsx(Icon, { name: "more-horizontal", size: 16 })
4350
+ }
4351
+ ),
4352
+ items: menuItems
4353
+ }
5206
4354
  )
4355
+ ] }) : null;
4356
+ const iconBlock = icon ? /* @__PURE__ */ jsx("span", { className: "shrink-0 h-10 w-10 rounded-[var(--ds-radius-md)] bg-[var(--ds-bg-muted)] flex items-center justify-center mb-ds-1", children: /* @__PURE__ */ jsx(Icon, { name: icon, size: 20, className: "text-[var(--ds-text-secondary)]" }) }) : null;
4357
+ if (hasActions) {
4358
+ return /* @__PURE__ */ jsx("div", { ref, className: cn(pl, "pr-ds-5 pt-ds-4 last:pb-ds-4", className), ...props, children: /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-ds-4", children: [
4359
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col space-y-ds-1.5 min-w-0", children: [
4360
+ iconBlock,
4361
+ children
4362
+ ] }),
4363
+ actionsBlock
4364
+ ] }) });
4365
+ }
4366
+ return /* @__PURE__ */ jsxs("div", { ref, className: cn("flex flex-col space-y-ds-1.5 pr-ds-5 pt-ds-4 last:pb-ds-4", pl, className), ...props, children: [
4367
+ iconBlock,
4368
+ children
5207
4369
  ] });
5208
- }
4370
+ });
4371
+ var cardLineClampClasses = {
4372
+ 1: "truncate",
4373
+ 2: "line-clamp-2",
4374
+ 3: "line-clamp-3",
4375
+ 4: "line-clamp-4",
4376
+ 5: "line-clamp-5",
4377
+ 6: "line-clamp-6"
4378
+ };
4379
+ var CardTitle = forwardRef(function CardTitle2({ className, children, lines = 2, ...props }, forwardedRef) {
4380
+ const { ref: overflowRef, isTruncated } = useTextOverflow({ lines, disabled: lines === 0 });
4381
+ const lineClampClass = lines > 0 ? cardLineClampClasses[lines] || `line-clamp-[${lines}]` : "";
4382
+ const heading = /* @__PURE__ */ jsx(
4383
+ "h3",
4384
+ {
4385
+ ref: (node) => {
4386
+ overflowRef(lines > 0 ? node : null);
4387
+ if (typeof forwardedRef === "function") forwardedRef(node);
4388
+ else if (forwardedRef) forwardedRef.current = node;
4389
+ },
4390
+ className: cn("font-[family-name:var(--ds-font-display)] text-lg font-[var(--ds-weight-heading)] text-[var(--ds-text-primary)]", lines > 0 && lineClampClass, className),
4391
+ ...props,
4392
+ children
4393
+ }
4394
+ );
4395
+ if (!isTruncated || lines === 0) return heading;
4396
+ return /* @__PURE__ */ jsx(Tooltip, { content: children, children: heading });
4397
+ });
4398
+ var CardDescription = forwardRef(function CardDescription2({ className, children, lines = 2, ...props }, forwardedRef) {
4399
+ const { ref: overflowRef, isTruncated } = useTextOverflow({ lines, disabled: lines === 0 });
4400
+ const lineClampClass = lines > 0 ? cardLineClampClasses[lines] || `line-clamp-[${lines}]` : "";
4401
+ const el = /* @__PURE__ */ jsx(
4402
+ "p",
4403
+ {
4404
+ ref: (node) => {
4405
+ if (lines > 0) overflowRef(node);
4406
+ if (typeof forwardedRef === "function") forwardedRef(node);
4407
+ else if (forwardedRef) forwardedRef.current = node;
4408
+ },
4409
+ className: cn("text-sm text-[var(--ds-text-muted)]", lines > 0 && lineClampClass, className),
4410
+ ...props,
4411
+ children
4412
+ }
4413
+ );
4414
+ if (!isTruncated || lines === 0) return el;
4415
+ return /* @__PURE__ */ jsx(Tooltip, { content: children, children: el });
4416
+ });
4417
+ var CardContent = forwardRef(function CardContent2({ className, children, ...props }, ref) {
4418
+ const isCheckable = useContext(CardCtx);
4419
+ return /* @__PURE__ */ jsx("div", { ref, className: cn("flex-1 pr-ds-5 py-ds-4", isCheckable ? "pl-ds-10" : "pl-ds-5", className), ...props, children });
4420
+ });
4421
+ var CardFooter = forwardRef(function CardFooter2({ className, children, ...props }, ref) {
4422
+ const isCheckable = useContext(CardCtx);
4423
+ return (
4424
+ // a footer that holds only buttons puts them at the end (round 11: a lone Save sat on the left); text keeps the start
4425
+ /* @__PURE__ */ jsx("div", { ref, className: cn("flex items-center gap-ds-2 pr-ds-5 pb-ds-4 pt-ds-2 mt-auto min-w-0 [&:not(:has(>:not(button)))]:justify-end", isCheckable ? "pl-ds-10" : "pl-ds-5", className), ...props, children })
4426
+ );
4427
+ });
5209
4428
  function TruncLabel({ children }) {
5210
4429
  const { ref, isTruncated } = useTextOverflow({ lines: 1 });
5211
4430
  const el = /* @__PURE__ */ jsx("span", { ref, className: "text-xs font-medium text-[var(--ds-text-muted)] uppercase tracking-wider truncate min-w-0", children });
@@ -5318,7 +4537,7 @@ function DataList({ items, columns = 1, className }) {
5318
4537
  }
5319
4538
  );
5320
4539
  }
5321
- var sizeStyles4 = {
4540
+ var sizeStyles3 = {
5322
4541
  sm: { gap: "gap-ds-0.5", label: "text-[length:var(--ds-font-size-xs)]", value: "text-[length:var(--ds-font-size-xs)]", iconSize: 12, py: "py-ds-1.5" },
5323
4542
  md: { gap: "gap-ds-1", label: "text-[length:var(--ds-font-size-sm)]", value: "text-[length:var(--ds-font-size-sm)]", iconSize: 14, py: "py-ds-2.5" },
5324
4543
  lg: { gap: "gap-ds-1.5", label: "text-[length:var(--ds-font-size-base)]", value: "text-[length:var(--ds-font-size-base)]", iconSize: 16, py: "py-ds-3" }
@@ -5348,7 +4567,7 @@ var DescriptionList = forwardRef(function DescriptionList2({
5348
4567
  stackBelow: stackBelowProp,
5349
4568
  className
5350
4569
  }, ref) {
5351
- const s = sizeStyles4[size];
4570
+ const s = sizeStyles3[size];
5352
4571
  const stackBelow = stackBelowProp ?? (columns > 1 ? "sm" : void 0);
5353
4572
  if (orientation === "vertical") {
5354
4573
  return /* @__PURE__ */ jsx(
@@ -5400,7 +4619,7 @@ var DescriptionList = forwardRef(function DescriptionList2({
5400
4619
  className: cn(
5401
4620
  "grid",
5402
4621
  columnClasses[columns],
5403
- "gap-x-ds-6",
4622
+ "gap-x-ds-8",
5404
4623
  stackBelow && stackBelow !== "none" && stackBreakpoints[stackBelow],
5405
4624
  dividers && "divide-y-[length:var(--ds-border-width)] divide-[var(--ds-border-subtle)]",
5406
4625
  className
@@ -5622,7 +4841,7 @@ function Table({
5622
4841
  if (data.length === 0 && emptyState) {
5623
4842
  return /* @__PURE__ */ jsx("div", { ref, className: cn("py-ds-10 text-center text-sm text-[var(--ds-text-muted)]", className), children: emptyState });
5624
4843
  }
5625
- return /* @__PURE__ */ jsx("div", { ref, className: cn("flex flex-col gap-ds-3 overflow-y-auto", className), children: sortedWithIndices.map(({ row, originalIdx }, i) => {
4844
+ return /* @__PURE__ */ jsx(TableCardCtx, { value: true, children: /* @__PURE__ */ jsx("div", { ref, className: cn("flex flex-col gap-ds-3 overflow-y-auto", className), children: sortedWithIndices.map(({ row, originalIdx }, i) => {
5626
4845
  const isSelected = selectedSet.has(originalIdx);
5627
4846
  return /* @__PURE__ */ jsxs(
5628
4847
  "div",
@@ -5630,7 +4849,7 @@ function Table({
5630
4849
  className: cn(
5631
4850
  "border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
5632
4851
  "rounded-[var(--ds-radius-md)] bg-[var(--ds-bg-surface)]",
5633
- "p-ds-4 space-y-ds-2",
4852
+ "p-ds-4 space-y-ds-3",
5634
4853
  isSelected && ACTIVE_RING,
5635
4854
  onRowClick && "cursor-pointer hover:bg-[var(--ds-bg-muted)] focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]"
5636
4855
  ),
@@ -5647,14 +4866,14 @@ function Table({
5647
4866
  selectable && /* @__PURE__ */ jsx(Checkbox, { checked: isSelected, onChange: () => toggleRow(originalIdx), label: "Select" }),
5648
4867
  columns.map((col) => /* @__PURE__ */ jsxs("div", { className: "flex justify-between gap-ds-2 text-sm", children: [
5649
4868
  /* @__PURE__ */ jsx("span", { className: "shrink-0 text-[var(--ds-text-secondary)] font-medium text-xs uppercase tracking-wider", children: col.header }),
5650
- /* @__PURE__ */ jsx("span", { className: "min-w-0 text-[var(--ds-text-primary)] text-right [overflow-wrap:anywhere]", children: row[col.key] })
4869
+ /* @__PURE__ */ jsx(CardValue, { value: row[col.key] })
5651
4870
  ] }, col.key)),
5652
4871
  expandable && renderExpanded && /* @__PURE__ */ jsx("div", { className: "pt-ds-2 border-t-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)] text-sm text-[var(--ds-text-secondary)]", children: renderExpanded(row, i) })
5653
4872
  ]
5654
4873
  },
5655
4874
  i
5656
4875
  );
5657
- }) });
4876
+ }) }) });
5658
4877
  }
5659
4878
  return /* @__PURE__ */ jsx(
5660
4879
  "div",
@@ -5682,12 +4901,15 @@ function Table({
5682
4901
  columns.map((col) => {
5683
4902
  const isSortable = sortable || col.sortable;
5684
4903
  const isActiveSort = sortKey === col.key;
5685
- const headerAlign = col.align ?? (kindOf(col) === "number" ? "right" : "left");
4904
+ const headerKind = kindOf(col);
4905
+ const headerAlign = col.align ?? (headerKind === "number" ? "right" : "left");
4906
+ const shrink = (headerKind === "number" || headerKind === "date") && !col.maxWidth;
5686
4907
  return /* @__PURE__ */ jsx(
5687
4908
  "th",
5688
4909
  {
5689
4910
  className: cn(
5690
4911
  density === "compact" ? "px-ds-3 py-ds-2" : "px-ds-4 py-ds-3",
4912
+ shrink && "w-px whitespace-nowrap",
5691
4913
  "font-[var(--ds-weight-heading)] text-[var(--ds-text-secondary)]",
5692
4914
  "text-xs uppercase tracking-wider",
5693
4915
  headerAlign === "right" ? "text-right" : headerAlign === "center" ? "text-center" : "text-left",
@@ -5774,13 +4996,12 @@ function Table({
5774
4996
  const kind = kindOf(col);
5775
4997
  const alignClass = col.align ? col.align === "right" ? "text-right" : col.align === "center" ? "text-center" : "text-left" : kind === "number" ? "text-right" : "text-left";
5776
4998
  const cellContent = row[col.key];
4999
+ const shrink = (kind === "number" || kind === "date") && !col.maxWidth;
5777
5000
  let inner;
5778
5001
  if (truncLines > 0) {
5779
5002
  inner = /* @__PURE__ */ jsx(TruncCell, { lines: truncLines, children: cellContent });
5780
5003
  } else if (kind === "email") {
5781
- inner = /* @__PURE__ */ jsx("span", { className: "max-w-[200px] truncate inline-block", title: typeof cellContent === "string" ? cellContent : void 0, children: cellContent });
5782
- } else if (kind === "date" || kind === "number") {
5783
- inner = /* @__PURE__ */ jsx("span", { className: "whitespace-nowrap", children: cellContent });
5004
+ inner = /* @__PURE__ */ jsx("span", { className: "max-w-[40ch] truncate inline-block align-bottom", title: typeof cellContent === "string" ? cellContent : void 0, children: cellContent });
5784
5005
  } else {
5785
5006
  inner = cellContent;
5786
5007
  }
@@ -5790,7 +5011,8 @@ function Table({
5790
5011
  className: cn(
5791
5012
  density === "compact" ? "px-ds-3 py-ds-2" : "px-ds-4 py-ds-3",
5792
5013
  "text-[var(--ds-text-primary)]",
5793
- alignClass
5014
+ alignClass,
5015
+ shrink && "w-px whitespace-nowrap"
5794
5016
  ),
5795
5017
  style: col.maxWidth ? { maxWidth: col.maxWidth } : void 0,
5796
5018
  children: inner
@@ -5815,7 +5037,10 @@ function Table({
5815
5037
  }
5816
5038
  );
5817
5039
  }
5818
- var sizes4 = {
5040
+ function optionsKeyForFit(options) {
5041
+ return options.map((o) => `${o.value}:${o.label}`).join("|");
5042
+ }
5043
+ var sizes3 = {
5819
5044
  xs: "text-xs px-ds-2",
5820
5045
  sm: "text-xs px-ds-3",
5821
5046
  md: "text-sm px-ds-3",
@@ -5847,9 +5072,34 @@ function SegmentedControl({
5847
5072
  }) {
5848
5073
  const contextSize = useContext(ControlSizeContext);
5849
5074
  const size = sizeProp ?? contextSize ?? "md";
5850
- const collapsed = options.length > maxOptions;
5851
5075
  const containerRef = useRef(null);
5852
5076
  const [indicator, setIndicator] = useState({ left: 0, width: 0 });
5077
+ const parentRef = useRef(null);
5078
+ const neededRef = useRef(0);
5079
+ const [overflows, setOverflows] = useState(false);
5080
+ const measureFit = useCallback(() => {
5081
+ const row = containerRef.current;
5082
+ if (row) {
5083
+ parentRef.current = row.parentElement;
5084
+ if (row.scrollWidth > 0) neededRef.current = row.scrollWidth;
5085
+ }
5086
+ const parent = parentRef.current;
5087
+ const needed = neededRef.current;
5088
+ if (!parent || !needed) return;
5089
+ const style = getComputedStyle(parent);
5090
+ const available = parent.clientWidth - (parseFloat(style.paddingLeft) || 0) - (parseFloat(style.paddingRight) || 0);
5091
+ if (available <= 0) return;
5092
+ setOverflows((prev) => prev ? needed > available - 8 : needed > available);
5093
+ }, []);
5094
+ useLayoutEffect(() => {
5095
+ measureFit();
5096
+ const parent = parentRef.current;
5097
+ if (!parent || typeof ResizeObserver === "undefined") return;
5098
+ const observer = new ResizeObserver(measureFit);
5099
+ observer.observe(parent);
5100
+ return () => observer.disconnect();
5101
+ }, [measureFit, optionsKeyForFit(options)]);
5102
+ const collapsed = options.length > maxOptions || overflows;
5853
5103
  const updateIndicator = useCallback(() => {
5854
5104
  if (!containerRef.current) return;
5855
5105
  const active = containerRef.current.querySelector("[data-active='true']");
@@ -5864,6 +5114,22 @@ function SegmentedControl({
5864
5114
  useLayoutEffect(() => {
5865
5115
  updateIndicator();
5866
5116
  }, [value, optionsKey, updateIndicator]);
5117
+ useEffect(() => {
5118
+ const fonts = document.fonts;
5119
+ if (!fonts?.ready) return;
5120
+ let live = true;
5121
+ fonts.ready.then(() => {
5122
+ if (live) updateIndicator();
5123
+ });
5124
+ return () => {
5125
+ live = false;
5126
+ };
5127
+ }, [updateIndicator]);
5128
+ const [animate, setAnimate] = useState(false);
5129
+ const select = useCallback((v) => {
5130
+ setAnimate(true);
5131
+ onChange(v);
5132
+ }, [onChange]);
5867
5133
  useLayoutEffect(() => {
5868
5134
  if (collapsed || !containerRef.current || typeof ResizeObserver === "undefined") return;
5869
5135
  const observer = new ResizeObserver(updateIndicator);
@@ -5889,7 +5155,7 @@ function SegmentedControl({
5889
5155
  else if (e.key === "End") next = enabled.length - 1;
5890
5156
  if (next === null) return;
5891
5157
  e.preventDefault();
5892
- onChange(enabled[next].value);
5158
+ select(enabled[next].value);
5893
5159
  const buttons = containerRef.current?.querySelectorAll('[role="radio"]');
5894
5160
  buttons?.forEach((b) => {
5895
5161
  if (b.dataset.value === enabled[next].value) b.focus();
@@ -5933,14 +5199,15 @@ function SegmentedControl({
5933
5199
  "absolute rounded-[var(--ds-radius-md)]",
5934
5200
  "bg-[var(--ds-bg-surface)]",
5935
5201
  variant === "default" && "shadow-[var(--ds-shadow-sm)]",
5936
- "transition-all"
5202
+ "transition-all motion-reduce:transition-none"
5937
5203
  ),
5204
+ "data-animate": animate ? "true" : "false",
5938
5205
  style: {
5939
5206
  left: indicator.left,
5940
5207
  width: indicator.width,
5941
5208
  top: indicatorInset[size],
5942
5209
  bottom: indicatorInset[size],
5943
- transitionDuration: "var(--ds-transition-speed)",
5210
+ transitionDuration: animate ? "var(--ds-transition-speed)" : "0ms",
5944
5211
  transitionTimingFunction: "var(--ds-transition-ease)"
5945
5212
  }
5946
5213
  }
@@ -5955,7 +5222,7 @@ function SegmentedControl({
5955
5222
  "data-value": opt.value,
5956
5223
  tabIndex: i === tabStop ? 0 : -1,
5957
5224
  disabled: opt.disabled,
5958
- onClick: () => !opt.disabled && onChange(opt.value),
5225
+ onClick: () => !opt.disabled && select(opt.value),
5959
5226
  onMouseEnter: onHoverChange && !opt.disabled ? () => onHoverChange(opt.value) : void 0,
5960
5227
  onMouseLeave: onHoverChange ? () => onHoverChange(null) : void 0,
5961
5228
  className: cn(
@@ -5964,7 +5231,7 @@ function SegmentedControl({
5964
5231
  "rounded-[var(--ds-radius-md)]",
5965
5232
  "transition-colors cursor-pointer",
5966
5233
  "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
5967
- sizes4[size],
5234
+ sizes3[size],
5968
5235
  opt.value === value ? "text-[var(--ds-text-primary)]" : "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-secondary)]",
5969
5236
  opt.disabled && "opacity-disabled cursor-not-allowed"
5970
5237
  ),
@@ -7529,6 +6796,7 @@ function EmptyState({ icon, iconFrame = "circle", title, description, action, va
7529
6796
  return /* @__PURE__ */ jsxs(
7530
6797
  "div",
7531
6798
  {
6799
+ "data-gs": "empty-state",
7532
6800
  className: cn(
7533
6801
  "flex flex-col items-center justify-center py-[var(--ds-space-10)] px-[var(--ds-space-6)]",
7534
6802
  "rounded-[var(--ds-radius-md)] bg-[var(--ds-bg-surface)]",
@@ -7544,22 +6812,41 @@ function EmptyState({ icon, iconFrame = "circle", title, description, action, va
7544
6812
  }
7545
6813
  );
7546
6814
  }
7547
- function EntityCell({ avatar, primary, secondary, size = "md", className }) {
7548
- const isPhone = useBreakpoint("sm");
7549
- const avatarElement = typeof avatar === "string" ? /* @__PURE__ */ jsx(Avatar, { src: avatar, alt: primary, size }) : avatar;
6815
+ function initialsOf(name) {
6816
+ return name.trim().split(/\s+/).slice(0, 2).map((w) => w[0] ?? "").join("").toUpperCase();
6817
+ }
6818
+ function looksLikeUrl(s) {
6819
+ return /^(?:https?:|data:|blob:|\/|\.{1,2}\/)/.test(s) || /\.[a-z]{2,5}(?:$|[?#])/i.test(s);
6820
+ }
6821
+ function EntityCell({ avatar, src, initials, primary, secondary, size = "md", className }) {
6822
+ const inTableCard = useContext(TableCardCtx);
6823
+ let avatarElement;
6824
+ if (typeof avatar === "string") {
6825
+ avatarElement = looksLikeUrl(avatar) ? /* @__PURE__ */ jsx(Avatar, { src: avatar, alt: primary, size }) : /* @__PURE__ */ jsx(Avatar, { initials: avatar, alt: primary, size });
6826
+ } else if (avatar != null) {
6827
+ avatarElement = avatar;
6828
+ } else if (src) {
6829
+ avatarElement = /* @__PURE__ */ jsx(Avatar, { src, alt: primary, size });
6830
+ } else {
6831
+ avatarElement = /* @__PURE__ */ jsx(Avatar, { initials: initials ?? initialsOf(primary), alt: primary, size });
6832
+ }
7550
6833
  return /* @__PURE__ */ jsxs(
7551
6834
  "div",
7552
6835
  {
7553
6836
  className: cn(
7554
- "flex gap-ds-2",
7555
- isPhone ? "flex-col items-center text-center" : "flex-row items-center",
6837
+ "flex flex-row items-center gap-ds-2",
6838
+ // Inside a Table or DataGrid card (the phone layout) the cell keeps the avatar beside the text and sits
6839
+ // against the value column's right edge; the name may take two lines, the secondary line truncates
6840
+ // (Sprint 25.4, round 7: the owner's call, replacing decision 2's stacked cell).
6841
+ inTableCard && "justify-end text-right",
7556
6842
  className
7557
6843
  ),
7558
6844
  children: [
7559
6845
  /* @__PURE__ */ jsx("div", { className: "shrink-0", children: avatarElement }),
7560
6846
  /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
7561
6847
  /* @__PURE__ */ jsx("p", { className: cn(
7562
- "font-medium text-[var(--ds-text-primary)] truncate",
6848
+ "font-medium text-[var(--ds-text-primary)]",
6849
+ inTableCard ? "line-clamp-2 [overflow-wrap:anywhere]" : "truncate",
7563
6850
  size === "sm" ? "text-xs" : "text-sm"
7564
6851
  ), children: primary }),
7565
6852
  secondary && /* @__PURE__ */ jsx("p", { className: cn(
@@ -7571,42 +6858,6 @@ function EntityCell({ avatar, primary, secondary, size = "md", className }) {
7571
6858
  }
7572
6859
  );
7573
6860
  }
7574
- function SkipLink({ href = "#main", children = "Skip to content", className }) {
7575
- return /* @__PURE__ */ jsx(
7576
- "a",
7577
- {
7578
- href,
7579
- className: cn(
7580
- "sr-only focus:not-sr-only focus:fixed focus:top-[var(--ds-space-2)] focus:left-[var(--ds-space-2)] focus:z-[var(--ds-z-tooltip)]",
7581
- "focus:px-[var(--ds-space-3)] focus:py-[var(--ds-space-2)] focus:rounded-[var(--ds-radius-md)] focus:text-sm focus:font-medium",
7582
- "focus:bg-[var(--ds-bg-surface-elevated)] focus:text-[var(--ds-text-primary)] focus:shadow-[var(--ds-shadow-md)]",
7583
- "focus:outline-none focus:ring-2 focus:ring-[var(--ds-accent-primary)]",
7584
- className
7585
- ),
7586
- children
7587
- }
7588
- );
7589
- }
7590
- function useCompact(ref, breakpoint) {
7591
- const px = typeof breakpoint === "number" ? breakpoint : readBreakpointPx(breakpoint);
7592
- const width = useElementWidth(ref);
7593
- return width > 0 && width < px;
7594
- }
7595
- function useElementWidth(ref) {
7596
- const [width, setWidth] = useState(0);
7597
- const check = useCallback(() => {
7598
- const next = ref.current?.offsetWidth ?? 0;
7599
- if (next > 0) setWidth(next);
7600
- }, [ref]);
7601
- useLayoutEffect(() => {
7602
- check();
7603
- if (!ref.current || typeof ResizeObserver === "undefined") return;
7604
- const observer = new ResizeObserver(check);
7605
- observer.observe(ref.current);
7606
- return () => observer.disconnect();
7607
- }, [check, ref]);
7608
- return width;
7609
- }
7610
6861
  function TableToolbar({
7611
6862
  search,
7612
6863
  primaryFilter,
@@ -7635,6 +6886,8 @@ function TableToolbar({
7635
6886
  const iconSize = size === "sm" ? 14 : 16;
7636
6887
  const count = activeFilterCount ?? activeFilters?.length ?? 0;
7637
6888
  const hasChips = !!activeFilters?.length;
6889
+ const inPane = useContext(SplitPaneCtx);
6890
+ const searchFills = inPane || !primaryFilter && !filters && !actions;
7638
6891
  return /* @__PURE__ */ jsx(ControlSizeContext.Provider, { value: size, children: /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-ds-3 min-w-0", className), "data-table-toolbar": "", children: [
7639
6892
  /* @__PURE__ */ jsxs("div", { ref: rowRef, className: "flex items-center gap-ds-2 flex-wrap", children: [
7640
6893
  search && /* @__PURE__ */ jsx(
@@ -7645,7 +6898,7 @@ function TableToolbar({
7645
6898
  value: search.value,
7646
6899
  size,
7647
6900
  onChange: (e) => search.onChange(e.target.value),
7648
- className: "flex-1 min-w-[160px] max-w-[280px]"
6901
+ className: cn("flex-1 min-w-[160px]", !searchFills && "max-w-[280px]")
7649
6902
  }
7650
6903
  ),
7651
6904
  primaryFilter && (compact ? /* @__PURE__ */ jsx(
@@ -7668,7 +6921,7 @@ function TableToolbar({
7668
6921
  onChange: primaryFilter.onChange
7669
6922
  }
7670
6923
  )),
7671
- (filters || actions) && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-2 flex-1 justify-end", children: [
6924
+ (filters || actions) && /* @__PURE__ */ jsxs("div", { className: cn("flex items-center gap-ds-2 justify-end", inPane ? "flex-none" : "flex-1"), children: [
7672
6925
  filters && /* @__PURE__ */ jsxs(
7673
6926
  Button,
7674
6927
  {
@@ -8076,7 +7329,7 @@ function TreeNodeRow({
8076
7329
  isSelected ? "bg-[var(--ds-tree-active-bg)] text-[var(--ds-tree-active-text)] font-medium border-[var(--ds-accent-primary)]" : "border-transparent text-[var(--ds-text-primary)] hover:bg-[var(--ds-tab-hover-bg)] hover:text-[var(--ds-text-primary)]",
8077
7330
  node.disabled && "opacity-disabled pointer-events-none"
8078
7331
  ),
8079
- style: { paddingLeft: `calc(var(--ds-space-5) * ${depth} + var(--ds-space-2))` },
7332
+ style: { paddingLeft: `calc(var(--ds-space-3) * ${depth} + var(--ds-space-2))` },
8080
7333
  onClick: () => {
8081
7334
  if (node.disabled) return;
8082
7335
  if (checkable) handleCheck(node);
@@ -8882,136 +8135,6 @@ function CornerWindow({ open, onClose, edge, align, width, height, offset, ancho
8882
8135
  container ?? document.body
8883
8136
  );
8884
8137
  }
8885
- var AppShellContext = createContext(null);
8886
- function useAppShell() {
8887
- return useContext(AppShellContext);
8888
- }
8889
- var AppShell = forwardRef(function AppShell2({
8890
- layout = "sidebar",
8891
- sidebar,
8892
- mobileMenu,
8893
- mobileMenuWidth,
8894
- mobileMenuLightBackdrop,
8895
- mobileMenuFlush = true,
8896
- navbar,
8897
- mobileNav,
8898
- footer,
8899
- skipLink = true,
8900
- mainId = "main",
8901
- aside,
8902
- asideSide = "right",
8903
- launcher,
8904
- children,
8905
- className,
8906
- scrollable = true,
8907
- theme,
8908
- currentPath,
8909
- homePath = "/"
8910
- }, ref) {
8911
- const [sidebarOpen, setSidebarOpen] = useState(false);
8912
- const [asideOpen, setAsideOpen] = useState(false);
8913
- const [asideFloating, setAsideFloating] = useState(false);
8914
- const isMobile = useBreakpoint("md");
8915
- const isSmallMobile = useBreakpoint("sm");
8916
- const isTablet = isMobile && !isSmallMobile;
8917
- const autoCollapsed = useBreakpoint("lg");
8918
- const [userCollapsed, setUserCollapsed] = useState(null);
8919
- const sidebarCollapsed = userCollapsed ?? autoCollapsed;
8920
- const hasSidebar = (layout === "sidebar" || layout === "sidebar-header") && sidebar != null;
8921
- const hasMobileMenu = mobileMenu != null;
8922
- const hasMobileNav = mobileNav != null;
8923
- const showMobileDrawer = hasMobileMenu && !(isSmallMobile && hasMobileNav);
8924
- const [shellEl, setShellEl] = useState(null);
8925
- const [chrome, setChrome] = useState({});
8926
- const registerChrome = useCallback((kind, position) => {
8927
- setChrome((c) => c[kind] === position ? c : { ...c, [kind]: position });
8928
- return () => setChrome((c) => {
8929
- if (!(kind in c)) return c;
8930
- const next = { ...c };
8931
- delete next[kind];
8932
- return next;
8933
- });
8934
- }, []);
8935
- const navbarFloating = (chrome.navbar ?? theme?.navbarPosition) === "floating";
8936
- const sidebarFloating = hasSidebar && !isMobile && (chrome.sidebar ?? theme?.sidebarPosition) === "floating";
8937
- const isHome = currentPath != null && currentPath === homePath;
8938
- const hasFooter = footer != null && scrollable;
8939
- const mainRef = useRef(null);
8940
- useEffect(() => {
8941
- const main = mainRef.current;
8942
- if (!hasFooter || !shellEl || !main || typeof ResizeObserver === "undefined") {
8943
- shellEl?.style.removeProperty("--ds-main-height");
8944
- return;
8945
- }
8946
- const ro = new ResizeObserver(() => shellEl.style.setProperty("--ds-main-height", `${main.clientHeight}px`));
8947
- ro.observe(main);
8948
- return () => {
8949
- ro.disconnect();
8950
- shellEl.style.removeProperty("--ds-main-height");
8951
- };
8952
- }, [hasFooter, shellEl]);
8953
- const hasLauncher = !!launcher && !isSmallMobile;
8954
- const launcherProps = isValidElement(launcher) ? launcher.props : null;
8955
- 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";
8956
- return /* @__PURE__ */ jsx(
8957
- AppShellContext,
8958
- {
8959
- 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 },
8960
- children: /* @__PURE__ */ jsxs("div", { ref: (el) => {
8961
- setShellEl(el);
8962
- if (typeof ref === "function") ref(el);
8963
- else if (ref) ref.current = el;
8964
- }, className: cn("relative h-screen flex flex-col bg-[var(--ds-bg-canvas)] overflow-hidden", className), children: [
8965
- skipLink && /* @__PURE__ */ jsx(SkipLink, { href: `#${mainId}` }),
8966
- layout === "sidebar-header" && navbar,
8967
- layout === "multi-column" && navbar,
8968
- /* @__PURE__ */ jsxs("div", { className: "flex flex-1 min-h-0", children: [
8969
- hasSidebar && !isMobile && /* @__PURE__ */ jsx("div", { className: "shrink-0 h-full", children: sidebar }),
8970
- aside && asideSide === "left" && aside,
8971
- /* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col min-w-0 min-h-0", children: [
8972
- layout === "sidebar" && navbar,
8973
- layout === "stacked" && navbar,
8974
- /* @__PURE__ */ jsx(
8975
- "main",
8976
- {
8977
- ref: mainRef,
8978
- id: mainId,
8979
- tabIndex: -1,
8980
- className: cn(
8981
- "flex-1 min-h-0 focus:outline-none",
8982
- scrollable ? "overflow-y-auto" : "relative",
8983
- layout === "multi-column" && "flex"
8984
- ),
8985
- style: { containerType: "inline-size" },
8986
- children: scrollable ? footer ? /* @__PURE__ */ jsxs("div", { className: "min-h-full flex flex-col", children: [
8987
- /* @__PURE__ */ jsx("div", { className: "flex-1", children }),
8988
- footer
8989
- ] }) : children : /* @__PURE__ */ jsx("div", { className: "absolute inset-0", children })
8990
- }
8991
- )
8992
- ] }),
8993
- aside && asideSide === "right" && aside
8994
- ] }),
8995
- showMobileDrawer && isMobile && /* @__PURE__ */ jsx(
8996
- Drawer,
8997
- {
8998
- open: sidebarOpen,
8999
- onClose: () => setSidebarOpen(false),
9000
- position: "left",
9001
- width: mobileMenuWidth ?? "var(--ds-drawer-width)",
9002
- container: shellEl ?? void 0,
9003
- flush: mobileMenuFlush,
9004
- lightBackdrop: mobileMenuLightBackdrop,
9005
- topOffset: layout !== "stacked" && navbar != null ? "var(--ds-navbar-height)" : void 0,
9006
- children: mobileMenu
9007
- }
9008
- ),
9009
- isSmallMobile && mobileNav && /* @__PURE__ */ jsx("div", { className: "sticky bottom-0 z-[var(--ds-z-sticky)]", children: mobileNav }),
9010
- !isSmallMobile && launcher
9011
- ] })
9012
- }
9013
- );
9014
- });
9015
8138
  var FALLBACK_MIN_PX = 320;
9016
8139
  var GUTTER = "var(--ds-space-3)";
9017
8140
  function AsidePanel({
@@ -9173,15 +8296,30 @@ function TruncText({ children, className }) {
9173
8296
  if (!isTruncated) return text;
9174
8297
  return /* @__PURE__ */ jsx(Tooltip, { content: children, children: text });
9175
8298
  }
8299
+ var warnedLegacyProps = false;
9176
8300
  var StackedList = forwardRef(function StackedList2({
9177
8301
  items,
9178
8302
  divided = true,
9179
- flush = false,
8303
+ variant: variantProp,
9180
8304
  selectedId,
9181
- hover = "tint",
9182
- className
8305
+ className,
8306
+ flush,
8307
+ hover
9183
8308
  }, ref) {
9184
8309
  const isPhone = useBreakpoint("sm");
8310
+ const inCard = useContext(CardSurfaceCtx);
8311
+ const inPane = useContext(SplitPaneCtx);
8312
+ const variant = variantProp ?? (flush || hover === "title" ? "articles" : "list");
8313
+ const legacy = flush !== void 0 || hover !== void 0;
8314
+ useEffect(() => {
8315
+ if (!legacy || warnedLegacyProps) return;
8316
+ warnedLegacyProps = true;
8317
+ console.warn('StackedList: `flush` and `hover` are deprecated \u2014 pass variant="articles" for an article list; the default variant takes its inset from the pane or card it sits in.');
8318
+ }, [legacy]);
8319
+ const articles = variant === "articles";
8320
+ const stackMeta = isPhone && articles;
8321
+ const selectable = selectedId !== void 0 || items.some((i) => i.selected !== void 0);
8322
+ const inset = articles ? inCard ? "px-ds-5" : "px-0" : inCard ? "px-ds-5" : inPane ? "pl-ds-3 pr-ds-4" : selectable ? "pl-ds-3 pr-0" : "px-0";
9185
8323
  return /* @__PURE__ */ jsx(
9186
8324
  "ul",
9187
8325
  {
@@ -9198,6 +8336,9 @@ var StackedList = forwardRef(function StackedList2({
9198
8336
  const multiline = !!item.description;
9199
8337
  const trailing = item.unread || item.meta ? /* @__PURE__ */ jsxs("div", { className: cn(
9200
8338
  "shrink-0 flex items-center gap-ds-2 text-sm",
8339
+ // Below sm an article's meta sits under the title, left-aligned, whatever alignment its own markup
8340
+ // carries (round 6: a builder's items-end column put its badge mid-row).
8341
+ stackMeta && "self-start [&>*]:items-start! [&>*]:text-left!",
9201
8342
  isSelected ? "text-[var(--ds-nav-v-active-muted)]" : "text-[var(--ds-text-muted)]"
9202
8343
  ), children: [
9203
8344
  item.unread && /* @__PURE__ */ jsx(
@@ -9216,18 +8357,20 @@ var StackedList = forwardRef(function StackedList2({
9216
8357
  className: cn(
9217
8358
  "flex gap-ds-3 py-ds-3",
9218
8359
  multiline ? "items-start" : "items-center",
9219
- flush ? "px-0" : "px-ds-5",
8360
+ inset,
9220
8361
  interactive && [
9221
8362
  "cursor-pointer [transition:var(--ds-transition-interactive)]",
9222
8363
  "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]"
9223
8364
  ],
9224
- // Same colours as the vertical Tabs item — the theme's in-page vertical
9225
- // style decides rail, tint or pill via the --ds-nav-v-* tokens — but a
9226
- // list row is a full-width band: no radius, ever, and a heavier rail
9227
- // (1.5× the tab rail; still 0 under tint and pill). Every row carries
9228
- // the rail width as a transparent placeholder so content never shifts.
9229
- "border-l-[length:calc(var(--ds-nav-v-active-rail)*1.5)]",
9230
- isSelected ? "bg-[var(--ds-nav-v-active-bg)] text-[var(--ds-nav-v-active-text)] border-[var(--ds-accent-primary)]" : cn("border-transparent", interactive && hover === "tint" && "hover:bg-[var(--ds-tab-hover-bg)]", interactive && hover === "title" && "group/row")
8365
+ // list: same colours as the vertical Tabs item — the theme's in-page vertical style decides rail, tint
8366
+ // or pill via the --ds-nav-v-* tokens — but a list row is a full-width band: no radius, ever, and a
8367
+ // heavier rail (1.5× the tab rail; still 0 under tint and pill). Every row carries the rail width as a
8368
+ // transparent placeholder so content never shifts, and the inset keeps content off the rail.
8369
+ // articles: no rail, no tint; the title answers the pointer and the current row.
8370
+ // The placeholder exists only where a selection can appear, so a plain canvas list aligns exactly with its heading.
8371
+ !articles && selectable && "border-l-[length:calc(var(--ds-nav-v-active-rail)*1.5)]",
8372
+ !articles && (isSelected ? "bg-[var(--ds-nav-v-active-bg)] text-[var(--ds-nav-v-active-text)] border-[var(--ds-accent-primary)]" : cn("border-transparent", interactive && "hover:bg-[var(--ds-tab-hover-bg)]")),
8373
+ articles && interactive && "group/row"
9231
8374
  ),
9232
8375
  onClick: item.onClick,
9233
8376
  role: interactive ? "button" : void 0,
@@ -9242,25 +8385,25 @@ var StackedList = forwardRef(function StackedList2({
9242
8385
  children: [
9243
8386
  item.leading && /* @__PURE__ */ jsx("div", { className: cn("shrink-0", multiline && "mt-ds-0.5"), children: item.leading }),
9244
8387
  /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
9245
- /* @__PURE__ */ jsxs("div", { className: cn("flex gap-ds-2", isPhone ? "flex-col" : "items-center justify-between"), children: [
8388
+ /* @__PURE__ */ jsxs("div", { className: cn("flex gap-ds-2", stackMeta ? "flex-col" : "items-center justify-between"), children: [
9246
8389
  /* @__PURE__ */ jsx(
9247
8390
  TruncText,
9248
8391
  {
9249
8392
  className: cn(
9250
8393
  "text-sm",
9251
- isSelected ? "font-medium text-[var(--ds-nav-v-active-text)]" : hasEmphasis && !item.unread ? "text-[var(--ds-text-secondary)]" : "font-medium text-[var(--ds-text-primary)]",
9252
- // hover="title": the title answers the pointer (accent, underline) instead of the row's background
9253
- interactive && hover === "title" && !isSelected && "transition-colors group-hover/row:text-[var(--ds-accent-primary)] group-hover/row:underline underline-offset-2"
8394
+ isSelected ? articles ? "font-medium text-[var(--ds-accent-primary)]" : "font-medium text-[var(--ds-nav-v-active-text)]" : hasEmphasis && !item.unread ? "text-[var(--ds-text-secondary)]" : "font-medium text-[var(--ds-text-primary)]",
8395
+ // articles: the title answers the pointer (accent, underline) instead of the row's background
8396
+ articles && interactive && !isSelected && "transition-colors group-hover/row:text-[var(--ds-accent-primary)] group-hover/row:underline underline-offset-2"
9254
8397
  ),
9255
8398
  children: item.title
9256
8399
  }
9257
8400
  ),
9258
- (multiline || isPhone) && trailing
8401
+ (multiline || stackMeta) && trailing
9259
8402
  ] }),
9260
8403
  item.subtitle && /* @__PURE__ */ jsx(TruncText, { className: cn("text-sm", isSelected ? "text-[var(--ds-nav-v-active-muted)]" : "text-[var(--ds-text-secondary)]"), children: item.subtitle }),
9261
8404
  item.description && /* @__PURE__ */ jsx(TruncText, { className: cn("text-xs", isSelected ? "text-[var(--ds-nav-v-active-muted)]" : "text-[var(--ds-text-muted)]"), children: item.description })
9262
8405
  ] }),
9263
- !multiline && !isPhone && trailing,
8406
+ !multiline && !stackMeta && trailing,
9264
8407
  item.actions && /* @__PURE__ */ jsx(
9265
8408
  "div",
9266
8409
  {
@@ -9314,6 +8457,7 @@ var TONE_SURFACE = {
9314
8457
  )
9315
8458
  };
9316
8459
  var BUTTON_ELEVATION = "shadow-[var(--ds-shadow-button)] active:shadow-[var(--ds-shadow-button-pressed)] active:translate-x-[var(--ds-press-depth)] active:translate-y-[var(--ds-press-depth)]";
8460
+ var BUTTON_ELEVATION_SHADOW_ONLY = "shadow-[var(--ds-shadow-button)] active:shadow-[var(--ds-shadow-button-pressed)]";
9317
8461
  var CORNER_SHAPE = cn(BUTTON_ELEVATION, "hover:-translate-y-[var(--ds-hover-lift)]");
9318
8462
  var LAUNCHER_RADIUS = "calc(var(--ds-radius-base) * 4)";
9319
8463
  var ACCENT_GLYPH = cn(
@@ -9489,13 +8633,13 @@ function Launcher({
9489
8633
  variant === "pill" && cn(CORNER_SHAPE, PILL_SIZE[size]),
9490
8634
  isRibbon && !bottomRibbon && cn(
9491
8635
  "flex-col gap-ds-2 px-ds-2 py-ds-4 text-sm",
9492
- BUTTON_ELEVATION,
8636
+ align === "center" ? BUTTON_ELEVATION_SHADOW_ONLY : BUTTON_ELEVATION,
9493
8637
  side === "right" ? "rounded-l-[var(--ds-radius-md)]" : "rounded-r-[var(--ds-radius-md)]",
9494
8638
  align === "center" && "-translate-y-1/2"
9495
8639
  ),
9496
8640
  bottomRibbon && cn(
9497
8641
  "flex-row gap-ds-2 px-ds-4 py-ds-2 text-sm rounded-t-[var(--ds-radius-md)]",
9498
- BUTTON_ELEVATION,
8642
+ align === "center" ? BUTTON_ELEVATION_SHADOW_ONLY : BUTTON_ELEVATION,
9499
8643
  align === "center" && "-translate-x-1/2"
9500
8644
  ),
9501
8645
  className
@@ -9569,6 +8713,100 @@ function trackClasses(orientation) {
9569
8713
  "border-b-[length:var(--ds-nav-h-track-border)] border-[var(--ds-border-default)]"
9570
8714
  );
9571
8715
  }
8716
+ var FADE = 24;
8717
+ function useScrollTrack(activeTab, enabled) {
8718
+ const ref = useRef(null);
8719
+ const [edges, setEdges] = useState({ left: false, right: false });
8720
+ const measure = useCallback(() => {
8721
+ const el = ref.current;
8722
+ if (!el) return;
8723
+ const left = el.scrollLeft > 1;
8724
+ const right = el.scrollLeft + el.clientWidth < el.scrollWidth - 1;
8725
+ setEdges((prev) => prev.left === left && prev.right === right ? prev : { left, right });
8726
+ }, []);
8727
+ useLayoutEffect(() => {
8728
+ if (!enabled) return;
8729
+ measure();
8730
+ const el = ref.current;
8731
+ if (!el || typeof ResizeObserver === "undefined") return;
8732
+ const observer = new ResizeObserver(measure);
8733
+ observer.observe(el);
8734
+ return () => observer.disconnect();
8735
+ }, [enabled, measure]);
8736
+ useLayoutEffect(() => {
8737
+ if (!enabled) return;
8738
+ const el = ref.current;
8739
+ const tab = el?.querySelector('[role="tab"][aria-selected="true"]');
8740
+ if (!el || !tab) return;
8741
+ const start = tab.offsetLeft;
8742
+ const end = start + tab.offsetWidth;
8743
+ if (start < el.scrollLeft + FADE) el.scrollLeft = Math.max(0, start - FADE);
8744
+ else if (end > el.scrollLeft + el.clientWidth - FADE) el.scrollLeft = end - el.clientWidth + FADE;
8745
+ measure();
8746
+ }, [activeTab, enabled, measure]);
8747
+ useEffect(() => {
8748
+ if (!enabled) return;
8749
+ const el = ref.current;
8750
+ if (!el) return;
8751
+ const onWheel = (e) => {
8752
+ if (el.scrollWidth <= el.clientWidth || Math.abs(e.deltaY) <= Math.abs(e.deltaX)) return;
8753
+ el.scrollLeft += e.deltaY;
8754
+ e.preventDefault();
8755
+ };
8756
+ let startX = 0, startLeft = 0, dragged = false, down = false;
8757
+ const onDown = (e) => {
8758
+ if (e.pointerType === "mouse" && e.button !== 0) return;
8759
+ down = true;
8760
+ dragged = false;
8761
+ startX = e.clientX;
8762
+ startLeft = el.scrollLeft;
8763
+ };
8764
+ const onMove = (e) => {
8765
+ if (!down) return;
8766
+ const dx = e.clientX - startX;
8767
+ if (!dragged && Math.abs(dx) < 5) return;
8768
+ if (!dragged) {
8769
+ dragged = true;
8770
+ el.setPointerCapture?.(e.pointerId);
8771
+ }
8772
+ el.scrollLeft = startLeft - dx;
8773
+ };
8774
+ const onUp = (e) => {
8775
+ if (dragged) el.releasePointerCapture?.(e.pointerId);
8776
+ down = false;
8777
+ };
8778
+ const onClick = (e) => {
8779
+ if (dragged) {
8780
+ e.stopPropagation();
8781
+ e.preventDefault();
8782
+ dragged = false;
8783
+ }
8784
+ };
8785
+ el.addEventListener("wheel", onWheel, { passive: false });
8786
+ el.addEventListener("pointerdown", onDown);
8787
+ el.addEventListener("pointermove", onMove);
8788
+ el.addEventListener("pointerup", onUp);
8789
+ el.addEventListener("pointercancel", onUp);
8790
+ el.addEventListener("click", onClick, true);
8791
+ return () => {
8792
+ el.removeEventListener("wheel", onWheel);
8793
+ el.removeEventListener("pointerdown", onDown);
8794
+ el.removeEventListener("pointermove", onMove);
8795
+ el.removeEventListener("pointerup", onUp);
8796
+ el.removeEventListener("pointercancel", onUp);
8797
+ el.removeEventListener("click", onClick, true);
8798
+ };
8799
+ }, [enabled]);
8800
+ const fade = enabled ? [edges.left && "left", edges.right && "right"].filter(Boolean).join(" ") : "";
8801
+ const mask = fade ? `linear-gradient(to right, ${edges.left ? `transparent 0, black ${FADE}px` : "black 0"}, ${edges.right ? `black calc(100% - ${FADE}px), transparent 100%` : "black 100%"})` : void 0;
8802
+ return {
8803
+ ref,
8804
+ onScroll: measure,
8805
+ "data-fade": fade || void 0,
8806
+ style: mask ? { maskImage: mask, WebkitMaskImage: mask } : void 0
8807
+ };
8808
+ }
8809
+ var SCROLL_TRACK = "overflow-x-auto overflow-y-hidden [scrollbar-width:none] [&::-webkit-scrollbar]:hidden max-w-full";
9572
8810
  function getTabsFromDOM(el) {
9573
8811
  const tablist = el.closest('[role="tablist"]');
9574
8812
  if (!tablist) return [];
@@ -9708,7 +8946,8 @@ function TabButton({
9708
8946
  return btn;
9709
8947
  }
9710
8948
  function TabsList({ children, className }) {
9711
- const { orientation, fullWidth, overflow } = useTabsCtx();
8949
+ const { orientation, fullWidth, overflow, activeTab } = useTabsCtx();
8950
+ const track = useScrollTrack(activeTab, orientation === "horizontal" && overflow === "scroll");
9712
8951
  if (orientation === "vertical") {
9713
8952
  return /* @__PURE__ */ jsx(
9714
8953
  "div",
@@ -9724,10 +8963,11 @@ function TabsList({ children, className }) {
9724
8963
  "div",
9725
8964
  {
9726
8965
  role: "tablist",
8966
+ ...overflow === "scroll" ? track : {},
9727
8967
  className: cn(
9728
8968
  trackClasses("horizontal"),
9729
8969
  "max-w-full",
9730
- overflow === "hidden" ? "overflow-hidden" : overflow === "truncate" ? "overflow-hidden" : "overflow-x-auto overflow-y-hidden",
8970
+ overflow === "hidden" ? "overflow-hidden" : overflow === "truncate" ? "overflow-hidden" : SCROLL_TRACK,
9731
8971
  fullWidth && "w-full",
9732
8972
  className
9733
8973
  ),
@@ -9781,6 +9021,9 @@ function TabsRoot(props) {
9781
9021
  } = props;
9782
9022
  const isMobile = useBreakpoint("md");
9783
9023
  const effectiveOrientation = orientation === "responsive" ? "vertical" : orientation;
9024
+ const arrayApi = "tabs" in props && !!props.tabs;
9025
+ const arrayOverflow = arrayApi && props.tabs.some((t) => t.truncate) ? "truncate" : overflow;
9026
+ const track = useScrollTrack(activeTab, arrayApi && effectiveOrientation === "horizontal" && arrayOverflow === "scroll");
9784
9027
  if ("tabs" in props && props.tabs) {
9785
9028
  const { tabs, mobile = "scroll" } = props;
9786
9029
  const collapseToDropdown = isMobile && (orientation === "responsive" || mobile === "dropdown");
@@ -9819,14 +9062,15 @@ function TabsRoot(props) {
9819
9062
  }
9820
9063
  );
9821
9064
  }
9822
- const effectiveOverflow = tabs.some((t) => t.truncate) ? "truncate" : overflow;
9065
+ const effectiveOverflow = arrayOverflow;
9823
9066
  return /* @__PURE__ */ jsx(
9824
9067
  "div",
9825
9068
  {
9826
9069
  role: "tablist",
9070
+ ...effectiveOverflow === "scroll" ? track : {},
9827
9071
  className: cn(
9828
9072
  trackClasses("horizontal"),
9829
- effectiveOverflow === "scroll" ? "overflow-x-auto overflow-y-hidden" : "overflow-hidden",
9073
+ effectiveOverflow === "scroll" ? SCROLL_TRACK : "overflow-hidden",
9830
9074
  fullWidth && "w-full",
9831
9075
  className
9832
9076
  ),
@@ -9870,78 +9114,18 @@ function SettingsLayout({
9870
9114
  /* @__PURE__ */ jsx(
9871
9115
  Dropdown,
9872
9116
  {
9873
- "aria-label": ariaLabel,
9874
- options: sections.filter((s) => !s.disabled).map((s) => ({ id: s.id, label: s.label, group: s.group })),
9875
- value,
9876
- onChange: (v) => onChange(v)
9877
- }
9878
- ),
9879
- /* @__PURE__ */ jsx("div", { className: "min-w-0", children })
9880
- ] });
9881
- }
9882
- return /* @__PURE__ */ jsxs("div", { ref: rootRef, "data-settings-layout": "sidebar", className: cn("flex flex-row items-start gap-ds-8", className), children: [
9883
- /* @__PURE__ */ jsx("nav", { "aria-label": ariaLabel, className: "sticky self-start shrink-0", style: { width: `${railWidth}px`, top: stickyTop }, children: /* @__PURE__ */ jsx(Tabs, { orientation: "vertical", tabs, activeTab: value, onTabChange: onChange, className: "w-full" }) }),
9884
- /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children })
9885
- ] });
9886
- }
9887
- function SplitView({
9888
- list,
9889
- detail,
9890
- aside,
9891
- asideTitle = "Details",
9892
- detailOpen = false,
9893
- onDetailClose,
9894
- detailMode = "stack",
9895
- backLabel = "Back",
9896
- listWidth = 35,
9897
- listMinWidth = 25,
9898
- asideWidth = 25,
9899
- collapseBelow = 768,
9900
- asideCollapseBelow = 1024,
9901
- bordered = false,
9902
- frame = "page",
9903
- "aria-label": ariaLabel,
9904
- className
9905
- }) {
9906
- const rootRef = useRef(null);
9907
- const compact = useCompact(rootRef, collapseBelow);
9908
- const asideCollapsed = useCompact(rootRef, asideCollapseBelow);
9909
- const [asideOpen, setAsideOpen] = useState(false);
9910
- useEffect(() => {
9911
- if (!asideCollapsed) setAsideOpen(false);
9912
- }, [asideCollapsed]);
9913
- const region = ariaLabel ? { role: "region", "aria-label": ariaLabel } : {};
9914
- const isFill = frame === "fill";
9915
- const shell = useAppShell();
9916
- const fillStyle = isFill && shell?.hasFooter ? { height: "var(--ds-main-height, 100%)" } : void 0;
9917
- const surface = cn(
9918
- "h-full min-h-0 w-full bg-[var(--ds-bg-surface)]",
9919
- isFill && "rounded-none border-0",
9920
- bordered && !isFill && "border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)] rounded-[var(--ds-radius-md)] overflow-hidden",
9921
- className
9922
- );
9923
- if (compact) {
9924
- const showDetail = detailMode === "stack" && detailOpen;
9925
- return /* @__PURE__ */ jsx("div", { ref: rootRef, "data-split-view": showDetail ? "detail" : "list", "data-frame": frame, ...region, className: cn(surface, "flex flex-col"), style: fillStyle, children: showDetail ? /* @__PURE__ */ jsxs(Fragment, { children: [
9926
- /* @__PURE__ */ jsx("div", { className: "shrink-0 px-ds-3 py-ds-2 border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]", children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", iconLeft: /* @__PURE__ */ jsx(Icon, { name: "arrow-left", size: 14 }), onClick: onDetailClose, children: backLabel }) }),
9927
- /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0 overflow-y-auto", children: detail })
9928
- ] }) : /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0 overflow-y-auto", children: list }) });
9117
+ "aria-label": ariaLabel,
9118
+ options: sections.filter((s) => !s.disabled).map((s) => ({ id: s.id, label: s.label, group: s.group })),
9119
+ value,
9120
+ onChange: (v) => onChange(v)
9121
+ }
9122
+ ),
9123
+ /* @__PURE__ */ jsx("div", { className: "min-w-0", children })
9124
+ ] });
9929
9125
  }
9930
- const asideToggle = aside && asideCollapsed ? /* @__PURE__ */ jsx("div", { className: "shrink-0 flex justify-end px-ds-3 py-ds-2 border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]", children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", iconLeft: /* @__PURE__ */ jsx(Icon, { name: "info", size: 14 }), onClick: () => setAsideOpen(true), "aria-expanded": asideOpen, children: asideTitle }) }) : null;
9931
- return /* @__PURE__ */ jsxs("div", { ref: rootRef, "data-split-view": "split", "data-frame": frame, ...region, className: surface, style: fillStyle, children: [
9932
- /* @__PURE__ */ jsxs(ResizablePanelGroup, { direction: "horizontal", stackOnMobile: false, className: "h-full", children: [
9933
- /* @__PURE__ */ jsx(ResizablePanel, { id: "list", defaultSize: `${listWidth}%`, minSize: `${listMinWidth}%`, children: /* @__PURE__ */ jsx("div", { "data-pane": "list", className: "h-full min-h-0 overflow-y-auto", children: list }) }),
9934
- /* @__PURE__ */ jsx(ResizableHandle, { withHandle: true }),
9935
- /* @__PURE__ */ jsx(ResizablePanel, { id: "detail", minSize: "30%", children: /* @__PURE__ */ jsxs("div", { "data-pane": "detail", className: "h-full min-h-0 flex flex-col", children: [
9936
- asideToggle,
9937
- /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0 overflow-y-auto", children: detail })
9938
- ] }) }),
9939
- aside && !asideCollapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
9940
- /* @__PURE__ */ jsx(ResizableHandle, { withHandle: true }),
9941
- /* @__PURE__ */ jsx(ResizablePanel, { id: "aside", defaultSize: `${asideWidth}%`, minSize: "15%", children: /* @__PURE__ */ jsx("div", { "data-pane": "aside", className: "h-full min-h-0 overflow-y-auto", children: aside }) })
9942
- ] })
9943
- ] }),
9944
- aside && asideCollapsed && /* @__PURE__ */ jsx(Drawer, { open: asideOpen, onClose: () => setAsideOpen(false), title: asideTitle, position: "right", children: aside })
9126
+ return /* @__PURE__ */ jsxs("div", { ref: rootRef, "data-settings-layout": "sidebar", className: cn("flex flex-row items-start gap-ds-8", className), children: [
9127
+ /* @__PURE__ */ jsx("nav", { "aria-label": ariaLabel, className: "sticky self-start shrink-0", style: { width: `${railWidth}px`, top: stickyTop }, children: /* @__PURE__ */ jsx(Tabs, { orientation: "vertical", tabs, activeTab: value, onTabChange: onChange, className: "w-full" }) }),
9128
+ /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children })
9945
9129
  ] });
9946
9130
  }
9947
9131
  function SeparatorIcon({ type, size }) {
@@ -10195,6 +9379,7 @@ function Popover({ open, onOpenChange, trigger, children, side = "bottom", align
10195
9379
  "div",
10196
9380
  {
10197
9381
  ref: floatingRef,
9382
+ "data-gs": "popover",
10198
9383
  className: cn(
10199
9384
  "fixed z-[var(--ds-z-popover)]",
10200
9385
  "bg-[var(--ds-bg-surface-elevated)]",
@@ -11494,11 +10679,23 @@ function isPageHeaderElement(node) {
11494
10679
  }
11495
10680
  var OVERLAP_MIN = 24;
11496
10681
  var OVERLAP_MAX = 64;
11497
- function overlapTarget(column) {
11498
- const first = column?.firstElementChild ?? null;
11499
- if (!first) return null;
11500
- const bare = first.tagName === "DIV" && !first.getAttribute("style") && !(first.getAttribute("class") ?? "").split(/\s+/).some((c) => /^(?:p[xytblr]?-|m[xytblr]?-|bg-|border|rounded|shadow|grid|@)/.test(c));
11501
- return bare && first.firstElementChild ? first.firstElementChild : first;
10682
+ var ROW_MAX = 56;
10683
+ function firstCardIn(el) {
10684
+ let cur = el;
10685
+ for (let depth = 0; cur && depth < 6; depth++) {
10686
+ if (cur.hasAttribute("data-card")) return cur;
10687
+ cur = cur.firstElementChild;
10688
+ }
10689
+ return null;
10690
+ }
10691
+ function overlapCard(column) {
10692
+ if (!column) return null;
10693
+ for (const child of Array.from(column.children)) {
10694
+ const card = firstCardIn(child);
10695
+ if (card) return card;
10696
+ if (child.getBoundingClientRect().height > ROW_MAX) return null;
10697
+ }
10698
+ return null;
11502
10699
  }
11503
10700
  function floatingBlockPadding(navbarFloating, sidebarFloating, overlap) {
11504
10701
  return {
@@ -11552,25 +10749,27 @@ var PageContainer = forwardRef(function PageContainer2({
11552
10749
  observedRef.current?.ro.disconnect();
11553
10750
  observedRef.current = null;
11554
10751
  };
11555
- if (!overlapActive) {
10752
+ const column2 = columnRef.current;
10753
+ const card = overlapActive ? overlapCard(column2) : null;
10754
+ if (!card || !column2) {
11556
10755
  stop();
11557
10756
  rootRef.current?.style.removeProperty("--ds-overlap");
10757
+ rootRef.current?.style.removeProperty("--ds-overlap-gap");
11558
10758
  return;
11559
10759
  }
11560
- const target = overlapTarget(columnRef.current);
11561
- if (!target) {
11562
- stop();
11563
- return;
11564
- }
11565
- if (observedRef.current?.target === target) return;
10760
+ if (observedRef.current?.target === card) return;
11566
10761
  stop();
11567
10762
  if (typeof ResizeObserver === "undefined") return;
11568
- const ro = new ResizeObserver(([entry]) => {
11569
- const h = entry.contentRect.height;
11570
- rootRef.current?.style.setProperty("--ds-overlap", `${Math.min(OVERLAP_MAX, Math.max(OVERLAP_MIN, Math.round(h / 2)))}px`);
10763
+ const ro = new ResizeObserver((entries) => {
10764
+ const cardHeight = card.getBoundingClientRect().height || (entries.find((e) => e.target === card)?.contentRect.height ?? 0);
10765
+ const rowsAbove = Math.max(0, Math.round(card.getBoundingClientRect().top - column2.getBoundingClientRect().top));
10766
+ const half = Math.min(OVERLAP_MAX, Math.max(OVERLAP_MIN, Math.round(cardHeight / 2)));
10767
+ rootRef.current?.style.setProperty("--ds-overlap", `${rowsAbove + half}px`);
10768
+ rootRef.current?.style.setProperty("--ds-overlap-gap", "0px");
11571
10769
  });
11572
- ro.observe(target);
11573
- observedRef.current = { target, ro };
10770
+ ro.observe(card);
10771
+ ro.observe(column2);
10772
+ observedRef.current = { target: card, ro };
11574
10773
  });
11575
10774
  useEffect(() => () => {
11576
10775
  observedRef.current?.ro.disconnect();
@@ -12052,6 +11251,7 @@ function ColourfulHeader({
12052
11251
  ) : isBold ? "border-b-[2px] border-[color-mix(in_srgb,var(--ds-accent-primary)_70%,black)]" : "border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
12053
11252
  className
12054
11253
  ),
11254
+ style: overlapActive && !isFloating && !noBottomMargin ? { marginBottom: "var(--ds-overlap-gap, var(--ds-space-6))" } : void 0,
12055
11255
  children: [
12056
11256
  backgroundImage && /* @__PURE__ */ jsxs(Fragment, { children: [
12057
11257
  /* @__PURE__ */ jsx(
@@ -12147,9 +11347,11 @@ function SnappingHeader({
12147
11347
  const stickyRef = useRef(null);
12148
11348
  const setStickyRef = useCallback((node) => {
12149
11349
  stickyRef.current = node;
11350
+ }, []);
11351
+ const mergedRef = headerRef ? (node) => {
11352
+ setStickyRef(node);
12150
11353
  if (typeof headerRef === "function") headerRef(node);
12151
- else if (headerRef) headerRef.current = node;
12152
- }, [headerRef]);
11354
+ } : setStickyRef;
12153
11355
  useLayoutEffect(() => {
12154
11356
  const report = container?.setPinnedHeight;
12155
11357
  if (!report) return;
@@ -12236,7 +11438,7 @@ function SnappingHeader({
12236
11438
  const headerEl = /* @__PURE__ */ jsxs(
12237
11439
  "header",
12238
11440
  {
12239
- ref: !isFloating ? setStickyRef : void 0,
11441
+ ref: !isFloating ? mergedRef : void 0,
12240
11442
  className: cn(
12241
11443
  "transition-all duration-[var(--ds-transition-speed)] ease-[var(--ds-transition-ease)]",
12242
11444
  !isFloating && pinned && collapsed && cn("sticky", stickyTop, "z-[calc(var(--ds-z-sticky,40)-1)]"),
@@ -12254,6 +11456,7 @@ function SnappingHeader({
12254
11456
  !collapsed && !isColourful && isFloating && !wrapperHasCard && FLOATING_HEADER,
12255
11457
  className
12256
11458
  ),
11459
+ style: !collapsed && isColourful && overlapActive && !(isFloating && !wrapperHasCard) && !wrapperHasCard ? { marginBottom: "var(--ds-overlap-gap, var(--ds-space-6))" } : void 0,
12257
11460
  children: [
12258
11461
  !collapsed && isColourful && backgroundImage && /* @__PURE__ */ jsxs(Fragment, { children: [
12259
11462
  /* @__PURE__ */ jsx(
@@ -12373,7 +11576,7 @@ function SnappingHeader({
12373
11576
  /* @__PURE__ */ jsx(
12374
11577
  "div",
12375
11578
  {
12376
- ref: setStickyRef,
11579
+ ref: mergedRef,
12377
11580
  className: cn(
12378
11581
  pinned && collapsed && "sticky z-[calc(var(--ds-z-sticky,40)-1)]",
12379
11582
  pinned && collapsed && "top-0"
@@ -12533,252 +11736,53 @@ function Dock({
12533
11736
  "cursor-pointer origin-bottom",
12534
11737
  "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]"
12535
11738
  ),
12536
- style: {
12537
- width: baseSize,
12538
- height: baseSize,
12539
- transition: "transform var(--ds-transition-speed) var(--ds-transition-ease)"
12540
- },
12541
- children: [
12542
- typeof item.icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: item.icon, size: iconSize }) : /* @__PURE__ */ jsx("span", { style: { fontSize: iconSize }, children: item.icon }),
12543
- item.badge !== void 0 && item.badge > 0 && /* @__PURE__ */ jsx(
12544
- "span",
12545
- {
12546
- className: cn(
12547
- "absolute -top-1.5 -right-1.5 flex items-center justify-center",
12548
- "min-w-[16px] h-4 px-ds-1 text-2xs font-bold rounded-[var(--ds-radius-full)]",
12549
- "bg-[var(--ds-status-error)] text-[var(--ds-text-on-error)]",
12550
- "pointer-events-none"
12551
- ),
12552
- children: item.badge > 99 ? "99+" : item.badge
12553
- }
12554
- )
12555
- ]
12556
- }
12557
- ),
12558
- /* @__PURE__ */ jsx(
12559
- "span",
12560
- {
12561
- ref: (el) => {
12562
- tooltipRefs.current[i] = el;
12563
- },
12564
- className: cn(
12565
- "absolute left-1/2 -translate-x-1/2 whitespace-nowrap text-2xs font-medium",
12566
- "bg-[var(--ds-bg-elevated)] text-[var(--ds-text-primary)]",
12567
- "px-ds-1.5 py-ds-0.5 rounded-[var(--ds-radius-sm)]",
12568
- "shadow-[var(--ds-shadow-button)]",
12569
- "pointer-events-none opacity-0",
12570
- "transition-opacity duration-[var(--ds-transition-speed)]"
12571
- ),
12572
- style: { bottom: maxSize + 4 },
12573
- children: item.label
12574
- }
12575
- )
12576
- ]
12577
- },
12578
- item.id
12579
- ))
12580
- }
12581
- );
12582
- }
12583
- function resolveNavItemType(type) {
12584
- return type === "default" ? "tint" : type;
12585
- }
12586
- var NavbarTypeContext = createContext("tint");
12587
- var NavItem = forwardRef(function NavItem2({ label, icon, active = false, href, onClick, type: typeProp, className, children }, ref) {
12588
- const contextType = useContext(NavbarTypeContext);
12589
- const type = resolveNavItemType(typeProp ?? contextType);
12590
- const baseClasses = cn(
12591
- "inline-flex items-center gap-ds-2 cursor-pointer select-none",
12592
- "transition-colors duration-[var(--ds-transition-speed)] ease-[var(--ds-transition-ease)]",
12593
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
12594
- "text-[length:var(--ds-font-size-sm)]"
12595
- );
12596
- const typeClasses = getTypeClasses(type, active);
12597
- return /* @__PURE__ */ jsxs(
12598
- "a",
12599
- {
12600
- ref,
12601
- href: href ?? "#",
12602
- onClick,
12603
- "aria-current": active ? "page" : void 0,
12604
- className: cn(baseClasses, typeClasses, className),
12605
- children: [
12606
- icon && /* @__PURE__ */ jsx(Icon, { name: icon, size: 16 }),
12607
- children ?? label,
12608
- type === "underline" && active && /* @__PURE__ */ jsx(
12609
- "span",
12610
- {
12611
- className: "absolute bottom-0 left-ds-1 right-ds-1 rounded-t-full bg-[var(--ds-accent-primary)]",
12612
- style: { height: "2px" }
12613
- }
12614
- )
12615
- ]
12616
- }
12617
- );
12618
- });
12619
- function getTypeClasses(type, active) {
12620
- switch (type) {
12621
- case "pill":
12622
- return cn(
12623
- "px-ds-2 py-ds-1 rounded-[var(--ds-radius-sm)]",
12624
- active ? "bg-[var(--ds-accent-primary)] text-[var(--ds-text-on-primary)] font-[var(--ds-weight-heading)]" : "text-[var(--ds-text-secondary)] hover:text-[var(--ds-text-primary)] hover:bg-[var(--ds-bg-muted)]"
12625
- );
12626
- case "underline":
12627
- return cn(
12628
- "px-ds-2 py-ds-2 relative",
12629
- active ? "text-[var(--ds-text-primary)] font-[var(--ds-weight-heading)]" : "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)]"
12630
- );
12631
- case "minimal":
12632
- return cn(
12633
- "px-ds-2 py-ds-1",
12634
- active ? "text-[var(--ds-accent-primary)] font-medium" : "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-secondary)]"
12635
- );
12636
- case "tint":
12637
- return cn(
12638
- "px-ds-2 py-ds-1 rounded-[var(--ds-radius-sm)]",
12639
- active ? "bg-[var(--ds-accent-subtle)] text-[var(--ds-accent-primary)] font-[var(--ds-weight-heading)]" : "text-[var(--ds-text-secondary)] hover:text-[var(--ds-text-primary)] hover:bg-[var(--ds-bg-muted)]"
12640
- );
12641
- }
12642
- }
12643
- function HamburgerButton({ onClick, open, className: cls }) {
12644
- return /* @__PURE__ */ jsx(
12645
- "button",
12646
- {
12647
- onClick,
12648
- "aria-label": open ? "Close navigation menu" : "Open navigation menu",
12649
- "aria-expanded": open ?? void 0,
12650
- className: cn(
12651
- "flex items-center justify-center shrink-0",
12652
- "w-10 h-10 rounded-[var(--ds-radius-md)]",
12653
- "text-[var(--ds-text-secondary)] hover:text-[var(--ds-text-primary)]",
12654
- "hover:bg-[var(--ds-bg-muted)] transition-colors",
12655
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
12656
- "cursor-pointer",
12657
- cls
12658
- ),
12659
- children: /* @__PURE__ */ jsx(Icon, { name: open ? "close" : "menu", size: 20 })
12660
- }
12661
- );
12662
- }
12663
- var Navbar = forwardRef(function Navbar2({
12664
- logo,
12665
- navLinks,
12666
- search,
12667
- actions,
12668
- userMenu,
12669
- variant = "default",
12670
- type: typeProp = "tint",
12671
- align = "left",
12672
- size,
12673
- position = "flush",
12674
- bordered,
12675
- sticky = true,
12676
- className,
12677
- onMenuClick
12678
- }, ref) {
12679
- const appShell = useAppShell();
12680
- useLayoutEffect(() => appShell?.registerChrome("navbar", position), [appShell?.registerChrome, position]);
12681
- const isSmallPhone = appShell?.isMobile && !appShell.isTablet;
12682
- const showHamburger = appShell?.isMobile && appShell.hasMobileMenu && !(isSmallPhone && appShell.hasMobileNav) || !!onMenuClick;
12683
- const handleHamburger = () => onMenuClick ? onMenuClick() : appShell?.setSidebarOpen(!appShell.sidebarOpen);
12684
- const hideNavLinks = appShell?.isMobile && (appShell.hasMobileMenu || appShell.hasMobileNav);
12685
- const type = resolveNavItemType(typeProp);
12686
- const isMinimal = type === "minimal";
12687
- const isFloating = position === "floating";
12688
- const resolvedSize = size ?? (variant === "compact" ? "sm" : "md");
12689
- const heightClass = resolvedSize === "sm" ? "min-h-[var(--ds-navbar-height-compact)]" : resolvedSize === "lg" ? "min-h-[var(--ds-size-14,3.5rem)]" : "min-h-[var(--ds-navbar-height)]";
12690
- const hasBorder = bordered ?? (!isFloating && variant !== "transparent" && !isMinimal);
12691
- const bgClasses = isMinimal ? "bg-transparent" : variant === "transparent" ? "bg-transparent" : variant === "elevated" ? cn(
12692
- "bg-[var(--ds-bg-surface)]",
12693
- "text-[var(--ds-text-primary)]",
12694
- "shadow-[var(--ds-shadow-elevated)]",
12695
- hasBorder && "border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]"
12696
- ) : variant === "blurred" ? cn(
12697
- "bg-[var(--ds-bg-canvas)]/80 [backdrop-filter:var(--ds-glass-filter)] [-webkit-backdrop-filter:var(--ds-glass-filter)]",
12698
- "text-[var(--ds-text-primary)]",
12699
- hasBorder && "border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]"
12700
- ) : cn(
12701
- "bg-[var(--ds-bg-surface)]",
12702
- "text-[var(--ds-text-primary)]",
12703
- hasBorder && "border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
12704
- GLASS_BACKDROP
12705
- );
12706
- const isMobile = appShell?.isMobile;
12707
- const padStyle = resolvedSize === "sm" ? "var(--ds-space-2) var(--ds-space-4)" : resolvedSize === "lg" ? `var(--ds-space-2) var(${isMobile ? "--ds-space-3" : "--ds-space-6"})` : `var(--ds-space-1) var(${isMobile ? "--ds-space-3" : "--ds-space-6"})`;
12708
- const floatingClasses = isFloating ? cn("rounded-[var(--ds-radius-md)] border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]", GLASS_SURFACE) : void 0;
12709
- let headerEl;
12710
- if (align === "center") {
12711
- headerEl = /* @__PURE__ */ jsxs(
12712
- "header",
12713
- {
12714
- ref: isFloating ? void 0 : ref,
12715
- className: cn(
12716
- "shrink-0 grid grid-cols-[1fr_auto_1fr] items-center z-[var(--ds-z-sticky)]",
12717
- !isFloating && sticky && "sticky top-0",
12718
- bgClasses,
12719
- heightClass,
12720
- floatingClasses,
12721
- !isFloating && className
12722
- ),
12723
- style: { padding: padStyle, gap: "var(--ds-space-4)" },
12724
- children: [
12725
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-4", children: [
12726
- showHamburger && /* @__PURE__ */ jsx(HamburgerButton, { onClick: handleHamburger, open: appShell?.sidebarOpen }),
12727
- logo && /* @__PURE__ */ jsx("div", { className: "flex items-center shrink-0", children: logo })
12728
- ] }),
12729
- navLinks && !hideNavLinks ? /* @__PURE__ */ jsx(NavbarTypeContext.Provider, { value: type, children: /* @__PURE__ */ jsx("nav", { className: "flex items-center gap-ds-2 min-w-0 overflow-x-clip justify-center", "data-navbar-type": type, children: navLinks }) }) : /* @__PURE__ */ jsx("div", {}),
12730
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-4 justify-end", children: [
12731
- search && /* @__PURE__ */ jsx("div", { className: "flex items-center shrink-0", children: search }),
12732
- actions && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-ds-2 shrink-0", children: actions }),
12733
- userMenu && /* @__PURE__ */ jsx("div", { className: cn("flex items-center", resolvedSize === "sm" ? "min-w-0 max-w-[180px]" : "shrink-0"), children: userMenu })
12734
- ] })
12735
- ]
12736
- }
12737
- );
12738
- } else {
12739
- headerEl = /* @__PURE__ */ jsxs(
12740
- "header",
12741
- {
12742
- ref: isFloating ? void 0 : ref,
12743
- className: cn(
12744
- "shrink-0 flex items-center z-[var(--ds-z-sticky)]",
12745
- !isFloating && sticky && "sticky top-0",
12746
- bgClasses,
12747
- heightClass,
12748
- floatingClasses,
12749
- !isFloating && className
12750
- ),
12751
- style: { padding: padStyle, gap: "var(--ds-space-4)" },
12752
- children: [
12753
- showHamburger && /* @__PURE__ */ jsx(HamburgerButton, { onClick: handleHamburger, open: appShell?.sidebarOpen }),
12754
- logo && /* @__PURE__ */ jsx("div", { className: "flex items-center shrink-0", children: logo }),
12755
- align === "right" && /* @__PURE__ */ jsx("div", { className: "flex-1" }),
12756
- navLinks && !hideNavLinks && /* @__PURE__ */ jsx(NavbarTypeContext.Provider, { value: type, children: /* @__PURE__ */ jsx("nav", { className: cn("flex items-center gap-ds-2 min-w-0 overflow-x-clip", align === "left" && "flex-1"), "data-navbar-type": type, children: navLinks }) }),
12757
- (!navLinks || hideNavLinks) && /* @__PURE__ */ jsx("div", { className: "flex-1" }),
12758
- search && /* @__PURE__ */ jsx("div", { className: "flex items-center shrink-0", children: search }),
12759
- actions && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-ds-2 shrink-0", children: actions }),
12760
- userMenu && /* @__PURE__ */ jsx("div", { className: cn("flex items-center", resolvedSize === "sm" ? "min-w-0 max-w-[180px]" : "shrink-0"), children: userMenu })
12761
- ]
12762
- }
12763
- );
12764
- }
12765
- if (isFloating) {
12766
- return /* @__PURE__ */ jsx(
12767
- "div",
12768
- {
12769
- ref,
12770
- className: cn(
12771
- "shrink-0 z-[var(--ds-z-sticky)]",
12772
- sticky && "sticky top-0",
12773
- className
12774
- ),
12775
- style: { padding: "var(--ds-space-3)" },
12776
- children: headerEl
12777
- }
12778
- );
12779
- }
12780
- return headerEl;
12781
- });
11739
+ style: {
11740
+ width: baseSize,
11741
+ height: baseSize,
11742
+ transition: "transform var(--ds-transition-speed) var(--ds-transition-ease)"
11743
+ },
11744
+ children: [
11745
+ typeof item.icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: item.icon, size: iconSize }) : /* @__PURE__ */ jsx("span", { style: { fontSize: iconSize }, children: item.icon }),
11746
+ item.badge !== void 0 && item.badge > 0 && /* @__PURE__ */ jsx(
11747
+ "span",
11748
+ {
11749
+ className: cn(
11750
+ "absolute -top-1.5 -right-1.5 flex items-center justify-center",
11751
+ "min-w-[16px] h-4 px-ds-1 text-2xs font-bold rounded-[var(--ds-radius-full)]",
11752
+ "bg-[var(--ds-status-error)] text-[var(--ds-text-on-error)]",
11753
+ "pointer-events-none"
11754
+ ),
11755
+ children: item.badge > 99 ? "99+" : item.badge
11756
+ }
11757
+ )
11758
+ ]
11759
+ }
11760
+ ),
11761
+ /* @__PURE__ */ jsx(
11762
+ "span",
11763
+ {
11764
+ ref: (el) => {
11765
+ tooltipRefs.current[i] = el;
11766
+ },
11767
+ className: cn(
11768
+ "absolute left-1/2 -translate-x-1/2 whitespace-nowrap text-2xs font-medium",
11769
+ "bg-[var(--ds-bg-elevated)] text-[var(--ds-text-primary)]",
11770
+ "px-ds-1.5 py-ds-0.5 rounded-[var(--ds-radius-sm)]",
11771
+ "shadow-[var(--ds-shadow-button)]",
11772
+ "pointer-events-none opacity-0",
11773
+ "transition-opacity duration-[var(--ds-transition-speed)]"
11774
+ ),
11775
+ style: { bottom: maxSize + 4 },
11776
+ children: item.label
11777
+ }
11778
+ )
11779
+ ]
11780
+ },
11781
+ item.id
11782
+ ))
11783
+ }
11784
+ );
11785
+ }
12782
11786
  function TruncSpan({ children, className }) {
12783
11787
  const { ref, isTruncated } = useTextOverflow({ lines: 1 });
12784
11788
  const el = /* @__PURE__ */ jsx("span", { ref, className: cn("truncate", className), children });
@@ -12888,6 +11892,7 @@ function NavMenu({
12888
11892
  {
12889
11893
  ref: panelRef,
12890
11894
  role: "menu",
11895
+ "data-gs": "menu",
12891
11896
  className: cn(
12892
11897
  "fixed z-[var(--ds-z-popover)] py-ds-1.5",
12893
11898
  "bg-[var(--ds-bg-surface)] rounded-[var(--ds-radius-md)]",
@@ -12988,6 +11993,286 @@ function NavMenuGroup({
12988
11993
  ))
12989
11994
  ] });
12990
11995
  }
11996
+ function resolveNavItemType(type) {
11997
+ return type === "default" ? "tint" : type;
11998
+ }
11999
+ var NavbarTypeContext = createContext("tint");
12000
+ var NavItem = forwardRef(function NavItem2({ label, icon, active = false, href, onClick, type: typeProp, className, children }, ref) {
12001
+ const contextType = useContext(NavbarTypeContext);
12002
+ const type = resolveNavItemType(typeProp ?? contextType);
12003
+ const baseClasses = cn(
12004
+ "inline-flex items-center gap-ds-2 cursor-pointer select-none whitespace-nowrap",
12005
+ // one line: the navbar measures each link (round 12)
12006
+ "transition-colors duration-[var(--ds-transition-speed)] ease-[var(--ds-transition-ease)]",
12007
+ "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
12008
+ "text-[length:var(--ds-font-size-sm)]"
12009
+ );
12010
+ const typeClasses = getTypeClasses(type, active);
12011
+ return /* @__PURE__ */ jsxs(
12012
+ "a",
12013
+ {
12014
+ ref,
12015
+ href: href ?? "#",
12016
+ onClick,
12017
+ "aria-current": active ? "page" : void 0,
12018
+ className: cn(baseClasses, typeClasses, className),
12019
+ children: [
12020
+ icon && /* @__PURE__ */ jsx(Icon, { name: icon, size: 16 }),
12021
+ children ?? label,
12022
+ type === "underline" && active && /* @__PURE__ */ jsx(
12023
+ "span",
12024
+ {
12025
+ className: "absolute bottom-0 left-ds-1 right-ds-1 rounded-t-full bg-[var(--ds-accent-primary)]",
12026
+ style: { height: "2px" }
12027
+ }
12028
+ )
12029
+ ]
12030
+ }
12031
+ );
12032
+ });
12033
+ function getTypeClasses(type, active) {
12034
+ switch (type) {
12035
+ case "pill":
12036
+ return cn(
12037
+ "px-ds-2 py-ds-1 rounded-[var(--ds-radius-sm)]",
12038
+ active ? "bg-[var(--ds-accent-primary)] text-[var(--ds-text-on-primary)] font-[var(--ds-weight-heading)]" : "text-[var(--ds-text-secondary)] hover:text-[var(--ds-text-primary)] hover:bg-[var(--ds-bg-muted)]"
12039
+ );
12040
+ case "underline":
12041
+ return cn(
12042
+ "px-ds-2 py-ds-2 relative",
12043
+ active ? "text-[var(--ds-text-primary)] font-[var(--ds-weight-heading)]" : "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)]"
12044
+ );
12045
+ case "minimal":
12046
+ return cn(
12047
+ "px-ds-2 py-ds-1",
12048
+ active ? "text-[var(--ds-accent-primary)] font-medium" : "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-secondary)]"
12049
+ );
12050
+ case "tint":
12051
+ return cn(
12052
+ "px-ds-2 py-ds-1 rounded-[var(--ds-radius-sm)]",
12053
+ active ? "bg-[var(--ds-accent-subtle)] text-[var(--ds-accent-primary)] font-[var(--ds-weight-heading)]" : "text-[var(--ds-text-secondary)] hover:text-[var(--ds-text-primary)] hover:bg-[var(--ds-bg-muted)]"
12054
+ );
12055
+ }
12056
+ }
12057
+ function HamburgerButton({ onClick, open, className: cls }) {
12058
+ return /* @__PURE__ */ jsx(
12059
+ "button",
12060
+ {
12061
+ onClick,
12062
+ "aria-label": open ? "Close navigation menu" : "Open navigation menu",
12063
+ "aria-expanded": open ?? void 0,
12064
+ className: cn(
12065
+ "flex items-center justify-center shrink-0",
12066
+ "w-10 h-10 rounded-[var(--ds-radius-md)]",
12067
+ "text-[var(--ds-text-secondary)] hover:text-[var(--ds-text-primary)]",
12068
+ "hover:bg-[var(--ds-bg-muted)] transition-colors",
12069
+ "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
12070
+ "cursor-pointer",
12071
+ cls
12072
+ ),
12073
+ children: /* @__PURE__ */ jsx(Icon, { name: open ? "close" : "menu", size: 20 })
12074
+ }
12075
+ );
12076
+ }
12077
+ function flattenLinks(node) {
12078
+ const out = [];
12079
+ Children.forEach(node, (child) => {
12080
+ if (!isValidElement(child)) return;
12081
+ if (child.type === Fragment$1) out.push(...flattenLinks(child.props.children));
12082
+ else out.push(child);
12083
+ });
12084
+ return out;
12085
+ }
12086
+ var Navbar = forwardRef(function Navbar2({
12087
+ logo,
12088
+ navLinks,
12089
+ search,
12090
+ actions,
12091
+ userMenu,
12092
+ variant = "default",
12093
+ type: typeProp = "tint",
12094
+ align = "left",
12095
+ size,
12096
+ position = "flush",
12097
+ bordered,
12098
+ sticky = true,
12099
+ className,
12100
+ onMenuClick
12101
+ }, ref) {
12102
+ const appShell = useAppShell();
12103
+ useLayoutEffect(() => appShell?.registerChrome("navbar", position), [appShell?.registerChrome, position]);
12104
+ const isSmallPhone = appShell?.isMobile && !appShell.isTablet;
12105
+ const headerRef = useRef(null);
12106
+ const navRef = useRef(null);
12107
+ const widthsRef = useRef([]);
12108
+ const moreWidthRef = useRef(88);
12109
+ const [visibleCount, setVisibleCount] = useState(null);
12110
+ const links = useMemo(() => flattenLinks(navLinks), [navLinks]);
12111
+ const mobileHidesLinks = !!appShell?.isMobile && (appShell.hasMobileMenu || appShell.hasMobileNav);
12112
+ useEffect(() => {
12113
+ const header = headerRef.current;
12114
+ if (!header || mobileHidesLinks || links.length === 0 || typeof ResizeObserver === "undefined") return;
12115
+ const measure = () => {
12116
+ const nav = navRef.current;
12117
+ if (!nav) return;
12118
+ const items = Array.from(nav.children);
12119
+ for (let i = 0; i < links.length; i++) {
12120
+ const el = items[i];
12121
+ if (el && !el.hasAttribute("hidden") && el.offsetWidth) widthsRef.current[i] = el.offsetWidth;
12122
+ }
12123
+ const more = nav.querySelector("[data-navbar-more]");
12124
+ if (more?.offsetWidth) moreWidthRef.current = more.offsetWidth;
12125
+ const widths = widthsRef.current;
12126
+ if (widths.length < links.length || widths.some((w) => !w)) return;
12127
+ const style = getComputedStyle(header);
12128
+ const gap = parseFloat(style.columnGap) || parseFloat(style.gap) || 0;
12129
+ const kids = Array.from(header.children).filter((el) => el !== nav && !el.hasAttribute("data-navbar-spacer"));
12130
+ const used = kids.reduce((w, el) => w + Math.max(el.offsetWidth, el.scrollWidth), 0);
12131
+ const available = header.clientWidth - (parseFloat(style.paddingLeft) || 0) - (parseFloat(style.paddingRight) || 0) - used - gap * kids.length;
12132
+ const linkGap = parseFloat(getComputedStyle(nav).columnGap) || 8;
12133
+ const total = widths.reduce((a, w) => a + w, 0) + linkGap * (links.length - 1);
12134
+ if (total <= available) {
12135
+ setVisibleCount(null);
12136
+ return;
12137
+ }
12138
+ const room = available - moreWidthRef.current - linkGap;
12139
+ let n = 0, acc = 0;
12140
+ for (const w of widths) {
12141
+ const next = acc + (n ? linkGap : 0) + w;
12142
+ if (next > room) break;
12143
+ acc = next;
12144
+ n++;
12145
+ }
12146
+ setVisibleCount(n);
12147
+ };
12148
+ const ro = new ResizeObserver(measure);
12149
+ ro.observe(header);
12150
+ return () => ro.disconnect();
12151
+ }, [mobileHidesLinks, links]);
12152
+ const hidden = useMemo(() => {
12153
+ if (visibleCount === null || links.length === 0) return /* @__PURE__ */ new Set();
12154
+ return new Set(links.map((_, i) => i).filter((i) => i >= visibleCount));
12155
+ }, [visibleCount, links]);
12156
+ const showHamburger = appShell?.isMobile && appShell.hasMobileMenu && !(isSmallPhone && appShell.hasMobileNav) || !!onMenuClick;
12157
+ const handleHamburger = () => onMenuClick ? onMenuClick() : appShell?.setSidebarOpen(!appShell.sidebarOpen);
12158
+ const hideNavLinks = mobileHidesLinks;
12159
+ const type = resolveNavItemType(typeProp);
12160
+ const isMinimal = type === "minimal";
12161
+ const isFloating = position === "floating";
12162
+ const moreItems = links.filter((_, i) => hidden.has(i)).map((el) => {
12163
+ const p = el.props;
12164
+ return { label: p.label ?? "", href: p.href, active: p.active, icon: p.icon };
12165
+ });
12166
+ const linkRow = links.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
12167
+ links.map((el, i) => hidden.has(i) ? /* @__PURE__ */ jsx("span", { hidden: true, children: el }, el.key ?? i) : el),
12168
+ hidden.size > 0 && /* @__PURE__ */ jsx("div", { "data-navbar-more": "", className: "shrink-0", children: /* @__PURE__ */ jsx(
12169
+ NavMenu,
12170
+ {
12171
+ label: "More",
12172
+ items: moreItems,
12173
+ active: moreItems.some((m) => m.active),
12174
+ type,
12175
+ onItemClick: (item) => {
12176
+ const el = links.find((l) => l.props.label === item.label);
12177
+ el?.props?.onClick?.({});
12178
+ }
12179
+ }
12180
+ ) })
12181
+ ] }) : navLinks;
12182
+ const resolvedSize = size ?? (variant === "compact" ? "sm" : "md");
12183
+ const heightClass = resolvedSize === "sm" ? "min-h-[var(--ds-navbar-height-compact)]" : resolvedSize === "lg" ? "min-h-[var(--ds-size-14,3.5rem)]" : "min-h-[var(--ds-navbar-height)]";
12184
+ const hasBorder = bordered ?? (!isFloating && variant !== "transparent" && !isMinimal);
12185
+ const bgClasses = isMinimal ? "bg-transparent" : variant === "transparent" ? "bg-transparent" : variant === "elevated" ? cn(
12186
+ "bg-[var(--ds-bg-surface)]",
12187
+ "text-[var(--ds-text-primary)]",
12188
+ "shadow-[var(--ds-shadow-elevated)]",
12189
+ hasBorder && "border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]"
12190
+ ) : variant === "blurred" ? cn(
12191
+ "bg-[var(--ds-bg-canvas)]/80 [backdrop-filter:var(--ds-glass-filter)] [-webkit-backdrop-filter:var(--ds-glass-filter)]",
12192
+ "text-[var(--ds-text-primary)]",
12193
+ hasBorder && "border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]"
12194
+ ) : cn(
12195
+ "bg-[var(--ds-bg-surface)]",
12196
+ "text-[var(--ds-text-primary)]",
12197
+ hasBorder && "border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
12198
+ GLASS_BACKDROP
12199
+ );
12200
+ const isMobile = appShell?.isMobile;
12201
+ const padStyle = resolvedSize === "sm" ? "var(--ds-space-2) var(--ds-space-4)" : resolvedSize === "lg" ? `var(--ds-space-2) var(${isMobile ? "--ds-space-3" : "--ds-space-6"})` : `var(--ds-space-1) var(${isMobile ? "--ds-space-3" : "--ds-space-6"})`;
12202
+ const floatingClasses = isFloating ? cn("rounded-[var(--ds-radius-md)] border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]", GLASS_SURFACE) : void 0;
12203
+ let headerEl;
12204
+ if (align === "center") {
12205
+ headerEl = /* @__PURE__ */ jsxs(
12206
+ "header",
12207
+ {
12208
+ ref: mergeRefs(isFloating ? void 0 : ref, headerRef),
12209
+ className: cn(
12210
+ "shrink-0 grid grid-cols-[1fr_auto_1fr] items-center z-[var(--ds-z-sticky)]",
12211
+ !isFloating && sticky && "sticky top-0",
12212
+ bgClasses,
12213
+ heightClass,
12214
+ floatingClasses,
12215
+ !isFloating && className
12216
+ ),
12217
+ style: { padding: padStyle, gap: "var(--ds-space-4)" },
12218
+ children: [
12219
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-4 min-w-max", children: [
12220
+ showHamburger && /* @__PURE__ */ jsx(HamburgerButton, { onClick: handleHamburger, open: appShell?.sidebarOpen }),
12221
+ logo && /* @__PURE__ */ jsx("div", { className: "flex items-center shrink-0 whitespace-nowrap", children: logo })
12222
+ ] }),
12223
+ navLinks && !hideNavLinks ? /* @__PURE__ */ jsx(NavbarTypeContext.Provider, { value: type, children: /* @__PURE__ */ jsx("nav", { ref: navRef, className: "flex items-center gap-ds-2 min-w-0 overflow-x-clip justify-center", "data-navbar-type": type, children: linkRow }) }) : /* @__PURE__ */ jsx("div", { "data-navbar-spacer": "" }),
12224
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-4 justify-end", children: [
12225
+ search && /* @__PURE__ */ jsx("div", { className: "flex items-center shrink-0", children: search }),
12226
+ actions && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-ds-2 shrink-0", children: actions }),
12227
+ userMenu && /* @__PURE__ */ jsx("div", { className: cn("flex items-center", resolvedSize === "sm" ? "min-w-0 max-w-[180px]" : "shrink-0"), children: userMenu })
12228
+ ] })
12229
+ ]
12230
+ }
12231
+ );
12232
+ } else {
12233
+ headerEl = /* @__PURE__ */ jsxs(
12234
+ "header",
12235
+ {
12236
+ ref: mergeRefs(isFloating ? void 0 : ref, headerRef),
12237
+ className: cn(
12238
+ "shrink-0 flex items-center z-[var(--ds-z-sticky)]",
12239
+ !isFloating && sticky && "sticky top-0",
12240
+ bgClasses,
12241
+ heightClass,
12242
+ floatingClasses,
12243
+ !isFloating && className
12244
+ ),
12245
+ style: { padding: padStyle, gap: "var(--ds-space-4)" },
12246
+ children: [
12247
+ showHamburger && /* @__PURE__ */ jsx(HamburgerButton, { onClick: handleHamburger, open: appShell?.sidebarOpen }),
12248
+ logo && /* @__PURE__ */ jsx("div", { className: "flex items-center shrink-0 whitespace-nowrap", children: logo }),
12249
+ align === "right" && /* @__PURE__ */ jsx("div", { "data-navbar-spacer": "", className: "flex-1" }),
12250
+ navLinks && !hideNavLinks && /* @__PURE__ */ jsx(NavbarTypeContext.Provider, { value: type, children: /* @__PURE__ */ jsx("nav", { ref: navRef, className: cn("flex items-center gap-ds-2 min-w-0 overflow-x-clip", align === "left" && "flex-1"), "data-navbar-type": type, children: linkRow }) }),
12251
+ (!navLinks || hideNavLinks) && /* @__PURE__ */ jsx("div", { "data-navbar-spacer": "", className: "flex-1" }),
12252
+ search && /* @__PURE__ */ jsx("div", { className: "flex items-center shrink-0", children: search }),
12253
+ actions && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-ds-2 shrink-0", children: actions }),
12254
+ userMenu && /* @__PURE__ */ jsx("div", { className: cn("flex items-center", resolvedSize === "sm" ? "min-w-0 max-w-[180px]" : "shrink-0"), children: userMenu })
12255
+ ]
12256
+ }
12257
+ );
12258
+ }
12259
+ if (isFloating) {
12260
+ return /* @__PURE__ */ jsx(
12261
+ "div",
12262
+ {
12263
+ ref,
12264
+ className: cn(
12265
+ "shrink-0 z-[var(--ds-z-sticky)]",
12266
+ sticky && "sticky top-0",
12267
+ className
12268
+ ),
12269
+ style: { padding: "var(--ds-space-3)" },
12270
+ children: headerEl
12271
+ }
12272
+ );
12273
+ }
12274
+ return headerEl;
12275
+ });
12991
12276
  function MobileHeaderMenu({ items, type = "tint", onItemClick, className }) {
12992
12277
  return /* @__PURE__ */ jsx("nav", { className: cn("flex flex-col", className), style: { padding: "var(--ds-space-4)" }, children: /* @__PURE__ */ jsx("div", { className: "flex flex-col", style: { gap: type === "underline" ? 0 : "var(--ds-space-1)" }, children: items.map((item, i) => /* @__PURE__ */ jsx(
12993
12278
  MobileMenuItem,
@@ -13175,6 +12460,7 @@ function Dialog({ open, onClose: onCloseProp, title, description, children, foot
13175
12460
  ref: dialogRef,
13176
12461
  role: alert ? "alertdialog" : "dialog",
13177
12462
  "aria-modal": "true",
12463
+ "data-gs": "dialog",
13178
12464
  "aria-labelledby": titleId,
13179
12465
  "aria-describedby": description ? descriptionId : void 0,
13180
12466
  style: { containerType: "inline-size" },
@@ -14076,7 +13362,7 @@ function getPopoverPosition(rect, placement, padding) {
14076
13362
  top = Math.max(8, Math.min(top, window.innerHeight - popH - 8));
14077
13363
  return { top, left };
14078
13364
  }
14079
- var variantStyles5 = {
13365
+ var variantStyles4 = {
14080
13366
  default: "border-[var(--ds-border-default)] bg-[var(--ds-bg-surface)]",
14081
13367
  success: "border-[var(--ds-status-success)] bg-[var(--ds-status-success-bg)]",
14082
13368
  warning: "border-[var(--ds-status-warning)] bg-[var(--ds-status-warning-bg)]",
@@ -14147,7 +13433,7 @@ function Toast({ variant = "default", message, visible, onDismiss, duration = 5e
14147
13433
  GLASS_DROPDOWN,
14148
13434
  "rounded-[var(--ds-radius-md)]",
14149
13435
  "text-sm text-[var(--ds-text-primary)]",
14150
- variantStyles5[variant]
13436
+ variantStyles4[variant]
14151
13437
  ),
14152
13438
  children: [
14153
13439
  /* @__PURE__ */ jsx(Icon, { name: icon.name, size: 18, className: cn("shrink-0", icon.className) }),
@@ -14280,35 +13566,42 @@ var SectionHeading = forwardRef(function SectionHeading2({
14280
13566
  description,
14281
13567
  actions,
14282
13568
  badge,
13569
+ count,
14283
13570
  tabs,
14284
13571
  bordered = false,
13572
+ variant = "section",
14285
13573
  className
14286
13574
  }, ref) {
14287
13575
  const isPhone = useBreakpoint("sm");
13576
+ const pane = variant === "pane";
14288
13577
  return /* @__PURE__ */ jsxs(
14289
13578
  "div",
14290
13579
  {
14291
13580
  ref,
13581
+ "data-variant": variant,
14292
13582
  className: cn(
14293
- "mb-[var(--ds-heading-gap)]",
14294
- bordered && "border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)] pb-ds-4",
13583
+ pane ? "shrink-0 px-ds-4 pt-ds-4 pb-ds-3 border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]" : "mb-[var(--ds-heading-gap)]",
13584
+ !pane && bordered && "border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)] pb-ds-4",
14295
13585
  className
14296
13586
  ),
14297
13587
  children: [
14298
13588
  /* @__PURE__ */ jsxs("div", { className: cn(
14299
13589
  "flex gap-ds-4",
14300
- isPhone && actions ? "flex-col" : "items-start justify-between"
13590
+ // the actions wrap under the title when the two do not fit on one line (round 11: a search Input in a 284 px
13591
+ // pane's actions slot squeezed "Inbox" to "I."); below sm they always stack
13592
+ isPhone && actions ? "flex-col" : "flex-wrap items-start justify-between"
14301
13593
  ), children: [
14302
13594
  /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
14303
13595
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-2", children: [
14304
13596
  /* @__PURE__ */ jsx("h2", { className: "font-[family-name:var(--ds-font-display)] text-lg font-[var(--ds-weight-heading)] text-[var(--ds-text-primary)] truncate", children: title }),
14305
- badge
13597
+ badge,
13598
+ count !== void 0 && /* @__PURE__ */ jsx(Badge, { variant: "neutral", size: "sm", children: count })
14306
13599
  ] }),
14307
13600
  description && /* @__PURE__ */ jsx("p", { className: "mt-ds-1 text-sm text-[var(--ds-text-secondary)] line-clamp-2", children: description })
14308
13601
  ] }),
14309
- actions && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-ds-2 shrink-0", children: actions })
13602
+ actions && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-ds-2 shrink-0 max-w-full", children: pane ? /* @__PURE__ */ jsx(ControlSizeContext.Provider, { value: "sm", children: actions }) : actions })
14310
13603
  ] }),
14311
- tabs && /* @__PURE__ */ jsx("div", { className: "mt-ds-3", children: tabs })
13604
+ tabs && /* @__PURE__ */ jsx("div", { className: "mt-ds-3", children: pane ? /* @__PURE__ */ jsx(ControlSizeContext.Provider, { value: "sm", children: tabs }) : tabs })
14312
13605
  ]
14313
13606
  }
14314
13607
  );
@@ -14334,7 +13627,7 @@ var ActionPanel = forwardRef(function ActionPanel2({
14334
13627
  /* @__PURE__ */ jsx("p", { id: titleId, className: "text-sm font-medium text-[var(--ds-text-primary)]", children: title }),
14335
13628
  description && /* @__PURE__ */ jsx("p", { className: "mt-ds-1 text-sm text-[var(--ds-text-secondary)]", children: description })
14336
13629
  ] }),
14337
- /* @__PURE__ */ jsx("div", { className: "shrink-0", "aria-labelledby": titleId, children: action })
13630
+ /* @__PURE__ */ jsx("div", { className: "shrink-0 w-full @sm:w-auto @max-sm:[&>*]:max-w-none", "aria-labelledby": titleId, children: action })
14338
13631
  ]
14339
13632
  }
14340
13633
  );
@@ -14407,6 +13700,6 @@ var TextOverflow = forwardRef(function TextOverflow2({
14407
13700
  return /* @__PURE__ */ jsx(Tooltip, { content: resolvedTooltipContent, children: content });
14408
13701
  });
14409
13702
 
14410
- export { Accordion, ActionPanel, ActionPanelGroup, Alert, AppShell, AsidePanel, Avatar, AvatarGroup, BottomNav, Breadcrumbs, BulkActionBar, Button, ButtonSizeContext, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, ChatMessageList, ChatPanel, Checkbox, CheckboxGroup, CheckboxGroupItem, Collapsible, ColorPicker, Combobox, CommandPalette, ContextMenu, ControlSizeContext, CornerPanel, DEFAULT_DATE_RANGE_PRESETS, DataList, DatePicker, DateRangePicker, DescriptionList, Dialog, Dock, Drawer, Dropdown, DropdownMenu, EmptyState, EntityCell, Field, FileUpload, FormWrapper, HoverCard, InlineFieldError, Input, Kbd, Label, Launcher, MentionInput, MobileHeaderMenu, NavItem, NavMenu, Navbar, NotificationCenter, NumberInput, PageContainer, PageHeader, Pagination, PinInput, Popover, Progress, RadioGroup, Rating, ScrollArea, SectionHeading, SegmentedControl, SettingsLayout, Sheet, Sidebar, SidebarNav, SkipLink, SliderRange, Spinner, SplitView, StackedList, StatsCard, StatusDot, Stepper, Table, TableToolbar, Tabs, Tag, TagInput, TextOverflow, Textarea, TimePicker, Timeline, Toast, ToastProvider, Toggle, Tour, TransferList, TreeView, useAppShell, useFormContext, useSidebar, useToast };
13703
+ export { Accordion, ActionPanel, ActionPanelGroup, Alert, AsidePanel, Avatar, AvatarGroup, BottomNav, Breadcrumbs, BulkActionBar, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, ChatMessageList, ChatPanel, Checkbox, CheckboxGroup, CheckboxGroupItem, Collapsible, ColorPicker, Combobox, CommandPalette, ContextMenu, CornerPanel, DEFAULT_DATE_RANGE_PRESETS, DataList, DatePicker, DateRangePicker, DescriptionList, Dialog, Dock, Dropdown, DropdownMenu, EmptyState, EntityCell, Field, FileUpload, FormWrapper, HoverCard, InlineFieldError, Input, Kbd, Label, Launcher, MentionInput, MobileHeaderMenu, NavItem, NavMenu, Navbar, NotificationCenter, NumberInput, PageContainer, PageHeader, Pagination, PinInput, Popover, Progress, RadioGroup, Rating, ScrollArea, SectionHeading, SegmentedControl, SettingsLayout, Sidebar, SidebarNav, SliderRange, StackedList, StatsCard, StatusDot, Stepper, Table, TableToolbar, Tabs, Tag, TagInput, TextOverflow, Textarea, TimePicker, Timeline, Toast, ToastProvider, Toggle, Tour, TransferList, TreeView, useFormContext, useSidebar, useToast };
14411
13704
  //# sourceMappingURL=index.js.map
14412
13705
  //# sourceMappingURL=index.js.map