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.js
CHANGED
|
@@ -100,10 +100,36 @@ function getCellSelectionBounds(start, end) {
|
|
|
100
100
|
endCol: Math.max(start.col, end.col)
|
|
101
101
|
};
|
|
102
102
|
}
|
|
103
|
+
function measureMergedSpanRowHeights(rowIndex, rowSpan, cellElement) {
|
|
104
|
+
if (rowSpan <= 1) return void 0;
|
|
105
|
+
const tbody = cellElement?.closest("tbody") ?? (typeof document !== "undefined" ? document.querySelector("tbody.data-table-body") : null);
|
|
106
|
+
if (!tbody) return void 0;
|
|
107
|
+
const rows = tbody.querySelectorAll(":scope > tr");
|
|
108
|
+
if (rows.length < rowIndex + rowSpan) return void 0;
|
|
109
|
+
const heights = [];
|
|
110
|
+
for (let i = 0; i < rowSpan; i++) {
|
|
111
|
+
const row = rows[rowIndex + i];
|
|
112
|
+
const height = row?.getBoundingClientRect().height ?? 0;
|
|
113
|
+
if (height <= 0) return void 0;
|
|
114
|
+
heights.push(height);
|
|
115
|
+
}
|
|
116
|
+
return heights;
|
|
117
|
+
}
|
|
103
118
|
function getRowIndexInMergedCell(clientY, cellElement, rowIndex, rowSpan) {
|
|
104
119
|
if (rowSpan <= 1) return rowIndex;
|
|
105
120
|
const rect = cellElement.getBoundingClientRect();
|
|
106
121
|
const relativeY = clientY - rect.top;
|
|
122
|
+
const heights = measureMergedSpanRowHeights(rowIndex, rowSpan, cellElement);
|
|
123
|
+
if (heights && heights.length === rowSpan) {
|
|
124
|
+
let accrued = 0;
|
|
125
|
+
for (let i = 0; i < rowSpan; i++) {
|
|
126
|
+
accrued += heights[i];
|
|
127
|
+
if (relativeY < accrued) {
|
|
128
|
+
return rowIndex + i;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return rowIndex + rowSpan - 1;
|
|
132
|
+
}
|
|
107
133
|
const rowHeight = rect.height / rowSpan;
|
|
108
134
|
const offset = Math.min(
|
|
109
135
|
Math.max(Math.floor(relativeY / rowHeight), 0),
|
|
@@ -125,7 +151,35 @@ function getActiveSelectionBounds(dragState, selectionBounds) {
|
|
|
125
151
|
var SELECTION_EDGE_WIDTH_PX = 2;
|
|
126
152
|
var SELECTION_EDGE_COLOR = "var(--color-brand-primary)";
|
|
127
153
|
var CELL_SELECTION_EDGES_CLASS = "cell-selection-edges";
|
|
128
|
-
function
|
|
154
|
+
function rowRangeToHeightRatios(rowIndex, span, fromRow, toRowExclusive, rowHeights) {
|
|
155
|
+
const clampedFrom = Math.max(fromRow, rowIndex);
|
|
156
|
+
const clampedTo = Math.min(toRowExclusive, rowIndex + span);
|
|
157
|
+
if (clampedTo <= clampedFrom) {
|
|
158
|
+
return { offsetRatio: 0, lengthRatio: 0 };
|
|
159
|
+
}
|
|
160
|
+
if (!rowHeights || rowHeights.length !== span) {
|
|
161
|
+
return {
|
|
162
|
+
offsetRatio: (clampedFrom - rowIndex) / span,
|
|
163
|
+
lengthRatio: (clampedTo - clampedFrom) / span
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
const total = rowHeights.reduce((sum, height) => sum + height, 0) || 1;
|
|
167
|
+
let offsetPx = 0;
|
|
168
|
+
for (let i = 0; i < clampedFrom - rowIndex; i++) {
|
|
169
|
+
offsetPx += rowHeights[i] ?? 0;
|
|
170
|
+
}
|
|
171
|
+
let lengthPx = 0;
|
|
172
|
+
for (let i = clampedFrom - rowIndex; i < clampedTo - rowIndex; i++) {
|
|
173
|
+
lengthPx += rowHeights[i] ?? 0;
|
|
174
|
+
}
|
|
175
|
+
return {
|
|
176
|
+
offsetRatio: offsetPx / total,
|
|
177
|
+
lengthRatio: lengthPx / total,
|
|
178
|
+
offsetPx,
|
|
179
|
+
lengthPx
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
function getMergedCellStepEdges(rowIndex, colIndex, bounds, rowSpan, isVisuallySelectedAt, rowHeights) {
|
|
129
183
|
const cellEndRow = rowIndex + rowSpan - 1;
|
|
130
184
|
const span = cellEndRow - rowIndex + 1;
|
|
131
185
|
if (span <= 1) return [];
|
|
@@ -144,20 +198,30 @@ function getMergedCellStepEdges(rowIndex, colIndex, bounds, rowSpan, isVisuallyS
|
|
|
144
198
|
continue;
|
|
145
199
|
}
|
|
146
200
|
if (runStart !== null) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
201
|
+
const ratios = rowRangeToHeightRatios(
|
|
202
|
+
rowIndex,
|
|
203
|
+
span,
|
|
204
|
+
runStart,
|
|
205
|
+
row,
|
|
206
|
+
rowHeights
|
|
207
|
+
);
|
|
208
|
+
if (ratios.lengthRatio > 0) {
|
|
209
|
+
edges.push({ side, ...ratios });
|
|
210
|
+
}
|
|
152
211
|
runStart = null;
|
|
153
212
|
}
|
|
154
213
|
}
|
|
155
214
|
if (runStart !== null) {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
215
|
+
const ratios = rowRangeToHeightRatios(
|
|
216
|
+
rowIndex,
|
|
217
|
+
span,
|
|
218
|
+
runStart,
|
|
219
|
+
toRowExclusive,
|
|
220
|
+
rowHeights
|
|
221
|
+
);
|
|
222
|
+
if (ratios.lengthRatio > 0) {
|
|
223
|
+
edges.push({ side, ...ratios });
|
|
224
|
+
}
|
|
161
225
|
}
|
|
162
226
|
};
|
|
163
227
|
const collectSide = (side, neighborCol) => {
|
|
@@ -186,14 +250,17 @@ function getMergedCellStepEdges(rowIndex, colIndex, bounds, rowSpan, isVisuallyS
|
|
|
186
250
|
}
|
|
187
251
|
return edges;
|
|
188
252
|
}
|
|
189
|
-
function
|
|
253
|
+
function buildPartialEdgeGradient(edge) {
|
|
254
|
+
const usePx = edge.offsetPx != null && edge.lengthPx != null;
|
|
190
255
|
const startPct = edge.offsetRatio * 100;
|
|
191
|
-
const endPct = (edge.offsetRatio + edge.
|
|
192
|
-
const
|
|
193
|
-
const
|
|
194
|
-
const
|
|
195
|
-
const
|
|
196
|
-
const
|
|
256
|
+
const endPct = (edge.offsetRatio + edge.lengthRatio) * 100;
|
|
257
|
+
const startPx = edge.offsetPx ?? 0;
|
|
258
|
+
const endPx = (edge.offsetPx ?? 0) + (edge.lengthPx ?? 0);
|
|
259
|
+
const overlapPx = SELECTION_EDGE_WIDTH_PX;
|
|
260
|
+
const isTopProtrusion = (usePx ? startPx === 0 : edge.offsetRatio === 0) && edge.lengthRatio < 1;
|
|
261
|
+
const isBottomProtrusion = usePx ? startPx > 0 : edge.offsetRatio > 0;
|
|
262
|
+
const startStop = usePx ? isBottomProtrusion ? `${Math.max(0, startPx - overlapPx)}px` : `${startPx}px` : isBottomProtrusion ? `calc(${startPct}% - ${overlapPx}px)` : `${startPct}%`;
|
|
263
|
+
const endStop = usePx ? isTopProtrusion ? `${endPx + overlapPx}px` : `${endPx}px` : isTopProtrusion ? `calc(${endPct}% + ${overlapPx}px)` : `${endPct}%`;
|
|
197
264
|
const xPos = edge.side === "left" ? "0" : "100%";
|
|
198
265
|
const layers = [
|
|
199
266
|
{
|
|
@@ -203,7 +270,7 @@ function buildPartialVerticalGradient(edge) {
|
|
|
203
270
|
}
|
|
204
271
|
];
|
|
205
272
|
if (isTopProtrusion || isBottomProtrusion) {
|
|
206
|
-
const capTop = isTopProtrusion ? `calc(${endPct}% - ${SELECTION_EDGE_WIDTH_PX}px)` : `calc(${startPct}% - ${SELECTION_EDGE_WIDTH_PX}px)`;
|
|
273
|
+
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)`;
|
|
207
274
|
layers.push({
|
|
208
275
|
image: `linear-gradient(${SELECTION_EDGE_COLOR}, ${SELECTION_EDGE_COLOR})`,
|
|
209
276
|
size: `${SELECTION_EDGE_WIDTH_PX}px ${SELECTION_EDGE_WIDTH_PX}px`,
|
|
@@ -212,7 +279,7 @@ function buildPartialVerticalGradient(edge) {
|
|
|
212
279
|
}
|
|
213
280
|
return layers;
|
|
214
281
|
}
|
|
215
|
-
function getCellSelectionEdgeStyle(rowIndex, colIndex, bounds, rowSpan = 1, isVisuallySelectedAt) {
|
|
282
|
+
function getCellSelectionEdgeStyle(rowIndex, colIndex, bounds, rowSpan = 1, isVisuallySelectedAt, rowHeights) {
|
|
216
283
|
if (!isCellInSelection(rowIndex, colIndex, bounds, rowSpan) || !bounds)
|
|
217
284
|
return void 0;
|
|
218
285
|
const cellEndRow = rowIndex + rowSpan - 1;
|
|
@@ -221,39 +288,47 @@ function getCellSelectionEdgeStyle(rowIndex, colIndex, bounds, rowSpan = 1, isVi
|
|
|
221
288
|
const isLeftEdge = colIndex === bounds.startCol;
|
|
222
289
|
const isRightEdge = colIndex === bounds.endCol;
|
|
223
290
|
const selectionContinuesBelow = cellEndRow < bounds.endRow;
|
|
224
|
-
const shadows = [];
|
|
225
|
-
if (isTopEdge) {
|
|
226
|
-
shadows.push(
|
|
227
|
-
`inset 0 ${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`
|
|
228
|
-
);
|
|
229
|
-
}
|
|
230
|
-
if (isBottomEdge) {
|
|
231
|
-
shadows.push(
|
|
232
|
-
`inset 0 -${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`
|
|
233
|
-
);
|
|
234
|
-
}
|
|
235
|
-
if (isLeftEdge) {
|
|
236
|
-
shadows.push(
|
|
237
|
-
`inset ${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`
|
|
238
|
-
);
|
|
239
|
-
}
|
|
240
|
-
if (isRightEdge) {
|
|
241
|
-
shadows.push(
|
|
242
|
-
`inset -${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`
|
|
243
|
-
);
|
|
244
|
-
}
|
|
245
291
|
const stepEdges = getMergedCellStepEdges(
|
|
246
292
|
rowIndex,
|
|
247
293
|
colIndex,
|
|
248
294
|
bounds,
|
|
249
295
|
rowSpan,
|
|
250
|
-
isVisuallySelectedAt
|
|
296
|
+
isVisuallySelectedAt,
|
|
297
|
+
rowHeights
|
|
251
298
|
);
|
|
299
|
+
const shadows = [];
|
|
300
|
+
const hasFullPerimeter = isTopEdge && isBottomEdge && isLeftEdge && isRightEdge && stepEdges.length === 0;
|
|
301
|
+
if (hasFullPerimeter) {
|
|
302
|
+
shadows.push(
|
|
303
|
+
`inset 0 0 0 ${SELECTION_EDGE_WIDTH_PX}px ${SELECTION_EDGE_COLOR}`
|
|
304
|
+
);
|
|
305
|
+
} else {
|
|
306
|
+
if (isTopEdge) {
|
|
307
|
+
shadows.push(
|
|
308
|
+
`inset 0 ${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
if (isBottomEdge) {
|
|
312
|
+
shadows.push(
|
|
313
|
+
`inset 0 -${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
if (isLeftEdge) {
|
|
317
|
+
shadows.push(
|
|
318
|
+
`inset ${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
if (isRightEdge) {
|
|
322
|
+
shadows.push(
|
|
323
|
+
`inset -${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
252
327
|
const gradients = [];
|
|
253
328
|
const sizes = [];
|
|
254
329
|
const positions = [];
|
|
255
330
|
for (const edge of stepEdges) {
|
|
256
|
-
for (const partial of
|
|
331
|
+
for (const partial of buildPartialEdgeGradient(edge)) {
|
|
257
332
|
gradients.push(partial.image);
|
|
258
333
|
sizes.push(partial.size);
|
|
259
334
|
positions.push(partial.position);
|
|
@@ -368,15 +443,16 @@ var useConvertTreeData = ({
|
|
|
368
443
|
children: [],
|
|
369
444
|
processed: false
|
|
370
445
|
}));
|
|
371
|
-
const
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
446
|
+
const findNearestPrecedingParent = (index, parentKey) => {
|
|
447
|
+
for (let i = index - 1; i >= 0; i -= 1) {
|
|
448
|
+
const candidate = dataWithLevels[i];
|
|
449
|
+
if (!candidate) continue;
|
|
450
|
+
if (getFieldValue(candidate, toggleField) === parentKey) {
|
|
451
|
+
return candidate;
|
|
452
|
+
}
|
|
377
453
|
}
|
|
378
|
-
|
|
379
|
-
}
|
|
454
|
+
return void 0;
|
|
455
|
+
};
|
|
380
456
|
const rootItems = [];
|
|
381
457
|
dataWithLevels.forEach((item) => {
|
|
382
458
|
if (!getFieldValue(item, childField)) {
|
|
@@ -384,29 +460,18 @@ var useConvertTreeData = ({
|
|
|
384
460
|
item.processed = true;
|
|
385
461
|
}
|
|
386
462
|
});
|
|
387
|
-
dataWithLevels.forEach((item) => {
|
|
463
|
+
dataWithLevels.forEach((item, index) => {
|
|
388
464
|
const parentKey = getFieldValue(item, childField);
|
|
389
465
|
if (!parentKey || item.processed) return;
|
|
390
|
-
const
|
|
391
|
-
|
|
392
|
-
);
|
|
393
|
-
if (parentItems.length > 0) {
|
|
394
|
-
const parent = parentItems[0];
|
|
466
|
+
const parent = findNearestPrecedingParent(index, parentKey);
|
|
467
|
+
if (parent) {
|
|
395
468
|
item.level = parent.level + 1;
|
|
396
469
|
parent.children.push(item);
|
|
397
470
|
item.processed = true;
|
|
398
|
-
|
|
399
|
-
const otherParents = itemMap.get(String(parentKey)) || [];
|
|
400
|
-
if (otherParents.length > 0) {
|
|
401
|
-
const parent = otherParents[0];
|
|
402
|
-
item.level = parent.level + 1;
|
|
403
|
-
parent.children.push(item);
|
|
404
|
-
item.processed = true;
|
|
405
|
-
} else {
|
|
406
|
-
rootItems.push(item);
|
|
407
|
-
item.processed = true;
|
|
408
|
-
}
|
|
471
|
+
return;
|
|
409
472
|
}
|
|
473
|
+
rootItems.push(item);
|
|
474
|
+
item.processed = true;
|
|
410
475
|
});
|
|
411
476
|
return rootItems;
|
|
412
477
|
}, [enabled, data, toggleField, childField, flattenField]);
|
|
@@ -431,16 +496,23 @@ var useConvertTreeData = ({
|
|
|
431
496
|
return result;
|
|
432
497
|
};
|
|
433
498
|
const flattenedData = flatten(processedData, [], 0);
|
|
434
|
-
flattenedData.forEach((item) => {
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
(parent) => getFieldValue(parent, toggleField) === getFieldValue(item, childField)
|
|
438
|
-
);
|
|
439
|
-
const parentAmount = parentItem ? Number(getFieldValue(parentItem, qtyField) ?? 1) : 1;
|
|
440
|
-
item.parentCount = parentAmount || 1;
|
|
441
|
-
} else {
|
|
499
|
+
flattenedData.forEach((item, index) => {
|
|
500
|
+
const parentKey = getFieldValue(item, childField);
|
|
501
|
+
if (!parentKey) {
|
|
442
502
|
item.parentCount = 1;
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
let parentItem;
|
|
506
|
+
for (let i = index - 1; i >= 0; i -= 1) {
|
|
507
|
+
const candidate = flattenedData[i];
|
|
508
|
+
if (!candidate) continue;
|
|
509
|
+
if (getFieldValue(candidate, toggleField) === parentKey) {
|
|
510
|
+
parentItem = candidate;
|
|
511
|
+
break;
|
|
512
|
+
}
|
|
443
513
|
}
|
|
514
|
+
const parentAmount = parentItem ? Number(getFieldValue(parentItem, qtyField) ?? 1) : 1;
|
|
515
|
+
item.parentCount = parentAmount || 1;
|
|
444
516
|
});
|
|
445
517
|
return flattenedData;
|
|
446
518
|
}, [
|
|
@@ -712,11 +784,10 @@ function DataTableRow({
|
|
|
712
784
|
const { classNames, rowSpan, selection, cellSelection, cellEdit, expand } = useDataTableRowContext();
|
|
713
785
|
const {
|
|
714
786
|
enableRowSpan,
|
|
715
|
-
|
|
787
|
+
primaryRowSpanColumnId,
|
|
716
788
|
columnRowSpanMap,
|
|
717
789
|
hoveredRowIndex,
|
|
718
|
-
|
|
719
|
-
selectedGroupKeys,
|
|
790
|
+
selectedRowIndices,
|
|
720
791
|
onRowHover
|
|
721
792
|
} = rowSpan;
|
|
722
793
|
const { rowSelectionMode, selectOnRowClick, onRowClick, getRowClassName } = selection;
|
|
@@ -749,9 +820,11 @@ function DataTableRow({
|
|
|
749
820
|
const rowData = row.original;
|
|
750
821
|
const isRowHovered = hoveredRowIndex === rowIndex;
|
|
751
822
|
const isRowSelected = row.getIsSelected();
|
|
752
|
-
const
|
|
753
|
-
|
|
754
|
-
|
|
823
|
+
const { startRow: primaryGroupStart, rowSpan: primaryGroupSpan } = resolveRowSpanAt(
|
|
824
|
+
primaryRowSpanColumnId ? columnRowSpanMap.get(primaryRowSpanColumnId) : void 0,
|
|
825
|
+
rowIndex
|
|
826
|
+
);
|
|
827
|
+
const isGroupHovered = enableRowSpan && hoveredRowIndex !== null && hoveredRowIndex >= primaryGroupStart && hoveredRowIndex <= primaryGroupStart + primaryGroupSpan - 1;
|
|
755
828
|
const visibleCells = row.getVisibleCells();
|
|
756
829
|
const columnIdsByIndex = visibleCells.map((cell) => cell.column.id);
|
|
757
830
|
const isVisuallySelectedAt = activeSelectionBounds ? (targetRow, targetCol) => {
|
|
@@ -825,9 +898,19 @@ function DataTableRow({
|
|
|
825
898
|
}
|
|
826
899
|
}
|
|
827
900
|
const isEditing = editingCell?.rowIndex === rowIndex && editingCell?.colIndex === cellIndex;
|
|
828
|
-
const showCellHover = isRowSpanColumn ? isGroupHovered : isRowHovered;
|
|
829
|
-
const showCellSelected = isRowSpanColumn ? isGroupSelected : isRowSelected;
|
|
830
901
|
const cellRowSpan = rowSpanInfo?.rowSpan ?? 1;
|
|
902
|
+
const isMergedCellHovered = hoveredRowIndex !== null && hoveredRowIndex >= rowIndex && hoveredRowIndex <= rowIndex + cellRowSpan - 1;
|
|
903
|
+
const showCellHover = isRowSpanColumn ? isMergedCellHovered : isRowHovered;
|
|
904
|
+
let isMergedCellSelected = false;
|
|
905
|
+
if (isRowSpanColumn) {
|
|
906
|
+
for (let r = rowIndex; r < rowIndex + cellRowSpan; r += 1) {
|
|
907
|
+
if (selectedRowIndices.has(r)) {
|
|
908
|
+
isMergedCellSelected = true;
|
|
909
|
+
break;
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
const showCellSelected = isRowSpanColumn ? isMergedCellSelected : isRowSelected;
|
|
831
914
|
const isMerged = cellRowSpan > 1;
|
|
832
915
|
const showMergedRightEdge = isMerged && (cellIndex === visibleCells.length - 1 || resolveRowSpanAt(
|
|
833
916
|
columnRowSpanMap.get(columnIdsByIndex[cellIndex + 1]),
|
|
@@ -840,12 +923,14 @@ function DataTableRow({
|
|
|
840
923
|
cellRowSpan
|
|
841
924
|
);
|
|
842
925
|
const isBottomRightCell = !isEditing && activeSelectionBounds && !dragState.isSelecting && activeSelectionBounds.endRow >= rowIndex && activeSelectionBounds.endRow <= rowIndex + cellRowSpan - 1 && cellIndex === activeSelectionBounds.endCol;
|
|
926
|
+
const spanRowHeights = enableCellSelection && activeSelectionBounds && isCellDragSelected && cellRowSpan > 1 ? measureMergedSpanRowHeights(rowIndex, cellRowSpan) : void 0;
|
|
843
927
|
const selectionEdgeStyle = getCellSelectionEdgeStyle(
|
|
844
928
|
rowIndex,
|
|
845
929
|
cellIndex,
|
|
846
930
|
activeSelectionBounds,
|
|
847
931
|
cellRowSpan,
|
|
848
|
-
isVisuallySelectedAt
|
|
932
|
+
isVisuallySelectedAt,
|
|
933
|
+
spanRowHeights
|
|
849
934
|
);
|
|
850
935
|
const resolveCellRowIndex = (clientY, element) => getRowIndexInMergedCell(clientY, element, rowIndex, cellRowSpan);
|
|
851
936
|
const hasSelectionEdges = hasCellSelectionEdges(selectionEdgeStyle);
|
|
@@ -855,6 +940,7 @@ function DataTableRow({
|
|
|
855
940
|
rowSpan: rowSpanInfo && rowSpanInfo.rowSpan > 1 ? rowSpanInfo.rowSpan : void 0,
|
|
856
941
|
"data-merged": isMerged && cellIndex > 0 ? "" : void 0,
|
|
857
942
|
"data-merged-edge-right": showMergedRightEdge ? "" : void 0,
|
|
943
|
+
"data-merged-row-first": isMerged && cellIndex === 0 && showMergedRightEdge ? "" : void 0,
|
|
858
944
|
"data-group-selected": enableRowSpan && showCellSelected ? "" : void 0,
|
|
859
945
|
"data-group-hovered": enableRowSpan && showCellHover && !showCellSelected ? "" : void 0,
|
|
860
946
|
"data-selection-fill": isCellDragSelected ? "" : void 0,
|
|
@@ -899,7 +985,8 @@ function DataTableRow({
|
|
|
899
985
|
"data-table-cell",
|
|
900
986
|
CELL_ALIGN_CLASS[align],
|
|
901
987
|
cellClassName,
|
|
902
|
-
isMerged &&
|
|
988
|
+
isMerged && "is-merged",
|
|
989
|
+
isMerged && cellIndex === 0 && showMergedRightEdge && "is-merged-row-first",
|
|
903
990
|
showMergedRightEdge && "is-merged-edge-right",
|
|
904
991
|
enableRowSpan && showCellSelected && "is-group-selected",
|
|
905
992
|
enableRowSpan && showCellHover && !showCellSelected && "is-group-hovered",
|
|
@@ -1073,7 +1160,7 @@ import {
|
|
|
1073
1160
|
useCallback as useCallback3,
|
|
1074
1161
|
useEffect as useEffect5,
|
|
1075
1162
|
useMemo as useMemo2,
|
|
1076
|
-
useRef as
|
|
1163
|
+
useRef as useRef5,
|
|
1077
1164
|
useState as useState3
|
|
1078
1165
|
} from "react";
|
|
1079
1166
|
|
|
@@ -1157,7 +1244,109 @@ function useCellEdit({
|
|
|
1157
1244
|
}
|
|
1158
1245
|
|
|
1159
1246
|
// src/components/ui/table/features/cell-selection/useCellSelection.ts
|
|
1160
|
-
import { useCallback as useCallback2, useEffect as useEffect4, useState as useState2 } from "react";
|
|
1247
|
+
import { useCallback as useCallback2, useEffect as useEffect4, useRef as useRef4, useState as useState2 } from "react";
|
|
1248
|
+
|
|
1249
|
+
// src/components/ui/table/features/cell-selection/copyData.ts
|
|
1250
|
+
function formatCellValue(value) {
|
|
1251
|
+
if (value === null || value === void 0) return "";
|
|
1252
|
+
return String(value);
|
|
1253
|
+
}
|
|
1254
|
+
function getNestedValue(row, path) {
|
|
1255
|
+
if (!path.includes(".")) return row[path];
|
|
1256
|
+
return path.split(".").reduce((current, key) => {
|
|
1257
|
+
if (current === null || current === void 0 || typeof current !== "object") {
|
|
1258
|
+
return void 0;
|
|
1259
|
+
}
|
|
1260
|
+
return current[key];
|
|
1261
|
+
}, row);
|
|
1262
|
+
}
|
|
1263
|
+
function readRowColumnValue(rowData, columnDef) {
|
|
1264
|
+
if ("accessorFn" in columnDef && typeof columnDef.accessorFn === "function") {
|
|
1265
|
+
return columnDef.accessorFn(rowData, 0);
|
|
1266
|
+
}
|
|
1267
|
+
if ("accessorKey" in columnDef && columnDef.accessorKey != null && columnDef.accessorKey !== "") {
|
|
1268
|
+
return getNestedValue(rowData, String(columnDef.accessorKey));
|
|
1269
|
+
}
|
|
1270
|
+
return void 0;
|
|
1271
|
+
}
|
|
1272
|
+
function hasSubtree(row) {
|
|
1273
|
+
const children = row.children;
|
|
1274
|
+
return Array.isArray(children) && children.length > 0;
|
|
1275
|
+
}
|
|
1276
|
+
function getOriginalRowId(original) {
|
|
1277
|
+
return String(original.id ?? original.uniqueId ?? "");
|
|
1278
|
+
}
|
|
1279
|
+
function getRowDepth(original) {
|
|
1280
|
+
return typeof original.level === "number" ? original.level : 0;
|
|
1281
|
+
}
|
|
1282
|
+
function collectCopyRowEntries(visibleRows, bounds, mode = "visible") {
|
|
1283
|
+
const { startRow, endRow } = bounds;
|
|
1284
|
+
const result = [];
|
|
1285
|
+
const includedOriginalIds = /* @__PURE__ */ new Set();
|
|
1286
|
+
const appendSubtree = (node, depth) => {
|
|
1287
|
+
const children = node.children;
|
|
1288
|
+
if (!Array.isArray(children) || children.length === 0) return;
|
|
1289
|
+
for (const child of children) {
|
|
1290
|
+
const childId = getOriginalRowId(child);
|
|
1291
|
+
if (!(childId && includedOriginalIds.has(childId))) {
|
|
1292
|
+
result.push({ row: child, depth });
|
|
1293
|
+
if (childId) includedOriginalIds.add(childId);
|
|
1294
|
+
}
|
|
1295
|
+
appendSubtree(child, depth + 1);
|
|
1296
|
+
}
|
|
1297
|
+
};
|
|
1298
|
+
for (let rowIndex = startRow; rowIndex <= endRow; rowIndex += 1) {
|
|
1299
|
+
const row = visibleRows[rowIndex];
|
|
1300
|
+
if (!row) continue;
|
|
1301
|
+
const originalId = getOriginalRowId(row.original);
|
|
1302
|
+
if (originalId && includedOriginalIds.has(originalId)) continue;
|
|
1303
|
+
const depth = getRowDepth(row.original);
|
|
1304
|
+
result.push({ row: row.original, depth });
|
|
1305
|
+
if (originalId) includedOriginalIds.add(originalId);
|
|
1306
|
+
if (mode !== "subtree" || !hasSubtree(row.original)) continue;
|
|
1307
|
+
appendSubtree(row.original, depth + 1);
|
|
1308
|
+
}
|
|
1309
|
+
return result;
|
|
1310
|
+
}
|
|
1311
|
+
function serializeCopyRowsToTSV(copyRows, visibleRows, bounds, depths) {
|
|
1312
|
+
if (copyRows.length === 0) return "";
|
|
1313
|
+
const { startCol, endCol } = bounds;
|
|
1314
|
+
const columnCells = visibleRows[0]?.getVisibleCells().slice(startCol, endCol + 1) ?? [];
|
|
1315
|
+
if (columnCells.length === 0) return "";
|
|
1316
|
+
const resolvedDepths = depths && depths.length === copyRows.length ? depths : copyRows.map((row) => getRowDepth(row));
|
|
1317
|
+
const minDepth = Math.min(...resolvedDepths);
|
|
1318
|
+
return copyRows.map((rowData, index) => {
|
|
1319
|
+
const relativeDepth = Math.max(0, (resolvedDepths[index] ?? 0) - minDepth);
|
|
1320
|
+
const line = columnCells.map(
|
|
1321
|
+
(cell) => formatCellValue(
|
|
1322
|
+
readRowColumnValue(
|
|
1323
|
+
rowData,
|
|
1324
|
+
cell.column.columnDef
|
|
1325
|
+
)
|
|
1326
|
+
)
|
|
1327
|
+
).join(" ");
|
|
1328
|
+
return `${" ".repeat(relativeDepth)}${line}`;
|
|
1329
|
+
}).join("\n");
|
|
1330
|
+
}
|
|
1331
|
+
function serializeSelectionToTSV(visibleRows, bounds, mode = "visible") {
|
|
1332
|
+
const entries = collectCopyRowEntries(visibleRows, bounds, mode);
|
|
1333
|
+
return serializeCopyRowsToTSV(
|
|
1334
|
+
entries.map((entry) => entry.row),
|
|
1335
|
+
visibleRows,
|
|
1336
|
+
bounds,
|
|
1337
|
+
entries.map((entry) => entry.depth)
|
|
1338
|
+
);
|
|
1339
|
+
}
|
|
1340
|
+
async function writeSelectionToClipboard(visibleRows, bounds, mode = "visible") {
|
|
1341
|
+
const text = serializeSelectionToTSV(visibleRows, bounds, mode);
|
|
1342
|
+
if (!text) return false;
|
|
1343
|
+
try {
|
|
1344
|
+
await navigator.clipboard.writeText(text);
|
|
1345
|
+
} catch {
|
|
1346
|
+
return false;
|
|
1347
|
+
}
|
|
1348
|
+
return true;
|
|
1349
|
+
}
|
|
1161
1350
|
|
|
1162
1351
|
// src/components/ui/table/features/cell-selection/fillData.ts
|
|
1163
1352
|
function getColumnAccessorKey2(columnDef) {
|
|
@@ -1215,15 +1404,100 @@ function hasFillExtension(sourceBounds, fillBounds) {
|
|
|
1215
1404
|
return fillBounds.startRow < sourceBounds.startRow || fillBounds.endRow > sourceBounds.endRow || fillBounds.startCol < sourceBounds.startCol || fillBounds.endCol > sourceBounds.endCol;
|
|
1216
1405
|
}
|
|
1217
1406
|
|
|
1407
|
+
// src/components/ui/table/features/cell-selection/pasteData.ts
|
|
1408
|
+
function countLeadingEmptyCells(cells) {
|
|
1409
|
+
let depth = 0;
|
|
1410
|
+
while (depth < cells.length && cells[depth] === "") {
|
|
1411
|
+
depth += 1;
|
|
1412
|
+
}
|
|
1413
|
+
return depth;
|
|
1414
|
+
}
|
|
1415
|
+
function looksLikeSubtreeIndentation(leadingEmptyCounts) {
|
|
1416
|
+
if (leadingEmptyCounts.length === 0) return false;
|
|
1417
|
+
const firstDepth = leadingEmptyCounts[0] ?? 0;
|
|
1418
|
+
if (firstDepth !== 0) return false;
|
|
1419
|
+
return leadingEmptyCounts.some((depth) => depth > 0);
|
|
1420
|
+
}
|
|
1421
|
+
function parseClipboardTSVWithDepths(text) {
|
|
1422
|
+
if (!text) return { values: [], depths: [] };
|
|
1423
|
+
const normalized = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
1424
|
+
const withoutTrailing = normalized.replace(/\n+$/, "");
|
|
1425
|
+
if (!withoutTrailing) return { values: [], depths: [] };
|
|
1426
|
+
const rows = withoutTrailing.split("\n").map((line) => line.split(" "));
|
|
1427
|
+
const leadingEmptyCounts = rows.map(countLeadingEmptyCells);
|
|
1428
|
+
const treatAsDepth = looksLikeSubtreeIndentation(leadingEmptyCounts);
|
|
1429
|
+
const values = [];
|
|
1430
|
+
const depths = [];
|
|
1431
|
+
for (let index = 0; index < rows.length; index += 1) {
|
|
1432
|
+
const cells = rows[index] ?? [];
|
|
1433
|
+
const depth = leadingEmptyCounts[index] ?? 0;
|
|
1434
|
+
if (treatAsDepth) {
|
|
1435
|
+
values.push(cells.slice(depth));
|
|
1436
|
+
depths.push(depth);
|
|
1437
|
+
} else {
|
|
1438
|
+
values.push(cells);
|
|
1439
|
+
depths.push(0);
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
return { values, depths };
|
|
1443
|
+
}
|
|
1444
|
+
function resolvePasteColumnIds(rows, startCol, width) {
|
|
1445
|
+
if (width <= 0) return [];
|
|
1446
|
+
const cells = rows[0]?.getVisibleCells() ?? [];
|
|
1447
|
+
const columnIds = [];
|
|
1448
|
+
for (let offset = 0; offset < width; offset += 1) {
|
|
1449
|
+
const cell = cells[startCol + offset];
|
|
1450
|
+
if (!cell) break;
|
|
1451
|
+
columnIds.push(cell.column.id);
|
|
1452
|
+
}
|
|
1453
|
+
return columnIds;
|
|
1454
|
+
}
|
|
1455
|
+
function buildRowsPastePayload(rows, startRow, startCol, text, mode, endRow = startRow) {
|
|
1456
|
+
const { values, depths } = parseClipboardTSVWithDepths(text);
|
|
1457
|
+
if (values.length === 0) return null;
|
|
1458
|
+
const width = Math.max(...values.map((row) => row.length), 0);
|
|
1459
|
+
if (width === 0) return null;
|
|
1460
|
+
const columnIds = resolvePasteColumnIds(rows, startCol, width);
|
|
1461
|
+
if (columnIds.length === 0) return null;
|
|
1462
|
+
const rowIds = [];
|
|
1463
|
+
for (let offset = 0; offset < values.length; offset += 1) {
|
|
1464
|
+
const row = rows[startRow + offset];
|
|
1465
|
+
if (!row) break;
|
|
1466
|
+
rowIds.push(row.id);
|
|
1467
|
+
}
|
|
1468
|
+
const anchorRow = rows[endRow] ?? rows[startRow];
|
|
1469
|
+
return {
|
|
1470
|
+
mode,
|
|
1471
|
+
startRow,
|
|
1472
|
+
startCol,
|
|
1473
|
+
endRow,
|
|
1474
|
+
rowIds,
|
|
1475
|
+
anchorRowId: anchorRow?.id ?? "",
|
|
1476
|
+
columnIds,
|
|
1477
|
+
values,
|
|
1478
|
+
depths
|
|
1479
|
+
};
|
|
1480
|
+
}
|
|
1481
|
+
function isEditablePasteTarget(target) {
|
|
1482
|
+
if (!(target instanceof HTMLElement)) return false;
|
|
1483
|
+
const tag = target.tagName;
|
|
1484
|
+
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
|
|
1485
|
+
return Boolean(target.isContentEditable);
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1218
1488
|
// src/components/ui/table/features/cell-selection/useCellSelection.ts
|
|
1219
1489
|
function useCellSelection({
|
|
1220
1490
|
data,
|
|
1221
1491
|
rows,
|
|
1222
1492
|
enabled = true,
|
|
1493
|
+
enableSubtreeCopy = false,
|
|
1494
|
+
enableInsertPaste = true,
|
|
1223
1495
|
onDataChange,
|
|
1224
|
-
onBatchChange
|
|
1496
|
+
onBatchChange,
|
|
1497
|
+
onRowsPaste
|
|
1225
1498
|
}) {
|
|
1226
1499
|
const [dragState, setDragState] = useState2(INITIAL_DRAG_STATE);
|
|
1500
|
+
const pendingPasteModeRef = useRef4(null);
|
|
1227
1501
|
const cellSelectionBounds = getCellSelectionBounds(dragState.start, dragState.end);
|
|
1228
1502
|
const activeSelectionBounds = enabled ? getActiveSelectionBounds(dragState, cellSelectionBounds) : null;
|
|
1229
1503
|
const handleCellMouseDown = useCallback2(
|
|
@@ -1277,21 +1551,113 @@ function useCellSelection({
|
|
|
1277
1551
|
setDragState(INITIAL_DRAG_STATE);
|
|
1278
1552
|
}
|
|
1279
1553
|
}, [enabled]);
|
|
1554
|
+
const copySelection = useCallback2(
|
|
1555
|
+
async (options) => {
|
|
1556
|
+
if (!enabled || !activeSelectionBounds) return false;
|
|
1557
|
+
const mode = options?.includeDescendants && enableSubtreeCopy ? "subtree" : "visible";
|
|
1558
|
+
return writeSelectionToClipboard(rows, activeSelectionBounds, mode);
|
|
1559
|
+
},
|
|
1560
|
+
[activeSelectionBounds, enableSubtreeCopy, enabled, rows]
|
|
1561
|
+
);
|
|
1280
1562
|
useEffect4(() => {
|
|
1281
1563
|
if (!enabled) return;
|
|
1282
1564
|
const handleKeyDown = (e) => {
|
|
1283
|
-
if (
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
}
|
|
1565
|
+
if (!activeSelectionBounds) return;
|
|
1566
|
+
if (!(e.ctrlKey || e.metaKey)) return;
|
|
1567
|
+
const isSubtreeShortcut = enableSubtreeCopy && e.shiftKey && e.key.toLowerCase() === "c";
|
|
1568
|
+
const isVisibleCopyShortcut = !e.shiftKey && e.key.toLowerCase() === "c";
|
|
1569
|
+
if (!isSubtreeShortcut && !isVisibleCopyShortcut) return;
|
|
1570
|
+
e.preventDefault();
|
|
1571
|
+
void copySelection({ includeDescendants: isSubtreeShortcut });
|
|
1291
1572
|
};
|
|
1292
1573
|
window.addEventListener("keydown", handleKeyDown);
|
|
1293
1574
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
1294
|
-
}, [activeSelectionBounds,
|
|
1575
|
+
}, [activeSelectionBounds, copySelection, enableSubtreeCopy, enabled]);
|
|
1576
|
+
const emitRowsPaste = useCallback2(
|
|
1577
|
+
(text, mode) => {
|
|
1578
|
+
if (!onRowsPaste || !activeSelectionBounds) return false;
|
|
1579
|
+
const payload = buildRowsPastePayload(
|
|
1580
|
+
rows,
|
|
1581
|
+
activeSelectionBounds.startRow,
|
|
1582
|
+
activeSelectionBounds.startCol,
|
|
1583
|
+
text,
|
|
1584
|
+
mode,
|
|
1585
|
+
activeSelectionBounds.endRow
|
|
1586
|
+
);
|
|
1587
|
+
if (!payload) return false;
|
|
1588
|
+
onRowsPaste(payload);
|
|
1589
|
+
return true;
|
|
1590
|
+
},
|
|
1591
|
+
[activeSelectionBounds, onRowsPaste, rows]
|
|
1592
|
+
);
|
|
1593
|
+
useEffect4(() => {
|
|
1594
|
+
if (!enabled || !onRowsPaste) return;
|
|
1595
|
+
const pasteHandledRef = { current: false };
|
|
1596
|
+
const ignoreNextPasteRef = { current: false };
|
|
1597
|
+
const handleKeyDown = (e) => {
|
|
1598
|
+
if (!activeSelectionBounds) return;
|
|
1599
|
+
if (!(e.ctrlKey || e.metaKey)) return;
|
|
1600
|
+
if (e.key.toLowerCase() !== "v") return;
|
|
1601
|
+
if (isEditablePasteTarget(e.target) || isEditablePasteTarget(document.activeElement)) {
|
|
1602
|
+
return;
|
|
1603
|
+
}
|
|
1604
|
+
if (e.shiftKey && !enableInsertPaste) {
|
|
1605
|
+
ignoreNextPasteRef.current = true;
|
|
1606
|
+
pendingPasteModeRef.current = null;
|
|
1607
|
+
return;
|
|
1608
|
+
}
|
|
1609
|
+
const mode = e.shiftKey ? "insert" : "overwrite";
|
|
1610
|
+
pasteHandledRef.current = false;
|
|
1611
|
+
ignoreNextPasteRef.current = false;
|
|
1612
|
+
pendingPasteModeRef.current = mode;
|
|
1613
|
+
void (async () => {
|
|
1614
|
+
try {
|
|
1615
|
+
const text = await navigator.clipboard.readText();
|
|
1616
|
+
if (pasteHandledRef.current) return;
|
|
1617
|
+
if (pendingPasteModeRef.current !== mode) return;
|
|
1618
|
+
if (!text) return;
|
|
1619
|
+
pasteHandledRef.current = true;
|
|
1620
|
+
emitRowsPaste(text, mode);
|
|
1621
|
+
pendingPasteModeRef.current = null;
|
|
1622
|
+
} catch {
|
|
1623
|
+
}
|
|
1624
|
+
})();
|
|
1625
|
+
};
|
|
1626
|
+
const handlePaste = (e) => {
|
|
1627
|
+
if (!activeSelectionBounds) return;
|
|
1628
|
+
if (isEditablePasteTarget(e.target) || isEditablePasteTarget(document.activeElement)) {
|
|
1629
|
+
return;
|
|
1630
|
+
}
|
|
1631
|
+
if (ignoreNextPasteRef.current) {
|
|
1632
|
+
ignoreNextPasteRef.current = false;
|
|
1633
|
+
pendingPasteModeRef.current = null;
|
|
1634
|
+
return;
|
|
1635
|
+
}
|
|
1636
|
+
const mode = pendingPasteModeRef.current ?? "overwrite";
|
|
1637
|
+
if (pasteHandledRef.current) {
|
|
1638
|
+
e.preventDefault();
|
|
1639
|
+
return;
|
|
1640
|
+
}
|
|
1641
|
+
const text = e.clipboardData?.getData("text/plain");
|
|
1642
|
+
if (text == null || text === "") return;
|
|
1643
|
+
pasteHandledRef.current = true;
|
|
1644
|
+
e.preventDefault();
|
|
1645
|
+
emitRowsPaste(text, mode);
|
|
1646
|
+
pendingPasteModeRef.current = null;
|
|
1647
|
+
};
|
|
1648
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
1649
|
+
window.addEventListener("paste", handlePaste);
|
|
1650
|
+
return () => {
|
|
1651
|
+
window.removeEventListener("keydown", handleKeyDown);
|
|
1652
|
+
window.removeEventListener("paste", handlePaste);
|
|
1653
|
+
};
|
|
1654
|
+
}, [
|
|
1655
|
+
activeSelectionBounds,
|
|
1656
|
+
emitRowsPaste,
|
|
1657
|
+
enableInsertPaste,
|
|
1658
|
+
enabled,
|
|
1659
|
+
onRowsPaste
|
|
1660
|
+
]);
|
|
1295
1661
|
useEffect4(() => {
|
|
1296
1662
|
if (!enabled) return;
|
|
1297
1663
|
const handleMouseUp = () => {
|
|
@@ -1337,7 +1703,8 @@ function useCellSelection({
|
|
|
1337
1703
|
activeSelectionBounds,
|
|
1338
1704
|
handleCellMouseDown,
|
|
1339
1705
|
handleCellMouseEnter,
|
|
1340
|
-
handleFillHandleMouseDown
|
|
1706
|
+
handleFillHandleMouseDown,
|
|
1707
|
+
copySelection
|
|
1341
1708
|
};
|
|
1342
1709
|
}
|
|
1343
1710
|
|
|
@@ -1401,6 +1768,10 @@ function useGlideTable(options) {
|
|
|
1401
1768
|
expandedRows: controlledExpandedRows,
|
|
1402
1769
|
onExpandedRowsChange,
|
|
1403
1770
|
preventExpand = false,
|
|
1771
|
+
enableSubtreeCopy,
|
|
1772
|
+
onCopyActionsReady,
|
|
1773
|
+
onRowsPaste,
|
|
1774
|
+
enableInsertPaste,
|
|
1404
1775
|
enableVirtualization = true,
|
|
1405
1776
|
estimateRowHeight = DATA_TABLE_ROW_HEIGHT,
|
|
1406
1777
|
virtualOverscan = DATA_TABLE_VIRTUAL_OVERSCAN
|
|
@@ -1415,13 +1786,13 @@ function useGlideTable(options) {
|
|
|
1415
1786
|
};
|
|
1416
1787
|
}, [labelsProp, emptyText, loadingText, selectionLabel]);
|
|
1417
1788
|
const enableExpand = Boolean(toggleField);
|
|
1789
|
+
const resolvedEnableSubtreeCopy = enableSubtreeCopy ?? enableExpand;
|
|
1418
1790
|
const [internalRowSelection, setInternalRowSelection] = useState3({});
|
|
1419
1791
|
const [internalExpandedRows, setInternalExpandedRows] = useState3(
|
|
1420
1792
|
() => /* @__PURE__ */ new Set()
|
|
1421
1793
|
);
|
|
1422
1794
|
const [hoveredRowIndex, setHoveredRowIndex] = useState3(null);
|
|
1423
|
-
const
|
|
1424
|
-
const scrollRef = useRef4(null);
|
|
1795
|
+
const scrollRef = useRef5(null);
|
|
1425
1796
|
const shouldVirtualize = enableVirtualization && !enableRowSpan;
|
|
1426
1797
|
useEffect5(() => {
|
|
1427
1798
|
if (enableVirtualization && enableRowSpan) {
|
|
@@ -1484,6 +1855,7 @@ function useGlideTable(options) {
|
|
|
1484
1855
|
return collectRowSpanColumns(columns);
|
|
1485
1856
|
}, [enableRowSpan, columns]);
|
|
1486
1857
|
const primaryRowSpanKey = rowSpanColumnKeys[0]?.rowSpanKey;
|
|
1858
|
+
const primaryRowSpanColumnId = rowSpanColumnKeys[0]?.columnId;
|
|
1487
1859
|
const columnRowSpanMap = useMemo2(
|
|
1488
1860
|
() => buildColumnRowSpanMap(tableData, rowSpanColumnKeys),
|
|
1489
1861
|
[tableData, rowSpanColumnKeys]
|
|
@@ -1502,27 +1874,29 @@ function useGlideTable(options) {
|
|
|
1502
1874
|
const totalSize = rowVirtualizer.getTotalSize();
|
|
1503
1875
|
const paddingTop = virtualRows.length > 0 ? virtualRows[0]?.start ?? 0 : 0;
|
|
1504
1876
|
const paddingBottom = virtualRows.length > 0 ? totalSize - (virtualRows[virtualRows.length - 1]?.end ?? 0) : 0;
|
|
1505
|
-
const
|
|
1506
|
-
|
|
1507
|
-
const keys = /* @__PURE__ */ new Set();
|
|
1877
|
+
const selectedRowIndices = useMemo2(() => {
|
|
1878
|
+
const indices = /* @__PURE__ */ new Set();
|
|
1508
1879
|
for (const selectedRow of selectedRows) {
|
|
1509
|
-
|
|
1510
|
-
if (value !== null && value !== void 0) keys.add(String(value));
|
|
1880
|
+
indices.add(selectedRow.index);
|
|
1511
1881
|
}
|
|
1512
|
-
return
|
|
1513
|
-
}, [
|
|
1882
|
+
return indices;
|
|
1883
|
+
}, [selectedRows]);
|
|
1514
1884
|
const {
|
|
1515
1885
|
dragState,
|
|
1516
1886
|
activeSelectionBounds,
|
|
1517
1887
|
handleCellMouseDown,
|
|
1518
1888
|
handleCellMouseEnter,
|
|
1519
|
-
handleFillHandleMouseDown
|
|
1889
|
+
handleFillHandleMouseDown,
|
|
1890
|
+
copySelection
|
|
1520
1891
|
} = useCellSelection({
|
|
1521
1892
|
data: tableData,
|
|
1522
1893
|
rows,
|
|
1523
1894
|
enabled: enableCellSelection,
|
|
1895
|
+
enableSubtreeCopy: resolvedEnableSubtreeCopy,
|
|
1896
|
+
enableInsertPaste: enableInsertPaste ?? true,
|
|
1524
1897
|
onDataChange,
|
|
1525
|
-
onBatchChange
|
|
1898
|
+
onBatchChange,
|
|
1899
|
+
onRowsPaste
|
|
1526
1900
|
});
|
|
1527
1901
|
const {
|
|
1528
1902
|
editingCell,
|
|
@@ -1544,22 +1918,10 @@ function useGlideTable(options) {
|
|
|
1544
1918
|
);
|
|
1545
1919
|
const clearHover = useCallback3(() => {
|
|
1546
1920
|
setHoveredRowIndex(null);
|
|
1547
|
-
setHoveredGroupKey(null);
|
|
1548
1921
|
}, []);
|
|
1549
|
-
const handleRowHover = useCallback3(
|
|
1550
|
-
(rowIndex
|
|
1551
|
-
|
|
1552
|
-
if (!primaryRowSpanKey) {
|
|
1553
|
-
setHoveredGroupKey(null);
|
|
1554
|
-
return;
|
|
1555
|
-
}
|
|
1556
|
-
const groupValue = rowData[primaryRowSpanKey];
|
|
1557
|
-
setHoveredGroupKey(
|
|
1558
|
-
groupValue === null || groupValue === void 0 ? null : String(groupValue)
|
|
1559
|
-
);
|
|
1560
|
-
},
|
|
1561
|
-
[primaryRowSpanKey]
|
|
1562
|
-
);
|
|
1922
|
+
const handleRowHover = useCallback3((rowIndex, _rowData) => {
|
|
1923
|
+
setHoveredRowIndex(rowIndex);
|
|
1924
|
+
}, []);
|
|
1563
1925
|
const handleToggleSelect = useCallback3(
|
|
1564
1926
|
(row) => {
|
|
1565
1927
|
if (!row.getCanSelect()) return;
|
|
@@ -1582,10 +1944,10 @@ function useGlideTable(options) {
|
|
|
1582
1944
|
rowSpan: {
|
|
1583
1945
|
enableRowSpan,
|
|
1584
1946
|
primaryRowSpanKey,
|
|
1947
|
+
primaryRowSpanColumnId,
|
|
1585
1948
|
columnRowSpanMap,
|
|
1586
1949
|
hoveredRowIndex,
|
|
1587
|
-
|
|
1588
|
-
selectedGroupKeys,
|
|
1950
|
+
selectedRowIndices,
|
|
1589
1951
|
onRowHover: handleRowHover
|
|
1590
1952
|
},
|
|
1591
1953
|
selection: {
|
|
@@ -1623,10 +1985,10 @@ function useGlideTable(options) {
|
|
|
1623
1985
|
}, [
|
|
1624
1986
|
enableRowSpan,
|
|
1625
1987
|
primaryRowSpanKey,
|
|
1988
|
+
primaryRowSpanColumnId,
|
|
1626
1989
|
columnRowSpanMap,
|
|
1627
1990
|
hoveredRowIndex,
|
|
1628
|
-
|
|
1629
|
-
selectedGroupKeys,
|
|
1991
|
+
selectedRowIndices,
|
|
1630
1992
|
handleRowHover,
|
|
1631
1993
|
rowSelectionMode,
|
|
1632
1994
|
selectOnRowClick,
|
|
@@ -1652,6 +2014,14 @@ function useGlideTable(options) {
|
|
|
1652
2014
|
labels.expandRow,
|
|
1653
2015
|
labels.collapseRow
|
|
1654
2016
|
]);
|
|
2017
|
+
const copySelectionRef = useRef5(copySelection);
|
|
2018
|
+
useEffect5(() => {
|
|
2019
|
+
copySelectionRef.current = copySelection;
|
|
2020
|
+
}, [copySelection]);
|
|
2021
|
+
const stableCopySelection = useCallback3((options2) => copySelectionRef.current(options2), []);
|
|
2022
|
+
useEffect5(() => {
|
|
2023
|
+
onCopyActionsReady?.({ copySelection: stableCopySelection });
|
|
2024
|
+
}, [onCopyActionsReady, stableCopySelection]);
|
|
1655
2025
|
return {
|
|
1656
2026
|
table,
|
|
1657
2027
|
tableData,
|
|
@@ -1671,7 +2041,8 @@ function useGlideTable(options) {
|
|
|
1671
2041
|
paddingBottom,
|
|
1672
2042
|
rowContextValue,
|
|
1673
2043
|
handleToggleSelect,
|
|
1674
|
-
clearHover
|
|
2044
|
+
clearHover,
|
|
2045
|
+
copySelection: stableCopySelection
|
|
1675
2046
|
};
|
|
1676
2047
|
}
|
|
1677
2048
|
|
|
@@ -1970,7 +2341,7 @@ function buildColumnDef(props, sort, onSort) {
|
|
|
1970
2341
|
}
|
|
1971
2342
|
|
|
1972
2343
|
// src/components/ui/table/components/Table/parseTableChildren.ts
|
|
1973
|
-
import { Children } from "react";
|
|
2344
|
+
import { Children, isValidElement as isValidElement2 } from "react";
|
|
1974
2345
|
|
|
1975
2346
|
// src/components/ui/table/components/Table/tableChildTypes.ts
|
|
1976
2347
|
import { isValidElement } from "react";
|
|
@@ -2019,10 +2390,26 @@ function parseTableChildren(children) {
|
|
|
2019
2390
|
}
|
|
2020
2391
|
return slots;
|
|
2021
2392
|
}
|
|
2393
|
+
function flattenColumnElements(children) {
|
|
2394
|
+
const result = [];
|
|
2395
|
+
for (const child of Children.toArray(children)) {
|
|
2396
|
+
if (isTableColumnElement(child)) {
|
|
2397
|
+
result.push(child);
|
|
2398
|
+
continue;
|
|
2399
|
+
}
|
|
2400
|
+
if (isValidElement2(child)) {
|
|
2401
|
+
const nested = child.props.children;
|
|
2402
|
+
if (nested != null) {
|
|
2403
|
+
result.push(...flattenColumnElements(nested));
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
}
|
|
2407
|
+
return result;
|
|
2408
|
+
}
|
|
2022
2409
|
function extractColumnElements(header) {
|
|
2023
2410
|
if (!header) return [];
|
|
2024
2411
|
const { children } = header.props;
|
|
2025
|
-
return
|
|
2412
|
+
return flattenColumnElements(children);
|
|
2026
2413
|
}
|
|
2027
2414
|
|
|
2028
2415
|
// src/components/ui/table/components/Table/TableBody.tsx
|