twico-ui 1.26.0 → 1.27.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 +7 -0
- package/dist/index.cjs +35 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.mjs +35 -4
- package/dist/index.mjs.map +1 -1
- package/llms.txt +3 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1956,8 +1956,15 @@ interface DatatableProps<T = any> extends Omit<React.HTMLAttributes<HTMLDivEleme
|
|
|
1956
1956
|
* TSV, Ctrl/Cmd+X cuts (copy then clear), Ctrl/Cmd+V pastes onto the target rectangle from the active
|
|
1957
1957
|
* cell. Paste is format-restricted by column `copyType` (incompatible cells are skipped) and commits
|
|
1958
1958
|
* through `onRowUpdate`/`onRowsChange` in one batched change; outcomes are announced via `aria-live`.
|
|
1959
|
-
*
|
|
1959
|
+
* Copy/cut/paste also show a brief **visible** in-grid confirmation toast + a flash on the affected cells
|
|
1960
|
+
* (#320), so sighted users get feedback too. @default false */
|
|
1960
1961
|
enableClipboard?: boolean;
|
|
1962
|
+
/** #320: fired on a clipboard copy/cut with the copied cells (row-major `{key,field}[]`) and `{ cut }`.
|
|
1963
|
+
* Use it to surface your own toast / integrate an app notification system. */
|
|
1964
|
+
onCellsCopy?: (cells: Array<{ key: string | number; field: string }>, meta: { cut: boolean }) => void;
|
|
1965
|
+
/** #320: fired after a clipboard paste with the outcome — `written` cells committed, `skipped` cells
|
|
1966
|
+
* rejected (read-only or incompatible `copyType`). */
|
|
1967
|
+
onCellsPaste?: (result: { written: number; skipped: number }) => void;
|
|
1961
1968
|
/** Show the **Aggregation** toolbar button and start with the totals row on. From that panel the user
|
|
1962
1969
|
* toggles totals and picks columns + functions (a column's `aggregation` prop seeds the initial choice);
|
|
1963
1970
|
* changing the prop re-applies it. When false (the default) the Aggregation button is hidden. @default false */
|
package/dist/index.d.ts
CHANGED
|
@@ -1956,8 +1956,15 @@ interface DatatableProps<T = any> extends Omit<React.HTMLAttributes<HTMLDivEleme
|
|
|
1956
1956
|
* TSV, Ctrl/Cmd+X cuts (copy then clear), Ctrl/Cmd+V pastes onto the target rectangle from the active
|
|
1957
1957
|
* cell. Paste is format-restricted by column `copyType` (incompatible cells are skipped) and commits
|
|
1958
1958
|
* through `onRowUpdate`/`onRowsChange` in one batched change; outcomes are announced via `aria-live`.
|
|
1959
|
-
*
|
|
1959
|
+
* Copy/cut/paste also show a brief **visible** in-grid confirmation toast + a flash on the affected cells
|
|
1960
|
+
* (#320), so sighted users get feedback too. @default false */
|
|
1960
1961
|
enableClipboard?: boolean;
|
|
1962
|
+
/** #320: fired on a clipboard copy/cut with the copied cells (row-major `{key,field}[]`) and `{ cut }`.
|
|
1963
|
+
* Use it to surface your own toast / integrate an app notification system. */
|
|
1964
|
+
onCellsCopy?: (cells: Array<{ key: string | number; field: string }>, meta: { cut: boolean }) => void;
|
|
1965
|
+
/** #320: fired after a clipboard paste with the outcome — `written` cells committed, `skipped` cells
|
|
1966
|
+
* rejected (read-only or incompatible `copyType`). */
|
|
1967
|
+
onCellsPaste?: (result: { written: number; skipped: number }) => void;
|
|
1961
1968
|
/** Show the **Aggregation** toolbar button and start with the totals row on. From that panel the user
|
|
1962
1969
|
* toggles totals and picks columns + functions (a column's `aggregation` prop seeds the initial choice);
|
|
1963
1970
|
* changing the prop re-applies it. When false (the default) the Aggregation button is hidden. @default false */
|
package/dist/index.mjs
CHANGED
|
@@ -8138,7 +8138,14 @@ var DT_CSS = `
|
|
|
8138
8138
|
/* Stay inside the parent even in a flex/grid container: min-width:0 lets a wide
|
|
8139
8139
|
grid shrink (default min-width:auto would force it to its content width and
|
|
8140
8140
|
overflow), so the internal __scroll handles horizontal scrolling. */
|
|
8141
|
-
width: 100%; min-width: 0; max-width: 100%; }
|
|
8141
|
+
width: 100%; min-width: 0; max-width: 100%; position: relative; }
|
|
8142
|
+
/* #320: visible clipboard feedback \u2014 a transient in-grid toast + a brief flash on the copied/pasted cells,
|
|
8143
|
+
alongside the SR-only aria-live message (position: relative on the root anchors the toast; it does NOT
|
|
8144
|
+
create a containing block for the position: fixed popovers, so those stay viewport-anchored). */
|
|
8145
|
+
.twc-dt__clip-toast { position: absolute; inset-block-end: 12px; inset-inline-end: 12px; z-index: 6; background: var(--color-text); color: var(--color-surface); font-size: var(--text-xs); font-weight: var(--font-semibold); padding: 6px 12px; border-radius: var(--radius-md); box-shadow: var(--shadow-md); pointer-events: none; }
|
|
8146
|
+
@keyframes twc-dt-copyflash { from { box-shadow: inset 0 0 0 2px var(--color-primary); } to { box-shadow: inset 0 0 0 2px transparent; } }
|
|
8147
|
+
.twc-dt__td[data-copied="true"] { animation: twc-dt-copyflash 1.3s var(--ease-standard); }
|
|
8148
|
+
@media (prefers-reduced-motion: reduce) { .twc-dt__td[data-copied="true"] { animation: none; } }
|
|
8142
8149
|
|
|
8143
8150
|
/* Toolbar */
|
|
8144
8151
|
.twc-dt__toolbar { position: relative; z-index: 10; display: flex; align-items: center; gap: 6px; padding: 8px 10px;
|
|
@@ -9155,6 +9162,8 @@ function Datatable({
|
|
|
9155
9162
|
onActiveCellChange,
|
|
9156
9163
|
onCellSelectionChange,
|
|
9157
9164
|
enableClipboard = false,
|
|
9165
|
+
onCellsCopy,
|
|
9166
|
+
onCellsPaste,
|
|
9158
9167
|
showAggregation = false,
|
|
9159
9168
|
ariaLabel = "Data table",
|
|
9160
9169
|
"aria-label": ariaLabelAttr,
|
|
@@ -10492,6 +10501,21 @@ function Datatable({
|
|
|
10492
10501
|
}
|
|
10493
10502
|
const [clipboardMsg, setClipboardMsg] = React51__default.useState("");
|
|
10494
10503
|
const clipboardRef = React51__default.useRef(null);
|
|
10504
|
+
const [clipFx, setClipFx] = React51__default.useState(null);
|
|
10505
|
+
const clipFxTimer = React51__default.useRef(null);
|
|
10506
|
+
const onCellsCopyRef = React51__default.useRef(onCellsCopy);
|
|
10507
|
+
onCellsCopyRef.current = onCellsCopy;
|
|
10508
|
+
const onCellsPasteRef = React51__default.useRef(onCellsPaste);
|
|
10509
|
+
onCellsPasteRef.current = onCellsPaste;
|
|
10510
|
+
React51__default.useEffect(() => () => {
|
|
10511
|
+
if (clipFxTimer.current) clearTimeout(clipFxTimer.current);
|
|
10512
|
+
}, []);
|
|
10513
|
+
const announceClip = (msg, rect) => {
|
|
10514
|
+
setClipboardMsg(msg);
|
|
10515
|
+
setClipFx({ msg, rect: rect || null });
|
|
10516
|
+
if (clipFxTimer.current) clearTimeout(clipFxTimer.current);
|
|
10517
|
+
clipFxTimer.current = setTimeout(() => setClipFx(null), 1600);
|
|
10518
|
+
};
|
|
10495
10519
|
const copyTypeOf = (col) => col && col.copyType != null ? col.copyType : col && col.type === "number" ? "number" : "text";
|
|
10496
10520
|
const writeClipboard = (text) => {
|
|
10497
10521
|
const execFallback = () => {
|
|
@@ -10556,6 +10580,7 @@ function Datatable({
|
|
|
10556
10580
|
clipboardRef.current = { tsv, colTypes: grid[0].map((cell) => copyTypeOf(cell.col)), matrix: grid.map((line) => line.map((cell) => cell.value)) };
|
|
10557
10581
|
writeClipboard(tsv);
|
|
10558
10582
|
const n = grid.reduce((a, line) => a + line.length, 0);
|
|
10583
|
+
onCellsCopyRef.current?.(grid.flatMap((line) => line.map((cell) => ({ key: cell.key, field: cell.field }))), { cut: !!cut });
|
|
10559
10584
|
if (cut) {
|
|
10560
10585
|
const patchByKey = /* @__PURE__ */ new Map();
|
|
10561
10586
|
let clearable = 0;
|
|
@@ -10567,9 +10592,9 @@ function Datatable({
|
|
|
10567
10592
|
patchByKey.set(cell.key, p);
|
|
10568
10593
|
}
|
|
10569
10594
|
writeCellPatches(patchByKey);
|
|
10570
|
-
|
|
10595
|
+
announceClip(`Cut ${clearable} cell${clearable === 1 ? "" : "s"}${clearable < n ? `, ${n - clearable} read-only kept` : ""}`, cellRect);
|
|
10571
10596
|
} else {
|
|
10572
|
-
|
|
10597
|
+
announceClip(`Copied ${n} cell${n === 1 ? "" : "s"}`, cellRect);
|
|
10573
10598
|
}
|
|
10574
10599
|
};
|
|
10575
10600
|
const pasteSelection = async () => {
|
|
@@ -10624,7 +10649,10 @@ function Datatable({
|
|
|
10624
10649
|
}
|
|
10625
10650
|
}
|
|
10626
10651
|
writeCellPatches(patchByKey);
|
|
10627
|
-
|
|
10652
|
+
onCellsPasteRef.current?.({ written, skipped });
|
|
10653
|
+
const maxCols = matrix.reduce((a, m) => Math.max(a, m.length), 0);
|
|
10654
|
+
const pasteRect = written ? { r0: cellRect.r0, c0: cellRect.c0, r1: Math.min(cellRect.r0 + matrix.length - 1, leafRows.length - 1), c1: Math.min(cellRect.c0 + maxCols - 1, ordered.length - 1) } : null;
|
|
10655
|
+
announceClip(`Pasted ${written} cell${written === 1 ? "" : "s"}${skipped ? `, ${skipped} skipped (incompatible column)` : ""}`, pasteRect);
|
|
10628
10656
|
};
|
|
10629
10657
|
React51__default.useEffect(() => {
|
|
10630
10658
|
if (!editing) return;
|
|
@@ -11158,6 +11186,7 @@ function Datatable({
|
|
|
11158
11186
|
const isEditing = editing && editing.key === k && editing.field === c.field;
|
|
11159
11187
|
const cellActive = selectionMode === "cell" && activeCell && activeCell.key === k && activeCell.field === c.field;
|
|
11160
11188
|
const cellSelected = selectionMode === "cell" && cellRect && ri >= cellRect.r0 && ri <= cellRect.r1 && ci >= cellRect.c0 && ci <= cellRect.c1;
|
|
11189
|
+
const cellFlash = clipFx && clipFx.rect && ri >= clipFx.rect.r0 && ri <= clipFx.rect.r1 && ci >= clipFx.rect.c0 && ci <= clipFx.rect.c1;
|
|
11161
11190
|
const cellId = selectionMode === "cell" ? `${gridId}-${ri}-${ci}` : void 0;
|
|
11162
11191
|
const display = c.valueFormatter ? c.valueFormatter(val, row) : val;
|
|
11163
11192
|
const cellTitle = !isActions && !c.renderCell && !isEditing && (typeof display === "string" || typeof display === "number") ? String(display) : void 0;
|
|
@@ -11178,6 +11207,7 @@ function Datatable({
|
|
|
11178
11207
|
"data-editing": isEditing || void 0,
|
|
11179
11208
|
"data-cell-active": cellActive || void 0,
|
|
11180
11209
|
"data-cell-selected": cellSelected || void 0,
|
|
11210
|
+
"data-copied": cellFlash || void 0,
|
|
11181
11211
|
"data-wrap": wrapped.has(c.field) || void 0,
|
|
11182
11212
|
"data-pin": st.pin,
|
|
11183
11213
|
"data-pin-edge": st.edge,
|
|
@@ -11540,6 +11570,7 @@ function Datatable({
|
|
|
11540
11570
|
] }) : null,
|
|
11541
11571
|
/* @__PURE__ */ jsx("div", { className: "twc-dt__sr", role: "status", "aria-live": "polite", children: reorderMsg }),
|
|
11542
11572
|
/* @__PURE__ */ jsx("div", { className: "twc-dt__sr", role: "status", "aria-live": "polite", children: clipboardMsg }),
|
|
11573
|
+
clipFx ? /* @__PURE__ */ jsx("div", { className: "twc-dt__clip-toast", "aria-hidden": "true", children: clipFx.msg }) : null,
|
|
11543
11574
|
pivotActive ? renderPivot() : /* @__PURE__ */ jsx(
|
|
11544
11575
|
"div",
|
|
11545
11576
|
{
|