twico-ui 1.15.0 → 1.16.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
@@ -1893,6 +1893,14 @@ interface DatatableProps<T = any> extends Omit<React.HTMLAttributes<HTMLDivEleme
1893
1893
  * resolves only rows on the currently loaded page; for cross-page selections use the
1894
1894
  * complete `selectedKeys` array and apply the `patch` server-side. */
1895
1895
  onBatchUpdate?: (changedRows: T[], patch: Record<string, any>, selectedKeys: Array<string | number>) => void;
1896
+ /** Render the built-in "Edit" button in the selection toolbar (the batch editor). Set `false` to
1897
+ * suppress it and ship your own batch-edit action via `batchActions` — otherwise you'd get two
1898
+ * "Edit" buttons. @default true */
1899
+ showBatchEdit?: boolean;
1900
+ /** Allow-list the column `field`s the built-in batch editor offers. Defaults to every editable
1901
+ * column. Use it to trim a wide grid's editor **without** touching `editable` (which would also
1902
+ * disable inline cell editing). */
1903
+ batchEditFields?: string[];
1896
1904
  /** Show a "Go to" page jumper in the footer when there are more than 5 pages. @default true */
1897
1905
  showPageJumper?: boolean;
1898
1906
  /** Click-to-select mode: "row" highlights the clicked row, "cell" highlights a single cell. @default "none" */
package/dist/index.d.ts CHANGED
@@ -1893,6 +1893,14 @@ interface DatatableProps<T = any> extends Omit<React.HTMLAttributes<HTMLDivEleme
1893
1893
  * resolves only rows on the currently loaded page; for cross-page selections use the
1894
1894
  * complete `selectedKeys` array and apply the `patch` server-side. */
1895
1895
  onBatchUpdate?: (changedRows: T[], patch: Record<string, any>, selectedKeys: Array<string | number>) => void;
1896
+ /** Render the built-in "Edit" button in the selection toolbar (the batch editor). Set `false` to
1897
+ * suppress it and ship your own batch-edit action via `batchActions` — otherwise you'd get two
1898
+ * "Edit" buttons. @default true */
1899
+ showBatchEdit?: boolean;
1900
+ /** Allow-list the column `field`s the built-in batch editor offers. Defaults to every editable
1901
+ * column. Use it to trim a wide grid's editor **without** touching `editable` (which would also
1902
+ * disable inline cell editing). */
1903
+ batchEditFields?: string[];
1896
1904
  /** Show a "Go to" page jumper in the footer when there are more than 5 pages. @default true */
1897
1905
  showPageJumper?: boolean;
1898
1906
  /** Click-to-select mode: "row" highlights the clicked row, "cell" highlights a single cell. @default "none" */
package/dist/index.mjs CHANGED
@@ -8307,12 +8307,17 @@ var DT_CSS = `
8307
8307
  .twc-dt__cfg-btn:hover { background: var(--color-surface-sunken); }
8308
8308
  .twc-dt__cfg-btn[data-primary="true"] { background: var(--color-primary); border-color: var(--color-primary); color: var(--color-primary-fg); }
8309
8309
  .twc-dt__cfg-btn[data-primary="true"]:hover { background: var(--color-primary-hover); border-color: var(--color-primary-hover); }
8310
- .twc-dt__be-row { display: flex; align-items: center; gap: 10px; padding: 5px 6px; opacity: 0.6; transition: opacity var(--duration-fast); }
8311
- .twc-dt__be-row[data-on] { opacity: 1; }
8312
- .twc-dt__be-check { display: inline-flex; align-items: center; gap: 7px; width: 120px; flex: none; font-size: var(--text-sm); color: var(--color-text); cursor: pointer; }
8313
- .twc-dt__be-check input { width: 16px; height: 16px; accent-color: var(--color-primary); cursor: pointer; flex: none; }
8314
- .twc-dt__be-check span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
8310
+ /* #244: batch editor = pick columns (searchable), then set a value for each. */
8311
+ .twc-dt__be-add { padding: 6px 6px 8px; border-bottom: var(--border-thin) solid var(--color-divider); }
8312
+ .twc-dt__be-row { display: flex; align-items: center; gap: 8px; padding: 5px 6px; }
8313
+ .twc-dt__be-name { width: 108px; flex: none; font-size: var(--text-sm); color: var(--color-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
8315
8314
  .twc-dt__be-ctl { flex: 1; min-width: 0; }
8315
+ .twc-dt__be-x { flex: none; display: inline-grid; place-items: center; width: 24px; height: 24px; padding: 0; border: none;
8316
+ background: transparent; color: var(--color-text-subtle); cursor: pointer; border-radius: var(--radius-sm);
8317
+ transition: background-color var(--duration-fast), color var(--duration-fast); }
8318
+ .twc-dt__be-x:hover { background: var(--color-surface-sunken); color: var(--color-text); }
8319
+ .twc-dt__be-x svg { width: 14px; height: 14px; }
8320
+ .twc-dt__be-empty { padding: 14px 8px; font-size: var(--text-xs); color: var(--color-text-muted); text-align: center; }
8316
8321
 
8317
8322
  /* Pinned rows */
8318
8323
  .twc-dt__row[data-pinned-row] > .twc-dt__td { background: var(--color-primary-subtle); }
@@ -8327,6 +8332,12 @@ var DT_CSS = `
8327
8332
  .twc-dt__row[data-op="added"] > .twc-dt__td { background: color-mix(in srgb, var(--color-success) 7%, transparent); }
