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/dist/index.d.cts CHANGED
@@ -1843,6 +1843,10 @@ interface DatatableProps<T = any> extends Omit<React.HTMLAttributes<HTMLDivEleme
1843
1843
  * full row objects server-side rather than relying on `selectedRows`.
1844
1844
  */
1845
1845
  batchActions?: DatatableBatchAction<T>[];
1846
+ /** #322: fired when the checkbox row selection changes — `(keys, rows)`. The row-mode analogue of
1847
+ * `onCellSelectionChange`, so a consumer can track the selection and drive a batch action's own
1848
+ * `disabled`/`hidden` (which also accept predicates). */
1849
+ onRowSelectionChange?: (keys: Array<string | number>, rows: T[]) => void;
1846
1850
  /** Row height preset. With `showDensity`, the toolbar density button cycles it locally; changing this prop re-applies it. @default "comfortable" */
1847
1851
  density?: "compact" | "standard" | "comfortable";
1848
1852
  /**
@@ -1945,6 +1949,13 @@ interface DatatableProps<T = any> extends Omit<React.HTMLAttributes<HTMLDivEleme
1945
1949
  selectionMode?: "none" | "row" | "cell";
1946
1950
  /** Fired when a row is clicked in "row" selection mode: (row, key). */
1947
1951
  onRowClick?: (row: T, key: string | number) => void;
1952
+ /** #324: controlled highlighted row key for `selectionMode="row"`. When provided, the grid highlights THIS
1953
+ * row (not its internal state) and reports clicks via `onRowClick` so you update the prop — the same
1954
+ * controlled/uncontrolled split as `page`. Pass `null` to clear. Lets an external selection (a graph, a
1955
+ * master/detail pane) drive the row highlight, with scroll-into-view. */
1956
+ activeRowId?: string | number | null;
1957
+ /** #324: scroll the controlled active row into view when `activeRowId` changes. @default true */
1958
+ scrollActiveRowIntoView?: boolean;
1948
1959
  /** Fired when a cell is clicked in "cell" selection mode: (value, row, field). */
1949
1960
  onCellClick?: (value: any, row: T, field: string) => void;
1950
1961
  /** Fired when the active cell changes: ({ key, field } | null). */
@@ -2229,7 +2240,12 @@ interface DatatableBatchAction<T = any> {
2229
2240
  ) => void;
2230
2241
  /** Render in danger color. */
2231
2242
  danger?: boolean;
2232
- disabled?: boolean;
2243
+ /** #322: disable the action — a static boolean, or a predicate evaluated against the current selection so
2244
+ * an action that only applies to a subset of the selected rows can grey out when it doesn't apply. */
2245
+ disabled?: boolean | ((keys: Array<string | number>, rows: T[]) => boolean);
2246
+ /** #322: hide the action entirely when the predicate returns true (e.g. `(_, rows) => rows.every(r => r.isGroup)`
2247
+ * drops "Mark as new thread" when only group rows are selected). A static `true` always hides it. */
2248
+ hidden?: boolean | ((keys: Array<string | number>, rows: T[]) => boolean);
2233
2249
  }
2234
2250
 
2235
2251
  declare function Datatable<T = any>(props: DatatableProps<T>): React.JSX.Element;
package/dist/index.d.ts CHANGED
@@ -1843,6 +1843,10 @@ interface DatatableProps<T = any> extends Omit<React.HTMLAttributes<HTMLDivEleme
1843
1843
  * full row objects server-side rather than relying on `selectedRows`.
1844
1844
  */
1845
1845
  batchActions?: DatatableBatchAction<T>[];
1846
+ /** #322: fired when the checkbox row selection changes — `(keys, rows)`. The row-mode analogue of
1847
+ * `onCellSelectionChange`, so a consumer can track the selection and drive a batch action's own
1848
+ * `disabled`/`hidden` (which also accept predicates). */
1849
+ onRowSelectionChange?: (keys: Array<string | number>, rows: T[]) => void;
1846
1850
  /** Row height preset. With `showDensity`, the toolbar density button cycles it locally; changing this prop re-applies it. @default "comfortable" */
1847
1851
  density?: "compact" | "standard" | "comfortable";
1848
1852
  /**
@@ -1945,6 +1949,13 @@ interface DatatableProps<T = any> extends Omit<React.HTMLAttributes<HTMLDivEleme
1945
1949
  selectionMode?: "none" | "row" | "cell";
1946
1950
  /** Fired when a row is clicked in "row" selection mode: (row, key). */
1947
1951
  onRowClick?: (row: T, key: string | number) => void;
