twico-ui 1.11.1 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -384,6 +384,12 @@ var ACCORDION_CSS = `
384
384
  transition: grid-template-rows var(--duration-base) var(--ease-standard), visibility 0s; }
385
385
  .twc-accordion__panel-inner { overflow: hidden; }
386
386
  .twc-accordion__content { padding: 0 var(--space-4) var(--space-4); font-size: var(--text-sm); color: var(--color-text-muted); line-height: var(--leading-normal); }
387
+ /* WCAG 2.3.3: panels expand/collapse (and the chevron rotates) instantly under reduced motion
388
+ \u2014 visibility still toggles, so open/closed state is unchanged. */
389
+ @media (prefers-reduced-motion: reduce) {
390
+ .twc-accordion__panel, .twc-accordion__panel[data-open="true"] { transition: none; }
391
+ .twc-accordion__chevron { transition: none; }
392
+ }
387
393
  `;
388
394
  function Accordion({
389
395
  items,
@@ -1143,6 +1149,9 @@ var CSS2 = `
1143
1149
  transform: scale(0); pointer-events: none;
1144
1150
  animation: twico-ripple var(--duration-slow) var(--ease-out) forwards;
1145
1151
  }
1152
+ /* WCAG 2.3.3: suppress the click ripple entirely under reduced motion (display:none, not
1153
+ animation:none, so no static scale(0) dot lingers for the 600ms cleanup window). */
1154
+ @media (prefers-reduced-motion: reduce) { .twc-ripple { display: none; } }
1146
1155
  `;
1147
1156
  function safeHref4(url) {
1148
1157
  if (url == null) return void 0;
@@ -1502,6 +1511,13 @@ var CAROUSEL_CSS = `
1502
1511
  .twc-carousel__pause svg { width: 15px; height: 15px; display: block; }
1503
1512
  .twc-carousel__live { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
1504
1513
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0; }
1514
+ /* WCAG 2.3.3: no slide/dot/arrow motion when the user asks for reduced motion \u2014 the index
1515
+ still changes, it just jumps rather than tweening. */
1516
+ @media (prefers-reduced-motion: reduce) {
1517
+ .twc-carousel__track { transition: none; }
1518
+ .twc-carousel__dot { transition: none; }
1519
+ .twc-carousel__arrow { transition: none; }
1520
+ }
1505
1521
  `;
1506
1522
  function Carousel({
1507
1523
  children,
@@ -1603,7 +1619,7 @@ function Carousel({
1603
1619
  /* @__PURE__ */ jsxRuntime.jsx("button", { className: "twc-carousel__arrow", "data-dir": "prev", "aria-label": "Previous", disabled: atStart, onClick: () => go(index - 1), children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.4", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m15 18-6-6 6-6" }) }) }),
1604
1620
  /* @__PURE__ */ jsxRuntime.jsx("button", { className: "twc-carousel__arrow", "data-dir": "next", "aria-label": "Next", disabled: atEnd, onClick: () => go(index + 1), children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.4", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m9 18 6-6-6-6" }) }) })
1605
1621
  ] }) : null,
1606
- showDots && count > 1 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-carousel__dots", children: slides.map((_, i) => /* @__PURE__ */ jsxRuntime.jsx("button", { className: "twc-carousel__dot", "data-active": i === index || void 0, "aria-label": `Go to slide ${i + 1}`, onClick: () => go(i) }, i)) }) : null
1622
+ showDots && count > 1 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-carousel__dots", children: slides.map((_, i) => /* @__PURE__ */ jsxRuntime.jsx("button", { className: "twc-carousel__dot", "data-active": i === index || void 0, "aria-current": i === index ? "true" : void 0, "aria-label": `Go to slide ${i + 1}`, onClick: () => go(i) }, i)) }) : null
1607
1623
  ]
1608
1624
  }
1609
1625
  );
@@ -1631,9 +1647,9 @@ function niceCeil(v) {
1631
1647
  const step = n <= 1 ? 1 : n <= 2 ? 2 : n <= 5 ? 5 : 10;
1632
1648
  return step * pow;
1633
1649
  }
