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.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
|
|
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
|
|
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
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
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
|
|
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.
|
|
287
|
-
const
|
|
288
|
-
const
|
|
289
|
-
const
|
|
290
|
-
const
|
|
291
|
-
const
|
|
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
|
|
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 (
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
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,
|
|
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
|
|
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 =
|
|
594
|
-
const hasInitializedRef =
|
|
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
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
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
|
-
|
|
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
|
|
660
|
-
|
|
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
|
-
|
|
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
|
-
|
|
705
|
-
|
|
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
|
|
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
|
|
961
|
-
|
|
962
|
-
const keys = /* @__PURE__ */ new Set();
|
|
1339
|
+
const selectedRowIndices = useMemo2(() => {
|
|
1340
|
+
const indices = /* @__PURE__ */ new Set();
|
|
963
1341
|
for (const selectedRow of selectedRows) {
|
|
964
|
-
|
|
965
|
-
if (value !== null && value !== void 0) keys.add(String(value));
|
|
1342
|
+
indices.add(selectedRow.index);
|
|
966
1343
|
}
|
|
967
|
-
return
|
|
968
|
-
}, [
|
|
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
|
|
1006
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
1707
|
+
primaryRowSpanColumnId,
|
|
1330
1708
|
columnRowSpanMap,
|
|
1331
1709
|
hoveredRowIndex,
|
|
1332
|
-
|
|
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
|
|
1367
|
-
|
|
1368
|
-
|
|
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 =
|
|
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 &&
|
|
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
|
|
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
|
};
|