gridsmith-ui 0.18.0 → 0.20.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 (39) 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-6MXHGH3D.js} +54 -15
  5. package/dist/chunk-6MXHGH3D.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-XVB55M3N.js +42 -0
  17. package/dist/chunk-XVB55M3N.js.map +1 -0
  18. package/dist/{chunk-PVTGMCVR.js → chunk-XYDIBW3J.js} +4 -4
  19. package/dist/{chunk-PVTGMCVR.js.map → chunk-XYDIBW3J.js.map} +1 -1
  20. package/dist/data-grid.js +3 -3
  21. package/dist/editor.d.ts +38 -2
  22. package/dist/editor.js +86 -10
  23. package/dist/editor.js.map +1 -1
  24. package/dist/index.d.ts +332 -92
  25. package/dist/index.js +1404 -542
  26. package/dist/index.js.map +1 -1
  27. package/dist/kanban.js +3 -3
  28. package/dist/resizable.d.ts +8 -1
  29. package/dist/resizable.js +35 -15
  30. package/dist/resizable.js.map +1 -1
  31. package/dist/roots.css +164 -2
  32. package/dist/tailwind.css +14 -0
  33. package/dist/utils.js +1 -1
  34. package/dist/virtualized.js +1 -1
  35. package/dist-static/tailwind.css +14 -0
  36. package/package.json +1 -1
  37. package/dist/chunk-4KVFH5KC.js.map +0 -1
  38. package/dist/chunk-CIVSNXFG.js.map +0 -1
  39. package/dist/chunk-Z63OSN64.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,24 +1,26 @@
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, useOverlayMark, useDirtyGuard, OverlayMarkCtx, OverlayCloseCtx, DiscardConfirm, Spinner, DrawerFooterCtx, useCompact, useElementWidth, Sheet, useCloseSettle, Drawer, useRememberedWidth, pxOf, ResizeEdge, defaultMaxWidth } from './chunk-6MXHGH3D.js';
2
+ export { AppShell, Button, ButtonSizeContext, ControlSizeContext, Drawer, Sheet, SkipLink, Spinner, useAppShell, useUnsavedChanges } from './chunk-6MXHGH3D.js';
3
+ import { Skeleton, TITLE_TEXT } from './chunk-NCIZQYXQ.js';
4
+ export { Skeleton } from './chunk-NCIZQYXQ.js';
5
+ import { useReportedChange, useOptionalFormContext, FormChangeCtx, FormCtx } from './chunk-XVB55M3N.js';
6
+ export { useFormContext } from './chunk-XVB55M3N.js';
5
7
  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
8
  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';
9
+ import { Link } from './chunk-EQDR57AM.js';
10
+ export { CodeBlock, Divider, Link } from './chunk-EQDR57AM.js';
11
+ import { Badge } from './chunk-XYDIBW3J.js';
12
+ export { Badge } from './chunk-XYDIBW3J.js';
13
+ import { inferColumnKinds, CardValue } from './chunk-JN37OVTP.js';
12
14
  import { SplitPaneCtx, useShellPreferences, TableCardCtx, PREFERENCE_HOUSE_DEFAULTS, preferenceActionKey } from './chunk-TKVTZWBN.js';
13
15
  export { PreferencesContext, preferenceActionKey, usePreferences } from './chunk-TKVTZWBN.js';
14
16
  import { useBreakpoint, readBreakpointPx } from './chunk-CRQ6KCRD.js';
15
17
  export { BreakpointWidthProvider, useBreakpoint } from './chunk-CRQ6KCRD.js';
16
- import { useTextOverflow, Tooltip, usePosition } from './chunk-EMCSARQO.js';
17
- export { Tooltip, useTextOverflow } from './chunk-EMCSARQO.js';
18
+ import { useTextOverflow, Tooltip, usePosition } from './chunk-ILIODA7W.js';
19
+ export { Tooltip, useTextOverflow } from './chunk-ILIODA7W.js';
18
20
  import { mergeRefs, Slot } from './chunk-7Q46JXSK.js';
19
21
  import { useLatest } from './chunk-WH2FUFAC.js';
20
22
  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';
23
+ 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
24
  import { createContext, forwardRef, useState, useRef, useCallback, useLayoutEffect, useMemo, useContext, Children, isValidElement, useId, memo, useEffect, useImperativeHandle, Fragment as Fragment$1, cloneElement, useSyncExternalStore } from 'react';
23
25
  import { createPortal, flushSync } from 'react-dom';