1634
- function niceNum(range, round) {
1635
- const exp = Math.floor(Math.log10(range || 1));
1636
- const frac = (range || 1) / Math.pow(10, exp);
1650
+ function niceNum(range2, round) {
1651
+ const exp = Math.floor(Math.log10(range2 || 1));
1652
+ const frac = (range2 || 1) / Math.pow(10, exp);
1637
1653
  let nf;
1638
1654
  if (round) nf = frac < 1.5 ? 1 : frac < 3 ? 2 : frac < 7 ? 5 : 10;
1639
1655
  else nf = frac <= 1 ? 1 : frac <= 2 ? 2 : frac <= 5 ? 5 : 10;
@@ -1644,8 +1660,8 @@ function niceScale(min, max, maxTicks = 5) {
1644
1660
  if (min === max) {
1645
1661
  max = min + 1;
1646
1662
  }
1647
- const range = niceNum(max - min, false);
1648
- const step = niceNum(range / Math.max(1, maxTicks - 1), true);
1663
+ const range2 = niceNum(max - min, false);
1664
+ const step = niceNum(range2 / Math.max(1, maxTicks - 1), true);
1649
1665
  const niceMin = Math.floor(min / step) * step;
1650
1666
  const niceMax = Math.ceil(max / step) * step;
1651
1667
  const ticks = [];
@@ -2423,7 +2439,7 @@ function PieChart({
2423
2439
  styles,
2424
2440
  /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: `0 0 ${H} ${H}`, role: "img", "aria-label": svgAriaLabel, "aria-describedby": tableId, children: total <= 0 ? /* @__PURE__ */ jsxRuntime.jsx("text", { className: "twc-chart__empty", x: cx, y: cy, textAnchor: "middle", dominantBaseline: "middle", children: "No data" }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2425
2441
  slices.map(({ d, i, v, s0, s1, span, pct, color }) => {
2426
- const pad = span > padAngle ? padAngle / 2 : 0;
2442
+ const pad2 = span > padAngle ? padAngle / 2 : 0;
2427
2443
  const items = [{ color, label: fmt(v), value: `${pct}%` }];
2428
2444
  let explode;
2429
2445
  if (selected === i) {
@@ -2442,7 +2458,7 @@ function PieChart({
2442
2458
  "data-mark": "",
2443
2459
  "data-active": emph === i ? "true" : void 0,
2444
2460
  "data-selected": selected === i ? "true" : void 0,
2445
- d: arcPath(cx, cy, rOuter, rInner, s0 + pad, s1 - pad),
2461
+ d: arcPath(cx, cy, rOuter, rInner, s0 + pad2, s1 - pad2),
2446
2462
  onMouseMove: (e) => {
2447
2463
  setActive(i);
2448
2464
  show({ title: d.label, items }, e);
@@ -2544,9 +2560,9 @@ function Gauge({
2544
2560
  const cx = size / 2, cy = size / 2;
2545
2561
  const span = endAngle - startAngle;
2546
2562
  const frac = (v) => {
2547
- const range = max - min;
2548
- if (!range) return 0;
2549
- return Math.max(0, Math.min(1, ((Number(v) || 0) - min) / range));
2563
+ const range2 = max - min;
2564
+ if (!range2) return 0;
2565
+ return Math.max(0, Math.min(1, ((Number(v) || 0) - min) / range2));
2550
2566
  };
2551
2567
  const isMulti = Array.isArray(series) && series.length > 0;
2552
2568
  const items = isMulti ? series : [{ value: Number(value) || 0, label, color }];
@@ -2721,9 +2737,9 @@ function Sparkline({
2721
2737
  const n = values.length;
2722
2738
  const W = width, H = height;
2723
2739
  const dotR = showDots ? Math.max(2, strokeWidth + 1) : 0;
2724
- const pad = Math.max(strokeWidth / 2, dotR) + 1;
2725
- const innerW = Math.max(1, W - pad * 2);
2726
- const innerH = Math.max(1, H - pad * 2);
2740
+ const pad2 = Math.max(strokeWidth / 2, dotR) + 1;
2741
+ const innerW = Math.max(1, W - pad2 * 2);
2742
+ const innerH = Math.max(1, H - pad2 * 2);
2727
2743
  const dMin = min != null ? min : n ? Math.min(...values) : 0;
2728
2744
  const dMax = max != null ? max : n ? Math.max(...values) : 1;
2729
2745
  let lo = dMin, hi = dMax;
@@ -2732,9 +2748,9 @@ function Sparkline({
2732
2748
  hi = hi + 1;
2733
2749
  }
2734
2750
  const span = hi - lo;
2735
- const x = (i) => n <= 1 ? pad + innerW / 2 : pad + innerW / (n - 1) * i;
2736
- const y = (v) => pad + innerH - (v - lo) / span * innerH;
2737
- const baselineY = pad + innerH;
2751
+ const x = (i) => n <= 1 ? pad2 + innerW / 2 : pad2 + innerW / (n - 1) * i;
2752
+ const y = (v) => pad2 + innerH - (v - lo) / span * innerH;
2753
+ const baselineY = pad2 + innerH;
2738
2754
  const points = values.map((v, i) => [x(i), y(v)]);
2739
2755
  const crv = isBar ? "straight" : curve;
2740
2756
  const pathOpts = { smooth: crv === "smooth", step: crv === "stepped" ? "after" : null };
@@ -2766,7 +2782,7 @@ function Sparkline({
2766
2782
  "data-mark": true,
2767
2783
  "data-selected": selected === i || void 0,
2768
2784
  style: { fill: color },
2769
- x: pad + band * i + (band - slot) / 2,
2785
+ x: pad2 + band * i + (band - slot) / 2,
2770
2786
  y: baselineY - h,
2771
2787
  width: Math.max(1, slot),
2772
2788
  height: Math.max(1, h),
@@ -3933,8 +3949,8 @@ function FunnelChart({
3933
3949
  const n = rows.length;
3934
3950
  const fmt = valueFormat || fmtNumber;
3935
3951
  const W = 600, H = height;
3936
- const pad = 8;
3937
- const innerW = W - pad * 2, innerH = H - pad * 2;
3952
+ const pad2 = 8;
3953
+ const innerW = W - pad2 * 2, innerH = H - pad2 * 2;
3938
3954
  const values = rows.map((d) => Number(d.value) || 0);
3939
3955
  const maxValue = Math.max(0, ...values);
3940
3956
  const denom = maxValue > 0 ? maxValue : 1;
@@ -3954,16 +3970,16 @@ function FunnelChart({
3954
3970
  let points, tx, ty;
3955
3971
  if (horizontal) {
3956
3972
  const bandW = innerW / Math.max(1, n);
3957
- const x0 = pad + bandW * i + gap / 2;
3958
- const x1 = pad + bandW * (i + 1) - gap / 2;
3973
+ const x0 = pad2 + bandW * i + gap / 2;
3974
+ const x1 = pad2 + bandW * (i + 1) - gap / 2;
3959
3975
  const cy = H / 2;
3960
3976
  points = [[x0, cy - lead], [x1, cy - trail], [x1, cy + trail], [x0, cy + lead]];
3961
3977
  tx = (x0 + x1) / 2;
3962
3978
  ty = cy;
3963
3979
  } else {
3964
3980
  const bandH = innerH / Math.max(1, n);
3965
- const y0 = pad + bandH * i + gap / 2;
3966
- const y1 = pad + bandH * (i + 1) - gap / 2;
3981
+ const y0 = pad2 + bandH * i + gap / 2;
3982
+ const y1 = pad2 + bandH * (i + 1) - gap / 2;
3967
3983
  const cx = W / 2;
3968
3984
  points = [[cx - lead, y0], [cx + lead, y0], [cx + trail, y1], [cx - trail, y1]];
3969
3985
  tx = cx;
@@ -5126,6 +5142,7 @@ function Checkbox({
5126
5142
  .twc-check[data-invalid="true"] .twc-check__box { border-color: var(--color-danger); }
5127
5143
  .twc-check__text { display: flex; flex-direction: column; gap: 2px; }
5128
5144
  .twc-check__label { font-size: var(--text-sm); font-weight: var(--font-medium); color: var(--color-text); line-height: 1.3; }
5145
+ .twc-field__req { color: var(--color-danger); }
5129
5146
  .twc-check__desc { font-size: var(--text-xs); color: var(--color-text-muted); }
5130
5147
  .twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
5131
5148
  .twc-field__error { font-size: var(--text-xs); color: var(--color-danger-subtle-fg); font-weight: var(--font-medium); }
@@ -5166,7 +5183,10 @@ function Checkbox({
5166
5183
  ),
5167
5184
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-check__box", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "twc-check__svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "3.5", strokeLinecap: "round", strokeLinejoin: "round", children: mixed ? /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 12h14" }) : /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M20 6 9 17l-5-5" }) }) }),
5168
5185
  label || description ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "twc-check__text", children: [
5169
- label ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-check__label", children: label }) : null,
5186
+ label ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "twc-check__label", children: [
5187
+ label,
5188
+ required ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-field__req", children: "*" }) : null
5189
+ ] }) : null,
5170
5190
  description ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-check__desc", children: description }) : null
5171
5191
  ] }) : null
5172
5192
  ] });
@@ -5186,9 +5206,11 @@ var FIELD_CSS = `
5186
5206
  var COLORPICKER_CSS = `
5187
5207
  .twc-cp { position: relative; font-family: var(--font-sans); display: flex; flex-direction: column; gap: var(--space-1-5); }
5188
5208
  .twc-cp__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); }
5189
- .twc-cp__trigger { display: flex; align-items: center; gap: var(--space-2-5); height: var(--control-h-md); padding: 0 var(--space-2) 0 var(--space-2);
5209
+ .twc-cp__trigger { display: flex; align-items: center; gap: var(--space-2-5); --_h: var(--control-h-md); height: var(--_h); padding: 0 var(--space-2) 0 var(--space-2);
5190
5210
  background: var(--color-surface); border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-md); cursor: pointer;
5191
5211
  transition: border-color var(--duration-fast) var(--ease-standard), box-shadow var(--duration-fast) var(--ease-standard); }
5212
+ .twc-cp__trigger[data-size="sm"] { --_h: var(--control-h-sm); }
5213
+ .twc-cp__trigger[data-size="lg"] { --_h: var(--control-h-lg); }
5192
5214
  .twc-cp__trigger:hover:not([data-open="true"]):not([data-disabled="true"]) { border-color: var(--color-border-strong); }
5193
5215
  .twc-cp__trigger[data-open="true"] { border-color: var(--color-primary); box-shadow: var(--ring); }
5194
5216
  .twc-cp__trigger[data-invalid="true"] { border-color: var(--color-danger); }
@@ -5282,6 +5304,7 @@ function ColorPicker({
5282
5304
  defaultValue = "#6366F1",
5283
5305
  presets = DEFAULT_PRESETS,
5284
5306
  disabled = false,
5307
+ size = "md",
5285
5308
  alpha = false,
5286
5309
  tone = "primary",
5287
5310
  onChange,
@@ -5433,6 +5456,7 @@ function ColorPicker({
5433
5456
  "data-open": open || void 0,
5434
5457
  "data-disabled": disabled || void 0,
5435
5458
  "data-invalid": invalid || void 0,
5459
+ "data-size": size,
5436
5460
  role: "button",
5437
5461
  tabIndex: disabled ? -1 : 0,
5438
5462
  "aria-haspopup": "dialog",
@@ -7353,6 +7377,8 @@ function MultiSelect({
7353
7377
  name,
7354
7378
  max,
7355
7379
  maxTagCount,
7380
+ filter,
7381
+ onInputChange,
7356
7382
  virtualized = false,
7357
7383
  overscan = 8,
7358
7384
  id,
@@ -7380,10 +7406,11 @@ function MultiSelect({
7380
7406
  const popRef = React51__namespace.default.useRef(null);
7381
7407
  const listRef = React51__namespace.default.useRef(null);
7382
7408
  const activeRef = React51__namespace.default.useRef(null);
7383
- const fGroups = React51__namespace.default.useMemo(
7384
- () => groups.map((g) => ({ group: g.group, options: g.options.filter((o) => matches2(o, query.trim())) })).filter((g) => g.options.length),
7385
- [groups, query]
7386
- );
7409
+ const fGroups = React51__namespace.default.useMemo(() => {
7410
+ const q = query.trim();
7411
+ const fn = filter === false ? null : typeof filter === "function" ? filter : matches2;
7412
+ return groups.map((g) => ({ group: g.group, options: fn ? g.options.filter((o) => fn(o, q)) : g.options })).filter((g) => g.options.length);
7413
+ }, [groups, query, filter]);
7387
7414
  const visible = React51__namespace.default.useMemo(() => fGroups.flatMap((g) => g.options), [fGroups]);
7388
7415
  const rowH = React51__namespace.default.useMemo(() => visible.some((o) => o.description) ? 48 : 36, [visible]);
7389
7416
  const GROUP_H = 30;
@@ -7663,6 +7690,7 @@ function MultiSelect({
7663
7690
  onChange: (e) => {
7664
7691
  setQuery(e.target.value);
7665
7692
  setOpen(true);
7693
+ onInputChange?.(e.target.value);
7666
7694
  },
7667
7695
  onKeyDown: (e) => {
7668
7696
  onKeyDown?.(e);
@@ -8505,6 +8533,7 @@ var NUM_OPS = [
8505
8533
  ];
8506
8534
  var opsFor = (type) => type === "number" ? NUM_OPS : STR_OPS;
8507
8535
  var isMultiOp = (op) => op === "isAnyOf";
8536
+ var getColVal = (col, row) => col && col.valueGetter ? col.valueGetter(row) : row == null ? void 0 : row[col && col.field];
8508
8537
  function testFilter(raw, op, target, type) {
8509
8538
  if (op === "isEmpty") return raw == null || raw === "";
8510
8539
  if (op === "isNotEmpty") return !(raw == null || raw === "");
@@ -8550,14 +8579,15 @@ function runDatatableQuery(rows, query, options = {}) {
8550
8579
  const cols = options.columns || [];
8551
8580
  const typeOf = (field) => cols.find((c) => c.field === field)?.type === "number" ? "number" : "string";
8552
8581
  const searchFields = options.searchFields || (cols.length ? cols.filter((c) => c.field && c.type !== "actions").map((c) => c.field) : rows && rows[0] ? Object.keys(rows[0]) : []);
8582
+ const gv = (field, r2) => getColVal(cols.find((c) => c.field === field) || { field }, r2);
8553
8583
  let out = Array.isArray(rows) ? rows : [];
8554
8584
  const quick = String(q.quickFilter || "").trim().toLowerCase();
8555
- if (quick) out = out.filter((r2) => searchFields.some((f) => String(r2[f] ?? "").toLowerCase().includes(quick)));
8556
- for (const f of q.filters || []) out = out.filter((r2) => testFilter(r2[f.field], f.op, f.value, typeOf(f.field)));
8585
+ if (quick) out = out.filter((r2) => searchFields.some((f) => String(gv(f, r2) ?? "").toLowerCase().includes(quick)));
8586
+ for (const f of q.filters || []) out = out.filter((r2) => testFilter(gv(f.field, r2), f.op, f.value, typeOf(f.field)));
8557
8587
  if (q.sort && q.sort.field) {
8558
8588
  const { field, dir } = q.sort, numeric = typeOf(field) === "number";
8559
8589
  out = [...out].sort((a, b) => {
8560
- const av = a[field], bv = b[field];
8590
+ const av = gv(field, a), bv = gv(field, b);
8561
8591
  if (av == null) return 1;
8562
8592
  if (bv == null) return -1;
8563
8593
  const r2 = numeric ? av - bv : String(av).localeCompare(String(bv));
@@ -8815,6 +8845,7 @@ function Datatable({
8815
8845
  "aria-label": ariaLabelAttr,
8816
8846
  rowGrouping = [],
8817
8847
  rowNumbers = false,
8848
+ searchFields = null,
8818
8849
  rowPinning = false,
8819
8850
  rowReorder = false,
8820
8851
  rowResize = false,
@@ -9156,17 +9187,18 @@ function Datatable({
9156
9187
  let out = rows;
9157
9188
  if (quick.trim()) {
9158
9189
  const q = quick.trim().toLowerCase();
9159
- out = out.filter((r2) => visibleCols.some((c) => String(r2[c.field] ?? "").toLowerCase().includes(q)));
9190
+ const searchCols = searchFields && searchFields.length ? searchFields.map((f) => colByField[f] || { field: f }) : visibleCols;
9191
+ out = out.filter((r2) => searchCols.some((c) => String(getColVal(c, r2) ?? "").toLowerCase().includes(q)));
9160
9192
  }
9161
9193
  for (const f of filters) {
9162
9194
  const col = colByField[f.field];
9163
9195
  if (!col) continue;
9164
- out = out.filter((r2) => testFilter(r2[f.field], f.op, f.value, col.type));
9196
+ out = out.filter((r2) => testFilter(getColVal(col, r2), f.op, f.value, col.type));
9165
9197
  }
9166
9198
  if (sort) {
9167
9199
  const col = colByField[sort.field];
9168
9200
  out = [...out].sort((a, b) => {
9169
- const av = a[sort.field], bv = b[sort.field];
9201
+ const av = getColVal(col, a), bv = getColVal(col, b);
9170
9202
  if (av == null) return 1;
9171
9203
  if (bv == null) return -1;
9172
9204
  const r2 = col?.type === "number" ? av - bv : String(av).localeCompare(String(bv));
@@ -9178,7 +9210,7 @@ function Datatable({
9178
9210
  out = [...out].sort((a, b) => (idx.get(keyOf(a)) ?? 1e9) - (idx.get(keyOf(b)) ?? 1e9));
9179
9211
  }
9180
9212
  return out;
9181
- }, [rows, quick, filters, sort, visibleCols, colByField, serverMode, rowOrder]);
9213
+ }, [rows, quick, filters, sort, visibleCols, colByField, serverMode, rowOrder, searchFields]);
9182
9214
  const paginated = pageSize > 0;
9183
9215
  const serverTotal = rowCount == null ? processed.length : rowCount;
9184
9216
  const totalRows = serverMode ? serverTotal : processed.length;
@@ -9203,7 +9235,7 @@ function Datatable({
9203
9235
  function computeAgg(col, data) {
9204
9236
  const agg = col.aggregation;
9205
9237
  if (!agg) return null;
9206
- const raw = data.map((r2) => r2[col.field]).filter((v) => v != null && v !== "");
9238
+ const raw = data.map((r2) => getColVal(col, r2)).filter((v) => v != null && v !== "");
9207
9239
  if (typeof agg === "function") return agg(raw, data);
9208
9240
  if (agg === "count") return raw.length;
9209
9241
  const nums = raw.map(Number).filter((n) => !Number.isNaN(n));
@@ -9221,7 +9253,8 @@ function Datatable({
9221
9253
  const field = activeGroupBy[depth];
9222
9254
  const map = /* @__PURE__ */ new Map();
9223
9255
  for (const r2 of rowsIn) {
9224
- const v = r2[field] == null || r2[field] === "" ? "\u2014" : r2[field];
9256
+ const gvv = getColVal(colByField[field], r2);
9257
+ const v = gvv == null || gvv === "" ? "\u2014" : gvv;
9225
9258
  if (!map.has(v)) map.set(v, []);
9226
9259
  map.get(v).push(r2);
9227
9260
  }
@@ -9518,13 +9551,17 @@ function Datatable({
9518
9551
  function optionsForField(field) {
9519
9552
  const col = colByField[field];
9520
9553
  if (col?.valueOptions) return col.valueOptions.map((o) => typeof o === "string" ? { value: o, label: o } : o);
9521
- const set = /* @__PURE__ */ new Set();
9554
+ const seen2 = /* @__PURE__ */ new Map();
9522
9555
  for (const r2 of rows) {
9523
- const v = r2[field];
9524
- if (v != null && v !== "") set.add(String(v));
9525
- if (set.size > 80) break;
9556
+ const raw = getColVal(col, r2);
9557
+ if (raw == null || raw === "") continue;
9558
+ const key = String(raw);
9559
+ if (seen2.has(key)) continue;
9560
+ const label = col?.valueFormatter ? col.valueFormatter(raw, r2) : key;
9561
+ seen2.set(key, { value: key, label: typeof label === "string" || typeof label === "number" ? String(label) : key });
9562
+ if (seen2.size > 80) break;
9526
9563
  }
9527
- return [...set].sort((a, b) => a.localeCompare(b)).map((v) => ({ value: v, label: v }));
9564
+ return [...seen2.values()].sort((a, b) => String(a.label).localeCompare(String(b.label)));
9528
9565
  }
9529
9566
  const [resizing, setResizing] = React51__namespace.default.useState(false);
9530
9567
  function startResize(e, field) {
@@ -9554,7 +9591,7 @@ function Datatable({
9554
9591
  meas.setAttribute("aria-hidden", "true");
9555
9592
  meas.style.cssText = "position:fixed;left:-9999px;top:-9999px;visibility:hidden;pointer-events:none;white-space:nowrap;display:inline-block;box-sizing:content-box;padding:0;";
9556
9593
  document.body.appendChild(meas);
9557
- let content = 0, pad = 0;
9594
+ let content = 0, pad2 = 0;
9558
9595
  try {
9559
9596
  for (const cell of cells) {
9560
9597
  const src = cell.tagName === "TH" ? cell.querySelector(".twc-dt__th-inner") || cell : cell;
@@ -9566,13 +9603,13 @@ function Datatable({
9566
9603
  meas.style.letterSpacing = cs.letterSpacing;
9567
9604
  meas.innerHTML = src.innerHTML;
9568
9605
  content = Math.max(content, meas.scrollWidth);
9569
- pad = Math.max(pad, (parseFloat(cs.paddingLeft) || 0) + (parseFloat(cs.paddingRight) || 0));
9606
+ pad2 = Math.max(pad2, (parseFloat(cs.paddingLeft) || 0) + (parseFloat(cs.paddingRight) || 0));
9570
9607
  }
9571
9608
  } finally {
9572
9609
  meas.innerHTML = "";
9573
9610
  document.body.removeChild(meas);
9574
9611
  }
9575
- const w = Math.min(640, Math.max(72, Math.ceil(content) + Math.ceil(pad) + 6));
9612
+ const w = Math.min(640, Math.max(72, Math.ceil(content) + Math.ceil(pad2) + 6));
9576
9613
  setWidths((m) => ({ ...m, [field]: w }));
9577
9614
  }
9578
9615
  function onColDrop(targetField) {
@@ -9591,7 +9628,7 @@ function Datatable({
9591
9628
  function exportData(format = "csv") {
9592
9629
  const expCols = ordered.filter((c) => c.type !== "actions");
9593
9630
  const source = serverMode ? paged : processed;
9594
- const cellValue = (c, row) => c.exportValue ? c.exportValue(row[c.field], row) : row[c.field];
9631
+ const cellValue = (c, row) => c.exportValue ? c.exportValue(row[c.field], row) : getColVal(c, row);
9595
9632
  const fname = (ext) => `${exportFilename}.${ext}`;
9596
9633
  const download = (text2, mime, ext) => {
9597
9634
  const blob = new Blob([text2], { type: mime });
@@ -9640,7 +9677,7 @@ function Datatable({
9640
9677
  }
9641
9678
  }
9642
9679
  const data = serverMode ? paged : processed;
9643
- const raw = data.map((r2) => r2[col.field]).filter((v) => v != null && v !== "");
9680
+ const raw = data.map((r2) => getColVal(col, r2)).filter((v) => v != null && v !== "");
9644
9681
  if (typeof agg === "function") return agg(raw, data);
9645
9682
  if (agg === "count") return raw.length;
9646
9683
  const nums = raw.map(Number).filter((n) => !Number.isNaN(n));
@@ -10059,7 +10096,7 @@ function Datatable({
10059
10096
  showRowNum ? /* @__PURE__ */ jsxRuntime.jsx("td", { className: "twc-dt__td 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 }, children: typeof ri === "number" ? (paginated || serverMode ? pageVal * sizeVal : 0) + ri + 1 : "" }) : null,
10060
10097
  ordered.map((c, ci) => {
10061
10098
  const st = stickyOf(c.field);
10062
- const val = row[c.field];
10099
+ const val = getColVal(c, row);
10063
10100
  const isActions = c.type === "actions";
10064
10101
  const editable = isColEditable(c);
10065
10102
  const isEditing = editing && editing.key === k && editing.field === c.field;
@@ -10116,9 +10153,9 @@ function Datatable({
10116
10153
  }
10117
10154
  function renderPivot() {
10118
10155
  const rFields = pivotConfig.rows, cFields = pivotConfig.columns || [], values = pivotConfig.values;
10119
- const rowKeyOf = (r2) => rFields.map((f) => r2[f] ?? "\u2014").join(" \xB7 ");
10120
- const colPathOf = (r2) => cFields.map((f) => String(r2[f] ?? "\u2014"));
10121
- const aggOf2 = (subset, v) => computeAgg({ field: v.field, aggregation: v.agg || "sum" }, subset || []);
10156
+ const rowKeyOf = (r2) => rFields.map((f) => getColVal(colByField[f], r2) ?? "\u2014").join(" \xB7 ");
10157
+ const colPathOf = (r2) => cFields.map((f) => String(getColVal(colByField[f], r2) ?? "\u2014"));
10158
+ const aggOf2 = (subset, v) => computeAgg({ ...colByField[v.field], field: v.field, aggregation: v.agg || "sum" }, subset || []);
10122
10159
  const fmt = (val, v) => {
10123
10160
  if (val == null) return "\u2014";
10124
10161
  const col = colByField[v.field];
@@ -11274,6 +11311,470 @@ function Datatable({
11274
11311
  ] }) : null
11275
11312
  ] });
11276
11313
  }
11314
+ function space(v) {
11315
+ if (v == null) return void 0;
11316
+ if (typeof v === "number") return `var(--space-${String(v).replace(".", "-")})`;
11317
+ return v;
11318
+ }
11319
+ function len(v) {
11320
+ return typeof v === "number" ? `${v}px` : v;
11321
+ }
11322
+ var BP = { sm: 640, md: 768, lg: 1024, xl: 1280 };
11323
+ function safeHref5(url) {
11324
+ if (url == null) return void 0;
11325
+ const s = String(url).replace(/[\x00-\x20]+/g, "").toLowerCase();
11326
+ return s.startsWith("javascript:") || s.startsWith("data:") || s.startsWith("vbscript:") ? void 0 : url;
11327
+ }
11328
+ var Grid = React51__namespace.default.forwardRef(function Grid2({
11329
+ as: Tag2 = "div",
11330
+ columns,
11331
+ minChildWidth,
11332
+ gap = 4,
11333
+ rowGap,
11334
+ columnGap,
11335
+ align,
11336
+ justify,
11337
+ justifyItems,
11338
+ alignContent,
11339
+ justifyContent,
11340
+ sx,
11341
+ className = "",
11342
+ style,
11343
+ children,
11344
+ ...rest
11345
+ }, ref) {
11346
+ const { flatStyle, styleNode, sxAttr } = useSx(sx);
11347
+ const responsive = columns != null && typeof columns === "object";
11348
+ const uid = React51__namespace.default.useId();
11349
+ const gridId = responsive ? `g${uid.replace(/[^a-zA-Z0-9]/g, "")}` : null;
11350
+ let css = "";
11351
+ if (responsive) {
11352
+ const base = columns.base ?? 1;
11353
+ const sel = `[data-twc-grid-id="${gridId}"]`;
11354
+ css = `${sel} { --twc-grid-cols: ${base}; }`;
11355
+ for (const bp of ["sm", "md", "lg", "xl"]) {
11356
+ if (columns[bp] != null) css += `
11357
+ @media (min-width: ${BP[bp]}px) { ${sel} { --twc-grid-cols: ${columns[bp]}; } }`;
11358
+ }
11359
+ }
11360
+ const __twcStyles = useScopedStyles(gridId ? `twc-grid-${gridId}` : "twc-grid", css);
11361
+ const templateColumns = minChildWidth ? `repeat(auto-fill, minmax(min(${len(minChildWidth)}, 100%), 1fr))` : responsive ? `repeat(var(--twc-grid-cols, ${columns.base ?? 1}), minmax(0, 1fr))` : `repeat(${columns || 1}, minmax(0, 1fr))`;
11362
+ if (Tag2 === "a" && rest.href != null) rest.href = safeHref5(rest.href);
11363
+ return /* @__PURE__ */ jsxRuntime.jsxs(
11364
+ Tag2,
11365
+ {
11366
+ ref,
11367
+ className: `twc-grid ${className}`.trim(),
11368
+ "data-twc-grid-id": gridId || void 0,
11369
+ "data-twc-sx": sxAttr,
11370
+ style: {
11371
+ display: "grid",
11372
+ gridTemplateColumns: templateColumns,
11373
+ rowGap: space(rowGap ?? gap),
11374
+ columnGap: space(columnGap ?? gap),
11375
+ alignItems: align,
11376
+ // #216: `justifyItems` is the explicit, correctly-named prop; `justify` remains a
11377
+ // backward-compatible alias for it (note: on Grid `justify` = justify-**items**, unlike
11378
+ // Stack.justify = justify-**content**). The explicit prop wins when both are set.
11379
+ justifyItems: justifyItems ?? justify,
11380
+ alignContent,
11381
+ justifyContent,
11382
+ ...style,
11383
+ ...flatStyle
11384
+ },
11385
+ ...rest,
11386
+ children: [
11387
+ responsive ? __twcStyles : null,
11388
+ styleNode,
11389
+ children
11390
+ ]
11391
+ }
11392
+ );
11393
+ });
11394
+ Grid.displayName = "Grid";
11395
+ var CARDGRID_CSS = `
11396
+ .twc-cardgrid { font-family: var(--font-sans); display: flex; flex-direction: column; gap: var(--space-3); }
11397
+ .twc-cardgrid__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); }
11398
+ .twc-cardgrid__toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
11399
+ .twc-cardgrid__search { flex: 1 1 200px; min-width: 160px; }
11400
+ .twc-cardgrid__spacer { flex: 1 1 auto; }
11401
+ .twc-cardgrid__sort { display: inline-flex; align-items: center; gap: var(--space-1); }
11402
+ .twc-cardgrid__dir { display: inline-grid; place-items: center; width: 32px; height: 32px; border: var(--border-thin) solid var(--color-border); background: var(--color-surface); color: var(--color-text-muted); cursor: pointer; border-radius: var(--radius-md); transition: background-color var(--duration-fast), color var(--duration-fast); }
11403
+ .twc-cardgrid__dir:hover { background: var(--color-surface-sunken); color: var(--color-text); }
11404
+ .twc-cardgrid__dir svg { width: 16px; height: 16px; transition: transform var(--duration-fast) var(--ease-standard); }
11405
+ .twc-cardgrid__dir[data-dir="desc"] svg { transform: scaleY(-1); }
11406
+ .twc-cardgrid__body { position: relative; min-height: 60px; }
11407
+ .twc-cardgrid__body[data-loading="true"] .twc-cardgrid__grid { opacity: 0.55; pointer-events: none; }
11408
+ .twc-cardgrid__overlay { position: absolute; inset: 0; display: grid; place-items: center; z-index: 1; }
11409
+ .twc-cardgrid__spinner { width: 26px; height: 26px; border: 3px solid var(--color-border); border-top-color: var(--color-primary); border-radius: var(--radius-full); animation: twc-cardgrid-spin 0.7s linear infinite; }
11410
+ @keyframes twc-cardgrid-spin { to { transform: rotate(360deg); } }
11411
+ @media (prefers-reduced-motion: reduce) { .twc-cardgrid__spinner { animation-duration: 1.6s; } }
11412
+ .twc-cardgrid__empty { padding: var(--space-8) var(--space-4); text-align: center; color: var(--color-text-muted); font-size: var(--text-sm); }
11413
+ .twc-cardgrid__footer { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2); }
11414
+ .twc-cardgrid__count { font-size: var(--text-sm); color: var(--color-text-muted); }
11415
+ .twc-cardgrid__perpage { display: inline-flex; align-items: center; gap: var(--space-2); font-size: var(--text-sm); color: var(--color-text-muted); }
11416
+ `;
11417
+ function CardGrid({
11418
+ rows = [],
11419
+ renderCard,
11420
+ rowKey = "id",
11421
+ minChildWidth = "18rem",
11422
+ gap = 4,
11423
+ columns,
11424
+ // pagination
11425
+ pageSize = 12,
11426
+ pageSizeOptions = [12, 24, 48, 96],
11427
+ page,
11428
+ defaultPage = 0,
11429
+ onPageChange,
11430
+ onPageSizeChange,
11431
+ showPageSize = true,
11432
+ // query / server contract (mirrors Datatable serverMode)
11433
+ serverMode = false,
11434
+ rowCount,
11435
+ loading = false,
11436
+ onServerChange,
11437
+ // filtering & sorting
11438
+ filters = [],
11439
+ quickFilter,
11440
+ defaultQuickFilter = "",
11441
+ onQuickFilterChange,
11442
+ searchable = false,
11443
+ searchFields,
11444
+ searchPlaceholder = "Search\u2026",
11445
+ sort,
11446
+ defaultSort = null,
11447
+ onSortChange,
11448
+ sortOptions,
11449
+ // slots / states
11450
+ toolbar,
11451
+ emptyState = "No results.",
11452
+ label,
11453
+ id,
11454
+ className = "",
11455
+ ...rest
11456
+ }) {
11457
+ const styles = useScopedStyles("twc-cardgrid-styles", CARDGRID_CSS);
11458
+ const autoId = React51__namespace.default.useId();
11459
+ const fieldId = id || autoId;
11460
+ const [internalPage, setInternalPage] = React51__namespace.default.useState(defaultPage);
11461
+ const pageVal = page !== void 0 ? page : internalPage;
11462
+ const commitPage = (p) => {
11463
+ onPageChange?.(p);
11464
+ if (page === void 0) setInternalPage(p);
11465
+ };
11466
+ const [internalSize, setInternalSize] = React51__namespace.default.useState(pageSize > 0 ? pageSize : 12);
11467
+ const sizeControlled = onPageSizeChange !== void 0;
11468
+ const sizeVal = sizeControlled ? pageSize > 0 ? pageSize : 12 : internalSize;
11469
+ const commitSize = (n) => {
11470
+ onPageSizeChange?.(n);
11471
+ if (!sizeControlled) setInternalSize(n);
11472
+ commitPage(0);
11473
+ };
11474
+ React51__namespace.default.useEffect(() => {
11475
+ if (!sizeControlled) setInternalSize(pageSize > 0 ? pageSize : 12);
11476
+ }, [pageSize]);
11477
+ const [internalQuick, setInternalQuick] = React51__namespace.default.useState(defaultQuickFilter);
11478
+ const quickVal = quickFilter !== void 0 ? quickFilter : internalQuick;
11479
+ const commitQuick = (q) => {
11480
+ onQuickFilterChange?.(q);
11481
+ if (quickFilter === void 0) setInternalQuick(q);
11482
+ commitPage(0);
11483
+ };
11484
+ const [internalSort, setInternalSort] = React51__namespace.default.useState(defaultSort);
11485
+ const sortVal = sort !== void 0 ? sort : internalSort;
11486
+ const commitSort = (s) => {
11487
+ onSortChange?.(s);
11488
+ if (sort === void 0) setInternalSort(s);
11489
+ commitPage(0);
11490
+ };
11491
+ const filtersKey = JSON.stringify(filters);
11492
+ const mounted = React51__namespace.default.useRef(false);
11493
+ React51__namespace.default.useEffect(() => {
11494
+ if (!mounted.current) {
11495
+ mounted.current = true;
11496
+ return;
11497
+ }
11498
+ commitPage(0);
11499
+ }, [filtersKey]);
11500
+ const paginated = sizeVal > 0;
11501
+ const query = { page: pageVal, pageSize: paginated ? sizeVal : 0, sort: sortVal, filters, quickFilter: String(quickVal || "").trim() };
11502
+ const queryCols = sortOptions ? sortOptions.map((o) => ({ field: o.field, type: o.type })) : columns;
11503
+ const client = serverMode ? null : runDatatableQuery(rows, query, { columns: queryCols, searchFields });
11504
+ const pageRows = serverMode ? rows : client.rows;
11505
+ const total = serverMode ? rowCount == null ? rows.length : rowCount : client.total;
11506
+ const totalPages = Math.max(1, paginated ? Math.ceil(total / sizeVal) : 1);
11507
+ const onServerChangeRef = React51__namespace.default.useRef(onServerChange);
11508
+ onServerChangeRef.current = onServerChange;
11509
+ const queryKey = JSON.stringify(query);
11510
+ React51__namespace.default.useEffect(() => {
11511
+ if (!serverMode) return void 0;
11512
+ const t = setTimeout(() => onServerChangeRef.current?.(JSON.parse(queryKey)), 250);
11513
+ return () => clearTimeout(t);
11514
+ }, [serverMode, queryKey]);
11515
+ const keyOf = typeof rowKey === "function" ? rowKey : (r2) => r2 && r2[rowKey];
11516
+ const from = total === 0 ? 0 : pageVal * sizeVal + 1;
11517
+ const to = paginated ? Math.min((pageVal + 1) * sizeVal, total) : total;
11518
+ const rppOptions = Array.from(new Set([...pageSizeOptions || [], pageSize].filter((n) => n > 0))).sort((a, b) => a - b).map((n) => ({ value: String(n), label: String(n) }));
11519
+ const sortSelectOptions = sortOptions ? [{ value: "", label: "Sort by\u2026" }, ...sortOptions.map((o) => ({ value: o.field, label: o.label || o.field }))] : null;
11520
+ const hasToolbar = searchable || sortSelectOptions || toolbar;
11521
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-cardgrid ${className}`, id: fieldId, ...rest, children: [
11522
+ styles,
11523
+ label ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-cardgrid__label", id: `${fieldId}-label`, children: label }) : null,
11524
+ hasToolbar ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-cardgrid__toolbar", children: [
11525
+ searchable ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-cardgrid__search", children: /* @__PURE__ */ jsxRuntime.jsx(
11526
+ Input,
11527
+ {
11528
+ size: "sm",
11529
+ type: "search",
11530
+ value: quickVal,
11531
+ placeholder: searchPlaceholder,
11532
+ "aria-label": searchPlaceholder,
11533
+ onChange: (e) => commitQuick(e.target.value)
11534
+ }
11535
+ ) }) : null,
11536
+ toolbar,
11537
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-cardgrid__spacer" }),
11538
+ sortSelectOptions ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-cardgrid__sort", children: [
11539
+ /* @__PURE__ */ jsxRuntime.jsx(
11540
+ Select,
11541
+ {
11542
+ size: "sm",
11543
+ searchable: false,
11544
+ value: sortVal?.field || "",
11545
+ options: sortSelectOptions,
11546
+ placeholder: "Sort by\u2026",
11547
+ "aria-label": "Sort by",
11548
+ onChange: (field) => commitSort(field ? { field, dir: sortVal?.dir || "asc" } : null)
11549
+ }
11550
+ ),
11551
+ /* @__PURE__ */ jsxRuntime.jsx(
11552
+ "button",
11553
+ {
11554
+ type: "button",
11555
+ className: "twc-cardgrid__dir",
11556
+ "data-dir": sortVal?.dir || "asc",
11557
+ disabled: !sortVal?.field,
11558
+ "aria-label": sortVal?.dir === "desc" ? "Descending \u2014 click to sort ascending" : "Ascending \u2014 click to sort descending",
11559
+ onClick: () => sortVal?.field && commitSort({ field: sortVal.field, dir: sortVal.dir === "desc" ? "asc" : "desc" }),
11560
+ children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 5v14M6 11l6-6 6 6" }) })
11561
+ }
11562
+ )
11563
+ ] }) : null
11564
+ ] }) : null,
11565
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-cardgrid__body", "data-loading": loading || void 0, "aria-busy": loading || void 0, children: [
11566
+ loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-cardgrid__overlay", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-cardgrid__spinner", role: "status", "aria-label": "Loading" }) }) : null,
11567
+ pageRows.length === 0 && !loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-cardgrid__empty", children: emptyState }) : /* @__PURE__ */ jsxRuntime.jsx(
11568
+ Grid,
11569
+ {
11570
+ className: "twc-cardgrid__grid",
11571
+ minChildWidth,
11572
+ columns,
11573
+ gap,
11574
+ role: "list",
11575
+ "aria-labelledby": label ? `${fieldId}-label` : void 0,
11576
+ children: pageRows.map((row, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { role: "listitem", children: renderCard(row, i) }, keyOf(row, i) ?? i))
11577
+ }
11578
+ )
11579
+ ] }),
11580
+ paginated && totalPages > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-cardgrid__footer", children: [
11581
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-cardgrid__count", children: total === 0 ? "No results" : `${from}\u2013${to} of ${total}` }),
11582
+ /* @__PURE__ */ jsxRuntime.jsx(
11583
+ Pagination,
11584
+ {
11585
+ size: "sm",
11586
+ page: pageVal + 1,
11587
+ total: totalPages,
11588
+ boundaries: 1,
11589
+ siblings: 1,
11590
+ showPageJumper: totalPages > 7,
11591
+ onChange: (p) => commitPage(p - 1)
11592
+ }
11593
+ ),
11594
+ showPageSize ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-cardgrid__perpage", children: [
11595
+ "Per page",
11596
+ /* @__PURE__ */ jsxRuntime.jsx(
11597
+ Select,
11598
+ {
11599
+ size: "sm",
11600
+ searchable: false,
11601
+ value: String(sizeVal),
11602
+ options: rppOptions,
11603
+ placement: "top",
11604
+ "aria-label": "Cards per page",
11605
+ onChange: (v) => commitSize(Number(v))
11606
+ }
11607
+ )
11608
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("span", {})
11609
+ ] }) : null
11610
+ ] });
11611
+ }
11612
+ var DIFFTABLE_CSS = `
11613
+ .twc-difft { font-family: var(--font-sans); display: flex; flex-direction: column; gap: var(--space-3); color: var(--color-text); }
11614
+ .twc-difft__head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); }
11615
+ .twc-difft__label { font-size: var(--text-sm); font-weight: var(--font-semibold); }
11616
+ .twc-difft__summary { display: inline-flex; flex-wrap: wrap; gap: var(--space-1-5); }
11617
+ .twc-difft__toggle { margin-inline-start: auto; }
11618
+ .twc-difft__scroll { overflow-x: auto; border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-lg); }
11619
+ .twc-difft__table { width: 100%; border-collapse: collapse; font-size: var(--text-sm); }
11620
+ .twc-difft__table th, .twc-difft__table td { text-align: start; padding: var(--space-2) var(--space-3); border-bottom: var(--border-thin) solid var(--color-divider); vertical-align: top; }
11621
+ .twc-difft__table thead th { position: sticky; top: 0; background: var(--color-surface-sunken); font-weight: var(--font-semibold); color: var(--color-text-muted); font-size: var(--text-xs); text-transform: uppercase; letter-spacing: 0.02em; white-space: nowrap; z-index: 1; }
11622
+ .twc-difft__table tbody tr:last-child td { border-bottom: none; }
11623
+ .twc-difft__table tbody tr[data-op="added"] { background: color-mix(in srgb, var(--color-success) 8%, transparent); }
11624
+ .twc-difft__table tbody tr[data-op="removed"] { background: color-mix(in srgb, var(--color-danger) 8%, transparent); }
11625
+ .twc-difft__table tbody tr[data-op="modified"] { background: color-mix(in srgb, var(--color-warning) 8%, transparent); }
11626
+ .twc-difft__op { white-space: nowrap; }
11627
+ .twc-difft__change { display: inline-flex; align-items: center; flex-wrap: wrap; gap: 2px; }
11628
+ .twc-difft__old { text-decoration: line-through; color: var(--color-text-subtle); }
11629
+ .twc-difft__new { color: var(--color-success-subtle-fg); font-weight: var(--font-medium); }
11630
+ .twc-difft__arrow { margin-inline: 6px; color: var(--color-text-subtle); }
11631
+ .twc-difft__empty { padding: var(--space-8) var(--space-4); text-align: center; color: var(--color-text-muted); }
11632
+ [dir="rtl"] .twc-difft__arrow { transform: scaleX(-1); display: inline-block; }
11633
+ `;
11634
+ var OPS = {
11635
+ added: { label: "Added", tone: "success" },
11636
+ removed: { label: "Removed", tone: "danger" },
11637
+ modified: { label: "Modified", tone: "warning" },
11638
+ moved: { label: "Moved", tone: "info" },
11639
+ unchanged: { label: "Unchanged", tone: "neutral" }
11640
+ };
11641
+ function lisIndexSet(seq) {
11642
+ const n = seq.length;
11643
+ if (n === 0) return /* @__PURE__ */ new Set();
11644
+ const dp = new Array(n).fill(1), prev = new Array(n).fill(-1);
11645
+ let best = 0;
11646
+ for (let i = 0; i < n; i++) {
11647
+ for (let j = 0; j < i; j++) {
11648
+ if (seq[j] < seq[i] && dp[j] + 1 > dp[i]) {
11649
+ dp[i] = dp[j] + 1;
11650
+ prev[i] = j;
11651
+ }
11652
+ }
11653
+ if (dp[i] > dp[best]) best = i;
11654
+ }
11655
+ const set = /* @__PURE__ */ new Set();
11656
+ for (let i = best; i !== -1; i = prev[i]) set.add(i);
11657
+ return set;
11658
+ }
11659
+ var getVal = (col, row) => col.valueGetter ? col.valueGetter(row) : row == null ? void 0 : row[col.field];
11660
+ var fmtVal = (col, v) => col.valueFormatter ? col.valueFormatter(v) : v == null ? "" : String(v);
11661
+ function DiffTable({
11662
+ from = [],
11663
+ to = [],
11664
+ rowKey,
11665
+ columns = [],
11666
+ onlyChanged = true,
11667
+ showToggle = true,
11668
+ showSummary = true,
11669
+ moveDetection = true,
11670
+ compare,
11671
+ emptyState = "No differences.",
11672
+ toggleLabel = "Only changed",
11673
+ label,
11674
+ id,
11675
+ className = "",
11676
+ ...rest
11677
+ }) {
11678
+ const styles = useScopedStyles("twc-difftable-styles", DIFFTABLE_CSS);
11679
+ const autoId = React51__namespace.default.useId();
11680
+ const fieldId = id || autoId;
11681
+ const [only, setOnly] = React51__namespace.default.useState(onlyChanged);
11682
+ const valEq = React51__namespace.default.useCallback((a, b, col) => {
11683
+ if (col.compare) return col.compare(a, b);
11684
+ if (compare) return compare(a, b, col.field);
11685
+ if (a === b) return true;
11686
+ if (a == null || b == null) return a === b;
11687
+ return JSON.stringify(a) === JSON.stringify(b);
11688
+ }, [compare]);
11689
+ const diff = React51__namespace.default.useMemo(() => {
11690
+ const k = rowKey || ((r2) => r2 == null ? r2 : r2.id);
11691
+ const fromMap = /* @__PURE__ */ new Map(), toMap = /* @__PURE__ */ new Map();
11692
+ from.forEach((r2, i) => fromMap.set(k(r2), { row: r2, i }));
11693
+ to.forEach((r2, i) => toMap.set(k(r2), { row: r2, i }));
11694
+ const movedKeys = /* @__PURE__ */ new Set();
11695
+ if (moveDetection) {
11696
+ const commonFrom = from.filter((r2) => toMap.has(k(r2)));
11697
+ const seq = commonFrom.map((r2) => toMap.get(k(r2)).i);
11698
+ const stable = lisIndexSet(seq);
11699
+ commonFrom.forEach((r2, idx) => {
11700
+ if (!stable.has(idx)) movedKeys.add(k(r2));
11701
+ });
11702
+ }
11703
+ const classify = (key) => {
11704
+ const a = fromMap.get(key), b = toMap.get(key);
11705
+ if (a && !b) return { op: "removed", from: a.row, to: null, changed: /* @__PURE__ */ new Set() };
11706
+ if (!a && b) return { op: "added", from: null, to: b.row, changed: /* @__PURE__ */ new Set() };
11707
+ const changed = /* @__PURE__ */ new Set();
11708
+ for (const col of columns) if (!valEq(getVal(col, a.row), getVal(col, b.row), col)) changed.add(col.field);
11709
+ if (changed.size) return { op: "modified", from: a.row, to: b.row, changed };
11710
+ if (movedKeys.has(key)) return { op: "moved", from: a.row, to: b.row, changed };
11711
+ return { op: "unchanged", from: a.row, to: b.row, changed };
11712
+ };
11713
+ const rows = [];
11714
+ to.forEach((r2) => rows.push({ key: k(r2), ...classify(k(r2)) }));
11715
+ from.forEach((r2) => {
11716
+ const key = k(r2);
11717
+ if (!toMap.has(key)) rows.push({ key, ...classify(key) });
11718
+ });
11719
+ const counts = { added: 0, removed: 0, modified: 0, moved: 0, unchanged: 0 };
11720
+ rows.forEach((r2) => {
11721
+ counts[r2.op] += 1;
11722
+ });
11723
+ return { rows, counts };
11724
+ }, [from, to, columns, rowKey, moveDetection, valEq]);
11725
+ const visible = only ? diff.rows.filter((r2) => r2.op !== "unchanged") : diff.rows;
11726
+ const renderCell = (r2, col) => {
11727
+ if (r2.op === "removed") return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-difft__old", children: fmtVal(col, getVal(col, r2.from)) });
11728
+ if (r2.op === "added") return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-difft__new", children: fmtVal(col, getVal(col, r2.to)) });
11729
+ if (r2.op === "modified" && r2.changed.has(col.field)) {
11730
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "twc-difft__change", children: [
11731
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-difft__old", children: fmtVal(col, getVal(col, r2.from)) }),
11732
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-difft__arrow", "aria-hidden": "true", children: "\u2192" }),
11733
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-difft__new", children: fmtVal(col, getVal(col, r2.to)) })
11734
+ ] });
11735
+ }
11736
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { children: fmtVal(col, getVal(col, r2.to ?? r2.from)) });
11737
+ };
11738
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-difft ${className}`, id: fieldId, ...rest, children: [
11739
+ styles,
11740
+ label || showSummary || showToggle ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-difft__head", children: [
11741
+ label ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-difft__label", children: label }) : null,
11742
+ showSummary ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-difft__summary", children: [
11743
+ /* @__PURE__ */ jsxRuntime.jsxs(Badge, { size: "sm", variant: "soft", tone: "success", children: [
11744
+ "+",
11745
+ diff.counts.added,
11746
+ " added"
11747
+ ] }),
11748
+ /* @__PURE__ */ jsxRuntime.jsxs(Badge, { size: "sm", variant: "soft", tone: "warning", children: [
11749
+ "~",
11750
+ diff.counts.modified,
11751
+ " modified"
11752
+ ] }),
11753
+ /* @__PURE__ */ jsxRuntime.jsxs(Badge, { size: "sm", variant: "soft", tone: "danger", children: [
11754
+ "-",
11755
+ diff.counts.removed,
11756
+ " removed"
11757
+ ] }),
11758
+ diff.counts.moved ? /* @__PURE__ */ jsxRuntime.jsxs(Badge, { size: "sm", variant: "soft", tone: "info", children: [
11759
+ "\u21C5 ",
11760
+ diff.counts.moved,
11761
+ " moved"
11762
+ ] }) : null
11763
+ ] }) : null,
11764
+ showToggle ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-difft__toggle", children: /* @__PURE__ */ jsxRuntime.jsx(Checkbox, { label: toggleLabel, checked: only, onChange: (e) => setOnly(e.target.checked) }) }) : null
11765
+ ] }) : null,
11766
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-difft__scroll", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "twc-difft__table", children: [
11767
+ /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
11768
+ /* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", children: "Change" }),
11769
+ columns.map((c) => /* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", children: c.headerName || c.field }, c.field))
11770
+ ] }) }),
11771
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: visible.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("tr", { children: /* @__PURE__ */ jsxRuntime.jsx("td", { className: "twc-difft__empty", colSpan: columns.length + 1, children: emptyState }) }) : visible.map((r2) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { "data-op": r2.op, children: [
11772
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "twc-difft__op", children: /* @__PURE__ */ jsxRuntime.jsx(Badge, { size: "sm", variant: "soft", tone: OPS[r2.op].tone, children: OPS[r2.op].label }) }),
11773
+ columns.map((c) => /* @__PURE__ */ jsxRuntime.jsx("td", { children: renderCell(r2, c) }, c.field))
11774
+ ] }, r2.key)) })
11775
+ ] }) })
11776
+ ] });
11777
+ }
11277
11778
  var FIELD_CSS3 = `
11278
11779
  .twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
11279
11780
  .twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
@@ -11285,13 +11786,15 @@ var DATEPICKER_CSS = `
11285
11786
  .twc-dp { position: relative; font-family: var(--font-sans); display: flex; flex-direction: column; gap: var(--space-1-5); }
11286
11787
  .twc-dp__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); }
11287
11788
  .twc-dp__control {
11288
- display: flex; align-items: center; gap: var(--space-2); height: var(--control-h-md); padding: 0 var(--space-3);
11789
+ display: flex; align-items: center; gap: var(--space-2); --_h: var(--control-h-md); height: var(--_h); padding: 0 var(--space-3);
11289
11790
  background: var(--color-surface); border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-md);
11290
11791
  cursor: pointer; transition: border-color var(--duration-fast) var(--ease-standard), box-shadow var(--duration-fast) var(--ease-standard);
11291
11792
  }
11292
11793
  .twc-dp__control:hover:not([data-open="true"]) { border-color: var(--color-border-strong); }
11293
11794
  /* tone \u2192 focus/open accent (default primary; reproduces current look). */
11294
11795
  .twc-dp__control { --_accent: var(--color-primary); --_ring: var(--ring); }
11796
+ .twc-dp__control[data-size="sm"] { --_h: var(--control-h-sm); }
11797
+ .twc-dp__control[data-size="lg"] { --_h: var(--control-h-lg); }
11295
11798
  .twc-dp__control[data-tone="success"] { --_accent: var(--color-success); --_ring: 0 0 0 var(--ring-width) color-mix(in srgb, var(--color-success) 32%, transparent); }
11296
11799
  .twc-dp__control[data-tone="warning"] { --_accent: var(--color-warning); --_ring: 0 0 0 var(--ring-width) color-mix(in srgb, var(--color-warning) 32%, transparent); }
11297
11800
  .twc-dp__control[data-tone="danger"] { --_accent: var(--color-danger); --_ring: 0 0 0 var(--ring-width) color-mix(in srgb, var(--color-danger) 32%, transparent); }
@@ -11351,6 +11854,12 @@ var DATEPICKER_CSS = `
11351
11854
  .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); }
11352
11855
  .twc-dp__mo:hover { background: var(--color-surface-sunken); }
11353
11856
  .twc-dp__mo[data-selected="true"] { background: var(--color-primary); color: var(--color-primary-fg); font-weight: var(--font-bold); }
11857
+ .twc-dp__years { display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px; }
11858
+ .twc-dp__yr { 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); }
11859
+ .twc-dp__yr:hover:not(:disabled) { background: var(--color-surface-sunken); }
11860
+ .twc-dp__yr[data-outside="true"] { color: var(--color-text-subtle); }
11861
+ .twc-dp__yr[data-selected="true"] { background: var(--color-primary); color: var(--color-primary-fg); font-weight: var(--font-bold); }
11862
+ .twc-dp__yr:disabled { opacity: 0.4; cursor: not-allowed; }
11354
11863
  [dir="rtl"] .twc-dp__nav svg { transform: scaleX(-1); }
11355
11864
  `;
11356
11865
  var MONTHS = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
@@ -11378,8 +11887,10 @@ function DatePicker({
11378
11887
  placeholder = "Select date",
11379
11888
  min,
11380
11889
  max,
11890
+ disabledDate,
11381
11891
  disabled = false,
11382
11892
  tone = "primary",
11893
+ size = "md",
11383
11894
  clearable = true,
11384
11895
  editable = false,
11385
11896
  parse,
@@ -11404,6 +11915,7 @@ function DatePicker({
11404
11915
  const [mode, setMode] = React51__namespace.default.useState("days");
11405
11916
  const [focusDate, setFocusDate] = React51__namespace.default.useState(null);
11406
11917
  const [focusMonth, setFocusMonth] = React51__namespace.default.useState(null);
11918
+ const [focusYear, setFocusYear] = React51__namespace.default.useState(null);
11407
11919
  const [editText, setEditText] = React51__namespace.default.useState(null);
11408
11920
  const [coords, setCoords] = React51__namespace.default.useState(null);
11409
11921
  const wrapRef = React51__namespace.default.useRef(null);
@@ -11496,11 +12008,13 @@ function DatePicker({
11496
12008
  pick(parsed);
11497
12009
  };
11498
12010
  const y = view.getFullYear(), m = view.getMonth();
12011
+ const decadeStart = Math.floor(y / 10) * 10;
12012
+ const yearCells = Array.from({ length: 12 }, (_, i) => decadeStart - 1 + i);
11499
12013
  const first = new Date(y, m, 1);
11500
12014
  const startOffset = (first.getDay() - weekStartsOn + 7) % 7;
11501
12015
  const gridStart = new Date(y, m, 1 - startOffset);
11502
12016
  const today = /* @__PURE__ */ new Date();
11503
- const outOfRange = (d) => min && d < new Date(min.getFullYear(), min.getMonth(), min.getDate()) || max && d > new Date(max.getFullYear(), max.getMonth(), max.getDate());
12017
+ const outOfRange = (d) => min && d < new Date(min.getFullYear(), min.getMonth(), min.getDate()) || max && d > new Date(max.getFullYear(), max.getMonth(), max.getDate()) || Boolean(disabledDate && disabledDate(d));
11504
12018
  const keyOf = (d) => `${d.getFullYear()}-${d.getMonth()}-${d.getDate()}`;
11505
12019
  const addDays = (d, n) => new Date(d.getFullYear(), d.getMonth(), d.getDate() + n);
11506
12020
  const addMonths = (d, n) => {
@@ -11537,6 +12051,24 @@ function DatePicker({
11537
12051
  }
11538
12052
  setFocusMonth(next);
11539
12053
  };
12054
+ React51__namespace.default.useEffect(() => {
12055
+ if (!open || mode !== "years" || focusYear == null) return;
12056
+ gridRef.current?.querySelector(`[data-yr="${focusYear}"]`)?.focus();
12057
+ }, [focusYear, open, mode]);
12058
+ const onYearsKeyDown = (e) => {
12059
+ const cur = focusYear == null ? y : focusYear;
12060
+ let next;
12061
+ if (e.key === "ArrowRight") next = cur + 1;
12062
+ else if (e.key === "ArrowLeft") next = cur - 1;
12063
+ else if (e.key === "ArrowDown") next = cur + 3;
12064
+ else if (e.key === "ArrowUp") next = cur - 3;
12065
+ else if (e.key === "Home") next = decadeStart;
12066
+ else if (e.key === "End") next = decadeStart + 9;
12067
+ else return;
12068
+ e.preventDefault();
12069
+ setView(new Date(next, m, 1));
12070
+ setFocusYear(next);
12071
+ };
11540
12072
  const onGridKeyDown = (e) => {
11541
12073
  const btn = e.target.closest?.("[data-key]");
11542
12074
  if (!btn) return;
@@ -11581,6 +12113,7 @@ function DatePicker({
11581
12113
  "data-disabled": disabled || void 0,
11582
12114
  "data-tone": tone,
11583
12115
  "data-invalid": invalid || void 0,
12116
+ "data-size": size,
11584
12117
  children: [
11585
12118
  /* @__PURE__ */ jsxRuntime.jsx(
11586
12119
  "input",
@@ -11654,6 +12187,7 @@ function DatePicker({
11654
12187
  id: fieldId,
11655
12188
  "data-open": open || void 0,
11656
12189
  "data-disabled": disabled || void 0,
12190
+ "data-size": size,
11657
12191
  "data-tone": tone,
11658
12192
  "data-has-clear": clearable && selected && !disabled ? "true" : void 0,
11659
12193
  "data-invalid": invalid || void 0,
@@ -11697,13 +12231,18 @@ function DatePicker({
11697
12231
  style: { position: "fixed", left: coords.left, right: "auto", top: coords.top, bottom: coords.bottom, zIndex: "var(--z-tooltip)" },
11698
12232
  children: [
11699
12233
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dp__head", children: [
11700
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-dp__nav", "aria-label": "Previous", onClick: () => setView(mode === "months" ? new Date(y - 1, m, 1) : new Date(y, m - 1, 1)), children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m15 18-6-6 6-6" }) }) }),
12234
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-dp__nav", "aria-label": mode === "years" ? "Previous decade" : mode === "months" ? "Previous year" : "Previous month", onClick: () => setView(mode === "years" ? new Date(y - 10, m, 1) : mode === "months" ? new Date(y - 1, m, 1) : new Date(y, m - 1, 1)), children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m15 18-6-6 6-6" }) }) }),
11701
12235
  /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-dp__title", onClick: () => {
11702
- if (mode === "days") setFocusMonth(m);
11703
- setMode(mode === "days" ? "months" : "days");
11704
- }, children: mode === "days" ? `${months[m]} ${y}` : y }),
11705
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-dp__sr", "aria-live": "polite", children: mode === "days" ? `${months[m]} ${y}` : y }),
11706
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-dp__nav", "aria-label": "Next", onClick: () => setView(mode === "months" ? new Date(y + 1, m, 1) : new Date(y, m + 1, 1)), children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m9 18 6-6-6-6" }) }) })
12236
+ if (mode === "days") {
12237
+ setFocusMonth(m);
12238
+ setMode("months");
12239
+ } else if (mode === "months") {
12240
+ setFocusYear(y);
12241
+ setMode("years");
12242
+ } else setMode("days");
12243
+ }, children: mode === "days" ? `${months[m]} ${y}` : mode === "months" ? y : `${decadeStart}\u2013${decadeStart + 9}` }),
12244
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-dp__sr", "aria-live": "polite", children: mode === "days" ? `${months[m]} ${y}` : mode === "months" ? y : `${decadeStart}\u2013${decadeStart + 9}` }),
12245
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-dp__nav", "aria-label": mode === "years" ? "Next decade" : mode === "months" ? "Next year" : "Next month", onClick: () => setView(mode === "years" ? new Date(y + 10, m, 1) : mode === "months" ? new Date(y + 1, m, 1) : new Date(y, m + 1, 1)), children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m9 18 6-6-6-6" }) }) })
11707
12246
  ] }),
11708
12247
  mode === "days" ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dp__grid", ref: gridRef, onKeyDown: onGridKeyDown, children: [
11709
12248
  dows.map((d, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-dp__dow", children: d }, i)),
@@ -11729,7 +12268,7 @@ function DatePicker({
11729
12268
  i
11730
12269
  );
11731
12270
  })
11732
- ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-dp__months", ref: gridRef, onKeyDown: onMonthsKeyDown, role: "grid", children: monthsShort.map((name, i) => /* @__PURE__ */ jsxRuntime.jsx(
12271
+ ] }) : mode === "months" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-dp__months", ref: gridRef, onKeyDown: onMonthsKeyDown, role: "grid", children: monthsShort.map((name, i) => /* @__PURE__ */ jsxRuntime.jsx(
11733
12272
  "button",
11734
12273
  {
11735
12274
  type: "button",
@@ -11745,7 +12284,30 @@ function DatePicker({
11745
12284
  children: name
11746
12285
  },
11747
12286
  i
11748
- )) })
12287
+ )) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-dp__years", ref: gridRef, onKeyDown: onYearsKeyDown, role: "grid", children: yearCells.map((yr) => {
12288
+ const outside = yr < decadeStart || yr > decadeStart + 9;
12289
+ const disabled2 = min && yr < min.getFullYear() || max && yr > max.getFullYear();
12290
+ return /* @__PURE__ */ jsxRuntime.jsx(
12291
+ "button",
12292
+ {
12293
+ type: "button",
12294
+ className: "twc-dp__yr",
12295
+ role: "gridcell",
12296
+ "data-yr": yr,
12297
+ "data-outside": outside || void 0,
12298
+ disabled: disabled2,
12299
+ "data-selected": selected && selected.getFullYear() === yr || void 0,
12300
+ tabIndex: yr === (focusYear ?? y) ? 0 : -1,
12301
+ onClick: () => {
12302
+ setView(new Date(yr, m, 1));
12303
+ setFocusMonth(m);
12304
+ setMode("months");
12305
+ },
12306
+ children: yr
12307
+ },
12308
+ yr
12309
+ );
12310
+ }) })
11749
12311
  ]
