twico-ui 1.19.1 → 1.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
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.20.0](https://github.com/EncryptionL/twico-ui/compare/v1.19.1...v1.20.0) (2026-07-23)
6
+
7
+
8
+ ### Features
9
+
10
+ * **datatable:** add column filterType, decoupling filter operators from the edit type ([68dbb6e](https://github.com/EncryptionL/twico-ui/commit/68dbb6e80cc3e50fd1072eff5fa8cf2415259ef0)), closes [#271](https://github.com/EncryptionL/twico-ui/issues/271) [263/#267](https://github.com/EncryptionL/twico-ui/issues/267) [#269](https://github.com/EncryptionL/twico-ui/issues/269) [#269](https://github.com/EncryptionL/twico-ui/issues/269) [#270](https://github.com/EncryptionL/twico-ui/issues/270) [#271](https://github.com/EncryptionL/twico-ui/issues/271)
11
+
5
12
  ## [1.19.1](https://github.com/EncryptionL/twico-ui/compare/v1.19.0...v1.19.1) (2026-07-23)
6
13
 
7
14
 
package/dist/index.cjs CHANGED
@@ -6769,6 +6769,7 @@ function Select({
6769
6769
  "data-disabled": o.disabled || void 0,
6770
6770
  "data-selected": isSel || void 0,
6771
6771
  "data-active": idx === active || void 0,
6772
+ title: typeof o.label === "string" ? o.label : void 0,
6772
6773
  onMouseEnter: () => {
6773
6774
  if (!o.disabled) setActive(idx);
6774
6775
  },
@@ -8495,13 +8496,15 @@ th.twc-dt__rownum .twc-dt__th-inner { padding-inline: 8px; gap: 2px; justify-con
8495
8496
  .twc-dt__col-list { max-height: 230px; overflow-y: auto; }
8496
8497
  .twc-dt__col-row { display: flex; align-items: center; gap: 10px; padding: 7px 8px; border-radius: var(--radius-md); cursor: pointer; }
8497
8498
  .twc-dt__col-row:hover { background: var(--color-surface-sunken); }
8498
- .twc-dt__col-row[draggable="true"] { cursor: grab; }
8499
+ /* #271 (columns-panel analog of #263/#267): grab lives on the grip only, not the whole row \u2014 the row
8500
+ keeps its 'cursor: pointer' (toggles visibility) and stays a drag source for the mouse. */
8499
8501
  .twc-dt__col-row[data-dragging="true"] { opacity: 0.5; }
8500
8502
  .twc-dt__col-row[data-dropbefore="true"] { box-shadow: inset 0 3px 0 var(--color-primary); }
8501
8503
  .twc-dt__col-row[data-dropafter="true"] { box-shadow: inset 0 -3px 0 var(--color-primary); }
8502
- .twc-dt__col-grip { display: inline-flex; color: var(--color-text-subtle); flex: none; opacity: 0.45; }
8504
+ .twc-dt__col-grip { display: inline-flex; color: var(--color-text-subtle); flex: none; opacity: 0.45; cursor: grab; }
8503
8505
  .twc-dt__col-row:hover .twc-dt__col-grip { opacity: 0.8; }
8504
8506
  .twc-dt__col-grip svg { width: 14px; height: 14px; }
8507
+ .twc-dt__col-grip:active, .twc-dt__col-row[data-dragging="true"] .twc-dt__col-grip { cursor: grabbing; }
8505
8508
  .twc-dt__col-name { flex: 1; font-size: var(--text-sm); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
8506
8509
  .twc-dt__col-pins { display: inline-flex; gap: 2px; flex: none; }
8507
8510
  .twc-dt__col-pin { display: inline-grid; place-items: center; width: 24px; height: 24px; border: none; padding: 0; background: transparent; color: var(--color-text-subtle); cursor: pointer; border-radius: var(--radius-sm); transition: background-color var(--duration-fast), color var(--duration-fast); }
@@ -8639,6 +8642,11 @@ var NUM_OPS = [
8639
8642
  ];
8640
8643
  var opsFor = (type) => type === "number" ? NUM_OPS : STR_OPS;
8641
8644
  var isMultiOp = (op) => op === "isAnyOf";
8645
+ var filterTypeOf = (col) => {
8646
+ const ft = col && col.filterType;
8647
+ if (ft === "number" || ft === "string") return ft;
8648
+ return col && col.type === "number" ? "number" : "string";
8649
+ };
8642
8650
  var getColVal = (col, row) => col && col.valueGetter ? col.valueGetter(row) : row == null ? void 0 : row[col && col.field];
8643
8651
  var diffValueNode = (col, v, row) => col.renderCell ? col.renderCell(v, row) : col.valueFormatter ? col.valueFormatter(v, row) : v == null ? "" : v;
8644
8652
  function renderDiffCell(col, val, row) {
@@ -8721,7 +8729,10 @@ function runDatatableQuery(rows, query, options = {}) {
8721
8729
  let out = Array.isArray(rows) ? rows : [];
8722
8730
  const quick = String(q.quickFilter || "").trim().toLowerCase();
8723
8731
  if (quick) out = out.filter((r2) => searchFields.some((f) => String(gv(f, r2) ?? "").toLowerCase().includes(quick)));
8724
- for (const f of q.filters || []) out = out.filter((r2) => testFilter(gv(f.field, r2), f.op, f.value, typeOf(f.field)));
8732
+ for (const f of q.filters || []) {
8733
+ const fc = cols.find((c) => c.field === f.field);
8734
+ out = out.filter((r2) => testFilter(gv(f.field, r2), f.op, f.value, filterTypeOf(fc)));
8735
+ }
8725
8736
  if (q.sort && q.sort.field) {
8726
8737
  const { field, dir } = q.sort, numeric = typeOf(field) === "number";
8727
8738
  out = [...out].sort((a, b) => {
@@ -9539,7 +9550,7 @@ function Datatable({
9539
9550
  for (const f of filters) {
9540
9551
  const col = colByField[f.field];
9541
9552
  if (!col) continue;
9542
- out = out.filter((r2) => testFilter(getColVal(col, r2), f.op, f.value, col.type));
9553
+ out = out.filter((r2) => testFilter(getColVal(col, r2), f.op, f.value, filterTypeOf(col)));
9543
9554
  }
9544
9555
  if (sort) {
9545
9556
  const col = colByField[sort.field];
@@ -9857,7 +9868,7 @@ function Datatable({
9857
9868
  }
9858
9869
  function addFilter(field) {
9859
9870
  const col = colByField[field] || cols[0];
9860
- setFilters((f) => [...f, { id: Date.now() + Math.random(), field: col.field, op: opsFor(col.type)[0].value, value: "" }]);
9871
+ setFilters((f) => [...f, { id: Date.now() + Math.random(), field: col.field, op: opsFor(filterTypeOf(col))[0].value, value: "" }]);
9861
9872
  }
9862
9873
  function setPin(field, side) {
9863
9874
  setPins((p) => {
@@ -11672,7 +11683,7 @@ function Datatable({
11672
11683
  ] }),
11673
11684
  filters.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-dt__empty", style: { padding: "16px 12px" }, children: "No filters applied" }) : filters.map((f) => {
11674
11685
  const col = colByField[f.field] || cols[0];
11675
- const ops = opsFor(col.type);
11686
+ const ops = opsFor(filterTypeOf(col));
11676
11687
  const op = ops.find((o) => o.value === f.op) || ops[0];
11677
11688
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dt__frow", children: [
11678
11689
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-dt__f-col", children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -11684,7 +11695,7 @@ function Datatable({
11684
11695
  options: filterableCols.map((c) => ({ value: c.field, label: c.headerName })),
11685
11696
  onChange: (v) => {
11686
11697
  const nc = colByField[v];
11687
- setFilters((arr) => arr.map((x) => x.id === f.id ? { ...x, field: v, op: opsFor(nc.type)[0].value, value: "" } : x));
11698
+ setFilters((arr) => arr.map((x) => x.id === f.id ? { ...x, field: v, op: opsFor(filterTypeOf(nc))[0].value, value: "" } : x));
11688
11699
  }
11689
11700
  }
11690
11701
  ) }),
@@ -11723,7 +11734,7 @@ function Datatable({
11723
11734
  Input,
11724
11735
  {
11725
11736
  size: "sm",
11726
- type: col.type === "number" ? "number" : "text",
11737
+ type: filterTypeOf(col) === "number" ? "number" : "text",
11727
11738
  placeholder: "Value",
11728
11739
  value: f.value,
11729
11740
  onChange: (e) => setFilters((arr) => arr.map((x) => x.id === f.id ? { ...x, value: e.target.value } : x))