react-glide-table 2.2.1 → 2.3.1
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 +33 -30
- package/dist/compound.cjs +690 -222
- package/dist/compound.d.cts +2 -2
- package/dist/compound.d.ts +2 -2
- package/dist/compound.js +594 -126
- package/dist/core.cjs +623 -223
- package/dist/core.d.cts +47 -7
- package/dist/core.d.ts +47 -7
- package/dist/core.js +510 -111
- package/dist/index.cjs +722 -252
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +598 -129
- package/dist/{types-CnsQ8GZb.d.cts → types-DdeVn-9s.d.cts} +23 -6
- package/dist/{types-CnsQ8GZb.d.ts → types-DdeVn-9s.d.ts} +23 -6
- package/package.json +1 -1
package/dist/core.js
CHANGED
|
@@ -432,6 +432,90 @@ function withCellUpdate(context, commitValue) {
|
|
|
432
432
|
};
|
|
433
433
|
}
|
|
434
434
|
|
|
435
|
+
// src/components/ui/table/features/cell-selection/pasteData.ts
|
|
436
|
+
function countLeadingEmptyCells(cells) {
|
|
437
|
+
let depth = 0;
|
|
438
|
+
while (depth < cells.length && cells[depth] === "") {
|
|
439
|
+
depth += 1;
|
|
440
|
+
}
|
|
441
|
+
return depth;
|
|
442
|
+
}
|
|
443
|
+
function looksLikeSubtreeIndentation(leadingEmptyCounts) {
|
|
444
|
+
if (leadingEmptyCounts.length === 0) return false;
|
|
445
|
+
const firstDepth = leadingEmptyCounts[0] ?? 0;
|
|
446
|
+
if (firstDepth !== 0) return false;
|
|
447
|
+
return leadingEmptyCounts.some((depth) => depth > 0);
|
|
448
|
+
}
|
|
449
|
+
function parseClipboardTSV(text) {
|
|
450
|
+
return parseClipboardTSVWithDepths(text).values;
|
|
451
|
+
}
|
|
452
|
+
function parseClipboardTSVWithDepths(text) {
|
|
453
|
+
if (!text) return { values: [], depths: [] };
|
|
454
|
+
const normalized = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
455
|
+
const withoutTrailing = normalized.replace(/\n+$/, "");
|
|
456
|
+
if (!withoutTrailing) return { values: [], depths: [] };
|
|
457
|
+
const rows = withoutTrailing.split("\n").map((line) => line.split(" "));
|
|
458
|
+
const leadingEmptyCounts = rows.map(countLeadingEmptyCells);
|
|
459
|
+
const treatAsDepth = looksLikeSubtreeIndentation(leadingEmptyCounts);
|
|
460
|
+
const values = [];
|
|
461
|
+
const depths = [];
|
|
462
|
+
for (let index = 0; index < rows.length; index += 1) {
|
|
463
|
+
const cells = rows[index] ?? [];
|
|
464
|
+
const depth = leadingEmptyCounts[index] ?? 0;
|
|
465
|
+
if (treatAsDepth) {
|
|
466
|
+
values.push(cells.slice(depth));
|
|
467
|
+
depths.push(depth);
|
|
468
|
+
} else {
|
|
469
|
+
values.push(cells);
|
|
470
|
+
depths.push(0);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
return { values, depths };
|
|
474
|
+
}
|
|
475
|
+
function resolvePasteColumnIds(rows, startCol, width) {
|
|
476
|
+
if (width <= 0) return [];
|
|
477
|
+
const cells = rows[0]?.getVisibleCells() ?? [];
|
|
478
|
+
const columnIds = [];
|
|
479
|
+
for (let offset = 0; offset < width; offset += 1) {
|
|
480
|
+
const cell = cells[startCol + offset];
|
|
481
|
+
if (!cell) break;
|
|
482
|
+
columnIds.push(cell.column.id);
|
|
483
|
+
}
|
|
484
|
+
return columnIds;
|
|
485
|
+
}
|
|
486
|
+
function buildRowsPastePayload(rows, startRow, startCol, text, mode, endRow = startRow) {
|
|
487
|
+
const { values, depths } = parseClipboardTSVWithDepths(text);
|
|
488
|
+
if (values.length === 0) return null;
|
|
489
|
+
const width = Math.max(...values.map((row) => row.length), 0);
|
|
490
|
+
if (width === 0) return null;
|
|
491
|
+
const columnIds = resolvePasteColumnIds(rows, startCol, width);
|
|
492
|
+
if (columnIds.length === 0) return null;
|
|
493
|
+
const rowIds = [];
|
|
494
|
+
for (let offset = 0; offset < values.length; offset += 1) {
|
|
495
|
+
const row = rows[startRow + offset];
|
|
496
|
+
if (!row) break;
|
|
497
|
+
rowIds.push(row.id);
|
|
498
|
+
}
|
|
499
|
+
const anchorRow = rows[endRow] ?? rows[startRow];
|
|
500
|
+
return {
|
|
501
|
+
mode,
|
|
502
|
+
startRow,
|
|
503
|
+
startCol,
|
|
504
|
+
endRow,
|
|
505
|
+
rowIds,
|
|
506
|
+
anchorRowId: anchorRow?.id ?? "",
|
|
507
|
+
columnIds,
|
|
508
|
+
values,
|
|
509
|
+
depths
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
function isEditablePasteTarget(target) {
|
|
513
|
+
if (!(target instanceof HTMLElement)) return false;
|
|
514
|
+
const tag = target.tagName;
|
|
515
|
+
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
|
|
516
|
+
return Boolean(target.isContentEditable);
|
|
517
|
+
}
|
|
518
|
+
|
|
435
519
|
// src/components/ui/table/features/cell-selection/useCellSelection.ts
|
|
436
520
|
import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
437
521
|
|
|
@@ -741,6 +825,219 @@ function hasCellSelectionEdges(style) {
|
|
|
741
825
|
}
|
|
742
826
|
|
|
743
827
|
// src/components/ui/table/features/cell-selection/copyData.ts
|
|
828
|
+
import { isValidElement } from "react";
|
|
829
|
+
function isReactNodeIterable(node) {
|
|
830
|
+
return typeof node === "object" && node !== null && !isValidElement(node) && Symbol.iterator in node;
|
|
831
|
+
}
|
|
832
|
+
function getElementTypeName(type) {
|
|
833
|
+
if (typeof type === "string") return type;
|
|
834
|
+
if (typeof type === "function") {
|
|
835
|
+
const fn = type;
|
|
836
|
+
return fn.displayName || fn.name || "";
|
|
837
|
+
}
|
|
838
|
+
if (typeof type === "object" && type !== null) {
|
|
839
|
+
const component = type;
|
|
840
|
+
return component.displayName || component.render?.displayName || component.render?.name || "";
|
|
841
|
+
}
|
|
842
|
+
return "";
|
|
843
|
+
}
|
|
844
|
+
function isButtonReactElement(node) {
|
|
845
|
+
const typeName = getElementTypeName(node.type);
|
|
846
|
+
if (typeName === "button" || /button/i.test(typeName)) return true;
|
|
847
|
+
const props = node.props;
|
|
848
|
+
if (props.role === "button") return true;
|
|
849
|
+
if (typeName === "input" && props.type === "button") return true;
|
|
850
|
+
return false;
|
|
851
|
+
}
|
|
852
|
+
function isImageReactElement(node) {
|
|
853
|
+
const typeName = getElementTypeName(node.type);
|
|
854
|
+
return typeName === "img" || typeName === "image" || /image/i.test(typeName);
|
|
855
|
+
}
|
|
856
|
+
var IMAGE_URL_PROP_KEYS = ["src", "url", "href", "imageUrl", "srcUrl"];
|
|
857
|
+
function isLikelyUrl(value) {
|
|
858
|
+
const trimmed = value.trim();
|
|
859
|
+
if (!trimmed) return false;
|
|
860
|
+
if (/^(https?:|blob:|data:|\/\/)/i.test(trimmed)) return true;
|
|
861
|
+
if (trimmed.startsWith("/") && trimmed.includes("/")) return true;
|
|
862
|
+
return false;
|
|
863
|
+
}
|
|
864
|
+
function pickUrlFromUnknown(value) {
|
|
865
|
+
if (typeof value === "string") {
|
|
866
|
+
return isLikelyUrl(value) ? value.trim() : "";
|
|
867
|
+
}
|
|
868
|
+
if (Array.isArray(value)) {
|
|
869
|
+
return value.map((item) => pickUrlFromUnknown(item)).filter((item) => item.length > 0).join(", ");
|
|
870
|
+
}
|
|
871
|
+
if (value && typeof value === "object") {
|
|
872
|
+
const record = value;
|
|
873
|
+
for (const key of IMAGE_URL_PROP_KEYS) {
|
|
874
|
+
const candidate = record[key];
|
|
875
|
+
if (typeof candidate === "string" && candidate.trim()) {
|
|
876
|
+
return candidate.trim();
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
return "";
|
|
881
|
+
}
|
|
882
|
+
function imageElementText(node) {
|
|
883
|
+
const props = node.props;
|
|
884
|
+
for (const key of IMAGE_URL_PROP_KEYS) {
|
|
885
|
+
const candidate = props[key];
|
|
886
|
+
if (typeof candidate === "string" && candidate.trim()) {
|
|
887
|
+
return candidate.trim();
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
return "";
|
|
891
|
+
}
|
|
892
|
+
function reactNodeContainsImage(node) {
|
|
893
|
+
if (isValidElement(node)) {
|
|
894
|
+
if (isImageReactElement(node)) return true;
|
|
895
|
+
return reactNodeContainsImage(node.props.children);
|
|
896
|
+
}
|
|
897
|
+
if (isReactNodeIterable(node)) {
|
|
898
|
+
for (const child of node) {
|
|
899
|
+
if (reactNodeContainsImage(child)) return true;
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
return false;
|
|
903
|
+
}
|
|
904
|
+
function readImgUrl(img) {
|
|
905
|
+
const attr = img.getAttribute("src")?.trim() ?? "";
|
|
906
|
+
if (attr) return attr;
|
|
907
|
+
if (img instanceof HTMLImageElement) {
|
|
908
|
+
const current = img.currentSrc?.trim() ?? "";
|
|
909
|
+
if (current && current !== img.baseURI) return current;
|
|
910
|
+
}
|
|
911
|
+
return "";
|
|
912
|
+
}
|
|
913
|
+
function readDomImageUrls(rowIndex, colIndex, root) {
|
|
914
|
+
const scope = root ?? (typeof document === "undefined" ? null : document);
|
|
915
|
+
if (!scope) return "";
|
|
916
|
+
const cells = scope.querySelectorAll(
|
|
917
|
+
`[data-row-index="${rowIndex}"][data-col-index="${colIndex}"]`
|
|
918
|
+
);
|
|
919
|
+
for (const cell of cells) {
|
|
920
|
+
const urls = Array.from(cell.querySelectorAll("img")).flatMap((img) => {
|
|
921
|
+
const url = readImgUrl(img);
|
|
922
|
+
return url ? [url] : [];
|
|
923
|
+
});
|
|
924
|
+
if (urls.length > 0) return urls.join(", ");
|
|
925
|
+
}
|
|
926
|
+
return "";
|
|
927
|
+
}
|
|
928
|
+
function reactNodeToText(node) {
|
|
929
|
+
if (node == null || typeof node === "boolean") return "";
|
|
930
|
+
if (typeof node === "string" || typeof node === "number" || typeof node === "bigint") {
|
|
931
|
+
return String(node);
|
|
932
|
+
}
|
|
933
|
+
if (isReactNodeIterable(node)) {
|
|
934
|
+
let text = "";
|
|
935
|
+
for (const child of node) {
|
|
936
|
+
text += reactNodeToText(child);
|
|
937
|
+
}
|
|
938
|
+
return text;
|
|
939
|
+
}
|
|
940
|
+
if (isValidElement(node)) {
|
|
941
|
+
if (isButtonReactElement(node)) return "";
|
|
942
|
+
const props = node.props;
|
|
943
|
+
const childText = reactNodeToText(props.children);
|
|
944
|
+
if (childText) return childText;
|
|
945
|
+
const fromImage = imageElementText(node);
|
|
946
|
+
if (fromImage) return fromImage;
|
|
947
|
+
if (isImageReactElement(node)) return "";
|
|
948
|
+
if (typeof props.alt === "string" && props.alt) return props.alt;
|
|
949
|
+
if (typeof props.title === "string" && props.title) return props.title;
|
|
950
|
+
return "";
|
|
951
|
+
}
|
|
952
|
+
return "";
|
|
953
|
+
}
|
|
954
|
+
function sanitizeClipboardCell(text) {
|
|
955
|
+
return text.replace(/\s+/g, " ").trim();
|
|
956
|
+
}
|
|
957
|
+
function createCopyRenderRow(rowData, index) {
|
|
958
|
+
return {
|
|
959
|
+
id: getOriginalRowId(rowData) || String(index),
|
|
960
|
+
index,
|
|
961
|
+
original: rowData,
|
|
962
|
+
getIsCellDragSelected: () => false
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
function buildVisibleRowLookup(visibleRows) {
|
|
966
|
+
const lookup = /* @__PURE__ */ new Map();
|
|
967
|
+
for (const row of visibleRows) {
|
|
968
|
+
lookup.set(row.original, row);
|
|
969
|
+
}
|
|
970
|
+
return lookup;
|
|
971
|
+
}
|
|
972
|
+
function resolveCopyColumnId(cell) {
|
|
973
|
+
if (cell.column.id) return cell.column.id;
|
|
974
|
+
const columnDef = cell.column.columnDef;
|
|
975
|
+
if (columnDef.id) return columnDef.id;
|
|
976
|
+
if (columnDef.accessorKey != null && columnDef.accessorKey !== "") {
|
|
977
|
+
return String(columnDef.accessorKey);
|
|
978
|
+
}
|
|
979
|
+
return "";
|
|
980
|
+
}
|
|
981
|
+
function isPrimitiveCopyValue(value) {
|
|
982
|
+
return value == null || typeof value !== "object";
|
|
983
|
+
}
|
|
984
|
+
function extractRenderedCopyText(node, value, cellPosition, root) {
|
|
985
|
+
const rendered = sanitizeClipboardCell(reactNodeToText(node));
|
|
986
|
+
if (reactNodeContainsImage(node)) {
|
|
987
|
+
const fromDom = cellPosition != null ? sanitizeClipboardCell(
|
|
988
|
+
readDomImageUrls(cellPosition.rowIndex, cellPosition.colIndex, root)
|
|
989
|
+
) : "";
|
|
990
|
+
if (fromDom) return fromDom;
|
|
991
|
+
if (rendered && isLikelyUrl(rendered)) return rendered;
|
|
992
|
+
return sanitizeClipboardCell(pickUrlFromUnknown(value));
|
|
993
|
+
}
|
|
994
|
+
return rendered;
|
|
995
|
+
}
|
|
996
|
+
function formatCopyCellText(rowData, columnDef, columnId, visibleRow, fallbackIndex, sourceCell, cellPosition, options) {
|
|
997
|
+
const meta = columnDef.meta;
|
|
998
|
+
const value = sourceCell ? sourceCell.getValue() : readRowColumnValue(rowData, columnDef);
|
|
999
|
+
const cellRender = meta?.cellRender;
|
|
1000
|
+
if (typeof cellRender === "function") {
|
|
1001
|
+
try {
|
|
1002
|
+
const row = visibleRow ?? createCopyRenderRow(rowData, fallbackIndex);
|
|
1003
|
+
const node = cellRender({
|
|
1004
|
+
value,
|
|
1005
|
+
row,
|
|
1006
|
+
index: row.index,
|
|
1007
|
+
columnId,
|
|
1008
|
+
cellProps: meta?.cellProps,
|
|
1009
|
+
update: () => {
|
|
1010
|
+
}
|
|
1011
|
+
});
|
|
1012
|
+
return extractRenderedCopyText(node, value, cellPosition, options?.root);
|
|
1013
|
+
} catch {
|
|
1014
|
+
return formatCellValue(value);
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
if (options?.registry && meta?.kind && isPrimitiveCopyValue(value)) {
|
|
1018
|
+
try {
|
|
1019
|
+
const row = visibleRow ?? createCopyRenderRow(rowData, fallbackIndex);
|
|
1020
|
+
const ctx = {
|
|
1021
|
+
value,
|
|
1022
|
+
row,
|
|
1023
|
+
index: row.index,
|
|
1024
|
+
columnId,
|
|
1025
|
+
cellProps: meta.cellProps,
|
|
1026
|
+
update: () => {
|
|
1027
|
+
}
|
|
1028
|
+
};
|
|
1029
|
+
const renderer = resolveCellRenderer(options.registry, meta.kind, ctx);
|
|
1030
|
+
if (renderer) {
|
|
1031
|
+
const node = renderer.render(ctx);
|
|
1032
|
+
const rendered = extractRenderedCopyText(node, value, cellPosition, options.root);
|
|
1033
|
+
if (rendered) return rendered;
|
|
1034
|
+
}
|
|
1035
|
+
} catch {
|
|
1036
|
+
return formatCellValue(value);
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
return formatCellValue(value);
|
|
1040
|
+
}
|
|
744
1041
|
function formatPrimitive(value) {
|
|
745
1042
|
if (value === null || value === void 0) return "";
|
|
746
1043
|
if (typeof value === "string") return value;
|
|
@@ -846,37 +1143,47 @@ function collectCopyRowEntries(visibleRows, bounds, mode = "visible") {
|
|
|
846
1143
|
function collectCopyRows(visibleRows, bounds, mode = "visible") {
|
|
847
1144
|
return collectCopyRowEntries(visibleRows, bounds, mode).map((entry) => entry.row);
|
|
848
1145
|
}
|
|
849
|
-
function serializeCopyRowsToTSV(copyRows, visibleRows, bounds, depths) {
|
|
1146
|
+
function serializeCopyRowsToTSV(copyRows, visibleRows, bounds, depths, options) {
|
|
850
1147
|
if (copyRows.length === 0) return "";
|
|
851
1148
|
const { startCol, endCol } = bounds;
|
|
852
1149
|
const columnCells = visibleRows[0]?.getVisibleCells().slice(startCol, endCol + 1) ?? [];
|
|
853
1150
|
if (columnCells.length === 0) return "";
|
|
854
1151
|
const resolvedDepths = depths && depths.length === copyRows.length ? depths : copyRows.map((row) => getRowDepth(row));
|
|
855
1152
|
const minDepth = Math.min(...resolvedDepths);
|
|
1153
|
+
const visibleRowByOriginal = buildVisibleRowLookup(visibleRows);
|
|
856
1154
|
return copyRows.map((rowData, index) => {
|
|
857
1155
|
const relativeDepth = Math.max(0, (resolvedDepths[index] ?? 0) - minDepth);
|
|
858
|
-
const
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
1156
|
+
const visibleRow = visibleRowByOriginal.get(rowData);
|
|
1157
|
+
const matchingCells = visibleRow?.getVisibleCells().slice(startCol, endCol + 1);
|
|
1158
|
+
const line = columnCells.map((templateCell, colOffset) => {
|
|
1159
|
+
const sourceCell = matchingCells?.[colOffset];
|
|
1160
|
+
const column = sourceCell?.column ?? templateCell.column;
|
|
1161
|
+
return formatCopyCellText(
|
|
1162
|
+
rowData,
|
|
1163
|
+
column.columnDef,
|
|
1164
|
+
resolveCopyColumnId(sourceCell ?? templateCell),
|
|
1165
|
+
visibleRow,
|
|
1166
|
+
visibleRow?.index ?? index,
|
|
1167
|
+
sourceCell,
|
|
1168
|
+
visibleRow != null ? { rowIndex: visibleRow.index, colIndex: startCol + colOffset } : void 0,
|
|
1169
|
+
options
|
|
1170
|
+
);
|
|
1171
|
+
}).join(" ");
|
|
866
1172
|
return `${" ".repeat(relativeDepth)}${line}`;
|
|
867
1173
|
}).join("\n");
|
|
868
1174
|
}
|
|
869
|
-
function serializeSelectionToTSV(visibleRows, bounds, mode = "visible") {
|
|
1175
|
+
function serializeSelectionToTSV(visibleRows, bounds, mode = "visible", options) {
|
|
870
1176
|
const entries = collectCopyRowEntries(visibleRows, bounds, mode);
|
|
871
1177
|
return serializeCopyRowsToTSV(
|
|
872
1178
|
entries.map((entry) => entry.row),
|
|
873
1179
|
visibleRows,
|
|
874
1180
|
bounds,
|
|
875
|
-
entries.map((entry) => entry.depth)
|
|
1181
|
+
entries.map((entry) => entry.depth),
|
|
1182
|
+
options
|
|
876
1183
|
);
|
|
877
1184
|
}
|
|
878
|
-
async function writeSelectionToClipboard(visibleRows, bounds, mode = "visible") {
|
|
879
|
-
const text = serializeSelectionToTSV(visibleRows, bounds, mode);
|
|
1185
|
+
async function writeSelectionToClipboard(visibleRows, bounds, mode = "visible", options) {
|
|
1186
|
+
const text = serializeSelectionToTSV(visibleRows, bounds, mode, options);
|
|
880
1187
|
if (!text) return false;
|
|
881
1188
|
try {
|
|
882
1189
|
await navigator.clipboard.writeText(text);
|
|
@@ -942,90 +1249,6 @@ function hasFillExtension(sourceBounds, fillBounds) {
|
|
|
942
1249
|
return fillBounds.startRow < sourceBounds.startRow || fillBounds.endRow > sourceBounds.endRow || fillBounds.startCol < sourceBounds.startCol || fillBounds.endCol > sourceBounds.endCol;
|
|
943
1250
|
}
|
|
944
1251
|
|
|
945
|
-
// src/components/ui/table/features/cell-selection/pasteData.ts
|
|
946
|
-
function countLeadingEmptyCells(cells) {
|
|
947
|
-
let depth = 0;
|
|
948
|
-
while (depth < cells.length && cells[depth] === "") {
|
|
949
|
-
depth += 1;
|
|
950
|
-
}
|
|
951
|
-
return depth;
|
|
952
|
-
}
|
|
953
|
-
function looksLikeSubtreeIndentation(leadingEmptyCounts) {
|
|
954
|
-
if (leadingEmptyCounts.length === 0) return false;
|
|
955
|
-
const firstDepth = leadingEmptyCounts[0] ?? 0;
|
|
956
|
-
if (firstDepth !== 0) return false;
|
|
957
|
-
return leadingEmptyCounts.some((depth) => depth > 0);
|
|
958
|
-
}
|
|
959
|
-
function parseClipboardTSV(text) {
|
|
960
|
-
return parseClipboardTSVWithDepths(text).values;
|
|
961
|
-
}
|
|
962
|
-
function parseClipboardTSVWithDepths(text) {
|
|
963
|
-
if (!text) return { values: [], depths: [] };
|
|
964
|
-
const normalized = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
965
|
-
const withoutTrailing = normalized.replace(/\n+$/, "");
|
|
966
|
-
if (!withoutTrailing) return { values: [], depths: [] };
|
|
967
|
-
const rows = withoutTrailing.split("\n").map((line) => line.split(" "));
|
|
968
|
-
const leadingEmptyCounts = rows.map(countLeadingEmptyCells);
|
|
969
|
-
const treatAsDepth = looksLikeSubtreeIndentation(leadingEmptyCounts);
|
|
970
|
-
const values = [];
|
|
971
|
-
const depths = [];
|
|
972
|
-
for (let index = 0; index < rows.length; index += 1) {
|
|
973
|
-
const cells = rows[index] ?? [];
|
|
974
|
-
const depth = leadingEmptyCounts[index] ?? 0;
|
|
975
|
-
if (treatAsDepth) {
|
|
976
|
-
values.push(cells.slice(depth));
|
|
977
|
-
depths.push(depth);
|
|
978
|
-
} else {
|
|
979
|
-
values.push(cells);
|
|
980
|
-
depths.push(0);
|
|
981
|
-
}
|
|
982
|
-
}
|
|
983
|
-
return { values, depths };
|
|
984
|
-
}
|
|
985
|
-
function resolvePasteColumnIds(rows, startCol, width) {
|
|
986
|
-
if (width <= 0) return [];
|
|
987
|
-
const cells = rows[0]?.getVisibleCells() ?? [];
|
|
988
|
-
const columnIds = [];
|
|
989
|
-
for (let offset = 0; offset < width; offset += 1) {
|
|
990
|
-
const cell = cells[startCol + offset];
|
|
991
|
-
if (!cell) break;
|
|
992
|
-
columnIds.push(cell.column.id);
|
|
993
|
-
}
|
|
994
|
-
return columnIds;
|
|
995
|
-
}
|
|
996
|
-
function buildRowsPastePayload(rows, startRow, startCol, text, mode, endRow = startRow) {
|
|
997
|
-
const { values, depths } = parseClipboardTSVWithDepths(text);
|
|
998
|
-
if (values.length === 0) return null;
|
|
999
|
-
const width = Math.max(...values.map((row) => row.length), 0);
|
|
1000
|
-
if (width === 0) return null;
|
|
1001
|
-
const columnIds = resolvePasteColumnIds(rows, startCol, width);
|
|
1002
|
-
if (columnIds.length === 0) return null;
|
|
1003
|
-
const rowIds = [];
|
|
1004
|
-
for (let offset = 0; offset < values.length; offset += 1) {
|
|
1005
|
-
const row = rows[startRow + offset];
|
|
1006
|
-
if (!row) break;
|
|
1007
|
-
rowIds.push(row.id);
|
|
1008
|
-
}
|
|
1009
|
-
const anchorRow = rows[endRow] ?? rows[startRow];
|
|
1010
|
-
return {
|
|
1011
|
-
mode,
|
|
1012
|
-
startRow,
|
|
1013
|
-
startCol,
|
|
1014
|
-
endRow,
|
|
1015
|
-
rowIds,
|
|
1016
|
-
anchorRowId: anchorRow?.id ?? "",
|
|
1017
|
-
columnIds,
|
|
1018
|
-
values,
|
|
1019
|
-
depths
|
|
1020
|
-
};
|
|
1021
|
-
}
|
|
1022
|
-
function isEditablePasteTarget(target) {
|
|
1023
|
-
if (!(target instanceof HTMLElement)) return false;
|
|
1024
|
-
const tag = target.tagName;
|
|
1025
|
-
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
|
|
1026
|
-
return Boolean(target.isContentEditable);
|
|
1027
|
-
}
|
|
1028
|
-
|
|
1029
1252
|
// src/components/ui/table/features/cell-selection/useCellSelection.ts
|
|
1030
1253
|
function useCellSelection({
|
|
1031
1254
|
data,
|
|
@@ -1037,7 +1260,9 @@ function useCellSelection({
|
|
|
1037
1260
|
onDataChange,
|
|
1038
1261
|
onBatchChange,
|
|
1039
1262
|
onRowsPaste,
|
|
1040
|
-
onCellNavigate
|
|
1263
|
+
onCellNavigate,
|
|
1264
|
+
cellRendererRegistry,
|
|
1265
|
+
rootRef
|
|
1041
1266
|
}) {
|
|
1042
1267
|
const [dragState, setDragState] = useState2(INITIAL_DRAG_STATE);
|
|
1043
1268
|
const pendingPasteModeRef = useRef2(null);
|
|
@@ -1105,11 +1330,19 @@ function useCellSelection({
|
|
|
1105
1330
|
},
|
|
1106
1331
|
[enabled]
|
|
1107
1332
|
);
|
|
1333
|
+
const clearSelection = useCallback2(() => {
|
|
1334
|
+
const prev = dragStateRef.current;
|
|
1335
|
+
if (prev.start === null && prev.end === null && !prev.isSelecting && !prev.isFillDragging) {
|
|
1336
|
+
return;
|
|
1337
|
+
}
|
|
1338
|
+
dragStateRef.current = INITIAL_DRAG_STATE;
|
|
1339
|
+
setDragState(INITIAL_DRAG_STATE);
|
|
1340
|
+
}, []);
|
|
1108
1341
|
useEffect2(() => {
|
|
1109
1342
|
if (!enabled) {
|
|
1110
|
-
|
|
1343
|
+
clearSelection();
|
|
1111
1344
|
}
|
|
1112
|
-
}, [enabled]);
|
|
1345
|
+
}, [clearSelection, enabled]);
|
|
1113
1346
|
useEffect2(() => {
|
|
1114
1347
|
if (!enabled) return;
|
|
1115
1348
|
const handleKeyDown = (e) => {
|
|
@@ -1161,15 +1394,28 @@ function useCellSelection({
|
|
|
1161
1394
|
async (options) => {
|
|
1162
1395
|
if (!enabled || !activeSelectionBounds) return false;
|
|
1163
1396
|
const mode = options?.includeDescendants && enableSubtreeCopy ? "subtree" : "visible";
|
|
1164
|
-
return writeSelectionToClipboard(rows, activeSelectionBounds, mode
|
|
1397
|
+
return writeSelectionToClipboard(rows, activeSelectionBounds, mode, {
|
|
1398
|
+
registry: cellRendererRegistry,
|
|
1399
|
+
root: rootRef?.current
|
|
1400
|
+
});
|
|
1165
1401
|
},
|
|
1166
|
-
[
|
|
1402
|
+
[
|
|
1403
|
+
activeSelectionBounds,
|
|
1404
|
+
cellRendererRegistry,
|
|
1405
|
+
enableSubtreeCopy,
|
|
1406
|
+
enabled,
|
|
1407
|
+
rootRef,
|
|
1408
|
+
rows
|
|
1409
|
+
]
|
|
1167
1410
|
);
|
|
1168
1411
|
useEffect2(() => {
|
|
1169
1412
|
if (!enabled) return;
|
|
1170
1413
|
const handleKeyDown = (e) => {
|
|
1171
1414
|
if (!activeSelectionBounds) return;
|
|
1172
1415
|
if (!(e.ctrlKey || e.metaKey)) return;
|
|
1416
|
+
if (isEditablePasteTarget(e.target) || isEditablePasteTarget(document.activeElement)) {
|
|
1417
|
+
return;
|
|
1418
|
+
}
|
|
1173
1419
|
const isSubtreeShortcut = enableSubtreeCopy && e.shiftKey && e.key.toLowerCase() === "c";
|
|
1174
1420
|
const isVisibleCopyShortcut = !e.shiftKey && e.key.toLowerCase() === "c";
|
|
1175
1421
|
if (!isSubtreeShortcut && !isVisibleCopyShortcut) return;
|
|
@@ -1310,6 +1556,7 @@ function useCellSelection({
|
|
|
1310
1556
|
handleCellMouseDown,
|
|
1311
1557
|
handleCellMouseEnter,
|
|
1312
1558
|
handleFillHandleMouseDown,
|
|
1559
|
+
clearSelection,
|
|
1313
1560
|
copySelection
|
|
1314
1561
|
};
|
|
1315
1562
|
}
|
|
@@ -2418,6 +2665,10 @@ function useGlideTable(options) {
|
|
|
2418
2665
|
const [hoveredRowIndex, setHoveredRowIndex] = useState4(null);
|
|
2419
2666
|
const scrollRef = useRef5(null);
|
|
2420
2667
|
const rootRef = useRef5(null);
|
|
2668
|
+
const cellRendererRegistry = useMemo3(
|
|
2669
|
+
() => createCellRendererRegistry(cellRenderers),
|
|
2670
|
+
[cellRenderers]
|
|
2671
|
+
);
|
|
2421
2672
|
const shouldVirtualize = enableVirtualization && !enableRowSpan;
|
|
2422
2673
|
useEffect5(() => {
|
|
2423
2674
|
if (enableVirtualization && enableRowSpan) {
|
|
@@ -2590,6 +2841,7 @@ function useGlideTable(options) {
|
|
|
2590
2841
|
handleCellMouseDown,
|
|
2591
2842
|
handleCellMouseEnter,
|
|
2592
2843
|
handleFillHandleMouseDown,
|
|
2844
|
+
clearSelection: clearCellSelection,
|
|
2593
2845
|
copySelection
|
|
2594
2846
|
} = useCellSelection({
|
|
2595
2847
|
data: tableData,
|
|
@@ -2601,8 +2853,46 @@ function useGlideTable(options) {
|
|
|
2601
2853
|
onDataChange,
|
|
2602
2854
|
onBatchChange,
|
|
2603
2855
|
onRowsPaste,
|
|
2604
|
-
onCellNavigate: handleCellNavigate
|
|
2856
|
+
onCellNavigate: handleCellNavigate,
|
|
2857
|
+
cellRendererRegistry,
|
|
2858
|
+
rootRef
|
|
2605
2859
|
});
|
|
2860
|
+
const clearRowSelection = useCallback4(() => {
|
|
2861
|
+
if (rowSelectionMode === "none") return;
|
|
2862
|
+
const hasSelection = Object.values(rowSelection).some(Boolean);
|
|
2863
|
+
if (!hasSelection) return;
|
|
2864
|
+
if (onRowSelectionChange) {
|
|
2865
|
+
onRowSelectionChange(() => ({}));
|
|
2866
|
+
return;
|
|
2867
|
+
}
|
|
2868
|
+
setInternalRowSelection({});
|
|
2869
|
+
}, [onRowSelectionChange, rowSelection, rowSelectionMode]);
|
|
2870
|
+
useEffect5(() => {
|
|
2871
|
+
const clearAllSelections = () => {
|
|
2872
|
+
clearCellSelection();
|
|
2873
|
+
clearRowSelection();
|
|
2874
|
+
};
|
|
2875
|
+
const handleKeyDown = (event) => {
|
|
2876
|
+
if (event.key !== "Escape") return;
|
|
2877
|
+
if (event.defaultPrevented) return;
|
|
2878
|
+
if (isEditablePasteTarget(event.target) || isEditablePasteTarget(document.activeElement)) {
|
|
2879
|
+
return;
|
|
2880
|
+
}
|
|
2881
|
+
clearAllSelections();
|
|
2882
|
+
};
|
|
2883
|
+
const handleMouseDown = (event) => {
|
|
2884
|
+
const root = rootRef.current;
|
|
2885
|
+
if (!root) return;
|
|
2886
|
+
if (event.target instanceof Node && root.contains(event.target)) return;
|
|
2887
|
+
clearAllSelections();
|
|
2888
|
+
};
|
|
2889
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
2890
|
+
document.addEventListener("mousedown", handleMouseDown);
|
|
2891
|
+
return () => {
|
|
2892
|
+
window.removeEventListener("keydown", handleKeyDown);
|
|
2893
|
+
document.removeEventListener("mousedown", handleMouseDown);
|
|
2894
|
+
};
|
|
2895
|
+
}, [clearCellSelection, clearRowSelection]);
|
|
2606
2896
|
const {
|
|
2607
2897
|
editingCell,
|
|
2608
2898
|
draftValue,
|
|
@@ -2611,10 +2901,6 @@ function useGlideTable(options) {
|
|
|
2611
2901
|
commitEdit,
|
|
2612
2902
|
cancelEdit
|
|
2613
2903
|
} = useCellEdit({ data: tableData, rows, onDataChange, onCellChange });
|
|
2614
|
-
const cellRendererRegistry = useMemo3(
|
|
2615
|
-
() => createCellRendererRegistry(cellRenderers),
|
|
2616
|
-
[cellRenderers]
|
|
2617
|
-
);
|
|
2618
2904
|
const commitRenderedCellValue = useCallback4(
|
|
2619
2905
|
(rowId, columnId, value) => commitCellValue({
|
|
2620
2906
|
data: tableData,
|
|
@@ -2996,7 +3282,9 @@ var DataTableContext = createContext(null);
|
|
|
2996
3282
|
function useDataTableRowContext() {
|
|
2997
3283
|
const context = use(DataTableContext);
|
|
2998
3284
|
if (!context) {
|
|
2999
|
-
throw new Error(
|
|
3285
|
+
throw new Error(
|
|
3286
|
+
"useDataTableRowContext must be used within a DataTableContextProvider"
|
|
3287
|
+
);
|
|
3000
3288
|
}
|
|
3001
3289
|
return context;
|
|
3002
3290
|
}
|
|
@@ -3035,8 +3323,105 @@ function ResolvedTableCell({
|
|
|
3035
3323
|
}
|
|
3036
3324
|
|
|
3037
3325
|
// src/components/ui/table/features/column-resize/columnResize.ts
|
|
3326
|
+
function clamp(value, min, max) {
|
|
3327
|
+
return Math.min(Math.max(value, min), max);
|
|
3328
|
+
}
|
|
3329
|
+
function floorOf(column) {
|
|
3330
|
+
return column.minWidth ?? 0;
|
|
3331
|
+
}
|
|
3332
|
+
function ceilOf(column) {
|
|
3333
|
+
return column.maxWidth ?? Number.POSITIVE_INFINITY;
|
|
3334
|
+
}
|
|
3335
|
+
function preferOf(column) {
|
|
3336
|
+
const floor = floorOf(column);
|
|
3337
|
+
const ceil = ceilOf(column);
|
|
3338
|
+
const preferred = column.maxWidth ?? column.minWidth ?? 0;
|
|
3339
|
+
return clamp(preferred, floor, Number.isFinite(ceil) ? ceil : preferred);
|
|
3340
|
+
}
|
|
3341
|
+
function resolveColumnLayoutWidths(containerWidth, columns) {
|
|
3342
|
+
const widths = /* @__PURE__ */ new Map();
|
|
3343
|
+
const fixed = [];
|
|
3344
|
+
const bounded = [];
|
|
3345
|
+
let flexCount = 0;
|
|
3346
|
+
for (const column of columns) {
|
|
3347
|
+
if (column.width != null) {
|
|
3348
|
+
fixed.push(column);
|
|
3349
|
+
} else if (column.minWidth != null || column.maxWidth != null) {
|
|
3350
|
+
bounded.push(column);
|
|
3351
|
+
} else {
|
|
3352
|
+
flexCount += 1;
|
|
3353
|
+
}
|
|
3354
|
+
}
|
|
3355
|
+
let used = 0;
|
|
3356
|
+
for (const column of fixed) {
|
|
3357
|
+
let size = column.width;
|
|
3358
|
+
if (column.minWidth != null) size = Math.max(size, column.minWidth);
|
|
3359
|
+
if (column.maxWidth != null) size = Math.min(size, column.maxWidth);
|
|
3360
|
+
widths.set(column.id, size);
|
|
3361
|
+
used += size;
|
|
3362
|
+
}
|
|
3363
|
+
if (bounded.length === 0) {
|
|
3364
|
+
return widths;
|
|
3365
|
+
}
|
|
3366
|
+
const boundedSizes = /* @__PURE__ */ new Map();
|
|
3367
|
+
let preferredSum = 0;
|
|
3368
|
+
let floorSum = 0;
|
|
3369
|
+
for (const column of bounded) {
|
|
3370
|
+
const preferred = preferOf(column);
|
|
3371
|
+
boundedSizes.set(column.id, preferred);
|
|
3372
|
+
preferredSum += preferred;
|
|
3373
|
+
floorSum += floorOf(column);
|
|
3374
|
+
}
|
|
3375
|
+
if (containerWidth > 0) {
|
|
3376
|
+
const remaining = Math.max(0, containerWidth - used);
|
|
3377
|
+
if (remaining >= preferredSum) {
|
|
3378
|
+
} else if (remaining >= floorSum) {
|
|
3379
|
+
let deficit = preferredSum - remaining;
|
|
3380
|
+
const open = bounded.map((column) => ({
|
|
3381
|
+
id: column.id,
|
|
3382
|
+
current: boundedSizes.get(column.id),
|
|
3383
|
+
floor: floorOf(column)
|
|
3384
|
+
}));
|
|
3385
|
+
while (deficit >= 1) {
|
|
3386
|
+
const shrinkable = open.filter((entry) => entry.current > entry.floor);
|
|
3387
|
+
if (shrinkable.length === 0) break;
|
|
3388
|
+
const portion = Math.floor(deficit / shrinkable.length);
|
|
3389
|
+
const rem = deficit % shrinkable.length;
|
|
3390
|
+
let consumed = 0;
|
|
3391
|
+
for (let index = 0; index < shrinkable.length; index += 1) {
|
|
3392
|
+
const entry = shrinkable[index];
|
|
3393
|
+
const reduce = Math.min(
|
|
3394
|
+
entry.current - entry.floor,
|
|
3395
|
+
portion + (index < rem ? 1 : 0)
|
|
3396
|
+
);
|
|
3397
|
+
entry.current -= reduce;
|
|
3398
|
+
consumed += reduce;
|
|
3399
|
+
}
|
|
3400
|
+
if (consumed === 0) break;
|
|
3401
|
+
deficit -= consumed;
|
|
3402
|
+
}
|
|
3403
|
+
for (const entry of open) {
|
|
3404
|
+
boundedSizes.set(entry.id, entry.current);
|
|
3405
|
+
}
|
|
3406
|
+
} else {
|
|
3407
|
+
for (const column of bounded) {
|
|
3408
|
+
boundedSizes.set(column.id, floorOf(column));
|
|
3409
|
+
}
|
|
3410
|
+
}
|
|
3411
|
+
}
|
|
3412
|
+
for (const [id, size] of boundedSizes) {
|
|
3413
|
+
widths.set(id, Math.round(size));
|
|
3414
|
+
}
|
|
3415
|
+
return widths;
|
|
3416
|
+
}
|
|
3038
3417
|
function getColumnSizeStyle(size, options) {
|
|
3039
|
-
const {
|
|
3418
|
+
const {
|
|
3419
|
+
force = false,
|
|
3420
|
+
lockMax = false,
|
|
3421
|
+
minWidth,
|
|
3422
|
+
maxWidth,
|
|
3423
|
+
layoutWidth
|
|
3424
|
+
} = options ?? {};
|
|
3040
3425
|
if (lockMax) {
|
|
3041
3426
|
return {
|
|
3042
3427
|
width: size,
|
|
@@ -3044,14 +3429,27 @@ function getColumnSizeStyle(size, options) {
|
|
|
3044
3429
|
maxWidth: size
|
|
3045
3430
|
};
|
|
3046
3431
|
}
|
|
3432
|
+
if (layoutWidth != null) {
|
|
3433
|
+
return {
|
|
3434
|
+
width: layoutWidth,
|
|
3435
|
+
minWidth: layoutWidth,
|
|
3436
|
+
maxWidth: layoutWidth
|
|
3437
|
+
};
|
|
3438
|
+
}
|
|
3439
|
+
const resolvedSize = size;
|
|
3047
3440
|
const hasExplicitSize = force || size !== DATA_TABLE_COLUMN_SIZE;
|
|
3048
3441
|
if (!hasExplicitSize && minWidth == null && maxWidth == null) {
|
|
3049
3442
|
return void 0;
|
|
3050
3443
|
}
|
|
3051
3444
|
const style = {};
|
|
3052
3445
|
if (hasExplicitSize) {
|
|
3053
|
-
|
|
3054
|
-
|
|
3446
|
+
const used = minWidth != null || maxWidth != null ? clamp(
|
|
3447
|
+
resolvedSize,
|
|
3448
|
+
minWidth ?? Number.NEGATIVE_INFINITY,
|
|
3449
|
+
maxWidth ?? Number.POSITIVE_INFINITY
|
|
3450
|
+
) : resolvedSize;
|
|
3451
|
+
style.width = used;
|
|
3452
|
+
style.minWidth = minWidth ?? used;
|
|
3055
3453
|
} else if (minWidth != null) {
|
|
3056
3454
|
style.minWidth = minWidth;
|
|
3057
3455
|
}
|
|
@@ -3334,6 +3732,7 @@ export {
|
|
|
3334
3732
|
previousSearchIndex,
|
|
3335
3733
|
resolveCellRenderer,
|
|
3336
3734
|
resolveColumnFreezeSide,
|
|
3735
|
+
resolveColumnLayoutWidths,
|
|
3337
3736
|
resolveDataTableLabels,
|
|
3338
3737
|
resolveDropEdge,
|
|
3339
3738
|
resolveHeaderFreezeOffset,
|