gridsmith-ui 0.6.7 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,18 +1,19 @@
1
+ import { ResizablePanelGroup, ResizablePanel, ResizableHandle } from './chunk-5B2ZFL2H.js';
1
2
  import { Skeleton } from './chunk-M3AARGLI.js';
2
3
  export { Skeleton } from './chunk-M3AARGLI.js';
3
4
  import { useOverlayLayer, useCloseOnFocusOut, useReturnFocus, focusInitial, useFocusTrap, OVERLAY_CLOSE_ATTR, useNonModalDialog, rovingTabStop } from './chunk-RLUK575N.js';
4
- import { Link } from './chunk-R2HNSJJG.js';
5
- export { CodeBlock, Divider, Link } from './chunk-R2HNSJJG.js';
6
- import { Badge } from './chunk-ADTPGMOD.js';
7
- export { Badge } from './chunk-ADTPGMOD.js';
8
- import { useBreakpoint } from './chunk-EKQHHP72.js';
9
- export { BreakpointWidthProvider, useBreakpoint } from './chunk-EKQHHP72.js';
10
- import { Tooltip, useTextOverflow, usePosition, Slot } from './chunk-LTV5X3S7.js';
11
- export { Tooltip, useTextOverflow } from './chunk-LTV5X3S7.js';
5
+ import { Link } from './chunk-VXKSQ56H.js';
6
+ export { CodeBlock, Divider, Link } from './chunk-VXKSQ56H.js';
7
+ import { Badge } from './chunk-QVNGQ64Q.js';
8
+ export { Badge } from './chunk-QVNGQ64Q.js';
9
+ import { useBreakpoint, readBreakpointPx } from './chunk-CRQ6KCRD.js';
10
+ export { BreakpointWidthProvider, useBreakpoint } from './chunk-CRQ6KCRD.js';
11
+ import { Tooltip, useTextOverflow, usePosition, Slot } from './chunk-RLNIRPOH.js';
12
+ export { Tooltip, useTextOverflow } from './chunk-RLNIRPOH.js';
12
13
  import { useLatest } from './chunk-WH2FUFAC.js';
13
- import { Icon } from './chunk-NM3BFWHH.js';
14
+ import { Icon } from './chunk-X4CNZFWC.js';
14
15
  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';
15
- import { createContext, forwardRef, useContext, useState, memo, useRef, useEffect, useCallback, useLayoutEffect, useId, isValidElement, cloneElement, Fragment as Fragment$1, useMemo, Children, useSyncExternalStore } from 'react';
16
+ import { createContext, forwardRef, useContext, useState, memo, useCallback, useRef, useEffect, useLayoutEffect, Children, useId, isValidElement, cloneElement, Fragment as Fragment$1, useMemo, useSyncExternalStore } from 'react';
16
17
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
17
18
  import { createPortal } from 'react-dom';
18
19
 
@@ -540,7 +541,8 @@ function Dropdown({
540
541
  ref,
541
542
  container,
542
543
  error = false,
543
- showChips = true
544
+ showChips = true,
545
+ "aria-label": ariaLabel
544
546
  }) {
545
547
  const [open, setOpen] = useState(false);
546
548
  const [search, setSearch] = useState("");
@@ -706,6 +708,7 @@ function Dropdown({
706
708
  "aria-invalid": error || void 0,
707
709
  "aria-haspopup": "listbox",
708
710
  "aria-expanded": open,
711
+ "aria-label": ariaLabel,
709
712
  className: cn(
710
713
  "w-full flex items-center justify-between gap-ds-2",
711
714
  size === "sm" ? "px-ds-2 py-ds-1 text-xs h-[var(--ds-control-height-sm)]" : "px-ds-3 py-ds-2 text-sm h-[var(--ds-control-height-md)]",
@@ -3343,40 +3346,253 @@ function Field({ label, children, hint, error, required, labelAction, id: idProp
3343
3346
  hasError && /* @__PURE__ */ jsx(InlineFieldError, { id: errorId, message: error, className: "mt-0" })
3344
3347
  ] });
3345
3348
  }
3349
+ var variantStyles3 = {
3350
+ info: "border-[var(--ds-accent-primary)] bg-[var(--ds-accent-subtle)]",
3351
+ success: "border-[var(--ds-status-success)] bg-[var(--ds-status-success-bg)]",
3352
+ warning: "border-[var(--ds-status-warning)] bg-[var(--ds-status-warning-bg)]",
3353
+ error: "border-[var(--ds-status-error)] bg-[var(--ds-status-error-bg)]"
3354
+ };
3355
+ var indicators = {
3356
+ info: "bg-[var(--ds-accent-primary)]",
3357
+ success: "bg-[var(--ds-status-success)]",
3358
+ warning: "bg-[var(--ds-status-warning)]",
3359
+ error: "bg-[var(--ds-status-error)]"
3360
+ };
3361
+ var actionColorsByVariant = {
3362
+ info: "text-[var(--ds-accent-primary)] hover:bg-[var(--ds-accent-muted)]",
3363
+ success: "text-[var(--ds-status-success)] hover:bg-[color-mix(in_srgb,var(--ds-status-success)_12%,transparent)]",
3364
+ warning: "text-[var(--ds-status-warning)] hover:bg-[color-mix(in_srgb,var(--ds-status-warning)_15%,transparent)]",
3365
+ error: "text-[var(--ds-status-error)] hover:bg-[color-mix(in_srgb,var(--ds-status-error)_12%,transparent)]"
3366
+ };
3367
+ var Alert = forwardRef(function Alert2({
3368
+ variant,
3369
+ title,
3370
+ children,
3371
+ dismissible,
3372
+ onDismiss,
3373
+ action,
3374
+ actions,
3375
+ placement = "inline",
3376
+ expandableContent,
3377
+ className
3378
+ }, ref) {
3379
+ const [detailsExpanded, setDetailsExpanded] = useState(false);
3380
+ const isMobile = useBreakpoint("sm");
3381
+ const resolvedActions = actions ?? (action ? [action] : []);
3382
+ const shouldCollapseActions = isMobile && resolvedActions.length > 1;
3383
+ const placementClasses = placement === "page-top" ? "sticky top-0 z-[var(--ds-z-sticky)] rounded-none" : placement === "section-top" ? "rounded-[var(--ds-radius-md)] my-ds-4" : "rounded-[var(--ds-radius-md)]";
3384
+ return /* @__PURE__ */ jsxs(
3385
+ "div",
3386
+ {
3387
+ ref,
3388
+ role: "alert",
3389
+ className: cn(
3390
+ "flex items-start gap-ds-3 p-ds-4",
3391
+ "border-[length:var(--ds-border-width)]",
3392
+ "shadow-[var(--ds-shadow-sm)]",
3393
+ "text-sm text-[var(--ds-text-primary)]",
3394
+ variantStyles3[variant],
3395
+ placementClasses,
3396
+ className
3397
+ ),
3398
+ children: [
3399
+ /* @__PURE__ */ jsx("div", { className: cn("mt-[0.3125em] w-3 h-3 shrink-0 rounded-[var(--ds-radius-sm)]", indicators[variant]) }),
3400
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
3401
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-ds-3", children: [
3402
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-[var(--ds-weight-heading)] text-[var(--ds-text-primary)]", children: title }),
3403
+ resolvedActions.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-ds-2 shrink-0", children: shouldCollapseActions ? /* @__PURE__ */ jsx(
3404
+ DropdownMenu,
3405
+ {
3406
+ trigger: /* @__PURE__ */ jsx(
3407
+ "button",
3408
+ {
3409
+ className: cn(
3410
+ "shrink-0 text-xs font-medium px-ds-2.5 py-ds-1 rounded-[var(--ds-radius-sm)]",
3411
+ "transition-colors cursor-pointer",
3412
+ "border-[length:var(--ds-border-width)] border-current",
3413
+ actionColorsByVariant[variant]
3414
+ ),
3415
+ children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-ds-1", children: [
3416
+ /* @__PURE__ */ jsx(Icon, { name: "more-vertical", size: 12 }),
3417
+ "Actions"
3418
+ ] })
3419
+ }
3420
+ ),
3421
+ items: resolvedActions.map((a) => ({
3422
+ label: a.label,
3423
+ onClick: a.onClick
3424
+ })),
3425
+ align: "end"
3426
+ }
3427
+ ) : resolvedActions.map((a, i) => {
3428
+ const effectiveVariant = a.variant ?? (i === 0 ? "secondary" : "outline");
3429
+ const isAccent = effectiveVariant === "secondary";
3430
+ return /* @__PURE__ */ jsxs(
3431
+ "button",
3432
+ {
3433
+ onClick: a.onClick,
3434
+ className: cn(
3435
+ "shrink-0 text-xs font-medium px-ds-2.5 py-ds-1 rounded-[var(--ds-radius-sm)]",
3436
+ "transition-colors cursor-pointer",
3437
+ isAccent ? cn("border-[length:var(--ds-border-width)] border-current", actionColorsByVariant[variant]) : cn("border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]", "text-[var(--ds-text-secondary)] hover:text-[var(--ds-text-primary)] hover:bg-[var(--ds-tab-hover-bg)]")
3438
+ ),
3439
+ children: [
3440
+ a.iconLeft && /* @__PURE__ */ jsx("span", { className: "inline-flex mr-ds-1", children: a.iconLeft }),
3441
+ a.label
3442
+ ]
3443
+ },
3444
+ i
3445
+ );
3446
+ }) })
3447
+ ] }),
3448
+ children && /* @__PURE__ */ jsx("div", { className: "mt-ds-1 text-xs text-[var(--ds-text-secondary)]", children }),
3449
+ expandableContent && /* @__PURE__ */ jsxs("div", { className: "mt-ds-2", children: [
3450
+ /* @__PURE__ */ jsxs(
3451
+ "button",
3452
+ {
3453
+ onClick: () => setDetailsExpanded(!detailsExpanded),
3454
+ "aria-expanded": detailsExpanded,
3455
+ className: cn(
3456
+ "inline-flex items-center gap-ds-1 text-xs font-medium cursor-pointer",
3457
+ "transition-colors duration-[var(--ds-transition-speed)]",
3458
+ actionColorsByVariant[variant],
3459
+ "rounded-[var(--ds-radius-sm)] px-ds-1.5 py-ds-0.5"
3460
+ ),
3461
+ children: [
3462
+ /* @__PURE__ */ jsx(
3463
+ Icon,
3464
+ {
3465
+ name: "chevron-right",
3466
+ size: 12,
3467
+ className: cn("transition-transform duration-[var(--ds-transition-speed)]", detailsExpanded && "rotate-90")
3468
+ }
3469
+ ),
3470
+ detailsExpanded ? "Hide details" : "Show details"
3471
+ ]
3472
+ }
3473
+ ),
3474
+ detailsExpanded && /* @__PURE__ */ jsx("div", { className: "mt-ds-2 text-xs text-[var(--ds-text-secondary)] border-l-2 border-current/20 pl-ds-3", children: expandableContent })
3475
+ ] })
3476
+ ] }),
3477
+ dismissible && /* @__PURE__ */ jsx(
3478
+ "button",
3479
+ {
3480
+ onClick: onDismiss,
3481
+ className: "shrink-0 text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer",
3482
+ "aria-label": "Dismiss",
3483
+ children: /* @__PURE__ */ jsx(Icon, { name: "close", size: 16 })
3484
+ }
3485
+ )
3486
+ ]
3487
+ }
3488
+ );
3489
+ });
3346
3490
  var FormCtx = createContext(null);
3347
3491
  function useFormContext() {
3348
3492
  const ctx = useContext(FormCtx);
3349
3493
  if (!ctx) throw new Error("useFormContext must be used within FormWrapper");
3350
3494
  return ctx;
3351
3495
  }