11750
12312
  }
11751
12313
  ),
@@ -11764,12 +12326,14 @@ var FIELD_CSS4 = `
11764
12326
  var RANGE_CSS2 = `
11765
12327
  .twc-drp { position: relative; font-family: var(--font-sans); display: flex; flex-direction: column; gap: var(--space-1-5); }
11766
12328
  .twc-drp__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); }
11767
- .twc-drp__control { display: flex; align-items: center; gap: var(--space-2); height: var(--control-h-md); padding: 0 var(--space-3);
12329
+ .twc-drp__control { display: flex; align-items: center; gap: var(--space-2); --_h: var(--control-h-md); height: var(--_h); padding: 0 var(--space-3);
11768
12330
  background: var(--color-surface); border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-md); cursor: pointer;
11769
12331
  transition: border-color var(--duration-fast) var(--ease-standard), box-shadow var(--duration-fast) var(--ease-standard); }
11770
12332
  .twc-drp__control:hover:not([data-open="true"]):not([data-disabled="true"]) { border-color: var(--color-border-strong); }
11771
12333
  /* tone \u2192 focus/open accent (default primary; reproduces current look). */
11772
12334
  .twc-drp__control { --_accent: var(--color-primary); --_ring: var(--ring); }
12335
+ .twc-drp__control[data-size="sm"] { --_h: var(--control-h-sm); }
12336
+ .twc-drp__control[data-size="lg"] { --_h: var(--control-h-lg); }
11773
12337
  .twc-drp__control[data-tone="success"] { --_accent: var(--color-success); --_ring: 0 0 0 var(--ring-width) color-mix(in srgb, var(--color-success) 32%, transparent); }
