twico-ui 1.19.0 → 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/dist/index.d.cts CHANGED
@@ -2046,6 +2046,13 @@ interface DatatableColumn<T = any> {
2046
2046
  headerName?: string;
2047
2047
  /** Data type. "actions" renders per-row action buttons via getActions. @default "string" */
2048
2048
  type?: "string" | "number" | "actions";
2049
+ /** Filter operators + value comparison for this column, **decoupled from the edit `type`** (#270).
2050
+ * A `"number"` column edits by coercing its commit with `Number()`, which would wipe a custom value
2051
+ * (e.g. a value+unit measurement edited via `renderEditCell`) — so such a column must edit as
2052
+ * `type: "string"`, yet may still want **numeric** filter operators (`=` `≠` `>` `≥` `<` `≤`).
2053
+ * Set `filterType: "number"` to offer them (provide a `valueGetter` returning the numeric value so
2054
+ * the comparison is numeric). Falls back to `type`. Sorting still uses `type`. @default type */
2055
+ filterType?: "string" | "number";
2049
2056
  /** Column width: a px number, or `"auto"` to size to the header's intrinsic width (chrome + label) so a short column fits without truncating or wasting the 160px default. @default 160 (120 for actions) */
2050
2057
  width?: number | "auto";
2051
2058
  /** Lower bound (px) for the resolved width, incl. `"auto"` and user resizes. */
package/dist/index.d.ts CHANGED
@@ -2046,6 +2046,13 @@ interface DatatableColumn<T = any> {
2046
2046
  headerName?: string;
2047
2047
  /** Data type. "actions" renders per-row action buttons via getActions. @default "string" */
2048
2048
  type?: "string" | "number" | "actions";
2049
+ /** Filter operators + value comparison for this column, **decoupled from the edit `type`** (#270).
2050
+ * A `"number"` column edits by coercing its commit with `Number()`, which would wipe a custom value
2051
+ * (e.g. a value+unit measurement edited via `renderEditCell`) — so such a column must edit as
2052
+ * `type: "string"`, yet may still want **numeric** filter operators (`=` `≠` `>` `≥` `<` `≤`).
2053
+ * Set `filterType: "number"` to offer them (provide a `valueGetter` returning the numeric value so
2054
+ * the comparison is numeric). Falls back to `type`. Sorting still uses `type`. @default type */
2055
+ filterType?: "string" | "number";
2049
2056
  /** Column width: a px number, or `"auto"` to size to the header's intrinsic width (chrome + label) so a short column fits without truncating or wasting the 160px default. @default 160 (120 for actions) */
2050
2057
  width?: number | "auto";
2051
2058
  /** Lower bound (px) for the resolved width, incl. `"auto"` and user resizes. */
package/dist/index.mjs CHANGED
@@ -6748,6 +6748,7 @@ function Select({
6748
6748
  "data-disabled": o.disabled || void 0,
6749
6749
  "data-selected": isSel || void 0,
6750
6750
  "data-active": idx === active || void 0,
6751
+ title: typeof o.label === "string" ? o.label : void 0,
6751
6752
  onMouseEnter: () => {
6752
6753
  if (!o.disabled) setActive(idx);
6753
6754
  },
@@ -8202,14 +8203,16 @@ var DT_CSS = `
8202
8203
  .twc-dt__resizer:focus-visible::after { background: var(--color-primary); }
8203
8204
  .twc-dt[data-resizing="true"] { cursor: col-resize; user-select: none; }
8204
8205
 
8205
- /* Column reorder (drag) */
8206
- .twc-dt__th-label[draggable="true"] { cursor: grab; }
8206
+ /* Column reorder (drag) \u2014 the grab affordance lives on the header grip only (#267, the column analog
8207
+ of #263), not the whole header, so a plain hover to read/sort/open the \u22EE menu isn't shown as
8208
+ draggable (the header stays a drag source for the mouse). */
8207
8209
  .twc-dt__th[data-dragging="true"] { opacity: 0.45; }
8208
8210
  .twc-dt__th[data-dropbefore="true"] .twc-dt__th-inner { box-shadow: inset 3px 0 0 var(--color-primary); }
8209
8211
  .twc-dt__th[data-dropafter="true"] .twc-dt__th-inner { box-shadow: inset -3px 0 0 var(--color-primary); }
8210
- .twc-dt__grip { display: inline-flex; color: var(--color-text-subtle); opacity: 0; margin-inline-end: -2px; transition: opacity var(--duration-fast); flex: none; }
8212
+ .twc-dt__grip { display: inline-flex; color: var(--color-text-subtle); opacity: 0; margin-inline-end: -2px; transition: opacity var(--duration-fast); flex: none; cursor: grab; }
8211
8213
  .twc-dt__grip svg { width: 13px; height: 13px; }
8212
8214
  .twc-dt__th:hover .twc-dt__grip { opacity: 0.5; }
8215
+ .twc-dt__grip:active, .twc-dt__th[data-dragging="true"] .twc-dt__grip { cursor: grabbing; }
8213
8216
 
8214
8217
  /* Summary / aggregation footer row */
8215
8218
  .twc-dt__table tfoot td { position: sticky; bottom: 0; z-index: 3; background: var(--color-surface-sunken);
@@ -8472,13 +8475,15 @@ th.twc-dt__rownum .twc-dt__th-inner { padding-inline: 8px; gap: 2px; justify-con
8472
8475
  .twc-dt__col-list { max-height: 230px; overflow-y: auto; }
8473
8476
  .twc-dt__col-row { display: flex; align-items: center; gap: 10px; padding: 7px 8px; border-radius: var(--radius-md); cursor: pointer; }
8474
8477
  .twc-dt__col-row:hover { background: var(--color-surface-sunken); }
8475
- .twc-dt__col-row[draggable="true"] { cursor: grab; }
8478
+ /* #271 (columns-panel analog of #263/#267): grab lives on the grip only, not the whole row \u2014 the row
8479
+ keeps its 'cursor: pointer' (toggles visibility) and stays a drag source for the mouse. */
8476
8480
  .twc-dt__col-row[data-dragging="true"] { opacity: 0.5; }
8477
8481
  .twc-dt__col-row[data-dropbefore="true"] { box-shadow: inset 0 3px 0 var(--color-primary); }
8478
8482
  .twc-dt__col-row[data-dropafter="true"] { box-shadow: inset 0 -3px 0 var(--color-primary); }
8479
- .twc-dt__col-grip { display: inline-flex; color: var(--color-text-subtle); flex: none; opacity: 0.45; }
8483
+ .twc-dt__col-grip { display: inline-flex; color: var(--color-text-subtle); flex: none; opacity: 0.45; cursor: grab; }
8480
8484
  .twc-dt__col-row:hover .twc-dt__col-grip { opacity: 0.8; }
8481
8485
  .twc-dt__col-grip svg { width: 14px; height: 14px; }
8486
+ .twc-dt__col-grip:active, .twc-dt__col-row[data-dragging="true"] .twc-dt__col-grip { cursor: grabbing; }
8482
8487
  .twc-dt__col-name { flex: 1; font-size: var(--text-sm); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
8483
8488
  .twc-dt__col-pins { display: inline-flex; gap: 2px; flex: none; }
8484
8489
  .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); }
@@ -8616,6 +8621,11 @@ var NUM_OPS = [
8616
8621
  ];
8617
8622
  var opsFor = (type) => type === "number" ? NUM_OPS : STR_OPS;
8618
8623
  var isMultiOp = (op) => op === "isAnyOf";
8624
+ var filterTypeOf = (col) => {
8625
+ const ft = col && col.filterType;
8626
+ if (ft === "number" || ft === "string") return ft;
8627
+ return col && col.type === "number" ? "number" : "string";
8628
+ };
8619
8629
  var getColVal = (col, row) => col && col.valueGetter ? col.valueGetter(row) : row == null ? void 0 : row[col && col.field];
8620
8630
  var diffValueNode = (col, v, row) => col.renderCell ? col.renderCell(v, row) : col.valueFormatter ? col.valueFormatter(v, row) : v == null ? "" : v;
8621
8631
  function renderDiffCell(col, val, row) {
@@ -8698,7 +8708,10 @@ function runDatatableQuery(rows, query, options = {}) {
8698
8708
  let out = Array.isArray(rows) ? rows : [];
8699
8709
  const quick = String(q.quickFilter || "").trim().toLowerCase();
8700
8710
  if (quick) out = out.filter((r2) => searchFields.some((f) => String(gv(f, r2) ?? "").toLowerCase().includes(quick)));
8701
- for (const f of q.filters || []) out = out.filter((r2) => testFilter(gv(f.field, r2), f.op, f.value, typeOf(f.field)));
8711
+ for (const f of q.filters || []) {
8712
+ const fc = cols.find((c) => c.field === f.field);
8713
+ out = out.filter((r2) => testFilter(gv(f.field, r2), f.op, f.value, filterTypeOf(fc)));
8714
+ }
8702
8715
  if (q.sort && q.sort.field) {
8703
8716
  const { field, dir } = q.sort, numeric = typeOf(field) === "number";
8704
8717
  out = [...out].sort((a, b) => {
@@ -9516,7 +9529,7 @@ function Datatable({
9516
9529
  for (const f of filters) {
9517
9530
  const col = colByField[f.field];
9518
9531
  if (!col) continue;
9519
- out = out.filter((r2) => testFilter(getColVal(col, r2), f.op, f.value, col.type));
9532
+ out = out.filter((r2) => testFilter(getColVal(col, r2), f.op, f.value, filterTypeOf(col)));
9520
9533
  }
9521
9534
  if (sort) {
9522
9535
  const col = colByField[sort.field];
@@ -9834,7 +9847,7 @@ function Datatable({
9834
9847
  }
9835
9848
  function addFilter(field) {
9836
9849
  const col = colByField[field] || cols[0];
9837
- setFilters((f) => [...f, { id: Date.now() + Math.random(), field: col.field, op: opsFor(col.type)[0].value, value: "" }]);
9850
+ setFilters((f) => [...f, { id: Date.now() + Math.random(), field: col.field, op: opsFor(filterTypeOf(col))[0].value, value: "" }]);
9838
9851
  }
9839
9852
  function setPin(field, side) {
9840
9853
  setPins((p) => {
@@ -11649,7 +11662,7 @@ function Datatable({
11649
11662
  ] }),
11650
11663
  filters.length === 0 ? /* @__PURE__ */ jsx("div", { className: "twc-dt__empty", style: { padding: "16px 12px" }, children: "No filters applied" }) : filters.map((f) => {
11651
11664
  const col = colByField[f.field] || cols[0];
11652
- const ops = opsFor(col.type);
11665
+ const ops = opsFor(filterTypeOf(col));
11653
11666
  const op = ops.find((o) => o.value === f.op) || ops[0];
11654
11667
  return /* @__PURE__ */ jsxs("div", { className: "twc-dt__frow", children: [
11655
11668
  /* @__PURE__ */ jsx("div", { className: "twc-dt__f-col", children: /* @__PURE__ */ jsx(
@@ -11661,7 +11674,7 @@ function Datatable({
11661
11674
  options: filterableCols.map((c) => ({ value: c.field, label: c.headerName })),
11662
11675
  onChange: (v) => {
11663
11676
  const nc = colByField[v];
11664
- setFilters((arr) => arr.map((x) => x.id === f.id ? { ...x, field: v, op: opsFor(nc.type)[0].value, value: "" } : x));
11677
+ setFilters((arr) => arr.map((x) => x.id === f.id ? { ...x, field: v, op: opsFor(filterTypeOf(nc))[0].value, value: "" } : x));
11665
11678
  }
11666
11679
  }
11667
11680
  ) }),
@@ -11700,7 +11713,7 @@ function Datatable({
11700
11713
  Input,
11701
11714
  {
11702
11715
  size: "sm",
11703
- type: col.type === "number" ? "number" : "text",
11716
+ type: filterTypeOf(col) === "number" ? "number" : "text",
11704
11717
  placeholder: "Value",
11705
11718
  value: f.value,
11706
11719
  onChange: (e) => setFilters((arr) => arr.map((x) => x.id === f.id ? { ...x, value: e.target.value } : x))