gridsmith-ui 0.18.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/charts.js +3 -3
  2. package/dist/charts.js.map +1 -1
  3. package/dist/chat.js +4 -4
  4. package/dist/{chunk-CIVSNXFG.js → chunk-5PDTHKW3.js} +17 -9
  5. package/dist/chunk-5PDTHKW3.js.map +1 -0
  6. package/dist/{chunk-XGSQOMEY.js → chunk-EQDR57AM.js} +4 -4
  7. package/dist/{chunk-XGSQOMEY.js.map → chunk-EQDR57AM.js.map} +1 -1
  8. package/dist/{chunk-EMCSARQO.js → chunk-ILIODA7W.js} +3 -3
  9. package/dist/{chunk-EMCSARQO.js.map → chunk-ILIODA7W.js.map} +1 -1
  10. package/dist/{chunk-OENJCGTJ.js → chunk-JN37OVTP.js} +4 -4
  11. package/dist/{chunk-OENJCGTJ.js.map → chunk-JN37OVTP.js.map} +1 -1
  12. package/dist/{chunk-4KVFH5KC.js → chunk-M2QRNBDQ.js} +4 -3
  13. package/dist/chunk-M2QRNBDQ.js.map +1 -0
  14. package/dist/{chunk-Z63OSN64.js → chunk-NCIZQYXQ.js} +7 -4
  15. package/dist/chunk-NCIZQYXQ.js.map +1 -0
  16. package/dist/{chunk-PVTGMCVR.js → chunk-XYDIBW3J.js} +4 -4
  17. package/dist/{chunk-PVTGMCVR.js.map → chunk-XYDIBW3J.js.map} +1 -1
  18. package/dist/data-grid.js +3 -3
  19. package/dist/editor.js +3 -3
  20. package/dist/editor.js.map +1 -1
  21. package/dist/index.d.ts +146 -53
  22. package/dist/index.js +437 -156
  23. package/dist/index.js.map +1 -1
  24. package/dist/kanban.js +3 -3
  25. package/dist/resizable.d.ts +8 -1
  26. package/dist/resizable.js +35 -15
  27. package/dist/resizable.js.map +1 -1
  28. package/dist/roots.css +164 -2
  29. package/dist/tailwind.css +14 -0
  30. package/dist/utils.js +1 -1
  31. package/dist/virtualized.js +1 -1
  32. package/dist-static/tailwind.css +14 -0
  33. package/package.json +1 -1
  34. package/dist/chunk-4KVFH5KC.js.map +0 -1
  35. package/dist/chunk-CIVSNXFG.js.map +0 -1
  36. package/dist/chunk-Z63OSN64.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,24 +1,24 @@
1
- import { Button, useAppShell, REACT_VERSION_PROBLEM, ControlSizeContext, useDirtyGuard, OverlayCloseCtx, DiscardConfirm, Spinner, DrawerFooterCtx, useCompact, useElementWidth, Sheet, useCloseSettle, Drawer, useRememberedWidth, pxOf, ResizeEdge, defaultMaxWidth } from './chunk-CIVSNXFG.js';
2
- export { AppShell, Button, ButtonSizeContext, ControlSizeContext, Drawer, Sheet, SkipLink, Spinner, useAppShell, useUnsavedChanges } from './chunk-CIVSNXFG.js';
3
- import { Skeleton } from './chunk-Z63OSN64.js';
4
- export { Skeleton } from './chunk-Z63OSN64.js';
1
+ import { Button, useAppShell, REACT_VERSION_PROBLEM, ControlSizeContext, useDirtyGuard, OverlayCloseCtx, DiscardConfirm, Spinner, DrawerFooterCtx, useCompact, useElementWidth, Sheet, useCloseSettle, Drawer, useRememberedWidth, pxOf, ResizeEdge, defaultMaxWidth } from './chunk-5PDTHKW3.js';
2
+ export { AppShell, Button, ButtonSizeContext, ControlSizeContext, Drawer, Sheet, SkipLink, Spinner, useAppShell, useUnsavedChanges } from './chunk-5PDTHKW3.js';
3
+ import { Skeleton, TITLE_TEXT } from './chunk-NCIZQYXQ.js';
4
+ export { Skeleton } from './chunk-NCIZQYXQ.js';
5
5
  import { FOCUSABLE, usePopupFocus, FIELD, ZOOM_KEYS, useOverlayLayer, useFocusTrap, useFocusBeforeOpen, rememberFocus, focusInitial, OVERLAY_CLOSE_ATTR, focusAfterRemoval, rovingTabStop, useGridKeyboard, useNonModalDialog, usePressMarks, useReturnFocus, useCloseOnFocusOut } from './chunk-JXANCXK6.js';
6
6
  export { useGridKeyboard } from './chunk-JXANCXK6.js';
7
- import { Link } from './chunk-XGSQOMEY.js';
8
- export { CodeBlock, Divider, Link } from './chunk-XGSQOMEY.js';
9
- import { Badge } from './chunk-PVTGMCVR.js';
10
- export { Badge } from './chunk-PVTGMCVR.js';
11
- import { inferColumnKinds, CardValue } from './chunk-OENJCGTJ.js';
7
+ import { Link } from './chunk-EQDR57AM.js';
8
+ export { CodeBlock, Divider, Link } from './chunk-EQDR57AM.js';
9
+ import { Badge } from './chunk-XYDIBW3J.js';
10
+ export { Badge } from './chunk-XYDIBW3J.js';
11
+ import { inferColumnKinds, CardValue } from './chunk-JN37OVTP.js';
12
12
  import { SplitPaneCtx, useShellPreferences, TableCardCtx, PREFERENCE_HOUSE_DEFAULTS, preferenceActionKey } from './chunk-TKVTZWBN.js';
13
13
  export { PreferencesContext, preferenceActionKey, usePreferences } from './chunk-TKVTZWBN.js';
14
14
  import { useBreakpoint, readBreakpointPx } from './chunk-CRQ6KCRD.js';
15
15
  export { BreakpointWidthProvider, useBreakpoint } from './chunk-CRQ6KCRD.js';
16
- import { useTextOverflow, Tooltip, usePosition } from './chunk-EMCSARQO.js';
17
- export { Tooltip, useTextOverflow } from './chunk-EMCSARQO.js';
16
+ import { useTextOverflow, Tooltip, usePosition } from './chunk-ILIODA7W.js';
17
+ export { Tooltip, useTextOverflow } from './chunk-ILIODA7W.js';
18
18
  import { mergeRefs, Slot } from './chunk-7Q46JXSK.js';
19
19
  import { useLatest } from './chunk-WH2FUFAC.js';
20
20
  import { Icon } from './chunk-4LDYR7QP.js';
21
- import { cn, GLASS_SURFACE, ACTIVE_RING, GLASS_BACKDROP, GLASS_DROPDOWN, lockBodyScroll, unlockBodyScroll, GLASS_OVERLAY, ERROR_ACTIVE_RING, ERROR_FOCUS_RING, FOCUS_RING, ERROR_HAS_FOCUS_RING, HAS_FOCUS_RING, ERROR_FOCUS_WITHIN_RING, FOCUS_WITHIN_RING } from './chunk-4KVFH5KC.js';
21
+ import { cn, GLASS_SURFACE, GLASS_BACKDROP, GLASS_DROPDOWN, lockBodyScroll, unlockBodyScroll, OVERLAY_SURFACE, GLASS_OVERLAY, ERROR_ACTIVE_RING, ACTIVE_RING, ERROR_FOCUS_RING, FOCUS_RING, ERROR_HAS_FOCUS_RING, HAS_FOCUS_RING, ERROR_FOCUS_WITHIN_RING, FOCUS_WITHIN_RING } from './chunk-M2QRNBDQ.js';
22
22
  import { createContext, forwardRef, useState, useRef, useCallback, useLayoutEffect, useMemo, useContext, Children, isValidElement, useId, memo, useEffect, useImperativeHandle, Fragment as Fragment$1, cloneElement, useSyncExternalStore } from 'react';
23
23
  import { createPortal, flushSync } from 'react-dom';
