twico-ui 1.24.0 → 1.26.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 +19 -0
- package/dist/index.cjs +282 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -3
- package/dist/index.d.ts +21 -3
- package/dist/index.mjs +282 -28
- package/dist/index.mjs.map +1 -1
- package/llms.txt +3 -1
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,25 @@
|
|
|
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.26.0](https://github.com/EncryptionL/twico-ui/compare/v1.25.0...v1.26.0) (2026-08-03)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **datatable:** bottom-left popover grip, cell range selection, keyboard clipboard ([#316](https://github.com/EncryptionL/twico-ui/issues/316), [#317](https://github.com/EncryptionL/twico-ui/issues/317), [#318](https://github.com/EncryptionL/twico-ui/issues/318)) ([0c1a583](https://github.com/EncryptionL/twico-ui/commit/0c1a583ef8b983410547f7f1fb92f8660ec926c3))
|
|
11
|
+
|
|
12
|
+
# [1.25.0](https://github.com/EncryptionL/twico-ui/compare/v1.24.0...v1.25.0) (2026-08-03)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* **datatable:** commit the default cell editor value on click-away instead of discarding ([#313](https://github.com/EncryptionL/twico-ui/issues/313)) ([08c0050](https://github.com/EncryptionL/twico-ui/commit/08c00501fc20a4deb5e0bf35be0ab9aa46a7c477))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Features
|
|
21
|
+
|
|
22
|
+
* **datatable:** default resizablePopovers to true; decouple it from resizableFilters ([#314](https://github.com/EncryptionL/twico-ui/issues/314)) ([c509956](https://github.com/EncryptionL/twico-ui/commit/c509956b11648e6d138777843514ff9a5ec09e85)), closes [#304](https://github.com/EncryptionL/twico-ui/issues/304) [#292](https://github.com/EncryptionL/twico-ui/issues/292)
|
|
23
|
+
|
|
5
24
|
# [1.24.0](https://github.com/EncryptionL/twico-ui/compare/v1.23.0...v1.24.0) (2026-07-31)
|
|
6
25
|
|
|
7
26
|
|
package/dist/index.cjs
CHANGED
|
@@ -8461,6 +8461,8 @@ th.twc-dt__rownum .twc-dt__th-inner { padding-inline: 8px; gap: 2px; justify-con
|
|
|
8461
8461
|
.twc-dt__row[data-selectable="true"] { cursor: pointer; }
|
|
8462
8462
|
.twc-dt__row[data-active="true"] .twc-dt__td { background: var(--color-primary-subtle); box-shadow: inset 0 0 0 9999px transparent; }
|
|
8463
8463
|
.twc-dt__row[data-active="true"] .twc-dt__td[data-pin] { background: var(--color-primary-subtle); }
|
|
8464
|
+
/* #317: data-cell-selected fills the range rectangle; data-cell-active keeps the ring on the moving corner. */
|
|
8465
|
+
.twc-dt__td[data-cell-selected="true"] { background: var(--color-primary-subtle); }
|
|
8464
8466
|
.twc-dt__td[data-cell-active="true"] { box-shadow: inset 0 0 0 2px var(--color-primary); background: var(--color-primary-subtle); }
|
|
8465
8467
|
.twc-dt__row:last-child .twc-dt__td { border-bottom: none; }
|
|
8466
8468
|
|
|
@@ -8615,6 +8617,9 @@ th.twc-dt__rownum .twc-dt__th-inner { padding-inline: 8px; gap: 2px; justify-con
|
|
|
8615
8617
|
.twc-dt__pop-grip:hover, .twc-dt__pop-grip:focus-visible, .twc-dt__pop-grip[data-active="true"] { opacity: 1; }
|
|
8616
8618
|
.twc-dt__pop-grip:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 1px; }
|
|
8617
8619
|
[dir="rtl"] .twc-dt__pop-grip { cursor: nesw-resize; }
|
|
8620
|
+
/* #316: a second grip in the bottom-inline-start corner grows a right-anchored panel toward the empty side (right edge pinned). */
|
|
8621
|
+
.twc-dt__pop-grip[data-side="start"] { inset-inline-start: 2px; inset-inline-end: auto; cursor: nesw-resize; background: linear-gradient(225deg, transparent 0 55%, var(--color-border-strong) 55% 64%, transparent 64% 73%, var(--color-border-strong) 73% 82%, transparent 82%); }
|
|
8622
|
+
[dir="rtl"] .twc-dt__pop-grip[data-side="start"] { cursor: nwse-resize; }
|
|
8618
8623
|
@media (pointer: coarse) {
|
|
8619
8624
|
.twc-dt__f-rz { width: 16px; inset-inline-end: -6px; opacity: 0.4; }
|
|
8620
8625
|
.twc-dt__pop-grip { width: 20px; height: 20px; opacity: 0.6; }
|
|
@@ -9169,6 +9174,8 @@ function Datatable({
|
|
|
9169
9174
|
onRowClick,
|
|
9170
9175
|
onCellClick,
|
|
9171
9176
|
onActiveCellChange,
|
|
9177
|
+
onCellSelectionChange,
|
|
9178
|
+
enableClipboard = false,
|
|
9172
9179
|
showAggregation = false,
|
|
9173
9180
|
ariaLabel = "Data table",
|
|
9174
9181
|
"aria-label": ariaLabelAttr,
|
|
@@ -9180,7 +9187,8 @@ function Datatable({
|
|
|
9180
9187
|
onQuickFilterChange,
|
|
9181
9188
|
toolbarActions,
|
|
9182
9189
|
resizableFilters = true,
|
|
9183
|
-
resizablePopovers =
|
|
9190
|
+
resizablePopovers = true,
|
|
9191
|
+
// #314: default-on (matches resizableFilters) — set false to opt a grid out
|
|
9184
9192
|
filterFieldMaxWidth,
|
|
9185
9193
|
rowPinning = false,
|
|
9186
9194
|
rowReorder = false,
|
|
@@ -9298,6 +9306,8 @@ function Datatable({
|
|
|
9298
9306
|
const [selected, setSelected] = React51__namespace.default.useState(() => /* @__PURE__ */ new Set());
|
|
9299
9307
|
const [activeRow, setActiveRow] = React51__namespace.default.useState(null);
|
|
9300
9308
|
const [activeCell, setActiveCell] = React51__namespace.default.useState(null);
|
|
9309
|
+
const [anchorCell, setAnchorCell] = React51__namespace.default.useState(null);
|
|
9310
|
+
const gridId = React51__namespace.default.useId();
|
|
9301
9311
|
const [internalPage, setInternalPage] = React51__namespace.default.useState(0);
|
|
9302
9312
|
const [internalRpp, setInternalRpp] = React51__namespace.default.useState(pageSize > 0 ? pageSize : 10);
|
|
9303
9313
|
const pageControlled = page !== void 0;
|
|
@@ -9397,6 +9407,7 @@ function Datatable({
|
|
|
9397
9407
|
for (const [id, sz] of Object.entries(s.popoverSizes)) {
|
|
9398
9408
|
if (!sz || typeof sz !== "object" || typeof sz.w !== "number" || typeof sz.h !== "number") continue;
|
|
9399
9409
|
next[id] = { w: clampNum(sz.w, popMinW(id), fMaxW()), h: clampNum(sz.h, 120, fMaxH()) };
|
|
9410
|
+
if (typeof sz.left === "number") next[id].left = clampNum(sz.left, 8, fMaxW());
|
|
9400
9411
|
}
|
|
9401
9412
|
if (Object.keys(next).length) setPopSizes(next);
|
|
9402
9413
|
}
|
|
@@ -9848,6 +9859,38 @@ function Datatable({
|
|
|
9848
9859
|
middleRows.forEach((r2, i) => m.set(keyOf(r2, keyIndex.get(keyOf(r2)) ?? i), i));
|
|
9849
9860
|
return m;
|
|
9850
9861
|
}, [middleRows, keyIndex]);
|
|
9862
|
+
const cellColIndex = React51__namespace.default.useCallback((field) => ordered.findIndex((c) => c.field === field), [ordered]);
|
|
9863
|
+
const cellRect = React51__namespace.default.useMemo(() => {
|
|
9864
|
+
if (selectionMode !== "cell" || !activeCell) return null;
|
|
9865
|
+
const ar = keyIndex.get(activeCell.key), ac = cellColIndex(activeCell.field);
|
|
9866
|
+
if (ar == null || ac < 0) return null;
|
|
9867
|
+
const anchor = anchorCell || activeCell;
|
|
9868
|
+
const nr = keyIndex.get(anchor.key), nc = cellColIndex(anchor.field);
|
|
9869
|
+
const r0 = nr == null ? ar : Math.min(ar, nr), r1 = nr == null ? ar : Math.max(ar, nr);
|
|
9870
|
+
const c0 = nc < 0 ? ac : Math.min(ac, nc), c1 = nc < 0 ? ac : Math.max(ac, nc);
|
|
9871
|
+
return { r0, r1, c0, c1 };
|
|
9872
|
+
}, [selectionMode, activeCell, anchorCell, keyIndex, cellColIndex]);
|
|
9873
|
+
const activeCellId = React51__namespace.default.useMemo(() => {
|
|
9874
|
+
if (selectionMode !== "cell" || !activeCell) return void 0;
|
|
9875
|
+
const ar = keyIndex.get(activeCell.key), ac = cellColIndex(activeCell.field);
|
|
9876
|
+
return ar == null || ac < 0 ? void 0 : `${gridId}-${ar}-${ac}`;
|
|
9877
|
+
}, [selectionMode, activeCell, keyIndex, cellColIndex, gridId]);
|
|
9878
|
+
const onCellSelRef = React51__namespace.default.useRef(onCellSelectionChange);
|
|
9879
|
+
onCellSelRef.current = onCellSelectionChange;
|
|
9880
|
+
const prevSelSigRef = React51__namespace.default.useRef("");
|
|
9881
|
+
React51__namespace.default.useEffect(() => {
|
|
9882
|
+
const cb = onCellSelRef.current;
|
|
9883
|
+
if (selectionMode !== "cell" || !cb) return;
|
|
9884
|
+
const cells = [];
|
|
9885
|
+
if (cellRect) for (let r2 = cellRect.r0; r2 <= cellRect.r1; r2++) for (let c = cellRect.c0; c <= cellRect.c1; c++) {
|
|
9886
|
+
const row = leafRows[r2], col = ordered[c];
|
|
9887
|
+
if (row && col) cells.push({ key: keyOf(row, r2), field: col.field });
|
|
9888
|
+
}
|
|
9889
|
+
const sig = cells.map((x) => `${x.key}|${x.field}`).join(",");
|
|
9890
|
+
if (sig === prevSelSigRef.current) return;
|
|
9891
|
+
prevSelSigRef.current = sig;
|
|
9892
|
+
cb(cells);
|
|
9893
|
+
}, [cellRect, selectionMode, leafRows, ordered]);
|
|
9851
9894
|
const canReorderRows = rowReorder && !activeGroupBy.length && !sort;
|
|
9852
9895
|
function onRowDrop(targetKey) {
|
|
9853
9896
|
const fromKey = rowDrag.from;
|
|
@@ -10260,6 +10303,7 @@ function Datatable({
|
|
|
10260
10303
|
const next = { key: k, field: col.field };
|
|
10261
10304
|
setActiveCell(next);
|
|
10262
10305
|
setActiveRow(k);
|
|
10306
|
+
if (!(e.shiftKey && anchorCell)) setAnchorCell(next);
|
|
10263
10307
|
onCellClick?.(row[col.field], row, col.field);
|
|
10264
10308
|
onActiveCellChange?.(next);
|
|
10265
10309
|
}
|
|
@@ -10271,6 +10315,24 @@ function Datatable({
|
|
|
10271
10315
|
if (editing) return;
|
|
10272
10316
|
const td = e.target.closest(".twc-dt__td[data-r]");
|
|
10273
10317
|
if (!td) return;
|
|
10318
|
+
if (enableClipboard && selectionMode === "cell" && (e.ctrlKey || e.metaKey)) {
|
|
10319
|
+
const k = e.key.toLowerCase();
|
|
10320
|
+
if (k === "c") {
|
|
10321
|
+
e.preventDefault();
|
|
10322
|
+
copySelection(false);
|
|
10323
|
+
return;
|
|
10324
|
+
}
|
|
10325
|
+
if (k === "x") {
|
|
10326
|
+
e.preventDefault();
|
|
10327
|
+
copySelection(true);
|
|
10328
|
+
return;
|
|
10329
|
+
}
|
|
10330
|
+
if (k === "v") {
|
|
10331
|
+
e.preventDefault();
|
|
10332
|
+
pasteSelection();
|
|
10333
|
+
return;
|
|
10334
|
+
}
|
|
10335
|
+
}
|
|
10274
10336
|
let r2 = +td.getAttribute("data-r"), c = +td.getAttribute("data-c");
|
|
10275
10337
|
const maxR = leafRows.length - 1, maxC = ordered.length - 1;
|
|
10276
10338
|
let handled = true;
|
|
@@ -10323,6 +10385,16 @@ function Datatable({
|
|
|
10323
10385
|
e.preventDefault();
|
|
10324
10386
|
setFocus({ r: r2, c });
|
|
10325
10387
|
focusCell(r2, c);
|
|
10388
|
+
if (selectionMode === "cell") {
|
|
10389
|
+
const nk = keyOf(leafRows[r2], r2), col = ordered[c];
|
|
10390
|
+
if (col) {
|
|
10391
|
+
const nc = { key: nk, field: col.field };
|
|
10392
|
+
setActiveCell(nc);
|
|
10393
|
+
setActiveRow(nk);
|
|
10394
|
+
if (!e.shiftKey) setAnchorCell(nc);
|
|
10395
|
+
onActiveCellChange?.(nc);
|
|
10396
|
+
}
|
|
10397
|
+
}
|
|
10326
10398
|
}
|
|
10327
10399
|
}
|
|
10328
10400
|
React51__namespace.default.useEffect(() => {
|
|
@@ -10439,11 +10511,149 @@ function Datatable({
|
|
|
10439
10511
|
setBatchEdit(null);
|
|
10440
10512
|
closeBatchEdit();
|
|
10441
10513
|
}
|
|
10514
|
+
const [clipboardMsg, setClipboardMsg] = React51__namespace.default.useState("");
|
|
10515
|
+
const clipboardRef = React51__namespace.default.useRef(null);
|
|
10516
|
+
const copyTypeOf = (col) => col && col.copyType != null ? col.copyType : col && col.type === "number" ? "number" : "text";
|
|
10517
|
+
const writeClipboard = (text) => {
|
|
10518
|
+
const execFallback = () => {
|
|
10519
|
+
try {
|
|
10520
|
+
if (typeof document !== "undefined") {
|
|
10521
|
+
const ta = document.createElement("textarea");
|
|
10522
|
+
ta.value = text;
|
|
10523
|
+
ta.style.position = "fixed";
|
|
10524
|
+
ta.style.opacity = "0";
|
|
10525
|
+
document.body.appendChild(ta);
|
|
10526
|
+
ta.focus();
|
|
10527
|
+
ta.select();
|
|
10528
|
+
document.execCommand("copy");
|
|
10529
|
+
document.body.removeChild(ta);
|
|
10530
|
+
}
|
|
10531
|
+
} catch {
|
|
10532
|
+
}
|
|
10533
|
+
};
|
|
10534
|
+
try {
|
|
10535
|
+
if (typeof navigator !== "undefined" && navigator.clipboard && navigator.clipboard.writeText) return navigator.clipboard.writeText(text).catch(execFallback);
|
|
10536
|
+
} catch {
|
|
10537
|
+
}
|
|
10538
|
+
execFallback();
|
|
10539
|
+
return Promise.resolve();
|
|
10540
|
+
};
|
|
10541
|
+
const writeCellPatches = (patchByKey) => {
|
|
10542
|
+
if (!patchByKey.size) return 0;
|
|
10543
|
+
const changed = [];
|
|
10544
|
+
const nextAll = rows.map((r2, i) => {
|
|
10545
|
+
const k = keyOf(r2, i);
|
|
10546
|
+
const patch = patchByKey.get(k);
|
|
10547
|
+
if (!patch) return r2;
|
|
10548
|
+
const updated = { ...r2, ...patch };
|
|
10549
|
+
changed.push([updated, r2, patch]);
|
|
10550
|
+
return updated;
|
|
10551
|
+
});
|
|
10552
|
+
changed.forEach(([updated, orig, patch]) => {
|
|
10553
|
+
for (const f of Object.keys(patch)) onRowUpdate?.(updated, orig, f);
|
|
10554
|
+
});
|
|
10555
|
+
if (onRowsChange && changed.length) onRowsChange(nextAll);
|
|
10556
|
+
return changed.length;
|
|
10557
|
+
};
|
|
10558
|
+
const rectCells = () => {
|
|
10559
|
+
if (!cellRect) return null;
|
|
10560
|
+
const out = [];
|
|
10561
|
+
for (let r2 = cellRect.r0; r2 <= cellRect.r1; r2++) {
|
|
10562
|
+
const row = leafRows[r2];
|
|
10563
|
+
if (!row) continue;
|
|
10564
|
+
const line = [];
|
|
10565
|
+
for (let c = cellRect.c0; c <= cellRect.c1; c++) {
|
|
10566
|
+
const col = ordered[c];
|
|
10567
|
+
if (col) line.push({ key: keyOf(row, r2), field: col.field, col, value: getColVal(col, row) });
|
|
10568
|
+
}
|
|
10569
|
+
out.push(line);
|
|
10570
|
+
}
|
|
10571
|
+
return out.length ? out : null;
|
|
10572
|
+
};
|
|
10573
|
+
const copySelection = (cut) => {
|
|
10574
|
+
const grid = rectCells();
|
|
10575
|
+
if (!grid) return;
|
|
10576
|
+
const tsv = grid.map((line) => line.map((cell) => cell.value == null ? "" : String(cell.value)).join(" ")).join("\n");
|
|
10577
|
+
clipboardRef.current = { tsv, colTypes: grid[0].map((cell) => copyTypeOf(cell.col)), matrix: grid.map((line) => line.map((cell) => cell.value)) };
|
|
10578
|
+
writeClipboard(tsv);
|
|
10579
|
+
const n = grid.reduce((a, line) => a + line.length, 0);
|
|
10580
|
+
if (cut) {
|
|
10581
|
+
const patchByKey = /* @__PURE__ */ new Map();
|
|
10582
|
+
let clearable = 0;
|
|
10583
|
+
for (const line of grid) for (const cell of line) {
|
|
10584
|
+
if (!isColEditable(cell.col)) continue;
|
|
10585
|
+
clearable++;
|
|
10586
|
+
const p = patchByKey.get(cell.key) || {};
|
|
10587
|
+
p[cell.field] = cell.col.type === "number" ? null : "";
|
|
10588
|
+
patchByKey.set(cell.key, p);
|
|
10589
|
+
}
|
|
10590
|
+
writeCellPatches(patchByKey);
|
|
10591
|
+
setClipboardMsg(`Cut ${clearable} cell${clearable === 1 ? "" : "s"}${clearable < n ? `, ${n - clearable} read-only kept` : ""}`);
|
|
10592
|
+
} else {
|
|
10593
|
+
setClipboardMsg(`Copied ${n} cell${n === 1 ? "" : "s"}`);
|
|
10594
|
+
}
|
|
10595
|
+
};
|
|
10596
|
+
const pasteSelection = async () => {
|
|
10597
|
+
if (!cellRect) return;
|
|
10598
|
+
let text = null;
|
|
10599
|
+
try {
|
|
10600
|
+
if (typeof navigator !== "undefined" && navigator.clipboard && navigator.clipboard.readText) text = await navigator.clipboard.readText();
|
|
10601
|
+
} catch {
|
|
10602
|
+
setClipboardMsg("Paste unavailable \u2014 clipboard permission denied");
|
|
10603
|
+
return;
|
|
10604
|
+
}
|
|
10605
|
+
if (text == null) {
|
|
10606
|
+
setClipboardMsg("Nothing to paste");
|
|
10607
|
+
return;
|
|
10608
|
+
}
|
|
10609
|
+
const inApp = clipboardRef.current && clipboardRef.current.tsv === text ? clipboardRef.current : null;
|
|
10610
|
+
let matrix;
|
|
10611
|
+
if (inApp) matrix = inApp.matrix;
|
|
10612
|
+
else {
|
|
10613
|
+
const lines = text.replace(/\r\n?/g, "\n").split("\n");
|
|
10614
|
+
if (lines.length && lines[lines.length - 1] === "") lines.pop();
|
|
10615
|
+
matrix = lines.map((line) => line.split(" "));
|
|
10616
|
+
}
|
|
10617
|
+
const patchByKey = /* @__PURE__ */ new Map();
|
|
10618
|
+
let written = 0, skipped = 0;
|
|
10619
|
+
for (let i = 0; i < matrix.length; i++) {
|
|
10620
|
+
const rr = cellRect.r0 + i;
|
|
10621
|
+
const row = leafRows[rr];
|
|
10622
|
+
if (!row) break;
|
|
10623
|
+
for (let j = 0; j < matrix[i].length; j++) {
|
|
10624
|
+
const cc = cellRect.c0 + j;
|
|
10625
|
+
const col = ordered[cc];
|
|
10626
|
+
if (!col) break;
|
|
10627
|
+
if (!isColEditable(col)) {
|
|
10628
|
+
skipped++;
|
|
10629
|
+
continue;
|
|
10630
|
+
}
|
|
10631
|
+
if (inApp && inApp.colTypes[j] != null && inApp.colTypes[j] !== copyTypeOf(col)) {
|
|
10632
|
+
skipped++;
|
|
10633
|
+
continue;
|
|
10634
|
+
}
|
|
10635
|
+
let v = matrix[i][j];
|
|
10636
|
+
if (col.type === "number") {
|
|
10637
|
+
v = v === "" ? null : Number(v);
|
|
10638
|
+
if (Number.isNaN(v)) v = null;
|
|
10639
|
+
}
|
|
10640
|
+
const k = keyOf(row, rr);
|
|
10641
|
+
const p = patchByKey.get(k) || {};
|
|
10642
|
+
p[col.field] = v;
|
|
10643
|
+
patchByKey.set(k, p);
|
|
10644
|
+
written++;
|
|
10645
|
+
}
|
|
10646
|
+
}
|
|
10647
|
+
writeCellPatches(patchByKey);
|
|
10648
|
+
setClipboardMsg(`Pasted ${written} cell${written === 1 ? "" : "s"}${skipped ? `, ${skipped} skipped (incompatible column)` : ""}`);
|
|
10649
|
+
};
|
|
10442
10650
|
React51__namespace.default.useEffect(() => {
|
|
10443
10651
|
if (!editing) return;
|
|
10444
10652
|
const onDown = (e) => {
|
|
10445
10653
|
if (e.target.closest(".twc-dt__editor-wrap") || e.target.closest(".twc-pop")) return;
|
|
10446
|
-
|
|
10654
|
+
const col = colByField[editing.field];
|
|
10655
|
+
if (col && col.renderEditCell) setEditing(null);
|
|
10656
|
+
else commitEdit();
|
|
10447
10657
|
};
|
|
10448
10658
|
document.addEventListener("mousedown", onDown, true);
|
|
10449
10659
|
return () => document.removeEventListener("mousedown", onDown, true);
|
|
@@ -10693,6 +10903,7 @@ function Datatable({
|
|
|
10693
10903
|
if (node) {
|
|
10694
10904
|
node.style.removeProperty("--twc-dt-pop-w");
|
|
10695
10905
|
node.style.removeProperty("--twc-dt-pop-h");
|
|
10906
|
+
node.style.removeProperty("left");
|
|
10696
10907
|
node.removeAttribute("data-pop-sized");
|
|
10697
10908
|
}
|
|
10698
10909
|
setPopSizes((s) => {
|
|
@@ -10702,7 +10913,7 @@ function Datatable({
|
|
|
10702
10913
|
return n;
|
|
10703
10914
|
});
|
|
10704
10915
|
};
|
|
10705
|
-
const startPopResize = (id) => (e) => {
|
|
10916
|
+
const startPopResize = (id, side) => (e) => {
|
|
10706
10917
|
if (e.button != null && e.button !== 0) return;
|
|
10707
10918
|
e.preventDefault();
|
|
10708
10919
|
e.stopPropagation();
|
|
@@ -10711,21 +10922,27 @@ function Datatable({
|
|
|
10711
10922
|
const grip = e.currentTarget;
|
|
10712
10923
|
const startX = e.clientX, startY = e.clientY;
|
|
10713
10924
|
const r2 = pop.getBoundingClientRect();
|
|
10714
|
-
const startW = r2.width, startH = r2.height;
|
|
10925
|
+
const startW = r2.width, startH = r2.height, startLeft = r2.left, startRight = r2.right;
|
|
10715
10926
|
const dir = typeof getComputedStyle !== "undefined" && getComputedStyle(pop).direction === "rtl" ? -1 : 1;
|
|
10927
|
+
const toStart = side === "start";
|
|
10716
10928
|
pop.style.setProperty("--twc-dt-pop-w", `${startW}px`);
|
|
10717
10929
|
pop.style.setProperty("--twc-dt-pop-h", `${startH}px`);
|
|
10718
10930
|
grip.setAttribute("data-active", "true");
|
|
10719
10931
|
pop.setAttribute("data-pop-sized", "");
|
|
10720
|
-
let lw = startW, lh = startH;
|
|
10932
|
+
let lw = startW, lh = startH, ll = startLeft;
|
|
10721
10933
|
const onMove = (ev) => {
|
|
10722
|
-
|
|
10934
|
+
const dw = toStart ? (startX - ev.clientX) * dir : (ev.clientX - startX) * dir;
|
|
10935
|
+
lw = clampNum(startW + dw, popMinW(id), fMaxW());
|
|
10723
10936
|
lh = clampNum(startH + (ev.clientY - startY), 120, fMaxH());
|
|
10724
10937
|
pop.style.setProperty("--twc-dt-pop-w", `${lw}px`);
|
|
10725
10938
|
pop.style.setProperty("--twc-dt-pop-h", `${lh}px`);
|
|
10939
|
+
if (toStart) {
|
|
10940
|
+
ll = Math.max(8, startRight - lw);
|
|
10941
|
+
pop.style.left = `${ll}px`;
|
|
10942
|
+
}
|
|
10726
10943
|
};
|
|
10727
10944
|
const onUp = () => {
|
|
10728
|
-
setPopSizes((s) => ({ ...s, [id]: { w: lw, h: lh } }));
|
|
10945
|
+
setPopSizes((s) => ({ ...s, [id]: { ...s[id] || {}, w: lw, h: lh, ...toStart ? { left: ll } : {} } }));
|
|
10729
10946
|
grip.removeAttribute("data-active");
|
|
10730
10947
|
window.removeEventListener("pointermove", onMove);
|
|
10731
10948
|
window.removeEventListener("pointerup", onUp);
|
|
@@ -10733,7 +10950,7 @@ function Datatable({
|
|
|
10733
10950
|
window.addEventListener("pointermove", onMove);
|
|
10734
10951
|
window.addEventListener("pointerup", onUp);
|
|
10735
10952
|
};
|
|
10736
|
-
const onPopGripKey = (id) => (e) => {
|
|
10953
|
+
const onPopGripKey = (id, side) => (e) => {
|
|
10737
10954
|
if (e.key === "Enter" || e.key === " " || e.key === "Backspace") {
|
|
10738
10955
|
e.preventDefault();
|
|
10739
10956
|
clearPop(id, e.currentTarget.closest(".twc-dt__pop"));
|
|
@@ -10758,26 +10975,55 @@ function Datatable({
|
|
|
10758
10975
|
w = fMaxW();
|
|
10759
10976
|
h = fMaxH();
|
|
10760
10977
|
}
|
|
10761
|
-
|
|
10978
|
+
w = clampNum(w, popMinW(id), fMaxW());
|
|
10979
|
+
h = clampNum(h, 120, fMaxH());
|
|
10980
|
+
const left = side === "start" ? Math.max(8, r2.right - w) : void 0;
|
|
10981
|
+
setPopSizes((s) => ({ ...s, [id]: { ...s[id] || {}, w, h, ...left != null ? { left } : {} } }));
|
|
10762
10982
|
};
|
|
10763
|
-
const popStyle = (id) =>
|
|
10764
|
-
|
|
10765
|
-
|
|
10766
|
-
{
|
|
10767
|
-
|
|
10768
|
-
|
|
10769
|
-
|
|
10770
|
-
|
|
10771
|
-
|
|
10772
|
-
|
|
10773
|
-
|
|
10774
|
-
|
|
10775
|
-
|
|
10776
|
-
|
|
10983
|
+
const popStyle = (id) => {
|
|
10984
|
+
const s = popSizes[id];
|
|
10985
|
+
if (!s) return null;
|
|
10986
|
+
const st = { "--twc-dt-pop-w": `${s.w}px`, "--twc-dt-pop-h": `${s.h}px` };
|
|
10987
|
+
if (s.left != null) st.left = `${s.left}px`;
|
|
10988
|
+
return st;
|
|
10989
|
+
};
|
|
10990
|
+
const popGrip = (id) => resizablePopovers ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
10991
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10992
|
+
"span",
|
|
10993
|
+
{
|
|
10994
|
+
className: "twc-dt__pop-grip",
|
|
10995
|
+
"data-side": "end",
|
|
10996
|
+
role: "slider",
|
|
10997
|
+
tabIndex: 0,
|
|
10998
|
+
"aria-label": "Resize panel (Enter or double-click to reset)",
|
|
10999
|
+
"aria-valuetext": popSizes[id] ? `Width ${Math.round(popSizes[id].w)} pixels, height ${Math.round(popSizes[id].h)} pixels` : "Default size",
|
|
11000
|
+
onPointerDown: startPopResize(id, "end"),
|
|
11001
|
+
onKeyDown: onPopGripKey(id, "end"),
|
|
11002
|
+
onDoubleClick: (e) => {
|
|
11003
|
+
e.preventDefault();
|
|
11004
|
+
clearPop(id, e.currentTarget.closest(".twc-dt__pop"));
|
|
11005
|
+
}
|
|
10777
11006
|
}
|
|
10778
|
-
|
|
10779
|
-
|
|
10780
|
-
|
|
11007
|
+
),
|
|
11008
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11009
|
+
"span",
|
|
11010
|
+
{
|
|
11011
|
+
className: "twc-dt__pop-grip",
|
|
11012
|
+
"data-side": "start",
|
|
11013
|
+
role: "slider",
|
|
11014
|
+
tabIndex: 0,
|
|
11015
|
+
"aria-label": "Resize panel leftward (Enter or double-click to reset)",
|
|
11016
|
+
"aria-valuetext": popSizes[id] ? `Width ${Math.round(popSizes[id].w)} pixels, height ${Math.round(popSizes[id].h)} pixels` : "Default size",
|
|
11017
|
+
onPointerDown: startPopResize(id, "start"),
|
|
11018
|
+
onKeyDown: onPopGripKey(id, "start"),
|
|
11019
|
+
onDoubleClick: (e) => {
|
|
11020
|
+
e.preventDefault();
|
|
11021
|
+
clearPop(id, e.currentTarget.closest(".twc-dt__pop"));
|
|
11022
|
+
}
|
|
11023
|
+
}
|
|
11024
|
+
)
|
|
11025
|
+
] }) : null;
|
|
11026
|
+
const fieldHandle = (field, rowIdx) => resizableFilters && rowIdx === 0 ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
10781
11027
|
"span",
|
|
10782
11028
|
{
|
|
10783
11029
|
className: "twc-dt__f-rz",
|
|
@@ -10932,22 +11178,27 @@ function Datatable({
|
|
|
10932
11178
|
const editable = isColEditable(c);
|
|
10933
11179
|
const isEditing = editing && editing.key === k && editing.field === c.field;
|
|
10934
11180
|
const cellActive = selectionMode === "cell" && activeCell && activeCell.key === k && activeCell.field === c.field;
|
|
11181
|
+
const cellSelected = selectionMode === "cell" && cellRect && ri >= cellRect.r0 && ri <= cellRect.r1 && ci >= cellRect.c0 && ci <= cellRect.c1;
|
|
11182
|
+
const cellId = selectionMode === "cell" ? `${gridId}-${ri}-${ci}` : void 0;
|
|
10935
11183
|
const display = c.valueFormatter ? c.valueFormatter(val, row) : val;
|
|
10936
11184
|
const cellTitle = !isActions && !c.renderCell && !isEditing && (typeof display === "string" || typeof display === "number") ? String(display) : void 0;
|
|
10937
11185
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
10938
11186
|
"td",
|
|
10939
11187
|
{
|
|
11188
|
+
id: cellId,
|
|
10940
11189
|
className: "twc-dt__td",
|
|
10941
11190
|
role: "gridcell",
|
|
10942
11191
|
"data-r": ri,
|
|
10943
11192
|
"data-c": ci,
|
|
10944
11193
|
"aria-colindex": ci + 1 + (checkboxSelection ? 1 : 0),
|
|
10945
11194
|
tabIndex: focus.r === ri && focus.c === ci ? 0 : -1,
|
|
11195
|
+
"aria-selected": cellSelected || void 0,
|
|
10946
11196
|
"data-num": c.type === "number" || void 0,
|
|
10947
11197
|
"data-actions": isActions || void 0,
|
|
10948
11198
|
"data-editable": editable && !isEditing || void 0,
|
|
10949
11199
|
"data-editing": isEditing || void 0,
|
|
10950
11200
|
"data-cell-active": cellActive || void 0,
|
|
11201
|
+
"data-cell-selected": cellSelected || void 0,
|
|
10951
11202
|
"data-wrap": wrapped.has(c.field) || void 0,
|
|
10952
11203
|
"data-pin": st.pin,
|
|
10953
11204
|
"data-pin-edge": st.edge,
|
|
@@ -11309,6 +11560,7 @@ function Datatable({
|
|
|
11309
11560
|
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-dt__groupbar-clear", onClick: () => setGroupBy([]), children: "Clear all" })
|
|
11310
11561
|
] }) : null,
|
|
11311
11562
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-dt__sr", role: "status", "aria-live": "polite", children: reorderMsg }),
|
|
11563
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "twc-dt__sr", role: "status", "aria-live": "polite", children: clipboardMsg }),
|
|
11312
11564
|
pivotActive ? renderPivot() : /* @__PURE__ */ jsxRuntime.jsx(
|
|
11313
11565
|
"div",
|
|
11314
11566
|
{
|
|
@@ -11330,6 +11582,8 @@ function Datatable({
|
|
|
11330
11582
|
"aria-label": ariaLabelAttr || ariaLabel,
|
|
11331
11583
|
"aria-rowcount": totalRows + 1,
|
|
11332
11584
|
"aria-colcount": ordered.length + (checkboxSelection ? 1 : 0),
|
|
11585
|
+
"aria-multiselectable": selectionMode === "cell" || void 0,
|
|
11586
|
+
"aria-activedescendant": activeCellId,
|
|
11333
11587
|
"aria-busy": loading || void 0,
|
|
11334
11588
|
onKeyDown: onGridKeyDown,
|
|
11335
11589
|
children: [
|
|
@@ -12191,7 +12445,7 @@ function Datatable({
|
|
|
12191
12445
|
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-dt__flogic-btn", "data-active": filterLogic === "and" || void 0, "aria-pressed": filterLogic === "and", onClick: () => setFilterLogic("and"), children: "AND" }),
|
|
12192
12446
|
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-dt__flogic-btn", "data-active": filterLogic === "or" || void 0, "aria-pressed": filterLogic === "or", onClick: () => setFilterLogic("or"), children: "OR" })
|
|
12193
12447
|
] }) : null,
|
|
12194
|
-
|
|
12448
|
+
resizableFilters && hasFilterSizeOverride ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-dt__link", onClick: resetFilterSizes, children: "Reset sizes" }) : null,
|
|
12195
12449
|
filters.length ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "twc-dt__link", onClick: () => setFilters([]), children: "Clear all" }) : null
|
|
12196
12450
|
] })
|
|
12197
12451
|
] }),
|
|
@@ -12271,7 +12525,7 @@ function Datatable({
|
|
|
12271
12525
|
/* @__PURE__ */ jsxRuntime.jsx(Svg, { d: I.plus, style: { color: "var(--color-primary)" } }),
|
|
12272
12526
|
" Add filter"
|
|
12273
12527
|
] }) }),
|
|
12274
|
-
resizableFilters
|
|
12528
|
+
resizableFilters ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
12275
12529
|
"span",
|
|
12276
12530
|
{
|
|
12277
12531
|
className: "twc-dt__pop-grip",
|