3352
- function FormWrapper({ onSubmit, validationMode = "onSubmit", children, className, ref }) {
3496
+ function collectInvalidFields(form) {
3497
+ const out = [];
3498
+ for (const el of Array.from(form.querySelectorAll("[aria-invalid='true']"))) {
3499
+ const id = el.id || "";
3500
+ 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";
3501
+ const described = (el.getAttribute("aria-describedby") ?? "").split(/\s+/).filter(Boolean);
3502
+ 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";
3503
+ out.push({ id, label, message });
3504
+ }
3505
+ return out;
3506
+ }
3507
+ function FormWrapper({ onSubmit, validationMode = "onSubmit", children, actions, actionsAlign = "end", focusFirstError = true, errorSummary = true, className, ref }) {
3353
3508
  const [errors, setErrors] = useState({});
3354
3509
  const [submitted, setSubmitted] = useState(false);
3510
+ const [submitCount, setSubmitCount] = useState(0);
3511
+ const [summary, setSummary] = useState([]);
3355
3512
  const errorsRef = useLatest(errors);
3513
+ const formRef = useRef(null);
3514
+ const setRefs = useCallback((node) => {
3515
+ formRef.current = node;
3516
+ if (typeof ref === "function") ref(node);
3517
+ else if (ref) ref.current = node;
3518
+ }, [ref]);
3356
3519
  const pendingSubmit = useRef(false);
3520
+ const pendingFocus = useRef(false);
3357
3521
  const onSubmitRef = useLatest(onSubmit);
3358
3522
  useEffect(() => {
3359
3523
  if (pendingSubmit.current && submitted) {
3360
3524
  pendingSubmit.current = false;
3361
3525
  onSubmitRef.current(errorsRef.current);
3362
3526
  }
3363
- }, [submitted, errors, errorsRef, onSubmitRef]);
3527
+ }, [submitted, submitCount, errors, errorsRef, onSubmitRef]);
3528
+ const settle = useCallback(() => {
3529
+ const form = formRef.current;
3530
+ if (!form) return;
3531
+ const invalid = collectInvalidFields(form);
3532
+ const next = invalid.length >= 2 ? invalid : [];
3533
+ setSummary((prev) => prev.length === next.length && prev.every((f, i) => f.id === next[i].id && f.message === next[i].message && f.label === next[i].label) ? prev : next);
3534
+ if (pendingFocus.current && invalid.length > 0) {
3535
+ pendingFocus.current = false;
3536
+ const first = form.querySelector("[aria-invalid='true']");
3537
+ if (first) {
3538
+ if (typeof first.scrollIntoView === "function") first.scrollIntoView({ block: "center", behavior: "smooth" });
3539
+ first.focus({ preventScroll: true });
3540
+ }
3541
+ }
3542
+ }, []);
3543
+ useLayoutEffect(() => {
3544
+ if (!submitted) return;
3545
+ settle();
3546
+ }, [submitted, submitCount, errors, settle]);
3547
+ useEffect(() => {
3548
+ const form = formRef.current;
3549
+ if (!form || typeof MutationObserver === "undefined") return;
3550
+ const obs = new MutationObserver(() => settle());
3551
+ obs.observe(form, { subtree: true, childList: true, attributes: true, attributeFilter: ["aria-invalid"] });
3552
+ return () => obs.disconnect();
3553
+ }, [settle]);
3554
+ const userActed = useCallback((e) => {
3555
+ if (!pendingFocus.current) return;
3556
+ const target = e.target;
3557
+ if (target && target.closest("button[type='submit']")) return;
3558
+ pendingFocus.current = false;
3559
+ }, []);
3364
3560
  const setError = useCallback((field, message) => {
3365
3561
  setErrors((prev) => ({ ...prev, [field]: message }));
3366
3562
  }, []);
3367
3563
  const clearErrors = useCallback(() => {
3368
3564
  setErrors({});
3369
3565
  setSubmitted(false);
3566
+ setSummary([]);
3370
3567
  }, []);
3371
3568
  const handleSubmit = useCallback(
3372
3569
  (e) => {
3373
3570
  e.preventDefault();
3374
3571
  pendingSubmit.current = true;
3572
+ pendingFocus.current = focusFirstError;
3375
3573
  setSubmitted(true);
3574
+ setSubmitCount((c) => c + 1);
3376
3575
  },
3377
- []
3576
+ [focusFirstError]
3378
3577
  );
