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/dist/compound.cjs CHANGED
@@ -471,15 +471,16 @@ var useConvertTreeData = ({
471
471
  children: [],
472
472
  processed: false
473
473
  }));
474
- const itemMap = /* @__PURE__ */ new Map();
475
- dataWithLevels.forEach((item) => {
476
- const key = getFieldValue(item, toggleField);
477
- if (typeof key !== "string" || !key) return;
478
- if (!itemMap.has(key)) {
479
- itemMap.set(key, []);
474
+ const findNearestPrecedingParent = (index, parentKey) => {
475
+ for (let i = index - 1; i >= 0; i -= 1) {
476
+ const candidate = dataWithLevels[i];
477
+ if (!candidate) continue;
478
+ if (getFieldValue(candidate, toggleField) === parentKey) {
479
+ return candidate;
480
+ }
480
481
  }
481
- itemMap.get(key)?.push(item);
482
- });
482
+ return void 0;
483
+ };
483
484
  const rootItems = [];
484
485
  dataWithLevels.forEach((item) => {
485
486
  if (!getFieldValue(item, childField)) {
@@ -487,29 +488,18 @@ var useConvertTreeData = ({
487
488
  item.processed = true;
488
489
  }
489
490
  });
490
- dataWithLevels.forEach((item) => {
491
+ dataWithLevels.forEach((item, index) => {
491
492
  const parentKey = getFieldValue(item, childField);
492
493
  if (!parentKey || item.processed) return;
493
- const parentItems = dataWithLevels.filter(
494
- (parent) => getFieldValue(parent, toggleField) === parentKey && !getFieldValue(parent, childField)
495
- );
496
- if (parentItems.length > 0) {
497
- const parent = parentItems[0];
494
+ const parent = findNearestPrecedingParent(index, parentKey);
495
+ if (parent) {
498
496
  item.level = parent.level + 1;
499
497
  parent.children.push(item);
500
498
  item.processed = true;
501
- } else {
502
- const otherParents = itemMap.get(String(parentKey)) || [];
503
- if (otherParents.length > 0) {
504
- const parent = otherParents[0];
505
- item.level = parent.level + 1;
506
- parent.children.push(item);
507
- item.processed = true;
508
- } else {
509
- rootItems.push(item);
510
- item.processed = true;
511
- }
499
+ return;
512
500
  }
501
+ rootItems.push(item);
502
+ item.processed = true;
513
503
  });
514
504
  return rootItems;
515
505
  }, [enabled, data, toggleField, childField, flattenField]);
@@ -534,16 +524,23 @@ var useConvertTreeData = ({
534
524
  return result;
535
525
  };
536
526
  const flattenedData = flatten(processedData, [], 0);
537
- flattenedData.forEach((item) => {
538
- if (getFieldValue(item, childField)) {
539
- const parentItem = flattenedData.find(
540
- (parent) => getFieldValue(parent, toggleField) === getFieldValue(item, childField)
541
- );
542
- const parentAmount = parentItem ? Number(getFieldValue(parentItem, qtyField) ?? 1) : 1;
543
- item.parentCount = parentAmount || 1;
544
- } else {
527
+ flattenedData.forEach((item, index) => {
528
+ const parentKey = getFieldValue(item, childField);
529
+ if (!parentKey) {
545
530
  item.parentCount = 1;
531
+ return;
532
+ }
533
+ let parentItem;
534
+ for (let i = index - 1; i >= 0; i -= 1) {
535
+ const candidate = flattenedData[i];
536
+ if (!candidate) continue;
537
+ if (getFieldValue(candidate, toggleField) === parentKey) {
538
+ parentItem = candidate;
539
+ break;
540
+ }
546
541
  }
542
+ const parentAmount = parentItem ? Number(getFieldValue(parentItem, qtyField) ?? 1) : 1;
543
+ item.parentCount = parentAmount || 1;
547
544
  });
548
545
  return flattenedData;
549
546
  }, [
@@ -815,11 +812,10 @@ function DataTableRow({
815
812
  const { classNames, rowSpan, selection, cellSelection, cellEdit, expand } = useDataTableRowContext();
816
813
  const {
817
814
  enableRowSpan,
818
- primaryRowSpanKey,
815
+ primaryRowSpanColumnId,
819
816
  columnRowSpanMap,
820
817
  hoveredRowIndex,
821
- hoveredGroupKey,
822
- selectedGroupKeys,
818
+ selectedRowIndices,
823
819
  onRowHover
824
820
  } = rowSpan;
825
821
  const { rowSelectionMode, selectOnRowClick, onRowClick, getRowClassName } = selection;
@@ -852,9 +848,11 @@ function DataTableRow({
852
848
  const rowData = row.original;
853
849
  const isRowHovered = hoveredRowIndex === rowIndex;
854
850
  const isRowSelected = row.getIsSelected();
855
- const rowGroupKey = primaryRowSpanKey !== void 0 && rowData[primaryRowSpanKey] !== null && rowData[primaryRowSpanKey] !== void 0 ? String(rowData[primaryRowSpanKey]) : null;
856
- const isGroupHovered = enableRowSpan && hoveredGroupKey !== null && rowGroupKey === hoveredGroupKey;
857
- const isGroupSelected = enableRowSpan && rowGroupKey !== null && selectedGroupKeys.has(rowGroupKey);
851
+ const { startRow: primaryGroupStart, rowSpan: primaryGroupSpan } = resolveRowSpanAt(
852
+ primaryRowSpanColumnId ? columnRowSpanMap.get(primaryRowSpanColumnId) : void 0,
853
+ rowIndex
854
+ );
855
+ const isGroupHovered = enableRowSpan && hoveredRowIndex !== null && hoveredRowIndex >= primaryGroupStart && hoveredRowIndex <= primaryGroupStart + primaryGroupSpan - 1;
858
856
  const visibleCells = row.getVisibleCells();
859
857
  const columnIdsByIndex = visibleCells.map((cell) => cell.column.id);
860
858
  const isVisuallySelectedAt = activeSelectionBounds ? (targetRow, targetCol) => {
@@ -931,7 +929,16 @@ function DataTableRow({
931
929
  const cellRowSpan = rowSpanInfo?.rowSpan ?? 1;
932
930
  const isMergedCellHovered = hoveredRowIndex !== null && hoveredRowIndex >= rowIndex && hoveredRowIndex <= rowIndex + cellRowSpan - 1;
933
931
  const showCellHover = isRowSpanColumn ? isMergedCellHovered : isRowHovered;
934
- const showCellSelected = isRowSpanColumn ? isGroupSelected : isRowSelected;
932
+ let isMergedCellSelected = false;
933
+ if (isRowSpanColumn) {
934
+ for (let r = rowIndex; r < rowIndex + cellRowSpan; r += 1) {
935
+ if (selectedRowIndices.has(r)) {
936
+ isMergedCellSelected = true;
937
+ break;
938
+ }
939
+ }
940
+ }
941
+ const showCellSelected = isRowSpanColumn ? isMergedCellSelected : isRowSelected;
935
942
  const isMerged = cellRowSpan > 1;
936
943
  const showMergedRightEdge = isMerged && (cellIndex === visibleCells.length - 1 || resolveRowSpanAt(
937
944
  columnRowSpanMap.get(columnIdsByIndex[cellIndex + 1]),
@@ -1256,6 +1263,108 @@ function useCellEdit({
1256
1263
  // src/components/ui/table/features/cell-selection/useCellSelection.ts
1257
1264
  var import_react5 = require("react");
1258
1265
 
1266
+ // src/components/ui/table/features/cell-selection/copyData.ts
1267
+ function formatCellValue(value) {
1268
+ if (value === null || value === void 0) return "";
1269
+ return String(value);
1270
+ }
1271
+ function getNestedValue(row, path) {
1272
+ if (!path.includes(".")) return row[path];
1273
+ return path.split(".").reduce((current, key) => {
1274
+ if (current === null || current === void 0 || typeof current !== "object") {
1275
+ return void 0;
1276
+ }
1277
+ return current[key];
1278
+ }, row);
1279
+ }
1280
+ function readRowColumnValue(rowData, columnDef) {
1281
+ if ("accessorFn" in columnDef && typeof columnDef.accessorFn === "function") {
1282
+ return columnDef.accessorFn(rowData, 0);
1283
+ }
1284
+ if ("accessorKey" in columnDef && columnDef.accessorKey != null && columnDef.accessorKey !== "") {
1285
+ return getNestedValue(rowData, String(columnDef.accessorKey));
1286
+ }
1287
+ return void 0;
1288
+ }
1289
+ function hasSubtree(row) {
1290
+ const children = row.children;
1291
+ return Array.isArray(children) && children.length > 0;
1292
+ }
1293
+ function getOriginalRowId(original) {
1294
+ return String(original.id ?? original.uniqueId ?? "");
1295
+ }
1296
+ function getRowDepth(original) {
1297
+ return typeof original.level === "number" ? original.level : 0;
1298
+ }
1299
+ function collectCopyRowEntries(visibleRows, bounds, mode = "visible") {
1300
+ const { startRow, endRow } = bounds;
1301
+ const result = [];
1302
+ const includedOriginalIds = /* @__PURE__ */ new Set();
1303
+ const appendSubtree = (node, depth) => {
1304
+ const children = node.children;
1305
+ if (!Array.isArray(children) || children.length === 0) return;
1306
+ for (const child of children) {
1307
+ const childId = getOriginalRowId(child);
1308
+ if (!(childId && includedOriginalIds.has(childId))) {
1309
+ result.push({ row: child, depth });
1310
+ if (childId) includedOriginalIds.add(childId);
1311
+ }
1312
+ appendSubtree(child, depth + 1);
1313
+ }
1314
+ };
1315
+ for (let rowIndex = startRow; rowIndex <= endRow; rowIndex += 1) {
1316
+ const row = visibleRows[rowIndex];
1317
+ if (!row) continue;
1318
+ const originalId = getOriginalRowId(row.original);
1319
+ if (originalId && includedOriginalIds.has(originalId)) continue;
1320
+ const depth = getRowDepth(row.original);
1321
+ result.push({ row: row.original, depth });
1322
+ if (originalId) includedOriginalIds.add(originalId);
1323
+ if (mode !== "subtree" || !hasSubtree(row.original)) continue;
1324
+ appendSubtree(row.original, depth + 1);
1325
+ }
1326
+ return result;
1327
+ }
1328
+ function serializeCopyRowsToTSV(copyRows, visibleRows, bounds, depths) {
1329
+ if (copyRows.length === 0) return "";
1330
+ const { startCol, endCol } = bounds;
1331
+ const columnCells = visibleRows[0]?.getVisibleCells().slice(startCol, endCol + 1) ?? [];
1332
+ if (columnCells.length === 0) return "";
1333
+ const resolvedDepths = depths && depths.length === copyRows.length ? depths : copyRows.map((row) => getRowDepth(row));
1334
+ const minDepth = Math.min(...resolvedDepths);
1335
+ return copyRows.map((rowData, index) => {
1336
+ const relativeDepth = Math.max(0, (resolvedDepths[index] ?? 0) - minDepth);
1337
+ const line = columnCells.map(
1338
+ (cell) => formatCellValue(
1339
+ readRowColumnValue(
1340
+ rowData,
1341
+ cell.column.columnDef
1342
+ )
1343
+ )
1344
+ ).join(" ");
1345
+ return `${" ".repeat(relativeDepth)}${line}`;
1346
+ }).join("\n");
1347
+ }
1348
+ function serializeSelectionToTSV(visibleRows, bounds, mode = "visible") {
1349
+ const entries = collectCopyRowEntries(visibleRows, bounds, mode);
1350
+ return serializeCopyRowsToTSV(
1351
+ entries.map((entry) => entry.row),
1352
+ visibleRows,
1353
+ bounds,
1354
+ entries.map((entry) => entry.depth)
1355
+ );
1356
+ }
1357
+ async function writeSelectionToClipboard(visibleRows, bounds, mode = "visible") {
1358
+ const text = serializeSelectionToTSV(visibleRows, bounds, mode);
1359
+ if (!text) return false;
1360
+ try {
1361
+ await navigator.clipboard.writeText(text);
1362
+ } catch {
1363
+ return false;
1364
+ }
1365
+ return true;
1366
+ }
1367
+
1259
1368
  // src/components/ui/table/features/cell-selection/fillData.ts
1260
1369
  function getColumnAccessorKey2(columnDef) {
1261
1370
  if ("accessorKey" in columnDef && columnDef.accessorKey) {
@@ -1312,15 +1421,100 @@ function hasFillExtension(sourceBounds, fillBounds) {
1312
1421
  return fillBounds.startRow < sourceBounds.startRow || fillBounds.endRow > sourceBounds.endRow || fillBounds.startCol < sourceBounds.startCol || fillBounds.endCol > sourceBounds.endCol;
1313
1422
  }
1314
1423
 
1424
+ // src/components/ui/table/features/cell-selection/pasteData.ts
1425
+ function countLeadingEmptyCells(cells) {
1426
+ let depth = 0;
1427
+ while (depth < cells.length && cells[depth] === "") {
1428
+ depth += 1;
1429
+ }
1430
+ return depth;
1431
+ }
1432
+ function looksLikeSubtreeIndentation(leadingEmptyCounts) {
1433
+ if (leadingEmptyCounts.length === 0) return false;
1434
+ const firstDepth = leadingEmptyCounts[0] ?? 0;
1435
+ if (firstDepth !== 0) return false;
1436
+ return leadingEmptyCounts.some((depth) => depth > 0);
1437
+ }
1438
+ function parseClipboardTSVWithDepths(text) {
1439
+ if (!text) return { values: [], depths: [] };
1440
+ const normalized = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
1441
+ const withoutTrailing = normalized.replace(/\n+$/, "");
1442
+ if (!withoutTrailing) return { values: [], depths: [] };
1443
+ const rows = withoutTrailing.split("\n").map((line) => line.split(" "));
1444
+ const leadingEmptyCounts = rows.map(countLeadingEmptyCells);
1445
+ const treatAsDepth = looksLikeSubtreeIndentation(leadingEmptyCounts);
1446
+ const values = [];
1447
+ const depths = [];
1448
+ for (let index = 0; index < rows.length; index += 1) {
1449
+ const cells = rows[index] ?? [];
1450
+ const depth = leadingEmptyCounts[index] ?? 0;
1451
+ if (treatAsDepth) {
1452
+ values.push(cells.slice(depth));
1453
+ depths.push(depth);
1454
+ } else {
1455
+ values.push(cells);
1456
+ depths.push(0);
1457
+ }
1458
+ }
1459
+ return { values, depths };
1460
+ }
1461
+ function resolvePasteColumnIds(rows, startCol, width) {
1462
+ if (width <= 0) return [];
1463
+ const cells = rows[0]?.getVisibleCells() ?? [];
1464
+ const columnIds = [];
1465
+ for (let offset = 0; offset < width; offset += 1) {
1466
+ const cell = cells[startCol + offset];
1467
+ if (!cell) break;
1468
+ columnIds.push(cell.column.id);
1469
+ }
1470
+ return columnIds;
1471
+ }
1472
+ function buildRowsPastePayload(rows, startRow, startCol, text, mode, endRow = startRow) {
1473
+ const { values, depths } = parseClipboardTSVWithDepths(text);
1474
+ if (values.length === 0) return null;
1475
+ const width = Math.max(...values.map((row) => row.length), 0);
1476
+ if (width === 0) return null;
1477
+ const columnIds = resolvePasteColumnIds(rows, startCol, width);
1478
+ if (columnIds.length === 0) return null;
1479
+ const rowIds = [];
1480
+ for (let offset = 0; offset < values.length; offset += 1) {
1481
+ const row = rows[startRow + offset];
1482
+ if (!row) break;
1483
+ rowIds.push(row.id);
1484
+ }
1485
+ const anchorRow = rows[endRow] ?? rows[startRow];
1486
+ return {
1487
+ mode,
1488
+ startRow,
1489
+ startCol,
1490
+ endRow,
1491
+ rowIds,
1492
+ anchorRowId: anchorRow?.id ?? "",
1493
+ columnIds,
1494
+ values,
1495
+ depths
1496
+ };
1497
+ }
1498
+ function isEditablePasteTarget(target) {
1499
+ if (!(target instanceof HTMLElement)) return false;
1500
+ const tag = target.tagName;
1501
+ if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
1502
+ return Boolean(target.isContentEditable);
1503
+ }
1504
+
1315
1505
  // src/components/ui/table/features/cell-selection/useCellSelection.ts
1316
1506
  function useCellSelection({
1317
1507
  data,
1318
1508
  rows,
1319
1509
  enabled = true,
1510
+ enableSubtreeCopy = false,
1511
+ enableInsertPaste = true,
1320
1512
  onDataChange,
1321
- onBatchChange
1513
+ onBatchChange,
1514
+ onRowsPaste
1322
1515
  }) {
1323
1516
  const [dragState, setDragState] = (0, import_react5.useState)(INITIAL_DRAG_STATE);
1517
+ const pendingPasteModeRef = (0, import_react5.useRef)(null);
1324
1518
  const cellSelectionBounds = getCellSelectionBounds(dragState.start, dragState.end);
1325
1519
  const activeSelectionBounds = enabled ? getActiveSelectionBounds(dragState, cellSelectionBounds) : null;
1326
1520
  const handleCellMouseDown = (0, import_react5.useCallback)(
@@ -1374,21 +1568,113 @@ function useCellSelection({
1374
1568
  setDragState(INITIAL_DRAG_STATE);
1375
1569
  }
1376
1570
  }, [enabled]);
1571
+ const copySelection = (0, import_react5.useCallback)(
1572
+ async (options) => {
1573
+ if (!enabled || !activeSelectionBounds) return false;
1574
+ const mode = options?.includeDescendants && enableSubtreeCopy ? "subtree" : "visible";
1575
+ return writeSelectionToClipboard(rows, activeSelectionBounds, mode);
1576
+ },
1577
+ [activeSelectionBounds, enableSubtreeCopy, enabled, rows]
1578
+ );
1377
1579
  (0, import_react5.useEffect)(() => {
1378
1580
  if (!enabled) return;
1379
1581
  const handleKeyDown = (e) => {
1380
- if ((e.ctrlKey || e.metaKey) && e.key === "c" && activeSelectionBounds) {
1381
- const { startRow, endRow, startCol, endCol } = activeSelectionBounds;
1382
- const selectedData = rows.slice(startRow, endRow + 1).map((row) => {
1383
- const cells = row.getVisibleCells();
1384
- return cells.slice(startCol, endCol + 1).map((cell) => cell.getValue()).join(" ");
1385
- }).join("\n");
1386
- navigator.clipboard.writeText(selectedData);
1387
- }
1582
+ if (!activeSelectionBounds) return;
1583
+ if (!(e.ctrlKey || e.metaKey)) return;
1584
+ const isSubtreeShortcut = enableSubtreeCopy && e.shiftKey && e.key.toLowerCase() === "c";
1585
+ const isVisibleCopyShortcut = !e.shiftKey && e.key.toLowerCase() === "c";
1586
+ if (!isSubtreeShortcut && !isVisibleCopyShortcut) return;
1587
+ e.preventDefault();
1588
+ void copySelection({ includeDescendants: isSubtreeShortcut });
1388
1589
  };
1389
1590
  window.addEventListener("keydown", handleKeyDown);
1390
1591
  return () => window.removeEventListener("keydown", handleKeyDown);
1391
- }, [activeSelectionBounds, enabled, rows]);
1592
+ }, [activeSelectionBounds, copySelection, enableSubtreeCopy, enabled]);
1593
+ const emitRowsPaste = (0, import_react5.useCallback)(
1594
+ (text, mode) => {
1595
+ if (!onRowsPaste || !activeSelectionBounds) return false;
1596
+ const payload = buildRowsPastePayload(
1597
+ rows,
1598
+ activeSelectionBounds.startRow,
1599
+ activeSelectionBounds.startCol,
1600
+ text,
1601
+ mode,
1602
+ activeSelectionBounds.endRow
1603
+ );
1604
+ if (!payload) return false;
1605
+ onRowsPaste(payload);
1606
+ return true;
1607
+ },
1608
+ [activeSelectionBounds, onRowsPaste, rows]
1609
+ );
1610
+ (0, import_react5.useEffect)(() => {
1611
+ if (!enabled || !onRowsPaste) return;
1612
+ const pasteHandledRef = { current: false };
1613
+ const ignoreNextPasteRef = { current: false };
1614
+ const handleKeyDown = (e) => {
1615
+ if (!activeSelectionBounds) return;
1616
+ if (!(e.ctrlKey || e.metaKey)) return;
1617
+ if (e.key.toLowerCase() !== "v") return;
1618
+ if (isEditablePasteTarget(e.target) || isEditablePasteTarget(document.activeElement)) {
1619
+ return;
1620
+ }
1621
+ if (e.shiftKey && !enableInsertPaste) {
1622
+ ignoreNextPasteRef.current = true;
1623
+ pendingPasteModeRef.current = null;
1624
+ return;
1625
+ }
1626
+ const mode = e.shiftKey ? "insert" : "overwrite";
1627
+ pasteHandledRef.current = false;
1628
+ ignoreNextPasteRef.current = false;
1629
+ pendingPasteModeRef.current = mode;
1630
+ void (async () => {
1631
+ try {
1632
+ const text = await navigator.clipboard.readText();
1633
+ if (pasteHandledRef.current) return;
1634
+ if (pendingPasteModeRef.current !== mode) return;
1635
+ if (!text) return;
1636
+ pasteHandledRef.current = true;
1637
+ emitRowsPaste(text, mode);
1638
+ pendingPasteModeRef.current = null;
1639
+ } catch {
1640
+ }
1641
+ })();
1642
+ };
1643
+ const handlePaste = (e) => {
1644
+ if (!activeSelectionBounds) return;
1645
+ if (isEditablePasteTarget(e.target) || isEditablePasteTarget(document.activeElement)) {
1646
+ return;
1647
+ }
1648
+ if (ignoreNextPasteRef.current) {
1649
+ ignoreNextPasteRef.current = false;
1650
+ pendingPasteModeRef.current = null;
1651
+ return;
1652
+ }
1653
+ const mode = pendingPasteModeRef.current ?? "overwrite";
1654
+ if (pasteHandledRef.current) {
1655
+ e.preventDefault();
1656
+ return;
1657
+ }
1658
+ const text = e.clipboardData?.getData("text/plain");
1659
+ if (text == null || text === "") return;
1660
+ pasteHandledRef.current = true;
1661
+ e.preventDefault();
1662
+ emitRowsPaste(text, mode);
1663
+ pendingPasteModeRef.current = null;
1664
+ };
1665
+ window.addEventListener("keydown", handleKeyDown);
1666
+ window.addEventListener("paste", handlePaste);
1667
+ return () => {
1668
+ window.removeEventListener("keydown", handleKeyDown);
1669
+ window.removeEventListener("paste", handlePaste);
1670
+ };
1671
+ }, [
1672
+ activeSelectionBounds,
1673
+ emitRowsPaste,
1674
+ enableInsertPaste,
1675
+ enabled,
1676
+ onRowsPaste
1677
+ ]);
1392
1678
  (0, import_react5.useEffect)(() => {
1393
1679
  if (!enabled) return;
1394
1680
  const handleMouseUp = () => {
@@ -1434,7 +1720,8 @@ function useCellSelection({
1434
1720
  activeSelectionBounds,
1435
1721
  handleCellMouseDown,
1436
1722
  handleCellMouseEnter,
1437
- handleFillHandleMouseDown
1723
+ handleFillHandleMouseDown,
1724
+ copySelection
1438
1725
  };
1439
1726
  }
1440
1727
 
@@ -1498,6 +1785,10 @@ function useGlideTable(options) {
1498
1785
  expandedRows: controlledExpandedRows,
1499
1786
  onExpandedRowsChange,
1500
1787
  preventExpand = false,
1788
+ enableSubtreeCopy,
1789
+ onCopyActionsReady,
1790
+ onRowsPaste,
1791
+ enableInsertPaste,
1501
1792
  enableVirtualization = true,
1502
1793
  estimateRowHeight = DATA_TABLE_ROW_HEIGHT,
1503
1794
  virtualOverscan = DATA_TABLE_VIRTUAL_OVERSCAN
@@ -1512,12 +1803,12 @@ function useGlideTable(options) {
1512
1803
  };
1513
1804
  }, [labelsProp, emptyText, loadingText, selectionLabel]);
1514
1805
  const enableExpand = Boolean(toggleField);
1806
+ const resolvedEnableSubtreeCopy = enableSubtreeCopy ?? enableExpand;
1515
1807
  const [internalRowSelection, setInternalRowSelection] = (0, import_react6.useState)({});
1516
1808
  const [internalExpandedRows, setInternalExpandedRows] = (0, import_react6.useState)(
1517
1809
  () => /* @__PURE__ */ new Set()
1518
1810
  );
1519
1811
  const [hoveredRowIndex, setHoveredRowIndex] = (0, import_react6.useState)(null);
1520
- const [hoveredGroupKey, setHoveredGroupKey] = (0, import_react6.useState)(null);
1521
1812
  const scrollRef = (0, import_react6.useRef)(null);
1522
1813
  const shouldVirtualize = enableVirtualization && !enableRowSpan;
1523
1814
  (0, import_react6.useEffect)(() => {
@@ -1581,6 +1872,7 @@ function useGlideTable(options) {
1581
1872
  return collectRowSpanColumns(columns);
1582
1873
  }, [enableRowSpan, columns]);
1583
1874
  const primaryRowSpanKey = rowSpanColumnKeys[0]?.rowSpanKey;
1875
+ const primaryRowSpanColumnId = rowSpanColumnKeys[0]?.columnId;
1584
1876
  const columnRowSpanMap = (0, import_react6.useMemo)(
1585
1877
  () => buildColumnRowSpanMap(tableData, rowSpanColumnKeys),
1586
1878
  [tableData, rowSpanColumnKeys]
@@ -1599,27 +1891,29 @@ function useGlideTable(options) {
1599
1891
  const totalSize = rowVirtualizer.getTotalSize();
1600
1892
  const paddingTop = virtualRows.length > 0 ? virtualRows[0]?.start ?? 0 : 0;
1601
1893
  const paddingBottom = virtualRows.length > 0 ? totalSize - (virtualRows[virtualRows.length - 1]?.end ?? 0) : 0;
1602
- const selectedGroupKeys = (0, import_react6.useMemo)(() => {
1603
- if (!enableRowSpan || !primaryRowSpanKey) return /* @__PURE__ */ new Set();
1604
- const keys = /* @__PURE__ */ new Set();
1894
+ const selectedRowIndices = (0, import_react6.useMemo)(() => {
1895
+ const indices = /* @__PURE__ */ new Set();
1605
1896
  for (const selectedRow of selectedRows) {
1606
- const value = selectedRow.original[primaryRowSpanKey];
1607
- if (value !== null && value !== void 0) keys.add(String(value));
1897
+ indices.add(selectedRow.index);
1608
1898
  }
1609
- return keys;
1610
- }, [enableRowSpan, primaryRowSpanKey, selectedRows]);
1899
+ return indices;
1900
+ }, [selectedRows]);
1611
1901
  const {
1612
1902
  dragState,
1613
1903
  activeSelectionBounds,
1614
1904
  handleCellMouseDown,
1615
1905
  handleCellMouseEnter,
1616
- handleFillHandleMouseDown
1906
+ handleFillHandleMouseDown,
1907
+ copySelection
1617
1908
  } = useCellSelection({
1618
1909
  data: tableData,
1619
1910
  rows,
1620
1911
  enabled: enableCellSelection,
1912
+ enableSubtreeCopy: resolvedEnableSubtreeCopy,
1913
+ enableInsertPaste: enableInsertPaste ?? true,
1621
1914
  onDataChange,
1622
- onBatchChange
1915
+ onBatchChange,
1916
+ onRowsPaste
1623
1917
  });
1624
1918
  const {
1625
1919
  editingCell,
@@ -1641,22 +1935,10 @@ function useGlideTable(options) {
1641
1935
  );
1642
1936
  const clearHover = (0, import_react6.useCallback)(() => {
1643
1937
  setHoveredRowIndex(null);
1644
- setHoveredGroupKey(null);
1645
1938
  }, []);
1646
- const handleRowHover = (0, import_react6.useCallback)(
1647
- (rowIndex, rowData) => {
1648
- setHoveredRowIndex(rowIndex);
1649
- if (!primaryRowSpanKey) {
1650
- setHoveredGroupKey(null);
1651
- return;
1652
- }
1653
- const groupValue = rowData[primaryRowSpanKey];
1654
- setHoveredGroupKey(
1655
- groupValue === null || groupValue === void 0 ? null : String(groupValue)
1656
- );
1657
- },
1658
- [primaryRowSpanKey]
1659
- );
1939
+ const handleRowHover = (0, import_react6.useCallback)((rowIndex, _rowData) => {
1940
+ setHoveredRowIndex(rowIndex);
1941
+ }, []);
1660
1942
  const handleToggleSelect = (0, import_react6.useCallback)(
1661
1943
  (row) => {
1662
1944
  if (!row.getCanSelect()) return;
@@ -1679,10 +1961,10 @@ function useGlideTable(options) {
1679
1961
  rowSpan: {
1680
1962
  enableRowSpan,
1681
1963
  primaryRowSpanKey,
1964
+ primaryRowSpanColumnId,
1682
1965
  columnRowSpanMap,
1683
1966
  hoveredRowIndex,
1684
- hoveredGroupKey,
1685
- selectedGroupKeys,
1967
+ selectedRowIndices,
1686
1968
  onRowHover: handleRowHover
1687
1969
  },
1688
1970
  selection: {
@@ -1720,10 +2002,10 @@ function useGlideTable(options) {
1720
2002
  }, [
1721
2003
  enableRowSpan,
1722
2004
  primaryRowSpanKey,
2005
+ primaryRowSpanColumnId,
1723
2006
  columnRowSpanMap,
1724
2007
  hoveredRowIndex,
1725
- hoveredGroupKey,
1726
- selectedGroupKeys,
2008
+ selectedRowIndices,
1727
2009
  handleRowHover,
1728
2010
  rowSelectionMode,
1729
2011
  selectOnRowClick,
@@ -1749,6 +2031,14 @@ function useGlideTable(options) {
1749
2031
  labels.expandRow,
1750
2032
  labels.collapseRow
1751
2033
  ]);
2034
+ const copySelectionRef = (0, import_react6.useRef)(copySelection);
2035
+ (0, import_react6.useEffect)(() => {
2036
+ copySelectionRef.current = copySelection;
2037
+ }, [copySelection]);
2038
+ const stableCopySelection = (0, import_react6.useCallback)((options2) => copySelectionRef.current(options2), []);
2039
+ (0, import_react6.useEffect)(() => {
2040
+ onCopyActionsReady?.({ copySelection: stableCopySelection });
2041
+ }, [onCopyActionsReady, stableCopySelection]);
1752
2042
  return {
1753
2043
  table,
1754
2044
  tableData,
@@ -1768,7 +2058,8 @@ function useGlideTable(options) {
1768
2058
  paddingBottom,
1769
2059
  rowContextValue,
1770
2060
  handleToggleSelect,
1771
- clearHover
2061
+ clearHover,
2062
+ copySelection: stableCopySelection
1772
2063
  };
1773
2064
  }
1774
2065
 
@@ -1894,7 +2185,12 @@ function DataTable({
1894
2185
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1895
2186
  "th",
1896
2187
  {
1897
- style: { width: header.getSize() !== 150 ? header.getSize() : void 0 },
2188
+ style: {
2189
+ width: header.getSize() !== 150 ? header.getSize() : void 0,
2190
+ // Column sizing follows TanStack's `size`, but
2191
+ // ensure the column keeps its min width when the container shrinks.
2192
+ minWidth: header.getSize() !== 150 ? header.getSize() : void 0
2193
+ },
1898
2194
  className: cn(
1899
2195
  "data-table-head-cell",
1900
2196
  CELL_ALIGN_CLASS[align],
@@ -1,7 +1,7 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode, ReactElement } from 'react';
3
- import { c as DataTableProps, T as TableColumnProps, e as TableProps } from './types-BfthylVR.cjs';
4
- export { a as DataTableClassNames, b as DataTableLabels, d as DataTableSlots, R as RowSelectionMode } from './types-BfthylVR.cjs';
3
+ import { d as DataTableProps, T as TableColumnProps, g as TableProps } from './types-DOLnknDe.cjs';
4
+ export { a as DataTableClassNames, c as DataTableLabels, e as DataTableSlots, P as PasteMode, R as RowSelectionMode, f as RowsPastePayload } from './types-DOLnknDe.cjs';
5
5
  export { ColumnDef, RowSelectionState } from '@tanstack/react-table';
6
6
 
7
7
  /**
@@ -1,7 +1,7 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode, ReactElement } from 'react';
3
- import { c as DataTableProps, T as TableColumnProps, e as TableProps } from './types-BfthylVR.js';
4
- export { a as DataTableClassNames, b as DataTableLabels, d as DataTableSlots, R as RowSelectionMode } from './types-BfthylVR.js';
3
+ import { d as DataTableProps, T as TableColumnProps, g as TableProps } from './types-DOLnknDe.js';
4
+ export { a as DataTableClassNames, c as DataTableLabels, e as DataTableSlots, P as PasteMode, R as RowSelectionMode, f as RowsPastePayload } from './types-DOLnknDe.js';
5
5
  export { ColumnDef, RowSelectionState } from '@tanstack/react-table';
6
6
 
7
7
  /**