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/LICENSE +21 -0
- package/README.md +38 -3
- package/dist/compound.cjs +515 -128
- package/dist/compound.d.cts +2 -2
- package/dist/compound.d.ts +2 -2
- package/dist/compound.js +519 -132
- package/dist/core.cjs +523 -119
- package/dist/core.d.cts +82 -8
- package/dist/core.d.ts +82 -8
- package/dist/core.js +515 -124
- package/dist/index.cjs +565 -130
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +560 -138
- package/dist/{types-BfthylVR.d.cts → types-DOLnknDe.d.cts} +52 -1
- package/dist/{types-BfthylVR.d.ts → types-DOLnknDe.d.ts} +52 -1
- package/package.json +1 -1
package/dist/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
|
|
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
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
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
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
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
|
|
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.
|
|
333
|
-
const
|
|
334
|
-
const
|
|
335
|
-
const
|
|
336
|
-
const
|
|
337
|
-
const
|
|
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
|
|
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 (
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
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,
|
|
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
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
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
|
-
|
|
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
|
|
706
|
-
|
|
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
|
-
|
|
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
|
-
|
|
751
|
-
|
|
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
|
|
1007
|
-
|
|
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
|
-
|
|
1011
|
-
if (value !== null && value !== void 0) keys.add(String(value));
|
|
1401
|
+
indices.add(selectedRow.index);
|
|
1012
1402
|
}
|
|
1013
|
-
return
|
|
1014
|
-
}, [
|
|
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
|
|
1052
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1766
|
+
primaryRowSpanColumnId,
|
|
1376
1767
|
columnRowSpanMap,
|
|
1377
1768
|
hoveredRowIndex,
|
|
1378
|
-
|
|
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
|
|
1413
|
-
|
|
1414
|
-
|
|
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 &&
|
|
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
|
|
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
|
});
|