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/compound.cjs
CHANGED
|
@@ -128,10 +128,36 @@ function getCellSelectionBounds(start, end) {
|
|
|
128
128
|
endCol: Math.max(start.col, end.col)
|
|
129
129
|
};
|
|
130
130
|
}
|
|
131
|
+
function measureMergedSpanRowHeights(rowIndex, rowSpan, cellElement) {
|
|
132
|
+
if (rowSpan <= 1) return void 0;
|
|
133
|
+
const tbody = cellElement?.closest("tbody") ?? (typeof document !== "undefined" ? document.querySelector("tbody.data-table-body") : null);
|
|
134
|
+
if (!tbody) return void 0;
|
|
135
|
+
const rows = tbody.querySelectorAll(":scope > tr");
|
|
136
|
+
if (rows.length < rowIndex + rowSpan) return void 0;
|
|
137
|
+
const heights = [];
|
|
138
|
+
for (let i = 0; i < rowSpan; i++) {
|
|
139
|
+
const row = rows[rowIndex + i];
|
|
140
|
+
const height = row?.getBoundingClientRect().height ?? 0;
|
|
141
|
+
if (height <= 0) return void 0;
|
|
142
|
+
heights.push(height);
|
|
143
|
+
}
|
|
144
|
+
return heights;
|
|
145
|
+
}
|
|
131
146
|
function getRowIndexInMergedCell(clientY, cellElement, rowIndex, rowSpan) {
|
|
132
147
|
if (rowSpan <= 1) return rowIndex;
|
|
133
148
|
const rect = cellElement.getBoundingClientRect();
|
|
134
149
|
const relativeY = clientY - rect.top;
|
|
150
|
+
const heights = measureMergedSpanRowHeights(rowIndex, rowSpan, cellElement);
|
|
151
|
+
if (heights && heights.length === rowSpan) {
|
|
152
|
+
let accrued = 0;
|
|
153
|
+
for (let i = 0; i < rowSpan; i++) {
|
|
154
|
+
accrued += heights[i];
|
|
155
|
+
if (relativeY < accrued) {
|
|
156
|
+
return rowIndex + i;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return rowIndex + rowSpan - 1;
|
|
160
|
+
}
|
|
135
161
|
const rowHeight = rect.height / rowSpan;
|
|
136
162
|
const offset = Math.min(
|
|
137
163
|
Math.max(Math.floor(relativeY / rowHeight), 0),
|
|
@@ -153,7 +179,35 @@ function getActiveSelectionBounds(dragState, selectionBounds) {
|
|
|
153
179
|
var SELECTION_EDGE_WIDTH_PX = 2;
|
|
154
180
|
var SELECTION_EDGE_COLOR = "var(--color-brand-primary)";
|
|
155
181
|
var CELL_SELECTION_EDGES_CLASS = "cell-selection-edges";
|
|
156
|
-
function
|
|
182
|
+
function rowRangeToHeightRatios(rowIndex, span, fromRow, toRowExclusive, rowHeights) {
|
|
183
|
+
const clampedFrom = Math.max(fromRow, rowIndex);
|
|
184
|
+
const clampedTo = Math.min(toRowExclusive, rowIndex + span);
|
|
185
|
+
if (clampedTo <= clampedFrom) {
|
|
186
|
+
return { offsetRatio: 0, lengthRatio: 0 };
|
|
187
|
+
}
|
|
188
|
+
if (!rowHeights || rowHeights.length !== span) {
|
|
189
|
+
return {
|
|
190
|
+
offsetRatio: (clampedFrom - rowIndex) / span,
|
|
191
|
+
lengthRatio: (clampedTo - clampedFrom) / span
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
const total = rowHeights.reduce((sum, height) => sum + height, 0) || 1;
|
|
195
|
+
let offsetPx = 0;
|
|
196
|
+
for (let i = 0; i < clampedFrom - rowIndex; i++) {
|
|
197
|
+
offsetPx += rowHeights[i] ?? 0;
|
|
198
|
+
}
|
|
199
|
+
let lengthPx = 0;
|
|
200
|
+
for (let i = clampedFrom - rowIndex; i < clampedTo - rowIndex; i++) {
|
|
201
|
+
lengthPx += rowHeights[i] ?? 0;
|
|
202
|
+
}
|
|
203
|
+
return {
|
|
204
|
+
offsetRatio: offsetPx / total,
|
|
205
|
+
lengthRatio: lengthPx / total,
|
|
206
|
+
offsetPx,
|
|
207
|
+
lengthPx
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
function getMergedCellStepEdges(rowIndex, colIndex, bounds, rowSpan, isVisuallySelectedAt, rowHeights) {
|
|
157
211
|
const cellEndRow = rowIndex + rowSpan - 1;
|
|
158
212
|
const span = cellEndRow - rowIndex + 1;
|
|
159
213
|
if (span <= 1) return [];
|
|
@@ -172,20 +226,30 @@ function getMergedCellStepEdges(rowIndex, colIndex, bounds, rowSpan, isVisuallyS
|
|
|
172
226
|
continue;
|
|
173
227
|
}
|
|
174
228
|
if (runStart !== null) {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
229
|
+
const ratios = rowRangeToHeightRatios(
|
|
230
|
+
rowIndex,
|
|
231
|
+
span,
|
|
232
|
+
runStart,
|
|
233
|
+
row,
|
|
234
|
+
rowHeights
|
|
235
|
+
);
|
|
236
|
+
if (ratios.lengthRatio > 0) {
|
|
237
|
+
edges.push({ side, ...ratios });
|
|
238
|
+
}
|
|
180
239
|
runStart = null;
|
|
181
240
|
}
|
|
182
241
|
}
|
|
183
242
|
if (runStart !== null) {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
243
|
+
const ratios = rowRangeToHeightRatios(
|
|
244
|
+
rowIndex,
|
|
245
|
+
span,
|
|
246
|
+
runStart,
|
|
247
|
+
toRowExclusive,
|
|
248
|
+
rowHeights
|
|
249
|
+
);
|
|
250
|
+
if (ratios.lengthRatio > 0) {
|
|
251
|
+
edges.push({ side, ...ratios });
|
|
252
|
+
}
|
|
189
253
|
}
|
|
190
254
|
};
|
|
191
255
|
const collectSide = (side, neighborCol) => {
|
|
@@ -214,14 +278,17 @@ function getMergedCellStepEdges(rowIndex, colIndex, bounds, rowSpan, isVisuallyS
|
|
|
214
278
|
}
|
|
215
279
|
return edges;
|
|
216
280
|
}
|
|
217
|
-
function
|
|
281
|
+
function buildPartialEdgeGradient(edge) {
|
|
282
|
+
const usePx = edge.offsetPx != null && edge.lengthPx != null;
|
|
218
283
|
const startPct = edge.offsetRatio * 100;
|
|
219
|
-
const endPct = (edge.offsetRatio + edge.
|
|
220
|
-
const
|
|
221
|
-
const
|
|
222
|
-
const
|
|
223
|
-
const
|
|
224
|
-
const
|
|
284
|
+
const endPct = (edge.offsetRatio + edge.lengthRatio) * 100;
|
|
285
|
+
const startPx = edge.offsetPx ?? 0;
|
|
286
|
+
const endPx = (edge.offsetPx ?? 0) + (edge.lengthPx ?? 0);
|
|
287
|
+
const overlapPx = SELECTION_EDGE_WIDTH_PX;
|
|
288
|
+
const isTopProtrusion = (usePx ? startPx === 0 : edge.offsetRatio === 0) && edge.lengthRatio < 1;
|
|
289
|
+
const isBottomProtrusion = usePx ? startPx > 0 : edge.offsetRatio > 0;
|
|
290
|
+
const startStop = usePx ? isBottomProtrusion ? `${Math.max(0, startPx - overlapPx)}px` : `${startPx}px` : isBottomProtrusion ? `calc(${startPct}% - ${overlapPx}px)` : `${startPct}%`;
|
|
291
|
+
const endStop = usePx ? isTopProtrusion ? `${endPx + overlapPx}px` : `${endPx}px` : isTopProtrusion ? `calc(${endPct}% + ${overlapPx}px)` : `${endPct}%`;
|
|
225
292
|
const xPos = edge.side === "left" ? "0" : "100%";
|
|
226
293
|
const layers = [
|
|
227
294
|
{
|
|
@@ -231,7 +298,7 @@ function buildPartialVerticalGradient(edge) {
|
|
|
231
298
|
}
|
|
232
299
|
];
|
|
233
300
|
if (isTopProtrusion || isBottomProtrusion) {
|
|
234
|
-
const capTop = isTopProtrusion ? `calc(${endPct}% - ${SELECTION_EDGE_WIDTH_PX}px)` : `calc(${startPct}% - ${SELECTION_EDGE_WIDTH_PX}px)`;
|
|
301
|
+
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)`;
|
|
235
302
|
layers.push({
|
|
236
303
|
image: `linear-gradient(${SELECTION_EDGE_COLOR}, ${SELECTION_EDGE_COLOR})`,
|
|
237
304
|
size: `${SELECTION_EDGE_WIDTH_PX}px ${SELECTION_EDGE_WIDTH_PX}px`,
|
|
@@ -240,7 +307,7 @@ function buildPartialVerticalGradient(edge) {
|
|
|
240
307
|
}
|
|
241
308
|
return layers;
|
|
242
309
|
}
|
|
243
|
-
function getCellSelectionEdgeStyle(rowIndex, colIndex, bounds, rowSpan = 1, isVisuallySelectedAt) {
|
|
310
|
+
function getCellSelectionEdgeStyle(rowIndex, colIndex, bounds, rowSpan = 1, isVisuallySelectedAt, rowHeights) {
|
|
244
311
|
if (!isCellInSelection(rowIndex, colIndex, bounds, rowSpan) || !bounds)
|
|
245
312
|
return void 0;
|
|
246
313
|
const cellEndRow = rowIndex + rowSpan - 1;
|
|
@@ -249,39 +316,47 @@ function getCellSelectionEdgeStyle(rowIndex, colIndex, bounds, rowSpan = 1, isVi
|
|
|
249
316
|
const isLeftEdge = colIndex === bounds.startCol;
|
|
250
317
|
const isRightEdge = colIndex === bounds.endCol;
|
|
251
318
|
const selectionContinuesBelow = cellEndRow < bounds.endRow;
|
|
252
|
-
const shadows = [];
|
|
253
|
-
if (isTopEdge) {
|
|
254
|
-
shadows.push(
|
|
255
|
-
`inset 0 ${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`
|
|
256
|
-
);
|
|
257
|
-
}
|
|
258
|
-
if (isBottomEdge) {
|
|
259
|
-
shadows.push(
|
|
260
|
-
`inset 0 -${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`
|
|
261
|
-
);
|
|
262
|
-
}
|
|
263
|
-
if (isLeftEdge) {
|
|
264
|
-
shadows.push(
|
|
265
|
-
`inset ${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`
|
|
266
|
-
);
|
|
267
|
-
}
|
|
268
|
-
if (isRightEdge) {
|
|
269
|
-
shadows.push(
|
|
270
|
-
`inset -${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`
|
|
271
|
-
);
|
|
272
|
-
}
|
|
273
319
|
const stepEdges = getMergedCellStepEdges(
|
|
274
320
|
rowIndex,
|
|
275
321
|
colIndex,
|
|
276
322
|
bounds,
|
|
277
323
|
rowSpan,
|
|
278
|
-
isVisuallySelectedAt
|
|
324
|
+
isVisuallySelectedAt,
|
|
325
|
+
rowHeights
|
|
279
326
|
);
|
|
327
|
+
const shadows = [];
|
|
328
|
+
const hasFullPerimeter = isTopEdge && isBottomEdge && isLeftEdge && isRightEdge && stepEdges.length === 0;
|
|
329
|
+
if (hasFullPerimeter) {
|
|
330
|
+
shadows.push(
|
|
331
|
+
`inset 0 0 0 ${SELECTION_EDGE_WIDTH_PX}px ${SELECTION_EDGE_COLOR}`
|
|
332
|
+
);
|
|
333
|
+
} else {
|
|
334
|
+
if (isTopEdge) {
|
|
335
|
+
shadows.push(
|
|
336
|
+
`inset 0 ${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
if (isBottomEdge) {
|
|
340
|
+
shadows.push(
|
|
341
|
+
`inset 0 -${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
if (isLeftEdge) {
|
|
345
|
+
shadows.push(
|
|
346
|
+
`inset ${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
if (isRightEdge) {
|
|
350
|
+
shadows.push(
|
|
351
|
+
`inset -${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
280
355
|
const gradients = [];
|
|
281
356
|
const sizes = [];
|
|
282
357
|
const positions = [];
|
|
283
358
|
for (const edge of stepEdges) {
|
|
284
|
-
for (const partial of
|
|
359
|
+
for (const partial of buildPartialEdgeGradient(edge)) {
|
|
285
360
|
gradients.push(partial.image);
|
|
286
361
|
sizes.push(partial.size);
|
|
287
362
|
positions.push(partial.position);
|
|
@@ -396,15 +471,16 @@ var useConvertTreeData = ({
|
|
|
396
471
|
children: [],
|
|
397
472
|
processed: false
|
|
398
473
|
}));
|
|
399
|
-
const
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
474
|
+
const findNearestPrecedingParent = (index, parentKey) => {
|
|
475
|
+
for (let i = index - 1; i >= 0; i -= 1) {
|
|
476
|
+
const candidate = dataWithLevels[i];
|
|
477
|
+
if (!candidate) continue;
|
|
478
|
+
if (getFieldValue(candidate, toggleField) === parentKey) {
|
|
479
|
+
return candidate;
|
|
480
|
+
}
|
|
405
481
|
}
|
|
406
|
-
|
|
407
|
-
}
|
|
482
|
+
return void 0;
|
|
483
|
+
};
|
|
408
484
|
const rootItems = [];
|
|
409
485
|
dataWithLevels.forEach((item) => {
|
|
410
486
|
if (!getFieldValue(item, childField)) {
|
|
@@ -412,29 +488,18 @@ var useConvertTreeData = ({
|
|
|
412
488
|
item.processed = true;
|
|
413
489
|
}
|
|
414
490
|
});
|
|
415
|
-
dataWithLevels.forEach((item) => {
|
|
491
|
+
dataWithLevels.forEach((item, index) => {
|
|
416
492
|
const parentKey = getFieldValue(item, childField);
|
|
417
493
|
if (!parentKey || item.processed) return;
|
|
418
|
-
const
|
|
419
|
-
|
|
420
|
-
);
|
|
421
|
-
if (parentItems.length > 0) {
|
|
422
|
-
const parent = parentItems[0];
|
|
494
|
+
const parent = findNearestPrecedingParent(index, parentKey);
|
|
495
|
+
if (parent) {
|
|
423
496
|
item.level = parent.level + 1;
|
|
424
497
|
parent.children.push(item);
|
|
425
498
|
item.processed = true;
|
|
426
|
-
|
|
427
|
-
const otherParents = itemMap.get(String(parentKey)) || [];
|
|
428
|
-
if (otherParents.length > 0) {
|
|
429
|
-
const parent = otherParents[0];
|
|
430
|
-
item.level = parent.level + 1;
|
|
431
|
-
parent.children.push(item);
|
|
432
|
-
item.processed = true;
|
|
433
|
-
} else {
|
|
434
|
-
rootItems.push(item);
|
|
435
|
-
item.processed = true;
|
|
436
|
-
}
|
|
499
|
+
return;
|
|
437
500
|
}
|
|
501
|
+
rootItems.push(item);
|
|
502
|
+
item.processed = true;
|
|
438
503
|
});
|
|
439
504
|
return rootItems;
|
|
440
505
|
}, [enabled, data, toggleField, childField, flattenField]);
|
|
@@ -459,16 +524,23 @@ var useConvertTreeData = ({
|
|
|
459
524
|
return result;
|
|
460
525
|
};
|
|
461
526
|
const flattenedData = flatten(processedData, [], 0);
|
|
462
|
-
flattenedData.forEach((item) => {
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
(parent) => getFieldValue(parent, toggleField) === getFieldValue(item, childField)
|
|
466
|
-
);
|
|
467
|
-
const parentAmount = parentItem ? Number(getFieldValue(parentItem, qtyField) ?? 1) : 1;
|
|
468
|
-
item.parentCount = parentAmount || 1;
|
|
469
|
-
} else {
|
|
527
|
+
flattenedData.forEach((item, index) => {
|
|
528
|
+
const parentKey = getFieldValue(item, childField);
|
|
529
|
+
if (!parentKey) {
|
|
470
530
|
item.parentCount = 1;
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
let parentItem;
|
|
534
|
+
for (let i = index - 1; i >= 0; i -= 1) {
|
|
535
|
+
const candidate = flattenedData[i];
|
|
536
|
+
if (!candidate) continue;
|
|
537
|
+
if (getFieldValue(candidate, toggleField) === parentKey) {
|
|
538
|
+
parentItem = candidate;
|
|
539
|
+
break;
|
|
540
|
+
}
|
|
471
541
|
}
|
|
542
|
+
const parentAmount = parentItem ? Number(getFieldValue(parentItem, qtyField) ?? 1) : 1;
|
|
543
|
+
item.parentCount = parentAmount || 1;
|
|
472
544
|
});
|
|
473
545
|
return flattenedData;
|
|
474
546
|
}, [
|
|
@@ -740,11 +812,10 @@ function DataTableRow({
|
|
|
740
812
|
const { classNames, rowSpan, selection, cellSelection, cellEdit, expand } = useDataTableRowContext();
|
|
741
813
|
const {
|
|
742
814
|
enableRowSpan,
|
|
743
|
-
|
|
815
|
+
primaryRowSpanColumnId,
|
|
744
816
|
columnRowSpanMap,
|
|
745
817
|
hoveredRowIndex,
|
|
746
|
-
|
|
747
|
-
selectedGroupKeys,
|
|
818
|
+
selectedRowIndices,
|
|
748
819
|
onRowHover
|
|
749
820
|
} = rowSpan;
|
|
750
821
|
const { rowSelectionMode, selectOnRowClick, onRowClick, getRowClassName } = selection;
|
|
@@ -777,9 +848,11 @@ function DataTableRow({
|
|
|
777
848
|
const rowData = row.original;
|
|
778
849
|
const isRowHovered = hoveredRowIndex === rowIndex;
|
|
779
850
|
const isRowSelected = row.getIsSelected();
|
|
780
|
-
const
|
|
781
|
-
|
|
782
|
-
|
|
851
|
+
const { startRow: primaryGroupStart, rowSpan: primaryGroupSpan } = resolveRowSpanAt(
|
|
852
|
+
primaryRowSpanColumnId ? columnRowSpanMap.get(primaryRowSpanColumnId) : void 0,
|
|
853
|
+
rowIndex
|
|
854
|
+
);
|
|
855
|
+
const isGroupHovered = enableRowSpan && hoveredRowIndex !== null && hoveredRowIndex >= primaryGroupStart && hoveredRowIndex <= primaryGroupStart + primaryGroupSpan - 1;
|
|
783
856
|
const visibleCells = row.getVisibleCells();
|
|
784
857
|
const columnIdsByIndex = visibleCells.map((cell) => cell.column.id);
|
|
785
858
|
const isVisuallySelectedAt = activeSelectionBounds ? (targetRow, targetCol) => {
|
|
@@ -853,9 +926,19 @@ function DataTableRow({
|
|
|
853
926
|
}
|
|
854
927
|
}
|
|
855
928
|
const isEditing = editingCell?.rowIndex === rowIndex && editingCell?.colIndex === cellIndex;
|
|
856
|
-
const showCellHover = isRowSpanColumn ? isGroupHovered : isRowHovered;
|
|
857
|
-
const showCellSelected = isRowSpanColumn ? isGroupSelected : isRowSelected;
|
|
858
929
|
const cellRowSpan = rowSpanInfo?.rowSpan ?? 1;
|
|
930
|
+
const isMergedCellHovered = hoveredRowIndex !== null && hoveredRowIndex >= rowIndex && hoveredRowIndex <= rowIndex + cellRowSpan - 1;
|
|
931
|
+
const showCellHover = isRowSpanColumn ? isMergedCellHovered : isRowHovered;
|
|
932
|
+
let isMergedCellSelected = false;
|
|
933
|
+
if (isRowSpanColumn) {
|
|
934
|
+
for (let r = rowIndex; r < rowIndex + cellRowSpan; r += 1) {
|
|
935
|
+
if (selectedRowIndices.has(r)) {
|
|
936
|
+
isMergedCellSelected = true;
|
|
937
|
+
break;
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
const showCellSelected = isRowSpanColumn ? isMergedCellSelected : isRowSelected;
|
|
859
942
|
const isMerged = cellRowSpan > 1;
|
|
860
943
|
const showMergedRightEdge = isMerged && (cellIndex === visibleCells.length - 1 || resolveRowSpanAt(
|
|
861
944
|
columnRowSpanMap.get(columnIdsByIndex[cellIndex + 1]),
|
|
@@ -868,12 +951,14 @@ function DataTableRow({
|
|
|
868
951
|
cellRowSpan
|
|
869
952
|
);
|
|
870
953
|
const isBottomRightCell = !isEditing && activeSelectionBounds && !dragState.isSelecting && activeSelectionBounds.endRow >= rowIndex && activeSelectionBounds.endRow <= rowIndex + cellRowSpan - 1 && cellIndex === activeSelectionBounds.endCol;
|
|
954
|
+
const spanRowHeights = enableCellSelection && activeSelectionBounds && isCellDragSelected && cellRowSpan > 1 ? measureMergedSpanRowHeights(rowIndex, cellRowSpan) : void 0;
|
|
871
955
|
const selectionEdgeStyle = getCellSelectionEdgeStyle(
|
|
872
956
|
rowIndex,
|
|
873
957
|
cellIndex,
|
|
874
958
|
activeSelectionBounds,
|
|
875
959
|
cellRowSpan,
|
|
876
|
-
isVisuallySelectedAt
|
|
960
|
+
isVisuallySelectedAt,
|
|
961
|
+
spanRowHeights
|
|
877
962
|
);
|
|
878
963
|
const resolveCellRowIndex = (clientY, element) => getRowIndexInMergedCell(clientY, element, rowIndex, cellRowSpan);
|
|
879
964
|
const hasSelectionEdges = hasCellSelectionEdges(selectionEdgeStyle);
|
|
@@ -883,6 +968,7 @@ function DataTableRow({
|
|
|
883
968
|
rowSpan: rowSpanInfo && rowSpanInfo.rowSpan > 1 ? rowSpanInfo.rowSpan : void 0,
|
|
884
969
|
"data-merged": isMerged && cellIndex > 0 ? "" : void 0,
|
|
885
970
|
"data-merged-edge-right": showMergedRightEdge ? "" : void 0,
|
|
971
|
+
"data-merged-row-first": isMerged && cellIndex === 0 && showMergedRightEdge ? "" : void 0,
|
|
886
972
|
"data-group-selected": enableRowSpan && showCellSelected ? "" : void 0,
|
|
887
973
|
"data-group-hovered": enableRowSpan && showCellHover && !showCellSelected ? "" : void 0,
|
|
888
974
|
"data-selection-fill": isCellDragSelected ? "" : void 0,
|
|
@@ -927,7 +1013,8 @@ function DataTableRow({
|
|
|
927
1013
|
"data-table-cell",
|
|
928
1014
|
CELL_ALIGN_CLASS[align],
|
|
929
1015
|
cellClassName,
|
|
930
|
-
isMerged &&
|
|
1016
|
+
isMerged && "is-merged",
|
|
1017
|
+
isMerged && cellIndex === 0 && showMergedRightEdge && "is-merged-row-first",
|
|
931
1018
|
showMergedRightEdge && "is-merged-edge-right",
|
|
932
1019
|
enableRowSpan && showCellSelected && "is-group-selected",
|
|
933
1020
|
enableRowSpan && showCellHover && !showCellSelected && "is-group-hovered",
|
|
@@ -1176,6 +1263,108 @@ function useCellEdit({
|
|
|
1176
1263
|
// src/components/ui/table/features/cell-selection/useCellSelection.ts
|
|
1177
1264
|
var import_react5 = require("react");
|
|
1178
1265
|
|
|
1266
|
+
// src/components/ui/table/features/cell-selection/copyData.ts
|
|
1267
|
+
function formatCellValue(value) {
|
|
1268
|
+
if (value === null || value === void 0) return "";
|
|
1269
|
+
return String(value);
|
|
1270
|
+
}
|
|
1271
|
+
function getNestedValue(row, path) {
|
|
1272
|
+
if (!path.includes(".")) return row[path];
|
|
1273
|
+
return path.split(".").reduce((current, key) => {
|
|
1274
|
+
if (current === null || current === void 0 || typeof current !== "object") {
|
|
1275
|
+
return void 0;
|
|
1276
|
+
}
|
|
1277
|
+
return current[key];
|
|
1278
|
+
}, row);
|
|
1279
|
+
}
|
|
1280
|
+
function readRowColumnValue(rowData, columnDef) {
|
|
1281
|
+
if ("accessorFn" in columnDef && typeof columnDef.accessorFn === "function") {
|
|
1282
|
+
return columnDef.accessorFn(rowData, 0);
|
|
1283
|
+
}
|
|
1284
|
+
if ("accessorKey" in columnDef && columnDef.accessorKey != null && columnDef.accessorKey !== "") {
|
|
1285
|
+
return getNestedValue(rowData, String(columnDef.accessorKey));
|
|
1286
|
+
}
|
|
1287
|
+
return void 0;
|
|
1288
|
+
}
|
|
1289
|
+
function hasSubtree(row) {
|
|
1290
|
+
const children = row.children;
|
|
1291
|
+
return Array.isArray(children) && children.length > 0;
|
|
1292
|
+
}
|
|
1293
|
+
function getOriginalRowId(original) {
|
|
1294
|
+
return String(original.id ?? original.uniqueId ?? "");
|
|
1295
|
+
}
|
|
1296
|
+
function getRowDepth(original) {
|
|
1297
|
+
return typeof original.level === "number" ? original.level : 0;
|
|
1298
|
+
}
|
|
1299
|
+
function collectCopyRowEntries(visibleRows, bounds, mode = "visible") {
|
|
1300
|
+
const { startRow, endRow } = bounds;
|
|
1301
|
+
const result = [];
|
|
1302
|
+
const includedOriginalIds = /* @__PURE__ */ new Set();
|
|
1303
|
+
const appendSubtree = (node, depth) => {
|
|
1304
|
+
const children = node.children;
|
|
1305
|
+
if (!Array.isArray(children) || children.length === 0) return;
|
|
1306
|
+
for (const child of children) {
|
|
1307
|
+
const childId = getOriginalRowId(child);
|
|
1308
|
+
if (!(childId && includedOriginalIds.has(childId))) {
|
|
1309
|
+
result.push({ row: child, depth });
|
|
1310
|
+
if (childId) includedOriginalIds.add(childId);
|
|
1311
|
+
}
|
|
1312
|
+
appendSubtree(child, depth + 1);
|
|
1313
|
+
}
|
|
1314
|
+
};
|
|
1315
|
+
for (let rowIndex = startRow; rowIndex <= endRow; rowIndex += 1) {
|
|
1316
|
+
const row = visibleRows[rowIndex];
|
|
1317
|
+
if (!row) continue;
|
|
1318
|
+
const originalId = getOriginalRowId(row.original);
|
|
1319
|
+
if (originalId && includedOriginalIds.has(originalId)) continue;
|
|
1320
|
+
const depth = getRowDepth(row.original);
|
|
1321
|
+
result.push({ row: row.original, depth });
|
|
1322
|
+
if (originalId) includedOriginalIds.add(originalId);
|
|
1323
|
+
if (mode !== "subtree" || !hasSubtree(row.original)) continue;
|
|
1324
|
+
appendSubtree(row.original, depth + 1);
|
|
1325
|
+
}
|
|
1326
|
+
return result;
|
|
1327
|
+
}
|
|
1328
|
+
function serializeCopyRowsToTSV(copyRows, visibleRows, bounds, depths) {
|
|
1329
|
+
if (copyRows.length === 0) return "";
|
|
1330
|
+
const { startCol, endCol } = bounds;
|
|
1331
|
+
const columnCells = visibleRows[0]?.getVisibleCells().slice(startCol, endCol + 1) ?? [];
|
|
1332
|
+
if (columnCells.length === 0) return "";
|
|
1333
|
+
const resolvedDepths = depths && depths.length === copyRows.length ? depths : copyRows.map((row) => getRowDepth(row));
|
|
1334
|
+
const minDepth = Math.min(...resolvedDepths);
|
|
1335
|
+
return copyRows.map((rowData, index) => {
|
|
1336
|
+
const relativeDepth = Math.max(0, (resolvedDepths[index] ?? 0) - minDepth);
|
|
1337
|
+
const line = columnCells.map(
|
|
1338
|
+
(cell) => formatCellValue(
|
|
1339
|
+
readRowColumnValue(
|
|
1340
|
+
rowData,
|
|
1341
|
+
cell.column.columnDef
|
|
1342
|
+
)
|
|
1343
|
+
)
|
|
1344
|
+
).join(" ");
|
|
1345
|
+
return `${" ".repeat(relativeDepth)}${line}`;
|
|
1346
|
+
}).join("\n");
|
|
1347
|
+
}
|
|
1348
|
+
function serializeSelectionToTSV(visibleRows, bounds, mode = "visible") {
|
|
1349
|
+
const entries = collectCopyRowEntries(visibleRows, bounds, mode);
|
|
1350
|
+
return serializeCopyRowsToTSV(
|
|
1351
|
+
entries.map((entry) => entry.row),
|
|
1352
|
+
visibleRows,
|
|
1353
|
+
bounds,
|
|
1354
|
+
entries.map((entry) => entry.depth)
|
|
1355
|
+
);
|
|
1356
|
+
}
|
|
1357
|
+
async function writeSelectionToClipboard(visibleRows, bounds, mode = "visible") {
|
|
1358
|
+
const text = serializeSelectionToTSV(visibleRows, bounds, mode);
|
|
1359
|
+
if (!text) return false;
|
|
1360
|
+
try {
|
|
1361
|
+
await navigator.clipboard.writeText(text);
|
|
1362
|
+
} catch {
|
|
1363
|
+
return false;
|
|
1364
|
+
}
|
|
1365
|
+
return true;
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1179
1368
|
// src/components/ui/table/features/cell-selection/fillData.ts
|
|
1180
1369
|
function getColumnAccessorKey2(columnDef) {
|
|
1181
1370
|
if ("accessorKey" in columnDef && columnDef.accessorKey) {
|
|
@@ -1232,15 +1421,100 @@ function hasFillExtension(sourceBounds, fillBounds) {
|
|
|
1232
1421
|
return fillBounds.startRow < sourceBounds.startRow || fillBounds.endRow > sourceBounds.endRow || fillBounds.startCol < sourceBounds.startCol || fillBounds.endCol > sourceBounds.endCol;
|
|
1233
1422
|
}
|
|
1234
1423
|
|
|
1424
|
+
// src/components/ui/table/features/cell-selection/pasteData.ts
|
|
1425
|
+
function countLeadingEmptyCells(cells) {
|
|
1426
|
+
let depth = 0;
|
|
1427
|
+
while (depth < cells.length && cells[depth] === "") {
|
|
1428
|
+
depth += 1;
|
|
1429
|
+
}
|
|
1430
|
+
return depth;
|
|
1431
|
+
}
|
|
1432
|
+
function looksLikeSubtreeIndentation(leadingEmptyCounts) {
|
|
1433
|
+
if (leadingEmptyCounts.length === 0) return false;
|
|
1434
|
+
const firstDepth = leadingEmptyCounts[0] ?? 0;
|
|
1435
|
+
if (firstDepth !== 0) return false;
|
|
1436
|
+
return leadingEmptyCounts.some((depth) => depth > 0);
|
|
1437
|
+
}
|
|
1438
|
+
function parseClipboardTSVWithDepths(text) {
|
|
1439
|
+
if (!text) return { values: [], depths: [] };
|
|
1440
|
+
const normalized = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
1441
|
+
const withoutTrailing = normalized.replace(/\n+$/, "");
|
|
1442
|
+
if (!withoutTrailing) return { values: [], depths: [] };
|
|
1443
|
+
const rows = withoutTrailing.split("\n").map((line) => line.split(" "));
|
|
1444
|
+
const leadingEmptyCounts = rows.map(countLeadingEmptyCells);
|
|
1445
|
+
const treatAsDepth = looksLikeSubtreeIndentation(leadingEmptyCounts);
|
|
1446
|
+
const values = [];
|
|
1447
|
+
const depths = [];
|
|
1448
|
+
for (let index = 0; index < rows.length; index += 1) {
|
|
1449
|
+
const cells = rows[index] ?? [];
|
|
1450
|
+
const depth = leadingEmptyCounts[index] ?? 0;
|
|
1451
|
+
if (treatAsDepth) {
|
|
1452
|
+
values.push(cells.slice(depth));
|
|
1453
|
+
depths.push(depth);
|
|
1454
|
+
} else {
|
|
1455
|
+
values.push(cells);
|
|
1456
|
+
depths.push(0);
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
return { values, depths };
|
|
1460
|
+
}
|
|
1461
|
+
function resolvePasteColumnIds(rows, startCol, width) {
|
|
1462
|
+
if (width <= 0) return [];
|
|
1463
|
+
const cells = rows[0]?.getVisibleCells() ?? [];
|
|
1464
|
+
const columnIds = [];
|
|
1465
|
+
for (let offset = 0; offset < width; offset += 1) {
|
|
1466
|
+
const cell = cells[startCol + offset];
|
|
1467
|
+
if (!cell) break;
|
|
1468
|
+
columnIds.push(cell.column.id);
|
|
1469
|
+
}
|
|
1470
|
+
return columnIds;
|
|
1471
|
+
}
|
|
1472
|
+
function buildRowsPastePayload(rows, startRow, startCol, text, mode, endRow = startRow) {
|
|
1473
|
+
const { values, depths } = parseClipboardTSVWithDepths(text);
|
|
1474
|
+
if (values.length === 0) return null;
|
|
1475
|
+
const width = Math.max(...values.map((row) => row.length), 0);
|
|
1476
|
+
if (width === 0) return null;
|
|
1477
|
+
const columnIds = resolvePasteColumnIds(rows, startCol, width);
|
|
1478
|
+
if (columnIds.length === 0) return null;
|
|
1479
|
+
const rowIds = [];
|
|
1480
|
+
for (let offset = 0; offset < values.length; offset += 1) {
|
|
1481
|
+
const row = rows[startRow + offset];
|
|
1482
|
+
if (!row) break;
|
|
1483
|
+
rowIds.push(row.id);
|
|
1484
|
+
}
|
|
1485
|
+
const anchorRow = rows[endRow] ?? rows[startRow];
|
|
1486
|
+
return {
|
|
1487
|
+
mode,
|
|
1488
|
+
startRow,
|
|
1489
|
+
startCol,
|
|
1490
|
+
endRow,
|
|
1491
|
+
rowIds,
|
|
1492
|
+
anchorRowId: anchorRow?.id ?? "",
|
|
1493
|
+
columnIds,
|
|
1494
|
+
values,
|
|
1495
|
+
depths
|
|
1496
|
+
};
|
|
1497
|
+
}
|
|
1498
|
+
function isEditablePasteTarget(target) {
|
|
1499
|
+
if (!(target instanceof HTMLElement)) return false;
|
|
1500
|
+
const tag = target.tagName;
|
|
1501
|
+
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
|
|
1502
|
+
return Boolean(target.isContentEditable);
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1235
1505
|
// src/components/ui/table/features/cell-selection/useCellSelection.ts
|
|
1236
1506
|
function useCellSelection({
|
|
1237
1507
|
data,
|
|
1238
1508
|
rows,
|
|
1239
1509
|
enabled = true,
|
|
1510
|
+
enableSubtreeCopy = false,
|
|
1511
|
+
enableInsertPaste = true,
|
|
1240
1512
|
onDataChange,
|
|
1241
|
-
onBatchChange
|
|
1513
|
+
onBatchChange,
|
|
1514
|
+
onRowsPaste
|
|
1242
1515
|
}) {
|
|
1243
1516
|
const [dragState, setDragState] = (0, import_react5.useState)(INITIAL_DRAG_STATE);
|
|
1517
|
+
const pendingPasteModeRef = (0, import_react5.useRef)(null);
|
|
1244
1518
|
const cellSelectionBounds = getCellSelectionBounds(dragState.start, dragState.end);
|
|
1245
1519
|
const activeSelectionBounds = enabled ? getActiveSelectionBounds(dragState, cellSelectionBounds) : null;
|
|
1246
1520
|
const handleCellMouseDown = (0, import_react5.useCallback)(
|
|
@@ -1294,21 +1568,113 @@ function useCellSelection({
|
|
|
1294
1568
|
setDragState(INITIAL_DRAG_STATE);
|
|
1295
1569
|
}
|
|
1296
1570
|
}, [enabled]);
|
|
1571
|
+
const copySelection = (0, import_react5.useCallback)(
|
|
1572
|
+
async (options) => {
|
|
1573
|
+
if (!enabled || !activeSelectionBounds) return false;
|
|
1574
|
+
const mode = options?.includeDescendants && enableSubtreeCopy ? "subtree" : "visible";
|
|
1575
|
+
return writeSelectionToClipboard(rows, activeSelectionBounds, mode);
|
|
1576
|
+
},
|
|
1577
|
+
[activeSelectionBounds, enableSubtreeCopy, enabled, rows]
|
|
1578
|
+
);
|
|
1297
1579
|
(0, import_react5.useEffect)(() => {
|
|
1298
1580
|
if (!enabled) return;
|
|
1299
1581
|
const handleKeyDown = (e) => {
|
|
1300
|
-
if (
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
}
|
|
1582
|
+
if (!activeSelectionBounds) return;
|
|
1583
|
+
if (!(e.ctrlKey || e.metaKey)) return;
|
|
1584
|
+
const isSubtreeShortcut = enableSubtreeCopy && e.shiftKey && e.key.toLowerCase() === "c";
|
|
1585
|
+
const isVisibleCopyShortcut = !e.shiftKey && e.key.toLowerCase() === "c";
|
|
1586
|
+
if (!isSubtreeShortcut && !isVisibleCopyShortcut) return;
|
|
1587
|
+
e.preventDefault();
|
|
1588
|
+
void copySelection({ includeDescendants: isSubtreeShortcut });
|
|
1308
1589
|
};
|
|
1309
1590
|
window.addEventListener("keydown", handleKeyDown);
|
|
1310
1591
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
1311
|
-
}, [activeSelectionBounds,
|
|
1592
|
+
}, [activeSelectionBounds, copySelection, enableSubtreeCopy, enabled]);
|
|
1593
|
+
const emitRowsPaste = (0, import_react5.useCallback)(
|
|
1594
|
+
(text, mode) => {
|
|
1595
|
+
if (!onRowsPaste || !activeSelectionBounds) return false;
|
|
1596
|
+
const payload = buildRowsPastePayload(
|
|
1597
|
+
rows,
|
|
1598
|
+
activeSelectionBounds.startRow,
|
|
1599
|
+
activeSelectionBounds.startCol,
|
|
1600
|
+
text,
|
|
1601
|
+
mode,
|
|
1602
|
+
activeSelectionBounds.endRow
|
|
1603
|
+
);
|
|
1604
|
+
if (!payload) return false;
|
|
1605
|
+
onRowsPaste(payload);
|
|
1606
|
+
return true;
|
|
1607
|
+
},
|
|
1608
|
+
[activeSelectionBounds, onRowsPaste, rows]
|
|
1609
|
+
);
|
|
1610
|
+
(0, import_react5.useEffect)(() => {
|
|
1611
|
+
if (!enabled || !onRowsPaste) return;
|
|
1612
|
+
const pasteHandledRef = { current: false };
|
|
1613
|
+
const ignoreNextPasteRef = { current: false };
|
|
1614
|
+
const handleKeyDown = (e) => {
|
|
1615
|
+
if (!activeSelectionBounds) return;
|
|
1616
|
+
if (!(e.ctrlKey || e.metaKey)) return;
|
|
1617
|
+
if (e.key.toLowerCase() !== "v") return;
|
|
1618
|
+
if (isEditablePasteTarget(e.target) || isEditablePasteTarget(document.activeElement)) {
|
|
1619
|
+
return;
|
|
1620
|
+
}
|
|
1621
|
+
if (e.shiftKey && !enableInsertPaste) {
|
|
1622
|
+
ignoreNextPasteRef.current = true;
|
|
1623
|
+
pendingPasteModeRef.current = null;
|
|
1624
|
+
return;
|
|
1625
|
+
}
|
|
1626
|
+
const mode = e.shiftKey ? "insert" : "overwrite";
|
|
1627
|
+
pasteHandledRef.current = false;
|
|
1628
|
+
ignoreNextPasteRef.current = false;
|
|
1629
|
+
pendingPasteModeRef.current = mode;
|
|
1630
|
+
void (async () => {
|
|
1631
|
+
try {
|
|
1632
|
+
const text = await navigator.clipboard.readText();
|
|
1633
|
+
if (pasteHandledRef.current) return;
|
|
1634
|
+
if (pendingPasteModeRef.current !== mode) return;
|
|
1635
|
+
if (!text) return;
|
|
1636
|
+
pasteHandledRef.current = true;
|
|
1637
|
+
emitRowsPaste(text, mode);
|
|
1638
|
+
pendingPasteModeRef.current = null;
|
|
1639
|
+
} catch {
|
|
1640
|
+
}
|
|
1641
|
+
})();
|
|
1642
|
+
};
|
|
1643
|
+
const handlePaste = (e) => {
|
|
1644
|
+
if (!activeSelectionBounds) return;
|
|
1645
|
+
if (isEditablePasteTarget(e.target) || isEditablePasteTarget(document.activeElement)) {
|
|
1646
|
+
return;
|
|
1647
|
+
}
|
|
1648
|
+
if (ignoreNextPasteRef.current) {
|
|
1649
|
+
ignoreNextPasteRef.current = false;
|
|
1650
|
+
pendingPasteModeRef.current = null;
|
|
1651
|
+
return;
|
|
1652
|
+
}
|
|
1653
|
+
const mode = pendingPasteModeRef.current ?? "overwrite";
|
|
1654
|
+
if (pasteHandledRef.current) {
|
|
1655
|
+
e.preventDefault();
|
|
1656
|
+
return;
|
|
1657
|
+
}
|
|
1658
|
+
const text = e.clipboardData?.getData("text/plain");
|
|
1659
|
+
if (text == null || text === "") return;
|
|
1660
|
+
pasteHandledRef.current = true;
|
|
1661
|
+
e.preventDefault();
|
|
1662
|
+
emitRowsPaste(text, mode);
|
|
1663
|
+
pendingPasteModeRef.current = null;
|
|
1664
|
+
};
|
|
1665
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
1666
|
+
window.addEventListener("paste", handlePaste);
|
|
1667
|
+
return () => {
|
|
1668
|
+
window.removeEventListener("keydown", handleKeyDown);
|
|
1669
|
+
window.removeEventListener("paste", handlePaste);
|
|
1670
|
+
};
|
|
1671
|
+
}, [
|
|
1672
|
+
activeSelectionBounds,
|
|
1673
|
+
emitRowsPaste,
|
|
1674
|
+
enableInsertPaste,
|
|
1675
|
+
enabled,
|
|
1676
|
+
onRowsPaste
|
|
1677
|
+
]);
|
|
1312
1678
|
(0, import_react5.useEffect)(() => {
|
|
1313
1679
|
if (!enabled) return;
|
|
1314
1680
|
const handleMouseUp = () => {
|
|
@@ -1354,7 +1720,8 @@ function useCellSelection({
|
|
|
1354
1720
|
activeSelectionBounds,
|
|
1355
1721
|
handleCellMouseDown,
|
|
1356
1722
|
handleCellMouseEnter,
|
|
1357
|
-
handleFillHandleMouseDown
|
|
1723
|
+
handleFillHandleMouseDown,
|
|
1724
|
+
copySelection
|
|
1358
1725
|
};
|
|
1359
1726
|
}
|
|
1360
1727
|
|
|
@@ -1418,6 +1785,10 @@ function useGlideTable(options) {
|
|
|
1418
1785
|
expandedRows: controlledExpandedRows,
|
|
1419
1786
|
onExpandedRowsChange,
|
|
1420
1787
|
preventExpand = false,
|
|
1788
|
+
enableSubtreeCopy,
|
|
1789
|
+
onCopyActionsReady,
|
|
1790
|
+
onRowsPaste,
|
|
1791
|
+
enableInsertPaste,
|
|
1421
1792
|
enableVirtualization = true,
|
|
1422
1793
|
estimateRowHeight = DATA_TABLE_ROW_HEIGHT,
|
|
1423
1794
|
virtualOverscan = DATA_TABLE_VIRTUAL_OVERSCAN
|
|
@@ -1432,12 +1803,12 @@ function useGlideTable(options) {
|
|
|
1432
1803
|
};
|
|
1433
1804
|
}, [labelsProp, emptyText, loadingText, selectionLabel]);
|
|
1434
1805
|
const enableExpand = Boolean(toggleField);
|
|
1806
|
+
const resolvedEnableSubtreeCopy = enableSubtreeCopy ?? enableExpand;
|
|
1435
1807
|
const [internalRowSelection, setInternalRowSelection] = (0, import_react6.useState)({});
|
|
1436
1808
|
const [internalExpandedRows, setInternalExpandedRows] = (0, import_react6.useState)(
|
|
1437
1809
|
() => /* @__PURE__ */ new Set()
|
|
1438
1810
|
);
|
|
1439
1811
|
const [hoveredRowIndex, setHoveredRowIndex] = (0, import_react6.useState)(null);
|
|
1440
|
-
const [hoveredGroupKey, setHoveredGroupKey] = (0, import_react6.useState)(null);
|
|
1441
1812
|
const scrollRef = (0, import_react6.useRef)(null);
|
|
1442
1813
|
const shouldVirtualize = enableVirtualization && !enableRowSpan;
|
|
1443
1814
|
(0, import_react6.useEffect)(() => {
|
|
@@ -1501,6 +1872,7 @@ function useGlideTable(options) {
|
|
|
1501
1872
|
return collectRowSpanColumns(columns);
|
|
1502
1873
|
}, [enableRowSpan, columns]);
|
|
1503
1874
|
const primaryRowSpanKey = rowSpanColumnKeys[0]?.rowSpanKey;
|
|
1875
|
+
const primaryRowSpanColumnId = rowSpanColumnKeys[0]?.columnId;
|
|
1504
1876
|
const columnRowSpanMap = (0, import_react6.useMemo)(
|
|
1505
1877
|
() => buildColumnRowSpanMap(tableData, rowSpanColumnKeys),
|
|
1506
1878
|
[tableData, rowSpanColumnKeys]
|
|
@@ -1519,27 +1891,29 @@ function useGlideTable(options) {
|
|
|
1519
1891
|
const totalSize = rowVirtualizer.getTotalSize();
|
|
1520
1892
|
const paddingTop = virtualRows.length > 0 ? virtualRows[0]?.start ?? 0 : 0;
|
|
1521
1893
|
const paddingBottom = virtualRows.length > 0 ? totalSize - (virtualRows[virtualRows.length - 1]?.end ?? 0) : 0;
|
|
1522
|
-
const
|
|
1523
|
-
|
|
1524
|
-
const keys = /* @__PURE__ */ new Set();
|
|
1894
|
+
const selectedRowIndices = (0, import_react6.useMemo)(() => {
|
|
1895
|
+
const indices = /* @__PURE__ */ new Set();
|
|
1525
1896
|
for (const selectedRow of selectedRows) {
|
|
1526
|
-
|
|
1527
|
-
if (value !== null && value !== void 0) keys.add(String(value));
|
|
1897
|
+
indices.add(selectedRow.index);
|
|
1528
1898
|
}
|
|
1529
|
-
return
|
|
1530
|
-
}, [
|
|
1899
|
+
return indices;
|
|
1900
|
+
}, [selectedRows]);
|
|
1531
1901
|
const {
|
|
1532
1902
|
dragState,
|
|
1533
1903
|
activeSelectionBounds,
|
|
1534
1904
|
handleCellMouseDown,
|
|
1535
1905
|
handleCellMouseEnter,
|
|
1536
|
-
handleFillHandleMouseDown
|
|
1906
|
+
handleFillHandleMouseDown,
|
|
1907
|
+
copySelection
|
|
1537
1908
|
} = useCellSelection({
|
|
1538
1909
|
data: tableData,
|
|
1539
1910
|
rows,
|
|
1540
1911
|
enabled: enableCellSelection,
|
|
1912
|
+
enableSubtreeCopy: resolvedEnableSubtreeCopy,
|
|
1913
|
+
enableInsertPaste: enableInsertPaste ?? true,
|
|
1541
1914
|
onDataChange,
|
|
1542
|
-
onBatchChange
|
|
1915
|
+
onBatchChange,
|
|
1916
|
+
onRowsPaste
|
|
1543
1917
|
});
|
|
1544
1918
|
const {
|
|
1545
1919
|
editingCell,
|
|
@@ -1561,22 +1935,10 @@ function useGlideTable(options) {
|
|
|
1561
1935
|
);
|
|
1562
1936
|
const clearHover = (0, import_react6.useCallback)(() => {
|
|
1563
1937
|
setHoveredRowIndex(null);
|
|
1564
|
-
setHoveredGroupKey(null);
|
|
1565
1938
|
}, []);
|
|
1566
|
-
const handleRowHover = (0, import_react6.useCallback)(
|
|
1567
|
-
(rowIndex
|
|
1568
|
-
|
|
1569
|
-
if (!primaryRowSpanKey) {
|
|
1570
|
-
setHoveredGroupKey(null);
|
|
1571
|
-
return;
|
|
1572
|
-
}
|
|
1573
|
-
const groupValue = rowData[primaryRowSpanKey];
|
|
1574
|
-
setHoveredGroupKey(
|
|
1575
|
-
groupValue === null || groupValue === void 0 ? null : String(groupValue)
|
|
1576
|
-
);
|
|
1577
|
-
},
|
|
1578
|
-
[primaryRowSpanKey]
|
|
1579
|
-
);
|
|
1939
|
+
const handleRowHover = (0, import_react6.useCallback)((rowIndex, _rowData) => {
|
|
1940
|
+
setHoveredRowIndex(rowIndex);
|
|
1941
|
+
}, []);
|
|
1580
1942
|
const handleToggleSelect = (0, import_react6.useCallback)(
|
|
1581
1943
|
(row) => {
|
|
1582
1944
|
if (!row.getCanSelect()) return;
|
|
@@ -1599,10 +1961,10 @@ function useGlideTable(options) {
|
|
|
1599
1961
|
rowSpan: {
|
|
1600
1962
|
enableRowSpan,
|
|
1601
1963
|
primaryRowSpanKey,
|
|
1964
|
+
primaryRowSpanColumnId,
|
|
1602
1965
|
columnRowSpanMap,
|
|
1603
1966
|
hoveredRowIndex,
|
|
1604
|
-
|
|
1605
|
-
selectedGroupKeys,
|
|
1967
|
+
selectedRowIndices,
|
|
1606
1968
|
onRowHover: handleRowHover
|
|
1607
1969
|
},
|
|
1608
1970
|
selection: {
|
|
@@ -1640,10 +2002,10 @@ function useGlideTable(options) {
|
|
|
1640
2002
|
}, [
|
|
1641
2003
|
enableRowSpan,
|
|
1642
2004
|
primaryRowSpanKey,
|
|
2005
|
+
primaryRowSpanColumnId,
|
|
1643
2006
|
columnRowSpanMap,
|
|
1644
2007
|
hoveredRowIndex,
|
|
1645
|
-
|
|
1646
|
-
selectedGroupKeys,
|
|
2008
|
+
selectedRowIndices,
|
|
1647
2009
|
handleRowHover,
|
|
1648
2010
|
rowSelectionMode,
|
|
1649
2011
|
selectOnRowClick,
|
|
@@ -1669,6 +2031,14 @@ function useGlideTable(options) {
|
|
|
1669
2031
|
labels.expandRow,
|
|
1670
2032
|
labels.collapseRow
|
|
1671
2033
|
]);
|
|
2034
|
+
const copySelectionRef = (0, import_react6.useRef)(copySelection);
|
|
2035
|
+
(0, import_react6.useEffect)(() => {
|
|
2036
|
+
copySelectionRef.current = copySelection;
|
|
2037
|
+
}, [copySelection]);
|
|
2038
|
+
const stableCopySelection = (0, import_react6.useCallback)((options2) => copySelectionRef.current(options2), []);
|
|
2039
|
+
(0, import_react6.useEffect)(() => {
|
|
2040
|
+
onCopyActionsReady?.({ copySelection: stableCopySelection });
|
|
2041
|
+
}, [onCopyActionsReady, stableCopySelection]);
|
|
1672
2042
|
return {
|
|
1673
2043
|
table,
|
|
1674
2044
|
tableData,
|
|
@@ -1688,7 +2058,8 @@ function useGlideTable(options) {
|
|
|
1688
2058
|
paddingBottom,
|
|
1689
2059
|
rowContextValue,
|
|
1690
2060
|
handleToggleSelect,
|
|
1691
|
-
clearHover
|
|
2061
|
+
clearHover,
|
|
2062
|
+
copySelection: stableCopySelection
|
|
1692
2063
|
};
|
|
1693
2064
|
}
|
|
1694
2065
|
|
|
@@ -2036,10 +2407,26 @@ function parseTableChildren(children) {
|
|
|
2036
2407
|
}
|
|
2037
2408
|
return slots;
|
|
2038
2409
|
}
|
|
2410
|
+
function flattenColumnElements(children) {
|
|
2411
|
+
const result = [];
|
|
2412
|
+
for (const child of import_react9.Children.toArray(children)) {
|
|
2413
|
+
if (isTableColumnElement(child)) {
|
|
2414
|
+
result.push(child);
|
|
2415
|
+
continue;
|
|
2416
|
+
}
|
|
2417
|
+
if ((0, import_react9.isValidElement)(child)) {
|
|
2418
|
+
const nested = child.props.children;
|
|
2419
|
+
if (nested != null) {
|
|
2420
|
+
result.push(...flattenColumnElements(nested));
|
|
2421
|
+
}
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
return result;
|
|
2425
|
+
}
|
|
2039
2426
|
function extractColumnElements(header) {
|
|
2040
2427
|
if (!header) return [];
|
|
2041
2428
|
const { children } = header.props;
|
|
2042
|
-
return
|
|
2429
|
+
return flattenColumnElements(children);
|
|
2043
2430
|
}
|
|
2044
2431
|
|
|
2045
2432
|
// src/components/ui/table/components/Table/TableBody.tsx
|