11774
12338
  .twc-drp__control[data-tone="warning"] { --_accent: var(--color-warning); --_ring: 0 0 0 var(--ring-width) color-mix(in srgb, var(--color-warning) 32%, transparent); }
11775
12339
  .twc-drp__control[data-tone="danger"] { --_accent: var(--color-danger); --_ring: 0 0 0 var(--ring-width) color-mix(in srgb, var(--color-danger) 32%, transparent); }
@@ -11783,6 +12347,9 @@ var RANGE_CSS2 = `
11783
12347
  .twc-drp__ic svg { width: 17px; height: 17px; }
11784
12348
  .twc-drp__text { flex: 1; font-size: var(--text-sm); color: var(--color-text); white-space: nowrap; }
11785
12349
  .twc-drp__text[data-placeholder="true"] { color: var(--color-text-subtle); }
12350
+ .twc-drp__clear { flex: none; display: inline-grid; place-items: center; width: 20px; height: 20px; border: none; padding: 0; background: transparent; color: var(--color-text-subtle); cursor: pointer; border-radius: var(--radius-full); }
12351
+ .twc-drp__clear:hover { background: var(--color-surface-sunken); color: var(--color-text); }
12352
+ .twc-drp__clear svg { width: 14px; height: 14px; }
11786
12353
  /* #105: editable (typed entry) variant. */
11787
12354
  .twc-drp__control--editable { cursor: text; gap: 0; padding-inline-end: var(--space-1); }
11788
12355
  .twc-drp__input { flex: 1; min-width: 0; height: 100%; border: none; background: transparent; outline: none; padding: 0;
@@ -11806,7 +12373,14 @@ var RANGE_CSS2 = `
11806
12373
  .twc-drp__nav { display: inline-grid; place-items: center; width: 30px; height: 30px; border: none; background: transparent; color: var(--color-text-muted); cursor: pointer; border-radius: var(--radius-md); }
11807
12374
  .twc-drp__nav:hover { background: var(--color-surface-sunken); color: var(--color-text); }
11808
12375
  .twc-drp__nav svg { width: 17px; height: 17px; }
11809
- .twc-drp__title { font-size: var(--text-sm); font-weight: var(--font-bold); }
12376
+ .twc-drp__title { font-size: var(--text-sm); font-weight: var(--font-bold); color: var(--color-text); border: none; background: transparent; cursor: pointer; padding: 5px 10px; border-radius: var(--radius-md); }
12377
+ .twc-drp__title:hover { background: var(--color-surface-sunken); }
12378
+ .twc-drp__months, .twc-drp__years { display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px; }
12379
+ .twc-drp__mo, .twc-drp__yr { 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); }
12380
+ .twc-drp__mo:hover:not(:disabled), .twc-drp__yr:hover:not(:disabled) { background: var(--color-surface-sunken); }
12381
+ .twc-drp__yr[data-outside="true"] { color: var(--color-text-subtle); }
12382
+ .twc-drp__mo[data-selected="true"], .twc-drp__yr[data-selected="true"] { background: var(--color-primary); color: var(--color-primary-fg); font-weight: var(--font-bold); }
12383
+ .twc-drp__mo:disabled, .twc-drp__yr:disabled { opacity: 0.4; cursor: not-allowed; }
11810
12384
  .twc-drp__grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 1px; }
11811
12385
  .twc-drp__dow { text-align: center; font-size: 11px; font-weight: var(--font-bold); color: var(--color-text-subtle); padding: 4px 0; }
11812
12386
  .twc-drp__day { aspect-ratio: 1; border: none; background: transparent; cursor: pointer; font-family: inherit; font-size: var(--text-sm); color: var(--color-text); display: grid; place-items: center; position: relative; }
@@ -11832,6 +12406,18 @@ var weekdayNames2 = (locale, weekStartsOn) => {
11832
12406
  };
11833
12407
  var ymd = (d) => d ? new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime() : null;
11834
12408
  var fmtD = (d, locale) => d ? d.toLocaleDateString(locale, { month: "short", day: "numeric", year: "numeric" }) : "";
