twico-ui 1.41.1 → 1.42.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,26 @@
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.42.1](https://github.com/EncryptionL/twico-ui/compare/v1.42.0...v1.42.1) (2026-09-24)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * **datatable:** limit the batch-picker escape hatch to server mode ([804d7cb](https://github.com/EncryptionL/twico-ui/commit/804d7cbd39b50190469a31b44477ee9755f07560)), closes [#428](https://github.com/EncryptionL/twico-ui/issues/428) [#433](https://github.com/EncryptionL/twico-ui/issues/433) [#432](https://github.com/EncryptionL/twico-ui/issues/432) [#244](https://github.com/EncryptionL/twico-ui/issues/244)
11
+ * **datatable:** offer per-row editable columns when the selection is on an unloaded page ([33c8cc4](https://github.com/EncryptionL/twico-ui/commit/33c8cc4174bcc07410740b923b7221b26a887d4c)), closes [#428](https://github.com/EncryptionL/twico-ui/issues/428) [#428](https://github.com/EncryptionL/twico-ui/issues/428)
12
+
13
+ # [1.42.0](https://github.com/EncryptionL/twico-ui/compare/v1.41.1...v1.42.0) (2026-09-24)
14
+
15
+
16
+ ### Bug Fixes
17
+
18
+ * **datatable:** gate row-tree sub-rows in the batch write + document the 4-arg onBatchUpdate ([1cfb163](https://github.com/EncryptionL/twico-ui/commit/1cfb1638aca476b3234800fb547cbcd78662807d)), closes [#432](https://github.com/EncryptionL/twico-ui/issues/432) [#359](https://github.com/EncryptionL/twico-ui/issues/359) [#428](https://github.com/EncryptionL/twico-ui/issues/428) [#428](https://github.com/EncryptionL/twico-ui/issues/428)
19
+
20
+
21
+ ### Features
22
+
23
+ * **datatable:** report rowPatches + skippedKeys from onBatchUpdate ([f15be7f](https://github.com/EncryptionL/twico-ui/commit/f15be7f6c9611d099f50df850acc47c3bb769bfa))
24
+
5
25
  ## [1.41.1](https://github.com/EncryptionL/twico-ui/compare/v1.41.0...v1.41.1) (2026-09-24)
6
26
 
7
27
 
package/dist/index.cjs CHANGED
@@ -8691,6 +8691,7 @@ var safeHref6 = (url) => {
8691
8691
  var dtNaturalAlign = (c) => c.type === "number" || c.type === "actions" ? "right" : "left";
8692
8692
  var DT_CELL_WIDGET_SEL = 'button:not(:disabled), a[href], [role="button"], [role="switch"], [role="checkbox"], [role="link"]';
8693
8693
  var dtCellWidgets = (td) => td ? Array.from(td.querySelectorAll(DT_CELL_WIDGET_SEL)).filter((el) => el.getAttribute("aria-hidden") !== "true" && el.getAttribute("aria-disabled") !== "true") : [];
8694
+ var EMPTY_BATCH_SEL = Object.freeze({ testable: Object.freeze([]), hasUntestable: false });
8694
8695
  var DT_CSS = `
8695
8696
  .twc-dt { display: flex; flex-direction: column; font-family: var(--font-sans); color: var(--color-text);
8696
8697
  border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-lg);
@@ -11508,9 +11509,30 @@ function Datatable({
11508
11509
  [cols, editMode, batchEditKey]
11509
11510
  // eslint-disable-line react-hooks/exhaustive-deps
11510
11511
  );
11512
+ const batchSel = React53__namespace.default.useMemo(() => {
11513
+ if (!selected.size) return EMPTY_BATCH_SEL;
11514
+ const testable = [], resolved = /* @__PURE__ */ new Set();
11515
+ const take = (r2, i) => {
11516
+ const k = keyOf(r2, i);
11517
+ if (selected.has(k) && !resolved.has(k)) {
11518
+ resolved.add(k);
11519
+ testable.push(r2);
11520
+ }
11521
+ };
11522
+ for (let i = 0; i < rows.length && resolved.size < selected.size; i++) take(rows[i], i);
11523
+ for (let i = 0; i < leafRows.length && resolved.size < selected.size; i++) take(leafRows[i], i);
11524
+ let hasUntestable = false;
11525
+ if (serverMode) {
11526
+ for (const k of selected) if (!resolved.has(k)) {
11527
+ hasUntestable = true;
11528
+ break;
11529
+ }
11530
+ }
11531
+ return { testable, hasUntestable };
11532
+ }, [rows, leafRows, selected, serverMode]);
11511
11533
  const batchEditableColsSel = React53__namespace.default.useMemo(
11512
- () => batchEditableCols.filter((c) => typeof c.editable !== "function" || selectedRows.some((r2) => c.editable(r2, { field: c.field }))),
11513
- [batchEditableCols, selectedRows]
11534
+ () => batchEditableCols.filter((c) => typeof c.editable !== "function" || batchSel.hasUntestable || batchSel.testable.some((r2) => c.editable(r2, { field: c.field }))),
11535
+ [batchEditableCols, batchSel]
11514
11536
  );
11515
11537
  const hasBatchEditor = showBatchEdit && batchEditableCols.length > 0;
11516
11538
  const [batchEdit, setBatchEdit] = React53__namespace.default.useState(null);
@@ -11565,6 +11587,8 @@ function Datatable({
11565
11587
  const loadedSel = /* @__PURE__ */ new Set();
11566
11588
  const changedRows = [];
11567
11589
  const safeKeys = [];
11590
+ const rowPatches = [];
11591
+ const skippedKeys = [];
11568
11592
  const nextAll = rows.map((r2, i) => {
11569
11593
  const k = keyOf(r2, i);
11570
11594
  if (!selKeys2.has(k)) return r2;
@@ -11576,13 +11600,32 @@ function Datatable({
11576
11600
  else allEditable = false;
11577
11601
  }
11578
11602
  if (allEditable) safeKeys.push(k);
11579
- if (!Object.keys(rowPatch).length) return r2;
11603
+ if (!Object.keys(rowPatch).length) {
11604
+ skippedKeys.push(k);
11605
+ return r2;
11606
+ }
11580
11607
  const updated = { ...r2, ...rowPatch };
11581
11608
  changedRows.push(updated);
11609
+ rowPatches.push({ key: k, patch: rowPatch });
11582
11610
  return updated;
11583
11611
  });
11584
- for (const k of selKeys2) if (!loadedSel.has(k)) safeKeys.push(k);
11585
- onBatchUpdate?.(changedRows, patch, safeKeys);
11612
+ for (const k of selKeys2) {
11613
+ if (loadedSel.has(k)) continue;
11614
+ const li = keyIndex.get(k);
11615
+ const sub = li == null ? null : leafRows[li];
11616
+ if (!sub) {
11617
+ if (serverMode) safeKeys.push(k);
11618
+ continue;
11619
+ }
11620
+ let allEditable = true, anyEditable = false;
11621
+ for (const c of active) {
11622
+ if (typeof c.editable !== "function" || c.editable(sub, { field: c.field })) anyEditable = true;
11623
+ else allEditable = false;
11624
+ }
11625
+ if (allEditable) safeKeys.push(k);
11626
+ if (!anyEditable) skippedKeys.push(k);
11627
+ }
11628
+ onBatchUpdate?.(changedRows, patch, safeKeys, { rowPatches, skippedKeys });
11586
11629
  onRowsChange?.(nextAll);
11587
11630
  setBatchEdit(null);
11588
11631
  closeBatchEdit();