3379
- return /* @__PURE__ */ jsx(FormCtx.Provider, { value: { errors, setError, clearErrors, validationMode, submitted }, children: /* @__PURE__ */ jsx("form", { ref, onSubmit: handleSubmit, className, noValidate: true, children }) });
3578
+ const focusField = (id) => (e) => {
3579
+ e.preventDefault();
3580
+ const el = id ? document.getElementById(id) : null;
3581
+ if (el) {
3582
+ el.scrollIntoView?.({ block: "center" });
3583
+ el.focus();
3584
+ }
3585
+ };
3586
+ const summaryNode = errorSummary && summary.length >= 2 ? typeof errorSummary === "function" ? errorSummary(summary) : /* @__PURE__ */ jsx(Alert, { variant: "error", title: `${summary.length} fields need attention`, className: "mb-ds-4", children: /* @__PURE__ */ jsx("ul", { className: "list-disc pl-ds-5 flex flex-col gap-ds-1", children: summary.map((f) => /* @__PURE__ */ jsxs("li", { children: [
3587
+ /* @__PURE__ */ jsx("a", { href: f.id ? `#${f.id}` : void 0, onClick: focusField(f.id), className: "underline underline-offset-2 text-[var(--ds-text-primary)]", children: f.label }),
3588
+ ": ",
3589
+ f.message
3590
+ ] }, f.id || f.label)) }) }) : null;
3591
+ return /* @__PURE__ */ jsx(FormCtx.Provider, { value: { errors, setError, clearErrors, validationMode, submitted }, children: /* @__PURE__ */ jsxs("form", { ref: setRefs, onSubmit: handleSubmit, onFocusCapture: userActed, onInputCapture: userActed, className, noValidate: true, children: [
3592
+ summaryNode && /* @__PURE__ */ jsx("div", { "data-form-error-summary": "", children: summaryNode }),
3593
+ children,
3594
+ actions && /* @__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 })
3595
+ ] }) });
3380
3596
  }
3381
3597
  function TransferList({
3382
3598
  available,
@@ -3956,7 +4172,7 @@ function MentionInput({
3956
4172
  ] });
3957
4173
  }
3958
4174
  var CardCtx = createContext(false);
3959
- var variantStyles3 = {
4175
+ var variantStyles4 = {
3960
4176
  default: `bg-[var(--ds-bg-surface)] border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)] ${GLASS_SURFACE}`,
3961
4177
  featured: [
3962
4178
  `bg-[var(--ds-bg-surface)] border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)] ${GLASS_SURFACE}`,
@@ -4105,7 +4321,7 @@ var Card = forwardRef(function Card2({
4105
4321
  style: variant === "interactive" ? { ...INTERACTIVE_SCALE_CAP, ...style } : style,
4106
4322
  className: cn(
4107
4323
  "rounded-[var(--ds-radius-md)] flex flex-col relative",
4108
- variantStyles3[variant],
4324
+ variantStyles4[variant],
4109
4325
  isSelectable && !disabled && "cursor-pointer select-none transition-all duration-[var(--ds-transition-speed)] ease-[var(--ds-transition-ease)]",
4110
4326
  isSelectable && !disabled && !selected && "hover:border-[var(--ds-accent-primary)]",
4111
4327
  isSelectable && "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
@@ -4189,32 +4405,33 @@ var CardHeader = forwardRef(function CardHeader2({ className, children, actions,
4189
4405
  children
4190
4406
  ] });
4191
4407
  });
4192
- var CardTitle = forwardRef(function CardTitle2({ className, children, ...props }, forwardedRef) {
4193
- const { ref: overflowRef, isTruncated } = useTextOverflow({ lines: 1 });
4408
+ var cardLineClampClasses = {
4409
+ 1: "truncate",
4410
+ 2: "line-clamp-2",
4411
+ 3: "line-clamp-3",
4412
+ 4: "line-clamp-4",
4413
+ 5: "line-clamp-5",
4414
+ 6: "line-clamp-6"
4415
+ };
4416
+ var CardTitle = forwardRef(function CardTitle2({ className, children, lines = 2, ...props }, forwardedRef) {
4417
+ const { ref: overflowRef, isTruncated } = useTextOverflow({ lines, disabled: lines === 0 });
4418
+ const lineClampClass = lines > 0 ? cardLineClampClasses[lines] || `line-clamp-[${lines}]` : "";
4194
4419
  const heading = /* @__PURE__ */ jsx(
4195
4420
  "h3",
4196
4421
  {
4197
4422
  ref: (node) => {
4198
- overflowRef(node);
4423
+ overflowRef(lines > 0 ? node : null);
4199
4424
  if (typeof forwardedRef === "function") forwardedRef(node);
4200
4425
  else if (forwardedRef) forwardedRef.current = node;
4201
4426
  },
4202
- className: cn("font-[family-name:var(--ds-font-display)] text-lg font-[var(--ds-weight-heading)] text-[var(--ds-text-primary)] truncate", className),
4427
+ 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),
4203
4428
  ...props,
4204
4429
  children
4205
4430
  }
4206
4431
  );
4207
- if (!isTruncated) return heading;
4432
+ if (!isTruncated || lines === 0) return heading;
4208
4433
  return /* @__PURE__ */ jsx(Tooltip, { content: children, children: heading });
4209
4434
  });
4210
- var cardLineClampClasses = {
4211
- 1: "truncate",
4212
- 2: "line-clamp-2",
4213
- 3: "line-clamp-3",
4214
- 4: "line-clamp-4",
4215
- 5: "line-clamp-5",
4216
- 6: "line-clamp-6"
4217
- };
4218
4435
  var CardDescription = forwardRef(function CardDescription2({ className, children, lines = 2, ...props }, forwardedRef) {
4219
4436
  const { ref: overflowRef, isTruncated } = useTextOverflow({ lines, disabled: lines === 0 });
4220
4437
  const lineClampClass = lines > 0 ? cardLineClampClasses[lines] || `line-clamp-[${lines}]` : "";
@@ -4303,24 +4520,29 @@ function StatsCard({ value, label, trend, icon, layout = "card", interactive, on
4303
4520
  }
4304
4521
  function TrendBadge({ trend }) {
4305
4522
  const colors = {
4306
- up: "text-[var(--ds-status-success)] bg-[var(--ds-status-success-bg)]",
4307
- down: "text-[var(--ds-status-error)] bg-[var(--ds-status-error-bg)]",
4308
- flat: "text-[var(--ds-text-muted)] bg-[var(--ds-bg-muted)]"
4523
+ positive: "text-[var(--ds-status-success)] bg-[var(--ds-status-success-bg)]",
4524
+ negative: "text-[var(--ds-status-error)] bg-[var(--ds-status-error-bg)]",
4525
+ neutral: "text-[var(--ds-text-muted)] bg-[var(--ds-bg-muted)]"
4309
4526
  };
4527
+ const defaultSentiment = { up: "positive", down: "negative", flat: "neutral" };
4528
+ const sentiment = trend.sentiment ?? defaultSentiment[trend.direction];
4310
4529
  const arrows = { up: "\u2191", down: "\u2193", flat: "\u2192" };
4311
- return /* @__PURE__ */ jsxs(
4530
+ const { ref, isTruncated } = useTextOverflow({ lines: 1, content: trend.value });
4531
+ const badge = /* @__PURE__ */ jsxs(
4312
4532
  "span",
4313
4533
  {
4314
4534
  className: cn(
4315
- "inline-flex items-center gap-ds-0.5 text-xs font-medium rounded-[var(--ds-radius-sm)] px-ds-1.5 py-ds-0.5",
4316
- colors[trend.direction]
4535
+ "inline-flex items-center gap-ds-0.5 text-xs font-medium rounded-[var(--ds-radius-sm)] px-ds-1.5 py-ds-0.5 min-w-0 max-w-full",
4536
+ colors[sentiment]
4317
4537
  ),
4318
4538
  children: [
4319
4539
  /* @__PURE__ */ jsx("span", { children: arrows[trend.direction] }),
4320
- trend.value
4540
+ /* @__PURE__ */ jsx("span", { ref, className: "truncate", children: trend.value })
4321
4541
  ]
4322
4542
  }
4323
4543
  );
4544
+ if (!isTruncated) return badge;
4545
+ return /* @__PURE__ */ jsx(Tooltip, { content: trend.value, children: badge });
4324
4546
  }
4325
4547
  function DataList({ items, columns = 1, className }) {
4326
4548
  return /* @__PURE__ */ jsx(
@@ -4376,10 +4598,11 @@ var DescriptionList = forwardRef(function DescriptionList2({
4376
4598
  columns = 1,
4377
4599
  size = "md",
4378
4600
  dividers = false,
4379
- stackBelow,
4601
+ stackBelow: stackBelowProp,
4380
4602
  className
4381
4603
  }, ref) {
4382
4604
  const s = sizeStyles4[size];
4605
+ const stackBelow = stackBelowProp ?? (columns > 1 ? "sm" : void 0);
4383
4606
  if (orientation === "vertical") {
4384
4607
  return /* @__PURE__ */ jsx(
4385
4608
  "dl",
@@ -4389,7 +4612,7 @@ var DescriptionList = forwardRef(function DescriptionList2({
4389
4612
  "grid",
4390
4613
  columnClasses[columns],
4391
4614
  "gap-x-ds-6 gap-y-ds-1",
4392
- stackBelow && stackBreakpoints[stackBelow],
4615
+ stackBelow && stackBelow !== "none" && stackBreakpoints[stackBelow],
4393
4616
  dividers && "divide-y-[length:var(--ds-border-width)] divide-[var(--ds-border-subtle)]",
4394
4617
  className
4395
4618
  ),
@@ -4431,7 +4654,7 @@ var DescriptionList = forwardRef(function DescriptionList2({
4431
4654
  "grid",
4432
4655
  columnClasses[columns],
4433
4656
  "gap-x-ds-6",
4434
- stackBelow && stackBreakpoints[stackBelow],
4657
+ stackBelow && stackBelow !== "none" && stackBreakpoints[stackBelow],
4435
4658
  dividers && "divide-y-[length:var(--ds-border-width)] divide-[var(--ds-border-subtle)]",
4436
4659
  className
4437
4660
  ),
@@ -4851,8 +5074,11 @@ function SegmentedControl({
4851
5074
  variant = "default",
4852
5075
  fullWidth = false,
4853
5076
  onHoverChange,
4854
- className
5077
+ className,
5078
+ "aria-label": ariaLabel,
5079
+ maxOptions = 6
4855
5080
  }) {
5081
+ const collapsed = options.length > maxOptions;
4856
5082
  const containerRef = useRef(null);
4857
5083
  const [indicator, setIndicator] = useState({ left: 0, width: 0 });
4858
5084
  const updateIndicator = useCallback(() => {
@@ -4870,12 +5096,12 @@ function SegmentedControl({
4870
5096
  updateIndicator();
4871
5097
  }, [value, optionsKey, updateIndicator]);
4872
5098
  useLayoutEffect(() => {
4873
- if (!containerRef.current || typeof ResizeObserver === "undefined") return;
5099
+ if (collapsed || !containerRef.current || typeof ResizeObserver === "undefined") return;
4874
5100
  const observer = new ResizeObserver(updateIndicator);
4875
5101
  observer.observe(containerRef.current);
4876
5102
  containerRef.current.querySelectorAll("button").forEach((b) => observer.observe(b));
4877
5103
  return () => observer.disconnect();
4878
- }, [updateIndicator, optionsKey]);
5104
+ }, [updateIndicator, optionsKey, collapsed]);
4879
5105
  const containerHeight = {
4880
5106
  xs: "h-ds-6",
4881
5107
  sm: "h-[var(--ds-control-height-sm)]",
@@ -4900,11 +5126,25 @@ function SegmentedControl({
4900
5126
  if (b.dataset.value === enabled[next].value) b.focus();
4901
5127
  });
4902
5128
  };
5129
+ if (collapsed) {
5130
+ return /* @__PURE__ */ jsx(
5131
+ Dropdown,
5132
+ {
5133
+ "aria-label": ariaLabel,
5134
+ size: size === "md" || size === "lg" ? "md" : "sm",
5135
+ options: options.filter((o) => !o.disabled).map((o) => ({ id: o.value, label: o.label })),
5136
+ value,
5137
+ onChange: (v) => onChange(v),
5138
+ className: cn("inline-block", fullWidth ? "w-full" : "w-auto", className)
5139
+ }
5140
+ );
5141
+ }
4903
5142
  return /* @__PURE__ */ jsxs(
4904
5143
  "div",
4905
5144
  {
4906
5145
  ref: containerRef,
4907
5146
  role: "radiogroup",
5147
+ "aria-label": ariaLabel,
4908
5148
  onKeyDown: handleKeyDown,
4909
5149
  className: cn(
4910
5150
  "relative items-center",
@@ -6509,25 +6749,161 @@ var defaultIcons = {
6509
6749
  error: "\u26A0",
6510
6750
  "no-permission": "\u{1F512}"
6511
6751
  };
6752
+ var FRAME_ICON_SIZE = 28;
6753
+ function renderIcon(icon) {
6754
+ if (typeof icon === "string") return /* @__PURE__ */ jsx(Icon, { name: icon, size: FRAME_ICON_SIZE, className: "text-[var(--ds-text-muted)]" });
6755
+ if (isValidElement(icon) && icon.type === Icon) return cloneElement(icon, { size: FRAME_ICON_SIZE });
6756
+ return icon;
6757
+ }
6512
6758
  function EmptyState({ icon, iconFrame = "circle", title, description, action, variant = "empty", bordered = false, className }) {
6759
+ const iconNode = icon === void 0 || icon === null ? void 0 : renderIcon(icon);
6513
6760
  return /* @__PURE__ */ jsxs(
6514
6761
  "div",
6515
6762
  {
6516
6763
  className: cn(
6517
- "flex flex-col items-center justify-center py-[var(--ds-space-10)]",
6764
+ "flex flex-col items-center justify-center py-[var(--ds-space-10)] px-[var(--ds-space-6)]",
6518
6765
  "rounded-[var(--ds-radius-md)] bg-[var(--ds-bg-surface)]",
6519
6766
  bordered && "border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
6520
6767
  className
6521
6768
  ),
6522
6769
  children: [
6523
- iconFrame === "none" && icon ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center mb-ds-4", children: icon }) : /* @__PURE__ */ jsx("div", { className: "w-16 h-16 rounded-[var(--ds-radius-full)] bg-[var(--ds-bg-emphasis)] flex items-center justify-center mb-ds-4", children: icon ?? /* @__PURE__ */ jsx("span", { className: "text-2xl text-[var(--ds-text-muted)]", children: defaultIcons[variant] }) }),
6524
- /* @__PURE__ */ jsx("h3", { className: "font-[family-name:var(--ds-font-display)] text-lg font-[var(--ds-weight-heading)] text-[var(--ds-text-primary)]", children: title }),
6770
+ iconFrame === "none" && iconNode ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center mb-ds-4", children: iconNode }) : /* @__PURE__ */ jsx("div", { className: "w-16 h-16 rounded-[var(--ds-radius-full)] bg-[var(--ds-bg-emphasis)] flex items-center justify-center mb-ds-4", children: iconNode ?? /* @__PURE__ */ jsx("span", { className: "text-2xl text-[var(--ds-text-muted)]", children: defaultIcons[variant] }) }),
6771
+ /* @__PURE__ */ jsx("h3", { className: "font-[family-name:var(--ds-font-display)] text-lg font-[var(--ds-weight-heading)] text-[var(--ds-text-primary)] text-center", children: title }),
6525
6772
  description && /* @__PURE__ */ jsx("p", { className: "mt-ds-1 text-sm text-[var(--ds-text-muted)] max-w-sm text-center", children: description }),
6526
6773
  action && /* @__PURE__ */ jsx("div", { className: "mt-ds-4", children: action })
6527
6774
  ]
6528
6775
  }
6529
6776
  );
6530
6777
  }
6778
+ function SkipLink({ href = "#main", children = "Skip to content", className }) {
6779
+ return /* @__PURE__ */ jsx(
6780
+ "a",
6781
+ {
6782
+ href,
6783
+ className: cn(
6784
+ "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)]",
6785
+ "focus:px-[var(--ds-space-3)] focus:py-[var(--ds-space-2)] focus:rounded-[var(--ds-radius-md)] focus:text-sm focus:font-medium",
6786
+ "focus:bg-[var(--ds-bg-surface-elevated)] focus:text-[var(--ds-text-primary)] focus:shadow-[var(--ds-shadow-md)]",
6787
+ "focus:outline-none focus:ring-2 focus:ring-[var(--ds-accent-primary)]",
6788
+ className
6789
+ ),
6790
+ children
6791
+ }
6792
+ );
6793
+ }
6794
+ function useCompact(ref, breakpoint) {
6795
+ const px = typeof breakpoint === "number" ? breakpoint : readBreakpointPx(breakpoint);
6796
+ const width = useElementWidth(ref);
6797
+ return width > 0 && width < px;
6798
+ }
6799
+ function useElementWidth(ref) {
6800
+ const [width, setWidth] = useState(0);
6801
+ const check = useCallback(() => {
6802
+ const next = ref.current?.offsetWidth ?? 0;
6803
+ if (next > 0) setWidth(next);
6804
+ }, [ref]);
6805
+ useLayoutEffect(() => {
6806
+ check();
6807
+ if (!ref.current || typeof ResizeObserver === "undefined") return;
6808
+ const observer = new ResizeObserver(check);
6809
+ observer.observe(ref.current);
6810
+ return () => observer.disconnect();
6811
+ }, [check, ref]);
6812
+ return width;
6813
+ }
6814
+ function TableToolbar({
6815
+ search,
6816
+ primaryFilter,
6817
+ filters,
6818
+ filtersOpen,
6819
+ onFiltersOpenChange,
6820
+ activeFilterCount,
6821
+ activeFilters,
6822
+ onClearAll,
6823
+ resultCount,
6824
+ resultNoun = ["result", "results"],
6825
+ actions,
6826
+ size = "sm",
6827
+ compactBreakpoint = 680,
6828
+ className
6829
+ }) {
6830
+ const rowRef = useRef(null);
6831
+ const compact = useCompact(rowRef, compactBreakpoint);
6832
+ const filterRowId = useId();
6833
+ const [openState, setOpenState] = useState(false);
6834
+ const open = filtersOpen ?? openState;
6835
+ const setOpen = (next) => {
6836
+ setOpenState(next);
6837
+ onFiltersOpenChange?.(next);
6838
+ };
6839
+ const iconSize = size === "sm" ? 14 : 16;
6840
+ const count = activeFilterCount ?? activeFilters?.length ?? 0;
6841
+ const hasChips = !!activeFilters?.length;
6842
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-ds-3 min-w-0", className), "data-table-toolbar": "", children: [
6843
+ /* @__PURE__ */ jsxs("div", { ref: rowRef, className: "flex items-center gap-ds-2 flex-wrap", children: [
6844
+ search && /* @__PURE__ */ jsx(
6845
+ Input,
6846
+ {
6847
+ "aria-label": search.label,
6848
+ placeholder: search.placeholder ?? search.label,
6849
+ value: search.value,
6850
+ size,
6851
+ onChange: (e) => search.onChange(e.target.value),
6852
+ className: "flex-1 min-w-[160px] max-w-[280px]"
6853
+ }
6854
+ ),
6855
+ primaryFilter && (compact ? /* @__PURE__ */ jsx(
6856
+ Dropdown,
6857
+ {
6858
+ "aria-label": primaryFilter.label,
6859
+ options: primaryFilter.options.map((o) => ({ id: o.value, label: o.label })),
6860
+ value: primaryFilter.value,
6861
+ onChange: (v) => primaryFilter.onChange(v),
6862
+ size,
6863
+ className: "w-[150px]"
6864
+ }
6865
+ ) : /* @__PURE__ */ jsx(
6866
+ SegmentedControl,
6867
+ {
6868
+ "aria-label": primaryFilter.label,
6869
+ options: primaryFilter.options,
6870
+ value: primaryFilter.value,
6871
+ size,
6872
+ onChange: primaryFilter.onChange
6873
+ }
6874
+ )),
6875
+ (filters || actions) && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-2 flex-1 justify-end", children: [
6876
+ filters && /* @__PURE__ */ jsxs(
6877
+ Button,
6878
+ {
6879
+ variant: open ? "secondary" : "outline",
6880
+ size,
6881
+ iconLeft: /* @__PURE__ */ jsx(Icon, { name: "filter", size: iconSize }),
6882
+ onClick: () => setOpen(!open),
6883
+ "aria-expanded": open,
6884
+ "aria-controls": filterRowId,
6885
+ className: "mr-auto",
6886
+ children: [
6887
+ "Filters",
6888
+ count > 0 && !open && /* @__PURE__ */ jsx(Badge, { variant: "default", size: "xs", className: "ml-ds-1", children: count })
6889
+ ]
6890
+ }
6891
+ ),
6892
+ actions
6893
+ ] })
6894
+ ] }),
6895
+ filters && open && /* @__PURE__ */ jsx("div", { id: filterRowId, className: "flex items-center gap-ds-3 flex-wrap", children: filters }),
6896
+ (resultCount !== void 0 || hasChips) && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-2 flex-wrap", children: [
6897
+ resultCount !== void 0 && /* @__PURE__ */ jsxs("span", { className: "text-xs text-[var(--ds-text-muted)]", "aria-live": "polite", children: [
6898
+ resultCount,
6899
+ " ",
6900
+ resultCount === 1 ? resultNoun[0] : resultNoun[1]
6901
+ ] }),
6902
+ activeFilters?.map((f) => /* @__PURE__ */ jsx(Tag, { size: "sm", variant: "subtle", onRemove: f.onRemove, children: f.label }, f.id)),
6903
+ hasChips && onClearAll && /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: onClearAll, children: "Clear all" })
6904
+ ] })
6905
+ ] });
6906
+ }
6531
6907
  var dotVariant = {
6532
6908
  default: "bg-[var(--ds-accent-primary)] shadow-[0_0_0_3px_var(--ds-accent-primary)]/20",
6533
6909
  success: "bg-[var(--ds-status-success)] shadow-[0_0_0_3px_var(--ds-status-success)]/20",
@@ -6561,11 +6937,11 @@ function ExpandableDescription({ children, className }) {
6561
6937
  }
6562
6938
  );
6563
6939
  }
6564
- function HorizontalLayout({ items, className }) {
6565
- return /* @__PURE__ */ jsx("div", { className: cn("flex items-start", className), children: items.map((item, i) => {
6940
+ function HorizontalLayout({ items, minItemWidth, className }) {
6941
+ return /* @__PURE__ */ jsx("div", { "data-timeline-row": "", className: cn("flex items-start overflow-x-auto snap-x", className), children: items.map((item, i) => {
6566
6942
  const isFirst = i === 0;
6567
6943
  const isLast = i === items.length - 1;
6568
- return /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col items-center min-w-[100px]", children: [
6944
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-1 shrink-0 flex-col items-center snap-start", style: { minWidth: minItemWidth }, children: [
6569
6945
  /* @__PURE__ */ jsxs("div", { className: "flex items-center w-full", children: [
6570
6946
  /* @__PURE__ */ jsx(
6571
6947
  "div",
@@ -6624,17 +7000,17 @@ function VerticalLayout({ items, className }) {
6624
7000
  ] })
6625
7001
  ] }, i)) });
6626
7002
  }
6627
- function Timeline({ items, orientation = "vertical", className, ref }) {
6628
- if (orientation === "horizontal") {
6629
- return /* @__PURE__ */ jsx("div", { ref, className, children: /* @__PURE__ */ jsx(HorizontalLayout, { items }) });
6630
- }
6631
- if (orientation === "responsive") {
6632
- return /* @__PURE__ */ jsxs("div", { ref, className, children: [
6633
- /* @__PURE__ */ jsx("div", { className: "hidden md:block", children: /* @__PURE__ */ jsx(HorizontalLayout, { items }) }),
6634
- /* @__PURE__ */ jsx("div", { className: "md:hidden", children: /* @__PURE__ */ jsx(VerticalLayout, { items }) })
6635
- ] });
6636
- }
6637
- return /* @__PURE__ */ jsx("div", { ref, className, children: /* @__PURE__ */ jsx(VerticalLayout, { items }) });
7003
+ function Timeline({ items, orientation = "vertical", minItemWidth = 200, className, ref }) {
7004
+ const measureRef = useRef(null);
7005
+ const width = useElementWidth(measureRef);
7006
+ const setRefs = useCallback((node) => {
7007
+ measureRef.current = node;
7008
+ if (typeof ref === "function") ref(node);
7009
+ else if (ref) ref.current = node;
7010
+ }, [ref]);
7011
+ const fits = width === 0 || width >= items.length * minItemWidth;
7012
+ const horizontal = orientation === "horizontal" || orientation === "responsive" && fits;
7013
+ return /* @__PURE__ */ jsx("div", { ref: setRefs, "data-timeline-orientation": horizontal ? "horizontal" : "vertical", className, children: horizontal ? /* @__PURE__ */ jsx(HorizontalLayout, { items, minItemWidth }) : /* @__PURE__ */ jsx(VerticalLayout, { items }) });
6638
7014
  }
6639
7015
  var CollapsibleContext = createContext(null);
6640
7016
  function useCollapsible() {
@@ -7144,147 +7520,6 @@ function Carousel({
7144
7520
  }
7145
7521
  );
7146
7522
  }
7147
- var variantStyles4 = {
7148
- info: "border-[var(--ds-accent-primary)] bg-[var(--ds-accent-subtle)]",
7149
- success: "border-[var(--ds-status-success)] bg-[var(--ds-status-success-bg)]",
7150
- warning: "border-[var(--ds-status-warning)] bg-[var(--ds-status-warning-bg)]",
7151
- error: "border-[var(--ds-status-error)] bg-[var(--ds-status-error-bg)]"
7152
- };
7153
- var indicators = {
7154
- info: "bg-[var(--ds-accent-primary)]",
7155
- success: "bg-[var(--ds-status-success)]",
7156
- warning: "bg-[var(--ds-status-warning)]",
7157
- error: "bg-[var(--ds-status-error)]"
7158
- };
7159
- var actionColorsByVariant = {
7160
- info: "text-[var(--ds-accent-primary)] hover:bg-[var(--ds-accent-muted)]",
7161
- success: "text-[var(--ds-status-success)] hover:bg-[color-mix(in_srgb,var(--ds-status-success)_12%,transparent)]",
7162
- warning: "text-[var(--ds-status-warning)] hover:bg-[color-mix(in_srgb,var(--ds-status-warning)_15%,transparent)]",
7163
- error: "text-[var(--ds-status-error)] hover:bg-[color-mix(in_srgb,var(--ds-status-error)_12%,transparent)]"
7164
- };
7165
- var Alert = forwardRef(function Alert2({
7166
- variant,
7167
- title,
7168
- children,
7169
- dismissible,
7170
- onDismiss,
7171
- action,
7172
- actions,
7173
- placement = "inline",
7174
- expandableContent,
7175
- className
7176
- }, ref) {
7177
- const [detailsExpanded, setDetailsExpanded] = useState(false);
7178
- const isMobile = useBreakpoint("sm");
7179
- const resolvedActions = actions ?? (action ? [action] : []);
7180
- const shouldCollapseActions = isMobile && resolvedActions.length > 1;
7181
- const placementClasses = placement === "page-top" ? "sticky top-0 z-[var(--ds-z-sticky)] rounded-none" : placement === "section-top" ? "rounded-[var(--ds-radius-md)] my-ds-4" : "rounded-[var(--ds-radius-md)]";
7182
- return /* @__PURE__ */ jsxs(
7183
- "div",
7184
- {
7185
- ref,
7186
- role: "alert",
7187
- className: cn(
7188
- "flex items-start gap-ds-3 p-ds-4",
7189
- "border-[length:var(--ds-border-width)]",
7190
- "shadow-[var(--ds-shadow-sm)]",
7191
- "text-sm text-[var(--ds-text-primary)]",
7192
- variantStyles4[variant],
7193
- placementClasses,
7194
- className
7195
- ),
7196
- children: [
7197
- /* @__PURE__ */ jsx("div", { className: cn("mt-[0.3125em] w-3 h-3 shrink-0 rounded-[var(--ds-radius-sm)]", indicators[variant]) }),
7198
- /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
7199
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-ds-3", children: [
7200
- /* @__PURE__ */ jsx("p", { className: "text-sm font-[var(--ds-weight-heading)] text-[var(--ds-text-primary)]", children: title }),
7201
- resolvedActions.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-ds-2 shrink-0", children: shouldCollapseActions ? /* @__PURE__ */ jsx(
7202
- DropdownMenu,
7203
- {
7204
- trigger: /* @__PURE__ */ jsx(
7205
- "button",
7206
- {
7207
- className: cn(
7208
- "shrink-0 text-xs font-medium px-ds-2.5 py-ds-1 rounded-[var(--ds-radius-sm)]",
7209
- "transition-colors cursor-pointer",
7210
- "border-[length:var(--ds-border-width)] border-current",
7211
- actionColorsByVariant[variant]
7212
- ),
7213
- children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-ds-1", children: [
7214
- /* @__PURE__ */ jsx(Icon, { name: "more-vertical", size: 12 }),
7215
- "Actions"
7216
- ] })
7217
- }
7218
- ),
7219
- items: resolvedActions.map((a) => ({
7220
- label: a.label,
7221
- onClick: a.onClick
7222
- })),
7223
- align: "end"
7224
- }
7225
- ) : resolvedActions.map((a, i) => {
7226
- const effectiveVariant = a.variant ?? (i === 0 ? "secondary" : "outline");
7227
- const isAccent = effectiveVariant === "secondary";
7228
- return /* @__PURE__ */ jsxs(
7229
- "button",
7230
- {
7231
- onClick: a.onClick,
7232
- className: cn(
7233
- "shrink-0 text-xs font-medium px-ds-2.5 py-ds-1 rounded-[var(--ds-radius-sm)]",
7234
- "transition-colors cursor-pointer",
7235
- isAccent ? cn("border-[length:var(--ds-border-width)] border-current", actionColorsByVariant[variant]) : cn("border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]", "text-[var(--ds-text-secondary)] hover:text-[var(--ds-text-primary)] hover:bg-[var(--ds-tab-hover-bg)]")
7236
- ),
7237
- children: [
7238
- a.iconLeft && /* @__PURE__ */ jsx("span", { className: "inline-flex mr-ds-1", children: a.iconLeft }),
7239
- a.label
7240
- ]
7241
- },
7242
- i
7243
- );
7244
- }) })
7245
- ] }),
7246
- children && /* @__PURE__ */ jsx("div", { className: "mt-ds-1 text-xs text-[var(--ds-text-secondary)]", children }),
7247
- expandableContent && /* @__PURE__ */ jsxs("div", { className: "mt-ds-2", children: [
7248
- /* @__PURE__ */ jsxs(
7249
- "button",
7250
- {
7251
- onClick: () => setDetailsExpanded(!detailsExpanded),
7252
- "aria-expanded": detailsExpanded,
7253
- className: cn(
7254
- "inline-flex items-center gap-ds-1 text-xs font-medium cursor-pointer",
7255
- "transition-colors duration-[var(--ds-transition-speed)]",
7256
- actionColorsByVariant[variant],
7257
- "rounded-[var(--ds-radius-sm)] px-ds-1.5 py-ds-0.5"
7258
- ),
7259
- children: [
7260
- /* @__PURE__ */ jsx(
7261
- Icon,
7262
- {
7263
- name: "chevron-right",
7264
- size: 12,
7265
- className: cn("transition-transform duration-[var(--ds-transition-speed)]", detailsExpanded && "rotate-90")
7266
- }
7267
- ),
7268
- detailsExpanded ? "Hide details" : "Show details"
7269
- ]
7270
- }
7271
- ),
7272
- detailsExpanded && /* @__PURE__ */ jsx("div", { className: "mt-ds-2 text-xs text-[var(--ds-text-secondary)] border-l-2 border-current/20 pl-ds-3", children: expandableContent })
7273
- ] })
7274
- ] }),
7275
- dismissible && /* @__PURE__ */ jsx(
7276
- "button",
7277
- {
7278
- onClick: onDismiss,
7279
- className: "shrink-0 text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer",
7280
- "aria-label": "Dismiss",
7281
- children: /* @__PURE__ */ jsx(Icon, { name: "close", size: 16 })
7282
- }
7283
- )
7284
- ]
7285
- }
7286
- );
7287
- });
7288
7523
  function formatTime2(d) {
7289
7524
  return d.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
7290
7525
  }
@@ -7784,7 +8019,7 @@ function useCornerInset(corner, inset, priority, target) {
7784
8019
  return claimCornerInset(corner, inset, priority, el);
7785
8020
  }, [corner, inset, priority, target]);
7786
8021
  }
