twico-ui 1.27.0 → 1.29.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,20 @@
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.29.0](https://github.com/EncryptionL/twico-ui/compare/v1.28.0...v1.29.0) (2026-08-03)
6
+
7
+
8
+ ### Features
9
+
10
+ * **datatable:** controlled active row for selectionMode="row" (activeRowId) ([#324](https://github.com/EncryptionL/twico-ui/issues/324)) ([c118a06](https://github.com/EncryptionL/twico-ui/commit/c118a069d3f91ce9640b39751cab24717569be2b))
11
+
12
+ # [1.28.0](https://github.com/EncryptionL/twico-ui/compare/v1.27.0...v1.28.0) (2026-08-03)
13
+
14
+
15
+ ### Features
16
+
17
+ * **datatable:** selection-aware batch actions (predicate disabled/hidden) + onRowSelectionChange ([#322](https://github.com/EncryptionL/twico-ui/issues/322)) ([33a1c36](https://github.com/EncryptionL/twico-ui/commit/33a1c36340e36cf0c9cc763cda7ed5d26fd46baa))
18
+
5
19
  # [1.27.0](https://github.com/EncryptionL/twico-ui/compare/v1.26.0...v1.27.0) (2026-08-03)
6
20
 
7
21
 
package/dist/index.cjs CHANGED
@@ -9158,6 +9158,7 @@ function Datatable({
9158
9158
  onServerChange,
9159
9159
  onColumnVisibilityChange,
9160
9160
  batchActions = [],
9161
+ onRowSelectionChange,
9161
9162
  showExport = false,
9162
9163
  showDensity = false,
9163
9164
  showPivot = false,
@@ -9182,6 +9183,8 @@ function Datatable({
9182
9183
  onCellClick,
9183
9184
  onActiveCellChange,
9184
9185
  onCellSelectionChange,
9186
+ activeRowId,
9187
+ scrollActiveRowIntoView = true,
9185
9188
  enableClipboard = false,
9186
9189
  onCellsCopy,
9187
9190
  onCellsPaste,
@@ -9314,6 +9317,8 @@ function Datatable({
9314
9317
  };
9315
9318
  const [selected, setSelected] = React51__namespace.default.useState(() => /* @__PURE__ */ new Set());
9316
9319
  const [activeRow, setActiveRow] = React51__namespace.default.useState(null);
9320
+ const activeRowControlled = activeRowId !== void 0;
9321
+ const activeRowVal = activeRowControlled ? activeRowId : activeRow;
9317
9322
  const [activeCell, setActiveCell] = React51__namespace.default.useState(null);
9318
9323
  const [anchorCell, setAnchorCell] = React51__namespace.default.useState(null);
9319
9324
  const gridId = React51__namespace.default.useId();
@@ -10301,7 +10306,7 @@ function Datatable({
10301
10306
  if (selectionMode === "none") return;
10302
10307
  if (e.target.closest("button, a, input, select, .twc-dt__check, .twc-dt__editor-wrap")) return;
10303
10308
  if (selectionMode === "row") {
10304
- setActiveRow(k);
10309
+ if (!activeRowControlled) setActiveRow(k);
10305
10310
  onRowClick?.(row, k);
10306
10311
  onActiveCellChange?.(null);
10307
10312
  }
@@ -10380,8 +10385,9 @@ function Datatable({
10380
10385
  }
10381
10386
  if (selectionMode === "row") {
10382
10387
  e.preventDefault();
10383
- setActiveRow(keyOf(leafRows[r2], r2));
10384
- onRowClick?.(leafRows[r2], keyOf(leafRows[r2], r2));
10388
+ const rk = keyOf(leafRows[r2], r2);
10389
+ if (!activeRowControlled) setActiveRow(rk);
10390
+ onRowClick?.(leafRows[r2], rk);
10385
10391
  return;
10386
10392
  }
10387
10393
  handled = false;
@@ -10410,6 +10416,23 @@ function Datatable({
10410
10416
  setFocus((f) => ({ r: Math.min(f.r, Math.max(0, leafRows.length - 1)), c: Math.min(f.c, Math.max(0, ordered.length - 1)) }));
10411
10417
  }, [leafRows.length, ordered.length, pageVal]);
10412
10418
  const selectedRows = React51__namespace.default.useMemo(() => rows.filter((r2, i) => selected.has(keyOf(r2, i))), [rows, selected]);
10419
+ const selKeys = React51__namespace.default.useMemo(() => [...selected], [selected]);
10420
+ const onRowSelRef = React51__namespace.default.useRef(onRowSelectionChange);
10421
+ onRowSelRef.current = onRowSelectionChange;
10422
+ const prevRowSelSigRef = React51__namespace.default.useRef("");
10423
+ React51__namespace.default.useEffect(() => {
10424
+ const cb = onRowSelRef.current;
10425
+ if (!cb) return;
10426
+ const sig = selKeys.join(",");
10427
+ if (sig === prevRowSelSigRef.current) return;
10428
+ prevRowSelSigRef.current = sig;
10429
+ cb(selKeys, selectedRows);
10430
+ }, [selKeys, selectedRows]);
10431
+ React51__namespace.default.useEffect(() => {
10432
+ if (!activeRowControlled || !scrollActiveRowIntoView || activeRowId == null) return;
10433
+ const el = gridRef.current && gridRef.current.querySelector('.twc-dt__row[data-active="true"]');
10434
+ if (el && el.scrollIntoView) el.scrollIntoView({ block: "nearest" });
10435
+ }, [activeRowId, activeRowControlled, scrollActiveRowIntoView]);
10413
10436
  function clearSelection() {
10414
10437
  setSelected(/* @__PURE__ */ new Set());
10415
10438
  }
@@ -10506,11 +10529,11 @@ function Datatable({
10506
10529
  }
10507
10530
  patch[c.field] = v;
10508
10531
  });
10509
- const selKeys = new Set(selected);
10532
+ const selKeys2 = new Set(selected);
10510
10533
  const changedRows = [];
10511
10534
  const nextAll = rows.map((r2, i) => {
10512
10535
  const k = keyOf(r2, i);
10513
- if (!selKeys.has(k)) return r2;
10536
+ if (!selKeys2.has(k)) return r2;
10514
10537
  const updated = { ...r2, ...patch };
10515
10538
  changedRows.push(updated);
10516
10539
  return updated;
@@ -11127,7 +11150,7 @@ function Datatable({
11127
11150
  function renderLeaf(row, ri, pinSide, midIdx) {
11128
11151
  const k = keyOf(row, ri);
11129
11152
  const sel = selected.has(k);
11130
- const rowActive = selectionMode === "row" && activeRow === k;
11153
+ const rowActive = selectionMode === "row" && activeRowVal === k;
11131
11154
  const stickyStyle = pinSide === "top" ? { position: "sticky", top: headH, zIndex: 5 } : pinSide === "bottom" ? { position: "sticky", bottom: 0, zIndex: 5 } : void 0;
11132
11155
  const h = rowHeights[k];
11133
11156
  const rowStyle = { ...stickyStyle || {}, ...h ? { height: h } : {} };
@@ -11400,21 +11423,26 @@ function Datatable({
11400
11423
  /* @__PURE__ */ jsxRuntime.jsx(Svg, { d: I.pencil }),
11401
11424
  "Edit"
11402
11425
  ] }) : null,
11403
- batchActions.map((a, i) => /* @__PURE__ */ jsxRuntime.jsxs(
11404
- "button",
11405
- {
11406
- type: "button",
11407
- className: "twc-dt__batch-btn",
11408
- "data-danger": a.danger || void 0,
11409
- disabled: a.disabled,
11410
- onClick: (e) => a.onClick?.([...selected], selectedRows, clearSelection, { anchorEl: e.currentTarget }),
11411
- children: [
11412
- a.icon,
11413
- a.label
11414
- ]
11415
- },
11416
- i
11417
- ))
11426
+ batchActions.map((a, i) => {
11427
+ const isHidden = typeof a.hidden === "function" ? a.hidden(selKeys, selectedRows) : !!a.hidden;
11428
+ if (isHidden) return null;
11429
+ const isDisabled = typeof a.disabled === "function" ? a.disabled(selKeys, selectedRows) : !!a.disabled;
11430
+ return /* @__PURE__ */ jsxRuntime.jsxs(
11431
+ "button",
11432
+ {
11433
+ type: "button",
11434
+ className: "twc-dt__batch-btn",
11435
+ "data-danger": a.danger || void 0,
11436
+ disabled: isDisabled,
11437
+ onClick: (e) => a.onClick?.(selKeys, selectedRows, clearSelection, { anchorEl: e.currentTarget }),
11438
+ children: [
11439
+ a.icon,
11440
+ a.label
11441
+ ]
11442
+ },
11443
+ i
11444
+ );
11445
+ })
11418
11446
  ] })
11419
11447
  ] }) : null,
11420
11448
  isDiff && diff.showSummary !== false ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "twc-dt__diff-summary", "aria-label": "Diff summary", children: [