1952
+ /** #324: controlled highlighted row key for `selectionMode="row"`. When provided, the grid highlights THIS
1953
+ * row (not its internal state) and reports clicks via `onRowClick` so you update the prop — the same
1954
+ * controlled/uncontrolled split as `page`. Pass `null` to clear. Lets an external selection (a graph, a
1955
+ * master/detail pane) drive the row highlight, with scroll-into-view. */
1956
+ activeRowId?: string | number | null;
1957
+ /** #324: scroll the controlled active row into view when `activeRowId` changes. @default true */
1958
+ scrollActiveRowIntoView?: boolean;
1948
1959
  /** Fired when a cell is clicked in "cell" selection mode: (value, row, field). */
1949
1960
  onCellClick?: (value: any, row: T, field: string) => void;
1950
1961
  /** Fired when the active cell changes: ({ key, field } | null). */
@@ -2229,7 +2240,12 @@ interface DatatableBatchAction<T = any> {
2229
2240
  ) => void;
2230
2241
  /** Render in danger color. */
2231
2242
  danger?: boolean;
2232
- disabled?: boolean;
2243
+ /** #322: disable the action — a static boolean, or a predicate evaluated against the current selection so
2244
+ * an action that only applies to a subset of the selected rows can grey out when it doesn't apply. */
2245
+ disabled?: boolean | ((keys: Array<string | number>, rows: T[]) => boolean);
2246
+ /** #322: hide the action entirely when the predicate returns true (e.g. `(_, rows) => rows.every(r => r.isGroup)`
2247
+ * drops "Mark as new thread" when only group rows are selected). A static `true` always hides it. */
2248
+ hidden?: boolean | ((keys: Array<string | number>, rows: T[]) => boolean);
2233
2249
  }
2234
2250
 
2235
2251
  declare function Datatable<T = any>(props: DatatableProps<T>): React.JSX.Element;
