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/CHANGELOG.md +14 -0
- package/dist/index.cjs +99 -59
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.mjs +99 -59
- package/dist/index.mjs.map +1 -1
- package/llms.txt +2 -0
- package/package.json +1 -1
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.16.0](https://github.com/EncryptionL/twico-ui/compare/v1.15.1...v1.16.0) (2026-07-15)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **datatable:** batch editor picks columns (searchable) + showBatchEdit/batchEditFields ([#244](https://github.com/EncryptionL/twico-ui/issues/244)) ([82b8806](https://github.com/EncryptionL/twico-ui/commit/82b8806f32f08153f15653de68bdb82ea2dc7ff3)), closes [#236](https://github.com/EncryptionL/twico-ui/issues/236)
|
|
11
|
+
|
|
12
|
+
## [1.15.1](https://github.com/EncryptionL/twico-ui/compare/v1.15.0...v1.15.1) (2026-07-14)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* **datatable:** keep pinned cells opaque in diff mode ([#242](https://github.com/EncryptionL/twico-ui/issues/242)) ([072b15e](https://github.com/EncryptionL/twico-ui/commit/072b15ea73443ad535b3f149d8b620618702b389)), closes [#239](https://github.com/EncryptionL/twico-ui/issues/239)
|
|
18
|
+
|
|
5
19
|
# [1.15.0](https://github.com/EncryptionL/twico-ui/compare/v1.14.1...v1.15.0) (2026-07-14)
|
|
6
20
|
|
|
7
21
|
|
package/dist/index.cjs
CHANGED
|
@@ -8328,12 +8328,17 @@ var DT_CSS = `
|
|
|
8328
8328
|
.twc-dt__cfg-btn:hover { background: var(--color-surface-sunken); }
|
|
8329
8329
|
.twc-dt__cfg-btn[data-primary="true"] { background: var(--color-primary); border-color: var(--color-primary); color: var(--color-primary-fg); }
|
|
8330
8330
|
.twc-dt__cfg-btn[data-primary="true"]:hover { background: var(--color-primary-hover); border-color: var(--color-primary-hover); }
|
|
8331
|
-
|
|
8332
|
-
.twc-dt__be-
|
|
8333
|
-
.twc-dt__be-
|
|
8334
|
-
.twc-dt__be-
|
|
8335
|
-
.twc-dt__be-check span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
8331
|
+
/* #244: batch editor = pick columns (searchable), then set a value for each. */
|
|
8332
|
+
.twc-dt__be-add { padding: 6px 6px 8px; border-bottom: var(--border-thin) solid var(--color-divider); }
|
|
8333
|
+
.twc-dt__be-row { display: flex; align-items: center; gap: 8px; padding: 5px 6px; }
|
|
8334
|
+
.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; }
|
|
8336
8335
|
.twc-dt__be-ctl { flex: 1; min-width: 0; }
|
|
8336
|
+
.twc-dt__be-x { flex: none; display: inline-grid; place-items: center; width: 24px; height: 24px; padding: 0; border: none;
|
|
8337
|
+
background: transparent; color: var(--color-text-subtle); cursor: pointer; border-radius: var(--radius-sm);
|
|
8338
|
+
transition: background-color var(--duration-fast), color var(--duration-fast); }
|
|
8339
|
+
.twc-dt__be-x:hover { background: var(--color-surface-sunken); color: var(--color-text); }
|
|
8340
|
+
.twc-dt__be-x svg { width: 14px; height: 14px; }
|
|
8341
|
+
.twc-dt__be-empty { padding: 14px 8px; font-size: var(--text-xs); color: var(--color-text-muted); text-align: center; }
|
|
8337
8342
|
|
|
8338
8343
|
/* Pinned rows */
|
|
8339
8344
|
.twc-dt__row[data-pinned-row] > .twc-dt__td { background: var(--color-primary-subtle); }
|
|
@@ -8348,6 +8353,12 @@ var DT_CSS = `
|
|
|
8348
8353
|
.twc-dt__row[data-op="added"] > .twc-dt__td { background: color-mix(in srgb, var(--color-success) 7%, transparent); }
|
|
8349
8354
|
.twc-dt__row[data-op="removed"] > .twc-dt__td { background: color-mix(in srgb, var(--color-danger) 7%, transparent); }
|
|
8350
8355
|
.twc-dt__row[data-op="modified"] > .twc-dt__td { background: color-mix(in srgb, var(--color-warning) 6%, transparent); }
|
|
8356
|
+
/* #242: pinned cells in a diff row must stay OPAQUE (composite the tint over the surface, not over
|
|
8357
|
+
transparent) or the cells scrolling under a sticky column bleed through. Specificity (0,4,0) wins
|
|
8358
|
+
over the (0,3,0) tint above and matches the non-pinned cells' effective color (tint over surface). */
|
|
8359
|
+
.twc-dt__row[data-op="added"] > .twc-dt__td[data-pin] { background: color-mix(in srgb, var(--color-success) 7%, var(--color-surface)); }
|
|
8360
|
+
.twc-dt__row[data-op="removed"] > .twc-dt__td[data-pin] { background: color-mix(in srgb, var(--color-danger) 7%, var(--color-surface)); }
|
|
8361
|
+
.twc-dt__row[data-op="modified"] > .twc-dt__td[data-pin] { background: color-mix(in srgb, var(--color-warning) 6%, var(--color-surface)); }
|
|
8351
8362
|
.twc-dt__diff-change { display: inline-flex; align-items: center; gap: 4px; min-width: 0; max-width: 100%; }
|
|
8352
8363
|
.twc-dt__diff-old { text-decoration: line-through; color: var(--color-text-subtle); }
|
|
8353
8364
|
.twc-dt__diff-new { color: var(--color-success-subtle-fg); font-weight: var(--font-medium); }
|
|
@@ -8998,6 +9009,8 @@ function Datatable({
|
|
|
8998
9009
|
onRowUpdate,
|
|
8999
9010
|
onRowsChange,
|
|
9000
9011
|
onBatchUpdate,
|
|
9012
|
+
showBatchEdit = true,
|
|
9013
|
+
batchEditFields = null,
|
|
9001
9014
|
showPageJumper = true,
|
|
9002
9015
|
selectionMode = "none",
|
|
9003
9016
|
onRowClick,
|
|
@@ -10037,9 +10050,14 @@ function Datatable({
|
|
|
10037
10050
|
cancelEdit();
|
|
10038
10051
|
}
|
|
10039
10052
|
}
|
|
10053
|
+
const batchEditKey = batchEditFields ? batchEditFields.join("\0") : "";
|
|
10040
10054
|
const batchEditableCols = React51__namespace.default.useMemo(
|
|
10041
|
-
() =>
|
|
10042
|
-
|
|
10055
|
+
() => {
|
|
10056
|
+
const base = cols.filter((c) => c.type !== "actions" && (c.editable ?? (editMode && c.editable !== false)));
|
|
10057
|
+
return batchEditFields ? base.filter((c) => batchEditFields.includes(c.field)) : base;
|
|
10058
|
+
},
|
|
10059
|
+
[cols, editMode, batchEditKey]
|
|
10060
|
+
// eslint-disable-line react-hooks/exhaustive-deps
|
|
10043
10061
|
);
|
|
10044
10062
|
const [batchEdit, setBatchEdit] = React51__namespace.default.useState(null);
|
|
10045
10063
|
const [batchEditPos, openBatchEdit, closeBatchEdit] = useFloating();
|
|
@@ -10474,7 +10492,7 @@ function Datatable({
|
|
|
10474
10492
|
" selected"
|
|
10475
10493
|
] }),
|
|
10476
10494
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dt__batch-actions", children: [
|
|
10477
|
-
batchEditableCols.length ? /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", className: "twc-dt__batch-btn", onClick: (e) => openBatchEditor(e.currentTarget), children: [
|
|
10495
|
+
showBatchEdit && batchEditableCols.length ? /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", className: "twc-dt__batch-btn", onClick: (e) => openBatchEditor(e.currentTarget), children: [
|
|
10478
10496
|
/* @__PURE__ */ jsxRuntime.jsx(Svg, { d: I.pencil }),
|
|
10479
10497
|
"Edit"
|
|
10480
10498
|
] }) : null,
|
|
@@ -10963,58 +10981,80 @@ function Datatable({
|
|
|
10963
10981
|
]
|
|
10964
10982
|
}
|
|
10965
10983
|
) : null,
|
|
10966
|
-
batchEdit && batchEditPos ?
|
|
10967
|
-
|
|
10968
|
-
|
|
10969
|
-
|
|
10970
|
-
|
|
10971
|
-
|
|
10972
|
-
|
|
10973
|
-
|
|
10974
|
-
|
|
10975
|
-
const
|
|
10976
|
-
|
|
10977
|
-
return
|
|
10978
|
-
|
|
10979
|
-
|
|
10980
|
-
|
|
10981
|
-
|
|
10982
|
-
|
|
10983
|
-
|
|
10984
|
-
|
|
10985
|
-
|
|
10986
|
-
|
|
10987
|
-
|
|
10988
|
-
|
|
10989
|
-
|
|
10990
|
-
|
|
10991
|
-
|
|
10992
|
-
|
|
10993
|
-
|
|
10994
|
-
|
|
10995
|
-
|
|
10996
|
-
{
|
|
10997
|
-
|
|
10998
|
-
|
|
10999
|
-
|
|
11000
|
-
|
|
11001
|
-
|
|
11002
|
-
|
|
11003
|
-
}
|
|
11004
|
-
|
|
11005
|
-
|
|
11006
|
-
|
|
11007
|
-
|
|
11008
|
-
|
|
11009
|
-
|
|
11010
|
-
|
|
11011
|
-
|
|
11012
|
-
|
|
11013
|
-
|
|
11014
|
-
|
|
10984
|
+
batchEdit && batchEditPos ? (() => {
|
|
10985
|
+
const pickedCols = Object.keys(batchEdit.fields).filter((f) => batchEdit.fields[f]).map((f) => batchEditableCols.find((c) => c.field === f)).filter(Boolean);
|
|
10986
|
+
const unpicked = batchEditableCols.filter((c) => !batchEdit.fields[c.field]);
|
|
10987
|
+
const addField = (f) => {
|
|
10988
|
+
if (f) setBatchEdit((b) => ({ ...b, fields: { ...b.fields, [f]: true }, values: { ...b.values, [f]: b.values[f] ?? "" } }));
|
|
10989
|
+
};
|
|
10990
|
+
const removeField = (f) => setBatchEdit((b) => {
|
|
10991
|
+
const fields = { ...b.fields };
|
|
10992
|
+
delete fields[f];
|
|
10993
|
+
const values = { ...b.values };
|
|
10994
|
+
delete values[f];
|
|
10995
|
+
return { ...b, fields, values };
|
|
10996
|
+
});
|
|
10997
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dt__pop twc-dt__cfg", style: { top: batchEditPos.top, left: batchEditPos.left, width: 320 }, children: [
|
|
10998
|
+
/* @__PURE__ */ jsxRuntime.jsx(Caret, { pos: batchEditPos }),
|
|
10999
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dt__cfg-head", children: [
|
|
11000
|
+
"Edit ",
|
|
11001
|
+
selected.size,
|
|
11002
|
+
" selected ",
|
|
11003
|
+
selected.size === 1 ? "row" : "rows"
|
|
11004
|
+
] }),
|
|
11005
|
+
unpicked.length ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-dt__be-add", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
11006
|
+
Select,
|
|
11007
|
+
{
|
|
11008
|
+
size: "sm",
|
|
11009
|
+
portal: true,
|
|
11010
|
+
searchable: true,
|
|
11011
|
+
placeholder: "Add a column\u2026",
|
|
11012
|
+
value: "",
|
|
11013
|
+
"aria-label": "Add a column to edit",
|
|
11014
|
+
options: unpicked.map((c) => ({ value: c.field, label: String(c.headerName ?? c.field) })),
|
|
11015
|
+
onChange: addField
|
|
11016
|
+
}
|
|
11017
|
+
) }) : null,
|
|
11018
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-dt__cfg-list", children: pickedCols.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-dt__be-empty", children: "Pick a column to set its value on all selected rows." }) : pickedCols.map((c) => {
|
|
11019
|
+
const opts = c.valueOptions ? c.valueOptions.map((o) => typeof o === "string" ? { value: o, label: o } : o) : null;
|
|
11020
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dt__be-row", children: [
|
|
11021
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "twc-dt__be-name", title: String(c.headerName ?? c.field), children: c.headerName }),
|
|
11022
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-dt__be-ctl", children: opts ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
11023
|
+
Select,
|
|
11024
|
+
{
|
|
11025
|
+
size: "sm",
|
|
11026
|
+
portal: true,
|
|
11027
|
+
searchable: true,
|
|
11028
|
+
placeholder: "New value\u2026",
|
|
11029
|
+
value: batchEdit.values[c.field] ?? "",
|
|
11030
|
+
options: opts,
|
|
11031
|
+
onChange: (v) => setBatchEdit((b) => ({ ...b, values: { ...b.values, [c.field]: v } }))
|
|
11032
|
+
}
|
|
11033
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
11034
|
+
Input,
|
|
11035
|
+
{
|
|
11036
|
+
size: "sm",
|
|
11037
|
+
type: c.type === "number" || c.type === "currency" ? "number" : "text",
|
|
11038
|
+
placeholder: "New value\u2026",
|
|
11039
|
+
value: batchEdit.values[c.field] ?? "",
|
|
11040
|
+
onChange: (e) => setBatchEdit((b) => ({ ...b, values: { ...b.values, [c.field]: e.target.value } }))
|
|
11041
|
+
}
|
|
11042
|
+
) }),
|
|
11043
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-dt__be-x", "aria-label": `Remove ${String(c.headerName ?? c.field)}`, onClick: () => removeField(c.field), children: /* @__PURE__ */ jsxRuntime.jsx(Svg, { d: I.x }) })
|
|
11044
|
+
] }, c.field);
|
|
11045
|
+
}) }),
|
|
11046
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twc-dt__cfg-foot", children: [
|
|
11047
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-dt__cfg-btn", onClick: () => {
|
|
11048
|
+
setBatchEdit(null);
|
|
11049
|
+
closeBatchEdit();
|
|
11050
|
+
}, children: "Cancel" }),
|
|
11051
|
+
/* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", className: "twc-dt__cfg-btn", "data-primary": "true", disabled: pickedCols.length === 0, onClick: applyBatchEdit, children: [
|
|
11052
|
+
"Apply to ",
|
|
11053
|
+
selected.size
|
|
11054
|
+
] })
|
|
11015
11055
|
] })
|
|
11016
|
-
] })
|
|
11017
|
-
|
|
11056
|
+
] });
|
|
11057
|
+
})() : null,
|
|
11018
11058
|
colMenu && menuPos ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
11019
11059
|
"div",
|
|
11020
11060
|
{
|