react-glide-table 1.1.4 → 1.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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 useRef3,
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 = {
@@ -195,10 +195,36 @@ function getCellSelectionBounds(start, end) {
195
195
  endCol: Math.max(start.col, end.col)
196
196
  };
197
197
  }
198
+ function measureMergedSpanRowHeights(rowIndex, rowSpan, cellElement) {
199
+ if (rowSpan <= 1) return void 0;
200
+ const tbody = cellElement?.closest("tbody") ?? (typeof document !== "undefined" ? document.querySelector("tbody.data-table-body") : null);
201
+ if (!tbody) return void 0;
202
+ const rows = tbody.querySelectorAll(":scope > tr");
203
+ if (rows.length < rowIndex + rowSpan) return void 0;
204
+ const heights = [];
205
+ for (let i = 0; i < rowSpan; i++) {
206
+ const row = rows[rowIndex + i];
207
+ const height = row?.getBoundingClientRect().height ?? 0;
208
+ if (height <= 0) return void 0;
209
+ heights.push(height);
210
+ }
211
+ return heights;
212
+ }
198
213
  function getRowIndexInMergedCell(clientY, cellElement, rowIndex, rowSpan) {
199
214
  if (rowSpan <= 1) return rowIndex;
200
215
  const rect = cellElement.getBoundingClientRect();
201
216
  const relativeY = clientY - rect.top;
217
+ const heights = measureMergedSpanRowHeights(rowIndex, rowSpan, cellElement);
218
+ if (heights && heights.length === rowSpan) {
219
+ let accrued = 0;
220
+ for (let i = 0; i < rowSpan; i++) {
221
+ accrued += heights[i];
222
+ if (relativeY < accrued) {
223
+ return rowIndex + i;
224
+ }
225
+ }
226
+ return rowIndex + rowSpan - 1;
227
+ }
202
228
  const rowHeight = rect.height / rowSpan;
203
229
  const offset = Math.min(
204
230
  Math.max(Math.floor(relativeY / rowHeight), 0),
@@ -220,7 +246,35 @@ function getActiveSelectionBounds(dragState, selectionBounds) {
220
246
  var SELECTION_EDGE_WIDTH_PX = 2;
221
247
  var SELECTION_EDGE_COLOR = "var(--color-brand-primary)";
222
248
  var CELL_SELECTION_EDGES_CLASS = "cell-selection-edges";
223
- function getMergedCellStepEdges(rowIndex, colIndex, bounds, rowSpan, isVisuallySelectedAt) {
249
+ function rowRangeToHeightRatios(rowIndex, span, fromRow, toRowExclusive, rowHeights) {
250
+ const clampedFrom = Math.max(fromRow, rowIndex);
251
+ const clampedTo = Math.min(toRowExclusive, rowIndex + span);
252
+ if (clampedTo <= clampedFrom) {
253
+ return { offsetRatio: 0, lengthRatio: 0 };
254
+ }
255
+ if (!rowHeights || rowHeights.length !== span) {
256
+ return {
257
+ offsetRatio: (clampedFrom - rowIndex) / span,
258
+ lengthRatio: (clampedTo - clampedFrom) / span
259
+ };
260
+ }
261
+ const total = rowHeights.reduce((sum, height) => sum + height, 0) || 1;
262
+ let offsetPx = 0;
263
+ for (let i = 0; i < clampedFrom - rowIndex; i++) {
264
+ offsetPx += rowHeights[i] ?? 0;
265
+ }
266
+ let lengthPx = 0;
267
+ for (let i = clampedFrom - rowIndex; i < clampedTo - rowIndex; i++) {
268
+ lengthPx += rowHeights[i] ?? 0;
269
+ }
270
+ return {
271
+ offsetRatio: offsetPx / total,
272
+ lengthRatio: lengthPx / total,
273
+ offsetPx,
274
+ lengthPx
275
+ };
276
+ }
277
+ function getMergedCellStepEdges(rowIndex, colIndex, bounds, rowSpan, isVisuallySelectedAt, rowHeights) {
224
278
  const cellEndRow = rowIndex + rowSpan - 1;
225
279
  const span = cellEndRow - rowIndex + 1;
226
280
  if (span <= 1) return [];
@@ -239,20 +293,30 @@ function getMergedCellStepEdges(rowIndex, colIndex, bounds, rowSpan, isVisuallyS
239
293
  continue;
240
294
  }
241
295
  if (runStart !== null) {
242
- edges.push({
243
- side,
244
- offsetRatio: (runStart - rowIndex) / span,
245
- heightRatio: (row - runStart) / span
246
- });
296
+ const ratios = rowRangeToHeightRatios(
297
+ rowIndex,
298
+ span,
299
+ runStart,
300
+ row,
301
+ rowHeights
302
+ );
303
+ if (ratios.lengthRatio > 0) {
304
+ edges.push({ side, ...ratios });
305
+ }
247
306
  runStart = null;
248
307
  }
249
308
  }
250
309
  if (runStart !== null) {
251
- edges.push({
252
- side,
253
- offsetRatio: (runStart - rowIndex) / span,
254
- heightRatio: (toRowExclusive - runStart) / span
255
- });
310
+ const ratios = rowRangeToHeightRatios(
311
+ rowIndex,
312
+ span,
313
+ runStart,
314
+ toRowExclusive,
315
+ rowHeights
316
+ );
317
+ if (ratios.lengthRatio > 0) {
318
+ edges.push({ side, ...ratios });
319
+ }
256
320
  }
257
321
  };
258
322
  const collectSide = (side, neighborCol) => {
@@ -281,14 +345,17 @@ function getMergedCellStepEdges(rowIndex, colIndex, bounds, rowSpan, isVisuallyS
281
345
  }
282
346
  return edges;
283
347
  }
284
- function buildPartialVerticalGradient(edge) {
348
+ function buildPartialEdgeGradient(edge) {
349
+ const usePx = edge.offsetPx != null && edge.lengthPx != null;
285
350
  const startPct = edge.offsetRatio * 100;
286
- const endPct = (edge.offsetRatio + edge.heightRatio) * 100;
287
- const overlapPx = SELECTION_EDGE_WIDTH_PX + 1;
288
- const isTopProtrusion = edge.offsetRatio === 0 && edge.heightRatio < 1;
289
- const isBottomProtrusion = edge.offsetRatio > 0;
290
- const startStop = isBottomProtrusion ? `calc(${startPct}% - ${overlapPx}px)` : `${startPct}%`;
291
- const endStop = isTopProtrusion ? `calc(${endPct}% + ${overlapPx}px)` : `${endPct}%`;
351
+ const endPct = (edge.offsetRatio + edge.lengthRatio) * 100;
352
+ const startPx = edge.offsetPx ?? 0;
353
+ const endPx = (edge.offsetPx ?? 0) + (edge.lengthPx ?? 0);
354
+ const overlapPx = SELECTION_EDGE_WIDTH_PX;
355
+ const isTopProtrusion = (usePx ? startPx === 0 : edge.offsetRatio === 0) && edge.lengthRatio < 1;
356
+ const isBottomProtrusion = usePx ? startPx > 0 : edge.offsetRatio > 0;
357
+ const startStop = usePx ? isBottomProtrusion ? `${Math.max(0, startPx - overlapPx)}px` : `${startPx}px` : isBottomProtrusion ? `calc(${startPct}% - ${overlapPx}px)` : `${startPct}%`;
358
+ const endStop = usePx ? isTopProtrusion ? `${endPx + overlapPx}px` : `${endPx}px` : isTopProtrusion ? `calc(${endPct}% + ${overlapPx}px)` : `${endPct}%`;
292
359
  const xPos = edge.side === "left" ? "0" : "100%";
293
360
  const layers = [
294
361
  {
@@ -298,7 +365,7 @@ function buildPartialVerticalGradient(edge) {
298
365
  }
299
366
  ];
300
367
  if (isTopProtrusion || isBottomProtrusion) {
301
- const capTop = isTopProtrusion ? `calc(${endPct}% - ${SELECTION_EDGE_WIDTH_PX}px)` : `calc(${startPct}% - ${SELECTION_EDGE_WIDTH_PX}px)`;
368
+ const capTop = usePx ? isTopProtrusion ? `${Math.max(0, endPx - SELECTION_EDGE_WIDTH_PX)}px` : `${Math.max(0, startPx - SELECTION_EDGE_WIDTH_PX)}px` : isTopProtrusion ? `calc(${endPct}% - ${SELECTION_EDGE_WIDTH_PX}px)` : `calc(${startPct}% - ${SELECTION_EDGE_WIDTH_PX}px)`;
302
369
  layers.push({
303
370
  image: `linear-gradient(${SELECTION_EDGE_COLOR}, ${SELECTION_EDGE_COLOR})`,
304
371
  size: `${SELECTION_EDGE_WIDTH_PX}px ${SELECTION_EDGE_WIDTH_PX}px`,
@@ -307,7 +374,7 @@ function buildPartialVerticalGradient(edge) {
307
374
  }
308
375
  return layers;
309
376
  }
310
- function getCellSelectionEdgeStyle(rowIndex, colIndex, bounds, rowSpan = 1, isVisuallySelectedAt) {
377
+ function getCellSelectionEdgeStyle(rowIndex, colIndex, bounds, rowSpan = 1, isVisuallySelectedAt, rowHeights) {
311
378
  if (!isCellInSelection(rowIndex, colIndex, bounds, rowSpan) || !bounds)
312
379
  return void 0;
313
380
  const cellEndRow = rowIndex + rowSpan - 1;
@@ -316,39 +383,47 @@ function getCellSelectionEdgeStyle(rowIndex, colIndex, bounds, rowSpan = 1, isVi
316
383
  const isLeftEdge = colIndex === bounds.startCol;
317
384
  const isRightEdge = colIndex === bounds.endCol;
318
385
  const selectionContinuesBelow = cellEndRow < bounds.endRow;
319
- const shadows = [];
320
- if (isTopEdge) {
321
- shadows.push(
322
- `inset 0 ${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`
323
- );
324
- }
325
- if (isBottomEdge) {
326
- shadows.push(
327
- `inset 0 -${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`
328
- );
329
- }
330
- if (isLeftEdge) {
331
- shadows.push(
332
- `inset ${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`
333
- );
334
- }
335
- if (isRightEdge) {
336
- shadows.push(
337
- `inset -${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`
338
- );
339
- }
340
386
  const stepEdges = getMergedCellStepEdges(
341
387
  rowIndex,
342
388
  colIndex,
343
389
  bounds,
344
390
  rowSpan,
345
- isVisuallySelectedAt
391
+ isVisuallySelectedAt,
392
+ rowHeights
346
393
  );
394
+ const shadows = [];
395
+ const hasFullPerimeter = isTopEdge && isBottomEdge && isLeftEdge && isRightEdge && stepEdges.length === 0;
396
+ if (hasFullPerimeter) {
397
+ shadows.push(
398
+ `inset 0 0 0 ${SELECTION_EDGE_WIDTH_PX}px ${SELECTION_EDGE_COLOR}`
399
+ );
400
+ } else {
401
+ if (isTopEdge) {
402
+ shadows.push(
403
+ `inset 0 ${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`
404
+ );
405
+ }
406
+ if (isBottomEdge) {
407
+ shadows.push(
408
+ `inset 0 -${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`
409
+ );
410
+ }
411
+ if (isLeftEdge) {
412
+ shadows.push(
413
+ `inset ${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`
414
+ );
415
+ }
416
+ if (isRightEdge) {
417
+ shadows.push(
418
+ `inset -${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`
419
+ );
420
+ }
421
+ }
347
422
  const gradients = [];
348
423
  const sizes = [];
349
424
  const positions = [];
350
425
  for (const edge of stepEdges) {
351
- for (const partial of buildPartialVerticalGradient(edge)) {
426
+ for (const partial of buildPartialEdgeGradient(edge)) {
352
427
  gradients.push(partial.image);
353
428
  sizes.push(partial.size);
354
429
  positions.push(partial.position);
@@ -377,6 +452,127 @@ function hasCellSelectionEdges(style) {
377
452
  );
378
453
  }
379
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
+
380
576
  // src/components/ui/table/features/cell-selection/fillData.ts
381
577
  function getColumnAccessorKey2(columnDef) {
382
578
  if ("accessorKey" in columnDef && columnDef.accessorKey) {
@@ -433,15 +629,103 @@ function hasFillExtension(sourceBounds, fillBounds) {
433
629
  return fillBounds.startRow < sourceBounds.startRow || fillBounds.endRow > sourceBounds.endRow || fillBounds.startCol < sourceBounds.startCol || fillBounds.endCol > sourceBounds.endCol;
434
630
  }
435
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
+
436
716
  // src/components/ui/table/features/cell-selection/useCellSelection.ts
437
717
  function useCellSelection({
438
718
  data,
439
719
  rows,
440
720
  enabled = true,
721
+ enableSubtreeCopy = false,
722
+ enableInsertPaste = true,
441
723
  onDataChange,
442
- onBatchChange
724
+ onBatchChange,
725
+ onRowsPaste
443
726
  }) {
444
727
  const [dragState, setDragState] = useState2(INITIAL_DRAG_STATE);
728
+ const pendingPasteModeRef = useRef2(null);
445
729
  const cellSelectionBounds = getCellSelectionBounds(dragState.start, dragState.end);
446
730
  const activeSelectionBounds = enabled ? getActiveSelectionBounds(dragState, cellSelectionBounds) : null;
447
731
  const handleCellMouseDown = useCallback2(
@@ -495,21 +779,113 @@ function useCellSelection({
495
779
  setDragState(INITIAL_DRAG_STATE);
496
780
  }
497
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
+ );
498
790
  useEffect2(() => {
499
791
  if (!enabled) return;
500
792
  const handleKeyDown = (e) => {
501
- if ((e.ctrlKey || e.metaKey) && e.key === "c" && activeSelectionBounds) {
502
- const { startRow, endRow, startCol, endCol } = activeSelectionBounds;
503
- const selectedData = rows.slice(startRow, endRow + 1).map((row) => {
504
- const cells = row.getVisibleCells();
505
- return cells.slice(startCol, endCol + 1).map((cell) => cell.getValue()).join(" ");
506
- }).join("\n");
507
- navigator.clipboard.writeText(selectedData);
508
- }
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 });
509
800
  };
510
801
  window.addEventListener("keydown", handleKeyDown);
511
802
  return () => window.removeEventListener("keydown", handleKeyDown);
512
- }, [activeSelectionBounds, enabled, rows]);
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
+ ]);
513
889
  useEffect2(() => {
514
890
  if (!enabled) return;
515
891
  const handleMouseUp = () => {
@@ -555,12 +931,13 @@ function useCellSelection({
555
931
  activeSelectionBounds,
556
932
  handleCellMouseDown,
557
933
  handleCellMouseEnter,
558
- handleFillHandleMouseDown
934
+ handleFillHandleMouseDown,
935
+ copySelection
559
936
  };
560
937
  }
561
938
 
562
939
  // src/components/ui/table/features/row-expand/row-expand.ts
563
- import { useEffect as useEffect3, useMemo, useRef as useRef2 } from "react";
940
+ import { useEffect as useEffect3, useMemo, useRef as useRef3 } from "react";
564
941
  function getFieldValue(row, key) {
565
942
  return row[key];
566
943
  }
@@ -590,8 +967,8 @@ var useConvertTreeData = ({
590
967
  expandedRows,
591
968
  onExpandedRowsChange
592
969
  }) => {
593
- const onExpandedRowsChangeRef = useRef2(onExpandedRowsChange);
594
- const hasInitializedRef = useRef2(false);
970
+ const onExpandedRowsChangeRef = useRef3(onExpandedRowsChange);
971
+ const hasInitializedRef = useRef3(false);
595
972
  useEffect3(() => {
596
973
  onExpandedRowsChangeRef.current = onExpandedRowsChange;
597
974
  }, [onExpandedRowsChange]);
@@ -637,15 +1014,16 @@ var useConvertTreeData = ({
637
1014
  children: [],
638
1015
  processed: false
639
1016
  }));
640
- const itemMap = /* @__PURE__ */ new Map();
641
- dataWithLevels.forEach((item) => {
642
- const key = getFieldValue(item, toggleField);
643
- if (typeof key !== "string" || !key) return;
644
- if (!itemMap.has(key)) {
645
- itemMap.set(key, []);
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
+ }
646
1024
  }
647
- itemMap.get(key)?.push(item);
648
- });
1025
+ return void 0;
1026
+ };
649
1027
  const rootItems = [];
650
1028
  dataWithLevels.forEach((item) => {
651
1029
  if (!getFieldValue(item, childField)) {
@@ -653,29 +1031,18 @@ var useConvertTreeData = ({
653
1031
  item.processed = true;
654
1032
  }
655
1033
  });
656
- dataWithLevels.forEach((item) => {
1034
+ dataWithLevels.forEach((item, index) => {
657
1035
  const parentKey = getFieldValue(item, childField);
658
1036
  if (!parentKey || item.processed) return;
659
- const parentItems = dataWithLevels.filter(
660
- (parent) => getFieldValue(parent, toggleField) === parentKey && !getFieldValue(parent, childField)
661
- );
662
- if (parentItems.length > 0) {
663
- const parent = parentItems[0];
1037
+ const parent = findNearestPrecedingParent(index, parentKey);
1038
+ if (parent) {
664
1039
  item.level = parent.level + 1;
665
1040
  parent.children.push(item);
666
1041
  item.processed = true;
667
- } else {
668
- const otherParents = itemMap.get(String(parentKey)) || [];
669
- if (otherParents.length > 0) {
670
- const parent = otherParents[0];
671
- item.level = parent.level + 1;
672
- parent.children.push(item);
673
- item.processed = true;
674
- } else {
675
- rootItems.push(item);
676
- item.processed = true;
677
- }
1042
+ return;
678
1043
  }
1044
+ rootItems.push(item);
1045
+ item.processed = true;
679
1046
  });
680
1047
  return rootItems;
681
1048
  }, [enabled, data, toggleField, childField, flattenField]);
@@ -700,16 +1067,23 @@ var useConvertTreeData = ({
700
1067
  return result;
701
1068
  };
702
1069
  const flattenedData = flatten(processedData, [], 0);
703
- flattenedData.forEach((item) => {
704
- if (getFieldValue(item, childField)) {
705
- const parentItem = flattenedData.find(
706
- (parent) => getFieldValue(parent, toggleField) === getFieldValue(item, childField)
707
- );
708
- const parentAmount = parentItem ? Number(getFieldValue(parentItem, qtyField) ?? 1) : 1;
709
- item.parentCount = parentAmount || 1;
710
- } else {
1070
+ flattenedData.forEach((item, index) => {
1071
+ const parentKey = getFieldValue(item, childField);
1072
+ if (!parentKey) {
711
1073
  item.parentCount = 1;
1074
+ return;
712
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;
713
1087
  });
714
1088
  return flattenedData;
715
1089
  }, [
@@ -856,6 +1230,10 @@ function useGlideTable(options) {
856
1230
  expandedRows: controlledExpandedRows,
857
1231
  onExpandedRowsChange,
858
1232
  preventExpand = false,
1233
+ enableSubtreeCopy,
1234
+ onCopyActionsReady,
1235
+ onRowsPaste,
1236
+ enableInsertPaste,
859
1237
  enableVirtualization = true,
860
1238
  estimateRowHeight = DATA_TABLE_ROW_HEIGHT,
861
1239
  virtualOverscan = DATA_TABLE_VIRTUAL_OVERSCAN
@@ -870,13 +1248,13 @@ function useGlideTable(options) {
870
1248
  };
871
1249
  }, [labelsProp, emptyText, loadingText, selectionLabel]);
872
1250
  const enableExpand = Boolean(toggleField);
1251
+ const resolvedEnableSubtreeCopy = enableSubtreeCopy ?? enableExpand;
873
1252
  const [internalRowSelection, setInternalRowSelection] = useState3({});
874
1253
  const [internalExpandedRows, setInternalExpandedRows] = useState3(
875
1254
  () => /* @__PURE__ */ new Set()
876
1255
  );
877
1256
  const [hoveredRowIndex, setHoveredRowIndex] = useState3(null);
878
- const [hoveredGroupKey, setHoveredGroupKey] = useState3(null);
879
- const scrollRef = useRef3(null);
1257
+ const scrollRef = useRef4(null);
880
1258
  const shouldVirtualize = enableVirtualization && !enableRowSpan;
881
1259
  useEffect4(() => {
882
1260
  if (enableVirtualization && enableRowSpan) {
@@ -939,6 +1317,7 @@ function useGlideTable(options) {
939
1317
  return collectRowSpanColumns(columns);
940
1318
  }, [enableRowSpan, columns]);
941
1319
  const primaryRowSpanKey = rowSpanColumnKeys[0]?.rowSpanKey;
1320
+ const primaryRowSpanColumnId = rowSpanColumnKeys[0]?.columnId;
942
1321
  const columnRowSpanMap = useMemo2(
943
1322
  () => buildColumnRowSpanMap(tableData, rowSpanColumnKeys),
944
1323
  [tableData, rowSpanColumnKeys]
@@ -957,27 +1336,29 @@ function useGlideTable(options) {
957
1336
  const totalSize = rowVirtualizer.getTotalSize();
958
1337
  const paddingTop = virtualRows.length > 0 ? virtualRows[0]?.start ?? 0 : 0;
959
1338
  const paddingBottom = virtualRows.length > 0 ? totalSize - (virtualRows[virtualRows.length - 1]?.end ?? 0) : 0;
960
- const selectedGroupKeys = useMemo2(() => {
961
- if (!enableRowSpan || !primaryRowSpanKey) return /* @__PURE__ */ new Set();
962
- const keys = /* @__PURE__ */ new Set();
1339
+ const selectedRowIndices = useMemo2(() => {
1340
+ const indices = /* @__PURE__ */ new Set();
963
1341
  for (const selectedRow of selectedRows) {
964
- const value = selectedRow.original[primaryRowSpanKey];
965
- if (value !== null && value !== void 0) keys.add(String(value));
1342
+ indices.add(selectedRow.index);
966
1343
  }
967
- return keys;
968
- }, [enableRowSpan, primaryRowSpanKey, selectedRows]);
1344
+ return indices;
1345
+ }, [selectedRows]);
969
1346
  const {
970
1347
  dragState,
971
1348
  activeSelectionBounds,
972
1349
  handleCellMouseDown,
973
1350
  handleCellMouseEnter,
974
- handleFillHandleMouseDown
1351
+ handleFillHandleMouseDown,
1352
+ copySelection
975
1353
  } = useCellSelection({
976
1354
  data: tableData,
977
1355
  rows,
978
1356
  enabled: enableCellSelection,
1357
+ enableSubtreeCopy: resolvedEnableSubtreeCopy,
1358
+ enableInsertPaste: enableInsertPaste ?? true,
979
1359
  onDataChange,
980
- onBatchChange
1360
+ onBatchChange,
1361
+ onRowsPaste
981
1362
  });
982
1363
  const {
983
1364
  editingCell,
@@ -999,22 +1380,10 @@ function useGlideTable(options) {
999
1380
  );
1000
1381
  const clearHover = useCallback3(() => {
1001
1382
  setHoveredRowIndex(null);
1002
- setHoveredGroupKey(null);
1003
1383
  }, []);
1004
- const handleRowHover = useCallback3(
1005
- (rowIndex, rowData) => {
1006
- setHoveredRowIndex(rowIndex);
1007
- if (!primaryRowSpanKey) {
1008
- setHoveredGroupKey(null);
1009
- return;
1010
- }
1011
- const groupValue = rowData[primaryRowSpanKey];
1012
- setHoveredGroupKey(
1013
- groupValue === null || groupValue === void 0 ? null : String(groupValue)
1014
- );
1015
- },
1016
- [primaryRowSpanKey]
1017
- );
1384
+ const handleRowHover = useCallback3((rowIndex, _rowData) => {
1385
+ setHoveredRowIndex(rowIndex);
1386
+ }, []);
1018
1387
  const handleToggleSelect = useCallback3(
1019
1388
  (row) => {
1020
1389
  if (!row.getCanSelect()) return;
@@ -1037,10 +1406,10 @@ function useGlideTable(options) {
1037
1406
  rowSpan: {
1038
1407
  enableRowSpan,
1039
1408
  primaryRowSpanKey,
1409
+ primaryRowSpanColumnId,
1040
1410
  columnRowSpanMap,
1041
1411
  hoveredRowIndex,
1042
- hoveredGroupKey,
1043
- selectedGroupKeys,
1412
+ selectedRowIndices,
1044
1413
  onRowHover: handleRowHover
1045
1414
  },
1046
1415
  selection: {
@@ -1078,10 +1447,10 @@ function useGlideTable(options) {
1078
1447
  }, [
1079
1448
  enableRowSpan,
1080
1449
  primaryRowSpanKey,
1450
+ primaryRowSpanColumnId,
1081
1451
  columnRowSpanMap,
1082
1452
  hoveredRowIndex,
1083
- hoveredGroupKey,
1084
- selectedGroupKeys,
1453
+ selectedRowIndices,
1085
1454
  handleRowHover,
1086
1455
  rowSelectionMode,
1087
1456
  selectOnRowClick,
@@ -1107,6 +1476,14 @@ function useGlideTable(options) {
1107
1476
  labels.expandRow,
1108
1477
  labels.collapseRow
1109
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]);
1110
1487
  return {
1111
1488
  table,
1112
1489
  tableData,
@@ -1126,7 +1503,8 @@ function useGlideTable(options) {
1126
1503
  paddingBottom,
1127
1504
  rowContextValue,
1128
1505
  handleToggleSelect,
1129
- clearHover
1506
+ clearHover,
1507
+ copySelection: stableCopySelection
1130
1508
  };
1131
1509
  }
1132
1510
 
@@ -1136,7 +1514,7 @@ import { useMemo as useMemo3 } from "react";
1136
1514
 
1137
1515
  // src/components/ui/table/components/DataTable/DataTableRow.tsx
1138
1516
  import { flexRender } from "@tanstack/react-table";
1139
- import { useEffect as useEffect5, useRef as useRef4 } from "react";
1517
+ import { useEffect as useEffect5, useRef as useRef5 } from "react";
1140
1518
 
1141
1519
  // src/components/ui/table/DataTableContext.tsx
1142
1520
  import { createContext, use } from "react";
@@ -1326,11 +1704,10 @@ function DataTableRow({
1326
1704
  const { classNames, rowSpan, selection, cellSelection, cellEdit, expand } = useDataTableRowContext();
1327
1705
  const {
1328
1706
  enableRowSpan,
1329
- primaryRowSpanKey,
1707
+ primaryRowSpanColumnId,
1330
1708
  columnRowSpanMap,
1331
1709
  hoveredRowIndex,
1332
- hoveredGroupKey,
1333
- selectedGroupKeys,
1710
+ selectedRowIndices,
1334
1711
  onRowHover
1335
1712
  } = rowSpan;
1336
1713
  const { rowSelectionMode, selectOnRowClick, onRowClick, getRowClassName } = selection;
@@ -1363,9 +1740,11 @@ function DataTableRow({
1363
1740
  const rowData = row.original;
1364
1741
  const isRowHovered = hoveredRowIndex === rowIndex;
1365
1742
  const isRowSelected = row.getIsSelected();
1366
- const rowGroupKey = primaryRowSpanKey !== void 0 && rowData[primaryRowSpanKey] !== null && rowData[primaryRowSpanKey] !== void 0 ? String(rowData[primaryRowSpanKey]) : null;
1367
- const isGroupHovered = enableRowSpan && hoveredGroupKey !== null && rowGroupKey === hoveredGroupKey;
1368
- const isGroupSelected = enableRowSpan && rowGroupKey !== null && selectedGroupKeys.has(rowGroupKey);
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;
1369
1748
  const visibleCells = row.getVisibleCells();
1370
1749
  const columnIdsByIndex = visibleCells.map((cell) => cell.column.id);
1371
1750
  const isVisuallySelectedAt = activeSelectionBounds ? (targetRow, targetCol) => {
@@ -1389,7 +1768,7 @@ function DataTableRow({
1389
1768
  const expandKey = toggleField && rowData[toggleField] !== null && rowData[toggleField] !== void 0 ? String(rowData[toggleField]) : null;
1390
1769
  const isExpanded = expandKey !== null && Boolean(expandedRows?.has(expandKey));
1391
1770
  const rowLevel = typeof rowData.level === "number" ? rowData.level : 0;
1392
- const editInputRef = useRef4(null);
1771
+ const editInputRef = useRef5(null);
1393
1772
  const isRowEditing = editingCell?.rowIndex === rowIndex;
1394
1773
  useEffect5(() => {
1395
1774
  if (!isRowEditing) return;
@@ -1439,9 +1818,19 @@ function DataTableRow({
1439
1818
  }
1440
1819
  }
1441
1820
  const isEditing = editingCell?.rowIndex === rowIndex && editingCell?.colIndex === cellIndex;
1442
- const showCellHover = isRowSpanColumn ? isGroupHovered : isRowHovered;
1443
- const showCellSelected = isRowSpanColumn ? isGroupSelected : isRowSelected;
1444
1821
  const cellRowSpan = rowSpanInfo?.rowSpan ?? 1;
1822
+ const isMergedCellHovered = hoveredRowIndex !== null && hoveredRowIndex >= rowIndex && hoveredRowIndex <= rowIndex + cellRowSpan - 1;
1823
+ const showCellHover = isRowSpanColumn ? isMergedCellHovered : isRowHovered;
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;
1445
1834
  const isMerged = cellRowSpan > 1;
1446
1835
  const showMergedRightEdge = isMerged && (cellIndex === visibleCells.length - 1 || resolveRowSpanAt(
1447
1836
  columnRowSpanMap.get(columnIdsByIndex[cellIndex + 1]),
@@ -1454,12 +1843,14 @@ function DataTableRow({
1454
1843
  cellRowSpan
1455
1844
  );
1456
1845
  const isBottomRightCell = !isEditing && activeSelectionBounds && !dragState.isSelecting && activeSelectionBounds.endRow >= rowIndex && activeSelectionBounds.endRow <= rowIndex + cellRowSpan - 1 && cellIndex === activeSelectionBounds.endCol;
1846
+ const spanRowHeights = enableCellSelection && activeSelectionBounds && isCellDragSelected && cellRowSpan > 1 ? measureMergedSpanRowHeights(rowIndex, cellRowSpan) : void 0;
1457
1847
  const selectionEdgeStyle = getCellSelectionEdgeStyle(
1458
1848
  rowIndex,
1459
1849
  cellIndex,
1460
1850
  activeSelectionBounds,
1461
1851
  cellRowSpan,
1462
- isVisuallySelectedAt
1852
+ isVisuallySelectedAt,
1853
+ spanRowHeights
1463
1854
  );
1464
1855
  const resolveCellRowIndex = (clientY, element) => getRowIndexInMergedCell(clientY, element, rowIndex, cellRowSpan);
1465
1856
  const hasSelectionEdges = hasCellSelectionEdges(selectionEdgeStyle);
@@ -1469,6 +1860,7 @@ function DataTableRow({
1469
1860
  rowSpan: rowSpanInfo && rowSpanInfo.rowSpan > 1 ? rowSpanInfo.rowSpan : void 0,
1470
1861
  "data-merged": isMerged && cellIndex > 0 ? "" : void 0,
1471
1862
  "data-merged-edge-right": showMergedRightEdge ? "" : void 0,
1863
+ "data-merged-row-first": isMerged && cellIndex === 0 && showMergedRightEdge ? "" : void 0,
1472
1864
  "data-group-selected": enableRowSpan && showCellSelected ? "" : void 0,
1473
1865
  "data-group-hovered": enableRowSpan && showCellHover && !showCellSelected ? "" : void 0,
1474
1866
  "data-selection-fill": isCellDragSelected ? "" : void 0,
@@ -1513,7 +1905,8 @@ function DataTableRow({
1513
1905
  "data-table-cell",
1514
1906
  CELL_ALIGN_CLASS[align],
1515
1907
  cellClassName,
1516
- isMerged && cellIndex > 0 && "is-merged",
1908
+ isMerged && "is-merged",
1909
+ isMerged && cellIndex === 0 && showMergedRightEdge && "is-merged-row-first",
1517
1910
  showMergedRightEdge && "is-merged-edge-right",
1518
1911
  enableRowSpan && showCellSelected && "is-group-selected",
1519
1912
  enableRowSpan && showCellHover && !showCellSelected && "is-group-hovered",
@@ -1970,7 +2363,7 @@ function buildColumnDef(props, sort, onSort) {
1970
2363
  }
1971
2364
 
1972
2365
  // src/components/ui/table/components/Table/parseTableChildren.ts
1973
- import { Children } from "react";
2366
+ import { Children, isValidElement as isValidElement2 } from "react";
1974
2367
 
1975
2368
  // src/components/ui/table/components/Table/tableChildTypes.ts
1976
2369
  import { isValidElement } from "react";
@@ -2019,10 +2412,26 @@ function parseTableChildren(children) {
2019
2412
  }
2020
2413
  return slots;
2021
2414
  }
2415
+ function flattenColumnElements(children) {
2416
+ const result = [];
2417
+ for (const child of Children.toArray(children)) {
2418
+ if (isTableColumnElement(child)) {
2419
+ result.push(child);
2420
+ continue;
2421
+ }
2422
+ if (isValidElement2(child)) {
2423
+ const nested = child.props.children;
2424
+ if (nested != null) {
2425
+ result.push(...flattenColumnElements(nested));
2426
+ }
2427
+ }
2428
+ }
2429
+ return result;
2430
+ }
2022
2431
  function extractColumnElements(header) {
2023
2432
  if (!header) return [];
2024
2433
  const { children } = header.props;
2025
- return Children.toArray(children).filter(isTableColumnElement);
2434
+ return flattenColumnElements(children);
2026
2435
  }
2027
2436
 
2028
2437
  // src/components/ui/table/components/Table/TableBody.tsx
@@ -2223,10 +2632,14 @@ export {
2223
2632
  applyFillData,
2224
2633
  applySelectionUpdater,
2225
2634
  buildColumnRowSpanMap,
2635
+ buildRowsPastePayload,
2226
2636
  canExpandRow,
2637
+ collectCopyRowEntries,
2638
+ collectCopyRows,
2227
2639
  collectFillChanges,
2228
2640
  collectRowSpanColumns,
2229
2641
  createTable,
2642
+ flattenSubtreeRows,
2230
2643
  getCellEditDraftValue,
2231
2644
  getCellSelectionEdgeStyle,
2232
2645
  getColumnEditType,
@@ -2234,13 +2647,22 @@ export {
2234
2647
  hasCellSelectionEdges,
2235
2648
  isCellInSelection,
2236
2649
  isColumnEditable,
2650
+ isEditablePasteTarget,
2651
+ measureMergedSpanRowHeights,
2237
2652
  parseCellEditValue,
2653
+ parseClipboardTSV,
2654
+ parseClipboardTSVWithDepths,
2238
2655
  resolveDataTableLabels,
2656
+ resolvePasteColumnIds,
2239
2657
  resolveRowSelection,
2240
2658
  resolveRowSpanAt,
2659
+ rowRangeToHeightRatios,
2660
+ serializeCopyRowsToTSV,
2661
+ serializeSelectionToTSV,
2241
2662
  toggleExpandedRowId,
2242
2663
  useCellEdit,
2243
2664
  useCellSelection,
2244
2665
  useConvertTreeData,
2245
- useGlideTable
2666
+ useGlideTable,
2667
+ writeSelectionToClipboard
2246
2668
  };