12409
+ var rollingDays = (days) => {
12410
+ const end = /* @__PURE__ */ new Date();
12411
+ const start = /* @__PURE__ */ new Date();
12412
+ start.setDate(end.getDate() - days + 1);
12413
+ return { start, end };
12414
+ };
12415
+ var DEFAULT_PRESETS2 = [
12416
+ { label: "Last 7 days", range: () => rollingDays(7) },
12417
+ { label: "Last 14 days", range: () => rollingDays(14) },
12418
+ { label: "Last 30 days", range: () => rollingDays(30) },
12419
+ { label: "Last 90 days", range: () => rollingDays(90) }
12420
+ ];
11835
12421
  function DateRangePicker({
11836
12422
  label,
11837
12423
  hint,
@@ -11848,6 +12434,9 @@ function DateRangePicker({
11848
12434
  disabledDate,
11849
12435
  disabled = false,
11850
12436
  tone = "primary",
12437
+ size = "md",
12438
+ clearable = true,
12439
+ format,
11851
12440
  editable = false,
11852
12441
  parse,
11853
12442
  onChange,
@@ -11857,11 +12446,14 @@ function DateRangePicker({
11857
12446
  const __twcStyles0 = useScopedStyles("twc-field-styles", FIELD_CSS4);
11858
12447
  const __twcStyles1 = useScopedStyles("twc-drp-styles", RANGE_CSS2);
11859
12448
  const [internal, setInternal] = React51__namespace.default.useState(defaultValue);
11860
- const range = value !== void 0 ? value : internal;
12449
+ const range2 = value !== void 0 ? value : internal;
11861
12450
  const [open, setOpen] = React51__namespace.default.useState(false);
11862
- const [view, setView] = React51__namespace.default.useState(range.start || /* @__PURE__ */ new Date());
12451
+ const [view, setView] = React51__namespace.default.useState(range2.start || /* @__PURE__ */ new Date());
11863
12452
  const [hover, setHover] = React51__namespace.default.useState(null);
11864
12453
  const [focusDate, setFocusDate] = React51__namespace.default.useState(null);
12454
+ const [mode, setMode] = React51__namespace.default.useState("days");
12455
+ const [focusMonth, setFocusMonth] = React51__namespace.default.useState(null);
12456
+ const [focusYear, setFocusYear] = React51__namespace.default.useState(null);
11865
12457
  const [editText, setEditText] = React51__namespace.default.useState(null);
11866
12458
  const [coords, setCoords] = React51__namespace.default.useState(null);
11867
12459
  const wrapRef = React51__namespace.default.useRef(null);
@@ -11933,7 +12525,7 @@ function DateRangePicker({
11933
12525
  const raw = editText.trim();
11934
12526
  if (raw === "") {
11935
12527
  setEditText(null);
11936
- if (range.start || range.end) set({ start: null, end: null });
12528
+ if (range2.start || range2.end) set({ start: null, end: null });
11937
12529
  return;
11938
12530
  }
11939
12531
  const parts = raw.split(/\s*(?:–|—|\bto\b|\.\.+)\s*/i).map((p) => p.trim());
@@ -11965,8 +12557,15 @@ function DateRangePicker({
11965
12557
  if (!open) {
11966
12558
  setFocusDate(null);
11967
12559
  setHover(null);
12560
+ setMode("days");
11968
12561
  }
11969
12562
  }, [open]);
12563
+ React51__namespace.default.useEffect(() => {
12564
+ if (open && mode === "months" && focusMonth != null) gridRef.current?.querySelector(`[data-mo="${focusMonth}"]`)?.focus();
12565
+ }, [focusMonth, open, mode]);
12566
+ React51__namespace.default.useEffect(() => {
12567
+ if (open && mode === "years" && focusYear != null) gridRef.current?.querySelector(`[data-yr="${focusYear}"]`)?.focus();
12568
+ }, [focusYear, open, mode]);
11970
12569
  React51__namespace.default.useEffect(() => {
11971
12570
  if (!open || !focusDate) return;
11972
12571
  gridRef.current?.querySelector(`[data-key="${keyOf(focusDate)}"]`)?.focus();
@@ -11997,35 +12596,74 @@ function DateRangePicker({
11997
12596
  setHover(next);
11998
12597
  if (next.getFullYear() !== view.getFullYear() || next.getMonth() !== view.getMonth()) setView(new Date(next.getFullYear(), next.getMonth(), 1));
11999
12598
  };
12000
- const tabbableDate = focusDate || range.start || /* @__PURE__ */ new Date();
12599
+ const onMonthsKeyDown = (e) => {
12600
+ const vy = view.getFullYear();
12601
+ const cur = focusMonth == null ? view.getMonth() : focusMonth;
12602
+ let next;
12603
+ if (e.key === "ArrowRight") next = cur + 1;
12604
+ else if (e.key === "ArrowLeft") next = cur - 1;
12605
+ else if (e.key === "ArrowDown") next = cur + 3;
12606
+ else if (e.key === "ArrowUp") next = cur - 3;
12607
+ else if (e.key === "Home") next = 0;
12608
+ else if (e.key === "End") next = 11;
12609
+ else return;
12610
+ e.preventDefault();
12611
+ if (next < 0) {
12612
+ setView(new Date(vy - 1, 0, 1));
12613
+ next += 12;
12614
+ } else if (next > 11) {
12615
+ setView(new Date(vy + 1, 0, 1));
12616
+ next -= 12;
12617
+ }
12618
+ setFocusMonth(next);
12619
+ };
12620
+ const onYearsKeyDown = (e) => {
12621
+ const vm = view.getMonth(), ds = Math.floor(view.getFullYear() / 10) * 10;
12622
+ const cur = focusYear == null ? view.getFullYear() : focusYear;
12623
+ let next;
12624
+ if (e.key === "ArrowRight") next = cur + 1;
12625
+ else if (e.key === "ArrowLeft") next = cur - 1;
12626
+ else if (e.key === "ArrowDown") next = cur + 3;
12627
+ else if (e.key === "ArrowUp") next = cur - 3;
12628
+ else if (e.key === "Home") next = ds;
12629
+ else if (e.key === "End") next = ds + 9;
12630
+ else return;
12631
+ e.preventDefault();
12632
+ setView(new Date(next, vm, 1));
12633
+ setFocusYear(next);
12634
+ };
12635
+ const tabbableDate = focusDate || range2.start || /* @__PURE__ */ new Date();
12001
12636
  const clickDay = (d) => {
12002
12637
  if (outOfRange(d)) return;
12003
- if (!range.start || range.start && range.end) {
12638
+ if (!range2.start || range2.start && range2.end) {
12004
12639
  set({ start: d, end: null });
12005
12640
  } else {
12006
- const [start, end] = ymd(d) < ymd(range.start) ? [d, range.start] : [range.start, d];
12641
+ const [start, end] = ymd(d) < ymd(range2.start) ? [d, range2.start] : [range2.start, d];
12007
12642
  set({ start, end });
12008
12643
  setOpen(false);
12009
12644
  }
12010
12645
  };
12011
- const applyPreset = (days) => {
12012
- const end = /* @__PURE__ */ new Date();
12013
- const start = /* @__PURE__ */ new Date();
12014
- start.setDate(end.getDate() - days + 1);
12015
- set({ start, end });
12016
- setView(start);
12646
+ const presetItems = presets === true ? DEFAULT_PRESETS2 : Array.isArray(presets) ? presets : [];
12647
+ const applyPreset = (preset) => {
12648
+ const r2 = typeof preset.range === "function" ? preset.range() : preset.range;
12649
+ if (!r2) return;
12650
+ set(r2);
12651
+ if (r2.start) setView(r2.start);
12017
12652
  setOpen(false);
12018
12653
  };
12019
12654
  const months = React51__namespace.default.useMemo(() => locale === void 0 ? MONTHS2 : monthNames2(locale), [locale]);
12655
+ const monthsShort = React51__namespace.default.useMemo(() => months.map((n) => n.slice(0, 3)), [months]);
12020
12656
  const dows = React51__namespace.default.useMemo(
12021
12657
  () => locale === void 0 ? Array.from({ length: 7 }, (_, i) => DOW2[(i + weekStartsOn) % 7]) : weekdayNames2(locale, weekStartsOn),
12022
12658
  [locale, weekStartsOn]
12023
12659
  );
12024
12660
  const y = view.getFullYear(), m = view.getMonth();
12661
+ const decadeStart = Math.floor(y / 10) * 10;
12662
+ const yearCells = Array.from({ length: 12 }, (_, i) => decadeStart - 1 + i);
12025
12663
  const startOffset = (new Date(y, m, 1).getDay() - weekStartsOn + 7) % 7;
12026
12664
  const gridStart = new Date(y, m, 1 - startOffset);
12027
12665
  const edgeOf = (t) => {
12028
- const s = ymd(range.start), e = ymd(range.end) || (range.start && hover ? ymd(hover) : null);
12666
+ const s = ymd(range2.start), e = ymd(range2.end) || (range2.start && hover ? ymd(hover) : null);
12029
12667
  const lo = s != null && e != null ? Math.min(s, e) : s, hi = s != null && e != null ? Math.max(s, e) : s;
12030
12668
  if (s != null && hi === lo && t === s) return "both";
12031
12669
  if (t === lo) return "start";
@@ -12033,13 +12671,13 @@ function DateRangePicker({
12033
12671
  return null;
12034
12672
  };
12035
12673
  const inRange = (t) => {
12036
- const s = ymd(range.start);
12037
- let e = ymd(range.end);
12674
+ const s = ymd(range2.start);
12675
+ let e = ymd(range2.end);
12038
12676
  if (s != null && e == null && hover) e = ymd(hover);
12039
12677
  if (s == null || e == null) return false;
12040
12678
  return t > Math.min(s, e) && t < Math.max(s, e);
12041
12679
  };
12042
- const text = range.start ? `${fmtD(range.start, locale)} \u2013 ${range.end ? fmtD(range.end, locale) : "\u2026"}` : "";
12680
+ const text = range2.start ? format ? format(range2) : `${fmtD(range2.start, locale)} \u2013 ${range2.end ? fmtD(range2.end, locale) : "\u2026"}` : "";
12043
12681
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-drp twc-field ${className}`, ref: wrapRef, ...rest, children: [
12044
12682
  __twcStyles0,
12045
12683
  __twcStyles1,
@@ -12049,7 +12687,7 @@ function DateRangePicker({
12049
12687
  ] }) : null,
12050
12688
  editable ? (
12051
12689
  // #105: typed entry — a text input accepting "start – end"; commits via `parse` on Enter/blur.
12052
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-drp__control twc-drp__control--editable", "data-open": open || void 0, "data-disabled": disabled || void 0, "data-invalid": invalid || void 0, "data-tone": tone, children: [
12690
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-drp__control twc-drp__control--editable", "data-open": open || void 0, "data-disabled": disabled || void 0, "data-invalid": invalid || void 0, "data-tone": tone, "data-size": size, children: [
12053
12691
  /* @__PURE__ */ jsxRuntime.jsx(
12054
12692
  "input",
12055
12693
  {
@@ -12081,6 +12719,18 @@ function DateRangePicker({
12081
12719
  }
12082
12720
  }
12083
12721
  ),
12722
+ clearable && (range2.start || range2.end) && !disabled ? /* @__PURE__ */ jsxRuntime.jsx(
12723
+ "button",
12724
+ {
12725
+ type: "button",
12726
+ className: "twc-drp__clear",
12727
+ "aria-label": "Clear",
12728
+ tabIndex: -1,
12729
+ onMouseDown: (e) => e.preventDefault(),
12730
+ onClick: () => set({ start: null, end: null }),
12731
+ children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 6 6 18M6 6l12 12" }) })
12732
+ }
12733
+ ) : null,
12084
12734
  /* @__PURE__ */ jsxRuntime.jsx(
12085
12735
  "button",
12086
12736
  {
@@ -12106,6 +12756,7 @@ function DateRangePicker({
12106
12756
  "data-disabled": disabled || void 0,
12107
12757
  "data-invalid": invalid || void 0,
12108
12758
  "data-tone": tone,
12759
+ "data-size": size,
12109
12760
  role: "button",
12110
12761
  tabIndex: disabled ? -1 : 0,
12111
12762
  "aria-haspopup": "dialog",
@@ -12126,7 +12777,22 @@ function DateRangePicker({
12126
12777
  /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "3", y: "4", width: "18", height: "18", rx: "2" }),
12127
12778
  /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M16 2v4M8 2v4M3 10h18" })
12128
12779
  ] }) }),
12129
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-drp__text", "data-placeholder": !range.start || void 0, children: range.start ? text : placeholder })
12780
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-drp__text", "data-placeholder": !range2.start || void 0, children: range2.start ? text : placeholder }),
12781
+ clearable && (range2.start || range2.end) && !disabled ? /* @__PURE__ */ jsxRuntime.jsx(
12782
+ "button",
12783
+ {
12784
+ type: "button",
12785
+ className: "twc-drp__clear",
12786
+ "aria-label": "Clear",
12787
+ tabIndex: -1,
12788
+ onMouseDown: (e) => e.preventDefault(),
12789
+ onClick: (e) => {
12790
+ e.stopPropagation();
12791
+ set({ start: null, end: null });
12792
+ },
12793
+ children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 6 6 18M6 6l12 12" }) })
12794
+ }
12795
+ ) : null
12130
12796
  ]
12131
12797
  }
12132
12798
  ),
@@ -12141,23 +12807,23 @@ function DateRangePicker({
12141
12807
  "aria-label": "Choose date range",
12142
12808
  style: { position: "fixed", left: coords.left, right: "auto", top: coords.top, bottom: coords.bottom, zIndex: "var(--z-tooltip)" },
12143
12809
  children: [
12144
- presets ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-drp__presets", children: [["Last 7 days", 7], ["Last 14 days", 14], ["Last 30 days", 30], ["Last 90 days", 90]].map(([lbl, n]) => /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-drp__preset", onClick: () => applyPreset(n), children: lbl }, n)) }) : null,
12810
+ presetItems.length ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-drp__presets", children: presetItems.map((p, i) => /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-drp__preset", onClick: () => applyPreset(p), children: p.label }, i)) }) : null,
12145
12811
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-drp__cal", children: [
12146
12812
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-drp__head", children: [
12147
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-drp__nav", "aria-label": "Previous month", onClick: () => setView(new Date(y, m - 1, 1)), children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m15 18-6-6 6-6" }) }) }),
12148
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "twc-drp__title", children: [
12149
- months[m],
12150
- " ",
12151
- y
12152
- ] }),
12153
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "twc-drp__sr", "aria-live": "polite", children: [
12154
- months[m],
12155
- " ",
12156
- y
12157
- ] }),
12158
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-drp__nav", "aria-label": "Next month", onClick: () => setView(new Date(y, m + 1, 1)), children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m9 18 6-6-6-6" }) }) })
12813
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-drp__nav", "aria-label": mode === "years" ? "Previous decade" : mode === "months" ? "Previous year" : "Previous month", onClick: () => setView(mode === "years" ? new Date(y - 10, m, 1) : mode === "months" ? new Date(y - 1, m, 1) : new Date(y, m - 1, 1)), children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m15 18-6-6 6-6" }) }) }),
12814
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-drp__title", "aria-label": "Switch calendar view", onClick: () => {
12815
+ if (mode === "days") {
12816
+ setFocusMonth(m);
12817
+ setMode("months");
12818
+ } else if (mode === "months") {
12819
+ setFocusYear(y);
12820
+ setMode("years");
12821
+ } else setMode("days");
12822
+ }, children: mode === "days" ? `${months[m]} ${y}` : mode === "months" ? y : `${decadeStart}\u2013${decadeStart + 9}` }),
12823
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-drp__sr", "aria-live": "polite", children: mode === "days" ? `${months[m]} ${y}` : mode === "months" ? y : `${decadeStart}\u2013${decadeStart + 9}` }),
12824
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-drp__nav", "aria-label": mode === "years" ? "Next decade" : mode === "months" ? "Next year" : "Next month", onClick: () => setView(mode === "years" ? new Date(y + 10, m, 1) : mode === "months" ? new Date(y + 1, m, 1) : new Date(y, m + 1, 1)), children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m9 18 6-6-6-6" }) }) })
12159
12825
  ] }),
12160
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-drp__grid", ref: gridRef, onKeyDown: onGridKeyDown, role: "grid", children: [
12826
+ mode === "days" ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-drp__grid", ref: gridRef, onKeyDown: onGridKeyDown, role: "grid", children: [
12161
12827
  dows.map((d, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-drp__dow", children: d }, i)),
12162
12828
  Array.from({ length: 42 }).map((_, i) => {
12163
12829
  const d = new Date(gridStart.getFullYear(), gridStart.getMonth(), gridStart.getDate() + i);
@@ -12182,7 +12848,53 @@ function DateRangePicker({
12182
12848
  i
12183
12849
  );
12184
12850
  })
12185
- ] })
12851
+ ] }) : mode === "months" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-drp__months", ref: gridRef, onKeyDown: onMonthsKeyDown, role: "grid", children: monthsShort.map((mo, i) => {
12852
+ const disabled2 = min && ymd(new Date(y, i + 1, 0)) < ymd(min) || max && ymd(new Date(y, i, 1)) > ymd(max);
12853
+ const sel = range2.start && range2.start.getFullYear() === y && range2.start.getMonth() === i || range2.end && range2.end.getFullYear() === y && range2.end.getMonth() === i;
12854
+ return /* @__PURE__ */ jsxRuntime.jsx(
12855
+ "button",
12856
+ {
12857
+ type: "button",
12858
+ className: "twc-drp__mo",
12859
+ role: "gridcell",
12860
+ "data-mo": i,
12861
+ disabled: disabled2 || void 0,
12862
+ "data-selected": sel || void 0,
12863
+ tabIndex: i === (focusMonth ?? m) ? 0 : -1,
12864
+ onClick: () => {
12865
+ setView(new Date(y, i, 1));
12866
+ setFocusDate(null);
12867
+ setMode("days");
12868
+ },
12869
+ children: mo
12870
+ },
12871
+ i
12872
+ );
12873
+ }) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-drp__years", ref: gridRef, onKeyDown: onYearsKeyDown, role: "grid", children: yearCells.map((yr) => {
12874
+ const outside = yr < decadeStart || yr > decadeStart + 9;
12875
+ const disabled2 = min && ymd(new Date(yr, 11, 31)) < ymd(min) || max && ymd(new Date(yr, 0, 1)) > ymd(max);
12876
+ const sel = range2.start && range2.start.getFullYear() === yr || range2.end && range2.end.getFullYear() === yr;
12877
+ return /* @__PURE__ */ jsxRuntime.jsx(
12878
+ "button",
12879
+ {
12880
+ type: "button",
12881
+ className: "twc-drp__yr",
12882
+ role: "gridcell",
12883
+ "data-yr": yr,
12884
+ "data-outside": outside || void 0,
12885
+ disabled: disabled2 || void 0,
12886
+ "data-selected": sel || void 0,
12887
+ tabIndex: yr === (focusYear ?? y) ? 0 : -1,
12888
+ onClick: () => {
12889
+ setView(new Date(yr, m, 1));
12890
+ setFocusMonth(m);
12891
+ setMode("months");
12892
+ },
12893
+ children: yr
12894
+ },
12895
+ yr
12896
+ );
12897
+ }) })
12186
12898
  ] })
12187
12899
  ]
12188
12900
  }
@@ -12192,6 +12904,776 @@ function DateRangePicker({
12192
12904
  error ? /* @__PURE__ */ jsxRuntime.jsx("span", { id: descId, className: "twc-field__error", children: error }) : hint ? /* @__PURE__ */ jsxRuntime.jsx("span", { id: descId, className: "twc-field__hint", children: hint }) : null
12193
12905
  ] });
12194
12906
  }
12907
+ var FIELD_CSS5 = `
12908
+ .twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
12909
+ .twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
12910
+ .twc-field__req { color: var(--color-danger); }
12911
+ .twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
12912
+ .twc-field__error { font-size: var(--text-xs); color: var(--color-danger-subtle-fg); font-weight: var(--font-medium); }
12913
+ `;
12914
+ var TIMEPICKER_CSS = `
12915
+ .twc-tp { position: relative; font-family: var(--font-sans); display: flex; flex-direction: column; gap: var(--space-1-5); }
12916
+ .twc-tp__control {
12917
+ display: flex; align-items: center; gap: var(--space-2); --_h: var(--control-h-md); height: var(--_h); padding: 0 var(--space-3);
12918
+ background: var(--color-surface); border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-md);
12919
+ cursor: pointer; transition: border-color var(--duration-fast) var(--ease-standard), box-shadow var(--duration-fast) var(--ease-standard);
12920
+ }
12921
+ .twc-tp__control:hover:not([data-open="true"]) { border-color: var(--color-border-strong); }
12922
+ .twc-tp__control { --_accent: var(--color-primary); --_ring: var(--ring); }
12923
+ .twc-tp__control[data-size="sm"] { --_h: var(--control-h-sm); }
12924
+ .twc-tp__control[data-size="lg"] { --_h: var(--control-h-lg); }
12925
+ .twc-tp__control[data-tone="success"] { --_accent: var(--color-success); --_ring: 0 0 0 var(--ring-width) color-mix(in srgb, var(--color-success) 32%, transparent); }
12926
+ .twc-tp__control[data-tone="warning"] { --_accent: var(--color-warning); --_ring: 0 0 0 var(--ring-width) color-mix(in srgb, var(--color-warning) 32%, transparent); }
12927
+ .twc-tp__control[data-tone="danger"] { --_accent: var(--color-danger); --_ring: 0 0 0 var(--ring-width) color-mix(in srgb, var(--color-danger) 32%, transparent); }
12928
+ .twc-tp__control[data-tone="info"] { --_accent: var(--color-info); --_ring: 0 0 0 var(--ring-width) color-mix(in srgb, var(--color-info) 32%, transparent); }
12929
+ .twc-tp__control[data-tone="neutral"] { --_accent: var(--color-border-strong); --_ring: 0 0 0 var(--ring-width) color-mix(in srgb, var(--color-text) 14%, transparent); }
12930
+ .twc-tp__control[data-open="true"] { border-color: var(--_accent); box-shadow: var(--_ring); }
12931
+ .twc-tp__control[data-invalid="true"] { border-color: var(--color-danger); }
12932
+ .twc-tp__control[data-invalid="true"][data-open="true"] { box-shadow: 0 0 0 var(--ring-width) color-mix(in srgb, var(--color-danger) 28%, transparent); }
12933
+ .twc-tp__control[data-disabled="true"] { background: var(--color-surface-sunken); opacity: 0.7; cursor: not-allowed; }
12934
+ .twc-tp__field { position: relative; display: flex; flex-direction: column; }
12935
+ .twc-tp__control[data-has-clear="true"] { padding-inline-end: 34px; }
12936
+ .twc-tp__ic { flex: none; color: var(--color-text-subtle); display: inline-flex; }
12937
+ .twc-tp__ic svg { width: 17px; height: 17px; }
12938
+ .twc-tp__text { flex: 1; font-size: var(--text-sm); color: var(--color-text); font-variant-numeric: tabular-nums; }
12939
+ .twc-tp__text[data-placeholder="true"] { color: var(--color-text-subtle); font-variant-numeric: normal; }
12940
+ .twc-tp__clear { position: absolute; inset-inline-end: 10px; top: 50%; transform: translateY(-50%); display: inline-grid; place-items: center; width: 20px; height: 20px; border: none; background: transparent; color: var(--color-text-subtle); cursor: pointer; border-radius: var(--radius-full); }
12941
+ .twc-tp__clear:hover { background: var(--color-surface-sunken); color: var(--color-text); }
12942
+ .twc-tp__clear svg { width: 14px; height: 14px; }
12943
+ .twc-tp__control--editable { cursor: text; gap: 0; padding-inline-end: var(--space-1); }
12944
+ .twc-tp__input { flex: 1; min-width: 0; height: 100%; border: none; background: transparent; outline: none; padding: 0; font-family: inherit; font-size: var(--text-sm); color: var(--color-text); font-variant-numeric: tabular-nums; }
12945
+ .twc-tp__input::placeholder { color: var(--color-text-subtle); font-variant-numeric: normal; }
12946
+ .twc-tp__input:disabled { cursor: not-allowed; }
12947
+ .twc-tp__clear--inline { position: static; transform: none; flex: none; }
12948
+ .twc-tp__toggle { flex: none; display: inline-grid; place-items: center; width: 26px; height: 26px; border: none; background: transparent; color: var(--color-text-subtle); cursor: pointer; border-radius: var(--radius-md); transition: background-color var(--duration-fast), color var(--duration-fast); }
12949
+ .twc-tp__toggle:hover:not(:disabled) { background: var(--color-surface-sunken); color: var(--color-text); }
12950
+ .twc-tp__toggle:disabled { cursor: not-allowed; }
12951
+ .twc-tp__toggle svg { width: 17px; height: 17px; }
12952
+
12953
+ .twc-tp__pop {
12954
+ position: fixed; z-index: var(--z-popover);
12955
+ background: var(--color-surface-raised); border: var(--border-thin) solid var(--color-border);
12956
+ border-radius: var(--radius-lg); box-shadow: var(--shadow-lg); padding: var(--space-2);
12957
+ animation: twico-scale-in var(--duration-fast) var(--ease-spring); transform-origin: top;
12958
+ }
12959
+ .twc-tp__cols { display: flex; gap: var(--space-1); max-height: 232px; }
12960
+ .twc-tp__col { display: flex; flex-direction: column; gap: 2px; overflow-y: auto; scrollbar-width: thin; padding-inline: 2px; scroll-behavior: smooth; }
12961
+ .twc-tp__col::-webkit-scrollbar { width: 6px; }
12962
+ .twc-tp__col::-webkit-scrollbar-thumb { background: var(--color-border-strong); border-radius: var(--radius-full); }
12963
+ .twc-tp__col + .twc-tp__col { border-inline-start: var(--border-thin) solid var(--color-divider); }
12964
+ .twc-tp__colhead { text-align: center; font-size: 11px; font-weight: var(--font-bold); color: var(--color-text-subtle); text-transform: uppercase; padding: 2px 0 4px; position: sticky; top: 0; background: var(--color-surface-raised); }
12965
+ .twc-tp__opt { min-width: 46px; padding: 6px 10px; border: none; background: transparent; cursor: pointer; font-family: inherit; font-size: var(--text-sm); color: var(--color-text); border-radius: var(--radius-md); text-align: center; font-variant-numeric: tabular-nums; transition: background-color var(--duration-fast) var(--ease-standard), color var(--duration-fast) var(--ease-standard); }
12966
+ .twc-tp__opt:hover:not(:disabled):not([data-selected="true"]) { background: var(--color-surface-sunken); }
12967
+ .twc-tp__opt[data-selected="true"] { background: var(--color-primary); color: var(--color-primary-fg); font-weight: var(--font-bold); }
12968
+ .twc-tp__opt:disabled { opacity: 0.35; cursor: not-allowed; }
12969
+ .twc-tp__opt:focus-visible { outline: none; box-shadow: var(--ring); }
12970
+ `;
12971
+ var pad = (n) => String(n).padStart(2, "0");
12972
+ var range = (start, end, step) => {
12973
+ const out = [];
12974
+ for (let i = start; i < end; i += step) out.push(i);
12975
+ return out;
12976
+ };
12977
+ var tod = (h, m, s) => h * 3600 + m * 60 + s;
12978
+ var to24 = (dh, mer) => mer === "PM" ? dh === 12 ? 12 : dh + 12 : dh === 12 ? 0 : dh;
12979
+ function TimePicker({
12980
+ label,
12981
+ hint,
12982
+ error,
12983
+ required = false,
12984
+ value,
12985
+ defaultValue = null,
12986
+ placeholder = "Select time",
12987
+ min,
12988
+ max,
12989
+ minuteStep = 5,
12990
+ secondStep = 5,
12991
+ granularity = "minute",
12992
+ // "hour" | "minute" | "second"
12993
+ hourCycle = 24,
12994
+ // 24 | 12
12995
+ referenceDate,
12996
+ disabled = false,
12997
+ tone = "primary",
12998
+ size = "md",
12999
+ clearable = true,
13000
+ editable = false,
13001
+ parse,
13002
+ format,
13003
+ locale,
13004
+ onChange,
13005
+ id,
13006
+ className = "",
13007
+ ...rest
13008
+ }) {
13009
+ const __twcStyles0 = useScopedStyles("twc-field-styles", FIELD_CSS5);
13010
+ const __twcStyles1 = useScopedStyles("twc-timepicker-styles", TIMEPICKER_CSS);
13011
+ const autoId = React51__namespace.default.useId();
13012
+ const fieldId = id || autoId;
13013
+ const descId = `${fieldId}-desc`;
13014
+ const invalid = Boolean(error);
13015
+ const [internal, setInternal] = React51__namespace.default.useState(defaultValue);
13016
+ const selected = value !== void 0 ? value : internal;
13017
+ const [open, setOpen] = React51__namespace.default.useState(false);
13018
+ const [editText, setEditText] = React51__namespace.default.useState(null);
13019
+ const [coords, setCoords] = React51__namespace.default.useState(null);
13020
+ const wrapRef = React51__namespace.default.useRef(null);
13021
+ const triggerRef = React51__namespace.default.useRef(null);
13022
+ const popRef = React51__namespace.default.useRef(null);
13023
+ const place = React51__namespace.default.useCallback(() => {
13024
+ const el = triggerRef.current;
13025
+ if (!el) return;
13026
+ const r2 = el.getBoundingClientRect();
13027
+ const vh = window.innerHeight, POP_W = 220;
13028
+ const below = vh - r2.bottom;
13029
+ const flip = below < 300 && r2.top > below;
13030
+ let left = Math.min(r2.left, window.innerWidth - POP_W - 8);
13031
+ left = Math.max(8, left);
13032
+ setCoords({ left, top: flip ? void 0 : Math.round(r2.bottom + 6), bottom: flip ? Math.round(vh - r2.top + 6) : void 0 });
13033
+ }, []);
13034
+ React51__namespace.default.useEffect(() => {
13035
+ if (!open) return void 0;
13036
+ place();
13037
+ const onMove = () => place();
13038
+ window.addEventListener("scroll", onMove, true);
13039
+ window.addEventListener("resize", onMove);
13040
+ return () => {
13041
+ window.removeEventListener("scroll", onMove, true);
13042
+ window.removeEventListener("resize", onMove);
13043
+ };
13044
+ }, [open, place]);
13045
+ React51__namespace.default.useEffect(() => {
13046
+ if (!open) return void 0;
13047
+ const onDown = (e) => {
13048
+ if (wrapRef.current && wrapRef.current.contains(e.target)) return;
13049
+ if (popRef.current && popRef.current.contains(e.target)) return;
13050
+ setOpen(false);
13051
+ };
13052
+ const onKey = (e) => {
13053
+ if (e.key === "Escape") setOpen(false);
13054
+ };
13055
+ document.addEventListener("mousedown", onDown);
13056
+ document.addEventListener("keydown", onKey);
13057
+ return () => {
13058
+ document.removeEventListener("mousedown", onDown);
13059
+ document.removeEventListener("keydown", onKey);
13060
+ };
13061
+ }, [open]);
13062
+ useFocusTrap(popRef, open && !!coords, {
13063
+ initialFocus: () => popRef.current?.querySelector('.twc-tp__opt[data-selected="true"]') || popRef.current?.querySelector('.twc-tp__opt[tabindex="0"]')
13064
+ });
13065
+ React51__namespace.default.useEffect(() => {
13066
+ if (!open || !coords) return;
13067
+ popRef.current?.querySelectorAll('.twc-tp__opt[data-selected="true"]').forEach((el) => el.scrollIntoView({ block: "center" }));
13068
+ }, [open, coords]);
13069
+ const refBase = referenceDate || /* @__PURE__ */ new Date();
13070
+ const minTod = min ? tod(min.getHours(), min.getMinutes(), min.getSeconds()) : null;
13071
+ const maxTod = max ? tod(max.getHours(), max.getMinutes(), max.getSeconds()) : null;
13072
+ const outOfRange = (h, m, s) => minTod != null && tod(h, m, s) < minTod || maxTod != null && tod(h, m, s) > maxTod;
13073
+ const curH = selected ? selected.getHours() : null;
13074
+ const curM = selected ? selected.getMinutes() : null;
13075
+ const curS = selected ? selected.getSeconds() : null;
13076
+ const curMer = curH == null ? "AM" : curH < 12 ? "AM" : "PM";
13077
+ const fmt = (d) => {
13078
+ if (!d) return "";
13079
+ if (format) return format(d);
13080
+ const opts = { hour: hourCycle === 12 ? "numeric" : "2-digit", minute: "2-digit", hour12: hourCycle === 12 };
13081
+ if (granularity === "second") opts.second = "2-digit";
13082
+ if (granularity === "hour") delete opts.minute;
13083
+ return d.toLocaleTimeString(locale, opts);
13084
+ };
13085
+ const commit = (h, m, s) => {
13086
+ const b = selected || refBase;
13087
+ const d = new Date(b.getFullYear(), b.getMonth(), b.getDate(), h, m, s, 0);
13088
+ if (value === void 0) setInternal(d);
13089
+ onChange?.(d);
13090
+ setEditText(null);
13091
+ };
13092
+ const pickHour = (h) => commit(h, granularity === "hour" ? 0 : curM ?? 0, granularity === "second" ? curS ?? 0 : 0);
13093
+ const pickMinute = (m) => commit(curH ?? refBase.getHours(), m, granularity === "second" ? curS ?? 0 : 0);
13094
+ const pickSecond = (s) => commit(curH ?? refBase.getHours(), curM ?? 0, s);
13095
+ const pickMeridiem = (mer) => {
13096
+ const dh = curH == null ? 0 : curH % 12 === 0 ? 12 : curH % 12;
13097
+ commit(to24(dh, mer), curM ?? 0, granularity === "second" ? curS ?? 0 : 0);
13098
+ };
13099
+ const clear = () => {
13100
+ if (value === void 0) setInternal(null);
13101
+ onChange?.(null);
13102
+ setEditText(null);
13103
+ };
13104
+ const parseTyped = (str) => {
13105
+ const s = str.trim();
13106
+ if (s === "") return null;
13107
+ if (parse) {
13108
+ const d = parse(s);
13109
+ return d instanceof Date && !Number.isNaN(d.getTime()) ? d : void 0;
13110
+ }
13111
+ const mm = s.match(/^(\d{1,2}):(\d{2})(?::(\d{2}))?\s*([ap]m)?$/i);
13112
+ if (!mm) return void 0;
13113
+ let h = +mm[1];
13114
+ const mi = +mm[2];
13115
+ const se = mm[3] ? +mm[3] : 0;
13116
+ const mer = mm[4] && mm[4].toLowerCase();
13117
+ if (mer === "pm" && h < 12) h += 12;
13118
+ else if (mer === "am" && h === 12) h = 0;
13119
+ if (h > 23 || mi > 59 || se > 59) return void 0;
13120
+ const b = selected || refBase;
13121
+ return new Date(b.getFullYear(), b.getMonth(), b.getDate(), h, mi, se, 0);
13122
+ };
13123
+ const commitText = () => {
13124
+ if (editText == null) return;
13125
+ const parsed = parseTyped(editText);
13126
+ if (parsed === null) {
13127
+ setEditText(null);
13128
+ if (selected) clear();
13129
+ return;
13130
+ }
13131
+ if (parsed === void 0 || outOfRange(parsed.getHours(), parsed.getMinutes(), parsed.getSeconds())) {
13132
+ setEditText(null);
13133
+ return;
13134
+ }
13135
+ commit(parsed.getHours(), parsed.getMinutes(), parsed.getSeconds());
13136
+ setOpen(false);
13137
+ };
13138
+ const hourVals = hourCycle === 12 ? [12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] : range(0, 24, 1);
13139
+ const columns = [
13140
+ {
13141
+ key: "h",
13142
+ head: "hr",
13143
+ aria: "Hour",
13144
+ items: hourVals.map((hv) => {
13145
+ const h24 = hourCycle === 12 ? to24(hv, curMer) : hv;
13146
+ return {
13147
+ val: hv,
13148
+ text: hourCycle === 12 ? String(hv) : pad(hv),
13149
+ selected: curH != null && h24 === curH,
13150
+ disabled: outOfRange(h24, 0, 0) && outOfRange(h24, 59, 59),
13151
+ onSelect: () => pickHour(h24)
13152
+ };
13153
+ })
13154
+ }
13155
+ ];
13156
+ if (granularity !== "hour") {
13157
+ const h = curH ?? refBase.getHours();
13158
+ columns.push({ key: "m", head: "min", aria: "Minute", items: range(0, 60, minuteStep).map((mv) => ({
13159
+ val: mv,
13160
+ text: pad(mv),
13161
+ selected: curM != null && mv === curM,
13162
+ disabled: outOfRange(h, mv, 0) && outOfRange(h, mv, 59),
13163
+ onSelect: () => pickMinute(mv)
13164
+ })) });
13165
+ }
13166
+ if (granularity === "second") {
13167
+ const h = curH ?? refBase.getHours(), m = curM ?? 0;
13168
+ columns.push({ key: "s", head: "sec", aria: "Second", items: range(0, 60, secondStep).map((sv) => ({
13169
+ val: sv,
13170
+ text: pad(sv),
13171
+ selected: curS != null && sv === curS,
13172
+ disabled: outOfRange(h, m, sv),
13173
+ onSelect: () => pickSecond(sv)
13174
+ })) });
13175
+ }
13176
+ if (hourCycle === 12) {
13177
+ columns.push({ key: "mer", head: "", aria: "AM or PM", items: ["AM", "PM"].map((mer) => ({
13178
+ val: mer,
13179
+ text: mer,
13180
+ selected: curH != null && curMer === mer,
13181
+ disabled: [...Array(12)].every((_, i) => {
13182
+ const hh = mer === "AM" ? i : i + 12;
13183
+ return outOfRange(hh, 0, 0) && outOfRange(hh, 59, 59);
13184
+ }),
13185
+ onSelect: () => pickMeridiem(mer)
13186
+ })) });
13187
+ }
13188
+ const onOptKey = (e) => {
13189
+ const btn = e.target;
13190
+ const col = btn.closest("[data-col]");
13191
+ if (!col) return;
13192
+ const opts = Array.from(col.querySelectorAll(".twc-tp__opt:not(:disabled)"));
13193
+ const i = opts.indexOf(btn);
13194
+ let target;
13195
+ if (e.key === "ArrowDown") target = opts[Math.min(i + 1, opts.length - 1)];
13196
+ else if (e.key === "ArrowUp") target = opts[Math.max(i - 1, 0)];
13197
+ else if (e.key === "Home") target = opts[0];
13198
+ else if (e.key === "End") target = opts[opts.length - 1];
13199
+ else if (e.key === "ArrowRight" || e.key === "ArrowLeft") {
13200
+ const cols = Array.from(popRef.current.querySelectorAll("[data-col]"));
13201
+ const nextCol = cols[cols.indexOf(col) + (e.key === "ArrowRight" ? 1 : -1)];
13202
+ if (nextCol) {
13203
+ const nopts = Array.from(nextCol.querySelectorAll(".twc-tp__opt:not(:disabled)"));
13204
+ target = nextCol.querySelector('.twc-tp__opt[data-selected="true"]') || nopts[Math.min(i, nopts.length - 1)] || nopts[0];
13205
+ }
13206
+ } else return;
13207
+ e.preventDefault();
13208
+ target?.focus();
13209
+ };
13210
+ const clockIcon = /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
13211
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "9" }),
13212
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 7v5l3 2" })
13213
+ ] });
13214
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-tp twc-field ${className}`, ref: wrapRef, ...rest, children: [
13215
+ __twcStyles0,
13216
+ __twcStyles1,
13217
+ label ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-field__label", id: `${fieldId}-label`, children: [
13218
+ label,
13219
+ required ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-field__req", children: "*" }) : null
13220
+ ] }) : null,
13221
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-tp__field", children: editable ? /* @__PURE__ */ jsxRuntime.jsxs(
13222
+ "div",
13223
+ {
13224
+ className: "twc-tp__control twc-tp__control--editable",
13225
+ "data-open": open || void 0,
13226
+ "data-disabled": disabled || void 0,
13227
+ "data-tone": tone,
13228
+ "data-invalid": invalid || void 0,
13229
+ "data-size": size,
13230
+ children: [
13231
+ /* @__PURE__ */ jsxRuntime.jsx(
13232
+ "input",
13233
+ {
13234
+ ref: triggerRef,
13235
+ className: "twc-tp__input",
13236
+ id: fieldId,
13237
+ type: "text",
13238
+ inputMode: "numeric",
13239
+ autoComplete: "off",
13240
+ disabled,
13241
+ placeholder,
13242
+ value: editText != null ? editText : selected ? fmt(selected) : "",
13243
+ "aria-haspopup": "dialog",
13244
+ "aria-expanded": open,
13245
+ "aria-labelledby": label ? `${fieldId}-label` : void 0,
13246
+ "aria-invalid": invalid || void 0,
13247
+ "aria-describedby": error || hint ? descId : void 0,
13248
+ onChange: (e) => setEditText(e.target.value),
13249
+ onKeyDown: (e) => {
13250
+ if (e.key === "Enter") {
13251
+ e.preventDefault();
13252
+ commitText();
13253
+ } else if (e.key === "ArrowDown" && (e.altKey || !open)) {
13254
+ e.preventDefault();
13255
+ setOpen(true);
13256
+ } else if (e.key === "Escape" && open) setOpen(false);
13257
+ },
13258
+ onBlur: (e) => {
13259
+ if (popRef.current && popRef.current.contains(e.relatedTarget)) return;
13260
+ commitText();
13261
+ }
13262
+ }
13263
+ ),
13264
+ clearable && selected && !disabled ? /* @__PURE__ */ jsxRuntime.jsx(
13265
+ "button",
13266
+ {
13267
+ type: "button",
13268
+ className: "twc-tp__clear twc-tp__clear--inline",
13269
+ "aria-label": "Clear",
13270
+ tabIndex: -1,
13271
+ onMouseDown: (e) => e.preventDefault(),
13272
+ onClick: clear,
13273
+ children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.4", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 6 6 18M6 6l12 12" }) })
13274
+ }
13275
+ ) : null,
13276
+ /* @__PURE__ */ jsxRuntime.jsx(
13277
+ "button",
13278
+ {
13279
+ type: "button",
13280
+ className: "twc-tp__toggle",
13281
+ "aria-label": "Open time list",
13282
+ tabIndex: -1,
13283
+ disabled,
13284
+ onClick: () => !disabled && setOpen((o) => !o),
13285
+ children: clockIcon
13286
+ }
13287
+ )
13288
+ ]
13289
+ }
13290
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
13291
+ /* @__PURE__ */ jsxRuntime.jsxs(
13292
+ "div",
13293
+ {
13294
+ ref: triggerRef,
13295
+ className: "twc-tp__control",
13296
+ id: fieldId,
13297
+ "data-open": open || void 0,
13298
+ "data-disabled": disabled || void 0,
13299
+ "data-size": size,
13300
+ "data-tone": tone,
13301
+ "data-has-clear": clearable && selected && !disabled ? "true" : void 0,
13302
+ "data-invalid": invalid || void 0,
13303
+ role: "button",
13304
+ tabIndex: disabled ? -1 : 0,
13305
+ "aria-haspopup": "dialog",
13306
+ "aria-expanded": open,
13307
+ "aria-labelledby": label ? `${fieldId}-label` : void 0,
13308
+ "aria-invalid": invalid || void 0,
13309
+ "aria-describedby": error || hint ? descId : void 0,
13310
+ onClick: () => !disabled && setOpen((o) => !o),
13311
+ onKeyDown: (e) => {
13312
+ if ((e.key === "Enter" || e.key === " ") && !disabled) {
13313
+ e.preventDefault();
13314
+ setOpen((o) => !o);
13315
+ }
13316
+ },
13317
+ children: [
13318
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-tp__ic", "aria-hidden": "true", children: clockIcon }),
13319
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-tp__text", "data-placeholder": !selected || void 0, children: selected ? fmt(selected) : placeholder })
13320
+ ]
13321
+ }
13322
+ ),
13323
+ clearable && selected && !disabled ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-tp__clear", "aria-label": "Clear", onClick: (e) => {
13324
+ e.stopPropagation();
13325
+ clear();
13326
+ }, children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.4", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 6 6 18M6 6l12 12" }) }) }) : null
13327
+ ] }) }),
13328
+ open && coords ? reactDom.createPortal(
13329
+ /* @__PURE__ */ jsxRuntime.jsx(
13330
+ "div",
13331
+ {
13332
+ className: "twc-tp__pop",
13333
+ ref: popRef,
13334
+ role: "dialog",
13335
+ "aria-modal": "true",
13336
+ "aria-label": "Choose time",
13337
+ style: { position: "fixed", left: coords.left, right: "auto", top: coords.top, bottom: coords.bottom, zIndex: "var(--z-tooltip)" },
13338
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-tp__cols", children: columns.map((col) => {
13339
+ const firstTab = col.items.find((it) => it.selected) || col.items.find((it) => !it.disabled);
13340
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-tp__col", role: "listbox", "aria-label": col.aria, "data-col": col.key, children: [
13341
+ col.head ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-tp__colhead", "aria-hidden": "true", children: col.head }) : null,
13342
+ col.items.map((it) => /* @__PURE__ */ jsxRuntime.jsx(
13343
+ "button",
13344
+ {
13345
+ type: "button",
13346
+ className: "twc-tp__opt",
13347
+ role: "option",
13348
+ "aria-selected": it.selected || false,
13349
+ "data-selected": it.selected || void 0,
13350
+ "data-val": it.val,
13351
+ disabled: it.disabled || void 0,
13352
+ tabIndex: it === firstTab ? 0 : -1,
13353
+ onClick: it.onSelect,
13354
+ onKeyDown: onOptKey,
13355
+ children: it.text
13356
+ },
13357
+ it.val
13358
+ ))
13359
+ ] }, col.key);
13360
+ }) })
13361
+ }
13362
+ ),
13363
+ document.body
13364
+ ) : null,
13365
+ error ? /* @__PURE__ */ jsxRuntime.jsx("span", { id: descId, className: "twc-field__error", children: error }) : hint ? /* @__PURE__ */ jsxRuntime.jsx("span", { id: descId, className: "twc-field__hint", children: hint }) : null
13366
+ ] });
13367
+ }
13368
+ var FIELD_CSS6 = `
13369
+ .twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
13370
+ .twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
13371
+ .twc-field__req { color: var(--color-danger); }
13372
+ .twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
13373
+ .twc-field__error { font-size: var(--text-xs); color: var(--color-danger-subtle-fg); font-weight: var(--font-medium); }
13374
+ `;
13375
+ var DTP_CSS = `
13376
+ .twc-dtp { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
13377
+ .twc-dtp__row { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: flex-start; }
13378
+ .twc-dtp__date { flex: 1 1 11rem; min-width: 9rem; }
13379
+ .twc-dtp__time { flex: 0 1 8rem; min-width: 7rem; }
13380
+ `;
13381
+ function DateTimePicker({
13382
+ label,
13383
+ hint,
13384
+ error,
13385
+ required = false,
13386
+ value,
13387
+ defaultValue = null,
13388
+ disabled = false,
13389
+ tone = "primary",
13390
+ size = "md",
13391
+ clearable = true,
13392
+ min,
13393
+ max,
13394
+ disabledDate,
13395
+ minuteStep = 5,
13396
+ secondStep = 5,
13397
+ granularity = "minute",
13398
+ hourCycle = 24,
13399
+ locale,
13400
+ weekStartsOn = 0,
13401
+ dateFormat,
13402
+ timeFormat,
13403
+ datePlaceholder = "Select date",
13404
+ timePlaceholder = "Time",
13405
+ onChange,
13406
+ id,
13407
+ className = "",
13408
+ ...rest
13409
+ }) {
13410
+ const __twcStyles0 = useScopedStyles("twc-field-styles", FIELD_CSS6);
13411
+ const __twcStyles1 = useScopedStyles("twc-datetimepicker-styles", DTP_CSS);
13412
+ const autoId = React51__namespace.default.useId();
13413
+ const fieldId = id || autoId;
13414
+ const descId = `${fieldId}-desc`;
13415
+ const [internal, setInternal] = React51__namespace.default.useState(defaultValue);
13416
+ const selected = value !== void 0 ? value : internal;
13417
+ const commitVal = (d) => {
13418
+ if (value === void 0) setInternal(d);
13419
+ onChange?.(d);
13420
+ };
13421
+ const onDateChange = (d) => {
13422
+ if (!d) return commitVal(null);
13423
+ const b = selected;
13424
+ commitVal(new Date(d.getFullYear(), d.getMonth(), d.getDate(), b ? b.getHours() : 0, b ? b.getMinutes() : 0, b ? b.getSeconds() : 0, 0));
13425
+ };
13426
+ const onTimeChange = (t) => {
13427
+ if (!t) return;
13428
+ const b = selected;
13429
+ commitVal(new Date(b ? b.getFullYear() : t.getFullYear(), b ? b.getMonth() : t.getMonth(), b ? b.getDate() : t.getDate(), t.getHours(), t.getMinutes(), t.getSeconds(), 0));
13430
+ };
13431
+ const childTone = error ? "danger" : tone;
13432
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-dtp twc-field ${className}`, id: fieldId, ...rest, children: [
13433
+ __twcStyles0,
13434
+ __twcStyles1,
13435
+ label ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-field__label", id: `${fieldId}-label`, children: [
13436
+ label,
13437
+ required ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-field__req", children: "*" }) : null
13438
+ ] }) : null,
13439
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dtp__row", role: "group", "aria-labelledby": label ? `${fieldId}-label` : void 0, "aria-describedby": error || hint ? descId : void 0, children: [
13440
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-dtp__date", children: /* @__PURE__ */ jsxRuntime.jsx(
13441
+ DatePicker,
13442
+ {
13443
+ value: selected,
13444
+ onChange: onDateChange,
13445
+ disabled,
13446
+ tone: childTone,
13447
+ size,
13448
+ clearable,
13449
+ min,
13450
+ max,
13451
+ disabledDate,
13452
+ locale,
13453
+ weekStartsOn,
13454
+ format: dateFormat,
13455
+ placeholder: datePlaceholder,
13456
+ "aria-label": "Date"
13457
+ }
13458
+ ) }),
13459
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-dtp__time", children: /* @__PURE__ */ jsxRuntime.jsx(
13460
+ TimePicker,
13461
+ {
13462
+ value: selected,
13463
+ onChange: onTimeChange,
13464
+ disabled,
13465
+ tone: childTone,
13466
+ size,
13467
+ clearable: false,
13468
+ minuteStep,
13469
+ secondStep,
13470
+ granularity,
13471
+ hourCycle,
13472
+ locale,
13473
+ referenceDate: selected || void 0,
13474
+ format: timeFormat,
13475
+ placeholder: timePlaceholder,
13476
+ "aria-label": "Time"
13477
+ }
13478
+ ) })
13479
+ ] }),
13480
+ error ? /* @__PURE__ */ jsxRuntime.jsx("span", { id: descId, className: "twc-field__error", children: error }) : hint ? /* @__PURE__ */ jsxRuntime.jsx("span", { id: descId, className: "twc-field__hint", children: hint }) : null
13481
+ ] });
13482
+ }
13483
+ var FILTERBAR_CSS = `
13484
+ .twc-fbar { font-family: var(--font-sans); display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--space-3); }
13485
+ .twc-fbar__field { display: flex; flex-direction: column; min-width: 170px; }
13486
+ .twc-fbar__field[data-type="search"], .twc-fbar__field[data-type="text"] { flex: 1 1 200px; }
13487
+ .twc-fbar__field[data-type="daterange"] { min-width: 220px; }
13488
+ .twc-fbar__field[data-type="boolean"], .twc-fbar__field[data-type="number"] { min-width: 130px; }
13489
+ .twc-fbar__labelrow { display: flex; align-items: center; gap: 6px; min-height: 18px; margin-bottom: var(--space-1); }
13490
+ .twc-fbar__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); }
13491
+ .twc-fbar__clearone { display: inline-grid; place-items: center; width: 16px; height: 16px; border: none; background: transparent; color: var(--color-text-subtle); cursor: pointer; border-radius: var(--radius-full); padding: 0; }
13492
+ .twc-fbar__clearone:hover { background: var(--color-surface-sunken); color: var(--color-text); }
13493
+ .twc-fbar__clearone svg { width: 11px; height: 11px; }
13494
+ .twc-fbar__actions { display: flex; align-items: center; gap: var(--space-2); }
13495
+ .twc-fbar__clearall { display: inline-flex; align-items: center; gap: 6px; height: var(--control-h-md); padding: 0 var(--space-3); border: var(--border-thin) solid var(--color-border); background: var(--color-surface); color: var(--color-text-muted); border-radius: var(--radius-md); cursor: pointer; font-family: inherit; font-size: var(--text-sm); font-weight: var(--font-medium); transition: background-color var(--duration-fast), color var(--duration-fast), border-color var(--duration-fast); }
13496
+ .twc-fbar__clearall:hover:not(:disabled) { background: var(--color-surface-sunken); color: var(--color-text); border-color: var(--color-border-strong); }
13497
+ .twc-fbar__clearall:disabled { opacity: 0.5; cursor: not-allowed; }
13498
+ .twc-fbar__clearall svg { width: 15px; height: 15px; }
13499
+ `;
13500
+ var isEmpty = (f, v) => {
13501
+ if (v == null) return true;
13502
+ if (f.type === "multiselect" || f.type === "select-multiple") return !Array.isArray(v) || v.length === 0;
13503
+ if (f.type === "daterange") return v.start == null && v.end == null;
13504
+ return v === "";
13505
+ };
13506
+ var emptyFor = (f) => f.type === "multiselect" ? [] : f.type === "daterange" ? { start: null, end: null } : void 0;
13507
+ function valuesToClauses(fields, values) {
13508
+ const out = [];
13509
+ for (const f of fields) {
13510
+ const v = values[f.field];
13511
+ if (isEmpty(f, v)) continue;
13512
+ if (f.type === "multiselect") out.push({ field: f.field, op: "isAnyOf", value: v });
13513
+ else if (f.type === "daterange") {
13514
+ if (v.start != null) out.push({ field: f.field, op: ">=", value: v.start });
13515
+ if (v.end != null) out.push({ field: f.field, op: "<", value: v.end });
13516
+ } else if (f.type === "search" || f.type === "text") out.push({ field: f.field, op: f.op || "contains", value: v });
13517
+ else if (f.type === "number") out.push({ field: f.field, op: f.op || "=", value: v });
13518
+ else if (f.type === "boolean") out.push({ field: f.field, op: "=", value: v });
13519
+ else out.push({ field: f.field, op: f.op || "=", value: v });
13520
+ }
13521
+ return out;
13522
+ }
13523
+ function clausesToValues(fields, clauses) {
13524
+ const v = {};
13525
+ for (const f of fields) v[f.field] = emptyFor(f);
13526
+ for (const c of clauses || []) {
13527
+ const f = fields.find((x) => x.field === c.field);
13528
+ if (!f) continue;
13529
+ if (f.type === "daterange") {
13530
+ v[f.field] = { ...v[f.field] || { start: null, end: null } };
13531
+ if (c.op === ">=" || c.op === ">") v[f.field].start = c.value;
13532
+ else if (c.op === "<" || c.op === "<=") v[f.field].end = c.value;
13533
+ } else if (f.type === "multiselect") {
13534
+ v[f.field] = Array.isArray(c.value) ? c.value : [];
13535
+ } else {
13536
+ v[f.field] = c.value;
13537
+ }
13538
+ }
13539
+ return v;
13540
+ }
13541
+ var xIcon = /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.4", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 6 6 18M6 6l12 12" }) });
13542
+ function FilterBar({
13543
+ fields = [],
13544
+ value,
13545
+ defaultValue = [],
13546
+ onChange,
13547
+ onValuesChange,
13548
+ showClearAll = false,
13549
+ clearAllLabel = "Clear all",
13550
+ showFieldClear = true,
13551
+ size = "md",
13552
+ tone = "primary",
13553
+ disabled = false,
13554
+ id,
13555
+ className = "",
13556
+ ...rest
13557
+ }) {
13558
+ const styles = useScopedStyles("twc-filterbar-styles", FILTERBAR_CSS);
13559
+ const autoId = React51__namespace.default.useId();
13560
+ const fieldId = id || autoId;
13561
+ const [internal, setInternal] = React51__namespace.default.useState(defaultValue);
13562
+ const clauses = value !== void 0 ? value : internal;
13563
+ const values = React51__namespace.default.useMemo(() => clausesToValues(fields, clauses), [fields, clauses]);
13564
+ const emit = (nextValues) => {
13565
+ const next = valuesToClauses(fields, nextValues);
13566
+ if (value === void 0) setInternal(next);
13567
+ onChange?.(next);
13568
+ onValuesChange?.(nextValues);
13569
+ };
13570
+ const setField = (f, v) => emit({ ...values, [f.field]: v });
13571
+ const clearField = (f) => setField(f, emptyFor(f));
13572
+ const clearAll = () => {
13573
+ const cleared = {};
13574
+ for (const f of fields) cleared[f.field] = emptyFor(f);
13575
+ emit(cleared);
13576
+ };
13577
+ const activeCount = fields.reduce((n, f) => n + (isEmpty(f, values[f.field]) ? 0 : 1), 0);
13578
+ const control = (f) => {
13579
+ const v = values[f.field];
13580
+ const name = f.label || f.field;
13581
+ const opts = typeof f.options === "function" ? f.options(values) : f.options;
13582
+ const common = { size, disabled: disabled || f.disabled, "aria-label": name };
13583
+ if (f.type === "multiselect") {
13584
+ return /* @__PURE__ */ jsxRuntime.jsx(
13585
+ MultiSelect,
13586
+ {
13587
+ ...common,
13588
+ options: opts || [],
13589
+ value: v || [],
13590
+ placeholder: f.placeholder || "Any",
13591
+ clearable: true,
13592
+ onChange: (next) => setField(f, next)
13593
+ }
13594
+ );
13595
+ }
13596
+ if (f.type === "daterange") {
13597
+ return /* @__PURE__ */ jsxRuntime.jsx(
13598
+ DateRangePicker,
13599
+ {
13600
+ ...common,
13601
+ tone,
13602
+ value: v || { start: null, end: null },
13603
+ clearable: true,
13604
+ onChange: (range2) => setField(f, range2)
13605
+ }
13606
+ );
13607
+ }
13608
+ if (f.type === "boolean") {
13609
+ const boolOpts = [{ value: "", label: f.anyLabel || "Any" }, { value: "true", label: f.trueLabel || "Yes" }, { value: "false", label: f.falseLabel || "No" }];
13610
+ return /* @__PURE__ */ jsxRuntime.jsx(
13611
+ Select,
13612
+ {
13613
+ ...common,
13614
+ searchable: false,
13615
+ options: boolOpts,
13616
+ value: v == null ? "" : String(v),
13617
+ onChange: (next) => setField(f, next === "" ? void 0 : next === "true")
13618
+ }
13619
+ );
13620
+ }
13621
+ if (f.type === "select") {
13622
+ return /* @__PURE__ */ jsxRuntime.jsx(
13623
+ Select,
13624
+ {
13625
+ ...common,
13626
+ searchable: f.searchable,
13627
+ options: opts || [],
13628
+ value: v ?? "",
13629
+ placeholder: f.placeholder || "Any",
13630
+ portal: f.portal,
13631
+ onChange: (next) => setField(f, next === "" ? void 0 : next)
13632
+ }
13633
+ );
13634
+ }
13635
+ if (f.type === "number") {
13636
+ return /* @__PURE__ */ jsxRuntime.jsx(
13637
+ Input,
13638
+ {
13639
+ ...common,
13640
+ type: "number",
13641
+ value: v ?? "",
13642
+ placeholder: f.placeholder,
13643
+ onChange: (e) => setField(f, e.target.value === "" ? void 0 : Number(e.target.value))
13644
+ }
13645
+ );
13646
+ }
13647
+ return /* @__PURE__ */ jsxRuntime.jsx(
13648
+ Input,
13649
+ {
13650
+ ...common,
13651
+ type: f.type === "search" ? "search" : "text",
13652
+ value: v ?? "",
13653
+ placeholder: f.placeholder || (f.type === "search" ? "Search\u2026" : ""),
13654
+ onChange: (e) => setField(f, e.target.value)
13655
+ }
13656
+ );
13657
+ };
13658
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-fbar ${className}`, id: fieldId, role: "group", "aria-label": "Filters", ...rest, children: [
13659
+ styles,
13660
+ fields.map((f) => {
13661
+ const active = !isEmpty(f, values[f.field]);
13662
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-fbar__field", "data-type": f.type, children: [
13663
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-fbar__labelrow", children: [
13664
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-fbar__label", children: f.label || f.field }),
13665
+ showFieldClear && active && !disabled ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-fbar__clearone", "aria-label": `Clear ${f.label || f.field}`, onClick: () => clearField(f), children: xIcon }) : null
13666
+ ] }),
13667
+ control(f)
13668
+ ] }, f.field);
13669
+ }),
13670
+ showClearAll ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-fbar__actions", children: /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", className: "twc-fbar__clearall", onClick: clearAll, disabled: disabled || activeCount === 0, children: [
13671
+ xIcon,
13672
+ clearAllLabel,
13673
+ activeCount > 0 ? ` (${activeCount})` : ""
13674
+ ] }) }) : null
13675
+ ] });
13676
+ }
12195
13677
  var DIALOG_CSS = `
12196
13678
  .twc-dialog__overlay {
12197
13679
  position: fixed; inset: 0; z-index: var(--z-modal);
@@ -12509,7 +13991,7 @@ function EmptyState({
12509
13991
  actions ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-empty__actions", children: actions }) : null
12510
13992
  ] });
12511
13993
  }
12512
- var FIELD_CSS5 = `
13994
+ var FIELD_CSS7 = `
12513
13995
  .twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
12514
13996
  .twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
12515
13997
  .twc-field__req { color: var(--color-danger); }
@@ -12528,7 +14010,7 @@ function Field({
12528
14010
  children,
12529
14011
  ...rest
12530
14012
  }) {
12531
- const __twcStyles = useScopedStyles("twc-field-styles", FIELD_CSS5);
14013
+ const __twcStyles = useScopedStyles("twc-field-styles", FIELD_CSS7);
12532
14014
  const autoId = React51__namespace.default.useId();
12533
14015
  const fieldId = id || autoId;
12534
14016
  const descId = `${fieldId}-desc`;
@@ -12549,6 +14031,8 @@ function Field({
12549
14031
  }
12550
14032
  var UPLOAD_CSS = `