package/dist/index.mjs CHANGED
@@ -9137,6 +9137,7 @@ function Datatable({
9137
9137
  onServerChange,
9138
9138
  onColumnVisibilityChange,
9139
9139
  batchActions = [],
9140
+ onRowSelectionChange,
9140
9141
  showExport = false,
9141
9142
  showDensity = false,
9142
9143
  showPivot = false,
@@ -9161,6 +9162,8 @@ function Datatable({
9161
9162
  onCellClick,
9162
9163
  onActiveCellChange,
9163
9164
  onCellSelectionChange,
9165
+ activeRowId,
9166
+ scrollActiveRowIntoView = true,
9164
9167
  enableClipboard = false,
9165
9168
  onCellsCopy,
9166
9169
  onCellsPaste,
@@ -9293,6 +9296,8 @@ function Datatable({
9293
9296
  };
9294
9297
  const [selected, setSelected] = React51__default.useState(() => /* @__PURE__ */ new Set());
9295
9298
  const [activeRow, setActiveRow] = React51__default.useState(null);
9299
+ const activeRowControlled = activeRowId !== void 0;
9300
+ const activeRowVal = activeRowControlled ? activeRowId : activeRow;
9296
9301
  const [activeCell, setActiveCell] = React51__default.useState(null);
9297
9302
  const [anchorCell, setAnchorCell] = React51__default.useState(null);
9298
9303
  const gridId = React51__default.useId();
@@ -10280,7 +10285,7 @@ function Datatable({
10280
10285
  if (selectionMode === "none") return;
10281
10286
  if (e.target.closest("button, a, input, select, .twc-dt__check, .twc-dt__editor-wrap")) return;
10282
10287
  if (selectionMode === "row") {
10283
- setActiveRow(k);
10288
+ if (!activeRowControlled) setActiveRow(k);
10284
10289
  onRowClick?.(row, k);
10285
10290
  onActiveCellChange?.(null);
10286
10291
  }
@@ -10359,8 +10364,9 @@ function Datatable({
10359
10364
  }
10360
10365
  if (selectionMode === "row") {
10361
10366
  e.preventDefault();
10362
- setActiveRow(keyOf(leafRows[r2], r2));
10363
- onRowClick?.(leafRows[r2], keyOf(leafRows[r2], r2));
10367
+ const rk = keyOf(leafRows[r2], r2);
10368
+ if (!activeRowControlled) setActiveRow(rk);
10369
+ onRowClick?.(leafRows[r2], rk);
10364
10370
  return;
10365
10371
  }
10366
10372
  handled = false;
@@ -10389,6 +10395,23 @@ function Datatable({
10389
10395
  setFocus((f) => ({ r: Math.min(f.r, Math.max(0, leafRows.length - 1)), c: Math.min(f.c, Math.max(0, ordered.length - 1)) }));
10390
10396
  }, [leafRows.length, ordered.length, pageVal]);
10391
10397
  const selectedRows = React51__default.useMemo(() => rows.filter((r2, i) => selected.has(keyOf(r2, i))), [rows, selected]);
10398
+ const selKeys = React51__default.useMemo(() => [...selected], [selected]);
10399
+ const onRowSelRef = React51__default.useRef(onRowSelectionChange);
10400
+ onRowSelRef.current = onRowSelectionChange;
10401
+ const prevRowSelSigRef = React51__default.useRef("");
10402
+ React51__default.useEffect(() => {
10403
+ const cb = onRowSelRef.current;
10404
+ if (!cb) return;
10405
+ const sig = selKeys.join(",");
10406
+ if (sig === prevRowSelSigRef.current) return;
10407
+ prevRowSelSigRef.current = sig;
10408
+ cb(selKeys, selectedRows);
10409
+ }, [selKeys, selectedRows]);
10410
+ React51__default.useEffect(() => {
10411
+ if (!activeRowControlled || !scrollActiveRowIntoView || activeRowId == null) return;
10412
+ const el = gridRef.current && gridRef.current.querySelector('.twc-dt__row[data-active="true"]');
10413
+ if (el && el.scrollIntoView) el.scrollIntoView({ block: "nearest" });
10414
+ }, [activeRowId, activeRowControlled, scrollActiveRowIntoView]);
10392
10415
  function clearSelection() {
10393
10416
  setSelected(/* @__PURE__ */ new Set());
10394
10417
  }
@@ -10485,11 +10508,11 @@ function Datatable({
10485
10508
  }
10486
10509
  patch[c.field] = v;
10487
10510
  });
10488
- const selKeys = new Set(selected);
10511
+ const selKeys2 = new Set(selected);
10489
10512
  const changedRows = [];
10490
10513
  const nextAll = rows.map((r2, i) => {
10491
10514
  const k = keyOf(r2, i);
10492
- if (!selKeys.has(k)) return r2;
10515
+ if (!selKeys2.has(k)) return r2;
10493
10516
  const updated = { ...r2, ...patch };
10494
10517
  changedRows.push(updated);
10495
10518
  return updated;
@@ -11106,7 +11129,7 @@ function Datatable({
11106
11129
  function renderLeaf(row, ri, pinSide, midIdx) {
11107
11130
  const k = keyOf(row, ri);
11108
11131
  const sel = selected.has(k);
11109
- const rowActive = selectionMode === "row" && activeRow === k;
11132
+ const rowActive = selectionMode === "row" && activeRowVal === k;
11110
11133
  const stickyStyle = pinSide === "top" ? { position: "sticky", top: headH, zIndex: 5 } : pinSide === "bottom" ? { position: "sticky", bottom: 0, zIndex: 5 } : void 0;
11111
11134
  const h = rowHeights[k];
11112
11135
  const rowStyle = { ...stickyStyle || {}, ...h ? { height: h } : {} };
@@ -11379,21 +11402,26 @@ function Datatable({
11379
11402
  /* @__PURE__ */ jsx(Svg, { d: I.pencil }),
11380
11403
  "Edit"
11381
11404
  ] }) : null,
11382
- batchActions.map((a, i) => /* @__PURE__ */ jsxs(
11383
- "button",
11384
- {
11385
- type: "button",
11386
- className: "twc-dt__batch-btn",
11387
- "data-danger": a.danger || void 0,
11388
- disabled: a.disabled,
11389
- onClick: (e) => a.onClick?.([...selected], selectedRows, clearSelection, { anchorEl: e.currentTarget }),
11390
- children: [
11391
- a.icon,
11392
- a.label
11393
- ]
11394
- },
11395
- i
11396
- ))
11405
+ batchActions.map((a, i) => {
11406
+ const isHidden = typeof a.hidden === "function" ? a.hidden(selKeys, selectedRows) : !!a.hidden;
11407
+ if (isHidden) return null;
11408
+ const isDisabled = typeof a.disabled === "function" ? a.disabled(selKeys, selectedRows) : !!a.disabled;
11409
+ return /* @__PURE__ */ jsxs(
11410
+ "button",
11411
+ {
11412
+ type: "button",
11413
+ className: "twc-dt__batch-btn",
11414
+ "data-danger": a.danger || void 0,
11415
+ disabled: isDisabled,
11416
+ onClick: (e) => a.onClick?.(selKeys, selectedRows, clearSelection, { anchorEl: e.currentTarget }),
11417
+ children: [
11418
+ a.icon,
11419
+ a.label
11420
+ ]
11421
+ },
11422
+ i
11423
+ );
11424
+ })
11397
11425
  ] })
11398
11426
  ] }) : null,
11399
11427
  isDiff && diff.showSummary !== false ? /* @__PURE__ */ jsxs("span", { className: "twc-dt__diff-summary", "aria-label": "Diff summary", children: [