twico-ui 1.20.2 → 1.21.1

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/CHANGELOG.md CHANGED
@@ -2,6 +2,21 @@
2
2
 
3
3
  All notable changes to Twico UI are documented here (newest first). This file is generated automatically on each release by [semantic-release](https://semantic-release.gitbook.io/) from [Conventional Commits](https://www.conventionalcommits.org/) — do not edit it by hand. The same notes appear on each [GitHub Release](https://github.com/EncryptionL/twico-ui/releases).
4
4
 
5
+ ## [1.21.1](https://github.com/EncryptionL/twico-ui/compare/v1.21.0...v1.21.1) (2026-07-29)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * **datatable:** auto-fit filter panel field widths to the widest label ([29ca74c](https://github.com/EncryptionL/twico-ui/commit/29ca74cd89fde500ca024f574488a59feaa87ee0)), closes [#289](https://github.com/EncryptionL/twico-ui/issues/289)
11
+ * **overlay:** stack tooltips below toasts, and dropdowns above ([7236003](https://github.com/EncryptionL/twico-ui/commit/7236003bbf97f73b7440abd89b39aa846b02edc1)), closes [#288](https://github.com/EncryptionL/twico-ui/issues/288)
12
+
13
+ # [1.21.0](https://github.com/EncryptionL/twico-ui/compare/v1.20.2...v1.21.0) (2026-07-28)
14
+
15
+
16
+ ### Features
17
+
18
+ * **datatable:** add toolbarActions slot for custom toolbar controls ([c1c3142](https://github.com/EncryptionL/twico-ui/commit/c1c3142787cb2a21811b16d6774f0e1f2c6045b9)), closes [#286](https://github.com/EncryptionL/twico-ui/issues/286)
19
+
5
20
  ## [1.20.2](https://github.com/EncryptionL/twico-ui/compare/v1.20.1...v1.20.2) (2026-07-24)
6
21
 
7
22
 
package/dist/index.cjs CHANGED
@@ -5488,7 +5488,7 @@ function ColorPicker({
5488
5488
  role: "dialog",
5489
5489
  "aria-modal": "true",
5490
5490
  "aria-label": "Color picker",
5491
- style: { position: "fixed", left: coords.left, right: "auto", top: coords.top, bottom: coords.bottom, zIndex: "var(--z-tooltip)" },
5491
+ style: { position: "fixed", left: coords.left, right: "auto", top: coords.top, bottom: coords.bottom, zIndex: "var(--z-floating)" },
5492
5492
  children: [
5493
5493
  /* @__PURE__ */ jsxRuntime.jsxs(
5494
5494
  "div",
@@ -5966,7 +5966,7 @@ function Combobox({
5966
5966
  role: "listbox",
5967
5967
  ref: popRef,
5968
5968
  "data-placement": coords.flip ? "top" : "bottom",
5969
- style: { position: "fixed", left: coords.left, top: coords.top, bottom: coords.bottom, width: coords.width, right: "auto", zIndex: "var(--z-tooltip)" },
5969
+ style: { position: "fixed", left: coords.left, top: coords.top, bottom: coords.bottom, width: coords.width, right: "auto", zIndex: "var(--z-floating)" },
5970
5970
  children: popInner
5971
5971
  }
5972
5972
  ),
@@ -6853,7 +6853,7 @@ function Select({
6853
6853
  ref: popRef,
6854
6854
  "data-state": popState,
6855
6855
  "data-placement": coords.flip ? "top" : "bottom",
6856
- style: { position: "fixed", left: coords.left, top: coords.top, bottom: coords.bottom, width: coords.width, minWidth: coords.minWidth, maxWidth: coords.maxWidth, right: "auto", zIndex: "var(--z-tooltip)" },
6856
+ style: { position: "fixed", left: coords.left, top: coords.top, bottom: coords.bottom, width: coords.width, minWidth: coords.minWidth, maxWidth: coords.maxWidth, right: "auto", zIndex: "var(--z-floating)" },
6857
6857
  children: popInner
6858
6858
  }
6859
6859
  ),
@@ -7620,7 +7620,7 @@ function MultiSelect({
7620
7620
  className: "twc-pop twc-pop--portal",
7621
7621
  ref: popRef,
7622
7622
  "data-placement": coords.flip ? "top" : "bottom",
7623
- style: { position: "fixed", left: coords.left, top: coords.top, bottom: coords.bottom, width: coords.width, right: "auto", zIndex: "var(--z-tooltip)" },
7623
+ style: { position: "fixed", left: coords.left, top: coords.top, bottom: coords.bottom, width: coords.width, right: "auto", zIndex: "var(--z-floating)" },
7624
7624
  children: popInner
7625
7625
  }
7626
7626
  ),
@@ -8128,6 +8128,8 @@ var DT_CSS = `
8128
8128
  /* Toolbar */
8129
8129
  .twc-dt__toolbar { position: relative; z-index: 10; display: flex; align-items: center; gap: 6px; padding: 8px 10px;
8130
8130
  border-bottom: var(--border-thin) solid var(--color-divider); flex-wrap: wrap; }
8131
+ /* #286: host-supplied leading toolbar slot \u2014 a flex cluster so multiple buttons align with the built-ins. */
8132
+ .twc-dt__toolbar-actions { display: inline-flex; align-items: center; gap: 6px; }
8131
8133
  /* z-index above the scroll area's sticky header (z:3) so a toolbar button's
8132
8134
  hover tooltip (a ::after that hangs below the bar) paints over the table
8133
8135
  instead of being covered by the header/first rows. */
@@ -8525,9 +8527,14 @@ th.twc-dt__rownum .twc-dt__th-inner { padding-inline: 8px; gap: 2px; justify-con
8525
8527
 
8526
8528
  /* Filter panel */
8527
8529
  .twc-dt__filters { width: 580px; max-width: calc(100vw - 32px); }
8528
- .twc-dt__frow { display: flex; align-items: center; gap: 6px; padding: 5px 4px; }
8529
- .twc-dt__f-col { width: 118px; flex: none; }
8530
- .twc-dt__f-op { width: 118px; flex: none; }
8530
+ /* #289: wrap is a safety net \u2014 a beyond-cap label or the narrow responsive panel drops the value input
8531
+ to its own line instead of clipping the remove button; no effect in the normal (capped) case. */
8532
+ .twc-dt__frow { display: flex; align-items: center; gap: 6px; padding: 5px 4px; flex-wrap: wrap; }
8533
+ /* #289: column-field auto-fits the widest header label \u2014 JS writes --twc-dt-fcol-w (measured, clamped),
8534
+ the max-width is the hard backstop that keeps the value input above its 140px floor inside the 580px
8535
+ panel even if the var is stale/too large. Fallback 118px = the previous fixed behavior. */
8536
+ .twc-dt__f-col { flex: none; width: var(--twc-dt-fcol-w, 118px); min-width: 118px; max-width: 210px; }
8537
+ .twc-dt__f-op { flex: none; width: var(--twc-dt-fop-w, 118px); min-width: 118px; max-width: 170px; }
8531
8538
  .twc-dt__f-val { flex: 1; min-width: 140px; }
8532
8539
  .twc-dt__frm-x { display: inline-grid; place-items: center; width: 30px; height: 38px; border: none; background: transparent; color: var(--color-text-subtle); border-radius: var(--radius-md); cursor: pointer; flex: none; align-self: flex-start; }
8533
8540
  .twc-dt__frm-x:hover { background: var(--color-danger-subtle); color: var(--color-danger-subtle-fg); }
@@ -8642,6 +8649,7 @@ var NUM_OPS = [
8642
8649
  ];
8643
8650
  var opsFor = (type) => type === "number" ? NUM_OPS : STR_OPS;
8644
8651
  var isMultiOp = (op) => op === "isAnyOf";
8652
+ var ALL_OP_LABELS = [...new Set([...STR_OPS, ...NUM_OPS].map((o) => o.label))];
8645
8653
  var filterTypeOf = (col) => {
8646
8654
  const ft = col && col.filterType;
8647
8655
  if (ft === "number" || ft === "string") return ft;
@@ -9082,6 +9090,7 @@ function Datatable({
9082
9090
  searchable = true,
9083
9091
  quickFilter,
9084
9092
  onQuickFilterChange,
9093
+ toolbarActions,
9085
9094
  rowPinning = false,
9086
9095
  rowReorder = false,
9087
9096
  rowResize = false,
@@ -9313,6 +9322,8 @@ function Datatable({
9313
9322
  };
9314
9323
  }, []);
9315
9324
  const scrollRef = React51__namespace.default.useRef(null);
9325
+ const filterPanelRef = React51__namespace.default.useRef(null);
9326
+ const measureCanvas = React51__namespace.default.useRef(null);
9316
9327
  const [scrollTop, setScrollTop] = React51__namespace.default.useState(0);
9317
9328
  const [viewportH, setViewportH] = React51__namespace.default.useState(0);
9318
9329
  const [ovTip, setOvTip] = React51__namespace.default.useState(null);
@@ -10346,7 +10357,36 @@ function Datatable({
10346
10357
  ] });
10347
10358
  }
10348
10359
  const tableMinWidth = leadW + ordered.reduce((a, c) => a + widthOf(c), 0);
10349
- const filterableCols = cols.filter((c) => c.filterable);
10360
+ const filterableCols = React51__namespace.default.useMemo(() => cols.filter((c) => c.filterable), [cols]);
10361
+ React51__namespace.default.useEffect(() => {
10362
+ const el = filterPanelRef.current;
10363
+ if (panel !== "filters" || !el || typeof document === "undefined") return void 0;
10364
+ const measure = () => {
10365
+ if (!el.isConnected) return;
10366
+ const trigger = el.querySelector(".twc-sel__trigger");
10367
+ const cs = getComputedStyle(trigger || el);
10368
+ const ctx = (measureCanvas.current || (measureCanvas.current = document.createElement("canvas"))).getContext("2d");
10369
+ if (!ctx) return;
10370
+ ctx.font = cs.font || `${cs.fontStyle} ${cs.fontWeight} ${cs.fontSize}/${cs.lineHeight} ${cs.fontFamily}`;
10371
+ const pad2 = trigger ? (parseFloat(cs.paddingLeft) || 0) + (parseFloat(cs.paddingRight) || 0) : 24;
10372
+ const chrome = pad2 + 8 + 16 + 4;
10373
+ const widest = (labels) => labels.reduce((m, l) => Math.max(m, ctx.measureText(String(l)).width), 0);
10374
+ const colCap = Math.max(118, Math.min(210, el.clientWidth - 118 - 30 - 140 - 3 * 6 - 2 * 4));
10375
+ const colW = Math.min(colCap, Math.max(118, Math.ceil(widest(filterableCols.map((c) => c.headerName ?? c.field)) + chrome)));
10376
+ const opW = Math.min(170, Math.max(118, Math.ceil(widest(ALL_OP_LABELS) + chrome)));
10377
+ if (Number.isFinite(colW)) el.style.setProperty("--twc-dt-fcol-w", `${colW}px`);
10378
+ if (Number.isFinite(opW)) el.style.setProperty("--twc-dt-fop-w", `${opW}px`);
10379
+ };
10380
+ measure();
10381
+ document.fonts?.ready?.then?.(measure).catch?.(() => {
10382
+ });
10383
+ let ro;
10384
+ if (typeof ResizeObserver !== "undefined") {
10385
+ ro = new ResizeObserver(measure);
10386
+ ro.observe(el);
10387
+ }
10388
+ return () => ro?.disconnect();
10389
+ }, [panel, filterableCols, filters.length]);
10350
10390
  const orderIdxOf = (f) => {
10351
10391
  const i = order.indexOf(f);
10352
10392
  return i === -1 ? 9999 : i;
@@ -10664,6 +10704,7 @@ function Datatable({
10664
10704
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `twc-dt ${className}`, ref: rootRef, "data-density": density, "data-resizing": resizing || void 0, ...rest, children: [
10665
10705
  __twcStyles,
10666
10706
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dt__toolbar", "data-compact": compact || void 0, children: [
10707
+ toolbarActions != null ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-dt__toolbar-actions", children: toolbarActions }) : null,
10667
10708
  (batchActions.length || hasBatchEditor) && selected.size > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dt__batch", children: [
10668
10709
  /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-dt__batch-x", onClick: clearSelection, "aria-label": "Clear selection", children: /* @__PURE__ */ jsxRuntime.jsx(Svg, { d: I.x }) }),
10669
10710
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "twc-dt__batch-count", children: [
@@ -11715,7 +11756,7 @@ function Datatable({
11715
11756
  ] })
11716
11757
  ] });
11717
11758
  })() : null,
11718
- panel === "filters" && panelPos ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dt__pop twc-dt__filters", style: { top: panelPos.top, left: panelPos.left }, children: [
11759
+ panel === "filters" && panelPos ? /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: filterPanelRef, className: "twc-dt__pop twc-dt__filters", style: { top: panelPos.top, left: panelPos.left }, children: [
11719
11760
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dt__panel-head", children: [
11720
11761
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-dt__panel-title", children: "Filters" }),
11721
11762
  filters.length ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-dt__link", onClick: () => setFilters([]), children: "Clear all" }) : null
@@ -12588,7 +12629,7 @@ function DatePicker({
12588
12629
  role: "dialog",
12589
12630
  "aria-modal": "true",
12590
12631
  "aria-label": "Choose date",
12591
- style: { position: "fixed", left: coords.left, right: "auto", top: coords.top, bottom: coords.bottom, zIndex: "var(--z-tooltip)" },
12632
+ style: { position: "fixed", left: coords.left, right: "auto", top: coords.top, bottom: coords.bottom, zIndex: "var(--z-floating)" },
12592
12633
  children: [
12593
12634
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dp__head", children: [
12594
12635
  /* @__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" }) }) }),
@@ -13165,7 +13206,7 @@ function DateRangePicker({
13165
13206
  role: "dialog",
13166
13207
  "aria-modal": "true",
13167
13208
  "aria-label": "Choose date range",
13168
- style: { position: "fixed", left: coords.left, right: "auto", top: coords.top, bottom: coords.bottom, zIndex: "var(--z-tooltip)" },
13209
+ style: { position: "fixed", left: coords.left, right: "auto", top: coords.top, bottom: coords.bottom, zIndex: "var(--z-floating)" },
13169
13210
  children: [
13170
13211
  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,
13171
13212
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-drp__cal", children: [
@@ -13694,7 +13735,7 @@ function TimePicker({
13694
13735
  role: "dialog",
13695
13736
  "aria-modal": "true",
13696
13737
  "aria-label": "Choose time",
13697
- style: { position: "fixed", left: coords.left, right: "auto", top: coords.top, bottom: coords.bottom, zIndex: "var(--z-tooltip)" },
13738
+ style: { position: "fixed", left: coords.left, right: "auto", top: coords.top, bottom: coords.bottom, zIndex: "var(--z-floating)" },
13698
13739
  children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-tp__cols", children: columns.map((col) => {
13699
13740
  const firstTab = col.items.find((it) => it.selected) || col.items.find((it) => !it.disabled);
13700
13741
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-tp__col", role: "listbox", "aria-label": col.aria, "data-col": col.key, children: [