react-glide-table 1.1.5 → 1.1.7
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 +378 -82
- package/dist/compound.d.cts +2 -2
- package/dist/compound.d.ts +2 -2
- package/dist/compound.js +381 -85
- 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 +424 -84
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +420 -91
- 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.js
CHANGED
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
useCallback as useCallback3,
|
|
32
32
|
useEffect as useEffect4,
|
|
33
33
|
useMemo as useMemo2,
|
|
34
|
-
useRef as
|
|
34
|
+
useRef as useRef4,
|
|
35
35
|
useState as useState3
|
|
36
36
|
} from "react";
|
|
37
37
|
|
|
@@ -175,7 +175,7 @@ function useCellEdit({
|
|
|
175
175
|
}
|
|
176
176
|
|
|
177
177
|
// src/components/ui/table/features/cell-selection/useCellSelection.ts
|
|
178
|
-
import { useCallback as useCallback2, useEffect as useEffect2, useState as useState2 } from "react";
|
|
178
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
179
179
|
|
|
180
180
|
// src/components/ui/table/features/cell-selection/cellSelection.ts
|
|
181
181
|
var INITIAL_DRAG_STATE = {
|
|
@@ -452,6 +452,127 @@ function hasCellSelectionEdges(style) {
|
|
|
452
452
|
);
|
|
453
453
|
}
|
|
454
454
|
|
|
455
|
+
// src/components/ui/table/features/cell-selection/copyData.ts
|
|
456
|
+
function formatCellValue(value) {
|
|
457
|
+
if (value === null || value === void 0) return "";
|
|
458
|
+
return String(value);
|
|
459
|
+
}
|
|
460
|
+
function getNestedValue(row, path) {
|
|
461
|
+
if (!path.includes(".")) return row[path];
|
|
462
|
+
return path.split(".").reduce((current, key) => {
|
|
463
|
+
if (current === null || current === void 0 || typeof current !== "object") {
|
|
464
|
+
return void 0;
|
|
465
|
+
}
|
|
466
|
+
return current[key];
|
|
467
|
+
}, row);
|
|
468
|
+
}
|
|
469
|
+
function readRowColumnValue(rowData, columnDef) {
|
|
470
|
+
if ("accessorFn" in columnDef && typeof columnDef.accessorFn === "function") {
|
|
471
|
+
return columnDef.accessorFn(rowData, 0);
|
|
472
|
+
}
|
|
473
|
+
if ("accessorKey" in columnDef && columnDef.accessorKey != null && columnDef.accessorKey !== "") {
|
|
474
|
+
return getNestedValue(rowData, String(columnDef.accessorKey));
|
|
475
|
+
}
|
|
476
|
+
return void 0;
|
|
477
|
+
}
|
|
478
|
+
function flattenSubtreeRows(row) {
|
|
479
|
+
const children = row.children;
|
|
480
|
+
if (!Array.isArray(children) || children.length === 0) return [];
|
|
481
|
+
const result = [];
|
|
482
|
+
const walk = (nodes) => {
|
|
483
|
+
for (const node of nodes) {
|
|
484
|
+
result.push(node);
|
|
485
|
+
const nested = node.children;
|
|
486
|
+
if (Array.isArray(nested) && nested.length > 0) {
|
|
487
|
+
walk(nested);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
walk(children);
|
|
492
|
+
return result;
|
|
493
|
+
}
|
|
494
|
+
function hasSubtree(row) {
|
|
495
|
+
const children = row.children;
|
|
496
|
+
return Array.isArray(children) && children.length > 0;
|
|
497
|
+
}
|
|
498
|
+
function getOriginalRowId(original) {
|
|
499
|
+
return String(original.id ?? original.uniqueId ?? "");
|
|
500
|
+
}
|
|
501
|
+
function getRowDepth(original) {
|
|
502
|
+
return typeof original.level === "number" ? original.level : 0;
|
|
503
|
+
}
|
|
504
|
+
function collectCopyRowEntries(visibleRows, bounds, mode = "visible") {
|
|
505
|
+
const { startRow, endRow } = bounds;
|
|
506
|
+
const result = [];
|
|
507
|
+
const includedOriginalIds = /* @__PURE__ */ new Set();
|
|
508
|
+
const appendSubtree = (node, depth) => {
|
|
509
|
+
const children = node.children;
|
|
510
|
+
if (!Array.isArray(children) || children.length === 0) return;
|
|
511
|
+
for (const child of children) {
|
|
512
|
+
const childId = getOriginalRowId(child);
|
|
513
|
+
if (!(childId && includedOriginalIds.has(childId))) {
|
|
514
|
+
result.push({ row: child, depth });
|
|
515
|
+
if (childId) includedOriginalIds.add(childId);
|
|
516
|
+
}
|
|
517
|
+
appendSubtree(child, depth + 1);
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
for (let rowIndex = startRow; rowIndex <= endRow; rowIndex += 1) {
|
|
521
|
+
const row = visibleRows[rowIndex];
|
|
522
|
+
if (!row) continue;
|
|
523
|
+
const originalId = getOriginalRowId(row.original);
|
|
524
|
+
if (originalId && includedOriginalIds.has(originalId)) continue;
|
|
525
|
+
const depth = getRowDepth(row.original);
|
|
526
|
+
result.push({ row: row.original, depth });
|
|
527
|
+
if (originalId) includedOriginalIds.add(originalId);
|
|
528
|
+
if (mode !== "subtree" || !hasSubtree(row.original)) continue;
|
|
529
|
+
appendSubtree(row.original, depth + 1);
|
|
530
|
+
}
|
|
531
|
+
return result;
|
|
532
|
+
}
|
|
533
|
+
function collectCopyRows(visibleRows, bounds, mode = "visible") {
|
|
534
|
+
return collectCopyRowEntries(visibleRows, bounds, mode).map((entry) => entry.row);
|
|
535
|
+
}
|
|
536
|
+
function serializeCopyRowsToTSV(copyRows, visibleRows, bounds, depths) {
|
|
537
|
+
if (copyRows.length === 0) return "";
|
|
538
|
+
const { startCol, endCol } = bounds;
|
|
539
|
+
const columnCells = visibleRows[0]?.getVisibleCells().slice(startCol, endCol + 1) ?? [];
|
|
540
|
+
if (columnCells.length === 0) return "";
|
|
541
|
+
const resolvedDepths = depths && depths.length === copyRows.length ? depths : copyRows.map((row) => getRowDepth(row));
|
|
542
|
+
const minDepth = Math.min(...resolvedDepths);
|
|
543
|
+
return copyRows.map((rowData, index) => {
|
|
544
|
+
const relativeDepth = Math.max(0, (resolvedDepths[index] ?? 0) - minDepth);
|
|
545
|
+
const line = columnCells.map(
|
|
546
|
+
(cell) => formatCellValue(
|
|
547
|
+
readRowColumnValue(
|
|
548
|
+
rowData,
|
|
549
|
+
cell.column.columnDef
|
|
550
|
+
)
|
|
551
|
+
)
|
|
552
|
+
).join(" ");
|
|
553
|
+
return `${" ".repeat(relativeDepth)}${line}`;
|
|
554
|
+
}).join("\n");
|
|
555
|
+
}
|
|
556
|
+
function serializeSelectionToTSV(visibleRows, bounds, mode = "visible") {
|
|
557
|
+
const entries = collectCopyRowEntries(visibleRows, bounds, mode);
|
|
558
|
+
return serializeCopyRowsToTSV(
|
|
559
|
+
entries.map((entry) => entry.row),
|
|
560
|
+
visibleRows,
|
|
561
|
+
bounds,
|
|
562
|
+
entries.map((entry) => entry.depth)
|
|
563
|
+
);
|
|
564
|
+
}
|
|
565
|
+
async function writeSelectionToClipboard(visibleRows, bounds, mode = "visible") {
|
|
566
|
+
const text = serializeSelectionToTSV(visibleRows, bounds, mode);
|
|
567
|
+
if (!text) return false;
|
|
568
|
+
try {
|
|
569
|
+
await navigator.clipboard.writeText(text);
|
|
570
|
+
} catch {
|
|
571
|
+
return false;
|
|
572
|
+
}
|
|
573
|
+
return true;
|
|
574
|
+
}
|
|
575
|
+
|
|
455
576
|
// src/components/ui/table/features/cell-selection/fillData.ts
|
|
456
577
|
function getColumnAccessorKey2(columnDef) {
|
|
457
578
|
if ("accessorKey" in columnDef && columnDef.accessorKey) {
|
|
@@ -508,15 +629,103 @@ function hasFillExtension(sourceBounds, fillBounds) {
|
|
|
508
629
|
return fillBounds.startRow < sourceBounds.startRow || fillBounds.endRow > sourceBounds.endRow || fillBounds.startCol < sourceBounds.startCol || fillBounds.endCol > sourceBounds.endCol;
|
|
509
630
|
}
|
|
510
631
|
|
|
632
|
+
// src/components/ui/table/features/cell-selection/pasteData.ts
|
|
633
|
+
function countLeadingEmptyCells(cells) {
|
|
634
|
+
let depth = 0;
|
|
635
|
+
while (depth < cells.length && cells[depth] === "") {
|
|
636
|
+
depth += 1;
|
|
637
|
+
}
|
|
638
|
+
return depth;
|
|
639
|
+
}
|
|
640
|
+
function looksLikeSubtreeIndentation(leadingEmptyCounts) {
|
|
641
|
+
if (leadingEmptyCounts.length === 0) return false;
|
|
642
|
+
const firstDepth = leadingEmptyCounts[0] ?? 0;
|
|
643
|
+
if (firstDepth !== 0) return false;
|
|
644
|
+
return leadingEmptyCounts.some((depth) => depth > 0);
|
|
645
|
+
}
|
|
646
|
+
function parseClipboardTSV(text) {
|
|
647
|
+
return parseClipboardTSVWithDepths(text).values;
|
|
648
|
+
}
|
|
649
|
+
function parseClipboardTSVWithDepths(text) {
|
|
650
|
+
if (!text) return { values: [], depths: [] };
|
|
651
|
+
const normalized = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
652
|
+
const withoutTrailing = normalized.replace(/\n+$/, "");
|
|
653
|
+
if (!withoutTrailing) return { values: [], depths: [] };
|
|
654
|
+
const rows = withoutTrailing.split("\n").map((line) => line.split(" "));
|
|
655
|
+
const leadingEmptyCounts = rows.map(countLeadingEmptyCells);
|
|
656
|
+
const treatAsDepth = looksLikeSubtreeIndentation(leadingEmptyCounts);
|
|
657
|
+
const values = [];
|
|
658
|
+
const depths = [];
|
|
659
|
+
for (let index = 0; index < rows.length; index += 1) {
|
|
660
|
+
const cells = rows[index] ?? [];
|
|
661
|
+
const depth = leadingEmptyCounts[index] ?? 0;
|
|
662
|
+
if (treatAsDepth) {
|
|
663
|
+
values.push(cells.slice(depth));
|
|
664
|
+
depths.push(depth);
|
|
665
|
+
} else {
|
|
666
|
+
values.push(cells);
|
|
667
|
+
depths.push(0);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
return { values, depths };
|
|
671
|
+
}
|
|
672
|
+
function resolvePasteColumnIds(rows, startCol, width) {
|
|
673
|
+
if (width <= 0) return [];
|
|
674
|
+
const cells = rows[0]?.getVisibleCells() ?? [];
|
|
675
|
+
const columnIds = [];
|
|
676
|
+
for (let offset = 0; offset < width; offset += 1) {
|
|
677
|
+
const cell = cells[startCol + offset];
|
|
678
|
+
if (!cell) break;
|
|
679
|
+
columnIds.push(cell.column.id);
|
|
680
|
+
}
|
|
681
|
+
return columnIds;
|
|
682
|
+
}
|
|
683
|
+
function buildRowsPastePayload(rows, startRow, startCol, text, mode, endRow = startRow) {
|
|
684
|
+
const { values, depths } = parseClipboardTSVWithDepths(text);
|
|
685
|
+
if (values.length === 0) return null;
|
|
686
|
+
const width = Math.max(...values.map((row) => row.length), 0);
|
|
687
|
+
if (width === 0) return null;
|
|
688
|
+
const columnIds = resolvePasteColumnIds(rows, startCol, width);
|
|
689
|
+
if (columnIds.length === 0) return null;
|
|
690
|
+
const rowIds = [];
|
|
691
|
+
for (let offset = 0; offset < values.length; offset += 1) {
|
|
692
|
+
const row = rows[startRow + offset];
|
|
693
|
+
if (!row) break;
|
|
694
|
+
rowIds.push(row.id);
|
|
695
|
+
}
|
|
696
|
+
const anchorRow = rows[endRow] ?? rows[startRow];
|
|
697
|
+
return {
|
|
698
|
+
mode,
|
|
699
|
+
startRow,
|
|
700
|
+
startCol,
|
|
701
|
+
endRow,
|
|
702
|
+
rowIds,
|
|
703
|
+
anchorRowId: anchorRow?.id ?? "",
|
|
704
|
+
columnIds,
|
|
705
|
+
values,
|
|
706
|
+
depths
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
function isEditablePasteTarget(target) {
|
|
710
|
+
if (!(target instanceof HTMLElement)) return false;
|
|
711
|
+
const tag = target.tagName;
|
|
712
|
+
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
|
|
713
|
+
return Boolean(target.isContentEditable);
|
|
714
|
+
}
|
|
715
|
+
|
|
511
716
|
// src/components/ui/table/features/cell-selection/useCellSelection.ts
|
|
512
717
|
function useCellSelection({
|
|
513
718
|
data,
|
|
514
719
|
rows,
|
|
515
720
|
enabled = true,
|
|
721
|
+
enableSubtreeCopy = false,
|
|
722
|
+
enableInsertPaste = true,
|
|
516
723
|
onDataChange,
|
|
517
|
-
onBatchChange
|
|
724
|
+
onBatchChange,
|
|
725
|
+
onRowsPaste
|
|
518
726
|
}) {
|
|
519
727
|
const [dragState, setDragState] = useState2(INITIAL_DRAG_STATE);
|
|
728
|
+
const pendingPasteModeRef = useRef2(null);
|
|
520
729
|
const cellSelectionBounds = getCellSelectionBounds(dragState.start, dragState.end);
|
|
521
730
|
const activeSelectionBounds = enabled ? getActiveSelectionBounds(dragState, cellSelectionBounds) : null;
|
|
522
731
|
const handleCellMouseDown = useCallback2(
|
|
@@ -570,21 +779,113 @@ function useCellSelection({
|
|
|
570
779
|
setDragState(INITIAL_DRAG_STATE);
|
|
571
780
|
}
|
|
572
781
|
}, [enabled]);
|
|
782
|
+
const copySelection = useCallback2(
|
|
783
|
+
async (options) => {
|
|
784
|
+
if (!enabled || !activeSelectionBounds) return false;
|
|
785
|
+
const mode = options?.includeDescendants && enableSubtreeCopy ? "subtree" : "visible";
|
|
786
|
+
return writeSelectionToClipboard(rows, activeSelectionBounds, mode);
|
|
787
|
+
},
|
|
788
|
+
[activeSelectionBounds, enableSubtreeCopy, enabled, rows]
|
|
789
|
+
);
|
|
573
790
|
useEffect2(() => {
|
|
574
791
|
if (!enabled) return;
|
|
575
792
|
const handleKeyDown = (e) => {
|
|
576
|
-
if (
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
}
|
|
793
|
+
if (!activeSelectionBounds) return;
|
|
794
|
+
if (!(e.ctrlKey || e.metaKey)) return;
|
|
795
|
+
const isSubtreeShortcut = enableSubtreeCopy && e.shiftKey && e.key.toLowerCase() === "c";
|
|
796
|
+
const isVisibleCopyShortcut = !e.shiftKey && e.key.toLowerCase() === "c";
|
|
797
|
+
if (!isSubtreeShortcut && !isVisibleCopyShortcut) return;
|
|
798
|
+
e.preventDefault();
|
|
799
|
+
void copySelection({ includeDescendants: isSubtreeShortcut });
|
|
584
800
|
};
|
|
585
801
|
window.addEventListener("keydown", handleKeyDown);
|
|
586
802
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
587
|
-
}, [activeSelectionBounds,
|
|
803
|
+
}, [activeSelectionBounds, copySelection, enableSubtreeCopy, enabled]);
|
|
804
|
+
const emitRowsPaste = useCallback2(
|
|
805
|
+
(text, mode) => {
|
|
806
|
+
if (!onRowsPaste || !activeSelectionBounds) return false;
|
|
807
|
+
const payload = buildRowsPastePayload(
|
|
808
|
+
rows,
|
|
809
|
+
activeSelectionBounds.startRow,
|
|
810
|
+
activeSelectionBounds.startCol,
|
|
811
|
+
text,
|
|
812
|
+
mode,
|
|
813
|
+
activeSelectionBounds.endRow
|
|
814
|
+
);
|
|
815
|
+
if (!payload) return false;
|
|
816
|
+
onRowsPaste(payload);
|
|
817
|
+
return true;
|
|
818
|
+
},
|
|
819
|
+
[activeSelectionBounds, onRowsPaste, rows]
|
|
820
|
+
);
|
|
821
|
+
useEffect2(() => {
|
|
822
|
+
if (!enabled || !onRowsPaste) return;
|
|
823
|
+
const pasteHandledRef = { current: false };
|
|
824
|
+
const ignoreNextPasteRef = { current: false };
|
|
825
|
+
const handleKeyDown = (e) => {
|
|
826
|
+
if (!activeSelectionBounds) return;
|
|
827
|
+
if (!(e.ctrlKey || e.metaKey)) return;
|
|
828
|
+
if (e.key.toLowerCase() !== "v") return;
|
|
829
|
+
if (isEditablePasteTarget(e.target) || isEditablePasteTarget(document.activeElement)) {
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
if (e.shiftKey && !enableInsertPaste) {
|
|
833
|
+
ignoreNextPasteRef.current = true;
|
|
834
|
+
pendingPasteModeRef.current = null;
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
837
|
+
const mode = e.shiftKey ? "insert" : "overwrite";
|
|
838
|
+
pasteHandledRef.current = false;
|
|
839
|
+
ignoreNextPasteRef.current = false;
|
|
840
|
+
pendingPasteModeRef.current = mode;
|
|
841
|
+
void (async () => {
|
|
842
|
+
try {
|
|
843
|
+
const text = await navigator.clipboard.readText();
|
|
844
|
+
if (pasteHandledRef.current) return;
|
|
845
|
+
if (pendingPasteModeRef.current !== mode) return;
|
|
846
|
+
if (!text) return;
|
|
847
|
+
pasteHandledRef.current = true;
|
|
848
|
+
emitRowsPaste(text, mode);
|
|
849
|
+
pendingPasteModeRef.current = null;
|
|
850
|
+
} catch {
|
|
851
|
+
}
|
|
852
|
+
})();
|
|
853
|
+
};
|
|
854
|
+
const handlePaste = (e) => {
|
|
855
|
+
if (!activeSelectionBounds) return;
|
|
856
|
+
if (isEditablePasteTarget(e.target) || isEditablePasteTarget(document.activeElement)) {
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
if (ignoreNextPasteRef.current) {
|
|
860
|
+
ignoreNextPasteRef.current = false;
|
|
861
|
+
pendingPasteModeRef.current = null;
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
864
|
+
const mode = pendingPasteModeRef.current ?? "overwrite";
|
|
865
|
+
if (pasteHandledRef.current) {
|
|
866
|
+
e.preventDefault();
|
|
867
|
+
return;
|
|
868
|
+
}
|
|
869
|
+
const text = e.clipboardData?.getData("text/plain");
|
|
870
|
+
if (text == null || text === "") return;
|
|
871
|
+
pasteHandledRef.current = true;
|
|
872
|
+
e.preventDefault();
|
|
873
|
+
emitRowsPaste(text, mode);
|
|
874
|
+
pendingPasteModeRef.current = null;
|
|
875
|
+
};
|
|
876
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
877
|
+
window.addEventListener("paste", handlePaste);
|
|
878
|
+
return () => {
|
|
879
|
+
window.removeEventListener("keydown", handleKeyDown);
|
|
880
|
+
window.removeEventListener("paste", handlePaste);
|
|
881
|
+
};
|
|
882
|
+
}, [
|
|
883
|
+
activeSelectionBounds,
|
|
884
|
+
emitRowsPaste,
|
|
885
|
+
enableInsertPaste,
|
|
886
|
+
enabled,
|
|
887
|
+
onRowsPaste
|
|
888
|
+
]);
|
|
588
889
|
useEffect2(() => {
|
|
589
890
|
if (!enabled) return;
|
|
590
891
|
const handleMouseUp = () => {
|
|
@@ -630,12 +931,13 @@ function useCellSelection({
|
|
|
630
931
|
activeSelectionBounds,
|
|
631
932
|
handleCellMouseDown,
|
|
632
933
|
handleCellMouseEnter,
|
|
633
|
-
handleFillHandleMouseDown
|
|
934
|
+
handleFillHandleMouseDown,
|
|
935
|
+
copySelection
|
|
634
936
|
};
|
|
635
937
|
}
|
|
636
938
|
|
|
637
939
|
// src/components/ui/table/features/row-expand/row-expand.ts
|
|
638
|
-
import { useEffect as useEffect3, useMemo, useRef as
|
|
940
|
+
import { useEffect as useEffect3, useMemo, useRef as useRef3 } from "react";
|
|
639
941
|
function getFieldValue(row, key) {
|
|
640
942
|
return row[key];
|
|
641
943
|
}
|
|
@@ -665,8 +967,8 @@ var useConvertTreeData = ({
|
|
|
665
967
|
expandedRows,
|
|
666
968
|
onExpandedRowsChange
|
|
667
969
|
}) => {
|
|
668
|
-
const onExpandedRowsChangeRef =
|
|
669
|
-
const hasInitializedRef =
|
|
970
|
+
const onExpandedRowsChangeRef = useRef3(onExpandedRowsChange);
|
|
971
|
+
const hasInitializedRef = useRef3(false);
|
|
670
972
|
useEffect3(() => {
|
|
671
973
|
onExpandedRowsChangeRef.current = onExpandedRowsChange;
|
|
672
974
|
}, [onExpandedRowsChange]);
|
|
@@ -712,15 +1014,16 @@ var useConvertTreeData = ({
|
|
|
712
1014
|
children: [],
|
|
713
1015
|
processed: false
|
|
714
1016
|
}));
|
|
715
|
-
const
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
1017
|
+
const findNearestPrecedingParent = (index, parentKey) => {
|
|
1018
|
+
for (let i = index - 1; i >= 0; i -= 1) {
|
|
1019
|
+
const candidate = dataWithLevels[i];
|
|
1020
|
+
if (!candidate) continue;
|
|
1021
|
+
if (getFieldValue(candidate, toggleField) === parentKey) {
|
|
1022
|
+
return candidate;
|
|
1023
|
+
}
|
|
721
1024
|
}
|
|
722
|
-
|
|
723
|
-
}
|
|
1025
|
+
return void 0;
|
|
1026
|
+
};
|
|
724
1027
|
const rootItems = [];
|
|
725
1028
|
dataWithLevels.forEach((item) => {
|
|
726
1029
|
if (!getFieldValue(item, childField)) {
|
|
@@ -728,29 +1031,18 @@ var useConvertTreeData = ({
|
|
|
728
1031
|
item.processed = true;
|
|
729
1032
|
}
|
|
730
1033
|
});
|
|
731
|
-
dataWithLevels.forEach((item) => {
|
|
1034
|
+
dataWithLevels.forEach((item, index) => {
|
|
732
1035
|
const parentKey = getFieldValue(item, childField);
|
|
733
1036
|
if (!parentKey || item.processed) return;
|
|
734
|
-
const
|
|
735
|
-
|
|
736
|
-
);
|
|
737
|
-
if (parentItems.length > 0) {
|
|
738
|
-
const parent = parentItems[0];
|
|
1037
|
+
const parent = findNearestPrecedingParent(index, parentKey);
|
|
1038
|
+
if (parent) {
|
|
739
1039
|
item.level = parent.level + 1;
|
|
740
1040
|
parent.children.push(item);
|
|
741
1041
|
item.processed = true;
|
|
742
|
-
|
|
743
|
-
const otherParents = itemMap.get(String(parentKey)) || [];
|
|
744
|
-
if (otherParents.length > 0) {
|
|
745
|
-
const parent = otherParents[0];
|
|
746
|
-
item.level = parent.level + 1;
|
|
747
|
-
parent.children.push(item);
|
|
748
|
-
item.processed = true;
|
|
749
|
-
} else {
|
|
750
|
-
rootItems.push(item);
|
|
751
|
-
item.processed = true;
|
|
752
|
-
}
|
|
1042
|
+
return;
|
|
753
1043
|
}
|
|
1044
|
+
rootItems.push(item);
|
|
1045
|
+
item.processed = true;
|
|
754
1046
|
});
|
|
755
1047
|
return rootItems;
|
|
756
1048
|
}, [enabled, data, toggleField, childField, flattenField]);
|
|
@@ -775,16 +1067,23 @@ var useConvertTreeData = ({
|
|
|
775
1067
|
return result;
|
|
776
1068
|
};
|
|
777
1069
|
const flattenedData = flatten(processedData, [], 0);
|
|
778
|
-
flattenedData.forEach((item) => {
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
(parent) => getFieldValue(parent, toggleField) === getFieldValue(item, childField)
|
|
782
|
-
);
|
|
783
|
-
const parentAmount = parentItem ? Number(getFieldValue(parentItem, qtyField) ?? 1) : 1;
|
|
784
|
-
item.parentCount = parentAmount || 1;
|
|
785
|
-
} else {
|
|
1070
|
+
flattenedData.forEach((item, index) => {
|
|
1071
|
+
const parentKey = getFieldValue(item, childField);
|
|
1072
|
+
if (!parentKey) {
|
|
786
1073
|
item.parentCount = 1;
|
|
1074
|
+
return;
|
|
787
1075
|
}
|
|
1076
|
+
let parentItem;
|
|
1077
|
+
for (let i = index - 1; i >= 0; i -= 1) {
|
|
1078
|
+
const candidate = flattenedData[i];
|
|
1079
|
+
if (!candidate) continue;
|
|
1080
|
+
if (getFieldValue(candidate, toggleField) === parentKey) {
|
|
1081
|
+
parentItem = candidate;
|
|
1082
|
+
break;
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
const parentAmount = parentItem ? Number(getFieldValue(parentItem, qtyField) ?? 1) : 1;
|
|
1086
|
+
item.parentCount = parentAmount || 1;
|
|
788
1087
|
});
|
|
789
1088
|
return flattenedData;
|
|
790
1089
|
}, [
|
|
@@ -931,6 +1230,10 @@ function useGlideTable(options) {
|
|
|
931
1230
|
expandedRows: controlledExpandedRows,
|
|
932
1231
|
onExpandedRowsChange,
|
|
933
1232
|
preventExpand = false,
|
|
1233
|
+
enableSubtreeCopy,
|
|
1234
|
+
onCopyActionsReady,
|
|
1235
|
+
onRowsPaste,
|
|
1236
|
+
enableInsertPaste,
|
|
934
1237
|
enableVirtualization = true,
|
|
935
1238
|
estimateRowHeight = DATA_TABLE_ROW_HEIGHT,
|
|
936
1239
|
virtualOverscan = DATA_TABLE_VIRTUAL_OVERSCAN
|
|
@@ -945,13 +1248,13 @@ function useGlideTable(options) {
|
|
|
945
1248
|
};
|
|
946
1249
|
}, [labelsProp, emptyText, loadingText, selectionLabel]);
|
|
947
1250
|
const enableExpand = Boolean(toggleField);
|
|
1251
|
+
const resolvedEnableSubtreeCopy = enableSubtreeCopy ?? enableExpand;
|
|
948
1252
|
const [internalRowSelection, setInternalRowSelection] = useState3({});
|
|
949
1253
|
const [internalExpandedRows, setInternalExpandedRows] = useState3(
|
|
950
1254
|
() => /* @__PURE__ */ new Set()
|
|
951
1255
|
);
|
|
952
1256
|
const [hoveredRowIndex, setHoveredRowIndex] = useState3(null);
|
|
953
|
-
const
|
|
954
|
-
const scrollRef = useRef3(null);
|
|
1257
|
+
const scrollRef = useRef4(null);
|
|
955
1258
|
const shouldVirtualize = enableVirtualization && !enableRowSpan;
|
|
956
1259
|
useEffect4(() => {
|
|
957
1260
|
if (enableVirtualization && enableRowSpan) {
|
|
@@ -1014,6 +1317,7 @@ function useGlideTable(options) {
|
|
|
1014
1317
|
return collectRowSpanColumns(columns);
|
|
1015
1318
|
}, [enableRowSpan, columns]);
|
|
1016
1319
|
const primaryRowSpanKey = rowSpanColumnKeys[0]?.rowSpanKey;
|
|
1320
|
+
const primaryRowSpanColumnId = rowSpanColumnKeys[0]?.columnId;
|
|
1017
1321
|
const columnRowSpanMap = useMemo2(
|
|
1018
1322
|
() => buildColumnRowSpanMap(tableData, rowSpanColumnKeys),
|
|
1019
1323
|
[tableData, rowSpanColumnKeys]
|
|
@@ -1032,27 +1336,29 @@ function useGlideTable(options) {
|
|
|
1032
1336
|
const totalSize = rowVirtualizer.getTotalSize();
|
|
1033
1337
|
const paddingTop = virtualRows.length > 0 ? virtualRows[0]?.start ?? 0 : 0;
|
|
1034
1338
|
const paddingBottom = virtualRows.length > 0 ? totalSize - (virtualRows[virtualRows.length - 1]?.end ?? 0) : 0;
|
|
1035
|
-
const
|
|
1036
|
-
|
|
1037
|
-
const keys = /* @__PURE__ */ new Set();
|
|
1339
|
+
const selectedRowIndices = useMemo2(() => {
|
|
1340
|
+
const indices = /* @__PURE__ */ new Set();
|
|
1038
1341
|
for (const selectedRow of selectedRows) {
|
|
1039
|
-
|
|
1040
|
-
if (value !== null && value !== void 0) keys.add(String(value));
|
|
1342
|
+
indices.add(selectedRow.index);
|
|
1041
1343
|
}
|
|
1042
|
-
return
|
|
1043
|
-
}, [
|
|
1344
|
+
return indices;
|
|
1345
|
+
}, [selectedRows]);
|
|
1044
1346
|
const {
|
|
1045
1347
|
dragState,
|
|
1046
1348
|
activeSelectionBounds,
|
|
1047
1349
|
handleCellMouseDown,
|
|
1048
1350
|
handleCellMouseEnter,
|
|
1049
|
-
handleFillHandleMouseDown
|
|
1351
|
+
handleFillHandleMouseDown,
|
|
1352
|
+
copySelection
|
|
1050
1353
|
} = useCellSelection({
|
|
1051
1354
|
data: tableData,
|
|
1052
1355
|
rows,
|
|
1053
1356
|
enabled: enableCellSelection,
|
|
1357
|
+
enableSubtreeCopy: resolvedEnableSubtreeCopy,
|
|
1358
|
+
enableInsertPaste: enableInsertPaste ?? true,
|
|
1054
1359
|
onDataChange,
|
|
1055
|
-
onBatchChange
|
|
1360
|
+
onBatchChange,
|
|
1361
|
+
onRowsPaste
|
|
1056
1362
|
});
|
|
1057
1363
|
const {
|
|
1058
1364
|
editingCell,
|
|
@@ -1074,22 +1380,10 @@ function useGlideTable(options) {
|
|
|
1074
1380
|
);
|
|
1075
1381
|
const clearHover = useCallback3(() => {
|
|
1076
1382
|
setHoveredRowIndex(null);
|
|
1077
|
-
setHoveredGroupKey(null);
|
|
1078
1383
|
}, []);
|
|
1079
|
-
const handleRowHover = useCallback3(
|
|
1080
|
-
(rowIndex
|
|
1081
|
-
|
|
1082
|
-
if (!primaryRowSpanKey) {
|
|
1083
|
-
setHoveredGroupKey(null);
|
|
1084
|
-
return;
|
|
1085
|
-
}
|
|
1086
|
-
const groupValue = rowData[primaryRowSpanKey];
|
|
1087
|
-
setHoveredGroupKey(
|
|
1088
|
-
groupValue === null || groupValue === void 0 ? null : String(groupValue)
|
|
1089
|
-
);
|
|
1090
|
-
},
|
|
1091
|
-
[primaryRowSpanKey]
|
|
1092
|
-
);
|
|
1384
|
+
const handleRowHover = useCallback3((rowIndex, _rowData) => {
|
|
1385
|
+
setHoveredRowIndex(rowIndex);
|
|
1386
|
+
}, []);
|
|
1093
1387
|
const handleToggleSelect = useCallback3(
|
|
1094
1388
|
(row) => {
|
|
1095
1389
|
if (!row.getCanSelect()) return;
|
|
@@ -1112,10 +1406,10 @@ function useGlideTable(options) {
|
|
|
1112
1406
|
rowSpan: {
|
|
1113
1407
|
enableRowSpan,
|
|
1114
1408
|
primaryRowSpanKey,
|
|
1409
|
+
primaryRowSpanColumnId,
|
|
1115
1410
|
columnRowSpanMap,
|
|
1116
1411
|
hoveredRowIndex,
|
|
1117
|
-
|
|
1118
|
-
selectedGroupKeys,
|
|
1412
|
+
selectedRowIndices,
|
|
1119
1413
|
onRowHover: handleRowHover
|
|
1120
1414
|
},
|
|
1121
1415
|
selection: {
|
|
@@ -1153,10 +1447,10 @@ function useGlideTable(options) {
|
|
|
1153
1447
|
}, [
|
|
1154
1448
|
enableRowSpan,
|
|
1155
1449
|
primaryRowSpanKey,
|
|
1450
|
+
primaryRowSpanColumnId,
|
|
1156
1451
|
columnRowSpanMap,
|
|
1157
1452
|
hoveredRowIndex,
|
|
1158
|
-
|
|
1159
|
-
selectedGroupKeys,
|
|
1453
|
+
selectedRowIndices,
|
|
1160
1454
|
handleRowHover,
|
|
1161
1455
|
rowSelectionMode,
|
|
1162
1456
|
selectOnRowClick,
|
|
@@ -1182,6 +1476,14 @@ function useGlideTable(options) {
|
|
|
1182
1476
|
labels.expandRow,
|
|
1183
1477
|
labels.collapseRow
|
|
1184
1478
|
]);
|
|
1479
|
+
const copySelectionRef = useRef4(copySelection);
|
|
1480
|
+
useEffect4(() => {
|
|
1481
|
+
copySelectionRef.current = copySelection;
|
|
1482
|
+
}, [copySelection]);
|
|
1483
|
+
const stableCopySelection = useCallback3((options2) => copySelectionRef.current(options2), []);
|
|
1484
|
+
useEffect4(() => {
|
|
1485
|
+
onCopyActionsReady?.({ copySelection: stableCopySelection });
|
|
1486
|
+
}, [onCopyActionsReady, stableCopySelection]);
|
|
1185
1487
|
return {
|
|
1186
1488
|
table,
|
|
1187
1489
|
tableData,
|
|
@@ -1201,7 +1503,8 @@ function useGlideTable(options) {
|
|
|
1201
1503
|
paddingBottom,
|
|
1202
1504
|
rowContextValue,
|
|
1203
1505
|
handleToggleSelect,
|
|
1204
|
-
clearHover
|
|
1506
|
+
clearHover,
|
|
1507
|
+
copySelection: stableCopySelection
|
|
1205
1508
|
};
|
|
1206
1509
|
}
|
|
1207
1510
|
|
|
@@ -1211,7 +1514,7 @@ import { useMemo as useMemo3 } from "react";
|
|
|
1211
1514
|
|
|
1212
1515
|
// src/components/ui/table/components/DataTable/DataTableRow.tsx
|
|
1213
1516
|
import { flexRender } from "@tanstack/react-table";
|
|
1214
|
-
import { useEffect as useEffect5, useRef as
|
|
1517
|
+
import { useEffect as useEffect5, useRef as useRef5 } from "react";
|
|
1215
1518
|
|
|
1216
1519
|
// src/components/ui/table/DataTableContext.tsx
|
|
1217
1520
|
import { createContext, use } from "react";
|
|
@@ -1401,11 +1704,10 @@ function DataTableRow({
|
|
|
1401
1704
|
const { classNames, rowSpan, selection, cellSelection, cellEdit, expand } = useDataTableRowContext();
|
|
1402
1705
|
const {
|
|
1403
1706
|
enableRowSpan,
|
|
1404
|
-
|
|
1707
|
+
primaryRowSpanColumnId,
|
|
1405
1708
|
columnRowSpanMap,
|
|
1406
1709
|
hoveredRowIndex,
|
|
1407
|
-
|
|
1408
|
-
selectedGroupKeys,
|
|
1710
|
+
selectedRowIndices,
|
|
1409
1711
|
onRowHover
|
|
1410
1712
|
} = rowSpan;
|
|
1411
1713
|
const { rowSelectionMode, selectOnRowClick, onRowClick, getRowClassName } = selection;
|
|
@@ -1438,9 +1740,11 @@ function DataTableRow({
|
|
|
1438
1740
|
const rowData = row.original;
|
|
1439
1741
|
const isRowHovered = hoveredRowIndex === rowIndex;
|
|
1440
1742
|
const isRowSelected = row.getIsSelected();
|
|
1441
|
-
const
|
|
1442
|
-
|
|
1443
|
-
|
|
1743
|
+
const { startRow: primaryGroupStart, rowSpan: primaryGroupSpan } = resolveRowSpanAt(
|
|
1744
|
+
primaryRowSpanColumnId ? columnRowSpanMap.get(primaryRowSpanColumnId) : void 0,
|
|
1745
|
+
rowIndex
|
|
1746
|
+
);
|
|
1747
|
+
const isGroupHovered = enableRowSpan && hoveredRowIndex !== null && hoveredRowIndex >= primaryGroupStart && hoveredRowIndex <= primaryGroupStart + primaryGroupSpan - 1;
|
|
1444
1748
|
const visibleCells = row.getVisibleCells();
|
|
1445
1749
|
const columnIdsByIndex = visibleCells.map((cell) => cell.column.id);
|
|
1446
1750
|
const isVisuallySelectedAt = activeSelectionBounds ? (targetRow, targetCol) => {
|
|
@@ -1464,7 +1768,7 @@ function DataTableRow({
|
|
|
1464
1768
|
const expandKey = toggleField && rowData[toggleField] !== null && rowData[toggleField] !== void 0 ? String(rowData[toggleField]) : null;
|
|
1465
1769
|
const isExpanded = expandKey !== null && Boolean(expandedRows?.has(expandKey));
|
|
1466
1770
|
const rowLevel = typeof rowData.level === "number" ? rowData.level : 0;
|
|
1467
|
-
const editInputRef =
|
|
1771
|
+
const editInputRef = useRef5(null);
|
|
1468
1772
|
const isRowEditing = editingCell?.rowIndex === rowIndex;
|
|
1469
1773
|
useEffect5(() => {
|
|
1470
1774
|
if (!isRowEditing) return;
|
|
@@ -1517,7 +1821,16 @@ function DataTableRow({
|
|
|
1517
1821
|
const cellRowSpan = rowSpanInfo?.rowSpan ?? 1;
|
|
1518
1822
|
const isMergedCellHovered = hoveredRowIndex !== null && hoveredRowIndex >= rowIndex && hoveredRowIndex <= rowIndex + cellRowSpan - 1;
|
|
1519
1823
|
const showCellHover = isRowSpanColumn ? isMergedCellHovered : isRowHovered;
|
|
1520
|
-
|
|
1824
|
+
let isMergedCellSelected = false;
|
|
1825
|
+
if (isRowSpanColumn) {
|
|
1826
|
+
for (let r = rowIndex; r < rowIndex + cellRowSpan; r += 1) {
|
|
1827
|
+
if (selectedRowIndices.has(r)) {
|
|
1828
|
+
isMergedCellSelected = true;
|
|
1829
|
+
break;
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
const showCellSelected = isRowSpanColumn ? isMergedCellSelected : isRowSelected;
|
|
1521
1834
|
const isMerged = cellRowSpan > 1;
|
|
1522
1835
|
const showMergedRightEdge = isMerged && (cellIndex === visibleCells.length - 1 || resolveRowSpanAt(
|
|
1523
1836
|
columnRowSpanMap.get(columnIdsByIndex[cellIndex + 1]),
|
|
@@ -1877,7 +2190,12 @@ function DataTable({
|
|
|
1877
2190
|
return /* @__PURE__ */ jsx5(
|
|
1878
2191
|
"th",
|
|
1879
2192
|
{
|
|
1880
|
-
style: {
|
|
2193
|
+
style: {
|
|
2194
|
+
width: header.getSize() !== 150 ? header.getSize() : void 0,
|
|
2195
|
+
// Column sizing follows TanStack's `size`, but
|
|
2196
|
+
// ensure the column keeps its min width when the container shrinks.
|
|
2197
|
+
minWidth: header.getSize() !== 150 ? header.getSize() : void 0
|
|
2198
|
+
},
|
|
1881
2199
|
className: cn(
|
|
1882
2200
|
"data-table-head-cell",
|
|
1883
2201
|
CELL_ALIGN_CLASS[align],
|
|
@@ -2319,10 +2637,14 @@ export {
|
|
|
2319
2637
|
applyFillData,
|
|
2320
2638
|
applySelectionUpdater,
|
|
2321
2639
|
buildColumnRowSpanMap,
|
|
2640
|
+
buildRowsPastePayload,
|
|
2322
2641
|
canExpandRow,
|
|
2642
|
+
collectCopyRowEntries,
|
|
2643
|
+
collectCopyRows,
|
|
2323
2644
|
collectFillChanges,
|
|
2324
2645
|
collectRowSpanColumns,
|
|
2325
2646
|
createTable,
|
|
2647
|
+
flattenSubtreeRows,
|
|
2326
2648
|
getCellEditDraftValue,
|
|
2327
2649
|
getCellSelectionEdgeStyle,
|
|
2328
2650
|
getColumnEditType,
|
|
@@ -2330,15 +2652,22 @@ export {
|
|
|
2330
2652
|
hasCellSelectionEdges,
|
|
2331
2653
|
isCellInSelection,
|
|
2332
2654
|
isColumnEditable,
|
|
2655
|
+
isEditablePasteTarget,
|
|
2333
2656
|
measureMergedSpanRowHeights,
|
|
2334
2657
|
parseCellEditValue,
|
|
2658
|
+
parseClipboardTSV,
|
|
2659
|
+
parseClipboardTSVWithDepths,
|
|
2335
2660
|
resolveDataTableLabels,
|
|
2661
|
+
resolvePasteColumnIds,
|
|
2336
2662
|
resolveRowSelection,
|
|
2337
2663
|
resolveRowSpanAt,
|
|
2338
2664
|
rowRangeToHeightRatios,
|
|
2665
|
+
serializeCopyRowsToTSV,
|
|
2666
|
+
serializeSelectionToTSV,
|
|
2339
2667
|
toggleExpandedRowId,
|
|
2340
2668
|
useCellEdit,
|
|
2341
2669
|
useCellSelection,
|
|
2342
2670
|
useConvertTreeData,
|
|
2343
|
-
useGlideTable
|
|
2671
|
+
useGlideTable,
|
|
2672
|
+
writeSelectionToClipboard
|
|
2344
2673
|
};
|