12551
14033
  .twc-upload { font-family: var(--font-sans); display: flex; flex-direction: column; gap: var(--space-3); }
14034
+ .twc-upload[data-size="sm"] { --_pad-y: var(--space-5); --_pad-x: var(--space-4); }
14035
+ .twc-upload[data-size="lg"] { --_pad-y: var(--space-10); --_pad-x: var(--space-8); }
12552
14036
  .twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
12553
14037
  .twc-field__req { color: var(--color-danger); }
12554
14038
  .twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
@@ -12556,7 +14040,7 @@ var UPLOAD_CSS = `
12556
14040
  .twc-upload__zone[data-invalid="true"] { border-color: var(--color-danger); }
12557
14041
  .twc-upload__zone {
12558
14042
  display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center;
12559
- gap: 6px; padding: var(--space-8) var(--space-6); cursor: pointer;
14043
+ gap: 6px; padding: var(--_pad-y, var(--space-8)) var(--_pad-x, var(--space-6)); cursor: pointer;
12560
14044
  border: var(--border-medium) dashed var(--color-border-strong); border-radius: var(--radius-xl);
12561
14045
  background: var(--color-surface); color: var(--color-text-muted);
12562
14046
  transition: border-color var(--duration-fast) var(--ease-standard), background-color var(--duration-fast) var(--ease-standard);
@@ -12609,6 +14093,7 @@ function FileUpload({
12609
14093
  accept,
12610
14094
  multiple = false,
12611
14095
  disabled = false,
14096
+ size = "md",
12612
14097
  tone = "primary",
12613
14098
  label,
12614
14099
  hint,
@@ -12664,7 +14149,7 @@ function FileUpload({
12664
14149
  function remove(i) {
12665
14150
  set(files.filter((_, idx) => idx !== i));
12666
14151
  }
12667
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-upload ${className}`, ...rest, children: [
14152
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-upload ${className}`, "data-size": size, ...rest, children: [
12668
14153
  __twcStyles,
12669
14154
  label ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "twc-field__label", htmlFor: fieldId, children: [
12670
14155
  label,
@@ -13077,7 +14562,7 @@ button.twc-navbar__brand, a.twc-navbar__brand { cursor: pointer; }
13077
14562
  .twc-navbar__menu-btn { display: inline-grid; }
13078
14563
  }
13079
14564
  `;
13080
- function safeHref5(url) {
14565
+ function safeHref6(url) {
13081
14566
  if (url == null) return void 0;
13082
14567
  const s = String(url).replace(/[\x00-\x20]+/g, "").toLowerCase();
13083
14568
  return s.startsWith("javascript:") || s.startsWith("data:") || s.startsWith("vbscript:") ? void 0 : url;
@@ -13100,9 +14585,9 @@ function Navbar({
13100
14585
  return /* @__PURE__ */ jsxRuntime.jsxs("header", { className: `twc-navbar ${className}`, "data-sticky": sticky || void 0, ...rest, children: [
13101
14586
  __twcStyles,
13102
14587
  onMenuClick ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-navbar__menu-btn", "aria-expanded": menuOpen, "aria-controls": menuControls, "aria-label": menuOpen ? "Close menu" : "Open menu", onClick: onMenuClick, children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4 6h16M4 12h16M4 18h16" }) }) }) : null,
13103
- brand ? brandHref ? /* @__PURE__ */ jsxRuntime.jsx("a", { className: "twc-navbar__brand", href: safeHref5(brandHref), children: brand }) : onBrandClick ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-navbar__brand", onClick: onBrandClick, children: brand }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-navbar__brand", children: brand }) : null,
14588
+ brand ? brandHref ? /* @__PURE__ */ jsxRuntime.jsx("a", { className: "twc-navbar__brand", href: safeHref6(brandHref), children: brand }) : onBrandClick ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-navbar__brand", onClick: onBrandClick, children: brand }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-navbar__brand", children: brand }) : null,
13104
14589
  /* @__PURE__ */ jsxRuntime.jsx("nav", { className: "twc-navbar__links", "aria-label": navLabel, children: links.map((l, i) => {
13105
- const href = safeHref5(l.href);
14590
+ const href = safeHref6(l.href);
13106
14591
  const inner = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
13107
14592
  l.icon,
13108
14593
  l.label
@@ -13462,6 +14947,7 @@ function Radio({
13462
14947
  .twc-radio[data-invalid="true"] .twc-radio__dot { border-color: var(--color-danger); }
13463
14948
  .twc-radio__text { display: flex; flex-direction: column; gap: 2px; }
13464
14949
  .twc-radio__label { font-size: var(--text-sm); font-weight: var(--font-medium); color: var(--color-text); line-height: 1.3; }
14950
+ .twc-field__req { color: var(--color-danger); }
13465
14951
  .twc-radio__desc { font-size: var(--text-xs); color: var(--color-text-muted); }
13466
14952
  .twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
13467
14953
  .twc-field__error { font-size: var(--text-xs); color: var(--color-danger-subtle-fg); font-weight: var(--font-medium); }
@@ -13497,7 +14983,10 @@ function Radio({
13497
14983
  ),
13498
14984
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-radio__dot", "aria-hidden": "true" }),
13499
14985
  label || description ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "twc-radio__text", children: [
13500
- label ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-radio__label", children: label }) : null,
14986
+ label ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "twc-radio__label", children: [
14987
+ label,
14988
+ required ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-field__req", children: "*" }) : null
14989
+ ] }) : null,
13501
14990
  description ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-radio__desc", children: description }) : null
13502
14991
  ] }) : null
13503
14992
  ] });
