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.mjs CHANGED
@@ -1,13 +1,13 @@
1
1
  "use client";
2
- import * as React24 from 'react';
3
- import React24__default from 'react';
2
+ import * as React25 from 'react';
3
+ import React25__default from 'react';
4
4
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
5
5
  import { createPortal } from 'react-dom';
6
6
 
7
7
  // components/navigation/Accordion.jsx
8
- var SUPPORTS_STYLE_HOIST = parseInt(String(React24.version), 10) >= 19;
8
+ var SUPPORTS_STYLE_HOIST = parseInt(String(React25.version), 10) >= 19;
9
9
  function useScopedStyles(id, css) {
10
- React24.useInsertionEffect(() => {
10
+ React25.useInsertionEffect(() => {
11
11
  if (SUPPORTS_STYLE_HOIST) return;
12
12
  if (typeof document === "undefined" || document.getElementById(id)) return;
13
13
  const el = document.createElement("style");
@@ -15,7 +15,7 @@ function useScopedStyles(id, css) {
15
15
  el.textContent = css;
16
16
  document.head.appendChild(el);
17
17
  }, [id, css]);
18
- return SUPPORTS_STYLE_HOIST ? React24.createElement("style", { href: id, precedence: "twc-ui" }, css) : null;
18
+ return SUPPORTS_STYLE_HOIST ? React25.createElement("style", { href: id, precedence: "twc-ui" }, css) : null;
19
19
  }
20
20
  var ACCORDION_CSS = `
21
21
  .twc-accordion { display: flex; flex-direction: column; font-family: var(--font-sans);
@@ -56,10 +56,10 @@ function Accordion({
56
56
  ...rest
57
57
  }) {
58
58
  const __twcStyles = useScopedStyles("twc-accordion-styles", ACCORDION_CSS);
59
- const rootRef = React24__default.useRef(null);
60
- const [internal, setInternal] = React24__default.useState(() => new Set(defaultOpen));
59
+ const rootRef = React25__default.useRef(null);
60
+ const [internal, setInternal] = React25__default.useState(() => new Set(defaultOpen));
61
61
  const open = openProp !== void 0 ? new Set(openProp) : internal;
62
- const baseId = React24__default.useId();
62
+ const baseId = React25__default.useId();
63
63
  function toggle(val) {
64
64
  const item = items.find((x) => x.value === val);
65
65
  if (item?.disabled) return;
@@ -245,7 +245,7 @@ function Avatar({
245
245
  ...rest
246
246
  }) {
247
247
  const __twcStyles = useScopedStyles("twc-avatar-styles", AVATAR_CSS);
248
- const [errored, setErrored] = React24__default.useState(false);
248
+ const [errored, setErrored] = React25__default.useState(false);
249
249
  const cleanSrc = safeSrc(src);
250
250
  const showImg = cleanSrc && !errored;
251
251
  const _statusLabel = status ? statusLabel ?? statusLabels?.[status] ?? STATUS_LABELS[status] : void 0;
@@ -270,7 +270,7 @@ function Avatar({
270
270
  var MENU_CSS = `
271
271
  .twc-menu-wrap { position: relative; display: inline-flex; }
272
272
  .twc-menu {
273
- position: fixed; z-index: var(--z-dropdown); min-width: 200px;
273
+ position: fixed; z-index: var(--z-popover); min-width: 200px;
274
274
  padding: var(--space-1-5); background: var(--color-surface-raised);
275
275
  border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-lg);
276
276
  box-shadow: var(--shadow-lg); font-family: var(--font-sans);
@@ -323,23 +323,25 @@ function Menu({
323
323
  ...rest
324
324
  }) {
325
325
  const __twcStyles = useScopedStyles("twc-menu-styles", MENU_CSS);
326
- const [openState, setOpenState] = React24__default.useState(defaultOpen);
327
- const [render, setRender] = React24__default.useState(false);
328
- const [pos, setPos] = React24__default.useState(null);
329
- const [active, setActive] = React24__default.useState(-1);
330
- const wrapRef = React24__default.useRef(null);
331
- const menuRef = React24__default.useRef(null);
332
- const menuId = React24__default.useId();
326
+ const [openState, setOpenState] = React25__default.useState(defaultOpen);
327
+ const [render, setRender] = React25__default.useState(false);
328
+ const [pos, setPos] = React25__default.useState(null);
329
+ const [active, setActive] = React25__default.useState(-1);
330
+ const wrapRef = React25__default.useRef(null);
331
+ const menuRef = React25__default.useRef(null);
332
+ const typeBufRef = React25__default.useRef("");
333
+ const typeTimerRef = React25__default.useRef(null);
334
+ const menuId = React25__default.useId();
333
335
  const headerId = `${menuId}-header`;
334
336
  const controlled = openProp !== void 0;
335
337
  const open = controlled ? !!openProp : openState;
336
- const openRef = React24__default.useRef(open);
338
+ const openRef = React25__default.useRef(open);
337
339
  openRef.current = open;
338
- const controlledRef = React24__default.useRef(controlled);
340
+ const controlledRef = React25__default.useRef(controlled);
339
341
  controlledRef.current = controlled;
340
- const onOpenChangeRef = React24__default.useRef(onOpenChange);
342
+ const onOpenChangeRef = React25__default.useRef(onOpenChange);
341
343
  onOpenChangeRef.current = onOpenChange;
342
- const setOpen = React24__default.useCallback((next) => {
344
+ const setOpen = React25__default.useCallback((next) => {
343
345
  const value = typeof next === "function" ? next(openRef.current) : next;
344
346
  if (value === openRef.current) return;
345
347
  if (!controlledRef.current) setOpenState(value);
@@ -347,7 +349,7 @@ function Menu({
347
349
  }, []);
348
350
  const RD = { createPortal: typeof createPortal === "function" ? createPortal : typeof window !== "undefined" && window.ReactDOM && window.ReactDOM.createPortal };
349
351
  const interactiveIdx = items.map((it, i) => !it.separator && !it.heading ? i : -1).filter((i) => i >= 0);
350
- const place = React24__default.useCallback(() => {
352
+ const place = React25__default.useCallback(() => {
351
353
  const t = wrapRef.current;
352
354
  if (!t) return;
353
355
  const r = t.getBoundingClientRect();
@@ -362,7 +364,7 @@ function Menu({
362
364
  const avail = (flip ? r.top : vh - r.bottom) - gap - M;
363
365
  setPos({ top, bottom, left, width: w, flip, maxHeight: Math.max(120, avail) });
364
366
  }, [align, width]);
365
- React24__default.useEffect(() => {
367
+ React25__default.useEffect(() => {
366
368
  if (!open) return;
367
369
  place();
368
370
  const onMove = () => place();
@@ -380,7 +382,7 @@ function Menu({
380
382
  document.removeEventListener("mousedown", onDown);
381
383
  };
382
384
  }, [open, place]);
383
- React24__default.useEffect(() => {
385
+ React25__default.useEffect(() => {
384
386
  if (open) {
385
387
  setRender(true);
386
388
  return;
@@ -397,7 +399,7 @@ function Menu({
397
399
  if (e.key === "Enter" || e.key === " " || e.key === "ArrowDown") {
398
400
  e.preventDefault();
399
401
  setOpen(true);
400
- setActive(-1);
402
+ setActive(interactiveIdx[0] ?? -1);
401
403
  }
402
404
  return;
403
405
  }
@@ -418,6 +420,24 @@ function Menu({
418
420
  if (next < 0) next = list.length - 1;
419
421
  if (next >= list.length) next = 0;
420
422
  setActive(list[next]);
423
+ } else if (e.key === "PageDown" || e.key === "PageUp") {
424
+ e.preventDefault();
425
+ const list = interactiveIdx;
426
+ if (list.length) {
427
+ const curPos = Math.max(0, list.indexOf(active));
428
+ const next = Math.min(list.length - 1, Math.max(0, curPos + (e.key === "PageDown" ? 5 : -5)));
429
+ setActive(list[next]);
430
+ }
431
+ } else if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
432
+ typeBufRef.current += e.key.toLowerCase();
433
+ clearTimeout(typeTimerRef.current);
434
+ typeTimerRef.current = setTimeout(() => {
435
+ typeBufRef.current = "";
436
+ }, 500);
437
+ const b = typeBufRef.current;
438
+ const labelOf = (i) => String(items[i]?.label ?? "").toLowerCase();
439
+ const hit = interactiveIdx.find((i) => labelOf(i).startsWith(b));
440
+ if (hit != null) setActive(hit);
421
441
  } else if (e.key === "Home" || e.key === "End") {
422
442
  e.preventDefault();
423
443
  const list = interactiveIdx;
@@ -445,12 +465,13 @@ function Menu({
445
465
  "data-align": align,
446
466
  "data-flip": pos.flip || void 0,
447
467
  role: "menu",
468
+ "aria-orientation": "vertical",
448
469
  "aria-label": ariaLabel,
449
470
  "aria-labelledby": ariaLabelledby ?? (ariaLabel ? void 0 : header ? headerId : void 0),
450
471
  style: { top: pos.top, bottom: pos.bottom, left: pos.left, minWidth: pos.width, width: pos.width, maxHeight: pos.maxHeight },
451
472
  children: [
452
473
  __twcStyles,
453
- header ? /* @__PURE__ */ jsx("div", { className: "twc-menu__header", id: headerId, children: header }) : null,
474
+ header ? /* @__PURE__ */ jsx("div", { className: "twc-menu__header", id: headerId, role: "presentation", children: header }) : null,
454
475
  items.map((it, i) => {
455
476
  if (it.separator) return /* @__PURE__ */ jsx("div", { className: "twc-menu__sep", role: "separator" }, `s${i}`);
456
477
  if (it.label && it.heading) return /* @__PURE__ */ jsx("div", { className: "twc-menu__label", children: it.label }, `h${i}`);
@@ -484,7 +505,7 @@ function Menu({
484
505
  }
485
506
  ) : null;
486
507
  const activeDescId = open && active >= 0 ? `${menuId}-item-${active}` : void 0;
487
- const triggerEl = React24__default.isValidElement(trigger) ? React24__default.cloneElement(trigger, {
508
+ const triggerEl = React25__default.isValidElement(trigger) ? React25__default.cloneElement(trigger, {
488
509
  onClick: (e) => {
489
510
  trigger.props.onClick?.(e);
490
511
  toggle();
@@ -624,8 +645,9 @@ function Badge({
624
645
  }
625
646
  var BREADCRUMB_CSS = `
626
647
  .twc-breadcrumb { display: flex; align-items: center; flex-wrap: wrap; gap: 2px; font-family: var(--font-sans); font-size: var(--text-sm); }
627
- .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); }
628
- .twc-breadcrumb__item:hover:not([aria-current]) { color: var(--color-text); background: var(--color-surface-sunken); }
648
+ .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); }
649
+ a.twc-breadcrumb__item, button.twc-breadcrumb__item { cursor: pointer; }
650
+ a.twc-breadcrumb__item:hover, button.twc-breadcrumb__item:hover { color: var(--color-text); background: var(--color-surface-sunken); }
629
651
  .twc-breadcrumb__item[aria-current="page"] { color: var(--color-text); font-weight: var(--font-semibold); cursor: default; }
630
652
  .twc-breadcrumb__item svg { width: 15px; height: 15px; flex: none; }
631
653
  .twc-breadcrumb__sep { display: inline-flex; align-items: center; color: var(--color-text-subtle); pointer-events: none; }
@@ -648,7 +670,7 @@ function Breadcrumb({
648
670
  ...rest
649
671
  }) {
650
672
  const __twcStyles = useScopedStyles("twc-breadcrumb-styles", BREADCRUMB_CSS);
651
- const [expanded, setExpanded] = React24__default.useState(false);
673
+ const [expanded, setExpanded] = React25__default.useState(false);
652
674
  const sep = separator || /* @__PURE__ */ jsx(ChevronSep, {});
653
675
  let display = items;
654
676
  if (maxItems > 0 && items.length > maxItems && !expanded) {
@@ -661,7 +683,7 @@ function Breadcrumb({
661
683
  __twcStyles,
662
684
  display.map((it, i) => {
663
685
  const last = i === display.length - 1;
664
- return /* @__PURE__ */ jsxs(React24__default.Fragment, { children: [
686
+ return /* @__PURE__ */ jsxs(React25__default.Fragment, { children: [
665
687
  it.ellipsis ? /* @__PURE__ */ jsx("button", { className: "twc-breadcrumb__ellipsis", "aria-label": "Show more", onClick: () => setExpanded(true), children: "\u2026" }) : last ? /* @__PURE__ */ jsxs(
666
688
  "span",
667
689
  {
@@ -673,17 +695,18 @@ function Breadcrumb({
673
695
  it.label
674
696
  ]
675
697
  }
676
- ) : /* @__PURE__ */ jsxs(
677
- "a",
678
- {
679
- className: "twc-breadcrumb__item",
680
- href: safeHref2(it.href) || "#",
681
- onClick: it.onClick,
682
- children: [
683
- it.icon ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", style: { display: "inline-flex" }, children: it.icon }) : null,
684
- it.label
685
- ]
686
- }
698
+ ) : safeHref2(it.href) ? /* @__PURE__ */ jsxs("a", { className: "twc-breadcrumb__item", href: safeHref2(it.href), onClick: it.onClick, children: [
699
+ it.icon ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", style: { display: "inline-flex" }, children: it.icon }) : null,
700
+ it.label
701
+ ] }) : it.onClick ? /* @__PURE__ */ jsxs("button", { type: "button", className: "twc-breadcrumb__item", onClick: it.onClick, children: [
702
+ it.icon ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", style: { display: "inline-flex" }, children: it.icon }) : null,
703
+ it.label
704
+ ] }) : (
705
+ // #46: no href and no onClick → inert plain-text crumb (not a fake <a href="#">).
706
+ /* @__PURE__ */ jsxs("span", { className: "twc-breadcrumb__item", children: [
707
+ it.icon ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", style: { display: "inline-flex" }, children: it.icon }) : null,
708
+ it.label
709
+ ] })
687
710
  ),
688
711
  !last ? /* @__PURE__ */ jsx("span", { className: "twc-breadcrumb__sep", "aria-hidden": "true", children: sep }) : null
689
712
  ] }, i);
@@ -802,7 +825,7 @@ function Button({
802
825
  ...rest
803
826
  }) {
804
827
  const __twcStyles = useScopedStyles("twc-btn-styles", CSS2);
805
- const [ripples, setRipples] = React24__default.useState([]);
828
+ const [ripples, setRipples] = React25__default.useState([]);
806
829
  const Tag2 = as;
807
830
  const inert = Tag2 === "a" && (disabled || loading);
808
831
  function handleClick(e) {
@@ -878,7 +901,9 @@ var CARD_CSS = `
878
901
  .twc-card[data-pad="md"] { padding: var(--space-5); }
879
902
  .twc-card[data-pad="lg"] { padding: var(--space-6); }
880
903
  .twc-card[data-pad="none"] { padding: 0; }
881
- .twc-card__header { display: flex; flex-direction: column; gap: 2px; margin-bottom: var(--space-3); }
904
+ .twc-card__header { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-3); margin-bottom: var(--space-3); }
905
+ .twc-card__heading { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
906
+ .twc-card__actions { flex: none; display: flex; align-items: center; gap: var(--space-2); }
882
907
  .twc-card__title { font-size: var(--text-lg); font-weight: var(--font-bold); letter-spacing: -0.01em; }
883
908
  .twc-card__subtitle { font-size: var(--text-sm); color: var(--color-text-muted); }
884
909
  .twc-card__body { font-size: var(--text-sm); color: var(--color-text-muted); line-height: var(--leading-normal); }
@@ -888,6 +913,7 @@ function Card({
888
913
  children,
889
914
  title,
890
915
  subtitle,
916
+ actions,
891
917
  footer,
892
918
  variant = "elevated",
893
919
  padding = "md",
@@ -908,9 +934,12 @@ function Card({
908
934
  ...rest,
909
935
  children: [
910
936
  __twcStyles,
911
- title || subtitle ? /* @__PURE__ */ jsxs("div", { className: "twc-card__header", children: [
912
- title ? /* @__PURE__ */ jsx("div", { className: "twc-card__title", children: title }) : null,
913
- subtitle ? /* @__PURE__ */ jsx("div", { className: "twc-card__subtitle", children: subtitle }) : null
937
+ title || subtitle || actions ? /* @__PURE__ */ jsxs("div", { className: "twc-card__header", children: [
938
+ /* @__PURE__ */ jsxs("div", { className: "twc-card__heading", children: [
939
+ title ? /* @__PURE__ */ jsx("div", { className: "twc-card__title", children: title }) : null,
940
+ subtitle ? /* @__PURE__ */ jsx("div", { className: "twc-card__subtitle", children: subtitle }) : null
941
+ ] }),
942
+ actions ? /* @__PURE__ */ jsx("div", { className: "twc-card__actions", children: actions }) : null
914
943
  ] }) : null,
915
944
  children ? /* @__PURE__ */ jsx("div", { className: "twc-card__body", children }) : null,
916
945
  footer ? /* @__PURE__ */ jsx("div", { className: "twc-card__footer", children: footer }) : null
@@ -970,14 +999,14 @@ function Carousel({
970
999
  ...rest
971
1000
  }) {
972
1001
  const __twcStyles = useScopedStyles("twc-carousel-styles", CAROUSEL_CSS);
973
- const slides = React24__default.Children.toArray(children);
1002
+ const slides = React25__default.Children.toArray(children);
974
1003
  const count = slides.length;
975
- const [internal, setInternal] = React24__default.useState(defaultIndex);
1004
+ const [internal, setInternal] = React25__default.useState(defaultIndex);
976
1005
  const index = indexProp !== void 0 ? indexProp : internal;
977
- const [paused, setPaused] = React24__default.useState(false);
978
- const [userPaused, setUserPaused] = React24__default.useState(false);
979
- const [prefersReduced, setPrefersReduced] = React24__default.useState(false);
980
- React24__default.useEffect(() => {
1006
+ const [paused, setPaused] = React25__default.useState(false);
1007
+ const [userPaused, setUserPaused] = React25__default.useState(false);
1008
+ const [prefersReduced, setPrefersReduced] = React25__default.useState(false);
1009
+ React25__default.useEffect(() => {
981
1010
  if (typeof window === "undefined" || !window.matchMedia) return void 0;
982
1011
  const mql = window.matchMedia("(prefers-reduced-motion: reduce)");
983
1012
  const on = () => setPrefersReduced(mql.matches);
@@ -985,15 +1014,15 @@ function Carousel({
985
1014
  mql.addEventListener("change", on);
986
1015
  return () => mql.removeEventListener("change", on);
987
1016
  }, []);
988
- const setIndex = React24__default.useCallback((i) => {
1017
+ const setIndex = React25__default.useCallback((i) => {
989
1018
  if (indexProp === void 0) setInternal(i);
990
1019
  onIndexChange?.(i);
991
1020
  }, [indexProp, onIndexChange]);
992
- const go = React24__default.useCallback((i) => {
1021
+ const go = React25__default.useCallback((i) => {
993
1022
  setIndex(loop ? (i + count) % count : Math.min(Math.max(i, 0), count - 1));
994
1023
  }, [count, loop, setIndex]);
995
1024
  const autoRotating = autoPlay && !paused && !userPaused && !prefersReduced && count > 1;
996
- React24__default.useEffect(() => {
1025
+ React25__default.useEffect(() => {
997
1026
  if (!autoRotating) return void 0;
998
1027
  const t = setInterval(() => {
999
1028
  const next = loop ? (index + 1) % count : index + 1 >= count ? index : index + 1;
@@ -1088,7 +1117,7 @@ function Chart({
1088
1117
  ...rest
1089
1118
  }) {
1090
1119
  const __twcStyles = useScopedStyles("twc-chart-styles", CHART_CSS);
1091
- const uid = React24__default.useId();
1120
+ const uid = React25__default.useId();
1092
1121
  const tableId = tableFallback ? `${uid}-table` : void 0;
1093
1122
  const keys = series || ["value"];
1094
1123
  const palette = colors && colors.length ? colors : SERIES_COLORS;
@@ -1180,6 +1209,15 @@ function shortNum(v) {
1180
1209
  if (v >= 1e3) return (v / 1e3).toFixed(v % 1e3 ? 1 : 0) + "k";
1181
1210
  return String(Math.round(v));
1182
1211
  }
1212
+
1213
+ // components/_warn.js
1214
+ var seen = /* @__PURE__ */ new Set();
1215
+ function warnOnce(key, message) {
1216
+ if (typeof process !== "undefined" && process.env && process.env.NODE_ENV === "production") return;
1217
+ if (seen.has(key)) return;
1218
+ seen.add(key);
1219
+ if (typeof console !== "undefined" && console.warn) console.warn(message);
1220
+ }
1183
1221
  function Checkbox({
1184
1222
  label,
1185
1223
  description,
@@ -1235,14 +1273,18 @@ function Checkbox({
1235
1273
  .twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
1236
1274
  .twc-field__error { font-size: var(--text-xs); color: var(--color-danger-subtle-fg); font-weight: var(--font-medium); }
1237
1275
  `);
1238
- const autoId = React24__default.useId();
1276
+ const autoId = React25__default.useId();
1239
1277
  const fieldId = id || autoId;
1240
1278
  const errId = `${fieldId}-error`;
1241
1279
  const invalid = Boolean(error);
1242
- const ref = React24__default.useRef(null);
1243
- React24__default.useEffect(() => {
1244
- if (ref.current) ref.current.indeterminate = indeterminate;
1245
- }, [indeterminate]);
1280
+ const ref = React25__default.useRef(null);
1281
+ const mixed = indeterminate && !checked;
1282
+ React25__default.useEffect(() => {
1283
+ if (ref.current) ref.current.indeterminate = mixed;
1284
+ }, [mixed]);
1285
+ if (process.env.NODE_ENV !== "production" && !label && !description && !rest["aria-label"] && !rest["aria-labelledby"]) {
1286
+ 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).");
1287
+ }
1246
1288
  const describedBy = [rest["aria-describedby"], error ? errId : null].filter(Boolean).join(" ") || void 0;
1247
1289
  const control = /* @__PURE__ */ 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: [
1248
1290
  __twcStyles,
@@ -1258,12 +1300,14 @@ function Checkbox({
1258
1300
  disabled,
1259
1301
  onChange,
1260
1302
  ...rest,
1303
+ required: required || void 0,
1261
1304
  "aria-required": required || void 0,
1305
+ "aria-checked": mixed ? "mixed" : void 0,
1262
1306
  "aria-invalid": invalid || void 0,
1263
1307
  "aria-describedby": describedBy
1264
1308
  }
1265
1309
  ),
1266
- /* @__PURE__ */ jsx("span", { className: "twc-check__box", "aria-hidden": "true", children: /* @__PURE__ */ 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__ */ jsx("path", { d: "M5 12h14" }) : /* @__PURE__ */ jsx("path", { d: "M20 6 9 17l-5-5" }) }) }),
1310
+ /* @__PURE__ */ jsx("span", { className: "twc-check__box", "aria-hidden": "true", children: /* @__PURE__ */ 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__ */ jsx("path", { d: "M5 12h14" }) : /* @__PURE__ */ jsx("path", { d: "M20 6 9 17l-5-5" }) }) }),
1267
1311
  label || description ? /* @__PURE__ */ jsxs("span", { className: "twc-check__text", children: [
1268
1312
  label ? /* @__PURE__ */ jsx("span", { className: "twc-check__label", children: label }) : null,
1269
1313
  description ? /* @__PURE__ */ jsx("span", { className: "twc-check__desc", children: description }) : null
@@ -1319,6 +1363,7 @@ var COLORPICKER_CSS = `
1319
1363
  .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); }
1320
1364
  .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); }
1321
1365
  .twc-cp__hex:focus { border-color: var(--_accent); box-shadow: var(--_ring); }
1366
+ .twc-cp__hex[data-invalid="true"] { border-color: var(--color-danger); }
1322
1367
  .twc-cp__presets { display: flex; flex-wrap: wrap; gap: 5px; margin-top: var(--space-3); }
1323
1368
  .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;
1324
1369
  transition: transform var(--duration-fast) var(--ease-spring); }
@@ -1377,20 +1422,20 @@ function ColorPicker({
1377
1422
  }) {
1378
1423
  const __twcStyles0 = useScopedStyles("twc-field-styles", FIELD_CSS);
1379
1424
  const __twcStyles1 = useScopedStyles("twc-colorpicker-styles", COLORPICKER_CSS);
1380
- const [internal, setInternal] = React24__default.useState(defaultValue);
1425
+ const [internal, setInternal] = React25__default.useState(defaultValue);
1381
1426
  const hex = (value !== void 0 ? value : internal) || "#000000";
1382
- const lastValidRef = React24__default.useRef(hex);
1427
+ const lastValidRef = React25__default.useRef(hex);
1383
1428
  if (/^#[0-9a-f]{6}$/i.test(hex)) lastValidRef.current = hex;
1384
- const [open, setOpen] = React24__default.useState(false);
1385
- const [hsv, setHsv] = React24__default.useState(() => hexToHsv(hex) || { h: 239, s: 60, v: 94 });
1386
- const [coords, setCoords] = React24__default.useState(null);
1387
- const wrapRef = React24__default.useRef(null);
1388
- const triggerRef = React24__default.useRef(null);
1389
- const popRef = React24__default.useRef(null);
1390
- const areaRef = React24__default.useRef(null);
1391
- const hueRef = React24__default.useRef(null);
1429
+ const [open, setOpen] = React25__default.useState(false);
1430
+ const [hsv, setHsv] = React25__default.useState(() => hexToHsv(hex) || { h: 239, s: 60, v: 94 });
1431
+ const [coords, setCoords] = React25__default.useState(null);
1432
+ const wrapRef = React25__default.useRef(null);
1433
+ const triggerRef = React25__default.useRef(null);
1434
+ const popRef = React25__default.useRef(null);
1435
+ const areaRef = React25__default.useRef(null);
1436
+ const hueRef = React25__default.useRef(null);
1392
1437
  const POP_W = 240;
1393
- const place = React24__default.useCallback(() => {
1438
+ const place = React25__default.useCallback(() => {
1394
1439
  const el = triggerRef.current;
1395
1440
  if (!el) return;
1396
1441
  const r = el.getBoundingClientRect();
@@ -1401,7 +1446,7 @@ function ColorPicker({
1401
1446
  left = Math.max(8, left);
1402
1447
  setCoords({ left, top: flip ? void 0 : Math.round(r.bottom + 6), bottom: flip ? Math.round(vh - r.top + 6) : void 0 });
1403
1448
  }, []);
1404
- React24__default.useEffect(() => {
1449
+ React25__default.useEffect(() => {
1405
1450
  if (!open) return void 0;
1406
1451
  place();
1407
1452
  const onMove = () => place();
@@ -1412,17 +1457,17 @@ function ColorPicker({
1412
1457
  window.removeEventListener("resize", onMove);
1413
1458
  };
1414
1459
  }, [open, place]);
1415
- const labelId = React24__default.useId();
1460
+ const labelId = React25__default.useId();
1416
1461
  const descId = `${labelId}-desc`;
1417
1462
  const invalid = Boolean(error);
1418
- React24__default.useEffect(() => {
1463
+ React25__default.useEffect(() => {
1419
1464
  if (disabled) setOpen(false);
1420
1465
  }, [disabled]);
1421
- React24__default.useEffect(() => {
1466
+ React25__default.useEffect(() => {
1422
1467
  const v = hexToHsv(hex);
1423
1468
  if (v && hsvToHex(v.h, v.s, v.v).toLowerCase() !== hsvToHex(hsv.h, hsv.s, hsv.v).toLowerCase()) setHsv(v);
1424
1469
  }, [hex]);
1425
- React24__default.useEffect(() => {
1470
+ React25__default.useEffect(() => {
1426
1471
  if (!open) return;
1427
1472
  const onDown = (e) => {
1428
1473
  if (wrapRef.current && wrapRef.current.contains(e.target)) return;
@@ -1439,8 +1484,8 @@ function ColorPicker({
1439
1484
  document.removeEventListener("keydown", onKey);
1440
1485
  };
1441
1486
  }, [open]);
1442
- const prevOpen = React24__default.useRef(false);
1443
- React24__default.useEffect(() => {
1487
+ const prevOpen = React25__default.useRef(false);
1488
+ React25__default.useEffect(() => {
1444
1489
  let id;
1445
1490
  if (!prevOpen.current && open) {
1446
1491
  id = setTimeout(() => areaRef.current?.focus({ preventScroll: true }), 20);
@@ -1594,6 +1639,8 @@ function ColorPicker({
1594
1639
  value: hex,
1595
1640
  maxLength: 7,
1596
1641
  "aria-label": "Hex color",
1642
+ "aria-invalid": !/^#[0-9a-f]{6}$/i.test(hex) || void 0,
1643
+ "data-invalid": !/^#[0-9a-f]{6}$/i.test(hex) || void 0,
1597
1644
  onChange: (e) => {
1598
1645
  let v = e.target.value;
1599
1646
  if (!v.startsWith("#")) v = "#" + v;
@@ -1704,6 +1751,9 @@ var COMBO_CSS = `
1704
1751
  .twc-opt__check { flex: none; color: var(--color-primary); display: inline-flex; }
1705
1752
  .twc-opt__check svg { width: 16px; height: 16px; }
1706
1753
  .twc-pop__empty { padding: 14px 12px; text-align: center; font-size: var(--text-sm); color: var(--color-text-subtle); }
1754
+ .twc-opt[data-disabled="true"] { opacity: 0.45; cursor: not-allowed; pointer-events: none; }
1755
+ .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); }
1756
+ .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; }
1707
1757
  `;
1708
1758
  function toOpt(o) {
1709
1759
  return typeof o === "string" ? { value: o, label: o } : o;
@@ -1753,6 +1803,11 @@ function Combobox({
1753
1803
  placement = "bottom",
1754
1804
  portal = true,
1755
1805
  minWidth = 0,
1806
+ onInputChange,
1807
+ filter,
1808
+ loading = false,
1809
+ emptyText = "No results found",
1810
+ name,
1756
1811
  id,
1757
1812
  className = "",
1758
1813
  onFocus,
@@ -1760,29 +1815,38 @@ function Combobox({
1760
1815
  ...rest
1761
1816
  }) {
1762
1817
  const __twcStyles = useScopedStyles("twc-combo-styles", COMBO_CSS);
1763
- const groups = React24__default.useMemo(() => normalizeGroups(options), [options]);
1764
- const flat = React24__default.useMemo(() => groups.flatMap((g) => g.options), [groups]);
1765
- const autoId = React24__default.useId();
1818
+ const groups = React25__default.useMemo(() => normalizeGroups(options), [options]);
1819
+ const flat = React25__default.useMemo(() => groups.flatMap((g) => g.options), [groups]);
1820
+ const autoId = React25__default.useId();
1766
1821
  const fieldId = id || autoId;
1767
- const [internal, setInternal] = React24__default.useState(defaultValue);
1822
+ const [internal, setInternal] = React25__default.useState(defaultValue);
1768
1823
  const current = value !== void 0 ? value : internal;
1769
1824
  const selected = flat.find((o) => o.value === current);
1770
- const [open, setOpen] = React24__default.useState(false);
1771
- const [query, setQuery] = React24__default.useState("");
1772
- const [active, setActive] = React24__default.useState(0);
1773
- const [coords, setCoords] = React24__default.useState(null);
1774
- const wrapRef = React24__default.useRef(null);
1775
- const controlRef = React24__default.useRef(null);
1776
- const inputRef = React24__default.useRef(null);
1777
- const popRef = React24__default.useRef(null);
1778
- const listRef = React24__default.useRef(null);
1779
- const activeRef = React24__default.useRef(null);
1780
- const fGroups = React24__default.useMemo(
1781
- () => groups.map((g) => ({ group: g.group, options: g.options.filter((o) => matches(o, query.trim())) })).filter((g) => g.options.length),
1782
- [groups, query]
1783
- );
1784
- const visible = React24__default.useMemo(() => fGroups.flatMap((g) => g.options), [fGroups]);
1785
- const place = React24__default.useCallback(() => {
1825
+ const [open, setOpen] = React25__default.useState(false);
1826
+ const [query, setQuery] = React25__default.useState("");
1827
+ const [active, setActive] = React25__default.useState(0);
1828
+ const [coords, setCoords] = React25__default.useState(null);
1829
+ const wrapRef = React25__default.useRef(null);
1830
+ const controlRef = React25__default.useRef(null);
1831
+ const inputRef = React25__default.useRef(null);
1832
+ const popRef = React25__default.useRef(null);
1833
+ const listRef = React25__default.useRef(null);
1834
+ const activeRef = React25__default.useRef(null);
1835
+ const fGroups = React25__default.useMemo(() => {
1836
+ const q = query.trim();
1837
+ const fn = filter === false ? null : typeof filter === "function" ? filter : matches;
1838
+ return groups.map((g) => ({ group: g.group, options: fn ? g.options.filter((o) => fn(o, q)) : g.options })).filter((g) => g.options.length);
1839
+ }, [groups, query, filter]);
1840
+ const visible = React25__default.useMemo(() => fGroups.flatMap((g) => g.options), [fGroups]);
1841
+ const nextEnabled = (from, dir) => {
1842
+ let i = from + dir;
1843
+ while (i >= 0 && i < visible.length) {
1844
+ if (!visible[i]?.disabled) return i;
1845
+ i += dir;
1846
+ }
1847
+ return from;
1848
+ };
1849
+ const place = React25__default.useCallback(() => {
1786
1850
  const el = controlRef.current;
1787
1851
  if (!el) return;
1788
1852
  const r = el.getBoundingClientRect();
@@ -1797,7 +1861,7 @@ function Combobox({
1797
1861
  flip
1798
1862
  });
1799
1863
  }, [placement, minWidth]);
1800
- React24__default.useEffect(() => {
1864
+ React25__default.useEffect(() => {
1801
1865
  if (!open || !portal) return;
1802
1866
  place();
1803
1867
  const onMove = () => place();
@@ -1808,7 +1872,7 @@ function Combobox({
1808
1872
  window.removeEventListener("resize", onMove);
1809
1873
  };
1810
1874
  }, [open, portal, place]);
1811
- React24__default.useEffect(() => {
1875
+ React25__default.useEffect(() => {
1812
1876
  if (!open) return;
1813
1877
  const onDown = (e) => {
1814
1878
  if (wrapRef.current && wrapRef.current.contains(e.target)) return;
@@ -1818,10 +1882,15 @@ function Combobox({
1818
1882
  document.addEventListener("mousedown", onDown);
1819
1883
  return () => document.removeEventListener("mousedown", onDown);
1820
1884
  }, [open]);
1821
- React24__default.useEffect(() => {
1885
+ React25__default.useEffect(() => {
1822
1886
  setActive(0);
1823
1887
  }, [query]);
1824
- React24__default.useEffect(() => {
1888
+ React25__default.useEffect(() => {
1889
+ if (!open) return;
1890
+ const idx = flat.findIndex((o) => o.value === current);
1891
+ setActive(idx >= 0 ? idx : 0);
1892
+ }, [open]);
1893
+ React25__default.useEffect(() => {
1825
1894
  if (open) ensureVisible(listRef.current, activeRef.current);
1826
1895
  }, [active, open]);
1827
1896
  function openMenu() {
@@ -1834,6 +1903,8 @@ function Combobox({
1834
1903
  setQuery("");
1835
1904
  }
1836
1905
  function commit(v) {
1906
+ const o = flat.find((x) => x.value === v);
1907
+ if (o && o.disabled) return;
1837
1908
  if (value === void 0) setInternal(v);
1838
1909
  onChange?.(v);
1839
1910
  close();
@@ -1845,17 +1916,20 @@ function Combobox({
1845
1916
  openMenu();
1846
1917
  return;
1847
1918
  }
1848
- setActive((a) => Math.min(a + 1, visible.length - 1));
1919
+ setActive((a) => nextEnabled(a, 1));
1849
1920
  } else if (e.key === "ArrowUp") {
1850
1921
  e.preventDefault();
1851
- setActive((a) => Math.max(a - 1, 0));
1922
+ setActive((a) => nextEnabled(a, -1));
1852
1923
  } else if (e.key === "Enter") {
1853
1924
  e.preventDefault();
1854
1925
  if (open && visible[active]) commit(visible[active].value);
1855
1926
  } else if (e.key === "Escape") {
1856
1927
  close();
1857
1928
  inputRef.current?.blur();
1858
- } else if (e.key === "Backspace" && !query && selected) ;
1929
+ } else if (e.key === "Backspace" && !query && selected) {
1930
+ e.preventDefault();
1931
+ commit(null);
1932
+ }
1859
1933
  }
1860
1934
  const displayValue = open ? query : selected ? selected.label : "";
1861
1935
  const listboxId = `${fieldId}-listbox`;
@@ -1864,7 +1938,10 @@ function Combobox({
1864
1938
  const descId = `${fieldId}-desc`;
1865
1939
  const describedBy = error || hint ? descId : void 0;
1866
1940
  let counter = -1;
1867
- const popInner = /* @__PURE__ */ jsx("div", { className: "twc-pop__list", ref: listRef, children: visible.length === 0 ? /* @__PURE__ */ jsx("div", { className: "twc-pop__empty", children: "No results found" }) : fGroups.map((g, gi) => /* @__PURE__ */ jsxs(React24__default.Fragment, { children: [
1941
+ const popInner = /* @__PURE__ */ jsx("div", { className: "twc-pop__list", ref: listRef, children: loading ? /* @__PURE__ */ jsxs("div", { className: "twc-pop__loading", role: "status", children: [
1942
+ /* @__PURE__ */ jsx("span", { className: "twc-pop__spinner", "aria-hidden": "true" }),
1943
+ "Loading\u2026"
1944
+ ] }) : visible.length === 0 ? /* @__PURE__ */ jsx("div", { className: "twc-pop__empty", children: emptyText }) : fGroups.map((g, gi) => /* @__PURE__ */ jsxs(React25__default.Fragment, { children: [
1868
1945
  g.group ? /* @__PURE__ */ jsx("div", { className: "twc-pop__group", children: g.group }) : null,
1869
1946
  g.options.map((o) => {
1870
1947
  counter += 1;
@@ -1879,9 +1956,14 @@ function Combobox({
1879
1956
  role: "option",
1880
1957
  "aria-selected": isSel,
1881
1958
  ref: idx === active ? activeRef : null,
1959
+ disabled: o.disabled || void 0,
1960
+ "aria-disabled": o.disabled || void 0,
1961
+ "data-disabled": o.disabled || void 0,
1882
1962
  "data-selected": isSel || void 0,
1883
1963
  "data-active": idx === active || void 0,
1884
- onMouseEnter: () => setActive(idx),
1964
+ onMouseEnter: () => {
1965
+ if (!o.disabled) setActive(idx);
1966
+ },
1885
1967
  onMouseDown: (e) => e.preventDefault(),
1886
1968
  onClick: () => commit(o.value),
1887
1969
  children: [
@@ -1909,7 +1991,7 @@ function Combobox({
1909
1991
  id: listboxId,
1910
1992
  role: "listbox",
1911
1993
  ref: popRef,
1912
- "data-placement": coords.flip ? "top" : void 0,
1994
+ "data-placement": coords.flip ? "top" : "bottom",
1913
1995
  style: { position: "fixed", left: coords.left, top: coords.top, bottom: coords.bottom, width: coords.width, right: "auto", zIndex: "var(--z-tooltip)" },
1914
1996
  children: popInner
1915
1997
  }
@@ -1922,6 +2004,7 @@ function Combobox({
1922
2004
  }
1923
2005
  return /* @__PURE__ */ jsxs("div", { className: `twc-field ${className}`, ref: wrapRef, children: [
1924
2006
  __twcStyles,
2007
+ name ? /* @__PURE__ */ jsx("input", { type: "hidden", name, value: current ?? "" }) : null,
1925
2008
  label ? /* @__PURE__ */ jsxs("label", { className: "twc-field__label", htmlFor: fieldId, children: [
1926
2009
  label,
1927
2010
  required ? /* @__PURE__ */ jsx("span", { className: "twc-field__req", children: "*" }) : null
@@ -1967,6 +2050,7 @@ function Combobox({
1967
2050
  onChange: (e) => {
1968
2051
  setQuery(e.target.value);
1969
2052
  setOpen(true);
2053
+ onInputChange?.(e.target.value);
1970
2054
  },
1971
2055
  onKeyDown: (e) => {
1972
2056
  onKeyDown?.(e);
@@ -2003,9 +2087,10 @@ function Combobox({
2003
2087
  ] });
2004
2088
  }
2005
2089
  var canUseDOM = typeof window !== "undefined" && typeof document !== "undefined";
2090
+ var useIsoLayoutEffect = canUseDOM ? React25__default.useLayoutEffect : React25__default.useEffect;
2006
2091
  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"])';
2007
2092
  function useFocusTrap(ref, active = true, { restoreFocus = true } = {}) {
2008
- React24__default.useEffect(() => {
2093
+ React25__default.useEffect(() => {
2009
2094
  if (!active || !canUseDOM) return void 0;
2010
2095
  const node = ref.current;
2011
2096
  if (!node) return void 0;
@@ -2016,7 +2101,7 @@ function useFocusTrap(ref, active = true, { restoreFocus = true } = {}) {
2016
2101
  if (restoreFocus && prevFocused && typeof prevFocused.focus === "function") prevFocused.focus();
2017
2102
  };
2018
2103
  }, [active]);
2019
- React24__default.useEffect(() => {
2104
+ React25__default.useEffect(() => {
2020
2105
  if (!active || !canUseDOM) return void 0;
2021
2106
  const node = ref.current;
2022
2107
  const onKey = (e) => {
@@ -2043,11 +2128,53 @@ function useFocusTrap(ref, active = true, { restoreFocus = true } = {}) {
2043
2128
  }, [active]);
2044
2129
  }
2045
2130
  function usePortal() {
2046
- return React24__default.useCallback((node) => {
2131
+ return React25__default.useCallback((node) => {
2047
2132
  if (typeof document === "undefined") return null;
2048
2133
  return typeof createPortal === "function" ? createPortal(node, document.body) : node;
2049
2134
  }, []);
2050
2135
  }
2136
+ var __scrollLockCount = 0;
2137
+ var __scrollLockSaved = { overflow: "", paddingRight: "" };
2138
+ function useScrollLock(locked = true) {
2139
+ useIsoLayoutEffect(() => {
2140
+ if (!canUseDOM || !locked) return void 0;
2141
+ if (__scrollLockCount === 0) {
2142
+ const b = document.body;
2143
+ __scrollLockSaved = { overflow: b.style.overflow, paddingRight: b.style.paddingRight };
2144
+ const gutter = window.innerWidth - document.documentElement.clientWidth;
2145
+ b.style.overflow = "hidden";
2146
+ if (gutter > 0) b.style.paddingRight = `${(parseFloat(getComputedStyle(b).paddingRight) || 0) + gutter}px`;
2147
+ }
2148
+ __scrollLockCount += 1;
2149
+ return () => {
2150
+ __scrollLockCount -= 1;
2151
+ if (__scrollLockCount === 0) {
2152
+ document.body.style.overflow = __scrollLockSaved.overflow;
2153
+ document.body.style.paddingRight = __scrollLockSaved.paddingRight;
2154
+ }
2155
+ };
2156
+ }, [locked]);
2157
+ }
2158
+ function useInertBackground(ref, active) {
2159
+ React25__default.useEffect(() => {
2160
+ if (!active || !canUseDOM) return void 0;
2161
+ const node = ref.current;
2162
+ if (!node) return void 0;
2163
+ const changed = [];
2164
+ Array.from(document.body.children).forEach((el) => {
2165
+ if (el.contains(node) || el.hasAttribute("aria-hidden") || el.inert) return;
2166
+ el.inert = true;
2167
+ el.setAttribute("aria-hidden", "true");
2168
+ changed.push(el);
2169
+ });
2170
+ return () => {
2171
+ changed.forEach((el) => {
2172
+ el.inert = false;
2173
+ el.removeAttribute("aria-hidden");
2174
+ });
2175
+ };
2176
+ }, [active]);
2177
+ }
2051
2178
  var COMMAND_CSS = `
2052
2179
  .twc-cmdk__overlay { position: fixed; inset: 0; z-index: var(--z-modal); background: var(--color-overlay); backdrop-filter: blur(3px);
2053
2180
  display: flex; align-items: flex-start; justify-content: center; padding-top: 12vh; }
@@ -2089,19 +2216,20 @@ function CommandPalette({
2089
2216
  commands,
2090
2217
  placeholder = "Type a command or search\u2026",
2091
2218
  emptyText = "No results found",
2219
+ searchLabel = "Command palette search",
2092
2220
  className = "",
2093
2221
  ...rest
2094
2222
  }) {
2095
2223
  const __twcStyles = useScopedStyles("twc-cmdk-styles", COMMAND_CSS);
2096
2224
  const renderPortal = usePortal();
2097
- const [query, setQuery] = React24__default.useState("");
2098
- const [active, setActive] = React24__default.useState(0);
2099
- const inputRef = React24__default.useRef(null);
2100
- const listRef = React24__default.useRef(null);
2101
- const paletteRef = React24__default.useRef(null);
2102
- const baseId = React24__default.useId();
2103
- const [mounted, setMounted] = React24__default.useState(open);
2104
- React24__default.useEffect(() => {
2225
+ const [query, setQuery] = React25__default.useState("");
2226
+ const [active, setActive] = React25__default.useState(0);
2227
+ const inputRef = React25__default.useRef(null);
2228
+ const listRef = React25__default.useRef(null);
2229
+ const paletteRef = React25__default.useRef(null);
2230
+ const baseId = React25__default.useId();
2231
+ const [mounted, setMounted] = React25__default.useState(open);
2232
+ React25__default.useEffect(() => {
2105
2233
  if (open) {
2106
2234
  setMounted(true);
2107
2235
  return;
@@ -2109,22 +2237,15 @@ function CommandPalette({
2109
2237
  const t = setTimeout(() => setMounted(false), 170);
2110
2238
  return () => clearTimeout(t);
2111
2239
  }, [open]);
2112
- React24__default.useEffect(() => {
2113
- if (!open) return void 0;
2114
- const prev = document.body.style.overflow;
2115
- document.body.style.overflow = "hidden";
2116
- return () => {
2117
- document.body.style.overflow = prev;
2118
- };
2119
- }, [open]);
2120
- React24__default.useEffect(() => {
2240
+ useScrollLock(open);
2241
+ React25__default.useEffect(() => {
2121
2242
  if (open) {
2122
2243
  setQuery("");
2123
2244
  setActive(0);
2124
2245
  }
2125
2246
  }, [open]);
2126
2247
  useFocusTrap(paletteRef, open && mounted);
2127
- React24__default.useEffect(() => {
2248
+ React25__default.useEffect(() => {
2128
2249
  if (!open) return void 0;
2129
2250
  const onKey = (e) => {
2130
2251
  if (e.key === "Escape") {
@@ -2136,7 +2257,7 @@ function CommandPalette({
2136
2257
  return () => document.removeEventListener("keydown", onKey);
2137
2258
  }, [open, onClose]);
2138
2259
  const q = query.trim().toLowerCase();
2139
- const filtered = React24__default.useMemo(() => commands.filter(
2260
+ const filtered = React25__default.useMemo(() => commands.filter(
2140
2261
  (c) => !q || c.label.toLowerCase().includes(q) || (c.keywords || "").toLowerCase().includes(q) || (c.group || "").toLowerCase().includes(q)
2141
2262
  ), [commands, q]);
2142
2263
  const groups = [];
@@ -2151,7 +2272,7 @@ function CommandPalette({
2151
2272
  g.items.push(c);
2152
2273
  flat.push(c);
2153
2274
  });
2154
- React24__default.useEffect(() => {
2275
+ React25__default.useEffect(() => {
2155
2276
  setActive(0);
2156
2277
  }, [query]);
2157
2278
  const run = (cmd) => {
@@ -2171,7 +2292,7 @@ function CommandPalette({
2171
2292
  if (cmd) run(cmd);
2172
2293
  }
2173
2294
  }
2174
- React24__default.useEffect(() => {
2295
+ React25__default.useEffect(() => {
2175
2296
  const node = listRef.current?.querySelector('[data-active="true"]');
2176
2297
  if (node) node.scrollIntoView({ block: "nearest" });
2177
2298
  }, [active]);
@@ -2199,7 +2320,8 @@ function CommandPalette({
2199
2320
  value: query,
2200
2321
  placeholder,
2201
2322
  role: "combobox",
2202
- "aria-expanded": "true",
2323
+ "aria-label": searchLabel,
2324
+ "aria-expanded": flat.length > 0 ? "true" : "false",
2203
2325
  "aria-controls": listId,
2204
2326
  "aria-activedescendant": activeId,
2205
2327
  onChange: (e) => setQuery(e.target.value),
@@ -2301,7 +2423,9 @@ var CUR_CSS = `
2301
2423
  .twc-cur:focus-within { border-color: var(--_accent); box-shadow: var(--_ring); }
2302
2424
  .twc-cur[data-invalid="true"] { border-color: var(--color-danger); }
2303
2425
  .twc-cur[data-invalid="true"]:focus-within { box-shadow: 0 0 0 var(--ring-width) color-mix(in srgb, var(--color-danger) 28%, transparent); }
2304
- .twc-cur[data-disabled="true"] { background: var(--color-surface-sunken); opacity: 0.7; }
2426
+ .twc-cur[data-disabled="true"] { background: var(--color-surface-sunken); opacity: 0.7; cursor: not-allowed; }
2427
+ .twc-cur[data-readonly="true"] { background: var(--color-surface-sunken); }
2428
+ .twc-cur__el:disabled { cursor: not-allowed; }
2305
2429
  .twc-cur__sym, .twc-cur__code {
2306
2430
  display: inline-flex; align-items: center; padding: 0 var(--space-3);
2307
2431
  background: var(--color-surface-sunken); color: var(--color-text-muted);
@@ -2344,18 +2468,29 @@ function Currency({
2344
2468
  const prec = precision != null ? precision : meta.precision;
2345
2469
  const sym = symbol != null ? symbol : meta.symbol;
2346
2470
  const cur = code != null ? code : meta.code;
2347
- const autoId = React24__default.useId();
2471
+ const autoId = React25__default.useId();
2348
2472
  const fieldId = id || autoId;
2349
2473
  const invalid = Boolean(error);
2350
2474
  const controlled = value !== void 0;
2351
- const [internal, setInternal] = React24__default.useState(() => clampPrecision(String(defaultValue ?? ""), prec));
2475
+ const [internal, setInternal] = React25__default.useState(() => clampPrecision(String(defaultValue ?? ""), prec));
2352
2476
  const shown = controlled ? clampPrecision(String(value ?? ""), prec) : internal;
2353
- const describedBy = [error ? `${fieldId}-error` : hint ? `${fieldId}-hint` : null, rest["aria-describedby"]].filter(Boolean).join(" ") || void 0;
2477
+ React25__default.useEffect(() => {
2478
+ if (!controlled) return;
2479
+ const raw = String(value ?? "");
2480
+ if (shown !== raw) {
2481
+ const n = Number(shown);
2482
+ onValueChange?.(shown === "" || Number.isNaN(n) ? null : n, shown);
2483
+ }
2484
+ }, [controlled, shown, value]);
2485
+ const descId = `${fieldId}-desc`;
2486
+ const describedBy = [error || hint ? descId : null, rest["aria-describedby"]].filter(Boolean).join(" ") || void 0;
2487
+ const ariaInvalid = invalid ? true : rest["aria-invalid"] ?? void 0;
2354
2488
  function handleChange(e) {
2355
2489
  const next = clampPrecision(e.target.value, prec);
2356
2490
  if (!controlled) setInternal(next);
2357
2491
  onChange?.(e);
2358
- onValueChange?.(next === "" ? null : Number(next), next);
2492
+ const n = Number(next);
2493
+ onValueChange?.(next === "" || Number.isNaN(n) ? null : n, next);
2359
2494
  }
2360
2495
  function handleBlur(e) {
2361
2496
  rest.onBlur?.(e);
@@ -2372,7 +2507,7 @@ function Currency({
2372
2507
  label,
2373
2508
  required ? /* @__PURE__ */ jsx("span", { className: "twc-field__req", children: "*" }) : null
2374
2509
  ] }) : null,
2375
- /* @__PURE__ */ jsxs("div", { className: "twc-cur", "data-size": size, "data-tone": tone, "data-invalid": invalid || void 0, "data-disabled": disabled || void 0, children: [
2510
+ /* @__PURE__ */ 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: [
2376
2511
  /* @__PURE__ */ jsx("span", { className: "twc-cur__sym", "aria-hidden": "true", children: sym }),
2377
2512
  /* @__PURE__ */ jsx(
2378
2513
  "input",
@@ -2385,16 +2520,16 @@ function Currency({
2385
2520
  placeholder,
2386
2521
  disabled,
2387
2522
  required: required || void 0,
2388
- "aria-invalid": invalid || void 0,
2389
2523
  ...rest,
2390
2524
  onChange: handleChange,
2391
2525
  onBlur: handleBlur,
2526
+ "aria-invalid": ariaInvalid,
2392
2527
  "aria-describedby": describedBy
2393
2528
  }
2394
2529
  ),
2395
2530
  /* @__PURE__ */ jsx("span", { className: "twc-cur__code", children: cur })
2396
2531
  ] }),
2397
- error ? /* @__PURE__ */ jsx("span", { id: `${fieldId}-error`, className: "twc-field__error", children: error }) : hint ? /* @__PURE__ */ jsx("span", { id: `${fieldId}-hint`, className: "twc-field__hint", children: hint }) : null
2532
+ error ? /* @__PURE__ */ jsx("span", { id: descId, className: "twc-field__error", children: error }) : hint ? /* @__PURE__ */ jsx("span", { id: descId, className: "twc-field__hint", children: hint }) : null
2398
2533
  ] });
2399
2534
  }
2400
2535
  var SELECT_CSS = `
@@ -2469,6 +2604,13 @@ var SELECT_CSS = `
2469
2604
  .twc-opt__check { flex: none; color: var(--color-primary); display: inline-flex; }
2470
2605
  .twc-opt__check svg { width: 16px; height: 16px; }
2471
2606
  .twc-pop__empty { padding: 14px 12px; text-align: center; font-size: var(--text-sm); color: var(--color-text-subtle); }
2607
+ .twc-opt[data-disabled="true"] { opacity: 0.45; cursor: not-allowed; pointer-events: none; }
2608
+ .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); }
2609
+ .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; }
2610
+ .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); }
2611
+ .twc-pop__search-clear:hover { background: var(--color-surface-sunken); color: var(--color-text); }
2612
+ .twc-pop__search-clear svg { width: 13px; height: 13px; }
2613
+ .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; }
2472
2614
  `;
2473
2615
  function toOpt2(o) {
2474
2616
  return typeof o === "string" ? { value: o, label: o } : o;
@@ -2515,6 +2657,10 @@ function Select({
2515
2657
  placement = "bottom",
2516
2658
  portal = true,
2517
2659
  minWidth = 0,
2660
+ matchTriggerWidth = true,
2661
+ loading = false,
2662
+ emptyText = "No results found",
2663
+ name,
2518
2664
  id,
2519
2665
  className = "",
2520
2666
  onClick,
@@ -2522,47 +2668,58 @@ function Select({
2522
2668
  ...rest
2523
2669
  }) {
2524
2670
  const __twcStyles = useScopedStyles("twc-select-styles", SELECT_CSS);
2525
- const groups = React24__default.useMemo(() => normalizeGroups2(options), [options]);
2526
- const flat = React24__default.useMemo(() => groups.flatMap((g) => g.options), [groups]);
2527
- const autoId = React24__default.useId();
2671
+ const bufferRef = React25__default.useRef("");
2672
+ const bufferTimerRef = React25__default.useRef(null);
2673
+ const groups = React25__default.useMemo(() => normalizeGroups2(options), [options]);
2674
+ const flat = React25__default.useMemo(() => groups.flatMap((g) => g.options), [groups]);
2675
+ const autoId = React25__default.useId();
2528
2676
  const fieldId = id || autoId;
2529
- const [internal, setInternal] = React24__default.useState(defaultValue);
2677
+ const [internal, setInternal] = React25__default.useState(defaultValue);
2530
2678
  const current = value !== void 0 ? value : internal;
2531
- const [open, setOpen] = React24__default.useState(false);
2532
- const [render, setRender] = React24__default.useState(false);
2533
- const [active, setActive] = React24__default.useState(0);
2534
- const [coords, setCoords] = React24__default.useState(null);
2535
- const [query, setQuery] = React24__default.useState("");
2536
- const wrapRef = React24__default.useRef(null);
2537
- const triggerRef = React24__default.useRef(null);
2538
- const popRef = React24__default.useRef(null);
2539
- const listRef = React24__default.useRef(null);
2540
- const activeRef = React24__default.useRef(null);
2541
- const searchRef = React24__default.useRef(null);
2679
+ const [open, setOpen] = React25__default.useState(false);
2680
+ const [render, setRender] = React25__default.useState(false);
2681
+ const [active, setActive] = React25__default.useState(0);
2682
+ const [coords, setCoords] = React25__default.useState(null);
2683
+ const [query, setQuery] = React25__default.useState("");
2684
+ const wrapRef = React25__default.useRef(null);
2685
+ const triggerRef = React25__default.useRef(null);
2686
+ const popRef = React25__default.useRef(null);
2687
+ const listRef = React25__default.useRef(null);
2688
+ const activeRef = React25__default.useRef(null);
2689
+ const searchRef = React25__default.useRef(null);
2542
2690
  const showSearch = searchable === void 0 ? flat.length > 5 : searchable;
2543
- const fGroups = React24__default.useMemo(() => {
2691
+ const fGroups = React25__default.useMemo(() => {
2544
2692
  const q = query.trim().toLowerCase();
2545
2693
  if (!q) return groups;
2546
2694
  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);
2547
2695
  }, [groups, query]);
2548
- const visible = React24__default.useMemo(() => fGroups.flatMap((g) => g.options), [fGroups]);
2696
+ const visible = React25__default.useMemo(() => fGroups.flatMap((g) => g.options), [fGroups]);
2549
2697
  const selected = flat.find((o) => o.value === current);
2550
- const place = React24__default.useCallback(() => {
2698
+ const nextEnabled = (from, dir) => {
2699
+ let i = from + dir;
2700
+ while (i >= 0 && i < visible.length) {
2701
+ if (!visible[i]?.disabled) return i;
2702
+ i += dir;
2703
+ }
2704
+ return from;
2705
+ };
2706
+ const place = React25__default.useCallback(() => {
2551
2707
  const el = triggerRef.current;
2552
2708
  if (!el) return;
2553
2709
  const r = el.getBoundingClientRect();
2554
2710
  const vh = window.innerHeight;
2555
2711
  const below = vh - r.bottom;
2556
2712
  const flip = placement === "top" || below < 260 && r.top > below;
2713
+ 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) };
2557
2714
  setCoords({
2558
2715
  left: r.left,
2559
- width: Math.max(r.width, minWidth),
2716
+ ...widthStyle,
2560
2717
  top: flip ? void 0 : Math.round(r.bottom + 6),
2561
2718
  bottom: flip ? Math.round(vh - r.top + 6) : void 0,
2562
2719
  flip
2563
2720
  });
2564
- }, [placement, minWidth]);
2565
- React24__default.useEffect(() => {
2721
+ }, [placement, minWidth, matchTriggerWidth]);
2722
+ React25__default.useEffect(() => {
2566
2723
  if (!open || !portal) return;
2567
2724
  place();
2568
2725
  const onMove = () => place();
@@ -2573,7 +2730,7 @@ function Select({
2573
2730
  window.removeEventListener("resize", onMove);
2574
2731
  };
2575
2732
  }, [open, portal, place]);
2576
- React24__default.useEffect(() => {
2733
+ React25__default.useEffect(() => {
2577
2734
  if (!open) return;
2578
2735
  const idx = flat.findIndex((o) => o.value === current);
2579
2736
  setActive(idx >= 0 ? idx : 0);
@@ -2589,13 +2746,13 @@ function Select({
2589
2746
  document.addEventListener("mousedown", onDown);
2590
2747
  return () => document.removeEventListener("mousedown", onDown);
2591
2748
  }, [open]);
2592
- React24__default.useEffect(() => {
2749
+ React25__default.useEffect(() => {
2593
2750
  setActive(0);
2594
2751
  }, [query]);
2595
- React24__default.useEffect(() => {
2752
+ React25__default.useEffect(() => {
2596
2753
  if (open) ensureVisible2(listRef.current, activeRef.current);
2597
2754
  }, [active, open]);
2598
- React24__default.useEffect(() => {
2755
+ React25__default.useEffect(() => {
2599
2756
  if (open) {
2600
2757
  setRender(true);
2601
2758
  return;
@@ -2603,8 +2760,8 @@ function Select({
2603
2760
  const t = setTimeout(() => setRender(false), 170);
2604
2761
  return () => clearTimeout(t);
2605
2762
  }, [open]);
2606
- const prevOpen = React24__default.useRef(false);
2607
- React24__default.useEffect(() => {
2763
+ const prevOpen = React25__default.useRef(false);
2764
+ React25__default.useEffect(() => {
2608
2765
  if (prevOpen.current && !open) {
2609
2766
  const ae = typeof document !== "undefined" ? document.activeElement : null;
2610
2767
  if (popRef.current && ae && popRef.current.contains(ae)) triggerRef.current?.focus();
@@ -2612,6 +2769,8 @@ function Select({
2612
2769
  prevOpen.current = open;
2613
2770
  }, [open]);
2614
2771
  function commit(v) {
2772
+ const o = flat.find((x) => x.value === v);
2773
+ if (o && o.disabled) return;
2615
2774
  if (value === void 0) setInternal(v);
2616
2775
  onChange?.(v);
2617
2776
  setOpen(false);
@@ -2632,6 +2791,18 @@ function Select({
2632
2791
  setOpen(true);
2633
2792
  return;
2634
2793
  }
2794
+ if (!open && !showSearch && e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
2795
+ e.preventDefault();
2796
+ bufferRef.current += e.key.toLowerCase();
2797
+ clearTimeout(bufferTimerRef.current);
2798
+ bufferTimerRef.current = setTimeout(() => {
2799
+ bufferRef.current = "";
2800
+ }, 500);
2801
+ const b = bufferRef.current;
2802
+ const match = flat.find((o) => !o.disabled && o.label.toLowerCase().startsWith(b)) || flat.find((o) => !o.disabled && o.label.toLowerCase().includes(b));
2803
+ if (match) commit(match.value);
2804
+ return;
2805
+ }
2635
2806
  if (!open) return;
2636
2807
  if (e.key === "Tab") {
2637
2808
  setOpen(false);
@@ -2640,10 +2811,10 @@ function Select({
2640
2811
  if (e.key === "Escape") setOpen(false);
2641
2812
  else if (e.key === "ArrowDown") {
2642
2813
  e.preventDefault();
2643
- setActive((a) => Math.min(a + 1, visible.length - 1));
2814
+ setActive((a) => nextEnabled(a, 1));
2644
2815
  } else if (e.key === "ArrowUp") {
2645
2816
  e.preventDefault();
2646
- setActive((a) => Math.max(a - 1, 0));
2817
+ setActive((a) => nextEnabled(a, -1));
2647
2818
  } else if (e.key === "Enter") {
2648
2819
  e.preventDefault();
2649
2820
  if (visible[active]) commit(visible[active].value);
@@ -2675,9 +2846,16 @@ function Select({
2675
2846
  "aria-controls": listboxId,
2676
2847
  "aria-activedescendant": activeId
2677
2848
  }
2678
- )
2849
+ ),
2850
+ query ? /* @__PURE__ */ jsx("button", { type: "button", className: "twc-pop__search-clear", "aria-label": "Clear search", onClick: () => {
2851
+ setQuery("");
2852
+ searchRef.current?.focus();
2853
+ }, children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.4", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "M18 6 6 18M6 6l12 12" }) }) }) : null
2679
2854
  ] }) : null,
2680
- /* @__PURE__ */ jsx("div", { className: "twc-pop__list", ref: listRef, children: visible.length === 0 ? /* @__PURE__ */ jsx("div", { className: "twc-pop__empty", children: "No results found" }) : fGroups.map((g, gi) => /* @__PURE__ */ jsxs(React24__default.Fragment, { children: [
2855
+ /* @__PURE__ */ jsx("div", { className: "twc-pop__list", ref: listRef, children: loading ? /* @__PURE__ */ jsxs("div", { className: "twc-pop__loading", role: "status", children: [
2856
+ /* @__PURE__ */ jsx("span", { className: "twc-pop__spinner", "aria-hidden": "true" }),
2857
+ "Loading\u2026"
2858
+ ] }) : visible.length === 0 ? /* @__PURE__ */ jsx("div", { className: "twc-pop__empty", children: emptyText }) : fGroups.map((g, gi) => /* @__PURE__ */ jsxs(React25__default.Fragment, { children: [
2681
2859
  g.group ? /* @__PURE__ */ jsx("div", { className: "twc-pop__group", children: g.group }) : null,
2682
2860
  g.options.map((o) => {
2683
2861
  counter += 1;
@@ -2692,9 +2870,14 @@ function Select({
2692
2870
  role: "option",
2693
2871
  "aria-selected": isSel,
2694
2872
  ref: idx === active ? activeRef : null,
2873
+ disabled: o.disabled || void 0,
2874
+ "aria-disabled": o.disabled || void 0,
2875
+ "data-disabled": o.disabled || void 0,
2695
2876
  "data-selected": isSel || void 0,
2696
2877
  "data-active": idx === active || void 0,
2697
- onMouseEnter: () => setActive(idx),
2878
+ onMouseEnter: () => {
2879
+ if (!o.disabled) setActive(idx);
2880
+ },
2698
2881
  onClick: () => commit(o.value),
2699
2882
  children: [
2700
2883
  /* @__PURE__ */ jsxs("span", { className: "twc-opt__main", children: [
@@ -2707,7 +2890,8 @@ function Select({
2707
2890
  o.value
2708
2891
  );
2709
2892
  })
2710
- ] }, gi)) })
2893
+ ] }, gi)) }),
2894
+ /* @__PURE__ */ 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"}` })
2711
2895
  ] });
2712
2896
  const RD = { createPortal: typeof createPortal === "function" ? createPortal : typeof window !== "undefined" && window.ReactDOM && window.ReactDOM.createPortal };
2713
2897
  const canPortal = portal && RD && RD.createPortal;
@@ -2724,19 +2908,20 @@ function Select({
2724
2908
  role: "listbox",
2725
2909
  ref: popRef,
2726
2910
  "data-state": popState,
2727
- "data-placement": coords.flip ? "top" : void 0,
2728
- style: { position: "fixed", left: coords.left, top: coords.top, bottom: coords.bottom, width: coords.width, right: "auto", zIndex: "var(--z-tooltip)" },
2911
+ "data-placement": coords.flip ? "top" : "bottom",
2912
+ 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)" },
2729
2913
  children: popInner
2730
2914
  }
2731
2915
  ),
2732
2916
  document.body
2733
2917
  );
2734
2918
  } else if (!portal) {
2735
- popEl = /* @__PURE__ */ jsx("div", { className: "twc-pop", id: listboxId, role: "listbox", ref: popRef, "data-state": popState, "data-placement": placement === "top" ? "top" : void 0, children: popInner });
2919
+ popEl = /* @__PURE__ */ jsx("div", { className: "twc-pop", id: listboxId, role: "listbox", ref: popRef, "data-state": popState, "data-placement": placement === "top" ? "top" : "bottom", children: popInner });
2736
2920
  }
2737
2921
  }
2738
2922
  return /* @__PURE__ */ jsxs("div", { className: `twc-field ${className}`, ref: wrapRef, children: [
2739
2923
  __twcStyles,
2924
+ name ? /* @__PURE__ */ jsx("input", { type: "hidden", name, value: current ?? "" }) : null,
2740
2925
  label ? /* @__PURE__ */ jsxs("label", { className: "twc-field__label", htmlFor: fieldId, children: [
2741
2926
  label,
2742
2927
  required ? /* @__PURE__ */ jsx("span", { className: "twc-field__req", children: "*" }) : null
@@ -2807,7 +2992,9 @@ var CURF_CSS = `
2807
2992
  .twc-cur:focus-within { border-color: var(--_accent); box-shadow: var(--_ring); }
2808
2993
  .twc-cur[data-invalid="true"] { border-color: var(--color-danger); }
2809
2994
  .twc-cur[data-invalid="true"]:focus-within { box-shadow: 0 0 0 var(--ring-width) color-mix(in srgb, var(--color-danger) 28%, transparent); }
2810
- .twc-cur[data-disabled="true"] { background: var(--color-surface-sunken); opacity: 0.7; }
2995
+ .twc-cur[data-disabled="true"] { background: var(--color-surface-sunken); opacity: 0.7; cursor: not-allowed; }
2996
+ .twc-cur[data-readonly="true"] { background: var(--color-surface-sunken); }
2997
+ .twc-cur__el:disabled { cursor: not-allowed; }
2811
2998
  .twc-cur__sym, .twc-cur__code {
2812
2999
  display: inline-flex; align-items: center; padding: 0 var(--space-3);
2813
3000
  background: var(--color-surface-sunken); color: var(--color-text-muted);
@@ -2851,19 +3038,37 @@ function CurrencyField({
2851
3038
  ...rest
2852
3039
  }) {
2853
3040
  const __twcStyles = useScopedStyles("twc-curf-styles", CURF_CSS);
2854
- const autoId = React24__default.useId();
3041
+ const autoId = React25__default.useId();
2855
3042
  const fieldId = id || autoId;
2856
3043
  const invalid = Boolean(error);
2857
3044
  const curControlled = currency !== void 0;
2858
- const [curInternal, setCurInternal] = React24__default.useState(defaultCurrency);
3045
+ const [curInternal, setCurInternal] = React25__default.useState(defaultCurrency);
2859
3046
  const cur = curControlled ? currency : curInternal;
2860
3047
  const meta = CURRENCIES[cur] || CURRENCIES.USD;
2861
3048
  const prec = meta.precision;
2862
3049
  const valControlled = value !== void 0;
2863
- const [valInternal, setValInternal] = React24__default.useState(() => clampPrecision(String(defaultValue ?? ""), prec));
3050
+ const [valInternal, setValInternal] = React25__default.useState(() => clampPrecision(String(defaultValue ?? ""), prec));
2864
3051
  const shown = valControlled ? clampPrecision(String(value ?? ""), prec) : valInternal;
2865
- const describedBy = [error ? `${fieldId}-error` : hint ? `${fieldId}-hint` : null, rest["aria-describedby"]].filter(Boolean).join(" ") || void 0;
2866
- const options = React24__default.useMemo(() => {
3052
+ const descId = `${fieldId}-desc`;
3053
+ const describedBy = [error || hint ? descId : null, rest["aria-describedby"]].filter(Boolean).join(" ") || void 0;
3054
+ const ariaInvalid = invalid ? true : rest["aria-invalid"] ?? void 0;
3055
+ React25__default.useEffect(() => {
3056
+ if (!valControlled) {
3057
+ const clamped = clampPrecision(valInternal, prec);
3058
+ if (clamped !== valInternal) {
3059
+ setValInternal(clamped);
3060
+ const n = Number(clamped);
3061
+ onValueChange?.(clamped === "" || Number.isNaN(n) ? null : n, clamped, cur);
3062
+ }
3063
+ } else {
3064
+ const raw = String(value ?? "");
3065
+ if (shown !== raw) {
3066
+ const n = Number(shown);
3067
+ onValueChange?.(shown === "" || Number.isNaN(n) ? null : n, shown, cur);
3068
+ }
3069
+ }
3070
+ }, [prec, cur, shown, value, valControlled, valInternal]);
3071
+ const options = React25__default.useMemo(() => {
2867
3072
  if (!currencies) return CURRENCY_OPTIONS;
2868
3073
  return currencies.map((c) => typeof c === "string" ? { value: c, label: `${c} \u2014 ${(CURRENCIES[c] || {}).label || c}` } : c);
2869
3074
  }, [currencies]);
@@ -2872,13 +3077,15 @@ function CurrencyField({
2872
3077
  onCurrencyChange?.(next);
2873
3078
  const reclamped = clampPrecision(shown, (CURRENCIES[next] || CURRENCIES.USD).precision);
2874
3079
  if (!valControlled) setValInternal(reclamped);
2875
- onValueChange?.(reclamped === "" ? null : Number(reclamped), reclamped, next);
3080
+ const rn = Number(reclamped);
3081
+ onValueChange?.(reclamped === "" || Number.isNaN(rn) ? null : rn, reclamped, next);
2876
3082
  }
2877
3083
  function handleChange(e) {
2878
3084
  onChange?.(e);
2879
3085
  const next = clampPrecision(e.target.value, prec);
2880
3086
  if (!valControlled) setValInternal(next);
2881
- onValueChange?.(next === "" ? null : Number(next), next, cur);
3087
+ const n = Number(next);
3088
+ onValueChange?.(next === "" || Number.isNaN(n) ? null : n, next, cur);
2882
3089
  }
2883
3090
  function handleBlur(e) {
2884
3091
  rest.onBlur?.(e);
@@ -2889,13 +3096,14 @@ function CurrencyField({
2889
3096
  if (!valControlled) setValInternal(fixed);
2890
3097
  onValueChange?.(Number(fixed), fixed, cur);
2891
3098
  }
3099
+ const curLabel = typeof label === "string" && label ? `${label} currency` : "Currency";
2892
3100
  return /* @__PURE__ */ jsxs("div", { className: `twc-field ${className}`, children: [
2893
3101
  __twcStyles,
2894
3102
  label ? /* @__PURE__ */ jsxs("label", { className: "twc-field__label", htmlFor: fieldId, children: [
2895
3103
  label,
2896
3104
  required ? /* @__PURE__ */ jsx("span", { className: "twc-field__req", children: "*" }) : null
2897
3105
  ] }) : null,
2898
- /* @__PURE__ */ jsxs("div", { className: "twc-cur", "data-size": size, "data-tone": tone, "data-invalid": invalid || void 0, "data-disabled": disabled || void 0, children: [
3106
+ /* @__PURE__ */ 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: [
2899
3107
  /* @__PURE__ */ jsx("span", { className: "twc-curf__pick", children: /* @__PURE__ */ jsx(
2900
3108
  Select,
2901
3109
  {
@@ -2906,7 +3114,7 @@ function CurrencyField({
2906
3114
  searchable: true,
2907
3115
  portal: true,
2908
3116
  minWidth: 260,
2909
- "aria-label": "Currency",
3117
+ "aria-label": curLabel,
2910
3118
  onChange: setCurrency
2911
3119
  }
2912
3120
  ) }),
@@ -2922,16 +3130,16 @@ function CurrencyField({
2922
3130
  placeholder,
2923
3131
  disabled,
2924
3132
  required: required || void 0,
2925
- "aria-invalid": invalid || void 0,
2926
3133
  ...rest,
2927
3134
  onChange: handleChange,
2928
3135
  onBlur: handleBlur,
3136
+ "aria-invalid": ariaInvalid,
2929
3137
  "aria-describedby": describedBy
2930
3138
  }
2931
3139
  ),
2932
3140
  /* @__PURE__ */ jsx("span", { className: "twc-cur__code", children: meta.code })
2933
3141
  ] }),
2934
- error ? /* @__PURE__ */ jsx("span", { id: `${fieldId}-error`, className: "twc-field__error", children: error }) : hint ? /* @__PURE__ */ jsx("span", { id: `${fieldId}-hint`, className: "twc-field__hint", children: hint }) : null
3142
+ error ? /* @__PURE__ */ jsx("span", { id: descId, className: "twc-field__error", children: error }) : hint ? /* @__PURE__ */ jsx("span", { id: descId, className: "twc-field__hint", children: hint }) : null
2935
3143
  ] });
2936
3144
  }
2937
3145
  var FIELD_CSS2 = `
@@ -2967,6 +3175,7 @@ var FIELD_CSS2 = `
2967
3175
  .twc-input[data-invalid="true"] { border-color: var(--color-danger); }
2968
3176
  .twc-input[data-invalid="true"]:focus-within { box-shadow: 0 0 0 var(--ring-width) color-mix(in srgb, var(--color-danger) 28%, transparent); }
2969
3177
  .twc-input[data-disabled] { background: var(--color-surface-sunken); opacity: 0.7; cursor: not-allowed; }
3178
+ .twc-input[data-readonly] { background: var(--color-surface-sunken); }
2970
3179
  .twc-input__el {
2971
3180
  flex: 1; min-width: 0; border: none; outline: none; background: transparent;
2972
3181
  font-family: inherit; font-size: var(--text-sm); color: var(--color-text); height: 100%;
@@ -2982,6 +3191,17 @@ var FIELD_CSS2 = `
2982
3191
  .twc-input__reveal:focus-visible { outline: none; box-shadow: var(--ring); }
2983
3192
  .twc-input__reveal svg { width: 17px; height: 17px; }
2984
3193
  `;
3194
+ var INPUT_EXTRA_CSS = `
3195
+ .twc-input { overflow: hidden; }
3196
+ .twc-input__addon { align-self: stretch; display: inline-flex; align-items: center; padding-inline: var(--space-3);
3197
+ background: var(--color-surface-sunken); color: var(--color-text-muted); font-size: var(--text-sm);
3198
+ font-weight: var(--font-semibold); white-space: nowrap; flex: none; }
3199
+ .twc-input__addon--start { border-inline-end: var(--border-thin) solid var(--color-border); margin-inline-start: calc(-1 * var(--space-3)); }
3200
+ .twc-input__addon--end { border-inline-start: var(--border-thin) solid var(--color-border); margin-inline-end: calc(-1 * var(--space-3)); }
3201
+ .twc-field__footer { display: flex; align-items: center; gap: var(--space-2); }
3202
+ .twc-field__count { margin-inline-start: auto; font-size: var(--text-xs); color: var(--color-text-muted); font-variant-numeric: tabular-nums; flex: none; }
3203
+ .twc-field__count[data-danger="true"] { color: var(--color-danger-subtle-fg); }
3204
+ `;
2985
3205
  function Input({
2986
3206
  label,
2987
3207
  hint,
@@ -2991,6 +3211,9 @@ function Input({
2991
3211
  tone = "primary",
2992
3212
  leftIcon,
2993
3213
  rightIcon,
3214
+ leftAddon,
3215
+ rightAddon,
3216
+ showCount = false,
2994
3217
  type = "text",
2995
3218
  disabled = false,
2996
3219
  id,
@@ -2998,12 +3221,26 @@ function Input({
2998
3221
  ...rest
2999
3222
  }) {
3000
3223
  const __twcStyles = useScopedStyles("twc-field-styles", FIELD_CSS2);
3001
- const autoId = React24__default.useId();
3224
+ const __twcExtra = useScopedStyles("twc-input-extra-styles", INPUT_EXTRA_CSS);
3225
+ const autoId = React25__default.useId();
3002
3226
  const fieldId = id || autoId;
3003
3227
  const descId = `${fieldId}-desc`;
3228
+ const countId = `${fieldId}-count`;
3004
3229
  const invalid = Boolean(error);
3230
+ const countControlled = rest.value !== void 0;
3231
+ const [len2, setLen] = React25__default.useState(() => String(rest.defaultValue ?? "").length);
3232
+ const count = countControlled ? String(rest.value ?? "").length : len2;
3233
+ const max = rest.maxLength;
3234
+ const showCounter = showCount && max != null;
3235
+ const near = showCounter && count >= Math.floor(max * 0.9);
3236
+ const handleChange = (e) => {
3237
+ rest.onChange?.(e);
3238
+ if (showCount && !countControlled) setLen(e.target.value.length);
3239
+ };
3240
+ const ariaInvalid = invalid ? true : rest["aria-invalid"] ?? void 0;
3241
+ const describedBy = [error || hint ? descId : null, showCounter ? countId : null, rest["aria-describedby"]].filter(Boolean).join(" ") || void 0;
3005
3242
  const isPassword = type === "password";
3006
- const [revealed, setRevealed] = React24__default.useState(false);
3243
+ const [revealed, setRevealed] = React25__default.useState(false);
3007
3244
  const effectiveType = isPassword && revealed ? "text" : type;
3008
3245
  const suffix = rightIcon ? /* @__PURE__ */ jsx("span", { className: "twc-input__affix", children: rightIcon }) : isPassword ? /* @__PURE__ */ jsx(
3009
3246
  "button",
@@ -3028,11 +3265,13 @@ function Input({
3028
3265
  ) : null;
3029
3266
  return /* @__PURE__ */ jsxs("div", { className: `twc-field ${className}`, children: [
3030
3267
  __twcStyles,
3268
+ __twcExtra,
3031
3269
  label ? /* @__PURE__ */ jsxs("label", { className: "twc-field__label", htmlFor: fieldId, children: [
3032
3270
  label,
3033
3271
  required ? /* @__PURE__ */ jsx("span", { className: "twc-field__req", children: "*" }) : null
3034
3272
  ] }) : null,
3035
- /* @__PURE__ */ jsxs("div", { className: "twc-input", "data-size": size, "data-tone": tone, "data-invalid": invalid || void 0, "data-disabled": disabled || void 0, children: [
3273
+ /* @__PURE__ */ 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: [
3274
+ leftAddon != null ? /* @__PURE__ */ jsx("span", { className: "twc-input__addon twc-input__addon--start", children: leftAddon }) : null,
3036
3275
  leftIcon ? /* @__PURE__ */ jsx("span", { className: "twc-input__affix", children: leftIcon }) : null,
3037
3276
  /* @__PURE__ */ jsx(
3038
3277
  "input",
@@ -3043,14 +3282,23 @@ function Input({
3043
3282
  disabled,
3044
3283
  required: required || void 0,
3045
3284
  "aria-required": required || void 0,
3046
- "aria-invalid": invalid || void 0,
3047
- "aria-describedby": error || hint ? descId : void 0,
3048
- ...rest
3285
+ ...rest,
3286
+ onChange: handleChange,
3287
+ "aria-invalid": ariaInvalid,
3288
+ "aria-describedby": describedBy
3049
3289
  }
3050
3290
  ),
3051
- suffix
3291
+ suffix,
3292
+ rightAddon != null ? /* @__PURE__ */ jsx("span", { className: "twc-input__addon twc-input__addon--end", children: rightAddon }) : null
3052
3293
  ] }),
3053
- error ? /* @__PURE__ */ jsx("span", { id: descId, className: "twc-field__error", children: error }) : hint ? /* @__PURE__ */ jsx("span", { id: descId, className: "twc-field__hint", children: hint }) : null
3294
+ error || hint || showCounter ? /* @__PURE__ */ jsxs("div", { className: "twc-field__footer", children: [
3295
+ error ? /* @__PURE__ */ jsx("span", { id: descId, className: "twc-field__error", children: error }) : hint ? /* @__PURE__ */ jsx("span", { id: descId, className: "twc-field__hint", children: hint }) : null,
3296
+ showCounter ? /* @__PURE__ */ jsxs("span", { id: countId, className: "twc-field__count", "data-danger": near || void 0, "aria-live": "polite", children: [
3297
+ count,
3298
+ " / ",
3299
+ max
3300
+ ] }) : null
3301
+ ] }) : null
3054
3302
  ] });
3055
3303
  }
3056
3304
  var MULTI_CSS = `
@@ -3128,6 +3376,10 @@ var MULTI_CSS = `
3128
3376
  .twc-opt__label { line-height: 1.3; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
3129
3377
  .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; }
3130
3378
  .twc-pop__empty { padding: 14px 12px; text-align: center; font-size: var(--text-sm); color: var(--color-text-subtle); }
3379
+ .twc-opt[data-disabled="true"] { opacity: 0.45; cursor: not-allowed; pointer-events: none; }
3380
+ .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); }
3381
+ .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; }
3382
+ .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; }
3131
3383
  `;
3132
3384
  function toOpt3(o) {
3133
3385
  return typeof o === "string" ? { value: o, label: o } : o;
@@ -3177,6 +3429,11 @@ function MultiSelect({
3177
3429
  placement = "bottom",
3178
3430
  portal = true,
3179
3431
  minWidth = 0,
3432
+ loading = false,
3433
+ emptyText = "No results found",
3434
+ name,
3435
+ max,
3436
+ maxTagCount,
3180
3437
  id,
3181
3438
  className = "",
3182
3439
  onFocus,
@@ -3184,28 +3441,38 @@ function MultiSelect({
3184
3441
  ...rest
3185
3442
  }) {
3186
3443
  const __twcStyles = useScopedStyles("twc-ms-styles", MULTI_CSS);
3187
- const groups = React24__default.useMemo(() => normalizeGroups3(options), [options]);
3188
- const flat = React24__default.useMemo(() => groups.flatMap((g) => g.options), [groups]);
3189
- const autoId = React24__default.useId();
3444
+ const groups = React25__default.useMemo(() => normalizeGroups3(options), [options]);
3445
+ const flat = React25__default.useMemo(() => groups.flatMap((g) => g.options), [groups]);
3446
+ const autoId = React25__default.useId();
3190
3447
  const fieldId = id || autoId;
3191
- const [internal, setInternal] = React24__default.useState(defaultValue);
3448
+ const [internal, setInternal] = React25__default.useState(defaultValue);
3192
3449
  const selected = value !== void 0 ? value : internal;
3193
- const [open, setOpen] = React24__default.useState(false);
3194
- const [query, setQuery] = React24__default.useState("");
3195
- const [active, setActive] = React24__default.useState(0);
3196
- const [coords, setCoords] = React24__default.useState(null);
3197
- const wrapRef = React24__default.useRef(null);
3198
- const controlRef = React24__default.useRef(null);
3199
- const inputRef = React24__default.useRef(null);
3200
- const popRef = React24__default.useRef(null);
3201
- const listRef = React24__default.useRef(null);
3202
- const activeRef = React24__default.useRef(null);
3203
- const fGroups = React24__default.useMemo(
3450
+ const [open, setOpen] = React25__default.useState(false);
3451
+ const [query, setQuery] = React25__default.useState("");
3452
+ const [active, setActive] = React25__default.useState(0);
3453
+ const [coords, setCoords] = React25__default.useState(null);
3454
+ const wrapRef = React25__default.useRef(null);
3455
+ const controlRef = React25__default.useRef(null);
3456
+ const inputRef = React25__default.useRef(null);
3457
+ const popRef = React25__default.useRef(null);
3458
+ const listRef = React25__default.useRef(null);
3459
+ const activeRef = React25__default.useRef(null);
3460
+ const fGroups = React25__default.useMemo(
3204
3461
  () => groups.map((g) => ({ group: g.group, options: g.options.filter((o) => matches2(o, query.trim())) })).filter((g) => g.options.length),
3205
3462
  [groups, query]
3206
3463
  );
3207
- const visible = React24__default.useMemo(() => fGroups.flatMap((g) => g.options), [fGroups]);
3208
- const place = React24__default.useCallback(() => {
3464
+ const visible = React25__default.useMemo(() => fGroups.flatMap((g) => g.options), [fGroups]);
3465
+ const capReached = max != null && (value !== void 0 ? value : internal).length >= max;
3466
+ const isOptDisabled = (o) => o.disabled || capReached && !(value !== void 0 ? value : internal).includes(o.value);
3467
+ const nextEnabled = (from, dir) => {
3468
+ let i = from + dir;
3469
+ while (i >= 0 && i < visible.length) {
3470
+ if (!isOptDisabled(visible[i])) return i;
3471
+ i += dir;
3472
+ }
3473
+ return from;
3474
+ };
3475
+ const place = React25__default.useCallback(() => {
3209
3476
  const el = controlRef.current;
3210
3477
  if (!el) return;
3211
3478
  const r = el.getBoundingClientRect();
@@ -3220,7 +3487,7 @@ function MultiSelect({
3220
3487
  flip
3221
3488
  });
3222
3489
  }, [placement, minWidth]);
3223
- React24__default.useEffect(() => {
3490
+ React25__default.useEffect(() => {
3224
3491
  if (!open || !portal) return;
3225
3492
  place();
3226
3493
  const onMove = () => place();
@@ -3231,7 +3498,7 @@ function MultiSelect({
3231
3498
  window.removeEventListener("resize", onMove);
3232
3499
  };
3233
3500
  }, [open, portal, place]);
3234
- React24__default.useEffect(() => {
3501
+ React25__default.useEffect(() => {
3235
3502
  if (!open) return;
3236
3503
  const onDown = (e) => {
3237
3504
  if (wrapRef.current && wrapRef.current.contains(e.target)) return;
@@ -3242,10 +3509,10 @@ function MultiSelect({
3242
3509
  document.addEventListener("mousedown", onDown);
3243
3510
  return () => document.removeEventListener("mousedown", onDown);
3244
3511
  }, [open]);
3245
- React24__default.useEffect(() => {
3512
+ React25__default.useEffect(() => {
3246
3513
  setActive(0);
3247
3514
  }, [query]);
3248
- React24__default.useEffect(() => {
3515
+ React25__default.useEffect(() => {
3249
3516
  if (open) ensureVisible3(listRef.current, activeRef.current);
3250
3517
  }, [active, open]);
3251
3518
  function commit(next) {
@@ -3253,7 +3520,10 @@ function MultiSelect({
3253
3520
  onChange?.(next);
3254
3521
  }
3255
3522
  function toggle(v) {
3256
- commit(selected.includes(v) ? selected.filter((x) => x !== v) : [...selected, v]);
3523
+ const o = flat.find((x) => x.value === v);
3524
+ const isSel = selected.includes(v);
3525
+ if (!isSel && o && isOptDisabled(o)) return;
3526
+ commit(isSel ? selected.filter((x) => x !== v) : [...selected, v]);
3257
3527
  setQuery("");
3258
3528
  inputRef.current?.focus();
3259
3529
  }
@@ -3261,10 +3531,10 @@ function MultiSelect({
3261
3531
  if (e.key === "ArrowDown") {
3262
3532
  e.preventDefault();
3263
3533
  if (!open) setOpen(true);
3264
- else setActive((a) => Math.min(a + 1, visible.length - 1));
3534
+ else setActive((a) => nextEnabled(a, 1));
3265
3535
  } else if (e.key === "ArrowUp") {
3266
3536
  e.preventDefault();
3267
- setActive((a) => Math.max(a - 1, 0));
3537
+ setActive((a) => nextEnabled(a, -1));
3268
3538
  } else if (e.key === "Enter") {
3269
3539
  e.preventDefault();
3270
3540
  if (open && visible[active]) toggle(visible[active].value);
@@ -3282,12 +3552,16 @@ function MultiSelect({
3282
3552
  const descId = `${fieldId}-desc`;
3283
3553
  const describedBy = error || hint ? descId : void 0;
3284
3554
  let counter = -1;
3285
- const popInner = /* @__PURE__ */ jsx("div", { className: "twc-pop__list", id: listboxId, ref: listRef, role: "listbox", "aria-multiselectable": "true", children: visible.length === 0 ? /* @__PURE__ */ jsx("div", { className: "twc-pop__empty", children: "No results found" }) : fGroups.map((g, gi) => /* @__PURE__ */ jsxs(React24__default.Fragment, { children: [
3555
+ const popInner = /* @__PURE__ */ jsx("div", { className: "twc-pop__list", id: listboxId, ref: listRef, role: "listbox", "aria-multiselectable": "true", children: loading ? /* @__PURE__ */ jsxs("div", { className: "twc-pop__loading", role: "status", children: [
3556
+ /* @__PURE__ */ jsx("span", { className: "twc-pop__spinner", "aria-hidden": "true" }),
3557
+ "Loading\u2026"
3558
+ ] }) : visible.length === 0 ? /* @__PURE__ */ jsx("div", { className: "twc-pop__empty", children: emptyText }) : fGroups.map((g, gi) => /* @__PURE__ */ jsxs(React25__default.Fragment, { children: [
3286
3559
  g.group ? /* @__PURE__ */ jsx("div", { className: "twc-pop__group", children: g.group }) : null,
3287
3560
  g.options.map((o) => {
3288
3561
  counter += 1;
3289
3562
  const idx = counter;
3290
3563
  const isSel = selected.includes(o.value);
3564
+ const optDisabled = isOptDisabled(o);
3291
3565
  return /* @__PURE__ */ jsxs(
3292
3566
  "button",
3293
3567
  {
@@ -3297,9 +3571,14 @@ function MultiSelect({
3297
3571
  role: "option",
3298
3572
  "aria-selected": isSel,
3299
3573
  ref: idx === active ? activeRef : null,
3574
+ disabled: optDisabled || void 0,
3575
+ "aria-disabled": optDisabled || void 0,
3576
+ "data-disabled": optDisabled || void 0,
3300
3577
  "data-selected": isSel || void 0,
3301
3578
  "data-active": idx === active || void 0,
3302
- onMouseEnter: () => setActive(idx),
3579
+ onMouseEnter: () => {
3580
+ if (!optDisabled) setActive(idx);
3581
+ },
3303
3582
  onMouseDown: (e) => e.preventDefault(),
3304
3583
  onClick: () => toggle(o.value),
3305
3584
  children: [
@@ -3325,7 +3604,7 @@ function MultiSelect({
3325
3604
  {
3326
3605
  className: "twc-pop twc-pop--portal",
3327
3606
  ref: popRef,
3328
- "data-placement": coords.flip ? "top" : void 0,
3607
+ "data-placement": coords.flip ? "top" : "bottom",
3329
3608
  style: { position: "fixed", left: coords.left, top: coords.top, bottom: coords.bottom, width: coords.width, right: "auto", zIndex: "var(--z-tooltip)" },
3330
3609
  children: popInner
3331
3610
  }
@@ -3338,6 +3617,7 @@ function MultiSelect({
3338
3617
  }
3339
3618
  return /* @__PURE__ */ jsxs("div", { className: `twc-field ${className}`, ref: wrapRef, children: [
3340
3619
  __twcStyles,
3620
+ name ? selected.map((v, i) => /* @__PURE__ */ jsx("input", { type: "hidden", name, value: v }, i)) : null,
3341
3621
  label ? /* @__PURE__ */ jsxs("label", { className: "twc-field__label", htmlFor: fieldId, children: [
3342
3622
  label,
3343
3623
  required ? /* @__PURE__ */ jsx("span", { className: "twc-field__req", children: "*" }) : null
@@ -3360,13 +3640,18 @@ function MultiSelect({
3360
3640
  }
3361
3641
  },
3362
3642
  children: [
3363
- selectedOpts.map((o) => /* @__PURE__ */ jsxs("span", { className: "twc-ms__chip", children: [
3643
+ (maxTagCount != null ? selectedOpts.slice(0, maxTagCount) : selectedOpts).map((o) => /* @__PURE__ */ jsxs("span", { className: "twc-ms__chip", children: [
3364
3644
  o.label,
3365
- /* @__PURE__ */ jsx("button", { type: "button", className: "twc-ms__chip-x", "aria-label": `Remove ${o.label}`, onClick: (e) => {
3645
+ /* @__PURE__ */ jsx("button", { type: "button", tabIndex: -1, className: "twc-ms__chip-x", "aria-label": `Remove ${o.label}`, onClick: (e) => {
3366
3646
  e.stopPropagation();
3367
3647
  toggle(o.value);
3368
3648
  }, children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.6", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "M18 6 6 18M6 6l12 12" }) }) })
3369
3649
  ] }, o.value)),
3650
+ maxTagCount != null && selectedOpts.length > maxTagCount ? /* @__PURE__ */ jsxs("span", { className: "twc-ms__more", title: selectedOpts.slice(maxTagCount).map((o) => o.label).join(", "), children: [
3651
+ "+",
3652
+ selectedOpts.length - maxTagCount,
3653
+ " more"
3654
+ ] }) : null,
3370
3655
  /* @__PURE__ */ jsx(
3371
3656
  "input",
3372
3657
  {
@@ -3398,7 +3683,7 @@ function MultiSelect({
3398
3683
  ...rest
3399
3684
  }
3400
3685
  ),
3401
- clearable && selected.length > 0 && !disabled ? /* @__PURE__ */ jsx("button", { type: "button", className: "twc-ms__clear", "aria-label": "Clear all", onClick: (e) => {
3686
+ clearable && selected.length > 0 && !disabled ? /* @__PURE__ */ jsx("button", { type: "button", tabIndex: -1, className: "twc-ms__clear", "aria-label": "Clear all", onClick: (e) => {
3402
3687
  e.stopPropagation();
3403
3688
  commit([]);
3404
3689
  setQuery("");
@@ -3426,15 +3711,6 @@ function MultiSelect({
3426
3711
  error ? /* @__PURE__ */ jsx("span", { id: descId, className: "twc-field__error", children: error }) : hint ? /* @__PURE__ */ jsx("span", { id: descId, className: "twc-field__hint", children: hint }) : null
3427
3712
  ] });
3428
3713
  }
3429
-
3430
- // components/_warn.js
3431
- var seen = /* @__PURE__ */ new Set();
3432
- function warnOnce(key, message) {
3433
- if (typeof process !== "undefined" && process.env && process.env.NODE_ENV === "production") return;
3434
- if (seen.has(key)) return;
3435
- seen.add(key);
3436
- if (typeof console !== "undefined" && console.warn) console.warn(message);
3437
- }
3438
3714
  var PAGINATION_CSS = `
3439
3715
  .twc-pagination { display: inline-flex; align-items: center; gap: 4px; font-family: var(--font-sans); }
3440
3716
  .twc-page {
@@ -3500,6 +3776,7 @@ function Pagination({
3500
3776
  showJumper,
3501
3777
  showPageJumper,
3502
3778
  jumperLabel = "Go to",
3779
+ getPageLabel,
3503
3780
  className = "",
3504
3781
  ...rest
3505
3782
  }) {
@@ -3508,7 +3785,7 @@ function Pagination({
3508
3785
  }
3509
3786
  const jumperEnabled = showPageJumper ?? showJumper;
3510
3787
  const __twcStyles = useScopedStyles("twc-pagination-styles", PAGINATION_CSS);
3511
- const [internal, setInternal] = React24__default.useState(defaultPage);
3788
+ const [internal, setInternal] = React25__default.useState(defaultPage);
3512
3789
  const page = pageProp !== void 0 ? pageProp : internal;
3513
3790
  const pages = buildPages(page, total, siblings, boundaries);
3514
3791
  const go = (p) => {
@@ -3516,7 +3793,7 @@ function Pagination({
3516
3793
  if (pageProp === void 0) setInternal(p);
3517
3794
  onChange?.(p);
3518
3795
  };
3519
- const [jump, setJump] = React24__default.useState("");
3796
+ const [jump, setJump] = React25__default.useState("");
3520
3797
  const submitJump = () => {
3521
3798
  const n = parseInt(jump, 10);
3522
3799
  if (!Number.isNaN(n)) go(Math.min(Math.max(n, 1), total));
@@ -3533,6 +3810,7 @@ function Pagination({
3533
3810
  className: "twc-page",
3534
3811
  "data-size": size,
3535
3812
  "data-active": p === page || void 0,
3813
+ "aria-label": getPageLabel ? getPageLabel(p) : `Page ${p}`,
3536
3814
  "aria-current": p === page ? "page" : void 0,
3537
3815
  onClick: () => go(p),
3538
3816
  children: p
@@ -3565,6 +3843,154 @@ function Pagination({
3565
3843
  ] }) : null
3566
3844
  ] });
3567
3845
  }
3846
+ var TOOLTIP_CSS = `
3847
+ .twc-tooltip-wrap { display: inline-flex; }
3848
+ .twc-tooltip {
3849
+ position: fixed; z-index: var(--z-tooltip);
3850
+ padding: 6px 10px; border-radius: var(--radius-md);
3851
+ background: var(--color-text); color: var(--color-surface);
3852
+ font-family: var(--font-sans); font-size: var(--text-xs); font-weight: var(--font-medium);
3853
+ line-height: 1.3; white-space: normal; max-width: var(--_tw-maxw, 260px); box-shadow: var(--shadow-md);
3854
+ opacity: 0; transform: scale(0.9); transition: opacity var(--duration-fast) var(--ease-out), transform var(--duration-fast) var(--ease-spring);
3855
+ }
3856
+ .twc-tooltip[data-show="true"] { pointer-events: auto; }
3857
+ .twc-tooltip[data-show="true"] { opacity: 1; transform: scale(1); }
3858
+ .twc-tooltip[data-place="top"] { transform-origin: bottom center; translate: -50% 0; }
3859
+ .twc-tooltip[data-place="bottom"] { transform-origin: top center; translate: -50% 0; }
3860
+ .twc-tooltip[data-place="left"] { transform-origin: right center; translate: 0 -50%; }
3861
+ .twc-tooltip[data-place="right"] { transform-origin: left center; translate: 0 -50%; }
3862
+ .twc-tooltip[data-show="true"][data-place="top"], .twc-tooltip[data-show="true"][data-place="bottom"] { transform: scale(1); }
3863
+ .twc-tooltip__arrow { position: absolute; width: 7px; height: 7px; background: var(--color-text); transform: rotate(45deg); }
3864
+ .twc-tooltip[data-place="top"] .twc-tooltip__arrow { bottom: -3px; left: 50%; margin-left: -3.5px; }
3865
+ .twc-tooltip[data-place="bottom"] .twc-tooltip__arrow { top: -3px; left: 50%; margin-left: -3.5px; }
3866
+ .twc-tooltip[data-place="left"] .twc-tooltip__arrow { right: -3px; top: 50%; margin-top: -3.5px; }
3867
+ .twc-tooltip[data-place="right"] .twc-tooltip__arrow { left: -3px; top: 50%; margin-top: -3.5px; }
3868
+ @media (prefers-reduced-motion: reduce) { .twc-tooltip { transition: opacity var(--duration-fast) linear; transform: none; } }
3869
+ `;
3870
+ function Tooltip({
3871
+ children,
3872
+ label,
3873
+ placement = "top",
3874
+ delay = 120,
3875
+ className = "",
3876
+ ...rest
3877
+ }) {
3878
+ const __twcStyles = useScopedStyles("twc-tooltip-styles", TOOLTIP_CSS);
3879
+ const id = React25__default.useId();
3880
+ const [show, setShow] = React25__default.useState(false);
3881
+ const [coords, setCoords] = React25__default.useState(null);
3882
+ const timer = React25__default.useRef(null);
3883
+ const closeTimer = React25__default.useRef(null);
3884
+ const wrapRef = React25__default.useRef(null);
3885
+ const tipRef = React25__default.useRef(null);
3886
+ const open = () => {
3887
+ clearTimeout(closeTimer.current);
3888
+ clearTimeout(timer.current);
3889
+ timer.current = setTimeout(() => setShow(true), delay);
3890
+ };
3891
+ const close = () => {
3892
+ clearTimeout(timer.current);
3893
+ clearTimeout(closeTimer.current);
3894
+ closeTimer.current = setTimeout(() => setShow(false), 120);
3895
+ };
3896
+ const cancelClose = () => {
3897
+ clearTimeout(closeTimer.current);
3898
+ };
3899
+ React25__default.useEffect(() => () => {
3900
+ clearTimeout(timer.current);
3901
+ clearTimeout(closeTimer.current);
3902
+ }, []);
3903
+ const place = React25__default.useCallback(() => {
3904
+ const el = wrapRef.current;
3905
+ if (!el) return;
3906
+ const r = el.getBoundingClientRect();
3907
+ const cx = Math.round(r.left + r.width / 2), cy = Math.round(r.top + r.height / 2);
3908
+ const vw = window.innerWidth, vh = window.innerHeight, gap = 8, margin = 8;
3909
+ const tip = tipRef.current;
3910
+ const tw = tip ? tip.offsetWidth : 0, th = tip ? tip.offsetHeight : 0;
3911
+ const clampX = (c) => tw ? Math.round(Math.min(Math.max(c, margin + tw / 2), vw - margin - tw / 2)) : c;
3912
+ const clampY = (c) => th ? Math.round(Math.min(Math.max(c, margin + th / 2), vh - margin - th / 2)) : c;
3913
+ const spaceAbove = r.top - gap - margin, spaceBelow = vh - r.bottom - gap - margin;
3914
+ const spaceLeft = r.left - gap - margin, spaceRight = vw - r.right - gap - margin;
3915
+ let side = placement;
3916
+ if (placement === "top" && spaceAbove < th && spaceBelow > spaceAbove) side = "bottom";
3917
+ else if (placement === "bottom" && spaceBelow < th && spaceAbove > spaceBelow) side = "top";
3918
+ else if (placement === "left" && spaceLeft < tw && spaceRight > spaceLeft) side = "right";
3919
+ else if (placement === "right" && spaceRight < tw && spaceLeft > spaceRight) side = "left";
3920
+ const base = { left: "auto", right: "auto", top: "auto", bottom: "auto", place: side };
3921
+ if (side === "bottom") setCoords({ ...base, left: clampX(cx), top: Math.round(r.bottom + gap) });
3922
+ else if (side === "left") setCoords({ ...base, top: clampY(cy), right: Math.round(vw - r.left + gap) });
3923
+ else if (side === "right") setCoords({ ...base, top: clampY(cy), left: Math.round(r.right + gap) });
3924
+ else setCoords({ ...base, left: clampX(cx), bottom: Math.round(vh - r.top + gap) });
3925
+ }, [placement]);
3926
+ React25__default.useEffect(() => {
3927
+ place();
3928
+ }, [place]);
3929
+ React25__default.useEffect(() => {
3930
+ if (!show) return void 0;
3931
+ place();
3932
+ const onMove = () => place();
3933
+ window.addEventListener("scroll", onMove, true);
3934
+ window.addEventListener("resize", onMove);
3935
+ return () => {
3936
+ window.removeEventListener("scroll", onMove, true);
3937
+ window.removeEventListener("resize", onMove);
3938
+ };
3939
+ }, [show, place]);
3940
+ React25__default.useEffect(() => {
3941
+ if (!show) return;
3942
+ const onKeyDown = (e) => {
3943
+ if (e.key === "Escape") {
3944
+ clearTimeout(timer.current);
3945
+ setShow(false);
3946
+ }
3947
+ };
3948
+ document.addEventListener("keydown", onKeyDown);
3949
+ return () => document.removeEventListener("keydown", onKeyDown);
3950
+ }, [show]);
3951
+ const describedBy = show ? id : void 0;
3952
+ const trigger = React25__default.isValidElement(children) && children.type !== React25__default.Fragment ? React25__default.cloneElement(children, {
3953
+ "aria-describedby": [children.props["aria-describedby"], describedBy].filter(Boolean).join(" ") || void 0
3954
+ }) : /* @__PURE__ */ jsx("span", { "aria-describedby": describedBy, children });
3955
+ return /* @__PURE__ */ jsxs(
3956
+ "span",
3957
+ {
3958
+ ref: wrapRef,
3959
+ className: `twc-tooltip-wrap ${className}`,
3960
+ onMouseEnter: open,
3961
+ onMouseLeave: close,
3962
+ onFocus: open,
3963
+ onBlur: close,
3964
+ ...rest,
3965
+ children: [
3966
+ __twcStyles,
3967
+ trigger,
3968
+ coords ? createPortal(
3969
+ /* @__PURE__ */ jsxs(
3970
+ "span",
3971
+ {
3972
+ ref: tipRef,
3973
+ id,
3974
+ className: "twc-tooltip",
3975
+ "data-place": coords.place || placement,
3976
+ "data-show": show || void 0,
3977
+ role: "tooltip",
3978
+ "aria-hidden": show ? void 0 : "true",
3979
+ onMouseEnter: cancelClose,
3980
+ onMouseLeave: close,
3981
+ style: { position: "fixed", left: coords.left, right: coords.right, top: coords.top, bottom: coords.bottom },
3982
+ children: [
3983
+ label,
3984
+ /* @__PURE__ */ jsx("span", { className: "twc-tooltip__arrow", "aria-hidden": "true" })
3985
+ ]
3986
+ }
3987
+ ),
3988
+ document.body
3989
+ ) : null
3990
+ ]
3991
+ }
3992
+ );
3993
+ }
3568
3994
  var DT_CSS = `
3569
3995
  .twc-dt { display: flex; flex-direction: column; font-family: var(--font-sans); color: var(--color-text);
3570
3996
  border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-lg);
@@ -4127,9 +4553,9 @@ function runDatatableQuery(rows, query, options = {}) {
4127
4553
  return { rows: page, total, filtered: out };
4128
4554
  }
4129
4555
  function useFloating() {
4130
- const [pos, setPos] = React24__default.useState(null);
4131
- const cfg = React24__default.useRef(null);
4132
- const place = React24__default.useCallback(() => {
4556
+ const [pos, setPos] = React25__default.useState(null);
4557
+ const cfg = React25__default.useRef(null);
4558
+ const place = React25__default.useCallback(() => {
4133
4559
  const c = cfg.current;
4134
4560
  if (!c || !c.el || !c.el.isConnected) return null;
4135
4561
  const r = c.el.getBoundingClientRect();
@@ -4140,17 +4566,17 @@ function useFloating() {
4140
4566
  const top = r.bottom + 6;
4141
4567
  return { top, left, width: c.width, anchorX: r.left + r.width / 2, maxHeight: Math.max(180, vh - top - M) };
4142
4568
  }, []);
4143
- const open = React24__default.useCallback((el, align = "left", width = 220) => {
4569
+ const open = React25__default.useCallback((el, align = "left", width = 220) => {
4144
4570
  cfg.current = { el, align, width };
4145
4571
  const p = place();
4146
4572
  if (p) setPos(p);
4147
4573
  }, [place]);
4148
- const close = React24__default.useCallback(() => {
4574
+ const close = React25__default.useCallback(() => {
4149
4575
  cfg.current = null;
4150
4576
  setPos(null);
4151
4577
  }, []);
4152
4578
  const isOpen = pos != null;
4153
- React24__default.useEffect(() => {
4579
+ React25__default.useEffect(() => {
4154
4580
  if (!isOpen) return void 0;
4155
4581
  const update = () => {
4156
4582
  const p = place();
@@ -4174,9 +4600,9 @@ function Caret({ pos }) {
4174
4600
  return /* @__PURE__ */ jsx("span", { className: "twc-dt__caret", style: { left } });
4175
4601
  }
4176
4602
  function EditCell({ col, value, options, onChange, onCommit, onCommitValue, onCancel, onKeyDown }) {
4177
- const ref = React24__default.useRef(null);
4603
+ const ref = React25__default.useRef(null);
4178
4604
  const isSelect = col.editType === "select" || options && col.editType !== "text";
4179
- React24__default.useEffect(() => {
4605
+ React25__default.useEffect(() => {
4180
4606
  if (isSelect) {
4181
4607
  const t = setTimeout(() => ref.current?.querySelector(".twc-sel__trigger")?.click(), 0);
4182
4608
  return () => clearTimeout(t);
@@ -4352,6 +4778,8 @@ function Datatable({
4352
4778
  aggregationValues = null,
4353
4779
  disableColumnReorder = false,
4354
4780
  disableColumnResize = false,
4781
+ emptyMessage,
4782
+ renderEmpty,
4355
4783
  editMode = false,
4356
4784
  onRowUpdate,
4357
4785
  onRowsChange,
@@ -4379,7 +4807,7 @@ function Datatable({
4379
4807
  ...rest
4380
4808
  }) {
4381
4809
  const __twcStyles = useScopedStyles("twc-dt-styles", DT_CSS);
4382
- const cols = React24__default.useMemo(() => {
4810
+ const cols = React25__default.useMemo(() => {
4383
4811
  const out = columns.map((c) => {
4384
4812
  const isActions = c.type === "actions";
4385
4813
  return {
@@ -4414,7 +4842,7 @@ function Datatable({
4414
4842
  }
4415
4843
  return out;
4416
4844
  }, [columns, rowPinning]);
4417
- const _autoKey = React24__default.useRef();
4845
+ const _autoKey = React25__default.useRef();
4418
4846
  if (!_autoKey.current) _autoKey.current = { map: /* @__PURE__ */ new WeakMap(), n: 0 };
4419
4847
  const keyOf = rowKey || ((r, i) => {
4420
4848
  if (r != null && r.id != null) return r.id;
@@ -4429,17 +4857,17 @@ function Datatable({
4429
4857
  }
4430
4858
  return i;
4431
4859
  });
4432
- const [sort, setSort] = React24__default.useState(null);
4433
- const [filters, setFilters] = React24__default.useState([]);
4434
- const [hidden, setHidden] = React24__default.useState(() => /* @__PURE__ */ new Set());
4435
- const [showRowNum, setShowRowNum] = React24__default.useState(rowNumbers);
4436
- const [wrapped, setWrapped] = React24__default.useState(() => new Set((columns || []).filter((c) => c.wrapText).map((c) => c.field)));
4860
+ const [sort, setSort] = React25__default.useState(null);
4861
+ const [filters, setFilters] = React25__default.useState([]);
4862
+ const [hidden, setHidden] = React25__default.useState(() => /* @__PURE__ */ new Set());
4863
+ const [showRowNum, setShowRowNum] = React25__default.useState(rowNumbers);
4864
+ const [wrapped, setWrapped] = React25__default.useState(() => new Set((columns || []).filter((c) => c.wrapText).map((c) => c.field)));
4437
4865
  const toggleWrap = (field) => setWrapped((w) => {
4438
4866
  const n = new Set(w);
4439
4867
  n.has(field) ? n.delete(field) : n.add(field);
4440
4868
  return n;
4441
4869
  });
4442
- const [pins, setPins] = React24__default.useState(() => {
4870
+ const [pins, setPins] = React25__default.useState(() => {
4443
4871
  const left = [], right = [];
4444
4872
  columns.forEach((c) => {
4445
4873
  if (c.pinned === "left") left.push(c.field);
@@ -4447,34 +4875,34 @@ function Datatable({
4447
4875
  });
4448
4876
  return { left, right };
4449
4877
  });
4450
- const [density, setDensity] = React24__default.useState(densityProp);
4451
- const [quick, setQuick] = React24__default.useState("");
4452
- const [selected, setSelected] = React24__default.useState(() => /* @__PURE__ */ new Set());
4453
- const [activeRow, setActiveRow] = React24__default.useState(null);
4454
- const [activeCell, setActiveCell] = React24__default.useState(null);
4455
- const [page, setPage] = React24__default.useState(0);
4456
- const [rowsPerPage, setRowsPerPage] = React24__default.useState(pageSize > 0 ? pageSize : 10);
4457
- const [colQuery, setColQuery] = React24__default.useState("");
4458
- const [widths, setWidths] = React24__default.useState({});
4459
- const [order, setOrder] = React24__default.useState(() => columns.map((c) => c.field));
4460
- const [drag, setDrag] = React24__default.useState({ from: null, over: null, after: false });
4461
- const [aggOn, setAggOn] = React24__default.useState(showAggregation);
4462
- const [focus, setFocus] = React24__default.useState({ r: 0, c: 0 });
4463
- const gridRef = React24__default.useRef(null);
4464
- const [groupBy, setGroupBy] = React24__default.useState(rowGrouping || []);
4465
- const [collapsed, setCollapsed] = React24__default.useState(() => /* @__PURE__ */ new Set());
4466
- const [pinnedRows, setPinnedRows] = React24__default.useState({ top: [], bottom: [] });
4467
- const [headH, setHeadH] = React24__default.useState(41);
4468
- const theadRef = React24__default.useRef(null);
4469
- const [rowOrder, setRowOrder] = React24__default.useState(null);
4470
- const [rowHeights, setRowHeights] = React24__default.useState({});
4471
- const [rowDrag, setRowDrag] = React24__default.useState({ from: null, over: null, after: false });
4472
- const [rowGrab, setRowGrab] = React24__default.useState(null);
4473
- const [reorderMsg, setReorderMsg] = React24__default.useState("");
4474
- const rowFocusRef = React24__default.useRef(null);
4475
- const rootRef = React24__default.useRef(null);
4476
- const [compact, setCompact] = React24__default.useState(false);
4477
- React24__default.useEffect(() => {
4878
+ const [density, setDensity] = React25__default.useState(densityProp);
4879
+ const [quick, setQuick] = React25__default.useState("");
4880
+ const [selected, setSelected] = React25__default.useState(() => /* @__PURE__ */ new Set());
4881
+ const [activeRow, setActiveRow] = React25__default.useState(null);
4882
+ const [activeCell, setActiveCell] = React25__default.useState(null);
4883
+ const [page, setPage] = React25__default.useState(0);
4884
+ const [rowsPerPage, setRowsPerPage] = React25__default.useState(pageSize > 0 ? pageSize : 10);
4885
+ const [colQuery, setColQuery] = React25__default.useState("");
4886
+ const [widths, setWidths] = React25__default.useState({});
4887
+ const [order, setOrder] = React25__default.useState(() => columns.map((c) => c.field));
4888
+ const [drag, setDrag] = React25__default.useState({ from: null, over: null, after: false });
4889
+ const [aggOn, setAggOn] = React25__default.useState(showAggregation);
4890
+ const [focus, setFocus] = React25__default.useState({ r: 0, c: 0 });
4891
+ const gridRef = React25__default.useRef(null);
4892
+ const [groupBy, setGroupBy] = React25__default.useState(rowGrouping || []);
4893
+ const [collapsed, setCollapsed] = React25__default.useState(() => /* @__PURE__ */ new Set());
4894
+ const [pinnedRows, setPinnedRows] = React25__default.useState({ top: [], bottom: [] });
4895
+ const [headH, setHeadH] = React25__default.useState(41);
4896
+ const theadRef = React25__default.useRef(null);
4897
+ const [rowOrder, setRowOrder] = React25__default.useState(null);
4898
+ const [rowHeights, setRowHeights] = React25__default.useState({});
4899
+ const [rowDrag, setRowDrag] = React25__default.useState({ from: null, over: null, after: false });
4900
+ const [rowGrab, setRowGrab] = React25__default.useState(null);
4901
+ const [reorderMsg, setReorderMsg] = React25__default.useState("");
4902
+ const rowFocusRef = React25__default.useRef(null);
4903
+ const rootRef = React25__default.useRef(null);
4904
+ const [compact, setCompact] = React25__default.useState(false);
4905
+ React25__default.useEffect(() => {
4478
4906
  const el = rootRef.current;
4479
4907
  if (!el) return void 0;
4480
4908
  const measure = () => setCompact((el.clientWidth || 0) < 720);
@@ -4487,11 +4915,11 @@ function Datatable({
4487
4915
  else if (typeof window !== "undefined") window.removeEventListener("resize", measure);
4488
4916
  };
4489
4917
  }, []);
4490
- const scrollRef = React24__default.useRef(null);
4491
- const [scrollTop, setScrollTop] = React24__default.useState(0);
4492
- const [viewportH, setViewportH] = React24__default.useState(0);
4493
- const [pivotOn, setPivotOn] = React24__default.useState(pivotMode);
4494
- const [aggConfig, setAggConfig] = React24__default.useState(() => {
4918
+ const scrollRef = React25__default.useRef(null);
4919
+ const [scrollTop, setScrollTop] = React25__default.useState(0);
4920
+ const [viewportH, setViewportH] = React25__default.useState(0);
4921
+ const [pivotOn, setPivotOn] = React25__default.useState(pivotMode);
4922
+ const [aggConfig, setAggConfig] = React25__default.useState(() => {
4495
4923
  const m = {};
4496
4924
  (columns || []).forEach((c) => {
4497
4925
  if (typeof c.aggregation === "string") m[c.field] = c.aggregation;
@@ -4499,13 +4927,13 @@ function Datatable({
4499
4927
  return m;
4500
4928
  });
4501
4929
  const aggOf = (c) => aggConfig[c.field] || (typeof c.aggregation === "function" ? c.aggregation : null);
4502
- const [pivotConfig, setPivotConfig] = React24__default.useState(() => ({
4930
+ const [pivotConfig, setPivotConfig] = React25__default.useState(() => ({
4503
4931
  rows: pivot && pivot.rows || [],
4504
4932
  columns: pivot && pivot.columns || [],
4505
4933
  values: pivot && pivot.values || []
4506
4934
  }));
4507
4935
  const pivotActive = pivotOn && pivotConfig.rows.length > 0 && pivotConfig.values.length > 0;
4508
- React24__default.useEffect(() => {
4936
+ React25__default.useEffect(() => {
4509
4937
  setOrder((prev) => {
4510
4938
  const fields = columns.map((c) => c.field);
4511
4939
  const kept = prev.filter((f) => fields.includes(f));
@@ -4513,32 +4941,32 @@ function Datatable({
4513
4941
  return [...kept, ...added];
4514
4942
  });
4515
4943
  }, [columns]);
4516
- React24__default.useEffect(() => {
4944
+ React25__default.useEffect(() => {
4517
4945
  setDensity(densityProp);
4518
4946
  }, [densityProp]);
4519
- React24__default.useEffect(() => {
4947
+ React25__default.useEffect(() => {
4520
4948
  setAggOn(showAggregation);
4521
4949
  }, [showAggregation]);
4522
4950
  const rowGroupingKey = (rowGrouping || []).join("\0");
4523
- const rowGroupingKeyRef = React24__default.useRef(rowGroupingKey);
4524
- React24__default.useEffect(() => {
4951
+ const rowGroupingKeyRef = React25__default.useRef(rowGroupingKey);
4952
+ React25__default.useEffect(() => {
4525
4953
  if (rowGroupingKeyRef.current === rowGroupingKey) return;
4526
4954
  rowGroupingKeyRef.current = rowGroupingKey;
4527
4955
  setGroupBy(rowGrouping || []);
4528
4956
  }, [rowGroupingKey]);
4529
4957
  const widthOf = (c) => widths[c.field] ?? c.width ?? 160;
4530
- const [colMenu, setColMenu] = React24__default.useState(null);
4958
+ const [colMenu, setColMenu] = React25__default.useState(null);
4531
4959
  const [menuPos, openMenu, closeMenu] = useFloating();
4532
- const [panel, setPanel] = React24__default.useState(null);
4960
+ const [panel, setPanel] = React25__default.useState(null);
4533
4961
  const [panelPos, openPanel, closePanel] = useFloating();
4534
- const [rowMenu, setRowMenu] = React24__default.useState(null);
4962
+ const [rowMenu, setRowMenu] = React25__default.useState(null);
4535
4963
  const [rowMenuPos, openRowMenu, closeRowMenu] = useFloating();
4536
- const [exportOpen, setExportOpen] = React24__default.useState(false);
4964
+ const [exportOpen, setExportOpen] = React25__default.useState(false);
4537
4965
  const [exportPos, openExport, closeExport] = useFloating();
4538
- const menuTriggerRef = React24__default.useRef(null);
4539
- const colMenuRef = React24__default.useRef(null);
4540
- const rowMenuRef = React24__default.useRef(null);
4541
- const exportMenuRef = React24__default.useRef(null);
4966
+ const menuTriggerRef = React25__default.useRef(null);
4967
+ const colMenuRef = React25__default.useRef(null);
4968
+ const rowMenuRef = React25__default.useRef(null);
4969
+ const exportMenuRef = React25__default.useRef(null);
4542
4970
  function focusFirstItem(container) {
4543
4971
  if (!container) return;
4544
4972
  const first = container.querySelector('[role="menuitem"]:not([disabled])');
@@ -4577,16 +5005,16 @@ function Datatable({
4577
5005
  restoreTriggerFocus();
4578
5006
  }
4579
5007
  }
4580
- React24__default.useEffect(() => {
5008
+ React25__default.useEffect(() => {
4581
5009
  if (colMenu) focusFirstItem(colMenuRef.current);
4582
5010
  }, [colMenu]);
4583
- React24__default.useEffect(() => {
5011
+ React25__default.useEffect(() => {
4584
5012
  if (rowMenu) focusFirstItem(rowMenuRef.current);
4585
5013
  }, [rowMenu]);
4586
- React24__default.useEffect(() => {
5014
+ React25__default.useEffect(() => {
4587
5015
  if (exportOpen) focusFirstItem(exportMenuRef.current);
4588
5016
  }, [exportOpen]);
4589
- React24__default.useEffect(() => {
5017
+ React25__default.useEffect(() => {
4590
5018
  if (!colMenu && !panel && !rowMenu) return;
4591
5019
  const closeAll = () => {
4592
5020
  setColMenu(null);
@@ -4609,7 +5037,7 @@ function Datatable({
4609
5037
  document.removeEventListener("keydown", onKey);
4610
5038
  };
4611
5039
  }, [colMenu, panel, rowMenu]);
4612
- React24__default.useEffect(() => {
5040
+ React25__default.useEffect(() => {
4613
5041
  if (!exportOpen) return;
4614
5042
  const close = () => {
4615
5043
  setExportOpen(false);
@@ -4628,9 +5056,9 @@ function Datatable({
4628
5056
  document.removeEventListener("keydown", onKey);
4629
5057
  };
4630
5058
  }, [exportOpen]);
4631
- const colByField = React24__default.useMemo(() => Object.fromEntries(cols.map((c) => [c.field, c])), [cols]);
5059
+ const colByField = React25__default.useMemo(() => Object.fromEntries(cols.map((c) => [c.field, c])), [cols]);
4632
5060
  const visibleCols = cols.filter((c) => !hidden.has(c.field));
4633
- const ordered = React24__default.useMemo(() => {
5061
+ const ordered = React25__default.useMemo(() => {
4634
5062
  const orderIdx = (f) => {
4635
5063
  const i = order.indexOf(f);
4636
5064
  return i === -1 ? 9999 : i;
@@ -4665,7 +5093,7 @@ function Datatable({
4665
5093
  }
4666
5094
  return {};
4667
5095
  };
4668
- const processed = React24__default.useMemo(() => {
5096
+ const processed = React25__default.useMemo(() => {
4669
5097
  if (serverMode) {
4670
5098
  if (rowOrder) {
4671
5099
  const idx = new Map(rowOrder.map((k, i) => [k, i]));
@@ -4704,10 +5132,10 @@ function Datatable({
4704
5132
  const totalRows = serverMode ? serverTotal : processed.length;
4705
5133
  const totalPages = paginated ? Math.max(1, Math.ceil(totalRows / rowsPerPage)) : 1;
4706
5134
  const paged = !paginated || serverMode ? processed : processed.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage);
4707
- React24__default.useEffect(() => {
5135
+ React25__default.useEffect(() => {
4708
5136
  if (page > totalPages - 1) setPage(0);
4709
5137
  }, [totalPages]);
4710
- const activeGroupBy = React24__default.useMemo(() => groupBy.filter((f) => colByField[f]), [groupBy, colByField]);
5138
+ const activeGroupBy = React25__default.useMemo(() => groupBy.filter((f) => colByField[f]), [groupBy, colByField]);
4711
5139
  function subtotalText(rowsIn) {
4712
5140
  const parts = [];
4713
5141
  for (const c of ordered) {
@@ -4734,7 +5162,7 @@ function Datatable({
4734
5162
  if (agg === "max") return Math.max(...nums);
4735
5163
  return null;
4736
5164
  }
4737
- const displayItems = React24__default.useMemo(() => {
5165
+ const displayItems = React25__default.useMemo(() => {
4738
5166
  if (!activeGroupBy.length) return null;
4739
5167
  const build = (rowsIn, depth, prefix) => {
4740
5168
  if (depth >= activeGroupBy.length) return rowsIn.map((row) => ({ kind: "leaf", row }));
@@ -4776,7 +5204,7 @@ function Datatable({
4776
5204
  return { top, bottom };
4777
5205
  });
4778
5206
  }
4779
- React24__default.useEffect(() => {
5207
+ React25__default.useEffect(() => {
4780
5208
  if (!rowPinning) return;
4781
5209
  const measure = () => {
4782
5210
  if (theadRef.current) setHeadH(theadRef.current.offsetHeight || 41);
@@ -4790,12 +5218,12 @@ function Datatable({
4790
5218
  const pinnedTopRows = canPinRows ? pinnedRows.top.map((k) => leafRows.find((r, i) => keyOf(r, i) === k)).filter(Boolean) : [];
4791
5219
  const pinnedBottomRows = canPinRows ? pinnedRows.bottom.map((k) => leafRows.find((r, i) => keyOf(r, i) === k)).filter(Boolean) : [];
4792
5220
  const middleRows = canPinRows ? leafRows.filter((r, i) => !pinSideOf(keyOf(r, i))) : leafRows;
4793
- const keyIndex = React24__default.useMemo(() => {
5221
+ const keyIndex = React25__default.useMemo(() => {
4794
5222
  const m = /* @__PURE__ */ new Map();
4795
5223
  leafRows.forEach((r, i) => m.set(keyOf(r, i), i));
4796
5224
  return m;
4797
5225
  }, [leafRows]);
4798
- const keyIndexMid = React24__default.useMemo(() => {
5226
+ const keyIndexMid = React25__default.useMemo(() => {
4799
5227
  const m = /* @__PURE__ */ new Map();
4800
5228
  middleRows.forEach((r, i) => m.set(keyOf(r, keyIndex.get(keyOf(r)) ?? i), i));
4801
5229
  return m;
@@ -4880,7 +5308,7 @@ function Datatable({
4880
5308
  setReorderMsg("Reorder cancelled.");
4881
5309
  }
4882
5310
  }
4883
- React24__default.useEffect(() => {
5311
+ React25__default.useEffect(() => {
4884
5312
  if (rowFocusRef.current == null) return;
4885
5313
  const key = rowFocusRef.current;
4886
5314
  rowFocusRef.current = null;
@@ -4892,7 +5320,7 @@ function Datatable({
4892
5320
  });
4893
5321
  const estRowH = rowHeight ?? (density === "compact" ? 36 : density === "comfortable" ? 56 : 44);
4894
5322
  const virtualizing = virtualized && !paginated && !activeGroupBy.length && !loading;
4895
- React24__default.useEffect(() => {
5323
+ React25__default.useEffect(() => {
4896
5324
  if (!virtualizing) return;
4897
5325
  const el = scrollRef.current;
4898
5326
  if (!el) return;
@@ -4902,13 +5330,13 @@ function Datatable({
4902
5330
  if (ro) ro.observe(el);
4903
5331
  return () => ro && ro.disconnect();
4904
5332
  }, [virtualizing, height]);
4905
- const onScrollVirtual = React24__default.useCallback((e) => {
5333
+ const onScrollVirtual = React25__default.useCallback((e) => {
4906
5334
  setScrollTop(e.currentTarget.scrollTop);
4907
5335
  }, []);
4908
5336
  const vh = viewportH || (typeof height === "number" ? height : 440);
4909
- const rowHRef = React24__default.useRef(/* @__PURE__ */ new Map());
4910
- const [measureTick, setMeasureTick] = React24__default.useState(0);
4911
- const offsets = React24__default.useMemo(() => {
5337
+ const rowHRef = React25__default.useRef(/* @__PURE__ */ new Map());
5338
+ const [measureTick, setMeasureTick] = React25__default.useState(0);
5339
+ const offsets = React25__default.useMemo(() => {
4912
5340
  if (!virtualizing) return null;
4913
5341
  const total = middleRows.length;
4914
5342
  const arr = new Float64Array(total + 1);
@@ -4918,7 +5346,7 @@ function Datatable({
4918
5346
  }
4919
5347
  return arr;
4920
5348
  }, [virtualizing, middleRows, estRowH, measureTick]);
4921
- const vWindow = React24__default.useMemo(() => {
5349
+ const vWindow = React25__default.useMemo(() => {
4922
5350
  if (!virtualizing || !offsets) return null;
4923
5351
  const total = middleRows.length;
4924
5352
  const totalH = offsets[total];
@@ -4947,7 +5375,7 @@ function Datatable({
4947
5375
  if (start > end) start = end;
4948
5376
  return { start, end, padTop: offsets[start], padBottom: Math.max(0, totalH - offsets[end]) };
4949
5377
  }, [virtualizing, offsets, middleRows.length, vh, scrollTop, overscan]);
4950
- React24__default.useLayoutEffect(() => {
5378
+ React25__default.useLayoutEffect(() => {
4951
5379
  if (!virtualizing) return;
4952
5380
  const el = scrollRef.current;
4953
5381
  if (!el) return;
@@ -4962,9 +5390,9 @@ function Datatable({
4962
5390
  }
4963
5391
  if (changed) setMeasureTick((t) => t + 1);
4964
5392
  });
4965
- const onServerChangeRef = React24__default.useRef(onServerChange);
5393
+ const onServerChangeRef = React25__default.useRef(onServerChange);
4966
5394
  onServerChangeRef.current = onServerChange;
4967
- React24__default.useEffect(() => {
5395
+ React25__default.useEffect(() => {
4968
5396
  if (!serverMode) return;
4969
5397
  const t = setTimeout(() => {
4970
5398
  onServerChangeRef.current?.({
@@ -4999,8 +5427,8 @@ function Datatable({
4999
5427
  return n;
5000
5428
  });
5001
5429
  }
5002
- const movableMidFields = React24__default.useMemo(
5003
- () => ordered.filter((c) => !pins.left.includes(c.field) && !pins.right.includes(c.field)).map((c) => c.field),
5430
+ const movableMidFields = React25__default.useMemo(
5431
+ () => ordered.filter((c) => !pins.left.includes(c.field) && !pins.right.includes(c.field) && c.reorderable !== false).map((c) => c.field),
5004
5432
  [ordered, pins]
5005
5433
  );
5006
5434
  function moveCol(field, dir) {
@@ -5030,7 +5458,7 @@ function Datatable({
5030
5458
  }
5031
5459
  return [...set].sort((a, b) => a.localeCompare(b)).map((v) => ({ value: v, label: v }));
5032
5460
  }
5033
- const [resizing, setResizing] = React24__default.useState(false);
5461
+ const [resizing, setResizing] = React25__default.useState(false);
5034
5462
  function startResize(e, field) {
5035
5463
  e.preventDefault();
5036
5464
  e.stopPropagation();
@@ -5137,7 +5565,7 @@ function Datatable({
5137
5565
  if (!agg) return null;
5138
5566
  if (aggregationValues && col.field in aggregationValues) {
5139
5567
  const pre = aggregationValues[col.field];
5140
- if (pre && typeof pre === "object" && !React24__default.isValidElement(pre)) {
5568
+ if (pre && typeof pre === "object" && !React25__default.isValidElement(pre)) {
5141
5569
  if (typeof agg === "string" && agg in pre) return pre[agg];
5142
5570
  } else {
5143
5571
  return pre;
@@ -5255,14 +5683,14 @@ function Datatable({
5255
5683
  focusCell(r, c);
5256
5684
  }
5257
5685
  }
5258
- React24__default.useEffect(() => {
5686
+ React25__default.useEffect(() => {
5259
5687
  setFocus((f) => ({ r: Math.min(f.r, Math.max(0, leafRows.length - 1)), c: Math.min(f.c, Math.max(0, ordered.length - 1)) }));
5260
5688
  }, [leafRows.length, ordered.length, page]);
5261
- const selectedRows = React24__default.useMemo(() => rows.filter((r, i) => selected.has(keyOf(r, i))), [rows, selected]);
5689
+ const selectedRows = React25__default.useMemo(() => rows.filter((r, i) => selected.has(keyOf(r, i))), [rows, selected]);
5262
5690
  function clearSelection() {
5263
5691
  setSelected(/* @__PURE__ */ new Set());
5264
5692
  }
5265
- const [editing, setEditing] = React24__default.useState(null);
5693
+ const [editing, setEditing] = React25__default.useState(null);
5266
5694
  const isColEditable = (c) => c.type !== "actions" && (c.editable ?? (editMode && c.editable !== false));
5267
5695
  function beginEdit(rowK, col, row) {
5268
5696
  if (!isColEditable(col)) return;
@@ -5297,17 +5725,17 @@ function Datatable({
5297
5725
  cancelEdit();
5298
5726
  }
5299
5727
  }
5300
- const batchEditableCols = React24__default.useMemo(
5728
+ const batchEditableCols = React25__default.useMemo(
5301
5729
  () => cols.filter((c) => c.type !== "actions" && (c.editable ?? (editMode && c.editable !== false))),
5302
5730
  [cols, editMode]
5303
5731
  );
5304
- const [batchEdit, setBatchEdit] = React24__default.useState(null);
5732
+ const [batchEdit, setBatchEdit] = React25__default.useState(null);
5305
5733
  const [batchEditPos, openBatchEdit, closeBatchEdit] = useFloating();
5306
5734
  function openBatchEditor(el) {
5307
5735
  setBatchEdit({ fields: {}, values: {} });
5308
5736
  openBatchEdit(el, "left", 320);
5309
5737
  }
5310
- React24__default.useEffect(() => {
5738
+ React25__default.useEffect(() => {
5311
5739
  if (!batchEdit) return;
5312
5740
  const close = () => {
5313
5741
  setBatchEdit(null);
@@ -5326,7 +5754,7 @@ function Datatable({
5326
5754
  document.removeEventListener("keydown", onKey);
5327
5755
  };
5328
5756
  }, [batchEdit]);
5329
- React24__default.useEffect(() => {
5757
+ React25__default.useEffect(() => {
5330
5758
  if (batchEdit && selected.size === 0) {
5331
5759
  setBatchEdit(null);
5332
5760
  closeBatchEdit();
@@ -5363,7 +5791,7 @@ function Datatable({
5363
5791
  setBatchEdit(null);
5364
5792
  closeBatchEdit();
5365
5793
  }
5366
- React24__default.useEffect(() => {
5794
+ React25__default.useEffect(() => {
5367
5795
  if (!editing) return;
5368
5796
  const onDown = (e) => {
5369
5797
  if (e.target.closest(".twc-dt__editor-wrap") || e.target.closest(".twc-pop")) return;
@@ -5385,13 +5813,12 @@ function Datatable({
5385
5813
  })() : [];
5386
5814
  const menu = [...items.filter((a) => a.showInMenu), ...pinItems];
5387
5815
  return /* @__PURE__ */ jsxs("div", { className: "twc-dt__actions", style: { justifyContent: col.align === "right" ? "flex-end" : "flex-start" }, children: [
5388
- inline.map((a, i) => /* @__PURE__ */ jsx(
5816
+ inline.map((a, i) => /* @__PURE__ */ jsx(Tooltip, { label: a.label, placement: "top", children: /* @__PURE__ */ jsx(
5389
5817
  "button",
5390
5818
  {
5391
5819
  type: "button",
5392
5820
  className: "twc-dt__act",
5393
5821
  "data-danger": a.danger || void 0,
5394
- title: a.label,
5395
5822
  "aria-label": a.label,
5396
5823
  disabled: a.disabled,
5397
5824
  onClick: (e) => {
@@ -5399,16 +5826,14 @@ function Datatable({
5399
5826
  a.onClick?.(row);
5400
5827
  },
5401
5828
  children: a.icon
5402
- },
5403
- i
5404
- )),
5405
- menu.length ? /* @__PURE__ */ jsx(
5829
+ }
5830
+ ) }, i)),
5831
+ menu.length ? /* @__PURE__ */ jsx(Tooltip, { label: "More actions", placement: "top", children: /* @__PURE__ */ jsx(
5406
5832
  "button",
5407
5833
  {
5408
5834
  type: "button",
5409
5835
  className: "twc-dt__act",
5410
5836
  "aria-label": "More actions",
5411
- title: "More",
5412
5837
  "aria-haspopup": "menu",
5413
5838
  "aria-expanded": rowMenu && rowMenu.row === row || void 0,
5414
5839
  onClick: (e) => {
@@ -5425,7 +5850,7 @@ function Datatable({
5425
5850
  },
5426
5851
  children: /* @__PURE__ */ jsx(Svg, { d: I.more })
5427
5852
  }
5428
- ) : null
5853
+ ) }) : null
5429
5854
  ] });
5430
5855
  }
5431
5856
  const tableMinWidth = leadW + ordered.reduce((a, c) => a + widthOf(c), 0);
@@ -5969,7 +6394,7 @@ function Datatable({
5969
6394
  const st = stickyOf(c.field);
5970
6395
  const sorted = sort && sort.field === c.field ? sort.dir : void 0;
5971
6396
  const w = widthOf(c);
5972
- const reorderable = !disableColumnReorder && st.pin == null && c.type !== "actions";
6397
+ const reorderable = !disableColumnReorder && st.pin == null && c.type !== "actions" && c.reorderable !== false;
5973
6398
  const resizable = !disableColumnResize && c.resizable !== false;
5974
6399
  const hasMenu = !c.disableColumnMenu && (c.sortable || c.filterable || c.pinnable || c.hideable);
5975
6400
  return /* @__PURE__ */ jsxs(
@@ -6093,14 +6518,17 @@ function Datatable({
6093
6518
  );
6094
6519
  })
6095
6520
  ] }) }),
6096
- /* @__PURE__ */ jsx("tbody", { children: loading ? Array.from({ length: paginated ? Math.min(rowsPerPage, 8) : 8 }).map((_, ri) => /* @__PURE__ */ jsxs("tr", { className: "twc-dt__row", children: [
6097
- checkboxSelection ? /* @__PURE__ */ jsx("td", { className: "twc-dt__td", "data-pin": "left", style: { insetInlineStart: 0, width: 44 }, children: /* @__PURE__ */ jsx("span", { className: "twc-dt__sk", style: { "--_w": "18px", height: 18, borderRadius: 4 } }) }) : null,
6098
- showRowNum ? /* @__PURE__ */ jsx("td", { className: "twc-dt__td twc-dt__rownum", "aria-hidden": "true", "data-pin": "left", style: { insetInlineStart: numLeft, width: NUM_W }, children: /* @__PURE__ */ jsx("span", { className: "twc-dt__sk", style: { "--_w": "16px", height: 14, borderRadius: 4 } }) }) : null,
6521
+ /* @__PURE__ */ jsx("tbody", { children: loading ? Array.from({ length: paginated ? Math.min(rowsPerPage, 8) : 8 }).map((_, ri) => /* @__PURE__ */ jsxs("tr", { className: "twc-dt__row", role: "row", children: [
6522
+ checkboxSelection ? /* @__PURE__ */ jsx("td", { className: "twc-dt__td", role: "gridcell", "data-pin": "left", style: { insetInlineStart: 0, width: 44 }, children: /* @__PURE__ */ jsx("span", { className: "twc-dt__sk", "aria-hidden": "true", style: { "--_w": "18px", height: 18, borderRadius: 4 } }) }) : null,
6523
+ showRowNum ? /* @__PURE__ */ 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__ */ jsx("span", { className: "twc-dt__sk", "aria-hidden": "true", style: { "--_w": "16px", height: 14, borderRadius: 4 } }) }) : null,
6099
6524
  ordered.map((c, ci) => {
6100
6525
  const st = stickyOf(c.field);
6101
- return /* @__PURE__ */ 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__ */ jsx("span", { className: "twc-dt__sk", style: { "--_w": `${45 + (ri * 13 + ci * 29) % 45}%` } }) }, c.field);
6526
+ return /* @__PURE__ */ 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__ */ jsx("span", { className: "twc-dt__sk", "aria-hidden": "true", style: { "--_w": `${45 + (ri * 13 + ci * 29) % 45}%` } }) }, c.field);
6102
6527
  })
6103
- ] }, ri)) : (displayItems ? displayItems.length === 0 : paged.length === 0) ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { className: "twc-dt__td", colSpan: totalCols, style: { maxWidth: "none" }, children: /* @__PURE__ */ jsx("div", { className: "twc-dt__empty", children: "No rows match your filters" }) }) }) : displayItems ? (() => {
6528
+ ] }, ri)) : (displayItems ? displayItems.length === 0 : paged.length === 0) ? (
6529
+ // #121: customizable, filter-aware empty state. #122: grid roles on the empty row.
6530
+ /* @__PURE__ */ jsx("tr", { role: "row", "aria-rowindex": 2, children: /* @__PURE__ */ jsx("td", { className: "twc-dt__td", role: "gridcell", colSpan: totalCols, style: { maxWidth: "none" }, children: /* @__PURE__ */ jsx("div", { className: "twc-dt__empty", children: renderEmpty ? renderEmpty() : emptyMessage ?? (filters.length > 0 || quick.trim() !== "" ? "No rows match your filters" : "No rows") }) }) })
6531
+ ) : displayItems ? (() => {
6104
6532
  let li = -1;
6105
6533
  return displayItems.map((item) => item.kind === "group" ? renderGroupRow(item) : renderLeaf(item.row, ++li));
6106
6534
  })() : /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -6113,13 +6541,13 @@ function Datatable({
6113
6541
  pinnedBottomRows.map((row) => renderLeaf(row, keyIndex.get(keyOf(row)), "bottom"))
6114
6542
  ] }) }),
6115
6543
  hasAggregation && aggOn && !loading && paged.length > 0 ? /* @__PURE__ */ jsx("tfoot", { children: /* @__PURE__ */ jsxs("tr", { role: "row", children: [
6116
- checkboxSelection ? /* @__PURE__ */ jsx("td", { "data-pin": "left", "data-pin-edge": pins.left.length || showRowNum ? void 0 : "left", style: { insetInlineStart: 0, width: 44 } }) : null,
6117
- showRowNum ? /* @__PURE__ */ 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,
6544
+ checkboxSelection ? /* @__PURE__ */ jsx("td", { role: "gridcell", "data-pin": "left", "data-pin-edge": pins.left.length || showRowNum ? void 0 : "left", style: { insetInlineStart: 0, width: 44 } }) : null,
6545
+ showRowNum ? /* @__PURE__ */ 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,
6118
6546
  ordered.map((c) => {
6119
6547
  const st = stickyOf(c.field);
6120
6548
  const v = aggregate(c);
6121
6549
  const display = v == null ? null : c.aggregationFormatter ? c.aggregationFormatter(v) : c.valueFormatter ? c.valueFormatter(v, null) : typeof v === "number" ? v.toLocaleString() : v;
6122
- return /* @__PURE__ */ 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__ */ jsxs(Fragment, { children: [
6550
+ return /* @__PURE__ */ 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__ */ jsxs(Fragment, { children: [
6123
6551
  typeof aggOf(c) === "string" ? /* @__PURE__ */ jsx("span", { className: "twc-dt__agg-label", children: aggLabels[aggOf(c)] }) : null,
6124
6552
  /* @__PURE__ */ jsx("span", { className: "twc-dt__agg-val", children: display })
6125
6553
  ] }) }, c.field);
@@ -6162,7 +6590,7 @@ function Datatable({
6162
6590
  })() }),
6163
6591
  paginated ? /* @__PURE__ */ jsxs("div", { className: "twc-dt__rpp", children: [
6164
6592
  /* @__PURE__ */ jsx("span", { className: "twc-dt__rpp-label", children: "Rows per page" }),
6165
- /* @__PURE__ */ jsx("div", { style: { width: 78 }, children: /* @__PURE__ */ jsx(Select, { size: "sm", portal: true, value: String(rowsPerPage), options: rppOptions, placement: "top", onChange: (v) => {
6593
+ /* @__PURE__ */ jsx("div", { style: { width: 78 }, children: /* @__PURE__ */ jsx(Select, { size: "sm", portal: true, searchable: false, value: String(rowsPerPage), options: rppOptions, placement: "top", onChange: (v) => {
6166
6594
  setRowsPerPage(Number(v));
6167
6595
  setPage(0);
6168
6596
  } }) })
@@ -6463,7 +6891,7 @@ function Datatable({
6463
6891
  )
6464
6892
  ] }) : null,
6465
6893
  shownColRows.length === 0 ? rowNumMatch ? null : /* @__PURE__ */ jsx("div", { className: "twc-dt__empty", style: { padding: "18px 12px" }, children: "No columns found" }) : shownColRows.map((c) => {
6466
- const canDrag = !disableColumnReorder && !colQuery.trim();
6894
+ const canDrag = !disableColumnReorder && c.reorderable !== false && !colQuery.trim();
6467
6895
  return /* @__PURE__ */ jsxs(
6468
6896
  "div",
6469
6897
  {
@@ -6840,6 +7268,7 @@ var DATEPICKER_CSS = `
6840
7268
  .twc-dp__day[data-today="true"] { font-weight: var(--font-bold); box-shadow: inset 0 0 0 1px var(--color-primary-border); }
6841
7269
  .twc-dp__day[data-selected="true"] { background: var(--color-primary); color: var(--color-primary-fg); font-weight: var(--font-bold); }
6842
7270
  .twc-dp__day:disabled { color: var(--color-text-subtle); opacity: 0.4; cursor: not-allowed; }
7271
+ .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; }
6843
7272
  .twc-dp__day:focus-visible { outline: none; box-shadow: var(--ring); }
6844
7273
  .twc-dp__months { display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px; }
6845
7274
  .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); }
@@ -6885,23 +7314,23 @@ function DatePicker({
6885
7314
  }) {
6886
7315
  const __twcStyles0 = useScopedStyles("twc-field-styles", FIELD_CSS3);
6887
7316
  const __twcStyles1 = useScopedStyles("twc-datepicker-styles", DATEPICKER_CSS);
6888
- const autoId = React24__default.useId();
7317
+ const autoId = React25__default.useId();
6889
7318
  const fieldId = id || autoId;
6890
7319
  const descId = `${fieldId}-desc`;
6891
7320
  const invalid = Boolean(error);
6892
- const [internal, setInternal] = React24__default.useState(defaultValue);
7321
+ const [internal, setInternal] = React25__default.useState(defaultValue);
6893
7322
  const selected = value !== void 0 ? value : internal;
6894
- const [open, setOpen] = React24__default.useState(false);
6895
- const [view, setView] = React24__default.useState(selected || /* @__PURE__ */ new Date());
6896
- const [mode, setMode] = React24__default.useState("days");
6897
- const [focusDate, setFocusDate] = React24__default.useState(null);
6898
- const [coords, setCoords] = React24__default.useState(null);
6899
- const wrapRef = React24__default.useRef(null);
6900
- const triggerRef = React24__default.useRef(null);
6901
- const popRef = React24__default.useRef(null);
6902
- const gridRef = React24__default.useRef(null);
7323
+ const [open, setOpen] = React25__default.useState(false);
7324
+ const [view, setView] = React25__default.useState(selected || /* @__PURE__ */ new Date());
7325
+ const [mode, setMode] = React25__default.useState("days");
7326
+ const [focusDate, setFocusDate] = React25__default.useState(null);
7327
+ const [coords, setCoords] = React25__default.useState(null);
7328
+ const wrapRef = React25__default.useRef(null);
7329
+ const triggerRef = React25__default.useRef(null);
7330
+ const popRef = React25__default.useRef(null);
7331
+ const gridRef = React25__default.useRef(null);
6903
7332
  const POP_W = 280;
6904
- const place = React24__default.useCallback(() => {
7333
+ const place = React25__default.useCallback(() => {
6905
7334
  const el = triggerRef.current;
6906
7335
  if (!el) return;
6907
7336
  const r = el.getBoundingClientRect();
@@ -6912,7 +7341,7 @@ function DatePicker({
6912
7341
  left = Math.max(8, left);
6913
7342
  setCoords({ left, top: flip ? void 0 : Math.round(r.bottom + 6), bottom: flip ? Math.round(vh - r.top + 6) : void 0 });
6914
7343
  }, []);
6915
- React24__default.useEffect(() => {
7344
+ React25__default.useEffect(() => {
6916
7345
  if (!open) return void 0;
6917
7346
  place();
6918
7347
  const onMove = () => place();
@@ -6923,7 +7352,7 @@ function DatePicker({
6923
7352
  window.removeEventListener("resize", onMove);
6924
7353
  };
6925
7354
  }, [open, place]);
6926
- React24__default.useEffect(() => {
7355
+ React25__default.useEffect(() => {
6927
7356
  if (!open) return;
6928
7357
  setView(selected || /* @__PURE__ */ new Date());
6929
7358
  setMode("days");
@@ -6943,8 +7372,8 @@ function DatePicker({
6943
7372
  document.removeEventListener("keydown", onKey);
6944
7373
  };
6945
7374
  }, [open]);
6946
- const prevOpen = React24__default.useRef(false);
6947
- React24__default.useEffect(() => {
7375
+ const prevOpen = React25__default.useRef(false);
7376
+ React25__default.useEffect(() => {
6948
7377
  let id2;
6949
7378
  if (!prevOpen.current && open) {
6950
7379
  id2 = setTimeout(() => gridRef.current?.querySelector('[tabindex="0"]')?.focus({ preventScroll: true }), 30);
@@ -6955,9 +7384,9 @@ function DatePicker({
6955
7384
  prevOpen.current = open;
6956
7385
  return () => clearTimeout(id2);
6957
7386
  }, [open]);
6958
- const months = React24__default.useMemo(() => locale === void 0 ? MONTHS : monthNames(locale), [locale]);
6959
- const monthsShort = React24__default.useMemo(() => locale === void 0 ? MONTHS.map((n) => n.slice(0, 3)) : monthShortNames(locale), [locale]);
6960
- const dows = React24__default.useMemo(
7387
+ const months = React25__default.useMemo(() => locale === void 0 ? MONTHS : monthNames(locale), [locale]);
7388
+ const monthsShort = React25__default.useMemo(() => locale === void 0 ? MONTHS.map((n) => n.slice(0, 3)) : monthShortNames(locale), [locale]);
7389
+ const dows = React25__default.useMemo(
6961
7390
  () => locale === void 0 ? Array.from({ length: 7 }, (_, i) => DOW[(i + weekStartsOn) % 7]) : weekdayNames(locale, weekStartsOn),
6962
7391
  [locale, weekStartsOn]
6963
7392
  );
@@ -6985,7 +7414,7 @@ function DatePicker({
6985
7414
  };
6986
7415
  const cells = Array.from({ length: 42 }, (_, i) => new Date(gridStart.getFullYear(), gridStart.getMonth(), gridStart.getDate() + i));
6987
7416
  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);
6988
- React24__default.useEffect(() => {
7417
+ React25__default.useEffect(() => {
6989
7418
  if (!open || mode !== "days" || !focusDate) return;
6990
7419
  gridRef.current?.querySelector(`[data-key="${keyOf(focusDate)}"]`)?.focus();
6991
7420
  }, [focusDate, open, mode]);
@@ -7075,6 +7504,7 @@ function DatePicker({
7075
7504
  /* @__PURE__ */ jsxs("div", { className: "twc-dp__head", children: [
7076
7505
  /* @__PURE__ */ 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__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "m15 18-6-6 6-6" }) }) }),
7077
7506
  /* @__PURE__ */ jsx("button", { type: "button", className: "twc-dp__title", onClick: () => setMode(mode === "days" ? "months" : "days"), children: mode === "days" ? `${months[m]} ${y}` : y }),
7507
+ /* @__PURE__ */ jsx("span", { className: "twc-dp__sr", "aria-live": "polite", children: mode === "days" ? `${months[m]} ${y}` : y }),
7078
7508
  /* @__PURE__ */ 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__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" }) }) })
7079
7509
  ] }),
7080
7510
  mode === "days" ? /* @__PURE__ */ jsxs("div", { className: "twc-dp__grid", ref: gridRef, onKeyDown: onGridKeyDown, children: [
@@ -7175,6 +7605,7 @@ var RANGE_CSS = `
7175
7605
  .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); }
7176
7606
  .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); }
7177
7607
  .twc-drp__day:disabled { opacity: 0.3; cursor: not-allowed; }
7608
+ .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; }
7178
7609
  [dir="rtl"] .twc-drp__nav svg { transform: scaleX(-1); }
7179
7610
  `;
7180
7611
  var MONTHS2 = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
@@ -7200,6 +7631,9 @@ function DateRangePicker({
7200
7631
  presets = true,
7201
7632
  locale,
7202
7633
  weekStartsOn = 0,
7634
+ min,
7635
+ max,
7636
+ disabledDate,
7203
7637
  disabled = false,
7204
7638
  tone = "primary",
7205
7639
  onChange,
@@ -7208,23 +7642,23 @@ function DateRangePicker({
7208
7642
  }) {
7209
7643
  const __twcStyles0 = useScopedStyles("twc-field-styles", FIELD_CSS4);
7210
7644
  const __twcStyles1 = useScopedStyles("twc-drp-styles", RANGE_CSS);
7211
- const [internal, setInternal] = React24__default.useState(defaultValue);
7645
+ const [internal, setInternal] = React25__default.useState(defaultValue);
7212
7646
  const range = value !== void 0 ? value : internal;
7213
- const [open, setOpen] = React24__default.useState(false);
7214
- const [view, setView] = React24__default.useState(range.start || /* @__PURE__ */ new Date());
7215
- const [hover, setHover] = React24__default.useState(null);
7216
- const [coords, setCoords] = React24__default.useState(null);
7217
- const wrapRef = React24__default.useRef(null);
7218
- const triggerRef = React24__default.useRef(null);
7219
- const popRef = React24__default.useRef(null);
7220
- const labelId = React24__default.useId();
7647
+ const [open, setOpen] = React25__default.useState(false);
7648
+ const [view, setView] = React25__default.useState(range.start || /* @__PURE__ */ new Date());
7649
+ const [hover, setHover] = React25__default.useState(null);
7650
+ const [coords, setCoords] = React25__default.useState(null);
7651
+ const wrapRef = React25__default.useRef(null);
7652
+ const triggerRef = React25__default.useRef(null);
7653
+ const popRef = React25__default.useRef(null);
7654
+ const labelId = React25__default.useId();
7221
7655
  const descId = `${labelId}-desc`;
7222
7656
  const invalid = Boolean(error);
7223
- React24__default.useEffect(() => {
7657
+ React25__default.useEffect(() => {
7224
7658
  if (disabled) setOpen(false);
7225
7659
  }, [disabled]);
7226
7660
  const POP_W = 420;
7227
- const place = React24__default.useCallback(() => {
7661
+ const place = React25__default.useCallback(() => {
7228
7662
  const el = triggerRef.current;
7229
7663
  if (!el) return;
7230
7664
  const r = el.getBoundingClientRect();
@@ -7235,7 +7669,7 @@ function DateRangePicker({
7235
7669
  left = Math.max(8, left);
7236
7670
  setCoords({ left, top: flip ? void 0 : Math.round(r.bottom + 6), bottom: flip ? Math.round(vh - r.top + 6) : void 0 });
7237
7671
  }, []);
7238
- React24__default.useEffect(() => {
7672
+ React25__default.useEffect(() => {
7239
7673
  if (!open) return void 0;
7240
7674
  place();
7241
7675
  const onMove = () => place();
@@ -7246,7 +7680,7 @@ function DateRangePicker({
7246
7680
  window.removeEventListener("resize", onMove);
7247
7681
  };
7248
7682
  }, [open, place]);
7249
- React24__default.useEffect(() => {
7683
+ React25__default.useEffect(() => {
7250
7684
  if (!open) return;
7251
7685
  const onDown = (e) => {
7252
7686
  if (wrapRef.current && wrapRef.current.contains(e.target)) return;
@@ -7267,7 +7701,9 @@ function DateRangePicker({
7267
7701
  if (value === void 0) setInternal(r);
7268
7702
  onChange?.(r);
7269
7703
  };
7704
+ const outOfRange = (d) => min && ymd(d) < ymd(min) || max && ymd(d) > ymd(max) || Boolean(disabledDate && disabledDate(d));
7270
7705
  const clickDay = (d) => {
7706
+ if (outOfRange(d)) return;
7271
7707
  if (!range.start || range.start && range.end) {
7272
7708
  set({ start: d, end: null });
7273
7709
  } else {
@@ -7284,8 +7720,8 @@ function DateRangePicker({
7284
7720
  setView(start);
7285
7721
  setOpen(false);
7286
7722
  };
7287
- const months = React24__default.useMemo(() => locale === void 0 ? MONTHS2 : monthNames2(locale), [locale]);
7288
- const dows = React24__default.useMemo(
7723
+ const months = React25__default.useMemo(() => locale === void 0 ? MONTHS2 : monthNames2(locale), [locale]);
7724
+ const dows = React25__default.useMemo(
7289
7725
  () => locale === void 0 ? Array.from({ length: 7 }, (_, i) => DOW2[(i + weekStartsOn) % 7]) : weekdayNames2(locale, weekStartsOn),
7290
7726
  [locale, weekStartsOn]
7291
7727
  );
@@ -7367,6 +7803,11 @@ function DateRangePicker({
7367
7803
  " ",
7368
7804
  y
7369
7805
  ] }),
7806
+ /* @__PURE__ */ jsxs("span", { className: "twc-drp__sr", "aria-live": "polite", children: [
7807
+ months[m],
7808
+ " ",
7809
+ y
7810
+ ] }),
7370
7811
  /* @__PURE__ */ jsx("button", { type: "button", className: "twc-drp__nav", "aria-label": "Next month", onClick: () => setView(new Date(y, m + 1, 1)), children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" }) }) })
7371
7812
  ] }),
7372
7813
  /* @__PURE__ */ jsxs("div", { className: "twc-drp__grid", children: [
@@ -7380,6 +7821,7 @@ function DateRangePicker({
7380
7821
  type: "button",
7381
7822
  className: "twc-drp__day",
7382
7823
  "data-outside": outside || void 0,
7824
+ disabled: outOfRange(d) || void 0,
7383
7825
  "data-in": inRange(t) || void 0,
7384
7826
  "data-edge": edgeOf(t) || void 0,
7385
7827
  "aria-pressed": !!edgeOf(t),
@@ -7466,14 +7908,14 @@ function Dialog({
7466
7908
  className = "",
7467
7909
  ...rest
7468
7910
  }) {
7469
- const autoId = React24__default.useId();
7911
+ const autoId = React25__default.useId();
7470
7912
  const titleId = `${autoId}-title`;
7471
7913
  const descId = `${autoId}-desc`;
7472
- const dialogRef = React24__default.useRef(null);
7914
+ const dialogRef = React25__default.useRef(null);
7473
7915
  const __twcStyles = useScopedStyles("twc-dialog-styles", DIALOG_CSS);
7474
7916
  const renderPortal = usePortal();
7475
- const [mounted, setMounted] = React24__default.useState(open);
7476
- React24__default.useEffect(() => {
7917
+ const [mounted, setMounted] = React25__default.useState(open);
7918
+ React25__default.useEffect(() => {
7477
7919
  if (open) {
7478
7920
  setMounted(true);
7479
7921
  return;
@@ -7481,16 +7923,10 @@ function Dialog({
7481
7923
  const t = setTimeout(() => setMounted(false), 170);
7482
7924
  return () => clearTimeout(t);
7483
7925
  }, [open]);
7484
- React24__default.useEffect(() => {
7485
- if (!open) return void 0;
7486
- const prev = document.body.style.overflow;
7487
- document.body.style.overflow = "hidden";
7488
- return () => {
7489
- document.body.style.overflow = prev;
7490
- };
7491
- }, [open]);
7926
+ useScrollLock(open);
7927
+ useInertBackground(dialogRef, open && mounted);
7492
7928
  useFocusTrap(dialogRef, open && mounted);
7493
- React24__default.useEffect(() => {
7929
+ React25__default.useEffect(() => {
7494
7930
  if (!open) return void 0;
7495
7931
  const onKey = (e) => {
7496
7932
  if (e.key === "Escape") {
@@ -7637,14 +8073,14 @@ function Drawer({
7637
8073
  style,
7638
8074
  ...rest
7639
8075
  }) {
7640
- const autoId = React24__default.useId();
8076
+ const autoId = React25__default.useId();
7641
8077
  const titleId = `${autoId}-title`;
7642
8078
  const descId = `${autoId}-desc`;
7643
- const panelRef = React24__default.useRef(null);
8079
+ const panelRef = React25__default.useRef(null);
7644
8080
  const __twcStyles = useScopedStyles("twc-drawer-styles", DRAWER_CSS);
7645
8081
  const renderPortal = usePortal();
7646
- const [mounted, setMounted] = React24__default.useState(open);
7647
- React24__default.useEffect(() => {
8082
+ const [mounted, setMounted] = React25__default.useState(open);
8083
+ React25__default.useEffect(() => {
7648
8084
  if (open) {
7649
8085
  setMounted(true);
7650
8086
  return;
@@ -7652,16 +8088,10 @@ function Drawer({
7652
8088
  const t = setTimeout(() => setMounted(false), 170);
7653
8089
  return () => clearTimeout(t);
7654
8090
  }, [open]);
7655
- React24__default.useEffect(() => {
7656
- if (!open) return void 0;
7657
- const prev = document.body.style.overflow;
7658
- document.body.style.overflow = "hidden";
7659
- return () => {
7660
- document.body.style.overflow = prev;
7661
- };
7662
- }, [open]);
7663
- useFocusTrap(panelRef, open && mounted);
7664
- React24__default.useEffect(() => {
8091
+ useScrollLock(open);
8092
+ useInertBackground(panelRef, open && mounted);
8093
+ useFocusTrap(panelRef, open && mounted);
8094
+ React25__default.useEffect(() => {
7665
8095
  if (!open) return void 0;
7666
8096
  const onKey = (e) => {
7667
8097
  if (e.key === "Escape") {
@@ -7750,16 +8180,21 @@ function Field({
7750
8180
  ...rest
7751
8181
  }) {
7752
8182
  const __twcStyles = useScopedStyles("twc-field-styles", FIELD_CSS5);
7753
- const autoId = React24__default.useId();
8183
+ const autoId = React25__default.useId();
7754
8184
  const fieldId = id || autoId;
7755
8185
  const descId = `${fieldId}-desc`;
8186
+ const hasMsg = Boolean(error || hint);
8187
+ const control = React25__default.isValidElement(children) ? React25__default.cloneElement(children, {
8188
+ "aria-describedby": [children.props["aria-describedby"], hasMsg ? descId : null].filter(Boolean).join(" ") || void 0,
8189
+ "aria-invalid": children.props["aria-invalid"] !== void 0 ? children.props["aria-invalid"] : error ? true : void 0
8190
+ }) : children;
7756
8191
  return /* @__PURE__ */ jsxs("div", { className: `twc-field ${className}`, "data-size": size, ...rest, children: [
7757
8192
  __twcStyles,
7758
8193
  label ? /* @__PURE__ */ jsxs("label", { className: "twc-field__label", htmlFor, children: [
7759
8194
  label,
7760
8195
  required ? /* @__PURE__ */ jsx("span", { className: "twc-field__req", children: "*" }) : null
7761
8196
  ] }) : null,
7762
- children,
8197
+ control,
7763
8198
  error ? /* @__PURE__ */ jsx("span", { id: descId, className: "twc-field__error", children: error }) : hint ? /* @__PURE__ */ jsx("span", { id: descId, className: "twc-field__hint", children: hint }) : null
7764
8199
  ] });
7765
8200
  }
@@ -7813,6 +8248,14 @@ function fmtSize(bytes) {
7813
8248
  if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + " KB";
7814
8249
  return (bytes / (1024 * 1024)).toFixed(1) + " MB";
7815
8250
  }
8251
+ function matchesAccept(file, accept) {
8252
+ if (!accept) return true;
8253
+ const toks = accept.split(",").map((t) => t.trim().toLowerCase()).filter(Boolean);
8254
+ if (!toks.length) return true;
8255
+ const name = file.name.toLowerCase(), type = (file.type || "").toLowerCase();
8256
+ return toks.some((t) => t.startsWith(".") ? name.endsWith(t) : t.endsWith("/*") ? type.startsWith(t.slice(0, -1)) : type === t);
8257
+ }
8258
+ var fileKey = (f) => `${f.name}-${f.size}-${f.lastModified}`;
7816
8259
  function FileUpload({
7817
8260
  accept,
7818
8261
  multiple = false,
@@ -7824,17 +8267,20 @@ function FileUpload({
7824
8267
  required = false,
7825
8268
  value,
7826
8269
  defaultValue = [],
8270
+ maxSize,
8271
+ maxFiles,
8272
+ onReject,
7827
8273
  onChange,
7828
8274
  id,
7829
8275
  className = "",
7830
8276
  ...rest
7831
8277
  }) {
7832
8278
  const __twcStyles = useScopedStyles("twc-upload-styles", UPLOAD_CSS);
7833
- const [internal, setInternal] = React24__default.useState(defaultValue);
8279
+ const [internal, setInternal] = React25__default.useState(defaultValue);
7834
8280
  const files = value !== void 0 ? value : internal;
7835
- const [drag, setDrag] = React24__default.useState(false);
7836
- const inputRef = React24__default.useRef(null);
7837
- const autoId = React24__default.useId();
8281
+ const [drag, setDrag] = React25__default.useState(false);
8282
+ const inputRef = React25__default.useRef(null);
8283
+ const autoId = React25__default.useId();
7838
8284
  const fieldId = id || autoId;
7839
8285
  const descId = `${fieldId}-desc`;
7840
8286
  const invalid = Boolean(error);
@@ -7843,8 +8289,28 @@ function FileUpload({
7843
8289
  onChange?.(next);
7844
8290
  };
7845
8291
  function addFiles(list) {
7846
- const arr = Array.from(list);
7847
- set(multiple ? [...files, ...arr] : arr.slice(0, 1));
8292
+ const incoming = Array.from(list);
8293
+ const retained = multiple ? files.slice() : [];
8294
+ const seen2 = new Set(retained.map(fileKey));
8295
+ const accepted = [];
8296
+ const rejections = [];
8297
+ for (const f of incoming) {
8298
+ if (multiple && seen2.has(fileKey(f))) continue;
8299
+ let reason = null;
8300
+ if (accept && !matchesAccept(f, accept)) reason = "type";
8301
+ else if (maxSize && f.size > maxSize) reason = "size";
8302
+ else if (multiple && maxFiles && retained.length + accepted.length >= maxFiles) reason = "count";
8303
+ if (reason) {
8304
+ rejections.push({ file: f, reason });
8305
+ continue;
8306
+ }
8307
+ accepted.push(f);
8308
+ if (multiple) seen2.add(fileKey(f));
8309
+ }
8310
+ if (rejections.length) onReject?.(rejections);
8311
+ if (multiple) {
8312
+ if (accepted.length) set([...retained, ...accepted]);
8313
+ } else if (accepted.length) set(accepted.slice(0, 1));
7848
8314
  }
7849
8315
  function remove(i) {
7850
8316
  set(files.filter((_, idx) => idx !== i));
@@ -7918,7 +8384,7 @@ function FileUpload({
7918
8384
  /* @__PURE__ */ jsx("span", { className: "twc-upload__file-size", children: fmtSize(f.size) })
7919
8385
  ] }),
7920
8386
  /* @__PURE__ */ jsx("button", { className: "twc-upload__file-x", "aria-label": `Remove ${f.name}`, onClick: () => remove(i), type: "button", children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.4", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "M18 6 6 18M6 6l12 12" }) }) })
7921
- ] }, i)) }) : null,
8387
+ ] }, fileKey(f))) }) : null,
7922
8388
  error ? /* @__PURE__ */ jsx("span", { id: descId, className: "twc-field__error", children: error }) : null
7923
8389
  ] });
7924
8390
  }
@@ -8037,15 +8503,15 @@ function Kanban({
8037
8503
  ...rest
8038
8504
  }) {
8039
8505
  const __twcStyles = useScopedStyles("twc-kanban-styles", KANBAN_CSS);
8040
- const [internal, setInternal] = React24__default.useState(cards ?? defaultCards ?? []);
8506
+ const [internal, setInternal] = React25__default.useState(cards ?? defaultCards ?? []);
8041
8507
  const items = cards !== void 0 ? cards : internal;
8042
- const [drag, setDrag] = React24__default.useState(null);
8043
- const [overCol, setOverCol] = React24__default.useState(null);
8044
- const [grab, setGrab] = React24__default.useState(null);
8045
- const [announce, setAnnounce] = React24__default.useState("");
8046
- const rootRef = React24__default.useRef(null);
8047
- const focusIdRef = React24__default.useRef(null);
8048
- React24__default.useEffect(() => {
8508
+ const [drag, setDrag] = React25__default.useState(null);
8509
+ const [overCol, setOverCol] = React25__default.useState(null);
8510
+ const [grab, setGrab] = React25__default.useState(null);
8511
+ const [announce, setAnnounce] = React25__default.useState("");
8512
+ const rootRef = React25__default.useRef(null);
8513
+ const focusIdRef = React25__default.useRef(null);
8514
+ React25__default.useEffect(() => {
8049
8515
  if (focusIdRef.current == null) return void 0;
8050
8516
  const raf = requestAnimationFrame(() => {
8051
8517
  const id = focusIdRef.current;
@@ -8190,42 +8656,49 @@ var LIST_CSS = `
8190
8656
  .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; }
8191
8657
  .twc-list__desc { font-size: var(--text-xs); color: var(--color-text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
8192
8658
  .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); }
8659
+ .twc-list__empty { padding: 30px 12px; text-align: center; color: var(--color-text-subtle); font-size: var(--text-sm); }
8193
8660
  `;
8194
- function List({ items, plain = false, className = "", ...rest }) {
8661
+ function List({ items, plain = false, emptyMessage, className = "", ...rest }) {
8195
8662
  const __twcStyles = useScopedStyles("twc-list-styles", LIST_CSS);
8196
8663
  const safeHref12 = (url) => {
8197
8664
  if (url == null) return void 0;
8198
8665
  const s = String(url).replace(/[\x00-\x20]+/g, "").toLowerCase();
8199
8666
  return s.startsWith("javascript:") || s.startsWith("data:") || s.startsWith("vbscript:") ? void 0 : url;
8200
8667
  };
8201
- return /* @__PURE__ */ jsxs("ul", { className: `twc-list ${className}`, "data-plain": plain || void 0, ...rest, children: [
8202
- __twcStyles,
8203
- items.map((it, i) => {
8204
- const href = safeHref12(it.href);
8205
- const interactive = Boolean(it.onClick || href);
8206
- const Tag2 = href ? "a" : interactive ? "button" : "div";
8207
- const content = /* @__PURE__ */ jsxs(Fragment, { children: [
8208
- it.leading ? /* @__PURE__ */ jsx("span", { className: "twc-list__lead", "aria-hidden": "true", children: it.leading }) : null,
8209
- /* @__PURE__ */ jsxs("span", { className: "twc-list__main", children: [
8210
- /* @__PURE__ */ jsx("span", { className: "twc-list__title", children: it.title }),
8211
- it.description ? /* @__PURE__ */ jsx("span", { className: "twc-list__desc", children: it.description }) : null
8212
- ] }),
8213
- it.trailing ? /* @__PURE__ */ jsx("span", { className: "twc-list__trail", children: it.trailing }) : null
8214
- ] });
8215
- return /* @__PURE__ */ jsx("li", { style: { display: "contents" }, children: /* @__PURE__ */ jsx(
8216
- Tag2,
8217
- {
8218
- className: "twc-list__item",
8219
- "data-interactive": interactive || void 0,
8220
- "data-active": it.active || void 0,
8221
- href,
8222
- type: Tag2 === "button" ? "button" : void 0,
8223
- onClick: it.onClick,
8224
- children: content
8225
- }
8226
- ) }, i);
8227
- })
8228
- ] });
8668
+ return (
8669
+ // role="list" before {...rest} so a consumer can still override it (e.g. role="menu");
8670
+ // list-style:none + display:contents on the <li> drop the implicit list/listitem roles
8671
+ // in some engines, so we set them explicitly (#123).
8672
+ /* @__PURE__ */ jsxs("ul", { className: `twc-list ${className}`, role: "list", "data-plain": plain || void 0, ...rest, children: [
8673
+ __twcStyles,
8674
+ items.length === 0 ? /* @__PURE__ */ jsx("li", { role: "presentation", style: { display: "contents" }, children: /* @__PURE__ */ jsx("div", { className: "twc-list__empty", children: emptyMessage ?? "Nothing here yet" }) }) : items.map((it, i) => {
8675
+ const href = safeHref12(it.href);
8676
+ const interactive = Boolean(it.onClick || href);
8677
+ const Tag2 = href ? "a" : interactive ? "button" : "div";
8678
+ const content = /* @__PURE__ */ jsxs(Fragment, { children: [
8679
+ it.leading ? /* @__PURE__ */ jsx("span", { className: "twc-list__lead", "aria-hidden": "true", children: it.leading }) : null,
8680
+ /* @__PURE__ */ jsxs("span", { className: "twc-list__main", children: [
8681
+ /* @__PURE__ */ jsx("span", { className: "twc-list__title", children: it.title }),
8682
+ it.description ? /* @__PURE__ */ jsx("span", { className: "twc-list__desc", children: it.description }) : null
8683
+ ] }),
8684
+ it.trailing ? /* @__PURE__ */ jsx("span", { className: "twc-list__trail", children: it.trailing }) : null
8685
+ ] });
8686
+ return /* @__PURE__ */ jsx("li", { role: "listitem", style: { display: "contents" }, children: /* @__PURE__ */ jsx(
8687
+ Tag2,
8688
+ {
8689
+ className: "twc-list__item",
8690
+ "data-interactive": interactive || void 0,
8691
+ "data-active": it.active || void 0,
8692
+ "aria-current": it.active ? "page" : void 0,
8693
+ href,
8694
+ type: Tag2 === "button" ? "button" : void 0,
8695
+ onClick: it.onClick,
8696
+ children: content
8697
+ }
8698
+ ) }, i);
8699
+ })
8700
+ ] })
8701
+ );
8229
8702
  }
8230
8703
  var NAVBAR_CSS = `
8231
8704
  .twc-navbar { display: flex; align-items: center; gap: var(--space-4); height: 60px; padding: 0 var(--space-5);
@@ -8348,21 +8821,21 @@ function Popover({
8348
8821
  ...rest
8349
8822
  }) {
8350
8823
  const __twcStyles = useScopedStyles("twc-popover-styles", POPOVER_CSS);
8351
- const [openState, setOpenState] = React24__default.useState(defaultOpen);
8352
- const [render, setRender] = React24__default.useState(false);
8353
- const [pos, setPos] = React24__default.useState(null);
8354
- const wrapRef = React24__default.useRef(null);
8355
- const popRef = React24__default.useRef(null);
8356
- const popId = React24__default.useId();
8824
+ const [openState, setOpenState] = React25__default.useState(defaultOpen);
8825
+ const [render, setRender] = React25__default.useState(false);
8826
+ const [pos, setPos] = React25__default.useState(null);
8827
+ const wrapRef = React25__default.useRef(null);
8828
+ const popRef = React25__default.useRef(null);
8829
+ const popId = React25__default.useId();
8357
8830
  const controlled = openProp !== void 0;
8358
8831
  const open = controlled ? !!openProp : openState;
8359
- const openRef = React24__default.useRef(open);
8832
+ const openRef = React25__default.useRef(open);
8360
8833
  openRef.current = open;
8361
- const controlledRef = React24__default.useRef(controlled);
8834
+ const controlledRef = React25__default.useRef(controlled);
8362
8835
  controlledRef.current = controlled;
8363
- const onOpenChangeRef = React24__default.useRef(onOpenChange);
8836
+ const onOpenChangeRef = React25__default.useRef(onOpenChange);
8364
8837
  onOpenChangeRef.current = onOpenChange;
8365
- const setOpen = React24__default.useCallback((next) => {
8838
+ const setOpen = React25__default.useCallback((next) => {
8366
8839
  const value = typeof next === "function" ? next(openRef.current) : next;
8367
8840
  if (value === openRef.current) return;
8368
8841
  if (!controlledRef.current) setOpenState(value);
@@ -8370,7 +8843,7 @@ function Popover({
8370
8843
  }, []);
8371
8844
  const toggle = () => setOpen((o) => !o);
8372
8845
  const RD = { createPortal: typeof createPortal === "function" ? createPortal : typeof window !== "undefined" && window.ReactDOM && window.ReactDOM.createPortal };
8373
- const place = React24__default.useCallback(() => {
8846
+ const place = React25__default.useCallback(() => {
8374
8847
  const t = wrapRef.current;
8375
8848
  if (!t) return;
8376
8849
  const r = t.getBoundingClientRect();
@@ -8380,7 +8853,13 @@ function Popover({
8380
8853
  let top, left, flip = false, arrow = {};
8381
8854
  const vertical = placement === "top" || placement === "bottom";
8382
8855
  if (vertical) {
8383
- flip = placement === "top" || placement === "bottom" && vh - r.bottom < 180 && r.top > vh - r.bottom;
8856
+ const ph2 = popRef.current ? popRef.current.offsetHeight : 120;
8857
+ const spaceAbove = r.top - gap - M;
8858
+ const spaceBelow = vh - r.bottom - gap - M;
8859
+ let openUp = placement === "top";
8860
+ if (openUp && spaceAbove < ph2 && spaceBelow > spaceAbove) openUp = false;
8861
+ else if (!openUp && spaceBelow < ph2 && spaceAbove > spaceBelow) openUp = true;
8862
+ flip = openUp;
8384
8863
  top = flip ? void 0 : r.bottom + gap;
8385
8864
  const bottom = flip ? vh - r.top + gap : void 0;
8386
8865
  let l = align === "start" ? r.left : align === "end" ? r.right - w : r.left + r.width / 2 - w / 2;
@@ -8401,7 +8880,7 @@ function Popover({
8401
8880
  arrow = { top: Math.min(Math.max(cy - top - 5.5, 8), ph - 18), [onRight ? "left" : "right"]: -6 };
8402
8881
  setPos({ top, left, width: w, flip: false, arrow });
8403
8882
  }, [placement, align, width]);
8404
- React24__default.useEffect(() => {
8883
+ React25__default.useEffect(() => {
8405
8884
  if (!open) return;
8406
8885
  place();
8407
8886
  const onMove = () => place();
@@ -8424,7 +8903,7 @@ function Popover({
8424
8903
  document.removeEventListener("keydown", onKey);
8425
8904
  };
8426
8905
  }, [open, place]);
8427
- React24__default.useEffect(() => {
8906
+ React25__default.useEffect(() => {
8428
8907
  if (open) {
8429
8908
  setRender(true);
8430
8909
  return;
@@ -8432,12 +8911,12 @@ function Popover({
8432
8911
  const t = setTimeout(() => setRender(false), 170);
8433
8912
  return () => clearTimeout(t);
8434
8913
  }, [open]);
8435
- React24__default.useEffect(() => {
8914
+ React25__default.useEffect(() => {
8436
8915
  if (render && open) place();
8437
8916
  }, [render, place]);
8438
- const restoreRef = React24__default.useRef(null);
8439
- const needFocusRef = React24__default.useRef(false);
8440
- React24__default.useEffect(() => {
8917
+ const restoreRef = React25__default.useRef(null);
8918
+ const needFocusRef = React25__default.useRef(false);
8919
+ React25__default.useEffect(() => {
8441
8920
  if (open) {
8442
8921
  restoreRef.current = document.activeElement;
8443
8922
  needFocusRef.current = true;
@@ -8448,12 +8927,7 @@ function Popover({
8448
8927
  needFocusRef.current = false;
8449
8928
  if (prev && typeof prev.focus === "function" && popRef.current?.contains(document.activeElement)) prev.focus();
8450
8929
  }, [open]);
8451
- React24__default.useEffect(() => {
8452
- if (open && render && pos && needFocusRef.current) {
8453
- needFocusRef.current = false;
8454
- popRef.current?.focus({ preventScroll: true });
8455
- }
8456
- }, [open, render, pos]);
8930
+ useFocusTrap(popRef, open && render && !!pos, { restoreFocus: false });
8457
8931
  const pop = render && pos ? /* @__PURE__ */ jsxs(
8458
8932
  "div",
8459
8933
  {
@@ -8464,6 +8938,7 @@ function Popover({
8464
8938
  "data-state": open ? "open" : "closed",
8465
8939
  "data-flip": pos.flip || void 0,
8466
8940
  role: "dialog",
8941
+ "aria-modal": "true",
8467
8942
  "aria-labelledby": title ? `${popId}-title` : void 0,
8468
8943
  style: { top: pos.top, bottom: pos.bottom, left: pos.left, width: pos.width },
8469
8944
  children: [
@@ -8476,7 +8951,7 @@ function Popover({
8476
8951
  ]
8477
8952
  }
8478
8953
  ) : null;
8479
- const triggerEl = React24__default.isValidElement(trigger) ? React24__default.cloneElement(trigger, {
8954
+ const triggerEl = React25__default.isValidElement(trigger) ? React25__default.cloneElement(trigger, {
8480
8955
  onClick: (e) => {
8481
8956
  trigger.props.onClick?.(e);
8482
8957
  toggle();
@@ -8519,7 +8994,7 @@ function Progress({
8519
8994
  className = "",
8520
8995
  ...rest
8521
8996
  }) {
8522
- const labelId = React24__default.useId();
8997
+ const labelId = React25__default.useId();
8523
8998
  const { "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, ...restProps } = rest;
8524
8999
  const __twcStyles = useScopedStyles("twc-progress-styles", `
8525
9000
  .twc-progress-wrap { display: flex; flex-direction: column; gap: 6px; font-family: var(--font-sans); }
@@ -8642,10 +9117,13 @@ function Radio({
8642
9117
  .twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
8643
9118
  .twc-field__error { font-size: var(--text-xs); color: var(--color-danger-subtle-fg); font-weight: var(--font-medium); }
8644
9119
  `);
8645
- const autoId = React24__default.useId();
9120
+ const autoId = React25__default.useId();
8646
9121
  const fieldId = id || autoId;
8647
9122
  const errId = `${fieldId}-error`;
8648
9123
  const invalid = Boolean(error);
9124
+ if (process.env.NODE_ENV !== "production" && !label && !description && !rest["aria-label"] && !rest["aria-labelledby"]) {
9125
+ 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).");
9126
+ }
8649
9127
  const describedBy = [rest["aria-describedby"], error ? errId : null].filter(Boolean).join(" ") || void 0;
8650
9128
  const control = /* @__PURE__ */ 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: [
8651
9129
  __twcStyles,
@@ -8662,6 +9140,7 @@ function Radio({
8662
9140
  disabled,
8663
9141
  onChange,
8664
9142
  ...rest,
9143
+ required: required || void 0,
8665
9144
  "aria-required": required || void 0,
8666
9145
  "aria-invalid": invalid || void 0,
8667
9146
  "aria-describedby": describedBy
@@ -8679,11 +9158,103 @@ function Radio({
8679
9158
  /* @__PURE__ */ jsx("span", { id: errId, className: "twc-field__error", children: error })
8680
9159
  ] });
8681
9160
  }
9161
+ var RADIO_GROUP_CSS = `
9162
+ .twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
9163
+ .twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
9164
+ .twc-field__req { color: var(--color-danger); }
9165
+ .twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
9166
+ .twc-field__error { font-size: var(--text-xs); color: var(--color-danger-subtle-fg); font-weight: var(--font-medium); }
9167
+ .twc-radio-group__items { display: flex; flex-direction: column; gap: var(--space-2-5); }
9168
+ .twc-radio-group__items[data-orientation="horizontal"] { flex-direction: row; flex-wrap: wrap; gap: var(--space-4); }
9169
+ `;
9170
+ function RadioGroup({
9171
+ name,
9172
+ value,
9173
+ defaultValue,
9174
+ onChange,
9175
+ options,
9176
+ children,
9177
+ label,
9178
+ hint,
9179
+ error,
9180
+ orientation = "vertical",
9181
+ disabled = false,
9182
+ required = false,
9183
+ size = "md",
9184
+ tone = "primary",
9185
+ id,
9186
+ className = "",
9187
+ ...rest
9188
+ }) {
9189
+ const __twcStyles = useScopedStyles("twc-radio-group-styles", RADIO_GROUP_CSS);
9190
+ const autoId = React25__default.useId();
9191
+ const fieldId = id || autoId;
9192
+ const labelId = `${fieldId}-label`;
9193
+ const descId = `${fieldId}-desc`;
9194
+ const groupName = name || `${fieldId}-name`;
9195
+ const invalid = Boolean(error);
9196
+ const [internal, setInternal] = React25__default.useState(defaultValue);
9197
+ const current = value !== void 0 ? value : internal;
9198
+ const select = (v) => {
9199
+ if (value === void 0) setInternal(v);
9200
+ onChange?.(v);
9201
+ };
9202
+ const radios = options ? options.map((o) => /* @__PURE__ */ jsx(
9203
+ Radio,
9204
+ {
9205
+ name: groupName,
9206
+ value: o.value,
9207
+ label: o.label,
9208
+ description: o.description,
9209
+ checked: current === o.value,
9210
+ onChange: () => select(o.value),
9211
+ disabled: disabled || o.disabled,
9212
+ required,
9213
+ size,
9214
+ tone
9215
+ },
9216
+ o.value
9217
+ )) : React25__default.Children.map(children, (child) => {
9218
+ if (!React25__default.isValidElement(child)) return child;
9219
+ const v = child.props.value;
9220
+ return React25__default.cloneElement(child, {
9221
+ name: child.props.name ?? groupName,
9222
+ checked: child.props.checked ?? current === v,
9223
+ onChange: child.props.onChange ?? (() => select(v)),
9224
+ disabled: child.props.disabled ?? disabled,
9225
+ required: child.props.required ?? required,
9226
+ size: child.props.size ?? size,
9227
+ tone: child.props.tone ?? tone
9228
+ });
9229
+ });
9230
+ return /* @__PURE__ */ jsxs(
9231
+ "div",
9232
+ {
9233
+ className: `twc-field ${className}`,
9234
+ role: "radiogroup",
9235
+ "aria-labelledby": label ? labelId : void 0,
9236
+ "aria-describedby": error || hint ? descId : void 0,
9237
+ "aria-invalid": invalid || void 0,
9238
+ "aria-required": required || void 0,
9239
+ "data-orientation": orientation,
9240
+ ...rest,
9241
+ children: [
9242
+ __twcStyles,
9243
+ label ? /* @__PURE__ */ jsxs("span", { id: labelId, className: "twc-field__label", children: [
9244
+ label,
9245
+ required ? /* @__PURE__ */ jsx("span", { className: "twc-field__req", children: "*" }) : null
9246
+ ] }) : null,
9247
+ /* @__PURE__ */ jsx("div", { className: "twc-radio-group__items", "data-orientation": orientation === "horizontal" ? "horizontal" : void 0, children: radios }),
9248
+ error ? /* @__PURE__ */ jsx("span", { id: descId, className: "twc-field__error", children: error }) : hint ? /* @__PURE__ */ jsx("span", { id: descId, className: "twc-field__hint", children: hint }) : null
9249
+ ]
9250
+ }
9251
+ );
9252
+ }
8682
9253
  var RATING_CSS = `
8683
9254
  .twc-rating { display: inline-flex; align-items: center; gap: 8px; font-family: var(--font-sans); }
8684
9255
  .twc-rating__stars { display: inline-flex; gap: 2px; }
8685
- .twc-rating__btn { border: none; background: transparent; padding: 1px; cursor: pointer; color: var(--color-border-strong); line-height: 0;
8686
- transition: transform var(--duration-fast) var(--ease-spring), color var(--duration-fast) var(--ease-standard); }
9256
+ .twc-rating__btn { border: none; background: transparent; padding: 1px; cursor: pointer; line-height: 0;
9257
+ transition: transform var(--duration-fast) var(--ease-spring); }
8687
9258
  .twc-rating__btn:focus-visible { outline: none; box-shadow: var(--ring); border-radius: var(--radius-sm); }
8688
9259
  .twc-rating[data-readonly="true"] .twc-rating__btn { cursor: default; }
8689
9260
  .twc-rating[data-disabled="true"] { opacity: 0.55; }
@@ -8697,8 +9268,11 @@ var RATING_CSS = `
8697
9268
  .twc-rating[data-tone="danger"] { --_accent: var(--color-danger); }
8698
9269
  .twc-rating[data-tone="info"] { --_accent: var(--color-info); }
8699
9270
  .twc-rating[data-tone="neutral"] { --_accent: var(--color-text); }
8700
- .twc-rating__btn[data-on="true"] { color: var(--_c, var(--_accent)); }
8701
- .twc-rating__btn svg { width: var(--_sz, 24px); height: var(--_sz, 24px); display: block; }
9271
+ /* Per-star partial fill: an accent-colored star clipped to var(--twc-fill) over an empty star. */
9272
+ .twc-rating__star { position: relative; display: inline-block; line-height: 0; }
9273
+ .twc-rating__bg { color: var(--color-border-strong); }
9274
+ .twc-rating__fg { position: absolute; inset-block: 0; inset-inline-start: 0; width: var(--twc-fill, 0%); overflow: hidden; color: var(--_c, var(--_accent)); }
9275
+ .twc-rating svg { width: var(--_sz, 24px); height: var(--_sz, 24px); display: block; }
8702
9276
  .twc-rating[data-size="sm"] { --_sz: 18px; }
8703
9277
  .twc-rating[data-size="lg"] { --_sz: 32px; }
8704
9278
  .twc-rating__value { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text-muted); font-variant-numeric: tabular-nums; }
@@ -8714,19 +9288,26 @@ function Rating({
8714
9288
  readOnly = false,
8715
9289
  disabled = false,
8716
9290
  showValue = false,
9291
+ clearable = true,
9292
+ format,
9293
+ name,
8717
9294
  onChange,
8718
9295
  className = "",
8719
9296
  style,
8720
9297
  ...rest
8721
9298
  }) {
8722
9299
  const __twcStyles = useScopedStyles("twc-rating-styles", RATING_CSS);
8723
- const [internal, setInternal] = React24__default.useState(defaultValue);
8724
- const [hover, setHover] = React24__default.useState(0);
9300
+ const [internal, setInternal] = React25__default.useState(defaultValue);
9301
+ const [hover, setHover] = React25__default.useState(0);
8725
9302
  const val = value !== void 0 ? value : internal;
8726
9303
  const shown = hover || val;
8727
9304
  const interactive = !readOnly && !disabled;
8728
- const btnRefs = React24__default.useRef([]);
9305
+ const btnRefs = React25__default.useRef([]);
8729
9306
  const tabStar = Math.min(count, Math.max(1, Math.round(val) || 1));
9307
+ const fmt = (v) => format ? format(v) : Number.isInteger(v) ? String(v) : v.toFixed(1);
9308
+ if (process.env.NODE_ENV !== "production" && interactive && !Number.isInteger(val)) {
9309
+ warnOnce("Rating.fractional-interactive", "Rating: a fractional value only renders (partial-fill display) in readOnly mode \u2014 interactive selection is integer-only.");
9310
+ }
8730
9311
  const set = (v) => {
8731
9312
  if (!interactive) return;
8732
9313
  if (value === void 0) setInternal(v);
@@ -8739,18 +9320,46 @@ function Rating({
8739
9320
  else if (e.key === "ArrowLeft" || e.key === "ArrowDown") next = Math.max(1, val - 1);
8740
9321
  else if (e.key === "Home") next = 1;
8741
9322
  else if (e.key === "End") next = count;
9323
+ else if (clearable && (e.key === "Delete" || e.key === "Backspace")) next = 0;
8742
9324
  else return;
8743
9325
  e.preventDefault();
8744
9326
  set(next);
8745
- btnRefs.current[next - 1]?.focus();
9327
+ btnRefs.current[Math.max(0, next - 1)]?.focus();
9328
+ };
9329
+ const star = (n) => {
9330
+ const fill = Math.max(0, Math.min(1, shown - (n - 1)));
9331
+ return /* @__PURE__ */ jsxs("span", { className: "twc-rating__star", style: { "--twc-fill": `${fill * 100}%` }, children: [
9332
+ /* @__PURE__ */ jsx("svg", { className: "twc-rating__bg", viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: StarPath }) }),
9333
+ /* @__PURE__ */ jsx("span", { className: "twc-rating__fg", "aria-hidden": "true", children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: StarPath }) }) })
9334
+ ] });
8746
9335
  };
9336
+ if (readOnly) {
9337
+ return /* @__PURE__ */ jsxs(
9338
+ "div",
9339
+ {
9340
+ className: `twc-rating ${className}`,
9341
+ "data-size": size,
9342
+ "data-tone": tone,
9343
+ "data-readonly": "true",
9344
+ style: color ? { "--_c": color, ...style } : style,
9345
+ role: "img",
9346
+ "aria-label": `${fmt(val)} out of ${count} stars`,
9347
+ ...rest,
9348
+ children: [
9349
+ __twcStyles,
9350
+ name ? /* @__PURE__ */ jsx("input", { type: "hidden", name, value: val, disabled: disabled || void 0 }) : null,
9351
+ /* @__PURE__ */ jsx("span", { className: "twc-rating__stars", children: Array.from({ length: count }).map((_, i) => /* @__PURE__ */ jsx("span", { className: "twc-rating__btn", children: star(i + 1) }, i + 1)) }),
9352
+ showValue ? /* @__PURE__ */ jsx("span", { className: "twc-rating__value", children: fmt(val) }) : null
9353
+ ]
9354
+ }
9355
+ );
9356
+ }
8747
9357
  return /* @__PURE__ */ jsxs(
8748
9358
  "div",
8749
9359
  {
8750
9360
  className: `twc-rating ${className}`,
8751
9361
  "data-size": size,
8752
9362
  "data-tone": tone,
8753
- "data-readonly": readOnly || void 0,
8754
9363
  "data-disabled": disabled || void 0,
8755
9364
  style: color ? { "--_c": color, ...style } : style,
8756
9365
  role: "radiogroup",
@@ -8759,6 +9368,7 @@ function Rating({
8759
9368
  ...rest,
8760
9369
  children: [
8761
9370
  __twcStyles,
9371
+ name ? /* @__PURE__ */ jsx("input", { type: "hidden", name, value: val, disabled: disabled || void 0 }) : null,
8762
9372
  /* @__PURE__ */ jsx("span", { className: "twc-rating__stars", onMouseLeave: () => setHover(0), onKeyDown, children: Array.from({ length: count }).map((_, i) => {
8763
9373
  const n = i + 1;
8764
9374
  return /* @__PURE__ */ jsx(
@@ -8769,149 +9379,19 @@ function Rating({
8769
9379
  },
8770
9380
  type: "button",
8771
9381
  className: "twc-rating__btn",
8772
- "data-on": n <= shown || void 0,
8773
9382
  role: "radio",
8774
9383
  "aria-label": `${n} star${n > 1 ? "s" : ""}`,
8775
9384
  "aria-checked": n === val,
8776
- disabled: readOnly || disabled,
9385
+ disabled,
8777
9386
  tabIndex: interactive && n === tabStar ? 0 : -1,
8778
9387
  onMouseEnter: () => interactive && setHover(n),
8779
- onClick: () => set(n === val ? 0 : n),
8780
- children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: StarPath }) })
9388
+ onClick: () => set(n === val && clearable ? 0 : n),
9389
+ children: star(n)
8781
9390
  },
8782
9391
  n
8783
9392
  );
8784
9393
  }) }),
8785
- showValue ? /* @__PURE__ */ jsx("span", { className: "twc-rating__value", children: val.toFixed(1) }) : null
8786
- ]
8787
- }
8788
- );
8789
- }
8790
- var TOOLTIP_CSS = `
8791
- .twc-tooltip-wrap { display: inline-flex; }
8792
- .twc-tooltip {
8793
- position: fixed; z-index: var(--z-tooltip); pointer-events: none;
8794
- padding: 6px 10px; border-radius: var(--radius-md);
8795
- background: var(--color-text); color: var(--color-surface);
8796
- font-family: var(--font-sans); font-size: var(--text-xs); font-weight: var(--font-medium);
8797
- line-height: 1.3; white-space: nowrap; box-shadow: var(--shadow-md);
8798
- opacity: 0; transform: scale(0.9); transition: opacity var(--duration-fast) var(--ease-out), transform var(--duration-fast) var(--ease-spring);
8799
- }
8800
- .twc-tooltip[data-show="true"] { opacity: 1; transform: scale(1); }
8801
- .twc-tooltip[data-place="top"] { transform-origin: bottom center; translate: -50% 0; }
8802
- .twc-tooltip[data-place="bottom"] { transform-origin: top center; translate: -50% 0; }
8803
- .twc-tooltip[data-place="left"] { transform-origin: right center; translate: 0 -50%; }
8804
- .twc-tooltip[data-place="right"] { transform-origin: left center; translate: 0 -50%; }
8805
- .twc-tooltip[data-show="true"][data-place="top"], .twc-tooltip[data-show="true"][data-place="bottom"] { transform: scale(1); }
8806
- .twc-tooltip__arrow { position: absolute; width: 7px; height: 7px; background: var(--color-text); transform: rotate(45deg); }
8807
- .twc-tooltip[data-place="top"] .twc-tooltip__arrow { bottom: -3px; left: 50%; margin-left: -3.5px; }
8808
- .twc-tooltip[data-place="bottom"] .twc-tooltip__arrow { top: -3px; left: 50%; margin-left: -3.5px; }
8809
- .twc-tooltip[data-place="left"] .twc-tooltip__arrow { right: -3px; top: 50%; margin-top: -3.5px; }
8810
- .twc-tooltip[data-place="right"] .twc-tooltip__arrow { left: -3px; top: 50%; margin-top: -3.5px; }
8811
- @media (prefers-reduced-motion: reduce) { .twc-tooltip { transition: opacity var(--duration-fast) linear; transform: none; } }
8812
- `;
8813
- function Tooltip({
8814
- children,
8815
- label,
8816
- placement = "top",
8817
- delay = 120,
8818
- className = "",
8819
- ...rest
8820
- }) {
8821
- const __twcStyles = useScopedStyles("twc-tooltip-styles", TOOLTIP_CSS);
8822
- const id = React24__default.useId();
8823
- const [show, setShow] = React24__default.useState(false);
8824
- const [coords, setCoords] = React24__default.useState(null);
8825
- const timer = React24__default.useRef(null);
8826
- const wrapRef = React24__default.useRef(null);
8827
- const tipRef = React24__default.useRef(null);
8828
- const open = () => {
8829
- clearTimeout(timer.current);
8830
- timer.current = setTimeout(() => setShow(true), delay);
8831
- };
8832
- const close = () => {
8833
- clearTimeout(timer.current);
8834
- setShow(false);
8835
- };
8836
- React24__default.useEffect(() => () => clearTimeout(timer.current), []);
8837
- const place = React24__default.useCallback(() => {
8838
- const el = wrapRef.current;
8839
- if (!el) return;
8840
- const r = el.getBoundingClientRect();
8841
- const cx = Math.round(r.left + r.width / 2), cy = Math.round(r.top + r.height / 2);
8842
- const vw = window.innerWidth, vh = window.innerHeight, gap = 8, margin = 8;
8843
- const tip = tipRef.current;
8844
- const tw = tip ? tip.offsetWidth : 0, th = tip ? tip.offsetHeight : 0;
8845
- const clampX = (c) => tw ? Math.round(Math.min(Math.max(c, margin + tw / 2), vw - margin - tw / 2)) : c;
8846
- const clampY = (c) => th ? Math.round(Math.min(Math.max(c, margin + th / 2), vh - margin - th / 2)) : c;
8847
- const base = { left: "auto", right: "auto", top: "auto", bottom: "auto" };
8848
- if (placement === "bottom") setCoords({ ...base, left: clampX(cx), top: Math.round(r.bottom + gap) });
8849
- else if (placement === "left") setCoords({ ...base, top: clampY(cy), right: Math.round(vw - r.left + gap) });
8850
- else if (placement === "right") setCoords({ ...base, top: clampY(cy), left: Math.round(r.right + gap) });
8851
- else setCoords({ ...base, left: clampX(cx), bottom: Math.round(vh - r.top + gap) });
8852
- }, [placement]);
8853
- React24__default.useEffect(() => {
8854
- place();
8855
- }, [place]);
8856
- React24__default.useEffect(() => {
8857
- if (!show) return void 0;
8858
- place();
8859
- const onMove = () => place();
8860
- window.addEventListener("scroll", onMove, true);
8861
- window.addEventListener("resize", onMove);
8862
- return () => {
8863
- window.removeEventListener("scroll", onMove, true);
8864
- window.removeEventListener("resize", onMove);
8865
- };
8866
- }, [show, place]);
8867
- React24__default.useEffect(() => {
8868
- if (!show) return;
8869
- const onKeyDown = (e) => {
8870
- if (e.key === "Escape") {
8871
- clearTimeout(timer.current);
8872
- setShow(false);
8873
- }
8874
- };
8875
- document.addEventListener("keydown", onKeyDown);
8876
- return () => document.removeEventListener("keydown", onKeyDown);
8877
- }, [show]);
8878
- const describedBy = show ? id : void 0;
8879
- const trigger = React24__default.isValidElement(children) && children.type !== React24__default.Fragment ? React24__default.cloneElement(children, {
8880
- "aria-describedby": [children.props["aria-describedby"], describedBy].filter(Boolean).join(" ") || void 0
8881
- }) : /* @__PURE__ */ jsx("span", { "aria-describedby": describedBy, children });
8882
- return /* @__PURE__ */ jsxs(
8883
- "span",
8884
- {
8885
- ref: wrapRef,
8886
- className: `twc-tooltip-wrap ${className}`,
8887
- onMouseEnter: open,
8888
- onMouseLeave: close,
8889
- onFocus: open,
8890
- onBlur: close,
8891
- ...rest,
8892
- children: [
8893
- __twcStyles,
8894
- trigger,
8895
- coords ? createPortal(
8896
- /* @__PURE__ */ jsxs(
8897
- "span",
8898
- {
8899
- ref: tipRef,
8900
- id,
8901
- className: "twc-tooltip",
8902
- "data-place": placement,
8903
- "data-show": show || void 0,
8904
- role: "tooltip",
8905
- "aria-hidden": show ? void 0 : "true",
8906
- style: { position: "fixed", left: coords.left, right: coords.right, top: coords.top, bottom: coords.bottom },
8907
- children: [
8908
- label,
8909
- /* @__PURE__ */ jsx("span", { className: "twc-tooltip__arrow", "aria-hidden": "true" })
8910
- ]
8911
- }
8912
- ),
8913
- document.body
8914
- ) : null
9394
+ showValue ? /* @__PURE__ */ jsx("span", { className: "twc-rating__value", children: fmt(val) }) : null
8915
9395
  ]
8916
9396
  }
8917
9397
  );
@@ -9009,24 +9489,24 @@ function Sidebar({
9009
9489
  ...rest
9010
9490
  }) {
9011
9491
  const __twcStyles = useScopedStyles("twc-sidebar-styles", SIDEBAR_CSS);
9012
- const navId = React24__default.useId();
9013
- const panelRef = React24__default.useRef(null);
9492
+ const navId = React25__default.useId();
9493
+ const panelRef = React25__default.useRef(null);
9014
9494
  const renderPortal = usePortal();
9015
- const [internal, setInternal] = React24__default.useState(defaultCollapsed);
9495
+ const [internal, setInternal] = React25__default.useState(defaultCollapsed);
9016
9496
  const collapsed = collapsedProp !== void 0 ? collapsedProp : internal;
9017
9497
  const toggle = () => {
9018
9498
  const next = !collapsed;
9019
9499
  if (collapsedProp === void 0) setInternal(next);
9020
9500
  onCollapsedChange?.(next);
9021
9501
  };
9022
- const [openInternal, setOpenInternal] = React24__default.useState(defaultOpen);
9502
+ const [openInternal, setOpenInternal] = React25__default.useState(defaultOpen);
9023
9503
  const open = openProp !== void 0 ? openProp : openInternal;
9024
9504
  const setOpen = (next) => {
9025
9505
  if (openProp === void 0) setOpenInternal(next);
9026
9506
  onOpenChange?.(next);
9027
9507
  };
9028
- const [mounted, setMounted] = React24__default.useState(open);
9029
- React24__default.useEffect(() => {
9508
+ const [mounted, setMounted] = React25__default.useState(open);
9509
+ React25__default.useEffect(() => {
9030
9510
  if (!overlay) return void 0;
9031
9511
  if (open) {
9032
9512
  setMounted(true);
@@ -9035,7 +9515,7 @@ function Sidebar({
9035
9515
  const t = setTimeout(() => setMounted(false), 170);
9036
9516
  return () => clearTimeout(t);
9037
9517
  }, [overlay, open]);
9038
- React24__default.useEffect(() => {
9518
+ React25__default.useEffect(() => {
9039
9519
  if (!overlay || !open) return void 0;
9040
9520
  const prev = document.body.style.overflow;
9041
9521
  document.body.style.overflow = "hidden";
@@ -9044,7 +9524,7 @@ function Sidebar({
9044
9524
  };
9045
9525
  }, [overlay, open]);
9046
9526
  useFocusTrap(panelRef, overlay && open && mounted);
9047
- React24__default.useEffect(() => {
9527
+ React25__default.useEffect(() => {
9048
9528
  if (!overlay || !open) return void 0;
9049
9529
  const onKey = (e) => {
9050
9530
  if (e.key === "Escape") {
@@ -9096,7 +9576,7 @@ function Sidebar({
9096
9576
  const body = /* @__PURE__ */ jsxs(Fragment, { children: [
9097
9577
  __twcStyles,
9098
9578
  brand ? /* @__PURE__ */ jsx("div", { className: "twc-sidebar__head", children: /* @__PURE__ */ jsx("span", { className: "twc-sidebar__brand", children: brand }) }) : null,
9099
- /* @__PURE__ */ jsx("nav", { id: navId, className: "twc-sidebar__nav", "aria-label": navLabel, children: groups.map((g, gi) => /* @__PURE__ */ jsxs(React24__default.Fragment, { children: [
9579
+ /* @__PURE__ */ jsx("nav", { id: navId, className: "twc-sidebar__nav", "aria-label": navLabel, children: groups.map((g, gi) => /* @__PURE__ */ jsxs(React25__default.Fragment, { children: [
9100
9580
  g.section != null ? /* @__PURE__ */ jsx("div", { className: "twc-sidebar__section", id: g.sid, children: g.section }) : null,
9101
9581
  /* @__PURE__ */ jsx("ul", { className: "twc-sidebar__list", ...g.section != null ? { "aria-labelledby": g.sid } : {}, children: g.entries.map(({ it, i }) => /* @__PURE__ */ jsx("li", { children: renderItem(it) }, i)) })
9102
9582
  ] }, gi)) }),
@@ -9235,11 +9715,16 @@ function Slider({
9235
9715
  min: minProp = 0,
9236
9716
  max: maxProp = 100,
9237
9717
  step: stepProp = 1,
9718
+ pageStep: pageStepProp,
9719
+ precision,
9238
9720
  tone = "primary",
9239
9721
  disabled = false,
9722
+ range = false,
9240
9723
  showValue = true,
9241
9724
  showTicks = false,
9242
9725
  formatValue,
9726
+ getAriaValueText,
9727
+ name,
9243
9728
  onChange,
9244
9729
  id,
9245
9730
  className = "",
@@ -9248,38 +9733,73 @@ function Slider({
9248
9733
  const min = Number.isFinite(minProp) ? minProp : 0;
9249
9734
  const max = Number.isFinite(maxProp) && maxProp > min ? maxProp : min + 1;
9250
9735
  const step = Number.isFinite(stepProp) && stepProp > 0 ? stepProp : 1;
9251
- const __twcStyles = useScopedStyles("twc-slider-styles", SLIDER_CSS);
9252
- const autoId = React24__default.useId();
9253
- const fieldId = id || autoId;
9254
- const [internal, setInternal] = React24__default.useState(defaultValue);
9255
- const val = value !== void 0 ? value : internal;
9256
- const trackRef = React24__default.useRef(null);
9257
- const [dragging, setDragging] = React24__default.useState(false);
9258
- const pct = (val - min) / (max - min) * 100;
9259
- const fmt = (v) => formatValue ? formatValue(v) : v;
9736
+ const pageStep = Number.isFinite(pageStepProp) && pageStepProp > 0 ? pageStepProp : Math.max(step, (max - min) / 10);
9737
+ const stepDecimals = (() => {
9738
+ const s = String(step);
9739
+ const i = s.indexOf(".");
9740
+ return i === -1 ? 0 : s.length - i - 1;
9741
+ })();
9742
+ const decimals = Number.isInteger(precision) && precision >= 0 ? precision : stepDecimals;
9260
9743
  const clampSnap = (raw) => {
9261
9744
  const stepped = Math.round((raw - min) / step) * step + min;
9262
9745
  return Math.min(max, Math.max(min, Math.round(stepped * 1e6) / 1e6));
9263
9746
  };
9264
- const setVal = (v) => {
9265
- if (value === void 0) setInternal(v);
9266
- onChange?.(v);
9747
+ const __twcStyles = useScopedStyles("twc-slider-styles", SLIDER_CSS);
9748
+ const autoId = React25__default.useId();
9749
+ const fieldId = id || autoId;
9750
+ const isRange = range || Array.isArray(value) || Array.isArray(defaultValue);
9751
+ const [internal, setInternal] = React25__default.useState(() => {
9752
+ if (isRange) {
9753
+ const dv = Array.isArray(defaultValue) ? defaultValue : [min, max];
9754
+ return [clampSnap(dv[0]), clampSnap(dv[1])];
9755
+ }
9756
+ return clampSnap(Number.isFinite(defaultValue) ? defaultValue : min);
9757
+ });
9758
+ const rawVal = value !== void 0 ? value : internal;
9759
+ const clampedVals = (isRange ? [Number(rawVal[0]), Number(rawVal[1])] : [Number(rawVal)]).map((v) => Math.min(max, Math.max(min, Number.isFinite(v) ? v : min)));
9760
+ const indices = isRange ? [0, 1] : [0];
9761
+ const trackRef = React25__default.useRef(null);
9762
+ const [dragging, setDragging] = React25__default.useState(false);
9763
+ const fmt = (v) => formatValue ? formatValue(v) : v.toFixed(decimals);
9764
+ const valueTextFor = (v) => {
9765
+ if (getAriaValueText) return getAriaValueText(v);
9766
+ if (formatValue) {
9767
+ const d = formatValue(v);
9768
+ return typeof d === "string" || typeof d === "number" ? String(d) : void 0;
9769
+ }
9770
+ return decimals > 0 ? v.toFixed(decimals) : void 0;
9771
+ };
9772
+ const commit = (nextVals) => {
9773
+ const out = isRange ? nextVals : nextVals[0];
9774
+ if (value === void 0) setInternal(out);
9775
+ onChange?.(out);
9776
+ };
9777
+ const setThumb = (i, raw) => {
9778
+ const next = clampedVals.slice();
9779
+ next[i] = clampSnap(raw);
9780
+ if (isRange) {
9781
+ if (i === 0) next[0] = Math.min(next[0], next[1]);
9782
+ else next[1] = Math.max(next[1], next[0]);
9783
+ }
9784
+ commit(next);
9267
9785
  };
9268
9786
  const fromClientX = (clientX) => {
9269
9787
  const node = trackRef.current;
9270
- if (!node) return val;
9788
+ if (!node) return clampedVals[0];
9271
9789
  const r = node.getBoundingClientRect();
9272
9790
  const ratio = Math.min(1, Math.max(0, (clientX - r.left) / r.width));
9273
9791
  return clampSnap(min + ratio * (max - min));
9274
9792
  };
9275
- const dragCleanupRef = React24__default.useRef(null);
9276
- React24__default.useEffect(() => () => dragCleanupRef.current?.(), []);
9793
+ const dragCleanupRef = React25__default.useRef(null);
9794
+ React25__default.useEffect(() => () => dragCleanupRef.current?.(), []);
9277
9795
  function onPointerDown(e) {
9278
9796
  if (disabled) return;
9279
9797
  e.preventDefault();
9798
+ const posVal = fromClientX(e.clientX);
9799
+ const idx = isRange ? Math.abs(posVal - clampedVals[0]) <= Math.abs(posVal - clampedVals[1]) ? 0 : 1 : 0;
9280
9800
  setDragging(true);
9281
- setVal(fromClientX(e.clientX));
9282
- const move = (ev) => setVal(fromClientX(ev.clientX));
9801
+ setThumb(idx, posVal);
9802
+ const move = (ev) => setThumb(idx, fromClientX(ev.clientX));
9283
9803
  const detach = () => {
9284
9804
  window.removeEventListener("pointermove", move);
9285
9805
  window.removeEventListener("pointerup", up);
@@ -9293,54 +9813,68 @@ function Slider({
9293
9813
  window.addEventListener("pointermove", move);
9294
9814
  window.addEventListener("pointerup", up);
9295
9815
  }
9296
- function onKeyDown(e) {
9816
+ function onThumbKeyDown(e, i) {
9297
9817
  if (disabled) return;
9818
+ const cur = clampedVals[i];
9298
9819
  let next;
9299
- if (e.key === "ArrowRight" || e.key === "ArrowUp") next = clampSnap(val + step);
9300
- else if (e.key === "ArrowLeft" || e.key === "ArrowDown") next = clampSnap(val - step);
9820
+ if (e.key === "ArrowRight" || e.key === "ArrowUp") next = cur + step;
9821
+ else if (e.key === "ArrowLeft" || e.key === "ArrowDown") next = cur - step;
9822
+ else if (e.key === "PageUp") next = cur + pageStep;
9823
+ else if (e.key === "PageDown") next = cur - pageStep;
9301
9824
  else if (e.key === "Home") next = min;
9302
9825
  else if (e.key === "End") next = max;
9303
9826
  else return;
9304
9827
  e.preventDefault();
9305
- setVal(next);
9828
+ setThumb(i, next);
9306
9829
  }
9307
9830
  const ticks = showTicks ? Math.floor((max - min) / step) + 1 : 0;
9308
9831
  const labelId = label ? `${fieldId}-label` : void 0;
9309
9832
  const descId = `${fieldId}-desc`;
9310
9833
  const invalid = Boolean(error);
9311
- const display = fmt(val);
9312
- const valueText = formatValue && (typeof display === "string" || typeof display === "number") ? String(display) : void 0;
9834
+ const headValue = isRange ? `${fmt(clampedVals[0])} \u2013 ${fmt(clampedVals[1])}` : fmt(clampedVals[0]);
9835
+ const fillLo = (Math.min(...clampedVals) - min) / (max - min) * 100;
9836
+ const fillHi = (Math.max(...clampedVals) - min) / (max - min) * 100;
9837
+ const fillStyle = isRange ? { left: `${fillLo}%`, width: `${fillHi - fillLo}%` } : { width: `${fillHi}%` };
9313
9838
  return /* @__PURE__ */ jsxs("div", { className: `twc-slider ${className}`, "data-tone": tone, "data-disabled": disabled || void 0, ...rest, children: [
9314
9839
  __twcStyles,
9840
+ name ? clampedVals.map((v, i) => /* @__PURE__ */ jsx("input", { type: "hidden", name, value: v, disabled: disabled || void 0 }, i)) : null,
9315
9841
  label || showValue ? /* @__PURE__ */ jsxs("div", { className: "twc-slider__head", children: [
9316
9842
  label ? /* @__PURE__ */ jsx("label", { className: "twc-slider__label", id: labelId, children: label }) : /* @__PURE__ */ jsx("span", {}),
9317
- showValue ? /* @__PURE__ */ jsx("span", { className: "twc-slider__value", children: display }) : null
9843
+ showValue ? /* @__PURE__ */ jsx("span", { className: "twc-slider__value", children: headValue }) : null
9318
9844
  ] }) : null,
9319
9845
  /* @__PURE__ */ jsxs("div", { className: "twc-slider__track", ref: trackRef, "data-show-bubble": dragging || void 0, onPointerDown, children: [
9320
9846
  /* @__PURE__ */ jsx("div", { className: "twc-slider__rail" }),
9321
- /* @__PURE__ */ jsx("div", { className: "twc-slider__fill", style: { width: `${pct}%` } }),
9847
+ /* @__PURE__ */ jsx("div", { className: "twc-slider__fill", style: fillStyle }),
9322
9848
  showTicks ? /* @__PURE__ */ jsx("div", { className: "twc-slider__ticks", children: Array.from({ length: ticks }).map((_, i) => /* @__PURE__ */ jsx("span", { className: "twc-slider__tick", style: { left: `${ticks > 1 ? i / (ticks - 1) * 100 : 0}%` } }, i)) }) : null,
9323
- /* @__PURE__ */ jsx(
9324
- "div",
9325
- {
9326
- id: fieldId,
9327
- className: "twc-slider__thumb",
9328
- style: { left: `${pct}%` },
9329
- role: "slider",
9330
- tabIndex: disabled ? -1 : 0,
9331
- "aria-valuemin": min,
9332
- "aria-valuemax": max,
9333
- "aria-valuenow": val,
9334
- "aria-valuetext": valueText,
9335
- "aria-labelledby": labelId,
9336
- "aria-label": labelId ? void 0 : "Slider",
9337
- "aria-describedby": error || hint ? descId : void 0,
9338
- "aria-invalid": invalid || void 0,
9339
- "aria-disabled": disabled || void 0,
9340
- onKeyDown,
9341
- children: /* @__PURE__ */ jsx("span", { className: "twc-slider__bubble", children: display })
9342
- }
9343
- )
9849
+ indices.map((i) => {
9850
+ const tv = clampedVals[i];
9851
+ const p = (tv - min) / (max - min) * 100;
9852
+ const tMin = isRange && i === 1 ? clampedVals[0] : min;
9853
+ const tMax = isRange && i === 0 ? clampedVals[1] : max;
9854
+ const thumbLabel = isRange ? i === 0 ? "Minimum" : "Maximum" : labelId ? void 0 : "Slider";
9855
+ return /* @__PURE__ */ jsx(
9856
+ "div",
9857
+ {
9858
+ id: i === 0 ? fieldId : void 0,
9859
+ className: "twc-slider__thumb",
9860
+ style: { left: `${p}%` },
9861
+ role: "slider",
9862
+ tabIndex: disabled ? -1 : 0,
9863
+ "aria-valuemin": tMin,
9864
+ "aria-valuemax": tMax,
9865
+ "aria-valuenow": tv,
9866
+ "aria-valuetext": valueTextFor(tv),
9867
+ "aria-labelledby": isRange ? void 0 : labelId,
9868
+ "aria-label": thumbLabel,
9869
+ "aria-describedby": error || hint ? descId : void 0,
9870
+ "aria-invalid": invalid || void 0,
9871
+ "aria-disabled": disabled || void 0,
9872
+ onKeyDown: (e) => onThumbKeyDown(e, i),
9873
+ children: /* @__PURE__ */ jsx("span", { className: "twc-slider__bubble", children: fmt(tv) })
9874
+ },
9875
+ i
9876
+ );
9877
+ })
9344
9878
  ] }),
9345
9879
  error || hint ? /* @__PURE__ */ jsx("div", { className: "twc-slider__msgs", children: error ? /* @__PURE__ */ jsx("span", { id: descId, className: "twc-field__error", children: error }) : /* @__PURE__ */ jsx("span", { id: descId, className: "twc-field__hint", children: hint }) }) : null
9346
9880
  ] });
@@ -9488,7 +10022,7 @@ function Stepper({
9488
10022
  ...rest
9489
10023
  }) {
9490
10024
  const __twcStyles = useScopedStyles("twc-stepper-styles", STEPPER_CSS);
9491
- const [activeInternal, setActiveInternal] = React24__default.useState(defaultActive);
10025
+ const [activeInternal, setActiveInternal] = React25__default.useState(defaultActive);
9492
10026
  const active = activeProp !== void 0 ? activeProp : activeInternal;
9493
10027
  const handleStep = (i) => {
9494
10028
  if (activeProp === void 0) setActiveInternal(i);
@@ -9620,10 +10154,13 @@ function Switch({
9620
10154
  .twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
9621
10155
  .twc-field__error { font-size: var(--text-xs); color: var(--color-danger-subtle-fg); font-weight: var(--font-medium); }
9622
10156
  `);
9623
- const autoId = React24__default.useId();
10157
+ const autoId = React25__default.useId();
9624
10158
  const fieldId = id || autoId;
9625
10159
  const errId = `${fieldId}-error`;
9626
10160
  const invalid = Boolean(error);
10161
+ if (process.env.NODE_ENV !== "production" && !label && !description && !rest["aria-label"] && !rest["aria-labelledby"]) {
10162
+ 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).");
10163
+ }
9627
10164
  const describedBy = [rest["aria-describedby"], error ? errId : null].filter(Boolean).join(" ") || void 0;
9628
10165
  const control = /* @__PURE__ */ 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: [
9629
10166
  __twcStyles,
@@ -9639,6 +10176,7 @@ function Switch({
9639
10176
  disabled,
9640
10177
  onChange,
9641
10178
  ...rest,
10179
+ required: required || void 0,
9642
10180
  "aria-required": required || void 0,
9643
10181
  "aria-invalid": invalid || void 0,
9644
10182
  "aria-describedby": describedBy
@@ -9685,6 +10223,13 @@ var TABLE_CSS = `
9685
10223
  .twc-table[data-size="lg"] thead th { padding: var(--space-4) var(--space-5); }
9686
10224
  .twc-table[data-size="lg"] tbody td { padding: var(--space-4) var(--space-5); font-size: var(--text-base); }
9687
10225
  .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); }
10226
+ .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; }
10227
+ .twc-table__empty { padding: 30px 12px; text-align: center; color: var(--color-text-subtle); font-size: var(--text-sm); }
10228
+ .twc-table__sk { display: inline-block; height: 12px; border-radius: var(--radius-sm); background: var(--color-surface-sunken);
10229
+ position: relative; overflow: hidden; width: var(--_w, 70%); }
10230
+ .twc-table__sk::after { content: ""; position: absolute; inset: 0; transform: translateX(-100%);
10231
+ background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--color-text) 8%, transparent), transparent);
10232
+ animation: twico-shimmer 1.4s infinite; }
9688
10233
  `;
9689
10234
  function Table({
9690
10235
  columns,
@@ -9700,18 +10245,26 @@ function Table({
9700
10245
  selectedKeys,
9701
10246
  stickyHeader = false,
9702
10247
  maxHeight,
10248
+ loading = false,
10249
+ loadingRows = 8,
10250
+ emptyMessage = "No data",
10251
+ ariaLabel,
10252
+ caption,
10253
+ // Pulled out of `rest` so they land on the <table>, not the wrapper <div> (#128).
10254
+ "aria-label": ariaLabelRaw,
10255
+ "aria-labelledby": ariaLabelledby,
9703
10256
  className = "",
9704
10257
  ...rest
9705
10258
  }) {
9706
10259
  const __twcStyles = useScopedStyles("twc-table-styles", TABLE_CSS);
9707
- const cols = React24__default.useMemo(
10260
+ const cols = React25__default.useMemo(
9708
10261
  () => (columns || []).map((c) => ({ ...c, header: c.headerName, render: c.renderCell })),
9709
10262
  [columns]
9710
10263
  );
9711
10264
  const dataRows = rowsProp || [];
9712
- const [internalSort, setInternalSort] = React24__default.useState(defaultSort || { field: null, dir: "asc" });
10265
+ const [internalSort, setInternalSort] = React25__default.useState(defaultSort || { field: null, dir: "asc" });
9713
10266
  const sort = (sortProp !== void 0 ? sortProp : internalSort) || { field: null, dir: "asc" };
9714
- const rows = React24__default.useMemo(() => {
10267
+ const rows = React25__default.useMemo(() => {
9715
10268
  if (!sortable || !sort.field) return dataRows;
9716
10269
  const col = cols.find((c) => c.field === sort.field);
9717
10270
  if (!col) return dataRows;
@@ -9734,32 +10287,46 @@ function Table({
9734
10287
  const wrapStyle = maxHeight !== void 0 ? { ...rest.style, maxHeight, overflowY: "auto" } : rest.style;
9735
10288
  return /* @__PURE__ */ jsxs("div", { className: `twc-table-wrap ${className}`, ...rest, style: wrapStyle, children: [
9736
10289
  __twcStyles,
9737
- /* @__PURE__ */ jsxs("table", { className: "twc-table", "data-hover": hover || void 0, "data-striped": striped || void 0, "data-size": size, "data-sticky": stickyHeader || void 0, children: [
9738
- /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: cols.map((c) => {
9739
- const active = sort.field === c.field;
9740
- const canSort = sortable && c.sortable !== false;
9741
- return /* @__PURE__ */ jsx(
9742
- "th",
9743
- {
9744
- "data-align": c.align,
9745
- "data-sortable": canSort || void 0,
9746
- "data-active": active || void 0,
9747
- "data-dir": active ? sort.dir : void 0,
9748
- style: c.width ? { width: c.width } : void 0,
9749
- "aria-sort": active ? sort.dir === "asc" ? "ascending" : "descending" : canSort ? "none" : void 0,
9750
- children: canSort ? /* @__PURE__ */ jsxs("button", { type: "button", className: "twc-table__sort", onClick: () => toggleSort(c.field), children: [
9751
- c.header,
9752
- /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "m18 15-6-6-6 6" }) })
9753
- ] }) : c.header
9754
- },
9755
- c.field
9756
- );
9757
- }) }) }),
9758
- /* @__PURE__ */ jsx("tbody", { children: rows.map((row, i) => {
9759
- const k = keyFn(row, i);
9760
- return /* @__PURE__ */ jsx("tr", { "data-selected": selected.has(k) || void 0, children: cols.map((c) => /* @__PURE__ */ jsx("td", { "data-align": c.align, children: c.render ? c.render(row[c.field], row, i) : row[c.field] }, c.field)) }, k);
9761
- }) })
9762
- ] })
10290
+ /* @__PURE__ */ jsxs(
10291
+ "table",
10292
+ {
10293
+ className: "twc-table",
10294
+ "data-hover": hover || void 0,
10295
+ "data-striped": striped || void 0,
10296
+ "data-size": size,
10297
+ "data-sticky": stickyHeader || void 0,
10298
+ "aria-label": ariaLabelRaw ?? ariaLabel,
10299
+ "aria-labelledby": ariaLabelledby,
10300
+ "aria-busy": loading || void 0,
10301
+ children: [
10302
+ caption != null ? /* @__PURE__ */ jsx("caption", { className: "twc-table__caption", children: caption }) : null,
10303
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: cols.map((c) => {
10304
+ const active = sort.field === c.field;
10305
+ const canSort = sortable && c.sortable !== false;
10306
+ return /* @__PURE__ */ jsx(
10307
+ "th",
10308
+ {
10309
+ "data-align": c.align,
10310
+ "data-sortable": canSort || void 0,
10311
+ "data-active": active || void 0,
10312
+ "data-dir": active ? sort.dir : void 0,
10313
+ style: c.width ? { width: c.width } : void 0,
10314
+ "aria-sort": active ? sort.dir === "asc" ? "ascending" : "descending" : canSort ? "none" : void 0,
10315
+ children: canSort ? /* @__PURE__ */ jsxs("button", { type: "button", className: "twc-table__sort", onClick: () => toggleSort(c.field), children: [
10316
+ c.header,
10317
+ /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "m18 15-6-6-6 6" }) })
10318
+ ] }) : c.header
10319
+ },
10320
+ c.field
10321
+ );
10322
+ }) }) }),
10323
+ /* @__PURE__ */ jsx("tbody", { children: loading ? Array.from({ length: loadingRows }).map((_, ri) => /* @__PURE__ */ jsx("tr", { children: cols.map((c) => /* @__PURE__ */ jsx("td", { "data-align": c.align, children: /* @__PURE__ */ jsx("span", { className: "twc-table__sk" }) }, c.field)) }, `sk${ri}`)) : rows.length === 0 ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan: cols.length || 1, className: "twc-table__empty", children: emptyMessage }) }) : rows.map((row, i) => {
10324
+ const k = keyFn(row, i);
10325
+ return /* @__PURE__ */ jsx("tr", { "data-selected": selected.has(k) || void 0, children: cols.map((c) => /* @__PURE__ */ jsx("td", { "data-align": c.align, children: c.render ? c.render(row[c.field], row, i) : row[c.field] }, c.field)) }, k);
10326
+ }) })
10327
+ ]
10328
+ }
10329
+ )
9763
10330
  ] });
9764
10331
  }
9765
10332
  var TABS_CSS = `
@@ -9844,15 +10411,15 @@ function Tabs({
9844
10411
  }) {
9845
10412
  const vertical = orientation === "vertical";
9846
10413
  const __twcStyles = useScopedStyles("twc-tabs-styles", TABS_CSS);
9847
- const [internal, setInternal] = React24__default.useState(defaultValue ?? items[0]?.value);
10414
+ const [internal, setInternal] = React25__default.useState(defaultValue ?? items[0]?.value);
9848
10415
  const active = value ?? internal;
9849
- const listRef = React24__default.useRef(null);
9850
- const [ind, setInd] = React24__default.useState({ left: 0, width: 0, top: 0, height: 0 });
9851
- const baseId = React24__default.useId();
10416
+ const listRef = React25__default.useRef(null);
10417
+ const [ind, setInd] = React25__default.useState({ left: 0, width: 0, top: 0, height: 0 });
10418
+ const baseId = React25__default.useId();
9852
10419
  const tabIdAt = (i) => `${baseId}-tab-${i}`;
9853
10420
  const panelId = `${baseId}-panel`;
9854
10421
  const activeIndex = items.findIndex((it) => it.value === active);
9855
- const updateIndicator = React24__default.useCallback(() => {
10422
+ const updateIndicator = React25__default.useCallback(() => {
9856
10423
  const list = listRef.current;
9857
10424
  if (!list) return;
9858
10425
  const el = list.querySelector('[data-active="true"]');
@@ -9861,10 +10428,10 @@ function Tabs({
9861
10428
  if (!vertical) el.scrollIntoView?.({ inline: "nearest", block: "nearest" });
9862
10429
  }
9863
10430
  }, [vertical]);
9864
- React24__default.useEffect(() => {
10431
+ React25__default.useEffect(() => {
9865
10432
  updateIndicator();
9866
10433
  }, [active, updateIndicator, items, orientation]);
9867
- React24__default.useEffect(() => {
10434
+ React25__default.useEffect(() => {
9868
10435
  const r = () => updateIndicator();
9869
10436
  window.addEventListener("resize", r);
9870
10437
  if (typeof document !== "undefined" && document.fonts && document.fonts.ready) {
@@ -10009,20 +10576,8 @@ function Tag({
10009
10576
  }, "aria-label": _removeLabel, type: "button", children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.4", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "M18 6 6 18M6 6l12 12" }) }) }) : null
10010
10577
  ] });
10011
10578
  }
10012
- function Textarea({
10013
- label,
10014
- hint,
10015
- error,
10016
- required = false,
10017
- size = "md",
10018
- tone = "primary",
10019
- rows = 4,
10020
- disabled = false,
10021
- id,
10022
- className = "",
10023
- ...rest
10024
- }) {
10025
- const __twcStyles = useScopedStyles("twc-textarea-styles", `
10579
+ var useIso = typeof document !== "undefined" ? React25__default.useLayoutEffect : React25__default.useEffect;
10580
+ var TEXTAREA_CSS = `
10026
10581
  .twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
10027
10582
  .twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
10028
10583
  .twc-field__req { color: var(--color-danger); }
@@ -10051,11 +10606,74 @@ function Textarea({
10051
10606
  .twc-textarea__el:focus { border-color: var(--_accent); box-shadow: var(--_ring); }
10052
10607
  .twc-textarea__el[aria-invalid="true"] { border-color: var(--color-danger); }
10053
10608
  .twc-textarea__el:disabled { background: var(--color-surface-sunken); opacity: 0.7; cursor: not-allowed; }
10054
- `);
10055
- const autoId = React24__default.useId();
10609
+ .twc-textarea__el[data-readonly] { background: var(--color-surface-sunken); }
10610
+ .twc-textarea__el[data-autosize="true"] { resize: none; overflow: hidden; }
10611
+ .twc-field__footer { display: flex; align-items: center; gap: var(--space-2); }
10612
+ .twc-field__count { margin-inline-start: auto; font-size: var(--text-xs); color: var(--color-text-muted); font-variant-numeric: tabular-nums; flex: none; }
10613
+ .twc-field__count[data-danger="true"] { color: var(--color-danger-subtle-fg); }
10614
+ `;
10615
+ var Textarea = React25__default.forwardRef(function Textarea2({
10616
+ label,
10617
+ hint,
10618
+ error,
10619
+ required = false,
10620
+ size = "md",
10621
+ tone = "primary",
10622
+ rows = 4,
10623
+ autosize = false,
10624
+ minRows,
10625
+ maxRows,
10626
+ showCount = false,
10627
+ disabled = false,
10628
+ id,
10629
+ className = "",
10630
+ ...rest
10631
+ }, ref) {
10632
+ const __twcStyles = useScopedStyles("twc-textarea-styles", TEXTAREA_CSS);
10633
+ const autoId = React25__default.useId();
10056
10634
  const fieldId = id || autoId;
10057
10635
  const descId = `${fieldId}-desc`;
10636
+ const countId = `${fieldId}-count`;
10058
10637
  const invalid = Boolean(error);
10638
+ const innerRef = React25__default.useRef(null);
10639
+ const setRef = (el) => {
10640
+ innerRef.current = el;
10641
+ if (typeof ref === "function") ref(el);
10642
+ else if (ref) ref.current = el;
10643
+ };
10644
+ const countControlled = rest.value !== void 0;
10645
+ const [len2, setLen] = React25__default.useState(() => String(rest.defaultValue ?? "").length);
10646
+ const count = countControlled ? String(rest.value ?? "").length : len2;
10647
+ const max = rest.maxLength;
10648
+ const showCounter = showCount && max != null;
10649
+ const near = showCounter && count >= Math.floor(max * 0.9);
10650
+ const resize = () => {
10651
+ const el = innerRef.current;
10652
+ if (!el) return;
10653
+ el.style.height = "auto";
10654
+ const cs = getComputedStyle(el);
10655
+ const line = parseFloat(cs.lineHeight) || 20;
10656
+ const vPad = (parseFloat(cs.paddingTop) || 0) + (parseFloat(cs.paddingBottom) || 0);
10657
+ const vBorder = (parseFloat(cs.borderTopWidth) || 0) + (parseFloat(cs.borderBottomWidth) || 0);
10658
+ const min = (minRows ?? rows) * line + vPad + vBorder;
10659
+ const maxH = maxRows ? maxRows * line + vPad + vBorder : Infinity;
10660
+ const next = Math.min(Math.max(el.scrollHeight + vBorder, min), maxH);
10661
+ el.style.height = `${next}px`;
10662
+ el.style.overflowY = el.scrollHeight + vBorder > maxH ? "auto" : "hidden";
10663
+ };
10664
+ useIso(() => {
10665
+ if (autosize && innerRef.current) resize();
10666
+ }, [autosize, rest.value, minRows, maxRows, rows]);
10667
+ const handleInput = (e) => {
10668
+ rest.onInput?.(e);
10669
+ if (autosize) resize();
10670
+ };
10671
+ const handleChange = (e) => {
10672
+ rest.onChange?.(e);
10673
+ if (showCount && !countControlled) setLen(e.target.value.length);
10674
+ };
10675
+ const ariaInvalid = invalid ? true : rest["aria-invalid"] ?? void 0;
10676
+ const describedBy = [error || hint ? descId : null, showCounter ? countId : null, rest["aria-describedby"]].filter(Boolean).join(" ") || void 0;
10059
10677
  return /* @__PURE__ */ jsxs("div", { className: `twc-field ${className}`, children: [
10060
10678
  __twcStyles,
10061
10679
  label ? /* @__PURE__ */ jsxs("label", { className: "twc-field__label", htmlFor: fieldId, children: [
@@ -10065,22 +10683,34 @@ function Textarea({
10065
10683
  /* @__PURE__ */ jsx(
10066
10684
  "textarea",
10067
10685
  {
10686
+ ref: setRef,
10068
10687
  id: fieldId,
10069
10688
  className: "twc-textarea__el",
10070
10689
  "data-size": size,
10071
10690
  "data-tone": tone,
10072
- rows,
10691
+ "data-autosize": autosize || void 0,
10692
+ "data-readonly": rest.readOnly || void 0,
10693
+ rows: autosize ? minRows ?? rows : rows,
10073
10694
  disabled,
10074
10695
  required: required || void 0,
10075
10696
  "aria-required": required || void 0,
10076
- "aria-invalid": invalid || void 0,
10077
- "aria-describedby": error || hint ? descId : void 0,
10078
- ...rest
10697
+ ...rest,
10698
+ onInput: handleInput,
10699
+ onChange: handleChange,
10700
+ "aria-invalid": ariaInvalid,
10701
+ "aria-describedby": describedBy
10079
10702
  }
10080
10703
  ),
10081
- error ? /* @__PURE__ */ jsx("span", { id: descId, className: "twc-field__error", children: error }) : hint ? /* @__PURE__ */ jsx("span", { id: descId, className: "twc-field__hint", children: hint }) : null
10704
+ error || hint || showCounter ? /* @__PURE__ */ jsxs("div", { className: "twc-field__footer", children: [
10705
+ error ? /* @__PURE__ */ jsx("span", { id: descId, className: "twc-field__error", children: error }) : hint ? /* @__PURE__ */ jsx("span", { id: descId, className: "twc-field__hint", children: hint }) : null,
10706
+ showCounter ? /* @__PURE__ */ jsxs("span", { id: countId, className: "twc-field__count", "data-danger": near || void 0, "aria-live": "polite", children: [
10707
+ count,
10708
+ " / ",
10709
+ max
10710
+ ] }) : null
10711
+ ] }) : null
10082
10712
  ] });
10083
- }
10713
+ });
10084
10714
  var TIMELINE_CSS = `
10085
10715
  .twc-timeline { display: flex; flex-direction: column; font-family: var(--font-sans); margin: 0; padding: 0; list-style: none; }
10086
10716
  .twc-timeline__item { position: relative; display: flex; gap: var(--space-4); padding-bottom: var(--space-6); }
@@ -10190,28 +10820,29 @@ function Toast({
10190
10820
  }) {
10191
10821
  const resolvedTone = tone === "neutral" ? "default" : tone;
10192
10822
  const __twcStyles = useScopedStyles("twc-toast-styles", TOAST_CSS);
10193
- const onCloseRef = React24__default.useRef(onClose);
10194
- React24__default.useEffect(() => {
10823
+ const onCloseRef = React25__default.useRef(onClose);
10824
+ React25__default.useEffect(() => {
10195
10825
  onCloseRef.current = onClose;
10196
10826
  }, [onClose]);
10197
- const timer = React24__default.useRef();
10198
- const stop = React24__default.useCallback(() => clearTimeout(timer.current), []);
10199
- const start = React24__default.useCallback(() => {
10827
+ const timer = React25__default.useRef();
10828
+ const stop = React25__default.useCallback(() => clearTimeout(timer.current), []);
10829
+ const start = React25__default.useCallback(() => {
10200
10830
  stop();
10201
10831
  if (duration && duration !== Infinity && onCloseRef.current) {
10202
10832
  timer.current = setTimeout(() => onCloseRef.current && onCloseRef.current(), duration);
10203
10833
  }
10204
10834
  }, [duration, stop]);
10205
- React24__default.useEffect(() => {
10835
+ React25__default.useEffect(() => {
10206
10836
  start();
10207
10837
  return stop;
10208
10838
  }, [start, stop]);
10839
+ const role = resolvedTone === "danger" || resolvedTone === "warning" ? "alert" : "status";
10209
10840
  return /* @__PURE__ */ jsxs(
10210
10841
  "div",
10211
10842
  {
10212
10843
  className: `twc-toast ${className}`,
10213
10844
  "data-tone": resolvedTone,
10214
- role: "status",
10845
+ role,
10215
10846
  ...rest,
10216
10847
  onMouseEnter: (e) => {
10217
10848
  onMouseEnter?.(e);
@@ -10246,30 +10877,30 @@ function Toast({
10246
10877
  }
10247
10878
  function ToastViewport({ children, limit, className = "", ...rest }) {
10248
10879
  const __twcStyles = useScopedStyles("twc-toast-styles", TOAST_CSS);
10249
- const shown = limit && limit > 0 ? React24__default.Children.toArray(children).slice(-limit) : children;
10250
- return /* @__PURE__ */ jsxs("div", { className: `twc-toast-viewport ${className}`, ...rest, children: [
10880
+ const shown = limit && limit > 0 ? React25__default.Children.toArray(children).slice(-limit) : children;
10881
+ return /* @__PURE__ */ jsxs("div", { className: `twc-toast-viewport ${className}`, role: "region", "aria-label": "Notifications", ...rest, children: [
10251
10882
  __twcStyles,
10252
10883
  shown
10253
10884
  ] });
10254
10885
  }
10255
- var ToastContext = React24__default.createContext(null);
10886
+ var ToastContext = React25__default.createContext(null);
10256
10887
  function useToast() {
10257
- const ctx = React24__default.useContext(ToastContext);
10888
+ const ctx = React25__default.useContext(ToastContext);
10258
10889
  if (!ctx) throw new Error("useToast must be used within a <ToastProvider>.");
10259
10890
  return ctx;
10260
10891
  }
10261
10892
  function ToastProvider({ children, limit = 4, duration = 4500, ...viewportProps }) {
10262
- const [toasts, setToasts] = React24__default.useState([]);
10263
- const idRef = React24__default.useRef(0);
10264
- const limitRef = React24__default.useRef(limit);
10265
- React24__default.useEffect(() => {
10893
+ const [toasts, setToasts] = React25__default.useState([]);
10894
+ const idRef = React25__default.useRef(0);
10895
+ const limitRef = React25__default.useRef(limit);
10896
+ React25__default.useEffect(() => {
10266
10897
  limitRef.current = limit;
10267
10898
  }, [limit]);
10268
- const dismiss = React24__default.useCallback((id) => {
10899
+ const dismiss = React25__default.useCallback((id) => {
10269
10900
  setToasts((s) => s.filter((t) => t.id !== id));
10270
10901
  }, []);
10271
- const clear = React24__default.useCallback(() => setToasts([]), []);
10272
- const api = React24__default.useMemo(() => {
10902
+ const clear = React25__default.useCallback(() => setToasts([]), []);
10903
+ const api = React25__default.useMemo(() => {
10273
10904
  const push = (opts) => {
10274
10905
  const id = idRef.current += 1;
10275
10906
  const next = typeof opts === "string" ? { title: opts } : opts && typeof opts === "object" ? opts : {};
@@ -10290,7 +10921,7 @@ function ToastProvider({ children, limit = 4, duration = 4500, ...viewportProps
10290
10921
  toast.show = withTone("default");
10291
10922
  return { toast, dismiss, clear };
10292
10923
  }, [dismiss, clear]);
10293
- const value = React24__default.useMemo(() => ({ ...api, toasts }), [api, toasts]);
10924
+ const value = React25__default.useMemo(() => ({ ...api, toasts }), [api, toasts]);
10294
10925
  return /* @__PURE__ */ jsxs(ToastContext.Provider, { value, children: [
10295
10926
  children,
10296
10927
  /* @__PURE__ */ jsx(ToastViewport, { limit, ...viewportProps, children: toasts.map((t) => /* @__PURE__ */ jsx(
@@ -10398,13 +11029,13 @@ function TreeView({
10398
11029
  }) {
10399
11030
  const nodes = items;
10400
11031
  const __twcStyles = useScopedStyles("twc-tree-styles", TREE_CSS);
10401
- const [expInternal, setExpInternal] = React24__default.useState(() => new Set(defaultExpanded));
11032
+ const [expInternal, setExpInternal] = React25__default.useState(() => new Set(defaultExpanded));
10402
11033
  const expanded = expandedProp !== void 0 ? new Set(expandedProp) : expInternal;
10403
- const [selInternal, setSelInternal] = React24__default.useState(defaultSelectedId);
11034
+ const [selInternal, setSelInternal] = React25__default.useState(defaultSelectedId);
10404
11035
  const selectedId = selectedProp !== void 0 ? selectedProp : selInternal;
10405
- const [focusId, setFocusId] = React24__default.useState(null);
10406
- const rowRefs = React24__default.useRef(/* @__PURE__ */ new Map());
10407
- const taRef = React24__default.useRef({ str: "", t: 0 });
11036
+ const [focusId, setFocusId] = React25__default.useState(null);
11037
+ const rowRefs = React25__default.useRef(/* @__PURE__ */ new Map());
11038
+ const taRef = React25__default.useRef({ str: "", t: 0 });
10408
11039
  const toggle = (id) => {
10409
11040
  const n = new Set(expanded);
10410
11041
  n.has(id) ? n.delete(id) : n.add(id);
@@ -10536,7 +11167,7 @@ function AppShell({
10536
11167
  ...rest
10537
11168
  }) {
10538
11169
  const __twcStyles = useScopedStyles("twc-shell-styles", SHELL_CSS);
10539
- const wiredSidebar = React24__default.isValidElement(sidebar) && (sidebarOpen !== void 0 || onSidebarOpenChange !== void 0) ? React24__default.cloneElement(sidebar, {
11170
+ const wiredSidebar = React25__default.isValidElement(sidebar) && (sidebarOpen !== void 0 || onSidebarOpenChange !== void 0) ? React25__default.cloneElement(sidebar, {
10540
11171
  overlay: sidebar.props.overlay !== void 0 ? sidebar.props.overlay : true,
10541
11172
  open: sidebar.props.open !== void 0 ? sidebar.props.open : sidebarOpen,
10542
11173
  onOpenChange: sidebar.props.onOpenChange !== void 0 ? sidebar.props.onOpenChange : onSidebarOpenChange
@@ -10678,7 +11309,7 @@ function Grid({
10678
11309
  ...rest
10679
11310
  }) {
10680
11311
  const responsive = columns != null && typeof columns === "object";
10681
- const uid = React24__default.useId();
11312
+ const uid = React25__default.useId();
10682
11313
  const gridId = responsive ? `g${uid.replace(/[^a-zA-Z0-9]/g, "")}` : null;
10683
11314
  let css = "";
10684
11315
  if (responsive) {
@@ -10749,7 +11380,7 @@ function Stack({
10749
11380
  ...rest
10750
11381
  }) {
10751
11382
  if (Tag2 === "a" && rest.href != null) rest.href = safeHref8(rest.href);
10752
- const kids = divider != null ? React24__default.Children.toArray(children).flatMap((child, i, arr) => i < arr.length - 1 ? [child, /* @__PURE__ */ jsx(React24__default.Fragment, { children: divider }, `twc-stack-div-${i}`)] : [child]) : children;
11383
+ const kids = divider != null ? React25__default.Children.toArray(children).flatMap((child, i, arr) => i < arr.length - 1 ? [child, /* @__PURE__ */ jsx(React25__default.Fragment, { children: divider }, `twc-stack-div-${i}`)] : [child]) : children;
10753
11384
  return /* @__PURE__ */ jsx(
10754
11385
  Tag2,
10755
11386
  {
@@ -10814,10 +11445,10 @@ function Code({
10814
11445
  const __twcStyles = useScopedStyles("twc-code-styles", CODE_CSS);
10815
11446
  const Tag2 = as ?? (block ? "pre" : "code");
10816
11447
  if (Tag2 === "a" && rest.href != null) rest.href = safeHref9(rest.href);
10817
- const rootRef = React24__default.useRef(null);
10818
- const [copied, setCopied] = React24__default.useState(false);
10819
- const timerRef = React24__default.useRef(null);
10820
- React24__default.useEffect(() => () => clearTimeout(timerRef.current), []);
11448
+ const rootRef = React25__default.useRef(null);
11449
+ const [copied, setCopied] = React25__default.useState(false);
11450
+ const timerRef = React25__default.useRef(null);
11451
+ React25__default.useEffect(() => () => clearTimeout(timerRef.current), []);
10821
11452
  const doCopy = () => {
10822
11453
  if (typeof navigator === "undefined" || !navigator.clipboard) return;
10823
11454
  const text = typeof children === "string" ? children : rootRef.current?.textContent ?? "";
@@ -10936,45 +11567,45 @@ function Text({
10936
11567
  );
10937
11568
  }
10938
11569
  var canUseDOM2 = typeof window !== "undefined" && typeof document !== "undefined";
10939
- var useIsomorphicLayoutEffect = canUseDOM2 ? React24__default.useLayoutEffect : React24__default.useEffect;
11570
+ var useIsomorphicLayoutEffect = canUseDOM2 ? React25__default.useLayoutEffect : React25__default.useEffect;
10940
11571
  function useMounted() {
10941
- const [mounted, setMounted] = React24__default.useState(false);
10942
- React24__default.useEffect(() => {
11572
+ const [mounted, setMounted] = React25__default.useState(false);
11573
+ React25__default.useEffect(() => {
10943
11574
  setMounted(true);
10944
11575
  }, []);
10945
11576
  return mounted;
10946
11577
  }
10947
11578
  function usePrevious(value) {
10948
- const ref = React24__default.useRef(void 0);
10949
- React24__default.useEffect(() => {
11579
+ const ref = React25__default.useRef(void 0);
11580
+ React25__default.useEffect(() => {
10950
11581
  ref.current = value;
10951
11582
  }, [value]);
10952
11583
  return ref.current;
10953
11584
  }
10954
11585
  function useToggle(initial = false) {
10955
- const [on, setOn] = React24__default.useState(initial);
10956
- const toggle = React24__default.useCallback((next) => {
11586
+ const [on, setOn] = React25__default.useState(initial);
11587
+ const toggle = React25__default.useCallback((next) => {
10957
11588
  setOn((prev) => typeof next === "boolean" ? next : !prev);
10958
11589
  }, []);
10959
11590
  return [on, toggle, setOn];
10960
11591
  }
10961
11592
  function useDisclosure(initial = false) {
10962
- const [open, setOpen] = React24__default.useState(initial);
10963
- const onOpen = React24__default.useCallback(() => setOpen(true), []);
10964
- const onClose = React24__default.useCallback(() => setOpen(false), []);
10965
- const onToggle = React24__default.useCallback(() => setOpen((p) => !p), []);
11593
+ const [open, setOpen] = React25__default.useState(initial);
11594
+ const onOpen = React25__default.useCallback(() => setOpen(true), []);
11595
+ const onClose = React25__default.useCallback(() => setOpen(false), []);
11596
+ const onToggle = React25__default.useCallback(() => setOpen((p) => !p), []);
10966
11597
  return { open, onOpen, onClose, onToggle, setOpen };
10967
11598
  }
10968
11599
  function useControllableState({ value, defaultValue, onChange } = {}) {
10969
11600
  const isControlled = value !== void 0;
10970
- const [uncontrolled, setUncontrolled] = React24__default.useState(defaultValue);
11601
+ const [uncontrolled, setUncontrolled] = React25__default.useState(defaultValue);
10971
11602
  const current = isControlled ? value : uncontrolled;
10972
- const ref = React24__default.useRef({ current, isControlled, onChange });
11603
+ const ref = React25__default.useRef({ current, isControlled, onChange });
10973
11604
  useIsomorphicLayoutEffect(() => {
10974
11605
  ref.current = { current, isControlled, onChange };
10975
11606
  });
10976
- const wasControlled = React24__default.useRef(isControlled);
10977
- React24__default.useEffect(() => {
11607
+ const wasControlled = React25__default.useRef(isControlled);
11608
+ React25__default.useEffect(() => {
10978
11609
  if (wasControlled.current !== isControlled) {
10979
11610
  warnOnce(
10980
11611
  "useControllableState:switch",
@@ -10983,7 +11614,7 @@ function useControllableState({ value, defaultValue, onChange } = {}) {
10983
11614
  wasControlled.current = isControlled;
10984
11615
  }
10985
11616
  });
10986
- const setValue = React24__default.useCallback((next) => {
11617
+ const setValue = React25__default.useCallback((next) => {
10987
11618
  const { current: cur, isControlled: ctrl, onChange: cb } = ref.current;
10988
11619
  const resolved = typeof next === "function" ? next(cur) : next;
10989
11620
  if (resolved === cur) return;
@@ -10994,7 +11625,7 @@ function useControllableState({ value, defaultValue, onChange } = {}) {
10994
11625
  }
10995
11626
  function useMediaQuery(query, { defaultValue = false, initializeWithValue = false } = {}) {
10996
11627
  const get = () => canUseDOM2 ? window.matchMedia(query).matches : defaultValue;
10997
- const [matches3, setMatches] = React24__default.useState(initializeWithValue ? get : defaultValue);
11628
+ const [matches3, setMatches] = React25__default.useState(initializeWithValue ? get : defaultValue);
10998
11629
  useIsomorphicLayoutEffect(() => {
10999
11630
  if (!canUseDOM2) return void 0;
11000
11631
  const mql = window.matchMedia(query);
@@ -11010,7 +11641,7 @@ function usePrefersReducedMotion() {
11010
11641
  }
11011
11642
  function useColorScheme({ storageKey = "twico-theme", attribute = "class", element, disableTransitionsOnChange = true } = {}) {
11012
11643
  const getTarget = () => element || (canUseDOM2 ? document.documentElement : null);
11013
- const firstApply = React24__default.useRef(true);
11644
+ const firstApply = React25__default.useRef(true);
11014
11645
  const read = () => {
11015
11646
  if (!canUseDOM2) return "light";
11016
11647
  try {
@@ -11020,8 +11651,8 @@ function useColorScheme({ storageKey = "twico-theme", attribute = "class", eleme
11020
11651
  }
11021
11652
  return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
11022
11653
  };
11023
- const [theme, setThemeState] = React24__default.useState(read);
11024
- const apply = React24__default.useCallback(
11654
+ const [theme, setThemeState] = React25__default.useState(read);
11655
+ const apply = React25__default.useCallback(
11025
11656
  (t) => {
11026
11657
  const target = getTarget();
11027
11658
  if (!target) return;
@@ -11047,11 +11678,11 @@ function useColorScheme({ storageKey = "twico-theme", attribute = "class", eleme
11047
11678
  useIsomorphicLayoutEffect(() => {
11048
11679
  apply(theme);
11049
11680
  }, [theme, apply]);
11050
- const themeRef = React24__default.useRef(theme);
11681
+ const themeRef = React25__default.useRef(theme);
11051
11682
  useIsomorphicLayoutEffect(() => {
11052
11683
  themeRef.current = theme;
11053
11684
  }, [theme]);
11054
- const setTheme = React24__default.useCallback(
11685
+ const setTheme = React25__default.useCallback(
11055
11686
  (t) => {
11056
11687
  setThemeState(t);
11057
11688
  try {
@@ -11062,10 +11693,10 @@ function useColorScheme({ storageKey = "twico-theme", attribute = "class", eleme
11062
11693
  },
11063
11694
  [storageKey]
11064
11695
  );
11065
- const toggle = React24__default.useCallback(() => {
11696
+ const toggle = React25__default.useCallback(() => {
11066
11697
  setTheme(themeRef.current === "dark" ? "light" : "dark");
11067
11698
  }, [setTheme]);
11068
- React24__default.useEffect(() => {
11699
+ React25__default.useEffect(() => {
11069
11700
  if (!canUseDOM2) return void 0;
11070
11701
  const sync = () => setThemeState(read());
11071
11702
  const onStorage = (e) => {
@@ -11081,14 +11712,14 @@ function useColorScheme({ storageKey = "twico-theme", attribute = "class", eleme
11081
11712
  return { theme, setTheme, toggle, isDark: theme === "dark" };
11082
11713
  }
11083
11714
  function useEventListener(eventName, handler, element, options) {
11084
- const saved = React24__default.useRef(handler);
11715
+ const saved = React25__default.useRef(handler);
11085
11716
  useIsomorphicLayoutEffect(() => {
11086
11717
  saved.current = handler;
11087
11718
  }, [handler]);
11088
11719
  const capture = typeof options === "boolean" ? options : options && options.capture;
11089
11720
  const passive = typeof options === "object" && options ? options.passive : void 0;
11090
11721
  const once = typeof options === "object" && options ? options.once : void 0;
11091
- React24__default.useEffect(() => {
11722
+ React25__default.useEffect(() => {
11092
11723
  const target = element && element.current || element || (canUseDOM2 ? window : null);
11093
11724
  if (!target || !target.addEventListener) return void 0;
11094
11725
  const opts = typeof options === "boolean" ? options : { capture, passive, once };
@@ -11098,11 +11729,11 @@ function useEventListener(eventName, handler, element, options) {
11098
11729
  }, [eventName, element, capture, passive, once]);
11099
11730
  }
11100
11731
  function useClickOutside(ref, handler, events = ["mousedown", "touchstart"]) {
11101
- const saved = React24__default.useRef(handler);
11732
+ const saved = React25__default.useRef(handler);
11102
11733
  useIsomorphicLayoutEffect(() => {
11103
11734
  saved.current = handler;
11104
11735
  }, [handler]);
11105
- React24__default.useEffect(() => {
11736
+ React25__default.useEffect(() => {
11106
11737
  if (!canUseDOM2) return void 0;
11107
11738
  const listener = (e) => {
11108
11739
  const el = ref && ref.current;
@@ -11137,8 +11768,8 @@ function useLocalStorage(key, initialValue) {
11137
11768
  return initialValue;
11138
11769
  }
11139
11770
  };
11140
- const [stored, setStored] = React24__default.useState(read);
11141
- const setValue = React24__default.useCallback(
11771
+ const [stored, setStored] = React25__default.useState(read);
11772
+ const setValue = React25__default.useCallback(
11142
11773
  (value) => {
11143
11774
  setStored((prev) => {
11144
11775
  const next = typeof value === "function" ? value(prev) : value;
@@ -11154,8 +11785,8 @@ function useLocalStorage(key, initialValue) {
11154
11785
  return [stored, setValue];
11155
11786
  }
11156
11787
  function useCopyToClipboard(timeout = 1500) {
11157
- const [copied, setCopied] = React24__default.useState(false);
11158
- const copy = React24__default.useCallback(async (text) => {
11788
+ const [copied, setCopied] = React25__default.useState(false);
11789
+ const copy = React25__default.useCallback(async (text) => {
11159
11790
  if (!canUseDOM2 || !navigator.clipboard) {
11160
11791
  setCopied(false);
11161
11792
  return false;
@@ -11169,7 +11800,7 @@ function useCopyToClipboard(timeout = 1500) {
11169
11800
  return false;
11170
11801
  }
11171
11802
  }, []);
11172
- React24__default.useEffect(() => {
11803
+ React25__default.useEffect(() => {
11173
11804
  if (!copied) return void 0;
11174
11805
  const t = setTimeout(() => setCopied(false), timeout);
11175
11806
  return () => clearTimeout(t);
@@ -11177,23 +11808,23 @@ function useCopyToClipboard(timeout = 1500) {
11177
11808
  return { copied, copy };
11178
11809
  }
11179
11810
  function useDebouncedValue(value, delay = 200) {
11180
- const [debounced, setDebounced] = React24__default.useState(value);
11181
- React24__default.useEffect(() => {
11811
+ const [debounced, setDebounced] = React25__default.useState(value);
11812
+ React25__default.useEffect(() => {
11182
11813
  const t = setTimeout(() => setDebounced(value), delay);
11183
11814
  return () => clearTimeout(t);
11184
11815
  }, [value, delay]);
11185
11816
  return debounced;
11186
11817
  }
11187
11818
  function useDebouncedCallback(callback, delay = 200) {
11188
- const saved = React24__default.useRef(callback);
11189
- const timer = React24__default.useRef(null);
11819
+ const saved = React25__default.useRef(callback);
11820
+ const timer = React25__default.useRef(null);
11190
11821
  useIsomorphicLayoutEffect(() => {
11191
11822
  saved.current = callback;
11192
11823
  }, [callback]);
11193
- React24__default.useEffect(() => () => {
11824
+ React25__default.useEffect(() => () => {
11194
11825
  if (timer.current) clearTimeout(timer.current);
11195
11826
  }, []);
11196
- return React24__default.useCallback(
11827
+ return React25__default.useCallback(
11197
11828
  (...args) => {
11198
11829
  if (timer.current) clearTimeout(timer.current);
11199
11830
  timer.current = setTimeout(() => saved.current(...args), delay);
@@ -11202,33 +11833,33 @@ function useDebouncedCallback(callback, delay = 200) {
11202
11833
  );
11203
11834
  }
11204
11835
  function useInterval(callback, delay) {
11205
- const saved = React24__default.useRef(callback);
11836
+ const saved = React25__default.useRef(callback);
11206
11837
  useIsomorphicLayoutEffect(() => {
11207
11838
  saved.current = callback;
11208
11839
  }, [callback]);
11209
- React24__default.useEffect(() => {
11840
+ React25__default.useEffect(() => {
11210
11841
  if (delay == null) return void 0;
11211
11842
  const id = setInterval(() => saved.current(), delay);
11212
11843
  return () => clearInterval(id);
11213
11844
  }, [delay]);
11214
11845
  }
11215
11846
  function useTimeout(callback, delay) {
11216
- const saved = React24__default.useRef(callback);
11847
+ const saved = React25__default.useRef(callback);
11217
11848
  useIsomorphicLayoutEffect(() => {
11218
11849
  saved.current = callback;
11219
11850
  }, [callback]);
11220
- React24__default.useEffect(() => {
11851
+ React25__default.useEffect(() => {
11221
11852
  if (delay == null) return void 0;
11222
11853
  const id = setTimeout(() => saved.current(), delay);
11223
11854
  return () => clearTimeout(id);
11224
11855
  }, [delay]);
11225
11856
  }
11226
11857
  function useWindowSize() {
11227
- const [size, setSize] = React24__default.useState(() => ({
11858
+ const [size, setSize] = React25__default.useState(() => ({
11228
11859
  width: canUseDOM2 ? window.innerWidth : 0,
11229
11860
  height: canUseDOM2 ? window.innerHeight : 0
11230
11861
  }));
11231
- React24__default.useEffect(() => {
11862
+ React25__default.useEffect(() => {
11232
11863
  if (!canUseDOM2) return void 0;
11233
11864
  const onResize = () => setSize({ width: window.innerWidth, height: window.innerHeight });
11234
11865
  onResize();
@@ -11238,8 +11869,8 @@ function useWindowSize() {
11238
11869
  return size;
11239
11870
  }
11240
11871
  function useHover(ref) {
11241
- const [hovered, setHovered] = React24__default.useState(false);
11242
- React24__default.useEffect(() => {
11872
+ const [hovered, setHovered] = React25__default.useState(false);
11873
+ React25__default.useEffect(() => {
11243
11874
  const el = ref && ref.current;
11244
11875
  if (!el) return void 0;
11245
11876
  const on = () => setHovered(true);
@@ -11255,8 +11886,8 @@ function useHover(ref) {
11255
11886
  }
11256
11887
  function useIntersectionObserver(ref, options = {}) {
11257
11888
  const { root = null, rootMargin = "0px", threshold = 0 } = options;
11258
- const [entry, setEntry] = React24__default.useState(null);
11259
- React24__default.useEffect(() => {
11889
+ const [entry, setEntry] = React25__default.useState(null);
11890
+ React25__default.useEffect(() => {
11260
11891
  const el = ref && ref.current;
11261
11892
  if (!canUseDOM2 || !el || typeof IntersectionObserver === "undefined") return void 0;
11262
11893
  const obs = new IntersectionObserver(([e]) => setEntry(e), { root, rootMargin, threshold });
@@ -11265,31 +11896,14 @@ function useIntersectionObserver(ref, options = {}) {
11265
11896
  }, [ref, root, rootMargin, JSON.stringify(threshold)]);
11266
11897
  return entry;
11267
11898
  }
11268
- var __scrollLockCount = 0;
11269
- var __scrollLockSaved = "";
11270
- function useScrollLock(locked = true) {
11271
- useIsomorphicLayoutEffect(() => {
11272
- if (!canUseDOM2 || !locked) return void 0;
11273
- if (__scrollLockCount === 0) {
11274
- __scrollLockSaved = document.body.style.overflow;
11275
- document.body.style.overflow = "hidden";
11276
- }
11277
- __scrollLockCount += 1;
11278
- return () => {
11279
- __scrollLockCount -= 1;
11280
- if (__scrollLockCount === 0) {
11281
- document.body.style.overflow = __scrollLockSaved;
11282
- }
11283
- };
11284
- }, [locked]);
11285
- }
11286
11899
  function useId(prefix = "twc") {
11287
- const raw = React24__default.useId();
11900
+ const raw = React25__default.useId();
11288
11901
  return prefix ? `${prefix}-${raw.replace(/:/g, "")}` : raw;
11289
11902
  }
11290
11903
  var useFocusTrap2 = useFocusTrap;
11291
11904
  var usePortal2 = usePortal;
11905
+ var useScrollLock2 = useScrollLock;
11292
11906
 
11293
- export { Accordion, Alert, AppShell, Avatar, AvatarMenu, Badge, Box, Breadcrumb, Button, Card, Carousel, Chart, Checkbox, Code, ColorPicker, Combobox, CommandPalette, Container, Currency, CurrencyField, Datatable, DatePicker, DateRangePicker, Dialog, Divider, Drawer, EmptyState, Field, FileUpload, Grid, Heading, IconButton, Input, Kanban, List, Menu, MultiSelect, Navbar, Pagination, Popover, Progress, Radio, Rating, Select, Sidebar, Skeleton, Slider, Spinner, Stack, Stat, Stepper, Switch, Table, Tabs, Tag, Text, Textarea, Timeline, Toast, ToastProvider, ToastViewport, Tooltip, TreeView, runDatatableQuery, useClickOutside, useColorScheme, useControllableState, useCopyToClipboard, useDebouncedCallback, useDebouncedValue, useDisclosure, useEventListener, useFocusTrap2 as useFocusTrap, useHover, useId, useIntersectionObserver, useInterval, useIsomorphicLayoutEffect, useKeyPress, useLocalStorage, useMediaQuery, useMounted, usePortal2 as usePortal, usePrefersReducedMotion, usePrevious, useScrollLock, useTimeout, useToast, useToggle, useWindowSize };
11907
+ export { Accordion, Alert, AppShell, Avatar, AvatarMenu, Badge, Box, Breadcrumb, Button, Card, Carousel, Chart, Checkbox, Code, ColorPicker, Combobox, CommandPalette, Container, Currency, CurrencyField, Datatable, DatePicker, DateRangePicker, Dialog, Divider, Drawer, EmptyState, Field, FileUpload, Grid, Heading, IconButton, Input, Kanban, List, Menu, MultiSelect, Navbar, Pagination, Popover, Progress, Radio, RadioGroup, Rating, Select, Sidebar, Skeleton, Slider, Spinner, Stack, Stat, Stepper, Switch, Table, Tabs, Tag, Text, Textarea, Timeline, Toast, ToastProvider, ToastViewport, Tooltip, TreeView, runDatatableQuery, useClickOutside, useColorScheme, useControllableState, useCopyToClipboard, useDebouncedCallback, useDebouncedValue, useDisclosure, useEventListener, useFocusTrap2 as useFocusTrap, useHover, useId, useIntersectionObserver, useInterval, useIsomorphicLayoutEffect, useKeyPress, useLocalStorage, useMediaQuery, useMounted, usePortal2 as usePortal, usePrefersReducedMotion, usePrevious, useScrollLock2 as useScrollLock, useTimeout, useToast, useToggle, useWindowSize };
11294
11908
  //# sourceMappingURL=index.mjs.map
11295
11909
  //# sourceMappingURL=index.mjs.map