8328
8333
  .twc-dt__row[data-op="removed"] > .twc-dt__td { background: color-mix(in srgb, var(--color-danger) 7%, transparent); }
8329
8334
  .twc-dt__row[data-op="modified"] > .twc-dt__td { background: color-mix(in srgb, var(--color-warning) 6%, transparent); }
8335
+ /* #242: pinned cells in a diff row must stay OPAQUE (composite the tint over the surface, not over
8336
+ transparent) or the cells scrolling under a sticky column bleed through. Specificity (0,4,0) wins
8337
+ over the (0,3,0) tint above and matches the non-pinned cells' effective color (tint over surface). */
8338
+ .twc-dt__row[data-op="added"] > .twc-dt__td[data-pin] { background: color-mix(in srgb, var(--color-success) 7%, var(--color-surface)); }
8339
+ .twc-dt__row[data-op="removed"] > .twc-dt__td[data-pin] { background: color-mix(in srgb, var(--color-danger) 7%, var(--color-surface)); }
8340
+ .twc-dt__row[data-op="modified"] > .twc-dt__td[data-pin] { background: color-mix(in srgb, var(--color-warning) 6%, var(--color-surface)); }
8330
8341
  .twc-dt__diff-change { display: inline-flex; align-items: center; gap: 4px; min-width: 0; max-width: 100%; }
8331
8342
  .twc-dt__diff-old { text-decoration: line-through; color: var(--color-text-subtle); }
8332
8343
  .twc-dt__diff-new { color: var(--color-success-subtle-fg); font-weight: var(--font-medium); }
