twico-ui 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  'use strict';
3
3
 
4
- var React24 = require('react');
4
+ var React25 = require('react');
5
5
  var jsxRuntime = require('react/jsx-runtime');
6
6
  var reactDom = require('react-dom');
7
7
 
@@ -23,12 +23,12 @@ function _interopNamespace(e) {
23
23
  return Object.freeze(n);
24
24
  }
25
25
 
26
- var React24__namespace = /*#__PURE__*/_interopNamespace(React24);
26
+ var React25__namespace = /*#__PURE__*/_interopNamespace(React25);
27
27
 
28
28
  // components/navigation/Accordion.jsx
29
- var SUPPORTS_STYLE_HOIST = parseInt(String(React24__namespace.version), 10) >= 19;
29
+ var SUPPORTS_STYLE_HOIST = parseInt(String(React25__namespace.version), 10) >= 19;
30
30
  function useScopedStyles(id, css) {
31
- React24__namespace.useInsertionEffect(() => {
31
+ React25__namespace.useInsertionEffect(() => {
32
32
  if (SUPPORTS_STYLE_HOIST) return;
33
33
  if (typeof document === "undefined" || document.getElementById(id)) return;
34
34
  const el = document.createElement("style");
@@ -36,7 +36,7 @@ function useScopedStyles(id, css) {
36
36
  el.textContent = css;
37
37
  document.head.appendChild(el);
38
38
  }, [id, css]);
39
- return SUPPORTS_STYLE_HOIST ? React24__namespace.createElement("style", { href: id, precedence: "twc-ui" }, css) : null;
39
+ return SUPPORTS_STYLE_HOIST ? React25__namespace.createElement("style", { href: id, precedence: "twc-ui" }, css) : null;
40
40
  }
41
41
  var ACCORDION_CSS = `
42
42
  .twc-accordion { display: flex; flex-direction: column; font-family: var(--font-sans);
@@ -77,10 +77,10 @@ function Accordion({
77
77
  ...rest
78
78
  }) {
79
79
  const __twcStyles = useScopedStyles("twc-accordion-styles", ACCORDION_CSS);
80
- const rootRef = React24__namespace.default.useRef(null);
81
- const [internal, setInternal] = React24__namespace.default.useState(() => new Set(defaultOpen));
80
+ const rootRef = React25__namespace.default.useRef(null);
81
+ const [internal, setInternal] = React25__namespace.default.useState(() => new Set(defaultOpen));
82
82
  const open = openProp !== void 0 ? new Set(openProp) : internal;
83
- const baseId = React24__namespace.default.useId();
83
+ const baseId = React25__namespace.default.useId();
84
84
  function toggle(val) {
85
85
  const item = items.find((x) => x.value === val);
86
86
  if (item?.disabled) return;
@@ -266,7 +266,7 @@ function Avatar({
266
266
  ...rest
267
267
  }) {
268
268
  const __twcStyles = useScopedStyles("twc-avatar-styles", AVATAR_CSS);
269
- const [errored, setErrored] = React24__namespace.default.useState(false);
269
+ const [errored, setErrored] = React25__namespace.default.useState(false);
270
270
  const cleanSrc = safeSrc(src);
271
271
  const showImg = cleanSrc && !errored;
272
272
  const _statusLabel = status ? statusLabel ?? statusLabels?.[status] ?? STATUS_LABELS[status] : void 0;
@@ -291,7 +291,7 @@ function Avatar({
291
291
  var MENU_CSS = `
292
292
  .twc-menu-wrap { position: relative; display: inline-flex; }
293
293
  .twc-menu {
294
- position: fixed; z-index: var(--z-dropdown); min-width: 200px;
294
+ position: fixed; z-index: var(--z-popover); min-width: 200px;
295
295
  padding: var(--space-1-5); background: var(--color-surface-raised);
296
296
  border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-lg);
297
297
  box-shadow: var(--shadow-lg); font-family: var(--font-sans);
@@ -344,23 +344,25 @@ function Menu({
344
344
  ...rest
345
345
  }) {
346
346
  const __twcStyles = useScopedStyles("twc-menu-styles", MENU_CSS);
347
- const [openState, setOpenState] = React24__namespace.default.useState(defaultOpen);
348
- const [render, setRender] = React24__namespace.default.useState(false);
349
- const [pos, setPos] = React24__namespace.default.useState(null);
350
- const [active, setActive] = React24__namespace.default.useState(-1);
351
- const wrapRef = React24__namespace.default.useRef(null);
352
- const menuRef = React24__namespace.default.useRef(null);
353
- const menuId = React24__namespace.default.useId();
347
+ const [openState, setOpenState] = React25__namespace.default.useState(defaultOpen);
348
+ const [render, setRender] = React25__namespace.default.useState(false);
349
+ const [pos, setPos] = React25__namespace.default.useState(null);
350
+ const [active, setActive] = React25__namespace.default.useState(-1);
351
+ const wrapRef = React25__namespace.default.useRef(null);
352
+ const menuRef = React25__namespace.default.useRef(null);
353
+ const typeBufRef = React25__namespace.default.useRef("");
354
+ const typeTimerRef = React25__namespace.default.useRef(null);
355
+ const menuId = React25__namespace.default.useId();
354
356
  const headerId = `${menuId}-header`;
355
357
  const controlled = openProp !== void 0;
356
358
  const open = controlled ? !!openProp : openState;
357
- const openRef = React24__namespace.default.useRef(open);
359
+ const openRef = React25__namespace.default.useRef(open);
358
360
  openRef.current = open;
359
- const controlledRef = React24__namespace.default.useRef(controlled);
361
+ const controlledRef = React25__namespace.default.useRef(controlled);
360
362
  controlledRef.current = controlled;
361
- const onOpenChangeRef = React24__namespace.default.useRef(onOpenChange);
363
+ const onOpenChangeRef = React25__namespace.default.useRef(onOpenChange);
362
364
  onOpenChangeRef.current = onOpenChange;
363
- const setOpen = React24__namespace.default.useCallback((next) => {
365
+ const setOpen = React25__namespace.default.useCallback((next) => {
364
366
  const value = typeof next === "function" ? next(openRef.current) : next;
365
367
  if (value === openRef.current) return;
366
368
  if (!controlledRef.current) setOpenState(value);
@@ -368,7 +370,7 @@ function Menu({
368
370
  }, []);
369
371
  const RD = { createPortal: typeof reactDom.createPortal === "function" ? reactDom.createPortal : typeof window !== "undefined" && window.ReactDOM && window.ReactDOM.createPortal };
370
372
  const interactiveIdx = items.map((it, i) => !it.separator && !it.heading ? i : -1).filter((i) => i >= 0);
371
- const place = React24__namespace.default.useCallback(() => {
373
+ const place = React25__namespace.default.useCallback(() => {
372
374
  const t = wrapRef.current;
373
375
  if (!t) return;
374
376
  const r = t.getBoundingClientRect();
@@ -383,7 +385,7 @@ function Menu({
383
385
  const avail = (flip ? r.top : vh - r.bottom) - gap - M;
384
386
  setPos({ top, bottom, left, width: w, flip, maxHeight: Math.max(120, avail) });
385
387
  }, [align, width]);
386
- React24__namespace.default.useEffect(() => {
388
+ React25__namespace.default.useEffect(() => {
387
389
  if (!open) return;
388
390
  place();
389
391
  const onMove = () => place();
@@ -401,7 +403,7 @@ function Menu({
401
403
  document.removeEventListener("mousedown", onDown);
402
404
  };
403
405
  }, [open, place]);
404
- React24__namespace.default.useEffect(() => {
406
+ React25__namespace.default.useEffect(() => {
405
407
  if (open) {
406
408
  setRender(true);
407
409
  return;
@@ -418,7 +420,7 @@ function Menu({
418
420
  if (e.key === "Enter" || e.key === " " || e.key === "ArrowDown") {
419
421
  e.preventDefault();
420
422
  setOpen(true);
421
- setActive(-1);
423
+ setActive(interactiveIdx[0] ?? -1);
422
424
  }
423
425
  return;
424
426
  }
@@ -439,6 +441,24 @@ function Menu({
439
441
  if (next < 0) next = list.length - 1;
440
442
  if (next >= list.length) next = 0;
441
443
  setActive(list[next]);
444
+ } else if (e.key === "PageDown" || e.key === "PageUp") {
445
+ e.preventDefault();
446
+ const list = interactiveIdx;
447
+ if (list.length) {
448
+ const curPos = Math.max(0, list.indexOf(active));
449
+ const next = Math.min(list.length - 1, Math.max(0, curPos + (e.key === "PageDown" ? 5 : -5)));
450
+ setActive(list[next]);
451
+ }
452
+ } else if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
453
+ typeBufRef.current += e.key.toLowerCase();
454
+ clearTimeout(typeTimerRef.current);
455
+ typeTimerRef.current = setTimeout(() => {
456
+ typeBufRef.current = "";
457
+ }, 500);
458
+ const b = typeBufRef.current;
459
+ const labelOf = (i) => String(items[i]?.label ?? "").toLowerCase();
460
+ const hit = interactiveIdx.find((i) => labelOf(i).startsWith(b));
461
+ if (hit != null) setActive(hit);
442
462
  } else if (e.key === "Home" || e.key === "End") {
443
463
  e.preventDefault();
444
464
  const list = interactiveIdx;
@@ -466,12 +486,13 @@ function Menu({
466
486
  "data-align": align,
467
487
  "data-flip": pos.flip || void 0,
468
488
  role: "menu",
489
+ "aria-orientation": "vertical",
469
490
  "aria-label": ariaLabel,
470
491
  "aria-labelledby": ariaLabelledby ?? (ariaLabel ? void 0 : header ? headerId : void 0),
471
492
  style: { top: pos.top, bottom: pos.bottom, left: pos.left, minWidth: pos.width, width: pos.width, maxHeight: pos.maxHeight },
472
493
  children: [
473
494
  __twcStyles,
474
- header ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-menu__header", id: headerId, children: header }) : null,
495
+ header ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-menu__header", id: headerId, role: "presentation", children: header }) : null,
475
496
  items.map((it, i) => {
476
497
  if (it.separator) return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-menu__sep", role: "separator" }, `s${i}`);
477
498
  if (it.label && it.heading) return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-menu__label", children: it.label }, `h${i}`);
@@ -505,7 +526,7 @@ function Menu({
505
526
  }
506
527
  ) : null;
507
528
  const activeDescId = open && active >= 0 ? `${menuId}-item-${active}` : void 0;
508
- const triggerEl = React24__namespace.default.isValidElement(trigger) ? React24__namespace.default.cloneElement(trigger, {
529
+ const triggerEl = React25__namespace.default.isValidElement(trigger) ? React25__namespace.default.cloneElement(trigger, {
509
530
  onClick: (e) => {
510
531
  trigger.props.onClick?.(e);
511
532
  toggle();
@@ -645,8 +666,9 @@ function Badge({
645
666
  }
646
667
  var BREADCRUMB_CSS = `
647
668
  .twc-breadcrumb { display: flex; align-items: center; flex-wrap: wrap; gap: 2px; font-family: var(--font-sans); font-size: var(--text-sm); }
648
- .twc-breadcrumb__item { display: inline-flex; align-items: center; gap: 6px; color: var(--color-text-muted); text-decoration: none; padding: 3px 7px; border-radius: var(--radius-sm); transition: color var(--duration-fast) var(--ease-standard), background-color var(--duration-fast) var(--ease-standard); }
649
- .twc-breadcrumb__item:hover:not([aria-current]) { color: var(--color-text); background: var(--color-surface-sunken); }
669
+ .twc-breadcrumb__item { display: inline-flex; align-items: center; gap: 6px; color: var(--color-text-muted); text-decoration: none; padding: 3px 7px; border: 0; background: transparent; font: inherit; border-radius: var(--radius-sm); transition: color var(--duration-fast) var(--ease-standard), background-color var(--duration-fast) var(--ease-standard); }
670
+ a.twc-breadcrumb__item, button.twc-breadcrumb__item { cursor: pointer; }
671
+ a.twc-breadcrumb__item:hover, button.twc-breadcrumb__item:hover { color: var(--color-text); background: var(--color-surface-sunken); }
650
672
  .twc-breadcrumb__item[aria-current="page"] { color: var(--color-text); font-weight: var(--font-semibold); cursor: default; }
651
673
  .twc-breadcrumb__item svg { width: 15px; height: 15px; flex: none; }
652
674
  .twc-breadcrumb__sep { display: inline-flex; align-items: center; color: var(--color-text-subtle); pointer-events: none; }
@@ -669,7 +691,7 @@ function Breadcrumb({
669
691
  ...rest
670
692
  }) {
671
693
  const __twcStyles = useScopedStyles("twc-breadcrumb-styles", BREADCRUMB_CSS);
672
- const [expanded, setExpanded] = React24__namespace.default.useState(false);
694
+ const [expanded, setExpanded] = React25__namespace.default.useState(false);
673
695
  const sep = separator || /* @__PURE__ */ jsxRuntime.jsx(ChevronSep, {});
674
696
  let display = items;
675
697
  if (maxItems > 0 && items.length > maxItems && !expanded) {
@@ -682,7 +704,7 @@ function Breadcrumb({
682
704
  __twcStyles,
683
705
  display.map((it, i) => {
684
706
  const last = i === display.length - 1;
685
- return /* @__PURE__ */ jsxRuntime.jsxs(React24__namespace.default.Fragment, { children: [
707
+ return /* @__PURE__ */ jsxRuntime.jsxs(React25__namespace.default.Fragment, { children: [
686
708
  it.ellipsis ? /* @__PURE__ */ jsxRuntime.jsx("button", { className: "twc-breadcrumb__ellipsis", "aria-label": "Show more", onClick: () => setExpanded(true), children: "\u2026" }) : last ? /* @__PURE__ */ jsxRuntime.jsxs(
687
709
  "span",
688
710
  {
@@ -694,17 +716,18 @@ function Breadcrumb({
694
716
  it.label
695
717
  ]
696
718
  }
697
- ) : /* @__PURE__ */ jsxRuntime.jsxs(
698
- "a",
699
- {
700
- className: "twc-breadcrumb__item",
701
- href: safeHref2(it.href) || "#",
702
- onClick: it.onClick,
703
- children: [
704
- it.icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", style: { display: "inline-flex" }, children: it.icon }) : null,
705
- it.label
706
- ]
707
- }
719
+ ) : safeHref2(it.href) ? /* @__PURE__ */ jsxRuntime.jsxs("a", { className: "twc-breadcrumb__item", href: safeHref2(it.href), onClick: it.onClick, children: [
720
+ it.icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", style: { display: "inline-flex" }, children: it.icon }) : null,
721
+ it.label
722
+ ] }) : it.onClick ? /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", className: "twc-breadcrumb__item", onClick: it.onClick, children: [
723
+ it.icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", style: { display: "inline-flex" }, children: it.icon }) : null,
724
+ it.label
725
+ ] }) : (
726
+ // #46: no href and no onClick → inert plain-text crumb (not a fake <a href="#">).
727
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "twc-breadcrumb__item", children: [
728
+ it.icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", style: { display: "inline-flex" }, children: it.icon }) : null,
729
+ it.label
730
+ ] })
708
731
  ),
709
732
  !last ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-breadcrumb__sep", "aria-hidden": "true", children: sep }) : null
710
733
  ] }, i);
@@ -823,7 +846,7 @@ function Button({
823
846
  ...rest
824
847
  }) {
825
848
  const __twcStyles = useScopedStyles("twc-btn-styles", CSS2);
826
- const [ripples, setRipples] = React24__namespace.default.useState([]);
849
+ const [ripples, setRipples] = React25__namespace.default.useState([]);
827
850
  const Tag2 = as;
828
851
  const inert = Tag2 === "a" && (disabled || loading);
829
852
  function handleClick(e) {
@@ -899,7 +922,9 @@ var CARD_CSS = `
899
922
  .twc-card[data-pad="md"] { padding: var(--space-5); }
900
923
  .twc-card[data-pad="lg"] { padding: var(--space-6); }
901
924
  .twc-card[data-pad="none"] { padding: 0; }
902
- .twc-card__header { display: flex; flex-direction: column; gap: 2px; margin-bottom: var(--space-3); }
925
+ .twc-card__header { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-3); margin-bottom: var(--space-3); }
926
+ .twc-card__heading { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
927
+ .twc-card__actions { flex: none; display: flex; align-items: center; gap: var(--space-2); }
903
928
  .twc-card__title { font-size: var(--text-lg); font-weight: var(--font-bold); letter-spacing: -0.01em; }
904
929
  .twc-card__subtitle { font-size: var(--text-sm); color: var(--color-text-muted); }
905
930
  .twc-card__body { font-size: var(--text-sm); color: var(--color-text-muted); line-height: var(--leading-normal); }
@@ -909,6 +934,7 @@ function Card({
909
934
  children,
910
935
  title,
911
936
  subtitle,
937
+ actions,
912
938
  footer,
913
939
  variant = "elevated",
914
940
  padding = "md",
@@ -929,9 +955,12 @@ function Card({
929
955
  ...rest,
930
956
  children: [
931
957
  __twcStyles,
932
- title || subtitle ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-card__header", children: [
933
- title ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-card__title", children: title }) : null,
934
- subtitle ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-card__subtitle", children: subtitle }) : null
958
+ title || subtitle || actions ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-card__header", children: [
959
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-card__heading", children: [
960
+ title ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-card__title", children: title }) : null,
961
+ subtitle ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-card__subtitle", children: subtitle }) : null
962
+ ] }),
963
+ actions ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-card__actions", children: actions }) : null
935
964
  ] }) : null,
936
965
  children ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-card__body", children }) : null,
937
966
  footer ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-card__footer", children: footer }) : null
@@ -991,14 +1020,14 @@ function Carousel({
991
1020
  ...rest
992
1021
  }) {
993
1022
  const __twcStyles = useScopedStyles("twc-carousel-styles", CAROUSEL_CSS);
994
- const slides = React24__namespace.default.Children.toArray(children);
1023
+ const slides = React25__namespace.default.Children.toArray(children);
995
1024
  const count = slides.length;
996
- const [internal, setInternal] = React24__namespace.default.useState(defaultIndex);
1025
+ const [internal, setInternal] = React25__namespace.default.useState(defaultIndex);
997
1026
  const index = indexProp !== void 0 ? indexProp : internal;
998
- const [paused, setPaused] = React24__namespace.default.useState(false);
999
- const [userPaused, setUserPaused] = React24__namespace.default.useState(false);
1000
- const [prefersReduced, setPrefersReduced] = React24__namespace.default.useState(false);
1001
- React24__namespace.default.useEffect(() => {
1027
+ const [paused, setPaused] = React25__namespace.default.useState(false);
1028
+ const [userPaused, setUserPaused] = React25__namespace.default.useState(false);
1029
+ const [prefersReduced, setPrefersReduced] = React25__namespace.default.useState(false);
1030
+ React25__namespace.default.useEffect(() => {
1002
1031
  if (typeof window === "undefined" || !window.matchMedia) return void 0;
1003
1032
  const mql = window.matchMedia("(prefers-reduced-motion: reduce)");
1004
1033
  const on = () => setPrefersReduced(mql.matches);
@@ -1006,15 +1035,15 @@ function Carousel({
1006
1035
  mql.addEventListener("change", on);
1007
1036
  return () => mql.removeEventListener("change", on);
1008
1037
  }, []);
1009
- const setIndex = React24__namespace.default.useCallback((i) => {
1038
+ const setIndex = React25__namespace.default.useCallback((i) => {
1010
1039
  if (indexProp === void 0) setInternal(i);
1011
1040
  onIndexChange?.(i);
1012
1041
  }, [indexProp, onIndexChange]);
1013
- const go = React24__namespace.default.useCallback((i) => {
1042
+ const go = React25__namespace.default.useCallback((i) => {
1014
1043
  setIndex(loop ? (i + count) % count : Math.min(Math.max(i, 0), count - 1));
1015
1044
  }, [count, loop, setIndex]);
1016
1045
  const autoRotating = autoPlay && !paused && !userPaused && !prefersReduced && count > 1;
1017
- React24__namespace.default.useEffect(() => {
1046
+ React25__namespace.default.useEffect(() => {
1018
1047
  if (!autoRotating) return void 0;
1019
1048
  const t = setInterval(() => {
1020
1049
  const next = loop ? (index + 1) % count : index + 1 >= count ? index : index + 1;
@@ -1109,7 +1138,7 @@ function Chart({
1109
1138
  ...rest
1110
1139
  }) {
1111
1140
  const __twcStyles = useScopedStyles("twc-chart-styles", CHART_CSS);
1112
- const uid = React24__namespace.default.useId();
1141
+ const uid = React25__namespace.default.useId();
1113
1142
  const tableId = tableFallback ? `${uid}-table` : void 0;
1114
1143
  const keys = series || ["value"];
1115
1144
  const palette = colors && colors.length ? colors : SERIES_COLORS;
@@ -1201,6 +1230,15 @@ function shortNum(v) {
1201
1230
  if (v >= 1e3) return (v / 1e3).toFixed(v % 1e3 ? 1 : 0) + "k";
1202
1231
  return String(Math.round(v));
1203
1232
  }
1233
+
1234
+ // components/_warn.js
1235
+ var seen = /* @__PURE__ */ new Set();
1236
+ function warnOnce(key, message) {
1237
+ if (typeof process !== "undefined" && process.env && process.env.NODE_ENV === "production") return;
1238
+ if (seen.has(key)) return;
1239
+ seen.add(key);
1240
+ if (typeof console !== "undefined" && console.warn) console.warn(message);
1241
+ }
1204
1242
  function Checkbox({
1205
1243
  label,
1206
1244
  description,
@@ -1256,14 +1294,18 @@ function Checkbox({
1256
1294
  .twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
1257
1295
  .twc-field__error { font-size: var(--text-xs); color: var(--color-danger-subtle-fg); font-weight: var(--font-medium); }
1258
1296
  `);
1259
- const autoId = React24__namespace.default.useId();
1297
+ const autoId = React25__namespace.default.useId();
1260
1298
  const fieldId = id || autoId;
1261
1299
  const errId = `${fieldId}-error`;
1262
1300
  const invalid = Boolean(error);
1263
- const ref = React24__namespace.default.useRef(null);
1264
- React24__namespace.default.useEffect(() => {
1265
- if (ref.current) ref.current.indeterminate = indeterminate;
1266
- }, [indeterminate]);
1301
+ const ref = React25__namespace.default.useRef(null);
1302
+ const mixed = indeterminate && !checked;
1303
+ React25__namespace.default.useEffect(() => {
1304
+ if (ref.current) ref.current.indeterminate = mixed;
1305
+ }, [mixed]);
1306
+ if (process.env.NODE_ENV !== "production" && !label && !description && !rest["aria-label"] && !rest["aria-labelledby"]) {
1307
+ warnOnce("Checkbox.no-name", "Checkbox: no accessible name \u2014 pass `label`, or `aria-label`/`aria-labelledby` for a label-less control (WCAG 4.1.2).");
1308
+ }
1267
1309
  const describedBy = [rest["aria-describedby"], error ? errId : null].filter(Boolean).join(" ") || void 0;
1268
1310
  const control = /* @__PURE__ */ jsxRuntime.jsxs("label", { className: `twc-check ${className}`, "data-size": size, "data-tone": tone, "data-invalid": invalid || void 0, "data-disabled": disabled || void 0, htmlFor: fieldId, children: [
1269
1311
  __twcStyles,
@@ -1279,12 +1321,14 @@ function Checkbox({
1279
1321
  disabled,
1280
1322
  onChange,
1281
1323
  ...rest,
1324
+ required: required || void 0,
1282
1325
  "aria-required": required || void 0,
1326
+ "aria-checked": mixed ? "mixed" : void 0,
1283
1327
  "aria-invalid": invalid || void 0,
1284
1328
  "aria-describedby": describedBy
1285
1329
  }
1286
1330
  ),
1287
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-check__box", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "twc-check__svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "3.5", strokeLinecap: "round", strokeLinejoin: "round", children: indeterminate ? /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 12h14" }) : /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M20 6 9 17l-5-5" }) }) }),
1331
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-check__box", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "twc-check__svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "3.5", strokeLinecap: "round", strokeLinejoin: "round", children: mixed ? /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 12h14" }) : /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M20 6 9 17l-5-5" }) }) }),
1288
1332
  label || description ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "twc-check__text", children: [
1289
1333
  label ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-check__label", children: label }) : null,
1290
1334
  description ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-check__desc", children: description }) : null
@@ -1340,6 +1384,7 @@ var COLORPICKER_CSS = `
1340
1384
  .twc-cp__hex[data-tone="info"] { --_accent: var(--color-info); --_ring: 0 0 0 var(--ring-width) color-mix(in srgb, var(--color-info) 32%, transparent); }
1341
1385
  .twc-cp__hex[data-tone="neutral"] { --_accent: var(--color-border-strong); --_ring: 0 0 0 var(--ring-width) color-mix(in srgb, var(--color-text) 14%, transparent); }
1342
1386
  .twc-cp__hex:focus { border-color: var(--_accent); box-shadow: var(--_ring); }
1387
+ .twc-cp__hex[data-invalid="true"] { border-color: var(--color-danger); }
1343
1388
  .twc-cp__presets { display: flex; flex-wrap: wrap; gap: 5px; margin-top: var(--space-3); }
1344
1389
  .twc-cp__preset { width: 22px; height: 22px; border-radius: var(--radius-sm); border: none; cursor: pointer; box-shadow: inset 0 0 0 1px rgb(0 0 0 / 0.12); padding: 0;
1345
1390
  transition: transform var(--duration-fast) var(--ease-spring); }
@@ -1398,20 +1443,20 @@ function ColorPicker({
1398
1443
  }) {
1399
1444
  const __twcStyles0 = useScopedStyles("twc-field-styles", FIELD_CSS);
1400
1445
  const __twcStyles1 = useScopedStyles("twc-colorpicker-styles", COLORPICKER_CSS);
1401
- const [internal, setInternal] = React24__namespace.default.useState(defaultValue);
1446
+ const [internal, setInternal] = React25__namespace.default.useState(defaultValue);
1402
1447
  const hex = (value !== void 0 ? value : internal) || "#000000";
1403
- const lastValidRef = React24__namespace.default.useRef(hex);
1448
+ const lastValidRef = React25__namespace.default.useRef(hex);
1404
1449
  if (/^#[0-9a-f]{6}$/i.test(hex)) lastValidRef.current = hex;
1405
- const [open, setOpen] = React24__namespace.default.useState(false);
1406
- const [hsv, setHsv] = React24__namespace.default.useState(() => hexToHsv(hex) || { h: 239, s: 60, v: 94 });
1407
- const [coords, setCoords] = React24__namespace.default.useState(null);
1408
- const wrapRef = React24__namespace.default.useRef(null);
1409
- const triggerRef = React24__namespace.default.useRef(null);
1410
- const popRef = React24__namespace.default.useRef(null);
1411
- const areaRef = React24__namespace.default.useRef(null);
1412
- const hueRef = React24__namespace.default.useRef(null);
1450
+ const [open, setOpen] = React25__namespace.default.useState(false);
1451
+ const [hsv, setHsv] = React25__namespace.default.useState(() => hexToHsv(hex) || { h: 239, s: 60, v: 94 });
1452
+ const [coords, setCoords] = React25__namespace.default.useState(null);
1453
+ const wrapRef = React25__namespace.default.useRef(null);
1454
+ const triggerRef = React25__namespace.default.useRef(null);
1455
+ const popRef = React25__namespace.default.useRef(null);
1456
+ const areaRef = React25__namespace.default.useRef(null);
1457
+ const hueRef = React25__namespace.default.useRef(null);
1413
1458
  const POP_W = 240;
1414
- const place = React24__namespace.default.useCallback(() => {
1459
+ const place = React25__namespace.default.useCallback(() => {
1415
1460
  const el = triggerRef.current;
1416
1461
  if (!el) return;
1417
1462
  const r = el.getBoundingClientRect();
@@ -1422,7 +1467,7 @@ function ColorPicker({
1422
1467
  left = Math.max(8, left);
1423
1468
  setCoords({ left, top: flip ? void 0 : Math.round(r.bottom + 6), bottom: flip ? Math.round(vh - r.top + 6) : void 0 });
1424
1469
  }, []);
1425
- React24__namespace.default.useEffect(() => {
1470
+ React25__namespace.default.useEffect(() => {
1426
1471
  if (!open) return void 0;
1427
1472
  place();
1428
1473
  const onMove = () => place();
@@ -1433,17 +1478,17 @@ function ColorPicker({
1433
1478
  window.removeEventListener("resize", onMove);
1434
1479
  };
1435
1480
  }, [open, place]);
1436
- const labelId = React24__namespace.default.useId();
1481
+ const labelId = React25__namespace.default.useId();
1437
1482
  const descId = `${labelId}-desc`;
1438
1483
  const invalid = Boolean(error);
1439
- React24__namespace.default.useEffect(() => {
1484
+ React25__namespace.default.useEffect(() => {
1440
1485
  if (disabled) setOpen(false);
1441
1486
  }, [disabled]);
1442
- React24__namespace.default.useEffect(() => {
1487
+ React25__namespace.default.useEffect(() => {
1443
1488
  const v = hexToHsv(hex);
1444
1489
  if (v && hsvToHex(v.h, v.s, v.v).toLowerCase() !== hsvToHex(hsv.h, hsv.s, hsv.v).toLowerCase()) setHsv(v);
1445
1490
  }, [hex]);
1446
- React24__namespace.default.useEffect(() => {
1491
+ React25__namespace.default.useEffect(() => {
1447
1492
  if (!open) return;
1448
1493
  const onDown = (e) => {
1449
1494
  if (wrapRef.current && wrapRef.current.contains(e.target)) return;
@@ -1460,8 +1505,8 @@ function ColorPicker({
1460
1505
  document.removeEventListener("keydown", onKey);
1461
1506
  };
1462
1507
  }, [open]);
1463
- const prevOpen = React24__namespace.default.useRef(false);
1464
- React24__namespace.default.useEffect(() => {
1508
+ const prevOpen = React25__namespace.default.useRef(false);
1509
+ React25__namespace.default.useEffect(() => {
1465
1510
  let id;
1466
1511
  if (!prevOpen.current && open) {
1467
1512
  id = setTimeout(() => areaRef.current?.focus({ preventScroll: true }), 20);
@@ -1615,6 +1660,8 @@ function ColorPicker({
1615
1660
  value: hex,
1616
1661
  maxLength: 7,
1617
1662
  "aria-label": "Hex color",
1663
+ "aria-invalid": !/^#[0-9a-f]{6}$/i.test(hex) || void 0,
1664
+ "data-invalid": !/^#[0-9a-f]{6}$/i.test(hex) || void 0,
1618
1665
  onChange: (e) => {
1619
1666
  let v = e.target.value;
1620
1667
  if (!v.startsWith("#")) v = "#" + v;
@@ -1725,6 +1772,9 @@ var COMBO_CSS = `
1725
1772
  .twc-opt__check { flex: none; color: var(--color-primary); display: inline-flex; }
1726
1773
  .twc-opt__check svg { width: 16px; height: 16px; }
1727
1774
  .twc-pop__empty { padding: 14px 12px; text-align: center; font-size: var(--text-sm); color: var(--color-text-subtle); }
1775
+ .twc-opt[data-disabled="true"] { opacity: 0.45; cursor: not-allowed; pointer-events: none; }
1776
+ .twc-pop__loading { display: flex; align-items: center; justify-content: center; gap: var(--space-2); padding: 14px 12px; font-size: var(--text-sm); color: var(--color-text-muted); }
1777
+ .twc-pop__spinner { width: 15px; height: 15px; border-radius: var(--radius-full); border: 2px solid var(--color-border); border-top-color: var(--color-primary); animation: twico-spin 0.7s linear infinite; }
1728
1778
  `;
1729
1779
  function toOpt(o) {
1730
1780
  return typeof o === "string" ? { value: o, label: o } : o;
@@ -1774,6 +1824,11 @@ function Combobox({
1774
1824
  placement = "bottom",
1775
1825
  portal = true,
1776
1826
  minWidth = 0,
1827
+ onInputChange,
1828
+ filter,
1829
+ loading = false,
1830
+ emptyText = "No results found",
1831
+ name,
1777
1832
  id,
1778
1833
  className = "",
1779
1834
  onFocus,
@@ -1781,29 +1836,38 @@ function Combobox({
1781
1836
  ...rest
1782
1837
  }) {
1783
1838
  const __twcStyles = useScopedStyles("twc-combo-styles", COMBO_CSS);
1784
- const groups = React24__namespace.default.useMemo(() => normalizeGroups(options), [options]);
1785
- const flat = React24__namespace.default.useMemo(() => groups.flatMap((g) => g.options), [groups]);
1786
- const autoId = React24__namespace.default.useId();
1839
+ const groups = React25__namespace.default.useMemo(() => normalizeGroups(options), [options]);
1840
+ const flat = React25__namespace.default.useMemo(() => groups.flatMap((g) => g.options), [groups]);
1841
+ const autoId = React25__namespace.default.useId();
1787
1842
  const fieldId = id || autoId;
1788
- const [internal, setInternal] = React24__namespace.default.useState(defaultValue);
1843
+ const [internal, setInternal] = React25__namespace.default.useState(defaultValue);
1789
1844
  const current = value !== void 0 ? value : internal;
1790
1845
  const selected = flat.find((o) => o.value === current);
1791
- const [open, setOpen] = React24__namespace.default.useState(false);
1792
- const [query, setQuery] = React24__namespace.default.useState("");
1793
- const [active, setActive] = React24__namespace.default.useState(0);
1794
- const [coords, setCoords] = React24__namespace.default.useState(null);
1795
- const wrapRef = React24__namespace.default.useRef(null);
1796
- const controlRef = React24__namespace.default.useRef(null);
1797
- const inputRef = React24__namespace.default.useRef(null);
1798
- const popRef = React24__namespace.default.useRef(null);
1799
- const listRef = React24__namespace.default.useRef(null);
1800
- const activeRef = React24__namespace.default.useRef(null);
1801
- const fGroups = React24__namespace.default.useMemo(
1802
- () => groups.map((g) => ({ group: g.group, options: g.options.filter((o) => matches(o, query.trim())) })).filter((g) => g.options.length),
1803
- [groups, query]
1804
- );
1805
- const visible = React24__namespace.default.useMemo(() => fGroups.flatMap((g) => g.options), [fGroups]);
1806
- const place = React24__namespace.default.useCallback(() => {
1846
+ const [open, setOpen] = React25__namespace.default.useState(false);
1847
+ const [query, setQuery] = React25__namespace.default.useState("");
1848
+ const [active, setActive] = React25__namespace.default.useState(0);
1849
+ const [coords, setCoords] = React25__namespace.default.useState(null);
1850
+ const wrapRef = React25__namespace.default.useRef(null);
1851
+ const controlRef = React25__namespace.default.useRef(null);
1852
+ const inputRef = React25__namespace.default.useRef(null);
1853
+ const popRef = React25__namespace.default.useRef(null);
1854
+ const listRef = React25__namespace.default.useRef(null);
1855
+ const activeRef = React25__namespace.default.useRef(null);
1856
+ const fGroups = React25__namespace.default.useMemo(() => {
1857
+ const q = query.trim();
1858
+ const fn = filter === false ? null : typeof filter === "function" ? filter : matches;
1859
+ return groups.map((g) => ({ group: g.group, options: fn ? g.options.filter((o) => fn(o, q)) : g.options })).filter((g) => g.options.length);
1860
+ }, [groups, query, filter]);
1861
+ const visible = React25__namespace.default.useMemo(() => fGroups.flatMap((g) => g.options), [fGroups]);
1862
+ const nextEnabled = (from, dir) => {
1863
+ let i = from + dir;
1864
+ while (i >= 0 && i < visible.length) {
1865
+ if (!visible[i]?.disabled) return i;
1866
+ i += dir;
1867
+ }
1868
+ return from;
1869
+ };
1870
+ const place = React25__namespace.default.useCallback(() => {
1807
1871
  const el = controlRef.current;
1808
1872
  if (!el) return;
1809
1873
  const r = el.getBoundingClientRect();
@@ -1818,7 +1882,7 @@ function Combobox({
1818
1882
  flip
1819
1883
  });
1820
1884
  }, [placement, minWidth]);
1821
- React24__namespace.default.useEffect(() => {
1885
+ React25__namespace.default.useEffect(() => {
1822
1886
  if (!open || !portal) return;
1823
1887
  place();
1824
1888
  const onMove = () => place();
@@ -1829,7 +1893,7 @@ function Combobox({
1829
1893
  window.removeEventListener("resize", onMove);
1830
1894
  };
1831
1895
  }, [open, portal, place]);
1832
- React24__namespace.default.useEffect(() => {
1896
+ React25__namespace.default.useEffect(() => {
1833
1897
  if (!open) return;
1834
1898
  const onDown = (e) => {
1835
1899
  if (wrapRef.current && wrapRef.current.contains(e.target)) return;
@@ -1839,10 +1903,15 @@ function Combobox({
1839
1903
  document.addEventListener("mousedown", onDown);
1840
1904
  return () => document.removeEventListener("mousedown", onDown);
1841
1905
  }, [open]);
1842
- React24__namespace.default.useEffect(() => {
1906
+ React25__namespace.default.useEffect(() => {
1843
1907
  setActive(0);
1844
1908
  }, [query]);
1845
- React24__namespace.default.useEffect(() => {
1909
+ React25__namespace.default.useEffect(() => {
1910
+ if (!open) return;
1911
+ const idx = flat.findIndex((o) => o.value === current);
1912
+ setActive(idx >= 0 ? idx : 0);
1913
+ }, [open]);
1914
+ React25__namespace.default.useEffect(() => {
1846
1915
  if (open) ensureVisible(listRef.current, activeRef.current);
1847
1916
  }, [active, open]);
1848
1917
  function openMenu() {
@@ -1855,6 +1924,8 @@ function Combobox({
1855
1924
  setQuery("");
1856
1925
  }
1857
1926
  function commit(v) {
1927
+ const o = flat.find((x) => x.value === v);
1928
+ if (o && o.disabled) return;
1858
1929
  if (value === void 0) setInternal(v);
1859
1930
  onChange?.(v);
1860
1931
  close();
@@ -1866,17 +1937,20 @@ function Combobox({
1866
1937
  openMenu();
1867
1938
  return;
1868
1939
  }
1869
- setActive((a) => Math.min(a + 1, visible.length - 1));
1940
+ setActive((a) => nextEnabled(a, 1));
1870
1941
  } else if (e.key === "ArrowUp") {
1871
1942
  e.preventDefault();
1872
- setActive((a) => Math.max(a - 1, 0));
1943
+ setActive((a) => nextEnabled(a, -1));
1873
1944
  } else if (e.key === "Enter") {
1874
1945
  e.preventDefault();
1875
1946
  if (open && visible[active]) commit(visible[active].value);
1876
1947
  } else if (e.key === "Escape") {
1877
1948
  close();
1878
1949
  inputRef.current?.blur();
1879
- } else if (e.key === "Backspace" && !query && selected) ;
1950
+ } else if (e.key === "Backspace" && !query && selected) {
1951
+ e.preventDefault();
1952
+ commit(null);
1953
+ }
1880
1954
  }
1881
1955
  const displayValue = open ? query : selected ? selected.label : "";
1882
1956
  const listboxId = `${fieldId}-listbox`;
@@ -1885,7 +1959,10 @@ function Combobox({
1885
1959
  const descId = `${fieldId}-desc`;
1886
1960
  const describedBy = error || hint ? descId : void 0;
1887
1961
  let counter = -1;
1888
- const popInner = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-pop__list", ref: listRef, children: visible.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-pop__empty", children: "No results found" }) : fGroups.map((g, gi) => /* @__PURE__ */ jsxRuntime.jsxs(React24__namespace.default.Fragment, { children: [
1962
+ const popInner = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-pop__list", ref: listRef, children: loading ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-pop__loading", role: "status", children: [
1963
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-pop__spinner", "aria-hidden": "true" }),
1964
+ "Loading\u2026"
1965
+ ] }) : visible.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-pop__empty", children: emptyText }) : fGroups.map((g, gi) => /* @__PURE__ */ jsxRuntime.jsxs(React25__namespace.default.Fragment, { children: [
1889
1966
  g.group ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-pop__group", children: g.group }) : null,
1890
1967
  g.options.map((o) => {
1891
1968
  counter += 1;
@@ -1900,9 +1977,14 @@ function Combobox({
1900
1977
  role: "option",
1901
1978
  "aria-selected": isSel,
1902
1979
  ref: idx === active ? activeRef : null,
1980
+ disabled: o.disabled || void 0,
1981
+ "aria-disabled": o.disabled || void 0,
1982
+ "data-disabled": o.disabled || void 0,
1903
1983
  "data-selected": isSel || void 0,
1904
1984
  "data-active": idx === active || void 0,
1905
- onMouseEnter: () => setActive(idx),
1985
+ onMouseEnter: () => {
1986
+ if (!o.disabled) setActive(idx);
1987
+ },
1906
1988
  onMouseDown: (e) => e.preventDefault(),
1907
1989
  onClick: () => commit(o.value),
1908
1990
  children: [
@@ -1930,7 +2012,7 @@ function Combobox({
1930
2012
  id: listboxId,
1931
2013
  role: "listbox",
1932
2014
  ref: popRef,
1933
- "data-placement": coords.flip ? "top" : void 0,
2015
+ "data-placement": coords.flip ? "top" : "bottom",
1934
2016
  style: { position: "fixed", left: coords.left, top: coords.top, bottom: coords.bottom, width: coords.width, right: "auto", zIndex: "var(--z-tooltip)" },
1935
2017
  children: popInner
1936
2018
  }
@@ -1943,6 +2025,7 @@ function Combobox({
1943
2025
  }
1944
2026
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, ref: wrapRef, children: [
1945
2027
  __twcStyles,
2028
+ name ? /* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name, value: current ?? "" }) : null,
1946
2029
  label ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-field__label", htmlFor: fieldId, children: [
1947
2030
  label,
1948
2031
  required ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-field__req", children: "*" }) : null
@@ -1988,6 +2071,7 @@ function Combobox({
1988
2071
  onChange: (e) => {
1989
2072
  setQuery(e.target.value);
1990
2073
  setOpen(true);
2074
+ onInputChange?.(e.target.value);
1991
2075
  },
1992
2076
  onKeyDown: (e) => {
1993
2077
  onKeyDown?.(e);
@@ -2024,9 +2108,10 @@ function Combobox({
2024
2108
  ] });
2025
2109
  }
2026
2110
  var canUseDOM = typeof window !== "undefined" && typeof document !== "undefined";
2111
+ var useIsoLayoutEffect = canUseDOM ? React25__namespace.default.useLayoutEffect : React25__namespace.default.useEffect;
2027
2112
  var FOCUSABLE = 'a[href]:not([tabindex="-1"]), button:not([disabled]):not([tabindex="-1"]), textarea:not([disabled]):not([tabindex="-1"]), input:not([disabled]):not([tabindex="-1"]), select:not([disabled]):not([tabindex="-1"]), [tabindex]:not([tabindex="-1"])';
2028
2113
  function useFocusTrap(ref, active = true, { restoreFocus = true } = {}) {
2029
- React24__namespace.default.useEffect(() => {
2114
+ React25__namespace.default.useEffect(() => {
2030
2115
  if (!active || !canUseDOM) return void 0;
2031
2116
  const node = ref.current;
2032
2117
  if (!node) return void 0;
@@ -2037,7 +2122,7 @@ function useFocusTrap(ref, active = true, { restoreFocus = true } = {}) {
2037
2122
  if (restoreFocus && prevFocused && typeof prevFocused.focus === "function") prevFocused.focus();
2038
2123
  };
2039
2124
  }, [active]);
2040
- React24__namespace.default.useEffect(() => {
2125
+ React25__namespace.default.useEffect(() => {
2041
2126
  if (!active || !canUseDOM) return void 0;
2042
2127
  const node = ref.current;
2043
2128
  const onKey = (e) => {
@@ -2064,11 +2149,53 @@ function useFocusTrap(ref, active = true, { restoreFocus = true } = {}) {
2064
2149
  }, [active]);
2065
2150
  }
2066
2151
  function usePortal() {
2067
- return React24__namespace.default.useCallback((node) => {
2152
+ return React25__namespace.default.useCallback((node) => {
2068
2153
  if (typeof document === "undefined") return null;
2069
2154
  return typeof reactDom.createPortal === "function" ? reactDom.createPortal(node, document.body) : node;
2070
2155
  }, []);
2071
2156
  }
2157
+ var __scrollLockCount = 0;
2158
+ var __scrollLockSaved = { overflow: "", paddingRight: "" };
2159
+ function useScrollLock(locked = true) {
2160
+ useIsoLayoutEffect(() => {
2161
+ if (!canUseDOM || !locked) return void 0;
2162
+ if (__scrollLockCount === 0) {
2163
+ const b = document.body;
2164
+ __scrollLockSaved = { overflow: b.style.overflow, paddingRight: b.style.paddingRight };
2165
+ const gutter = window.innerWidth - document.documentElement.clientWidth;
2166
+ b.style.overflow = "hidden";
2167
+ if (gutter > 0) b.style.paddingRight = `${(parseFloat(getComputedStyle(b).paddingRight) || 0) + gutter}px`;
2168
+ }
2169
+ __scrollLockCount += 1;
2170
+ return () => {
2171
+ __scrollLockCount -= 1;
2172
+ if (__scrollLockCount === 0) {
2173
+ document.body.style.overflow = __scrollLockSaved.overflow;
2174
+ document.body.style.paddingRight = __scrollLockSaved.paddingRight;
2175
+ }
2176
+ };
2177
+ }, [locked]);
2178
+ }
2179
+ function useInertBackground(ref, active) {
2180
+ React25__namespace.default.useEffect(() => {
2181
+ if (!active || !canUseDOM) return void 0;
2182
+ const node = ref.current;
2183
+ if (!node) return void 0;
2184
+ const changed = [];
2185
+ Array.from(document.body.children).forEach((el) => {
2186
+ if (el.contains(node) || el.hasAttribute("aria-hidden") || el.inert) return;
2187
+ el.inert = true;
2188
+ el.setAttribute("aria-hidden", "true");
2189
+ changed.push(el);
2190
+ });
2191
+ return () => {
2192
+ changed.forEach((el) => {
2193
+ el.inert = false;
2194
+ el.removeAttribute("aria-hidden");
2195
+ });
2196
+ };
2197
+ }, [active]);
2198
+ }
2072
2199
  var COMMAND_CSS = `
2073
2200
  .twc-cmdk__overlay { position: fixed; inset: 0; z-index: var(--z-modal); background: var(--color-overlay); backdrop-filter: blur(3px);
2074
2201
  display: flex; align-items: flex-start; justify-content: center; padding-top: 12vh; }
@@ -2110,19 +2237,20 @@ function CommandPalette({
2110
2237
  commands,
2111
2238
  placeholder = "Type a command or search\u2026",
2112
2239
  emptyText = "No results found",
2240
+ searchLabel = "Command palette search",
2113
2241
  className = "",
2114
2242
  ...rest
2115
2243
  }) {
2116
2244
  const __twcStyles = useScopedStyles("twc-cmdk-styles", COMMAND_CSS);
2117
2245
  const renderPortal = usePortal();
2118
- const [query, setQuery] = React24__namespace.default.useState("");
2119
- const [active, setActive] = React24__namespace.default.useState(0);
2120
- const inputRef = React24__namespace.default.useRef(null);
2121
- const listRef = React24__namespace.default.useRef(null);
2122
- const paletteRef = React24__namespace.default.useRef(null);
2123
- const baseId = React24__namespace.default.useId();
2124
- const [mounted, setMounted] = React24__namespace.default.useState(open);
2125
- React24__namespace.default.useEffect(() => {
2246
+ const [query, setQuery] = React25__namespace.default.useState("");
2247
+ const [active, setActive] = React25__namespace.default.useState(0);
2248
+ const inputRef = React25__namespace.default.useRef(null);
2249
+ const listRef = React25__namespace.default.useRef(null);
2250
+ const paletteRef = React25__namespace.default.useRef(null);
2251
+ const baseId = React25__namespace.default.useId();
2252
+ const [mounted, setMounted] = React25__namespace.default.useState(open);
2253
+ React25__namespace.default.useEffect(() => {
2126
2254
  if (open) {
2127
2255
  setMounted(true);
2128
2256
  return;
@@ -2130,22 +2258,15 @@ function CommandPalette({
2130
2258
  const t = setTimeout(() => setMounted(false), 170);
2131
2259
  return () => clearTimeout(t);
2132
2260
  }, [open]);
2133
- React24__namespace.default.useEffect(() => {
2134
- if (!open) return void 0;
2135
- const prev = document.body.style.overflow;
2136
- document.body.style.overflow = "hidden";
2137
- return () => {
2138
- document.body.style.overflow = prev;
2139
- };
2140
- }, [open]);
2141
- React24__namespace.default.useEffect(() => {
2261
+ useScrollLock(open);
2262
+ React25__namespace.default.useEffect(() => {
2142
2263
  if (open) {
2143
2264
  setQuery("");
2144
2265
  setActive(0);
2145
2266
  }
2146
2267
  }, [open]);
2147
2268
  useFocusTrap(paletteRef, open && mounted);
2148
- React24__namespace.default.useEffect(() => {
2269
+ React25__namespace.default.useEffect(() => {
2149
2270
  if (!open) return void 0;
2150
2271
  const onKey = (e) => {
2151
2272
  if (e.key === "Escape") {
@@ -2157,7 +2278,7 @@ function CommandPalette({
2157
2278
  return () => document.removeEventListener("keydown", onKey);
2158
2279
  }, [open, onClose]);
2159
2280
  const q = query.trim().toLowerCase();
2160
- const filtered = React24__namespace.default.useMemo(() => commands.filter(
2281
+ const filtered = React25__namespace.default.useMemo(() => commands.filter(
2161
2282
  (c) => !q || c.label.toLowerCase().includes(q) || (c.keywords || "").toLowerCase().includes(q) || (c.group || "").toLowerCase().includes(q)
2162
2283
  ), [commands, q]);
2163
2284
  const groups = [];
@@ -2172,7 +2293,7 @@ function CommandPalette({
2172
2293
  g.items.push(c);
2173
2294
  flat.push(c);
2174
2295
  });
2175
- React24__namespace.default.useEffect(() => {
2296
+ React25__namespace.default.useEffect(() => {
2176
2297
  setActive(0);
2177
2298
  }, [query]);
2178
2299
  const run = (cmd) => {
@@ -2192,7 +2313,7 @@ function CommandPalette({
2192
2313
  if (cmd) run(cmd);
2193
2314
  }
2194
2315
  }
2195
- React24__namespace.default.useEffect(() => {
2316
+ React25__namespace.default.useEffect(() => {
2196
2317
  const node = listRef.current?.querySelector('[data-active="true"]');
2197
2318
  if (node) node.scrollIntoView({ block: "nearest" });
2198
2319
  }, [active]);
@@ -2220,7 +2341,8 @@ function CommandPalette({
2220
2341
  value: query,
2221
2342
  placeholder,
2222
2343
  role: "combobox",
2223
- "aria-expanded": "true",
2344
+ "aria-label": searchLabel,
2345
+ "aria-expanded": flat.length > 0 ? "true" : "false",
2224
2346
  "aria-controls": listId,
2225
2347
  "aria-activedescendant": activeId,
2226
2348
  onChange: (e) => setQuery(e.target.value),
@@ -2322,7 +2444,9 @@ var CUR_CSS = `
2322
2444
  .twc-cur:focus-within { border-color: var(--_accent); box-shadow: var(--_ring); }
2323
2445
  .twc-cur[data-invalid="true"] { border-color: var(--color-danger); }
2324
2446
  .twc-cur[data-invalid="true"]:focus-within { box-shadow: 0 0 0 var(--ring-width) color-mix(in srgb, var(--color-danger) 28%, transparent); }
2325
- .twc-cur[data-disabled="true"] { background: var(--color-surface-sunken); opacity: 0.7; }
2447
+ .twc-cur[data-disabled="true"] { background: var(--color-surface-sunken); opacity: 0.7; cursor: not-allowed; }
2448
+ .twc-cur[data-readonly="true"] { background: var(--color-surface-sunken); }
2449
+ .twc-cur__el:disabled { cursor: not-allowed; }
2326
2450
  .twc-cur__sym, .twc-cur__code {
2327
2451
  display: inline-flex; align-items: center; padding: 0 var(--space-3);
2328
2452
  background: var(--color-surface-sunken); color: var(--color-text-muted);
@@ -2365,18 +2489,29 @@ function Currency({
2365
2489
  const prec = precision != null ? precision : meta.precision;
2366
2490
  const sym = symbol != null ? symbol : meta.symbol;
2367
2491
  const cur = code != null ? code : meta.code;
2368
- const autoId = React24__namespace.default.useId();
2492
+ const autoId = React25__namespace.default.useId();
2369
2493
  const fieldId = id || autoId;
2370
2494
  const invalid = Boolean(error);
2371
2495
  const controlled = value !== void 0;
2372
- const [internal, setInternal] = React24__namespace.default.useState(() => clampPrecision(String(defaultValue ?? ""), prec));
2496
+ const [internal, setInternal] = React25__namespace.default.useState(() => clampPrecision(String(defaultValue ?? ""), prec));
2373
2497
  const shown = controlled ? clampPrecision(String(value ?? ""), prec) : internal;
2374
- const describedBy = [error ? `${fieldId}-error` : hint ? `${fieldId}-hint` : null, rest["aria-describedby"]].filter(Boolean).join(" ") || void 0;
2498
+ React25__namespace.default.useEffect(() => {
2499
+ if (!controlled) return;
2500
+ const raw = String(value ?? "");
2501
+ if (shown !== raw) {
2502
+ const n = Number(shown);
2503
+ onValueChange?.(shown === "" || Number.isNaN(n) ? null : n, shown);
2504
+ }
2505
+ }, [controlled, shown, value]);
2506
+ const descId = `${fieldId}-desc`;
2507
+ const describedBy = [error || hint ? descId : null, rest["aria-describedby"]].filter(Boolean).join(" ") || void 0;
2508
+ const ariaInvalid = invalid ? true : rest["aria-invalid"] ?? void 0;
2375
2509
  function handleChange(e) {
2376
2510
  const next = clampPrecision(e.target.value, prec);
2377
2511
  if (!controlled) setInternal(next);
2378
2512
  onChange?.(e);
2379
- onValueChange?.(next === "" ? null : Number(next), next);
2513
+ const n = Number(next);
2514
+ onValueChange?.(next === "" || Number.isNaN(n) ? null : n, next);
2380
2515
  }
2381
2516
  function handleBlur(e) {
2382
2517
  rest.onBlur?.(e);
@@ -2393,7 +2528,7 @@ function Currency({
2393
2528
  label,
2394
2529
  required ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-field__req", children: "*" }) : null
2395
2530
  ] }) : null,
2396
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-cur", "data-size": size, "data-tone": tone, "data-invalid": invalid || void 0, "data-disabled": disabled || void 0, children: [
2531
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-cur", "data-size": size, "data-tone": tone, "data-invalid": invalid || void 0, "data-disabled": disabled || void 0, "data-readonly": rest.readOnly || void 0, children: [
2397
2532
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-cur__sym", "aria-hidden": "true", children: sym }),
2398
2533
  /* @__PURE__ */ jsxRuntime.jsx(
2399
2534
  "input",
@@ -2406,16 +2541,16 @@ function Currency({
2406
2541
  placeholder,
2407
2542
  disabled,
2408
2543
  required: required || void 0,
2409
- "aria-invalid": invalid || void 0,
2410
2544
  ...rest,
2411
2545
  onChange: handleChange,
2412
2546
  onBlur: handleBlur,
2547
+ "aria-invalid": ariaInvalid,
2413
2548
  "aria-describedby": describedBy
2414
2549
  }
2415
2550
  ),
2416
2551
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-cur__code", children: cur })
2417
2552
  ] }),
2418
- error ? /* @__PURE__ */ jsxRuntime.jsx("span", { id: `${fieldId}-error`, className: "twc-field__error", children: error }) : hint ? /* @__PURE__ */ jsxRuntime.jsx("span", { id: `${fieldId}-hint`, className: "twc-field__hint", children: hint }) : null
2553
+ error ? /* @__PURE__ */ jsxRuntime.jsx("span", { id: descId, className: "twc-field__error", children: error }) : hint ? /* @__PURE__ */ jsxRuntime.jsx("span", { id: descId, className: "twc-field__hint", children: hint }) : null
2419
2554
  ] });
2420
2555
  }
2421
2556
  var SELECT_CSS = `
@@ -2490,6 +2625,13 @@ var SELECT_CSS = `
2490
2625
  .twc-opt__check { flex: none; color: var(--color-primary); display: inline-flex; }
2491
2626
  .twc-opt__check svg { width: 16px; height: 16px; }
2492
2627
  .twc-pop__empty { padding: 14px 12px; text-align: center; font-size: var(--text-sm); color: var(--color-text-subtle); }
2628
+ .twc-opt[data-disabled="true"] { opacity: 0.45; cursor: not-allowed; pointer-events: none; }
2629
+ .twc-pop__loading { display: flex; align-items: center; justify-content: center; gap: var(--space-2); padding: 14px 12px; font-size: var(--text-sm); color: var(--color-text-muted); }
2630
+ .twc-pop__spinner { width: 15px; height: 15px; border-radius: var(--radius-full); border: 2px solid var(--color-border); border-top-color: var(--color-primary); animation: twico-spin 0.7s linear infinite; }
2631
+ .twc-pop__search-clear { flex: none; display: inline-grid; place-items: center; width: 18px; height: 18px; border: none; background: transparent; color: var(--color-text-subtle); cursor: pointer; border-radius: var(--radius-full); }
2632
+ .twc-pop__search-clear:hover { background: var(--color-surface-sunken); color: var(--color-text); }
2633
+ .twc-pop__search-clear svg { width: 13px; height: 13px; }
2634
+ .twc-sel__sr { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
2493
2635
  `;
2494
2636
  function toOpt2(o) {
2495
2637
  return typeof o === "string" ? { value: o, label: o } : o;
@@ -2536,6 +2678,10 @@ function Select({
2536
2678
  placement = "bottom",
2537
2679
  portal = true,
2538
2680
  minWidth = 0,
2681
+ matchTriggerWidth = true,
2682
+ loading = false,
2683
+ emptyText = "No results found",
2684
+ name,
2539
2685
  id,
2540
2686
  className = "",
2541
2687
  onClick,
@@ -2543,47 +2689,58 @@ function Select({
2543
2689
  ...rest
2544
2690
  }) {
2545
2691
  const __twcStyles = useScopedStyles("twc-select-styles", SELECT_CSS);
2546
- const groups = React24__namespace.default.useMemo(() => normalizeGroups2(options), [options]);
2547
- const flat = React24__namespace.default.useMemo(() => groups.flatMap((g) => g.options), [groups]);
2548
- const autoId = React24__namespace.default.useId();
2692
+ const bufferRef = React25__namespace.default.useRef("");
2693
+ const bufferTimerRef = React25__namespace.default.useRef(null);
2694
+ const groups = React25__namespace.default.useMemo(() => normalizeGroups2(options), [options]);
2695
+ const flat = React25__namespace.default.useMemo(() => groups.flatMap((g) => g.options), [groups]);
2696
+ const autoId = React25__namespace.default.useId();
2549
2697
  const fieldId = id || autoId;
2550
- const [internal, setInternal] = React24__namespace.default.useState(defaultValue);
2698
+ const [internal, setInternal] = React25__namespace.default.useState(defaultValue);
2551
2699
  const current = value !== void 0 ? value : internal;
2552
- const [open, setOpen] = React24__namespace.default.useState(false);
2553
- const [render, setRender] = React24__namespace.default.useState(false);
2554
- const [active, setActive] = React24__namespace.default.useState(0);
2555
- const [coords, setCoords] = React24__namespace.default.useState(null);
2556
- const [query, setQuery] = React24__namespace.default.useState("");
2557
- const wrapRef = React24__namespace.default.useRef(null);
2558
- const triggerRef = React24__namespace.default.useRef(null);
2559
- const popRef = React24__namespace.default.useRef(null);
2560
- const listRef = React24__namespace.default.useRef(null);
2561
- const activeRef = React24__namespace.default.useRef(null);
2562
- const searchRef = React24__namespace.default.useRef(null);
2700
+ const [open, setOpen] = React25__namespace.default.useState(false);
2701
+ const [render, setRender] = React25__namespace.default.useState(false);
2702
+ const [active, setActive] = React25__namespace.default.useState(0);
2703
+ const [coords, setCoords] = React25__namespace.default.useState(null);
2704
+ const [query, setQuery] = React25__namespace.default.useState("");
2705
+ const wrapRef = React25__namespace.default.useRef(null);
2706
+ const triggerRef = React25__namespace.default.useRef(null);
2707
+ const popRef = React25__namespace.default.useRef(null);
2708
+ const listRef = React25__namespace.default.useRef(null);
2709
+ const activeRef = React25__namespace.default.useRef(null);
2710
+ const searchRef = React25__namespace.default.useRef(null);
2563
2711
  const showSearch = searchable === void 0 ? flat.length > 5 : searchable;
2564
- const fGroups = React24__namespace.default.useMemo(() => {
2712
+ const fGroups = React25__namespace.default.useMemo(() => {
2565
2713
  const q = query.trim().toLowerCase();
2566
2714
  if (!q) return groups;
2567
2715
  return groups.map((g) => ({ group: g.group, options: g.options.filter((o) => o.label.toLowerCase().includes(q) || o.description && o.description.toLowerCase().includes(q)) })).filter((g) => g.options.length);
2568
2716
  }, [groups, query]);
2569
- const visible = React24__namespace.default.useMemo(() => fGroups.flatMap((g) => g.options), [fGroups]);
2717
+ const visible = React25__namespace.default.useMemo(() => fGroups.flatMap((g) => g.options), [fGroups]);
2570
2718
  const selected = flat.find((o) => o.value === current);
2571
- const place = React24__namespace.default.useCallback(() => {
2719
+ const nextEnabled = (from, dir) => {
2720
+ let i = from + dir;
2721
+ while (i >= 0 && i < visible.length) {
2722
+ if (!visible[i]?.disabled) return i;
2723
+ i += dir;
2724
+ }
2725
+ return from;
2726
+ };
2727
+ const place = React25__namespace.default.useCallback(() => {
2572
2728
  const el = triggerRef.current;
2573
2729
  if (!el) return;
2574
2730
  const r = el.getBoundingClientRect();
2575
2731
  const vh = window.innerHeight;
2576
2732
  const below = vh - r.bottom;
2577
2733
  const flip = placement === "top" || below < 260 && r.top > below;
2734
+ const widthStyle = matchTriggerWidth ? { width: Math.max(r.width, minWidth) } : { width: "auto", minWidth: Math.max(r.width, minWidth), maxWidth: Math.max(160, window.innerWidth - r.left - 8) };
2578
2735
  setCoords({
2579
2736
  left: r.left,
2580
- width: Math.max(r.width, minWidth),
2737
+ ...widthStyle,
2581
2738
  top: flip ? void 0 : Math.round(r.bottom + 6),
2582
2739
  bottom: flip ? Math.round(vh - r.top + 6) : void 0,
2583
2740
  flip
2584
2741
  });
2585
- }, [placement, minWidth]);
2586
- React24__namespace.default.useEffect(() => {
2742
+ }, [placement, minWidth, matchTriggerWidth]);
2743
+ React25__namespace.default.useEffect(() => {
2587
2744
  if (!open || !portal) return;
2588
2745
  place();
2589
2746
  const onMove = () => place();
@@ -2594,7 +2751,7 @@ function Select({
2594
2751
  window.removeEventListener("resize", onMove);
2595
2752
  };
2596
2753
  }, [open, portal, place]);
2597
- React24__namespace.default.useEffect(() => {
2754
+ React25__namespace.default.useEffect(() => {
2598
2755
  if (!open) return;
2599
2756
  const idx = flat.findIndex((o) => o.value === current);
2600
2757
  setActive(idx >= 0 ? idx : 0);
@@ -2610,13 +2767,13 @@ function Select({
2610
2767
  document.addEventListener("mousedown", onDown);
2611
2768
  return () => document.removeEventListener("mousedown", onDown);
2612
2769
  }, [open]);
2613
- React24__namespace.default.useEffect(() => {
2770
+ React25__namespace.default.useEffect(() => {
2614
2771
  setActive(0);
2615
2772
  }, [query]);
2616
- React24__namespace.default.useEffect(() => {
2773
+ React25__namespace.default.useEffect(() => {
2617
2774
  if (open) ensureVisible2(listRef.current, activeRef.current);
2618
2775
  }, [active, open]);
2619
- React24__namespace.default.useEffect(() => {
2776
+ React25__namespace.default.useEffect(() => {
2620
2777
  if (open) {
2621
2778
  setRender(true);
2622
2779
  return;
@@ -2624,8 +2781,8 @@ function Select({
2624
2781
  const t = setTimeout(() => setRender(false), 170);
2625
2782
  return () => clearTimeout(t);
2626
2783
  }, [open]);
2627
- const prevOpen = React24__namespace.default.useRef(false);
2628
- React24__namespace.default.useEffect(() => {
2784
+ const prevOpen = React25__namespace.default.useRef(false);
2785
+ React25__namespace.default.useEffect(() => {
2629
2786
  if (prevOpen.current && !open) {
2630
2787
  const ae = typeof document !== "undefined" ? document.activeElement : null;
2631
2788
  if (popRef.current && ae && popRef.current.contains(ae)) triggerRef.current?.focus();
@@ -2633,6 +2790,8 @@ function Select({
2633
2790
  prevOpen.current = open;
2634
2791
  }, [open]);
2635
2792
  function commit(v) {
2793
+ const o = flat.find((x) => x.value === v);
2794
+ if (o && o.disabled) return;
2636
2795
  if (value === void 0) setInternal(v);
2637
2796
  onChange?.(v);
2638
2797
  setOpen(false);
@@ -2653,6 +2812,18 @@ function Select({
2653
2812
  setOpen(true);
2654
2813
  return;
2655
2814
  }
2815
+ if (!open && !showSearch && e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
2816
+ e.preventDefault();
2817
+ bufferRef.current += e.key.toLowerCase();
2818
+ clearTimeout(bufferTimerRef.current);
2819
+ bufferTimerRef.current = setTimeout(() => {
2820
+ bufferRef.current = "";
2821
+ }, 500);
2822
+ const b = bufferRef.current;
2823
+ const match = flat.find((o) => !o.disabled && o.label.toLowerCase().startsWith(b)) || flat.find((o) => !o.disabled && o.label.toLowerCase().includes(b));
2824
+ if (match) commit(match.value);
2825
+ return;
2826
+ }
2656
2827
  if (!open) return;
2657
2828
  if (e.key === "Tab") {
2658
2829
  setOpen(false);
@@ -2661,10 +2832,10 @@ function Select({
2661
2832
  if (e.key === "Escape") setOpen(false);
2662
2833
  else if (e.key === "ArrowDown") {
2663
2834
  e.preventDefault();
2664
- setActive((a) => Math.min(a + 1, visible.length - 1));
2835
+ setActive((a) => nextEnabled(a, 1));
2665
2836
  } else if (e.key === "ArrowUp") {
2666
2837
  e.preventDefault();
2667
- setActive((a) => Math.max(a - 1, 0));
2838
+ setActive((a) => nextEnabled(a, -1));
2668
2839
  } else if (e.key === "Enter") {
2669
2840
  e.preventDefault();
2670
2841
  if (visible[active]) commit(visible[active].value);
@@ -2696,9 +2867,16 @@ function Select({
2696
2867
  "aria-controls": listboxId,
2697
2868
  "aria-activedescendant": activeId
2698
2869
  }
2699
- )
2870
+ ),
2871
+ query ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-pop__search-clear", "aria-label": "Clear search", onClick: () => {
2872
+ setQuery("");
2873
+ searchRef.current?.focus();
2874
+ }, children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.4", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 6 6 18M6 6l12 12" }) }) }) : null
2700
2875
  ] }) : null,
2701
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-pop__list", ref: listRef, children: visible.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-pop__empty", children: "No results found" }) : fGroups.map((g, gi) => /* @__PURE__ */ jsxRuntime.jsxs(React24__namespace.default.Fragment, { children: [
2876
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-pop__list", ref: listRef, children: loading ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-pop__loading", role: "status", children: [
2877
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-pop__spinner", "aria-hidden": "true" }),
2878
+ "Loading\u2026"
2879
+ ] }) : visible.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-pop__empty", children: emptyText }) : fGroups.map((g, gi) => /* @__PURE__ */ jsxRuntime.jsxs(React25__namespace.default.Fragment, { children: [
2702
2880
  g.group ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-pop__group", children: g.group }) : null,
2703
2881
  g.options.map((o) => {
2704
2882
  counter += 1;
@@ -2713,9 +2891,14 @@ function Select({
2713
2891
  role: "option",
2714
2892
  "aria-selected": isSel,
2715
2893
  ref: idx === active ? activeRef : null,
2894
+ disabled: o.disabled || void 0,
2895
+ "aria-disabled": o.disabled || void 0,
2896
+ "data-disabled": o.disabled || void 0,
2716
2897
  "data-selected": isSel || void 0,
2717
2898
  "data-active": idx === active || void 0,
2718
- onMouseEnter: () => setActive(idx),
2899
+ onMouseEnter: () => {
2900
+ if (!o.disabled) setActive(idx);
2901
+ },
2719
2902
  onClick: () => commit(o.value),
2720
2903
  children: [
2721
2904
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "twc-opt__main", children: [
@@ -2728,7 +2911,8 @@ function Select({
2728
2911
  o.value
2729
2912
  );
2730
2913
  })
2731
- ] }, gi)) })
2914
+ ] }, gi)) }),
2915
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-sel__sr", role: "status", "aria-live": "polite", children: loading ? "Loading\u2026" : visible.length === 0 ? emptyText : `${visible.length} result${visible.length === 1 ? "" : "s"}` })
2732
2916
  ] });
2733
2917
  const RD = { createPortal: typeof reactDom.createPortal === "function" ? reactDom.createPortal : typeof window !== "undefined" && window.ReactDOM && window.ReactDOM.createPortal };
2734
2918
  const canPortal = portal && RD && RD.createPortal;
@@ -2745,19 +2929,20 @@ function Select({
2745
2929
  role: "listbox",
2746
2930
  ref: popRef,
2747
2931
  "data-state": popState,
2748
- "data-placement": coords.flip ? "top" : void 0,
2749
- style: { position: "fixed", left: coords.left, top: coords.top, bottom: coords.bottom, width: coords.width, right: "auto", zIndex: "var(--z-tooltip)" },
2932
+ "data-placement": coords.flip ? "top" : "bottom",
2933
+ style: { position: "fixed", left: coords.left, top: coords.top, bottom: coords.bottom, width: coords.width, minWidth: coords.minWidth, maxWidth: coords.maxWidth, right: "auto", zIndex: "var(--z-tooltip)" },
2750
2934
  children: popInner
2751
2935
  }
2752
2936
  ),
2753
2937
  document.body
2754
2938
  );
2755
2939
  } else if (!portal) {
2756
- popEl = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-pop", id: listboxId, role: "listbox", ref: popRef, "data-state": popState, "data-placement": placement === "top" ? "top" : void 0, children: popInner });
2940
+ popEl = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-pop", id: listboxId, role: "listbox", ref: popRef, "data-state": popState, "data-placement": placement === "top" ? "top" : "bottom", children: popInner });
2757
2941
  }
2758
2942
  }
2759
2943
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, ref: wrapRef, children: [
2760
2944
  __twcStyles,
2945
+ name ? /* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name, value: current ?? "" }) : null,
2761
2946
  label ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-field__label", htmlFor: fieldId, children: [
2762
2947
  label,
2763
2948
  required ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-field__req", children: "*" }) : null
@@ -2828,7 +3013,9 @@ var CURF_CSS = `
2828
3013
  .twc-cur:focus-within { border-color: var(--_accent); box-shadow: var(--_ring); }
2829
3014
  .twc-cur[data-invalid="true"] { border-color: var(--color-danger); }
2830
3015
  .twc-cur[data-invalid="true"]:focus-within { box-shadow: 0 0 0 var(--ring-width) color-mix(in srgb, var(--color-danger) 28%, transparent); }
2831
- .twc-cur[data-disabled="true"] { background: var(--color-surface-sunken); opacity: 0.7; }
3016
+ .twc-cur[data-disabled="true"] { background: var(--color-surface-sunken); opacity: 0.7; cursor: not-allowed; }
3017
+ .twc-cur[data-readonly="true"] { background: var(--color-surface-sunken); }
3018
+ .twc-cur__el:disabled { cursor: not-allowed; }
2832
3019
  .twc-cur__sym, .twc-cur__code {
2833
3020
  display: inline-flex; align-items: center; padding: 0 var(--space-3);
2834
3021
  background: var(--color-surface-sunken); color: var(--color-text-muted);
@@ -2872,19 +3059,37 @@ function CurrencyField({
2872
3059
  ...rest
2873
3060
  }) {
2874
3061
  const __twcStyles = useScopedStyles("twc-curf-styles", CURF_CSS);
2875
- const autoId = React24__namespace.default.useId();
3062
+ const autoId = React25__namespace.default.useId();
2876
3063
  const fieldId = id || autoId;
2877
3064
  const invalid = Boolean(error);
2878
3065
  const curControlled = currency !== void 0;
2879
- const [curInternal, setCurInternal] = React24__namespace.default.useState(defaultCurrency);
3066
+ const [curInternal, setCurInternal] = React25__namespace.default.useState(defaultCurrency);
2880
3067
  const cur = curControlled ? currency : curInternal;
2881
3068
  const meta = CURRENCIES[cur] || CURRENCIES.USD;
2882
3069
  const prec = meta.precision;
2883
3070
  const valControlled = value !== void 0;
2884
- const [valInternal, setValInternal] = React24__namespace.default.useState(() => clampPrecision(String(defaultValue ?? ""), prec));
3071
+ const [valInternal, setValInternal] = React25__namespace.default.useState(() => clampPrecision(String(defaultValue ?? ""), prec));
2885
3072
  const shown = valControlled ? clampPrecision(String(value ?? ""), prec) : valInternal;
2886
- const describedBy = [error ? `${fieldId}-error` : hint ? `${fieldId}-hint` : null, rest["aria-describedby"]].filter(Boolean).join(" ") || void 0;
2887
- const options = React24__namespace.default.useMemo(() => {
3073
+ const descId = `${fieldId}-desc`;
3074
+ const describedBy = [error || hint ? descId : null, rest["aria-describedby"]].filter(Boolean).join(" ") || void 0;
3075
+ const ariaInvalid = invalid ? true : rest["aria-invalid"] ?? void 0;
3076
+ React25__namespace.default.useEffect(() => {
3077
+ if (!valControlled) {
3078
+ const clamped = clampPrecision(valInternal, prec);
3079
+ if (clamped !== valInternal) {
3080
+ setValInternal(clamped);
3081
+ const n = Number(clamped);
3082
+ onValueChange?.(clamped === "" || Number.isNaN(n) ? null : n, clamped, cur);
3083
+ }
3084
+ } else {
3085
+ const raw = String(value ?? "");
3086
+ if (shown !== raw) {
3087
+ const n = Number(shown);
3088
+ onValueChange?.(shown === "" || Number.isNaN(n) ? null : n, shown, cur);
3089
+ }
3090
+ }
3091
+ }, [prec, cur, shown, value, valControlled, valInternal]);
3092
+ const options = React25__namespace.default.useMemo(() => {
2888
3093
  if (!currencies) return CURRENCY_OPTIONS;
2889
3094
  return currencies.map((c) => typeof c === "string" ? { value: c, label: `${c} \u2014 ${(CURRENCIES[c] || {}).label || c}` } : c);
2890
3095
  }, [currencies]);
@@ -2893,13 +3098,15 @@ function CurrencyField({
2893
3098
  onCurrencyChange?.(next);
2894
3099
  const reclamped = clampPrecision(shown, (CURRENCIES[next] || CURRENCIES.USD).precision);
2895
3100
  if (!valControlled) setValInternal(reclamped);
2896
- onValueChange?.(reclamped === "" ? null : Number(reclamped), reclamped, next);
3101
+ const rn = Number(reclamped);
3102
+ onValueChange?.(reclamped === "" || Number.isNaN(rn) ? null : rn, reclamped, next);
2897
3103
  }
2898
3104
  function handleChange(e) {
2899
3105
  onChange?.(e);
2900
3106
  const next = clampPrecision(e.target.value, prec);
2901
3107
  if (!valControlled) setValInternal(next);
2902
- onValueChange?.(next === "" ? null : Number(next), next, cur);
3108
+ const n = Number(next);
3109
+ onValueChange?.(next === "" || Number.isNaN(n) ? null : n, next, cur);
2903
3110
  }
2904
3111
  function handleBlur(e) {
2905
3112
  rest.onBlur?.(e);
@@ -2910,13 +3117,14 @@ function CurrencyField({
2910
3117
  if (!valControlled) setValInternal(fixed);
2911
3118
  onValueChange?.(Number(fixed), fixed, cur);
2912
3119
  }
3120
+ const curLabel = typeof label === "string" && label ? `${label} currency` : "Currency";
2913
3121
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, children: [
2914
3122
  __twcStyles,
2915
3123
  label ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-field__label", htmlFor: fieldId, children: [
2916
3124
  label,
2917
3125
  required ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-field__req", children: "*" }) : null
2918
3126
  ] }) : null,
2919
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-cur", "data-size": size, "data-tone": tone, "data-invalid": invalid || void 0, "data-disabled": disabled || void 0, children: [
3127
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-cur", "data-size": size, "data-tone": tone, "data-invalid": invalid || void 0, "data-disabled": disabled || void 0, "data-readonly": rest.readOnly || void 0, children: [
2920
3128
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-curf__pick", children: /* @__PURE__ */ jsxRuntime.jsx(
2921
3129
  Select,
2922
3130
  {
@@ -2927,7 +3135,7 @@ function CurrencyField({
2927
3135
  searchable: true,
2928
3136
  portal: true,
2929
3137
  minWidth: 260,
2930
- "aria-label": "Currency",
3138
+ "aria-label": curLabel,
2931
3139
  onChange: setCurrency
2932
3140
  }
2933
3141
  ) }),
@@ -2943,16 +3151,16 @@ function CurrencyField({
2943
3151
  placeholder,
2944
3152
  disabled,
2945
3153
  required: required || void 0,
2946
- "aria-invalid": invalid || void 0,
2947
3154
  ...rest,
2948
3155
  onChange: handleChange,
2949
3156
  onBlur: handleBlur,
3157
+ "aria-invalid": ariaInvalid,
2950
3158
  "aria-describedby": describedBy
2951
3159
  }
2952
3160
  ),
2953
3161
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-cur__code", children: meta.code })
2954
3162
  ] }),
2955
- error ? /* @__PURE__ */ jsxRuntime.jsx("span", { id: `${fieldId}-error`, className: "twc-field__error", children: error }) : hint ? /* @__PURE__ */ jsxRuntime.jsx("span", { id: `${fieldId}-hint`, className: "twc-field__hint", children: hint }) : null
3163
+ error ? /* @__PURE__ */ jsxRuntime.jsx("span", { id: descId, className: "twc-field__error", children: error }) : hint ? /* @__PURE__ */ jsxRuntime.jsx("span", { id: descId, className: "twc-field__hint", children: hint }) : null
2956
3164
  ] });
2957
3165
  }
2958
3166
  var FIELD_CSS2 = `
@@ -2988,6 +3196,7 @@ var FIELD_CSS2 = `
2988
3196
  .twc-input[data-invalid="true"] { border-color: var(--color-danger); }
2989
3197
  .twc-input[data-invalid="true"]:focus-within { box-shadow: 0 0 0 var(--ring-width) color-mix(in srgb, var(--color-danger) 28%, transparent); }
2990
3198
  .twc-input[data-disabled] { background: var(--color-surface-sunken); opacity: 0.7; cursor: not-allowed; }
3199
+ .twc-input[data-readonly] { background: var(--color-surface-sunken); }
2991
3200
  .twc-input__el {
2992
3201
  flex: 1; min-width: 0; border: none; outline: none; background: transparent;
2993
3202
  font-family: inherit; font-size: var(--text-sm); color: var(--color-text); height: 100%;
@@ -3003,6 +3212,17 @@ var FIELD_CSS2 = `
3003
3212
  .twc-input__reveal:focus-visible { outline: none; box-shadow: var(--ring); }
3004
3213
  .twc-input__reveal svg { width: 17px; height: 17px; }
3005
3214
  `;
3215
+ var INPUT_EXTRA_CSS = `
3216
+ .twc-input { overflow: hidden; }
3217
+ .twc-input__addon { align-self: stretch; display: inline-flex; align-items: center; padding-inline: var(--space-3);
3218
+ background: var(--color-surface-sunken); color: var(--color-text-muted); font-size: var(--text-sm);
3219
+ font-weight: var(--font-semibold); white-space: nowrap; flex: none; }
3220
+ .twc-input__addon--start { border-inline-end: var(--border-thin) solid var(--color-border); margin-inline-start: calc(-1 * var(--space-3)); }
3221
+ .twc-input__addon--end { border-inline-start: var(--border-thin) solid var(--color-border); margin-inline-end: calc(-1 * var(--space-3)); }
3222
+ .twc-field__footer { display: flex; align-items: center; gap: var(--space-2); }
3223
+ .twc-field__count { margin-inline-start: auto; font-size: var(--text-xs); color: var(--color-text-muted); font-variant-numeric: tabular-nums; flex: none; }
3224
+ .twc-field__count[data-danger="true"] { color: var(--color-danger-subtle-fg); }
3225
+ `;
3006
3226
  function Input({
3007
3227
  label,
3008
3228
  hint,
@@ -3012,6 +3232,9 @@ function Input({
3012
3232
  tone = "primary",
3013
3233
  leftIcon,
3014
3234
  rightIcon,
3235
+ leftAddon,
3236
+ rightAddon,
3237
+ showCount = false,
3015
3238
  type = "text",
3016
3239
  disabled = false,
3017
3240
  id,
@@ -3019,12 +3242,26 @@ function Input({
3019
3242
  ...rest
3020
3243
  }) {
3021
3244
  const __twcStyles = useScopedStyles("twc-field-styles", FIELD_CSS2);
3022
- const autoId = React24__namespace.default.useId();
3245
+ const __twcExtra = useScopedStyles("twc-input-extra-styles", INPUT_EXTRA_CSS);
3246
+ const autoId = React25__namespace.default.useId();
3023
3247
  const fieldId = id || autoId;
3024
3248
  const descId = `${fieldId}-desc`;
3249
+ const countId = `${fieldId}-count`;
3025
3250
  const invalid = Boolean(error);
3251
+ const countControlled = rest.value !== void 0;
3252
+ const [len2, setLen] = React25__namespace.default.useState(() => String(rest.defaultValue ?? "").length);
3253
+ const count = countControlled ? String(rest.value ?? "").length : len2;
3254
+ const max = rest.maxLength;
3255
+ const showCounter = showCount && max != null;
3256
+ const near = showCounter && count >= Math.floor(max * 0.9);
3257
+ const handleChange = (e) => {
3258
+ rest.onChange?.(e);
3259
+ if (showCount && !countControlled) setLen(e.target.value.length);
3260
+ };
3261
+ const ariaInvalid = invalid ? true : rest["aria-invalid"] ?? void 0;
3262
+ const describedBy = [error || hint ? descId : null, showCounter ? countId : null, rest["aria-describedby"]].filter(Boolean).join(" ") || void 0;
3026
3263
  const isPassword = type === "password";
3027
- const [revealed, setRevealed] = React24__namespace.default.useState(false);
3264
+ const [revealed, setRevealed] = React25__namespace.default.useState(false);
3028
3265
  const effectiveType = isPassword && revealed ? "text" : type;
3029
3266
  const suffix = rightIcon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-input__affix", children: rightIcon }) : isPassword ? /* @__PURE__ */ jsxRuntime.jsx(
3030
3267
  "button",
@@ -3049,11 +3286,13 @@ function Input({
3049
3286
  ) : null;
3050
3287
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, children: [
3051
3288
  __twcStyles,
3289
+ __twcExtra,
3052
3290
  label ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-field__label", htmlFor: fieldId, children: [
3053
3291
  label,
3054
3292
  required ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-field__req", children: "*" }) : null
3055
3293
  ] }) : null,
3056
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-input", "data-size": size, "data-tone": tone, "data-invalid": invalid || void 0, "data-disabled": disabled || void 0, children: [
3294
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-input", "data-size": size, "data-tone": tone, "data-invalid": invalid || void 0, "data-disabled": disabled || void 0, "data-readonly": rest.readOnly || void 0, children: [
3295
+ leftAddon != null ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-input__addon twc-input__addon--start", children: leftAddon }) : null,
3057
3296
  leftIcon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-input__affix", children: leftIcon }) : null,
3058
3297
  /* @__PURE__ */ jsxRuntime.jsx(
3059
3298
  "input",
@@ -3064,14 +3303,23 @@ function Input({
3064
3303
  disabled,
3065
3304
  required: required || void 0,
3066
3305
  "aria-required": required || void 0,
3067
- "aria-invalid": invalid || void 0,
3068
- "aria-describedby": error || hint ? descId : void 0,
3069
- ...rest
3306
+ ...rest,
3307
+ onChange: handleChange,
3308
+ "aria-invalid": ariaInvalid,
3309
+ "aria-describedby": describedBy
3070
3310
  }
3071
3311
  ),
3072
- suffix
3312
+ suffix,
3313
+ rightAddon != null ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-input__addon twc-input__addon--end", children: rightAddon }) : null
3073
3314
  ] }),
3074
- error ? /* @__PURE__ */ jsxRuntime.jsx("span", { id: descId, className: "twc-field__error", children: error }) : hint ? /* @__PURE__ */ jsxRuntime.jsx("span", { id: descId, className: "twc-field__hint", children: hint }) : null
3315
+ error || hint || showCounter ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-field__footer", children: [
3316
+ error ? /* @__PURE__ */ jsxRuntime.jsx("span", { id: descId, className: "twc-field__error", children: error }) : hint ? /* @__PURE__ */ jsxRuntime.jsx("span", { id: descId, className: "twc-field__hint", children: hint }) : null,
3317
+ showCounter ? /* @__PURE__ */ jsxRuntime.jsxs("span", { id: countId, className: "twc-field__count", "data-danger": near || void 0, "aria-live": "polite", children: [
3318
+ count,
3319
+ " / ",
3320
+ max
3321
+ ] }) : null
3322
+ ] }) : null
3075
3323
  ] });
3076
3324
  }
3077
3325
  var MULTI_CSS = `
@@ -3149,6 +3397,10 @@ var MULTI_CSS = `
3149
3397
  .twc-opt__label { line-height: 1.3; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
3150
3398
  .twc-opt__desc { font-size: var(--text-xs); color: var(--color-text-muted); line-height: 1.3; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
3151
3399
  .twc-pop__empty { padding: 14px 12px; text-align: center; font-size: var(--text-sm); color: var(--color-text-subtle); }
3400
+ .twc-opt[data-disabled="true"] { opacity: 0.45; cursor: not-allowed; pointer-events: none; }
3401
+ .twc-pop__loading { display: flex; align-items: center; justify-content: center; gap: var(--space-2); padding: 14px 12px; font-size: var(--text-sm); color: var(--color-text-muted); }
3402
+ .twc-pop__spinner { width: 15px; height: 15px; border-radius: var(--radius-full); border: 2px solid var(--color-border); border-top-color: var(--color-primary); animation: twico-spin 0.7s linear infinite; }
3403
+ .twc-ms__more { flex: none; align-self: center; font-size: var(--text-xs); font-weight: var(--font-semibold); color: var(--color-text-muted); background: var(--color-surface-sunken); border-radius: var(--radius-sm); padding: 2px 7px; }
3152
3404
  `;
3153
3405
  function toOpt3(o) {
3154
3406
  return typeof o === "string" ? { value: o, label: o } : o;
@@ -3198,6 +3450,11 @@ function MultiSelect({
3198
3450
  placement = "bottom",
3199
3451
  portal = true,
3200
3452
  minWidth = 0,
3453
+ loading = false,
3454
+ emptyText = "No results found",
3455
+ name,
3456
+ max,
3457
+ maxTagCount,
3201
3458
  id,
3202
3459
  className = "",
3203
3460
  onFocus,
@@ -3205,28 +3462,38 @@ function MultiSelect({
3205
3462
  ...rest
3206
3463
  }) {
3207
3464
  const __twcStyles = useScopedStyles("twc-ms-styles", MULTI_CSS);
3208
- const groups = React24__namespace.default.useMemo(() => normalizeGroups3(options), [options]);
3209
- const flat = React24__namespace.default.useMemo(() => groups.flatMap((g) => g.options), [groups]);
3210
- const autoId = React24__namespace.default.useId();
3465
+ const groups = React25__namespace.default.useMemo(() => normalizeGroups3(options), [options]);
3466
+ const flat = React25__namespace.default.useMemo(() => groups.flatMap((g) => g.options), [groups]);
3467
+ const autoId = React25__namespace.default.useId();
3211
3468
  const fieldId = id || autoId;
3212
- const [internal, setInternal] = React24__namespace.default.useState(defaultValue);
3469
+ const [internal, setInternal] = React25__namespace.default.useState(defaultValue);
3213
3470
  const selected = value !== void 0 ? value : internal;
3214
- const [open, setOpen] = React24__namespace.default.useState(false);
3215
- const [query, setQuery] = React24__namespace.default.useState("");
3216
- const [active, setActive] = React24__namespace.default.useState(0);
3217
- const [coords, setCoords] = React24__namespace.default.useState(null);
3218
- const wrapRef = React24__namespace.default.useRef(null);
3219
- const controlRef = React24__namespace.default.useRef(null);
3220
- const inputRef = React24__namespace.default.useRef(null);
3221
- const popRef = React24__namespace.default.useRef(null);
3222
- const listRef = React24__namespace.default.useRef(null);
3223
- const activeRef = React24__namespace.default.useRef(null);
3224
- const fGroups = React24__namespace.default.useMemo(
3471
+ const [open, setOpen] = React25__namespace.default.useState(false);
3472
+ const [query, setQuery] = React25__namespace.default.useState("");
3473
+ const [active, setActive] = React25__namespace.default.useState(0);
3474
+ const [coords, setCoords] = React25__namespace.default.useState(null);
3475
+ const wrapRef = React25__namespace.default.useRef(null);
3476
+ const controlRef = React25__namespace.default.useRef(null);
3477
+ const inputRef = React25__namespace.default.useRef(null);
3478
+ const popRef = React25__namespace.default.useRef(null);
3479
+ const listRef = React25__namespace.default.useRef(null);
3480
+ const activeRef = React25__namespace.default.useRef(null);
3481
+ const fGroups = React25__namespace.default.useMemo(
3225
3482
  () => groups.map((g) => ({ group: g.group, options: g.options.filter((o) => matches2(o, query.trim())) })).filter((g) => g.options.length),
3226
3483
  [groups, query]
3227
3484
  );
3228
- const visible = React24__namespace.default.useMemo(() => fGroups.flatMap((g) => g.options), [fGroups]);
3229
- const place = React24__namespace.default.useCallback(() => {
3485
+ const visible = React25__namespace.default.useMemo(() => fGroups.flatMap((g) => g.options), [fGroups]);
3486
+ const capReached = max != null && (value !== void 0 ? value : internal).length >= max;
3487
+ const isOptDisabled = (o) => o.disabled || capReached && !(value !== void 0 ? value : internal).includes(o.value);
3488
+ const nextEnabled = (from, dir) => {
3489
+ let i = from + dir;
3490
+ while (i >= 0 && i < visible.length) {
3491
+ if (!isOptDisabled(visible[i])) return i;
3492
+ i += dir;
3493
+ }
3494
+ return from;
3495
+ };
3496
+ const place = React25__namespace.default.useCallback(() => {
3230
3497
  const el = controlRef.current;
3231
3498
  if (!el) return;
3232
3499
  const r = el.getBoundingClientRect();
@@ -3241,7 +3508,7 @@ function MultiSelect({
3241
3508
  flip
3242
3509
  });
3243
3510
  }, [placement, minWidth]);
3244
- React24__namespace.default.useEffect(() => {
3511
+ React25__namespace.default.useEffect(() => {
3245
3512
  if (!open || !portal) return;
3246
3513
  place();
3247
3514
  const onMove = () => place();
@@ -3252,7 +3519,7 @@ function MultiSelect({
3252
3519
  window.removeEventListener("resize", onMove);
3253
3520
  };
3254
3521
  }, [open, portal, place]);
3255
- React24__namespace.default.useEffect(() => {
3522
+ React25__namespace.default.useEffect(() => {
3256
3523
  if (!open) return;
3257
3524
  const onDown = (e) => {
3258
3525
  if (wrapRef.current && wrapRef.current.contains(e.target)) return;
@@ -3263,10 +3530,10 @@ function MultiSelect({
3263
3530
  document.addEventListener("mousedown", onDown);
3264
3531
  return () => document.removeEventListener("mousedown", onDown);
3265
3532
  }, [open]);
3266
- React24__namespace.default.useEffect(() => {
3533
+ React25__namespace.default.useEffect(() => {
3267
3534
  setActive(0);
3268
3535
  }, [query]);
3269
- React24__namespace.default.useEffect(() => {
3536
+ React25__namespace.default.useEffect(() => {
3270
3537
  if (open) ensureVisible3(listRef.current, activeRef.current);
3271
3538
  }, [active, open]);
3272
3539
  function commit(next) {
@@ -3274,7 +3541,10 @@ function MultiSelect({
3274
3541
  onChange?.(next);
3275
3542
  }
3276
3543
  function toggle(v) {
3277
- commit(selected.includes(v) ? selected.filter((x) => x !== v) : [...selected, v]);
3544
+ const o = flat.find((x) => x.value === v);
3545
+ const isSel = selected.includes(v);
3546
+ if (!isSel && o && isOptDisabled(o)) return;
3547
+ commit(isSel ? selected.filter((x) => x !== v) : [...selected, v]);
3278
3548
  setQuery("");
3279
3549
  inputRef.current?.focus();
3280
3550
  }
@@ -3282,10 +3552,10 @@ function MultiSelect({
3282
3552
  if (e.key === "ArrowDown") {
3283
3553
  e.preventDefault();
3284
3554
  if (!open) setOpen(true);
3285
- else setActive((a) => Math.min(a + 1, visible.length - 1));
3555
+ else setActive((a) => nextEnabled(a, 1));
3286
3556
  } else if (e.key === "ArrowUp") {
3287
3557
  e.preventDefault();
3288
- setActive((a) => Math.max(a - 1, 0));
3558
+ setActive((a) => nextEnabled(a, -1));
3289
3559
  } else if (e.key === "Enter") {
3290
3560
  e.preventDefault();
3291
3561
  if (open && visible[active]) toggle(visible[active].value);
@@ -3303,12 +3573,16 @@ function MultiSelect({
3303
3573
  const descId = `${fieldId}-desc`;
3304
3574
  const describedBy = error || hint ? descId : void 0;
3305
3575
  let counter = -1;
3306
- const popInner = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-pop__list", id: listboxId, ref: listRef, role: "listbox", "aria-multiselectable": "true", children: visible.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-pop__empty", children: "No results found" }) : fGroups.map((g, gi) => /* @__PURE__ */ jsxRuntime.jsxs(React24__namespace.default.Fragment, { children: [
3576
+ const popInner = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-pop__list", id: listboxId, ref: listRef, role: "listbox", "aria-multiselectable": "true", children: loading ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-pop__loading", role: "status", children: [
3577
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-pop__spinner", "aria-hidden": "true" }),
3578
+ "Loading\u2026"
3579
+ ] }) : visible.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-pop__empty", children: emptyText }) : fGroups.map((g, gi) => /* @__PURE__ */ jsxRuntime.jsxs(React25__namespace.default.Fragment, { children: [
3307
3580
  g.group ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-pop__group", children: g.group }) : null,
3308
3581
  g.options.map((o) => {
3309
3582
  counter += 1;
3310
3583
  const idx = counter;
3311
3584
  const isSel = selected.includes(o.value);
3585
+ const optDisabled = isOptDisabled(o);
3312
3586
  return /* @__PURE__ */ jsxRuntime.jsxs(
3313
3587
  "button",
3314
3588
  {
@@ -3318,9 +3592,14 @@ function MultiSelect({
3318
3592
  role: "option",
3319
3593
  "aria-selected": isSel,
3320
3594
  ref: idx === active ? activeRef : null,
3595
+ disabled: optDisabled || void 0,
3596
+ "aria-disabled": optDisabled || void 0,
3597
+ "data-disabled": optDisabled || void 0,
3321
3598
  "data-selected": isSel || void 0,
3322
3599
  "data-active": idx === active || void 0,
3323
- onMouseEnter: () => setActive(idx),
3600
+ onMouseEnter: () => {
3601
+ if (!optDisabled) setActive(idx);
3602
+ },
3324
3603
  onMouseDown: (e) => e.preventDefault(),
3325
3604
  onClick: () => toggle(o.value),
3326
3605
  children: [
@@ -3346,7 +3625,7 @@ function MultiSelect({
3346
3625
  {
3347
3626
  className: "twc-pop twc-pop--portal",
3348
3627
  ref: popRef,
3349
- "data-placement": coords.flip ? "top" : void 0,
3628
+ "data-placement": coords.flip ? "top" : "bottom",
3350
3629
  style: { position: "fixed", left: coords.left, top: coords.top, bottom: coords.bottom, width: coords.width, right: "auto", zIndex: "var(--z-tooltip)" },
3351
3630
  children: popInner
3352
3631
  }
@@ -3359,6 +3638,7 @@ function MultiSelect({
3359
3638
  }
3360
3639
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, ref: wrapRef, children: [
3361
3640
  __twcStyles,
3641
+ name ? selected.map((v, i) => /* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name, value: v }, i)) : null,
3362
3642
  label ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-field__label", htmlFor: fieldId, children: [
3363
3643
  label,
3364
3644
  required ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-field__req", children: "*" }) : null
@@ -3381,13 +3661,18 @@ function MultiSelect({
3381
3661
  }
3382
3662
  },
3383
3663
  children: [
3384
- selectedOpts.map((o) => /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "twc-ms__chip", children: [
3664
+ (maxTagCount != null ? selectedOpts.slice(0, maxTagCount) : selectedOpts).map((o) => /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "twc-ms__chip", children: [
3385
3665
  o.label,
3386
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-ms__chip-x", "aria-label": `Remove ${o.label}`, onClick: (e) => {
3666
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", tabIndex: -1, className: "twc-ms__chip-x", "aria-label": `Remove ${o.label}`, onClick: (e) => {
3387
3667
  e.stopPropagation();
3388
3668
  toggle(o.value);
3389
3669
  }, children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.6", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 6 6 18M6 6l12 12" }) }) })
3390
3670
  ] }, o.value)),
3671
+ maxTagCount != null && selectedOpts.length > maxTagCount ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "twc-ms__more", title: selectedOpts.slice(maxTagCount).map((o) => o.label).join(", "), children: [
3672
+ "+",
3673
+ selectedOpts.length - maxTagCount,
3674
+ " more"
3675
+ ] }) : null,
3391
3676
  /* @__PURE__ */ jsxRuntime.jsx(
3392
3677
  "input",
3393
3678
  {
@@ -3419,7 +3704,7 @@ function MultiSelect({
3419
3704
  ...rest
3420
3705
  }
3421
3706
  ),
3422
- clearable && selected.length > 0 && !disabled ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-ms__clear", "aria-label": "Clear all", onClick: (e) => {
3707
+ clearable && selected.length > 0 && !disabled ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", tabIndex: -1, className: "twc-ms__clear", "aria-label": "Clear all", onClick: (e) => {
3423
3708
  e.stopPropagation();
3424
3709
  commit([]);
3425
3710
  setQuery("");
@@ -3447,15 +3732,6 @@ function MultiSelect({
3447
3732
  error ? /* @__PURE__ */ jsxRuntime.jsx("span", { id: descId, className: "twc-field__error", children: error }) : hint ? /* @__PURE__ */ jsxRuntime.jsx("span", { id: descId, className: "twc-field__hint", children: hint }) : null
3448
3733
  ] });
3449
3734
  }
3450
-
3451
- // components/_warn.js
3452
- var seen = /* @__PURE__ */ new Set();
3453
- function warnOnce(key, message) {
3454
- if (typeof process !== "undefined" && process.env && process.env.NODE_ENV === "production") return;
3455
- if (seen.has(key)) return;
3456
- seen.add(key);
3457
- if (typeof console !== "undefined" && console.warn) console.warn(message);
3458
- }
3459
3735
  var PAGINATION_CSS = `
3460
3736
  .twc-pagination { display: inline-flex; align-items: center; gap: 4px; font-family: var(--font-sans); }
3461
3737
  .twc-page {
@@ -3521,6 +3797,7 @@ function Pagination({
3521
3797
  showJumper,
3522
3798
  showPageJumper,
3523
3799
  jumperLabel = "Go to",
3800
+ getPageLabel,
3524
3801
  className = "",
3525
3802
  ...rest
3526
3803
  }) {
@@ -3529,7 +3806,7 @@ function Pagination({
3529
3806
  }
3530
3807
  const jumperEnabled = showPageJumper ?? showJumper;
3531
3808
  const __twcStyles = useScopedStyles("twc-pagination-styles", PAGINATION_CSS);
3532
- const [internal, setInternal] = React24__namespace.default.useState(defaultPage);
3809
+ const [internal, setInternal] = React25__namespace.default.useState(defaultPage);
3533
3810
  const page = pageProp !== void 0 ? pageProp : internal;
3534
3811
  const pages = buildPages(page, total, siblings, boundaries);
3535
3812
  const go = (p) => {
@@ -3537,7 +3814,7 @@ function Pagination({
3537
3814
  if (pageProp === void 0) setInternal(p);
3538
3815
  onChange?.(p);
3539
3816
  };
3540
- const [jump, setJump] = React24__namespace.default.useState("");
3817
+ const [jump, setJump] = React25__namespace.default.useState("");
3541
3818
  const submitJump = () => {
3542
3819
  const n = parseInt(jump, 10);
3543
3820
  if (!Number.isNaN(n)) go(Math.min(Math.max(n, 1), total));
@@ -3554,6 +3831,7 @@ function Pagination({
3554
3831
  className: "twc-page",
3555
3832
  "data-size": size,
3556
3833
  "data-active": p === page || void 0,
3834
+ "aria-label": getPageLabel ? getPageLabel(p) : `Page ${p}`,
3557
3835
  "aria-current": p === page ? "page" : void 0,
3558
3836
  onClick: () => go(p),
3559
3837
  children: p
@@ -3586,6 +3864,154 @@ function Pagination({
3586
3864
  ] }) : null
3587
3865
  ] });
3588
3866
  }
3867
+ var TOOLTIP_CSS = `
3868
+ .twc-tooltip-wrap { display: inline-flex; }
3869
+ .twc-tooltip {
3870
+ position: fixed; z-index: var(--z-tooltip);
3871
+ padding: 6px 10px; border-radius: var(--radius-md);
3872
+ background: var(--color-text); color: var(--color-surface);
3873
+ font-family: var(--font-sans); font-size: var(--text-xs); font-weight: var(--font-medium);
3874
+ line-height: 1.3; white-space: normal; max-width: var(--_tw-maxw, 260px); box-shadow: var(--shadow-md);
3875
+ opacity: 0; transform: scale(0.9); transition: opacity var(--duration-fast) var(--ease-out), transform var(--duration-fast) var(--ease-spring);
3876
+ }
3877
+ .twc-tooltip[data-show="true"] { pointer-events: auto; }
3878
+ .twc-tooltip[data-show="true"] { opacity: 1; transform: scale(1); }
3879
+ .twc-tooltip[data-place="top"] { transform-origin: bottom center; translate: -50% 0; }
3880
+ .twc-tooltip[data-place="bottom"] { transform-origin: top center; translate: -50% 0; }
3881
+ .twc-tooltip[data-place="left"] { transform-origin: right center; translate: 0 -50%; }
3882
+ .twc-tooltip[data-place="right"] { transform-origin: left center; translate: 0 -50%; }
3883
+ .twc-tooltip[data-show="true"][data-place="top"], .twc-tooltip[data-show="true"][data-place="bottom"] { transform: scale(1); }
3884
+ .twc-tooltip__arrow { position: absolute; width: 7px; height: 7px; background: var(--color-text); transform: rotate(45deg); }
3885
+ .twc-tooltip[data-place="top"] .twc-tooltip__arrow { bottom: -3px; left: 50%; margin-left: -3.5px; }
3886
+ .twc-tooltip[data-place="bottom"] .twc-tooltip__arrow { top: -3px; left: 50%; margin-left: -3.5px; }
3887
+ .twc-tooltip[data-place="left"] .twc-tooltip__arrow { right: -3px; top: 50%; margin-top: -3.5px; }
3888
+ .twc-tooltip[data-place="right"] .twc-tooltip__arrow { left: -3px; top: 50%; margin-top: -3.5px; }
3889
+ @media (prefers-reduced-motion: reduce) { .twc-tooltip { transition: opacity var(--duration-fast) linear; transform: none; } }
3890
+ `;
3891
+ function Tooltip({
3892
+ children,
3893
+ label,
3894
+ placement = "top",
3895
+ delay = 120,
3896
+ className = "",
3897
+ ...rest
3898
+ }) {
3899
+ const __twcStyles = useScopedStyles("twc-tooltip-styles", TOOLTIP_CSS);
3900
+ const id = React25__namespace.default.useId();
3901
+ const [show, setShow] = React25__namespace.default.useState(false);
3902
+ const [coords, setCoords] = React25__namespace.default.useState(null);
3903
+ const timer = React25__namespace.default.useRef(null);
3904
+ const closeTimer = React25__namespace.default.useRef(null);
3905
+ const wrapRef = React25__namespace.default.useRef(null);
3906
+ const tipRef = React25__namespace.default.useRef(null);
3907
+ const open = () => {
3908
+ clearTimeout(closeTimer.current);
3909
+ clearTimeout(timer.current);
3910
+ timer.current = setTimeout(() => setShow(true), delay);
3911
+ };
3912
+ const close = () => {
3913
+ clearTimeout(timer.current);
3914
+ clearTimeout(closeTimer.current);
3915
+ closeTimer.current = setTimeout(() => setShow(false), 120);
3916
+ };
3917
+ const cancelClose = () => {
3918
+ clearTimeout(closeTimer.current);
3919
+ };
3920
+ React25__namespace.default.useEffect(() => () => {
3921
+ clearTimeout(timer.current);
3922
+ clearTimeout(closeTimer.current);
3923
+ }, []);
3924
+ const place = React25__namespace.default.useCallback(() => {
3925
+ const el = wrapRef.current;
3926
+ if (!el) return;
3927
+ const r = el.getBoundingClientRect();
3928
+ const cx = Math.round(r.left + r.width / 2), cy = Math.round(r.top + r.height / 2);
3929
+ const vw = window.innerWidth, vh = window.innerHeight, gap = 8, margin = 8;
3930
+ const tip = tipRef.current;
3931
+ const tw = tip ? tip.offsetWidth : 0, th = tip ? tip.offsetHeight : 0;
3932
+ const clampX = (c) => tw ? Math.round(Math.min(Math.max(c, margin + tw / 2), vw - margin - tw / 2)) : c;
3933
+ const clampY = (c) => th ? Math.round(Math.min(Math.max(c, margin + th / 2), vh - margin - th / 2)) : c;
3934
+ const spaceAbove = r.top - gap - margin, spaceBelow = vh - r.bottom - gap - margin;
3935
+ const spaceLeft = r.left - gap - margin, spaceRight = vw - r.right - gap - margin;
3936
+ let side = placement;
3937
+ if (placement === "top" && spaceAbove < th && spaceBelow > spaceAbove) side = "bottom";
3938
+ else if (placement === "bottom" && spaceBelow < th && spaceAbove > spaceBelow) side = "top";
3939
+ else if (placement === "left" && spaceLeft < tw && spaceRight > spaceLeft) side = "right";
3940
+ else if (placement === "right" && spaceRight < tw && spaceLeft > spaceRight) side = "left";
3941
+ const base = { left: "auto", right: "auto", top: "auto", bottom: "auto", place: side };
3942
+ if (side === "bottom") setCoords({ ...base, left: clampX(cx), top: Math.round(r.bottom + gap) });
3943
+ else if (side === "left") setCoords({ ...base, top: clampY(cy), right: Math.round(vw - r.left + gap) });
3944
+ else if (side === "right") setCoords({ ...base, top: clampY(cy), left: Math.round(r.right + gap) });
3945
+ else setCoords({ ...base, left: clampX(cx), bottom: Math.round(vh - r.top + gap) });
3946
+ }, [placement]);
3947
+ React25__namespace.default.useEffect(() => {
3948
+ place();
3949
+ }, [place]);
3950
+ React25__namespace.default.useEffect(() => {
3951
+ if (!show) return void 0;
3952
+ place();
3953
+ const onMove = () => place();
3954
+ window.addEventListener("scroll", onMove, true);
3955
+ window.addEventListener("resize", onMove);
3956
+ return () => {
3957
+ window.removeEventListener("scroll", onMove, true);
3958
+ window.removeEventListener("resize", onMove);
3959
+ };
3960
+ }, [show, place]);
3961
+ React25__namespace.default.useEffect(() => {
3962
+ if (!show) return;
3963
+ const onKeyDown = (e) => {
3964
+ if (e.key === "Escape") {
3965
+ clearTimeout(timer.current);
3966
+ setShow(false);
3967
+ }
3968
+ };
3969
+ document.addEventListener("keydown", onKeyDown);
3970
+ return () => document.removeEventListener("keydown", onKeyDown);
3971
+ }, [show]);
3972
+ const describedBy = show ? id : void 0;
3973
+ const trigger = React25__namespace.default.isValidElement(children) && children.type !== React25__namespace.default.Fragment ? React25__namespace.default.cloneElement(children, {
3974
+ "aria-describedby": [children.props["aria-describedby"], describedBy].filter(Boolean).join(" ") || void 0
3975
+ }) : /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-describedby": describedBy, children });
3976
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3977
+ "span",
3978
+ {
3979
+ ref: wrapRef,
3980
+ className: `twc-tooltip-wrap ${className}`,
3981
+ onMouseEnter: open,
3982
+ onMouseLeave: close,
3983
+ onFocus: open,
3984
+ onBlur: close,
3985
+ ...rest,
3986
+ children: [
3987
+ __twcStyles,
3988
+ trigger,
3989
+ coords ? reactDom.createPortal(
3990
+ /* @__PURE__ */ jsxRuntime.jsxs(
3991
+ "span",
3992
+ {
3993
+ ref: tipRef,
3994
+ id,
3995
+ className: "twc-tooltip",
3996
+ "data-place": coords.place || placement,
3997
+ "data-show": show || void 0,
3998
+ role: "tooltip",
3999
+ "aria-hidden": show ? void 0 : "true",
4000
+ onMouseEnter: cancelClose,
4001
+ onMouseLeave: close,
4002
+ style: { position: "fixed", left: coords.left, right: coords.right, top: coords.top, bottom: coords.bottom },
4003
+ children: [
4004
+ label,
4005
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-tooltip__arrow", "aria-hidden": "true" })
4006
+ ]
4007
+ }
4008
+ ),
4009
+ document.body
4010
+ ) : null
4011
+ ]
4012
+ }
4013
+ );
4014
+ }
3589
4015
  var DT_CSS = `
3590
4016
  .twc-dt { display: flex; flex-direction: column; font-family: var(--font-sans); color: var(--color-text);
3591
4017
  border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-lg);
@@ -4148,9 +4574,9 @@ function runDatatableQuery(rows, query, options = {}) {
4148
4574
  return { rows: page, total, filtered: out };
4149
4575
  }
4150
4576
  function useFloating() {
4151
- const [pos, setPos] = React24__namespace.default.useState(null);
4152
- const cfg = React24__namespace.default.useRef(null);
4153
- const place = React24__namespace.default.useCallback(() => {
4577
+ const [pos, setPos] = React25__namespace.default.useState(null);
4578
+ const cfg = React25__namespace.default.useRef(null);
4579
+ const place = React25__namespace.default.useCallback(() => {
4154
4580
  const c = cfg.current;
4155
4581
  if (!c || !c.el || !c.el.isConnected) return null;
4156
4582
  const r = c.el.getBoundingClientRect();
@@ -4161,17 +4587,17 @@ function useFloating() {
4161
4587
  const top = r.bottom + 6;
4162
4588
  return { top, left, width: c.width, anchorX: r.left + r.width / 2, maxHeight: Math.max(180, vh - top - M) };
4163
4589
  }, []);
4164
- const open = React24__namespace.default.useCallback((el, align = "left", width = 220) => {
4590
+ const open = React25__namespace.default.useCallback((el, align = "left", width = 220) => {
4165
4591
  cfg.current = { el, align, width };
4166
4592
  const p = place();
4167
4593
  if (p) setPos(p);
4168
4594
  }, [place]);
4169
- const close = React24__namespace.default.useCallback(() => {
4595
+ const close = React25__namespace.default.useCallback(() => {
4170
4596
  cfg.current = null;
4171
4597
  setPos(null);
4172
4598
  }, []);
4173
4599
  const isOpen = pos != null;
4174
- React24__namespace.default.useEffect(() => {
4600
+ React25__namespace.default.useEffect(() => {
4175
4601
  if (!isOpen) return void 0;
4176
4602
  const update = () => {
4177
4603
  const p = place();
@@ -4195,9 +4621,9 @@ function Caret({ pos }) {
4195
4621
  return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-dt__caret", style: { left } });
4196
4622
  }
4197
4623
  function EditCell({ col, value, options, onChange, onCommit, onCommitValue, onCancel, onKeyDown }) {
4198
- const ref = React24__namespace.default.useRef(null);
4624
+ const ref = React25__namespace.default.useRef(null);
4199
4625
  const isSelect = col.editType === "select" || options && col.editType !== "text";
4200
- React24__namespace.default.useEffect(() => {
4626
+ React25__namespace.default.useEffect(() => {
4201
4627
  if (isSelect) {
4202
4628
  const t = setTimeout(() => ref.current?.querySelector(".twc-sel__trigger")?.click(), 0);
4203
4629
  return () => clearTimeout(t);
@@ -4373,6 +4799,8 @@ function Datatable({
4373
4799
  aggregationValues = null,
4374
4800
  disableColumnReorder = false,
4375
4801
  disableColumnResize = false,
4802
+ emptyMessage,
4803
+ renderEmpty,
4376
4804
  editMode = false,
4377
4805
  onRowUpdate,
4378
4806
  onRowsChange,
@@ -4400,7 +4828,7 @@ function Datatable({
4400
4828
  ...rest
4401
4829
  }) {
4402
4830
  const __twcStyles = useScopedStyles("twc-dt-styles", DT_CSS);
4403
- const cols = React24__namespace.default.useMemo(() => {
4831
+ const cols = React25__namespace.default.useMemo(() => {
4404
4832
  const out = columns.map((c) => {
4405
4833
  const isActions = c.type === "actions";
4406
4834
  return {
@@ -4435,7 +4863,7 @@ function Datatable({
4435
4863
  }
4436
4864
  return out;
4437
4865
  }, [columns, rowPinning]);
4438
- const _autoKey = React24__namespace.default.useRef();
4866
+ const _autoKey = React25__namespace.default.useRef();
4439
4867
  if (!_autoKey.current) _autoKey.current = { map: /* @__PURE__ */ new WeakMap(), n: 0 };
4440
4868
  const keyOf = rowKey || ((r, i) => {
4441
4869
  if (r != null && r.id != null) return r.id;
@@ -4450,17 +4878,17 @@ function Datatable({
4450
4878
  }
4451
4879
  return i;
4452
4880
  });
4453
- const [sort, setSort] = React24__namespace.default.useState(null);
4454
- const [filters, setFilters] = React24__namespace.default.useState([]);
4455
- const [hidden, setHidden] = React24__namespace.default.useState(() => /* @__PURE__ */ new Set());
4456
- const [showRowNum, setShowRowNum] = React24__namespace.default.useState(rowNumbers);
4457
- const [wrapped, setWrapped] = React24__namespace.default.useState(() => new Set((columns || []).filter((c) => c.wrapText).map((c) => c.field)));
4881
+ const [sort, setSort] = React25__namespace.default.useState(null);
4882
+ const [filters, setFilters] = React25__namespace.default.useState([]);
4883
+ const [hidden, setHidden] = React25__namespace.default.useState(() => /* @__PURE__ */ new Set());
4884
+ const [showRowNum, setShowRowNum] = React25__namespace.default.useState(rowNumbers);
4885
+ const [wrapped, setWrapped] = React25__namespace.default.useState(() => new Set((columns || []).filter((c) => c.wrapText).map((c) => c.field)));
4458
4886
  const toggleWrap = (field) => setWrapped((w) => {
4459
4887
  const n = new Set(w);
4460
4888
  n.has(field) ? n.delete(field) : n.add(field);
4461
4889
  return n;
4462
4890
  });
4463
- const [pins, setPins] = React24__namespace.default.useState(() => {
4891
+ const [pins, setPins] = React25__namespace.default.useState(() => {
4464
4892
  const left = [], right = [];
4465
4893
  columns.forEach((c) => {
4466
4894
  if (c.pinned === "left") left.push(c.field);
@@ -4468,34 +4896,34 @@ function Datatable({
4468
4896
  });
4469
4897
  return { left, right };
4470
4898
  });
4471
- const [density, setDensity] = React24__namespace.default.useState(densityProp);
4472
- const [quick, setQuick] = React24__namespace.default.useState("");
4473
- const [selected, setSelected] = React24__namespace.default.useState(() => /* @__PURE__ */ new Set());
4474
- const [activeRow, setActiveRow] = React24__namespace.default.useState(null);
4475
- const [activeCell, setActiveCell] = React24__namespace.default.useState(null);
4476
- const [page, setPage] = React24__namespace.default.useState(0);
4477
- const [rowsPerPage, setRowsPerPage] = React24__namespace.default.useState(pageSize > 0 ? pageSize : 10);
4478
- const [colQuery, setColQuery] = React24__namespace.default.useState("");
4479
- const [widths, setWidths] = React24__namespace.default.useState({});
4480
- const [order, setOrder] = React24__namespace.default.useState(() => columns.map((c) => c.field));
4481
- const [drag, setDrag] = React24__namespace.default.useState({ from: null, over: null, after: false });
4482
- const [aggOn, setAggOn] = React24__namespace.default.useState(showAggregation);
4483
- const [focus, setFocus] = React24__namespace.default.useState({ r: 0, c: 0 });
4484
- const gridRef = React24__namespace.default.useRef(null);
4485
- const [groupBy, setGroupBy] = React24__namespace.default.useState(rowGrouping || []);
4486
- const [collapsed, setCollapsed] = React24__namespace.default.useState(() => /* @__PURE__ */ new Set());
4487
- const [pinnedRows, setPinnedRows] = React24__namespace.default.useState({ top: [], bottom: [] });
4488
- const [headH, setHeadH] = React24__namespace.default.useState(41);
4489
- const theadRef = React24__namespace.default.useRef(null);
4490
- const [rowOrder, setRowOrder] = React24__namespace.default.useState(null);
4491
- const [rowHeights, setRowHeights] = React24__namespace.default.useState({});
4492
- const [rowDrag, setRowDrag] = React24__namespace.default.useState({ from: null, over: null, after: false });
4493
- const [rowGrab, setRowGrab] = React24__namespace.default.useState(null);
4494
- const [reorderMsg, setReorderMsg] = React24__namespace.default.useState("");
4495
- const rowFocusRef = React24__namespace.default.useRef(null);
4496
- const rootRef = React24__namespace.default.useRef(null);
4497
- const [compact, setCompact] = React24__namespace.default.useState(false);
4498
- React24__namespace.default.useEffect(() => {
4899
+ const [density, setDensity] = React25__namespace.default.useState(densityProp);
4900
+ const [quick, setQuick] = React25__namespace.default.useState("");
4901
+ const [selected, setSelected] = React25__namespace.default.useState(() => /* @__PURE__ */ new Set());
4902
+ const [activeRow, setActiveRow] = React25__namespace.default.useState(null);
4903
+ const [activeCell, setActiveCell] = React25__namespace.default.useState(null);
4904
+ const [page, setPage] = React25__namespace.default.useState(0);
4905
+ const [rowsPerPage, setRowsPerPage] = React25__namespace.default.useState(pageSize > 0 ? pageSize : 10);
4906
+ const [colQuery, setColQuery] = React25__namespace.default.useState("");
4907
+ const [widths, setWidths] = React25__namespace.default.useState({});
4908
+ const [order, setOrder] = React25__namespace.default.useState(() => columns.map((c) => c.field));
4909
+ const [drag, setDrag] = React25__namespace.default.useState({ from: null, over: null, after: false });
4910
+ const [aggOn, setAggOn] = React25__namespace.default.useState(showAggregation);
4911
+ const [focus, setFocus] = React25__namespace.default.useState({ r: 0, c: 0 });
4912
+ const gridRef = React25__namespace.default.useRef(null);
4913
+ const [groupBy, setGroupBy] = React25__namespace.default.useState(rowGrouping || []);
4914
+ const [collapsed, setCollapsed] = React25__namespace.default.useState(() => /* @__PURE__ */ new Set());
4915
+ const [pinnedRows, setPinnedRows] = React25__namespace.default.useState({ top: [], bottom: [] });
4916
+ const [headH, setHeadH] = React25__namespace.default.useState(41);
4917
+ const theadRef = React25__namespace.default.useRef(null);
4918
+ const [rowOrder, setRowOrder] = React25__namespace.default.useState(null);
4919
+ const [rowHeights, setRowHeights] = React25__namespace.default.useState({});
4920
+ const [rowDrag, setRowDrag] = React25__namespace.default.useState({ from: null, over: null, after: false });
4921
+ const [rowGrab, setRowGrab] = React25__namespace.default.useState(null);
4922
+ const [reorderMsg, setReorderMsg] = React25__namespace.default.useState("");
4923
+ const rowFocusRef = React25__namespace.default.useRef(null);
4924
+ const rootRef = React25__namespace.default.useRef(null);
4925
+ const [compact, setCompact] = React25__namespace.default.useState(false);
4926
+ React25__namespace.default.useEffect(() => {
4499
4927
  const el = rootRef.current;
4500
4928
  if (!el) return void 0;
4501
4929
  const measure = () => setCompact((el.clientWidth || 0) < 720);
@@ -4508,11 +4936,11 @@ function Datatable({
4508
4936
  else if (typeof window !== "undefined") window.removeEventListener("resize", measure);
4509
4937
  };
4510
4938
  }, []);
4511
- const scrollRef = React24__namespace.default.useRef(null);
4512
- const [scrollTop, setScrollTop] = React24__namespace.default.useState(0);
4513
- const [viewportH, setViewportH] = React24__namespace.default.useState(0);
4514
- const [pivotOn, setPivotOn] = React24__namespace.default.useState(pivotMode);
4515
- const [aggConfig, setAggConfig] = React24__namespace.default.useState(() => {
4939
+ const scrollRef = React25__namespace.default.useRef(null);
4940
+ const [scrollTop, setScrollTop] = React25__namespace.default.useState(0);
4941
+ const [viewportH, setViewportH] = React25__namespace.default.useState(0);
4942
+ const [pivotOn, setPivotOn] = React25__namespace.default.useState(pivotMode);
4943
+ const [aggConfig, setAggConfig] = React25__namespace.default.useState(() => {
4516
4944
  const m = {};
4517
4945
  (columns || []).forEach((c) => {
4518
4946
  if (typeof c.aggregation === "string") m[c.field] = c.aggregation;
@@ -4520,13 +4948,13 @@ function Datatable({
4520
4948
  return m;
4521
4949
  });
4522
4950
  const aggOf = (c) => aggConfig[c.field] || (typeof c.aggregation === "function" ? c.aggregation : null);
4523
- const [pivotConfig, setPivotConfig] = React24__namespace.default.useState(() => ({
4951
+ const [pivotConfig, setPivotConfig] = React25__namespace.default.useState(() => ({
4524
4952
  rows: pivot && pivot.rows || [],
4525
4953
  columns: pivot && pivot.columns || [],
4526
4954
  values: pivot && pivot.values || []
4527
4955
  }));
4528
4956
  const pivotActive = pivotOn && pivotConfig.rows.length > 0 && pivotConfig.values.length > 0;
4529
- React24__namespace.default.useEffect(() => {
4957
+ React25__namespace.default.useEffect(() => {
4530
4958
  setOrder((prev) => {
4531
4959
  const fields = columns.map((c) => c.field);
4532
4960
  const kept = prev.filter((f) => fields.includes(f));
@@ -4534,32 +4962,32 @@ function Datatable({
4534
4962
  return [...kept, ...added];
4535
4963
  });
4536
4964
  }, [columns]);
4537
- React24__namespace.default.useEffect(() => {
4965
+ React25__namespace.default.useEffect(() => {
4538
4966
  setDensity(densityProp);
4539
4967
  }, [densityProp]);
4540
- React24__namespace.default.useEffect(() => {
4968
+ React25__namespace.default.useEffect(() => {
4541
4969
  setAggOn(showAggregation);
4542
4970
  }, [showAggregation]);
4543
4971
  const rowGroupingKey = (rowGrouping || []).join("\0");
4544
- const rowGroupingKeyRef = React24__namespace.default.useRef(rowGroupingKey);
4545
- React24__namespace.default.useEffect(() => {
4972
+ const rowGroupingKeyRef = React25__namespace.default.useRef(rowGroupingKey);
4973
+ React25__namespace.default.useEffect(() => {
4546
4974
  if (rowGroupingKeyRef.current === rowGroupingKey) return;
4547
4975
  rowGroupingKeyRef.current = rowGroupingKey;
4548
4976
  setGroupBy(rowGrouping || []);
4549
4977
  }, [rowGroupingKey]);
4550
4978
  const widthOf = (c) => widths[c.field] ?? c.width ?? 160;
4551
- const [colMenu, setColMenu] = React24__namespace.default.useState(null);
4979
+ const [colMenu, setColMenu] = React25__namespace.default.useState(null);
4552
4980
  const [menuPos, openMenu, closeMenu] = useFloating();
4553
- const [panel, setPanel] = React24__namespace.default.useState(null);
4981
+ const [panel, setPanel] = React25__namespace.default.useState(null);
4554
4982
  const [panelPos, openPanel, closePanel] = useFloating();
4555
- const [rowMenu, setRowMenu] = React24__namespace.default.useState(null);
4983
+ const [rowMenu, setRowMenu] = React25__namespace.default.useState(null);
4556
4984
  const [rowMenuPos, openRowMenu, closeRowMenu] = useFloating();
4557
- const [exportOpen, setExportOpen] = React24__namespace.default.useState(false);
4985
+ const [exportOpen, setExportOpen] = React25__namespace.default.useState(false);
4558
4986
  const [exportPos, openExport, closeExport] = useFloating();
4559
- const menuTriggerRef = React24__namespace.default.useRef(null);
4560
- const colMenuRef = React24__namespace.default.useRef(null);
4561
- const rowMenuRef = React24__namespace.default.useRef(null);
4562
- const exportMenuRef = React24__namespace.default.useRef(null);
4987
+ const menuTriggerRef = React25__namespace.default.useRef(null);
4988
+ const colMenuRef = React25__namespace.default.useRef(null);
4989
+ const rowMenuRef = React25__namespace.default.useRef(null);
4990
+ const exportMenuRef = React25__namespace.default.useRef(null);
4563
4991
  function focusFirstItem(container) {
4564
4992
  if (!container) return;
4565
4993
  const first = container.querySelector('[role="menuitem"]:not([disabled])');
@@ -4598,16 +5026,16 @@ function Datatable({
4598
5026
  restoreTriggerFocus();
4599
5027
  }
4600
5028
  }
4601
- React24__namespace.default.useEffect(() => {
5029
+ React25__namespace.default.useEffect(() => {
4602
5030
  if (colMenu) focusFirstItem(colMenuRef.current);
4603
5031
  }, [colMenu]);
4604
- React24__namespace.default.useEffect(() => {
5032
+ React25__namespace.default.useEffect(() => {
4605
5033
  if (rowMenu) focusFirstItem(rowMenuRef.current);
4606
5034
  }, [rowMenu]);
4607
- React24__namespace.default.useEffect(() => {
5035
+ React25__namespace.default.useEffect(() => {
4608
5036
  if (exportOpen) focusFirstItem(exportMenuRef.current);
4609
5037
  }, [exportOpen]);
4610
- React24__namespace.default.useEffect(() => {
5038
+ React25__namespace.default.useEffect(() => {
4611
5039
  if (!colMenu && !panel && !rowMenu) return;
4612
5040
  const closeAll = () => {
4613
5041
  setColMenu(null);
@@ -4630,7 +5058,7 @@ function Datatable({
4630
5058
  document.removeEventListener("keydown", onKey);
4631
5059
  };
4632
5060
  }, [colMenu, panel, rowMenu]);
4633
- React24__namespace.default.useEffect(() => {
5061
+ React25__namespace.default.useEffect(() => {
4634
5062
  if (!exportOpen) return;
4635
5063
  const close = () => {
4636
5064
  setExportOpen(false);
@@ -4649,9 +5077,9 @@ function Datatable({
4649
5077
  document.removeEventListener("keydown", onKey);
4650
5078
  };
4651
5079
  }, [exportOpen]);
4652
- const colByField = React24__namespace.default.useMemo(() => Object.fromEntries(cols.map((c) => [c.field, c])), [cols]);
5080
+ const colByField = React25__namespace.default.useMemo(() => Object.fromEntries(cols.map((c) => [c.field, c])), [cols]);
4653
5081
  const visibleCols = cols.filter((c) => !hidden.has(c.field));
4654
- const ordered = React24__namespace.default.useMemo(() => {
5082
+ const ordered = React25__namespace.default.useMemo(() => {
4655
5083
  const orderIdx = (f) => {
4656
5084
  const i = order.indexOf(f);
4657
5085
  return i === -1 ? 9999 : i;
@@ -4686,7 +5114,7 @@ function Datatable({
4686
5114
  }
4687
5115
  return {};
4688
5116
  };
4689
- const processed = React24__namespace.default.useMemo(() => {
5117
+ const processed = React25__namespace.default.useMemo(() => {
4690
5118
  if (serverMode) {
4691
5119
  if (rowOrder) {
4692
5120
  const idx = new Map(rowOrder.map((k, i) => [k, i]));
@@ -4725,10 +5153,10 @@ function Datatable({
4725
5153
  const totalRows = serverMode ? serverTotal : processed.length;
4726
5154
  const totalPages = paginated ? Math.max(1, Math.ceil(totalRows / rowsPerPage)) : 1;
4727
5155
  const paged = !paginated || serverMode ? processed : processed.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage);
4728
- React24__namespace.default.useEffect(() => {
5156
+ React25__namespace.default.useEffect(() => {
4729
5157
  if (page > totalPages - 1) setPage(0);
4730
5158
  }, [totalPages]);
4731
- const activeGroupBy = React24__namespace.default.useMemo(() => groupBy.filter((f) => colByField[f]), [groupBy, colByField]);
5159
+ const activeGroupBy = React25__namespace.default.useMemo(() => groupBy.filter((f) => colByField[f]), [groupBy, colByField]);
4732
5160
  function subtotalText(rowsIn) {
4733
5161
  const parts = [];
4734
5162
  for (const c of ordered) {
@@ -4755,7 +5183,7 @@ function Datatable({
4755
5183
  if (agg === "max") return Math.max(...nums);
4756
5184
  return null;
4757
5185
  }
4758
- const displayItems = React24__namespace.default.useMemo(() => {
5186
+ const displayItems = React25__namespace.default.useMemo(() => {
4759
5187
  if (!activeGroupBy.length) return null;
4760
5188
  const build = (rowsIn, depth, prefix) => {
4761
5189
  if (depth >= activeGroupBy.length) return rowsIn.map((row) => ({ kind: "leaf", row }));
@@ -4797,7 +5225,7 @@ function Datatable({
4797
5225
  return { top, bottom };
4798
5226
  });
4799
5227
  }
4800
- React24__namespace.default.useEffect(() => {
5228
+ React25__namespace.default.useEffect(() => {
4801
5229
  if (!rowPinning) return;
4802
5230
  const measure = () => {
4803
5231
  if (theadRef.current) setHeadH(theadRef.current.offsetHeight || 41);
@@ -4811,12 +5239,12 @@ function Datatable({
4811
5239
  const pinnedTopRows = canPinRows ? pinnedRows.top.map((k) => leafRows.find((r, i) => keyOf(r, i) === k)).filter(Boolean) : [];
4812
5240
  const pinnedBottomRows = canPinRows ? pinnedRows.bottom.map((k) => leafRows.find((r, i) => keyOf(r, i) === k)).filter(Boolean) : [];
4813
5241
  const middleRows = canPinRows ? leafRows.filter((r, i) => !pinSideOf(keyOf(r, i))) : leafRows;
4814
- const keyIndex = React24__namespace.default.useMemo(() => {
5242
+ const keyIndex = React25__namespace.default.useMemo(() => {
4815
5243
  const m = /* @__PURE__ */ new Map();
4816
5244
  leafRows.forEach((r, i) => m.set(keyOf(r, i), i));
4817
5245
  return m;
4818
5246
  }, [leafRows]);
4819
- const keyIndexMid = React24__namespace.default.useMemo(() => {
5247
+ const keyIndexMid = React25__namespace.default.useMemo(() => {
4820
5248
  const m = /* @__PURE__ */ new Map();
4821
5249
  middleRows.forEach((r, i) => m.set(keyOf(r, keyIndex.get(keyOf(r)) ?? i), i));
4822
5250
  return m;
@@ -4901,7 +5329,7 @@ function Datatable({
4901
5329
  setReorderMsg("Reorder cancelled.");
4902
5330
  }
4903
5331
  }
4904
- React24__namespace.default.useEffect(() => {
5332
+ React25__namespace.default.useEffect(() => {
4905
5333
  if (rowFocusRef.current == null) return;
4906
5334
  const key = rowFocusRef.current;
4907
5335
  rowFocusRef.current = null;
@@ -4913,7 +5341,7 @@ function Datatable({
4913
5341
  });
4914
5342
  const estRowH = rowHeight ?? (density === "compact" ? 36 : density === "comfortable" ? 56 : 44);
4915
5343
  const virtualizing = virtualized && !paginated && !activeGroupBy.length && !loading;
4916
- React24__namespace.default.useEffect(() => {
5344
+ React25__namespace.default.useEffect(() => {
4917
5345
  if (!virtualizing) return;
4918
5346
  const el = scrollRef.current;
4919
5347
  if (!el) return;
@@ -4923,13 +5351,13 @@ function Datatable({
4923
5351
  if (ro) ro.observe(el);
4924
5352
  return () => ro && ro.disconnect();
4925
5353
  }, [virtualizing, height]);
4926
- const onScrollVirtual = React24__namespace.default.useCallback((e) => {
5354
+ const onScrollVirtual = React25__namespace.default.useCallback((e) => {
4927
5355
  setScrollTop(e.currentTarget.scrollTop);
4928
5356
  }, []);
4929
5357
  const vh = viewportH || (typeof height === "number" ? height : 440);
4930
- const rowHRef = React24__namespace.default.useRef(/* @__PURE__ */ new Map());
4931
- const [measureTick, setMeasureTick] = React24__namespace.default.useState(0);
4932
- const offsets = React24__namespace.default.useMemo(() => {
5358
+ const rowHRef = React25__namespace.default.useRef(/* @__PURE__ */ new Map());
5359
+ const [measureTick, setMeasureTick] = React25__namespace.default.useState(0);
5360
+ const offsets = React25__namespace.default.useMemo(() => {
4933
5361
  if (!virtualizing) return null;
4934
5362
  const total = middleRows.length;
4935
5363
  const arr = new Float64Array(total + 1);
@@ -4939,7 +5367,7 @@ function Datatable({
4939
5367
  }
4940
5368
  return arr;
4941
5369
  }, [virtualizing, middleRows, estRowH, measureTick]);
4942
- const vWindow = React24__namespace.default.useMemo(() => {
5370
+ const vWindow = React25__namespace.default.useMemo(() => {
4943
5371
  if (!virtualizing || !offsets) return null;
4944
5372
  const total = middleRows.length;
4945
5373
  const totalH = offsets[total];
@@ -4968,7 +5396,7 @@ function Datatable({
4968
5396
  if (start > end) start = end;
4969
5397
  return { start, end, padTop: offsets[start], padBottom: Math.max(0, totalH - offsets[end]) };
4970
5398
  }, [virtualizing, offsets, middleRows.length, vh, scrollTop, overscan]);
4971
- React24__namespace.default.useLayoutEffect(() => {
5399
+ React25__namespace.default.useLayoutEffect(() => {
4972
5400
  if (!virtualizing) return;
4973
5401
  const el = scrollRef.current;
4974
5402
  if (!el) return;
@@ -4983,9 +5411,9 @@ function Datatable({
4983
5411
  }
4984
5412
  if (changed) setMeasureTick((t) => t + 1);
4985
5413
  });
4986
- const onServerChangeRef = React24__namespace.default.useRef(onServerChange);
5414
+ const onServerChangeRef = React25__namespace.default.useRef(onServerChange);
4987
5415
  onServerChangeRef.current = onServerChange;
4988
- React24__namespace.default.useEffect(() => {
5416
+ React25__namespace.default.useEffect(() => {
4989
5417
  if (!serverMode) return;
4990
5418
  const t = setTimeout(() => {
4991
5419
  onServerChangeRef.current?.({
@@ -5020,8 +5448,8 @@ function Datatable({
5020
5448
  return n;
5021
5449
  });
5022
5450
  }
5023
- const movableMidFields = React24__namespace.default.useMemo(
5024
- () => ordered.filter((c) => !pins.left.includes(c.field) && !pins.right.includes(c.field)).map((c) => c.field),
5451
+ const movableMidFields = React25__namespace.default.useMemo(
5452
+ () => ordered.filter((c) => !pins.left.includes(c.field) && !pins.right.includes(c.field) && c.reorderable !== false).map((c) => c.field),
5025
5453
  [ordered, pins]
5026
5454
  );
5027
5455
  function moveCol(field, dir) {
@@ -5051,7 +5479,7 @@ function Datatable({
5051
5479
  }
5052
5480
  return [...set].sort((a, b) => a.localeCompare(b)).map((v) => ({ value: v, label: v }));
5053
5481
  }
5054
- const [resizing, setResizing] = React24__namespace.default.useState(false);
5482
+ const [resizing, setResizing] = React25__namespace.default.useState(false);
5055
5483
  function startResize(e, field) {
5056
5484
  e.preventDefault();
5057
5485
  e.stopPropagation();
@@ -5158,7 +5586,7 @@ function Datatable({
5158
5586
  if (!agg) return null;
5159
5587
  if (aggregationValues && col.field in aggregationValues) {
5160
5588
  const pre = aggregationValues[col.field];
5161
- if (pre && typeof pre === "object" && !React24__namespace.default.isValidElement(pre)) {
5589
+ if (pre && typeof pre === "object" && !React25__namespace.default.isValidElement(pre)) {
5162
5590
  if (typeof agg === "string" && agg in pre) return pre[agg];
5163
5591
  } else {
5164
5592
  return pre;
@@ -5276,14 +5704,14 @@ function Datatable({
5276
5704
  focusCell(r, c);
5277
5705
  }
5278
5706
  }
5279
- React24__namespace.default.useEffect(() => {
5707
+ React25__namespace.default.useEffect(() => {
5280
5708
  setFocus((f) => ({ r: Math.min(f.r, Math.max(0, leafRows.length - 1)), c: Math.min(f.c, Math.max(0, ordered.length - 1)) }));
5281
5709
  }, [leafRows.length, ordered.length, page]);
5282
- const selectedRows = React24__namespace.default.useMemo(() => rows.filter((r, i) => selected.has(keyOf(r, i))), [rows, selected]);
5710
+ const selectedRows = React25__namespace.default.useMemo(() => rows.filter((r, i) => selected.has(keyOf(r, i))), [rows, selected]);
5283
5711
  function clearSelection() {
5284
5712
  setSelected(/* @__PURE__ */ new Set());
5285
5713
  }
5286
- const [editing, setEditing] = React24__namespace.default.useState(null);
5714
+ const [editing, setEditing] = React25__namespace.default.useState(null);
5287
5715
  const isColEditable = (c) => c.type !== "actions" && (c.editable ?? (editMode && c.editable !== false));
5288
5716
  function beginEdit(rowK, col, row) {
5289
5717
  if (!isColEditable(col)) return;
@@ -5318,17 +5746,17 @@ function Datatable({
5318
5746
  cancelEdit();
5319
5747
  }
5320
5748
  }
5321
- const batchEditableCols = React24__namespace.default.useMemo(
5749
+ const batchEditableCols = React25__namespace.default.useMemo(
5322
5750
  () => cols.filter((c) => c.type !== "actions" && (c.editable ?? (editMode && c.editable !== false))),
5323
5751
  [cols, editMode]
5324
5752
  );
5325
- const [batchEdit, setBatchEdit] = React24__namespace.default.useState(null);
5753
+ const [batchEdit, setBatchEdit] = React25__namespace.default.useState(null);
5326
5754
  const [batchEditPos, openBatchEdit, closeBatchEdit] = useFloating();
5327
5755
  function openBatchEditor(el) {
5328
5756
  setBatchEdit({ fields: {}, values: {} });
5329
5757
  openBatchEdit(el, "left", 320);
5330
5758
  }
5331
- React24__namespace.default.useEffect(() => {
5759
+ React25__namespace.default.useEffect(() => {
5332
5760
  if (!batchEdit) return;
5333
5761
  const close = () => {
5334
5762
  setBatchEdit(null);
@@ -5347,7 +5775,7 @@ function Datatable({
5347
5775
  document.removeEventListener("keydown", onKey);
5348
5776
  };
5349
5777
  }, [batchEdit]);
5350
- React24__namespace.default.useEffect(() => {
5778
+ React25__namespace.default.useEffect(() => {
5351
5779
  if (batchEdit && selected.size === 0) {
5352
5780
  setBatchEdit(null);
5353
5781
  closeBatchEdit();
@@ -5384,7 +5812,7 @@ function Datatable({
5384
5812
  setBatchEdit(null);
5385
5813
  closeBatchEdit();
5386
5814
  }
5387
- React24__namespace.default.useEffect(() => {
5815
+ React25__namespace.default.useEffect(() => {
5388
5816
  if (!editing) return;
5389
5817
  const onDown = (e) => {
5390
5818
  if (e.target.closest(".twc-dt__editor-wrap") || e.target.closest(".twc-pop")) return;
@@ -5406,13 +5834,12 @@ function Datatable({
5406
5834
  })() : [];
5407
5835
  const menu = [...items.filter((a) => a.showInMenu), ...pinItems];
5408
5836
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dt__actions", style: { justifyContent: col.align === "right" ? "flex-end" : "flex-start" }, children: [
5409
- inline.map((a, i) => /* @__PURE__ */ jsxRuntime.jsx(
5837
+ inline.map((a, i) => /* @__PURE__ */ jsxRuntime.jsx(Tooltip, { label: a.label, placement: "top", children: /* @__PURE__ */ jsxRuntime.jsx(
5410
5838
  "button",
5411
5839
  {
5412
5840
  type: "button",
5413
5841
  className: "twc-dt__act",
5414
5842
  "data-danger": a.danger || void 0,
5415
- title: a.label,
5416
5843
  "aria-label": a.label,
5417
5844
  disabled: a.disabled,
5418
5845
  onClick: (e) => {
@@ -5420,16 +5847,14 @@ function Datatable({
5420
5847
  a.onClick?.(row);
5421
5848
  },
5422
5849
  children: a.icon
5423
- },
5424
- i
5425
- )),
5426
- menu.length ? /* @__PURE__ */ jsxRuntime.jsx(
5850
+ }
5851
+ ) }, i)),
5852
+ menu.length ? /* @__PURE__ */ jsxRuntime.jsx(Tooltip, { label: "More actions", placement: "top", children: /* @__PURE__ */ jsxRuntime.jsx(
5427
5853
  "button",
5428
5854
  {
5429
5855
  type: "button",
5430
5856
  className: "twc-dt__act",
5431
5857
  "aria-label": "More actions",
5432
- title: "More",
5433
5858
  "aria-haspopup": "menu",
5434
5859
  "aria-expanded": rowMenu && rowMenu.row === row || void 0,
5435
5860
  onClick: (e) => {
@@ -5446,7 +5871,7 @@ function Datatable({
5446
5871
  },
5447
5872
  children: /* @__PURE__ */ jsxRuntime.jsx(Svg, { d: I.more })
5448
5873
  }
5449
- ) : null
5874
+ ) }) : null
5450
5875
  ] });
5451
5876
  }
5452
5877
  const tableMinWidth = leadW + ordered.reduce((a, c) => a + widthOf(c), 0);
@@ -5990,7 +6415,7 @@ function Datatable({
5990
6415
  const st = stickyOf(c.field);
5991
6416
  const sorted = sort && sort.field === c.field ? sort.dir : void 0;
5992
6417
  const w = widthOf(c);
5993
- const reorderable = !disableColumnReorder && st.pin == null && c.type !== "actions";
6418
+ const reorderable = !disableColumnReorder && st.pin == null && c.type !== "actions" && c.reorderable !== false;
5994
6419
  const resizable = !disableColumnResize && c.resizable !== false;
5995
6420
  const hasMenu = !c.disableColumnMenu && (c.sortable || c.filterable || c.pinnable || c.hideable);
5996
6421
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -6114,14 +6539,17 @@ function Datatable({
6114
6539
  );
6115
6540
  })
6116
6541
  ] }) }),
6117
- /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: loading ? Array.from({ length: paginated ? Math.min(rowsPerPage, 8) : 8 }).map((_, ri) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "twc-dt__row", children: [
6118
- checkboxSelection ? /* @__PURE__ */ jsxRuntime.jsx("td", { className: "twc-dt__td", "data-pin": "left", style: { insetInlineStart: 0, width: 44 }, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-dt__sk", style: { "--_w": "18px", height: 18, borderRadius: 4 } }) }) : null,
6119
- showRowNum ? /* @__PURE__ */ jsxRuntime.jsx("td", { className: "twc-dt__td twc-dt__rownum", "aria-hidden": "true", "data-pin": "left", style: { insetInlineStart: numLeft, width: NUM_W }, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-dt__sk", style: { "--_w": "16px", height: 14, borderRadius: 4 } }) }) : null,
6542
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: loading ? Array.from({ length: paginated ? Math.min(rowsPerPage, 8) : 8 }).map((_, ri) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "twc-dt__row", role: "row", children: [
6543
+ checkboxSelection ? /* @__PURE__ */ jsxRuntime.jsx("td", { className: "twc-dt__td", role: "gridcell", "data-pin": "left", style: { insetInlineStart: 0, width: 44 }, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-dt__sk", "aria-hidden": "true", style: { "--_w": "18px", height: 18, borderRadius: 4 } }) }) : null,
6544
+ showRowNum ? /* @__PURE__ */ jsxRuntime.jsx("td", { className: "twc-dt__td twc-dt__rownum", role: "gridcell", "aria-hidden": "true", "data-pin": "left", style: { insetInlineStart: numLeft, width: NUM_W }, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-dt__sk", "aria-hidden": "true", style: { "--_w": "16px", height: 14, borderRadius: 4 } }) }) : null,
6120
6545
  ordered.map((c, ci) => {
6121
6546
  const st = stickyOf(c.field);
6122
- return /* @__PURE__ */ jsxRuntime.jsx("td", { className: "twc-dt__td", "data-num": c.type === "number" || void 0, "data-pin": st.pin, "data-pin-edge": st.edge, style: { width: widthOf(c), ...st.style }, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-dt__sk", style: { "--_w": `${45 + (ri * 13 + ci * 29) % 45}%` } }) }, c.field);
6547
+ return /* @__PURE__ */ jsxRuntime.jsx("td", { className: "twc-dt__td", role: "gridcell", "data-num": c.type === "number" || void 0, "data-pin": st.pin, "data-pin-edge": st.edge, style: { width: widthOf(c), ...st.style }, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-dt__sk", "aria-hidden": "true", style: { "--_w": `${45 + (ri * 13 + ci * 29) % 45}%` } }) }, c.field);
6123
6548
  })
6124
- ] }, ri)) : (displayItems ? displayItems.length === 0 : paged.length === 0) ? /* @__PURE__ */ jsxRuntime.jsx("tr", { children: /* @__PURE__ */ jsxRuntime.jsx("td", { className: "twc-dt__td", colSpan: totalCols, style: { maxWidth: "none" }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-dt__empty", children: "No rows match your filters" }) }) }) : displayItems ? (() => {
6549
+ ] }, ri)) : (displayItems ? displayItems.length === 0 : paged.length === 0) ? (
6550
+ // #121: customizable, filter-aware empty state. #122: grid roles on the empty row.
6551
+ /* @__PURE__ */ jsxRuntime.jsx("tr", { role: "row", "aria-rowindex": 2, children: /* @__PURE__ */ jsxRuntime.jsx("td", { className: "twc-dt__td", role: "gridcell", colSpan: totalCols, style: { maxWidth: "none" }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-dt__empty", children: renderEmpty ? renderEmpty() : emptyMessage ?? (filters.length > 0 || quick.trim() !== "" ? "No rows match your filters" : "No rows") }) }) })
6552
+ ) : displayItems ? (() => {
6125
6553
  let li = -1;
6126
6554
  return displayItems.map((item) => item.kind === "group" ? renderGroupRow(item) : renderLeaf(item.row, ++li));
6127
6555
  })() : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
@@ -6134,13 +6562,13 @@ function Datatable({
6134
6562
  pinnedBottomRows.map((row) => renderLeaf(row, keyIndex.get(keyOf(row)), "bottom"))
6135
6563
  ] }) }),
6136
6564
  hasAggregation && aggOn && !loading && paged.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("tfoot", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { role: "row", children: [
6137
- checkboxSelection ? /* @__PURE__ */ jsxRuntime.jsx("td", { "data-pin": "left", "data-pin-edge": pins.left.length || showRowNum ? void 0 : "left", style: { insetInlineStart: 0, width: 44 } }) : null,
6138
- showRowNum ? /* @__PURE__ */ jsxRuntime.jsx("td", { className: "twc-dt__rownum", "aria-hidden": "true", "data-pin": "left", "data-pin-edge": pins.left.length ? void 0 : "left", style: { insetInlineStart: numLeft, width: NUM_W } }) : null,
6565
+ checkboxSelection ? /* @__PURE__ */ jsxRuntime.jsx("td", { role: "gridcell", "data-pin": "left", "data-pin-edge": pins.left.length || showRowNum ? void 0 : "left", style: { insetInlineStart: 0, width: 44 } }) : null,
6566
+ showRowNum ? /* @__PURE__ */ jsxRuntime.jsx("td", { className: "twc-dt__rownum", role: "gridcell", "aria-hidden": "true", "data-pin": "left", "data-pin-edge": pins.left.length ? void 0 : "left", style: { insetInlineStart: numLeft, width: NUM_W } }) : null,
6139
6567
  ordered.map((c) => {
6140
6568
  const st = stickyOf(c.field);
6141
6569
  const v = aggregate(c);
6142
6570
  const display = v == null ? null : c.aggregationFormatter ? c.aggregationFormatter(v) : c.valueFormatter ? c.valueFormatter(v, null) : typeof v === "number" ? v.toLocaleString() : v;
6143
- return /* @__PURE__ */ jsxRuntime.jsx("td", { "data-num": c.type === "number" || void 0, "data-pin": st.pin, "data-pin-edge": st.edge, style: { width: widthOf(c), ...st.style }, children: v == null ? null : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
6571
+ return /* @__PURE__ */ jsxRuntime.jsx("td", { role: "gridcell", "data-num": c.type === "number" || void 0, "data-pin": st.pin, "data-pin-edge": st.edge, style: { width: widthOf(c), ...st.style }, children: v == null ? null : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
6144
6572
  typeof aggOf(c) === "string" ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-dt__agg-label", children: aggLabels[aggOf(c)] }) : null,
6145
6573
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-dt__agg-val", children: display })
6146
6574
  ] }) }, c.field);
@@ -6183,7 +6611,7 @@ function Datatable({
6183
6611
  })() }),
6184
6612
  paginated ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dt__rpp", children: [
6185
6613
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-dt__rpp-label", children: "Rows per page" }),
6186
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { width: 78 }, children: /* @__PURE__ */ jsxRuntime.jsx(Select, { size: "sm", portal: true, value: String(rowsPerPage), options: rppOptions, placement: "top", onChange: (v) => {
6614
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { width: 78 }, children: /* @__PURE__ */ jsxRuntime.jsx(Select, { size: "sm", portal: true, searchable: false, value: String(rowsPerPage), options: rppOptions, placement: "top", onChange: (v) => {
6187
6615
  setRowsPerPage(Number(v));
6188
6616
  setPage(0);
6189
6617
  } }) })
@@ -6484,7 +6912,7 @@ function Datatable({
6484
6912
  )
6485
6913
  ] }) : null,
6486
6914
  shownColRows.length === 0 ? rowNumMatch ? null : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-dt__empty", style: { padding: "18px 12px" }, children: "No columns found" }) : shownColRows.map((c) => {
6487
- const canDrag = !disableColumnReorder && !colQuery.trim();
6915
+ const canDrag = !disableColumnReorder && c.reorderable !== false && !colQuery.trim();
6488
6916
  return /* @__PURE__ */ jsxRuntime.jsxs(
6489
6917
  "div",
6490
6918
  {
@@ -6861,6 +7289,7 @@ var DATEPICKER_CSS = `
6861
7289
  .twc-dp__day[data-today="true"] { font-weight: var(--font-bold); box-shadow: inset 0 0 0 1px var(--color-primary-border); }
6862
7290
  .twc-dp__day[data-selected="true"] { background: var(--color-primary); color: var(--color-primary-fg); font-weight: var(--font-bold); }
6863
7291
  .twc-dp__day:disabled { color: var(--color-text-subtle); opacity: 0.4; cursor: not-allowed; }
7292
+ .twc-dp__sr { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
6864
7293
  .twc-dp__day:focus-visible { outline: none; box-shadow: var(--ring); }
6865
7294
  .twc-dp__months { display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px; }
6866
7295
  .twc-dp__mo { padding: 10px 0; border: none; background: transparent; cursor: pointer; font-family: inherit; font-size: var(--text-sm); color: var(--color-text); border-radius: var(--radius-md); }
@@ -6906,23 +7335,23 @@ function DatePicker({
6906
7335
  }) {
6907
7336
  const __twcStyles0 = useScopedStyles("twc-field-styles", FIELD_CSS3);
6908
7337
  const __twcStyles1 = useScopedStyles("twc-datepicker-styles", DATEPICKER_CSS);
6909
- const autoId = React24__namespace.default.useId();
7338
+ const autoId = React25__namespace.default.useId();
6910
7339
  const fieldId = id || autoId;
6911
7340
  const descId = `${fieldId}-desc`;
6912
7341
  const invalid = Boolean(error);
6913
- const [internal, setInternal] = React24__namespace.default.useState(defaultValue);
7342
+ const [internal, setInternal] = React25__namespace.default.useState(defaultValue);
6914
7343
  const selected = value !== void 0 ? value : internal;
6915
- const [open, setOpen] = React24__namespace.default.useState(false);
6916
- const [view, setView] = React24__namespace.default.useState(selected || /* @__PURE__ */ new Date());
6917
- const [mode, setMode] = React24__namespace.default.useState("days");
6918
- const [focusDate, setFocusDate] = React24__namespace.default.useState(null);
6919
- const [coords, setCoords] = React24__namespace.default.useState(null);
6920
- const wrapRef = React24__namespace.default.useRef(null);
6921
- const triggerRef = React24__namespace.default.useRef(null);
6922
- const popRef = React24__namespace.default.useRef(null);
6923
- const gridRef = React24__namespace.default.useRef(null);
7344
+ const [open, setOpen] = React25__namespace.default.useState(false);
7345
+ const [view, setView] = React25__namespace.default.useState(selected || /* @__PURE__ */ new Date());
7346
+ const [mode, setMode] = React25__namespace.default.useState("days");
7347
+ const [focusDate, setFocusDate] = React25__namespace.default.useState(null);
7348
+ const [coords, setCoords] = React25__namespace.default.useState(null);
7349
+ const wrapRef = React25__namespace.default.useRef(null);
7350
+ const triggerRef = React25__namespace.default.useRef(null);
7351
+ const popRef = React25__namespace.default.useRef(null);
7352
+ const gridRef = React25__namespace.default.useRef(null);
6924
7353
  const POP_W = 280;
6925
- const place = React24__namespace.default.useCallback(() => {
7354
+ const place = React25__namespace.default.useCallback(() => {
6926
7355
  const el = triggerRef.current;
6927
7356
  if (!el) return;
6928
7357
  const r = el.getBoundingClientRect();
@@ -6933,7 +7362,7 @@ function DatePicker({
6933
7362
  left = Math.max(8, left);
6934
7363
  setCoords({ left, top: flip ? void 0 : Math.round(r.bottom + 6), bottom: flip ? Math.round(vh - r.top + 6) : void 0 });
6935
7364
  }, []);
6936
- React24__namespace.default.useEffect(() => {
7365
+ React25__namespace.default.useEffect(() => {
6937
7366
  if (!open) return void 0;
6938
7367
  place();
6939
7368
  const onMove = () => place();
@@ -6944,7 +7373,7 @@ function DatePicker({
6944
7373
  window.removeEventListener("resize", onMove);
6945
7374
  };
6946
7375
  }, [open, place]);
6947
- React24__namespace.default.useEffect(() => {
7376
+ React25__namespace.default.useEffect(() => {
6948
7377
  if (!open) return;
6949
7378
  setView(selected || /* @__PURE__ */ new Date());
6950
7379
  setMode("days");
@@ -6964,8 +7393,8 @@ function DatePicker({
6964
7393
  document.removeEventListener("keydown", onKey);
6965
7394
  };
6966
7395
  }, [open]);
6967
- const prevOpen = React24__namespace.default.useRef(false);
6968
- React24__namespace.default.useEffect(() => {
7396
+ const prevOpen = React25__namespace.default.useRef(false);
7397
+ React25__namespace.default.useEffect(() => {
6969
7398
  let id2;
6970
7399
  if (!prevOpen.current && open) {
6971
7400
  id2 = setTimeout(() => gridRef.current?.querySelector('[tabindex="0"]')?.focus({ preventScroll: true }), 30);
@@ -6976,9 +7405,9 @@ function DatePicker({
6976
7405
  prevOpen.current = open;
6977
7406
  return () => clearTimeout(id2);
6978
7407
  }, [open]);
6979
- const months = React24__namespace.default.useMemo(() => locale === void 0 ? MONTHS : monthNames(locale), [locale]);
6980
- const monthsShort = React24__namespace.default.useMemo(() => locale === void 0 ? MONTHS.map((n) => n.slice(0, 3)) : monthShortNames(locale), [locale]);
6981
- const dows = React24__namespace.default.useMemo(
7408
+ const months = React25__namespace.default.useMemo(() => locale === void 0 ? MONTHS : monthNames(locale), [locale]);
7409
+ const monthsShort = React25__namespace.default.useMemo(() => locale === void 0 ? MONTHS.map((n) => n.slice(0, 3)) : monthShortNames(locale), [locale]);
7410
+ const dows = React25__namespace.default.useMemo(
6982
7411
  () => locale === void 0 ? Array.from({ length: 7 }, (_, i) => DOW[(i + weekStartsOn) % 7]) : weekdayNames(locale, weekStartsOn),
6983
7412
  [locale, weekStartsOn]
6984
7413
  );
@@ -7006,7 +7435,7 @@ function DatePicker({
7006
7435
  };
7007
7436
  const cells = Array.from({ length: 42 }, (_, i) => new Date(gridStart.getFullYear(), gridStart.getMonth(), gridStart.getDate() + i));
7008
7437
  const tabbableDate = (focusDate && cells.some((c) => sameDay(c, focusDate)) ? focusDate : null) || (selected && cells.some((c) => sameDay(c, selected)) ? selected : null) || (cells.some((c) => sameDay(c, today)) ? today : null) || new Date(y, m, 1);
7009
- React24__namespace.default.useEffect(() => {
7438
+ React25__namespace.default.useEffect(() => {
7010
7439
  if (!open || mode !== "days" || !focusDate) return;
7011
7440
  gridRef.current?.querySelector(`[data-key="${keyOf(focusDate)}"]`)?.focus();
7012
7441
  }, [focusDate, open, mode]);
@@ -7096,6 +7525,7 @@ function DatePicker({
7096
7525
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dp__head", children: [
7097
7526
  /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-dp__nav", "aria-label": "Previous", onClick: () => setView(mode === "months" ? new Date(y - 1, m, 1) : new Date(y, m - 1, 1)), children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m15 18-6-6 6-6" }) }) }),
7098
7527
  /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-dp__title", onClick: () => setMode(mode === "days" ? "months" : "days"), children: mode === "days" ? `${months[m]} ${y}` : y }),
7528
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-dp__sr", "aria-live": "polite", children: mode === "days" ? `${months[m]} ${y}` : y }),
7099
7529
  /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-dp__nav", "aria-label": "Next", onClick: () => setView(mode === "months" ? new Date(y + 1, m, 1) : new Date(y, m + 1, 1)), children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m9 18 6-6-6-6" }) }) })
7100
7530
  ] }),
7101
7531
  mode === "days" ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dp__grid", ref: gridRef, onKeyDown: onGridKeyDown, children: [
@@ -7196,6 +7626,7 @@ var RANGE_CSS = `
7196
7626
  .twc-drp__day[data-edge="end"] { background: var(--color-primary); color: var(--color-primary-fg); border-radius: 0 var(--radius-md) var(--radius-md) 0; font-weight: var(--font-bold); }
7197
7627
  .twc-drp__day[data-edge="both"] { background: var(--color-primary); color: var(--color-primary-fg); border-radius: var(--radius-md); font-weight: var(--font-bold); }
7198
7628
  .twc-drp__day:disabled { opacity: 0.3; cursor: not-allowed; }
7629
+ .twc-drp__sr { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
7199
7630
  [dir="rtl"] .twc-drp__nav svg { transform: scaleX(-1); }
7200
7631
  `;
7201
7632
  var MONTHS2 = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
@@ -7221,6 +7652,9 @@ function DateRangePicker({
7221
7652
  presets = true,
7222
7653
  locale,
7223
7654
  weekStartsOn = 0,
7655
+ min,
7656
+ max,
7657
+ disabledDate,
7224
7658
  disabled = false,
7225
7659
  tone = "primary",
7226
7660
  onChange,
@@ -7229,23 +7663,23 @@ function DateRangePicker({
7229
7663
  }) {
7230
7664
  const __twcStyles0 = useScopedStyles("twc-field-styles", FIELD_CSS4);
7231
7665
  const __twcStyles1 = useScopedStyles("twc-drp-styles", RANGE_CSS);
7232
- const [internal, setInternal] = React24__namespace.default.useState(defaultValue);
7666
+ const [internal, setInternal] = React25__namespace.default.useState(defaultValue);
7233
7667
  const range = value !== void 0 ? value : internal;
7234
- const [open, setOpen] = React24__namespace.default.useState(false);
7235
- const [view, setView] = React24__namespace.default.useState(range.start || /* @__PURE__ */ new Date());
7236
- const [hover, setHover] = React24__namespace.default.useState(null);
7237
- const [coords, setCoords] = React24__namespace.default.useState(null);
7238
- const wrapRef = React24__namespace.default.useRef(null);
7239
- const triggerRef = React24__namespace.default.useRef(null);
7240
- const popRef = React24__namespace.default.useRef(null);
7241
- const labelId = React24__namespace.default.useId();
7668
+ const [open, setOpen] = React25__namespace.default.useState(false);
7669
+ const [view, setView] = React25__namespace.default.useState(range.start || /* @__PURE__ */ new Date());
7670
+ const [hover, setHover] = React25__namespace.default.useState(null);
7671
+ const [coords, setCoords] = React25__namespace.default.useState(null);
7672
+ const wrapRef = React25__namespace.default.useRef(null);
7673
+ const triggerRef = React25__namespace.default.useRef(null);
7674
+ const popRef = React25__namespace.default.useRef(null);
7675
+ const labelId = React25__namespace.default.useId();
7242
7676
  const descId = `${labelId}-desc`;
7243
7677
  const invalid = Boolean(error);
7244
- React24__namespace.default.useEffect(() => {
7678
+ React25__namespace.default.useEffect(() => {
7245
7679
  if (disabled) setOpen(false);
7246
7680
  }, [disabled]);
7247
7681
  const POP_W = 420;
7248
- const place = React24__namespace.default.useCallback(() => {
7682
+ const place = React25__namespace.default.useCallback(() => {
7249
7683
  const el = triggerRef.current;
7250
7684
  if (!el) return;
7251
7685
  const r = el.getBoundingClientRect();
@@ -7256,7 +7690,7 @@ function DateRangePicker({
7256
7690
  left = Math.max(8, left);
7257
7691
  setCoords({ left, top: flip ? void 0 : Math.round(r.bottom + 6), bottom: flip ? Math.round(vh - r.top + 6) : void 0 });
7258
7692
  }, []);
7259
- React24__namespace.default.useEffect(() => {
7693
+ React25__namespace.default.useEffect(() => {
7260
7694
  if (!open) return void 0;
7261
7695
  place();
7262
7696
  const onMove = () => place();
@@ -7267,7 +7701,7 @@ function DateRangePicker({
7267
7701
  window.removeEventListener("resize", onMove);
7268
7702
  };
7269
7703
  }, [open, place]);
7270
- React24__namespace.default.useEffect(() => {
7704
+ React25__namespace.default.useEffect(() => {
7271
7705
  if (!open) return;
7272
7706
  const onDown = (e) => {
7273
7707
  if (wrapRef.current && wrapRef.current.contains(e.target)) return;
@@ -7288,7 +7722,9 @@ function DateRangePicker({
7288
7722
  if (value === void 0) setInternal(r);
7289
7723
  onChange?.(r);
7290
7724
  };
7725
+ const outOfRange = (d) => min && ymd(d) < ymd(min) || max && ymd(d) > ymd(max) || Boolean(disabledDate && disabledDate(d));
7291
7726
  const clickDay = (d) => {
7727
+ if (outOfRange(d)) return;
7292
7728
  if (!range.start || range.start && range.end) {
7293
7729
  set({ start: d, end: null });
7294
7730
  } else {
@@ -7305,8 +7741,8 @@ function DateRangePicker({
7305
7741
  setView(start);
7306
7742
  setOpen(false);
7307
7743
  };
7308
- const months = React24__namespace.default.useMemo(() => locale === void 0 ? MONTHS2 : monthNames2(locale), [locale]);
7309
- const dows = React24__namespace.default.useMemo(
7744
+ const months = React25__namespace.default.useMemo(() => locale === void 0 ? MONTHS2 : monthNames2(locale), [locale]);
7745
+ const dows = React25__namespace.default.useMemo(
7310
7746
  () => locale === void 0 ? Array.from({ length: 7 }, (_, i) => DOW2[(i + weekStartsOn) % 7]) : weekdayNames2(locale, weekStartsOn),
7311
7747
  [locale, weekStartsOn]
7312
7748
  );
@@ -7388,6 +7824,11 @@ function DateRangePicker({
7388
7824
  " ",
7389
7825
  y
7390
7826
  ] }),
7827
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "twc-drp__sr", "aria-live": "polite", children: [
7828
+ months[m],
7829
+ " ",
7830
+ y
7831
+ ] }),
7391
7832
  /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-drp__nav", "aria-label": "Next month", onClick: () => setView(new Date(y, m + 1, 1)), children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m9 18 6-6-6-6" }) }) })
7392
7833
  ] }),
7393
7834
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-drp__grid", children: [
@@ -7401,6 +7842,7 @@ function DateRangePicker({
7401
7842
  type: "button",
7402
7843
  className: "twc-drp__day",
7403
7844
  "data-outside": outside || void 0,
7845
+ disabled: outOfRange(d) || void 0,
7404
7846
  "data-in": inRange(t) || void 0,
7405
7847
  "data-edge": edgeOf(t) || void 0,
7406
7848
  "aria-pressed": !!edgeOf(t),
@@ -7487,14 +7929,14 @@ function Dialog({
7487
7929
  className = "",
7488
7930
  ...rest
7489
7931
  }) {
7490
- const autoId = React24__namespace.default.useId();
7932
+ const autoId = React25__namespace.default.useId();
7491
7933
  const titleId = `${autoId}-title`;
7492
7934
  const descId = `${autoId}-desc`;
7493
- const dialogRef = React24__namespace.default.useRef(null);
7935
+ const dialogRef = React25__namespace.default.useRef(null);
7494
7936
  const __twcStyles = useScopedStyles("twc-dialog-styles", DIALOG_CSS);
7495
7937
  const renderPortal = usePortal();
7496
- const [mounted, setMounted] = React24__namespace.default.useState(open);
7497
- React24__namespace.default.useEffect(() => {
7938
+ const [mounted, setMounted] = React25__namespace.default.useState(open);
7939
+ React25__namespace.default.useEffect(() => {
7498
7940
  if (open) {
7499
7941
  setMounted(true);
7500
7942
  return;
@@ -7502,16 +7944,10 @@ function Dialog({
7502
7944
  const t = setTimeout(() => setMounted(false), 170);
7503
7945
  return () => clearTimeout(t);
7504
7946
  }, [open]);
7505
- React24__namespace.default.useEffect(() => {
7506
- if (!open) return void 0;
7507
- const prev = document.body.style.overflow;
7508
- document.body.style.overflow = "hidden";
7509
- return () => {
7510
- document.body.style.overflow = prev;
7511
- };
7512
- }, [open]);
7947
+ useScrollLock(open);
7948
+ useInertBackground(dialogRef, open && mounted);
7513
7949
  useFocusTrap(dialogRef, open && mounted);
7514
- React24__namespace.default.useEffect(() => {
7950
+ React25__namespace.default.useEffect(() => {
7515
7951
  if (!open) return void 0;
7516
7952
  const onKey = (e) => {
7517
7953
  if (e.key === "Escape") {
@@ -7658,14 +8094,14 @@ function Drawer({
7658
8094
  style,
7659
8095
  ...rest
7660
8096
  }) {
7661
- const autoId = React24__namespace.default.useId();
8097
+ const autoId = React25__namespace.default.useId();
7662
8098
  const titleId = `${autoId}-title`;
7663
8099
  const descId = `${autoId}-desc`;
7664
- const panelRef = React24__namespace.default.useRef(null);
8100
+ const panelRef = React25__namespace.default.useRef(null);
7665
8101
  const __twcStyles = useScopedStyles("twc-drawer-styles", DRAWER_CSS);
7666
8102
  const renderPortal = usePortal();
7667
- const [mounted, setMounted] = React24__namespace.default.useState(open);
7668
- React24__namespace.default.useEffect(() => {
8103
+ const [mounted, setMounted] = React25__namespace.default.useState(open);
8104
+ React25__namespace.default.useEffect(() => {
7669
8105
  if (open) {
7670
8106
  setMounted(true);
7671
8107
  return;
@@ -7673,16 +8109,10 @@ function Drawer({
7673
8109
  const t = setTimeout(() => setMounted(false), 170);
7674
8110
  return () => clearTimeout(t);
7675
8111
  }, [open]);
7676
- React24__namespace.default.useEffect(() => {
7677
- if (!open) return void 0;
7678
- const prev = document.body.style.overflow;
7679
- document.body.style.overflow = "hidden";
7680
- return () => {
7681
- document.body.style.overflow = prev;
7682
- };
7683
- }, [open]);
7684
- useFocusTrap(panelRef, open && mounted);
7685
- React24__namespace.default.useEffect(() => {
8112
+ useScrollLock(open);
8113
+ useInertBackground(panelRef, open && mounted);
8114
+ useFocusTrap(panelRef, open && mounted);
8115
+ React25__namespace.default.useEffect(() => {
7686
8116
  if (!open) return void 0;
7687
8117
  const onKey = (e) => {
7688
8118
  if (e.key === "Escape") {
@@ -7771,16 +8201,21 @@ function Field({
7771
8201
  ...rest
7772
8202
  }) {
7773
8203
  const __twcStyles = useScopedStyles("twc-field-styles", FIELD_CSS5);
7774
- const autoId = React24__namespace.default.useId();
8204
+ const autoId = React25__namespace.default.useId();
7775
8205
  const fieldId = id || autoId;
7776
8206
  const descId = `${fieldId}-desc`;
8207
+ const hasMsg = Boolean(error || hint);
8208
+ const control = React25__namespace.default.isValidElement(children) ? React25__namespace.default.cloneElement(children, {
8209
+ "aria-describedby": [children.props["aria-describedby"], hasMsg ? descId : null].filter(Boolean).join(" ") || void 0,
8210
+ "aria-invalid": children.props["aria-invalid"] !== void 0 ? children.props["aria-invalid"] : error ? true : void 0
8211
+ }) : children;
7777
8212
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, "data-size": size, ...rest, children: [
7778
8213
  __twcStyles,
7779
8214
  label ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-field__label", htmlFor, children: [
7780
8215
  label,
7781
8216
  required ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-field__req", children: "*" }) : null
7782
8217
  ] }) : null,
7783
- children,
8218
+ control,
7784
8219
  error ? /* @__PURE__ */ jsxRuntime.jsx("span", { id: descId, className: "twc-field__error", children: error }) : hint ? /* @__PURE__ */ jsxRuntime.jsx("span", { id: descId, className: "twc-field__hint", children: hint }) : null
7785
8220
  ] });
7786
8221
  }
@@ -7834,6 +8269,14 @@ function fmtSize(bytes) {
7834
8269
  if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + " KB";
7835
8270
  return (bytes / (1024 * 1024)).toFixed(1) + " MB";
7836
8271
  }
8272
+ function matchesAccept(file, accept) {
8273
+ if (!accept) return true;
8274
+ const toks = accept.split(",").map((t) => t.trim().toLowerCase()).filter(Boolean);
8275
+ if (!toks.length) return true;
8276
+ const name = file.name.toLowerCase(), type = (file.type || "").toLowerCase();
8277
+ return toks.some((t) => t.startsWith(".") ? name.endsWith(t) : t.endsWith("/*") ? type.startsWith(t.slice(0, -1)) : type === t);
8278
+ }
8279
+ var fileKey = (f) => `${f.name}-${f.size}-${f.lastModified}`;
7837
8280
  function FileUpload({
7838
8281
  accept,
7839
8282
  multiple = false,
@@ -7845,17 +8288,20 @@ function FileUpload({
7845
8288
  required = false,
7846
8289
  value,
7847
8290
  defaultValue = [],
8291
+ maxSize,
8292
+ maxFiles,
8293
+ onReject,
7848
8294
  onChange,
7849
8295
  id,
7850
8296
  className = "",
7851
8297
  ...rest
7852
8298
  }) {
7853
8299
  const __twcStyles = useScopedStyles("twc-upload-styles", UPLOAD_CSS);
7854
- const [internal, setInternal] = React24__namespace.default.useState(defaultValue);
8300
+ const [internal, setInternal] = React25__namespace.default.useState(defaultValue);
7855
8301
  const files = value !== void 0 ? value : internal;
7856
- const [drag, setDrag] = React24__namespace.default.useState(false);
7857
- const inputRef = React24__namespace.default.useRef(null);
7858
- const autoId = React24__namespace.default.useId();
8302
+ const [drag, setDrag] = React25__namespace.default.useState(false);
8303
+ const inputRef = React25__namespace.default.useRef(null);
8304
+ const autoId = React25__namespace.default.useId();
7859
8305
  const fieldId = id || autoId;
7860
8306
  const descId = `${fieldId}-desc`;
7861
8307
  const invalid = Boolean(error);
@@ -7864,8 +8310,28 @@ function FileUpload({
7864
8310
  onChange?.(next);
7865
8311
  };
7866
8312
  function addFiles(list) {
7867
- const arr = Array.from(list);
7868
- set(multiple ? [...files, ...arr] : arr.slice(0, 1));
8313
+ const incoming = Array.from(list);
8314
+ const retained = multiple ? files.slice() : [];
8315
+ const seen2 = new Set(retained.map(fileKey));
8316
+ const accepted = [];
8317
+ const rejections = [];
8318
+ for (const f of incoming) {
8319
+ if (multiple && seen2.has(fileKey(f))) continue;
8320
+ let reason = null;
8321
+ if (accept && !matchesAccept(f, accept)) reason = "type";
8322
+ else if (maxSize && f.size > maxSize) reason = "size";
8323
+ else if (multiple && maxFiles && retained.length + accepted.length >= maxFiles) reason = "count";
8324
+ if (reason) {
8325
+ rejections.push({ file: f, reason });
8326
+ continue;
8327
+ }
8328
+ accepted.push(f);
8329
+ if (multiple) seen2.add(fileKey(f));
8330
+ }
8331
+ if (rejections.length) onReject?.(rejections);
8332
+ if (multiple) {
8333
+ if (accepted.length) set([...retained, ...accepted]);
8334
+ } else if (accepted.length) set(accepted.slice(0, 1));
7869
8335
  }
7870
8336
  function remove(i) {
7871
8337
  set(files.filter((_, idx) => idx !== i));
@@ -7939,7 +8405,7 @@ function FileUpload({
7939
8405
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-upload__file-size", children: fmtSize(f.size) })
7940
8406
  ] }),
7941
8407
  /* @__PURE__ */ jsxRuntime.jsx("button", { className: "twc-upload__file-x", "aria-label": `Remove ${f.name}`, onClick: () => remove(i), type: "button", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.4", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 6 6 18M6 6l12 12" }) }) })
7942
- ] }, i)) }) : null,
8408
+ ] }, fileKey(f))) }) : null,
7943
8409
  error ? /* @__PURE__ */ jsxRuntime.jsx("span", { id: descId, className: "twc-field__error", children: error }) : null
7944
8410
  ] });
7945
8411
  }
@@ -8058,15 +8524,15 @@ function Kanban({
8058
8524
  ...rest
8059
8525
  }) {
8060
8526
  const __twcStyles = useScopedStyles("twc-kanban-styles", KANBAN_CSS);
8061
- const [internal, setInternal] = React24__namespace.default.useState(cards ?? defaultCards ?? []);
8527
+ const [internal, setInternal] = React25__namespace.default.useState(cards ?? defaultCards ?? []);
8062
8528
  const items = cards !== void 0 ? cards : internal;
8063
- const [drag, setDrag] = React24__namespace.default.useState(null);
8064
- const [overCol, setOverCol] = React24__namespace.default.useState(null);
8065
- const [grab, setGrab] = React24__namespace.default.useState(null);
8066
- const [announce, setAnnounce] = React24__namespace.default.useState("");
8067
- const rootRef = React24__namespace.default.useRef(null);
8068
- const focusIdRef = React24__namespace.default.useRef(null);
8069
- React24__namespace.default.useEffect(() => {
8529
+ const [drag, setDrag] = React25__namespace.default.useState(null);
8530
+ const [overCol, setOverCol] = React25__namespace.default.useState(null);
8531
+ const [grab, setGrab] = React25__namespace.default.useState(null);
8532
+ const [announce, setAnnounce] = React25__namespace.default.useState("");
8533
+ const rootRef = React25__namespace.default.useRef(null);
8534
+ const focusIdRef = React25__namespace.default.useRef(null);
8535
+ React25__namespace.default.useEffect(() => {
8070
8536
  if (focusIdRef.current == null) return void 0;
8071
8537
  const raf = requestAnimationFrame(() => {
8072
8538
  const id = focusIdRef.current;
@@ -8211,42 +8677,49 @@ var LIST_CSS = `
8211
8677
  .twc-list__title { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
8212
8678
  .twc-list__desc { font-size: var(--text-xs); color: var(--color-text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
8213
8679
  .twc-list__trail { flex: none; display: inline-flex; align-items: center; gap: var(--space-2); color: var(--color-text-subtle); font-size: var(--text-sm); }
8680
+ .twc-list__empty { padding: 30px 12px; text-align: center; color: var(--color-text-subtle); font-size: var(--text-sm); }
8214
8681
  `;
8215
- function List({ items, plain = false, className = "", ...rest }) {
8682
+ function List({ items, plain = false, emptyMessage, className = "", ...rest }) {
8216
8683
  const __twcStyles = useScopedStyles("twc-list-styles", LIST_CSS);
8217
8684
  const safeHref12 = (url) => {
8218
8685
  if (url == null) return void 0;
8219
8686
  const s = String(url).replace(/[\x00-\x20]+/g, "").toLowerCase();
8220
8687
  return s.startsWith("javascript:") || s.startsWith("data:") || s.startsWith("vbscript:") ? void 0 : url;
8221
8688
  };
8222
- return /* @__PURE__ */ jsxRuntime.jsxs("ul", { className: `twc-list ${className}`, "data-plain": plain || void 0, ...rest, children: [
8223
- __twcStyles,
8224
- items.map((it, i) => {
8225
- const href = safeHref12(it.href);
8226
- const interactive = Boolean(it.onClick || href);
8227
- const Tag2 = href ? "a" : interactive ? "button" : "div";
8228
- const content = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
8229
- it.leading ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-list__lead", "aria-hidden": "true", children: it.leading }) : null,
8230
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "twc-list__main", children: [
8231
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-list__title", children: it.title }),
8232
- it.description ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-list__desc", children: it.description }) : null
8233
- ] }),
8234
- it.trailing ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-list__trail", children: it.trailing }) : null
8235
- ] });
8236
- return /* @__PURE__ */ jsxRuntime.jsx("li", { style: { display: "contents" }, children: /* @__PURE__ */ jsxRuntime.jsx(
8237
- Tag2,
8238
- {
8239
- className: "twc-list__item",
8240
- "data-interactive": interactive || void 0,
8241
- "data-active": it.active || void 0,
8242
- href,
8243
- type: Tag2 === "button" ? "button" : void 0,
8244
- onClick: it.onClick,
8245
- children: content
8246
- }
8247
- ) }, i);
8248
- })
8249
- ] });
8689
+ return (
8690
+ // role="list" before {...rest} so a consumer can still override it (e.g. role="menu");
8691
+ // list-style:none + display:contents on the <li> drop the implicit list/listitem roles
8692
+ // in some engines, so we set them explicitly (#123).
8693
+ /* @__PURE__ */ jsxRuntime.jsxs("ul", { className: `twc-list ${className}`, role: "list", "data-plain": plain || void 0, ...rest, children: [
8694
+ __twcStyles,
8695
+ items.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("li", { role: "presentation", style: { display: "contents" }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-list__empty", children: emptyMessage ?? "Nothing here yet" }) }) : items.map((it, i) => {
8696
+ const href = safeHref12(it.href);
8697
+ const interactive = Boolean(it.onClick || href);
8698
+ const Tag2 = href ? "a" : interactive ? "button" : "div";
8699
+ const content = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
8700
+ it.leading ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-list__lead", "aria-hidden": "true", children: it.leading }) : null,
8701
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "twc-list__main", children: [
8702
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-list__title", children: it.title }),
8703
+ it.description ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-list__desc", children: it.description }) : null
8704
+ ] }),
8705
+ it.trailing ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-list__trail", children: it.trailing }) : null
8706
+ ] });
8707
+ return /* @__PURE__ */ jsxRuntime.jsx("li", { role: "listitem", style: { display: "contents" }, children: /* @__PURE__ */ jsxRuntime.jsx(
8708
+ Tag2,
8709
+ {
8710
+ className: "twc-list__item",
8711
+ "data-interactive": interactive || void 0,
8712
+ "data-active": it.active || void 0,
8713
+ "aria-current": it.active ? "page" : void 0,
8714
+ href,
8715
+ type: Tag2 === "button" ? "button" : void 0,
8716
+ onClick: it.onClick,
8717
+ children: content
8718
+ }
8719
+ ) }, i);
8720
+ })
8721
+ ] })
8722
+ );
8250
8723
  }
8251
8724
  var NAVBAR_CSS = `
8252
8725
  .twc-navbar { display: flex; align-items: center; gap: var(--space-4); height: 60px; padding: 0 var(--space-5);
@@ -8369,21 +8842,21 @@ function Popover({
8369
8842
  ...rest
8370
8843
  }) {
8371
8844
  const __twcStyles = useScopedStyles("twc-popover-styles", POPOVER_CSS);
8372
- const [openState, setOpenState] = React24__namespace.default.useState(defaultOpen);
8373
- const [render, setRender] = React24__namespace.default.useState(false);
8374
- const [pos, setPos] = React24__namespace.default.useState(null);
8375
- const wrapRef = React24__namespace.default.useRef(null);
8376
- const popRef = React24__namespace.default.useRef(null);
8377
- const popId = React24__namespace.default.useId();
8845
+ const [openState, setOpenState] = React25__namespace.default.useState(defaultOpen);
8846
+ const [render, setRender] = React25__namespace.default.useState(false);
8847
+ const [pos, setPos] = React25__namespace.default.useState(null);
8848
+ const wrapRef = React25__namespace.default.useRef(null);
8849
+ const popRef = React25__namespace.default.useRef(null);
8850
+ const popId = React25__namespace.default.useId();
8378
8851
  const controlled = openProp !== void 0;
8379
8852
  const open = controlled ? !!openProp : openState;
8380
- const openRef = React24__namespace.default.useRef(open);
8853
+ const openRef = React25__namespace.default.useRef(open);
8381
8854
  openRef.current = open;
8382
- const controlledRef = React24__namespace.default.useRef(controlled);
8855
+ const controlledRef = React25__namespace.default.useRef(controlled);
8383
8856
  controlledRef.current = controlled;
8384
- const onOpenChangeRef = React24__namespace.default.useRef(onOpenChange);
8857
+ const onOpenChangeRef = React25__namespace.default.useRef(onOpenChange);
8385
8858
  onOpenChangeRef.current = onOpenChange;
8386
- const setOpen = React24__namespace.default.useCallback((next) => {
8859
+ const setOpen = React25__namespace.default.useCallback((next) => {
8387
8860
  const value = typeof next === "function" ? next(openRef.current) : next;
8388
8861
  if (value === openRef.current) return;
8389
8862
  if (!controlledRef.current) setOpenState(value);
@@ -8391,7 +8864,7 @@ function Popover({
8391
8864
  }, []);
8392
8865
  const toggle = () => setOpen((o) => !o);
8393
8866
  const RD = { createPortal: typeof reactDom.createPortal === "function" ? reactDom.createPortal : typeof window !== "undefined" && window.ReactDOM && window.ReactDOM.createPortal };
8394
- const place = React24__namespace.default.useCallback(() => {
8867
+ const place = React25__namespace.default.useCallback(() => {
8395
8868
  const t = wrapRef.current;
8396
8869
  if (!t) return;
8397
8870
  const r = t.getBoundingClientRect();
@@ -8401,7 +8874,13 @@ function Popover({
8401
8874
  let top, left, flip = false, arrow = {};
8402
8875
  const vertical = placement === "top" || placement === "bottom";
8403
8876
  if (vertical) {
8404
- flip = placement === "top" || placement === "bottom" && vh - r.bottom < 180 && r.top > vh - r.bottom;
8877
+ const ph2 = popRef.current ? popRef.current.offsetHeight : 120;
8878
+ const spaceAbove = r.top - gap - M;
8879
+ const spaceBelow = vh - r.bottom - gap - M;
8880
+ let openUp = placement === "top";
8881
+ if (openUp && spaceAbove < ph2 && spaceBelow > spaceAbove) openUp = false;
8882
+ else if (!openUp && spaceBelow < ph2 && spaceAbove > spaceBelow) openUp = true;
8883
+ flip = openUp;
8405
8884
  top = flip ? void 0 : r.bottom + gap;
8406
8885
  const bottom = flip ? vh - r.top + gap : void 0;
8407
8886
  let l = align === "start" ? r.left : align === "end" ? r.right - w : r.left + r.width / 2 - w / 2;
@@ -8422,7 +8901,7 @@ function Popover({
8422
8901
  arrow = { top: Math.min(Math.max(cy - top - 5.5, 8), ph - 18), [onRight ? "left" : "right"]: -6 };
8423
8902
  setPos({ top, left, width: w, flip: false, arrow });
8424
8903
  }, [placement, align, width]);
8425
- React24__namespace.default.useEffect(() => {
8904
+ React25__namespace.default.useEffect(() => {
8426
8905
  if (!open) return;
8427
8906
  place();
8428
8907
  const onMove = () => place();
@@ -8445,7 +8924,7 @@ function Popover({
8445
8924
  document.removeEventListener("keydown", onKey);
8446
8925
  };
8447
8926
  }, [open, place]);
8448
- React24__namespace.default.useEffect(() => {
8927
+ React25__namespace.default.useEffect(() => {
8449
8928
  if (open) {
8450
8929
  setRender(true);
8451
8930
  return;
@@ -8453,12 +8932,12 @@ function Popover({
8453
8932
  const t = setTimeout(() => setRender(false), 170);
8454
8933
  return () => clearTimeout(t);
8455
8934
  }, [open]);
8456
- React24__namespace.default.useEffect(() => {
8935
+ React25__namespace.default.useEffect(() => {
8457
8936
  if (render && open) place();
8458
8937
  }, [render, place]);
8459
- const restoreRef = React24__namespace.default.useRef(null);
8460
- const needFocusRef = React24__namespace.default.useRef(false);
8461
- React24__namespace.default.useEffect(() => {
8938
+ const restoreRef = React25__namespace.default.useRef(null);
8939
+ const needFocusRef = React25__namespace.default.useRef(false);
8940
+ React25__namespace.default.useEffect(() => {
8462
8941
  if (open) {
8463
8942
  restoreRef.current = document.activeElement;
8464
8943
  needFocusRef.current = true;
@@ -8469,12 +8948,7 @@ function Popover({
8469
8948
  needFocusRef.current = false;
8470
8949
  if (prev && typeof prev.focus === "function" && popRef.current?.contains(document.activeElement)) prev.focus();
8471
8950
  }, [open]);
8472
- React24__namespace.default.useEffect(() => {
8473
- if (open && render && pos && needFocusRef.current) {
8474
- needFocusRef.current = false;
8475
- popRef.current?.focus({ preventScroll: true });
8476
- }
8477
- }, [open, render, pos]);
8951
+ useFocusTrap(popRef, open && render && !!pos, { restoreFocus: false });
8478
8952
  const pop = render && pos ? /* @__PURE__ */ jsxRuntime.jsxs(
8479
8953
  "div",
8480
8954
  {
@@ -8485,6 +8959,7 @@ function Popover({
8485
8959
  "data-state": open ? "open" : "closed",
8486
8960
  "data-flip": pos.flip || void 0,
8487
8961
  role: "dialog",
8962
+ "aria-modal": "true",
8488
8963
  "aria-labelledby": title ? `${popId}-title` : void 0,
8489
8964
  style: { top: pos.top, bottom: pos.bottom, left: pos.left, width: pos.width },
8490
8965
  children: [
@@ -8497,7 +8972,7 @@ function Popover({
8497
8972
  ]
8498
8973
  }
8499
8974
  ) : null;
8500
- const triggerEl = React24__namespace.default.isValidElement(trigger) ? React24__namespace.default.cloneElement(trigger, {
8975
+ const triggerEl = React25__namespace.default.isValidElement(trigger) ? React25__namespace.default.cloneElement(trigger, {
8501
8976
  onClick: (e) => {
8502
8977
  trigger.props.onClick?.(e);
8503
8978
  toggle();
@@ -8540,7 +9015,7 @@ function Progress({
8540
9015
  className = "",
8541
9016
  ...rest
8542
9017
  }) {
8543
- const labelId = React24__namespace.default.useId();
9018
+ const labelId = React25__namespace.default.useId();
8544
9019
  const { "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, ...restProps } = rest;
8545
9020
  const __twcStyles = useScopedStyles("twc-progress-styles", `
8546
9021
  .twc-progress-wrap { display: flex; flex-direction: column; gap: 6px; font-family: var(--font-sans); }
@@ -8663,10 +9138,13 @@ function Radio({
8663
9138
  .twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
8664
9139
  .twc-field__error { font-size: var(--text-xs); color: var(--color-danger-subtle-fg); font-weight: var(--font-medium); }
8665
9140
  `);
8666
- const autoId = React24__namespace.default.useId();
9141
+ const autoId = React25__namespace.default.useId();
8667
9142
  const fieldId = id || autoId;
8668
9143
  const errId = `${fieldId}-error`;
8669
9144
  const invalid = Boolean(error);
9145
+ if (process.env.NODE_ENV !== "production" && !label && !description && !rest["aria-label"] && !rest["aria-labelledby"]) {
9146
+ warnOnce("Radio.no-name", "Radio: no accessible name \u2014 pass `label`, or `aria-label`/`aria-labelledby` for a label-less control (WCAG 4.1.2).");
9147
+ }
8670
9148
  const describedBy = [rest["aria-describedby"], error ? errId : null].filter(Boolean).join(" ") || void 0;
8671
9149
  const control = /* @__PURE__ */ jsxRuntime.jsxs("label", { className: `twc-radio ${className}`, "data-size": size, "data-tone": tone, "data-invalid": invalid || void 0, "data-disabled": disabled || void 0, htmlFor: fieldId, children: [
8672
9150
  __twcStyles,
@@ -8683,6 +9161,7 @@ function Radio({
8683
9161
  disabled,
8684
9162
  onChange,
8685
9163
  ...rest,
9164
+ required: required || void 0,
8686
9165
  "aria-required": required || void 0,
8687
9166
  "aria-invalid": invalid || void 0,
8688
9167
  "aria-describedby": describedBy
@@ -8700,11 +9179,103 @@ function Radio({
8700
9179
  /* @__PURE__ */ jsxRuntime.jsx("span", { id: errId, className: "twc-field__error", children: error })
8701
9180
  ] });
8702
9181
  }
9182
+ var RADIO_GROUP_CSS = `
9183
+ .twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
9184
+ .twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
9185
+ .twc-field__req { color: var(--color-danger); }
9186
+ .twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
9187
+ .twc-field__error { font-size: var(--text-xs); color: var(--color-danger-subtle-fg); font-weight: var(--font-medium); }
9188
+ .twc-radio-group__items { display: flex; flex-direction: column; gap: var(--space-2-5); }
9189
+ .twc-radio-group__items[data-orientation="horizontal"] { flex-direction: row; flex-wrap: wrap; gap: var(--space-4); }
9190
+ `;
9191
+ function RadioGroup({
9192
+ name,
9193
+ value,
9194
+ defaultValue,
9195
+ onChange,
9196
+ options,
9197
+ children,
9198
+ label,
9199
+ hint,
9200
+ error,
9201
+ orientation = "vertical",
9202
+ disabled = false,
9203
+ required = false,
9204
+ size = "md",
9205
+ tone = "primary",
9206
+ id,
9207
+ className = "",
9208
+ ...rest
9209
+ }) {
9210
+ const __twcStyles = useScopedStyles("twc-radio-group-styles", RADIO_GROUP_CSS);
9211
+ const autoId = React25__namespace.default.useId();
9212
+ const fieldId = id || autoId;
9213
+ const labelId = `${fieldId}-label`;
9214
+ const descId = `${fieldId}-desc`;
9215
+ const groupName = name || `${fieldId}-name`;
9216
+ const invalid = Boolean(error);
9217
+ const [internal, setInternal] = React25__namespace.default.useState(defaultValue);
9218
+ const current = value !== void 0 ? value : internal;
9219
+ const select = (v) => {
9220
+ if (value === void 0) setInternal(v);
9221
+ onChange?.(v);
9222
+ };
9223
+ const radios = options ? options.map((o) => /* @__PURE__ */ jsxRuntime.jsx(
9224
+ Radio,
9225
+ {
9226
+ name: groupName,
9227
+ value: o.value,
9228
+ label: o.label,
9229
+ description: o.description,
9230
+ checked: current === o.value,
9231
+ onChange: () => select(o.value),
9232
+ disabled: disabled || o.disabled,
9233
+ required,
9234
+ size,
9235
+ tone
9236
+ },
9237
+ o.value
9238
+ )) : React25__namespace.default.Children.map(children, (child) => {
9239
+ if (!React25__namespace.default.isValidElement(child)) return child;
9240
+ const v = child.props.value;
9241
+ return React25__namespace.default.cloneElement(child, {
9242
+ name: child.props.name ?? groupName,
9243
+ checked: child.props.checked ?? current === v,
9244
+ onChange: child.props.onChange ?? (() => select(v)),
9245
+ disabled: child.props.disabled ?? disabled,
9246
+ required: child.props.required ?? required,
9247
+ size: child.props.size ?? size,
9248
+ tone: child.props.tone ?? tone
9249
+ });
9250
+ });
9251
+ return /* @__PURE__ */ jsxRuntime.jsxs(
9252
+ "div",
9253
+ {
9254
+ className: `twc-field ${className}`,
9255
+ role: "radiogroup",
9256
+ "aria-labelledby": label ? labelId : void 0,
9257
+ "aria-describedby": error || hint ? descId : void 0,
9258
+ "aria-invalid": invalid || void 0,
9259
+ "aria-required": required || void 0,
9260
+ "data-orientation": orientation,
9261
+ ...rest,
9262
+ children: [
9263
+ __twcStyles,
9264
+ label ? /* @__PURE__ */ jsxRuntime.jsxs("span", { id: labelId, className: "twc-field__label", children: [
9265
+ label,
9266
+ required ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-field__req", children: "*" }) : null
9267
+ ] }) : null,
9268
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-radio-group__items", "data-orientation": orientation === "horizontal" ? "horizontal" : void 0, children: radios }),
9269
+ error ? /* @__PURE__ */ jsxRuntime.jsx("span", { id: descId, className: "twc-field__error", children: error }) : hint ? /* @__PURE__ */ jsxRuntime.jsx("span", { id: descId, className: "twc-field__hint", children: hint }) : null
9270
+ ]
9271
+ }
9272
+ );
9273
+ }
8703
9274
  var RATING_CSS = `
8704
9275
  .twc-rating { display: inline-flex; align-items: center; gap: 8px; font-family: var(--font-sans); }
8705
9276
  .twc-rating__stars { display: inline-flex; gap: 2px; }
8706
- .twc-rating__btn { border: none; background: transparent; padding: 1px; cursor: pointer; color: var(--color-border-strong); line-height: 0;
8707
- transition: transform var(--duration-fast) var(--ease-spring), color var(--duration-fast) var(--ease-standard); }
9277
+ .twc-rating__btn { border: none; background: transparent; padding: 1px; cursor: pointer; line-height: 0;
9278
+ transition: transform var(--duration-fast) var(--ease-spring); }
8708
9279
  .twc-rating__btn:focus-visible { outline: none; box-shadow: var(--ring); border-radius: var(--radius-sm); }
8709
9280
  .twc-rating[data-readonly="true"] .twc-rating__btn { cursor: default; }
8710
9281
  .twc-rating[data-disabled="true"] { opacity: 0.55; }
@@ -8718,8 +9289,11 @@ var RATING_CSS = `
8718
9289
  .twc-rating[data-tone="danger"] { --_accent: var(--color-danger); }
8719
9290
  .twc-rating[data-tone="info"] { --_accent: var(--color-info); }
8720
9291
  .twc-rating[data-tone="neutral"] { --_accent: var(--color-text); }
8721
- .twc-rating__btn[data-on="true"] { color: var(--_c, var(--_accent)); }
8722
- .twc-rating__btn svg { width: var(--_sz, 24px); height: var(--_sz, 24px); display: block; }
9292
+ /* Per-star partial fill: an accent-colored star clipped to var(--twc-fill) over an empty star. */
9293
+ .twc-rating__star { position: relative; display: inline-block; line-height: 0; }
9294
+ .twc-rating__bg { color: var(--color-border-strong); }
9295
+ .twc-rating__fg { position: absolute; inset-block: 0; inset-inline-start: 0; width: var(--twc-fill, 0%); overflow: hidden; color: var(--_c, var(--_accent)); }
9296
+ .twc-rating svg { width: var(--_sz, 24px); height: var(--_sz, 24px); display: block; }
8723
9297
  .twc-rating[data-size="sm"] { --_sz: 18px; }
8724
9298
  .twc-rating[data-size="lg"] { --_sz: 32px; }
8725
9299
  .twc-rating__value { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text-muted); font-variant-numeric: tabular-nums; }
@@ -8735,19 +9309,26 @@ function Rating({
8735
9309
  readOnly = false,
8736
9310
  disabled = false,
8737
9311
  showValue = false,
9312
+ clearable = true,
9313
+ format,
9314
+ name,
8738
9315
  onChange,
8739
9316
  className = "",
8740
9317
  style,
8741
9318
  ...rest
8742
9319
  }) {
8743
9320
  const __twcStyles = useScopedStyles("twc-rating-styles", RATING_CSS);
8744
- const [internal, setInternal] = React24__namespace.default.useState(defaultValue);
8745
- const [hover, setHover] = React24__namespace.default.useState(0);
9321
+ const [internal, setInternal] = React25__namespace.default.useState(defaultValue);
9322
+ const [hover, setHover] = React25__namespace.default.useState(0);
8746
9323
  const val = value !== void 0 ? value : internal;
8747
9324
  const shown = hover || val;
8748
9325
  const interactive = !readOnly && !disabled;
8749
- const btnRefs = React24__namespace.default.useRef([]);
9326
+ const btnRefs = React25__namespace.default.useRef([]);
8750
9327
  const tabStar = Math.min(count, Math.max(1, Math.round(val) || 1));
9328
+ const fmt = (v) => format ? format(v) : Number.isInteger(v) ? String(v) : v.toFixed(1);
9329
+ if (process.env.NODE_ENV !== "production" && interactive && !Number.isInteger(val)) {
9330
+ warnOnce("Rating.fractional-interactive", "Rating: a fractional value only renders (partial-fill display) in readOnly mode \u2014 interactive selection is integer-only.");
9331
+ }
8751
9332
  const set = (v) => {
8752
9333
  if (!interactive) return;
8753
9334
  if (value === void 0) setInternal(v);
@@ -8760,18 +9341,46 @@ function Rating({
8760
9341
  else if (e.key === "ArrowLeft" || e.key === "ArrowDown") next = Math.max(1, val - 1);
8761
9342
  else if (e.key === "Home") next = 1;
8762
9343
  else if (e.key === "End") next = count;
9344
+ else if (clearable && (e.key === "Delete" || e.key === "Backspace")) next = 0;
8763
9345
  else return;
8764
9346
  e.preventDefault();
8765
9347
  set(next);
8766
- btnRefs.current[next - 1]?.focus();
9348
+ btnRefs.current[Math.max(0, next - 1)]?.focus();
9349
+ };
9350
+ const star = (n) => {
9351
+ const fill = Math.max(0, Math.min(1, shown - (n - 1)));
9352
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "twc-rating__star", style: { "--twc-fill": `${fill * 100}%` }, children: [
9353
+ /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "twc-rating__bg", viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: StarPath }) }),
9354
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-rating__fg", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: StarPath }) }) })
9355
+ ] });
8767
9356
  };
9357
+ if (readOnly) {
9358
+ return /* @__PURE__ */ jsxRuntime.jsxs(
9359
+ "div",
9360
+ {
9361
+ className: `twc-rating ${className}`,
9362
+ "data-size": size,
9363
+ "data-tone": tone,
9364
+ "data-readonly": "true",
9365
+ style: color ? { "--_c": color, ...style } : style,
9366
+ role: "img",
9367
+ "aria-label": `${fmt(val)} out of ${count} stars`,
9368
+ ...rest,
9369
+ children: [
9370
+ __twcStyles,
9371
+ name ? /* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name, value: val, disabled: disabled || void 0 }) : null,
9372
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-rating__stars", children: Array.from({ length: count }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-rating__btn", children: star(i + 1) }, i + 1)) }),
9373
+ showValue ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-rating__value", children: fmt(val) }) : null
9374
+ ]
9375
+ }
9376
+ );
9377
+ }
8768
9378
  return /* @__PURE__ */ jsxRuntime.jsxs(
8769
9379
  "div",
8770
9380
  {
8771
9381
  className: `twc-rating ${className}`,
8772
9382
  "data-size": size,
8773
9383
  "data-tone": tone,
8774
- "data-readonly": readOnly || void 0,
8775
9384
  "data-disabled": disabled || void 0,
8776
9385
  style: color ? { "--_c": color, ...style } : style,
8777
9386
  role: "radiogroup",
@@ -8780,6 +9389,7 @@ function Rating({
8780
9389
  ...rest,
8781
9390
  children: [
8782
9391
  __twcStyles,
9392
+ name ? /* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name, value: val, disabled: disabled || void 0 }) : null,
8783
9393
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-rating__stars", onMouseLeave: () => setHover(0), onKeyDown, children: Array.from({ length: count }).map((_, i) => {
8784
9394
  const n = i + 1;
8785
9395
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -8790,149 +9400,19 @@ function Rating({
8790
9400
  },
8791
9401
  type: "button",
8792
9402
  className: "twc-rating__btn",
8793
- "data-on": n <= shown || void 0,
8794
9403
  role: "radio",
8795
9404
  "aria-label": `${n} star${n > 1 ? "s" : ""}`,
8796
9405
  "aria-checked": n === val,
8797
- disabled: readOnly || disabled,
9406
+ disabled,
8798
9407
  tabIndex: interactive && n === tabStar ? 0 : -1,
8799
9408
  onMouseEnter: () => interactive && setHover(n),
8800
- onClick: () => set(n === val ? 0 : n),
8801
- children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: StarPath }) })
9409
+ onClick: () => set(n === val && clearable ? 0 : n),
9410
+ children: star(n)
8802
9411
  },
8803
9412
  n
8804
9413
  );
8805
9414
  }) }),
8806
- showValue ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-rating__value", children: val.toFixed(1) }) : null
8807
- ]
8808
- }
8809
- );
8810
- }
8811
- var TOOLTIP_CSS = `
8812
- .twc-tooltip-wrap { display: inline-flex; }
8813
- .twc-tooltip {
8814
- position: fixed; z-index: var(--z-tooltip); pointer-events: none;
8815
- padding: 6px 10px; border-radius: var(--radius-md);
8816
- background: var(--color-text); color: var(--color-surface);
8817
- font-family: var(--font-sans); font-size: var(--text-xs); font-weight: var(--font-medium);
8818
- line-height: 1.3; white-space: nowrap; box-shadow: var(--shadow-md);
8819
- opacity: 0; transform: scale(0.9); transition: opacity var(--duration-fast) var(--ease-out), transform var(--duration-fast) var(--ease-spring);
8820
- }
8821
- .twc-tooltip[data-show="true"] { opacity: 1; transform: scale(1); }
8822
- .twc-tooltip[data-place="top"] { transform-origin: bottom center; translate: -50% 0; }
8823
- .twc-tooltip[data-place="bottom"] { transform-origin: top center; translate: -50% 0; }
8824
- .twc-tooltip[data-place="left"] { transform-origin: right center; translate: 0 -50%; }
8825
- .twc-tooltip[data-place="right"] { transform-origin: left center; translate: 0 -50%; }
8826
- .twc-tooltip[data-show="true"][data-place="top"], .twc-tooltip[data-show="true"][data-place="bottom"] { transform: scale(1); }
8827
- .twc-tooltip__arrow { position: absolute; width: 7px; height: 7px; background: var(--color-text); transform: rotate(45deg); }
8828
- .twc-tooltip[data-place="top"] .twc-tooltip__arrow { bottom: -3px; left: 50%; margin-left: -3.5px; }
8829
- .twc-tooltip[data-place="bottom"] .twc-tooltip__arrow { top: -3px; left: 50%; margin-left: -3.5px; }
8830
- .twc-tooltip[data-place="left"] .twc-tooltip__arrow { right: -3px; top: 50%; margin-top: -3.5px; }
8831
- .twc-tooltip[data-place="right"] .twc-tooltip__arrow { left: -3px; top: 50%; margin-top: -3.5px; }
8832
- @media (prefers-reduced-motion: reduce) { .twc-tooltip { transition: opacity var(--duration-fast) linear; transform: none; } }
8833
- `;
8834
- function Tooltip({
8835
- children,
8836
- label,
8837
- placement = "top",
8838
- delay = 120,
8839
- className = "",
8840
- ...rest
8841
- }) {
8842
- const __twcStyles = useScopedStyles("twc-tooltip-styles", TOOLTIP_CSS);
8843
- const id = React24__namespace.default.useId();
8844
- const [show, setShow] = React24__namespace.default.useState(false);
8845
- const [coords, setCoords] = React24__namespace.default.useState(null);
8846
- const timer = React24__namespace.default.useRef(null);
8847
- const wrapRef = React24__namespace.default.useRef(null);
8848
- const tipRef = React24__namespace.default.useRef(null);
8849
- const open = () => {
8850
- clearTimeout(timer.current);
8851
- timer.current = setTimeout(() => setShow(true), delay);
8852
- };
8853
- const close = () => {
8854
- clearTimeout(timer.current);
8855
- setShow(false);
8856
- };
8857
- React24__namespace.default.useEffect(() => () => clearTimeout(timer.current), []);
8858
- const place = React24__namespace.default.useCallback(() => {
8859
- const el = wrapRef.current;
8860
- if (!el) return;
8861
- const r = el.getBoundingClientRect();
8862
- const cx = Math.round(r.left + r.width / 2), cy = Math.round(r.top + r.height / 2);
8863
- const vw = window.innerWidth, vh = window.innerHeight, gap = 8, margin = 8;
8864
- const tip = tipRef.current;
8865
- const tw = tip ? tip.offsetWidth : 0, th = tip ? tip.offsetHeight : 0;
8866
- const clampX = (c) => tw ? Math.round(Math.min(Math.max(c, margin + tw / 2), vw - margin - tw / 2)) : c;
8867
- const clampY = (c) => th ? Math.round(Math.min(Math.max(c, margin + th / 2), vh - margin - th / 2)) : c;
8868
- const base = { left: "auto", right: "auto", top: "auto", bottom: "auto" };
8869
- if (placement === "bottom") setCoords({ ...base, left: clampX(cx), top: Math.round(r.bottom + gap) });
8870
- else if (placement === "left") setCoords({ ...base, top: clampY(cy), right: Math.round(vw - r.left + gap) });
8871
- else if (placement === "right") setCoords({ ...base, top: clampY(cy), left: Math.round(r.right + gap) });
8872
- else setCoords({ ...base, left: clampX(cx), bottom: Math.round(vh - r.top + gap) });
8873
- }, [placement]);
8874
- React24__namespace.default.useEffect(() => {
8875
- place();
8876
- }, [place]);
8877
- React24__namespace.default.useEffect(() => {
8878
- if (!show) return void 0;
8879
- place();
8880
- const onMove = () => place();
8881
- window.addEventListener("scroll", onMove, true);
8882
- window.addEventListener("resize", onMove);
8883
- return () => {
8884
- window.removeEventListener("scroll", onMove, true);
8885
- window.removeEventListener("resize", onMove);
8886
- };
8887
- }, [show, place]);
8888
- React24__namespace.default.useEffect(() => {
8889
- if (!show) return;
8890
- const onKeyDown = (e) => {
8891
- if (e.key === "Escape") {
8892
- clearTimeout(timer.current);
8893
- setShow(false);
8894
- }
8895
- };
8896
- document.addEventListener("keydown", onKeyDown);
8897
- return () => document.removeEventListener("keydown", onKeyDown);
8898
- }, [show]);
8899
- const describedBy = show ? id : void 0;
8900
- const trigger = React24__namespace.default.isValidElement(children) && children.type !== React24__namespace.default.Fragment ? React24__namespace.default.cloneElement(children, {
8901
- "aria-describedby": [children.props["aria-describedby"], describedBy].filter(Boolean).join(" ") || void 0
8902
- }) : /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-describedby": describedBy, children });
8903
- return /* @__PURE__ */ jsxRuntime.jsxs(
8904
- "span",
8905
- {
8906
- ref: wrapRef,
8907
- className: `twc-tooltip-wrap ${className}`,
8908
- onMouseEnter: open,
8909
- onMouseLeave: close,
8910
- onFocus: open,
8911
- onBlur: close,
8912
- ...rest,
8913
- children: [
8914
- __twcStyles,
8915
- trigger,
8916
- coords ? reactDom.createPortal(
8917
- /* @__PURE__ */ jsxRuntime.jsxs(
8918
- "span",
8919
- {
8920
- ref: tipRef,
8921
- id,
8922
- className: "twc-tooltip",
8923
- "data-place": placement,
8924
- "data-show": show || void 0,
8925
- role: "tooltip",
8926
- "aria-hidden": show ? void 0 : "true",
8927
- style: { position: "fixed", left: coords.left, right: coords.right, top: coords.top, bottom: coords.bottom },
8928
- children: [
8929
- label,
8930
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-tooltip__arrow", "aria-hidden": "true" })
8931
- ]
8932
- }
8933
- ),
8934
- document.body
8935
- ) : null
9415
+ showValue ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-rating__value", children: fmt(val) }) : null
8936
9416
  ]
8937
9417
  }
8938
9418
  );
@@ -9030,24 +9510,24 @@ function Sidebar({
9030
9510
  ...rest
9031
9511
  }) {
9032
9512
  const __twcStyles = useScopedStyles("twc-sidebar-styles", SIDEBAR_CSS);
9033
- const navId = React24__namespace.default.useId();
9034
- const panelRef = React24__namespace.default.useRef(null);
9513
+ const navId = React25__namespace.default.useId();
9514
+ const panelRef = React25__namespace.default.useRef(null);
9035
9515
  const renderPortal = usePortal();
9036
- const [internal, setInternal] = React24__namespace.default.useState(defaultCollapsed);
9516
+ const [internal, setInternal] = React25__namespace.default.useState(defaultCollapsed);
9037
9517
  const collapsed = collapsedProp !== void 0 ? collapsedProp : internal;
9038
9518
  const toggle = () => {
9039
9519
  const next = !collapsed;
9040
9520
  if (collapsedProp === void 0) setInternal(next);
9041
9521
  onCollapsedChange?.(next);
9042
9522
  };
9043
- const [openInternal, setOpenInternal] = React24__namespace.default.useState(defaultOpen);
9523
+ const [openInternal, setOpenInternal] = React25__namespace.default.useState(defaultOpen);
9044
9524
  const open = openProp !== void 0 ? openProp : openInternal;
9045
9525
  const setOpen = (next) => {
9046
9526
  if (openProp === void 0) setOpenInternal(next);
9047
9527
  onOpenChange?.(next);
9048
9528
  };
9049
- const [mounted, setMounted] = React24__namespace.default.useState(open);
9050
- React24__namespace.default.useEffect(() => {
9529
+ const [mounted, setMounted] = React25__namespace.default.useState(open);
9530
+ React25__namespace.default.useEffect(() => {
9051
9531
  if (!overlay) return void 0;
9052
9532
  if (open) {
9053
9533
  setMounted(true);
@@ -9056,7 +9536,7 @@ function Sidebar({
9056
9536
  const t = setTimeout(() => setMounted(false), 170);
9057
9537
  return () => clearTimeout(t);
9058
9538
  }, [overlay, open]);
9059
- React24__namespace.default.useEffect(() => {
9539
+ React25__namespace.default.useEffect(() => {
9060
9540
  if (!overlay || !open) return void 0;
9061
9541
  const prev = document.body.style.overflow;
9062
9542
  document.body.style.overflow = "hidden";
@@ -9065,7 +9545,7 @@ function Sidebar({
9065
9545
  };
9066
9546
  }, [overlay, open]);
9067
9547
  useFocusTrap(panelRef, overlay && open && mounted);
9068
- React24__namespace.default.useEffect(() => {
9548
+ React25__namespace.default.useEffect(() => {
9069
9549
  if (!overlay || !open) return void 0;
9070
9550
  const onKey = (e) => {
9071
9551
  if (e.key === "Escape") {
@@ -9117,7 +9597,7 @@ function Sidebar({
9117
9597
  const body = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
9118
9598
  __twcStyles,
9119
9599
  brand ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-sidebar__head", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-sidebar__brand", children: brand }) }) : null,
9120
- /* @__PURE__ */ jsxRuntime.jsx("nav", { id: navId, className: "twc-sidebar__nav", "aria-label": navLabel, children: groups.map((g, gi) => /* @__PURE__ */ jsxRuntime.jsxs(React24__namespace.default.Fragment, { children: [
9600
+ /* @__PURE__ */ jsxRuntime.jsx("nav", { id: navId, className: "twc-sidebar__nav", "aria-label": navLabel, children: groups.map((g, gi) => /* @__PURE__ */ jsxRuntime.jsxs(React25__namespace.default.Fragment, { children: [
9121
9601
  g.section != null ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-sidebar__section", id: g.sid, children: g.section }) : null,
9122
9602
  /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "twc-sidebar__list", ...g.section != null ? { "aria-labelledby": g.sid } : {}, children: g.entries.map(({ it, i }) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: renderItem(it) }, i)) })
9123
9603
  ] }, gi)) }),
@@ -9256,11 +9736,16 @@ function Slider({
9256
9736
  min: minProp = 0,
9257
9737
  max: maxProp = 100,
9258
9738
  step: stepProp = 1,
9739
+ pageStep: pageStepProp,
9740
+ precision,
9259
9741
  tone = "primary",
9260
9742
  disabled = false,
9743
+ range = false,
9261
9744
  showValue = true,
9262
9745
  showTicks = false,
9263
9746
  formatValue,
9747
+ getAriaValueText,
9748
+ name,
9264
9749
  onChange,
9265
9750
  id,
9266
9751
  className = "",
@@ -9269,38 +9754,73 @@ function Slider({
9269
9754
  const min = Number.isFinite(minProp) ? minProp : 0;
9270
9755
  const max = Number.isFinite(maxProp) && maxProp > min ? maxProp : min + 1;
9271
9756
  const step = Number.isFinite(stepProp) && stepProp > 0 ? stepProp : 1;
9272
- const __twcStyles = useScopedStyles("twc-slider-styles", SLIDER_CSS);
9273
- const autoId = React24__namespace.default.useId();
9274
- const fieldId = id || autoId;
9275
- const [internal, setInternal] = React24__namespace.default.useState(defaultValue);
9276
- const val = value !== void 0 ? value : internal;
9277
- const trackRef = React24__namespace.default.useRef(null);
9278
- const [dragging, setDragging] = React24__namespace.default.useState(false);
9279
- const pct = (val - min) / (max - min) * 100;
9280
- const fmt = (v) => formatValue ? formatValue(v) : v;
9757
+ const pageStep = Number.isFinite(pageStepProp) && pageStepProp > 0 ? pageStepProp : Math.max(step, (max - min) / 10);
9758
+ const stepDecimals = (() => {
9759
+ const s = String(step);
9760
+ const i = s.indexOf(".");
9761
+ return i === -1 ? 0 : s.length - i - 1;
9762
+ })();
9763
+ const decimals = Number.isInteger(precision) && precision >= 0 ? precision : stepDecimals;
9281
9764
  const clampSnap = (raw) => {
9282
9765
  const stepped = Math.round((raw - min) / step) * step + min;
9283
9766
  return Math.min(max, Math.max(min, Math.round(stepped * 1e6) / 1e6));
9284
9767
  };
9285
- const setVal = (v) => {
9286
- if (value === void 0) setInternal(v);
9287
- onChange?.(v);
9768
+ const __twcStyles = useScopedStyles("twc-slider-styles", SLIDER_CSS);
9769
+ const autoId = React25__namespace.default.useId();
9770
+ const fieldId = id || autoId;
9771
+ const isRange = range || Array.isArray(value) || Array.isArray(defaultValue);
9772
+ const [internal, setInternal] = React25__namespace.default.useState(() => {
9773
+ if (isRange) {
9774
+ const dv = Array.isArray(defaultValue) ? defaultValue : [min, max];
9775
+ return [clampSnap(dv[0]), clampSnap(dv[1])];
9776
+ }
9777
+ return clampSnap(Number.isFinite(defaultValue) ? defaultValue : min);
9778
+ });
9779
+ const rawVal = value !== void 0 ? value : internal;
9780
+ const clampedVals = (isRange ? [Number(rawVal[0]), Number(rawVal[1])] : [Number(rawVal)]).map((v) => Math.min(max, Math.max(min, Number.isFinite(v) ? v : min)));
9781
+ const indices = isRange ? [0, 1] : [0];
9782
+ const trackRef = React25__namespace.default.useRef(null);
9783
+ const [dragging, setDragging] = React25__namespace.default.useState(false);
9784
+ const fmt = (v) => formatValue ? formatValue(v) : v.toFixed(decimals);
9785
+ const valueTextFor = (v) => {
9786
+ if (getAriaValueText) return getAriaValueText(v);
9787
+ if (formatValue) {
9788
+ const d = formatValue(v);
9789
+ return typeof d === "string" || typeof d === "number" ? String(d) : void 0;
9790
+ }
9791
+ return decimals > 0 ? v.toFixed(decimals) : void 0;
9792
+ };
9793
+ const commit = (nextVals) => {
9794
+ const out = isRange ? nextVals : nextVals[0];
9795
+ if (value === void 0) setInternal(out);
9796
+ onChange?.(out);
9797
+ };
9798
+ const setThumb = (i, raw) => {
9799
+ const next = clampedVals.slice();
9800
+ next[i] = clampSnap(raw);
9801
+ if (isRange) {
9802
+ if (i === 0) next[0] = Math.min(next[0], next[1]);
9803
+ else next[1] = Math.max(next[1], next[0]);
9804
+ }
9805
+ commit(next);
9288
9806
  };
9289
9807
  const fromClientX = (clientX) => {
9290
9808
  const node = trackRef.current;
9291
- if (!node) return val;
9809
+ if (!node) return clampedVals[0];
9292
9810
  const r = node.getBoundingClientRect();
9293
9811
  const ratio = Math.min(1, Math.max(0, (clientX - r.left) / r.width));
9294
9812
  return clampSnap(min + ratio * (max - min));
9295
9813
  };
9296
- const dragCleanupRef = React24__namespace.default.useRef(null);
9297
- React24__namespace.default.useEffect(() => () => dragCleanupRef.current?.(), []);
9814
+ const dragCleanupRef = React25__namespace.default.useRef(null);
9815
+ React25__namespace.default.useEffect(() => () => dragCleanupRef.current?.(), []);
9298
9816
  function onPointerDown(e) {
9299
9817
  if (disabled) return;
9300
9818
  e.preventDefault();
9819
+ const posVal = fromClientX(e.clientX);
9820
+ const idx = isRange ? Math.abs(posVal - clampedVals[0]) <= Math.abs(posVal - clampedVals[1]) ? 0 : 1 : 0;
9301
9821
  setDragging(true);
9302
- setVal(fromClientX(e.clientX));
9303
- const move = (ev) => setVal(fromClientX(ev.clientX));
9822
+ setThumb(idx, posVal);
9823
+ const move = (ev) => setThumb(idx, fromClientX(ev.clientX));
9304
9824
  const detach = () => {
9305
9825
  window.removeEventListener("pointermove", move);
9306
9826
  window.removeEventListener("pointerup", up);
@@ -9314,54 +9834,68 @@ function Slider({
9314
9834
  window.addEventListener("pointermove", move);
9315
9835
  window.addEventListener("pointerup", up);
9316
9836
  }
9317
- function onKeyDown(e) {
9837
+ function onThumbKeyDown(e, i) {
9318
9838
  if (disabled) return;
9839
+ const cur = clampedVals[i];
9319
9840
  let next;
9320
- if (e.key === "ArrowRight" || e.key === "ArrowUp") next = clampSnap(val + step);
9321
- else if (e.key === "ArrowLeft" || e.key === "ArrowDown") next = clampSnap(val - step);
9841
+ if (e.key === "ArrowRight" || e.key === "ArrowUp") next = cur + step;
9842
+ else if (e.key === "ArrowLeft" || e.key === "ArrowDown") next = cur - step;
9843
+ else if (e.key === "PageUp") next = cur + pageStep;
9844
+ else if (e.key === "PageDown") next = cur - pageStep;
9322
9845
  else if (e.key === "Home") next = min;
9323
9846
  else if (e.key === "End") next = max;
9324
9847
  else return;
9325
9848
  e.preventDefault();
9326
- setVal(next);
9849
+ setThumb(i, next);
9327
9850
  }
9328
9851
  const ticks = showTicks ? Math.floor((max - min) / step) + 1 : 0;
9329
9852
  const labelId = label ? `${fieldId}-label` : void 0;
9330
9853
  const descId = `${fieldId}-desc`;
9331
9854
  const invalid = Boolean(error);
9332
- const display = fmt(val);
9333
- const valueText = formatValue && (typeof display === "string" || typeof display === "number") ? String(display) : void 0;
9855
+ const headValue = isRange ? `${fmt(clampedVals[0])} \u2013 ${fmt(clampedVals[1])}` : fmt(clampedVals[0]);
9856
+ const fillLo = (Math.min(...clampedVals) - min) / (max - min) * 100;
9857
+ const fillHi = (Math.max(...clampedVals) - min) / (max - min) * 100;
9858
+ const fillStyle = isRange ? { left: `${fillLo}%`, width: `${fillHi - fillLo}%` } : { width: `${fillHi}%` };
9334
9859
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-slider ${className}`, "data-tone": tone, "data-disabled": disabled || void 0, ...rest, children: [
9335
9860
  __twcStyles,
9861
+ name ? clampedVals.map((v, i) => /* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name, value: v, disabled: disabled || void 0 }, i)) : null,
9336
9862
  label || showValue ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-slider__head", children: [
9337
9863
  label ? /* @__PURE__ */ jsxRuntime.jsx("label", { className: "twc-slider__label", id: labelId, children: label }) : /* @__PURE__ */ jsxRuntime.jsx("span", {}),
9338
- showValue ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-slider__value", children: display }) : null
9864
+ showValue ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-slider__value", children: headValue }) : null
9339
9865
  ] }) : null,
9340
9866
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-slider__track", ref: trackRef, "data-show-bubble": dragging || void 0, onPointerDown, children: [
9341
9867
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-slider__rail" }),
9342
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-slider__fill", style: { width: `${pct}%` } }),
9868
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-slider__fill", style: fillStyle }),
9343
9869
  showTicks ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-slider__ticks", children: Array.from({ length: ticks }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-slider__tick", style: { left: `${ticks > 1 ? i / (ticks - 1) * 100 : 0}%` } }, i)) }) : null,
9344
- /* @__PURE__ */ jsxRuntime.jsx(
9345
- "div",
9346
- {
9347
- id: fieldId,
9348
- className: "twc-slider__thumb",
9349
- style: { left: `${pct}%` },
9350
- role: "slider",
9351
- tabIndex: disabled ? -1 : 0,
9352
- "aria-valuemin": min,
9353
- "aria-valuemax": max,
9354
- "aria-valuenow": val,
9355
- "aria-valuetext": valueText,
9356
- "aria-labelledby": labelId,
9357
- "aria-label": labelId ? void 0 : "Slider",
9358
- "aria-describedby": error || hint ? descId : void 0,
9359
- "aria-invalid": invalid || void 0,
9360
- "aria-disabled": disabled || void 0,
9361
- onKeyDown,
9362
- children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-slider__bubble", children: display })
9363
- }
9364
- )
9870
+ indices.map((i) => {
9871
+ const tv = clampedVals[i];
9872
+ const p = (tv - min) / (max - min) * 100;
9873
+ const tMin = isRange && i === 1 ? clampedVals[0] : min;
9874
+ const tMax = isRange && i === 0 ? clampedVals[1] : max;
9875
+ const thumbLabel = isRange ? i === 0 ? "Minimum" : "Maximum" : labelId ? void 0 : "Slider";
9876
+ return /* @__PURE__ */ jsxRuntime.jsx(
9877
+ "div",
9878
+ {
9879
+ id: i === 0 ? fieldId : void 0,
9880
+ className: "twc-slider__thumb",
9881
+ style: { left: `${p}%` },
9882
+ role: "slider",
9883
+ tabIndex: disabled ? -1 : 0,
9884
+ "aria-valuemin": tMin,
9885
+ "aria-valuemax": tMax,
9886
+ "aria-valuenow": tv,
9887
+ "aria-valuetext": valueTextFor(tv),
9888
+ "aria-labelledby": isRange ? void 0 : labelId,
9889
+ "aria-label": thumbLabel,
9890
+ "aria-describedby": error || hint ? descId : void 0,
9891
+ "aria-invalid": invalid || void 0,
9892
+ "aria-disabled": disabled || void 0,
9893
+ onKeyDown: (e) => onThumbKeyDown(e, i),
9894
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-slider__bubble", children: fmt(tv) })
9895
+ },
9896
+ i
9897
+ );
9898
+ })
9365
9899
  ] }),
9366
9900
  error || hint ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-slider__msgs", children: error ? /* @__PURE__ */ jsxRuntime.jsx("span", { id: descId, className: "twc-field__error", children: error }) : /* @__PURE__ */ jsxRuntime.jsx("span", { id: descId, className: "twc-field__hint", children: hint }) }) : null
9367
9901
  ] });
@@ -9509,7 +10043,7 @@ function Stepper({
9509
10043
  ...rest
9510
10044
  }) {
9511
10045
  const __twcStyles = useScopedStyles("twc-stepper-styles", STEPPER_CSS);
9512
- const [activeInternal, setActiveInternal] = React24__namespace.default.useState(defaultActive);
10046
+ const [activeInternal, setActiveInternal] = React25__namespace.default.useState(defaultActive);
9513
10047
  const active = activeProp !== void 0 ? activeProp : activeInternal;
9514
10048
  const handleStep = (i) => {
9515
10049
  if (activeProp === void 0) setActiveInternal(i);
@@ -9641,10 +10175,13 @@ function Switch({
9641
10175
  .twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
9642
10176
  .twc-field__error { font-size: var(--text-xs); color: var(--color-danger-subtle-fg); font-weight: var(--font-medium); }
9643
10177
  `);
9644
- const autoId = React24__namespace.default.useId();
10178
+ const autoId = React25__namespace.default.useId();
9645
10179
  const fieldId = id || autoId;
9646
10180
  const errId = `${fieldId}-error`;
9647
10181
  const invalid = Boolean(error);
10182
+ if (process.env.NODE_ENV !== "production" && !label && !description && !rest["aria-label"] && !rest["aria-labelledby"]) {
10183
+ warnOnce("Switch.no-name", "Switch: no accessible name \u2014 pass `label`, or `aria-label`/`aria-labelledby` for a label-less control (WCAG 4.1.2).");
10184
+ }
9648
10185
  const describedBy = [rest["aria-describedby"], error ? errId : null].filter(Boolean).join(" ") || void 0;
9649
10186
  const control = /* @__PURE__ */ jsxRuntime.jsxs("label", { className: `twc-switch ${className}`, "data-size": size, "data-tone": tone, "data-invalid": invalid || void 0, "data-disabled": disabled || void 0, htmlFor: fieldId, children: [
9650
10187
  __twcStyles,
@@ -9660,6 +10197,7 @@ function Switch({
9660
10197
  disabled,
9661
10198
  onChange,
9662
10199
  ...rest,
10200
+ required: required || void 0,
9663
10201
  "aria-required": required || void 0,
9664
10202
  "aria-invalid": invalid || void 0,
9665
10203
  "aria-describedby": describedBy
@@ -9706,6 +10244,13 @@ var TABLE_CSS = `
9706
10244
  .twc-table[data-size="lg"] thead th { padding: var(--space-4) var(--space-5); }
9707
10245
  .twc-table[data-size="lg"] tbody td { padding: var(--space-4) var(--space-5); font-size: var(--text-base); }
9708
10246
  .twc-table[data-sticky="true"] thead th { position: sticky; top: 0; z-index: 2; background: var(--color-surface); box-shadow: inset 0 calc(-1 * var(--border-thin)) 0 0 var(--color-border); }
10247
+ .twc-table__caption { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
10248
+ .twc-table__empty { padding: 30px 12px; text-align: center; color: var(--color-text-subtle); font-size: var(--text-sm); }
10249
+ .twc-table__sk { display: inline-block; height: 12px; border-radius: var(--radius-sm); background: var(--color-surface-sunken);
10250
+ position: relative; overflow: hidden; width: var(--_w, 70%); }
10251
+ .twc-table__sk::after { content: ""; position: absolute; inset: 0; transform: translateX(-100%);
10252
+ background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--color-text) 8%, transparent), transparent);
10253
+ animation: twico-shimmer 1.4s infinite; }
9709
10254
  `;
9710
10255
  function Table({
9711
10256
  columns,
@@ -9721,18 +10266,26 @@ function Table({
9721
10266
  selectedKeys,
9722
10267
  stickyHeader = false,
9723
10268
  maxHeight,
10269
+ loading = false,
10270
+ loadingRows = 8,
10271
+ emptyMessage = "No data",
10272
+ ariaLabel,
10273
+ caption,
10274
+ // Pulled out of `rest` so they land on the <table>, not the wrapper <div> (#128).
10275
+ "aria-label": ariaLabelRaw,
10276
+ "aria-labelledby": ariaLabelledby,
9724
10277
  className = "",
9725
10278
  ...rest
9726
10279
  }) {
9727
10280
  const __twcStyles = useScopedStyles("twc-table-styles", TABLE_CSS);
9728
- const cols = React24__namespace.default.useMemo(
10281
+ const cols = React25__namespace.default.useMemo(
9729
10282
  () => (columns || []).map((c) => ({ ...c, header: c.headerName, render: c.renderCell })),
9730
10283
  [columns]
9731
10284
  );
9732
10285
  const dataRows = rowsProp || [];
9733
- const [internalSort, setInternalSort] = React24__namespace.default.useState(defaultSort || { field: null, dir: "asc" });
10286
+ const [internalSort, setInternalSort] = React25__namespace.default.useState(defaultSort || { field: null, dir: "asc" });
9734
10287
  const sort = (sortProp !== void 0 ? sortProp : internalSort) || { field: null, dir: "asc" };
9735
- const rows = React24__namespace.default.useMemo(() => {
10288
+ const rows = React25__namespace.default.useMemo(() => {
9736
10289
  if (!sortable || !sort.field) return dataRows;
9737
10290
  const col = cols.find((c) => c.field === sort.field);
9738
10291
  if (!col) return dataRows;
@@ -9755,32 +10308,46 @@ function Table({
9755
10308
  const wrapStyle = maxHeight !== void 0 ? { ...rest.style, maxHeight, overflowY: "auto" } : rest.style;
9756
10309
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-table-wrap ${className}`, ...rest, style: wrapStyle, children: [
9757
10310
  __twcStyles,
9758
- /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "twc-table", "data-hover": hover || void 0, "data-striped": striped || void 0, "data-size": size, "data-sticky": stickyHeader || void 0, children: [
9759
- /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsx("tr", { children: cols.map((c) => {
9760
- const active = sort.field === c.field;
9761
- const canSort = sortable && c.sortable !== false;
9762
- return /* @__PURE__ */ jsxRuntime.jsx(
9763
- "th",
9764
- {
9765
- "data-align": c.align,
9766
- "data-sortable": canSort || void 0,
9767
- "data-active": active || void 0,
9768
- "data-dir": active ? sort.dir : void 0,
9769
- style: c.width ? { width: c.width } : void 0,
9770
- "aria-sort": active ? sort.dir === "asc" ? "ascending" : "descending" : canSort ? "none" : void 0,
9771
- children: canSort ? /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", className: "twc-table__sort", onClick: () => toggleSort(c.field), children: [
9772
- c.header,
9773
- /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m18 15-6-6-6 6" }) })
9774
- ] }) : c.header
9775
- },
9776
- c.field
9777
- );
9778
- }) }) }),
9779
- /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: rows.map((row, i) => {
9780
- const k = keyFn(row, i);
9781
- return /* @__PURE__ */ jsxRuntime.jsx("tr", { "data-selected": selected.has(k) || void 0, children: cols.map((c) => /* @__PURE__ */ jsxRuntime.jsx("td", { "data-align": c.align, children: c.render ? c.render(row[c.field], row, i) : row[c.field] }, c.field)) }, k);
9782
- }) })
9783
- ] })
10311
+ /* @__PURE__ */ jsxRuntime.jsxs(
10312
+ "table",
10313
+ {
10314
+ className: "twc-table",
10315
+ "data-hover": hover || void 0,
10316
+ "data-striped": striped || void 0,
10317
+ "data-size": size,
10318
+ "data-sticky": stickyHeader || void 0,
10319
+ "aria-label": ariaLabelRaw ?? ariaLabel,
10320
+ "aria-labelledby": ariaLabelledby,
10321
+ "aria-busy": loading || void 0,
10322
+ children: [
10323
+ caption != null ? /* @__PURE__ */ jsxRuntime.jsx("caption", { className: "twc-table__caption", children: caption }) : null,
10324
+ /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsx("tr", { children: cols.map((c) => {
10325
+ const active = sort.field === c.field;
10326
+ const canSort = sortable && c.sortable !== false;
10327
+ return /* @__PURE__ */ jsxRuntime.jsx(
10328
+ "th",
10329
+ {
10330
+ "data-align": c.align,
10331
+ "data-sortable": canSort || void 0,
10332
+ "data-active": active || void 0,
10333
+ "data-dir": active ? sort.dir : void 0,
10334
+ style: c.width ? { width: c.width } : void 0,
10335
+ "aria-sort": active ? sort.dir === "asc" ? "ascending" : "descending" : canSort ? "none" : void 0,
10336
+ children: canSort ? /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", className: "twc-table__sort", onClick: () => toggleSort(c.field), children: [
10337
+ c.header,
10338
+ /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m18 15-6-6-6 6" }) })
10339
+ ] }) : c.header
10340
+ },
10341
+ c.field
10342
+ );
10343
+ }) }) }),
10344
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: loading ? Array.from({ length: loadingRows }).map((_, ri) => /* @__PURE__ */ jsxRuntime.jsx("tr", { children: cols.map((c) => /* @__PURE__ */ jsxRuntime.jsx("td", { "data-align": c.align, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-table__sk" }) }, c.field)) }, `sk${ri}`)) : rows.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("tr", { children: /* @__PURE__ */ jsxRuntime.jsx("td", { colSpan: cols.length || 1, className: "twc-table__empty", children: emptyMessage }) }) : rows.map((row, i) => {
10345
+ const k = keyFn(row, i);
10346
+ return /* @__PURE__ */ jsxRuntime.jsx("tr", { "data-selected": selected.has(k) || void 0, children: cols.map((c) => /* @__PURE__ */ jsxRuntime.jsx("td", { "data-align": c.align, children: c.render ? c.render(row[c.field], row, i) : row[c.field] }, c.field)) }, k);
10347
+ }) })
10348
+ ]
10349
+ }
10350
+ )
9784
10351
  ] });
9785
10352
  }
9786
10353
  var TABS_CSS = `
@@ -9865,15 +10432,15 @@ function Tabs({
9865
10432
  }) {
9866
10433
  const vertical = orientation === "vertical";
9867
10434
  const __twcStyles = useScopedStyles("twc-tabs-styles", TABS_CSS);
9868
- const [internal, setInternal] = React24__namespace.default.useState(defaultValue ?? items[0]?.value);
10435
+ const [internal, setInternal] = React25__namespace.default.useState(defaultValue ?? items[0]?.value);
9869
10436
  const active = value ?? internal;
9870
- const listRef = React24__namespace.default.useRef(null);
9871
- const [ind, setInd] = React24__namespace.default.useState({ left: 0, width: 0, top: 0, height: 0 });
9872
- const baseId = React24__namespace.default.useId();
10437
+ const listRef = React25__namespace.default.useRef(null);
10438
+ const [ind, setInd] = React25__namespace.default.useState({ left: 0, width: 0, top: 0, height: 0 });
10439
+ const baseId = React25__namespace.default.useId();
9873
10440
  const tabIdAt = (i) => `${baseId}-tab-${i}`;
9874
10441
  const panelId = `${baseId}-panel`;
9875
10442
  const activeIndex = items.findIndex((it) => it.value === active);
9876
- const updateIndicator = React24__namespace.default.useCallback(() => {
10443
+ const updateIndicator = React25__namespace.default.useCallback(() => {
9877
10444
  const list = listRef.current;
9878
10445
  if (!list) return;
9879
10446
  const el = list.querySelector('[data-active="true"]');
@@ -9882,10 +10449,10 @@ function Tabs({
9882
10449
  if (!vertical) el.scrollIntoView?.({ inline: "nearest", block: "nearest" });
9883
10450
  }
9884
10451
  }, [vertical]);
9885
- React24__namespace.default.useEffect(() => {
10452
+ React25__namespace.default.useEffect(() => {
9886
10453
  updateIndicator();
9887
10454
  }, [active, updateIndicator, items, orientation]);
9888
- React24__namespace.default.useEffect(() => {
10455
+ React25__namespace.default.useEffect(() => {
9889
10456
  const r = () => updateIndicator();
9890
10457
  window.addEventListener("resize", r);
9891
10458
  if (typeof document !== "undefined" && document.fonts && document.fonts.ready) {
@@ -10030,20 +10597,8 @@ function Tag({
10030
10597
  }, "aria-label": _removeLabel, type: "button", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.4", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 6 6 18M6 6l12 12" }) }) }) : null
10031
10598
  ] });
10032
10599
  }
10033
- function Textarea({
10034
- label,
10035
- hint,
10036
- error,
10037
- required = false,
10038
- size = "md",
10039
- tone = "primary",
10040
- rows = 4,
10041
- disabled = false,
10042
- id,
10043
- className = "",
10044
- ...rest
10045
- }) {
10046
- const __twcStyles = useScopedStyles("twc-textarea-styles", `
10600
+ var useIso = typeof document !== "undefined" ? React25__namespace.default.useLayoutEffect : React25__namespace.default.useEffect;
10601
+ var TEXTAREA_CSS = `
10047
10602
  .twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
10048
10603
  .twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
10049
10604
  .twc-field__req { color: var(--color-danger); }
@@ -10072,11 +10627,74 @@ function Textarea({
10072
10627
  .twc-textarea__el:focus { border-color: var(--_accent); box-shadow: var(--_ring); }
10073
10628
  .twc-textarea__el[aria-invalid="true"] { border-color: var(--color-danger); }
10074
10629
  .twc-textarea__el:disabled { background: var(--color-surface-sunken); opacity: 0.7; cursor: not-allowed; }
10075
- `);
10076
- const autoId = React24__namespace.default.useId();
10630
+ .twc-textarea__el[data-readonly] { background: var(--color-surface-sunken); }
10631
+ .twc-textarea__el[data-autosize="true"] { resize: none; overflow: hidden; }
10632
+ .twc-field__footer { display: flex; align-items: center; gap: var(--space-2); }
10633
+ .twc-field__count { margin-inline-start: auto; font-size: var(--text-xs); color: var(--color-text-muted); font-variant-numeric: tabular-nums; flex: none; }
10634
+ .twc-field__count[data-danger="true"] { color: var(--color-danger-subtle-fg); }
10635
+ `;
10636
+ var Textarea = React25__namespace.default.forwardRef(function Textarea2({
10637
+ label,
10638
+ hint,
10639
+ error,
10640
+ required = false,
10641
+ size = "md",
10642
+ tone = "primary",
10643
+ rows = 4,
10644
+ autosize = false,
10645
+ minRows,
10646
+ maxRows,
10647
+ showCount = false,
10648
+ disabled = false,
10649
+ id,
10650
+ className = "",
10651
+ ...rest
10652
+ }, ref) {
10653
+ const __twcStyles = useScopedStyles("twc-textarea-styles", TEXTAREA_CSS);
10654
+ const autoId = React25__namespace.default.useId();
10077
10655
  const fieldId = id || autoId;
10078
10656
  const descId = `${fieldId}-desc`;
10657
+ const countId = `${fieldId}-count`;
10079
10658
  const invalid = Boolean(error);
10659
+ const innerRef = React25__namespace.default.useRef(null);
10660
+ const setRef = (el) => {
10661
+ innerRef.current = el;
10662
+ if (typeof ref === "function") ref(el);
10663
+ else if (ref) ref.current = el;
10664
+ };
10665
+ const countControlled = rest.value !== void 0;
10666
+ const [len2, setLen] = React25__namespace.default.useState(() => String(rest.defaultValue ?? "").length);
10667
+ const count = countControlled ? String(rest.value ?? "").length : len2;
10668
+ const max = rest.maxLength;
10669
+ const showCounter = showCount && max != null;
10670
+ const near = showCounter && count >= Math.floor(max * 0.9);
10671
+ const resize = () => {
10672
+ const el = innerRef.current;
10673
+ if (!el) return;
10674
+ el.style.height = "auto";
10675
+ const cs = getComputedStyle(el);
10676
+ const line = parseFloat(cs.lineHeight) || 20;
10677
+ const vPad = (parseFloat(cs.paddingTop) || 0) + (parseFloat(cs.paddingBottom) || 0);
10678
+ const vBorder = (parseFloat(cs.borderTopWidth) || 0) + (parseFloat(cs.borderBottomWidth) || 0);
10679
+ const min = (minRows ?? rows) * line + vPad + vBorder;
10680
+ const maxH = maxRows ? maxRows * line + vPad + vBorder : Infinity;
10681
+ const next = Math.min(Math.max(el.scrollHeight + vBorder, min), maxH);
10682
+ el.style.height = `${next}px`;
10683
+ el.style.overflowY = el.scrollHeight + vBorder > maxH ? "auto" : "hidden";
10684
+ };
10685
+ useIso(() => {
10686
+ if (autosize && innerRef.current) resize();
10687
+ }, [autosize, rest.value, minRows, maxRows, rows]);
10688
+ const handleInput = (e) => {
10689
+ rest.onInput?.(e);
10690
+ if (autosize) resize();
10691
+ };
10692
+ const handleChange = (e) => {
10693
+ rest.onChange?.(e);
10694
+ if (showCount && !countControlled) setLen(e.target.value.length);
10695
+ };
10696
+ const ariaInvalid = invalid ? true : rest["aria-invalid"] ?? void 0;
10697
+ const describedBy = [error || hint ? descId : null, showCounter ? countId : null, rest["aria-describedby"]].filter(Boolean).join(" ") || void 0;
10080
10698
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-field ${className}`, children: [
10081
10699
  __twcStyles,
10082
10700
  label ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-field__label", htmlFor: fieldId, children: [
@@ -10086,22 +10704,34 @@ function Textarea({
10086
10704
  /* @__PURE__ */ jsxRuntime.jsx(
10087
10705
  "textarea",
10088
10706
  {
10707
+ ref: setRef,
10089
10708
  id: fieldId,
10090
10709
  className: "twc-textarea__el",
10091
10710
  "data-size": size,
10092
10711
  "data-tone": tone,
10093
- rows,
10712
+ "data-autosize": autosize || void 0,
10713
+ "data-readonly": rest.readOnly || void 0,
10714
+ rows: autosize ? minRows ?? rows : rows,
10094
10715
  disabled,
10095
10716
  required: required || void 0,
10096
10717
  "aria-required": required || void 0,
10097
- "aria-invalid": invalid || void 0,
10098
- "aria-describedby": error || hint ? descId : void 0,
10099
- ...rest
10718
+ ...rest,
10719
+ onInput: handleInput,
10720
+ onChange: handleChange,
10721
+ "aria-invalid": ariaInvalid,
10722
+ "aria-describedby": describedBy
10100
10723
  }
10101
10724
  ),
10102
- error ? /* @__PURE__ */ jsxRuntime.jsx("span", { id: descId, className: "twc-field__error", children: error }) : hint ? /* @__PURE__ */ jsxRuntime.jsx("span", { id: descId, className: "twc-field__hint", children: hint }) : null
10725
+ error || hint || showCounter ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-field__footer", children: [
10726
+ error ? /* @__PURE__ */ jsxRuntime.jsx("span", { id: descId, className: "twc-field__error", children: error }) : hint ? /* @__PURE__ */ jsxRuntime.jsx("span", { id: descId, className: "twc-field__hint", children: hint }) : null,
10727
+ showCounter ? /* @__PURE__ */ jsxRuntime.jsxs("span", { id: countId, className: "twc-field__count", "data-danger": near || void 0, "aria-live": "polite", children: [
10728
+ count,
10729
+ " / ",
10730
+ max
10731
+ ] }) : null
10732
+ ] }) : null
10103
10733
  ] });
10104
- }
10734
+ });
10105
10735
  var TIMELINE_CSS = `
10106
10736
  .twc-timeline { display: flex; flex-direction: column; font-family: var(--font-sans); margin: 0; padding: 0; list-style: none; }
10107
10737
  .twc-timeline__item { position: relative; display: flex; gap: var(--space-4); padding-bottom: var(--space-6); }
@@ -10211,28 +10841,29 @@ function Toast({
10211
10841
  }) {
10212
10842
  const resolvedTone = tone === "neutral" ? "default" : tone;
10213
10843
  const __twcStyles = useScopedStyles("twc-toast-styles", TOAST_CSS);
10214
- const onCloseRef = React24__namespace.default.useRef(onClose);
10215
- React24__namespace.default.useEffect(() => {
10844
+ const onCloseRef = React25__namespace.default.useRef(onClose);
10845
+ React25__namespace.default.useEffect(() => {
10216
10846
  onCloseRef.current = onClose;
10217
10847
  }, [onClose]);
10218
- const timer = React24__namespace.default.useRef();
10219
- const stop = React24__namespace.default.useCallback(() => clearTimeout(timer.current), []);
10220
- const start = React24__namespace.default.useCallback(() => {
10848
+ const timer = React25__namespace.default.useRef();
10849
+ const stop = React25__namespace.default.useCallback(() => clearTimeout(timer.current), []);
10850
+ const start = React25__namespace.default.useCallback(() => {
10221
10851
  stop();
10222
10852
  if (duration && duration !== Infinity && onCloseRef.current) {
10223
10853
  timer.current = setTimeout(() => onCloseRef.current && onCloseRef.current(), duration);
10224
10854
  }
10225
10855
  }, [duration, stop]);
10226
- React24__namespace.default.useEffect(() => {
10856
+ React25__namespace.default.useEffect(() => {
10227
10857
  start();
10228
10858
  return stop;
10229
10859
  }, [start, stop]);
10860
+ const role = resolvedTone === "danger" || resolvedTone === "warning" ? "alert" : "status";
10230
10861
  return /* @__PURE__ */ jsxRuntime.jsxs(
10231
10862
  "div",
10232
10863
  {
10233
10864
  className: `twc-toast ${className}`,
10234
10865
  "data-tone": resolvedTone,
10235
- role: "status",
10866
+ role,
10236
10867
  ...rest,
10237
10868
  onMouseEnter: (e) => {
10238
10869
  onMouseEnter?.(e);
@@ -10267,30 +10898,30 @@ function Toast({
10267
10898
  }
10268
10899
  function ToastViewport({ children, limit, className = "", ...rest }) {
10269
10900
  const __twcStyles = useScopedStyles("twc-toast-styles", TOAST_CSS);
10270
- const shown = limit && limit > 0 ? React24__namespace.default.Children.toArray(children).slice(-limit) : children;
10271
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-toast-viewport ${className}`, ...rest, children: [
10901
+ const shown = limit && limit > 0 ? React25__namespace.default.Children.toArray(children).slice(-limit) : children;
10902
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-toast-viewport ${className}`, role: "region", "aria-label": "Notifications", ...rest, children: [
10272
10903
  __twcStyles,
10273
10904
  shown
10274
10905
  ] });
10275
10906
  }
10276
- var ToastContext = React24__namespace.default.createContext(null);
10907
+ var ToastContext = React25__namespace.default.createContext(null);
10277
10908
  function useToast() {
10278
- const ctx = React24__namespace.default.useContext(ToastContext);
10909
+ const ctx = React25__namespace.default.useContext(ToastContext);
10279
10910
  if (!ctx) throw new Error("useToast must be used within a <ToastProvider>.");
10280
10911
  return ctx;
10281
10912
  }
10282
10913
  function ToastProvider({ children, limit = 4, duration = 4500, ...viewportProps }) {
10283
- const [toasts, setToasts] = React24__namespace.default.useState([]);
10284
- const idRef = React24__namespace.default.useRef(0);
10285
- const limitRef = React24__namespace.default.useRef(limit);
10286
- React24__namespace.default.useEffect(() => {
10914
+ const [toasts, setToasts] = React25__namespace.default.useState([]);
10915
+ const idRef = React25__namespace.default.useRef(0);
10916
+ const limitRef = React25__namespace.default.useRef(limit);
10917
+ React25__namespace.default.useEffect(() => {
10287
10918
  limitRef.current = limit;
10288
10919
  }, [limit]);
10289
- const dismiss = React24__namespace.default.useCallback((id) => {
10920
+ const dismiss = React25__namespace.default.useCallback((id) => {
10290
10921
  setToasts((s) => s.filter((t) => t.id !== id));
10291
10922
  }, []);
10292
- const clear = React24__namespace.default.useCallback(() => setToasts([]), []);
10293
- const api = React24__namespace.default.useMemo(() => {
10923
+ const clear = React25__namespace.default.useCallback(() => setToasts([]), []);
10924
+ const api = React25__namespace.default.useMemo(() => {
10294
10925
  const push = (opts) => {
10295
10926
  const id = idRef.current += 1;
10296
10927
  const next = typeof opts === "string" ? { title: opts } : opts && typeof opts === "object" ? opts : {};
@@ -10311,7 +10942,7 @@ function ToastProvider({ children, limit = 4, duration = 4500, ...viewportProps
10311
10942
  toast.show = withTone("default");
10312
10943
  return { toast, dismiss, clear };
10313
10944
  }, [dismiss, clear]);
10314
- const value = React24__namespace.default.useMemo(() => ({ ...api, toasts }), [api, toasts]);
10945
+ const value = React25__namespace.default.useMemo(() => ({ ...api, toasts }), [api, toasts]);
10315
10946
  return /* @__PURE__ */ jsxRuntime.jsxs(ToastContext.Provider, { value, children: [
10316
10947
  children,
10317
10948
  /* @__PURE__ */ jsxRuntime.jsx(ToastViewport, { limit, ...viewportProps, children: toasts.map((t) => /* @__PURE__ */ jsxRuntime.jsx(
@@ -10419,13 +11050,13 @@ function TreeView({
10419
11050
  }) {
10420
11051
  const nodes = items;
10421
11052
  const __twcStyles = useScopedStyles("twc-tree-styles", TREE_CSS);
10422
- const [expInternal, setExpInternal] = React24__namespace.default.useState(() => new Set(defaultExpanded));
11053
+ const [expInternal, setExpInternal] = React25__namespace.default.useState(() => new Set(defaultExpanded));
10423
11054
  const expanded = expandedProp !== void 0 ? new Set(expandedProp) : expInternal;
10424
- const [selInternal, setSelInternal] = React24__namespace.default.useState(defaultSelectedId);
11055
+ const [selInternal, setSelInternal] = React25__namespace.default.useState(defaultSelectedId);
10425
11056
  const selectedId = selectedProp !== void 0 ? selectedProp : selInternal;
10426
- const [focusId, setFocusId] = React24__namespace.default.useState(null);
10427
- const rowRefs = React24__namespace.default.useRef(/* @__PURE__ */ new Map());
10428
- const taRef = React24__namespace.default.useRef({ str: "", t: 0 });
11057
+ const [focusId, setFocusId] = React25__namespace.default.useState(null);
11058
+ const rowRefs = React25__namespace.default.useRef(/* @__PURE__ */ new Map());
11059
+ const taRef = React25__namespace.default.useRef({ str: "", t: 0 });
10429
11060
  const toggle = (id) => {
10430
11061
  const n = new Set(expanded);
10431
11062
  n.has(id) ? n.delete(id) : n.add(id);
@@ -10557,7 +11188,7 @@ function AppShell({
10557
11188
  ...rest
10558
11189
  }) {
10559
11190
  const __twcStyles = useScopedStyles("twc-shell-styles", SHELL_CSS);
10560
- const wiredSidebar = React24__namespace.default.isValidElement(sidebar) && (sidebarOpen !== void 0 || onSidebarOpenChange !== void 0) ? React24__namespace.default.cloneElement(sidebar, {
11191
+ const wiredSidebar = React25__namespace.default.isValidElement(sidebar) && (sidebarOpen !== void 0 || onSidebarOpenChange !== void 0) ? React25__namespace.default.cloneElement(sidebar, {
10561
11192
  overlay: sidebar.props.overlay !== void 0 ? sidebar.props.overlay : true,
10562
11193
  open: sidebar.props.open !== void 0 ? sidebar.props.open : sidebarOpen,
10563
11194
  onOpenChange: sidebar.props.onOpenChange !== void 0 ? sidebar.props.onOpenChange : onSidebarOpenChange
@@ -10699,7 +11330,7 @@ function Grid({
10699
11330
  ...rest
10700
11331
  }) {
10701
11332
  const responsive = columns != null && typeof columns === "object";
10702
- const uid = React24__namespace.default.useId();
11333
+ const uid = React25__namespace.default.useId();
10703
11334
  const gridId = responsive ? `g${uid.replace(/[^a-zA-Z0-9]/g, "")}` : null;
10704
11335
  let css = "";
10705
11336
  if (responsive) {
@@ -10770,7 +11401,7 @@ function Stack({
10770
11401
  ...rest
10771
11402
  }) {
10772
11403
  if (Tag2 === "a" && rest.href != null) rest.href = safeHref8(rest.href);
10773
- const kids = divider != null ? React24__namespace.default.Children.toArray(children).flatMap((child, i, arr) => i < arr.length - 1 ? [child, /* @__PURE__ */ jsxRuntime.jsx(React24__namespace.default.Fragment, { children: divider }, `twc-stack-div-${i}`)] : [child]) : children;
11404
+ const kids = divider != null ? React25__namespace.default.Children.toArray(children).flatMap((child, i, arr) => i < arr.length - 1 ? [child, /* @__PURE__ */ jsxRuntime.jsx(React25__namespace.default.Fragment, { children: divider }, `twc-stack-div-${i}`)] : [child]) : children;
10774
11405
  return /* @__PURE__ */ jsxRuntime.jsx(
10775
11406
  Tag2,
10776
11407
  {
@@ -10835,10 +11466,10 @@ function Code({
10835
11466
  const __twcStyles = useScopedStyles("twc-code-styles", CODE_CSS);
10836
11467
  const Tag2 = as ?? (block ? "pre" : "code");
10837
11468
  if (Tag2 === "a" && rest.href != null) rest.href = safeHref9(rest.href);
10838
- const rootRef = React24__namespace.default.useRef(null);
10839
- const [copied, setCopied] = React24__namespace.default.useState(false);
10840
- const timerRef = React24__namespace.default.useRef(null);
10841
- React24__namespace.default.useEffect(() => () => clearTimeout(timerRef.current), []);
11469
+ const rootRef = React25__namespace.default.useRef(null);
11470
+ const [copied, setCopied] = React25__namespace.default.useState(false);
11471
+ const timerRef = React25__namespace.default.useRef(null);
11472
+ React25__namespace.default.useEffect(() => () => clearTimeout(timerRef.current), []);
10842
11473
  const doCopy = () => {
10843
11474
  if (typeof navigator === "undefined" || !navigator.clipboard) return;
10844
11475
  const text = typeof children === "string" ? children : rootRef.current?.textContent ?? "";
@@ -10957,45 +11588,45 @@ function Text({
10957
11588
  );
10958
11589
  }
10959
11590
  var canUseDOM2 = typeof window !== "undefined" && typeof document !== "undefined";
10960
- var useIsomorphicLayoutEffect = canUseDOM2 ? React24__namespace.default.useLayoutEffect : React24__namespace.default.useEffect;
11591
+ var useIsomorphicLayoutEffect = canUseDOM2 ? React25__namespace.default.useLayoutEffect : React25__namespace.default.useEffect;
10961
11592
  function useMounted() {
10962
- const [mounted, setMounted] = React24__namespace.default.useState(false);
10963
- React24__namespace.default.useEffect(() => {
11593
+ const [mounted, setMounted] = React25__namespace.default.useState(false);
11594
+ React25__namespace.default.useEffect(() => {
10964
11595
  setMounted(true);
10965
11596
  }, []);
10966
11597
  return mounted;
10967
11598
  }
10968
11599
  function usePrevious(value) {
10969
- const ref = React24__namespace.default.useRef(void 0);
10970
- React24__namespace.default.useEffect(() => {
11600
+ const ref = React25__namespace.default.useRef(void 0);
11601
+ React25__namespace.default.useEffect(() => {
10971
11602
  ref.current = value;
10972
11603
  }, [value]);
10973
11604
  return ref.current;
10974
11605
  }
10975
11606
  function useToggle(initial = false) {
10976
- const [on, setOn] = React24__namespace.default.useState(initial);
10977
- const toggle = React24__namespace.default.useCallback((next) => {
11607
+ const [on, setOn] = React25__namespace.default.useState(initial);
11608
+ const toggle = React25__namespace.default.useCallback((next) => {
10978
11609
  setOn((prev) => typeof next === "boolean" ? next : !prev);
10979
11610
  }, []);
10980
11611
  return [on, toggle, setOn];
10981
11612
  }
10982
11613
  function useDisclosure(initial = false) {
10983
- const [open, setOpen] = React24__namespace.default.useState(initial);
10984
- const onOpen = React24__namespace.default.useCallback(() => setOpen(true), []);
10985
- const onClose = React24__namespace.default.useCallback(() => setOpen(false), []);
10986
- const onToggle = React24__namespace.default.useCallback(() => setOpen((p) => !p), []);
11614
+ const [open, setOpen] = React25__namespace.default.useState(initial);
11615
+ const onOpen = React25__namespace.default.useCallback(() => setOpen(true), []);
11616
+ const onClose = React25__namespace.default.useCallback(() => setOpen(false), []);
11617
+ const onToggle = React25__namespace.default.useCallback(() => setOpen((p) => !p), []);
10987
11618
  return { open, onOpen, onClose, onToggle, setOpen };
10988
11619
  }
10989
11620
  function useControllableState({ value, defaultValue, onChange } = {}) {
10990
11621
  const isControlled = value !== void 0;
10991
- const [uncontrolled, setUncontrolled] = React24__namespace.default.useState(defaultValue);
11622
+ const [uncontrolled, setUncontrolled] = React25__namespace.default.useState(defaultValue);
10992
11623
  const current = isControlled ? value : uncontrolled;
10993
- const ref = React24__namespace.default.useRef({ current, isControlled, onChange });
11624
+ const ref = React25__namespace.default.useRef({ current, isControlled, onChange });
10994
11625
  useIsomorphicLayoutEffect(() => {
10995
11626
  ref.current = { current, isControlled, onChange };
10996
11627
  });
10997
- const wasControlled = React24__namespace.default.useRef(isControlled);
10998
- React24__namespace.default.useEffect(() => {
11628
+ const wasControlled = React25__namespace.default.useRef(isControlled);
11629
+ React25__namespace.default.useEffect(() => {
10999
11630
  if (wasControlled.current !== isControlled) {
11000
11631
  warnOnce(
11001
11632
  "useControllableState:switch",
@@ -11004,7 +11635,7 @@ function useControllableState({ value, defaultValue, onChange } = {}) {
11004
11635
  wasControlled.current = isControlled;
11005
11636
  }
11006
11637
  });
11007
- const setValue = React24__namespace.default.useCallback((next) => {
11638
+ const setValue = React25__namespace.default.useCallback((next) => {
11008
11639
  const { current: cur, isControlled: ctrl, onChange: cb } = ref.current;
11009
11640
  const resolved = typeof next === "function" ? next(cur) : next;
11010
11641
  if (resolved === cur) return;
@@ -11015,7 +11646,7 @@ function useControllableState({ value, defaultValue, onChange } = {}) {
11015
11646
  }
11016
11647
  function useMediaQuery(query, { defaultValue = false, initializeWithValue = false } = {}) {
11017
11648
  const get = () => canUseDOM2 ? window.matchMedia(query).matches : defaultValue;
11018
- const [matches3, setMatches] = React24__namespace.default.useState(initializeWithValue ? get : defaultValue);
11649
+ const [matches3, setMatches] = React25__namespace.default.useState(initializeWithValue ? get : defaultValue);
11019
11650
  useIsomorphicLayoutEffect(() => {
11020
11651
  if (!canUseDOM2) return void 0;
11021
11652
  const mql = window.matchMedia(query);
@@ -11031,7 +11662,7 @@ function usePrefersReducedMotion() {
11031
11662
  }
11032
11663
  function useColorScheme({ storageKey = "twico-theme", attribute = "class", element, disableTransitionsOnChange = true } = {}) {
11033
11664
  const getTarget = () => element || (canUseDOM2 ? document.documentElement : null);
11034
- const firstApply = React24__namespace.default.useRef(true);
11665
+ const firstApply = React25__namespace.default.useRef(true);
11035
11666
  const read = () => {
11036
11667
  if (!canUseDOM2) return "light";
11037
11668
  try {
@@ -11041,8 +11672,8 @@ function useColorScheme({ storageKey = "twico-theme", attribute = "class", eleme
11041
11672
  }
11042
11673
  return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
11043
11674
  };
11044
- const [theme, setThemeState] = React24__namespace.default.useState(read);
11045
- const apply = React24__namespace.default.useCallback(
11675
+ const [theme, setThemeState] = React25__namespace.default.useState(read);
11676
+ const apply = React25__namespace.default.useCallback(
11046
11677
  (t) => {
11047
11678
  const target = getTarget();
11048
11679
  if (!target) return;
@@ -11068,11 +11699,11 @@ function useColorScheme({ storageKey = "twico-theme", attribute = "class", eleme
11068
11699
  useIsomorphicLayoutEffect(() => {
11069
11700
  apply(theme);
11070
11701
  }, [theme, apply]);
11071
- const themeRef = React24__namespace.default.useRef(theme);
11702
+ const themeRef = React25__namespace.default.useRef(theme);
11072
11703
  useIsomorphicLayoutEffect(() => {
11073
11704
  themeRef.current = theme;
11074
11705
  }, [theme]);
11075
- const setTheme = React24__namespace.default.useCallback(
11706
+ const setTheme = React25__namespace.default.useCallback(
11076
11707
  (t) => {
11077
11708
  setThemeState(t);
11078
11709
  try {
@@ -11083,10 +11714,10 @@ function useColorScheme({ storageKey = "twico-theme", attribute = "class", eleme
11083
11714
  },
11084
11715
  [storageKey]
11085
11716
  );
11086
- const toggle = React24__namespace.default.useCallback(() => {
11717
+ const toggle = React25__namespace.default.useCallback(() => {
11087
11718
  setTheme(themeRef.current === "dark" ? "light" : "dark");
11088
11719
  }, [setTheme]);
11089
- React24__namespace.default.useEffect(() => {
11720
+ React25__namespace.default.useEffect(() => {
11090
11721
  if (!canUseDOM2) return void 0;
11091
11722
  const sync = () => setThemeState(read());
11092
11723
  const onStorage = (e) => {
@@ -11102,14 +11733,14 @@ function useColorScheme({ storageKey = "twico-theme", attribute = "class", eleme
11102
11733
  return { theme, setTheme, toggle, isDark: theme === "dark" };
11103
11734
  }
11104
11735
  function useEventListener(eventName, handler, element, options) {
11105
- const saved = React24__namespace.default.useRef(handler);
11736
+ const saved = React25__namespace.default.useRef(handler);
11106
11737
  useIsomorphicLayoutEffect(() => {
11107
11738
  saved.current = handler;
11108
11739
  }, [handler]);
11109
11740
  const capture = typeof options === "boolean" ? options : options && options.capture;
11110
11741
  const passive = typeof options === "object" && options ? options.passive : void 0;
11111
11742
  const once = typeof options === "object" && options ? options.once : void 0;
11112
- React24__namespace.default.useEffect(() => {
11743
+ React25__namespace.default.useEffect(() => {
11113
11744
  const target = element && element.current || element || (canUseDOM2 ? window : null);
11114
11745
  if (!target || !target.addEventListener) return void 0;
11115
11746
  const opts = typeof options === "boolean" ? options : { capture, passive, once };
@@ -11119,11 +11750,11 @@ function useEventListener(eventName, handler, element, options) {
11119
11750
  }, [eventName, element, capture, passive, once]);
11120
11751
  }
11121
11752
  function useClickOutside(ref, handler, events = ["mousedown", "touchstart"]) {
11122
- const saved = React24__namespace.default.useRef(handler);
11753
+ const saved = React25__namespace.default.useRef(handler);
11123
11754
  useIsomorphicLayoutEffect(() => {
11124
11755
  saved.current = handler;
11125
11756
  }, [handler]);
11126
- React24__namespace.default.useEffect(() => {
11757
+ React25__namespace.default.useEffect(() => {
11127
11758
  if (!canUseDOM2) return void 0;
11128
11759
  const listener = (e) => {
11129
11760
  const el = ref && ref.current;
@@ -11158,8 +11789,8 @@ function useLocalStorage(key, initialValue) {
11158
11789
  return initialValue;
11159
11790
  }
11160
11791
  };
11161
- const [stored, setStored] = React24__namespace.default.useState(read);
11162
- const setValue = React24__namespace.default.useCallback(
11792
+ const [stored, setStored] = React25__namespace.default.useState(read);
11793
+ const setValue = React25__namespace.default.useCallback(
11163
11794
  (value) => {
11164
11795
  setStored((prev) => {
11165
11796
  const next = typeof value === "function" ? value(prev) : value;
@@ -11175,8 +11806,8 @@ function useLocalStorage(key, initialValue) {
11175
11806
  return [stored, setValue];
11176
11807
  }
11177
11808
  function useCopyToClipboard(timeout = 1500) {
11178
- const [copied, setCopied] = React24__namespace.default.useState(false);
11179
- const copy = React24__namespace.default.useCallback(async (text) => {
11809
+ const [copied, setCopied] = React25__namespace.default.useState(false);
11810
+ const copy = React25__namespace.default.useCallback(async (text) => {
11180
11811
  if (!canUseDOM2 || !navigator.clipboard) {
11181
11812
  setCopied(false);
11182
11813
  return false;
@@ -11190,7 +11821,7 @@ function useCopyToClipboard(timeout = 1500) {
11190
11821
  return false;
11191
11822
  }
11192
11823
  }, []);
11193
- React24__namespace.default.useEffect(() => {
11824
+ React25__namespace.default.useEffect(() => {
11194
11825
  if (!copied) return void 0;
11195
11826
  const t = setTimeout(() => setCopied(false), timeout);
11196
11827
  return () => clearTimeout(t);
@@ -11198,23 +11829,23 @@ function useCopyToClipboard(timeout = 1500) {
11198
11829
  return { copied, copy };
11199
11830
  }
11200
11831
  function useDebouncedValue(value, delay = 200) {
11201
- const [debounced, setDebounced] = React24__namespace.default.useState(value);
11202
- React24__namespace.default.useEffect(() => {
11832
+ const [debounced, setDebounced] = React25__namespace.default.useState(value);
11833
+ React25__namespace.default.useEffect(() => {
11203
11834
  const t = setTimeout(() => setDebounced(value), delay);
11204
11835
  return () => clearTimeout(t);
11205
11836
  }, [value, delay]);
11206
11837
  return debounced;
11207
11838
  }
11208
11839
  function useDebouncedCallback(callback, delay = 200) {
11209
- const saved = React24__namespace.default.useRef(callback);
11210
- const timer = React24__namespace.default.useRef(null);
11840
+ const saved = React25__namespace.default.useRef(callback);
11841
+ const timer = React25__namespace.default.useRef(null);
11211
11842
  useIsomorphicLayoutEffect(() => {
11212
11843
  saved.current = callback;
11213
11844
  }, [callback]);
11214
- React24__namespace.default.useEffect(() => () => {
11845
+ React25__namespace.default.useEffect(() => () => {
11215
11846
  if (timer.current) clearTimeout(timer.current);
11216
11847
  }, []);
11217
- return React24__namespace.default.useCallback(
11848
+ return React25__namespace.default.useCallback(
11218
11849
  (...args) => {
11219
11850
  if (timer.current) clearTimeout(timer.current);
11220
11851
  timer.current = setTimeout(() => saved.current(...args), delay);
@@ -11223,33 +11854,33 @@ function useDebouncedCallback(callback, delay = 200) {
11223
11854
  );
11224
11855
  }
11225
11856
  function useInterval(callback, delay) {
11226
- const saved = React24__namespace.default.useRef(callback);
11857
+ const saved = React25__namespace.default.useRef(callback);
11227
11858
  useIsomorphicLayoutEffect(() => {
11228
11859
  saved.current = callback;
11229
11860
  }, [callback]);
11230
- React24__namespace.default.useEffect(() => {
11861
+ React25__namespace.default.useEffect(() => {
11231
11862
  if (delay == null) return void 0;
11232
11863
  const id = setInterval(() => saved.current(), delay);
11233
11864
  return () => clearInterval(id);
11234
11865
  }, [delay]);
11235
11866
  }
11236
11867
  function useTimeout(callback, delay) {
11237
- const saved = React24__namespace.default.useRef(callback);
11868
+ const saved = React25__namespace.default.useRef(callback);
11238
11869
  useIsomorphicLayoutEffect(() => {
11239
11870
  saved.current = callback;
11240
11871
  }, [callback]);
11241
- React24__namespace.default.useEffect(() => {
11872
+ React25__namespace.default.useEffect(() => {
11242
11873
  if (delay == null) return void 0;
11243
11874
  const id = setTimeout(() => saved.current(), delay);
11244
11875
  return () => clearTimeout(id);
11245
11876
  }, [delay]);
11246
11877
  }
11247
11878
  function useWindowSize() {
11248
- const [size, setSize] = React24__namespace.default.useState(() => ({
11879
+ const [size, setSize] = React25__namespace.default.useState(() => ({
11249
11880
  width: canUseDOM2 ? window.innerWidth : 0,
11250
11881
  height: canUseDOM2 ? window.innerHeight : 0
11251
11882
  }));
11252
- React24__namespace.default.useEffect(() => {
11883
+ React25__namespace.default.useEffect(() => {
11253
11884
  if (!canUseDOM2) return void 0;
11254
11885
  const onResize = () => setSize({ width: window.innerWidth, height: window.innerHeight });
11255
11886
  onResize();
@@ -11259,8 +11890,8 @@ function useWindowSize() {
11259
11890
  return size;
11260
11891
  }
11261
11892
  function useHover(ref) {
11262
- const [hovered, setHovered] = React24__namespace.default.useState(false);
11263
- React24__namespace.default.useEffect(() => {
11893
+ const [hovered, setHovered] = React25__namespace.default.useState(false);
11894
+ React25__namespace.default.useEffect(() => {
11264
11895
  const el = ref && ref.current;
11265
11896
  if (!el) return void 0;
11266
11897
  const on = () => setHovered(true);
@@ -11276,8 +11907,8 @@ function useHover(ref) {
11276
11907
  }
11277
11908
  function useIntersectionObserver(ref, options = {}) {
11278
11909
  const { root = null, rootMargin = "0px", threshold = 0 } = options;
11279
- const [entry, setEntry] = React24__namespace.default.useState(null);
11280
- React24__namespace.default.useEffect(() => {
11910
+ const [entry, setEntry] = React25__namespace.default.useState(null);
11911
+ React25__namespace.default.useEffect(() => {
11281
11912
  const el = ref && ref.current;
11282
11913
  if (!canUseDOM2 || !el || typeof IntersectionObserver === "undefined") return void 0;
11283
11914
  const obs = new IntersectionObserver(([e]) => setEntry(e), { root, rootMargin, threshold });
@@ -11286,30 +11917,13 @@ function useIntersectionObserver(ref, options = {}) {
11286
11917
  }, [ref, root, rootMargin, JSON.stringify(threshold)]);
11287
11918
  return entry;
11288
11919
  }
11289
- var __scrollLockCount = 0;
11290
- var __scrollLockSaved = "";
11291
- function useScrollLock(locked = true) {
11292
- useIsomorphicLayoutEffect(() => {
11293
- if (!canUseDOM2 || !locked) return void 0;
11294
- if (__scrollLockCount === 0) {
11295
- __scrollLockSaved = document.body.style.overflow;
11296
- document.body.style.overflow = "hidden";
11297
- }
11298
- __scrollLockCount += 1;
11299
- return () => {
11300
- __scrollLockCount -= 1;
11301
- if (__scrollLockCount === 0) {
11302
- document.body.style.overflow = __scrollLockSaved;
11303
- }
11304
- };
11305
- }, [locked]);
11306
- }
11307
11920
  function useId(prefix = "twc") {
11308
- const raw = React24__namespace.default.useId();
11921
+ const raw = React25__namespace.default.useId();
11309
11922
  return prefix ? `${prefix}-${raw.replace(/:/g, "")}` : raw;
11310
11923
  }
11311
11924
  var useFocusTrap2 = useFocusTrap;
11312
11925
  var usePortal2 = usePortal;
11926
+ var useScrollLock2 = useScrollLock;
11313
11927
 
11314
11928
  exports.Accordion = Accordion;
11315
11929
  exports.Alert = Alert;
@@ -11353,6 +11967,7 @@ exports.Pagination = Pagination;
11353
11967
  exports.Popover = Popover;
11354
11968
  exports.Progress = Progress;
11355
11969
  exports.Radio = Radio;
11970
+ exports.RadioGroup = RadioGroup;
11356
11971
  exports.Rating = Rating;
11357
11972
  exports.Select = Select;
11358
11973
  exports.Sidebar = Sidebar;
@@ -11396,7 +12011,7 @@ exports.useMounted = useMounted;
11396
12011
  exports.usePortal = usePortal2;
11397
12012
  exports.usePrefersReducedMotion = usePrefersReducedMotion;
11398
12013
  exports.usePrevious = usePrevious;
11399
- exports.useScrollLock = useScrollLock;
12014
+ exports.useScrollLock = useScrollLock2;
11400
12015
  exports.useTimeout = useTimeout;
11401
12016
  exports.useToast = useToast;
11402
12017
  exports.useToggle = useToggle;