24
26
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
@@ -115,6 +117,7 @@ function DropdownMenu({ trigger, items, align = "end", className, container }) {
115
117
  return /* @__PURE__ */ jsxs(
116
118
  "button",
117
119
  {
120
+ type: "button",
118
121
  "data-index": i,
119
122
  role: "menuitem",
120
123
  disabled: item.disabled,
@@ -184,7 +187,7 @@ function BulkActionBar({ selectedCount, actions, onClearSelection, className })
184
187
  ) : /* @__PURE__ */ jsx("div", { className: "flex items-center gap-ds-2", children: actions.map((action, i) => /* @__PURE__ */ jsx(
185
188
  Button,
186
189
  {
187
- variant: action.variant === "destructive" ? "destructive" : "secondary",
190
+ variant: "secondary",
188
191
  size: "sm",
189
192
  onClick: action.onClick,
190
193
  children: action.label
@@ -194,6 +197,7 @@ function BulkActionBar({ selectedCount, actions, onClearSelection, className })
194
197
  /* @__PURE__ */ jsx(
195
198
  "button",
196
199
  {
200
+ type: "button",
197
201
  onClick: onClearSelection,
198
202
  className: "ml-auto text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer",
199
203
  "aria-label": "Clear selection",
@@ -204,6 +208,11 @@ function BulkActionBar({ selectedCount, actions, onClearSelection, className })
204
208
  }
205
209
  );
206
210
  }
211
+ var valueOf = (e) => {
212
+ const t = e.target;
213
+ if (!t) return void 0;
214
+ return t instanceof HTMLInputElement && (t.type === "checkbox" || t.type === "radio") ? t.checked : t.value;
215
+ };
207
216
  var INPUT_HEIGHT = {
208
217
  sm: "h-[var(--ds-control-height-sm)]",
209
218
  md: "h-[var(--ds-control-height-md)]",
@@ -217,6 +226,7 @@ var INPUT_TEXT = {
217
226
  function Input({ className, size: sizeProp, error = false, prefix, suffix, ...props }) {
218
227
  const contextSize = useContext(ControlSizeContext);
219
228
  const size = sizeProp ?? contextSize ?? "md";
229
+ const onChange = useReportedChange(props.onChange, props.value, valueOf);
220
230
  const borderColor = error ? "border-[var(--ds-status-error)]" : "border-[var(--ds-input-border)]";
221
231
  const hoverBorder = !error && !props.disabled && !props.readOnly ? "hover:border-[var(--ds-input-border-hover)]" : "";
222
232
  if (prefix || suffix) {
@@ -252,7 +262,8 @@ function Input({ className, size: sizeProp, error = false, prefix, suffix, ...pr
252
262
  "disabled:cursor-not-allowed",
253
263
  "read-only:text-[var(--ds-text-secondary)] read-only:cursor-default"
254
264
  ),
255
- ...props
265
+ ...props,
266
+ onChange: props.onChange || props.value === void 0 ? onChange : void 0
256
267
  }
257
268
  ),
258
269
  suffix && /* @__PURE__ */ jsx("span", { className: "shrink-0 pr-ds-3 text-[var(--ds-text-muted)] flex items-center", children: suffix })
@@ -281,11 +292,13 @@ function Input({ className, size: sizeProp, error = false, prefix, suffix, ...pr
281
292
  "transition-colors duration-[var(--ds-transition-speed)]",
282
293
  className
283
294
  ),
284
- ...props
295
+ ...props,
296
+ onChange: props.onChange || props.value === void 0 ? onChange : void 0
285
297
  }
286
298
  );
287
299
  }
288
300
  function Textarea({ className, autoResize, maxRows, error = false, ref, ...props }) {
301
+ const onChange = useReportedChange(props.onChange, props.value, valueOf);
289
302
  const internalRef = useRef(null);
290
303
  const textareaRef = ref ?? internalRef;
291
304
  const resize = useCallback(() => {
@@ -324,7 +337,8 @@ function Textarea({ className, autoResize, maxRows, error = false, ref, ...props
324
337
  className
325
338
  ),
326
339
  onInput: autoResize ? resize : void 0,
327
- ...props
340
+ ...props,
341
+ onChange: props.onChange || props.value === void 0 ? onChange : void 0
328
342
  }
329
343
  );
330
344
  }
@@ -347,18 +361,20 @@ var TOGGLE_SIZES = {
347
361
  md: { w: 44, h: 24, thumb: 16 }
348
362
  };
349
363
  function Toggle({ checked, onChange, label, disabled, error = false, layout = "full", size = "md", "aria-labelledby": ariaLabelledBy, ...rest }) {
364
+ const change = useReportedChange(onChange, checked);
350
365
  const dims = TOGGLE_SIZES[size];
351
366
  const travel = dims.w - dims.thumb - 3;
352
367
  const switchButton = /* @__PURE__ */ jsx(
353
368
  "button",
354
369
  {
370
+ type: "button",
355
371
  role: "switch",
356
372
  "aria-checked": checked,
357
373
  "aria-label": !label && !ariaLabelledBy ? "Toggle" : void 0,
358
374
  "aria-labelledby": ariaLabelledBy,
359
375
  "aria-invalid": error || void 0,
360
376
  disabled,
361
- onClick: () => !disabled && onChange(!checked),
377
+ onClick: () => !disabled && change(!checked),
362
378
  ...rest,
363
379
  className: cn(
364
380
  "relative inline-flex shrink-0 items-center overflow-hidden",
@@ -407,6 +423,149 @@ function Toggle({ checked, onChange, label, disabled, error = false, layout = "f
407
423
  }
408
424
  );
409
425
  }
426
+ function InlineFieldError({ message, id, className }) {
427
+ return /* @__PURE__ */ jsxs(
428
+ "p",
429
+ {
430
+ id,
431
+ role: "alert",
432
+ className: cn(
433
+ "flex items-center gap-ds-1.5 text-xs text-[var(--ds-status-error)]",
434
+ "mt-ds-1.5",
435
+ className
436
+ ),
437
+ children: [
438
+ /* @__PURE__ */ jsx(Icon, { name: "error", size: 14, className: "shrink-0" }),
439
+ /* @__PURE__ */ jsx("span", { children: message })
440
+ ]
441
+ }
442
+ );
443
+ }
444
+ var FieldContext = createContext(null);
445
+ function useField() {
446
+ return useContext(FieldContext);
447
+ }
448
+ function useFieldControl(own, options = {}) {
449
+ const field = useField();
450
+ const id = own.id ?? field?.controlId;
451
+ const invalid = !!own.error || own["aria-invalid"] === true || own["aria-invalid"] === "true" || !!field?.invalid;
452
+ const required = !!own.required || own["aria-required"] === true || own["aria-required"] === "true" || !!field?.required;
453
+ const noteId = options.requiredInDescription && required && id ? `${id}-required` : void 0;
454
+ const ids = [own["aria-describedby"], field?.describedBy, noteId].filter(Boolean).join(" ").split(/\s+/).filter(Boolean);
455
+ return {
456
+ id,
457
+ labelId: own["aria-labelledby"] ?? field?.labelId,
458
+ describedBy: [...new Set(ids)].join(" ") || void 0,
459
+ invalid,
460
+ required,
461
+ requiredNote: noteId ? /* @__PURE__ */ jsx("span", { id: noteId, hidden: true, children: "Required" }) : null
462
+ };
463
+ }
464
+ var AddAnotherRowCtx = createContext(false);
465
+ function Field({ label, children, hint, error, required, labelAction, hideLabel = false, id: idProp, validate, className }) {
466
+ const inGroupRow = useContext(AddAnotherRowCtx);
467
+ const generated = useId();
468
+ const id = idProp ?? `field-${generated}`;
469
+ const hintId = `${id}-hint`;
470
+ const errorId = `${id}-error`;
471
+ const form = useOptionalFormContext();
472
+ const mode = form?.validationMode ?? "onBlur";
473
+ const [localError, setLocalError] = useState(void 0);
474
+ const [left, setLeft] = useState(false);
475
+ const checkError = validate ? form ? form.errors[id] : localError : void 0;
476
+ const child = typeof children === "function" ? null : Children.only(children);
477
+ const childProps = child && isValidElement(child) ? child.props : null;
478
+ const latest = useLatest({ validate, childProps, id, mode, left, showing: !!checkError, form });
479
+ const check = useCallback(() => {
480
+ const { validate: fn, childProps: props, id: controlId } = latest.current;
481
+ if (!fn) return void 0;
482
+ const value = props && "value" in props ? props.value : props && "checked" in props ? props.checked : document.getElementById(controlId)?.value;
483
+ const message = fn(value);
484
+ return typeof message === "string" && message.length > 0 ? message : void 0;
485
+ }, [latest]);
486
+ const show = useCallback((message) => {
487
+ const { form: f, id: controlId } = latest.current;
488
+ if (f) f.setError(controlId, message);
489
+ else setLocalError(message);
490
+ }, [latest]);
491
+ const register = form?.register;
492
+ const checks = !!validate;
493
+ useEffect(() => register && checks ? register(id, check) : void 0, [register, checks, id, check]);
494
+ const leaving = useRef(null);
495
+ useEffect(() => () => {
496
+ if (leaving.current) clearTimeout(leaving.current);
497
+ }, []);
498
+ const onFocusCapture = () => {
499
+ if (leaving.current) {
500
+ clearTimeout(leaving.current);
501
+ leaving.current = null;
502
+ }
503
+ };
504
+ const onBlurCapture = () => {
505
+ if (!validate) return;
506
+ if (leaving.current) clearTimeout(leaving.current);
507
+ leaving.current = setTimeout(() => {
508
+ leaving.current = null;
509
+ setLeft(true);
510
+ if (latest.current.mode !== "onSubmit" || latest.current.showing) show(check());
511
+ }, 0);
512
+ };
513
+ const recheck = () => {
514
+ if (!validate) return;
515
+ setTimeout(() => {
516
+ const { mode: m, left: wasLeft, showing } = latest.current;
517
+ if (m === "onChange" && wasLeft || showing) show(check());
518
+ }, 0);
519
+ };
520
+ const shownError = typeof error === "string" && error.length > 0 ? error : checkError;
521
+ const hasError = typeof shownError === "string" && shownError.length > 0;
522
+ const describedBy = [hint ? hintId : null, hasError ? errorId : null].filter(Boolean).join(" ") || void 0;
523
+ const labelId = `${id}-label`;
524
+ const fieldContext = useMemo(() => ({ controlId: id, labelId, describedBy, invalid: hasError, required: !!required }), [id, labelId, describedBy, hasError, required]);
525
+ const controlProps = {
526
+ id,
527
+ "aria-describedby": describedBy,
528
+ ...hasError ? { "aria-invalid": true, error: true } : {},
529
+ ...required ? { "aria-required": true, required: true } : {}
530
+ };
531
+ let control;
532
+ if (typeof children === "function") {
533
+ control = children(controlProps);
534
+ } else {
535
+ if (!child || !isValidElement(child)) throw new Error("Field expects a single form control as its child");
536
+ const isNative = typeof child.type === "string";
537
+ const { error: errorFlag, ...ariaOnly } = controlProps;
538
+ const ownOnChange = childProps?.onChange;
539
+ const onChange = validate && !isNative ? { onChange: (...args) => {
540
+ ownOnChange?.(...args);
541
+ recheck();
542
+ } } : {};
543
+ control = cloneElement(child, isNative ? ariaOnly : { ...ariaOnly, ...errorFlag ? { error: true } : {}, ...onChange });
544
+ }
545
+ const labelNode = /* @__PURE__ */ jsxs(Label, { id: labelId, htmlFor: id, className: cn(hideLabel && "sr-only", hideLabel && inGroupRow && "@max-[30rem]:not-sr-only"), children: [
546
+ label,
547
+ required && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "ml-ds-0.5 text-[var(--ds-status-error)]", children: "*" })
548
+ ] });
549
+ return (
550
+ // In a settings panel a field keeps the narrow reading width however wide the page is (--ds-form-max, Sprint 25.9 A4)
551
+ /* @__PURE__ */ jsxs(
552
+ "div",
553
+ {
554
+ className: cn("flex flex-col gap-ds-1.5 max-w-[var(--ds-form-max,none)]", className),
555
+ ...validate ? { onFocusCapture, onBlurCapture, onInputCapture: recheck } : {},
556
+ children: [
557
+ hideLabel && !labelAction ? labelNode : /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-ds-2", children: [
558
+ labelNode,
559
+ labelAction
560
+ ] }),
561
+ /* @__PURE__ */ jsx(FieldContext.Provider, { value: fieldContext, children: control }),
562
+ hint && /* @__PURE__ */ jsx("p", { id: hintId, className: "text-xs text-[var(--ds-text-muted)]", children: hint }),
563
+ hasError && /* @__PURE__ */ jsx(InlineFieldError, { id: errorId, message: shownError, className: "mt-0" })
564
+ ]
565
+ }
566
+ )
567
+ );
568
+ }
410
569
  var DROPDOWN_HEIGHT = {
411
570
  sm: "px-ds-2 py-ds-1 text-xs h-[var(--ds-control-height-sm)]",
412
571
  md: "px-ds-3 py-ds-2 text-sm h-[var(--ds-control-height-md)]",
@@ -415,7 +574,7 @@ var DROPDOWN_HEIGHT = {
415
574
  function Dropdown({
416
575
  options,
417
576
  value,
418
- onChange,
577
+ onChange: onChangeProp,
419
578
  label,
420
579
  disabled,
421
580
  placeholder = "Select...",
@@ -427,9 +586,17 @@ function Dropdown({
427
586
  container,
428
587
  error = false,
429
588
  showChips = true,
430
- "aria-label": ariaLabel
589
+ "aria-label": ariaLabel,
590
+ ...wiring
431
591
  }) {
592
+ const onChange = useReportedChange(onChangeProp, value);
432
593
  const contextSize = useContext(ControlSizeContext);
594
+ const generatedId = useId();
595
+ const control = useFieldControl({ ...wiring, error: error || void 0 }, { requiredInDescription: true });
596
+ const triggerId = control.id ?? generatedId;
597
+ const labelSpanId = `${triggerId}-label`;
598
+ const labelledBy = control.labelId ? `${control.labelId} ${triggerId}` : label && !ariaLabel ? `${labelSpanId} ${triggerId}` : void 0;
599
+ const invalid = control.invalid;
433
600
  const size = sizeProp ?? contextSize ?? "md";
434
601
  const [open, setOpen] = useState(false);
435
602
  const [search, setSearch] = useState("");
@@ -597,11 +764,13 @@ function Dropdown({
597
764
  const displayLabel = multiple ? selectedValues.length > 0 ? `${selectedValues.length} selected` : placeholder : options.find((o) => o.id === selectedValues[0])?.label ?? placeholder;
598
765
  const { ref: overflowRef, isTruncated } = useTextOverflow({ lines: 1, content: displayLabel });
599
766
  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 }),
767
+ label && /* @__PURE__ */ jsx("span", { id: labelSpanId, className: "block mb-ds-1.5 text-sm font-medium text-[var(--ds-text-secondary)]", children: label }),
768
+ control.requiredNote,
601
769
  /* @__PURE__ */ jsxs(
602
770
  "button",
603
771
  {
604
772
  ref: triggerRef,
773
+ id: triggerId,
605
774
  type: "button",
606
775
  onClick: () => !disabled && setOpen(!open),
607
776
  onKeyDown: (e) => {
@@ -612,22 +781,24 @@ function Dropdown({
612
781
  }
613
782
  },
614
783
  disabled,
615
- "aria-invalid": error || void 0,
784
+ "aria-invalid": invalid || void 0,
616
785
  "aria-haspopup": "listbox",
617
786
  "aria-expanded": open,
618
- "aria-label": ariaLabel,
787
+ "aria-label": labelledBy ? void 0 : ariaLabel,
788
+ "aria-labelledby": labelledBy,
789
+ "aria-describedby": control.describedBy,
619
790
  className: cn(
620
791
  "w-full flex items-center justify-between gap-ds-2",
621
792
  DROPDOWN_HEIGHT[size],
622
793
  "bg-[var(--ds-input-bg)] text-[var(--ds-text-primary)]",
623
794
  "border-[length:var(--ds-border-width)]",
624
- error ? "border-[var(--ds-status-error)]" : "border-[var(--ds-input-border)]",
795
+ invalid ? "border-[var(--ds-status-error)]" : "border-[var(--ds-input-border)]",
625
796
  "rounded-[var(--ds-radius-md)]",
626
797
  "shadow-[var(--ds-input-shadow)]",
627
798
  "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,
799
+ !invalid && !disabled && !open && "hover:border-[var(--ds-input-border-hover)]",
800
+ open && (invalid ? ERROR_ACTIVE_RING : ACTIVE_RING),
801
+ invalid ? ERROR_FOCUS_RING : FOCUS_RING,
631
802
  "cursor-pointer disabled:cursor-not-allowed disabled:opacity-disabled",
632
803
  !selectedValues.length && "text-[var(--ds-text-muted)]"
633
804
  ),
@@ -642,9 +813,10 @@ function Dropdown({
642
813
  "div",
643
814
  {
644
815
  ref: menuRef,
816
+ tabIndex: -1,
645
817
  onClick: (e) => e.stopPropagation(),
646
818
  className: cn(
647
- "fixed z-[var(--ds-z-popover)]",
819
+ "fixed z-[var(--ds-z-popover)] outline-none",
648
820
  "bg-[var(--ds-bg-surface-elevated)]",
649
821
  "[backdrop-filter:var(--ds-glass-filter)] [-webkit-backdrop-filter:var(--ds-glass-filter)]",
650
822
  "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
@@ -704,6 +876,7 @@ function Dropdown({
704
876
  /* @__PURE__ */ jsx(
705
877
  "button",
706
878
  {
879
+ type: "button",
707
880
  "data-chip-remove": "",
708
881
  onClick: () => {
709
882
  pendingChipFocus.current = { index: i, value: v };
@@ -771,8 +944,10 @@ var TEXT_SIZE = {
771
944
  md: "text-sm",
772
945
  lg: "text-base"
773
946
  };
774
- function Combobox({ value, onChange, options, placeholder = "Search\u2026", disabled, loading = false, className, ref, container, error = false, size: sizeProp }) {
947
+ function Combobox({ value, onChange: onChangeProp, options, placeholder = "Search\u2026", disabled, loading = false, className, ref, container, error = false, size: sizeProp, ...wiring }) {
948
+ const onChange = useReportedChange(onChangeProp, value);
775
949
  const contextSize = useContext(ControlSizeContext);
950
+ const control = useFieldControl({ ...wiring, error: error || void 0 });
776
951
  const size = sizeProp ?? contextSize ?? "md";
777
952
  const [open, setOpen] = useState(false);
778
953
  useOverlayLayer(open);
@@ -883,12 +1058,12 @@ function Combobox({ value, onChange, options, placeholder = "Search\u2026", disa
883
1058
  "flex items-center",
884
1059
  CONTROL_HEIGHT[size],
885
1060
  "border-[length:var(--ds-border-width)]",
886
- error ? "border-[var(--ds-status-error)]" : "border-[var(--ds-input-border)]",
1061
+ control.invalid ? "border-[var(--ds-status-error)]" : "border-[var(--ds-input-border)]",
887
1062
  "bg-[var(--ds-input-bg)] rounded-[var(--ds-radius-md)]",
888
1063
  "shadow-[var(--ds-input-shadow)]",
889
1064
  "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,
1065
+ !control.invalid && !disabled && "hover:border-[var(--ds-input-border-hover)]",
1066
+ control.invalid ? ERROR_HAS_FOCUS_RING : HAS_FOCUS_RING,
892
1067
  disabled && "opacity-disabled pointer-events-none"
893
1068
  ),
894
1069
  children: [
@@ -898,8 +1073,12 @@ function Combobox({ value, onChange, options, placeholder = "Search\u2026", disa
898
1073
  {
899
1074
  ref: mergeInputRefs,
900
1075
  type: "text",
1076
+ id: control.id,
901
1077
  role: "combobox",
902
- "aria-invalid": error || void 0,
1078
+ "aria-labelledby": wiring["aria-labelledby"],
1079
+ "aria-describedby": control.describedBy,
1080
+ "aria-invalid": control.invalid || void 0,
1081
+ "aria-required": control.required || void 0,
903
1082
  "aria-expanded": open,
904
1083
  "aria-controls": listboxId,
905
1084
  "aria-autocomplete": "list",
@@ -964,6 +1143,7 @@ function Combobox({ value, onChange, options, placeholder = "Search\u2026", disa
964
1143
  children: /* @__PURE__ */ jsx("div", { style: { padding: "var(--ds-space-1)" }, children: loading ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-ds-4", children: /* @__PURE__ */ jsx(Spinner, { size: "sm", label: "Loading options" }) }) : filtered.length === 0 ? /* @__PURE__ */ jsx("div", { className: "px-ds-2 py-ds-3 text-sm text-[var(--ds-text-muted)] text-center", children: "No results" }) : filtered.map((opt, i) => /* @__PURE__ */ jsxs(
965
1144
  "button",
966
1145
  {
1146
+ type: "button",
967
1147
  id: `${listboxId}-opt-${i}`,
968
1148
  role: "option",
969
1149
  tabIndex: -1,
@@ -995,7 +1175,8 @@ var CHECKBOX_SIZES = {
995
1175
  sm: { box: "h-[16px] w-[16px]", glyph: 10, label: "text-xs" },
996
1176
  md: { box: "h-[18px] w-[18px]", glyph: 12, label: "text-sm" }
997
1177
  };
998
- function Checkbox({ checked, onChange, label, size = "md", disabled = false, indeterminate = false, className, ref }) {
1178
+ function Checkbox({ checked, onChange: onChangeProp, label, size = "md", disabled = false, indeterminate = false, className, ref }) {
1179
+ const onChange = useReportedChange(onChangeProp, checked);
999
1180
  const internalRef = useRef(null);
1000
1181
  const dims = CHECKBOX_SIZES[size];
1001
1182
  const mergedRef = useCallback(
@@ -1087,7 +1268,8 @@ function CheckboxGroupItem({ value, label, disabled, groupValue, onGroupChange }
1087
1268
  }
1088
1269
  );
1089
1270
  }
1090
- function RadioGroup({ value, onChange, options, orientation = "vertical", className, label }) {
1271
+ function RadioGroup({ value, onChange: onChangeProp, options, orientation = "vertical", className, label }) {
1272
+ const onChange = useReportedChange(onChangeProp, value);
1091
1273
  const groupRef = useRef(null);
1092
1274
  const groupId = useId();
1093
1275
  const enabledOptions = options.filter((o) => !o.disabled);
@@ -1312,7 +1494,8 @@ function parseTypedDate(text, dayFirst = localeDayFirst()) {
1312
1494
  }
1313
1495
  return null;
1314
1496
  }
1315
- function DatePicker({ value, onChange, placeholder = "Pick a date", disabled, className, ref, container, error = false, label, id, size: sizeProp, min, max, range: range2 }) {
1497
+ function DatePicker({ value, onChange: onChangeProp, placeholder = "Pick a date", disabled, className, ref, container, error = false, label, id, size: sizeProp, min, max, range: range2, ...wiring }) {
1498
+ const onChange = useReportedChange(onChangeProp, value);
1316
1499
  const contextSize = useContext(ControlSizeContext);
1317
1500
  const size = sizeProp ?? contextSize ?? "md";
1318
1501
  const [open, setOpen] = useState(false);
@@ -1323,7 +1506,8 @@ function DatePicker({ value, onChange, placeholder = "Pick a date", disabled, cl
1323
1506
  const panelRef = useRef(null);
1324
1507
  const gridRef = useRef(null);
1325
1508
  const generatedId = useId();
1326
- const inputId = id ?? generatedId;
1509
+ const control = useFieldControl({ ...wiring, id, error: error || void 0 }, { requiredInDescription: true });
1510
+ const inputId = control.id ?? generatedId;
1327
1511
  const pos = usePosition(triggerRef, panelRef, open, { side: "bottom", align: "start", offset: 4, flip: true });
1328
1512
  const year = viewDate.getFullYear();
1329
1513
  const month = viewDate.getMonth();
@@ -1508,6 +1692,7 @@ function DatePicker({ value, onChange, placeholder = "Pick a date", disabled, cl
1508
1692
  for (let i = 0; i < cells.length; i += 7) weeks.push(cells.slice(i, i + 7));
1509
1693
  return /* @__PURE__ */ jsxs("div", { ref, className: cn(className), children: [
1510
1694
  label && /* @__PURE__ */ jsx("label", { htmlFor: inputId, className: "block text-sm font-medium text-[var(--ds-text-primary)] mb-ds-1", children: label }),
1695
+ control.requiredNote,
1511
1696
  /* @__PURE__ */ jsxs(
1512
1697
  "div",
1513
1698
  {
@@ -1515,9 +1700,11 @@ function DatePicker({ value, onChange, placeholder = "Pick a date", disabled, cl
1515
1700
  "data-date-field": "",
1516
1701
  role: "group",
1517
1702
  id: inputId,
1518
- "aria-label": label ?? "Date",
1703
+ "aria-label": control.labelId ? void 0 : label ?? "Date",
1704
+ "aria-labelledby": control.labelId,
1705
+ "aria-describedby": control.describedBy,
1519
1706
  "aria-description": !value ? placeholder : void 0,
1520
- "aria-invalid": error || typedInvalid || void 0,
1707
+ "aria-invalid": control.invalid || typedInvalid || void 0,
1521
1708
  onPaste: (e) => {
1522
1709
  const text = e.clipboardData.getData("text");
1523
1710
  if (text) {
@@ -1530,12 +1717,12 @@ function DatePicker({ value, onChange, placeholder = "Pick a date", disabled, cl
1530
1717
  CONTROL_HEIGHT2[size],
1531
1718
  TEXT_SIZE2[size],
1532
1719
  "border-[length:var(--ds-border-width)]",
1533
- error || typedInvalid ? "border-[var(--ds-status-error)]" : "border-[var(--ds-input-border)]",
1720
+ control.invalid || typedInvalid ? "border-[var(--ds-status-error)]" : "border-[var(--ds-input-border)]",
1534
1721
  "bg-[var(--ds-input-bg)] rounded-[var(--ds-radius-md)]",
1535
1722
  "shadow-[var(--ds-input-shadow)]",
1536
1723
  "transition-colors",
1537
1724
  "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)]",
1725
+ !control.invalid && !disabled && !open && "hover:border-[var(--ds-input-border-hover)] focus-within:border-[var(--ds-input-border-hover)]",
1539
1726
  open && ACTIVE_RING,
1540
1727
  disabled && "opacity-disabled cursor-not-allowed",
1541
1728
  "text-[var(--ds-text-primary)]"
@@ -1592,8 +1779,9 @@ function DatePicker({ value, onChange, placeholder = "Pick a date", disabled, cl
1592
1779
  ref: panelRef,
1593
1780
  role: "dialog",
1594
1781
  "aria-label": "Choose a date",
1782
+ tabIndex: -1,
1595
1783
  className: cn(
1596
- "fixed z-[var(--ds-z-popover)] w-[280px]",
1784
+ "fixed z-[var(--ds-z-popover)] w-[280px] outline-none",
1597
1785
  "bg-[var(--ds-bg-surface-elevated)]",
1598
1786
  "[backdrop-filter:var(--ds-glass-filter)] [-webkit-backdrop-filter:var(--ds-glass-filter)]",
1599
1787
  "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
@@ -1912,7 +2100,7 @@ var DEFAULT_DATE_RANGE_PRESETS = [
1912
2100
  ];
1913
2101
  function DateRangePicker({
1914
2102
  value,
1915
- onChange,
2103
+ onChange: onChangeProp,
1916
2104
  placeholder = "Pick a date range",
1917
2105
  disabled,
1918
2106
  error = false,
@@ -1925,8 +2113,10 @@ function DateRangePicker({
1925
2113
  max,
1926
2114
  presets = DEFAULT_DATE_RANGE_PRESETS,
1927
2115
  weekStartsOn = 0,
1928
- size: sizeProp
2116
+ size: sizeProp,
2117
+ ...wiring
1929
2118
  }) {
2119
+ const onChange = useReportedChange(onChangeProp, value);
1930
2120
  const contextSize = useContext(ControlSizeContext);
1931
2121
  const size = sizeProp ?? contextSize ?? "md";
1932
2122
  const [open, setOpen] = useState(false);
@@ -1935,7 +2125,9 @@ function DateRangePicker({
1935
2125
  const triggerRef = useRef(null);
1936
2126
  const panelRef = useRef(null);
1937
2127
  const generatedId = useId();
1938
- const triggerId = id ?? generatedId;
2128
+ const control = useFieldControl({ ...wiring, id, error: error || void 0 }, { requiredInDescription: true });
2129
+ const triggerId = control.id ?? generatedId;
2130
+ const labelledBy = control.labelId ? `${control.labelId} ${triggerId}` : void 0;
1939
2131
  const pos = usePosition(triggerRef, panelRef, open, { side: "bottom", align: "start", offset: 4, flip: true });
1940
2132
  const today = useMemo(() => startOfDay(/* @__PURE__ */ new Date()), []);
1941
2133
  const dayNames = getOrderedDayNamesShort(weekStartsOn);
@@ -2018,6 +2210,7 @@ function DateRangePicker({
2018
2210
  const summary = value.start && value.end ? `${fmt(value.start)} \u2013 ${fmt(value.end)}` : value.start ? `${fmt(value.start)} \u2013 \u2026` : null;
2019
2211
  return /* @__PURE__ */ jsxs("div", { ref, className: cn(className), children: [
2020
2212
  label && /* @__PURE__ */ jsx("label", { htmlFor: triggerId, className: "block text-sm font-medium text-[var(--ds-text-primary)] mb-ds-1", children: label }),
2213
+ control.requiredNote,
2021
2214
  /* @__PURE__ */ jsxs(
2022
2215
  "button",
2023
2216
  {
@@ -2026,7 +2219,9 @@ function DateRangePicker({
2026
2219
  type: "button",
2027
2220
  onClick: () => !disabled && setOpen((o) => !o),
2028
2221
  disabled,
2029
- "aria-invalid": error || void 0,
2222
+ "aria-labelledby": labelledBy,
2223
+ "aria-describedby": control.describedBy,
2224
+ "aria-invalid": control.invalid || void 0,
2030
2225
  "aria-haspopup": "dialog",
2031
2226
  "aria-expanded": open,
2032
2227
  className: cn(
@@ -2034,14 +2229,14 @@ function DateRangePicker({
2034
2229
  CONTROL_HEIGHT3[size],
2035
2230
  TEXT_SIZE3[size],
2036
2231
  "border-[length:var(--ds-border-width)]",
2037
- error ? "border-[var(--ds-status-error)]" : "border-[var(--ds-input-border)]",
2232
+ control.invalid ? "border-[var(--ds-status-error)]" : "border-[var(--ds-input-border)]",
2038
2233
  "bg-[var(--ds-input-bg)] rounded-[var(--ds-radius-md)]",
2039
2234
  "shadow-[var(--ds-input-shadow)]",
2040
2235
  "transition-colors cursor-pointer",
2041
2236
  "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,
2237
+ !control.invalid && !disabled && !open && "hover:border-[var(--ds-input-border-hover)]",
2238
+ open && (control.invalid ? ERROR_ACTIVE_RING : ACTIVE_RING),
2239
+ control.invalid ? ERROR_FOCUS_RING : FOCUS_RING,
2045
2240
  disabled && "opacity-disabled cursor-not-allowed",
2046
2241
  summary ? "text-[var(--ds-text-primary)]" : "text-[var(--ds-text-muted)]"
2047
2242
  ),
@@ -2058,8 +2253,9 @@ function DateRangePicker({
2058
2253
  ref: panelRef,
2059
2254
  role: "dialog",
2060
2255
  "aria-label": "Choose a date range",
2256
+ tabIndex: -1,
2061
2257
  className: cn(
2062
- "fixed z-[var(--ds-z-popover)] flex",
2258
+ "fixed z-[var(--ds-z-popover)] flex outline-none",
2063
2259
  "bg-[var(--ds-bg-surface-elevated)]",
2064
2260
  "[backdrop-filter:var(--ds-glass-filter)] [-webkit-backdrop-filter:var(--ds-glass-filter)]",
2065
2261
  "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
@@ -2264,7 +2460,7 @@ function Progress({ value, max = 100, variant = "default", size = "md", indeterm
2264
2460
  {
2265
2461
  className: cn(
2266
2462
  "h-full rounded-[var(--ds-radius-full)]",
2267
- indeterminate ? "animate-[indeterminate_1.5s_ease-in-out_infinite] w-1/3" : "transition-all",
2463
+ indeterminate ? "animate-[gs-indeterminate_1.5s_ease-in-out_infinite] w-1/3" : "transition-all",
2268
2464
  variantFills[variant]
2269
2465
  ),
2270
2466
  style: indeterminate ? void 0 : { width: `${pct}%` }
@@ -2278,91 +2474,33 @@ function Progress({ value, max = 100, variant = "default", size = "md", indeterm
2278
2474
  ] })
2279
2475
  ] });
2280
2476
  }
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
2477
  function TruncFileName({ children, className }) {
2345
2478
  const { ref, isTruncated } = useTextOverflow({ lines: 1 });
2346
2479
  const el = /* @__PURE__ */ jsx("p", { ref, className: cn("truncate", className), children });
2347
2480
  if (!isTruncated) return el;
2348
2481
  return /* @__PURE__ */ jsx(Tooltip, { content: children, children: el });
2349
2482
  }
2483
+ var PARALLEL = 3;
2350
2484
  function formatSize(bytes) {
2351
2485
  if (bytes < 1024) return `${bytes} B`;
2352
2486
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
2353
2487
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
2354
2488
  }
2355
- function FileUpload({ onFiles, accept, multiple = true, maxSize, className, error = false, label, id }) {
2356
- const [files, setFiles] = useState([]);
2489
+ var running = (item) => item.status === "queued" || item.status === "uploading";
2490
+ var failureOf = (err) => err instanceof Error && err.message ? err.message : "The upload didn't work.";
2491
+ function FileUpload({ onFiles, files, upload, onCancel, onRetry, onRemove, accept, multiple = true, maxSize, className, error = false, label, id }) {
2492
+ const controlled = files !== void 0;
2493
+ const [own, setOwn] = useState([]);
2494
+ const shown = controlled ? files : own;
2357
2495
  const [dragOver, setDragOver] = useState(false);
2358
2496
  const [rejected, setRejected] = useState(null);
2359
2497
  const inputRef = useRef(null);
2498
+ const listRef = useRef(null);
2360
2499
  const nextEntryId = useRef(0);
2361
- const timers = useRef(/* @__PURE__ */ new Map());
2362
- useEffect(() => {
2363
- const running = timers.current;
2364
- return () => running.forEach(clearInterval);
2365
- }, []);
2500
+ const controllers = useRef(/* @__PURE__ */ new Map());
2501
+ const uploadRef = useLatest(upload);
2502
+ const overlayMark = useContext(OverlayMarkCtx);
2503
+ const reportChange = useContext(FormChangeCtx);
2366
2504
  const field = useField();
2367
2505
  const ownId = useId();
2368
2506
  const inputId = id ?? `${ownId}-input`;
@@ -2372,37 +2510,100 @@ function FileUpload({ onFiles, accept, multiple = true, maxSize, className, erro
2372
2510
  const constraints = fileConstraints(accept, maxSize, multiple);
2373
2511
  const invalid = error || rejected != null;
2374
2512
  const describedBy = [constraints ? constraintsId : null, field?.describedBy, rejected ? rejectedId : null].filter(Boolean).join(" ") || void 0;
2375
- const stopTimer = (id2) => {
2376
- clearInterval(timers.current.get(id2));
2377
- timers.current.delete(id2);
2513
+ const patch = (itemId, change) => setOwn((prev) => prev.map((i) => i.id === itemId ? { ...i, ...change } : i));
2514
+ const stop = (itemId) => {
2515
+ controllers.current.get(itemId)?.abort();
2516
+ controllers.current.delete(itemId);
2517
+ };
2518
+ const start = (item) => {
2519
+ const run = uploadRef.current;
2520
+ if (!run || !item.file) return;
2521
+ const controller = new AbortController();
2522
+ controllers.current.set(item.id, controller);
2523
+ patch(item.id, { status: "uploading", progress: 0, error: void 0 });
2524
+ const settle = (change) => {
2525
+ if (controller.signal.aborted) return;
2526
+ controllers.current.delete(item.id);
2527
+ patch(item.id, change);
2528
+ };
2529
+ let attempt;
2530
+ try {
2531
+ attempt = Promise.resolve(run(item.file, {
2532
+ signal: controller.signal,
2533
+ onProgress: (percent) => {
2534
+ if (!controller.signal.aborted) patch(item.id, { progress: Math.max(0, Math.min(100, percent)) });
2535
+ }
2536
+ }));
2537
+ } catch (err) {
2538
+ attempt = Promise.reject(err);
2539
+ }
2540
+ attempt.then(() => settle({ status: "done", progress: 100 }), (err) => settle({ status: "error", error: failureOf(err) }));
2378
2541
  };
2542
+ useEffect(() => {
2543
+ if (controlled || !upload) return;
2544
+ let active = own.filter((i) => i.status === "uploading" && controllers.current.has(i.id)).length;
2545
+ for (const item of own) {
2546
+ if (active >= PARALLEL) break;
2547
+ if (item.status === "queued" || item.status === "uploading" && !controllers.current.has(item.id)) {
2548
+ start(item);
2549
+ active++;
2550
+ }
2551
+ }
2552
+ }, [own, controlled, upload]);
2553
+ const stopAll = useCallback(() => {
2554
+ controllers.current.forEach((c) => c.abort());
2555
+ controllers.current.clear();
2556
+ }, []);
2557
+ useEffect(() => stopAll, [stopAll]);
2558
+ const overlayOpen = overlayMark?.open ?? true;
2559
+ const [wasOpen, setWasOpen] = useState(overlayOpen);
2560
+ if (overlayOpen !== wasOpen) {
2561
+ setWasOpen(overlayOpen);
2562
+ if (!overlayOpen && !controlled) setOwn((prev) => prev.filter((i) => !running(i)));
2563
+ }
2564
+ useEffect(() => {
2565
+ if (!overlayOpen && !controlled) stopAll();
2566
+ }, [overlayOpen, controlled, stopAll]);
2567
+ const busy = shown.some(running);
2568
+ useEffect(() => {
2569
+ if (!busy || !overlayMark) return;
2570
+ return overlayMark.hold();
2571
+ }, [busy, overlayMark]);
2572
+ const [heard, setHeard] = useState("");
2573
+ const [seen, setSeen] = useState(() => new Map(shown.map((i) => [i.id, i.status])));
2574
+ if (shown.length !== seen.size || shown.some((i) => seen.get(i.id) !== i.status)) {
2575
+ const said = [];
2576
+ for (const item of shown) {
2577
+ const before = seen.get(item.id);
2578
+ if (!before || before === item.status) continue;
2579
+ if (item.status === "done") said.push(`${item.name} uploaded`);
2580
+ else if (item.status === "error") said.push(`${item.name} didn't upload: ${item.error ?? "the upload failed"}`);
2581
+ }
2582
+ setSeen(new Map(shown.map((i) => [i.id, i.status])));
2583
+ if (said.length) setHeard(said.join(". "));
2584
+ }
2585
+ const pendingFocus = useRef(null);
2586
+ useEffect(() => {
2587
+ const index = pendingFocus.current;
2588
+ if (index === null) return;
2589
+ pendingFocus.current = null;
2590
+ const rows = Array.from(listRef.current?.querySelectorAll(":scope > li") ?? []).map((row) => row.querySelector("button")).filter((b) => !!b);
2591
+ focusAfterRemoval(rows, index, inputRef.current);
2592
+ }, [shown]);
2379
2593
  const addFiles = useCallback(
2380
2594
  (fileList) => {
2381
2595
  const { accepted, message } = checkFiles(Array.from(fileList), { accept, maxSize, multiple });
2382
2596
  setRejected(message);
2383
2597
  if (accepted.length === 0) return;
2384
- const entries = accepted.map((file) => ({ id: nextEntryId.current++, file, progress: 0, status: "uploading" }));
2385
- if (!multiple) {
2386
- timers.current.forEach(clearInterval);
2387
- timers.current.clear();
2388
- }
2389
- setFiles((prev) => multiple ? [...prev, ...entries] : entries);
2390
- onFiles(accepted);
2391
- for (const { id: id2 } of entries) {
2392
- let progress = 0;
2393
- const timer = setInterval(() => {
2394
- progress = Math.min(100, progress + Math.random() * 30 + 10);
2395
- const done = progress >= 100;
2396
- if (done) {
2397
- clearInterval(timer);
2398
- timers.current.delete(id2);
2399
- }
2400
- setFiles((prev) => prev.map((f) => f.id === id2 ? { ...f, progress, status: done ? "complete" : "uploading" } : f));
2401
- }, 300);
2402
- timers.current.set(id2, timer);
2403
- }
2598
+ reportChange?.();
2599
+ onFiles?.(accepted);
2600
+ if (controlled) return;
2601
+ const status = uploadRef.current ? "queued" : "added";
2602
+ const entries = accepted.map((file) => ({ id: `${ownId}-file-${nextEntryId.current++}`, name: file.name, size: file.size, status, file }));
2603
+ if (!multiple) stopAll();
2604
+ setOwn((prev) => multiple ? [...prev, ...entries] : entries);
2404
2605
  },
2405
- [accept, maxSize, multiple, onFiles]
2606
+ [accept, maxSize, multiple, onFiles, controlled, ownId, reportChange, stopAll, uploadRef]
2406
2607
  );
2407
2608
  const handleDrop = useCallback(
2408
2609
  (e) => {
@@ -2412,10 +2613,31 @@ function FileUpload({ onFiles, accept, multiple = true, maxSize, className, erro
2412
2613
  },
2413
2614
  [addFiles]
2414
2615
  );
2415
- const removeFile = (id2) => {
2416
- stopTimer(id2);
2417
- setFiles((prev) => prev.filter((f) => f.id !== id2));
2616
+ const indexOf = (item) => shown.findIndex((i) => i.id === item.id);
2617
+ const cancel = (item) => {
2618
+ pendingFocus.current = indexOf(item);
2619
+ if (!controlled) {
2620
+ stop(item.id);
2621
+ setOwn((prev) => prev.filter((i) => i.id !== item.id));
2622
+ }
2623
+ onCancel?.(item);
2418
2624
  };
2625
+ const retry = (item) => {
2626
+ if (!controlled) patch(item.id, { status: "queued", progress: 0, error: void 0 });
2627
+ onRetry?.(item);
2628
+ };
2629
+ const remove = (item) => {
2630
+ pendingFocus.current = indexOf(item);
2631
+ reportChange?.();
2632
+ if (!controlled) {
2633
+ stop(item.id);
2634
+ setOwn((prev) => prev.filter((i) => i.id !== item.id));
2635
+ }
2636
+ onRemove?.(item);
2637
+ };
2638
+ const canCancel = !controlled || !!onCancel;
2639
+ const canRetry = !controlled || !!onRetry;
2640
+ const canRemove = !controlled || !!onRemove;
2419
2641
  return /* @__PURE__ */ jsxs("div", { className: cn("space-y-ds-3", className), children: [
2420
2642
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-ds-1.5", children: [
2421
2643
  showLabel && /* @__PURE__ */ jsx(Label, { htmlFor: inputId, children: label }),
@@ -2471,33 +2693,43 @@ function FileUpload({ onFiles, accept, multiple = true, maxSize, className, erro
2471
2693
  ),
2472
2694
  rejected && /* @__PURE__ */ jsx(InlineFieldError, { id: rejectedId, message: rejected, className: "mt-0" })
2473
2695
  ] }),
2474
- files.length > 0 && /* @__PURE__ */ jsx("ul", { className: "space-y-ds-2", children: files.map((entry) => /* @__PURE__ */ jsxs(
2475
- "li",
2476
- {
2477
- className: cn(
2478
- "flex items-center gap-ds-3 text-sm",
2479
- "border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
2480
- "rounded-[var(--ds-radius-md)] bg-[var(--ds-bg-surface)]"
2481
- ),
2482
- style: { padding: "var(--ds-space-3)" },
2483
- children: [
2484
- /* @__PURE__ */ jsx(Icon, { name: "folder", size: 16, className: "text-[var(--ds-text-muted)] shrink-0" }),
2485
- /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
2486
- /* @__PURE__ */ jsx(TruncFileName, { className: "text-[var(--ds-text-primary)]", children: entry.file.name }),
2487
- /* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--ds-text-muted)]", children: formatSize(entry.file.size) }),
2488
- entry.status === "uploading" && /* @__PURE__ */ jsx(Progress, { value: entry.progress, size: "sm", className: "mt-ds-1" })
2489
- ] }),
2490
- entry.status === "complete" && /* @__PURE__ */ jsx(Icon, { name: "check", size: 16, className: "text-[var(--ds-status-success)] shrink-0" }),
2491
- /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon-sm", onClick: () => removeFile(entry.id), "aria-label": "Remove file", children: /* @__PURE__ */ jsx(Icon, { name: "close", size: 14 }) })
2492
- ]
2493
- },
2494
- entry.id
2495
- )) })
2696
+ shown.length > 0 && /* @__PURE__ */ jsx("ul", { ref: listRef, className: "space-y-ds-2", children: shown.map((item) => {
2697
+ const words = item.status === "queued" ? "Waiting" : item.status === "uploading" ? `${Math.round(item.progress ?? 0)}%` : item.status === "done" ? "Uploaded" : item.status === "error" ? "Failed" : null;
2698
+ return /* @__PURE__ */ jsxs(
2699
+ "li",
2700
+ {
2701
+ "data-upload-status": item.status,
2702
+ className: cn(
2703
+ "flex items-center gap-ds-3 text-sm",
2704
+ "border-[length:var(--ds-border-width)]",
2705
+ item.status === "error" ? "border-[var(--ds-status-error)]" : "border-[var(--ds-border-subtle)]",
2706
+ "rounded-[var(--ds-radius-md)] bg-[var(--ds-bg-surface)]"
2707
+ ),
2708
+ style: { padding: "var(--ds-space-3)" },
2709
+ children: [
2710
+ /* @__PURE__ */ jsx(Icon, { name: "folder", size: 16, className: "text-[var(--ds-text-muted)] shrink-0" }),
2711
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
2712
+ /* @__PURE__ */ jsx(TruncFileName, { className: "text-[var(--ds-text-primary)]", children: item.name }),
2713
+ (item.size != null || words) && /* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--ds-text-muted)]", children: [item.size != null ? formatSize(item.size) : null, words].filter(Boolean).join(" \xB7 ") }),
2714
+ item.status === "uploading" && /* @__PURE__ */ jsx(Progress, { value: item.progress ?? 0, size: "sm", label: `Uploading ${item.name}`, className: "mt-ds-1" }),
2715
+ item.status === "error" && /* @__PURE__ */ jsx("p", { className: "mt-ds-1 text-xs text-[var(--ds-status-error)]", children: item.error ?? "The upload didn't work." })
2716
+ ] }),
2717
+ item.status === "done" && /* @__PURE__ */ jsx(Icon, { name: "check", size: 16, className: "text-[var(--ds-status-success)] shrink-0", "aria-hidden": "true" }),
2718
+ running(item) && canCancel && // the visible word starts the name, which says which file (WCAG 2.5.3)
2719
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: () => cancel(item), "aria-label": `Cancel ${item.name}`, children: "Cancel" }),
2720
+ item.status === "error" && canRetry && /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: () => retry(item), "aria-label": `Retry ${item.name}`, children: "Retry" }),
2721
+ !running(item) && canRemove && /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon-sm", onClick: () => remove(item), "aria-label": `Remove ${item.name}`, children: /* @__PURE__ */ jsx(Icon, { name: "close", size: 14 }) })
2722
+ ]
2723
+ },
2724
+ item.id
2725
+ );
2726
+ }) }),
2727
+ /* @__PURE__ */ jsx("p", { role: "status", className: "sr-only", children: heard })
2496
2728
  ] });
2497
2729
  }
2498
2730
  function SliderRange({
2499
2731
  value,
2500
- onChange,
2732
+ onChange: onChangeProp,
2501
2733
  min = 0,
2502
2734
  max = 100,
2503
2735
  step = 1,
@@ -2512,6 +2744,7 @@ function SliderRange({
2512
2744
  trackStyle,
2513
2745
  formatValue
2514
2746
  }) {
2747
+ const onChange = useReportedChange(onChangeProp, value);
2515
2748
  const field = useField();
2516
2749
  const ownId = useId();
2517
2750
  const showLabel = label != null && !field;
@@ -2717,7 +2950,7 @@ function parseTime(value) {
2717
2950
  }
2718
2951
  function TimePicker({
2719
2952
  value,
2720
- onChange,
2953
+ onChange: onChangeProp,
2721
2954
  use24Hour = false,
2722
2955
  minuteStep = 15,
2723
2956
  disabled,
@@ -2726,9 +2959,12 @@ function TimePicker({
2726
2959
  container,
2727
2960
  error = false,
2728
2961
  id,
2729
- size: sizeProp
2962
+ size: sizeProp,
2963
+ ...wiring
2730
2964
  }) {
2965
+ const onChange = useReportedChange(onChangeProp, value);
2731
2966
  const contextSize = useContext(ControlSizeContext);
2967
+ const control = useFieldControl({ ...wiring, id, error: error || void 0 }, { requiredInDescription: true });
2732
2968
  const size = sizeProp ?? contextSize ?? "md";
2733
2969
  const [open, setOpen] = useState(false);
2734
2970
  const triggerRef = useRef(null);
@@ -2912,15 +3148,18 @@ function TimePicker({
2912
3148
  return segs[k] === null ? spec.placeholder : pad2(segs[k]);
2913
3149
  };
2914
3150
  return /* @__PURE__ */ jsxs("div", { ref, className: cn(className), children: [
3151
+ control.requiredNote,
2915
3152
  /* @__PURE__ */ jsxs(
2916
3153
  "div",
2917
3154
  {
2918
3155
  ref: triggerRef,
2919
3156
  "data-time-field": "",
2920
3157
  role: "group",
2921
- id,
2922
- "aria-label": "Time",
2923
- "aria-invalid": error || void 0,
3158
+ id: control.id,
3159
+ "aria-label": control.labelId ? void 0 : "Time",
3160
+ "aria-labelledby": control.labelId,
3161
+ "aria-describedby": control.describedBy,
3162
+ "aria-invalid": control.invalid || void 0,
2924
3163
  onPaste: (e) => {
2925
3164
  const text = e.clipboardData.getData("text");
2926
3165
  if (text) {
@@ -2933,12 +3172,12 @@ function TimePicker({
2933
3172
  CONTROL_HEIGHT4[size],
2934
3173
  TEXT_SIZE4[size],
2935
3174
  "border-[length:var(--ds-border-width)]",
2936
- error ? "border-[var(--ds-status-error)]" : "border-[var(--ds-input-border)]",
3175
+ control.invalid ? "border-[var(--ds-status-error)]" : "border-[var(--ds-input-border)]",
2937
3176
  "bg-[var(--ds-input-bg)] rounded-[var(--ds-radius-md)]",
2938
3177
  "shadow-[var(--ds-input-shadow)]",
2939
3178
  "transition-colors",
2940
3179
  "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)]",
3180
+ !control.invalid && !disabled && !open && "hover:border-[var(--ds-input-border-hover)] focus-within:border-[var(--ds-input-border-hover)]",
2942
3181
  open && ACTIVE_RING,
2943
3182
  disabled && "opacity-disabled cursor-not-allowed",
2944
3183
  "text-[var(--ds-text-primary)]"
@@ -3019,9 +3258,10 @@ function TimePicker({
3019
3258
  id: listboxId,
3020
3259
  role: "dialog",
3021
3260
  "aria-label": "Choose a time",
3261
+ tabIndex: -1,
3022
3262
  onKeyDown: handlePanelKeyDown,
3023
3263
  className: cn(
3024
- "fixed z-[var(--ds-z-popover)]",
3264
+ "fixed z-[var(--ds-z-popover)] outline-none",
3025
3265
  "bg-[var(--ds-bg-surface-elevated)]",
3026
3266
  GLASS_DROPDOWN,
3027
3267
  "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
@@ -3101,7 +3341,7 @@ var sizes = {
3101
3341
  };
3102
3342
  function NumberInput({
3103
3343
  value,
3104
- onChange,
3344
+ onChange: onChangeProp,
3105
3345
  min = -Infinity,
3106
3346
  max = Infinity,
3107
3347
  step = 1,
@@ -3115,6 +3355,7 @@ function NumberInput({
3115
3355
  allowDecimals,
3116
3356
  id
3117
3357
  }) {
3358
+ const onChange = useReportedChange(onChangeProp, value);
3118
3359
  const contextSize = useContext(ControlSizeContext);
3119
3360
  const size = sizeProp ?? contextSize ?? "md";
3120
3361
  const intervalRef = useRef(void 0);
@@ -3133,13 +3374,13 @@ function NumberInput({
3133
3374
  [min, max]
3134
3375
  );
3135
3376
  const valueRef = useLatest(value);
3136
- const commitValue = useCallback((v) => {
3377
+ const commitValue = useCallback((v, own = false) => {
3137
3378
  const clamped = clamp(v);
3138
3379
  valueRef.current = clamped;
3139
3380
  setDraft(String(clamped));
3140
- onChange(clamped);
3381
+ (own ? onChangeProp : onChange)(clamped);
3141
3382
  return clamped;
3142
- }, [onChange, clamp, valueRef]);
3383
+ }, [onChange, onChangeProp, clamp, valueRef]);
3143
3384
  const increment = useCallback(() => commitValue(valueRef.current + step), [commitValue, step, valueRef]);
3144
3385
  const decrement = useCallback(() => commitValue(valueRef.current - step), [commitValue, step, valueRef]);
3145
3386
  const startRepeat = useCallback(
@@ -3181,7 +3422,7 @@ function NumberInput({
3181
3422
  setDraft(String(committed));
3182
3423
  } else {
3183
3424
  const fallback = isFinite(min) ? min : 0;
3184
- const committed = commitValue(value !== void 0 ? value : fallback);
3425
+ const committed = commitValue(value !== void 0 ? value : fallback, true);
3185
3426
  setDraft(String(committed));
3186
3427
  }
3187
3428
  }, [draft, commitValue, min, value]);
@@ -3422,7 +3663,7 @@ var Tag = forwardRef(function Tag2({
3422
3663
  });
3423
3664
  function TagInput({
3424
3665
  value,
3425
- onChange,
3666
+ onChange: onChangeProp,
3426
3667
  placeholder = "Add a tag...",
3427
3668
  maxTags = Infinity,
3428
3669
  disabled = false,
@@ -3431,6 +3672,7 @@ function TagInput({
3431
3672
  id,
3432
3673
  "aria-label": ariaLabel
3433
3674
  }) {
3675
+ const onChange = useReportedChange(onChangeProp, value);
3434
3676
  const [input, setInput] = useState("");
3435
3677
  const inputRef = useRef(null);
3436
3678
  const addTag = useCallback(
@@ -3556,7 +3798,7 @@ var sizes2 = {
3556
3798
  };
3557
3799
  function PinInput({
3558
3800
  value,
3559
- onChange,
3801
+ onChange: onChangeProp,
3560
3802
  length = 6,
3561
3803
  mask = false,
3562
3804
  disabled = false,
@@ -3565,6 +3807,7 @@ function PinInput({
3565
3807
  error = false,
3566
3808
  id
3567
3809
  }) {
3810
+ const onChange = useReportedChange(onChangeProp, value);
3568
3811
  const contextSize = useContext(ControlSizeContext);
3569
3812
  const size = sizeProp ?? contextSize ?? "md";
3570
3813
  const inputsRef = useRef([]);
@@ -3700,7 +3943,7 @@ function StarIcon({ filled, half, size }) {
3700
3943
  }
3701
3944
  function Rating({
3702
3945
  value,
3703
- onChange,
3946
+ onChange: onChangeProp,
3704
3947
  max = 5,
3705
3948
  allowHalf = false,
3706
3949
  readOnly = false,
@@ -3708,15 +3951,16 @@ function Rating({
3708
3951
  className,
3709
3952
  error = false
3710
3953
  }) {
3954
+ const onChange = useReportedChange(onChangeProp, value);
3711
3955
  const [hoverValue, setHoverValue] = useState(null);
3712
3956
  const displayValue = hoverValue ?? value;
3713
3957
  const handleClick = useCallback(
3714
3958
  (starIdx, isLeftHalf) => {
3715
- if (readOnly || !onChange) return;
3959
+ if (readOnly || !onChangeProp) return;
3716
3960
  const newValue = allowHalf && isLeftHalf ? starIdx + 0.5 : starIdx + 1;
3717
3961
  onChange(newValue === value ? 0 : newValue);
3718
3962
  },
3719
- [readOnly, onChange, allowHalf, value]
3963
+ [readOnly, onChangeProp, onChange, allowHalf, value]
3720
3964
  );
3721
3965
  const handleMouseMove = useCallback(
3722
3966
  (starIdx, e) => {
@@ -3730,7 +3974,7 @@ function Rating({
3730
3974
  const iconSize = iconSizes[size];
3731
3975
  const step = allowHalf ? 0.5 : 1;
3732
3976
  const handleKeyDown = (e) => {
3733
- if (readOnly || !onChange) return;
3977
+ if (readOnly || !onChangeProp) return;
3734
3978
  let next = null;
3735
3979
  if (e.key === "ArrowRight" || e.key === "ArrowUp") next = Math.min(max, value + step);
3736
3980
  else if (e.key === "ArrowLeft" || e.key === "ArrowDown") next = Math.max(0, value - step);
@@ -3854,13 +4098,14 @@ var DEFAULT_SWATCHES = [
3854
4098
  ];
3855
4099
  function ColorPicker({
3856
4100
  value,
3857
- onChange,
4101
+ onChange: onChangeProp,
3858
4102
  showAlpha = false,
3859
4103
  swatches = DEFAULT_SWATCHES,
3860
4104
  disabled = false,
3861
4105
  className,
3862
4106
  error = false
3863
4107
  }) {
4108
+ const onChange = useReportedChange(onChangeProp, value);
3864
4109
  const [hsl, setHsl] = useState(() => hexToHsl(value));
3865
4110
  const [hexInput, setHexInput] = useState(value);
3866
4111
  const satLightRef = useRef(null);
@@ -4101,6 +4346,7 @@ var Alert = forwardRef(function Alert2({
4101
4346
  trigger: /* @__PURE__ */ jsx(
4102
4347
  "button",
4103
4348
  {
4349
+ type: "button",
4104
4350
  className: cn(
4105
4351
  "shrink-0 text-xs font-medium px-ds-2.5 py-ds-1 rounded-[var(--ds-radius-sm)]",
4106
4352
  "transition-colors cursor-pointer",
@@ -4125,6 +4371,7 @@ var Alert = forwardRef(function Alert2({
4125
4371
  return /* @__PURE__ */ jsxs(
4126
4372
  "button",
4127
4373
  {
4374
+ type: "button",
4128
4375
  onClick: a.onClick,
4129
4376
  className: cn(
4130
4377
  "shrink-0 text-xs font-medium px-ds-2.5 py-ds-1 rounded-[var(--ds-radius-sm)]",
@@ -4145,6 +4392,7 @@ var Alert = forwardRef(function Alert2({
4145
4392
  /* @__PURE__ */ jsxs(
4146
4393
  "button",
4147
4394
  {
4395
+ type: "button",
4148
4396
  onClick: () => setDetailsExpanded(!detailsExpanded),
4149
4397
  "aria-expanded": detailsExpanded,
4150
4398
  className: cn(
@@ -4172,6 +4420,7 @@ var Alert = forwardRef(function Alert2({
4172
4420
  dismissible && /* @__PURE__ */ jsx(
4173
4421
  "button",
4174
4422
  {
4423
+ type: "button",
4175
4424
  onClick: onDismiss,
4176
4425
  className: "shrink-0 text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer",
4177
4426
  "aria-label": "Dismiss",
@@ -4182,6 +4431,12 @@ var Alert = forwardRef(function Alert2({
4182
4431
  }
4183
4432
  );
4184
4433
  });
4434
+ function controlsOf(form) {
4435
+ if (!form) return "";
4436
+ const controls = form.querySelectorAll("input, select, textarea, [role='combobox'], [role='radiogroup'], [role='checkbox'], [role='switch'], [role='slider'], [role='spinbutton'], [contenteditable='true']");
4437
+ return Array.from(controls).map((el) => el.id || el.getAttribute("name") || el.getAttribute("role") || el.tagName).join("|");
4438
+ }
4439
+ var isThenable = (value) => !!value && typeof value.then === "function";
4185
4440
  function stampFormId(node, formId) {
4186
4441
  return Children.map(node, (child) => {
4187
4442
  if (!isValidElement(child)) return child;
@@ -4189,23 +4444,21 @@ function stampFormId(node, formId) {
4189
4444
  return cloneElement(child, { form: formId });
4190
4445
  });
4191
4446
  }
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
4447
  function collectInvalidFields(form) {
4199
4448
  const out = [];
4200
4449
  for (const el of Array.from(form.querySelectorAll("[aria-invalid='true']"))) {
4201
4450
  const id = el.id || "";
4202
4451
  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";
4452
+ const described = (el.getAttribute("aria-describedby") ?? "").split(/\s+/).filter(Boolean).map((d) => document.getElementById(d)).filter((d) => !!d);
4453
+ 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
4454
  out.push({ id, label, message });
4206
4455
  }
4207
4456
  return out;
4208
4457
  }
4458
+ function focusTarget(el) {
4459
+ if (el.tabIndex >= 0 || /^(?:INPUT|SELECT|TEXTAREA|BUTTON)$/.test(el.tagName)) return el;
4460
+ return el.querySelector("[aria-checked='true'], [aria-selected='true']:not([tabindex='-1'])") ?? el.querySelector("input, select, textarea, button, [tabindex]:not([tabindex='-1'])") ?? el;
4461
+ }
4209
4462
  function FormWrapper({ onSubmit, validationMode: validationModeProp, children, actions: actionsProp, actionsAlign: actionsAlignProp, onCancel, cancelLabel = "Cancel", focusFirstError = true, errorSummary = true, className, ref }) {
4210
4463
  const preferences = useShellPreferences();
4211
4464
  const validationMode = validationModeProp ?? preferences.validationTiming ?? "onSubmit";
@@ -4240,27 +4493,65 @@ function FormWrapper({ onSubmit, validationMode: validationModeProp, children, a
4240
4493
  const pendingSubmit = useRef(false);
4241
4494
  const pendingFocus = useRef(false);
4242
4495
  const onSubmitRef = useLatest(onSubmit);
4496
+ const overlayMark = useContext(OverlayMarkCtx);
4497
+ const reportChange = overlayMark ? overlayMark.report : null;
4498
+ const [savedCheck, setSavedCheck] = useState(null);
4499
+ useEffect(() => {
4500
+ if (!savedCheck) return;
4501
+ const form = formRef.current;
4502
+ if (form && !form.querySelector("[aria-invalid='true']") && controlsOf(form) === savedCheck.controls) overlayMark?.clear();
4503
+ }, [savedCheck, overlayMark]);
4243
4504
  useEffect(() => {
4244
4505
  if (pendingSubmit.current && submitted) {
4245
4506
  pendingSubmit.current = false;
4246
- onSubmitRef.current(errorsRef.current);
4507
+ const form = formRef.current;
4508
+ const passed = Object.keys(errorsRef.current).length === 0 && !form?.querySelector("[aria-invalid='true']");
4509
+ const controls = controlsOf(form);
4510
+ const result = onSubmitRef.current(errorsRef.current);
4511
+ if (!passed || !overlayMark) return;
4512
+ if (isThenable(result)) {
4513
+ result.then(() => overlayMark.clear(), () => {
4514
+ });
4515
+ } else {
4516
+ setSavedCheck({ controls });
4517
+ }
4247
4518
  }
4248
- }, [submitted, submitCount, errors, errorsRef, onSubmitRef]);
4519
+ }, [submitted, submitCount, errors, errorsRef, onSubmitRef, overlayMark]);
4520
+ const submittedRef = useLatest(submitted);
4521
+ const errorSummaryRef = useLatest(errorSummary);
4522
+ const summaryRef = useRef(null);
4523
+ const pendingSummaryFocus = useRef(false);
4524
+ const focusSummary = useCallback(() => {
4525
+ const el = summaryRef.current;
4526
+ if (!el) return;
4527
+ if (typeof el.scrollIntoView === "function") el.scrollIntoView({ block: "center", behavior: "smooth" });
4528
+ el.focus({ preventScroll: true });
4529
+ }, []);
4249
4530
  const settle = useCallback(() => {
4250
4531
  const form = formRef.current;
4251
4532
  if (!form) return;
4252
4533
  const invalid = collectInvalidFields(form);
4253
- const next = invalid.length >= 2 ? invalid : [];
4534
+ const next = submittedRef.current && invalid.length >= 2 ? invalid : [];
4254
4535
  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
4536
  if (pendingFocus.current && invalid.length > 0) {
4256
4537
  pendingFocus.current = false;
4538
+ if (errorSummaryRef.current && next.length >= 2) {
4539
+ if (summaryRef.current) focusSummary();
4540
+ else pendingSummaryFocus.current = true;
4541
+ return;
4542
+ }
4257
4543
  const first = form.querySelector("[aria-invalid='true']");
4258
4544
  if (first) {
4259
4545
  if (typeof first.scrollIntoView === "function") first.scrollIntoView({ block: "center", behavior: "smooth" });
4260
- first.focus({ preventScroll: true });
4546
+ focusTarget(first).focus({ preventScroll: true });
4261
4547
  }
4262
4548
  }
4263
- }, []);
4549
+ }, [submittedRef, errorSummaryRef, focusSummary]);
4550
+ useLayoutEffect(() => {
4551
+ if (!pendingSummaryFocus.current || summary.length < 2) return;
4552
+ pendingSummaryFocus.current = false;
4553
+ focusSummary();
4554
+ }, [summary, focusSummary]);
4264
4555
  useLayoutEffect(() => {
4265
4556
  if (!submitted) return;
4266
4557
  settle();
@@ -4279,7 +4570,29 @@ function FormWrapper({ onSubmit, validationMode: validationModeProp, children, a
4279
4570
  pendingFocus.current = false;
4280
4571
  }, []);
4281
4572
  const setError = useCallback((field, message) => {
4282
- setErrors((prev) => ({ ...prev, [field]: message }));
4573
+ setErrors((prev) => {
4574
+ if (!message) {
4575
+ if (!(field in prev)) return prev;
4576
+ const next = { ...prev };
4577
+ delete next[field];
4578
+ return next;
4579
+ }
4580
+ return prev[field] === message ? prev : { ...prev, [field]: message };
4581
+ });
4582
+ }, []);
4583
+ const checks = useRef(/* @__PURE__ */ new Map());
4584
+ const register = useCallback((field, check) => {
4585
+ checks.current.set(field, check);
4586
+ return () => {
4587
+ if (checks.current.get(field) !== check) return;
4588
+ checks.current.delete(field);
4589
+ setErrors((prev) => {
4590
+ if (!(field in prev)) return prev;
4591
+ const next = { ...prev };
4592
+ delete next[field];
4593
+ return next;
4594
+ });
4595
+ };
4283
4596
  }, []);
4284
4597
  const clearErrors = useCallback(() => {
4285
4598
  setErrors({});
@@ -4289,6 +4602,18 @@ function FormWrapper({ onSubmit, validationMode: validationModeProp, children, a
4289
4602
  const handleSubmit = useCallback(
4290
4603
  (e) => {
4291
4604
  e.preventDefault();
4605
+ if (checks.current.size > 0) {
4606
+ const found = /* @__PURE__ */ new Map();
4607
+ for (const [field, check] of checks.current) found.set(field, check());
4608
+ setErrors((prev) => {
4609
+ const next = { ...prev };
4610
+ for (const [field, message] of found) {
4611
+ if (message) next[field] = message;
4612
+ else delete next[field];
4613
+ }
4614
+ return next;
4615
+ });
4616
+ }
4292
4617
  pendingSubmit.current = true;
4293
4618
  pendingFocus.current = focusFirstError;
4294
4619
  setSubmitted(true);
@@ -4301,7 +4626,7 @@ function FormWrapper({ onSubmit, validationMode: validationModeProp, children, a
4301
4626
  const el = id ? document.getElementById(id) : null;
4302
4627
  if (el) {
4303
4628
  el.scrollIntoView?.({ block: "center" });
4304
- el.focus();
4629
+ focusTarget(el).focus();
4305
4630
  }
4306
4631
  };
4307
4632
  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,22 +4635,67 @@ function FormWrapper({ onSubmit, validationMode: validationModeProp, children, a
4310
4635
  f.message
4311
4636
  ] }, f.id || f.label)) }) }) : null;
4312
4637
  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 }),
4638
+ return /* @__PURE__ */ jsx(FormCtx.Provider, { value: { errors, setError, clearErrors, validationMode, submitted, register }, children: /* @__PURE__ */ jsx(FormChangeCtx.Provider, { value: reportChange, 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: [
4639
+ 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
4640
  children,
4316
4641
  actionsNode
4317
- ] }) });
4642
+ ] }) }) });
4643
+ }
4644
+ var FOCUSABLE2 = "input:not([disabled]):not([type='hidden']), select:not([disabled]), textarea:not([disabled]), [role='combobox'], [role='switch'], [role='radio'], [tabindex]:not([tabindex='-1'])";
4645
+ function AddAnother({ items, getId, itemName, columns, renderRow, onAdd, onRemove, addLabel = "Add another", label, minItems = 1, maxItems, className }) {
4646
+ const listRef = useRef(null);
4647
+ const addRef = useRef(null);
4648
+ const pending = useRef(null);
4649
+ const firstFields = () => Array.from(listRef.current?.querySelectorAll(":scope > li") ?? []).map((row) => row.querySelector(FOCUSABLE2)).filter((el) => !!el);
4650
+ useEffect(() => {
4651
+ const next = pending.current;
4652
+ if (!next) return;
4653
+ pending.current = null;
4654
+ const fields = firstFields();
4655
+ if (next.kind === "add") fields[fields.length - 1]?.focus();
4656
+ else focusAfterRemoval(fields, next.index, addRef.current);
4657
+ }, [items]);
4658
+ const canRemove = items.length > minItems;
4659
+ const canAdd = maxItems === void 0 || items.length < maxItems;
4660
+ const grid = { "--gs-add-another-cols": `repeat(${columns.length}, minmax(0, 1fr)) max-content` };
4661
+ const body = /* @__PURE__ */ jsxs("div", { "data-add-another": "", className: cn("@container flex flex-col gap-ds-3", className), style: grid, children: [
4662
+ /* @__PURE__ */ jsxs("div", { "aria-hidden": "true", className: "hidden gap-ds-3 text-sm font-medium text-[var(--ds-text-secondary)] @min-[30rem]:grid @min-[30rem]:grid-cols-[var(--gs-add-another-cols)]", children: [
4663
+ columns.map((column) => /* @__PURE__ */ jsx("span", { children: column }, column)),
4664
+ canRemove && /* @__PURE__ */ jsx("span", { className: "invisible inline-flex items-center whitespace-nowrap px-ds-4 text-xs font-medium", children: "Remove" })
4665
+ ] }),
4666
+ /* @__PURE__ */ jsx("ul", { ref: listRef, className: "flex flex-col gap-ds-4 @min-[30rem]:gap-ds-3", children: items.map((item, index) => {
4667
+ const id = getId(item);
4668
+ return /* @__PURE__ */ jsxs("li", { "data-add-another-row": "", className: "grid grid-cols-1 items-start gap-ds-3 @min-[30rem]:grid-cols-[var(--gs-add-another-cols)]", children: [
4669
+ /* @__PURE__ */ jsx(AddAnotherRowCtx.Provider, { value: true, children: renderRow(item, index, (column) => `${column}, ${itemName(index)}`) }),
4670
+ canRemove && // the visible "Remove" starts its name, which says whose row it removes (WCAG 2.5.3 label in name)
4671
+ /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "sm", "aria-label": `Remove ${itemName(index)}`, className: "justify-self-start @min-[30rem]:mt-[calc((var(--ds-control-height-md)-var(--ds-control-height-sm))/2)]", onClick: () => {
4672
+ pending.current = { kind: "remove", index };
4673
+ onRemove(id);
4674
+ }, children: "Remove" })
4675
+ ] }, id);
4676
+ }) }),
4677
+ canAdd && /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Button, { ref: addRef, type: "button", variant: "secondary", onClick: () => {
4678
+ pending.current = { kind: "add" };
4679
+ onAdd();
4680
+ }, children: addLabel }) })
4681
+ ] });
4682
+ if (!label) return body;
4683
+ return /* @__PURE__ */ jsxs("fieldset", { className: "min-w-0", children: [
4684
+ /* @__PURE__ */ jsx("legend", { className: "mb-ds-2 text-sm font-medium text-[var(--ds-text-primary)]", children: label }),
4685
+ body
4686
+ ] });
4318
4687
  }
4319
4688
  function TransferList({
4320
4689
  available,
4321
4690
  selected,
4322
- onChange,
4691
+ onChange: onChangeProp,
4323
4692
  availableTitle = "Available",
4324
4693
  selectedTitle = "Selected",
4325
4694
  searchable = true,
4326
4695
  className,
4327
4696
  ref
4328
4697
  }) {
4698
+ const onChange = useReportedChange(onChangeProp, selected);
4329
4699
  const [checkedAvailable, setCheckedAvailable] = useState(/* @__PURE__ */ new Set());
4330
4700
  const [checkedSelected, setCheckedSelected] = useState(/* @__PURE__ */ new Set());
4331
4701
  const [searchLeft, setSearchLeft] = useState("");
@@ -4407,6 +4777,7 @@ function TransferList({
4407
4777
  /* @__PURE__ */ jsx(
4408
4778
  "button",
4409
4779
  {
4780
+ type: "button",
4410
4781
  onClick: moveAllRight,
4411
4782
  disabled: !available.filter((i) => !i.disabled).length,
4412
4783
  "aria-label": isStacked ? "Move all down" : "Move all right",
@@ -4417,6 +4788,7 @@ function TransferList({
4417
4788
  /* @__PURE__ */ jsx(
4418
4789
  "button",
4419
4790
  {
4791
+ type: "button",
4420
4792
  onClick: moveRight,
4421
4793
  disabled: !checkedAvailable.size,
4422
4794
  "aria-label": isStacked ? "Move selected down" : "Move selected right",
@@ -4427,6 +4799,7 @@ function TransferList({
4427
4799
  /* @__PURE__ */ jsx(
4428
4800
  "button",
4429
4801
  {
4802
+ type: "button",
4430
4803
  onClick: moveLeft,
4431
4804
  disabled: !checkedSelected.size,
4432
4805
  "aria-label": isStacked ? "Move selected up" : "Move selected left",
@@ -4437,6 +4810,7 @@ function TransferList({
4437
4810
  /* @__PURE__ */ jsx(
4438
4811
  "button",
4439
4812
  {
4813
+ type: "button",
4440
4814
  onClick: moveAllLeft,
4441
4815
  disabled: !selected.filter((i) => !i.disabled).length,
4442
4816
  "aria-label": isStacked ? "Move all up" : "Move all left",
@@ -4568,66 +4942,10 @@ function toInitials(value) {
4568
4942
  if (!/\s/.test(s) && Array.from(s).length <= 3) return s;
4569
4943
  return initialsOf(s);
4570
4944
  }
4571
- var variantColors = {
4572
- success: "bg-[var(--ds-status-success)]",
4573
- warning: "bg-[var(--ds-status-warning)]",
4574
- error: "bg-[var(--ds-status-error)]",
4575
- info: "bg-[var(--ds-accent-primary)]",
4576
- neutral: "bg-[var(--ds-text-muted)]",
4577
- offline: "bg-[var(--ds-text-muted)] opacity-disabled"
4578
- };
4579
- var pulseRingColors = {
4580
- success: "bg-[color-mix(in_oklch,var(--ds-status-success)_30%,transparent)]",
4581
- warning: "bg-[color-mix(in_oklch,var(--ds-status-warning)_30%,transparent)]",
4582
- error: "bg-[color-mix(in_oklch,var(--ds-status-error)_30%,transparent)]",
4583
- info: "bg-[var(--ds-accent-soft)]",
4584
- neutral: "bg-[color-mix(in_oklch,var(--ds-text-muted)_20%,transparent)]",
4585
- offline: "bg-[color-mix(in_oklch,var(--ds-text-muted)_10%,transparent)]"
4586
- };
4587
- var StatusDot = forwardRef(function StatusDot2({
4588
- variant = "neutral",
4589
- label,
4590
- pulse = false,
4591
- className
4592
- }, ref) {
4593
- return /* @__PURE__ */ jsxs(
4594
- "span",
4595
- {
4596
- ref,
4597
- className: cn("inline-flex items-center gap-ds-1.5", className),
4598
- role: "status",
4599
- "aria-label": label ?? variant,
4600
- children: [
4601
- /* @__PURE__ */ jsxs("span", { className: "relative inline-flex shrink-0", children: [
4602
- /* @__PURE__ */ jsx(
4603
- "span",
4604
- {
4605
- className: cn(
4606
- "rounded-[var(--ds-radius-full)] h-2.5 w-2.5",
4607
- variantColors[variant]
4608
- )
4609
- }
4610
- ),
4611
- pulse && variant !== "offline" && /* @__PURE__ */ jsx(
4612
- "span",
4613
- {
4614
- className: cn(
4615
- "absolute inset-0 rounded-[var(--ds-radius-full)]",
4616
- "animate-ping motion-reduce:animate-none",
4617
- pulseRingColors[variant]
4618
- )
4619
- }
4620
- )
4621
- ] }),
4622
- label && /* @__PURE__ */ jsx("span", { className: "font-normal text-[var(--ds-text-primary)] text-[length:var(--ds-font-size-xs)]", children: label })
4623
- ]
4624
- }
4625
- );
4626
- });
4627
- var sizeStyles2 = {
4628
- sm: "h-8 w-8 text-xs",
4629
- md: "h-10 w-10 text-sm",
4630
- lg: "h-12 w-12 text-base"
4945
+ var sizeStyles2 = {
4946
+ sm: "h-8 w-8 text-xs",
4947
+ md: "h-10 w-10 text-sm",
4948
+ lg: "h-12 w-12 text-base"
4631
4949
  };
4632
4950
  var paletteStyles = {
4633
4951
  1: "bg-[var(--ds-palette-1-bg,var(--ds-bg-muted))] text-[var(--ds-palette-1-text,var(--ds-text-secondary))]",
@@ -4646,13 +4964,23 @@ function colorStyles(color, seed) {
4646
4964
  if (color === "neutral") return "bg-[var(--ds-bg-muted)] text-[var(--ds-text-secondary)]";
4647
4965
  return paletteStyles[color === "auto" ? paletteFor(seed) : color];
4648
4966
  }
4649
- var statusVariantMap = {
4650
- online: "success",
4651
- away: "warning",
4652
- busy: "error",
4653
- offline: "offline"
4654
- };
4967
+ function PresenceMark({ status }) {
4968
+ return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 10 10", width: "10", height: "10", "aria-hidden": "true", focusable: "false", "data-presence": status, className: "block", children: [
4969
+ status === "online" && /* @__PURE__ */ jsx("circle", { cx: "5", cy: "5", r: "5", className: "fill-[var(--ds-status-success)]" }),
4970
+ status === "away" && /* @__PURE__ */ jsx("circle", { cx: "5", cy: "5", r: "4", fill: "none", strokeWidth: "2", className: "stroke-[var(--ds-status-warning)]" }),
4971
+ status === "busy" && /* @__PURE__ */ jsxs(Fragment, { children: [
4972
+ /* @__PURE__ */ jsx("circle", { cx: "5", cy: "5", r: "5", className: "fill-[var(--ds-status-error)]" }),
4973
+ /* @__PURE__ */ jsx("rect", { x: "2.5", y: "4.1", width: "5", height: "1.8", rx: "0.9", className: "fill-[var(--ds-bg-canvas)]" })
4974
+ ] }),
4975
+ status === "offline" && /* @__PURE__ */ jsxs(Fragment, { children: [
4976
+ /* @__PURE__ */ jsx("circle", { cx: "5", cy: "5", r: "4", fill: "none", strokeWidth: "2", className: "stroke-[var(--ds-text-muted)]" }),
4977
+ /* @__PURE__ */ jsx("line", { x1: "3.1", y1: "6.9", x2: "6.9", y2: "3.1", strokeWidth: "1.5", strokeLinecap: "round", className: "stroke-[var(--ds-text-muted)]" })
4978
+ ] })
4979
+ ] });
4980
+ }
4655
4981
  function Avatar({ initials, src, alt, size = "md", status, color = "auto", className }) {
4982
+ const who = alt ?? initials ?? "";
4983
+ const named = status ? [who, status].filter(Boolean).join(", ") : void 0;
4656
4984
  const base = cn(
4657
4985
  "inline-flex items-center justify-center font-medium shrink-0",
4658
4986
  "rounded-[var(--ds-radius-full)]",
@@ -4664,13 +4992,15 @@ function Avatar({ initials, src, alt, size = "md", status, color = "auto", class
4664
4992
  {
4665
4993
  "data-avatar": "",
4666
4994
  src,
4667
- alt: alt ?? initials ?? "",
4995
+ alt: named ?? alt ?? initials ?? "",
4668
4996
  className: cn(base, "object-cover")
4669
4997
  }
4670
4998
  ) : /* @__PURE__ */ jsx(
4671
4999
  "div",
4672
5000
  {
4673
5001
  "data-avatar": "",
5002
+ role: named ? "img" : void 0,
5003
+ "aria-label": named,
4674
5004
  className: cn(
4675
5005
  base,
4676
5006
  colorStyles(color, alt ?? initials ?? ""),
@@ -4692,20 +5022,14 @@ function Avatar({ initials, src, alt, size = "md", status, color = "auto", class
4692
5022
  size === "lg" && "bottom-[0px] right-[0px]"
4693
5023
  ),
4694
5024
  "aria-hidden": "true",
4695
- children: /* @__PURE__ */ jsx(
4696
- StatusDot,
4697
- {
4698
- variant: statusVariantMap[status],
4699
- className: "rounded-[var(--ds-radius-full)] shadow-[0_0_0_2px_var(--ds-bg-canvas)]"
4700
- }
4701
- )
5025
+ children: /* @__PURE__ */ jsx("span", { className: "block rounded-[var(--ds-radius-full)] bg-[var(--ds-bg-canvas)] shadow-[0_0_0_2px_var(--ds-bg-canvas)]", children: /* @__PURE__ */ jsx(PresenceMark, { status }) })
4702
5026
  }
4703
5027
  )
4704
5028
  ] });
4705
5029
  }
4706
5030
  function MentionInput({
4707
5031
  value: controlledValue,
4708
- onChange,
5032
+ onChange: onChangeProp,
4709
5033
  suggestions,
4710
5034
  trigger = "@",
4711
5035
  placeholder,
@@ -4714,6 +5038,7 @@ function MentionInput({
4714
5038
  }) {
4715
5039
  const [internalValue, setInternalValue] = useState("");
4716
5040
  const value = controlledValue ?? internalValue;
5041
+ const onChange = useReportedChange(onChangeProp, value);
4717
5042
  const [query, setQuery] = useState("");
4718
5043
  const [showPopup, setShowPopup] = useState(false);
4719
5044
  const [activeIndex, setActiveIndex] = useState(0);
@@ -4909,11 +5234,36 @@ function MentionInput({
4909
5234
  }
4910
5235
  var HEADER_LAYOUT = {
4911
5236
  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",
5237
+ 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",
5238
+ titleLeads: "grow basis-1/2 max-w-max break-words has-[[data-title-one-line]]:min-w-0",
5239
+ floorOff: "min-w-0",
4914
5240
  actions: "flex items-center gap-ds-2 shrink-0 ml-auto",
4915
5241
  below: "mt-ds-1.5"
4916
5242
  };
5243
+ function useTitleFloorFits(rowRef, active) {
5244
+ const [fits, setFits] = useState(true);
5245
+ useLayoutEffect(() => {
5246
+ const row = rowRef.current;
5247
+ if (!active || !row) return;
5248
+ const measure = () => {
5249
+ const title = row.querySelector("h1, h2, h3, h4, h5, h6");
5250
+ if (!title) return;
5251
+ const width = title.style.width;
5252
+ title.style.width = "min-content";
5253
+ const longest = title.getBoundingClientRect().width;
5254
+ title.style.width = width;
5255
+ if (longest === 0) return;
5256
+ const next = longest <= row.clientWidth + 0.5;
5257
+ setFits((was) => was === next ? was : next);
5258
+ };
5259
+ measure();
5260
+ if (typeof ResizeObserver === "undefined") return;
5261
+ const observer = new ResizeObserver(measure);
5262
+ observer.observe(row);
5263
+ return () => observer.disconnect();
5264
+ }, [rowRef, active]);
5265
+ return fits;
5266
+ }
4917
5267
  function actionsAreBadges(actions) {
4918
5268
  const items = Children.toArray(actions);
4919
5269
  return items.length > 0 && items.every((item) => isValidElement(item) && (item.type === Fragment$1 ? actionsAreBadges(item.props.children) : item.type === Badge));
@@ -5038,7 +5388,6 @@ var Card = forwardRef(function Card2({
5038
5388
  ...props
5039
5389
  }, ref) {
5040
5390
  const isSelectable = selectable || !!selectType;
5041
- const isToggle = selectType === "toggle";
5042
5391
  const hasSelectType = !!selectType;
5043
5392
  const isAction = !isSelectable && variant === "interactive" && !!onClick;
5044
5393
  const rootRef = useRef(null);
@@ -5092,7 +5441,6 @@ var Card = forwardRef(function Card2({
5092
5441
  const isButton = isSelectable || isAction && !holdsControls;
5093
5442
  const role = selectType === "radio" ? "radio" : selectType === "checkbox" ? "checkbox" : selectType === "toggle" ? "switch" : isButton ? "button" : void 0;
5094
5443
  const ariaAttr = selectType === "radio" || selectType === "checkbox" || selectType === "toggle" ? { "aria-checked": !!selected } : isSelectable ? { "aria-pressed": !!selected } : {};
5095
- const showRing = isSelectable && !isToggle && !hasSelectType && selected;
5096
5444
  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
5445
  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
5446
  return /* @__PURE__ */ jsxs(
@@ -5111,7 +5459,10 @@ var Card = forwardRef(function Card2({
5111
5459
  isSelectable && !disabled && "cursor-pointer select-none transition-all duration-[var(--ds-transition-speed)] ease-[var(--ds-transition-ease)]",
5112
5460
  isSelectable && !disabled && !selected && "hover:border-[var(--ds-accent-primary)]",
5113
5461
  isButton && "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
5114
- (showRing || hasSelectType && selected) && ACTIVE_RING,
5462
+ // Selected is the accent border, with the accent-subtle fill on a card with a selectType. No ring: a ring round the
5463
+ // whole card is the focus state (focus-visible:shadow-[var(--ds-shadow-focus)] above), and the active halo other
5464
+ // controls take (ACTIVE_RING, since Sprint 20.6) made a selected card look focused (the owner, 27 September 2026).
5465
+ isSelectable && selected && "border-[var(--ds-border-focus)]",
5115
5466
  hasSelectType && selected && "bg-[var(--ds-accent-subtle)]",
5116
5467
  disabled && "opacity-disabled cursor-not-allowed",
5117
5468
  className
@@ -5184,14 +5535,16 @@ var CardHeader = forwardRef(function CardHeader2({ className, children, actions,
5184
5535
  )
5185
5536
  ] }) : null;
5186
5537
  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;
5538
+ const parts = Children.toArray(children);
5539
+ const titleLeads = !!hasActions && !iconBlock && !menuItems && actionsAreBadges(actions) && parts.length > 0 && isValidElement(parts[0]) && parts[0].type === CardTitle;
5540
+ const leadRowRef = useRef(null);
5541
+ const leadFloorFits = useTitleFloorFits(leadRowRef, titleLeads);
5187
5542
  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
5543
  if (titleLeads) {
5191
5544
  const below = parts.slice(1);
5192
5545
  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] }),
5546
+ /* @__PURE__ */ jsxs("div", { ref: leadRowRef, className: HEADER_LAYOUT.row, children: [
5547
+ /* @__PURE__ */ jsx("div", { "data-card-title-row": "", className: cn(HEADER_LAYOUT.titleLeads, !leadFloorFits && HEADER_LAYOUT.floorOff), children: parts[0] }),
5195
5548
  actionsBlock
5196
5549
  ] }),
5197
5550
  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 +5586,8 @@ var CardTitle = forwardRef(function CardTitle2({ className, children, lines = 2,
5233
5586
  },
5234
5587
  id: id ?? generatedId,
5235
5588
  "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),
5589
+ ...lines === 1 ? { "data-title-one-line": "" } : {},
5590
+ className: cn(TITLE_TEXT, !action && lines > 0 && lineClampClass, action && lines === 1 && "text-ellipsis", className),
5237
5591
  ...props,
5238
5592
  children: action ? (
5239
5593
  // The card's action when the card holds controls of its own (Sprint 25.7): a real button, which a click
@@ -5401,6 +5755,58 @@ function DataList({ items, columns = 1, className }) {
5401
5755
  }
5402
5756
  );
5403
5757
  }
5758
+ var COUNT_FROM_PAGE = [
5759
+ "[&:has(>:nth-child(2):last-child)]:[--gs-cards:2]",
5760
+ "[&:has(>:nth-child(3):last-child)]:[--gs-cards:3]",
5761
+ "[&:has(>:nth-child(4):last-child)]:[--gs-cards:4]",
5762
+ "[&:has(>:nth-child(5):last-child)]:[--gs-cards:5]",
5763
+ "[&:has(>:nth-child(6):last-child)]:[--gs-cards:6]",
5764
+ "[&:has(>:nth-child(7):last-child)]:[--gs-cards:7]",
5765
+ "[&:has(>:nth-child(8):last-child)]:[--gs-cards:8]",
5766
+ "[&:has(>:nth-child(9):last-child)]:[--gs-cards:9]",
5767
+ "[&:has(>:nth-child(10):last-child)]:[--gs-cards:10]",
5768
+ "[&:has(>:nth-child(11):last-child)]:[--gs-cards:11]",
5769
+ "[&:has(>:nth-child(12))]:[--gs-cards:12]"
5770
+ ].join(" ");
5771
+ function countCards(children) {
5772
+ let n = 0;
5773
+ Children.forEach(children, (child) => {
5774
+ if (isValidElement(child) && child.type === Fragment$1) n += countCards(child.props.children);
5775
+ else if (child !== null && child !== void 0 && typeof child !== "boolean" && child !== "") n += 1;
5776
+ });
5777
+ return n;
5778
+ }
5779
+ function CardGrid({ minWidth = "14rem", maxColumns = 4, compare = false, gap = "var(--ds-space-4)", className, style, children, ref, ...props }) {
5780
+ const most = Math.max(1, Math.floor(maxColumns));
5781
+ const n = compare ? "var(--gs-cards, var(--gs-cards-counted))" : String(most);
5782
+ const share = `calc((100% - (${n} - 1) * var(--gs-card-gap)) / ${n})`;
5783
+ const column = compare ? `max(${share}, min(100%, calc((${n} * ${minWidth} + (${n} - 1) * var(--gs-card-gap) - 100%) * 999)))` : `max(min(${minWidth}, 100%), ${share})`;
5784
+ const template = {
5785
+ "--gs-card-gap": gap,
5786
+ ...compare ? { "--gs-cards-counted": Math.max(1, countCards(children)) } : {},
5787
+ gap: "var(--gs-card-gap)",
5788
+ gridTemplateColumns: `repeat(auto-fill, minmax(${column}, 1fr))`,
5789
+ // a comparison's cards share their rows: the headers, the contents and the footers line up across the row
5790
+ ...compare ? { gridAutoRows: "auto 1fr auto" } : {}
5791
+ };
5792
+ return /* @__PURE__ */ jsx(
5793
+ "div",
5794
+ {
5795
+ ref,
5796
+ "data-card-grid": compare ? "compare" : "",
5797
+ className: cn(
5798
+ "grid",
5799
+ compare && COUNT_FROM_PAGE,
5800
+ // each card spans its three rows and lays its parts in them; anything else just fills them
5801
+ compare && "[&>*]:row-span-3 [&>[data-card]]:grid [&>[data-card]]:grid-rows-subgrid [&>[data-card]]:gap-y-0",
5802
+ className
5803
+ ),
5804
+ style: { ...template, ...style },
5805
+ ...props,
5806
+ children
5807
+ }
5808
+ );
5809
+ }
5404
5810
  var sizeStyles3 = {
5405
5811
  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
5812
  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 +5936,7 @@ var DescriptionList = forwardRef(function DescriptionList2({
5530
5936
  ]
5531
5937
  }
5532
5938
  ),
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 })
5939
+ /* @__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
5940
  ]
5535
5941
  },
5536
5942
  i
@@ -5538,6 +5944,57 @@ var DescriptionList = forwardRef(function DescriptionList2({
5538
5944
  }
5539
5945
  );
5540
5946
  });
5947
+ var variantColors = {
5948
+ success: "bg-[var(--ds-status-success)]",
5949
+ warning: "bg-[var(--ds-status-warning)]",
5950
+ error: "bg-[var(--ds-status-error)]",
5951
+ info: "bg-[var(--ds-accent-primary)]",
5952
+ neutral: "bg-[var(--ds-text-muted)]",
5953
+ offline: "bg-[var(--ds-text-muted)] opacity-disabled"
5954
+ };
5955
+ var pulseRingColors = {
5956
+ success: "bg-[color-mix(in_oklch,var(--ds-status-success)_30%,transparent)]",
5957
+ warning: "bg-[color-mix(in_oklch,var(--ds-status-warning)_30%,transparent)]",
5958
+ error: "bg-[color-mix(in_oklch,var(--ds-status-error)_30%,transparent)]",
5959
+ info: "bg-[var(--ds-accent-soft)]",
5960
+ neutral: "bg-[color-mix(in_oklch,var(--ds-text-muted)_20%,transparent)]",
5961
+ offline: "bg-[color-mix(in_oklch,var(--ds-text-muted)_10%,transparent)]"
5962
+ };
5963
+ var StatusDot = forwardRef(function StatusDot2({
5964
+ variant = "neutral",
5965
+ label,
5966
+ pulse = false,
5967
+ className
5968
+ }, ref) {
5969
+ return (
5970
+ // No live region (A48): the dot is read as its words where it sits; telling the user about a change as it happens
5971
+ // is the toasts' job (WCAG 4.1.3). The dot itself is hidden from assistive technology.
5972
+ /* @__PURE__ */ jsxs("span", { ref, "data-status-dot": variant, className: cn("inline-flex items-center gap-ds-1.5", className), children: [
5973
+ /* @__PURE__ */ jsxs("span", { "aria-hidden": "true", className: "relative inline-flex shrink-0", children: [
5974
+ /* @__PURE__ */ jsx(
5975
+ "span",
5976
+ {
5977
+ className: cn(
5978
+ "rounded-[var(--ds-radius-full)] h-2.5 w-2.5",
5979
+ variantColors[variant]
5980
+ )
5981
+ }
5982
+ ),
5983
+ pulse && variant !== "offline" && /* @__PURE__ */ jsx(
5984
+ "span",
5985
+ {
5986
+ className: cn(
5987
+ "absolute inset-0 rounded-[var(--ds-radius-full)]",
5988
+ "animate-ping motion-reduce:animate-none",
5989
+ pulseRingColors[variant]
5990
+ )
5991
+ }
5992
+ )
5993
+ ] }),
5994
+ /* @__PURE__ */ jsx("span", { className: "font-normal text-[var(--ds-text-primary)] text-[length:var(--ds-font-size-xs)]", children: label })
5995
+ ] })
5996
+ );
5997
+ });
5541
5998
  function Accordion({ items, type = "single", defaultOpen = [], open: controlledOpen, onOpenChange, className }) {
5542
5999
  const [internalOpen, setInternalOpen] = useState(new Set(defaultOpen));
5543
6000
  const baseId = useId();
@@ -5572,6 +6029,7 @@ function Accordion({ items, type = "single", defaultOpen = [], open: controlledO
5572
6029
  /* @__PURE__ */ jsxs(
5573
6030
  "button",
5574
6031
  {
6032
+ type: "button",
5575
6033
  id: triggerId,
5576
6034
  "aria-expanded": isOpen,
5577
6035
  "aria-controls": panelId,
@@ -5692,7 +6150,7 @@ function TruncCell({ children, lines }) {
5692
6150
  if (!isTruncated) return cell;
5693
6151
  return /* @__PURE__ */ jsx(Tooltip, { content: children, children: cell });
5694
6152
  }
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";
6153
+ var TITLE_CLASSES = TITLE_TEXT;
5696
6154
  var FRAME_CLASSES = cn(
5697
6155
  "border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
5698
6156
  "rounded-[var(--ds-radius-md)]",
@@ -5702,16 +6160,19 @@ var FRAME_CLASSES = cn(
5702
6160
  function TableHeading({ title, description, actions, titleId, descriptionId, inset }) {
5703
6161
  const heading = /* @__PURE__ */ jsx("h3", { id: titleId, className: TITLE_CLASSES, children: title });
5704
6162
  const actionsBlock = actions ? /* @__PURE__ */ jsx("div", { className: HEADER_LAYOUT.actions, children: actions }) : null;
6163
+ const titleLeads = !actions || actionsAreBadges(actions);
6164
+ const leadRowRef = useRef(null);
6165
+ const leadFloorFits = useTitleFloorFits(leadRowRef, titleLeads);
5705
6166
  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: [
6167
+ 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
6168
  /* @__PURE__ */ jsxs("div", { className: HEADER_LAYOUT.block, children: [
5708
6169
  heading,
5709
6170
  descriptionText()
5710
6171
  ] }),
5711
6172
  actionsBlock
5712
6173
  ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
5713
- /* @__PURE__ */ jsxs("div", { className: HEADER_LAYOUT.row, children: [
5714
- /* @__PURE__ */ jsx("div", { className: HEADER_LAYOUT.titleLeads, children: heading }),
6174
+ /* @__PURE__ */ jsxs("div", { ref: leadRowRef, className: HEADER_LAYOUT.row, children: [
6175
+ /* @__PURE__ */ jsx("div", { className: cn(HEADER_LAYOUT.titleLeads, !leadFloorFits && HEADER_LAYOUT.floorOff), children: heading }),
5715
6176
  actionsBlock
5716
6177
  ] }),
5717
6178
  descriptionText(HEADER_LAYOUT.below)
@@ -5954,6 +6415,7 @@ function Table({
5954
6415
  expandable && /* @__PURE__ */ jsx("td", { className: cn("w-8", density === "compact" ? "px-ds-1 py-ds-2" : "px-ds-2 py-ds-3"), children: /* @__PURE__ */ jsx(
5955
6416
  "button",
5956
6417
  {
6418
+ type: "button",
5957
6419
  onClick: () => toggleExpand(i),
5958
6420
  className: "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] cursor-pointer",
5959
6421
  "aria-expanded": isExpanded,
@@ -6070,7 +6532,7 @@ var indicatorInset = {
6070
6532
  };
6071
6533
  function SegmentedControl({
6072
6534
  value,
6073
- onChange,
6535
+ onChange: onChangeProp,
6074
6536
  options,
6075
6537
  size: sizeProp,
6076
6538
  variant = "default",
@@ -6078,9 +6540,13 @@ function SegmentedControl({
6078
6540
  onHoverChange,
6079
6541
  className,
6080
6542
  "aria-label": ariaLabel,
6081
- maxOptions = 6
6543
+ maxOptions = 6,
6544
+ ...wiring
6082
6545
  }) {
6546
+ const onChange = useReportedChange(onChangeProp, value);
6083
6547
  const contextSize = useContext(ControlSizeContext);
6548
+ const control = useFieldControl(wiring);
6549
+ const named = !ariaLabel && control.labelId ? { "aria-labelledby": control.labelId } : {};
6084
6550
  const size = sizeProp ?? contextSize ?? "md";
6085
6551
  const containerRef = useRef(null);
6086
6552
  const [indicator, setIndicator] = useState({ left: 0, width: 0 });
@@ -6176,6 +6642,7 @@ function SegmentedControl({
6176
6642
  Dropdown,
6177
6643
  {
6178
6644
  "aria-label": ariaLabel,
6645
+ ...wiring,
6179
6646
  size: size === "md" || size === "lg" ? "md" : "sm",
6180
6647
  options: options.filter((o) => !o.disabled).map((o) => ({ id: o.value, label: o.label })),
6181
6648
  value,
@@ -6189,7 +6656,12 @@ function SegmentedControl({
6189
6656
  {
6190
6657
  ref: containerRef,
6191
6658
  role: "radiogroup",
6659
+ id: control.id,
6192
6660
  "aria-label": ariaLabel,
6661
+ ...named,
6662
+ "aria-describedby": control.describedBy,
6663
+ "aria-invalid": control.invalid || void 0,
6664
+ "aria-required": control.required || void 0,
6193
6665
  onKeyDown: handleKeyDown,
6194
6666
  className: cn(
6195
6667
  "relative items-center",
@@ -6199,6 +6671,7 @@ function SegmentedControl({
6199
6671
  "bg-[var(--ds-bg-muted)]",
6200
6672
  "rounded-[var(--ds-radius-lg)]",
6201
6673
  variant === "default" && "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
6674
+ control.invalid && "border-[length:var(--ds-border-width)] border-[var(--ds-status-error)]",
6202
6675
  className
6203
6676
  ),
6204
6677
  children: [
@@ -6709,7 +7182,7 @@ function WeekView({
6709
7182
  return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "flex flex-col h-full overflow-hidden", children: [
6710
7183
  /* @__PURE__ */ jsxs("div", { className: "flex border-b-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)]", children: [
6711
7184
  /* @__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(
7185
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-x-auto gs-scrollbar-none", "data-week-scroll": true, children: /* @__PURE__ */ jsx(
6713
7186
  "div",
6714
7187
  {
6715
7188
  className: "grid",
@@ -6749,7 +7222,7 @@ function WeekView({
6749
7222
  ] }),
6750
7223
  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
7224
  /* @__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(
7225
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-x-auto gs-scrollbar-none", "data-week-scroll": true, children: /* @__PURE__ */ jsx(
6753
7226
  "div",
6754
7227
  {
6755
7228
  className: "grid",
@@ -6786,7 +7259,7 @@ function WeekView({
6786
7259
  },
6787
7260
  h
6788
7261
  )) }),
6789
- /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-x-auto scrollbar-none", "data-week-scroll": true, children: /* @__PURE__ */ jsx(
7262
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-x-auto gs-scrollbar-none", "data-week-scroll": true, children: /* @__PURE__ */ jsx(
6790
7263
  "div",
6791
7264
  {
6792
7265
  className: "relative",
@@ -7799,7 +8272,7 @@ function AvatarGroup({ items, max = 4, size = "sm", ring = "canvas", onOverflowC
7799
8272
  const shown = items.slice(0, Math.max(0, max));
7800
8273
  const hidden = items.slice(shown.length);
7801
8274
  const ringClass = cn(
7802
- "rounded-[var(--ds-radius-full)] ring-2",
8275
+ "relative rounded-[var(--ds-radius-full)] ring-2",
7803
8276
  ring === "canvas" ? "ring-[var(--ds-bg-canvas)]" : "ring-[var(--ds-bg-surface)]"
7804
8277
  );
7805
8278
  const hiddenNames = hidden.map((i) => i.name ?? i.initials).filter(Boolean).join(", ");
@@ -8099,11 +8572,38 @@ function HorizontalLayout({ items, minItemWidth, className }) {
8099
8572
  /* @__PURE__ */ jsxs("div", { className: "text-center mt-ds-2 px-ds-3", children: [
8100
8573
  /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-[var(--ds-text-primary)]", children: item.title }),
8101
8574
  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 })
8575
+ /* @__PURE__ */ jsx(ItemTime, { item })
8103
8576
  ] })
8104
8577
  ] }, i);
8105
8578
  }) });
8106
8579
  }
8580
+ var EXACT = new Intl.DateTimeFormat(void 0, { dateStyle: "medium", timeStyle: "short" });
8581
+ var EXACT_DAY = new Intl.DateTimeFormat(void 0, { dateStyle: "medium" });
8582
+ var DATE_ONLY = /^(\d{4})-(\d{2})-(\d{2})$/;
8583
+ function exactTime(value) {
8584
+ if (typeof value === "string") {
8585
+ const day = DATE_ONLY.exec(value.trim());
8586
+ if (day) {
8587
+ const [y, m, d] = [Number(day[1]), Number(day[2]) - 1, Number(day[3])];
8588
+ const date2 = new Date(y, m, d);
8589
+ return date2.getFullYear() === y && date2.getMonth() === m && date2.getDate() === d ? { iso: value.trim(), text: EXACT_DAY.format(date2) } : null;
8590
+ }
8591
+ }
8592
+ const date = value instanceof Date ? value : new Date(value);
8593
+ return Number.isNaN(date.getTime()) ? null : { iso: date.toISOString(), text: EXACT.format(date) };
8594
+ }
8595
+ function ItemTime({ item }) {
8596
+ const exact = item.dateTime ? exactTime(item.dateTime) : null;
8597
+ if (!item.timestamp && !exact) return null;
8598
+ 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: [
8599
+ item.timestamp ?? exact.text,
8600
+ item.timestamp && /* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
8601
+ " (",
8602
+ exact.text,
8603
+ ")"
8604
+ ] })
8605
+ ] }) : item.timestamp });
8606
+ }
8107
8607
  function VerticalLayout({ items, className }) {
8108
8608
  return /* @__PURE__ */ jsx("div", { className: cn("relative", className), children: items.map((item, i) => /* @__PURE__ */ jsxs("div", { className: "flex gap-ds-4", children: [
8109
8609
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center", children: [
@@ -8113,7 +8613,7 @@ function VerticalLayout({ items, className }) {
8113
8613
  /* @__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
8614
  /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-[var(--ds-text-primary)]", children: item.title }),
8115
8615
  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 })
8616
+ /* @__PURE__ */ jsx(ItemTime, { item })
8117
8617
  ] })
8118
8618
  ] }, i)) });
8119
8619
  }
@@ -8585,6 +9085,7 @@ function Carousel({
8585
9085
  /* @__PURE__ */ jsx(
8586
9086
  "button",
8587
9087
  {
9088
+ type: "button",
8588
9089
  onClick: prev,
8589
9090
  disabled: !canPrev,
8590
9091
  "aria-label": "Previous slide",
@@ -8604,6 +9105,7 @@ function Carousel({
8604
9105
  /* @__PURE__ */ jsx(
8605
9106
  "button",
8606
9107
  {
9108
+ type: "button",
8607
9109
  onClick: next,
8608
9110
  disabled: !canNext,
8609
9111
  "aria-label": "Next slide",
@@ -8624,6 +9126,7 @@ function Carousel({
8624
9126
  showIndicators && count > 1 && /* @__PURE__ */ jsx("div", { className: "absolute bottom-ds-3 left-1/2 -translate-x-1/2 flex gap-ds-1.5 z-10", children: children.map((_, i) => /* @__PURE__ */ jsx(
8625
9127
  "button",
8626
9128
  {
9129
+ type: "button",
8627
9130
  onClick: () => goTo(i),
8628
9131
  "aria-label": `Go to slide ${i + 1}`,
8629
9132
  className: cn(
@@ -9496,7 +9999,7 @@ var StackedList = forwardRef(function StackedList2({
9496
9999
  });
9497
10000
  var TIME_ZOOMS = ["day", "week", "month", "quarter"];
9498
10001
  var DAY_MS = 864e5;
9499
- var DATE_ONLY = /^(\d{4})-(\d{2})-(\d{2})$/;
10002
+ var DATE_ONLY2 = /^(\d{4})-(\d{2})-(\d{2})$/;
9500
10003
  var utcDay = (y, m, d) => Math.round(Date.UTC(y, m, d) / DAY_MS);
9501
10004
  function dayOfDate(d) {
9502
10005
  const t = d.getTime();
@@ -9512,7 +10015,7 @@ function dayOf(value) {
9512
10015
  if (typeof value === "number") return Number.isFinite(value) ? value : NaN;
9513
10016
  if (value instanceof Date) return dayOfDate(value);
9514
10017
  if (typeof value !== "string") return NaN;
9515
- const m = DATE_ONLY.exec(value.trim());
10018
+ const m = DATE_ONLY2.exec(value.trim());
9516
10019
  if (!m) return dayOfDate(new Date(value));
9517
10020
  const [y, mo, d] = [Number(m[1]), Number(m[2]) - 1, Number(m[3])];
9518
10021
  const day = utcDay(y, mo, d);
@@ -9525,7 +10028,7 @@ function dateOfDay(day) {
9525
10028
  return new Date(u.getUTCFullYear(), u.getUTCMonth(), u.getUTCDate(), 0, 0, 0, Math.round((day - whole) * DAY_MS));
9526
10029
  }
9527
10030
  function isWholeDay(value) {
9528
- return typeof value === "string" ? DATE_ONLY.test(value.trim()) : Number.isInteger(dayOf(value));
10031
+ return typeof value === "string" ? DATE_ONLY2.test(value.trim()) : Number.isInteger(dayOf(value));
9529
10032
  }
9530
10033
  function endDayOf(value) {
9531
10034
  const day = dayOf(value);
@@ -11372,6 +11875,7 @@ function TabButton({
11372
11875
  const btn2 = /* @__PURE__ */ jsxs(
11373
11876
  "button",
11374
11877
  {
11878
+ type: "button",
11375
11879
  id: tabId,
11376
11880
  role: "tab",
11377
11881
  "aria-selected": isActive,
@@ -11407,6 +11911,7 @@ function TabButton({
11407
11911
  const btn = /* @__PURE__ */ jsx(
11408
11912
  "button",
11409
11913
  {
11914
+ type: "button",
11410
11915
  id: tabId,
11411
11916
  role: "tab",
11412
11917
  "aria-selected": isActive,
@@ -11780,6 +12285,7 @@ var Breadcrumbs = forwardRef(function Breadcrumbs2({
11780
12285
  /* @__PURE__ */ jsx(
11781
12286
  "button",
11782
12287
  {
12288
+ type: "button",
11783
12289
  ref: ellipsisBtnRef,
11784
12290
  onClick: () => {
11785
12291
  if (!dropdownOpen) setActiveIdx(0);
@@ -11830,6 +12336,7 @@ var Breadcrumbs = forwardRef(function Breadcrumbs2({
11830
12336
  children: collapsedItems.map((ci, idx) => /* @__PURE__ */ jsxs(
11831
12337
  "button",
11832
12338
  {
12339
+ type: "button",
11833
12340
  "data-index": idx,
11834
12341
  role: "menuitem",
11835
12342
  tabIndex: idx === activeIdx ? 0 : -1,
@@ -12025,6 +12532,7 @@ function PageNumbers({ currentPage, totalPages, onPageChange, pageSize: pageSize
12025
12532
  /* @__PURE__ */ jsx(
12026
12533
  "button",
12027
12534
  {
12535
+ type: "button",
12028
12536
  onClick: () => onPageChange(currentPage - 1),
12029
12537
  disabled: currentPage <= 1,
12030
12538
  "aria-label": "Previous page",
@@ -12044,6 +12552,7 @@ function PageNumbers({ currentPage, totalPages, onPageChange, pageSize: pageSize
12044
12552
  (p, i) => p === "..." ? /* @__PURE__ */ jsx(EllipsisJumper, { totalPages, onPageChange }, `ellipsis-${i}`) : /* @__PURE__ */ jsx(
12045
12553
  "button",
12046
12554
  {
12555
+ type: "button",
12047
12556
  onClick: () => onPageChange(p),
12048
12557
  "aria-current": p === currentPage ? "page" : void 0,
12049
12558
  className: cn(
@@ -12061,6 +12570,7 @@ function PageNumbers({ currentPage, totalPages, onPageChange, pageSize: pageSize
12061
12570
  /* @__PURE__ */ jsx(
12062
12571
  "button",
12063
12572
  {
12573
+ type: "button",
12064
12574
  onClick: () => onPageChange(currentPage + 1),
12065
12575
  disabled: currentPage >= totalPages,
12066
12576
  "aria-label": "Next page",
@@ -12323,6 +12833,7 @@ function StepCircle({ status, index, onClick, clickable }) {
12323
12833
  return /* @__PURE__ */ jsx(
12324
12834
  "button",
12325
12835
  {
12836
+ type: "button",
12326
12837
  onClick,
12327
12838
  disabled: !clickable,
12328
12839
  "aria-current": status === "current" ? "step" : void 0,
@@ -12517,7 +13028,7 @@ var Sidebar = forwardRef(function Sidebar2({
12517
13028
  position = "flush",
12518
13029
  navbarFloating: navbarFloatingProp,
12519
13030
  navSize = "md",
12520
- fill = false,
13031
+ fill: fill2 = false,
12521
13032
  className
12522
13033
  }, ref) {
12523
13034
  const [internalCollapsed, setInternalCollapsed] = useState(defaultCollapsed ?? false);
@@ -12527,10 +13038,10 @@ var Sidebar = forwardRef(function Sidebar2({
12527
13038
  const appShell = useAppShell();
12528
13039
  const navbarFloating = navbarFloatingProp ?? appShell?.navbarFloating;
12529
13040
  useLayoutEffect(() => {
12530
- if (fill || !appShell) return;
13041
+ if (fill2 || !appShell) return;
12531
13042
  return appShell.registerChrome("sidebar", position);
12532
- }, [appShell?.registerChrome, fill, position]);
12533
- const usesShellState = appShell !== null && !isControlled && defaultCollapsed === void 0 && !fill && type === "traditional";
13043
+ }, [appShell?.registerChrome, fill2, position]);
13044
+ const usesShellState = appShell !== null && !isControlled && defaultCollapsed === void 0 && !fill2 && type === "traditional";
12534
13045
  const uncontrolledCollapsed = usesShellState ? appShell.sidebarCollapsed : internalCollapsed;
12535
13046
  const collapsed = mini || (isControlled ? controlledCollapsed : uncontrolledCollapsed);
12536
13047
  const handleCollapsedChange = (value) => {
@@ -12549,11 +13060,11 @@ var Sidebar = forwardRef(function Sidebar2({
12549
13060
  effectiveDarkMode = false;
12550
13061
  }
12551
13062
  const isDark = effectiveDarkMode ?? variant === "dark";
12552
- const showCollapse = type === "traditional" && !mini && !fill;
13063
+ const showCollapse = type === "traditional" && !mini && !fill2;
12553
13064
  const isMinimal = type === "minimal";
12554
13065
  const isCompact = type === "compact";
12555
- const isFloating = position === "floating" && !fill;
12556
- const sidebarWidth = fill ? "100%" : isCompact ? "var(--ds-sidebar-compact-width)" : collapsed ? "var(--ds-sidebar-collapsed-width)" : "var(--ds-sidebar-width)";
13066
+ const isFloating = position === "floating" && !fill2;
13067
+ const sidebarWidth = fill2 ? "100%" : isCompact ? "var(--ds-sidebar-compact-width)" : collapsed ? "var(--ds-sidebar-collapsed-width)" : "var(--ds-sidebar-width)";
12557
13068
  const aside = /* @__PURE__ */ jsxs(
12558
13069
  "aside",
12559
13070
  {
@@ -12591,6 +13102,7 @@ var Sidebar = forwardRef(function Sidebar2({
12591
13102
  const collapseButton = /* @__PURE__ */ jsxs(
12592
13103
  "button",
12593
13104
  {
13105
+ type: "button",
12594
13106
  onClick: () => handleCollapsedChange(!collapsed),
12595
13107
  "aria-expanded": !collapsed,
12596
13108
  "aria-label": collapsed ? "Expand sidebar" : "Collapse sidebar",
@@ -12943,6 +13455,7 @@ function SidebarNav({
12943
13455
  /* @__PURE__ */ jsxs(
12944
13456
  "button",
12945
13457
  {
13458
+ type: "button",
12946
13459
  onClick: () => {
12947
13460
  toggleExpand(key);
12948
13461
  onItemClick?.(item, index);
@@ -13077,6 +13590,7 @@ function SidebarNav({
13077
13590
  collapsible && /* @__PURE__ */ jsxs(
13078
13591
  "button",
13079
13592
  {
13593
+ type: "button",
13080
13594
  onClick: () => onCollapsedChange(!collapsed),
13081
13595
  title: collapsed ? "Expand sidebar" : "Collapse sidebar",
13082
13596
  className: cn(
@@ -13143,6 +13657,7 @@ function CompactNavItem({
13143
13657
  /* @__PURE__ */ jsxs(
13144
13658
  "button",
13145
13659
  {
13660
+ type: "button",
13146
13661
  ref: setTriggerEl,
13147
13662
  onMouseEnter: !mobile && hasChildren ? handleMouseEnter : void 0,
13148
13663
  onMouseLeave: !mobile && hasChildren && isOpen ? handleMouseLeave : void 0,
@@ -13205,6 +13720,7 @@ function CompactNavItem({
13205
13720
  mobile && expanded && hasChildren && item.children && /* @__PURE__ */ jsx("div", { className: "w-full flex flex-col gap-ds-1 pb-ds-1", children: item.children.map((child, ci) => /* @__PURE__ */ jsxs(
13206
13721
  "button",
13207
13722
  {
13723
+ type: "button",
13208
13724
  onClick: () => onItemClick?.(child, ci),
13209
13725
  className: cn(
13210
13726
  "flex items-center gap-ds-2 px-ds-3 py-ds-1.5 text-[length:var(--ds-font-size-xs)] rounded-[var(--ds-radius-sm)] transition-colors cursor-pointer w-full",
@@ -13219,7 +13735,473 @@ function CompactNavItem({
13219
13735
  )) })
13220
13736
  ] });
13221
13737
  }
13738
+ var variantStyles4 = {
13739
+ default: "border-[var(--ds-border-default)] bg-[var(--ds-bg-surface)]",
13740
+ success: "border-[var(--ds-status-success)] bg-[var(--ds-status-success-bg)]",
13741
+ warning: "border-[var(--ds-status-warning)] bg-[var(--ds-status-warning-bg)]",
13742
+ error: "border-[var(--ds-status-error)] bg-[var(--ds-status-error-bg)]"
13743
+ };
13744
+ var variantIcons = {
13745
+ default: { name: "info", className: "text-[var(--ds-text-muted)]" },
13746
+ success: { name: "check", className: "text-[var(--ds-status-success)]" },
13747
+ warning: { name: "warning", className: "text-[var(--ds-status-warning)]" },
13748
+ error: { name: "error", className: "text-[var(--ds-status-error)]" }
13749
+ };
13750
+ var TOAST_POSITION_CLASSES = {
13751
+ "top-right": "top-[var(--ds-toast-inset-top)] right-[var(--ds-toast-inset-right)]",
13752
+ "top-left": "top-[var(--ds-toast-inset-top)] left-[var(--ds-toast-inset-left)]",
13753
+ "bottom-right": "bottom-[var(--ds-toast-inset-bottom-right)] right-[var(--ds-toast-inset-right)]",
13754
+ "bottom-left": "bottom-[var(--ds-toast-inset-bottom-left)] left-[var(--ds-toast-inset-left)]",
13755
+ "top-center": "top-[var(--ds-toast-inset-top)] left-1/2 -translate-x-1/2",
13756
+ "bottom-center": "bottom-ds-4 left-1/2 -translate-x-1/2"
13757
+ };
13758
+ var finite = (ms) => typeof ms === "number" && Number.isFinite(ms) && ms > 0;
13759
+ function Toast({ variant = "default", message, visible, onDismiss, duration = 5e3, pauseOnHover = false, action, actionWindow, position = "bottom-right", mode, container, inline = false, paused = false, onActionLapse, onAction, restartKey = 0 }) {
13760
+ const isMobile = useBreakpoint("sm");
13761
+ const isCentered = position === "top-center" || position === "bottom-center";
13762
+ const [hovered, setHovered] = useState(false);
13763
+ const onDismissRef = useLatest(onDismiss);
13764
+ const onLapseRef = useLatest(onActionLapse);
13765
+ const actionUsed = useRef(false);
13766
+ const phase = action ? finite(actionWindow) ? "window" : "none" : finite(duration) ? "plain" : "none";
13767
+ const total = phase === "window" ? actionWindow : phase === "plain" ? duration : 0;
13768
+ const timer = useRef(void 0);
13769
+ const remaining = useRef(total);
13770
+ const startedAt = useRef(0);
13771
+ const isPaused = paused || !inline && pauseOnHover && hovered;
13772
+ const stop = useCallback(() => {
13773
+ if (timer.current) {
13774
+ clearTimeout(timer.current);
13775
+ timer.current = void 0;
13776
+ remaining.current -= Date.now() - startedAt.current;
13777
+ }
13778
+ }, []);
13779
+ const start = useCallback(() => {
13780
+ if (phase === "none" || timer.current) return;
13781
+ startedAt.current = Date.now();
13782
+ const expire = phase === "window" ? () => {
13783
+ timer.current = void 0;
13784
+ if (onLapseRef.current) onLapseRef.current();
13785
+ else onDismissRef.current();
13786
+ } : () => {
13787
+ timer.current = void 0;
13788
+ onDismissRef.current();
13789
+ };
13790
+ timer.current = setTimeout(expire, Math.max(0, remaining.current));
13791
+ }, [phase, onDismissRef, onLapseRef]);
13792
+ useEffect(() => {
13793
+ if (!visible) return;
13794
+ remaining.current = total;
13795
+ if (timer.current) {
13796
+ clearTimeout(timer.current);
13797
+ timer.current = void 0;
13798
+ }
13799
+ if (!isPaused) start();
13800
+ return () => {
13801
+ if (timer.current) {
13802
+ clearTimeout(timer.current);
13803
+ timer.current = void 0;
13804
+ }
13805
+ };
13806
+ }, [visible, phase, total, restartKey]);
13807
+ useEffect(() => {
13808
+ if (!visible) return;
13809
+ if (isPaused) stop();
13810
+ else start();
13811
+ }, [isPaused, visible, start, stop]);
13812
+ useEffect(() => {
13813
+ actionUsed.current = false;
13814
+ }, [restartKey]);
13815
+ if (!visible) return null;
13816
+ const icon = variantIcons[variant];
13817
+ const pressAction = () => {
13818
+ if (!action || actionUsed.current) return;
13819
+ actionUsed.current = true;
13820
+ if (onAction) onAction();
13821
+ else {
13822
+ void action.onClick();
13823
+ onDismissRef.current();
13824
+ }
13825
+ };
13826
+ const card = /* @__PURE__ */ jsxs(
13827
+ "div",
13828
+ {
13829
+ "data-toast": "",
13830
+ "data-variant": variant,
13831
+ role: inline ? void 0 : variant === "error" ? "alert" : "status",
13832
+ "aria-live": inline ? void 0 : variant === "error" ? "assertive" : "polite",
13833
+ "data-mode": inline ? mode : void 0,
13834
+ className: cn(
13835
+ "flex items-center gap-ds-3 px-ds-4 py-ds-3",
13836
+ isCentered ? "min-w-[360px] max-w-[560px]" : "min-w-[280px] max-w-[420px]",
13837
+ "border-[length:var(--ds-border-width)]",
13838
+ GLASS_DROPDOWN,
13839
+ "rounded-[var(--ds-radius-md)]",
13840
+ "text-sm text-[var(--ds-text-primary)]",
13841
+ variantStyles4[variant]
13842
+ ),
13843
+ children: [
13844
+ /* @__PURE__ */ jsx(Icon, { name: icon.name, size: 18, className: cn("shrink-0", icon.className) }),
13845
+ /* @__PURE__ */ jsx("span", { className: "flex-1", children: message }),
13846
+ action && /* @__PURE__ */ jsx(
13847
+ "button",
13848
+ {
13849
+ type: "button",
13850
+ onClick: pressAction,
13851
+ className: "shrink-0 text-xs font-semibold text-[var(--ds-accent-primary)] hover:underline cursor-pointer",
13852
+ children: action.label
13853
+ }
13854
+ ),
13855
+ /* @__PURE__ */ jsx(
13856
+ "button",
13857
+ {
13858
+ type: "button",
13859
+ onClick: onDismiss,
13860
+ "aria-label": "Dismiss notification",
13861
+ className: "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer shrink-0",
13862
+ children: /* @__PURE__ */ jsx(Icon, { name: "close", size: 16 })
13863
+ }
13864
+ )
13865
+ ]
13866
+ }
13867
+ );
13868
+ if (inline) return card;
13869
+ return createPortal(
13870
+ /* @__PURE__ */ jsx(
13871
+ "div",
13872
+ {
13873
+ "data-mode": mode,
13874
+ onMouseEnter: pauseOnHover ? () => setHovered(true) : void 0,
13875
+ onMouseLeave: pauseOnHover ? () => setHovered(false) : void 0,
13876
+ className: cn(
13877
+ "fixed z-[var(--ds-z-toast)]",
13878
+ isMobile ? "bottom-ds-4 left-ds-4 right-ds-4" : TOAST_POSITION_CLASSES[position]
13879
+ ),
13880
+ children: card
13881
+ }
13882
+ ),
13883
+ container ?? document.body
13884
+ );
13885
+ }
13886
+ var ToastContext = createContext(null);
13887
+ var ToastLedgerCtx = createContext(null);
13888
+ var BottomNavPresenceCtx = createContext(null);
13889
+ function useToast() {
13890
+ const ctx = useContext(ToastContext);
13891
+ if (!ctx) {
13892
+ throw new Error("useToast must be used within <ToastProvider>: the app shell's (get_app_shell writes it around AppShell). A page outside the shell (sign-in, a public page) shows its messages inline instead: an Alert above the form, and each field's error below the field.");
13893
+ }
13894
+ return ctx;
13895
+ }
13896
+ function useToastOptional() {
13897
+ return useContext(ToastContext);
13898
+ }
13899
+ var toastSeq = 0;
13900
+ var heardSeq = 0;
13901
+ var mergeable = (e) => !e.action && e.variant !== "error";
13902
+ function fill(entries, slots) {
13903
+ let shown = entries.filter((e) => e.shown).length;
13904
+ if (shown >= slots || shown === entries.length) return entries;
13905
+ const next = [...entries];
13906
+ while (shown < slots) {
13907
+ const urgent = next.findIndex((e) => !e.shown && !mergeable(e));
13908
+ const at = urgent !== -1 ? urgent : next.findIndex((e) => !e.shown);
13909
+ if (at === -1) break;
13910
+ next[at] = { ...next[at], shown: true };
13911
+ shown++;
13912
+ }
13913
+ return next;
13914
+ }
13915
+ function ToastProvider({ children, maxVisible = 3, position = "bottom-right", duration = 5e3, mode, pauseOnHover = true, coalesce = true }) {
13916
+ const slots = Math.max(1, Math.floor(Number.isFinite(maxVisible) ? maxVisible : 1));
13917
+ const [list, setList] = useState([]);
13918
+ const entries = useRef([]);
13919
+ const isMobile = useBreakpoint("sm");
13920
+ const createdIds = useRef([]);
13921
+ const [heard, setHeard] = useState([]);
13922
+ const [hovered, setHovered] = useState(false);
13923
+ const [focusInside, setFocusInside] = useState(false);
13924
+ const paused = pauseOnHover && hovered || focusInside;
13925
+ const stackRef = useRef(null);
13926
+ const returnTo = useRef(null);
13927
+ const answering = useRef([]);
13928
+ const viaKey = useRef(false);
13929
+ const refocus = useRef(false);
13930
+ const pendingHeard = useRef(/* @__PURE__ */ new Map());
13931
+ const heardTimer = useRef(void 0);
13932
+ const [bottomNavs, setBottomNavs] = useState(0);
13933
+ const registerBottomNav = useCallback(() => {
13934
+ setBottomNavs((n) => n + 1);
13935
+ let gone = false;
13936
+ return () => {
13937
+ if (gone) return;
13938
+ gone = true;
13939
+ setBottomNavs((n) => n - 1);
13940
+ };
13941
+ }, []);
13942
+ const commit = useCallback((next) => {
13943
+ entries.current = fill(next, slots);
13944
+ setList(entries.current);
13945
+ }, [slots]);
13946
+ const [slotsSeen, setSlotsSeen] = useState(slots);
13947
+ if (slots !== slotsSeen) {
13948
+ setSlotsSeen(slots);
13949
+ let kept = 0;
13950
+ setList(fill(list.map((e) => e.shown && ++kept > slots ? { ...e, shown: false } : e), slots));
13951
+ }
13952
+ useEffect(() => {
13953
+ entries.current = list;
13954
+ }, [list]);
13955
+ const announce = useCallback((entry) => {
13956
+ pendingHeard.current.set(entry.id, entry);
13957
+ if (heardTimer.current) return;
13958
+ heardTimer.current = setTimeout(() => {
13959
+ heardTimer.current = void 0;
13960
+ const batch = [...pendingHeard.current.values()];
13961
+ pendingHeard.current.clear();
13962
+ setHeard((list2) => [...list2, ...batch.map((e) => ({
13963
+ id: `heard-${++heardSeq}`,
13964
+ assertive: e.variant === "error",
13965
+ node: /* @__PURE__ */ jsxs(Fragment, { children: [
13966
+ e.message,
13967
+ e.action ? ` Press F8 to reach ${e.action.label}.` : ""
13968
+ ] })
13969
+ }))].slice(-5));
13970
+ }, 0);
13971
+ }, []);
13972
+ useEffect(() => () => {
13973
+ if (heardTimer.current) clearTimeout(heardTimer.current);
13974
+ }, []);
13975
+ const leaving = useCallback(() => {
13976
+ if (!stackRef.current?.contains(document.activeElement)) return;
13977
+ refocus.current = true;
13978
+ setFocusInside(false);
13979
+ }, []);
13980
+ const remove = useCallback((id) => {
13981
+ leaving();
13982
+ commit(entries.current.filter((t) => t.id !== id));
13983
+ }, [commit, leaving]);
13984
+ const dismissAll = useCallback(() => {
13985
+ leaving();
13986
+ commit([]);
13987
+ }, [commit, leaving]);
13988
+ const update = useCallback((id, message, options = {}) => {
13989
+ const list2 = entries.current;
13990
+ const at = list2.findIndex((t2) => t2.id === id);
13991
+ if (at === -1) return;
13992
+ const t = list2[at];
13993
+ const next = [...list2];
13994
+ next[at] = { ...t, message, variant: options.variant ?? t.variant, duration: options.duration ?? t.duration, action: options.action ?? t.action, actionWindow: options.actionWindow ?? t.actionWindow, key: options.key ?? t.key, version: t.version + 1 };
13995
+ commit(next);
13996
+ announce(next[at]);
13997
+ }, [commit, announce]);
13998
+ const toast = useCallback((message, options = {}) => {
13999
+ const list2 = entries.current;
14000
+ const base = { message, variant: options.variant ?? "default", duration: options.duration ?? duration, action: options.action, actionWindow: options.actionWindow, key: options.key };
14001
+ if (options.id) {
14002
+ const at = list2.findIndex((t) => t.id === options.id);
14003
+ if (at !== -1) {
14004
+ const next = [...list2];
14005
+ next[at] = { ...list2[at], ...base, version: list2[at].version + 1 };
14006
+ createdIds.current.push(options.id);
14007
+ commit(next);
14008
+ announce(next[at]);
14009
+ return options.id;
14010
+ }
14011
+ }
14012
+ while (answering.current.length) {
14013
+ const answered = answering.current.shift();
14014
+ const at = list2.findIndex((t) => t.id === answered);
14015
+ if (at === -1) continue;
14016
+ const next = [...list2];
14017
+ next[at] = { ...list2[at], ...base, version: list2[at].version + 1 };
14018
+ createdIds.current.push(answered);
14019
+ commit(next);
14020
+ announce(next[at]);
14021
+ return answered;
14022
+ }
14023
+ if (coalesce && base.key && mergeable(base)) {
14024
+ const at = list2.findIndex((t) => t.key === base.key && mergeable(t));
14025
+ if (at !== -1) {
14026
+ const next = [...list2];
14027
+ next[at] = { ...list2[at], ...base, version: list2[at].version + 1 };
14028
+ createdIds.current.push(list2[at].id);
14029
+ commit(next);
14030
+ announce(next[at]);
14031
+ return list2[at].id;
14032
+ }
14033
+ }
14034
+ const id = options.id ?? `toast-${++toastSeq}`;
14035
+ const entry = { id, ...base, shown: false, version: 0 };
14036
+ createdIds.current.push(id);
14037
+ commit([...list2, entry]);
14038
+ announce(entry);
14039
+ return id;
14040
+ }, [duration, coalesce, commit, announce]);
14041
+ const api = useMemo(() => ({
14042
+ toast,
14043
+ success: (m, o) => toast(m, { ...o, variant: "success" }),
14044
+ warning: (m, o) => toast(m, { ...o, variant: "warning" }),
14045
+ error: (m, o) => toast(m, { ...o, variant: "error" }),
14046
+ update,
14047
+ dismiss: remove,
14048
+ dismissAll,
14049
+ promise: async (promise, messages, options = {}) => {
14050
+ const id = toast(messages.loading, { ...options, variant: "default", duration: Infinity });
14051
+ try {
14052
+ const value = await promise;
14053
+ update(id, typeof messages.success === "function" ? messages.success(value) : messages.success, { variant: "success", duration });
14054
+ return value;
14055
+ } catch (err) {
14056
+ update(id, typeof messages.error === "function" ? messages.error(err) : messages.error, { variant: "error", duration });
14057
+ throw err;
14058
+ }
14059
+ }
14060
+ }), [toast, update, remove, dismissAll, duration]);
14061
+ const ledger = useMemo(() => ({
14062
+ created: () => createdIds.current.length,
14063
+ latestSince: (count) => {
14064
+ if (createdIds.current.length <= count) return void 0;
14065
+ const id = createdIds.current[createdIds.current.length - 1];
14066
+ const entry = entries.current.find((t) => t.id === id);
14067
+ return entry ? { id, message: entry.message } : void 0;
14068
+ }
14069
+ }), []);
14070
+ const pressAction = useCallback((id) => {
14071
+ const entry = entries.current.find((t) => t.id === id);
14072
+ if (!entry?.action) return;
14073
+ const run = entry.action.onClick;
14074
+ leaving();
14075
+ commit(entries.current.map((t) => t.id === id ? { ...t, action: void 0, actionWindow: void 0, version: t.version + 1 } : t));
14076
+ answering.current.push(id);
14077
+ const done = () => {
14078
+ const waiting2 = answering.current.indexOf(id);
14079
+ if (waiting2 === -1) return;
14080
+ answering.current.splice(waiting2, 1);
14081
+ remove(id);
14082
+ };
14083
+ let result;
14084
+ try {
14085
+ result = run();
14086
+ } catch {
14087
+ result = void 0;
14088
+ }
14089
+ if (result && typeof result.then === "function") result.then(done, done);
14090
+ else done();
14091
+ }, [commit, leaving, remove]);
14092
+ const lapse = useCallback((id) => {
14093
+ const list2 = entries.current;
14094
+ const at = list2.findIndex((t) => t.id === id);
14095
+ if (at === -1) return;
14096
+ const next = [...list2];
14097
+ next[at] = { ...list2[at], action: void 0, actionWindow: void 0, version: list2[at].version + 1 };
14098
+ commit(next);
14099
+ }, [commit]);
14100
+ useEffect(() => {
14101
+ const onKey = (e) => {
14102
+ if (e.key !== "F8" || e.defaultPrevented) return;
14103
+ const stack = stackRef.current;
14104
+ if (!stack) return;
14105
+ e.preventDefault();
14106
+ const active = document.activeElement;
14107
+ if (!stack.contains(active)) {
14108
+ returnTo.current = active instanceof HTMLElement && active !== document.body ? active : null;
14109
+ viaKey.current = true;
14110
+ }
14111
+ (stack.querySelector("button") ?? stack).focus();
14112
+ viaKey.current = false;
14113
+ };
14114
+ document.addEventListener("keydown", onKey);
14115
+ return () => document.removeEventListener("keydown", onKey);
14116
+ }, []);
14117
+ useEffect(() => {
14118
+ if (!refocus.current) return;
14119
+ refocus.current = false;
14120
+ const back = returnTo.current;
14121
+ const next = stackRef.current?.querySelector("button");
14122
+ const main = document.querySelector("main");
14123
+ if (back?.isConnected) back.focus();
14124
+ else if (next) next.focus();
14125
+ else if (main) {
14126
+ if (!main.hasAttribute("tabindex")) {
14127
+ main.setAttribute("tabindex", "-1");
14128
+ main.addEventListener("blur", () => main.removeAttribute("tabindex"), { once: true });
14129
+ }
14130
+ main.focus();
14131
+ }
14132
+ }, [list]);
14133
+ const shown = list.filter((t) => t.shown);
14134
+ const waiting = list.length - shown.length;
14135
+ const isTop = position.startsWith("top");
14136
+ const hasBottomNav = isMobile && bottomNavs > 0;
14137
+ return /* @__PURE__ */ jsx(ToastContext.Provider, { value: api, children: /* @__PURE__ */ jsxs(ToastLedgerCtx.Provider, { value: ledger, children: [
14138
+ /* @__PURE__ */ jsx(BottomNavPresenceCtx.Provider, { value: registerBottomNav, children }),
14139
+ /* @__PURE__ */ jsx("div", { className: "sr-only", role: "status", "aria-live": "polite", "data-toast-announcer": "polite", children: heard.filter((h) => !h.assertive).map((h) => /* @__PURE__ */ jsx("p", { children: h.node }, h.id)) }),
14140
+ /* @__PURE__ */ jsx("div", { className: "sr-only", role: "alert", "aria-live": "assertive", "data-toast-announcer": "assertive", children: heard.filter((h) => h.assertive).map((h) => /* @__PURE__ */ jsx("p", { children: h.node }, h.id)) }),
14141
+ shown.length > 0 && createPortal(
14142
+ /* @__PURE__ */ jsxs(
14143
+ "div",
14144
+ {
14145
+ ref: stackRef,
14146
+ role: "region",
14147
+ "aria-label": "Notifications",
14148
+ tabIndex: -1,
14149
+ "data-mode": mode,
14150
+ "data-toast-stack": "",
14151
+ onMouseEnter: () => setHovered(true),
14152
+ onMouseLeave: () => setHovered(false),
14153
+ onFocusCapture: (e) => {
14154
+ setFocusInside(true);
14155
+ const from = e.relatedTarget;
14156
+ if (viaKey.current || from instanceof Node && e.currentTarget.contains(from)) return;
14157
+ returnTo.current = from instanceof HTMLElement ? from : null;
14158
+ },
14159
+ onBlurCapture: (e) => {
14160
+ if (!e.currentTarget.contains(e.relatedTarget)) setFocusInside(false);
14161
+ },
14162
+ className: cn(
14163
+ "fixed z-[var(--ds-z-toast)] flex gap-ds-2 pointer-events-none outline-none",
14164
+ isTop ? "flex-col" : "flex-col-reverse",
14165
+ isMobile ? cn("left-ds-4 right-ds-4", !hasBottomNav && "bottom-ds-4") : TOAST_POSITION_CLASSES[position]
14166
+ ),
14167
+ style: hasBottomNav ? { bottom: "calc(var(--ds-space-12) + var(--ds-space-2) + var(--ds-space-4) + env(safe-area-inset-bottom))" } : void 0,
14168
+ children: [
14169
+ shown.map((t) => /* @__PURE__ */ jsx("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsx(
14170
+ Toast,
14171
+ {
14172
+ inline: true,
14173
+ visible: true,
14174
+ variant: t.variant,
14175
+ message: t.message,
14176
+ duration: t.duration,
14177
+ action: t.action,
14178
+ actionWindow: t.actionWindow,
14179
+ mode,
14180
+ paused,
14181
+ restartKey: t.version,
14182
+ onAction: () => pressAction(t.id),
14183
+ onActionLapse: () => lapse(t.id),
14184
+ onDismiss: () => remove(t.id)
14185
+ }
14186
+ ) }, t.id)),
14187
+ (waiting > 0 || shown.length >= 2) && /* @__PURE__ */ jsxs("div", { className: "pointer-events-auto flex items-center justify-end gap-ds-2 text-xs", "data-toast-overflow": "", children: [
14188
+ waiting > 0 && /* @__PURE__ */ jsxs("span", { className: "rounded-[var(--ds-radius-full)] bg-[var(--ds-bg-muted)] px-ds-2 py-ds-0.5 font-medium text-[var(--ds-text-secondary)]", children: [
14189
+ "+",
14190
+ waiting,
14191
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: " more waiting" })
14192
+ ] }),
14193
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: dismissAll, className: "font-medium text-[var(--ds-text-secondary)] hover:text-[var(--ds-text-primary)] hover:underline cursor-pointer", children: "Clear all" })
14194
+ ] })
14195
+ ]
14196
+ }
14197
+ ),
14198
+ document.body
14199
+ )
14200
+ ] }) });
14201
+ }
13222
14202
  function BottomNav({ items, onItemClick, maxVisible = 5, className, ref }) {
14203
+ const registerWithToasts = useContext(BottomNavPresenceCtx);
14204
+ useEffect(() => registerWithToasts?.(), [registerWithToasts]);
13223
14205
  const needsOverflow = items.length > maxVisible;
13224
14206
  const visibleItems = needsOverflow ? items.slice(0, maxVisible - 1) : items;
13225
14207
  const overflowItems = needsOverflow ? items.slice(maxVisible - 1) : [];
@@ -13254,6 +14236,7 @@ function BottomNav({ items, onItemClick, maxVisible = 5, className, ref }) {
13254
14236
  /* @__PURE__ */ jsxs(
13255
14237
  "button",
13256
14238
  {
14239
+ type: "button",
13257
14240
  onClick: () => setOverflowOpen(!overflowOpen),
13258
14241
  "aria-expanded": overflowOpen,
13259
14242
  "aria-haspopup": "true",
@@ -13281,6 +14264,7 @@ function BottomNav({ items, onItemClick, maxVisible = 5, className, ref }) {
13281
14264
  children: overflowItems.map((item, i) => /* @__PURE__ */ jsxs(
13282
14265
  "button",
13283
14266
  {
14267
+ type: "button",
13284
14268
  role: "menuitem",
13285
14269
  onClick: () => {
13286
14270
  onItemClick?.(item, maxVisible - 1 + i);
@@ -13310,6 +14294,7 @@ function NavButton({ item, onClick }) {
13310
14294
  return /* @__PURE__ */ jsxs(
13311
14295
  "button",
13312
14296
  {
14297
+ type: "button",
13313
14298
  onClick,
13314
14299
  className: cn(
13315
14300
  "flex flex-col items-center justify-center gap-ds-0.5 flex-1 h-full",
@@ -14712,6 +15697,7 @@ function NavMenu({
14712
15697
  /* @__PURE__ */ jsxs(
14713
15698
  "button",
14714
15699
  {
15700
+ type: "button",
14715
15701
  ref: triggerRef,
14716
15702
  onClick: () => setOpen(!open),
14717
15703
  "aria-expanded": open,
@@ -14908,7 +15894,8 @@ var sizeClasses = {
14908
15894
  full: "max-w-[calc(100vw-2rem)]"
14909
15895
  };
14910
15896
  function Dialog({ open, onClose: onCloseProp, title, description, children, footer, variant = "default", size = "md", alert = false, container, onSubmit, dirty, onDiscard }) {
14911
- const guard = useDirtyGuard(dirty, onCloseProp, onDiscard);
15897
+ const { marked, mark } = useOverlayMark(open);
15898
+ const guard = useDirtyGuard(dirty ?? marked, onCloseProp, onDiscard);
14912
15899
  const onClose = guard.requestClose;
14913
15900
  const titleId = useId();
14914
15901
  const descriptionId = useId();
@@ -15013,7 +16000,8 @@ function Dialog({ open, onClose: onCloseProp, title, description, children, foot
15013
16000
  className: cn(
15014
16001
  "relative z-10 w-full",
15015
16002
  isMobile ? "max-w-none h-full" : sizeClasses[size],
15016
- "bg-[var(--ds-bg-surface)] text-[var(--ds-text-primary)]",
16003
+ OVERLAY_SURFACE,
16004
+ "text-[var(--ds-text-primary)]",
15017
16005
  GLASS_OVERLAY,
15018
16006
  "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
15019
16007
  isMobile ? "rounded-none" : "rounded-[var(--ds-radius-xl)]",
@@ -15044,7 +16032,7 @@ function Dialog({ open, onClose: onCloseProp, title, description, children, foot
15044
16032
  }
15045
16033
  ),
15046
16034
  description && /* @__PURE__ */ jsx("p", { id: descriptionId, className: "mt-ds-2 text-sm text-[var(--ds-text-muted)]", children: description }),
15047
- children && /* @__PURE__ */ jsx("div", { className: "mt-ds-4", children: /* @__PURE__ */ jsx(OverlayCloseCtx, { value: onClose, children }) })
16035
+ children && /* @__PURE__ */ jsx("div", { className: "mt-ds-4", children: /* @__PURE__ */ jsx(OverlayMarkCtx, { value: mark, children: /* @__PURE__ */ jsx(OverlayCloseCtx, { value: onClose, children }) }) })
15048
16036
  ] })
15049
16037
  ] }) }),
15050
16038
  /* @__PURE__ */ jsx(DiscardConfirm, { open: guard.confirming, onKeep: guard.keep, onDiscard: guard.discard }),
@@ -15070,6 +16058,7 @@ function HamburgerButton({ onClick, open, className: cls }) {
15070
16058
  return /* @__PURE__ */ jsx(
15071
16059
  "button",
15072
16060
  {
16061
+ type: "button",
15073
16062
  onClick,
15074
16063
  "aria-label": open ? "Close navigation menu" : "Open navigation menu",
15075
16064
  "aria-expanded": open ?? void 0,
@@ -15372,6 +16361,7 @@ function MobileMenuItem({
15372
16361
  return /* @__PURE__ */ jsxs(
15373
16362
  "button",
15374
16363
  {
16364
+ type: "button",
15375
16365
  onClick,
15376
16366
  className: cn(
15377
16367
  "flex items-center gap-ds-3 px-ds-3 py-ds-3 text-sm relative cursor-pointer w-full text-left",
@@ -15396,6 +16386,7 @@ function MobileMenuItem({
15396
16386
  return /* @__PURE__ */ jsxs(
15397
16387
  "button",
15398
16388
  {
16389
+ type: "button",
15399
16390
  onClick,
15400
16391
  className: cn(
15401
16392
  "flex items-center gap-ds-3 px-ds-3 py-ds-2.5 text-sm cursor-pointer w-full text-left",
@@ -15413,6 +16404,7 @@ function MobileMenuItem({
15413
16404
  return /* @__PURE__ */ jsxs(
15414
16405
  "button",
15415
16406
  {
16407
+ type: "button",
15416
16408
  onClick,
15417
16409
  className: cn(
15418
16410
  "flex items-center gap-ds-3 px-ds-3 py-ds-2.5 text-sm rounded-[var(--ds-radius-md)] cursor-pointer w-full text-left",
@@ -15429,6 +16421,7 @@ function MobileMenuItem({
15429
16421
  return /* @__PURE__ */ jsxs(
15430
16422
  "button",
15431
16423
  {
16424
+ type: "button",
15432
16425
  onClick,
15433
16426
  className: cn(
15434
16427
  "flex items-center gap-ds-3 px-ds-3 py-ds-2.5 text-sm rounded-[var(--ds-radius-md)] cursor-pointer w-full text-left",
@@ -15532,7 +16525,8 @@ function CommandPalette({ open, onOpenChange, commands, placeholder = "Type a co
15532
16525
  "aria-label": "Command palette",
15533
16526
  className: cn(
15534
16527
  "relative z-10 w-full max-w-lg",
15535
- "bg-[var(--ds-bg-surface)] text-[var(--ds-text-primary)]",
16528
+ OVERLAY_SURFACE,
16529
+ "text-[var(--ds-text-primary)]",
15536
16530
  "[backdrop-filter:var(--ds-glass-filter)] [-webkit-backdrop-filter:var(--ds-glass-filter)]",
15537
16531
  "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
15538
16532
  "rounded-[var(--ds-radius-xl)] overflow-hidden",
@@ -15589,6 +16583,7 @@ function CommandPalette({ open, onOpenChange, commands, placeholder = "Type a co
15589
16583
  return /* @__PURE__ */ jsxs(
15590
16584
  "button",
15591
16585
  {
16586
+ type: "button",
15592
16587
  id: `${listboxId}-opt-${myIdx}`,
15593
16588
  role: "option",
15594
16589
  tabIndex: -1,
@@ -15777,6 +16772,7 @@ function ContextMenu({ items, children, className, ref, container }) {
15777
16772
  return /* @__PURE__ */ jsxs(
15778
16773
  "button",
15779
16774
  {
16775
+ type: "button",
15780
16776
  role: "menuitem",
15781
16777
  disabled: item.disabled,
15782
16778
  onClick: () => {
@@ -15887,6 +16883,7 @@ function NotificationCenter({
15887
16883
  /* @__PURE__ */ jsx("div", { className: "flex items-center gap-ds-3", children: unreadCount > 0 && /* @__PURE__ */ jsx(
15888
16884
  "button",
15889
16885
  {
16886
+ type: "button",
15890
16887
  onClick: onMarkAllRead,
15891
16888
  className: "text-sm text-[var(--ds-accent-primary)] hover:underline cursor-pointer",
15892
16889
  children: "Mark all read"
@@ -15897,6 +16894,7 @@ function NotificationCenter({
15897
16894
  /* @__PURE__ */ jsx(
15898
16895
  "button",
15899
16896
  {
16897
+ type: "button",
15900
16898
  onClick: () => setFilter("all"),
15901
16899
  className: cn(
15902
16900
  "px-ds-3 py-ds-1.5 text-sm rounded-[var(--ds-radius-md)] transition-colors cursor-pointer",
@@ -15908,6 +16906,7 @@ function NotificationCenter({
15908
16906
  /* @__PURE__ */ jsxs(
15909
16907
  "button",
15910
16908
  {
16909
+ type: "button",
15911
16910
  onClick: () => setFilter("unread"),
15912
16911
  className: cn(
15913
16912
  "px-ds-3 py-ds-1.5 text-sm rounded-[var(--ds-radius-md)] transition-colors cursor-pointer",
@@ -15957,6 +16956,7 @@ function NotificationCenter({
15957
16956
  !n.read && onMarkRead && /* @__PURE__ */ jsx(
15958
16957
  "button",
15959
16958
  {
16959
+ type: "button",
15960
16960
  onClick: () => onMarkRead(n.id),
15961
16961
  "aria-label": "Mark as read",
15962
16962
  className: "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer p-ds-0.5",
@@ -15966,6 +16966,7 @@ function NotificationCenter({
15966
16966
  onDismiss && /* @__PURE__ */ jsx(
15967
16967
  "button",
15968
16968
  {
16969
+ type: "button",
15969
16970
  onClick: () => onDismiss(n.id),
15970
16971
  "aria-label": "Dismiss",
15971
16972
  className: "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer p-ds-0.5",
@@ -15984,6 +16985,7 @@ function NotificationCenter({
15984
16985
  /* @__PURE__ */ jsxs(
15985
16986
  "button",
15986
16987
  {
16988
+ type: "button",
15987
16989
  ref: triggerRef,
15988
16990
  onClick: () => setOpen(!open),
15989
16991
  "aria-label": `Notifications${unreadCount ? ` (${unreadCount} unread)` : ""}`,
@@ -16023,6 +17025,7 @@ function NotificationCenter({
16023
17025
  unreadCount > 0 && /* @__PURE__ */ jsx(
16024
17026
  "button",
16025
17027
  {
17028
+ type: "button",
16026
17029
  onClick: onMarkAllRead,
16027
17030
  className: "text-xs text-[var(--ds-accent-primary)] hover:underline cursor-pointer",
16028
17031
  children: "Mark all read"
@@ -16067,6 +17070,7 @@ function NotificationCenter({
16067
17070
  !n.read && onMarkRead && /* @__PURE__ */ jsx(
16068
17071
  "button",
16069
17072
  {
17073
+ type: "button",
16070
17074
  onClick: () => onMarkRead(n.id),
16071
17075
  "aria-label": "Mark as read",
16072
17076
  className: "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer p-ds-0.5",
@@ -16076,6 +17080,7 @@ function NotificationCenter({
16076
17080
  onDismiss && /* @__PURE__ */ jsx(
16077
17081
  "button",
16078
17082
  {
17083
+ type: "button",
16079
17084
  onClick: () => onDismiss(n.id),
16080
17085
  "aria-label": "Dismiss",
16081
17086
  className: "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer p-ds-0.5",
@@ -16239,6 +17244,7 @@ function Tour({ steps, open, onClose, onComplete, container }) {
16239
17244
  /* @__PURE__ */ jsx(
16240
17245
  "button",
16241
17246
  {
17247
+ type: "button",
16242
17248
  onClick: onClose,
16243
17249
  className: "shrink-0 text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer -mt-ds-0.5",
16244
17250
  "aria-label": "Close tour",
@@ -16258,6 +17264,7 @@ function Tour({ steps, open, onClose, onComplete, container }) {
16258
17264
  current > 0 && /* @__PURE__ */ jsx(
16259
17265
  "button",
16260
17266
  {
17267
+ type: "button",
16261
17268
  onClick: handlePrev,
16262
17269
  className: cn(
16263
17270
  "px-ds-3 py-ds-1.5 text-xs font-medium rounded-[var(--ds-radius-sm)]",
@@ -16271,6 +17278,7 @@ function Tour({ steps, open, onClose, onComplete, container }) {
16271
17278
  /* @__PURE__ */ jsx(
16272
17279
  "button",
16273
17280
  {
17281
+ type: "button",
16274
17282
  ref: nextRef,
16275
17283
  onClick: handleNext,
16276
17284
  className: cn(
@@ -16319,223 +17327,6 @@ function getPopoverPosition(rect, placement, padding) {
16319
17327
  top = Math.max(8, Math.min(top, window.innerHeight - popH - 8));
16320
17328
  return { top, left };
16321
17329
  }
16322
- var variantStyles4 = {
16323
- default: "border-[var(--ds-border-default)] bg-[var(--ds-bg-surface)]",
16324
- success: "border-[var(--ds-status-success)] bg-[var(--ds-status-success-bg)]",
16325
- warning: "border-[var(--ds-status-warning)] bg-[var(--ds-status-warning-bg)]",
16326
- error: "border-[var(--ds-status-error)] bg-[var(--ds-status-error-bg)]"
16327
- };
16328
- var variantIcons = {
16329
- default: { name: "info", className: "text-[var(--ds-text-muted)]" },
16330
- success: { name: "check", className: "text-[var(--ds-status-success)]" },
16331
- warning: { name: "warning", className: "text-[var(--ds-status-warning)]" },
16332
- error: { name: "error", className: "text-[var(--ds-status-error)]" }
16333
- };
16334
- var TOAST_POSITION_CLASSES = {
16335
- "top-right": "top-[var(--ds-toast-inset-top)] right-[var(--ds-toast-inset-right)]",
16336
- "top-left": "top-[var(--ds-toast-inset-top)] left-[var(--ds-toast-inset-left)]",
16337
- "bottom-right": "bottom-[var(--ds-toast-inset-bottom-right)] right-[var(--ds-toast-inset-right)]",
16338
- "bottom-left": "bottom-[var(--ds-toast-inset-bottom-left)] left-[var(--ds-toast-inset-left)]",
16339
- "top-center": "top-[var(--ds-toast-inset-top)] left-1/2 -translate-x-1/2",
16340
- "bottom-center": "bottom-ds-4 left-1/2 -translate-x-1/2"
16341
- };
16342
- function Toast({ variant = "default", message, visible, onDismiss, duration = 5e3, pauseOnHover = false, action, position = "bottom-right", mode, container, inline = false }) {
16343
- const isMobile = useBreakpoint("sm");
16344
- const isCentered = position === "top-center" || position === "bottom-center";
16345
- const timerRef = useRef(void 0);
16346
- const startedAtRef = useRef(0);
16347
- const remainingRef = useRef(duration);
16348
- const onDismissRef = useLatest(onDismiss);
16349
- const startTimer = useCallback((ms) => {
16350
- const delay = ms ?? remainingRef.current;
16351
- remainingRef.current = delay;
16352
- startedAtRef.current = Date.now();
16353
- if (!Number.isFinite(delay) || delay <= 0) return;
16354
- timerRef.current = setTimeout(() => onDismissRef.current(), delay);
16355
- }, [onDismissRef]);
16356
- const clearTimer = useCallback(() => {
16357
- if (timerRef.current) {
16358
- clearTimeout(timerRef.current);
16359
- remainingRef.current -= Date.now() - startedAtRef.current;
16360
- }
16361
- }, []);
16362
- useEffect(() => {
16363
- if (!visible) return;
16364
- remainingRef.current = duration;
16365
- startTimer(duration);
16366
- return () => {
16367
- if (timerRef.current) clearTimeout(timerRef.current);
16368
- };
16369
- }, [visible, duration, startTimer]);
16370
- const handleMouseEnter = pauseOnHover ? () => {
16371
- clearTimer();
16372
- } : void 0;
16373
- const handleMouseLeave = pauseOnHover ? () => {
16374
- startTimer();
16375
- } : void 0;
16376
- if (!visible) return null;
16377
- const icon = variantIcons[variant];
16378
- const card = /* @__PURE__ */ jsxs(
16379
- "div",
16380
- {
16381
- role: variant === "error" ? "alert" : "status",
16382
- "aria-live": variant === "error" ? "assertive" : "polite",
16383
- "data-mode": inline ? mode : void 0,
16384
- onMouseEnter: inline ? handleMouseEnter : void 0,
16385
- onMouseLeave: inline ? handleMouseLeave : void 0,
16386
- className: cn(
16387
- "flex items-center gap-ds-3 px-ds-4 py-ds-3",
16388
- isCentered ? "min-w-[360px] max-w-[560px]" : "min-w-[280px] max-w-[420px]",
16389
- "border-[length:var(--ds-border-width)]",
16390
- GLASS_DROPDOWN,
16391
- "rounded-[var(--ds-radius-md)]",
16392
- "text-sm text-[var(--ds-text-primary)]",
16393
- variantStyles4[variant]
16394
- ),
16395
- children: [
16396
- /* @__PURE__ */ jsx(Icon, { name: icon.name, size: 18, className: cn("shrink-0", icon.className) }),
16397
- /* @__PURE__ */ jsx("span", { className: "flex-1", children: message }),
16398
- action && /* @__PURE__ */ jsx(
16399
- "button",
16400
- {
16401
- type: "button",
16402
- onClick: action.onClick,
16403
- className: "shrink-0 text-xs font-semibold text-[var(--ds-accent-primary)] hover:underline cursor-pointer",
16404
- children: action.label
16405
- }
16406
- ),
16407
- /* @__PURE__ */ jsx(
16408
- "button",
16409
- {
16410
- type: "button",
16411
- onClick: onDismiss,
16412
- "aria-label": "Dismiss notification",
16413
- className: "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer shrink-0",
16414
- children: /* @__PURE__ */ jsx(Icon, { name: "close", size: 16 })
16415
- }
16416
- )
16417
- ]
16418
- }
16419
- );
16420
- if (inline) return card;
16421
- return createPortal(
16422
- /* @__PURE__ */ jsx(
16423
- "div",
16424
- {
16425
- "data-mode": mode,
16426
- onMouseEnter: handleMouseEnter,
16427
- onMouseLeave: handleMouseLeave,
16428
- className: cn(
16429
- "fixed z-[var(--ds-z-toast)]",
16430
- isMobile ? "bottom-ds-4 left-ds-4 right-ds-4" : TOAST_POSITION_CLASSES[position]
16431
- ),
16432
- children: card
16433
- }
16434
- ),
16435
- container ?? document.body
16436
- );
16437
- }
16438
- var ToastContext = createContext(null);
16439
- function useToast() {
16440
- const ctx = useContext(ToastContext);
16441
- if (!ctx) {
16442
- throw new Error("useToast must be used within <ToastProvider>: the app shell's (get_app_shell writes it around AppShell). A page outside the shell (sign-in, a public page) shows its messages inline instead: an Alert above the form, and each field's error below the field.");
16443
- }
16444
- return ctx;
16445
- }
16446
- function useToastOptional() {
16447
- return useContext(ToastContext);
16448
- }
16449
- var toastSeq = 0;
16450
- function ToastProvider({ children, maxVisible = 1, position = "bottom-right", duration = 5e3, mode, pauseOnHover = true, coalesce = true }) {
16451
- const [queue, setQueue] = useState([]);
16452
- const isMobile = useBreakpoint("sm");
16453
- const dismiss = useCallback((id) => {
16454
- setQueue((q) => q.filter((t) => t.id !== id));
16455
- }, []);
16456
- const dismissAll = useCallback(() => setQueue([]), []);
16457
- const update = useCallback((id, message, options = {}) => {
16458
- setQueue((q) => q.map((t) => t.id === id ? { ...t, message, variant: options.variant ?? t.variant, duration: options.duration ?? t.duration, action: options.action ?? t.action } : t));
16459
- }, []);
16460
- const toast = useCallback((message, options = {}) => {
16461
- const id = options.id ?? `toast-${++toastSeq}`;
16462
- const entry = {
16463
- id,
16464
- message,
16465
- variant: options.variant ?? "default",
16466
- duration: options.duration ?? duration,
16467
- action: options.action,
16468
- key: options.key ?? options.variant ?? "default"
16469
- };
16470
- const slots = Math.max(1, maxVisible);
16471
- setQueue((q) => {
16472
- if (q.some((t) => t.id === id)) return q.map((t) => t.id === id ? entry : t);
16473
- if (coalesce && q.length >= slots) {
16474
- const waiting = q.findIndex((t, i) => i >= slots && t.key === entry.key);
16475
- if (waiting >= 0) {
16476
- const next = [...q];
16477
- next[waiting] = entry;
16478
- return next;
16479
- }
16480
- }
16481
- return [...q, entry];
16482
- });
16483
- return id;
16484
- }, [duration, maxVisible, coalesce]);
16485
- const api = useMemo(() => ({
16486
- toast,
16487
- success: (m, o) => toast(m, { ...o, variant: "success" }),
16488
- warning: (m, o) => toast(m, { ...o, variant: "warning" }),
16489
- error: (m, o) => toast(m, { ...o, variant: "error" }),
16490
- update,
16491
- dismiss,
16492
- dismissAll,
16493
- promise: async (promise, messages, options = {}) => {
16494
- const id = toast(messages.loading, { ...options, variant: "default", duration: Infinity });
16495
- try {
16496
- const value = await promise;
16497
- update(id, typeof messages.success === "function" ? messages.success(value) : messages.success, { variant: "success", duration });
16498
- return value;
16499
- } catch (err) {
16500
- update(id, typeof messages.error === "function" ? messages.error(err) : messages.error, { variant: "error", duration });
16501
- throw err;
16502
- }
16503
- }
16504
- }), [toast, update, dismiss, dismissAll, duration]);
16505
- const visible = queue.slice(0, Math.max(1, maxVisible));
16506
- const isTop = position.startsWith("top");
16507
- return /* @__PURE__ */ jsxs(ToastContext.Provider, { value: api, children: [
16508
- children,
16509
- visible.length > 0 && createPortal(
16510
- /* @__PURE__ */ jsx(
16511
- "div",
16512
- {
16513
- "data-mode": mode,
16514
- className: cn(
16515
- "fixed z-[var(--ds-z-toast)] flex gap-ds-2 pointer-events-none",
16516
- isTop ? "flex-col" : "flex-col-reverse",
16517
- isMobile ? "bottom-ds-4 left-ds-4 right-ds-4" : TOAST_POSITION_CLASSES[position]
16518
- ),
16519
- children: visible.map((t) => /* @__PURE__ */ jsx("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsx(
16520
- Toast,
16521
- {
16522
- inline: true,
16523
- visible: true,
16524
- variant: t.variant,
16525
- message: t.message,
16526
- duration: t.duration,
16527
- pauseOnHover,
16528
- action: t.action,
16529
- mode,
16530
- onDismiss: () => dismiss(t.id)
16531
- }
16532
- ) }, t.id))
16533
- }
16534
- ),
16535
- document.body
16536
- )
16537
- ] });
16538
- }
16539
17330
  function firstKey(item) {
16540
17331
  if (item !== null && typeof item === "object") return "id" in item ? item.id : void 0;
16541
17332
  return item;
@@ -16598,9 +17389,10 @@ function usePagination(list, options = {}) {
16598
17389
  };
16599
17390
  }
16600
17391
  function useDestructiveAction(options) {
16601
- const { action, title, description, confirmLabel, typeToConfirm, onConfirm, onUndo, undoMessage, severity } = options;
17392
+ const { action, title, description, confirmLabel, typeToConfirm, onConfirm, onUndo, doneMessage, undoMessage, restoredMessage, severity } = options;
16602
17393
  const preferences = useShellPreferences();
16603
17394
  const toast = useToastOptional();
17395
+ const ledger = useContext(ToastLedgerCtx);
16604
17396
  const [open, setOpen] = useState(false);
16605
17397
  const [typed, setTyped] = useState("");
16606
17398
  const [busy, setBusy] = useState(false);
@@ -16617,20 +17409,32 @@ function useDestructiveAction(options) {
16617
17409
  setTyped("");
16618
17410
  };
16619
17411
  const failureMessage = (err) => err instanceof Error && err.message ? err.message : `${title.replace(/\?$/, "")} failed. Try again.`;
17412
+ const done = doneMessage ?? undoMessage ?? pastTenseOf(label);
16620
17413
  const confirm = async () => {
16621
17414
  setBusy(true);
17415
+ const before = ledger?.created() ?? 0;
16622
17416
  try {
16623
17417
  await onConfirm();
16624
17418
  close();
17419
+ if (toast && (ledger?.created() ?? 0) === before) toast.success(done);
16625
17420
  } catch (err) {
16626
17421
  toast?.error(failureMessage(err));
16627
17422
  } finally {
16628
17423
  setBusy(false);
16629
17424
  }
16630
17425
  };
17426
+ const undo = async () => {
17427
+ try {
17428
+ await onUndo();
17429
+ toast?.success(restoredMessage ?? "Restored");
17430
+ } catch (err) {
17431
+ toast?.error(err instanceof Error && err.message ? err.message : "Undo didn't work. Try again.");
17432
+ }
17433
+ };
16631
17434
  const run = () => {
16632
17435
  if (undoable) {
16633
17436
  void (async () => {
17437
+ const before = ledger?.created() ?? 0;
16634
17438
  try {
16635
17439
  await onConfirm();
16636
17440
  } catch (err) {
@@ -16638,7 +17442,10 @@ function useDestructiveAction(options) {
16638
17442
  return;
16639
17443
  }
16640
17444
  const seconds = preferences.undoWindowSeconds ?? 5;
16641
- toast.success(undoMessage ?? `${title.replace(/\?$/, "")} done`, { duration: seconds * 1e3, action: { label: "Undo", onClick: onUndo } });
17445
+ const undoAction = { label: "Undo", onClick: () => undo() };
17446
+ const pages = (ledger?.created() ?? 0) > before ? ledger?.latestSince(before) : void 0;
17447
+ if (pages) toast.update(pages.id, pages.message, { variant: "success", action: undoAction, actionWindow: seconds * 1e3 });
17448
+ else toast.success(done, { action: undoAction, actionWindow: seconds * 1e3 });
16642
17449
  })();
16643
17450
  return;
16644
17451
  }
@@ -16666,6 +17473,61 @@ function useDestructiveAction(options) {
16666
17473
  ) : null;
16667
17474
  return { run, element, open };
16668
17475
  }
17476
+ var PAST_TENSE = {
17477
+ delete: "Deleted",
17478
+ remove: "Removed",
17479
+ archive: "Archived",
17480
+ leave: "Left",
17481
+ revoke: "Revoked",
17482
+ disconnect: "Disconnected",
17483
+ cancel: "Cancelled",
17484
+ reset: "Reset",
17485
+ clear: "Cleared",
17486
+ discard: "Discarded",
17487
+ unpublish: "Unpublished",
17488
+ deactivate: "Deactivated",
17489
+ disable: "Disabled",
17490
+ uninstall: "Uninstalled",
17491
+ unsubscribe: "Unsubscribed",
17492
+ block: "Blocked",
17493
+ ban: "Banned",
17494
+ suspend: "Suspended",
17495
+ close: "Closed",
17496
+ end: "Ended",
17497
+ stop: "Stopped",
17498
+ "sign out": "Signed out",
17499
+ "log out": "Logged out",
17500
+ transfer: "Transferred",
17501
+ merge: "Merged",
17502
+ overwrite: "Overwritten",
17503
+ replace: "Replaced",
17504
+ erase: "Erased",
17505
+ purge: "Purged",
17506
+ wipe: "Wiped",
17507
+ drop: "Dropped",
17508
+ detach: "Detached",
17509
+ unlink: "Unlinked",
17510
+ expire: "Expired",
17511
+ reject: "Rejected",
17512
+ decline: "Declined"
17513
+ };
17514
+ var warned2 = /* @__PURE__ */ new Set();
17515
+ function pastTenseOf(label) {
17516
+ const words = label.trim().toLowerCase().split(/\s+/);
17517
+ const found = PAST_TENSE[words.slice(0, 2).join(" ")] ?? PAST_TENSE[words[0] ?? ""];
17518
+ if (found) return found;
17519
+ let dev = false;
17520
+ try {
17521
+ dev = process.env.NODE_ENV !== "production";
17522
+ } catch {
17523
+ dev = false;
17524
+ }
17525
+ if (dev && !warned2.has(label)) {
17526
+ warned2.add(label);
17527
+ console.warn(`useDestructiveAction: "${label}" isn't a verb the hook knows; pass doneMessage so the confirmation says what happened.`);
17528
+ }
17529
+ return "Done";
17530
+ }
16669
17531
  var SectionHeading = forwardRef(function SectionHeading2({
16670
17532
  title,
16671
17533
  description,
@@ -16806,6 +17668,6 @@ var TextOverflow = forwardRef(function TextOverflow2({
16806
17668
  return /* @__PURE__ */ jsx(Tooltip, { content: resolvedTooltipContent, children: content });
16807
17669
  });
16808
17670
 
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 };
17671
+ export { Accordion, ActionPanel, ActionPanelGroup, AddAnother, 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, usePagination, useSidebar, useTextMeasure, useTimeScale, useTimeViewport, useTimeViewportLayout, useToast, useToastOptional };
16810
17672
  //# sourceMappingURL=index.js.map
16811
17673
  //# sourceMappingURL=index.js.map