@@ -14027,20 +15516,23 @@ var SLIDER_CSS = `
14027
15516
  .twc-slider__msgs { display: flex; flex-direction: column; gap: var(--space-1-5); margin-top: var(--space-2); }
14028
15517
  .twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
14029
15518
  .twc-field__error { font-size: var(--text-xs); color: var(--color-danger-subtle-fg); font-weight: var(--font-medium); }
15519
+ .twc-slider { --_track-h: 22px; --_thumb: 18px; --_rail: 6px; }
15520
+ .twc-slider[data-size="sm"] { --_track-h: 18px; --_thumb: 14px; --_rail: 4px; }
15521
+ .twc-slider[data-size="lg"] { --_track-h: 28px; --_thumb: 22px; --_rail: 8px; }
14030
15522
  .twc-slider__head { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: var(--space-2); }
14031
15523
  .twc-slider__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); }
14032
15524
  .twc-slider__value { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--_accent); font-variant-numeric: tabular-nums; }
14033
15525
  .twc-slider__track {
14034
- position: relative; height: 22px; display: flex; align-items: center; cursor: pointer; touch-action: none;
15526
+ position: relative; height: var(--_track-h); display: flex; align-items: center; cursor: pointer; touch-action: none;
14035
15527
  }
14036
15528
  .twc-slider[data-disabled="true"] { opacity: 0.55; }
14037
15529
  .twc-slider[data-disabled="true"] .twc-slider__track { cursor: not-allowed; }
14038
- .twc-slider__rail { position: absolute; left: 0; right: 0; height: 6px; border-radius: var(--radius-full); background: var(--color-surface-sunken); box-shadow: inset 0 0 0 1px var(--color-border); }
14039
- .twc-slider__fill { position: absolute; height: 6px; border-radius: var(--radius-full); background: var(--_accent); }
15530
+ .twc-slider__rail { position: absolute; left: 0; right: 0; height: var(--_rail); border-radius: var(--radius-full); background: var(--color-surface-sunken); box-shadow: inset 0 0 0 1px var(--color-border); }
15531
+ .twc-slider__fill { position: absolute; height: var(--_rail); border-radius: var(--radius-full); background: var(--_accent); }
14040
15532
  .twc-slider__thumb {
14041
- position: absolute; width: 18px; height: 18px; border-radius: var(--radius-full);
15533
+ position: absolute; width: var(--_thumb); height: var(--_thumb); border-radius: var(--radius-full);
14042
15534
  background: var(--color-surface); border: var(--border-thick) solid var(--_accent);
14043
- box-shadow: var(--shadow-sm); transform: translateX(-50%); top: 50%; margin-top: -9px;
15535
+ box-shadow: var(--shadow-sm); transform: translateX(-50%); top: 50%; margin-top: calc(var(--_thumb) / -2);
14044
15536
  transition: box-shadow var(--duration-fast) var(--ease-standard), transform var(--duration-fast) var(--ease-spring);
14045
15537
  }
14046
15538
  .twc-slider__thumb:focus-visible { outline: none; box-shadow: var(--ring); }
@@ -14068,7 +15560,8 @@ function Slider({
14068
15560
  precision,
14069
15561
  tone = "primary",
14070
15562
  disabled = false,
14071
- range = false,
15563
+ size = "md",
15564
+ range: range2 = false,
14072
15565
  showValue = true,
14073
15566
  showTicks = false,
14074
15567
  formatValue,
@@ -14096,7 +15589,7 @@ function Slider({
14096
15589
  const __twcStyles = useScopedStyles("twc-slider-styles", SLIDER_CSS);
14097
15590
  const autoId = React51__namespace.default.useId();
14098
15591
  const fieldId = id || autoId;
14099
- const isRange = range || Array.isArray(value) || Array.isArray(defaultValue);
15592
+ const isRange = range2 || Array.isArray(value) || Array.isArray(defaultValue);
14100
15593
  const [internal, setInternal] = React51__namespace.default.useState(() => {
14101
15594
  if (isRange) {
14102
15595
  const dv = Array.isArray(defaultValue) ? defaultValue : [min, max];
@@ -14184,7 +15677,7 @@ function Slider({
14184
15677
  const fillLo = (Math.min(...clampedVals) - min) / (max - min) * 100;
14185
15678
  const fillHi = (Math.max(...clampedVals) - min) / (max - min) * 100;
14186
15679
  const fillStyle = isRange ? { left: `${fillLo}%`, width: `${fillHi - fillLo}%` } : { width: `${fillHi}%` };
14187
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-slider ${className}`, "data-tone": tone, "data-disabled": disabled || void 0, ...rest, children: [
15680
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-slider ${className}`, "data-tone": tone, "data-size": size, "data-disabled": disabled || void 0, ...rest, children: [
14188
15681
  __twcStyles,
14189
15682
  name ? clampedVals.map((v, i) => /* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name, value: v, disabled: disabled || void 0 }, i)) : null,
14190
15683
  label || showValue ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-slider__head", children: [
@@ -14236,10 +15729,10 @@ function Spinner({
14236
15729
  className = "",
14237
15730
  ...rest
14238
15731
  }) {
14239
- if (process.env.NODE_ENV !== "production" && tone !== void 0 && color === void 0) {
14240
- warnOnce("Spinner.tone", "Spinner: `tone` is deprecated and will be removed in 2.0 \u2014 use `color`.");
15732
+ if (process.env.NODE_ENV !== "production" && color !== void 0 && tone === void 0) {
15733
+ warnOnce("Spinner.color", "Spinner: `color` is deprecated and will be removed in 2.0 \u2014 use `tone` (matches every other tone-driven component).");
14241
15734
  }
14242
- const resolvedColor = color ?? tone ?? "current";
15735
+ const resolvedTone = tone ?? color ?? "current";
14243
15736
  const __twcStyles = useScopedStyles("twc-spinner-styles", `
14244
15737
  .twc-spinner {
14245
15738
  --_sz: 24px; --_bw: 2.5px;
@@ -14265,7 +15758,7 @@ function Spinner({
14265
15758
  .twc-spinner { animation-duration: 0.65s !important; animation-iteration-count: infinite !important; }
14266
15759
  }
14267
15760
  `);
14268
- return /* @__PURE__ */ jsxRuntime.jsx("span", { className: `twc-spinner ${className}`, "data-size": size, "data-tone": resolvedColor, role: "status", "aria-label": label, ...rest, children: __twcStyles });
15761
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: `twc-spinner ${className}`, "data-size": size, "data-tone": resolvedTone, role: "status", "aria-label": label, ...rest, children: __twcStyles });
14269
15762
  }
14270
15763
  var STAT_CSS = `
14271
15764
  .twc-stat { display: flex; flex-direction: column; gap: 6px; font-family: var(--font-sans);
@@ -14499,6 +15992,7 @@ function Switch({
14499
15992
  .twc-switch[data-invalid="true"] .twc-switch__track { box-shadow: inset 0 0 0 var(--border-medium) var(--color-danger); }
14500
15993
  .twc-switch__text { display: flex; flex-direction: column; gap: 2px; }
14501
15994
  .twc-switch__label { font-size: var(--text-sm); font-weight: var(--font-medium); color: var(--color-text); line-height: 1.3; }
15995
+ .twc-field__req { color: var(--color-danger); }
14502
15996
  .twc-switch__desc { font-size: var(--text-xs); color: var(--color-text-muted); }
14503
15997
  .twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
14504
15998
  .twc-field__error { font-size: var(--text-xs); color: var(--color-danger-subtle-fg); font-weight: var(--font-medium); }
@@ -14533,7 +16027,10 @@ function Switch({
14533
16027
  ),
14534
16028
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-switch__track", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-switch__thumb" }) }),
14535
16029
  label || description ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "twc-switch__text", children: [
14536
- label ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-switch__label", children: label }) : null,
16030
+ label ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "twc-switch__label", children: [
16031
+ label,
16032
+ required ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-field__req", children: "*" }) : null
16033
+ ] }) : null,
14537
16034
  description ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-switch__desc", children: description }) : null
14538
16035
  ] }) : null
