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.cjs CHANGED
@@ -32,10 +32,14 @@ __export(src_exports, {
32
32
  applyFillData: () => applyFillData,
33
33
  applySelectionUpdater: () => applySelectionUpdater,
34
34
  buildColumnRowSpanMap: () => buildColumnRowSpanMap,
35
+ buildRowsPastePayload: () => buildRowsPastePayload,
35
36
  canExpandRow: () => canExpandRow,
37
+ collectCopyRowEntries: () => collectCopyRowEntries,
38
+ collectCopyRows: () => collectCopyRows,
36
39
  collectFillChanges: () => collectFillChanges,
37
40
  collectRowSpanColumns: () => collectRowSpanColumns,
38
41
  createTable: () => createTable,
42
+ flattenSubtreeRows: () => flattenSubtreeRows,
39
43
  getCellEditDraftValue: () => getCellEditDraftValue,
40
44
  getCellSelectionEdgeStyle: () => getCellSelectionEdgeStyle,
41
45
  getColumnEditType: () => getColumnEditType,
@@ -43,15 +47,24 @@ __export(src_exports, {
43
47
  hasCellSelectionEdges: () => hasCellSelectionEdges,
44
48
  isCellInSelection: () => isCellInSelection,
45
49
  isColumnEditable: () => isColumnEditable,
50
+ isEditablePasteTarget: () => isEditablePasteTarget,
51
+ measureMergedSpanRowHeights: () => measureMergedSpanRowHeights,
46
52
  parseCellEditValue: () => parseCellEditValue,
53
+ parseClipboardTSV: () => parseClipboardTSV,
54
+ parseClipboardTSVWithDepths: () => parseClipboardTSVWithDepths,
47
55
  resolveDataTableLabels: () => resolveDataTableLabels,
56
+ resolvePasteColumnIds: () => resolvePasteColumnIds,
48
57
  resolveRowSelection: () => resolveRowSelection,
49
58
  resolveRowSpanAt: () => resolveRowSpanAt,
59
+ rowRangeToHeightRatios: () => rowRangeToHeightRatios,
60
+ serializeCopyRowsToTSV: () => serializeCopyRowsToTSV,
61
+ serializeSelectionToTSV: () => serializeSelectionToTSV,
50
62
  toggleExpandedRowId: () => toggleExpandedRowId,
51
63
  useCellEdit: () => useCellEdit,
52
64
  useCellSelection: () => useCellSelection,
53
65
  useConvertTreeData: () => useConvertTreeData,
54
- useGlideTable: () => useGlideTable
66
+ useGlideTable: () => useGlideTable,
67
+ writeSelectionToClipboard: () => writeSelectionToClipboard
55
68
  });
56
69
  module.exports = __toCommonJS(src_exports);
57
70
 
@@ -241,10 +254,36 @@ function getCellSelectionBounds(start, end) {
241
254
  endCol: Math.max(start.col, end.col)
242
255
  };
243
256
  }
257
+ function measureMergedSpanRowHeights(rowIndex, rowSpan, cellElement) {
258
+ if (rowSpan <= 1) return void 0;
259
+ const tbody = cellElement?.closest("tbody") ?? (typeof document !== "undefined" ? document.querySelector("tbody.data-table-body") : null);
260
+ if (!tbody) return void 0;
261
+ const rows = tbody.querySelectorAll(":scope > tr");
262
+ if (rows.length < rowIndex + rowSpan) return void 0;
263
+ const heights = [];
264
+ for (let i = 0; i < rowSpan; i++) {
265
+ const row = rows[rowIndex + i];
266
+ const height = row?.getBoundingClientRect().height ?? 0;
267
+ if (height <= 0) return void 0;
268
+ heights.push(height);
269
+ }
270
+ return heights;
271
+ }
244
272
  function getRowIndexInMergedCell(clientY, cellElement, rowIndex, rowSpan) {
245
273
  if (rowSpan <= 1) return rowIndex;
246
274
  const rect = cellElement.getBoundingClientRect();
247
275
  const relativeY = clientY - rect.top;
276
+ const heights = measureMergedSpanRowHeights(rowIndex, rowSpan, cellElement);
277
+ if (heights && heights.length === rowSpan) {
278
+ let accrued = 0;
279
+ for (let i = 0; i < rowSpan; i++) {
280
+ accrued += heights[i];
281
+ if (relativeY < accrued) {
282
+ return rowIndex + i;
283
+ }
284
+ }
285
+ return rowIndex + rowSpan - 1;
286
+ }
248
287
  const rowHeight = rect.height / rowSpan;
249
288
  const offset = Math.min(
250
289
  Math.max(Math.floor(relativeY / rowHeight), 0),
@@ -266,7 +305,35 @@ function getActiveSelectionBounds(dragState, selectionBounds) {
266
305
  var SELECTION_EDGE_WIDTH_PX = 2;
267
306
  var SELECTION_EDGE_COLOR = "var(--color-brand-primary)";
268
307
  var CELL_SELECTION_EDGES_CLASS = "cell-selection-edges";
269
- function getMergedCellStepEdges(rowIndex, colIndex, bounds, rowSpan, isVisuallySelectedAt) {
308
+ function rowRangeToHeightRatios(rowIndex, span, fromRow, toRowExclusive, rowHeights) {
309
+ const clampedFrom = Math.max(fromRow, rowIndex);
310
+ const clampedTo = Math.min(toRowExclusive, rowIndex + span);
311
+ if (clampedTo <= clampedFrom) {
312
+ return { offsetRatio: 0, lengthRatio: 0 };
313
+ }
314
+ if (!rowHeights || rowHeights.length !== span) {
315
+ return {
316
+ offsetRatio: (clampedFrom - rowIndex) / span,
317
+ lengthRatio: (clampedTo - clampedFrom) / span
318
+ };
319
+ }
320
+ const total = rowHeights.reduce((sum, height) => sum + height, 0) || 1;
321
+ let offsetPx = 0;
322
+ for (let i = 0; i < clampedFrom - rowIndex; i++) {
323
+ offsetPx += rowHeights[i] ?? 0;
324
+ }
325
+ let lengthPx = 0;
326
+ for (let i = clampedFrom - rowIndex; i < clampedTo - rowIndex; i++) {
327
+ lengthPx += rowHeights[i] ?? 0;
328
+ }
329
+ return {
330
+ offsetRatio: offsetPx / total,
331
+ lengthRatio: lengthPx / total,
332
+ offsetPx,
333
+ lengthPx
334
+ };
335
+ }
336
+ function getMergedCellStepEdges(rowIndex, colIndex, bounds, rowSpan, isVisuallySelectedAt, rowHeights) {
270
337
  const cellEndRow = rowIndex + rowSpan - 1;
271
338
  const span = cellEndRow - rowIndex + 1;
272
339
  if (span <= 1) return [];
@@ -285,20 +352,30 @@ function getMergedCellStepEdges(rowIndex, colIndex, bounds, rowSpan, isVisuallyS
285
352
  continue;
286
353
  }
287
354
  if (runStart !== null) {
288
- edges.push({
289
- side,
290
- offsetRatio: (runStart - rowIndex) / span,
291
- heightRatio: (row - runStart) / span
292
- });
355
+ const ratios = rowRangeToHeightRatios(
356
+ rowIndex,
357
+ span,
358
+ runStart,
359
+ row,
360
+ rowHeights
361
+ );
362
+ if (ratios.lengthRatio > 0) {
363
+ edges.push({ side, ...ratios });
364
+ }
293
365
  runStart = null;
294
366
  }
295
367
  }
296
368
  if (runStart !== null) {
297
- edges.push({
298
- side,
299
- offsetRatio: (runStart - rowIndex) / span,
300
- heightRatio: (toRowExclusive - runStart) / span
301
- });
369
+ const ratios = rowRangeToHeightRatios(
370
+ rowIndex,
371
+ span,
372
+ runStart,
373
+ toRowExclusive,
374
+ rowHeights
375
+ );
376
+ if (ratios.lengthRatio > 0) {
377
+ edges.push({ side, ...ratios });
378
+ }
302
379
  }
303
380
  };
304
381
  const collectSide = (side, neighborCol) => {
@@ -327,14 +404,17 @@ function getMergedCellStepEdges(rowIndex, colIndex, bounds, rowSpan, isVisuallyS
327
404
  }
328
405
  return edges;
329
406
  }
330
- function buildPartialVerticalGradient(edge) {
407
+ function buildPartialEdgeGradient(edge) {
408
+ const usePx = edge.offsetPx != null && edge.lengthPx != null;
331
409
  const startPct = edge.offsetRatio * 100;
332
- const endPct = (edge.offsetRatio + edge.heightRatio) * 100;
333
- const overlapPx = SELECTION_EDGE_WIDTH_PX + 1;
334
- const isTopProtrusion = edge.offsetRatio === 0 && edge.heightRatio < 1;
335
- const isBottomProtrusion = edge.offsetRatio > 0;
336
- const startStop = isBottomProtrusion ? `calc(${startPct}% - ${overlapPx}px)` : `${startPct}%`;
337
- const endStop = isTopProtrusion ? `calc(${endPct}% + ${overlapPx}px)` : `${endPct}%`;
410
+ const endPct = (edge.offsetRatio + edge.lengthRatio) * 100;
411
+ const startPx = edge.offsetPx ?? 0;
412
+ const endPx = (edge.offsetPx ?? 0) + (edge.lengthPx ?? 0);
413
+ const overlapPx = SELECTION_EDGE_WIDTH_PX;
414
+ const isTopProtrusion = (usePx ? startPx === 0 : edge.offsetRatio === 0) && edge.lengthRatio < 1;
415
+ const isBottomProtrusion = usePx ? startPx > 0 : edge.offsetRatio > 0;
416
+ const startStop = usePx ? isBottomProtrusion ? `${Math.max(0, startPx - overlapPx)}px` : `${startPx}px` : isBottomProtrusion ? `calc(${startPct}% - ${overlapPx}px)` : `${startPct}%`;
417
+ const endStop = usePx ? isTopProtrusion ? `${endPx + overlapPx}px` : `${endPx}px` : isTopProtrusion ? `calc(${endPct}% + ${overlapPx}px)` : `${endPct}%`;
338
418
  const xPos = edge.side === "left" ? "0" : "100%";
339
419
  const layers = [
340
420
  {
@@ -344,7 +424,7 @@ function buildPartialVerticalGradient(edge) {
344
424
  }
345
425
  ];
346
426
  if (isTopProtrusion || isBottomProtrusion) {
347
- const capTop = isTopProtrusion ? `calc(${endPct}% - ${SELECTION_EDGE_WIDTH_PX}px)` : `calc(${startPct}% - ${SELECTION_EDGE_WIDTH_PX}px)`;
427
+ 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)`;
348
428
  layers.push({
349
429
  image: `linear-gradient(${SELECTION_EDGE_COLOR}, ${SELECTION_EDGE_COLOR})`,
350
430
  size: `${SELECTION_EDGE_WIDTH_PX}px ${SELECTION_EDGE_WIDTH_PX}px`,
@@ -353,7 +433,7 @@ function buildPartialVerticalGradient(edge) {
353
433
  }
354
434
  return layers;
355
435
  }
356
- function getCellSelectionEdgeStyle(rowIndex, colIndex, bounds, rowSpan = 1, isVisuallySelectedAt) {
436
+ function getCellSelectionEdgeStyle(rowIndex, colIndex, bounds, rowSpan = 1, isVisuallySelectedAt, rowHeights) {
357
437
  if (!isCellInSelection(rowIndex, colIndex, bounds, rowSpan) || !bounds)
358
438
  return void 0;
359
439
  const cellEndRow = rowIndex + rowSpan - 1;
@@ -362,39 +442,47 @@ function getCellSelectionEdgeStyle(rowIndex, colIndex, bounds, rowSpan = 1, isVi
362
442
  const isLeftEdge = colIndex === bounds.startCol;
363
443
  const isRightEdge = colIndex === bounds.endCol;
364
444
  const selectionContinuesBelow = cellEndRow < bounds.endRow;
365
- const shadows = [];
366
- if (isTopEdge) {
367
- shadows.push(
368
- `inset 0 ${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`
369
- );
370
- }
371
- if (isBottomEdge) {
372
- shadows.push(
373
- `inset 0 -${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`
374
- );
375
- }
376
- if (isLeftEdge) {
377
- shadows.push(
378
- `inset ${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`
379
- );
380
- }
381
- if (isRightEdge) {
382
- shadows.push(
383
- `inset -${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`
384
- );
385
- }
386
445
  const stepEdges = getMergedCellStepEdges(
387
446
  rowIndex,
388
447
  colIndex,
389
448
  bounds,
390
449
  rowSpan,
391
- isVisuallySelectedAt
450
+ isVisuallySelectedAt,
451
+ rowHeights
392
452
  );
453
+ const shadows = [];
454
+ const hasFullPerimeter = isTopEdge && isBottomEdge && isLeftEdge && isRightEdge && stepEdges.length === 0;
455
+ if (hasFullPerimeter) {
456
+ shadows.push(
457
+ `inset 0 0 0 ${SELECTION_EDGE_WIDTH_PX}px ${SELECTION_EDGE_COLOR}`
458
+ );
459
+ } else {
460
+ if (isTopEdge) {
461
+ shadows.push(
462
+ `inset 0 ${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`
463
+ );
464
+ }
465
+ if (isBottomEdge) {
466
+ shadows.push(
467
+ `inset 0 -${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`
468
+ );
469
+ }
470
+ if (isLeftEdge) {
471
+ shadows.push(
472
+ `inset ${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`
473
+ );
474
+ }
475
+ if (isRightEdge) {
476
+ shadows.push(
477
+ `inset -${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`
478
+ );
479
+ }
480
+ }
393
481
  const gradients = [];
394
482
  const sizes = [];
395
483
  const positions = [];
396
484
  for (const edge of stepEdges) {
397
- for (const partial of buildPartialVerticalGradient(edge)) {
485
+ for (const partial of buildPartialEdgeGradient(edge)) {
398
486
  gradients.push(partial.image);
399
487
  sizes.push(partial.size);
400
488
  positions.push(partial.position);
@@ -423,6 +511,127 @@ function hasCellSelectionEdges(style) {
423
511
  );
424
512
  }
425
513
 
514
+ // src/components/ui/table/features/cell-selection/copyData.ts
515
+ function formatCellValue(value) {
516
+ if (value === null || value === void 0) return "";
517
+ return String(value);
518
+ }
519
+ function getNestedValue(row, path) {
520
+ if (!path.includes(".")) return row[path];
521
+ return path.split(".").reduce((current, key) => {
522
+ if (current === null || current === void 0 || typeof current !== "object") {
523
+ return void 0;
524
+ }
525
+ return current[key];
526
+ }, row);
527
+ }
528
+ function readRowColumnValue(rowData, columnDef) {
529
+ if ("accessorFn" in columnDef && typeof columnDef.accessorFn === "function") {
530
+ return columnDef.accessorFn(rowData, 0);
531
+ }
532
+ if ("accessorKey" in columnDef && columnDef.accessorKey != null && columnDef.accessorKey !== "") {
533
+ return getNestedValue(rowData, String(columnDef.accessorKey));
534
+ }
535
+ return void 0;
536
+ }
537
+ function flattenSubtreeRows(row) {
538
+ const children = row.children;
539
+ if (!Array.isArray(children) || children.length === 0) return [];
540
+ const result = [];
541
+ const walk = (nodes) => {
542
+ for (const node of nodes) {
543
+ result.push(node);
544
+ const nested = node.children;
545
+ if (Array.isArray(nested) && nested.length > 0) {
546
+ walk(nested);
547
+ }
548
+ }
549
+ };
550
+ walk(children);
551
+ return result;
552
+ }
553
+ function hasSubtree(row) {
554
+ const children = row.children;
555
+ return Array.isArray(children) && children.length > 0;
556
+ }
557
+ function getOriginalRowId(original) {
558
+ return String(original.id ?? original.uniqueId ?? "");
559
+ }
560
+ function getRowDepth(original) {
561
+ return typeof original.level === "number" ? original.level : 0;
562
+ }
563
+ function collectCopyRowEntries(visibleRows, bounds, mode = "visible") {
564
+ const { startRow, endRow } = bounds;
565
+ const result = [];
566
+ const includedOriginalIds = /* @__PURE__ */ new Set();
567
+ const appendSubtree = (node, depth) => {
568
+ const children = node.children;
569
+ if (!Array.isArray(children) || children.length === 0) return;
570
+ for (const child of children) {
571
+ const childId = getOriginalRowId(child);
572
+ if (!(childId && includedOriginalIds.has(childId))) {
573
+ result.push({ row: child, depth });
574
+ if (childId) includedOriginalIds.add(childId);
575
+ }
576
+ appendSubtree(child, depth + 1);
577
+ }
578
+ };
579
+ for (let rowIndex = startRow; rowIndex <= endRow; rowIndex += 1) {
580
+ const row = visibleRows[rowIndex];
581
+ if (!row) continue;
582
+ const originalId = getOriginalRowId(row.original);
583
+ if (originalId && includedOriginalIds.has(originalId)) continue;
584
+ const depth = getRowDepth(row.original);
585
+ result.push({ row: row.original, depth });
586
+ if (originalId) includedOriginalIds.add(originalId);
587
+ if (mode !== "subtree" || !hasSubtree(row.original)) continue;
588
+ appendSubtree(row.original, depth + 1);
589
+ }
590
+ return result;
591
+ }
592
+ function collectCopyRows(visibleRows, bounds, mode = "visible") {
593
+ return collectCopyRowEntries(visibleRows, bounds, mode).map((entry) => entry.row);
594
+ }
595
+ function serializeCopyRowsToTSV(copyRows, visibleRows, bounds, depths) {
596
+ if (copyRows.length === 0) return "";
597
+ const { startCol, endCol } = bounds;
598
+ const columnCells = visibleRows[0]?.getVisibleCells().slice(startCol, endCol + 1) ?? [];
599
+ if (columnCells.length === 0) return "";
600
+ const resolvedDepths = depths && depths.length === copyRows.length ? depths : copyRows.map((row) => getRowDepth(row));
601
+ const minDepth = Math.min(...resolvedDepths);
602
+ return copyRows.map((rowData, index) => {
603
+ const relativeDepth = Math.max(0, (resolvedDepths[index] ?? 0) - minDepth);
604
+ const line = columnCells.map(
605
+ (cell) => formatCellValue(
606
+ readRowColumnValue(
607
+ rowData,
608
+ cell.column.columnDef
609
+ )
610
+ )
611
+ ).join(" ");
612
+ return `${" ".repeat(relativeDepth)}${line}`;
613
+ }).join("\n");
614
+ }
615
+ function serializeSelectionToTSV(visibleRows, bounds, mode = "visible") {
616
+ const entries = collectCopyRowEntries(visibleRows, bounds, mode);
617
+ return serializeCopyRowsToTSV(
618
+ entries.map((entry) => entry.row),
619
+ visibleRows,
620
+ bounds,
621
+ entries.map((entry) => entry.depth)
622
+ );
623
+ }
624
+ async function writeSelectionToClipboard(visibleRows, bounds, mode = "visible") {
625
+ const text = serializeSelectionToTSV(visibleRows, bounds, mode);
626
+ if (!text) return false;
627
+ try {
628
+ await navigator.clipboard.writeText(text);
629
+ } catch {
630
+ return false;
631
+ }
632
+ return true;
633
+ }
634
+
426
635
  // src/components/ui/table/features/cell-selection/fillData.ts
427
636
  function getColumnAccessorKey2(columnDef) {
428
637
  if ("accessorKey" in columnDef && columnDef.accessorKey) {
@@ -479,15 +688,103 @@ function hasFillExtension(sourceBounds, fillBounds) {
479
688
  return fillBounds.startRow < sourceBounds.startRow || fillBounds.endRow > sourceBounds.endRow || fillBounds.startCol < sourceBounds.startCol || fillBounds.endCol > sourceBounds.endCol;
480
689
  }
481
690
 
691
+ // src/components/ui/table/features/cell-selection/pasteData.ts
692
+ function countLeadingEmptyCells(cells) {
693
+ let depth = 0;
694
+ while (depth < cells.length && cells[depth] === "") {
695
+ depth += 1;
696
+ }
697
+ return depth;
698
+ }
699
+ function looksLikeSubtreeIndentation(leadingEmptyCounts) {
700
+ if (leadingEmptyCounts.length === 0) return false;
701
+ const firstDepth = leadingEmptyCounts[0] ?? 0;
702
+ if (firstDepth !== 0) return false;
703
+ return leadingEmptyCounts.some((depth) => depth > 0);
704
+ }
705
+ function parseClipboardTSV(text) {
706
+ return parseClipboardTSVWithDepths(text).values;
707
+ }
708
+ function parseClipboardTSVWithDepths(text) {
709
+ if (!text) return { values: [], depths: [] };
710
+ const normalized = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
711
+ const withoutTrailing = normalized.replace(/\n+$/, "");
712
+ if (!withoutTrailing) return { values: [], depths: [] };
713
+ const rows = withoutTrailing.split("\n").map((line) => line.split(" "));
714
+ const leadingEmptyCounts = rows.map(countLeadingEmptyCells);
715
+ const treatAsDepth = looksLikeSubtreeIndentation(leadingEmptyCounts);
716
+ const values = [];
717
+ const depths = [];
718
+ for (let index = 0; index < rows.length; index += 1) {
719
+ const cells = rows[index] ?? [];
720
+ const depth = leadingEmptyCounts[index] ?? 0;
721
+ if (treatAsDepth) {
722
+ values.push(cells.slice(depth));
723
+ depths.push(depth);
724
+ } else {
725
+ values.push(cells);
726
+ depths.push(0);
727
+ }
728
+ }
729
+ return { values, depths };
730
+ }
731
+ function resolvePasteColumnIds(rows, startCol, width) {
732
+ if (width <= 0) return [];
733
+ const cells = rows[0]?.getVisibleCells() ?? [];
734
+ const columnIds = [];
735
+ for (let offset = 0; offset < width; offset += 1) {
736
+ const cell = cells[startCol + offset];
737
+ if (!cell) break;
738
+ columnIds.push(cell.column.id);
739
+ }
740
+ return columnIds;
741
+ }
742
+ function buildRowsPastePayload(rows, startRow, startCol, text, mode, endRow = startRow) {
743
+ const { values, depths } = parseClipboardTSVWithDepths(text);
744
+ if (values.length === 0) return null;
745
+ const width = Math.max(...values.map((row) => row.length), 0);
746
+ if (width === 0) return null;
747
+ const columnIds = resolvePasteColumnIds(rows, startCol, width);
748
+ if (columnIds.length === 0) return null;
749
+ const rowIds = [];
750
+ for (let offset = 0; offset < values.length; offset += 1) {
751
+ const row = rows[startRow + offset];
752
+ if (!row) break;
753
+ rowIds.push(row.id);
754
+ }
755
+ const anchorRow = rows[endRow] ?? rows[startRow];
756
+ return {
757
+ mode,
758
+ startRow,
759
+ startCol,
760
+ endRow,
761
+ rowIds,
762
+ anchorRowId: anchorRow?.id ?? "",
763
+ columnIds,
764
+ values,
765
+ depths
766
+ };
767
+ }
768
+ function isEditablePasteTarget(target) {
769
+ if (!(target instanceof HTMLElement)) return false;
770
+ const tag = target.tagName;
771
+ if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
772
+ return Boolean(target.isContentEditable);
773
+ }
774
+
482
775
  // src/components/ui/table/features/cell-selection/useCellSelection.ts
483
776
  function useCellSelection({
484
777
  data,
485
778
  rows,
486
779
  enabled = true,
780
+ enableSubtreeCopy = false,
781
+ enableInsertPaste = true,
487
782
  onDataChange,
488
- onBatchChange
783
+ onBatchChange,
784
+ onRowsPaste
489
785
  }) {
490
786
  const [dragState, setDragState] = (0, import_react2.useState)(INITIAL_DRAG_STATE);
787
+ const pendingPasteModeRef = (0, import_react2.useRef)(null);
491
788
  const cellSelectionBounds = getCellSelectionBounds(dragState.start, dragState.end);
492
789
  const activeSelectionBounds = enabled ? getActiveSelectionBounds(dragState, cellSelectionBounds) : null;
493
790
  const handleCellMouseDown = (0, import_react2.useCallback)(
@@ -541,21 +838,113 @@ function useCellSelection({
541
838
  setDragState(INITIAL_DRAG_STATE);
542
839
  }
543
840
  }, [enabled]);
841
+ const copySelection = (0, import_react2.useCallback)(
842
+ async (options) => {
843
+ if (!enabled || !activeSelectionBounds) return false;
844
+ const mode = options?.includeDescendants && enableSubtreeCopy ? "subtree" : "visible";
845
+ return writeSelectionToClipboard(rows, activeSelectionBounds, mode);
846
+ },
847
+ [activeSelectionBounds, enableSubtreeCopy, enabled, rows]
848
+ );
544
849
  (0, import_react2.useEffect)(() => {
545
850
  if (!enabled) return;
546
851
  const handleKeyDown = (e) => {
547
- if ((e.ctrlKey || e.metaKey) && e.key === "c" && activeSelectionBounds) {
548
- const { startRow, endRow, startCol, endCol } = activeSelectionBounds;
549
- const selectedData = rows.slice(startRow, endRow + 1).map((row) => {
550
- const cells = row.getVisibleCells();
551
- return cells.slice(startCol, endCol + 1).map((cell) => cell.getValue()).join(" ");
552
- }).join("\n");
553
- navigator.clipboard.writeText(selectedData);
554
- }
852
+ if (!activeSelectionBounds) return;
853
+ if (!(e.ctrlKey || e.metaKey)) return;
854
+ const isSubtreeShortcut = enableSubtreeCopy && e.shiftKey && e.key.toLowerCase() === "c";
855
+ const isVisibleCopyShortcut = !e.shiftKey && e.key.toLowerCase() === "c";
856
+ if (!isSubtreeShortcut && !isVisibleCopyShortcut) return;
857
+ e.preventDefault();
858
+ void copySelection({ includeDescendants: isSubtreeShortcut });
555
859
  };
556
860
  window.addEventListener("keydown", handleKeyDown);
557
861
  return () => window.removeEventListener("keydown", handleKeyDown);
558
- }, [activeSelectionBounds, enabled, rows]);
862
+ }, [activeSelectionBounds, copySelection, enableSubtreeCopy, enabled]);
863
+ const emitRowsPaste = (0, import_react2.useCallback)(
864
+ (text, mode) => {
865
+ if (!onRowsPaste || !activeSelectionBounds) return false;
866
+ const payload = buildRowsPastePayload(
867
+ rows,
868
+ activeSelectionBounds.startRow,
869
+ activeSelectionBounds.startCol,
870
+ text,
871
+ mode,
872
+ activeSelectionBounds.endRow
873
+ );
874
+ if (!payload) return false;
875
+ onRowsPaste(payload);
876
+ return true;
877
+ },
878
+ [activeSelectionBounds, onRowsPaste, rows]
879
+ );
880
+ (0, import_react2.useEffect)(() => {
881
+ if (!enabled || !onRowsPaste) return;
882
+ const pasteHandledRef = { current: false };
883
+ const ignoreNextPasteRef = { current: false };
884
+ const handleKeyDown = (e) => {
885
+ if (!activeSelectionBounds) return;
886
+ if (!(e.ctrlKey || e.metaKey)) return;
887
+ if (e.key.toLowerCase() !== "v") return;
888
+ if (isEditablePasteTarget(e.target) || isEditablePasteTarget(document.activeElement)) {
889
+ return;
890
+ }
891
+ if (e.shiftKey && !enableInsertPaste) {
892
+ ignoreNextPasteRef.current = true;
893
+ pendingPasteModeRef.current = null;
894
+ return;
895
+ }
896
+ const mode = e.shiftKey ? "insert" : "overwrite";
897
+ pasteHandledRef.current = false;
898
+ ignoreNextPasteRef.current = false;
899
+ pendingPasteModeRef.current = mode;
900
+ void (async () => {
901
+ try {
902
+ const text = await navigator.clipboard.readText();
903
+ if (pasteHandledRef.current) return;
904
+ if (pendingPasteModeRef.current !== mode) return;
905
+ if (!text) return;
906
+ pasteHandledRef.current = true;
907
+ emitRowsPaste(text, mode);
908
+ pendingPasteModeRef.current = null;
909
+ } catch {
910
+ }
911
+ })();
912
+ };
913
+ const handlePaste = (e) => {
914
+ if (!activeSelectionBounds) return;
915
+ if (isEditablePasteTarget(e.target) || isEditablePasteTarget(document.activeElement)) {
916
+ return;
917
+ }
918
+ if (ignoreNextPasteRef.current) {
919
+ ignoreNextPasteRef.current = false;
920
+ pendingPasteModeRef.current = null;
921
+ return;
922
+ }
923
+ const mode = pendingPasteModeRef.current ?? "overwrite";
924
+ if (pasteHandledRef.current) {
925
+ e.preventDefault();
926
+ return;
927
+ }
928
+ const text = e.clipboardData?.getData("text/plain");
929
+ if (text == null || text === "") return;
930
+ pasteHandledRef.current = true;
931
+ e.preventDefault();
932
+ emitRowsPaste(text, mode);
933
+ pendingPasteModeRef.current = null;
934
+ };
935
+ window.addEventListener("keydown", handleKeyDown);
936
+ window.addEventListener("paste", handlePaste);
937
+ return () => {
938
+ window.removeEventListener("keydown", handleKeyDown);
939
+ window.removeEventListener("paste", handlePaste);
940
+ };
941
+ }, [
942
+ activeSelectionBounds,
943
+ emitRowsPaste,
944
+ enableInsertPaste,
945
+ enabled,
946
+ onRowsPaste
947
+ ]);
559
948
  (0, import_react2.useEffect)(() => {
560
949
  if (!enabled) return;
561
950
  const handleMouseUp = () => {
@@ -601,7 +990,8 @@ function useCellSelection({
601
990
  activeSelectionBounds,
602
991
  handleCellMouseDown,
603
992
  handleCellMouseEnter,
604
- handleFillHandleMouseDown
993
+ handleFillHandleMouseDown,
994
+ copySelection
605
995
  };
606
996
  }
607
997
 
@@ -683,15 +1073,16 @@ var useConvertTreeData = ({
683
1073
  children: [],
684
1074
  processed: false
685
1075
  }));
686
- const itemMap = /* @__PURE__ */ new Map();
687
- dataWithLevels.forEach((item) => {
688
- const key = getFieldValue(item, toggleField);
689
- if (typeof key !== "string" || !key) return;
690
- if (!itemMap.has(key)) {
691
- itemMap.set(key, []);
1076
+ const findNearestPrecedingParent = (index, parentKey) => {
1077
+ for (let i = index - 1; i >= 0; i -= 1) {
1078
+ const candidate = dataWithLevels[i];
1079
+ if (!candidate) continue;
1080
+ if (getFieldValue(candidate, toggleField) === parentKey) {
1081
+ return candidate;
1082
+ }
692
1083
  }
693
- itemMap.get(key)?.push(item);
694
- });
1084
+ return void 0;
1085
+ };
695
1086
  const rootItems = [];
696
1087
  dataWithLevels.forEach((item) => {
697
1088
  if (!getFieldValue(item, childField)) {
@@ -699,29 +1090,18 @@ var useConvertTreeData = ({
699
1090
  item.processed = true;
700
1091
  }
701
1092
  });
702
- dataWithLevels.forEach((item) => {
1093
+ dataWithLevels.forEach((item, index) => {
703
1094
  const parentKey = getFieldValue(item, childField);
704
1095
  if (!parentKey || item.processed) return;
705
- const parentItems = dataWithLevels.filter(
706
- (parent) => getFieldValue(parent, toggleField) === parentKey && !getFieldValue(parent, childField)
707
- );
708
- if (parentItems.length > 0) {
709
- const parent = parentItems[0];
1096
+ const parent = findNearestPrecedingParent(index, parentKey);
1097
+ if (parent) {
710
1098
  item.level = parent.level + 1;
711
1099
  parent.children.push(item);
712
1100
  item.processed = true;
713
- } else {
714
- const otherParents = itemMap.get(String(parentKey)) || [];
715
- if (otherParents.length > 0) {
716
- const parent = otherParents[0];
717
- item.level = parent.level + 1;
718
- parent.children.push(item);
719
- item.processed = true;
720
- } else {
721
- rootItems.push(item);
722
- item.processed = true;
723
- }
1101
+ return;
724
1102
  }
1103
+ rootItems.push(item);
1104
+ item.processed = true;
725
1105
  });
726
1106
  return rootItems;
727
1107
  }, [enabled, data, toggleField, childField, flattenField]);
@@ -746,16 +1126,23 @@ var useConvertTreeData = ({
746
1126
  return result;
747
1127
  };
748
1128
  const flattenedData = flatten(processedData, [], 0);
749
- flattenedData.forEach((item) => {
750
- if (getFieldValue(item, childField)) {
751
- const parentItem = flattenedData.find(
752
- (parent) => getFieldValue(parent, toggleField) === getFieldValue(item, childField)
753
- );
754
- const parentAmount = parentItem ? Number(getFieldValue(parentItem, qtyField) ?? 1) : 1;
755
- item.parentCount = parentAmount || 1;
756
- } else {
1129
+ flattenedData.forEach((item, index) => {
1130
+ const parentKey = getFieldValue(item, childField);
1131
+ if (!parentKey) {
757
1132
  item.parentCount = 1;
1133
+ return;
758
1134
  }
1135
+ let parentItem;
1136
+ for (let i = index - 1; i >= 0; i -= 1) {
1137
+ const candidate = flattenedData[i];
1138
+ if (!candidate) continue;
1139
+ if (getFieldValue(candidate, toggleField) === parentKey) {
1140
+ parentItem = candidate;
1141
+ break;
1142
+ }
1143
+ }
1144
+ const parentAmount = parentItem ? Number(getFieldValue(parentItem, qtyField) ?? 1) : 1;
1145
+ item.parentCount = parentAmount || 1;
759
1146
  });
760
1147
  return flattenedData;
761
1148
  }, [
@@ -902,6 +1289,10 @@ function useGlideTable(options) {
902
1289
  expandedRows: controlledExpandedRows,
903
1290
  onExpandedRowsChange,
904
1291
  preventExpand = false,
1292
+ enableSubtreeCopy,
1293
+ onCopyActionsReady,
1294
+ onRowsPaste,
1295
+ enableInsertPaste,
905
1296
  enableVirtualization = true,
906
1297
  estimateRowHeight = DATA_TABLE_ROW_HEIGHT,
907
1298
  virtualOverscan = DATA_TABLE_VIRTUAL_OVERSCAN
@@ -916,12 +1307,12 @@ function useGlideTable(options) {
916
1307
  };
917
1308
  }, [labelsProp, emptyText, loadingText, selectionLabel]);
918
1309
  const enableExpand = Boolean(toggleField);
1310
+ const resolvedEnableSubtreeCopy = enableSubtreeCopy ?? enableExpand;
919
1311
  const [internalRowSelection, setInternalRowSelection] = (0, import_react4.useState)({});
920
1312
  const [internalExpandedRows, setInternalExpandedRows] = (0, import_react4.useState)(
921
1313
  () => /* @__PURE__ */ new Set()
922
1314
  );
923
1315
  const [hoveredRowIndex, setHoveredRowIndex] = (0, import_react4.useState)(null);
924
- const [hoveredGroupKey, setHoveredGroupKey] = (0, import_react4.useState)(null);
925
1316
  const scrollRef = (0, import_react4.useRef)(null);
926
1317
  const shouldVirtualize = enableVirtualization && !enableRowSpan;
927
1318
  (0, import_react4.useEffect)(() => {
@@ -985,6 +1376,7 @@ function useGlideTable(options) {
985
1376
  return collectRowSpanColumns(columns);
986
1377
  }, [enableRowSpan, columns]);
987
1378
  const primaryRowSpanKey = rowSpanColumnKeys[0]?.rowSpanKey;
1379
+ const primaryRowSpanColumnId = rowSpanColumnKeys[0]?.columnId;
988
1380
  const columnRowSpanMap = (0, import_react4.useMemo)(
989
1381
  () => buildColumnRowSpanMap(tableData, rowSpanColumnKeys),
990
1382
  [tableData, rowSpanColumnKeys]
@@ -1003,27 +1395,29 @@ function useGlideTable(options) {
1003
1395
  const totalSize = rowVirtualizer.getTotalSize();
1004
1396
  const paddingTop = virtualRows.length > 0 ? virtualRows[0]?.start ?? 0 : 0;
1005
1397
  const paddingBottom = virtualRows.length > 0 ? totalSize - (virtualRows[virtualRows.length - 1]?.end ?? 0) : 0;
1006
- const selectedGroupKeys = (0, import_react4.useMemo)(() => {
1007
- if (!enableRowSpan || !primaryRowSpanKey) return /* @__PURE__ */ new Set();
1008
- const keys = /* @__PURE__ */ new Set();
1398
+ const selectedRowIndices = (0, import_react4.useMemo)(() => {
1399
+ const indices = /* @__PURE__ */ new Set();
1009
1400
  for (const selectedRow of selectedRows) {
1010
- const value = selectedRow.original[primaryRowSpanKey];
1011
- if (value !== null && value !== void 0) keys.add(String(value));
1401
+ indices.add(selectedRow.index);
1012
1402
  }
1013
- return keys;
1014
- }, [enableRowSpan, primaryRowSpanKey, selectedRows]);
1403
+ return indices;
1404
+ }, [selectedRows]);
1015
1405
  const {
1016
1406
  dragState,
1017
1407
  activeSelectionBounds,
1018
1408
  handleCellMouseDown,
1019
1409
  handleCellMouseEnter,
1020
- handleFillHandleMouseDown
1410
+ handleFillHandleMouseDown,
1411
+ copySelection
1021
1412
  } = useCellSelection({
1022
1413
  data: tableData,
1023
1414
  rows,
1024
1415
  enabled: enableCellSelection,
1416
+ enableSubtreeCopy: resolvedEnableSubtreeCopy,
1417
+ enableInsertPaste: enableInsertPaste ?? true,
1025
1418
  onDataChange,
1026
- onBatchChange
1419
+ onBatchChange,
1420
+ onRowsPaste
1027
1421
  });
1028
1422
  const {
1029
1423
  editingCell,
@@ -1045,22 +1439,10 @@ function useGlideTable(options) {
1045
1439
  );
1046
1440
  const clearHover = (0, import_react4.useCallback)(() => {
1047
1441
  setHoveredRowIndex(null);
1048
- setHoveredGroupKey(null);
1049
1442
  }, []);
1050
- const handleRowHover = (0, import_react4.useCallback)(
1051
- (rowIndex, rowData) => {
1052
- setHoveredRowIndex(rowIndex);
1053
- if (!primaryRowSpanKey) {
1054
- setHoveredGroupKey(null);
1055
- return;
1056
- }
1057
- const groupValue = rowData[primaryRowSpanKey];
1058
- setHoveredGroupKey(
1059
- groupValue === null || groupValue === void 0 ? null : String(groupValue)
1060
- );
1061
- },
1062
- [primaryRowSpanKey]
1063
- );
1443
+ const handleRowHover = (0, import_react4.useCallback)((rowIndex, _rowData) => {
1444
+ setHoveredRowIndex(rowIndex);
1445
+ }, []);
1064
1446
  const handleToggleSelect = (0, import_react4.useCallback)(
1065
1447
  (row) => {
1066
1448
  if (!row.getCanSelect()) return;
@@ -1083,10 +1465,10 @@ function useGlideTable(options) {
1083
1465
  rowSpan: {
1084
1466
  enableRowSpan,
1085
1467
  primaryRowSpanKey,
1468
+ primaryRowSpanColumnId,
1086
1469
  columnRowSpanMap,
1087
1470
  hoveredRowIndex,
1088
- hoveredGroupKey,
1089
- selectedGroupKeys,
1471
+ selectedRowIndices,
1090
1472
  onRowHover: handleRowHover
1091
1473
  },
1092
1474
  selection: {
@@ -1124,10 +1506,10 @@ function useGlideTable(options) {
1124
1506
  }, [
1125
1507
  enableRowSpan,
1126
1508
  primaryRowSpanKey,
1509
+ primaryRowSpanColumnId,
1127
1510
  columnRowSpanMap,
1128
1511
  hoveredRowIndex,
1129
- hoveredGroupKey,
1130
- selectedGroupKeys,
1512
+ selectedRowIndices,
1131
1513
  handleRowHover,
1132
1514
  rowSelectionMode,
1133
1515
  selectOnRowClick,
@@ -1153,6 +1535,14 @@ function useGlideTable(options) {
1153
1535
  labels.expandRow,
1154
1536
  labels.collapseRow
1155
1537
  ]);
1538
+ const copySelectionRef = (0, import_react4.useRef)(copySelection);
1539
+ (0, import_react4.useEffect)(() => {
1540
+ copySelectionRef.current = copySelection;
1541
+ }, [copySelection]);
1542
+ const stableCopySelection = (0, import_react4.useCallback)((options2) => copySelectionRef.current(options2), []);
1543
+ (0, import_react4.useEffect)(() => {
1544
+ onCopyActionsReady?.({ copySelection: stableCopySelection });
1545
+ }, [onCopyActionsReady, stableCopySelection]);
1156
1546
  return {
1157
1547
  table,
1158
1548
  tableData,
@@ -1172,7 +1562,8 @@ function useGlideTable(options) {
1172
1562
  paddingBottom,
1173
1563
  rowContextValue,
1174
1564
  handleToggleSelect,
1175
- clearHover
1565
+ clearHover,
1566
+ copySelection: stableCopySelection
1176
1567
  };
1177
1568
  }
1178
1569
 
@@ -1372,11 +1763,10 @@ function DataTableRow({
1372
1763
  const { classNames, rowSpan, selection, cellSelection, cellEdit, expand } = useDataTableRowContext();
1373
1764
  const {
1374
1765
  enableRowSpan,
1375
- primaryRowSpanKey,
1766
+ primaryRowSpanColumnId,
1376
1767
  columnRowSpanMap,
1377
1768
  hoveredRowIndex,
1378
- hoveredGroupKey,
1379
- selectedGroupKeys,
1769
+ selectedRowIndices,
1380
1770
  onRowHover
1381
1771
  } = rowSpan;
1382
1772
  const { rowSelectionMode, selectOnRowClick, onRowClick, getRowClassName } = selection;
@@ -1409,9 +1799,11 @@ function DataTableRow({
1409
1799
  const rowData = row.original;
1410
1800
  const isRowHovered = hoveredRowIndex === rowIndex;
1411
1801
  const isRowSelected = row.getIsSelected();
1412
- const rowGroupKey = primaryRowSpanKey !== void 0 && rowData[primaryRowSpanKey] !== null && rowData[primaryRowSpanKey] !== void 0 ? String(rowData[primaryRowSpanKey]) : null;
1413
- const isGroupHovered = enableRowSpan && hoveredGroupKey !== null && rowGroupKey === hoveredGroupKey;
1414
- const isGroupSelected = enableRowSpan && rowGroupKey !== null && selectedGroupKeys.has(rowGroupKey);
1802
+ const { startRow: primaryGroupStart, rowSpan: primaryGroupSpan } = resolveRowSpanAt(
1803
+ primaryRowSpanColumnId ? columnRowSpanMap.get(primaryRowSpanColumnId) : void 0,
1804
+ rowIndex
1805
+ );
1806
+ const isGroupHovered = enableRowSpan && hoveredRowIndex !== null && hoveredRowIndex >= primaryGroupStart && hoveredRowIndex <= primaryGroupStart + primaryGroupSpan - 1;
1415
1807
  const visibleCells = row.getVisibleCells();
1416
1808
  const columnIdsByIndex = visibleCells.map((cell) => cell.column.id);
1417
1809
  const isVisuallySelectedAt = activeSelectionBounds ? (targetRow, targetCol) => {
@@ -1485,9 +1877,19 @@ function DataTableRow({
1485
1877
  }
1486
1878
  }
1487
1879
  const isEditing = editingCell?.rowIndex === rowIndex && editingCell?.colIndex === cellIndex;
1488
- const showCellHover = isRowSpanColumn ? isGroupHovered : isRowHovered;
1489
- const showCellSelected = isRowSpanColumn ? isGroupSelected : isRowSelected;
1490
1880
  const cellRowSpan = rowSpanInfo?.rowSpan ?? 1;
1881
+ const isMergedCellHovered = hoveredRowIndex !== null && hoveredRowIndex >= rowIndex && hoveredRowIndex <= rowIndex + cellRowSpan - 1;
1882
+ const showCellHover = isRowSpanColumn ? isMergedCellHovered : isRowHovered;
1883
+ let isMergedCellSelected = false;
1884
+ if (isRowSpanColumn) {
1885
+ for (let r = rowIndex; r < rowIndex + cellRowSpan; r += 1) {
1886
+ if (selectedRowIndices.has(r)) {
1887
+ isMergedCellSelected = true;
1888
+ break;
1889
+ }
1890
+ }
1891
+ }
1892
+ const showCellSelected = isRowSpanColumn ? isMergedCellSelected : isRowSelected;
1491
1893
  const isMerged = cellRowSpan > 1;
1492
1894
  const showMergedRightEdge = isMerged && (cellIndex === visibleCells.length - 1 || resolveRowSpanAt(
1493
1895
  columnRowSpanMap.get(columnIdsByIndex[cellIndex + 1]),
@@ -1500,12 +1902,14 @@ function DataTableRow({
1500
1902
  cellRowSpan
1501
1903
  );
1502
1904
  const isBottomRightCell = !isEditing && activeSelectionBounds && !dragState.isSelecting && activeSelectionBounds.endRow >= rowIndex && activeSelectionBounds.endRow <= rowIndex + cellRowSpan - 1 && cellIndex === activeSelectionBounds.endCol;
1905
+ const spanRowHeights = enableCellSelection && activeSelectionBounds && isCellDragSelected && cellRowSpan > 1 ? measureMergedSpanRowHeights(rowIndex, cellRowSpan) : void 0;
1503
1906
  const selectionEdgeStyle = getCellSelectionEdgeStyle(
1504
1907
  rowIndex,
1505
1908
  cellIndex,
1506
1909
  activeSelectionBounds,
1507
1910
  cellRowSpan,
1508
- isVisuallySelectedAt
1911
+ isVisuallySelectedAt,
1912
+ spanRowHeights
1509
1913
  );
1510
1914
  const resolveCellRowIndex = (clientY, element) => getRowIndexInMergedCell(clientY, element, rowIndex, cellRowSpan);
1511
1915
  const hasSelectionEdges = hasCellSelectionEdges(selectionEdgeStyle);
@@ -1515,6 +1919,7 @@ function DataTableRow({
1515
1919
  rowSpan: rowSpanInfo && rowSpanInfo.rowSpan > 1 ? rowSpanInfo.rowSpan : void 0,
1516
1920
  "data-merged": isMerged && cellIndex > 0 ? "" : void 0,
1517
1921
  "data-merged-edge-right": showMergedRightEdge ? "" : void 0,
1922
+ "data-merged-row-first": isMerged && cellIndex === 0 && showMergedRightEdge ? "" : void 0,
1518
1923
  "data-group-selected": enableRowSpan && showCellSelected ? "" : void 0,
1519
1924
  "data-group-hovered": enableRowSpan && showCellHover && !showCellSelected ? "" : void 0,
1520
1925
  "data-selection-fill": isCellDragSelected ? "" : void 0,
@@ -1559,7 +1964,8 @@ function DataTableRow({
1559
1964
  "data-table-cell",
1560
1965
  CELL_ALIGN_CLASS[align],
1561
1966
  cellClassName,
1562
- isMerged && cellIndex > 0 && "is-merged",
1967
+ isMerged && "is-merged",
1968
+ isMerged && cellIndex === 0 && showMergedRightEdge && "is-merged-row-first",
1563
1969
  showMergedRightEdge && "is-merged-edge-right",
1564
1970
  enableRowSpan && showCellSelected && "is-group-selected",
1565
1971
  enableRowSpan && showCellHover && !showCellSelected && "is-group-hovered",
@@ -2065,10 +2471,26 @@ function parseTableChildren(children) {
2065
2471
  }
2066
2472
  return slots;
2067
2473
  }
2474
+ function flattenColumnElements(children) {
2475
+ const result = [];
2476
+ for (const child of import_react9.Children.toArray(children)) {
2477
+ if (isTableColumnElement(child)) {
2478
+ result.push(child);
2479
+ continue;
2480
+ }
2481
+ if ((0, import_react9.isValidElement)(child)) {
2482
+ const nested = child.props.children;
2483
+ if (nested != null) {
2484
+ result.push(...flattenColumnElements(nested));
2485
+ }
2486
+ }
2487
+ }
2488
+ return result;
2489
+ }
2068
2490
  function extractColumnElements(header) {
2069
2491
  if (!header) return [];
2070
2492
  const { children } = header.props;
2071
- return import_react9.Children.toArray(children).filter(isTableColumnElement);
2493
+ return flattenColumnElements(children);
2072
2494
  }
2073
2495
 
2074
2496
  // src/components/ui/table/components/Table/TableBody.tsx
@@ -2270,10 +2692,14 @@ var Table = Object.assign(TableRoot, {
2270
2692
  applyFillData,
2271
2693
  applySelectionUpdater,
2272
2694
  buildColumnRowSpanMap,
2695
+ buildRowsPastePayload,
2273
2696
  canExpandRow,
2697
+ collectCopyRowEntries,
2698
+ collectCopyRows,
2274
2699
  collectFillChanges,
2275
2700
  collectRowSpanColumns,
2276
2701
  createTable,
2702
+ flattenSubtreeRows,
2277
2703
  getCellEditDraftValue,
2278
2704
  getCellSelectionEdgeStyle,
2279
2705
  getColumnEditType,
@@ -2281,13 +2707,22 @@ var Table = Object.assign(TableRoot, {
2281
2707
  hasCellSelectionEdges,
2282
2708
  isCellInSelection,
2283
2709
  isColumnEditable,
2710
+ isEditablePasteTarget,
2711
+ measureMergedSpanRowHeights,
2284
2712
  parseCellEditValue,
2713
+ parseClipboardTSV,
2714
+ parseClipboardTSVWithDepths,
2285
2715
  resolveDataTableLabels,
2716
+ resolvePasteColumnIds,
2286
2717
  resolveRowSelection,
2287
2718
  resolveRowSpanAt,
2719
+ rowRangeToHeightRatios,
2720
+ serializeCopyRowsToTSV,
2721
+ serializeSelectionToTSV,
2288
2722
  toggleExpandedRowId,
2289
2723
  useCellEdit,
2290
2724
  useCellSelection,
2291
2725
  useConvertTreeData,
2292
- useGlideTable
2726
+ useGlideTable,
2727
+ writeSelectionToClipboard
2293
2728
  });