@@ -8977,6 +8988,8 @@ function Datatable({
8977
8988
  onRowUpdate,
8978
8989
  onRowsChange,
8979
8990
  onBatchUpdate,
8991
+ showBatchEdit = true,
8992
+ batchEditFields = null,
8980
8993
  showPageJumper = true,
8981
8994
  selectionMode = "none",
8982
8995
  onRowClick,
@@ -10016,9 +10029,14 @@ function Datatable({
10016
10029
  cancelEdit();
10017
10030
  }
10018
10031
  }
10032
+ const batchEditKey = batchEditFields ? batchEditFields.join("\0") : "";
10019
10033
  const batchEditableCols = React51__default.useMemo(
10020
- () => cols.filter((c) => c.type !== "actions" && (c.editable ?? (editMode && c.editable !== false))),
10021
- [cols, editMode]
10034
+ () => {
10035
+ const base = cols.filter((c) => c.type !== "actions" && (c.editable ?? (editMode && c.editable !== false)));
10036
+ return batchEditFields ? base.filter((c) => batchEditFields.includes(c.field)) : base;
10037
+ },
10038
+ [cols, editMode, batchEditKey]
10039
+ // eslint-disable-line react-hooks/exhaustive-deps
10022
10040
  );
10023
10041
  const [batchEdit, setBatchEdit] = React51__default.useState(null);
10024
10042
  const [batchEditPos, openBatchEdit, closeBatchEdit] = useFloating();
@@ -10453,7 +10471,7 @@ function Datatable({
10453
10471
  " selected"
10454
10472
  ] }),
10455
10473
  /* @__PURE__ */ jsxs("div", { className: "twc-dt__batch-actions", children: [
10456
- batchEditableCols.length ? /* @__PURE__ */ jsxs("button", { type: "button", className: "twc-dt__batch-btn", onClick: (e) => openBatchEditor(e.currentTarget), children: [
10474
+ showBatchEdit && batchEditableCols.length ? /* @__PURE__ */ jsxs("button", { type: "button", className: "twc-dt__batch-btn", onClick: (e) => openBatchEditor(e.currentTarget), children: [
10457
10475
  /* @__PURE__ */ jsx(Svg, { d: I.pencil }),
10458
10476
  "Edit"
10459
10477
  ] }) : null,
@@ -10942,58 +10960,80 @@ function Datatable({
10942
10960
  ]
10943
10961
  }
10944
10962
  ) : null,
10945
- batchEdit && batchEditPos ? /* @__PURE__ */ jsxs("div", { className: "twc-dt__pop twc-dt__cfg", style: { top: batchEditPos.top, left: batchEditPos.left, width: 320 }, children: [
10946
- /* @__PURE__ */ jsx(Caret, { pos: batchEditPos }),
10947
- /* @__PURE__ */ jsxs("div", { className: "twc-dt__cfg-head", children: [
10948
- "Edit ",
10949
- selected.size,
10950
- " selected ",
10951
- selected.size === 1 ? "row" : "rows"
10952
- ] }),
10953
- /* @__PURE__ */ jsx("div", { className: "twc-dt__cfg-list", children: batchEditableCols.map((c) => {
10954
- const on = !!batchEdit.fields[c.field];
10955
- const opts = c.valueOptions ? c.valueOptions.map((o) => typeof o === "string" ? { value: o, label: o } : o) : null;
10956
- return /* @__PURE__ */ jsxs("div", { className: "twc-dt__be-row", "data-on": on || void 0, children: [
10957
- /* @__PURE__ */ jsxs("label", { className: "twc-dt__be-check", children: [
10958
- /* @__PURE__ */ jsx("input", { type: "checkbox", checked: on, onChange: (e) => setBatchEdit((b) => ({ ...b, fields: { ...b.fields, [c.field]: e.target.checked } })) }),
10959
- /* @__PURE__ */ jsx("span", { children: c.headerName })
10960
- ] }),
10961
- /* @__PURE__ */ jsx("div", { className: "twc-dt__be-ctl", children: opts ? /* @__PURE__ */ jsx(
10962
- Select,
10963
- {
10964
- size: "sm",
10965
- portal: true,
10966
- searchable: true,
10967
- placeholder: "New value\u2026",
10968
- value: batchEdit.values[c.field] ?? "",
10969
- options: opts,
10970
- disabled: !on,
10971
- onChange: (v) => setBatchEdit((b) => ({ ...b, fields: { ...b.fields, [c.field]: true }, values: { ...b.values, [c.field]: v } }))
10972
- }
10973
- ) : /* @__PURE__ */ jsx(
10974
- Input,
10975
- {
10976
- size: "sm",
10977
- type: c.type === "number" || c.type === "currency" ? "number" : "text",
10978
- placeholder: "New value\u2026",
10979
- value: batchEdit.values[c.field] ?? "",
10980
- disabled: !on,
10981
- onChange: (e) => setBatchEdit((b) => ({ ...b, values: { ...b.values, [c.field]: e.target.value } }))
10982
- }
10983
- ) })
10984
- ] }, c.field);
10985
- }) }),
10986
- /* @__PURE__ */ jsxs("div", { className: "twc-dt__cfg-foot", children: [
10987
- /* @__PURE__ */ jsx("button", { type: "button", className: "twc-dt__cfg-btn", onClick: () => {
10988
- setBatchEdit(null);
10989
- closeBatchEdit();
10990
- }, children: "Cancel" }),
10991
- /* @__PURE__ */ jsxs("button", { type: "button", className: "twc-dt__cfg-btn", "data-primary": "true", onClick: applyBatchEdit, children: [
10992
- "Apply to ",
10993
- selected.size
10963
+ batchEdit && batchEditPos ? (() => {
10964
+ const pickedCols = Object.keys(batchEdit.fields).filter((f) => batchEdit.fields[f]).map((f) => batchEditableCols.find((c) => c.field === f)).filter(Boolean);
10965
+ const unpicked = batchEditableCols.filter((c) => !batchEdit.fields[c.field]);
10966
+ const addField = (f) => {
10967
+ if (f) setBatchEdit((b) => ({ ...b, fields: { ...b.fields, [f]: true }, values: { ...b.values, [f]: b.values[f] ?? "" } }));
10968
+ };
10969
+ const removeField = (f) => setBatchEdit((b) => {
10970
+ const fields = { ...b.fields };
10971
+ delete fields[f];
10972
+ const values = { ...b.values };
10973
+ delete values[f];
10974
+ return { ...b, fields, values };
10975
+ });
10976
+ return /* @__PURE__ */ jsxs("div", { className: "twc-dt__pop twc-dt__cfg", style: { top: batchEditPos.top, left: batchEditPos.left, width: 320 }, children: [
10977
+ /* @__PURE__ */ jsx(Caret, { pos: batchEditPos }),
10978
+ /* @__PURE__ */ jsxs("div", { className: "twc-dt__cfg-head", children: [
10979
+ "Edit ",
10980
+ selected.size,
10981
+ " selected ",
10982
+ selected.size === 1 ? "row" : "rows"
10983
+ ] }),
10984
+ unpicked.length ? /* @__PURE__ */ jsx("div", { className: "twc-dt__be-add", children: /* @__PURE__ */ jsx(
10985
+ Select,
10986
+ {
10987
+ size: "sm",
10988
+ portal: true,
10989
+ searchable: true,
10990
+ placeholder: "Add a column\u2026",
10991
+ value: "",
10992
+ "aria-label": "Add a column to edit",
10993
+ options: unpicked.map((c) => ({ value: c.field, label: String(c.headerName ?? c.field) })),
10994
+ onChange: addField
10995
+ }
10996
+ ) }) : null,
10997
+ /* @__PURE__ */ jsx("div", { className: "twc-dt__cfg-list", children: pickedCols.length === 0 ? /* @__PURE__ */ jsx("div", { className: "twc-dt__be-empty", children: "Pick a column to set its value on all selected rows." }) : pickedCols.map((c) => {
10998
+ const opts = c.valueOptions ? c.valueOptions.map((o) => typeof o === "string" ? { value: o, label: o } : o) : null;
10999
+ return /* @__PURE__ */ jsxs("div", { className: "twc-dt__be-row", children: [
11000
+ /* @__PURE__ */ jsx("span", { className: "twc-dt__be-name", title: String(c.headerName ?? c.field), children: c.headerName }),
11001
+ /* @__PURE__ */ jsx("div", { className: "twc-dt__be-ctl", children: opts ? /* @__PURE__ */ jsx(
11002
+ Select,
11003
+ {
11004
+ size: "sm",
11005
+ portal: true,
11006
+ searchable: true,
11007
+ placeholder: "New value\u2026",
11008
+ value: batchEdit.values[c.field] ?? "",
11009
+ options: opts,
11010
+ onChange: (v) => setBatchEdit((b) => ({ ...b, values: { ...b.values, [c.field]: v } }))
11011
+ }
11012
+ ) : /* @__PURE__ */ jsx(
11013
+ Input,
11014
+ {
11015
+ size: "sm",
11016
+ type: c.type === "number" || c.type === "currency" ? "number" : "text",
11017
+ placeholder: "New value\u2026",
11018
+ value: batchEdit.values[c.field] ?? "",
11019
+ onChange: (e) => setBatchEdit((b) => ({ ...b, values: { ...b.values, [c.field]: e.target.value } }))
11020
+ }
11021
+ ) }),
11022
+ /* @__PURE__ */ jsx("button", { type: "button", className: "twc-dt__be-x", "aria-label": `Remove ${String(c.headerName ?? c.field)}`, onClick: () => removeField(c.field), children: /* @__PURE__ */ jsx(Svg, { d: I.x }) })
11023
+ ] }, c.field);
11024
+ }) }),
11025
+ /* @__PURE__ */ jsxs("div", { className: "twc-dt__cfg-foot", children: [
11026
+ /* @__PURE__ */ jsx("button", { type: "button", className: "twc-dt__cfg-btn", onClick: () => {
11027
+ setBatchEdit(null);
11028
+ closeBatchEdit();
11029
+ }, children: "Cancel" }),
11030
+ /* @__PURE__ */ jsxs("button", { type: "button", className: "twc-dt__cfg-btn", "data-primary": "true", disabled: pickedCols.length === 0, onClick: applyBatchEdit, children: [
11031
+ "Apply to ",
11032
+ selected.size
11033
+ ] })
10994
11034
  ] })
10995
- ] })
10996
- ] }) : null,
11035
+ ] });
11036
+ })() : null,
10997
11037
  colMenu && menuPos ? /* @__PURE__ */ jsxs(
10998
11038
  "div",
10999
11039
  {