14539
16036
  ] });
@@ -14746,6 +16243,11 @@ var TABS_CSS = `
14746
16243
  top: auto; inset-inline-end: 4px; inset-inline-start: 4px; width: auto;
14747
16244
  }
14748
16245
  .twc-tabs[data-orientation="vertical"] .twc-tabs__panel { padding-top: 0; flex: 1; min-width: 0; }
16246
+ /* WCAG 2.3.3: the active-tab indicator jumps instead of sliding under reduced motion (both
16247
+ the horizontal left/width and vertical top/height variants). */
16248
+ @media (prefers-reduced-motion: reduce) {
16249
+ .twc-tabs__indicator, .twc-tabs[data-orientation="vertical"] .twc-tabs__indicator { transition: none; }
16250
+ }
14749
16251
  `;
14750
16252
  function Tabs({
14751
16253
  items,
@@ -15124,6 +16626,12 @@ var TOAST_CSS = `
15124
16626
  from { opacity: 0; transform: translateX(-24px); }
15125
16627
  to { opacity: 1; transform: translateX(0); }
15126
16628
  }
16629
+ /* WCAG 2.3.3: appear without the slide under reduced motion (covers the RTL variant too). */
16630
+ @media (prefers-reduced-motion: reduce) {
16631
+ .twc-toast, [dir="rtl"] .twc-toast { animation: none; }
16632
+ }
16633
+ /* #209: visually-hidden persistent live-region announcers (rendered by ToastProvider). */
16634
+ .twc-toast-sr { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0; }
15127
16635
  .twc-toast::before { content: ""; position: absolute; inset-inline-start: 0; top: 0; bottom: 0; width: 4px; background: var(--_accent, var(--color-primary)); }
15128
16636
  .twc-toast[data-tone="success"] { --_accent: var(--color-success); }
15129
16637
  .twc-toast[data-tone="warning"] { --_accent: var(--color-warning); }
@@ -15240,6 +16748,8 @@ function useToast() {
15240
16748
  }
15241
16749
  function ToastProvider({ children, limit = 4, duration = 4500, ...viewportProps }) {
15242
16750
  const [toasts, setToasts] = React51__namespace.default.useState([]);
16751
+ const [politeMsg, setPoliteMsg] = React51__namespace.default.useState("");
16752
+ const [assertiveMsg, setAssertiveMsg] = React51__namespace.default.useState("");
15243
16753
  const idRef = React51__namespace.default.useRef(0);
15244
16754
  const limitRef = React51__namespace.default.useRef(limit);
15245
16755
  React51__namespace.default.useEffect(() => {
@@ -15258,6 +16768,12 @@ function ToastProvider({ children, limit = 4, duration = 4500, ...viewportProps
15258
16768
  const n = limitRef.current;
15259
16769
  return n && n > 0 ? arr.slice(-n) : arr;
15260
16770
  });
16771
+ const parts = [next.title, next.description != null ? next.description : next.children].filter((p) => typeof p === "string" || typeof p === "number").map(String);
16772
+ const msg = parts.join(". ").trim();
16773
+ if (msg) {
16774
+ const setter = next.tone === "danger" || next.tone === "warning" ? setAssertiveMsg : setPoliteMsg;
16775
+ setter((prev) => prev === msg ? msg + "\u200B" : msg);
16776
+ }
15261
16777
  return id;
15262
16778
  };
15263
16779
  const withTone = (tone) => (title, options) => push(typeof title === "object" && title !== null ? { tone, ...title } : { tone, title, ...options });
@@ -15273,12 +16789,15 @@ function ToastProvider({ children, limit = 4, duration = 4500, ...viewportProps
15273
16789
  const value = React51__namespace.default.useMemo(() => ({ ...api, toasts }), [api, toasts]);
15274
16790
  return /* @__PURE__ */ jsxRuntime.jsxs(ToastContext.Provider, { value, children: [
15275
16791
  children,
16792
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-toast-sr", "aria-live": "polite", "aria-atomic": "true", children: politeMsg }),
16793
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-toast-sr", "aria-live": "assertive", "aria-atomic": "true", children: assertiveMsg }),
15276
16794
  /* @__PURE__ */ jsxRuntime.jsx(ToastViewport, { limit, ...viewportProps, children: toasts.map((t) => /* @__PURE__ */ jsxRuntime.jsx(
15277
16795
  Toast,
15278
16796
  {
15279
16797
  tone: t.tone,
15280
16798
  title: t.title,
15281
16799
  icon: t.icon,
16800
+ role: "group",
15282
16801
  duration: t.duration != null ? t.duration : duration,
15283
16802
  onClose: () => dismiss(t.id),
15284
16803
  children: t.description != null ? t.description : t.children
@@ -15552,7 +17071,7 @@ function bgValue(bg) {
15552
17071
  if (BG[bg]) return `var(${BG[bg]})`;
15553
17072
  return String(bg).startsWith("--") ? `var(${bg})` : bg;
15554
17073
  }
15555
- function safeHref6(url) {
17074
+ function safeHref7(url) {
15556
17075
  if (url == null) return void 0;
15557
17076
  const s = String(url).replace(/[\x00-\x20]+/g, "").toLowerCase();
15558
17077
  return s.startsWith("javascript:") || s.startsWith("data:") || s.startsWith("vbscript:") ? void 0 : url;
@@ -15600,7 +17119,7 @@ var Box = React51__namespace.default.forwardRef(function Box2({
15600
17119
  ...style,
15601
17120
  ...flatStyle
15602
17121
  };
15603
- if (Tag2 === "a" && rest.href != null) rest.href = safeHref6(rest.href);
17122
+ if (Tag2 === "a" && rest.href != null) rest.href = safeHref7(rest.href);
15604
17123
  return /* @__PURE__ */ jsxRuntime.jsxs(Tag2, { ref, className: `twc-box ${className}`.trim(), style: s, "data-twc-sx": sxAttr, ...rest, children: [
15605
17124
  styleNode,
15606
17125
  children
@@ -15608,7 +17127,7 @@ var Box = React51__namespace.default.forwardRef(function Box2({
15608
17127
  });
15609
17128
  Box.displayName = "Box";
15610
17129
  var SIZES = { sm: 640, md: 768, lg: 1024, xl: 1280, full: "100%" };
15611
- function safeHref7(url) {
17130
+ function safeHref8(url) {
15612
17131
  if (url == null) return void 0;
15613
17132
  const s = String(url).replace(/[\x00-\x20]+/g, "").toLowerCase();
15614
17133
  return s.startsWith("javascript:") || s.startsWith("data:") || s.startsWith("vbscript:") ? void 0 : url;
@@ -15625,7 +17144,7 @@ var Container = React51__namespace.default.forwardRef(function Container2({
15625
17144
  }, ref) {
15626
17145
  const { flatStyle, styleNode, sxAttr } = useSx(sx);
15627
17146
  const max = SIZES[size] ?? size;
15628
- if (Tag2 === "a" && rest.href != null) rest.href = safeHref7(rest.href);
17147
+ if (Tag2 === "a" && rest.href != null) rest.href = safeHref8(rest.href);
15629
17148
  return /* @__PURE__ */ jsxRuntime.jsxs(
15630
17149
  Tag2,
15631
17150
  {
@@ -15651,83 +17170,6 @@ var Container = React51__namespace.default.forwardRef(function Container2({
15651
17170
  );
15652
17171
  });
15653
17172
  Container.displayName = "Container";
15654
- function space(v) {
15655
- if (v == null) return void 0;
15656
- if (typeof v === "number") return `var(--space-${String(v).replace(".", "-")})`;
15657
- return v;
15658
- }
15659
- function len(v) {
15660
- return typeof v === "number" ? `${v}px` : v;
15661
- }
15662
- var BP = { sm: 640, md: 768, lg: 1024, xl: 1280 };
15663
- function safeHref8(url) {
15664
- if (url == null) return void 0;
15665
- const s = String(url).replace(/[\x00-\x20]+/g, "").toLowerCase();
15666
- return s.startsWith("javascript:") || s.startsWith("data:") || s.startsWith("vbscript:") ? void 0 : url;
15667
- }
15668
- var Grid = React51__namespace.default.forwardRef(function Grid2({
15669
- as: Tag2 = "div",
15670
- columns,
15671
- minChildWidth,
15672
- gap = 4,
15673
- rowGap,
15674
- columnGap,
15675
- align,
15676
- justify,
15677
- alignContent,
15678
- justifyContent,
15679
- sx,
15680
- className = "",
15681
- style,
15682
- children,
15683
- ...rest
15684
- }, ref) {
15685
- const { flatStyle, styleNode, sxAttr } = useSx(sx);
15686
- const responsive = columns != null && typeof columns === "object";
15687
- const uid = React51__namespace.default.useId();
15688
- const gridId = responsive ? `g${uid.replace(/[^a-zA-Z0-9]/g, "")}` : null;
15689
- let css = "";
15690
- if (responsive) {
15691
- const base = columns.base ?? 1;
15692
- const sel = `[data-twc-grid-id="${gridId}"]`;
15693
- css = `${sel} { --twc-grid-cols: ${base}; }`;
15694
- for (const bp of ["sm", "md", "lg", "xl"]) {
15695
- if (columns[bp] != null) css += `
15696
- @media (min-width: ${BP[bp]}px) { ${sel} { --twc-grid-cols: ${columns[bp]}; } }`;
15697
- }
15698
- }
15699
- const __twcStyles = useScopedStyles(gridId ? `twc-grid-${gridId}` : "twc-grid", css);
15700
- const templateColumns = minChildWidth ? `repeat(auto-fill, minmax(min(${len(minChildWidth)}, 100%), 1fr))` : responsive ? `repeat(var(--twc-grid-cols, ${columns.base ?? 1}), minmax(0, 1fr))` : `repeat(${columns || 1}, minmax(0, 1fr))`;
15701
- if (Tag2 === "a" && rest.href != null) rest.href = safeHref8(rest.href);
15702
- return /* @__PURE__ */ jsxRuntime.jsxs(
15703
- Tag2,
15704
- {
15705
- ref,
15706
- className: `twc-grid ${className}`.trim(),
15707
- "data-twc-grid-id": gridId || void 0,
15708
- "data-twc-sx": sxAttr,
15709
- style: {
15710
- display: "grid",
15711
- gridTemplateColumns: templateColumns,
15712
- rowGap: space(rowGap ?? gap),
15713
- columnGap: space(columnGap ?? gap),
15714
- alignItems: align,
15715
- justifyItems: justify,
15716
- alignContent,
15717
- justifyContent,
15718
- ...style,
15719
- ...flatStyle
15720
- },
15721
- ...rest,
15722
- children: [
15723
- responsive ? __twcStyles : null,
15724
- styleNode,
15725
- children
15726
- ]
15727
- }
15728
- );
15729
- });
15730
- Grid.displayName = "Grid";
15731
17173
  function space2(v) {
15732
17174
  if (v == null) return void 0;
15733
17175
  if (typeof v === "number") return `var(--space-${String(v).replace(".", "-")})`;
@@ -16331,6 +17773,7 @@ exports.BubbleChart = BubbleChart;
16331
17773
  exports.Button = Button;
16332
17774
  exports.Candlestick = Candlestick;
16333
17775
  exports.Card = Card;
17776
+ exports.CardGrid = CardGrid;
16334
17777
  exports.Carousel = Carousel;
16335
17778
  exports.Chart = Chart;
16336
17779
  exports.Checkbox = Checkbox;
@@ -16345,13 +17788,16 @@ exports.CurrencyField = CurrencyField;
16345
17788
  exports.Datatable = Datatable;
16346
17789
  exports.DatePicker = DatePicker;
16347
17790
  exports.DateRangePicker = DateRangePicker;
17791
+ exports.DateTimePicker = DateTimePicker;
16348
17792
  exports.Dialog = Dialog;
17793
+ exports.DiffTable = DiffTable;
16349
17794
  exports.Divider = Divider;
16350
17795
  exports.DonutChart = DonutChart;
16351
17796
  exports.Drawer = Drawer;
16352
17797
  exports.EmptyState = EmptyState;
16353
17798
  exports.Field = Field;
16354
17799
  exports.FileUpload = FileUpload;
17800
+ exports.FilterBar = FilterBar;
16355
17801
  exports.Form = Form;
16356
17802
  exports.FunnelChart = FunnelChart;
16357
17803
  exports.Gauge = Gauge;
@@ -16397,6 +17843,7 @@ exports.Tag = Tag;
16397
17843
  exports.Text = Text;
16398
17844
  exports.Textarea = Textarea;
16399
17845
  exports.ThemeProvider = ThemeProvider;
17846
+ exports.TimePicker = TimePicker;
16400
17847
  exports.Timeline = Timeline;
16401
17848
  exports.Toast = Toast;
16402
17849
  exports.ToastProvider = ToastProvider;