7787
- function Sheet({ open, onClose, title, children, footer, snapPoints = [0.5, 0.92], container, flush, keepMounted = false, id, labelledBy }) {
8022
+ function Sheet({ open, onClose, title, children, footer, confirm, snapPoints = [0.5, 0.92], container, flush, keepMounted = false, id, labelledBy }) {
7788
8023
  const titleId = useId();
7789
8024
  const panelRef = useRef(null);
7790
8025
  const [mounted, setMounted] = useState(open);
@@ -7954,6 +8189,7 @@ function Sheet({ open, onClose, title, children, footer, snapPoints = [0.5, 0.92
7954
8189
  children
7955
8190
  }
7956
8191
  ),
8192
+ confirm,
7957
8193
  footer && /* @__PURE__ */ jsx(
7958
8194
  "div",
7959
8195
  {
@@ -8203,6 +8439,66 @@ function defaultMaxWidth(container) {
8203
8439
  const base = container instanceof HTMLElement ? container.clientWidth : typeof window !== "undefined" ? window.innerWidth : 1200;
8204
8440
  return Math.round(base * 0.6);
8205
8441
  }
8442
+ function useDirtyGuard(dirty, onClose, onDiscard) {
8443
+ const [confirming, setConfirming] = useState(false);
8444
+ useEffect(() => {
8445
+ if (!dirty) setConfirming(false);
8446
+ }, [dirty]);
8447
+ const requestClose = useCallback(() => {
8448
+ if (!dirty) {
8449
+ onClose();
8450
+ return;
8451
+ }
8452
+ setConfirming((c) => !c);
8453
+ }, [dirty, onClose]);
8454
+ const keep = useCallback(() => setConfirming(false), []);
8455
+ const discard = useCallback(() => {
8456
+ setConfirming(false);
8457
+ onDiscard?.();
8458
+ onClose();
8459
+ }, [onClose, onDiscard]);
8460
+ return { confirming, requestClose, keep, discard };
8461
+ }
8462
+ function DiscardConfirm({ open, onKeep, onDiscard }) {
8463
+ const keepRef = useRef(null);
8464
+ const returnTo = useRef(null);
8465
+ const overlay = useRef(null);
8466
+ useEffect(() => {
8467
+ if (!open) return;
8468
+ const active = document.activeElement instanceof HTMLElement ? document.activeElement : null;
8469
+ overlay.current = keepRef.current?.closest("[role='dialog'], [role='alertdialog']") ?? null;
8470
+ returnTo.current = active && active !== document.body && overlay.current?.contains(active) ? active : null;
8471
+ keepRef.current?.focus();
8472
+ return () => {
8473
+ if (document.activeElement !== document.body) return;
8474
+ const back = returnTo.current;
8475
+ if (back && back.isConnected) {
8476
+ back.focus();
8477
+ return;
8478
+ }
8479
+ const first = overlay.current?.querySelector("button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex='-1'])");
8480
+ (first ?? overlay.current)?.focus();
8481
+ };
8482
+ }, [open]);
8483
+ if (!open) return null;
8484
+ return /* @__PURE__ */ jsxs(
8485
+ "div",
8486
+ {
8487
+ role: "group",
8488
+ "aria-label": "Unsaved changes",
8489
+ "data-discard-confirm": "",
8490
+ 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)]",
8491
+ style: { padding: "var(--ds-space-3) var(--ds-space-5)" },
8492
+ children: [
8493
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--ds-text-primary)]", children: "You have unsaved changes." }),
8494
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-2", children: [
8495
+ /* @__PURE__ */ jsx(Button, { ref: keepRef, variant: "secondary", size: "sm", onClick: onKeep, children: "Keep editing" }),
8496
+ /* @__PURE__ */ jsx(Button, { variant: "destructive", size: "sm", onClick: onDiscard, children: "Discard changes" })
8497
+ ] })
8498
+ ]
8499
+ }
8500
+ );
8501
+ }
8206
8502
  var slideClasses = {
8207
8503
  right: {
8208
8504
  panel: "top-0 right-0 h-full",
@@ -8226,15 +8522,17 @@ var borderByPosition = {
8226
8522
  bottom: "border-t-[length:var(--ds-border-width)] border-t-[var(--ds-border-default)]"
8227
8523
  };
8228
8524
  function Drawer(props) {
8229
- const { position = "right", mobile, snapPoints, open, onClose, title, children, footer, container, flush, keepMounted, id, labelledBy } = props;
8525
+ const { position = "right", mobile, snapPoints, open, onClose, title, children, footer, container, flush, keepMounted, id, labelledBy, dirty, onDiscard } = props;
8230
8526
  const isPhone = useBreakpoint("sm");
8231
8527
  const mobileMode = mobile ?? (position === "left" ? "panel" : "sheet");
8528
+ const guard = useDirtyGuard(dirty, onClose, onDiscard);
8529
+ const confirm = /* @__PURE__ */ jsx(DiscardConfirm, { open: guard.confirming, onKeep: guard.keep, onDiscard: guard.discard });
8232
8530
  if (isPhone && mobileMode === "sheet") {
8233
- return /* @__PURE__ */ jsx(Sheet, { open, onClose, title, footer, container, flush, snapPoints, keepMounted, id, labelledBy, children });
8531
+ return /* @__PURE__ */ jsx(Sheet, { open, onClose: guard.requestClose, title, footer, confirm, container, flush, snapPoints, keepMounted, id, labelledBy, children });
8234
8532
  }
8235
- return /* @__PURE__ */ jsx(DrawerPanel, { ...props });
8533
+ return /* @__PURE__ */ jsx(DrawerPanel, { ...props, onClose: guard.requestClose, confirm });
8236
8534
  }
8237
- function DrawerPanel({ open, onClose, position = "right", width = "400px", title, children, footer, container, flush, lightBackdrop, topOffset, modal = true, keepMounted = false, id, labelledBy, resizable = false, maxWidth }) {
8535
+ 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 }) {
8238
8536
  const nonModal = !modal;
8239
8537
  const [chosen, remember] = useRememberedWidth(id ?? labelledBy);
8240
8538
  const canResize = nonModal && resizable && position !== "bottom";
@@ -8380,6 +8678,7 @@ function DrawerPanel({ open, onClose, position = "right", width = "400px", title
8380
8678
  children
8381
8679
  }
8382
8680
  ),
8681
+ confirm,
8383
8682
  footer && /* @__PURE__ */ jsx(
8384
8683
  "div",
8385
8684
  {
@@ -8410,12 +8709,18 @@ var AppShell = forwardRef(function AppShell2({
8410
8709
  mobileMenuFlush = true,
8411
8710
  navbar,
8412
8711
  mobileNav,
8712
+ footer,
8713
+ skipLink = true,
8714
+ mainId = "main",
8413
8715
  aside,
8414
8716
  asideSide = "right",
8415
8717
  launcher,
8416
8718
  children,
8417
8719
  className,
8418
- scrollable = true
8720
+ scrollable = true,
8721
+ theme,
8722
+ currentPath,
8723
+ homePath = "/"
8419
8724
  }, ref) {
8420
8725
  const [sidebarOpen, setSidebarOpen] = useState(false);
8421
8726
  const [asideOpen, setAsideOpen] = useState(false);
@@ -8431,15 +8736,29 @@ var AppShell = forwardRef(function AppShell2({
8431
8736
  const hasMobileNav = mobileNav != null;
8432
8737
  const showMobileDrawer = hasMobileMenu && !(isSmallMobile && hasMobileNav);
8433
8738
  const [shellEl, setShellEl] = useState(null);
8739
+ const [chrome, setChrome] = useState({});
8740
+ const registerChrome = useCallback((kind, position) => {
8741
+ setChrome((c) => c[kind] === position ? c : { ...c, [kind]: position });
8742
+ return () => setChrome((c) => {
8743
+ if (!(kind in c)) return c;
8744
+ const next = { ...c };
8745
+ delete next[kind];
8746
+ return next;
8747
+ });
8748
+ }, []);
8749
+ const navbarFloating = (chrome.navbar ?? theme?.navbarPosition) === "floating";
8750
+ const sidebarFloating = hasSidebar && !isMobile && (chrome.sidebar ?? theme?.sidebarPosition) === "floating";
8751
+ const isHome = currentPath != null && currentPath === homePath;
8434
8752
  return /* @__PURE__ */ jsx(
8435
8753
  AppShellContext,
8436
8754
  {
8437
- value: { hasSidebar, hasMobileMenu, hasMobileNav, sidebarOpen, setSidebarOpen, isMobile, isTablet, sidebarCollapsed, setSidebarCollapsed: setUserCollapsed, asideOpen, setAsideOpen, asideSide, asideFloating, setAsideFloating, container: shellEl },
8755
+ 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 },
8438
8756
  children: /* @__PURE__ */ jsxs("div", { ref: (el) => {
8439
8757
  setShellEl(el);
8440
8758
  if (typeof ref === "function") ref(el);
8441
8759
  else if (ref) ref.current = el;
8442
8760
  }, className: cn("relative h-screen flex flex-col bg-[var(--ds-bg-canvas)] overflow-hidden", className), children: [
8761
+ skipLink && /* @__PURE__ */ jsx(SkipLink, { href: `#${mainId}` }),
8443
8762
  layout === "sidebar-header" && navbar,
8444
8763
  layout === "multi-column" && navbar,
8445
8764
  /* @__PURE__ */ jsxs("div", { className: "flex flex-1 min-h-0", children: [
@@ -8451,13 +8770,18 @@ var AppShell = forwardRef(function AppShell2({
8451
8770
  /* @__PURE__ */ jsx(
8452
8771
  "main",
8453
8772
  {
8773
+ id: mainId,
8774
+ tabIndex: -1,
8454
8775
  className: cn(
8455
- "flex-1 min-h-0",
8776
+ "flex-1 min-h-0 focus:outline-none",
8456
8777
  scrollable ? "overflow-y-auto" : "relative",
8457
8778
  layout === "multi-column" && "flex"
8458
8779
  ),
8459
8780
  style: { containerType: "inline-size" },
8460
- children: scrollable ? children : /* @__PURE__ */ jsx("div", { className: "absolute inset-0", children })
8781
+ children: scrollable ? footer ? /* @__PURE__ */ jsxs("div", { className: "min-h-full flex flex-col", children: [
8782
+ /* @__PURE__ */ jsx("div", { className: "flex-1", children }),
8783
+ footer
8784
+ ] }) : children : /* @__PURE__ */ jsx("div", { className: "absolute inset-0", children })
8461
8785
  }
8462
8786
  )
8463
8787
  ] }),
@@ -8491,8 +8815,8 @@ function AsidePanel({
8491
8815
  side = "right",
8492
8816
  width = "420px",
8493
8817
  position = "flush",
8494
- navbarFloating = false,
8495
- sidebarFloating = false,
8818
+ navbarFloating: navbarFloatingProp,
8819
+ sidebarFloating: sidebarFloatingProp,
8496
8820
  collapseBelow = "lg",
8497
8821
  keepMounted = true,
8498
8822
  resizable = true,
@@ -8504,6 +8828,8 @@ function AsidePanel({
8504
8828
  }) {
8505
8829
  const collapsed = useBreakpoint(collapseBelow);
8506
8830
  const shell = useAppShell();
8831
+ const navbarFloating = navbarFloatingProp ?? shell?.navbarFloating ?? false;
8832
+ const sidebarFloating = sidebarFloatingProp ?? shell?.sidebarFloating ?? false;
8507
8833
  const setAsideOpen = shell?.setAsideOpen;
8508
8834
  const setAsideFloating = shell?.setAsideFloating;
8509
8835
  const floating = position === "floating";
@@ -8647,6 +8973,7 @@ var StackedList = forwardRef(function StackedList2({
8647
8973
  divided = true,
8648
8974
  flush = false,
8649
8975
  selectedId,
8976
+ hover = "tint",
8650
8977
  className
8651
8978
  }, ref) {
8652
8979
  return /* @__PURE__ */ jsx(
@@ -8694,7 +9021,7 @@ var StackedList = forwardRef(function StackedList2({
8694
9021
  // (1.5× the tab rail; still 0 under tint and pill). Every row carries
8695
9022
  // the rail width as a transparent placeholder so content never shifts.
8696
9023
  "border-l-[length:calc(var(--ds-nav-v-active-rail)*1.5)]",
8697
- 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)]")
9024
+ 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")
8698
9025
  ),
8699
9026
  onClick: item.onClick,
8700
9027
  role: interactive ? "button" : void 0,
@@ -8715,7 +9042,9 @@ var StackedList = forwardRef(function StackedList2({
8715
9042
  {
8716
9043
  className: cn(
8717
9044
  "text-sm",
8718
- 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)]"
9045
+ 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)]",
9046
+ // hover="title": the title answers the pointer (accent, underline) instead of the row's background
9047
+ interactive && hover === "title" && !isSelected && "transition-colors group-hover/row:text-[var(--ds-accent-primary)] group-hover/row:underline underline-offset-2"
8719
9048
  ),
8720
9049
  children: item.title
8721
9050
  }
@@ -8832,7 +9161,7 @@ var subscribeNever = () => () => {
8832
9161
  var getClientSnapshot = () => true;
8833
9162
  var getServerSnapshot = () => false;
8834
9163
  var bodyLaunchers = 0;
8835
- function renderIcon(icon, size) {
9164
+ function renderIcon2(icon, size) {
8836
9165
  return typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon, size }) : icon;
8837
9166
  }
8838
9167
  function Launcher({
@@ -8976,7 +9305,7 @@ function Launcher({
8976
9305
  accentGlyph && ACCENT_GLYPH,
8977
9306
  showClose && "rotate-90"
8978
9307
  ),
8979
- children: renderIcon(glyph, glyphSize)
9308
+ children: renderIcon2(glyph, glyphSize)
8980
9309
  }
8981
9310
  ),
8982
9311
  variant === "pill" && /* @__PURE__ */ jsx("span", { children: label }),
@@ -8996,7 +9325,7 @@ function Launcher({
8996
9325
  children: label
8997
9326
  }
8998
9327
  ),
8999
- ribbonOverlayClose && /* @__PURE__ */ jsx("span", { "data-launcher-close": "", className: cn("absolute inset-0 inline-flex items-center justify-center", accentGlyph && ACCENT_GLYPH), children: renderIcon("close", glyphSize) }),
9328
+ ribbonOverlayClose && /* @__PURE__ */ jsx("span", { "data-launcher-close": "", className: cn("absolute inset-0 inline-flex items-center justify-center", accentGlyph && ACCENT_GLYPH), children: renderIcon2("close", glyphSize) }),
9000
9329
  badge && !showClose && // A ribbon is flush to the viewport edge, so its badge hangs off the inner corner instead.
9001
9330
  /* @__PURE__ */ jsx("span", { className: "absolute", style: badgeStyle, "data-testid": "launcher-badge", children: typeof badge === "number" ? /* @__PURE__ */ jsx(Badge, { variant: "error", size: "xs", children: badge > 99 ? "99+" : badge }) : /* @__PURE__ */ jsx("span", { className: "block h-ds-2.5 w-ds-2.5 rounded-[var(--ds-radius-full)] bg-[var(--ds-status-error)] ring-2 ring-[var(--ds-bg-surface)]" }) })
9002
9331
  ]
@@ -9306,6 +9635,104 @@ var Tabs = Object.assign(TabsRoot, {
9306
9635
  Trigger: TabsTrigger,
9307
9636
  Content: TabsContent
9308
9637
  });
9638
+ var MIN_PANEL_WIDTH = 480;
9639
+ function SettingsLayout({
9640
+ sections,
9641
+ value,
9642
+ onChange,
9643
+ layout: layoutProp,
9644
+ railWidth = 220,
9645
+ collapseBelow = railWidth + MIN_PANEL_WIDTH,
9646
+ "aria-label": ariaLabel = "Settings sections",
9647
+ children,
9648
+ className
9649
+ }) {
9650
+ const shell = useAppShell();
9651
+ const layout = layoutProp ?? shell?.theme?.settingsLayout ?? "sidebar";
9652
+ const rootRef = useRef(null);
9653
+ const compact = useCompact(rootRef, collapseBelow);
9654
+ const tabs = sections.map((s) => ({ id: s.id, label: s.label, icon: s.icon, group: s.group, disabled: s.disabled }));
9655
+ const stickyTop = shell ? "var(--ds-sticky-offset, 0px)" : "calc(var(--ds-navbar-height) + var(--ds-sticky-offset, 0px))";
9656
+ if (layout === "tabs") {
9657
+ return /* @__PURE__ */ jsxs("div", { ref: rootRef, "data-settings-layout": "tabs", className: cn("flex flex-col gap-ds-6", className), children: [
9658
+ /* @__PURE__ */ jsx("nav", { "aria-label": ariaLabel, children: /* @__PURE__ */ jsx(Tabs, { orientation: "horizontal", mobile: "dropdown", tabs, activeTab: value, onTabChange: onChange }) }),
9659
+ /* @__PURE__ */ jsx("div", { className: "min-w-0", children })
9660
+ ] });
9661
+ }
9662
+ if (compact) {
9663
+ return /* @__PURE__ */ jsxs("div", { ref: rootRef, "data-settings-layout": "sidebar", "data-collapsed": "true", className: cn("flex flex-col gap-ds-6", className), children: [
9664
+ /* @__PURE__ */ jsx(
9665
+ Dropdown,
9666
+ {
9667
+ "aria-label": ariaLabel,
9668
+ options: sections.filter((s) => !s.disabled).map((s) => ({ id: s.id, label: s.label, group: s.group })),
9669
+ value,
9670
+ onChange: (v) => onChange(v)
9671
+ }
9672
+ ),
9673
+ /* @__PURE__ */ jsx("div", { className: "min-w-0", children })
9674
+ ] });
9675
+ }
9676
+ return /* @__PURE__ */ jsxs("div", { ref: rootRef, "data-settings-layout": "sidebar", className: cn("flex flex-row items-start gap-ds-8", className), children: [
9677
+ /* @__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" }) }),
9678
+ /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children })
9679
+ ] });
9680
+ }
9681
+ function SplitView({
9682
+ list,
9683
+ detail,
9684
+ aside,
9685
+ asideTitle = "Details",
9686
+ detailOpen = false,
9687
+ onDetailClose,
9688
+ detailMode = "stack",
9689
+ backLabel = "Back",
9690
+ listWidth = 35,
9691
+ listMinWidth = 25,
9692
+ asideWidth = 25,
9693
+ collapseBelow = 768,
9694
+ asideCollapseBelow = 1024,
9695
+ bordered = false,
9696
+ "aria-label": ariaLabel,
9697
+ className
9698
+ }) {
9699
+ const rootRef = useRef(null);
9700
+ const compact = useCompact(rootRef, collapseBelow);
9701
+ const asideCollapsed = useCompact(rootRef, asideCollapseBelow);
9702
+ const [asideOpen, setAsideOpen] = useState(false);
9703
+ useEffect(() => {
9704
+ if (!asideCollapsed) setAsideOpen(false);
9705
+ }, [asideCollapsed]);
9706
+ const region = ariaLabel ? { role: "region", "aria-label": ariaLabel } : {};
9707
+ const surface = cn(
9708
+ "h-full min-h-0 w-full bg-[var(--ds-bg-surface)]",
9709
+ bordered && "border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)] rounded-[var(--ds-radius-md)] overflow-hidden",
9710
+ className
9711
+ );
9712
+ if (compact) {
9713
+ const showDetail = detailMode === "stack" && detailOpen;
9714
+ return /* @__PURE__ */ jsx("div", { ref: rootRef, "data-split-view": showDetail ? "detail" : "list", ...region, className: cn(surface, "flex flex-col"), children: showDetail ? /* @__PURE__ */ jsxs(Fragment, { children: [
9715
+ /* @__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 }) }),
9716
+ /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0 overflow-y-auto", children: detail })
9717
+ ] }) : /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0 overflow-y-auto", children: list }) });
9718
+ }
9719
+ 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;
9720
+ return /* @__PURE__ */ jsxs("div", { ref: rootRef, "data-split-view": "split", ...region, className: surface, children: [
9721
+ /* @__PURE__ */ jsxs(ResizablePanelGroup, { direction: "horizontal", stackOnMobile: false, className: "h-full", children: [
9722
+ /* @__PURE__ */ jsx(ResizablePanel, { id: "list", defaultSize: `${listWidth}%`, minSize: `${listMinWidth}%`, children: /* @__PURE__ */ jsx("div", { className: "h-full min-h-0 overflow-y-auto", children: list }) }),
9723
+ /* @__PURE__ */ jsx(ResizableHandle, { withHandle: true }),
9724
+ /* @__PURE__ */ jsx(ResizablePanel, { id: "detail", minSize: "30%", children: /* @__PURE__ */ jsxs("div", { className: "h-full min-h-0 flex flex-col", children: [
9725
+ asideToggle,
9726
+ /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0 overflow-y-auto", children: detail })
9727
+ ] }) }),
9728
+ aside && !asideCollapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
9729
+ /* @__PURE__ */ jsx(ResizableHandle, { withHandle: true }),
9730
+ /* @__PURE__ */ jsx(ResizablePanel, { id: "aside", defaultSize: `${asideWidth}%`, minSize: "15%", children: /* @__PURE__ */ jsx("div", { className: "h-full min-h-0 overflow-y-auto", children: aside }) })
9731
+ ] })
9732
+ ] }),
9733
+ aside && asideCollapsed && /* @__PURE__ */ jsx(Drawer, { open: asideOpen, onClose: () => setAsideOpen(false), title: asideTitle, position: "right", children: aside })
9734
+ ] });
9735
+ }
9309
9736
  function SeparatorIcon({ type, size }) {
9310
9737
  const iconSize = size === "sm" ? 10 : 12;
9311
9738
  if (type === "chevron") return /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: iconSize });
@@ -10009,7 +10436,7 @@ var Sidebar = forwardRef(function Sidebar2({
10009
10436
  mini = false,
10010
10437
  type = "traditional",
10011
10438
  position = "flush",
10012
- navbarFloating,
10439
+ navbarFloating: navbarFloatingProp,
10013
10440
  navSize = "md",
10014
10441
  fill = false,
10015
10442
  className
@@ -10019,6 +10446,11 @@ var Sidebar = forwardRef(function Sidebar2({
10019
10446
  const portalRef = useCallback((node) => setPortalEl(node), []);
10020
10447
  const isControlled = controlledCollapsed !== void 0;
10021
10448
  const appShell = useAppShell();
10449
+ const navbarFloating = navbarFloatingProp ?? appShell?.navbarFloating;
10450
+ useLayoutEffect(() => {
10451
+ if (fill || !appShell) return;
10452
+ return appShell.registerChrome("sidebar", position);
10453
+ }, [appShell?.registerChrome, fill, position]);
10022
10454
  const usesShellState = appShell !== null && !isControlled && defaultCollapsed === void 0 && !fill && type === "traditional";
10023
10455
  const uncontrolledCollapsed = usesShellState ? appShell.sidebarCollapsed : internalCollapsed;
10024
10456
  const collapsed = mini || (isControlled ? controlledCollapsed : uncontrolledCollapsed);
@@ -10772,9 +11204,174 @@ function NavButton({ item, onClick }) {
10772
11204
  }
10773
11205
  );
10774
11206
  }
10775
- function TruncHeading({ as: Tag3 = "h1", className, children }) {
10776
- const { ref, isTruncated } = useTextOverflow({ lines: 1 });
10777
- const heading = /* @__PURE__ */ jsx(Tag3, { ref, className: cn("truncate", className), children });
11207
+
11208
+ // ../../showcase/src/lib/shell-theme.ts
11209
+ var PAGE_HEADER_MARK = /* @__PURE__ */ Symbol.for("gridsmith.PageHeader");
11210
+ function resolvePageHeader(theme, props, facts) {
11211
+ const t = theme?.pageHeader ?? {};
11212
+ const themeVariant = t.variant ?? "default";
11213
+ const homeOnlyElsewhere = themeVariant === "colourful" && !!t.colourfulHomeOnly && !facts.isHome;
11214
+ const variant = props.variant ?? (homeOnlyElsewhere ? t.otherPagesVariant ?? "default" : themeVariant);
11215
+ const behavior = props.behavior ?? t.behavior ?? "static";
11216
+ const position = props.position ?? t.position ?? "flush";
11217
+ const intensity = props.intensity ?? t.intensity ?? "bold";
11218
+ const b = theme?.breadcrumbs ?? {};
11219
+ const navbarFloating = props.navbarFloating ?? facts.navbarFloating;
11220
+ const sidebarFloating = (props.sidebarFloating ?? facts.sidebarFloating) && !facts.isMobile;
11221
+ return {
11222
+ variant,
11223
+ behavior,
11224
+ position,
11225
+ intensity,
11226
+ breadcrumbPosition: b.position ?? "inline",
11227
+ breadcrumbStyle: props.breadcrumbStyle ?? b.style ?? "default",
11228
+ breadcrumbSeparator: props.breadcrumbSeparator ?? b.separator ?? "chevron",
11229
+ navbarFloating,
11230
+ sidebarFloating,
11231
+ outside: variant === "colourful" || behavior === "snapping" || position === "floating"
11232
+ };
11233
+ }
11234
+ var maxWidthValues = {
11235
+ sm: "var(--ds-bp-sm)",
11236
+ md: "var(--ds-bp-md)",
11237
+ lg: "var(--ds-bp-lg)",
11238
+ xl: "var(--ds-bp-xl)",
11239
+ full: "100%"
11240
+ };
11241
+ var widthToMaxWidth = {
11242
+ constrained: "xl",
11243
+ full: "full",
11244
+ narrow: "md"
11245
+ };
11246
+ var paddingClasses = {
11247
+ none: "",
11248
+ default: "px-ds-6",
11249
+ wide: "px-ds-10"
11250
+ };
11251
+ var verticalPaddingClasses = {
11252
+ none: "",
11253
+ default: "pt-ds-8 pb-ds-12",
11254
+ spacious: "pt-ds-8 pb-ds-16"
11255
+ };
11256
+ var bottomPaddingClasses = {
11257
+ none: "",
11258
+ default: "pb-ds-12",
11259
+ spacious: "pb-ds-16"
11260
+ };
11261
+ var FLOATING_BLOCK = cn(
11262
+ "rounded-[var(--ds-radius-md)]",
11263
+ "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
11264
+ GLASS_SURFACE,
11265
+ "overflow-hidden"
11266
+ );
11267
+ var PageContainerContext = createContext(null);
11268
+ function usePageContainer() {
11269
+ return useContext(PageContainerContext);
11270
+ }
11271
+ function isPageHeaderElement(node) {
11272
+ if (!isValidElement(node)) return false;
11273
+ const type = node.type;
11274
+ return !!type && typeof type !== "string" && type[PAGE_HEADER_MARK] === true;
11275
+ }
11276
+ function floatingBlockPadding(navbarFloating, sidebarFloating, overlap) {
11277
+ return {
11278
+ paddingTop: navbarFloating ? 0 : "var(--ds-space-3)",
11279
+ paddingRight: "var(--ds-space-3)",
11280
+ paddingBottom: overlap ? 0 : "var(--ds-space-3)",
11281
+ paddingLeft: sidebarFloating ? 0 : "var(--ds-space-3)",
11282
+ marginBottom: overlap ? 0 : "var(--ds-space-3)"
11283
+ };
11284
+ }
11285
+ var PageContainer = forwardRef(function PageContainer2({
11286
+ width,
11287
+ maxWidth = "xl",
11288
+ padding = "default",
11289
+ verticalPadding,
11290
+ centered = true,
11291
+ children,
11292
+ className,
11293
+ renderHeader
11294
+ }, ref) {
11295
+ const shell = useAppShell();
11296
+ const rootRef = useRef(null);
11297
+ const setRoot = useCallback((node) => {
11298
+ rootRef.current = node;
11299
+ if (typeof ref === "function") ref(node);
11300
+ else if (ref) ref.current = node;
11301
+ }, [ref]);
11302
+ const setPinnedHeight = useCallback((px) => {
11303
+ if (px) rootRef.current?.style.setProperty("--ds-sticky-offset", `${px}px`);
11304
+ else rootRef.current?.style.removeProperty("--ds-sticky-offset");
11305
+ }, []);
11306
+ const resolvedMaxWidth = width ? widthToMaxWidth[width] : maxWidth;
11307
+ const maxWidthValue = maxWidthValues[resolvedMaxWidth];
11308
+ const kids = Children.toArray(children);
11309
+ const headerIdx = kids.findIndex(isPageHeaderElement);
11310
+ if (headerIdx === -1) {
11311
+ return /* @__PURE__ */ jsx(
11312
+ "div",
11313
+ {
11314
+ ref,
11315
+ className: cn(
11316
+ "w-full",
11317
+ paddingClasses[padding],
11318
+ verticalPaddingClasses[verticalPadding ?? "default"],
11319
+ centered && "mx-auto",
11320
+ className
11321
+ ),
11322
+ style: { maxWidth: maxWidthValue, containerType: "inline-size" },
11323
+ children
11324
+ }
11325
+ );
11326
+ }
11327
+ const headerEl = kids[headerIdx];
11328
+ const rest = kids.filter((_, i) => i !== headerIdx);
11329
+ const r = resolvePageHeader(shell?.theme, headerEl.props, {
11330
+ isHome: shell?.isHome ?? false,
11331
+ isMobile: shell?.isMobile ?? false,
11332
+ navbarFloating: shell?.navbarFloating ?? false,
11333
+ sidebarFloating: shell?.sidebarFloating ?? false
11334
+ });
11335
+ const snapping = r.behavior === "snapping";
11336
+ const floatingHeader = r.position === "floating";
11337
+ const colourful = r.variant === "colourful";
11338
+ const crumbs = Array.isArray(headerEl.props.breadcrumbs) && headerEl.props.breadcrumbs.length > 0 ? headerEl.props.breadcrumbs : null;
11339
+ const contained = r.breadcrumbPosition === "contained" && !!crumbs;
11340
+ const block = floatingHeader && contained && !snapping;
11341
+ const placement = block ? "block" : r.outside ? "outside" : "inside";
11342
+ const breadcrumbsInsideHeader = snapping && floatingHeader && contained;
11343
+ const overlap = colourful && typeof headerEl.props.overlapHeight === "number" && headerEl.props.overlapHeight > 0 ? headerEl.props.overlapHeight : 0;
11344
+ const strip = contained && !breadcrumbsInsideHeader && crumbs ? (
11345
+ // The strip is the whole trail: the path, then this page as the current item (Breadcrumbs renders the last item as text).
11346
+ /* @__PURE__ */ jsx(Breadcrumbs, { variant: "contained", itemVariant: r.breadcrumbStyle, bleed: true, contentMaxWidth: maxWidthValue, separator: r.breadcrumbSeparator, items: headerEl.props.title ? [...crumbs, { label: headerEl.props.title }] : crumbs })
11347
+ ) : null;
11348
+ const columnClass = (pad2) => cn("w-full", paddingClasses[pad2], centered && "mx-auto");
11349
+ const columnStyle = { maxWidth: maxWidthValue, containerType: "inline-size" };
11350
+ const header = /* @__PURE__ */ jsx(PageContainerContext, { value: { contentMaxWidth: maxWidthValue, placement, containedBreadcrumbs: contained, breadcrumbsInsideHeader, noBottomMargin: block, setPinnedHeight }, children: renderHeader ? renderHeader(headerEl) : headerEl });
11351
+ const contentPadding = floatingHeader && shell?.isMobile ? "none" : padding;
11352
+ const wrapperStyle = floatingHeader || overlap ? {
11353
+ ...floatingHeader ? { paddingRight: "var(--ds-space-3)", paddingLeft: r.sidebarFloating ? 0 : "var(--ds-space-3)" } : {},
11354
+ ...overlap ? { marginTop: -overlap, position: "relative", zIndex: 1 } : {}
11355
+ } : void 0;
11356
+ const column = /* @__PURE__ */ jsx("div", { className: cn(columnClass(contentPadding), bottomPaddingClasses[verticalPadding ?? "default"], className), style: columnStyle, children: rest });
11357
+ return /* @__PURE__ */ jsxs("div", { ref: setRoot, "data-page-container": "", className: "w-full", children: [
11358
+ placement === "block" ? /* @__PURE__ */ jsx("div", { style: floatingBlockPadding(r.navbarFloating, r.sidebarFloating, overlap > 0), children: /* @__PURE__ */ jsxs("div", { className: FLOATING_BLOCK, children: [
11359
+ strip,
11360
+ colourful ? header : /* @__PURE__ */ jsx("div", { className: columnClass(padding), style: columnStyle, children: header })
11361
+ ] }) }) : placement === "outside" ? /* @__PURE__ */ jsxs(Fragment, { children: [
11362
+ strip,
11363
+ header
11364
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
11365
+ strip,
11366
+ /* @__PURE__ */ jsx("div", { className: columnClass(padding), style: columnStyle, children: header })
11367
+ ] }),
11368
+ wrapperStyle ? /* @__PURE__ */ jsx("div", { style: wrapperStyle, children: column }) : column
11369
+ ] });
11370
+ });
11371
+ function TruncHeading({ as: Tag3 = "h1", className, children, lines: linesProp }) {
11372
+ const lines = linesProp ?? (Tag3 === "h1" ? 2 : 1);
11373
+ const { ref, isTruncated } = useTextOverflow({ lines });
11374
+ const heading = /* @__PURE__ */ jsx(Tag3, { ref, className: cn(lines === 2 ? "line-clamp-2" : "truncate", className), children });
10778
11375
  if (!isTruncated) return heading;
10779
11376
  return /* @__PURE__ */ jsx(Tooltip, { content: children, children: heading });
10780
11377
  }
@@ -10895,30 +11492,49 @@ var PageHeader = forwardRef(function PageHeader2({
10895
11492
  bannerImage,
10896
11493
  as: Heading = "h1",
10897
11494
  className,
10898
- variant = "default",
10899
- behavior = "static",
10900
- intensity = "bold",
11495
+ variant: variantProp,
11496
+ behavior: behaviorProp,
11497
+ intensity: intensityProp,
10901
11498
  backgroundImage,
10902
11499
  overlapHeight,
10903
11500
  expandedContent,
10904
11501
  collapseOffset = 100,
10905
11502
  pinned = true,
10906
- contentMaxWidth,
11503
+ contentMaxWidth: contentMaxWidthProp,
10907
11504
  tabs,
10908
11505
  breadcrumbStyle,
10909
11506
  breadcrumbSeparator,
10910
- hideExpandedBreadcrumbs,
10911
- position = "flush",
10912
- navbarFloating,
10913
- sidebarFloating,
10914
- noBottomMargin,
10915
- containedBreadcrumbs
11507
+ hideExpandedBreadcrumbs: hideExpandedBreadcrumbsProp,
11508
+ position: positionProp,
11509
+ navbarFloating: navbarFloatingProp,
11510
+ sidebarFloating: sidebarFloatingProp,
11511
+ noBottomMargin: noBottomMarginProp,
11512
+ containedBreadcrumbs: containedBreadcrumbsProp
10916
11513
  }, ref) {
10917
- const isFloating = position === "floating";
10918
11514
  const shell = useAppShell();
11515
+ const container = usePageContainer();
11516
+ const r = resolvePageHeader(shell?.theme, {
11517
+ variant: variantProp,
11518
+ behavior: behaviorProp,
11519
+ position: positionProp,
11520
+ intensity: intensityProp,
11521
+ breadcrumbStyle,
11522
+ breadcrumbSeparator,
11523
+ navbarFloating: navbarFloatingProp,
11524
+ sidebarFloating: sidebarFloatingProp
11525
+ }, { isHome: shell?.isHome ?? false, isMobile: shell?.isMobile ?? false, navbarFloating: shell?.navbarFloating ?? false, sidebarFloating: shell?.sidebarFloating ?? false });
11526
+ const { variant, behavior, position, intensity, navbarFloating, sidebarFloating } = r;
11527
+ const isFloating = position === "floating";
10919
11528
  const asideFloatingSide = shell?.asideOpen && shell.asideFloating ? shell.asideSide : null;
10920
- const bcPill = breadcrumbStyle === "pill";
10921
- const bcSep = breadcrumbSeparator ?? "chevron";
11529
+ const bcPill = r.breadcrumbStyle === "pill";
11530
+ const bcSep = r.breadcrumbSeparator;
11531
+ const contentMaxWidth = contentMaxWidthProp ?? container?.contentMaxWidth;
11532
+ const stickyTop = shell ? "top-0" : "top-[var(--ds-navbar-height)]";
11533
+ const snapping = behavior === "snapping";
11534
+ const inlineBreadcrumbs = container?.containedBreadcrumbs && !snapping ? void 0 : breadcrumbs;
11535
+ const hideExpandedBreadcrumbs = hideExpandedBreadcrumbsProp ?? (!!container?.containedBreadcrumbs && snapping);
11536
+ const containedBreadcrumbs = containedBreadcrumbsProp ?? (container?.breadcrumbsInsideHeader && breadcrumbs?.length ? /* @__PURE__ */ jsx(Breadcrumbs, { variant: "contained", itemVariant: r.breadcrumbStyle, bleed: true, contentMaxWidth, separator: bcSep, items: [...breadcrumbs, { label: title }] }) : void 0);
11537
+ const noBottomMargin = noBottomMarginProp ?? container?.noBottomMargin;
10922
11538
  if (behavior === "snapping") {
10923
11539
  return /* @__PURE__ */ jsx(
10924
11540
  SnappingHeader,
@@ -10926,7 +11542,7 @@ var PageHeader = forwardRef(function PageHeader2({
10926
11542
  title,
10927
11543
  description,
10928
11544
  badge,
10929
- breadcrumbs,
11545
+ breadcrumbs: inlineBreadcrumbs,
10930
11546
  onBack,
10931
11547
  actions,
10932
11548
  avatar,
@@ -10945,6 +11561,7 @@ var PageHeader = forwardRef(function PageHeader2({
10945
11561
  hideExpandedBreadcrumbs,
10946
11562
  as: Heading,
10947
11563
  className,
11564
+ stickyTop,
10948
11565
  isFloating,
10949
11566
  navbarFloating,
10950
11567
  sidebarFloating,
@@ -10955,7 +11572,7 @@ var PageHeader = forwardRef(function PageHeader2({
10955
11572
  }
10956
11573
  let header;
10957
11574
  if (variant === "minimal") {
10958
- header = /* @__PURE__ */ jsx(MinimalHeader, { title, badge, breadcrumbs, actions, breadcrumbPill: bcPill, breadcrumbSeparator: bcSep, as: Heading, className, headerRef: isFloating ? void 0 : ref, isFloating, contentMaxWidth });
11575
+ header = /* @__PURE__ */ jsx(MinimalHeader, { title, badge, breadcrumbs: inlineBreadcrumbs, actions, breadcrumbPill: bcPill, breadcrumbSeparator: bcSep, as: Heading, className, headerRef: isFloating ? void 0 : ref, isFloating, contentMaxWidth });
10959
11576
  } else if (variant === "tabs-integrated") {
10960
11577
  header = /* @__PURE__ */ jsx(
10961
11578
  TabsIntegratedHeader,
@@ -10963,7 +11580,7 @@ var PageHeader = forwardRef(function PageHeader2({
10963
11580
  title,
10964
11581
  description,
10965
11582
  badge,
10966
- breadcrumbs,
11583
+ breadcrumbs: inlineBreadcrumbs,
10967
11584
  onBack,
10968
11585
  actions,
10969
11586
  avatar,
@@ -10984,7 +11601,7 @@ var PageHeader = forwardRef(function PageHeader2({
10984
11601
  title,
10985
11602
  description,
10986
11603
  badge,
10987
- breadcrumbs,
11604
+ breadcrumbs: inlineBreadcrumbs,
10988
11605
  onBack,
10989
11606
  actions,
10990
11607
  avatar,
@@ -11009,7 +11626,7 @@ var PageHeader = forwardRef(function PageHeader2({
11009
11626
  title,
11010
11627
  description,
11011
11628
  badge,
11012
- breadcrumbs,
11629
+ breadcrumbs: inlineBreadcrumbs,
11013
11630
  onBack,
11014
11631
  actions,
11015
11632
  avatar,
@@ -11038,6 +11655,7 @@ var PageHeader = forwardRef(function PageHeader2({
11038
11655
  }
11039
11656
  return header;
11040
11657
  });
11658
+ Object.assign(PageHeader, { [PAGE_HEADER_MARK]: true });
11041
11659
  function floatingContentProps(isFloating, contentMaxWidth, extraClassName) {
11042
11660
  if (!isFloating) return { className: extraClassName, style: void 0 };
11043
11661
  return {
@@ -11131,7 +11749,7 @@ function MinimalHeader({
11131
11749
  hasBreadcrumbs && /* @__PURE__ */ jsx(Breadcrumbs3, { items: breadcrumbs, pill: breadcrumbPill, separator: breadcrumbSeparator }),
11132
11750
  /* @__PURE__ */ jsxs("div", { className: cn("flex items-center justify-between gap-ds-4", hasBreadcrumbs && "mt-ds-0.5"), children: [
11133
11751
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-2 min-w-0", children: [
11134
- /* @__PURE__ */ jsx(TruncHeading, { as: Heading, className: "font-[family-name:var(--ds-font-display)] text-xl font-[var(--ds-weight-heading)] text-[var(--ds-text-primary)]", children: title }),
11752
+ /* @__PURE__ */ jsx(TruncHeading, { as: Heading, lines: 1, className: "font-[family-name:var(--ds-font-display)] text-xl font-[var(--ds-weight-heading)] text-[var(--ds-text-primary)]", children: title }),
11135
11753
  badge
11136
11754
  ] }),
11137
11755
  actions && /* @__PURE__ */ jsx(ButtonSizeContext.Provider, { value: "sm", children: /* @__PURE__ */ jsx("div", { className: "flex items-center gap-ds-2 shrink-0", children: actions }) })
@@ -11264,9 +11882,35 @@ function SnappingHeader({
11264
11882
  navbarFloating,
11265
11883
  sidebarFloating,
11266
11884
  containedBreadcrumbs,
11267
- headerRef
11885
+ headerRef,
11886
+ stickyTop = "top-[var(--ds-navbar-height)]"
11268
11887
  }) {
11269
11888
  const [collapsed, setCollapsed] = useState(false);
11889
+ const container = usePageContainer();
11890
+ const stickyRef = useRef(null);
11891
+ const setStickyRef = useCallback((node) => {
11892
+ stickyRef.current = node;
11893
+ if (typeof headerRef === "function") headerRef(node);
11894
+ else if (headerRef) headerRef.current = node;
11895
+ }, [headerRef]);
11896
+ useLayoutEffect(() => {
11897
+ const report = container?.setPinnedHeight;
11898
+ if (!report) return;
11899
+ const el = stickyRef.current;
11900
+ if (!(pinned && collapsed) || !el) {
11901
+ report(null);
11902
+ return;
11903
+ }
11904
+ const sync = () => report(el.offsetHeight);
11905
+ sync();
11906
+ if (typeof ResizeObserver === "undefined") return () => report(null);
11907
+ const obs = new ResizeObserver(sync);
11908
+ obs.observe(el);
11909
+ return () => {
11910
+ obs.disconnect();
11911
+ report(null);
11912
+ };
11913
+ }, [container?.setPinnedHeight, pinned, collapsed]);
11270
11914
  const sentinelRef = useRef(null);
11271
11915
  const collapsedRef = useRef(false);
11272
11916
  const rafRef = useRef(0);
@@ -11335,10 +11979,10 @@ function SnappingHeader({
11335
11979
  const headerEl = /* @__PURE__ */ jsxs(
11336
11980
  "header",
11337
11981
  {
11338
- ref: !isFloating ? headerRef : void 0,
11982
+ ref: !isFloating ? setStickyRef : void 0,
11339
11983
  className: cn(
11340
11984
  "transition-all duration-[var(--ds-transition-speed)] ease-[var(--ds-transition-ease)]",
11341
- !isFloating && pinned && collapsed && "sticky top-[var(--ds-navbar-height)] z-[calc(var(--ds-z-sticky,40)-1)]",
11985
+ !isFloating && pinned && collapsed && cn("sticky", stickyTop, "z-[calc(var(--ds-z-sticky,40)-1)]"),
11342
11986
  collapsed && (isFloating ? cn(FLOATING_HEADER, "bg-[var(--ds-bg-surface)]") : ["bg-[var(--ds-bg-surface)]", GLASS_BACKDROP, "border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]", "shadow-[var(--ds-shadow-sm)]"]),
11343
11987
  !collapsed && isColourful && (isFloating && !wrapperHasCard ? [
11344
11988
  "relative overflow-hidden",
@@ -11432,7 +12076,7 @@ function SnappingHeader({
11432
12076
  }
11433
12077
  );
11434
12078
  })(),
11435
- /* @__PURE__ */ jsx(TruncHeading, { as: Heading, className: cn(
12079
+ /* @__PURE__ */ jsx(TruncHeading, { as: Heading, lines: collapsed ? 1 : void 0, className: cn(
11436
12080
  "font-[family-name:var(--ds-font-display)] font-[var(--ds-weight-heading)]",
11437
12081
  collapsed ? "text-lg text-[var(--ds-text-primary)]" : cn("text-3xl", onLight ? "text-[var(--ds-text-on-primary)]" : "text-[var(--ds-text-primary)]")
11438
12082
  ), children: title }),
@@ -11473,7 +12117,7 @@ function SnappingHeader({
11473
12117
  /* @__PURE__ */ jsx(
11474
12118
  "div",
11475
12119
  {
11476
- ref: headerRef,
12120
+ ref: setStickyRef,
11477
12121
  className: cn(
11478
12122
  pinned && collapsed && "sticky z-[calc(var(--ds-z-sticky,40)-1)]",
11479
12123
  pinned && collapsed && "top-0"
@@ -11777,6 +12421,7 @@ var Navbar = forwardRef(function Navbar2({
11777
12421
  onMenuClick
11778
12422
  }, ref) {
11779
12423
  const appShell = useAppShell();
12424
+ useLayoutEffect(() => appShell?.registerChrome("navbar", position), [appShell?.registerChrome, position]);
11780
12425
  const isSmallPhone = appShell?.isMobile && !appShell.isTablet;
11781
12426
  const showHamburger = appShell?.isMobile && appShell.hasMobileMenu && !(isSmallPhone && appShell.hasMobileNav) || !!onMenuClick;
11782
12427
  const handleHamburger = () => onMenuClick ? onMenuClick() : appShell?.setSidebarOpen(!appShell.sidebarOpen);
@@ -12185,7 +12830,9 @@ var sizeClasses = {
12185
12830
  xl: "max-w-4xl",
12186
12831
  full: "max-w-[calc(100vw-2rem)]"
12187
12832
  };
12188
- function Dialog({ open, onClose, title, description, children, footer, variant = "default", size = "md", alert = false, container, onSubmit }) {
12833
+ function Dialog({ open, onClose: onCloseProp, title, description, children, footer, variant = "default", size = "md", alert = false, container, onSubmit, dirty, onDiscard }) {
12834
+ const guard = useDirtyGuard(dirty, onCloseProp, onDiscard);
12835
+ const onClose = guard.requestClose;
12189
12836
  const titleId = useId();
12190
12837
  const descriptionId = useId();
12191
12838
  const isMobile = useBreakpoint("sm");
@@ -12312,6 +12959,7 @@ function Dialog({ open, onClose, title, description, children, footer, variant =
12312
12959
  children && /* @__PURE__ */ jsx("div", { className: "mt-ds-4", children })
12313
12960
  ] })
12314
12961
  ] }) }),
12962
+ /* @__PURE__ */ jsx(DiscardConfirm, { open: guard.confirming, onKeep: guard.keep, onDiscard: guard.discard }),
12315
12963
  footer && /* @__PURE__ */ jsx("div", { className: cn(
12316
12964
  "flex justify-end gap-ds-2 p-ds-4",
12317
12965
  "border-t-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
@@ -13192,7 +13840,7 @@ var TOAST_POSITION_CLASSES = {
13192
13840
  "top-center": "top-ds-4 left-1/2 -translate-x-1/2",
13193
13841
  "bottom-center": "bottom-ds-4 left-1/2 -translate-x-1/2"
13194
13842
  };
13195
- function Toast({ variant = "default", message, visible, onDismiss, duration = 3e3, pauseOnHover = false, action, position = "bottom-right", mode, container, inline = false }) {
13843
+ function Toast({ variant = "default", message, visible, onDismiss, duration = 5e3, pauseOnHover = false, action, position = "bottom-right", mode, container, inline = false }) {
13196
13844
  const isMobile = useBreakpoint("sm");
13197
13845
  const isCentered = position === "top-center" || position === "bottom-center";
13198
13846
  const timerRef = useRef(void 0);
@@ -13295,7 +13943,7 @@ function useToast() {
13295
13943
  return ctx;
13296
13944
  }
13297
13945
  var toastSeq = 0;
13298
- function ToastProvider({ children, maxVisible = 3, position = "bottom-right", duration = 3e3, mode, pauseOnHover = true }) {
13946
+ function ToastProvider({ children, maxVisible = 1, position = "bottom-right", duration = 5e3, mode, pauseOnHover = true }) {
13299
13947
  const [queue, setQueue] = useState([]);
13300
13948
  const isMobile = useBreakpoint("sm");
13301
13949
  const dismiss = useCallback((id) => {
@@ -13371,54 +14019,6 @@ function ToastProvider({ children, maxVisible = 3, position = "bottom-right", du
13371
14019
  )
13372
14020
  ] });
13373
14021
  }
13374
- var maxWidthValues = {
13375
- sm: "var(--ds-bp-sm)",
13376
- md: "var(--ds-bp-md)",
13377
- lg: "var(--ds-bp-lg)",
13378
- xl: "var(--ds-bp-xl)",
13379
- full: "100%"
13380
- };
13381
- var widthToMaxWidth = {
13382
- constrained: "xl",
13383
- full: "full",
13384
- narrow: "md"
13385
- };
13386
- var paddingClasses = {
13387
- none: "",
13388
- default: "px-ds-6",
13389
- wide: "px-ds-10"
13390
- };
13391
- var verticalPaddingClasses = {
13392
- none: "",
13393
- default: "pt-ds-8 pb-ds-12",
13394
- spacious: "pt-ds-8 pb-ds-16"
13395
- };
13396
- var PageContainer = forwardRef(function PageContainer2({
13397
- width,
13398
- maxWidth = "xl",
13399
- padding = "default",
13400
- verticalPadding = "default",
13401
- centered = true,
13402
- children,
13403
- className
13404
- }, ref) {
13405
- const resolvedMaxWidth = width ? widthToMaxWidth[width] : maxWidth;
13406
- return /* @__PURE__ */ jsx(
13407
- "div",
13408
- {
13409
- ref,
13410
- className: cn(
13411
- "w-full",
13412
- paddingClasses[padding],
13413
- verticalPaddingClasses[verticalPadding],
13414
- centered && "mx-auto",
13415
- className
13416
- ),
13417
- style: { maxWidth: maxWidthValues[resolvedMaxWidth], containerType: "inline-size" },
13418
- children
13419
- }
13420
- );
13421
- });
13422
14022
  var SectionHeading = forwardRef(function SectionHeading2({
13423
14023
  title,
13424
14024
  description,
@@ -13546,6 +14146,6 @@ var TextOverflow = forwardRef(function TextOverflow2({
13546
14146
  return /* @__PURE__ */ jsx(Tooltip, { content: resolvedTooltipContent, children: content });
13547
14147
  });
13548
14148
 
13549
- 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, CornerPanel, DEFAULT_DATE_RANGE_PRESETS, DataList, DatePicker, DateRangePicker, DescriptionList, Dialog, Dock, Drawer, Dropdown, DropdownMenu, EmptyState, 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, Sheet, Sidebar, SidebarNav, SliderRange, Spinner, StackedList, StatsCard, StatusDot, Stepper, Table, Tabs, Tag, TagInput, TextOverflow, Textarea, TimePicker, Timeline, Toast, ToastProvider, Toggle, Tour, TransferList, TreeView, useAppShell, useFormContext, useSidebar, useToast };
14149
+ 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, CornerPanel, DEFAULT_DATE_RANGE_PRESETS, DataList, DatePicker, DateRangePicker, DescriptionList, Dialog, Dock, Drawer, Dropdown, DropdownMenu, EmptyState, 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 };
13550
14150
  //# sourceMappingURL=index.js.map
13551
14151
  //# sourceMappingURL=index.js.map