24
24
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
@@ -184,7 +184,7 @@ function BulkActionBar({ selectedCount, actions, onClearSelection, className })
184
184
  ) : /* @__PURE__ */ jsx("div", { className: "flex items-center gap-ds-2", children: actions.map((action, i) => /* @__PURE__ */ jsx(
185
185
  Button,
186
186
  {
187
- variant: action.variant === "destructive" ? "destructive" : "secondary",
187
+ variant: "secondary",
188
188
  size: "sm",
189
189
  onClick: action.onClick,
190
190
  children: action.label
@@ -407,6 +407,155 @@ function Toggle({ checked, onChange, label, disabled, error = false, layout = "f
407
407
  }
408
408
  );
409
409
  }
410
+ var FormCtx = createContext(null);
411
+ function useFormContext() {
412
+ const ctx = useContext(FormCtx);
413
+ if (!ctx) throw new Error("useFormContext must be used within FormWrapper");
414
+ return ctx;
415
+ }
416
+ function useOptionalFormContext() {
417
+ return useContext(FormCtx);
418
+ }
419
+ function InlineFieldError({ message, id, className }) {
420
+ return /* @__PURE__ */ jsxs(
421
+ "p",
422
+ {
423
+ id,
424
+ role: "alert",
425
+ className: cn(
426
+ "flex items-center gap-ds-1.5 text-xs text-[var(--ds-status-error)]",
427
+ "mt-ds-1.5",
428
+ className
429
+ ),
430
+ children: [
431
+ /* @__PURE__ */ jsx(Icon, { name: "error", size: 14, className: "shrink-0" }),
432
+ /* @__PURE__ */ jsx("span", { children: message })
433
+ ]
434
+ }
435
+ );
436
+ }
437
+ var FieldContext = createContext(null);
438
+ function useField() {
439
+ return useContext(FieldContext);
440
+ }
441
+ function useFieldControl(own, options = {}) {
442
+ const field = useField();
443
+ const id = own.id ?? field?.controlId;
444
+ const invalid = !!own.error || own["aria-invalid"] === true || own["aria-invalid"] === "true" || !!field?.invalid;
445
+ const required = !!own.required || own["aria-required"] === true || own["aria-required"] === "true" || !!field?.required;
446
+ const noteId = options.requiredInDescription && required && id ? `${id}-required` : void 0;
447
+ const ids = [own["aria-describedby"], field?.describedBy, noteId].filter(Boolean).join(" ").split(/\s+/).filter(Boolean);
448
+ return {
449
+ id,
450
+ labelId: own["aria-labelledby"] ?? field?.labelId,
451
+ describedBy: [...new Set(ids)].join(" ") || void 0,
452
+ invalid,
453
+ required,
454
+ requiredNote: noteId ? /* @__PURE__ */ jsx("span", { id: noteId, hidden: true, children: "Required" }) : null
455
+ };
456
+ }
457
+ function Field({ label, children, hint, error, required, labelAction, id: idProp, validate, className }) {
458
+ const generated = useId();
459
+ const id = idProp ?? `field-${generated}`;
460
+ const hintId = `${id}-hint`;
461
+ const errorId = `${id}-error`;
462
+ const form = useOptionalFormContext();
463
+ const mode = form?.validationMode ?? "onBlur";
464
+ const [localError, setLocalError] = useState(void 0);
465
+ const [left, setLeft] = useState(false);
466
+ const checkError = validate ? form ? form.errors[id] : localError : void 0;
467
+ const child = typeof children === "function" ? null : Children.only(children);
468
+ const childProps = child && isValidElement(child) ? child.props : null;
469
+ const latest = useLatest({ validate, childProps, id, mode, left, showing: !!checkError, form });
470
+ const check = useCallback(() => {
471
+ const { validate: fn, childProps: props, id: controlId } = latest.current;
472
+ if (!fn) return void 0;
473
+ const value = props && "value" in props ? props.value : props && "checked" in props ? props.checked : document.getElementById(controlId)?.value;
474
+ const message = fn(value);
475
+ return typeof message === "string" && message.length > 0 ? message : void 0;
476
+ }, [latest]);
477
+ const show = useCallback((message) => {
478
+ const { form: f, id: controlId } = latest.current;
479
+ if (f) f.setError(controlId, message);
480
+ else setLocalError(message);
481
+ }, [latest]);
482
+ const register = form?.register;
483
+ const checks = !!validate;
484
+ useEffect(() => register && checks ? register(id, check) : void 0, [register, checks, id, check]);
485
+ const leaving = useRef(null);
486
+ useEffect(() => () => {
487
+ if (leaving.current) clearTimeout(leaving.current);
488
+ }, []);
489
+ const onFocusCapture = () => {
490
+ if (leaving.current) {
491
+ clearTimeout(leaving.current);
492
+ leaving.current = null;
493
+ }
494
+ };
495
+ const onBlurCapture = () => {
496
+ if (!validate) return;
497
+ if (leaving.current) clearTimeout(leaving.current);
498
+ leaving.current = setTimeout(() => {
499
+ leaving.current = null;
500
+ setLeft(true);
501
+ if (latest.current.mode !== "onSubmit" || latest.current.showing) show(check());
502
+ }, 0);
503
+ };
504
+ const recheck = () => {
505
+ if (!validate) return;
506
+ setTimeout(() => {
507
+ const { mode: m, left: wasLeft, showing } = latest.current;
508
+ if (m === "onChange" && wasLeft || showing) show(check());
509
+ }, 0);
510
+ };
511
+ const shownError = typeof error === "string" && error.length > 0 ? error : checkError;
512
+ const hasError = typeof shownError === "string" && shownError.length > 0;
513
+ const describedBy = [hint ? hintId : null, hasError ? errorId : null].filter(Boolean).join(" ") || void 0;
514
+ const labelId = `${id}-label`;
515
+ const fieldContext = useMemo(() => ({ controlId: id, labelId, describedBy, invalid: hasError, required: !!required }), [id, labelId, describedBy, hasError, required]);
516
+ const controlProps = {
517
+ id,
518
+ "aria-describedby": describedBy,
519
+ ...hasError ? { "aria-invalid": true, error: true } : {},
520
+ ...required ? { "aria-required": true, required: true } : {}
521
+ };
522
+ let control;
523
+ if (typeof children === "function") {
524
+ control = children(controlProps);
525
+ } else {
526
+ if (!child || !isValidElement(child)) throw new Error("Field expects a single form control as its child");
527
+ const isNative = typeof child.type === "string";
528
+ const { error: errorFlag, ...ariaOnly } = controlProps;
529
+ const ownOnChange = childProps?.onChange;
530
+ const onChange = validate && !isNative ? { onChange: (...args) => {
531
+ ownOnChange?.(...args);
532
+ recheck();
533
+ } } : {};
534
+ control = cloneElement(child, isNative ? ariaOnly : { ...ariaOnly, ...errorFlag ? { error: true } : {}, ...onChange });
535
+ }
536
+ return (
537
+ // In a settings panel a field keeps the narrow reading width however wide the page is (--ds-form-max, Sprint 25.9 A4)
538
+ /* @__PURE__ */ jsxs(
539
+ "div",
540
+ {
541
+ className: cn("flex flex-col gap-ds-1.5 max-w-[var(--ds-form-max,none)]", className),
542
+ ...validate ? { onFocusCapture, onBlurCapture, onInputCapture: recheck } : {},
543
+ children: [
544
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-ds-2", children: [
545
+ /* @__PURE__ */ jsxs(Label, { id: labelId, htmlFor: id, children: [
546
+ label,
547
+ required && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "ml-ds-0.5 text-[var(--ds-status-error)]", children: "*" })
548
+ ] }),
549
+ labelAction
550
+ ] }),
551
+ /* @__PURE__ */ jsx(FieldContext.Provider, { value: fieldContext, children: control }),
552
+ hint && /* @__PURE__ */ jsx("p", { id: hintId, className: "text-xs text-[var(--ds-text-muted)]", children: hint }),
553
+ hasError && /* @__PURE__ */ jsx(InlineFieldError, { id: errorId, message: shownError, className: "mt-0" })
554
+ ]
555
+ }
556
+ )
557
+ );
558
+ }
410
559
  var DROPDOWN_HEIGHT = {
411
560
  sm: "px-ds-2 py-ds-1 text-xs h-[var(--ds-control-height-sm)]",
412
561
  md: "px-ds-3 py-ds-2 text-sm h-[var(--ds-control-height-md)]",
@@ -427,9 +576,16 @@ function Dropdown({
427
576
  container,
428
577
  error = false,
429
578
  showChips = true,
430
- "aria-label": ariaLabel
579
+ "aria-label": ariaLabel,
580
+ ...wiring
431
581
  }) {
432
582
  const contextSize = useContext(ControlSizeContext);
583
+ const generatedId = useId();
584
+ const control = useFieldControl({ ...wiring, error: error || void 0 }, { requiredInDescription: true });
585
+ const triggerId = control.id ?? generatedId;
586
+ const labelSpanId = `${triggerId}-label`;
587
+ const labelledBy = control.labelId ? `${control.labelId} ${triggerId}` : label && !ariaLabel ? `${labelSpanId} ${triggerId}` : void 0;
588
+ const invalid = control.invalid;
433
589
  const size = sizeProp ?? contextSize ?? "md";
434
590
  const [open, setOpen] = useState(false);
435
591
  const [search, setSearch] = useState("");
@@ -597,11 +753,13 @@ function Dropdown({
597
753
  const displayLabel = multiple ? selectedValues.length > 0 ? `${selectedValues.length} selected` : placeholder : options.find((o) => o.id === selectedValues[0])?.label ?? placeholder;
598
754
  const { ref: overflowRef, isTruncated } = useTextOverflow({ lines: 1, content: displayLabel });
599
755
  return /* @__PURE__ */ jsxs("div", { ref, className: cn("relative", className), children: [
600
- label && /* @__PURE__ */ jsx("span", { className: "block mb-ds-1.5 text-sm font-medium text-[var(--ds-text-secondary)]", children: label }),
756
+ label && /* @__PURE__ */ jsx("span", { id: labelSpanId, className: "block mb-ds-1.5 text-sm font-medium text-[var(--ds-text-secondary)]", children: label }),
757
+ control.requiredNote,
601
758
  /* @__PURE__ */ jsxs(
602
759
  "button",
603
760
  {
604
761
  ref: triggerRef,
762
+ id: triggerId,
605
763
  type: "button",
606
764
  onClick: () => !disabled && setOpen(!open),
607
765
  onKeyDown: (e) => {
@@ -612,22 +770,24 @@ function Dropdown({
612
770
  }
613
771
  },
614
772
  disabled,
615
- "aria-invalid": error || void 0,
773
+ "aria-invalid": invalid || void 0,
616
774
  "aria-haspopup": "listbox",
617
775
  "aria-expanded": open,
618
- "aria-label": ariaLabel,
776
+ "aria-label": labelledBy ? void 0 : ariaLabel,
777
+ "aria-labelledby": labelledBy,
778
+ "aria-describedby": control.describedBy,
619
779
  className: cn(
620
780
  "w-full flex items-center justify-between gap-ds-2",
621
781
  DROPDOWN_HEIGHT[size],
622
782
  "bg-[var(--ds-input-bg)] text-[var(--ds-text-primary)]",
623
783
  "border-[length:var(--ds-border-width)]",
624
- error ? "border-[var(--ds-status-error)]" : "border-[var(--ds-input-border)]",
784
+ invalid ? "border-[var(--ds-status-error)]" : "border-[var(--ds-input-border)]",
625
785
  "rounded-[var(--ds-radius-md)]",
626
786
  "shadow-[var(--ds-input-shadow)]",
627
787
  "transition-colors",
628
- !error && !disabled && !open && "hover:border-[var(--ds-input-border-hover)]",
629
- open && (error ? ERROR_ACTIVE_RING : ACTIVE_RING),
630
- error ? ERROR_FOCUS_RING : FOCUS_RING,
788
+ !invalid && !disabled && !open && "hover:border-[var(--ds-input-border-hover)]",
789
+ open && (invalid ? ERROR_ACTIVE_RING : ACTIVE_RING),
790
+ invalid ? ERROR_FOCUS_RING : FOCUS_RING,
631
791
  "cursor-pointer disabled:cursor-not-allowed disabled:opacity-disabled",
632
792
  !selectedValues.length && "text-[var(--ds-text-muted)]"
633
793
  ),
@@ -642,9 +802,10 @@ function Dropdown({
642
802
  "div",
643
803
  {
644
804
  ref: menuRef,
805
+ tabIndex: -1,
645
806
  onClick: (e) => e.stopPropagation(),
646
807
  className: cn(
647
- "fixed z-[var(--ds-z-popover)]",
808
+ "fixed z-[var(--ds-z-popover)] outline-none",
648
809
  "bg-[var(--ds-bg-surface-elevated)]",
649
810
  "[backdrop-filter:var(--ds-glass-filter)] [-webkit-backdrop-filter:var(--ds-glass-filter)]",
650
811
  "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
@@ -771,8 +932,9 @@ var TEXT_SIZE = {
771
932
  md: "text-sm",
772
933
  lg: "text-base"
773
934
  };
774
- function Combobox({ value, onChange, options, placeholder = "Search\u2026", disabled, loading = false, className, ref, container, error = false, size: sizeProp }) {
935
+ function Combobox({ value, onChange, options, placeholder = "Search\u2026", disabled, loading = false, className, ref, container, error = false, size: sizeProp, ...wiring }) {
775
936
  const contextSize = useContext(ControlSizeContext);
937
+ const control = useFieldControl({ ...wiring, error: error || void 0 });
776
938
  const size = sizeProp ?? contextSize ?? "md";
777
939
  const [open, setOpen] = useState(false);
778
940
  useOverlayLayer(open);
@@ -883,12 +1045,12 @@ function Combobox({ value, onChange, options, placeholder = "Search\u2026", disa
883
1045
  "flex items-center",
884
1046
  CONTROL_HEIGHT[size],
885
1047
  "border-[length:var(--ds-border-width)]",
886
- error ? "border-[var(--ds-status-error)]" : "border-[var(--ds-input-border)]",
1048
+ control.invalid ? "border-[var(--ds-status-error)]" : "border-[var(--ds-input-border)]",
887
1049
  "bg-[var(--ds-input-bg)] rounded-[var(--ds-radius-md)]",
888
1050
  "shadow-[var(--ds-input-shadow)]",
889
1051
  "transition-colors duration-[var(--ds-transition-speed)]",
890
- !error && !disabled && "hover:border-[var(--ds-input-border-hover)]",
891
- error ? ERROR_HAS_FOCUS_RING : HAS_FOCUS_RING,
1052
+ !control.invalid && !disabled && "hover:border-[var(--ds-input-border-hover)]",
1053
+ control.invalid ? ERROR_HAS_FOCUS_RING : HAS_FOCUS_RING,
892
1054
  disabled && "opacity-disabled pointer-events-none"
893
1055
  ),
894
1056
  children: [
@@ -898,8 +1060,12 @@ function Combobox({ value, onChange, options, placeholder = "Search\u2026", disa
898
1060
  {
899
1061
  ref: mergeInputRefs,
900
1062
  type: "text",
1063
+ id: control.id,
901
1064
  role: "combobox",
902
- "aria-invalid": error || void 0,
1065
+ "aria-labelledby": wiring["aria-labelledby"],
1066
+ "aria-describedby": control.describedBy,
1067
+ "aria-invalid": control.invalid || void 0,
1068
+ "aria-required": control.required || void 0,
903
1069
  "aria-expanded": open,
904
1070
  "aria-controls": listboxId,
905
1071
  "aria-autocomplete": "list",
@@ -1312,7 +1478,7 @@ function parseTypedDate(text, dayFirst = localeDayFirst()) {
1312
1478
  }
1313
1479
  return null;
1314
1480
  }
1315
- function DatePicker({ value, onChange, placeholder = "Pick a date", disabled, className, ref, container, error = false, label, id, size: sizeProp, min, max, range: range2 }) {
1481
+ function DatePicker({ value, onChange, placeholder = "Pick a date", disabled, className, ref, container, error = false, label, id, size: sizeProp, min, max, range: range2, ...wiring }) {
1316
1482
  const contextSize = useContext(ControlSizeContext);
1317
1483
  const size = sizeProp ?? contextSize ?? "md";
1318
1484
  const [open, setOpen] = useState(false);
@@ -1323,7 +1489,8 @@ function DatePicker({ value, onChange, placeholder = "Pick a date", disabled, cl
1323
1489
  const panelRef = useRef(null);
1324
1490
  const gridRef = useRef(null);
1325
1491
  const generatedId = useId();
1326
- const inputId = id ?? generatedId;
1492
+ const control = useFieldControl({ ...wiring, id, error: error || void 0 }, { requiredInDescription: true });
1493
+ const inputId = control.id ?? generatedId;
1327
1494
  const pos = usePosition(triggerRef, panelRef, open, { side: "bottom", align: "start", offset: 4, flip: true });
1328
1495
  const year = viewDate.getFullYear();
1329
1496
  const month = viewDate.getMonth();
@@ -1508,6 +1675,7 @@ function DatePicker({ value, onChange, placeholder = "Pick a date", disabled, cl
1508
1675
  for (let i = 0; i < cells.length; i += 7) weeks.push(cells.slice(i, i + 7));
1509
1676
  return /* @__PURE__ */ jsxs("div", { ref, className: cn(className), children: [
1510
1677
  label && /* @__PURE__ */ jsx("label", { htmlFor: inputId, className: "block text-sm font-medium text-[var(--ds-text-primary)] mb-ds-1", children: label }),
1678
+ control.requiredNote,
1511
1679
  /* @__PURE__ */ jsxs(
1512
1680
  "div",
1513
1681
  {
@@ -1515,9 +1683,11 @@ function DatePicker({ value, onChange, placeholder = "Pick a date", disabled, cl
1515
1683
  "data-date-field": "",
1516
1684
  role: "group",
1517
1685
  id: inputId,
1518
- "aria-label": label ?? "Date",
1686
+ "aria-label": control.labelId ? void 0 : label ?? "Date",
1687
+ "aria-labelledby": control.labelId,
1688
+ "aria-describedby": control.describedBy,
1519
1689
  "aria-description": !value ? placeholder : void 0,
1520
- "aria-invalid": error || typedInvalid || void 0,
1690
+ "aria-invalid": control.invalid || typedInvalid || void 0,
1521
1691
  onPaste: (e) => {
1522
1692
  const text = e.clipboardData.getData("text");
1523
1693
  if (text) {
@@ -1530,12 +1700,12 @@ function DatePicker({ value, onChange, placeholder = "Pick a date", disabled, cl
1530
1700
  CONTROL_HEIGHT2[size],
1531
1701
  TEXT_SIZE2[size],
1532
1702
  "border-[length:var(--ds-border-width)]",
1533
- error || typedInvalid ? "border-[var(--ds-status-error)]" : "border-[var(--ds-input-border)]",
1703
+ control.invalid || typedInvalid ? "border-[var(--ds-status-error)]" : "border-[var(--ds-input-border)]",
1534
1704
  "bg-[var(--ds-input-bg)] rounded-[var(--ds-radius-md)]",
1535
1705
  "shadow-[var(--ds-input-shadow)]",
1536
1706
  "transition-colors",
1537
1707
  "px-ds-3 py-ds-2",
1538
- !error && !disabled && !open && "hover:border-[var(--ds-input-border-hover)] focus-within:border-[var(--ds-input-border-hover)]",
1708
+ !control.invalid && !disabled && !open && "hover:border-[var(--ds-input-border-hover)] focus-within:border-[var(--ds-input-border-hover)]",
1539
1709
  open && ACTIVE_RING,
1540
1710
  disabled && "opacity-disabled cursor-not-allowed",
1541
1711
  "text-[var(--ds-text-primary)]"
@@ -1592,8 +1762,9 @@ function DatePicker({ value, onChange, placeholder = "Pick a date", disabled, cl
1592
1762
  ref: panelRef,
1593
1763
  role: "dialog",
1594
1764
  "aria-label": "Choose a date",
1765
+ tabIndex: -1,
1595
1766
  className: cn(
1596
- "fixed z-[var(--ds-z-popover)] w-[280px]",
1767
+ "fixed z-[var(--ds-z-popover)] w-[280px] outline-none",
1597
1768
  "bg-[var(--ds-bg-surface-elevated)]",
1598
1769
  "[backdrop-filter:var(--ds-glass-filter)] [-webkit-backdrop-filter:var(--ds-glass-filter)]",
1599
1770
  "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
@@ -1925,7 +2096,8 @@ function DateRangePicker({
1925
2096
  max,
1926
2097
  presets = DEFAULT_DATE_RANGE_PRESETS,
1927
2098
  weekStartsOn = 0,
1928
- size: sizeProp
2099
+ size: sizeProp,
2100
+ ...wiring
1929
2101
  }) {
1930
2102
  const contextSize = useContext(ControlSizeContext);
1931
2103
  const size = sizeProp ?? contextSize ?? "md";
@@ -1935,7 +2107,9 @@ function DateRangePicker({
1935
2107
  const triggerRef = useRef(null);
1936
2108
  const panelRef = useRef(null);
1937
2109
  const generatedId = useId();
1938
- const triggerId = id ?? generatedId;
2110
+ const control = useFieldControl({ ...wiring, id, error: error || void 0 }, { requiredInDescription: true });
2111
+ const triggerId = control.id ?? generatedId;
2112
+ const labelledBy = control.labelId ? `${control.labelId} ${triggerId}` : void 0;
1939
2113
  const pos = usePosition(triggerRef, panelRef, open, { side: "bottom", align: "start", offset: 4, flip: true });
1940
2114
  const today = useMemo(() => startOfDay(/* @__PURE__ */ new Date()), []);
1941
2115
  const dayNames = getOrderedDayNamesShort(weekStartsOn);
@@ -2018,6 +2192,7 @@ function DateRangePicker({
2018
2192
  const summary = value.start && value.end ? `${fmt(value.start)} \u2013 ${fmt(value.end)}` : value.start ? `${fmt(value.start)} \u2013 \u2026` : null;
2019
2193
  return /* @__PURE__ */ jsxs("div", { ref, className: cn(className), children: [
2020
2194
  label && /* @__PURE__ */ jsx("label", { htmlFor: triggerId, className: "block text-sm font-medium text-[var(--ds-text-primary)] mb-ds-1", children: label }),
2195
+ control.requiredNote,
2021
2196
  /* @__PURE__ */ jsxs(
2022
2197
  "button",
2023
2198
  {
@@ -2026,7 +2201,9 @@ function DateRangePicker({
2026
2201
  type: "button",
2027
2202
  onClick: () => !disabled && setOpen((o) => !o),
2028
2203
  disabled,
2029
- "aria-invalid": error || void 0,
2204
+ "aria-labelledby": labelledBy,
2205
+ "aria-describedby": control.describedBy,
2206
+ "aria-invalid": control.invalid || void 0,
2030
2207
  "aria-haspopup": "dialog",
2031
2208
  "aria-expanded": open,
2032
2209
  className: cn(
@@ -2034,14 +2211,14 @@ function DateRangePicker({
2034
2211
  CONTROL_HEIGHT3[size],
2035
2212
  TEXT_SIZE3[size],
2036
2213
  "border-[length:var(--ds-border-width)]",
2037
- error ? "border-[var(--ds-status-error)]" : "border-[var(--ds-input-border)]",
2214
+ control.invalid ? "border-[var(--ds-status-error)]" : "border-[var(--ds-input-border)]",
2038
2215
  "bg-[var(--ds-input-bg)] rounded-[var(--ds-radius-md)]",
2039
2216
  "shadow-[var(--ds-input-shadow)]",
2040
2217
  "transition-colors cursor-pointer",
2041
2218
  "px-ds-3 py-ds-2",
2042
- !error && !disabled && !open && "hover:border-[var(--ds-input-border-hover)]",
2043
- open && (error ? ERROR_ACTIVE_RING : ACTIVE_RING),
2044
- error ? ERROR_FOCUS_RING : FOCUS_RING,
2219
+ !control.invalid && !disabled && !open && "hover:border-[var(--ds-input-border-hover)]",
2220
+ open && (control.invalid ? ERROR_ACTIVE_RING : ACTIVE_RING),
2221
+ control.invalid ? ERROR_FOCUS_RING : FOCUS_RING,
2045
2222
  disabled && "opacity-disabled cursor-not-allowed",
2046
2223
  summary ? "text-[var(--ds-text-primary)]" : "text-[var(--ds-text-muted)]"
2047
2224
  ),
@@ -2058,8 +2235,9 @@ function DateRangePicker({
2058
2235
  ref: panelRef,
2059
2236
  role: "dialog",
2060
2237
  "aria-label": "Choose a date range",
2238
+ tabIndex: -1,
2061
2239
  className: cn(
2062
- "fixed z-[var(--ds-z-popover)] flex",
2240
+ "fixed z-[var(--ds-z-popover)] flex outline-none",
2063
2241
  "bg-[var(--ds-bg-surface-elevated)]",
2064
2242
  "[backdrop-filter:var(--ds-glass-filter)] [-webkit-backdrop-filter:var(--ds-glass-filter)]",
2065
2243
  "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
@@ -2264,7 +2442,7 @@ function Progress({ value, max = 100, variant = "default", size = "md", indeterm
2264
2442
  {
2265
2443
  className: cn(
2266
2444
  "h-full rounded-[var(--ds-radius-full)]",
2267
- indeterminate ? "animate-[indeterminate_1.5s_ease-in-out_infinite] w-1/3" : "transition-all",
2445
+ indeterminate ? "animate-[gs-indeterminate_1.5s_ease-in-out_infinite] w-1/3" : "transition-all",
2268
2446
  variantFills[variant]
2269
2447
  ),
2270
2448
  style: indeterminate ? void 0 : { width: `${pct}%` }
@@ -2278,69 +2456,6 @@ function Progress({ value, max = 100, variant = "default", size = "md", indeterm
2278
2456
  ] })
2279
2457
  ] });
2280
2458
  }
2281
- function InlineFieldError({ message, id, className }) {
2282
- return /* @__PURE__ */ jsxs(
2283
- "p",
2284
- {
2285
- id,
2286
- role: "alert",
2287
- className: cn(
2288
- "flex items-center gap-ds-1.5 text-xs text-[var(--ds-status-error)]",
2289
- "mt-ds-1.5",
2290
- className
2291
- ),
2292
- children: [
2293
- /* @__PURE__ */ jsx(Icon, { name: "error", size: 14, className: "shrink-0" }),
2294
- /* @__PURE__ */ jsx("span", { children: message })
2295
- ]
2296
- }
2297
- );
2298
- }
2299
- var FieldContext = createContext(null);
2300
- function useField() {
2301
- return useContext(FieldContext);
2302
- }
2303
- function Field({ label, children, hint, error, required, labelAction, id: idProp, className }) {
2304
- const generated = useId();
2305
- const id = idProp ?? `field-${generated}`;
2306
- const hintId = `${id}-hint`;
2307
- const errorId = `${id}-error`;
2308
- const hasError = typeof error === "string" && error.length > 0;
2309
- const describedBy = [hint ? hintId : null, hasError ? errorId : null].filter(Boolean).join(" ") || void 0;
2310
- const labelId = `${id}-label`;
2311
- const fieldContext = useMemo(() => ({ controlId: id, labelId, describedBy }), [id, labelId, describedBy]);
2312
- const controlProps = {
2313
- id,
2314
- "aria-describedby": describedBy,
2315
- ...hasError ? { "aria-invalid": true, error: true } : {},
2316
- ...required ? { "aria-required": true, required: true } : {}
2317
- };
2318
- let control;
2319
- if (typeof children === "function") {
2320
- control = children(controlProps);
2321
- } else {
2322
- const child = Children.only(children);
2323
- if (!isValidElement(child)) throw new Error("Field expects a single form control as its child");
2324
- const isNative = typeof child.type === "string";
2325
- const { error: errorFlag, ...ariaOnly } = controlProps;
2326
- control = cloneElement(child, isNative ? ariaOnly : { ...ariaOnly, ...errorFlag ? { error: true } : {} });
2327
- }
2328
- return (
2329
- // In a settings panel a field keeps the narrow reading width however wide the page is (--ds-form-max, Sprint 25.9 A4)
2330
- /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-ds-1.5 max-w-[var(--ds-form-max,none)]", className), children: [
2331
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-ds-2", children: [
2332
- /* @__PURE__ */ jsxs(Label, { id: labelId, htmlFor: id, children: [
2333
- label,
2334
- required && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "ml-ds-0.5 text-[var(--ds-status-error)]", children: "*" })
2335
- ] }),
2336
- labelAction
2337
- ] }),
2338
- /* @__PURE__ */ jsx(FieldContext.Provider, { value: fieldContext, children: control }),
2339
- hint && /* @__PURE__ */ jsx("p", { id: hintId, className: "text-xs text-[var(--ds-text-muted)]", children: hint }),
2340
- hasError && /* @__PURE__ */ jsx(InlineFieldError, { id: errorId, message: error, className: "mt-0" })
2341
- ] })
2342
- );
2343
- }
2344
2459
  function TruncFileName({ children, className }) {
2345
2460
  const { ref, isTruncated } = useTextOverflow({ lines: 1 });
2346
2461
  const el = /* @__PURE__ */ jsx("p", { ref, className: cn("truncate", className), children });
@@ -2726,9 +2841,11 @@ function TimePicker({
2726
2841
  container,
2727
2842
  error = false,
2728
2843
  id,
2729
- size: sizeProp
2844
+ size: sizeProp,
2845
+ ...wiring
2730
2846
  }) {
2731
2847
  const contextSize = useContext(ControlSizeContext);
2848
+ const control = useFieldControl({ ...wiring, id, error: error || void 0 }, { requiredInDescription: true });
2732
2849
  const size = sizeProp ?? contextSize ?? "md";
2733
2850
  const [open, setOpen] = useState(false);
2734
2851
  const triggerRef = useRef(null);
@@ -2912,15 +3029,18 @@ function TimePicker({
2912
3029
  return segs[k] === null ? spec.placeholder : pad2(segs[k]);
2913
3030
  };
2914
3031
  return /* @__PURE__ */ jsxs("div", { ref, className: cn(className), children: [
3032
+ control.requiredNote,
2915
3033
  /* @__PURE__ */ jsxs(
2916
3034
  "div",
2917
3035
  {
2918
3036
  ref: triggerRef,
2919
3037
  "data-time-field": "",
2920
3038
  role: "group",
2921
- id,
2922
- "aria-label": "Time",
2923
- "aria-invalid": error || void 0,
3039
+ id: control.id,
3040
+ "aria-label": control.labelId ? void 0 : "Time",
3041
+ "aria-labelledby": control.labelId,
3042
+ "aria-describedby": control.describedBy,
3043
+ "aria-invalid": control.invalid || void 0,
2924
3044
  onPaste: (e) => {
2925
3045
  const text = e.clipboardData.getData("text");
2926
3046
  if (text) {
@@ -2933,12 +3053,12 @@ function TimePicker({
2933
3053
  CONTROL_HEIGHT4[size],
2934
3054
  TEXT_SIZE4[size],
2935
3055
  "border-[length:var(--ds-border-width)]",
2936
- error ? "border-[var(--ds-status-error)]" : "border-[var(--ds-input-border)]",
3056
+ control.invalid ? "border-[var(--ds-status-error)]" : "border-[var(--ds-input-border)]",
2937
3057
  "bg-[var(--ds-input-bg)] rounded-[var(--ds-radius-md)]",
2938
3058
  "shadow-[var(--ds-input-shadow)]",
2939
3059
  "transition-colors",
2940
3060
  "px-ds-3 py-ds-2",
2941
- !error && !disabled && !open && "hover:border-[var(--ds-input-border-hover)] focus-within:border-[var(--ds-input-border-hover)]",
3061
+ !control.invalid && !disabled && !open && "hover:border-[var(--ds-input-border-hover)] focus-within:border-[var(--ds-input-border-hover)]",
2942
3062
  open && ACTIVE_RING,
2943
3063
  disabled && "opacity-disabled cursor-not-allowed",
2944
3064
  "text-[var(--ds-text-primary)]"
@@ -3019,9 +3139,10 @@ function TimePicker({
3019
3139
  id: listboxId,
3020
3140
  role: "dialog",
3021
3141
  "aria-label": "Choose a time",
3142
+ tabIndex: -1,
3022
3143
  onKeyDown: handlePanelKeyDown,
3023
3144
  className: cn(
3024
- "fixed z-[var(--ds-z-popover)]",
3145
+ "fixed z-[var(--ds-z-popover)] outline-none",
3025
3146
  "bg-[var(--ds-bg-surface-elevated)]",
3026
3147
  GLASS_DROPDOWN,
3027
3148
  "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
@@ -4189,23 +4310,21 @@ function stampFormId(node, formId) {
4189
4310
  return cloneElement(child, { form: formId });
4190
4311
  });
4191
4312
  }
4192
- var FormCtx = createContext(null);
4193
- function useFormContext() {
4194
- const ctx = useContext(FormCtx);
4195
- if (!ctx) throw new Error("useFormContext must be used within FormWrapper");
4196
- return ctx;
4197
- }
4198
4313
  function collectInvalidFields(form) {
4199
4314
  const out = [];
4200
4315
  for (const el of Array.from(form.querySelectorAll("[aria-invalid='true']"))) {
4201
4316
  const id = el.id || "";
4202
4317
  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";
4203
- const described = (el.getAttribute("aria-describedby") ?? "").split(/\s+/).filter(Boolean);
4204
- 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";
4318
+ const described = (el.getAttribute("aria-describedby") ?? "").split(/\s+/).filter(Boolean).map((d) => document.getElementById(d)).filter((d) => !!d);
4319
+ const message = described.find((d) => d.getAttribute("role") === "alert")?.textContent?.trim() || described.map((d) => d.textContent?.trim()).find((t) => t && t !== "Required" && /error|invalid|required|must|enter|choose|select|please/i.test(t)) || described.map((d) => d.textContent?.trim()).filter(Boolean).pop() || "Needs attention";
4205
4320
  out.push({ id, label, message });
4206
4321
  }
4207
4322
  return out;
4208
4323
  }
4324
+ function focusTarget(el) {
4325
+ if (el.tabIndex >= 0 || /^(?:INPUT|SELECT|TEXTAREA|BUTTON)$/.test(el.tagName)) return el;
4326
+ return el.querySelector("[aria-checked='true'], [aria-selected='true']:not([tabindex='-1'])") ?? el.querySelector("input, select, textarea, button, [tabindex]:not([tabindex='-1'])") ?? el;
4327
+ }
4209
4328
  function FormWrapper({ onSubmit, validationMode: validationModeProp, children, actions: actionsProp, actionsAlign: actionsAlignProp, onCancel, cancelLabel = "Cancel", focusFirstError = true, errorSummary = true, className, ref }) {
4210
4329
  const preferences = useShellPreferences();
4211
4330
  const validationMode = validationModeProp ?? preferences.validationTiming ?? "onSubmit";
@@ -4246,21 +4365,41 @@ function FormWrapper({ onSubmit, validationMode: validationModeProp, children, a
4246
4365
  onSubmitRef.current(errorsRef.current);
4247
4366
  }
4248
4367
  }, [submitted, submitCount, errors, errorsRef, onSubmitRef]);
4368
+ const submittedRef = useLatest(submitted);
4369
+ const errorSummaryRef = useLatest(errorSummary);
4370
+ const summaryRef = useRef(null);
4371
+ const pendingSummaryFocus = useRef(false);
4372
+ const focusSummary = useCallback(() => {
4373
+ const el = summaryRef.current;
4374
+ if (!el) return;
4375
+ if (typeof el.scrollIntoView === "function") el.scrollIntoView({ block: "center", behavior: "smooth" });
4376
+ el.focus({ preventScroll: true });
4377
+ }, []);
4249
4378
  const settle = useCallback(() => {
4250
4379
  const form = formRef.current;
4251
4380
  if (!form) return;
4252
4381
  const invalid = collectInvalidFields(form);
4253
- const next = invalid.length >= 2 ? invalid : [];
4382
+ const next = submittedRef.current && invalid.length >= 2 ? invalid : [];
4254
4383
  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);
4255
4384
  if (pendingFocus.current && invalid.length > 0) {
4256
4385
  pendingFocus.current = false;
4386
+ if (errorSummaryRef.current && next.length >= 2) {
4387
+ if (summaryRef.current) focusSummary();
4388
+ else pendingSummaryFocus.current = true;
4389
+ return;
4390
+ }
4257
4391
  const first = form.querySelector("[aria-invalid='true']");
4258
4392
  if (first) {
4259
4393
  if (typeof first.scrollIntoView === "function") first.scrollIntoView({ block: "center", behavior: "smooth" });
4260
- first.focus({ preventScroll: true });
4394
+ focusTarget(first).focus({ preventScroll: true });
4261
4395
  }
4262
4396
  }
4263
- }, []);
4397
+ }, [submittedRef, errorSummaryRef, focusSummary]);
4398
+ useLayoutEffect(() => {
4399
+ if (!pendingSummaryFocus.current || summary.length < 2) return;
4400
+ pendingSummaryFocus.current = false;
4401
+ focusSummary();
4402
+ }, [summary, focusSummary]);
4264
4403
  useLayoutEffect(() => {
4265
4404
  if (!submitted) return;
4266
4405
  settle();
@@ -4279,7 +4418,21 @@ function FormWrapper({ onSubmit, validationMode: validationModeProp, children, a
4279
4418
  pendingFocus.current = false;
4280
4419
  }, []);
4281
4420
  const setError = useCallback((field, message) => {
4282
- setErrors((prev) => ({ ...prev, [field]: message }));
4421
+ setErrors((prev) => prev[field] === message ? prev : { ...prev, [field]: message });
4422
+ }, []);
4423
+ const checks = useRef(/* @__PURE__ */ new Map());
4424
+ const register = useCallback((field, check) => {
4425
+ checks.current.set(field, check);
4426
+ return () => {
4427
+ if (checks.current.get(field) !== check) return;
4428
+ checks.current.delete(field);
4429
+ setErrors((prev) => {
4430
+ if (!(field in prev)) return prev;
4431
+ const next = { ...prev };
4432
+ delete next[field];
4433
+ return next;
4434
+ });
4435
+ };
4283
4436
  }, []);
4284
4437
  const clearErrors = useCallback(() => {
4285
4438
  setErrors({});
@@ -4289,6 +4442,11 @@ function FormWrapper({ onSubmit, validationMode: validationModeProp, children, a
4289
4442
  const handleSubmit = useCallback(
4290
4443
  (e) => {
4291
4444
  e.preventDefault();
4445
+ if (checks.current.size > 0) {
4446
+ const found = {};
4447
+ for (const [field, check] of checks.current) found[field] = check();
4448
+ setErrors((prev) => ({ ...prev, ...found }));
4449
+ }
4292
4450
  pendingSubmit.current = true;
4293
4451
  pendingFocus.current = focusFirstError;
4294
4452
  setSubmitted(true);
@@ -4301,7 +4459,7 @@ function FormWrapper({ onSubmit, validationMode: validationModeProp, children, a
4301
4459
  const el = id ? document.getElementById(id) : null;
4302
4460
  if (el) {
4303
4461
  el.scrollIntoView?.({ block: "center" });
4304
- el.focus();
4462
+ focusTarget(el).focus();
4305
4463
  }
4306
4464
  };
4307
4465
  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: [
@@ -4310,8 +4468,8 @@ function FormWrapper({ onSubmit, validationMode: validationModeProp, children, a
4310
4468
  f.message
4311
4469
  ] }, f.id || f.label)) }) }) : null;
4312
4470
  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;
4313
- 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 max-w-[var(--ds-form-max,none)]", className), noValidate: true, children: [
4314
- summaryNode && /* @__PURE__ */ jsx("div", { "data-form-error-summary": "", children: summaryNode }),
4471
+ return /* @__PURE__ */ jsx(FormCtx.Provider, { value: { errors, setError, clearErrors, validationMode, submitted, register }, 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 max-w-[var(--ds-form-max,none)]", className), noValidate: true, children: [
4472
+ summaryNode && /* @__PURE__ */ jsx("div", { ref: summaryRef, "data-form-error-summary": "", tabIndex: -1, className: "rounded-[var(--ds-radius-md)] outline-none focus-visible:shadow-[var(--ds-shadow-focus)]", children: summaryNode }),
4315
4473
  children,
4316
4474
  actionsNode
4317
4475
  ] }) });
@@ -4909,11 +5067,36 @@ function MentionInput({
4909
5067
  }
4910
5068
  var HEADER_LAYOUT = {
4911
5069
  row: "flex flex-wrap items-start justify-between gap-ds-4",
4912
- block: "flex flex-col space-y-ds-1.5 grow basis-1/2 [min-width:min-content] break-words",
4913
- titleLeads: "grow basis-1/2 max-w-max [min-width:min-content] break-words",
5070
+ block: "flex flex-col space-y-ds-1.5 grow basis-1/2 break-words max-w-full has-[[data-title-one-line]]:min-w-0",
5071
+ titleLeads: "grow basis-1/2 max-w-max break-words has-[[data-title-one-line]]:min-w-0",
5072
+ floorOff: "min-w-0",
4914
5073
  actions: "flex items-center gap-ds-2 shrink-0 ml-auto",
4915
5074
  below: "mt-ds-1.5"
4916
5075
  };
5076
+ function useTitleFloorFits(rowRef, active) {
5077
+ const [fits, setFits] = useState(true);
5078
+ useLayoutEffect(() => {
5079
+ const row = rowRef.current;
5080
+ if (!active || !row) return;
5081
+ const measure = () => {
5082
+ const title = row.querySelector("h1, h2, h3, h4, h5, h6");
5083
+ if (!title) return;
5084
+ const width = title.style.width;
5085
+ title.style.width = "min-content";
5086
+ const longest = title.getBoundingClientRect().width;
5087
+ title.style.width = width;
5088
+ if (longest === 0) return;
5089
+ const next = longest <= row.clientWidth + 0.5;
5090
+ setFits((was) => was === next ? was : next);
5091
+ };
5092
+ measure();
5093
+ if (typeof ResizeObserver === "undefined") return;
5094
+ const observer = new ResizeObserver(measure);
5095
+ observer.observe(row);
5096
+ return () => observer.disconnect();
5097
+ }, [rowRef, active]);
5098
+ return fits;
5099
+ }
4917
5100
  function actionsAreBadges(actions) {
4918
5101
  const items = Children.toArray(actions);
4919
5102
  return items.length > 0 && items.every((item) => isValidElement(item) && (item.type === Fragment$1 ? actionsAreBadges(item.props.children) : item.type === Badge));
@@ -5038,7 +5221,6 @@ var Card = forwardRef(function Card2({
5038
5221
  ...props
5039
5222
  }, ref) {
5040
5223
  const isSelectable = selectable || !!selectType;
5041
- const isToggle = selectType === "toggle";
5042
5224
  const hasSelectType = !!selectType;
5043
5225
  const isAction = !isSelectable && variant === "interactive" && !!onClick;
5044
5226
  const rootRef = useRef(null);
@@ -5092,7 +5274,6 @@ var Card = forwardRef(function Card2({
5092
5274
  const isButton = isSelectable || isAction && !holdsControls;
5093
5275
  const role = selectType === "radio" ? "radio" : selectType === "checkbox" ? "checkbox" : selectType === "toggle" ? "switch" : isButton ? "button" : void 0;
5094
5276
  const ariaAttr = selectType === "radio" || selectType === "checkbox" || selectType === "toggle" ? { "aria-checked": !!selected } : isSelectable ? { "aria-pressed": !!selected } : {};
5095
- const showRing = isSelectable && !isToggle && !hasSelectType && selected;
5096
5277
  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;
5097
5278
  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;
5098
5279
  return /* @__PURE__ */ jsxs(
@@ -5111,7 +5292,10 @@ var Card = forwardRef(function Card2({
5111
5292
  isSelectable && !disabled && "cursor-pointer select-none transition-all duration-[var(--ds-transition-speed)] ease-[var(--ds-transition-ease)]",
5112
5293
  isSelectable && !disabled && !selected && "hover:border-[var(--ds-accent-primary)]",
5113
5294
  isButton && "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
5114
- (showRing || hasSelectType && selected) && ACTIVE_RING,
5295
+ // Selected is the accent border, with the accent-subtle fill on a card with a selectType. No ring: a ring round the
5296
+ // whole card is the focus state (focus-visible:shadow-[var(--ds-shadow-focus)] above), and the active halo other
5297
+ // controls take (ACTIVE_RING, since Sprint 20.6) made a selected card look focused (the owner, 27 September 2026).
5298
+ isSelectable && selected && "border-[var(--ds-border-focus)]",
5115
5299
  hasSelectType && selected && "bg-[var(--ds-accent-subtle)]",
5116
5300
  disabled && "opacity-disabled cursor-not-allowed",
5117
5301
  className
@@ -5184,14 +5368,16 @@ var CardHeader = forwardRef(function CardHeader2({ className, children, actions,
5184
5368
  )
5185
5369
  ] }) : null;
5186
5370
  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;
5371
+ const parts = Children.toArray(children);
5372
+ const titleLeads = !!hasActions && !iconBlock && !menuItems && actionsAreBadges(actions) && parts.length > 0 && isValidElement(parts[0]) && parts[0].type === CardTitle;
5373
+ const leadRowRef = useRef(null);
5374
+ const leadFloorFits = useTitleFloorFits(leadRowRef, titleLeads);
5187
5375
  if (hasActions) {
5188
- const parts = Children.toArray(children);
5189
- const titleLeads = !iconBlock && !menuItems && actionsAreBadges(actions) && parts.length > 0 && isValidElement(parts[0]) && parts[0].type === CardTitle;
5190
5376
  if (titleLeads) {
5191
5377
  const below = parts.slice(1);
5192
5378
  return /* @__PURE__ */ jsxs("div", { ref, className: cn(pl, "pr-ds-5 pt-ds-4 last:pb-ds-4", className), ...props, children: [
5193
- /* @__PURE__ */ jsxs("div", { className: HEADER_LAYOUT.row, children: [
5194
- /* @__PURE__ */ jsx("div", { "data-card-title-row": "", className: HEADER_LAYOUT.titleLeads, children: parts[0] }),
5379
+ /* @__PURE__ */ jsxs("div", { ref: leadRowRef, className: HEADER_LAYOUT.row, children: [
5380
+ /* @__PURE__ */ jsx("div", { "data-card-title-row": "", className: cn(HEADER_LAYOUT.titleLeads, !leadFloorFits && HEADER_LAYOUT.floorOff), children: parts[0] }),
5195
5381
  actionsBlock
5196
5382
  ] }),
5197
5383
  below.length > 0 && /* @__PURE__ */ jsx("div", { className: cn("flex flex-col space-y-ds-1.5 break-words", HEADER_LAYOUT.below), children: below })
@@ -5233,7 +5419,8 @@ var CardTitle = forwardRef(function CardTitle2({ className, children, lines = 2,
5233
5419
  },
5234
5420
  id: id ?? generatedId,
5235
5421
  "data-card-title": "",
5236
- className: cn("font-[family-name:var(--ds-font-display)] text-lg font-[var(--ds-weight-heading)] text-[var(--ds-text-primary)]", !action && lines > 0 && lineClampClass, className),
5422
+ ...lines === 1 ? { "data-title-one-line": "" } : {},
5423
+ className: cn(TITLE_TEXT, !action && lines > 0 && lineClampClass, action && lines === 1 && "text-ellipsis", className),
5237
5424
  ...props,
5238
5425
  children: action ? (
5239
5426
  // The card's action when the card holds controls of its own (Sprint 25.7): a real button, which a click
@@ -5401,6 +5588,58 @@ function DataList({ items, columns = 1, className }) {
5401
5588
  }
5402
5589
  );
5403
5590
  }
5591
+ var COUNT_FROM_PAGE = [
5592
+ "[&:has(>:nth-child(2):last-child)]:[--gs-cards:2]",
5593
+ "[&:has(>:nth-child(3):last-child)]:[--gs-cards:3]",
5594
+ "[&:has(>:nth-child(4):last-child)]:[--gs-cards:4]",
5595
+ "[&:has(>:nth-child(5):last-child)]:[--gs-cards:5]",
5596
+ "[&:has(>:nth-child(6):last-child)]:[--gs-cards:6]",
5597
+ "[&:has(>:nth-child(7):last-child)]:[--gs-cards:7]",
5598
+ "[&:has(>:nth-child(8):last-child)]:[--gs-cards:8]",
5599
+ "[&:has(>:nth-child(9):last-child)]:[--gs-cards:9]",
5600
+ "[&:has(>:nth-child(10):last-child)]:[--gs-cards:10]",
5601
+ "[&:has(>:nth-child(11):last-child)]:[--gs-cards:11]",
5602
+ "[&:has(>:nth-child(12))]:[--gs-cards:12]"
5603
+ ].join(" ");
5604
+ function countCards(children) {
5605
+ let n = 0;
5606
+ Children.forEach(children, (child) => {
5607
+ if (isValidElement(child) && child.type === Fragment$1) n += countCards(child.props.children);
5608
+ else if (child !== null && child !== void 0 && typeof child !== "boolean" && child !== "") n += 1;
5609
+ });
5610
+ return n;
5611
+ }
5612
+ function CardGrid({ minWidth = "14rem", maxColumns = 4, compare = false, gap = "var(--ds-space-4)", className, style, children, ref, ...props }) {
5613
+ const most = Math.max(1, Math.floor(maxColumns));
5614
+ const n = compare ? "var(--gs-cards, var(--gs-cards-counted))" : String(most);
5615
+ const share = `calc((100% - (${n} - 1) * var(--gs-card-gap)) / ${n})`;
5616
+ const column = compare ? `max(${share}, min(100%, calc((${n} * ${minWidth} + (${n} - 1) * var(--gs-card-gap) - 100%) * 999)))` : `max(min(${minWidth}, 100%), ${share})`;
5617
+ const template = {
5618
+ "--gs-card-gap": gap,
5619
+ ...compare ? { "--gs-cards-counted": Math.max(1, countCards(children)) } : {},
5620
+ gap: "var(--gs-card-gap)",
5621
+ gridTemplateColumns: `repeat(auto-fill, minmax(${column}, 1fr))`,
5622
+ // a comparison's cards share their rows: the headers, the contents and the footers line up across the row
5623
+ ...compare ? { gridAutoRows: "auto 1fr auto" } : {}
5624
+ };
5625
+ return /* @__PURE__ */ jsx(
5626
+ "div",
5627
+ {
5628
+ ref,
5629
+ "data-card-grid": compare ? "compare" : "",
5630
+ className: cn(
5631
+ "grid",
5632
+ compare && COUNT_FROM_PAGE,
5633
+ // each card spans its three rows and lays its parts in them; anything else just fills them
5634
+ compare && "[&>*]:row-span-3 [&>[data-card]]:grid [&>[data-card]]:grid-rows-subgrid [&>[data-card]]:gap-y-0",
5635
+ className
5636
+ ),
5637
+ style: { ...template, ...style },
5638
+ ...props,
5639
+ children
5640
+ }
5641
+ );
5642
+ }
5404
5643
  var sizeStyles3 = {
5405
5644
  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" },
5406
5645
  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" },
@@ -5530,7 +5769,7 @@ var DescriptionList = forwardRef(function DescriptionList2({
5530
5769
  ]
5531
5770
  }
5532
5771
  ),
5533
- /* @__PURE__ */ jsx("dd", { className: cn("font-normal text-[var(--ds-text-primary)] text-right flex-1 min-w-0", s.value), children: item.value })
5772
+ /* @__PURE__ */ jsx("dd", { className: cn("font-normal text-[var(--ds-text-primary)] text-right flex-1 min-w-0 [overflow-wrap:anywhere] [:where(&)>*]:[justify-content:safe_flex-end] [:where(&)>*]:flex-wrap", s.value), children: item.value })
5534
5773
  ]
5535
5774
  },
5536
5775
  i
@@ -5692,7 +5931,7 @@ function TruncCell({ children, lines }) {
5692
5931
  if (!isTruncated) return cell;
5693
5932
  return /* @__PURE__ */ jsx(Tooltip, { content: children, children: cell });
5694
5933
  }
5695
- var TITLE_CLASSES = "font-[family-name:var(--ds-font-display)] text-lg font-[var(--ds-weight-heading)] text-[var(--ds-text-primary)] break-words";
5934
+ var TITLE_CLASSES = TITLE_TEXT;
5696
5935
  var FRAME_CLASSES = cn(
5697
5936
  "border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
5698
5937
  "rounded-[var(--ds-radius-md)]",
@@ -5702,16 +5941,19 @@ var FRAME_CLASSES = cn(
5702
5941
  function TableHeading({ title, description, actions, titleId, descriptionId, inset }) {
5703
5942
  const heading = /* @__PURE__ */ jsx("h3", { id: titleId, className: TITLE_CLASSES, children: title });
5704
5943
  const actionsBlock = actions ? /* @__PURE__ */ jsx("div", { className: HEADER_LAYOUT.actions, children: actions }) : null;
5944
+ const titleLeads = !actions || actionsAreBadges(actions);
5945
+ const leadRowRef = useRef(null);
5946
+ const leadFloorFits = useTitleFloorFits(leadRowRef, titleLeads);
5705
5947
  const descriptionText = (className) => description ? /* @__PURE__ */ jsx("p", { id: descriptionId, className: cn(className, "text-sm text-[var(--ds-text-muted)] break-words"), children: description }) : null;
5706
- return /* @__PURE__ */ jsx("div", { "data-table-heading": "", className: cn(inset ? "px-ds-5 py-ds-4" : "pb-ds-4"), children: actions && !actionsAreBadges(actions) ? /* @__PURE__ */ jsxs("div", { className: HEADER_LAYOUT.row, children: [
5948
+ return /* @__PURE__ */ jsx("div", { "data-table-heading": "", className: cn(inset ? "px-ds-5 py-ds-4" : "pb-ds-4"), children: !titleLeads ? /* @__PURE__ */ jsxs("div", { className: HEADER_LAYOUT.row, children: [
5707
5949
  /* @__PURE__ */ jsxs("div", { className: HEADER_LAYOUT.block, children: [
5708
5950
  heading,
5709
5951
  descriptionText()
5710
5952
  ] }),
5711
5953
  actionsBlock
5712
5954
  ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
5713
- /* @__PURE__ */ jsxs("div", { className: HEADER_LAYOUT.row, children: [
5714
- /* @__PURE__ */ jsx("div", { className: HEADER_LAYOUT.titleLeads, children: heading }),
5955
+ /* @__PURE__ */ jsxs("div", { ref: leadRowRef, className: HEADER_LAYOUT.row, children: [
5956
+ /* @__PURE__ */ jsx("div", { className: cn(HEADER_LAYOUT.titleLeads, !leadFloorFits && HEADER_LAYOUT.floorOff), children: heading }),
5715
5957
  actionsBlock
5716
5958
  ] }),
5717
5959
  descriptionText(HEADER_LAYOUT.below)
@@ -6078,9 +6320,12 @@ function SegmentedControl({
6078
6320
  onHoverChange,
6079
6321
  className,
6080
6322
  "aria-label": ariaLabel,
6081
- maxOptions = 6
6323
+ maxOptions = 6,
6324
+ ...wiring
6082
6325
  }) {
6083
6326
  const contextSize = useContext(ControlSizeContext);
6327
+ const control = useFieldControl(wiring);
6328
+ const named = !ariaLabel && control.labelId ? { "aria-labelledby": control.labelId } : {};
6084
6329
  const size = sizeProp ?? contextSize ?? "md";
6085
6330
  const containerRef = useRef(null);
6086
6331
  const [indicator, setIndicator] = useState({ left: 0, width: 0 });
@@ -6176,6 +6421,7 @@ function SegmentedControl({
6176
6421
  Dropdown,
6177
6422
  {
6178
6423
  "aria-label": ariaLabel,
6424
+ ...wiring,
6179
6425
  size: size === "md" || size === "lg" ? "md" : "sm",
6180
6426
  options: options.filter((o) => !o.disabled).map((o) => ({ id: o.value, label: o.label })),
6181
6427
  value,
@@ -6189,7 +6435,12 @@ function SegmentedControl({
6189
6435
  {
6190
6436
  ref: containerRef,
6191
6437
  role: "radiogroup",
6438
+ id: control.id,
6192
6439
  "aria-label": ariaLabel,
6440
+ ...named,
6441
+ "aria-describedby": control.describedBy,
6442
+ "aria-invalid": control.invalid || void 0,
6443
+ "aria-required": control.required || void 0,
6193
6444
  onKeyDown: handleKeyDown,
6194
6445
  className: cn(
6195
6446
  "relative items-center",
@@ -6199,6 +6450,7 @@ function SegmentedControl({
6199
6450
  "bg-[var(--ds-bg-muted)]",
6200
6451
  "rounded-[var(--ds-radius-lg)]",
6201
6452
  variant === "default" && "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
6453
+ control.invalid && "border-[length:var(--ds-border-width)] border-[var(--ds-status-error)]",
6202
6454
  className
6203
6455
  ),
6204
6456
  children: [
@@ -6709,7 +6961,7 @@ function WeekView({
6709
6961
  return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "flex flex-col h-full overflow-hidden", children: [
6710
6962
  /* @__PURE__ */ jsxs("div", { className: "flex border-b-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)]", children: [
6711
6963
  /* @__PURE__ */ jsx("div", { className: "w-14 shrink-0 border-r-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)]" }),
6712
- /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-x-auto scrollbar-none", "data-week-scroll": true, children: /* @__PURE__ */ jsx(
6964
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-x-auto gs-scrollbar-none", "data-week-scroll": true, children: /* @__PURE__ */ jsx(
6713
6965
  "div",
6714
6966
  {
6715
6967
  className: "grid",
@@ -6749,7 +7001,7 @@ function WeekView({
6749
7001
  ] }),
6750
7002
  hasAllDay && /* @__PURE__ */ jsxs("div", { className: "flex border-b-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)] bg-[var(--ds-bg-muted)]/30", children: [
6751
7003
  /* @__PURE__ */ jsx("div", { className: "w-14 shrink-0 border-r-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)] text-2xs text-[var(--ds-text-muted)] px-ds-1 py-ds-1 text-right", children: "all-day" }),
6752
- /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-x-auto scrollbar-none", "data-week-scroll": true, children: /* @__PURE__ */ jsx(
7004
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-x-auto gs-scrollbar-none", "data-week-scroll": true, children: /* @__PURE__ */ jsx(
6753
7005
  "div",
6754
7006
  {
6755
7007
  className: "grid",
@@ -6786,7 +7038,7 @@ function WeekView({
6786
7038
  },
6787
7039
  h
6788
7040
  )) }),
6789
- /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-x-auto scrollbar-none", "data-week-scroll": true, children: /* @__PURE__ */ jsx(
7041
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-x-auto gs-scrollbar-none", "data-week-scroll": true, children: /* @__PURE__ */ jsx(
6790
7042
  "div",
6791
7043
  {
6792
7044
  className: "relative",
@@ -7799,7 +8051,7 @@ function AvatarGroup({ items, max = 4, size = "sm", ring = "canvas", onOverflowC
7799
8051
  const shown = items.slice(0, Math.max(0, max));
7800
8052
  const hidden = items.slice(shown.length);
7801
8053
  const ringClass = cn(
7802
- "rounded-[var(--ds-radius-full)] ring-2",
8054
+ "relative rounded-[var(--ds-radius-full)] ring-2",
7803
8055
  ring === "canvas" ? "ring-[var(--ds-bg-canvas)]" : "ring-[var(--ds-bg-surface)]"
7804
8056
  );
7805
8057
  const hiddenNames = hidden.map((i) => i.name ?? i.initials).filter(Boolean).join(", ");
@@ -8099,11 +8351,38 @@ function HorizontalLayout({ items, minItemWidth, className }) {
8099
8351
  /* @__PURE__ */ jsxs("div", { className: "text-center mt-ds-2 px-ds-3", children: [
8100
8352
  /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-[var(--ds-text-primary)]", children: item.title }),
8101
8353
  item.description && /* @__PURE__ */ jsx(ExpandableDescription, { className: "text-center", children: item.description }),
8102
- item.timestamp && /* @__PURE__ */ jsx("p", { className: "text-2xs text-[var(--ds-text-muted)] mt-ds-0.5 tabular-nums", children: item.timestamp })
8354
+ /* @__PURE__ */ jsx(ItemTime, { item })
8103
8355
  ] })
8104
8356
  ] }, i);
8105
8357
  }) });
8106
8358
  }
8359
+ var EXACT = new Intl.DateTimeFormat(void 0, { dateStyle: "medium", timeStyle: "short" });
8360
+ var EXACT_DAY = new Intl.DateTimeFormat(void 0, { dateStyle: "medium" });
8361
+ var DATE_ONLY = /^(\d{4})-(\d{2})-(\d{2})$/;
8362
+ function exactTime(value) {
8363
+ if (typeof value === "string") {
8364
+ const day = DATE_ONLY.exec(value.trim());
8365
+ if (day) {
8366
+ const [y, m, d] = [Number(day[1]), Number(day[2]) - 1, Number(day[3])];
8367
+ const date2 = new Date(y, m, d);
8368
+ return date2.getFullYear() === y && date2.getMonth() === m && date2.getDate() === d ? { iso: value.trim(), text: EXACT_DAY.format(date2) } : null;
8369
+ }
8370
+ }
8371
+ const date = value instanceof Date ? value : new Date(value);
8372
+ return Number.isNaN(date.getTime()) ? null : { iso: date.toISOString(), text: EXACT.format(date) };
8373
+ }
8374
+ function ItemTime({ item }) {
8375
+ const exact = item.dateTime ? exactTime(item.dateTime) : null;
8376
+ if (!item.timestamp && !exact) return null;
8377
+ return /* @__PURE__ */ jsx("p", { className: "text-2xs text-[var(--ds-text-muted)] mt-ds-0.5 tabular-nums", children: exact ? /* @__PURE__ */ jsxs("time", { dateTime: exact.iso, title: exact.text, children: [
8378
+ item.timestamp ?? exact.text,
8379
+ item.timestamp && /* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
8380
+ " (",
8381
+ exact.text,
8382
+ ")"
8383
+ ] })
8384
+ ] }) : item.timestamp });
8385
+ }
8107
8386
  function VerticalLayout({ items, className }) {
8108
8387
  return /* @__PURE__ */ jsx("div", { className: cn("relative", className), children: items.map((item, i) => /* @__PURE__ */ jsxs("div", { className: "flex gap-ds-4", children: [
8109
8388
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center", children: [
@@ -8113,7 +8392,7 @@ function VerticalLayout({ items, className }) {
8113
8392
  /* @__PURE__ */ jsxs("div", { className: cn("pb-ds-6 min-w-0", i === items.length - 1 && "pb-0"), style: item.icon ? { paddingTop: ICON_TEXT_OFFSET } : void 0, children: [
8114
8393
  /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-[var(--ds-text-primary)]", children: item.title }),
8115
8394
  item.description && /* @__PURE__ */ jsx(ExpandableDescription, { children: item.description }),
8116
- item.timestamp && /* @__PURE__ */ jsx("p", { className: "text-2xs text-[var(--ds-text-muted)] mt-ds-0.5 tabular-nums", children: item.timestamp })
8395
+ /* @__PURE__ */ jsx(ItemTime, { item })
8117
8396
  ] })
8118
8397
  ] }, i)) });
8119
8398
  }
@@ -9496,7 +9775,7 @@ var StackedList = forwardRef(function StackedList2({
9496
9775
  });
9497
9776
  var TIME_ZOOMS = ["day", "week", "month", "quarter"];
9498
9777
  var DAY_MS = 864e5;
9499
- var DATE_ONLY = /^(\d{4})-(\d{2})-(\d{2})$/;
9778
+ var DATE_ONLY2 = /^(\d{4})-(\d{2})-(\d{2})$/;
9500
9779
  var utcDay = (y, m, d) => Math.round(Date.UTC(y, m, d) / DAY_MS);
9501
9780
  function dayOfDate(d) {
9502
9781
  const t = d.getTime();
@@ -9512,7 +9791,7 @@ function dayOf(value) {
9512
9791
  if (typeof value === "number") return Number.isFinite(value) ? value : NaN;
9513
9792
  if (value instanceof Date) return dayOfDate(value);
9514
9793
  if (typeof value !== "string") return NaN;
9515
- const m = DATE_ONLY.exec(value.trim());
9794
+ const m = DATE_ONLY2.exec(value.trim());
9516
9795
  if (!m) return dayOfDate(new Date(value));
9517
9796
  const [y, mo, d] = [Number(m[1]), Number(m[2]) - 1, Number(m[3])];
9518
9797
  const day = utcDay(y, mo, d);
@@ -9525,7 +9804,7 @@ function dateOfDay(day) {
9525
9804
  return new Date(u.getUTCFullYear(), u.getUTCMonth(), u.getUTCDate(), 0, 0, 0, Math.round((day - whole) * DAY_MS));
9526
9805
  }
9527
9806
  function isWholeDay(value) {
9528
- return typeof value === "string" ? DATE_ONLY.test(value.trim()) : Number.isInteger(dayOf(value));
9807
+ return typeof value === "string" ? DATE_ONLY2.test(value.trim()) : Number.isInteger(dayOf(value));
9529
9808
  }
9530
9809
  function endDayOf(value) {
9531
9810
  const day = dayOf(value);
@@ -15013,7 +15292,8 @@ function Dialog({ open, onClose: onCloseProp, title, description, children, foot
15013
15292
  className: cn(
15014
15293
  "relative z-10 w-full",
15015
15294
  isMobile ? "max-w-none h-full" : sizeClasses[size],
15016
- "bg-[var(--ds-bg-surface)] text-[var(--ds-text-primary)]",
15295
+ OVERLAY_SURFACE,
15296
+ "text-[var(--ds-text-primary)]",
15017
15297
  GLASS_OVERLAY,
15018
15298
  "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
15019
15299
  isMobile ? "rounded-none" : "rounded-[var(--ds-radius-xl)]",
@@ -15532,7 +15812,8 @@ function CommandPalette({ open, onOpenChange, commands, placeholder = "Type a co
15532
15812
  "aria-label": "Command palette",
15533
15813
  className: cn(
15534
15814
  "relative z-10 w-full max-w-lg",
15535
- "bg-[var(--ds-bg-surface)] text-[var(--ds-text-primary)]",
15815
+ OVERLAY_SURFACE,
15816
+ "text-[var(--ds-text-primary)]",
15536
15817
  "[backdrop-filter:var(--ds-glass-filter)] [-webkit-backdrop-filter:var(--ds-glass-filter)]",
15537
15818
  "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
15538
15819
  "rounded-[var(--ds-radius-xl)] overflow-hidden",
@@ -16806,6 +17087,6 @@ var TextOverflow = forwardRef(function TextOverflow2({
16806
17087
  return /* @__PURE__ */ jsx(Tooltip, { content: resolvedTooltipContent, children: content });
16807
17088
  });
16808
17089
 
16809
- export { Accordion, ActionPanel, ActionPanelGroup, Alert, AsidePanel, Avatar, AvatarGroup, BottomNav, Breadcrumbs, BulkActionBar, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, ChatMessageList, ChatPanel, Checkbox, CheckboxGroup, CheckboxGroupItem, Collapsible, ColorPicker, Combobox, CommandPalette, ContextMenu, CornerPanel, DEFAULT_DATE_RANGE_PRESETS, DataList, DatePicker, DateRangePicker, DescriptionList, Dialog, Dock, Dropdown, DropdownMenu, EmptyState, EntityCell, Field, FileUpload, FormWrapper, Gantt, HoverCard, InlineFieldError, Input, Kbd, Label, Launcher, MentionInput, MobileHeaderMenu, NavItem, NavMenu, Navbar, NotificationCenter, NumberInput, PageContainer, PageHeader, Pagination, PinInput, Popover, Progress, RadioGroup, Rating, ScrollArea, SectionHeading, SegmentedControl, SettingsLayout, Sidebar, SidebarNav, SliderRange, StackedList, StatsCard, StatusDot, Stepper, TIME_ZOOMS, Table, TableToolbar, Tabs, Tag, TagInput, TextOverflow, Textarea, TimeBar, TimePicker, TimeRow, TimeScaleHeader, TimeViewport, Timeline, Toast, ToastProvider, Toggle, Tour, TransferList, TreeView, dateOfDay, dayOf, endDayOf, fitLabel, fitLabels, formatDateRange, timeExtent, useContentWidth, useDestructiveAction, useFittedLabels, useFormContext, usePagination, useSidebar, useTextMeasure, useTimeScale, useTimeViewport, useTimeViewportLayout, useToast, useToastOptional };
17090
+ export { Accordion, ActionPanel, ActionPanelGroup, Alert, AsidePanel, Avatar, AvatarGroup, BottomNav, Breadcrumbs, BulkActionBar, Calendar, Card, CardContent, CardDescription, CardFooter, CardGrid, CardHeader, CardTitle, Carousel, ChatMessageList, ChatPanel, Checkbox, CheckboxGroup, CheckboxGroupItem, Collapsible, ColorPicker, Combobox, CommandPalette, ContextMenu, CornerPanel, DEFAULT_DATE_RANGE_PRESETS, DataList, DatePicker, DateRangePicker, DescriptionList, Dialog, Dock, Dropdown, DropdownMenu, EmptyState, EntityCell, Field, FileUpload, FormWrapper, Gantt, HoverCard, InlineFieldError, Input, Kbd, Label, Launcher, MentionInput, MobileHeaderMenu, NavItem, NavMenu, Navbar, NotificationCenter, NumberInput, PageContainer, PageHeader, Pagination, PinInput, Popover, Progress, RadioGroup, Rating, ScrollArea, SectionHeading, SegmentedControl, SettingsLayout, Sidebar, SidebarNav, SliderRange, StackedList, StatsCard, StatusDot, Stepper, TIME_ZOOMS, Table, TableToolbar, Tabs, Tag, TagInput, TextOverflow, Textarea, TimeBar, TimePicker, TimeRow, TimeScaleHeader, TimeViewport, Timeline, Toast, ToastProvider, Toggle, Tour, TransferList, TreeView, dateOfDay, dayOf, endDayOf, fitLabel, fitLabels, formatDateRange, timeExtent, useContentWidth, useDestructiveAction, useFittedLabels, useFormContext, usePagination, useSidebar, useTextMeasure, useTimeScale, useTimeViewport, useTimeViewportLayout, useToast, useToastOptional };
16810
17091
  //# sourceMappingURL=index.js.map
16811
17092
  //# sourceMappingURL=index.js.map