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/core.cjs
CHANGED
|
@@ -30,9 +30,13 @@ __export(core_exports, {
|
|
|
30
30
|
applyFillData: () => applyFillData,
|
|
31
31
|
applySelectionUpdater: () => applySelectionUpdater,
|
|
32
32
|
buildColumnRowSpanMap: () => buildColumnRowSpanMap,
|
|
33
|
+
buildRowsPastePayload: () => buildRowsPastePayload,
|
|
33
34
|
canExpandRow: () => canExpandRow,
|
|
35
|
+
collectCopyRowEntries: () => collectCopyRowEntries,
|
|
36
|
+
collectCopyRows: () => collectCopyRows,
|
|
34
37
|
collectFillChanges: () => collectFillChanges,
|
|
35
38
|
collectRowSpanColumns: () => collectRowSpanColumns,
|
|
39
|
+
flattenSubtreeRows: () => flattenSubtreeRows,
|
|
36
40
|
getCellEditDraftValue: () => getCellEditDraftValue,
|
|
37
41
|
getCellSelectionEdgeStyle: () => getCellSelectionEdgeStyle,
|
|
38
42
|
getColumnEditType: () => getColumnEditType,
|
|
@@ -40,17 +44,24 @@ __export(core_exports, {
|
|
|
40
44
|
hasCellSelectionEdges: () => hasCellSelectionEdges,
|
|
41
45
|
isCellInSelection: () => isCellInSelection,
|
|
42
46
|
isColumnEditable: () => isColumnEditable,
|
|
47
|
+
isEditablePasteTarget: () => isEditablePasteTarget,
|
|
43
48
|
measureMergedSpanRowHeights: () => measureMergedSpanRowHeights,
|
|
44
49
|
parseCellEditValue: () => parseCellEditValue,
|
|
50
|
+
parseClipboardTSV: () => parseClipboardTSV,
|
|
51
|
+
parseClipboardTSVWithDepths: () => parseClipboardTSVWithDepths,
|
|
45
52
|
resolveDataTableLabels: () => resolveDataTableLabels,
|
|
53
|
+
resolvePasteColumnIds: () => resolvePasteColumnIds,
|
|
46
54
|
resolveRowSelection: () => resolveRowSelection,
|
|
47
55
|
resolveRowSpanAt: () => resolveRowSpanAt,
|
|
48
56
|
rowRangeToHeightRatios: () => rowRangeToHeightRatios,
|
|
57
|
+
serializeCopyRowsToTSV: () => serializeCopyRowsToTSV,
|
|
58
|
+
serializeSelectionToTSV: () => serializeSelectionToTSV,
|
|
49
59
|
toggleExpandedRowId: () => toggleExpandedRowId,
|
|
50
60
|
useCellEdit: () => useCellEdit,
|
|
51
61
|
useCellSelection: () => useCellSelection,
|
|
52
62
|
useConvertTreeData: () => useConvertTreeData,
|
|
53
|
-
useGlideTable: () => useGlideTable
|
|
63
|
+
useGlideTable: () => useGlideTable,
|
|
64
|
+
writeSelectionToClipboard: () => writeSelectionToClipboard
|
|
54
65
|
});
|
|
55
66
|
module.exports = __toCommonJS(core_exports);
|
|
56
67
|
|
|
@@ -489,6 +500,127 @@ function hasCellSelectionEdges(style) {
|
|
|
489
500
|
);
|
|
490
501
|
}
|
|
491
502
|
|
|
503
|
+
// src/components/ui/table/features/cell-selection/copyData.ts
|
|
504
|
+
function formatCellValue(value) {
|
|
505
|
+
if (value === null || value === void 0) return "";
|
|
506
|
+
return String(value);
|
|
507
|
+
}
|
|
508
|
+
function getNestedValue(row, path) {
|
|
509
|
+
if (!path.includes(".")) return row[path];
|
|
510
|
+
return path.split(".").reduce((current, key) => {
|
|
511
|
+
if (current === null || current === void 0 || typeof current !== "object") {
|
|
512
|
+
return void 0;
|
|
513
|
+
}
|
|
514
|
+
return current[key];
|
|
515
|
+
}, row);
|
|
516
|
+
}
|
|
517
|
+
function readRowColumnValue(rowData, columnDef) {
|
|
518
|
+
if ("accessorFn" in columnDef && typeof columnDef.accessorFn === "function") {
|
|
519
|
+
return columnDef.accessorFn(rowData, 0);
|
|
520
|
+
}
|
|
521
|
+
if ("accessorKey" in columnDef && columnDef.accessorKey != null && columnDef.accessorKey !== "") {
|
|
522
|
+
return getNestedValue(rowData, String(columnDef.accessorKey));
|
|
523
|
+
}
|
|
524
|
+
return void 0;
|
|
525
|
+
}
|
|
526
|
+
function flattenSubtreeRows(row) {
|
|
527
|
+
const children = row.children;
|
|
528
|
+
if (!Array.isArray(children) || children.length === 0) return [];
|
|
529
|
+
const result = [];
|
|
530
|
+
const walk = (nodes) => {
|
|
531
|
+
for (const node of nodes) {
|
|
532
|
+
result.push(node);
|
|
533
|
+
const nested = node.children;
|
|
534
|
+
if (Array.isArray(nested) && nested.length > 0) {
|
|
535
|
+
walk(nested);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
walk(children);
|
|
540
|
+
return result;
|
|
541
|
+
}
|
|
542
|
+
function hasSubtree(row) {
|
|
543
|
+
const children = row.children;
|
|
544
|
+
return Array.isArray(children) && children.length > 0;
|
|
545
|
+
}
|
|
546
|
+
function getOriginalRowId(original) {
|
|
547
|
+
return String(original.id ?? original.uniqueId ?? "");
|
|
548
|
+
}
|
|
549
|
+
function getRowDepth(original) {
|
|
550
|
+
return typeof original.level === "number" ? original.level : 0;
|
|
551
|
+
}
|
|
552
|
+
function collectCopyRowEntries(visibleRows, bounds, mode = "visible") {
|
|
553
|
+
const { startRow, endRow } = bounds;
|
|
554
|
+
const result = [];
|
|
555
|
+
const includedOriginalIds = /* @__PURE__ */ new Set();
|
|
556
|
+
const appendSubtree = (node, depth) => {
|
|
557
|
+
const children = node.children;
|
|
558
|
+
if (!Array.isArray(children) || children.length === 0) return;
|
|
559
|
+
for (const child of children) {
|
|
560
|
+
const childId = getOriginalRowId(child);
|
|
561
|
+
if (!(childId && includedOriginalIds.has(childId))) {
|
|
562
|
+
result.push({ row: child, depth });
|
|
563
|
+
if (childId) includedOriginalIds.add(childId);
|
|
564
|
+
}
|
|
565
|
+
appendSubtree(child, depth + 1);
|
|
566
|
+
}
|
|
567
|
+
};
|
|
568
|
+
for (let rowIndex = startRow; rowIndex <= endRow; rowIndex += 1) {
|
|
569
|
+
const row = visibleRows[rowIndex];
|
|
570
|
+
if (!row) continue;
|
|
571
|
+
const originalId = getOriginalRowId(row.original);
|
|
572
|
+
if (originalId && includedOriginalIds.has(originalId)) continue;
|
|
573
|
+
const depth = getRowDepth(row.original);
|
|
574
|
+
result.push({ row: row.original, depth });
|
|
575
|
+
if (originalId) includedOriginalIds.add(originalId);
|
|
576
|
+
if (mode !== "subtree" || !hasSubtree(row.original)) continue;
|
|
577
|
+
appendSubtree(row.original, depth + 1);
|
|
578
|
+
}
|
|
579
|
+
return result;
|
|
580
|
+
}
|
|
581
|
+
function collectCopyRows(visibleRows, bounds, mode = "visible") {
|
|
582
|
+
return collectCopyRowEntries(visibleRows, bounds, mode).map((entry) => entry.row);
|
|
583
|
+
}
|
|
584
|
+
function serializeCopyRowsToTSV(copyRows, visibleRows, bounds, depths) {
|
|
585
|
+
if (copyRows.length === 0) return "";
|
|
586
|
+
const { startCol, endCol } = bounds;
|
|
587
|
+
const columnCells = visibleRows[0]?.getVisibleCells().slice(startCol, endCol + 1) ?? [];
|
|
588
|
+
if (columnCells.length === 0) return "";
|
|
589
|
+
const resolvedDepths = depths && depths.length === copyRows.length ? depths : copyRows.map((row) => getRowDepth(row));
|
|
590
|
+
const minDepth = Math.min(...resolvedDepths);
|
|
591
|
+
return copyRows.map((rowData, index) => {
|
|
592
|
+
const relativeDepth = Math.max(0, (resolvedDepths[index] ?? 0) - minDepth);
|
|
593
|
+
const line = columnCells.map(
|
|
594
|
+
(cell) => formatCellValue(
|
|
595
|
+
readRowColumnValue(
|
|
596
|
+
rowData,
|
|
597
|
+
cell.column.columnDef
|
|
598
|
+
)
|
|
599
|
+
)
|
|
600
|
+
).join(" ");
|
|
601
|
+
return `${" ".repeat(relativeDepth)}${line}`;
|
|
602
|
+
}).join("\n");
|
|
603
|
+
}
|
|
604
|
+
function serializeSelectionToTSV(visibleRows, bounds, mode = "visible") {
|
|
605
|
+
const entries = collectCopyRowEntries(visibleRows, bounds, mode);
|
|
606
|
+
return serializeCopyRowsToTSV(
|
|
607
|
+
entries.map((entry) => entry.row),
|
|
608
|
+
visibleRows,
|
|
609
|
+
bounds,
|
|
610
|
+
entries.map((entry) => entry.depth)
|
|
611
|
+
);
|
|
612
|
+
}
|
|
613
|
+
async function writeSelectionToClipboard(visibleRows, bounds, mode = "visible") {
|
|
614
|
+
const text = serializeSelectionToTSV(visibleRows, bounds, mode);
|
|
615
|
+
if (!text) return false;
|
|
616
|
+
try {
|
|
617
|
+
await navigator.clipboard.writeText(text);
|
|
618
|
+
} catch {
|
|
619
|
+
return false;
|
|
620
|
+
}
|
|
621
|
+
return true;
|
|
622
|
+
}
|
|
623
|
+
|
|
492
624
|
// src/components/ui/table/features/cell-selection/fillData.ts
|
|
493
625
|
function getColumnAccessorKey2(columnDef) {
|
|
494
626
|
if ("accessorKey" in columnDef && columnDef.accessorKey) {
|
|
@@ -545,15 +677,103 @@ function hasFillExtension(sourceBounds, fillBounds) {
|
|
|
545
677
|
return fillBounds.startRow < sourceBounds.startRow || fillBounds.endRow > sourceBounds.endRow || fillBounds.startCol < sourceBounds.startCol || fillBounds.endCol > sourceBounds.endCol;
|
|
546
678
|
}
|
|
547
679
|
|
|
680
|
+
// src/components/ui/table/features/cell-selection/pasteData.ts
|
|
681
|
+
function countLeadingEmptyCells(cells) {
|
|
682
|
+
let depth = 0;
|
|
683
|
+
while (depth < cells.length && cells[depth] === "") {
|
|
684
|
+
depth += 1;
|
|
685
|
+
}
|
|
686
|
+
return depth;
|
|
687
|
+
}
|
|
688
|
+
function looksLikeSubtreeIndentation(leadingEmptyCounts) {
|
|
689
|
+
if (leadingEmptyCounts.length === 0) return false;
|
|
690
|
+
const firstDepth = leadingEmptyCounts[0] ?? 0;
|
|
691
|
+
if (firstDepth !== 0) return false;
|
|
692
|
+
return leadingEmptyCounts.some((depth) => depth > 0);
|
|
693
|
+
}
|
|
694
|
+
function parseClipboardTSV(text) {
|
|
695
|
+
return parseClipboardTSVWithDepths(text).values;
|
|
696
|
+
}
|
|
697
|
+
function parseClipboardTSVWithDepths(text) {
|
|
698
|
+
if (!text) return { values: [], depths: [] };
|
|
699
|
+
const normalized = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
700
|
+
const withoutTrailing = normalized.replace(/\n+$/, "");
|
|
701
|
+
if (!withoutTrailing) return { values: [], depths: [] };
|
|
702
|
+
const rows = withoutTrailing.split("\n").map((line) => line.split(" "));
|
|
703
|
+
const leadingEmptyCounts = rows.map(countLeadingEmptyCells);
|
|
704
|
+
const treatAsDepth = looksLikeSubtreeIndentation(leadingEmptyCounts);
|
|
705
|
+
const values = [];
|
|
706
|
+
const depths = [];
|
|
707
|
+
for (let index = 0; index < rows.length; index += 1) {
|
|
708
|
+
const cells = rows[index] ?? [];
|
|
709
|
+
const depth = leadingEmptyCounts[index] ?? 0;
|
|
710
|
+
if (treatAsDepth) {
|
|
711
|
+
values.push(cells.slice(depth));
|
|
712
|
+
depths.push(depth);
|
|
713
|
+
} else {
|
|
714
|
+
values.push(cells);
|
|
715
|
+
depths.push(0);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
return { values, depths };
|
|
719
|
+
}
|
|
720
|
+
function resolvePasteColumnIds(rows, startCol, width) {
|
|
721
|
+
if (width <= 0) return [];
|
|
722
|
+
const cells = rows[0]?.getVisibleCells() ?? [];
|
|
723
|
+
const columnIds = [];
|
|
724
|
+
for (let offset = 0; offset < width; offset += 1) {
|
|
725
|
+
const cell = cells[startCol + offset];
|
|
726
|
+
if (!cell) break;
|
|
727
|
+
columnIds.push(cell.column.id);
|
|
728
|
+
}
|
|
729
|
+
return columnIds;
|
|
730
|
+
}
|
|
731
|
+
function buildRowsPastePayload(rows, startRow, startCol, text, mode, endRow = startRow) {
|
|
732
|
+
const { values, depths } = parseClipboardTSVWithDepths(text);
|
|
733
|
+
if (values.length === 0) return null;
|
|
734
|
+
const width = Math.max(...values.map((row) => row.length), 0);
|
|
735
|
+
if (width === 0) return null;
|
|
736
|
+
const columnIds = resolvePasteColumnIds(rows, startCol, width);
|
|
737
|
+
if (columnIds.length === 0) return null;
|
|
738
|
+
const rowIds = [];
|
|
739
|
+
for (let offset = 0; offset < values.length; offset += 1) {
|
|
740
|
+
const row = rows[startRow + offset];
|
|
741
|
+
if (!row) break;
|
|
742
|
+
rowIds.push(row.id);
|
|
743
|
+
}
|
|
744
|
+
const anchorRow = rows[endRow] ?? rows[startRow];
|
|
745
|
+
return {
|
|
746
|
+
mode,
|
|
747
|
+
startRow,
|
|
748
|
+
startCol,
|
|
749
|
+
endRow,
|
|
750
|
+
rowIds,
|
|
751
|
+
anchorRowId: anchorRow?.id ?? "",
|
|
752
|
+
columnIds,
|
|
753
|
+
values,
|
|
754
|
+
depths
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
function isEditablePasteTarget(target) {
|
|
758
|
+
if (!(target instanceof HTMLElement)) return false;
|
|
759
|
+
const tag = target.tagName;
|
|
760
|
+
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
|
|
761
|
+
return Boolean(target.isContentEditable);
|
|
762
|
+
}
|
|
763
|
+
|
|
548
764
|
// src/components/ui/table/features/cell-selection/useCellSelection.ts
|
|
549
765
|
function useCellSelection({
|
|
550
766
|
data,
|
|
551
767
|
rows,
|
|
552
768
|
enabled = true,
|
|
769
|
+
enableSubtreeCopy = false,
|
|
770
|
+
enableInsertPaste = true,
|
|
553
771
|
onDataChange,
|
|
554
|
-
onBatchChange
|
|
772
|
+
onBatchChange,
|
|
773
|
+
onRowsPaste
|
|
555
774
|
}) {
|
|
556
775
|
const [dragState, setDragState] = (0, import_react2.useState)(INITIAL_DRAG_STATE);
|
|
776
|
+
const pendingPasteModeRef = (0, import_react2.useRef)(null);
|
|
557
777
|
const cellSelectionBounds = getCellSelectionBounds(dragState.start, dragState.end);
|
|
558
778
|
const activeSelectionBounds = enabled ? getActiveSelectionBounds(dragState, cellSelectionBounds) : null;
|
|
559
779
|
const handleCellMouseDown = (0, import_react2.useCallback)(
|
|
@@ -607,21 +827,113 @@ function useCellSelection({
|
|
|
607
827
|
setDragState(INITIAL_DRAG_STATE);
|
|
608
828
|
}
|
|
609
829
|
}, [enabled]);
|
|
830
|
+
const copySelection = (0, import_react2.useCallback)(
|
|
831
|
+
async (options) => {
|
|
832
|
+
if (!enabled || !activeSelectionBounds) return false;
|
|
833
|
+
const mode = options?.includeDescendants && enableSubtreeCopy ? "subtree" : "visible";
|
|
834
|
+
return writeSelectionToClipboard(rows, activeSelectionBounds, mode);
|
|
835
|
+
},
|
|
836
|
+
[activeSelectionBounds, enableSubtreeCopy, enabled, rows]
|
|
837
|
+
);
|
|
610
838
|
(0, import_react2.useEffect)(() => {
|
|
611
839
|
if (!enabled) return;
|
|
612
840
|
const handleKeyDown = (e) => {
|
|
613
|
-
if (
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
}
|
|
841
|
+
if (!activeSelectionBounds) return;
|
|
842
|
+
if (!(e.ctrlKey || e.metaKey)) return;
|
|
843
|
+
const isSubtreeShortcut = enableSubtreeCopy && e.shiftKey && e.key.toLowerCase() === "c";
|
|
844
|
+
const isVisibleCopyShortcut = !e.shiftKey && e.key.toLowerCase() === "c";
|
|
845
|
+
if (!isSubtreeShortcut && !isVisibleCopyShortcut) return;
|
|
846
|
+
e.preventDefault();
|
|
847
|
+
void copySelection({ includeDescendants: isSubtreeShortcut });
|
|
621
848
|
};
|
|
622
849
|
window.addEventListener("keydown", handleKeyDown);
|
|
623
850
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
624
|
-
}, [activeSelectionBounds,
|
|
851
|
+
}, [activeSelectionBounds, copySelection, enableSubtreeCopy, enabled]);
|
|
852
|
+
const emitRowsPaste = (0, import_react2.useCallback)(
|
|
853
|
+
(text, mode) => {
|
|
854
|
+
if (!onRowsPaste || !activeSelectionBounds) return false;
|
|
855
|
+
const payload = buildRowsPastePayload(
|
|
856
|
+
rows,
|
|
857
|
+
activeSelectionBounds.startRow,
|
|
858
|
+
activeSelectionBounds.startCol,
|
|
859
|
+
text,
|
|
860
|
+
mode,
|
|
861
|
+
activeSelectionBounds.endRow
|
|
862
|
+
);
|
|
863
|
+
if (!payload) return false;
|
|
864
|
+
onRowsPaste(payload);
|
|
865
|
+
return true;
|
|
866
|
+
},
|
|
867
|
+
[activeSelectionBounds, onRowsPaste, rows]
|
|
868
|
+
);
|
|
869
|
+
(0, import_react2.useEffect)(() => {
|
|
870
|
+
if (!enabled || !onRowsPaste) return;
|
|
871
|
+
const pasteHandledRef = { current: false };
|
|
872
|
+
const ignoreNextPasteRef = { current: false };
|
|
873
|
+
const handleKeyDown = (e) => {
|
|
874
|
+
if (!activeSelectionBounds) return;
|
|
875
|
+
if (!(e.ctrlKey || e.metaKey)) return;
|
|
876
|
+
if (e.key.toLowerCase() !== "v") return;
|
|
877
|
+
if (isEditablePasteTarget(e.target) || isEditablePasteTarget(document.activeElement)) {
|
|
878
|
+
return;
|
|
879
|
+
}
|
|
880
|
+
if (e.shiftKey && !enableInsertPaste) {
|
|
881
|
+
ignoreNextPasteRef.current = true;
|
|
882
|
+
pendingPasteModeRef.current = null;
|
|
883
|
+
return;
|
|
884
|
+
}
|
|
885
|
+
const mode = e.shiftKey ? "insert" : "overwrite";
|
|
886
|
+
pasteHandledRef.current = false;
|
|
887
|
+
ignoreNextPasteRef.current = false;
|
|
888
|
+
pendingPasteModeRef.current = mode;
|
|
889
|
+
void (async () => {
|
|
890
|
+
try {
|
|
891
|
+
const text = await navigator.clipboard.readText();
|
|
892
|
+
if (pasteHandledRef.current) return;
|
|
893
|
+
if (pendingPasteModeRef.current !== mode) return;
|
|
894
|
+
if (!text) return;
|
|
895
|
+
pasteHandledRef.current = true;
|
|
896
|
+
emitRowsPaste(text, mode);
|
|
897
|
+
pendingPasteModeRef.current = null;
|
|
898
|
+
} catch {
|
|
899
|
+
}
|
|
900
|
+
})();
|
|
901
|
+
};
|
|
902
|
+
const handlePaste = (e) => {
|
|
903
|
+
if (!activeSelectionBounds) return;
|
|
904
|
+
if (isEditablePasteTarget(e.target) || isEditablePasteTarget(document.activeElement)) {
|
|
905
|
+
return;
|
|
906
|
+
}
|
|
907
|
+
if (ignoreNextPasteRef.current) {
|
|
908
|
+
ignoreNextPasteRef.current = false;
|
|
909
|
+
pendingPasteModeRef.current = null;
|
|
910
|
+
return;
|
|
911
|
+
}
|
|
912
|
+
const mode = pendingPasteModeRef.current ?? "overwrite";
|
|
913
|
+
if (pasteHandledRef.current) {
|
|
914
|
+
e.preventDefault();
|
|
915
|
+
return;
|
|
916
|
+
}
|
|
917
|
+
const text = e.clipboardData?.getData("text/plain");
|
|
918
|
+
if (text == null || text === "") return;
|
|
919
|
+
pasteHandledRef.current = true;
|
|
920
|
+
e.preventDefault();
|
|
921
|
+
emitRowsPaste(text, mode);
|
|
922
|
+
pendingPasteModeRef.current = null;
|
|
923
|
+
};
|
|
924
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
925
|
+
window.addEventListener("paste", handlePaste);
|
|
926
|
+
return () => {
|
|
927
|
+
window.removeEventListener("keydown", handleKeyDown);
|
|
928
|
+
window.removeEventListener("paste", handlePaste);
|
|
929
|
+
};
|
|
930
|
+
}, [
|
|
931
|
+
activeSelectionBounds,
|
|
932
|
+
emitRowsPaste,
|
|
933
|
+
enableInsertPaste,
|
|
934
|
+
enabled,
|
|
935
|
+
onRowsPaste
|
|
936
|
+
]);
|
|
625
937
|
(0, import_react2.useEffect)(() => {
|
|
626
938
|
if (!enabled) return;
|
|
627
939
|
const handleMouseUp = () => {
|
|
@@ -667,7 +979,8 @@ function useCellSelection({
|
|
|
667
979
|
activeSelectionBounds,
|
|
668
980
|
handleCellMouseDown,
|
|
669
981
|
handleCellMouseEnter,
|
|
670
|
-
handleFillHandleMouseDown
|
|
982
|
+
handleFillHandleMouseDown,
|
|
983
|
+
copySelection
|
|
671
984
|
};
|
|
672
985
|
}
|
|
673
986
|
|
|
@@ -749,15 +1062,16 @@ var useConvertTreeData = ({
|
|
|
749
1062
|
children: [],
|
|
750
1063
|
processed: false
|
|
751
1064
|
}));
|
|
752
|
-
const
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
1065
|
+
const findNearestPrecedingParent = (index, parentKey) => {
|
|
1066
|
+
for (let i = index - 1; i >= 0; i -= 1) {
|
|
1067
|
+
const candidate = dataWithLevels[i];
|
|
1068
|
+
if (!candidate) continue;
|
|
1069
|
+
if (getFieldValue(candidate, toggleField) === parentKey) {
|
|
1070
|
+
return candidate;
|
|
1071
|
+
}
|
|
758
1072
|
}
|
|
759
|
-
|
|
760
|
-
}
|
|
1073
|
+
return void 0;
|
|
1074
|
+
};
|
|
761
1075
|
const rootItems = [];
|
|
762
1076
|
dataWithLevels.forEach((item) => {
|
|
763
1077
|
if (!getFieldValue(item, childField)) {
|
|
@@ -765,29 +1079,18 @@ var useConvertTreeData = ({
|
|
|
765
1079
|
item.processed = true;
|
|
766
1080
|
}
|
|
767
1081
|
});
|
|
768
|
-
dataWithLevels.forEach((item) => {
|
|
1082
|
+
dataWithLevels.forEach((item, index) => {
|
|
769
1083
|
const parentKey = getFieldValue(item, childField);
|
|
770
1084
|
if (!parentKey || item.processed) return;
|
|
771
|
-
const
|
|
772
|
-
|
|
773
|
-
);
|
|
774
|
-
if (parentItems.length > 0) {
|
|
775
|
-
const parent = parentItems[0];
|
|
1085
|
+
const parent = findNearestPrecedingParent(index, parentKey);
|
|
1086
|
+
if (parent) {
|
|
776
1087
|
item.level = parent.level + 1;
|
|
777
1088
|
parent.children.push(item);
|
|
778
1089
|
item.processed = true;
|
|
779
|
-
|
|
780
|
-
const otherParents = itemMap.get(String(parentKey)) || [];
|
|
781
|
-
if (otherParents.length > 0) {
|
|
782
|
-
const parent = otherParents[0];
|
|
783
|
-
item.level = parent.level + 1;
|
|
784
|
-
parent.children.push(item);
|
|
785
|
-
item.processed = true;
|
|
786
|
-
} else {
|
|
787
|
-
rootItems.push(item);
|
|
788
|
-
item.processed = true;
|
|
789
|
-
}
|
|
1090
|
+
return;
|
|
790
1091
|
}
|
|
1092
|
+
rootItems.push(item);
|
|
1093
|
+
item.processed = true;
|
|
791
1094
|
});
|
|
792
1095
|
return rootItems;
|
|
793
1096
|
}, [enabled, data, toggleField, childField, flattenField]);
|
|
@@ -812,16 +1115,23 @@ var useConvertTreeData = ({
|
|
|
812
1115
|
return result;
|
|
813
1116
|
};
|
|
814
1117
|
const flattenedData = flatten(processedData, [], 0);
|
|
815
|
-
flattenedData.forEach((item) => {
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
(parent) => getFieldValue(parent, toggleField) === getFieldValue(item, childField)
|
|
819
|
-
);
|
|
820
|
-
const parentAmount = parentItem ? Number(getFieldValue(parentItem, qtyField) ?? 1) : 1;
|
|
821
|
-
item.parentCount = parentAmount || 1;
|
|
822
|
-
} else {
|
|
1118
|
+
flattenedData.forEach((item, index) => {
|
|
1119
|
+
const parentKey = getFieldValue(item, childField);
|
|
1120
|
+
if (!parentKey) {
|
|
823
1121
|
item.parentCount = 1;
|
|
1122
|
+
return;
|
|
1123
|
+
}
|
|
1124
|
+
let parentItem;
|
|
1125
|
+
for (let i = index - 1; i >= 0; i -= 1) {
|
|
1126
|
+
const candidate = flattenedData[i];
|
|
1127
|
+
if (!candidate) continue;
|
|
1128
|
+
if (getFieldValue(candidate, toggleField) === parentKey) {
|
|
1129
|
+
parentItem = candidate;
|
|
1130
|
+
break;
|
|
1131
|
+
}
|
|
824
1132
|
}
|
|
1133
|
+
const parentAmount = parentItem ? Number(getFieldValue(parentItem, qtyField) ?? 1) : 1;
|
|
1134
|
+
item.parentCount = parentAmount || 1;
|
|
825
1135
|
});
|
|
826
1136
|
return flattenedData;
|
|
827
1137
|
}, [
|
|
@@ -968,6 +1278,10 @@ function useGlideTable(options) {
|
|
|
968
1278
|
expandedRows: controlledExpandedRows,
|
|
969
1279
|
onExpandedRowsChange,
|
|
970
1280
|
preventExpand = false,
|
|
1281
|
+
enableSubtreeCopy,
|
|
1282
|
+
onCopyActionsReady,
|
|
1283
|
+
onRowsPaste,
|
|
1284
|
+
enableInsertPaste,
|
|
971
1285
|
enableVirtualization = true,
|
|
972
1286
|
estimateRowHeight = DATA_TABLE_ROW_HEIGHT,
|
|
973
1287
|
virtualOverscan = DATA_TABLE_VIRTUAL_OVERSCAN
|
|
@@ -982,12 +1296,12 @@ function useGlideTable(options) {
|
|
|
982
1296
|
};
|
|
983
1297
|
}, [labelsProp, emptyText, loadingText, selectionLabel]);
|
|
984
1298
|
const enableExpand = Boolean(toggleField);
|
|
1299
|
+
const resolvedEnableSubtreeCopy = enableSubtreeCopy ?? enableExpand;
|
|
985
1300
|
const [internalRowSelection, setInternalRowSelection] = (0, import_react4.useState)({});
|
|
986
1301
|
const [internalExpandedRows, setInternalExpandedRows] = (0, import_react4.useState)(
|
|
987
1302
|
() => /* @__PURE__ */ new Set()
|
|
988
1303
|
);
|
|
989
1304
|
const [hoveredRowIndex, setHoveredRowIndex] = (0, import_react4.useState)(null);
|
|
990
|
-
const [hoveredGroupKey, setHoveredGroupKey] = (0, import_react4.useState)(null);
|
|
991
1305
|
const scrollRef = (0, import_react4.useRef)(null);
|
|
992
1306
|
const shouldVirtualize = enableVirtualization && !enableRowSpan;
|
|
993
1307
|
(0, import_react4.useEffect)(() => {
|
|
@@ -1051,6 +1365,7 @@ function useGlideTable(options) {
|
|
|
1051
1365
|
return collectRowSpanColumns(columns);
|
|
1052
1366
|
}, [enableRowSpan, columns]);
|
|
1053
1367
|
const primaryRowSpanKey = rowSpanColumnKeys[0]?.rowSpanKey;
|
|
1368
|
+
const primaryRowSpanColumnId = rowSpanColumnKeys[0]?.columnId;
|
|
1054
1369
|
const columnRowSpanMap = (0, import_react4.useMemo)(
|
|
1055
1370
|
() => buildColumnRowSpanMap(tableData, rowSpanColumnKeys),
|
|
1056
1371
|
[tableData, rowSpanColumnKeys]
|
|
@@ -1069,27 +1384,29 @@ function useGlideTable(options) {
|
|
|
1069
1384
|
const totalSize = rowVirtualizer.getTotalSize();
|
|
1070
1385
|
const paddingTop = virtualRows.length > 0 ? virtualRows[0]?.start ?? 0 : 0;
|
|
1071
1386
|
const paddingBottom = virtualRows.length > 0 ? totalSize - (virtualRows[virtualRows.length - 1]?.end ?? 0) : 0;
|
|
1072
|
-
const
|
|
1073
|
-
|
|
1074
|
-
const keys = /* @__PURE__ */ new Set();
|
|
1387
|
+
const selectedRowIndices = (0, import_react4.useMemo)(() => {
|
|
1388
|
+
const indices = /* @__PURE__ */ new Set();
|
|
1075
1389
|
for (const selectedRow of selectedRows) {
|
|
1076
|
-
|
|
1077
|
-
if (value !== null && value !== void 0) keys.add(String(value));
|
|
1390
|
+
indices.add(selectedRow.index);
|
|
1078
1391
|
}
|
|
1079
|
-
return
|
|
1080
|
-
}, [
|
|
1392
|
+
return indices;
|
|
1393
|
+
}, [selectedRows]);
|
|
1081
1394
|
const {
|
|
1082
1395
|
dragState,
|
|
1083
1396
|
activeSelectionBounds,
|
|
1084
1397
|
handleCellMouseDown,
|
|
1085
1398
|
handleCellMouseEnter,
|
|
1086
|
-
handleFillHandleMouseDown
|
|
1399
|
+
handleFillHandleMouseDown,
|
|
1400
|
+
copySelection
|
|
1087
1401
|
} = useCellSelection({
|
|
1088
1402
|
data: tableData,
|
|
1089
1403
|
rows,
|
|
1090
1404
|
enabled: enableCellSelection,
|
|
1405
|
+
enableSubtreeCopy: resolvedEnableSubtreeCopy,
|
|
1406
|
+
enableInsertPaste: enableInsertPaste ?? true,
|
|
1091
1407
|
onDataChange,
|
|
1092
|
-
onBatchChange
|
|
1408
|
+
onBatchChange,
|
|
1409
|
+
onRowsPaste
|
|
1093
1410
|
});
|
|
1094
1411
|
const {
|
|
1095
1412
|
editingCell,
|
|
@@ -1111,22 +1428,10 @@ function useGlideTable(options) {
|
|
|
1111
1428
|
);
|
|
1112
1429
|
const clearHover = (0, import_react4.useCallback)(() => {
|
|
1113
1430
|
setHoveredRowIndex(null);
|
|
1114
|
-
setHoveredGroupKey(null);
|
|
1115
1431
|
}, []);
|
|
1116
|
-
const handleRowHover = (0, import_react4.useCallback)(
|
|
1117
|
-
(rowIndex
|
|
1118
|
-
|
|
1119
|
-
if (!primaryRowSpanKey) {
|
|
1120
|
-
setHoveredGroupKey(null);
|
|
1121
|
-
return;
|
|
1122
|
-
}
|
|
1123
|
-
const groupValue = rowData[primaryRowSpanKey];
|
|
1124
|
-
setHoveredGroupKey(
|
|
1125
|
-
groupValue === null || groupValue === void 0 ? null : String(groupValue)
|
|
1126
|
-
);
|
|
1127
|
-
},
|
|
1128
|
-
[primaryRowSpanKey]
|
|
1129
|
-
);
|
|
1432
|
+
const handleRowHover = (0, import_react4.useCallback)((rowIndex, _rowData) => {
|
|
1433
|
+
setHoveredRowIndex(rowIndex);
|
|
1434
|
+
}, []);
|
|
1130
1435
|
const handleToggleSelect = (0, import_react4.useCallback)(
|
|
1131
1436
|
(row) => {
|
|
1132
1437
|
if (!row.getCanSelect()) return;
|
|
@@ -1149,10 +1454,10 @@ function useGlideTable(options) {
|
|
|
1149
1454
|
rowSpan: {
|
|
1150
1455
|
enableRowSpan,
|
|
1151
1456
|
primaryRowSpanKey,
|
|
1457
|
+
primaryRowSpanColumnId,
|
|
1152
1458
|
columnRowSpanMap,
|
|
1153
1459
|
hoveredRowIndex,
|
|
1154
|
-
|
|
1155
|
-
selectedGroupKeys,
|
|
1460
|
+
selectedRowIndices,
|
|
1156
1461
|
onRowHover: handleRowHover
|
|
1157
1462
|
},
|
|
1158
1463
|
selection: {
|
|
@@ -1190,10 +1495,10 @@ function useGlideTable(options) {
|
|
|
1190
1495
|
}, [
|
|
1191
1496
|
enableRowSpan,
|
|
1192
1497
|
primaryRowSpanKey,
|
|
1498
|
+
primaryRowSpanColumnId,
|
|
1193
1499
|
columnRowSpanMap,
|
|
1194
1500
|
hoveredRowIndex,
|
|
1195
|
-
|
|
1196
|
-
selectedGroupKeys,
|
|
1501
|
+
selectedRowIndices,
|
|
1197
1502
|
handleRowHover,
|
|
1198
1503
|
rowSelectionMode,
|
|
1199
1504
|
selectOnRowClick,
|
|
@@ -1219,6 +1524,14 @@ function useGlideTable(options) {
|
|
|
1219
1524
|
labels.expandRow,
|
|
1220
1525
|
labels.collapseRow
|
|
1221
1526
|
]);
|
|
1527
|
+
const copySelectionRef = (0, import_react4.useRef)(copySelection);
|
|
1528
|
+
(0, import_react4.useEffect)(() => {
|
|
1529
|
+
copySelectionRef.current = copySelection;
|
|
1530
|
+
}, [copySelection]);
|
|
1531
|
+
const stableCopySelection = (0, import_react4.useCallback)((options2) => copySelectionRef.current(options2), []);
|
|
1532
|
+
(0, import_react4.useEffect)(() => {
|
|
1533
|
+
onCopyActionsReady?.({ copySelection: stableCopySelection });
|
|
1534
|
+
}, [onCopyActionsReady, stableCopySelection]);
|
|
1222
1535
|
return {
|
|
1223
1536
|
table,
|
|
1224
1537
|
tableData,
|
|
@@ -1238,7 +1551,8 @@ function useGlideTable(options) {
|
|
|
1238
1551
|
paddingBottom,
|
|
1239
1552
|
rowContextValue,
|
|
1240
1553
|
handleToggleSelect,
|
|
1241
|
-
clearHover
|
|
1554
|
+
clearHover,
|
|
1555
|
+
copySelection: stableCopySelection
|
|
1242
1556
|
};
|
|
1243
1557
|
}
|
|
1244
1558
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -1253,9 +1567,13 @@ function useGlideTable(options) {
|
|
|
1253
1567
|
applyFillData,
|
|
1254
1568
|
applySelectionUpdater,
|
|
1255
1569
|
buildColumnRowSpanMap,
|
|
1570
|
+
buildRowsPastePayload,
|
|
1256
1571
|
canExpandRow,
|
|
1572
|
+
collectCopyRowEntries,
|
|
1573
|
+
collectCopyRows,
|
|
1257
1574
|
collectFillChanges,
|
|
1258
1575
|
collectRowSpanColumns,
|
|
1576
|
+
flattenSubtreeRows,
|
|
1259
1577
|
getCellEditDraftValue,
|
|
1260
1578
|
getCellSelectionEdgeStyle,
|
|
1261
1579
|
getColumnEditType,
|
|
@@ -1263,15 +1581,22 @@ function useGlideTable(options) {
|
|
|
1263
1581
|
hasCellSelectionEdges,
|
|
1264
1582
|
isCellInSelection,
|
|
1265
1583
|
isColumnEditable,
|
|
1584
|
+
isEditablePasteTarget,
|
|
1266
1585
|
measureMergedSpanRowHeights,
|
|
1267
1586
|
parseCellEditValue,
|
|
1587
|
+
parseClipboardTSV,
|
|
1588
|
+
parseClipboardTSVWithDepths,
|
|
1268
1589
|
resolveDataTableLabels,
|
|
1590
|
+
resolvePasteColumnIds,
|
|
1269
1591
|
resolveRowSelection,
|
|
1270
1592
|
resolveRowSpanAt,
|
|
1271
1593
|
rowRangeToHeightRatios,
|
|
1594
|
+
serializeCopyRowsToTSV,
|
|
1595
|
+
serializeSelectionToTSV,
|
|
1272
1596
|
toggleExpandedRowId,
|
|
1273
1597
|
useCellEdit,
|
|
1274
1598
|
useCellSelection,
|
|
1275
1599
|
useConvertTreeData,
|
|
1276
|
-
useGlideTable
|
|
1600
|
+
useGlideTable,
|
|
1601
|
+
writeSelectionToClipboard
|
|
1277
1602
|
});
|