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/compound.js
CHANGED
|
@@ -443,15 +443,16 @@ var useConvertTreeData = ({
|
|
|
443
443
|
children: [],
|
|
444
444
|
processed: false
|
|
445
445
|
}));
|
|
446
|
-
const
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
446
|
+
const findNearestPrecedingParent = (index, parentKey) => {
|
|
447
|
+
for (let i = index - 1; i >= 0; i -= 1) {
|
|
448
|
+
const candidate = dataWithLevels[i];
|
|
449
|
+
if (!candidate) continue;
|
|
450
|
+
if (getFieldValue(candidate, toggleField) === parentKey) {
|
|
451
|
+
return candidate;
|
|
452
|
+
}
|
|
452
453
|
}
|
|
453
|
-
|
|
454
|
-
}
|
|
454
|
+
return void 0;
|
|
455
|
+
};
|
|
455
456
|
const rootItems = [];
|
|
456
457
|
dataWithLevels.forEach((item) => {
|
|
457
458
|
if (!getFieldValue(item, childField)) {
|
|
@@ -459,29 +460,18 @@ var useConvertTreeData = ({
|
|
|
459
460
|
item.processed = true;
|
|
460
461
|
}
|
|
461
462
|
});
|
|
462
|
-
dataWithLevels.forEach((item) => {
|
|
463
|
+
dataWithLevels.forEach((item, index) => {
|
|
463
464
|
const parentKey = getFieldValue(item, childField);
|
|
464
465
|
if (!parentKey || item.processed) return;
|
|
465
|
-
const
|
|
466
|
-
|
|
467
|
-
);
|
|
468
|
-
if (parentItems.length > 0) {
|
|
469
|
-
const parent = parentItems[0];
|
|
466
|
+
const parent = findNearestPrecedingParent(index, parentKey);
|
|
467
|
+
if (parent) {
|
|
470
468
|
item.level = parent.level + 1;
|
|
471
469
|
parent.children.push(item);
|
|
472
470
|
item.processed = true;
|
|
473
|
-
|
|
474
|
-
const otherParents = itemMap.get(String(parentKey)) || [];
|
|
475
|
-
if (otherParents.length > 0) {
|
|
476
|
-
const parent = otherParents[0];
|
|
477
|
-
item.level = parent.level + 1;
|
|
478
|
-
parent.children.push(item);
|
|
479
|
-
item.processed = true;
|
|
480
|
-
} else {
|
|
481
|
-
rootItems.push(item);
|
|
482
|
-
item.processed = true;
|
|
483
|
-
}
|
|
471
|
+
return;
|
|
484
472
|
}
|
|
473
|
+
rootItems.push(item);
|
|
474
|
+
item.processed = true;
|
|
485
475
|
});
|
|
486
476
|
return rootItems;
|
|
487
477
|
}, [enabled, data, toggleField, childField, flattenField]);
|
|
@@ -506,16 +496,23 @@ var useConvertTreeData = ({
|
|
|
506
496
|
return result;
|
|
507
497
|
};
|
|
508
498
|
const flattenedData = flatten(processedData, [], 0);
|
|
509
|
-
flattenedData.forEach((item) => {
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
(parent) => getFieldValue(parent, toggleField) === getFieldValue(item, childField)
|
|
513
|
-
);
|
|
514
|
-
const parentAmount = parentItem ? Number(getFieldValue(parentItem, qtyField) ?? 1) : 1;
|
|
515
|
-
item.parentCount = parentAmount || 1;
|
|
516
|
-
} else {
|
|
499
|
+
flattenedData.forEach((item, index) => {
|
|
500
|
+
const parentKey = getFieldValue(item, childField);
|
|
501
|
+
if (!parentKey) {
|
|
517
502
|
item.parentCount = 1;
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
let parentItem;
|
|
506
|
+
for (let i = index - 1; i >= 0; i -= 1) {
|
|
507
|
+
const candidate = flattenedData[i];
|
|
508
|
+
if (!candidate) continue;
|
|
509
|
+
if (getFieldValue(candidate, toggleField) === parentKey) {
|
|
510
|
+
parentItem = candidate;
|
|
511
|
+
break;
|
|
512
|
+
}
|
|
518
513
|
}
|
|
514
|
+
const parentAmount = parentItem ? Number(getFieldValue(parentItem, qtyField) ?? 1) : 1;
|
|
515
|
+
item.parentCount = parentAmount || 1;
|
|
519
516
|
});
|
|
520
517
|
return flattenedData;
|
|
521
518
|
}, [
|
|
@@ -787,11 +784,10 @@ function DataTableRow({
|
|
|
787
784
|
const { classNames, rowSpan, selection, cellSelection, cellEdit, expand } = useDataTableRowContext();
|
|
788
785
|
const {
|
|
789
786
|
enableRowSpan,
|
|
790
|
-
|
|
787
|
+
primaryRowSpanColumnId,
|
|
791
788
|
columnRowSpanMap,
|
|
792
789
|
hoveredRowIndex,
|
|
793
|
-
|
|
794
|
-
selectedGroupKeys,
|
|
790
|
+
selectedRowIndices,
|
|
795
791
|
onRowHover
|
|
796
792
|
} = rowSpan;
|
|
797
793
|
const { rowSelectionMode, selectOnRowClick, onRowClick, getRowClassName } = selection;
|
|
@@ -824,9 +820,11 @@ function DataTableRow({
|
|
|
824
820
|
const rowData = row.original;
|
|
825
821
|
const isRowHovered = hoveredRowIndex === rowIndex;
|
|
826
822
|
const isRowSelected = row.getIsSelected();
|
|
827
|
-
const
|
|
828
|
-
|
|
829
|
-
|
|
823
|
+
const { startRow: primaryGroupStart, rowSpan: primaryGroupSpan } = resolveRowSpanAt(
|
|
824
|
+
primaryRowSpanColumnId ? columnRowSpanMap.get(primaryRowSpanColumnId) : void 0,
|
|
825
|
+
rowIndex
|
|
826
|
+
);
|
|
827
|
+
const isGroupHovered = enableRowSpan && hoveredRowIndex !== null && hoveredRowIndex >= primaryGroupStart && hoveredRowIndex <= primaryGroupStart + primaryGroupSpan - 1;
|
|
830
828
|
const visibleCells = row.getVisibleCells();
|
|
831
829
|
const columnIdsByIndex = visibleCells.map((cell) => cell.column.id);
|
|
832
830
|
const isVisuallySelectedAt = activeSelectionBounds ? (targetRow, targetCol) => {
|
|
@@ -903,7 +901,16 @@ function DataTableRow({
|
|
|
903
901
|
const cellRowSpan = rowSpanInfo?.rowSpan ?? 1;
|
|
904
902
|
const isMergedCellHovered = hoveredRowIndex !== null && hoveredRowIndex >= rowIndex && hoveredRowIndex <= rowIndex + cellRowSpan - 1;
|
|
905
903
|
const showCellHover = isRowSpanColumn ? isMergedCellHovered : isRowHovered;
|
|
906
|
-
|
|
904
|
+
let isMergedCellSelected = false;
|
|
905
|
+
if (isRowSpanColumn) {
|
|
906
|
+
for (let r = rowIndex; r < rowIndex + cellRowSpan; r += 1) {
|
|
907
|
+
if (selectedRowIndices.has(r)) {
|
|
908
|
+
isMergedCellSelected = true;
|
|
909
|
+
break;
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
const showCellSelected = isRowSpanColumn ? isMergedCellSelected : isRowSelected;
|
|
907
914
|
const isMerged = cellRowSpan > 1;
|
|
908
915
|
const showMergedRightEdge = isMerged && (cellIndex === visibleCells.length - 1 || resolveRowSpanAt(
|
|
909
916
|
columnRowSpanMap.get(columnIdsByIndex[cellIndex + 1]),
|
|
@@ -1153,7 +1160,7 @@ import {
|
|
|
1153
1160
|
useCallback as useCallback3,
|
|
1154
1161
|
useEffect as useEffect5,
|
|
1155
1162
|
useMemo as useMemo2,
|
|
1156
|
-
useRef as
|
|
1163
|
+
useRef as useRef5,
|
|
1157
1164
|
useState as useState3
|
|
1158
1165
|
} from "react";
|
|
1159
1166
|
|
|
@@ -1237,7 +1244,109 @@ function useCellEdit({
|
|
|
1237
1244
|
}
|
|
1238
1245
|
|
|
1239
1246
|
// src/components/ui/table/features/cell-selection/useCellSelection.ts
|
|
1240
|
-
import { useCallback as useCallback2, useEffect as useEffect4, useState as useState2 } from "react";
|
|
1247
|
+
import { useCallback as useCallback2, useEffect as useEffect4, useRef as useRef4, useState as useState2 } from "react";
|
|
1248
|
+
|
|
1249
|
+
// src/components/ui/table/features/cell-selection/copyData.ts
|
|
1250
|
+
function formatCellValue(value) {
|
|
1251
|
+
if (value === null || value === void 0) return "";
|
|
1252
|
+
return String(value);
|
|
1253
|
+
}
|
|
1254
|
+
function getNestedValue(row, path) {
|
|
1255
|
+
if (!path.includes(".")) return row[path];
|
|
1256
|
+
return path.split(".").reduce((current, key) => {
|
|
1257
|
+
if (current === null || current === void 0 || typeof current !== "object") {
|
|
1258
|
+
return void 0;
|
|
1259
|
+
}
|
|
1260
|
+
return current[key];
|
|
1261
|
+
}, row);
|
|
1262
|
+
}
|
|
1263
|
+
function readRowColumnValue(rowData, columnDef) {
|
|
1264
|
+
if ("accessorFn" in columnDef && typeof columnDef.accessorFn === "function") {
|
|
1265
|
+
return columnDef.accessorFn(rowData, 0);
|
|
1266
|
+
}
|
|
1267
|
+
if ("accessorKey" in columnDef && columnDef.accessorKey != null && columnDef.accessorKey !== "") {
|
|
1268
|
+
return getNestedValue(rowData, String(columnDef.accessorKey));
|
|
1269
|
+
}
|
|
1270
|
+
return void 0;
|
|
1271
|
+
}
|
|
1272
|
+
function hasSubtree(row) {
|
|
1273
|
+
const children = row.children;
|
|
1274
|
+
return Array.isArray(children) && children.length > 0;
|
|
1275
|
+
}
|
|
1276
|
+
function getOriginalRowId(original) {
|
|
1277
|
+
return String(original.id ?? original.uniqueId ?? "");
|
|
1278
|
+
}
|
|
1279
|
+
function getRowDepth(original) {
|
|
1280
|
+
return typeof original.level === "number" ? original.level : 0;
|
|
1281
|
+
}
|
|
1282
|
+
function collectCopyRowEntries(visibleRows, bounds, mode = "visible") {
|
|
1283
|
+
const { startRow, endRow } = bounds;
|
|
1284
|
+
const result = [];
|
|
1285
|
+
const includedOriginalIds = /* @__PURE__ */ new Set();
|
|
1286
|
+
const appendSubtree = (node, depth) => {
|
|
1287
|
+
const children = node.children;
|
|
1288
|
+
if (!Array.isArray(children) || children.length === 0) return;
|
|
1289
|
+
for (const child of children) {
|
|
1290
|
+
const childId = getOriginalRowId(child);
|
|
1291
|
+
if (!(childId && includedOriginalIds.has(childId))) {
|
|
1292
|
+
result.push({ row: child, depth });
|
|
1293
|
+
if (childId) includedOriginalIds.add(childId);
|
|
1294
|
+
}
|
|
1295
|
+
appendSubtree(child, depth + 1);
|
|
1296
|
+
}
|
|
1297
|
+
};
|
|
1298
|
+
for (let rowIndex = startRow; rowIndex <= endRow; rowIndex += 1) {
|
|
1299
|
+
const row = visibleRows[rowIndex];
|
|
1300
|
+
if (!row) continue;
|
|
1301
|
+
const originalId = getOriginalRowId(row.original);
|
|
1302
|
+
if (originalId && includedOriginalIds.has(originalId)) continue;
|
|
1303
|
+
const depth = getRowDepth(row.original);
|
|
1304
|
+
result.push({ row: row.original, depth });
|
|
1305
|
+
if (originalId) includedOriginalIds.add(originalId);
|
|
1306
|
+
if (mode !== "subtree" || !hasSubtree(row.original)) continue;
|
|
1307
|
+
appendSubtree(row.original, depth + 1);
|
|
1308
|
+
}
|
|
1309
|
+
return result;
|
|
1310
|
+
}
|
|
1311
|
+
function serializeCopyRowsToTSV(copyRows, visibleRows, bounds, depths) {
|
|
1312
|
+
if (copyRows.length === 0) return "";
|
|
1313
|
+
const { startCol, endCol } = bounds;
|
|
1314
|
+
const columnCells = visibleRows[0]?.getVisibleCells().slice(startCol, endCol + 1) ?? [];
|
|
1315
|
+
if (columnCells.length === 0) return "";
|
|
1316
|
+
const resolvedDepths = depths && depths.length === copyRows.length ? depths : copyRows.map((row) => getRowDepth(row));
|
|
1317
|
+
const minDepth = Math.min(...resolvedDepths);
|
|
1318
|
+
return copyRows.map((rowData, index) => {
|
|
1319
|
+
const relativeDepth = Math.max(0, (resolvedDepths[index] ?? 0) - minDepth);
|
|
1320
|
+
const line = columnCells.map(
|
|
1321
|
+
(cell) => formatCellValue(
|
|
1322
|
+
readRowColumnValue(
|
|
1323
|
+
rowData,
|
|
1324
|
+
cell.column.columnDef
|
|
1325
|
+
)
|
|
1326
|
+
)
|
|
1327
|
+
).join(" ");
|
|
1328
|
+
return `${" ".repeat(relativeDepth)}${line}`;
|
|
1329
|
+
}).join("\n");
|
|
1330
|
+
}
|
|
1331
|
+
function serializeSelectionToTSV(visibleRows, bounds, mode = "visible") {
|
|
1332
|
+
const entries = collectCopyRowEntries(visibleRows, bounds, mode);
|
|
1333
|
+
return serializeCopyRowsToTSV(
|
|
1334
|
+
entries.map((entry) => entry.row),
|
|
1335
|
+
visibleRows,
|
|
1336
|
+
bounds,
|
|
1337
|
+
entries.map((entry) => entry.depth)
|
|
1338
|
+
);
|
|
1339
|
+
}
|
|
1340
|
+
async function writeSelectionToClipboard(visibleRows, bounds, mode = "visible") {
|
|
1341
|
+
const text = serializeSelectionToTSV(visibleRows, bounds, mode);
|
|
1342
|
+
if (!text) return false;
|
|
1343
|
+
try {
|
|
1344
|
+
await navigator.clipboard.writeText(text);
|
|
1345
|
+
} catch {
|
|
1346
|
+
return false;
|
|
1347
|
+
}
|
|
1348
|
+
return true;
|
|
1349
|
+
}
|
|
1241
1350
|
|
|
1242
1351
|
// src/components/ui/table/features/cell-selection/fillData.ts
|
|
1243
1352
|
function getColumnAccessorKey2(columnDef) {
|
|
@@ -1295,15 +1404,100 @@ function hasFillExtension(sourceBounds, fillBounds) {
|
|
|
1295
1404
|
return fillBounds.startRow < sourceBounds.startRow || fillBounds.endRow > sourceBounds.endRow || fillBounds.startCol < sourceBounds.startCol || fillBounds.endCol > sourceBounds.endCol;
|
|
1296
1405
|
}
|
|
1297
1406
|
|
|
1407
|
+
// src/components/ui/table/features/cell-selection/pasteData.ts
|
|
1408
|
+
function countLeadingEmptyCells(cells) {
|
|
1409
|
+
let depth = 0;
|
|
1410
|
+
while (depth < cells.length && cells[depth] === "") {
|
|
1411
|
+
depth += 1;
|
|
1412
|
+
}
|
|
1413
|
+
return depth;
|
|
1414
|
+
}
|
|
1415
|
+
function looksLikeSubtreeIndentation(leadingEmptyCounts) {
|
|
1416
|
+
if (leadingEmptyCounts.length === 0) return false;
|
|
1417
|
+
const firstDepth = leadingEmptyCounts[0] ?? 0;
|
|
1418
|
+
if (firstDepth !== 0) return false;
|
|
1419
|
+
return leadingEmptyCounts.some((depth) => depth > 0);
|
|
1420
|
+
}
|
|
1421
|
+
function parseClipboardTSVWithDepths(text) {
|
|
1422
|
+
if (!text) return { values: [], depths: [] };
|
|
1423
|
+
const normalized = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
1424
|
+
const withoutTrailing = normalized.replace(/\n+$/, "");
|
|
1425
|
+
if (!withoutTrailing) return { values: [], depths: [] };
|
|
1426
|
+
const rows = withoutTrailing.split("\n").map((line) => line.split(" "));
|
|
1427
|
+
const leadingEmptyCounts = rows.map(countLeadingEmptyCells);
|
|
1428
|
+
const treatAsDepth = looksLikeSubtreeIndentation(leadingEmptyCounts);
|
|
1429
|
+
const values = [];
|
|
1430
|
+
const depths = [];
|
|
1431
|
+
for (let index = 0; index < rows.length; index += 1) {
|
|
1432
|
+
const cells = rows[index] ?? [];
|
|
1433
|
+
const depth = leadingEmptyCounts[index] ?? 0;
|
|
1434
|
+
if (treatAsDepth) {
|
|
1435
|
+
values.push(cells.slice(depth));
|
|
1436
|
+
depths.push(depth);
|
|
1437
|
+
} else {
|
|
1438
|
+
values.push(cells);
|
|
1439
|
+
depths.push(0);
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
return { values, depths };
|
|
1443
|
+
}
|
|
1444
|
+
function resolvePasteColumnIds(rows, startCol, width) {
|
|
1445
|
+
if (width <= 0) return [];
|
|
1446
|
+
const cells = rows[0]?.getVisibleCells() ?? [];
|
|
1447
|
+
const columnIds = [];
|
|
1448
|
+
for (let offset = 0; offset < width; offset += 1) {
|
|
1449
|
+
const cell = cells[startCol + offset];
|
|
1450
|
+
if (!cell) break;
|
|
1451
|
+
columnIds.push(cell.column.id);
|
|
1452
|
+
}
|
|
1453
|
+
return columnIds;
|
|
1454
|
+
}
|
|
1455
|
+
function buildRowsPastePayload(rows, startRow, startCol, text, mode, endRow = startRow) {
|
|
1456
|
+
const { values, depths } = parseClipboardTSVWithDepths(text);
|
|
1457
|
+
if (values.length === 0) return null;
|
|
1458
|
+
const width = Math.max(...values.map((row) => row.length), 0);
|
|
1459
|
+
if (width === 0) return null;
|
|
1460
|
+
const columnIds = resolvePasteColumnIds(rows, startCol, width);
|
|
1461
|
+
if (columnIds.length === 0) return null;
|
|
1462
|
+
const rowIds = [];
|
|
1463
|
+
for (let offset = 0; offset < values.length; offset += 1) {
|
|
1464
|
+
const row = rows[startRow + offset];
|
|
1465
|
+
if (!row) break;
|
|
1466
|
+
rowIds.push(row.id);
|
|
1467
|
+
}
|
|
1468
|
+
const anchorRow = rows[endRow] ?? rows[startRow];
|
|
1469
|
+
return {
|
|
1470
|
+
mode,
|
|
1471
|
+
startRow,
|
|
1472
|
+
startCol,
|
|
1473
|
+
endRow,
|
|
1474
|
+
rowIds,
|
|
1475
|
+
anchorRowId: anchorRow?.id ?? "",
|
|
1476
|
+
columnIds,
|
|
1477
|
+
values,
|
|
1478
|
+
depths
|
|
1479
|
+
};
|
|
1480
|
+
}
|
|
1481
|
+
function isEditablePasteTarget(target) {
|
|
1482
|
+
if (!(target instanceof HTMLElement)) return false;
|
|
1483
|
+
const tag = target.tagName;
|
|
1484
|
+
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
|
|
1485
|
+
return Boolean(target.isContentEditable);
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1298
1488
|
// src/components/ui/table/features/cell-selection/useCellSelection.ts
|
|
1299
1489
|
function useCellSelection({
|
|
1300
1490
|
data,
|
|
1301
1491
|
rows,
|
|
1302
1492
|
enabled = true,
|
|
1493
|
+
enableSubtreeCopy = false,
|
|
1494
|
+
enableInsertPaste = true,
|
|
1303
1495
|
onDataChange,
|
|
1304
|
-
onBatchChange
|
|
1496
|
+
onBatchChange,
|
|
1497
|
+
onRowsPaste
|
|
1305
1498
|
}) {
|
|
1306
1499
|
const [dragState, setDragState] = useState2(INITIAL_DRAG_STATE);
|
|
1500
|
+
const pendingPasteModeRef = useRef4(null);
|
|
1307
1501
|
const cellSelectionBounds = getCellSelectionBounds(dragState.start, dragState.end);
|
|
1308
1502
|
const activeSelectionBounds = enabled ? getActiveSelectionBounds(dragState, cellSelectionBounds) : null;
|
|
1309
1503
|
const handleCellMouseDown = useCallback2(
|
|
@@ -1357,21 +1551,113 @@ function useCellSelection({
|
|
|
1357
1551
|
setDragState(INITIAL_DRAG_STATE);
|
|
1358
1552
|
}
|
|
1359
1553
|
}, [enabled]);
|
|
1554
|
+
const copySelection = useCallback2(
|
|
1555
|
+
async (options) => {
|
|
1556
|
+
if (!enabled || !activeSelectionBounds) return false;
|
|
1557
|
+
const mode = options?.includeDescendants && enableSubtreeCopy ? "subtree" : "visible";
|
|
1558
|
+
return writeSelectionToClipboard(rows, activeSelectionBounds, mode);
|
|
1559
|
+
},
|
|
1560
|
+
[activeSelectionBounds, enableSubtreeCopy, enabled, rows]
|
|
1561
|
+
);
|
|
1360
1562
|
useEffect4(() => {
|
|
1361
1563
|
if (!enabled) return;
|
|
1362
1564
|
const handleKeyDown = (e) => {
|
|
1363
|
-
if (
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
}
|
|
1565
|
+
if (!activeSelectionBounds) return;
|
|
1566
|
+
if (!(e.ctrlKey || e.metaKey)) return;
|
|
1567
|
+
const isSubtreeShortcut = enableSubtreeCopy && e.shiftKey && e.key.toLowerCase() === "c";
|
|
1568
|
+
const isVisibleCopyShortcut = !e.shiftKey && e.key.toLowerCase() === "c";
|
|
1569
|
+
if (!isSubtreeShortcut && !isVisibleCopyShortcut) return;
|
|
1570
|
+
e.preventDefault();
|
|
1571
|
+
void copySelection({ includeDescendants: isSubtreeShortcut });
|
|
1371
1572
|
};
|
|
1372
1573
|
window.addEventListener("keydown", handleKeyDown);
|
|
1373
1574
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
1374
|
-
}, [activeSelectionBounds,
|
|
1575
|
+
}, [activeSelectionBounds, copySelection, enableSubtreeCopy, enabled]);
|
|
1576
|
+
const emitRowsPaste = useCallback2(
|
|
1577
|
+
(text, mode) => {
|
|
1578
|
+
if (!onRowsPaste || !activeSelectionBounds) return false;
|
|
1579
|
+
const payload = buildRowsPastePayload(
|
|
1580
|
+
rows,
|
|
1581
|
+
activeSelectionBounds.startRow,
|
|
1582
|
+
activeSelectionBounds.startCol,
|
|
1583
|
+
text,
|
|
1584
|
+
mode,
|
|
1585
|
+
activeSelectionBounds.endRow
|
|
1586
|
+
);
|
|
1587
|
+
if (!payload) return false;
|
|
1588
|
+
onRowsPaste(payload);
|
|
1589
|
+
return true;
|
|
1590
|
+
},
|
|
1591
|
+
[activeSelectionBounds, onRowsPaste, rows]
|
|
1592
|
+
);
|
|
1593
|
+
useEffect4(() => {
|
|
1594
|
+
if (!enabled || !onRowsPaste) return;
|
|
1595
|
+
const pasteHandledRef = { current: false };
|
|
1596
|
+
const ignoreNextPasteRef = { current: false };
|
|
1597
|
+
const handleKeyDown = (e) => {
|
|
1598
|
+
if (!activeSelectionBounds) return;
|
|
1599
|
+
if (!(e.ctrlKey || e.metaKey)) return;
|
|
1600
|
+
if (e.key.toLowerCase() !== "v") return;
|
|
1601
|
+
if (isEditablePasteTarget(e.target) || isEditablePasteTarget(document.activeElement)) {
|
|
1602
|
+
return;
|
|
1603
|
+
}
|
|
1604
|
+
if (e.shiftKey && !enableInsertPaste) {
|
|
1605
|
+
ignoreNextPasteRef.current = true;
|
|
1606
|
+
pendingPasteModeRef.current = null;
|
|
1607
|
+
return;
|
|
1608
|
+
}
|
|
1609
|
+
const mode = e.shiftKey ? "insert" : "overwrite";
|
|
1610
|
+
pasteHandledRef.current = false;
|
|
1611
|
+
ignoreNextPasteRef.current = false;
|
|
1612
|
+
pendingPasteModeRef.current = mode;
|
|
1613
|
+
void (async () => {
|
|
1614
|
+
try {
|
|
1615
|
+
const text = await navigator.clipboard.readText();
|
|
1616
|
+
if (pasteHandledRef.current) return;
|
|
1617
|
+
if (pendingPasteModeRef.current !== mode) return;
|
|
1618
|
+
if (!text) return;
|
|
1619
|
+
pasteHandledRef.current = true;
|
|
1620
|
+
emitRowsPaste(text, mode);
|
|
1621
|
+
pendingPasteModeRef.current = null;
|
|
1622
|
+
} catch {
|
|
1623
|
+
}
|
|
1624
|
+
})();
|
|
1625
|
+
};
|
|
1626
|
+
const handlePaste = (e) => {
|
|
1627
|
+
if (!activeSelectionBounds) return;
|
|
1628
|
+
if (isEditablePasteTarget(e.target) || isEditablePasteTarget(document.activeElement)) {
|
|
1629
|
+
return;
|
|
1630
|
+
}
|
|
1631
|
+
if (ignoreNextPasteRef.current) {
|
|
1632
|
+
ignoreNextPasteRef.current = false;
|
|
1633
|
+
pendingPasteModeRef.current = null;
|
|
1634
|
+
return;
|
|
1635
|
+
}
|
|
1636
|
+
const mode = pendingPasteModeRef.current ?? "overwrite";
|
|
1637
|
+
if (pasteHandledRef.current) {
|
|
1638
|
+
e.preventDefault();
|
|
1639
|
+
return;
|
|
1640
|
+
}
|
|
1641
|
+
const text = e.clipboardData?.getData("text/plain");
|
|
1642
|
+
if (text == null || text === "") return;
|
|
1643
|
+
pasteHandledRef.current = true;
|
|
1644
|
+
e.preventDefault();
|
|
1645
|
+
emitRowsPaste(text, mode);
|
|
1646
|
+
pendingPasteModeRef.current = null;
|
|
1647
|
+
};
|
|
1648
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
1649
|
+
window.addEventListener("paste", handlePaste);
|
|
1650
|
+
return () => {
|
|
1651
|
+
window.removeEventListener("keydown", handleKeyDown);
|
|
1652
|
+
window.removeEventListener("paste", handlePaste);
|
|
1653
|
+
};
|
|
1654
|
+
}, [
|
|
1655
|
+
activeSelectionBounds,
|
|
1656
|
+
emitRowsPaste,
|
|
1657
|
+
enableInsertPaste,
|
|
1658
|
+
enabled,
|
|
1659
|
+
onRowsPaste
|
|
1660
|
+
]);
|
|
1375
1661
|
useEffect4(() => {
|
|
1376
1662
|
if (!enabled) return;
|
|
1377
1663
|
const handleMouseUp = () => {
|
|
@@ -1417,7 +1703,8 @@ function useCellSelection({
|
|
|
1417
1703
|
activeSelectionBounds,
|
|
1418
1704
|
handleCellMouseDown,
|
|
1419
1705
|
handleCellMouseEnter,
|
|
1420
|
-
handleFillHandleMouseDown
|
|
1706
|
+
handleFillHandleMouseDown,
|
|
1707
|
+
copySelection
|
|
1421
1708
|
};
|
|
1422
1709
|
}
|
|
1423
1710
|
|
|
@@ -1481,6 +1768,10 @@ function useGlideTable(options) {
|
|
|
1481
1768
|
expandedRows: controlledExpandedRows,
|
|
1482
1769
|
onExpandedRowsChange,
|
|
1483
1770
|
preventExpand = false,
|
|
1771
|
+
enableSubtreeCopy,
|
|
1772
|
+
onCopyActionsReady,
|
|
1773
|
+
onRowsPaste,
|
|
1774
|
+
enableInsertPaste,
|
|
1484
1775
|
enableVirtualization = true,
|
|
1485
1776
|
estimateRowHeight = DATA_TABLE_ROW_HEIGHT,
|
|
1486
1777
|
virtualOverscan = DATA_TABLE_VIRTUAL_OVERSCAN
|
|
@@ -1495,13 +1786,13 @@ function useGlideTable(options) {
|
|
|
1495
1786
|
};
|
|
1496
1787
|
}, [labelsProp, emptyText, loadingText, selectionLabel]);
|
|
1497
1788
|
const enableExpand = Boolean(toggleField);
|
|
1789
|
+
const resolvedEnableSubtreeCopy = enableSubtreeCopy ?? enableExpand;
|
|
1498
1790
|
const [internalRowSelection, setInternalRowSelection] = useState3({});
|
|
1499
1791
|
const [internalExpandedRows, setInternalExpandedRows] = useState3(
|
|
1500
1792
|
() => /* @__PURE__ */ new Set()
|
|
1501
1793
|
);
|
|
1502
1794
|
const [hoveredRowIndex, setHoveredRowIndex] = useState3(null);
|
|
1503
|
-
const
|
|
1504
|
-
const scrollRef = useRef4(null);
|
|
1795
|
+
const scrollRef = useRef5(null);
|
|
1505
1796
|
const shouldVirtualize = enableVirtualization && !enableRowSpan;
|
|
1506
1797
|
useEffect5(() => {
|
|
1507
1798
|
if (enableVirtualization && enableRowSpan) {
|
|
@@ -1564,6 +1855,7 @@ function useGlideTable(options) {
|
|
|
1564
1855
|
return collectRowSpanColumns(columns);
|
|
1565
1856
|
}, [enableRowSpan, columns]);
|
|
1566
1857
|
const primaryRowSpanKey = rowSpanColumnKeys[0]?.rowSpanKey;
|
|
1858
|
+
const primaryRowSpanColumnId = rowSpanColumnKeys[0]?.columnId;
|
|
1567
1859
|
const columnRowSpanMap = useMemo2(
|
|
1568
1860
|
() => buildColumnRowSpanMap(tableData, rowSpanColumnKeys),
|
|
1569
1861
|
[tableData, rowSpanColumnKeys]
|
|
@@ -1582,27 +1874,29 @@ function useGlideTable(options) {
|
|
|
1582
1874
|
const totalSize = rowVirtualizer.getTotalSize();
|
|
1583
1875
|
const paddingTop = virtualRows.length > 0 ? virtualRows[0]?.start ?? 0 : 0;
|
|
1584
1876
|
const paddingBottom = virtualRows.length > 0 ? totalSize - (virtualRows[virtualRows.length - 1]?.end ?? 0) : 0;
|
|
1585
|
-
const
|
|
1586
|
-
|
|
1587
|
-
const keys = /* @__PURE__ */ new Set();
|
|
1877
|
+
const selectedRowIndices = useMemo2(() => {
|
|
1878
|
+
const indices = /* @__PURE__ */ new Set();
|
|
1588
1879
|
for (const selectedRow of selectedRows) {
|
|
1589
|
-
|
|
1590
|
-
if (value !== null && value !== void 0) keys.add(String(value));
|
|
1880
|
+
indices.add(selectedRow.index);
|
|
1591
1881
|
}
|
|
1592
|
-
return
|
|
1593
|
-
}, [
|
|
1882
|
+
return indices;
|
|
1883
|
+
}, [selectedRows]);
|
|
1594
1884
|
const {
|
|
1595
1885
|
dragState,
|
|
1596
1886
|
activeSelectionBounds,
|
|
1597
1887
|
handleCellMouseDown,
|
|
1598
1888
|
handleCellMouseEnter,
|
|
1599
|
-
handleFillHandleMouseDown
|
|
1889
|
+
handleFillHandleMouseDown,
|
|
1890
|
+
copySelection
|
|
1600
1891
|
} = useCellSelection({
|
|
1601
1892
|
data: tableData,
|
|
1602
1893
|
rows,
|
|
1603
1894
|
enabled: enableCellSelection,
|
|
1895
|
+
enableSubtreeCopy: resolvedEnableSubtreeCopy,
|
|
1896
|
+
enableInsertPaste: enableInsertPaste ?? true,
|
|
1604
1897
|
onDataChange,
|
|
1605
|
-
onBatchChange
|
|
1898
|
+
onBatchChange,
|
|
1899
|
+
onRowsPaste
|
|
1606
1900
|
});
|
|
1607
1901
|
const {
|
|
1608
1902
|
editingCell,
|
|
@@ -1624,22 +1918,10 @@ function useGlideTable(options) {
|
|
|
1624
1918
|
);
|
|
1625
1919
|
const clearHover = useCallback3(() => {
|
|
1626
1920
|
setHoveredRowIndex(null);
|
|
1627
|
-
setHoveredGroupKey(null);
|
|
1628
1921
|
}, []);
|
|
1629
|
-
const handleRowHover = useCallback3(
|
|
1630
|
-
(rowIndex
|
|
1631
|
-
|
|
1632
|
-
if (!primaryRowSpanKey) {
|
|
1633
|
-
setHoveredGroupKey(null);
|
|
1634
|
-
return;
|
|
1635
|
-
}
|
|
1636
|
-
const groupValue = rowData[primaryRowSpanKey];
|
|
1637
|
-
setHoveredGroupKey(
|
|
1638
|
-
groupValue === null || groupValue === void 0 ? null : String(groupValue)
|
|
1639
|
-
);
|
|
1640
|
-
},
|
|
1641
|
-
[primaryRowSpanKey]
|
|
1642
|
-
);
|
|
1922
|
+
const handleRowHover = useCallback3((rowIndex, _rowData) => {
|
|
1923
|
+
setHoveredRowIndex(rowIndex);
|
|
1924
|
+
}, []);
|
|
1643
1925
|
const handleToggleSelect = useCallback3(
|
|
1644
1926
|
(row) => {
|
|
1645
1927
|
if (!row.getCanSelect()) return;
|
|
@@ -1662,10 +1944,10 @@ function useGlideTable(options) {
|
|
|
1662
1944
|
rowSpan: {
|
|
1663
1945
|
enableRowSpan,
|
|
1664
1946
|
primaryRowSpanKey,
|
|
1947
|
+
primaryRowSpanColumnId,
|
|
1665
1948
|
columnRowSpanMap,
|
|
1666
1949
|
hoveredRowIndex,
|
|
1667
|
-
|
|
1668
|
-
selectedGroupKeys,
|
|
1950
|
+
selectedRowIndices,
|
|
1669
1951
|
onRowHover: handleRowHover
|
|
1670
1952
|
},
|
|
1671
1953
|
selection: {
|
|
@@ -1703,10 +1985,10 @@ function useGlideTable(options) {
|
|
|
1703
1985
|
}, [
|
|
1704
1986
|
enableRowSpan,
|
|
1705
1987
|
primaryRowSpanKey,
|
|
1988
|
+
primaryRowSpanColumnId,
|
|
1706
1989
|
columnRowSpanMap,
|
|
1707
1990
|
hoveredRowIndex,
|
|
1708
|
-
|
|
1709
|
-
selectedGroupKeys,
|
|
1991
|
+
selectedRowIndices,
|
|
1710
1992
|
handleRowHover,
|
|
1711
1993
|
rowSelectionMode,
|
|
1712
1994
|
selectOnRowClick,
|
|
@@ -1732,6 +2014,14 @@ function useGlideTable(options) {
|
|
|
1732
2014
|
labels.expandRow,
|
|
1733
2015
|
labels.collapseRow
|
|
1734
2016
|
]);
|
|
2017
|
+
const copySelectionRef = useRef5(copySelection);
|
|
2018
|
+
useEffect5(() => {
|
|
2019
|
+
copySelectionRef.current = copySelection;
|
|
2020
|
+
}, [copySelection]);
|
|
2021
|
+
const stableCopySelection = useCallback3((options2) => copySelectionRef.current(options2), []);
|
|
2022
|
+
useEffect5(() => {
|
|
2023
|
+
onCopyActionsReady?.({ copySelection: stableCopySelection });
|
|
2024
|
+
}, [onCopyActionsReady, stableCopySelection]);
|
|
1735
2025
|
return {
|
|
1736
2026
|
table,
|
|
1737
2027
|
tableData,
|
|
@@ -1751,7 +2041,8 @@ function useGlideTable(options) {
|
|
|
1751
2041
|
paddingBottom,
|
|
1752
2042
|
rowContextValue,
|
|
1753
2043
|
handleToggleSelect,
|
|
1754
|
-
clearHover
|
|
2044
|
+
clearHover,
|
|
2045
|
+
copySelection: stableCopySelection
|
|
1755
2046
|
};
|
|
1756
2047
|
}
|
|
1757
2048
|
|
|
@@ -1877,7 +2168,12 @@ function DataTable({
|
|
|
1877
2168
|
return /* @__PURE__ */ jsx5(
|
|
1878
2169
|
"th",
|
|
1879
2170
|
{
|
|
1880
|
-
style: {
|
|
2171
|
+
style: {
|
|
2172
|
+
width: header.getSize() !== 150 ? header.getSize() : void 0,
|
|
2173
|
+
// Column sizing follows TanStack's `size`, but
|
|
2174
|
+
// ensure the column keeps its min width when the container shrinks.
|
|
2175
|
+
minWidth: header.getSize() !== 150 ? header.getSize() : void 0
|
|
2176
|
+
},
|
|
1881
2177
|
className: cn(
|
|
1882
2178
|
"data-table-head-cell",
|
|
1883
2179
|
CELL_ALIGN_CLASS[align],
|