react-glide-table 1.1.5 → 1.1.6
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/README.md +38 -3
- package/dist/compound.cjs +372 -81
- package/dist/compound.d.cts +2 -2
- package/dist/compound.d.ts +2 -2
- package/dist/compound.js +375 -84
- package/dist/core.cjs +401 -76
- package/dist/core.d.cts +59 -6
- package/dist/core.d.ts +59 -6
- package/dist/core.js +395 -81
- package/dist/index.cjs +418 -83
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +414 -90
- package/dist/{types-BfthylVR.d.cts → types-DOLnknDe.d.cts} +52 -1
- package/dist/{types-BfthylVR.d.ts → types-DOLnknDe.d.ts} +52 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -32,10 +32,14 @@ __export(src_exports, {
|
|
|
32
32
|
applyFillData: () => applyFillData,
|
|
33
33
|
applySelectionUpdater: () => applySelectionUpdater,
|
|
34
34
|
buildColumnRowSpanMap: () => buildColumnRowSpanMap,
|
|
35
|
+
buildRowsPastePayload: () => buildRowsPastePayload,
|
|
35
36
|
canExpandRow: () => canExpandRow,
|
|
37
|
+
collectCopyRowEntries: () => collectCopyRowEntries,
|
|
38
|
+
collectCopyRows: () => collectCopyRows,
|
|
36
39
|
collectFillChanges: () => collectFillChanges,
|
|
37
40
|
collectRowSpanColumns: () => collectRowSpanColumns,
|
|
38
41
|
createTable: () => createTable,
|
|
42
|
+
flattenSubtreeRows: () => flattenSubtreeRows,
|
|
39
43
|
getCellEditDraftValue: () => getCellEditDraftValue,
|
|
40
44
|
getCellSelectionEdgeStyle: () => getCellSelectionEdgeStyle,
|
|
41
45
|
getColumnEditType: () => getColumnEditType,
|
|
@@ -43,17 +47,24 @@ __export(src_exports, {
|
|
|
43
47
|
hasCellSelectionEdges: () => hasCellSelectionEdges,
|
|
44
48
|
isCellInSelection: () => isCellInSelection,
|
|
45
49
|
isColumnEditable: () => isColumnEditable,
|
|
50
|
+
isEditablePasteTarget: () => isEditablePasteTarget,
|
|
46
51
|
measureMergedSpanRowHeights: () => measureMergedSpanRowHeights,
|
|
47
52
|
parseCellEditValue: () => parseCellEditValue,
|
|
53
|
+
parseClipboardTSV: () => parseClipboardTSV,
|
|
54
|
+
parseClipboardTSVWithDepths: () => parseClipboardTSVWithDepths,
|
|
48
55
|
resolveDataTableLabels: () => resolveDataTableLabels,
|
|
56
|
+
resolvePasteColumnIds: () => resolvePasteColumnIds,
|
|
49
57
|
resolveRowSelection: () => resolveRowSelection,
|
|
50
58
|
resolveRowSpanAt: () => resolveRowSpanAt,
|
|
51
59
|
rowRangeToHeightRatios: () => rowRangeToHeightRatios,
|
|
60
|
+
serializeCopyRowsToTSV: () => serializeCopyRowsToTSV,
|
|
61
|
+
serializeSelectionToTSV: () => serializeSelectionToTSV,
|
|
52
62
|
toggleExpandedRowId: () => toggleExpandedRowId,
|
|
53
63
|
useCellEdit: () => useCellEdit,
|
|
54
64
|
useCellSelection: () => useCellSelection,
|
|
55
65
|
useConvertTreeData: () => useConvertTreeData,
|
|
56
|
-
useGlideTable: () => useGlideTable
|
|
66
|
+
useGlideTable: () => useGlideTable,
|
|
67
|
+
writeSelectionToClipboard: () => writeSelectionToClipboard
|
|
57
68
|
});
|
|
58
69
|
module.exports = __toCommonJS(src_exports);
|
|
59
70
|
|
|
@@ -500,6 +511,127 @@ function hasCellSelectionEdges(style) {
|
|
|
500
511
|
);
|
|
501
512
|
}
|
|
502
513
|
|
|
514
|
+
// src/components/ui/table/features/cell-selection/copyData.ts
|
|
515
|
+
function formatCellValue(value) {
|
|
516
|
+
if (value === null || value === void 0) return "";
|
|
517
|
+
return String(value);
|
|
518
|
+
}
|
|
519
|
+
function getNestedValue(row, path) {
|
|
520
|
+
if (!path.includes(".")) return row[path];
|
|
521
|
+
return path.split(".").reduce((current, key) => {
|
|
522
|
+
if (current === null || current === void 0 || typeof current !== "object") {
|
|
523
|
+
return void 0;
|
|
524
|
+
}
|
|
525
|
+
return current[key];
|
|
526
|
+
}, row);
|
|
527
|
+
}
|
|
528
|
+
function readRowColumnValue(rowData, columnDef) {
|
|
529
|
+
if ("accessorFn" in columnDef && typeof columnDef.accessorFn === "function") {
|
|
530
|
+
return columnDef.accessorFn(rowData, 0);
|
|
531
|
+
}
|
|
532
|
+
if ("accessorKey" in columnDef && columnDef.accessorKey != null && columnDef.accessorKey !== "") {
|
|
533
|
+
return getNestedValue(rowData, String(columnDef.accessorKey));
|
|
534
|
+
}
|
|
535
|
+
return void 0;
|
|
536
|
+
}
|
|
537
|
+
function flattenSubtreeRows(row) {
|
|
538
|
+
const children = row.children;
|
|
539
|
+
if (!Array.isArray(children) || children.length === 0) return [];
|
|
540
|
+
const result = [];
|
|
541
|
+
const walk = (nodes) => {
|
|
542
|
+
for (const node of nodes) {
|
|
543
|
+
result.push(node);
|
|
544
|
+
const nested = node.children;
|
|
545
|
+
if (Array.isArray(nested) && nested.length > 0) {
|
|
546
|
+
walk(nested);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
};
|
|
550
|
+
walk(children);
|
|
551
|
+
return result;
|
|
552
|
+
}
|
|
553
|
+
function hasSubtree(row) {
|
|
554
|
+
const children = row.children;
|
|
555
|
+
return Array.isArray(children) && children.length > 0;
|
|
556
|
+
}
|
|
557
|
+
function getOriginalRowId(original) {
|
|
558
|
+
return String(original.id ?? original.uniqueId ?? "");
|
|
559
|
+
}
|
|
560
|
+
function getRowDepth(original) {
|
|
561
|
+
return typeof original.level === "number" ? original.level : 0;
|
|
562
|
+
}
|
|
563
|
+
function collectCopyRowEntries(visibleRows, bounds, mode = "visible") {
|
|
564
|
+
const { startRow, endRow } = bounds;
|
|
565
|
+
const result = [];
|
|
566
|
+
const includedOriginalIds = /* @__PURE__ */ new Set();
|
|
567
|
+
const appendSubtree = (node, depth) => {
|
|
568
|
+
const children = node.children;
|
|
569
|
+
if (!Array.isArray(children) || children.length === 0) return;
|
|
570
|
+
for (const child of children) {
|
|
571
|
+
const childId = getOriginalRowId(child);
|
|
572
|
+
if (!(childId && includedOriginalIds.has(childId))) {
|
|
573
|
+
result.push({ row: child, depth });
|
|
574
|
+
if (childId) includedOriginalIds.add(childId);
|
|
575
|
+
}
|
|
576
|
+
appendSubtree(child, depth + 1);
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
for (let rowIndex = startRow; rowIndex <= endRow; rowIndex += 1) {
|
|
580
|
+
const row = visibleRows[rowIndex];
|
|
581
|
+
if (!row) continue;
|
|
582
|
+
const originalId = getOriginalRowId(row.original);
|
|
583
|
+
if (originalId && includedOriginalIds.has(originalId)) continue;
|
|
584
|
+
const depth = getRowDepth(row.original);
|
|
585
|
+
result.push({ row: row.original, depth });
|
|
586
|
+
if (originalId) includedOriginalIds.add(originalId);
|
|
587
|
+
if (mode !== "subtree" || !hasSubtree(row.original)) continue;
|
|
588
|
+
appendSubtree(row.original, depth + 1);
|
|
589
|
+
}
|
|
590
|
+
return result;
|
|
591
|
+
}
|
|
592
|
+
function collectCopyRows(visibleRows, bounds, mode = "visible") {
|
|
593
|
+
return collectCopyRowEntries(visibleRows, bounds, mode).map((entry) => entry.row);
|
|
594
|
+
}
|
|
595
|
+
function serializeCopyRowsToTSV(copyRows, visibleRows, bounds, depths) {
|
|
596
|
+
if (copyRows.length === 0) return "";
|
|
597
|
+
const { startCol, endCol } = bounds;
|
|
598
|
+
const columnCells = visibleRows[0]?.getVisibleCells().slice(startCol, endCol + 1) ?? [];
|
|
599
|
+
if (columnCells.length === 0) return "";
|
|
600
|
+
const resolvedDepths = depths && depths.length === copyRows.length ? depths : copyRows.map((row) => getRowDepth(row));
|
|
601
|
+
const minDepth = Math.min(...resolvedDepths);
|
|
602
|
+
return copyRows.map((rowData, index) => {
|
|
603
|
+
const relativeDepth = Math.max(0, (resolvedDepths[index] ?? 0) - minDepth);
|
|
604
|
+
const line = columnCells.map(
|
|
605
|
+
(cell) => formatCellValue(
|
|
606
|
+
readRowColumnValue(
|
|
607
|
+
rowData,
|
|
608
|
+
cell.column.columnDef
|
|
609
|
+
)
|
|
610
|
+
)
|
|
611
|
+
).join(" ");
|
|
612
|
+
return `${" ".repeat(relativeDepth)}${line}`;
|
|
613
|
+
}).join("\n");
|
|
614
|
+
}
|
|
615
|
+
function serializeSelectionToTSV(visibleRows, bounds, mode = "visible") {
|
|
616
|
+
const entries = collectCopyRowEntries(visibleRows, bounds, mode);
|
|
617
|
+
return serializeCopyRowsToTSV(
|
|
618
|
+
entries.map((entry) => entry.row),
|
|
619
|
+
visibleRows,
|
|
620
|
+
bounds,
|
|
621
|
+
entries.map((entry) => entry.depth)
|
|
622
|
+
);
|
|
623
|
+
}
|
|
624
|
+
async function writeSelectionToClipboard(visibleRows, bounds, mode = "visible") {
|
|
625
|
+
const text = serializeSelectionToTSV(visibleRows, bounds, mode);
|
|
626
|
+
if (!text) return false;
|
|
627
|
+
try {
|
|
628
|
+
await navigator.clipboard.writeText(text);
|
|
629
|
+
} catch {
|
|
630
|
+
return false;
|
|
631
|
+
}
|
|
632
|
+
return true;
|
|
633
|
+
}
|
|
634
|
+
|
|
503
635
|
// src/components/ui/table/features/cell-selection/fillData.ts
|
|
504
636
|
function getColumnAccessorKey2(columnDef) {
|
|
505
637
|
if ("accessorKey" in columnDef && columnDef.accessorKey) {
|
|
@@ -556,15 +688,103 @@ function hasFillExtension(sourceBounds, fillBounds) {
|
|
|
556
688
|
return fillBounds.startRow < sourceBounds.startRow || fillBounds.endRow > sourceBounds.endRow || fillBounds.startCol < sourceBounds.startCol || fillBounds.endCol > sourceBounds.endCol;
|
|
557
689
|
}
|
|
558
690
|
|
|
691
|
+
// src/components/ui/table/features/cell-selection/pasteData.ts
|
|
692
|
+
function countLeadingEmptyCells(cells) {
|
|
693
|
+
let depth = 0;
|
|
694
|
+
while (depth < cells.length && cells[depth] === "") {
|
|
695
|
+
depth += 1;
|
|
696
|
+
}
|
|
697
|
+
return depth;
|
|
698
|
+
}
|
|
699
|
+
function looksLikeSubtreeIndentation(leadingEmptyCounts) {
|
|
700
|
+
if (leadingEmptyCounts.length === 0) return false;
|
|
701
|
+
const firstDepth = leadingEmptyCounts[0] ?? 0;
|
|
702
|
+
if (firstDepth !== 0) return false;
|
|
703
|
+
return leadingEmptyCounts.some((depth) => depth > 0);
|
|
704
|
+
}
|
|
705
|
+
function parseClipboardTSV(text) {
|
|
706
|
+
return parseClipboardTSVWithDepths(text).values;
|
|
707
|
+
}
|
|
708
|
+
function parseClipboardTSVWithDepths(text) {
|
|
709
|
+
if (!text) return { values: [], depths: [] };
|
|
710
|
+
const normalized = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
711
|
+
const withoutTrailing = normalized.replace(/\n+$/, "");
|
|
712
|
+
if (!withoutTrailing) return { values: [], depths: [] };
|
|
713
|
+
const rows = withoutTrailing.split("\n").map((line) => line.split(" "));
|
|
714
|
+
const leadingEmptyCounts = rows.map(countLeadingEmptyCells);
|
|
715
|
+
const treatAsDepth = looksLikeSubtreeIndentation(leadingEmptyCounts);
|
|
716
|
+
const values = [];
|
|
717
|
+
const depths = [];
|
|
718
|
+
for (let index = 0; index < rows.length; index += 1) {
|
|
719
|
+
const cells = rows[index] ?? [];
|
|
720
|
+
const depth = leadingEmptyCounts[index] ?? 0;
|
|
721
|
+
if (treatAsDepth) {
|
|
722
|
+
values.push(cells.slice(depth));
|
|
723
|
+
depths.push(depth);
|
|
724
|
+
} else {
|
|
725
|
+
values.push(cells);
|
|
726
|
+
depths.push(0);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
return { values, depths };
|
|
730
|
+
}
|
|
731
|
+
function resolvePasteColumnIds(rows, startCol, width) {
|
|
732
|
+
if (width <= 0) return [];
|
|
733
|
+
const cells = rows[0]?.getVisibleCells() ?? [];
|
|
734
|
+
const columnIds = [];
|
|
735
|
+
for (let offset = 0; offset < width; offset += 1) {
|
|
736
|
+
const cell = cells[startCol + offset];
|
|
737
|
+
if (!cell) break;
|
|
738
|
+
columnIds.push(cell.column.id);
|
|
739
|
+
}
|
|
740
|
+
return columnIds;
|
|
741
|
+
}
|
|
742
|
+
function buildRowsPastePayload(rows, startRow, startCol, text, mode, endRow = startRow) {
|
|
743
|
+
const { values, depths } = parseClipboardTSVWithDepths(text);
|
|
744
|
+
if (values.length === 0) return null;
|
|
745
|
+
const width = Math.max(...values.map((row) => row.length), 0);
|
|
746
|
+
if (width === 0) return null;
|
|
747
|
+
const columnIds = resolvePasteColumnIds(rows, startCol, width);
|
|
748
|
+
if (columnIds.length === 0) return null;
|
|
749
|
+
const rowIds = [];
|
|
750
|
+
for (let offset = 0; offset < values.length; offset += 1) {
|
|
751
|
+
const row = rows[startRow + offset];
|
|
752
|
+
if (!row) break;
|
|
753
|
+
rowIds.push(row.id);
|
|
754
|
+
}
|
|
755
|
+
const anchorRow = rows[endRow] ?? rows[startRow];
|
|
756
|
+
return {
|
|
757
|
+
mode,
|
|
758
|
+
startRow,
|
|
759
|
+
startCol,
|
|
760
|
+
endRow,
|
|
761
|
+
rowIds,
|
|
762
|
+
anchorRowId: anchorRow?.id ?? "",
|
|
763
|
+
columnIds,
|
|
764
|
+
values,
|
|
765
|
+
depths
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
function isEditablePasteTarget(target) {
|
|
769
|
+
if (!(target instanceof HTMLElement)) return false;
|
|
770
|
+
const tag = target.tagName;
|
|
771
|
+
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
|
|
772
|
+
return Boolean(target.isContentEditable);
|
|
773
|
+
}
|
|
774
|
+
|
|
559
775
|
// src/components/ui/table/features/cell-selection/useCellSelection.ts
|
|
560
776
|
function useCellSelection({
|
|
561
777
|
data,
|
|
562
778
|
rows,
|
|
563
779
|
enabled = true,
|
|
780
|
+
enableSubtreeCopy = false,
|
|
781
|
+
enableInsertPaste = true,
|
|
564
782
|
onDataChange,
|
|
565
|
-
onBatchChange
|
|
783
|
+
onBatchChange,
|
|
784
|
+
onRowsPaste
|
|
566
785
|
}) {
|
|
567
786
|
const [dragState, setDragState] = (0, import_react2.useState)(INITIAL_DRAG_STATE);
|
|
787
|
+
const pendingPasteModeRef = (0, import_react2.useRef)(null);
|
|
568
788
|
const cellSelectionBounds = getCellSelectionBounds(dragState.start, dragState.end);
|
|
569
789
|
const activeSelectionBounds = enabled ? getActiveSelectionBounds(dragState, cellSelectionBounds) : null;
|
|
570
790
|
const handleCellMouseDown = (0, import_react2.useCallback)(
|
|
@@ -618,21 +838,113 @@ function useCellSelection({
|
|
|
618
838
|
setDragState(INITIAL_DRAG_STATE);
|
|
619
839
|
}
|
|
620
840
|
}, [enabled]);
|
|
841
|
+
const copySelection = (0, import_react2.useCallback)(
|
|
842
|
+
async (options) => {
|
|
843
|
+
if (!enabled || !activeSelectionBounds) return false;
|
|
844
|
+
const mode = options?.includeDescendants && enableSubtreeCopy ? "subtree" : "visible";
|
|
845
|
+
return writeSelectionToClipboard(rows, activeSelectionBounds, mode);
|
|
846
|
+
},
|
|
847
|
+
[activeSelectionBounds, enableSubtreeCopy, enabled, rows]
|
|
848
|
+
);
|
|
621
849
|
(0, import_react2.useEffect)(() => {
|
|
622
850
|
if (!enabled) return;
|
|
623
851
|
const handleKeyDown = (e) => {
|
|
624
|
-
if (
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
}
|
|
852
|
+
if (!activeSelectionBounds) return;
|
|
853
|
+
if (!(e.ctrlKey || e.metaKey)) return;
|
|
854
|
+
const isSubtreeShortcut = enableSubtreeCopy && e.shiftKey && e.key.toLowerCase() === "c";
|
|
855
|
+
const isVisibleCopyShortcut = !e.shiftKey && e.key.toLowerCase() === "c";
|
|
856
|
+
if (!isSubtreeShortcut && !isVisibleCopyShortcut) return;
|
|
857
|
+
e.preventDefault();
|
|
858
|
+
void copySelection({ includeDescendants: isSubtreeShortcut });
|
|
632
859
|
};
|
|
633
860
|
window.addEventListener("keydown", handleKeyDown);
|
|
634
861
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
635
|
-
}, [activeSelectionBounds,
|
|
862
|
+
}, [activeSelectionBounds, copySelection, enableSubtreeCopy, enabled]);
|
|
863
|
+
const emitRowsPaste = (0, import_react2.useCallback)(
|
|
864
|
+
(text, mode) => {
|
|
865
|
+
if (!onRowsPaste || !activeSelectionBounds) return false;
|
|
866
|
+
const payload = buildRowsPastePayload(
|
|
867
|
+
rows,
|
|
868
|
+
activeSelectionBounds.startRow,
|
|
869
|
+
activeSelectionBounds.startCol,
|
|
870
|
+
text,
|
|
871
|
+
mode,
|
|
872
|
+
activeSelectionBounds.endRow
|
|
873
|
+
);
|
|
874
|
+
if (!payload) return false;
|
|
875
|
+
onRowsPaste(payload);
|
|
876
|
+
return true;
|
|
877
|
+
},
|
|
878
|
+
[activeSelectionBounds, onRowsPaste, rows]
|
|
879
|
+
);
|
|
880
|
+
(0, import_react2.useEffect)(() => {
|
|
881
|
+
if (!enabled || !onRowsPaste) return;
|
|
882
|
+
const pasteHandledRef = { current: false };
|
|
883
|
+
const ignoreNextPasteRef = { current: false };
|
|
884
|
+
const handleKeyDown = (e) => {
|
|
885
|
+
if (!activeSelectionBounds) return;
|
|
886
|
+
if (!(e.ctrlKey || e.metaKey)) return;
|
|
887
|
+
if (e.key.toLowerCase() !== "v") return;
|
|
888
|
+
if (isEditablePasteTarget(e.target) || isEditablePasteTarget(document.activeElement)) {
|
|
889
|
+
return;
|
|
890
|
+
}
|
|
891
|
+
if (e.shiftKey && !enableInsertPaste) {
|
|
892
|
+
ignoreNextPasteRef.current = true;
|
|
893
|
+
pendingPasteModeRef.current = null;
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
const mode = e.shiftKey ? "insert" : "overwrite";
|
|
897
|
+
pasteHandledRef.current = false;
|
|
898
|
+
ignoreNextPasteRef.current = false;
|
|
899
|
+
pendingPasteModeRef.current = mode;
|
|
900
|
+
void (async () => {
|
|
901
|
+
try {
|
|
902
|
+
const text = await navigator.clipboard.readText();
|
|
903
|
+
if (pasteHandledRef.current) return;
|
|
904
|
+
if (pendingPasteModeRef.current !== mode) return;
|
|
905
|
+
if (!text) return;
|
|
906
|
+
pasteHandledRef.current = true;
|
|
907
|
+
emitRowsPaste(text, mode);
|
|
908
|
+
pendingPasteModeRef.current = null;
|
|
909
|
+
} catch {
|
|
910
|
+
}
|
|
911
|
+
})();
|
|
912
|
+
};
|
|
913
|
+
const handlePaste = (e) => {
|
|
914
|
+
if (!activeSelectionBounds) return;
|
|
915
|
+
if (isEditablePasteTarget(e.target) || isEditablePasteTarget(document.activeElement)) {
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
918
|
+
if (ignoreNextPasteRef.current) {
|
|
919
|
+
ignoreNextPasteRef.current = false;
|
|
920
|
+
pendingPasteModeRef.current = null;
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
923
|
+
const mode = pendingPasteModeRef.current ?? "overwrite";
|
|
924
|
+
if (pasteHandledRef.current) {
|
|
925
|
+
e.preventDefault();
|
|
926
|
+
return;
|
|
927
|
+
}
|
|
928
|
+
const text = e.clipboardData?.getData("text/plain");
|
|
929
|
+
if (text == null || text === "") return;
|
|
930
|
+
pasteHandledRef.current = true;
|
|
931
|
+
e.preventDefault();
|
|
932
|
+
emitRowsPaste(text, mode);
|
|
933
|
+
pendingPasteModeRef.current = null;
|
|
934
|
+
};
|
|
935
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
936
|
+
window.addEventListener("paste", handlePaste);
|
|
937
|
+
return () => {
|
|
938
|
+
window.removeEventListener("keydown", handleKeyDown);
|
|
939
|
+
window.removeEventListener("paste", handlePaste);
|
|
940
|
+
};
|
|
941
|
+
}, [
|
|
942
|
+
activeSelectionBounds,
|
|
943
|
+
emitRowsPaste,
|
|
944
|
+
enableInsertPaste,
|
|
945
|
+
enabled,
|
|
946
|
+
onRowsPaste
|
|
947
|
+
]);
|
|
636
948
|
(0, import_react2.useEffect)(() => {
|
|
637
949
|
if (!enabled) return;
|
|
638
950
|
const handleMouseUp = () => {
|
|
@@ -678,7 +990,8 @@ function useCellSelection({
|
|
|
678
990
|
activeSelectionBounds,
|
|
679
991
|
handleCellMouseDown,
|
|
680
992
|
handleCellMouseEnter,
|
|
681
|
-
handleFillHandleMouseDown
|
|
993
|
+
handleFillHandleMouseDown,
|
|
994
|
+
copySelection
|
|
682
995
|
};
|
|
683
996
|
}
|
|
684
997
|
|
|
@@ -760,15 +1073,16 @@ var useConvertTreeData = ({
|
|
|
760
1073
|
children: [],
|
|
761
1074
|
processed: false
|
|
762
1075
|
}));
|
|
763
|
-
const
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
1076
|
+
const findNearestPrecedingParent = (index, parentKey) => {
|
|
1077
|
+
for (let i = index - 1; i >= 0; i -= 1) {
|
|
1078
|
+
const candidate = dataWithLevels[i];
|
|
1079
|
+
if (!candidate) continue;
|
|
1080
|
+
if (getFieldValue(candidate, toggleField) === parentKey) {
|
|
1081
|
+
return candidate;
|
|
1082
|
+
}
|
|
769
1083
|
}
|
|
770
|
-
|
|
771
|
-
}
|
|
1084
|
+
return void 0;
|
|
1085
|
+
};
|
|
772
1086
|
const rootItems = [];
|
|
773
1087
|
dataWithLevels.forEach((item) => {
|
|
774
1088
|
if (!getFieldValue(item, childField)) {
|
|
@@ -776,29 +1090,18 @@ var useConvertTreeData = ({
|
|
|
776
1090
|
item.processed = true;
|
|
777
1091
|
}
|
|
778
1092
|
});
|
|
779
|
-
dataWithLevels.forEach((item) => {
|
|
1093
|
+
dataWithLevels.forEach((item, index) => {
|
|
780
1094
|
const parentKey = getFieldValue(item, childField);
|
|
781
1095
|
if (!parentKey || item.processed) return;
|
|
782
|
-
const
|
|
783
|
-
|
|
784
|
-
);
|
|
785
|
-
if (parentItems.length > 0) {
|
|
786
|
-
const parent = parentItems[0];
|
|
1096
|
+
const parent = findNearestPrecedingParent(index, parentKey);
|
|
1097
|
+
if (parent) {
|
|
787
1098
|
item.level = parent.level + 1;
|
|
788
1099
|
parent.children.push(item);
|
|
789
1100
|
item.processed = true;
|
|
790
|
-
|
|
791
|
-
const otherParents = itemMap.get(String(parentKey)) || [];
|
|
792
|
-
if (otherParents.length > 0) {
|
|
793
|
-
const parent = otherParents[0];
|
|
794
|
-
item.level = parent.level + 1;
|
|
795
|
-
parent.children.push(item);
|
|
796
|
-
item.processed = true;
|
|
797
|
-
} else {
|
|
798
|
-
rootItems.push(item);
|
|
799
|
-
item.processed = true;
|
|
800
|
-
}
|
|
1101
|
+
return;
|
|
801
1102
|
}
|
|
1103
|
+
rootItems.push(item);
|
|
1104
|
+
item.processed = true;
|
|
802
1105
|
});
|
|
803
1106
|
return rootItems;
|
|
804
1107
|
}, [enabled, data, toggleField, childField, flattenField]);
|
|
@@ -823,16 +1126,23 @@ var useConvertTreeData = ({
|
|
|
823
1126
|
return result;
|
|
824
1127
|
};
|
|
825
1128
|
const flattenedData = flatten(processedData, [], 0);
|
|
826
|
-
flattenedData.forEach((item) => {
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
(parent) => getFieldValue(parent, toggleField) === getFieldValue(item, childField)
|
|
830
|
-
);
|
|
831
|
-
const parentAmount = parentItem ? Number(getFieldValue(parentItem, qtyField) ?? 1) : 1;
|
|
832
|
-
item.parentCount = parentAmount || 1;
|
|
833
|
-
} else {
|
|
1129
|
+
flattenedData.forEach((item, index) => {
|
|
1130
|
+
const parentKey = getFieldValue(item, childField);
|
|
1131
|
+
if (!parentKey) {
|
|
834
1132
|
item.parentCount = 1;
|
|
1133
|
+
return;
|
|
835
1134
|
}
|
|
1135
|
+
let parentItem;
|
|
1136
|
+
for (let i = index - 1; i >= 0; i -= 1) {
|
|
1137
|
+
const candidate = flattenedData[i];
|
|
1138
|
+
if (!candidate) continue;
|
|
1139
|
+
if (getFieldValue(candidate, toggleField) === parentKey) {
|
|
1140
|
+
parentItem = candidate;
|
|
1141
|
+
break;
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
const parentAmount = parentItem ? Number(getFieldValue(parentItem, qtyField) ?? 1) : 1;
|
|
1145
|
+
item.parentCount = parentAmount || 1;
|
|
836
1146
|
});
|
|
837
1147
|
return flattenedData;
|
|
838
1148
|
}, [
|
|
@@ -979,6 +1289,10 @@ function useGlideTable(options) {
|
|
|
979
1289
|
expandedRows: controlledExpandedRows,
|
|
980
1290
|
onExpandedRowsChange,
|
|
981
1291
|
preventExpand = false,
|
|
1292
|
+
enableSubtreeCopy,
|
|
1293
|
+
onCopyActionsReady,
|
|
1294
|
+
onRowsPaste,
|
|
1295
|
+
enableInsertPaste,
|
|
982
1296
|
enableVirtualization = true,
|
|
983
1297
|
estimateRowHeight = DATA_TABLE_ROW_HEIGHT,
|
|
984
1298
|
virtualOverscan = DATA_TABLE_VIRTUAL_OVERSCAN
|
|
@@ -993,12 +1307,12 @@ function useGlideTable(options) {
|
|
|
993
1307
|
};
|
|
994
1308
|
}, [labelsProp, emptyText, loadingText, selectionLabel]);
|
|
995
1309
|
const enableExpand = Boolean(toggleField);
|
|
1310
|
+
const resolvedEnableSubtreeCopy = enableSubtreeCopy ?? enableExpand;
|
|
996
1311
|
const [internalRowSelection, setInternalRowSelection] = (0, import_react4.useState)({});
|
|
997
1312
|
const [internalExpandedRows, setInternalExpandedRows] = (0, import_react4.useState)(
|
|
998
1313
|
() => /* @__PURE__ */ new Set()
|
|
999
1314
|
);
|
|
1000
1315
|
const [hoveredRowIndex, setHoveredRowIndex] = (0, import_react4.useState)(null);
|
|
1001
|
-
const [hoveredGroupKey, setHoveredGroupKey] = (0, import_react4.useState)(null);
|
|
1002
1316
|
const scrollRef = (0, import_react4.useRef)(null);
|
|
1003
1317
|
const shouldVirtualize = enableVirtualization && !enableRowSpan;
|
|
1004
1318
|
(0, import_react4.useEffect)(() => {
|
|
@@ -1062,6 +1376,7 @@ function useGlideTable(options) {
|
|
|
1062
1376
|
return collectRowSpanColumns(columns);
|
|
1063
1377
|
}, [enableRowSpan, columns]);
|
|
1064
1378
|
const primaryRowSpanKey = rowSpanColumnKeys[0]?.rowSpanKey;
|
|
1379
|
+
const primaryRowSpanColumnId = rowSpanColumnKeys[0]?.columnId;
|
|
1065
1380
|
const columnRowSpanMap = (0, import_react4.useMemo)(
|
|
1066
1381
|
() => buildColumnRowSpanMap(tableData, rowSpanColumnKeys),
|
|
1067
1382
|
[tableData, rowSpanColumnKeys]
|
|
@@ -1080,27 +1395,29 @@ function useGlideTable(options) {
|
|
|
1080
1395
|
const totalSize = rowVirtualizer.getTotalSize();
|
|
1081
1396
|
const paddingTop = virtualRows.length > 0 ? virtualRows[0]?.start ?? 0 : 0;
|
|
1082
1397
|
const paddingBottom = virtualRows.length > 0 ? totalSize - (virtualRows[virtualRows.length - 1]?.end ?? 0) : 0;
|
|
1083
|
-
const
|
|
1084
|
-
|
|
1085
|
-
const keys = /* @__PURE__ */ new Set();
|
|
1398
|
+
const selectedRowIndices = (0, import_react4.useMemo)(() => {
|
|
1399
|
+
const indices = /* @__PURE__ */ new Set();
|
|
1086
1400
|
for (const selectedRow of selectedRows) {
|
|
1087
|
-
|
|
1088
|
-
if (value !== null && value !== void 0) keys.add(String(value));
|
|
1401
|
+
indices.add(selectedRow.index);
|
|
1089
1402
|
}
|
|
1090
|
-
return
|
|
1091
|
-
}, [
|
|
1403
|
+
return indices;
|
|
1404
|
+
}, [selectedRows]);
|
|
1092
1405
|
const {
|
|
1093
1406
|
dragState,
|
|
1094
1407
|
activeSelectionBounds,
|
|
1095
1408
|
handleCellMouseDown,
|
|
1096
1409
|
handleCellMouseEnter,
|
|
1097
|
-
handleFillHandleMouseDown
|
|
1410
|
+
handleFillHandleMouseDown,
|
|
1411
|
+
copySelection
|
|
1098
1412
|
} = useCellSelection({
|
|
1099
1413
|
data: tableData,
|
|
1100
1414
|
rows,
|
|
1101
1415
|
enabled: enableCellSelection,
|
|
1416
|
+
enableSubtreeCopy: resolvedEnableSubtreeCopy,
|
|
1417
|
+
enableInsertPaste: enableInsertPaste ?? true,
|
|
1102
1418
|
onDataChange,
|
|
1103
|
-
onBatchChange
|
|
1419
|
+
onBatchChange,
|
|
1420
|
+
onRowsPaste
|
|
1104
1421
|
});
|
|
1105
1422
|
const {
|
|
1106
1423
|
editingCell,
|
|
@@ -1122,22 +1439,10 @@ function useGlideTable(options) {
|
|
|
1122
1439
|
);
|
|
1123
1440
|
const clearHover = (0, import_react4.useCallback)(() => {
|
|
1124
1441
|
setHoveredRowIndex(null);
|
|
1125
|
-
setHoveredGroupKey(null);
|
|
1126
1442
|
}, []);
|
|
1127
|
-
const handleRowHover = (0, import_react4.useCallback)(
|
|
1128
|
-
(rowIndex
|
|
1129
|
-
|
|
1130
|
-
if (!primaryRowSpanKey) {
|
|
1131
|
-
setHoveredGroupKey(null);
|
|
1132
|
-
return;
|
|
1133
|
-
}
|
|
1134
|
-
const groupValue = rowData[primaryRowSpanKey];
|
|
1135
|
-
setHoveredGroupKey(
|
|
1136
|
-
groupValue === null || groupValue === void 0 ? null : String(groupValue)
|
|
1137
|
-
);
|
|
1138
|
-
},
|
|
1139
|
-
[primaryRowSpanKey]
|
|
1140
|
-
);
|
|
1443
|
+
const handleRowHover = (0, import_react4.useCallback)((rowIndex, _rowData) => {
|
|
1444
|
+
setHoveredRowIndex(rowIndex);
|
|
1445
|
+
}, []);
|
|
1141
1446
|
const handleToggleSelect = (0, import_react4.useCallback)(
|
|
1142
1447
|
(row) => {
|
|
1143
1448
|
if (!row.getCanSelect()) return;
|
|
@@ -1160,10 +1465,10 @@ function useGlideTable(options) {
|
|
|
1160
1465
|
rowSpan: {
|
|
1161
1466
|
enableRowSpan,
|
|
1162
1467
|
primaryRowSpanKey,
|
|
1468
|
+
primaryRowSpanColumnId,
|
|
1163
1469
|
columnRowSpanMap,
|
|
1164
1470
|
hoveredRowIndex,
|
|
1165
|
-
|
|
1166
|
-
selectedGroupKeys,
|
|
1471
|
+
selectedRowIndices,
|
|
1167
1472
|
onRowHover: handleRowHover
|
|
1168
1473
|
},
|
|
1169
1474
|
selection: {
|
|
@@ -1201,10 +1506,10 @@ function useGlideTable(options) {
|
|
|
1201
1506
|
}, [
|
|
1202
1507
|
enableRowSpan,
|
|
1203
1508
|
primaryRowSpanKey,
|
|
1509
|
+
primaryRowSpanColumnId,
|
|
1204
1510
|
columnRowSpanMap,
|
|
1205
1511
|
hoveredRowIndex,
|
|
1206
|
-
|
|
1207
|
-
selectedGroupKeys,
|
|
1512
|
+
selectedRowIndices,
|
|
1208
1513
|
handleRowHover,
|
|
1209
1514
|
rowSelectionMode,
|
|
1210
1515
|
selectOnRowClick,
|
|
@@ -1230,6 +1535,14 @@ function useGlideTable(options) {
|
|
|
1230
1535
|
labels.expandRow,
|
|
1231
1536
|
labels.collapseRow
|
|
1232
1537
|
]);
|
|
1538
|
+
const copySelectionRef = (0, import_react4.useRef)(copySelection);
|
|
1539
|
+
(0, import_react4.useEffect)(() => {
|
|
1540
|
+
copySelectionRef.current = copySelection;
|
|
1541
|
+
}, [copySelection]);
|
|
1542
|
+
const stableCopySelection = (0, import_react4.useCallback)((options2) => copySelectionRef.current(options2), []);
|
|
1543
|
+
(0, import_react4.useEffect)(() => {
|
|
1544
|
+
onCopyActionsReady?.({ copySelection: stableCopySelection });
|
|
1545
|
+
}, [onCopyActionsReady, stableCopySelection]);
|
|
1233
1546
|
return {
|
|
1234
1547
|
table,
|
|
1235
1548
|
tableData,
|
|
@@ -1249,7 +1562,8 @@ function useGlideTable(options) {
|
|
|
1249
1562
|
paddingBottom,
|
|
1250
1563
|
rowContextValue,
|
|
1251
1564
|
handleToggleSelect,
|
|
1252
|
-
clearHover
|
|
1565
|
+
clearHover,
|
|
1566
|
+
copySelection: stableCopySelection
|
|
1253
1567
|
};
|
|
1254
1568
|
}
|
|
1255
1569
|
|
|
@@ -1449,11 +1763,10 @@ function DataTableRow({
|
|
|
1449
1763
|
const { classNames, rowSpan, selection, cellSelection, cellEdit, expand } = useDataTableRowContext();
|
|
1450
1764
|
const {
|
|
1451
1765
|
enableRowSpan,
|
|
1452
|
-
|
|
1766
|
+
primaryRowSpanColumnId,
|
|
1453
1767
|
columnRowSpanMap,
|
|
1454
1768
|
hoveredRowIndex,
|
|
1455
|
-
|
|
1456
|
-
selectedGroupKeys,
|
|
1769
|
+
selectedRowIndices,
|
|
1457
1770
|
onRowHover
|
|
1458
1771
|
} = rowSpan;
|
|
1459
1772
|
const { rowSelectionMode, selectOnRowClick, onRowClick, getRowClassName } = selection;
|
|
@@ -1486,9 +1799,11 @@ function DataTableRow({
|
|
|
1486
1799
|
const rowData = row.original;
|
|
1487
1800
|
const isRowHovered = hoveredRowIndex === rowIndex;
|
|
1488
1801
|
const isRowSelected = row.getIsSelected();
|
|
1489
|
-
const
|
|
1490
|
-
|
|
1491
|
-
|
|
1802
|
+
const { startRow: primaryGroupStart, rowSpan: primaryGroupSpan } = resolveRowSpanAt(
|
|
1803
|
+
primaryRowSpanColumnId ? columnRowSpanMap.get(primaryRowSpanColumnId) : void 0,
|
|
1804
|
+
rowIndex
|
|
1805
|
+
);
|
|
1806
|
+
const isGroupHovered = enableRowSpan && hoveredRowIndex !== null && hoveredRowIndex >= primaryGroupStart && hoveredRowIndex <= primaryGroupStart + primaryGroupSpan - 1;
|
|
1492
1807
|
const visibleCells = row.getVisibleCells();
|
|
1493
1808
|
const columnIdsByIndex = visibleCells.map((cell) => cell.column.id);
|
|
1494
1809
|
const isVisuallySelectedAt = activeSelectionBounds ? (targetRow, targetCol) => {
|
|
@@ -1565,7 +1880,16 @@ function DataTableRow({
|
|
|
1565
1880
|
const cellRowSpan = rowSpanInfo?.rowSpan ?? 1;
|
|
1566
1881
|
const isMergedCellHovered = hoveredRowIndex !== null && hoveredRowIndex >= rowIndex && hoveredRowIndex <= rowIndex + cellRowSpan - 1;
|
|
1567
1882
|
const showCellHover = isRowSpanColumn ? isMergedCellHovered : isRowHovered;
|
|
1568
|
-
|
|
1883
|
+
let isMergedCellSelected = false;
|
|
1884
|
+
if (isRowSpanColumn) {
|
|
1885
|
+
for (let r = rowIndex; r < rowIndex + cellRowSpan; r += 1) {
|
|
1886
|
+
if (selectedRowIndices.has(r)) {
|
|
1887
|
+
isMergedCellSelected = true;
|
|
1888
|
+
break;
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
const showCellSelected = isRowSpanColumn ? isMergedCellSelected : isRowSelected;
|
|
1569
1893
|
const isMerged = cellRowSpan > 1;
|
|
1570
1894
|
const showMergedRightEdge = isMerged && (cellIndex === visibleCells.length - 1 || resolveRowSpanAt(
|
|
1571
1895
|
columnRowSpanMap.get(columnIdsByIndex[cellIndex + 1]),
|
|
@@ -2368,10 +2692,14 @@ var Table = Object.assign(TableRoot, {
|
|
|
2368
2692
|
applyFillData,
|
|
2369
2693
|
applySelectionUpdater,
|
|
2370
2694
|
buildColumnRowSpanMap,
|
|
2695
|
+
buildRowsPastePayload,
|
|
2371
2696
|
canExpandRow,
|
|
2697
|
+
collectCopyRowEntries,
|
|
2698
|
+
collectCopyRows,
|
|
2372
2699
|
collectFillChanges,
|
|
2373
2700
|
collectRowSpanColumns,
|
|
2374
2701
|
createTable,
|
|
2702
|
+
flattenSubtreeRows,
|
|
2375
2703
|
getCellEditDraftValue,
|
|
2376
2704
|
getCellSelectionEdgeStyle,
|
|
2377
2705
|
getColumnEditType,
|
|
@@ -2379,15 +2707,22 @@ var Table = Object.assign(TableRoot, {
|
|
|
2379
2707
|
hasCellSelectionEdges,
|
|
2380
2708
|
isCellInSelection,
|
|
2381
2709
|
isColumnEditable,
|
|
2710
|
+
isEditablePasteTarget,
|
|
2382
2711
|
measureMergedSpanRowHeights,
|
|
2383
2712
|
parseCellEditValue,
|
|
2713
|
+
parseClipboardTSV,
|
|
2714
|
+
parseClipboardTSVWithDepths,
|
|
2384
2715
|
resolveDataTableLabels,
|
|
2716
|
+
resolvePasteColumnIds,
|
|
2385
2717
|
resolveRowSelection,
|
|
2386
2718
|
resolveRowSpanAt,
|
|
2387
2719
|
rowRangeToHeightRatios,
|
|
2720
|
+
serializeCopyRowsToTSV,
|
|
2721
|
+
serializeSelectionToTSV,
|
|
2388
2722
|
toggleExpandedRowId,
|
|
2389
2723
|
useCellEdit,
|
|
2390
2724
|
useCellSelection,
|
|
2391
2725
|
useConvertTreeData,
|
|
2392
|
-
useGlideTable
|
|
2726
|
+
useGlideTable,
|
|
2727
|
+
writeSelectionToClipboard
|
|
2393
2728
|
});
|