react-glide-table 1.1.4 → 1.1.5
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/dist/compound.cjs +144 -48
- package/dist/compound.js +145 -49
- package/dist/core.cjs +122 -43
- package/dist/core.d.cts +24 -3
- package/dist/core.d.ts +24 -3
- package/dist/core.js +120 -43
- package/dist/index.cjs +148 -48
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +147 -49
- package/package.json +1 -1
package/dist/core.cjs
CHANGED
|
@@ -40,10 +40,12 @@ __export(core_exports, {
|
|
|
40
40
|
hasCellSelectionEdges: () => hasCellSelectionEdges,
|
|
41
41
|
isCellInSelection: () => isCellInSelection,
|
|
42
42
|
isColumnEditable: () => isColumnEditable,
|
|
43
|
+
measureMergedSpanRowHeights: () => measureMergedSpanRowHeights,
|
|
43
44
|
parseCellEditValue: () => parseCellEditValue,
|
|
44
45
|
resolveDataTableLabels: () => resolveDataTableLabels,
|
|
45
46
|
resolveRowSelection: () => resolveRowSelection,
|
|
46
47
|
resolveRowSpanAt: () => resolveRowSpanAt,
|
|
48
|
+
rowRangeToHeightRatios: () => rowRangeToHeightRatios,
|
|
47
49
|
toggleExpandedRowId: () => toggleExpandedRowId,
|
|
48
50
|
useCellEdit: () => useCellEdit,
|
|
49
51
|
useCellSelection: () => useCellSelection,
|
|
@@ -230,10 +232,36 @@ function getCellSelectionBounds(start, end) {
|
|
|
230
232
|
endCol: Math.max(start.col, end.col)
|
|
231
233
|
};
|
|
232
234
|
}
|
|
235
|
+
function measureMergedSpanRowHeights(rowIndex, rowSpan, cellElement) {
|
|
236
|
+
if (rowSpan <= 1) return void 0;
|
|
237
|
+
const tbody = cellElement?.closest("tbody") ?? (typeof document !== "undefined" ? document.querySelector("tbody.data-table-body") : null);
|
|
238
|
+
if (!tbody) return void 0;
|
|
239
|
+
const rows = tbody.querySelectorAll(":scope > tr");
|
|
240
|
+
if (rows.length < rowIndex + rowSpan) return void 0;
|
|
241
|
+
const heights = [];
|
|
242
|
+
for (let i = 0; i < rowSpan; i++) {
|
|
243
|
+
const row = rows[rowIndex + i];
|
|
244
|
+
const height = row?.getBoundingClientRect().height ?? 0;
|
|
245
|
+
if (height <= 0) return void 0;
|
|
246
|
+
heights.push(height);
|
|
247
|
+
}
|
|
248
|
+
return heights;
|
|
249
|
+
}
|
|
233
250
|
function getRowIndexInMergedCell(clientY, cellElement, rowIndex, rowSpan) {
|
|
234
251
|
if (rowSpan <= 1) return rowIndex;
|
|
235
252
|
const rect = cellElement.getBoundingClientRect();
|
|
236
253
|
const relativeY = clientY - rect.top;
|
|
254
|
+
const heights = measureMergedSpanRowHeights(rowIndex, rowSpan, cellElement);
|
|
255
|
+
if (heights && heights.length === rowSpan) {
|
|
256
|
+
let accrued = 0;
|
|
257
|
+
for (let i = 0; i < rowSpan; i++) {
|
|
258
|
+
accrued += heights[i];
|
|
259
|
+
if (relativeY < accrued) {
|
|
260
|
+
return rowIndex + i;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return rowIndex + rowSpan - 1;
|
|
264
|
+
}
|
|
237
265
|
const rowHeight = rect.height / rowSpan;
|
|
238
266
|
const offset = Math.min(
|
|
239
267
|
Math.max(Math.floor(relativeY / rowHeight), 0),
|
|
@@ -255,7 +283,35 @@ function getActiveSelectionBounds(dragState, selectionBounds) {
|
|
|
255
283
|
var SELECTION_EDGE_WIDTH_PX = 2;
|
|
256
284
|
var SELECTION_EDGE_COLOR = "var(--color-brand-primary)";
|
|
257
285
|
var CELL_SELECTION_EDGES_CLASS = "cell-selection-edges";
|
|
258
|
-
function
|
|
286
|
+
function rowRangeToHeightRatios(rowIndex, span, fromRow, toRowExclusive, rowHeights) {
|
|
287
|
+
const clampedFrom = Math.max(fromRow, rowIndex);
|
|
288
|
+
const clampedTo = Math.min(toRowExclusive, rowIndex + span);
|
|
289
|
+
if (clampedTo <= clampedFrom) {
|
|
290
|
+
return { offsetRatio: 0, lengthRatio: 0 };
|
|
291
|
+
}
|
|
292
|
+
if (!rowHeights || rowHeights.length !== span) {
|
|
293
|
+
return {
|
|
294
|
+
offsetRatio: (clampedFrom - rowIndex) / span,
|
|
295
|
+
lengthRatio: (clampedTo - clampedFrom) / span
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
const total = rowHeights.reduce((sum, height) => sum + height, 0) || 1;
|
|
299
|
+
let offsetPx = 0;
|
|
300
|
+
for (let i = 0; i < clampedFrom - rowIndex; i++) {
|
|
301
|
+
offsetPx += rowHeights[i] ?? 0;
|
|
302
|
+
}
|
|
303
|
+
let lengthPx = 0;
|
|
304
|
+
for (let i = clampedFrom - rowIndex; i < clampedTo - rowIndex; i++) {
|
|
305
|
+
lengthPx += rowHeights[i] ?? 0;
|
|
306
|
+
}
|
|
307
|
+
return {
|
|
308
|
+
offsetRatio: offsetPx / total,
|
|
309
|
+
lengthRatio: lengthPx / total,
|
|
310
|
+
offsetPx,
|
|
311
|
+
lengthPx
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
function getMergedCellStepEdges(rowIndex, colIndex, bounds, rowSpan, isVisuallySelectedAt, rowHeights) {
|
|
259
315
|
const cellEndRow = rowIndex + rowSpan - 1;
|
|
260
316
|
const span = cellEndRow - rowIndex + 1;
|
|
261
317
|
if (span <= 1) return [];
|
|
@@ -274,20 +330,30 @@ function getMergedCellStepEdges(rowIndex, colIndex, bounds, rowSpan, isVisuallyS
|
|
|
274
330
|
continue;
|
|
275
331
|
}
|
|
276
332
|
if (runStart !== null) {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
333
|
+
const ratios = rowRangeToHeightRatios(
|
|
334
|
+
rowIndex,
|
|
335
|
+
span,
|
|
336
|
+
runStart,
|
|
337
|
+
row,
|
|
338
|
+
rowHeights
|
|
339
|
+
);
|
|
340
|
+
if (ratios.lengthRatio > 0) {
|
|
341
|
+
edges.push({ side, ...ratios });
|
|
342
|
+
}
|
|
282
343
|
runStart = null;
|
|
283
344
|
}
|
|
284
345
|
}
|
|
285
346
|
if (runStart !== null) {
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
347
|
+
const ratios = rowRangeToHeightRatios(
|
|
348
|
+
rowIndex,
|
|
349
|
+
span,
|
|
350
|
+
runStart,
|
|
351
|
+
toRowExclusive,
|
|
352
|
+
rowHeights
|
|
353
|
+
);
|
|
354
|
+
if (ratios.lengthRatio > 0) {
|
|
355
|
+
edges.push({ side, ...ratios });
|
|
356
|
+
}
|
|
291
357
|
}
|
|
292
358
|
};
|
|
293
359
|
const collectSide = (side, neighborCol) => {
|
|
@@ -316,14 +382,17 @@ function getMergedCellStepEdges(rowIndex, colIndex, bounds, rowSpan, isVisuallyS
|
|
|
316
382
|
}
|
|
317
383
|
return edges;
|
|
318
384
|
}
|
|
319
|
-
function
|
|
385
|
+
function buildPartialEdgeGradient(edge) {
|
|
386
|
+
const usePx = edge.offsetPx != null && edge.lengthPx != null;
|
|
320
387
|
const startPct = edge.offsetRatio * 100;
|
|
321
|
-
const endPct = (edge.offsetRatio + edge.
|
|
322
|
-
const
|
|
323
|
-
const
|
|
324
|
-
const
|
|
325
|
-
const
|
|
326
|
-
const
|
|
388
|
+
const endPct = (edge.offsetRatio + edge.lengthRatio) * 100;
|
|
389
|
+
const startPx = edge.offsetPx ?? 0;
|
|
390
|
+
const endPx = (edge.offsetPx ?? 0) + (edge.lengthPx ?? 0);
|
|
391
|
+
const overlapPx = SELECTION_EDGE_WIDTH_PX;
|
|
392
|
+
const isTopProtrusion = (usePx ? startPx === 0 : edge.offsetRatio === 0) && edge.lengthRatio < 1;
|
|
393
|
+
const isBottomProtrusion = usePx ? startPx > 0 : edge.offsetRatio > 0;
|
|
394
|
+
const startStop = usePx ? isBottomProtrusion ? `${Math.max(0, startPx - overlapPx)}px` : `${startPx}px` : isBottomProtrusion ? `calc(${startPct}% - ${overlapPx}px)` : `${startPct}%`;
|
|
395
|
+
const endStop = usePx ? isTopProtrusion ? `${endPx + overlapPx}px` : `${endPx}px` : isTopProtrusion ? `calc(${endPct}% + ${overlapPx}px)` : `${endPct}%`;
|
|
327
396
|
const xPos = edge.side === "left" ? "0" : "100%";
|
|
328
397
|
const layers = [
|
|
329
398
|
{
|
|
@@ -333,7 +402,7 @@ function buildPartialVerticalGradient(edge) {
|
|
|
333
402
|
}
|
|
334
403
|
];
|
|
335
404
|
if (isTopProtrusion || isBottomProtrusion) {
|
|
336
|
-
const capTop = isTopProtrusion ? `calc(${endPct}% - ${SELECTION_EDGE_WIDTH_PX}px)` : `calc(${startPct}% - ${SELECTION_EDGE_WIDTH_PX}px)`;
|
|
405
|
+
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)`;
|
|
337
406
|
layers.push({
|
|
338
407
|
image: `linear-gradient(${SELECTION_EDGE_COLOR}, ${SELECTION_EDGE_COLOR})`,
|
|
339
408
|
size: `${SELECTION_EDGE_WIDTH_PX}px ${SELECTION_EDGE_WIDTH_PX}px`,
|
|
@@ -342,7 +411,7 @@ function buildPartialVerticalGradient(edge) {
|
|
|
342
411
|
}
|
|
343
412
|
return layers;
|
|
344
413
|
}
|
|
345
|
-
function getCellSelectionEdgeStyle(rowIndex, colIndex, bounds, rowSpan = 1, isVisuallySelectedAt) {
|
|
414
|
+
function getCellSelectionEdgeStyle(rowIndex, colIndex, bounds, rowSpan = 1, isVisuallySelectedAt, rowHeights) {
|
|
346
415
|
if (!isCellInSelection(rowIndex, colIndex, bounds, rowSpan) || !bounds)
|
|
347
416
|
return void 0;
|
|
348
417
|
const cellEndRow = rowIndex + rowSpan - 1;
|
|
@@ -351,39 +420,47 @@ function getCellSelectionEdgeStyle(rowIndex, colIndex, bounds, rowSpan = 1, isVi
|
|
|
351
420
|
const isLeftEdge = colIndex === bounds.startCol;
|
|
352
421
|
const isRightEdge = colIndex === bounds.endCol;
|
|
353
422
|
const selectionContinuesBelow = cellEndRow < bounds.endRow;
|
|
354
|
-
const shadows = [];
|
|
355
|
-
if (isTopEdge) {
|
|
356
|
-
shadows.push(
|
|
357
|
-
`inset 0 ${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`
|
|
358
|
-
);
|
|
359
|
-
}
|
|
360
|
-
if (isBottomEdge) {
|
|
361
|
-
shadows.push(
|
|
362
|
-
`inset 0 -${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`
|
|
363
|
-
);
|
|
364
|
-
}
|
|
365
|
-
if (isLeftEdge) {
|
|
366
|
-
shadows.push(
|
|
367
|
-
`inset ${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`
|
|
368
|
-
);
|
|
369
|
-
}
|
|
370
|
-
if (isRightEdge) {
|
|
371
|
-
shadows.push(
|
|
372
|
-
`inset -${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`
|
|
373
|
-
);
|
|
374
|
-
}
|
|
375
423
|
const stepEdges = getMergedCellStepEdges(
|
|
376
424
|
rowIndex,
|
|
377
425
|
colIndex,
|
|
378
426
|
bounds,
|
|
379
427
|
rowSpan,
|
|
380
|
-
isVisuallySelectedAt
|
|
428
|
+
isVisuallySelectedAt,
|
|
429
|
+
rowHeights
|
|
381
430
|
);
|
|
431
|
+
const shadows = [];
|
|
432
|
+
const hasFullPerimeter = isTopEdge && isBottomEdge && isLeftEdge && isRightEdge && stepEdges.length === 0;
|
|
433
|
+
if (hasFullPerimeter) {
|
|
434
|
+
shadows.push(
|
|
435
|
+
`inset 0 0 0 ${SELECTION_EDGE_WIDTH_PX}px ${SELECTION_EDGE_COLOR}`
|
|
436
|
+
);
|
|
437
|
+
} else {
|
|
438
|
+
if (isTopEdge) {
|
|
439
|
+
shadows.push(
|
|
440
|
+
`inset 0 ${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
if (isBottomEdge) {
|
|
444
|
+
shadows.push(
|
|
445
|
+
`inset 0 -${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
if (isLeftEdge) {
|
|
449
|
+
shadows.push(
|
|
450
|
+
`inset ${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
if (isRightEdge) {
|
|
454
|
+
shadows.push(
|
|
455
|
+
`inset -${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
382
459
|
const gradients = [];
|
|
383
460
|
const sizes = [];
|
|
384
461
|
const positions = [];
|
|
385
462
|
for (const edge of stepEdges) {
|
|
386
|
-
for (const partial of
|
|
463
|
+
for (const partial of buildPartialEdgeGradient(edge)) {
|
|
387
464
|
gradients.push(partial.image);
|
|
388
465
|
sizes.push(partial.size);
|
|
389
466
|
positions.push(partial.position);
|
|
@@ -1186,10 +1263,12 @@ function useGlideTable(options) {
|
|
|
1186
1263
|
hasCellSelectionEdges,
|
|
1187
1264
|
isCellInSelection,
|
|
1188
1265
|
isColumnEditable,
|
|
1266
|
+
measureMergedSpanRowHeights,
|
|
1189
1267
|
parseCellEditValue,
|
|
1190
1268
|
resolveDataTableLabels,
|
|
1191
1269
|
resolveRowSelection,
|
|
1192
1270
|
resolveRowSpanAt,
|
|
1271
|
+
rowRangeToHeightRatios,
|
|
1193
1272
|
toggleExpandedRowId,
|
|
1194
1273
|
useCellEdit,
|
|
1195
1274
|
useCellSelection,
|
package/dist/core.d.cts
CHANGED
|
@@ -55,29 +55,50 @@ type DragState = {
|
|
|
55
55
|
fillAnchor: CellPosition | null;
|
|
56
56
|
fillEnd: CellPosition | null;
|
|
57
57
|
};
|
|
58
|
+
/**
|
|
59
|
+
* Reads per-row heights for a merged cell span from the table body.
|
|
60
|
+
* Expand + merge yields unequal row heights (e.g. parent 25px / child 22px);
|
|
61
|
+
* step borders must use these instead of equal-ratio splits.
|
|
62
|
+
*/
|
|
63
|
+
declare function measureMergedSpanRowHeights(rowIndex: number, rowSpan: number, cellElement?: HTMLElement | null): number[] | undefined;
|
|
58
64
|
declare function getRowIndexInMergedCell(clientY: number, cellElement: HTMLElement, rowIndex: number, rowSpan: number): number;
|
|
59
65
|
declare function isCellInSelection(rowIndex: number, colIndex: number, bounds: CellSelectionBounds | null, rowSpan?: number): boolean;
|
|
60
66
|
declare const CELL_SELECTION_EDGES_CLASS = "cell-selection-edges";
|
|
61
67
|
type CellSelectionEdgeStyle = {
|
|
62
68
|
/** Full-side inset border applied via ::after */
|
|
63
69
|
["--selection-edge-shadows"]?: string;
|
|
64
|
-
/** Partial
|
|
70
|
+
/** Partial borders for merged-cell step (stair) segments */
|
|
65
71
|
["--selection-edge-gradients"]?: string;
|
|
66
72
|
["--selection-edge-sizes"]?: string;
|
|
67
73
|
["--selection-edge-positions"]?: string;
|
|
68
74
|
/** Hide internal horizontal grid lines inside the selection with background color */
|
|
69
75
|
borderBottomColor?: string;
|
|
70
76
|
};
|
|
77
|
+
/**
|
|
78
|
+
* Maps a logical row range inside a merged cell to height ratios / pixels.
|
|
79
|
+
* When `rowHeights` is omitted, falls back to equal row splits.
|
|
80
|
+
*/
|
|
81
|
+
declare function rowRangeToHeightRatios(rowIndex: number, span: number, fromRow: number, toRowExclusive: number, rowHeights?: number[]): {
|
|
82
|
+
offsetRatio: number;
|
|
83
|
+
lengthRatio: number;
|
|
84
|
+
offsetPx?: number;
|
|
85
|
+
lengthPx?: number;
|
|
86
|
+
};
|
|
71
87
|
/**
|
|
72
88
|
* Draws the selection border with ::after (extended -1px at the bottom) plus
|
|
73
89
|
* inset box-shadow / partial gradients so it does not break on the cell's
|
|
74
90
|
* border-bottom.
|
|
75
91
|
*
|
|
92
|
+
* Merged cells that overlap the selection are filled as a whole, so top/bottom
|
|
93
|
+
* edges follow the cell's visual box when it contains the selection start/end.
|
|
94
|
+
* Step (stair) segments use height-weighted ratios when expand makes rows unequal.
|
|
95
|
+
*
|
|
76
96
|
* @param isVisuallySelectedAt Whether a logical cell is visually selected
|
|
77
97
|
* (including adjacent merged cells). Used to suppress internal step borders
|
|
78
98
|
* when multiple columns have rowSpan.
|
|
99
|
+
* @param rowHeights Per-row heights inside this merged span.
|
|
79
100
|
*/
|
|
80
|
-
declare function getCellSelectionEdgeStyle(rowIndex: number, colIndex: number, bounds: CellSelectionBounds | null, rowSpan?: number, isVisuallySelectedAt?: (row: number, col: number) => boolean): CellSelectionEdgeStyle | undefined;
|
|
101
|
+
declare function getCellSelectionEdgeStyle(rowIndex: number, colIndex: number, bounds: CellSelectionBounds | null, rowSpan?: number, isVisuallySelectedAt?: (row: number, col: number) => boolean, rowHeights?: number[]): CellSelectionEdgeStyle | undefined;
|
|
81
102
|
declare function hasCellSelectionEdges(style: CellSelectionEdgeStyle | undefined): boolean;
|
|
82
103
|
|
|
83
104
|
type RowSpanInfo = {
|
|
@@ -253,4 +274,4 @@ declare const useConvertTreeData: <T extends Record<string, unknown>>({ data, en
|
|
|
253
274
|
declare function resolveRowSelection(mode: RowSelectionMode, controlledSelection: RowSelectionState | undefined, internalSelection: RowSelectionState): RowSelectionState;
|
|
254
275
|
declare function applySelectionUpdater(mode: RowSelectionMode, updater: Updater<RowSelectionState>, previous: RowSelectionState): RowSelectionState;
|
|
255
276
|
|
|
256
|
-
export { CELL_SELECTION_EDGES_CLASS, type CellSelectionBounds, type ColumnRowSpanMap, DEFAULT_TREE_CHILDREN_FIELD, DEFAULT_TREE_ID_FIELD, DEFAULT_TREE_PARENT_ID_FIELD, DEFAULT_TREE_QTY_FIELD, DataTableLabels, DataTableProps, type DragState, type EditingCell, RowSelectionMode, type RowSpanInfo, type TreeRow, type UseConvertTreeDataParams, type UseGlideTableOptions, type UseGlideTableResult, applyCellEdit, applyFillData, applySelectionUpdater, buildColumnRowSpanMap, canExpandRow, collectFillChanges, collectRowSpanColumns, getCellEditDraftValue, getCellSelectionEdgeStyle, getColumnEditType, getRowIndexInMergedCell, hasCellSelectionEdges, isCellInSelection, isColumnEditable, parseCellEditValue, resolveRowSelection, resolveRowSpanAt, toggleExpandedRowId, useCellEdit, useCellSelection, useConvertTreeData, useGlideTable };
|
|
277
|
+
export { CELL_SELECTION_EDGES_CLASS, type CellSelectionBounds, type ColumnRowSpanMap, DEFAULT_TREE_CHILDREN_FIELD, DEFAULT_TREE_ID_FIELD, DEFAULT_TREE_PARENT_ID_FIELD, DEFAULT_TREE_QTY_FIELD, DataTableLabels, DataTableProps, type DragState, type EditingCell, RowSelectionMode, type RowSpanInfo, type TreeRow, type UseConvertTreeDataParams, type UseGlideTableOptions, type UseGlideTableResult, applyCellEdit, applyFillData, applySelectionUpdater, buildColumnRowSpanMap, canExpandRow, collectFillChanges, collectRowSpanColumns, getCellEditDraftValue, getCellSelectionEdgeStyle, getColumnEditType, getRowIndexInMergedCell, hasCellSelectionEdges, isCellInSelection, isColumnEditable, measureMergedSpanRowHeights, parseCellEditValue, resolveRowSelection, resolveRowSpanAt, rowRangeToHeightRatios, toggleExpandedRowId, useCellEdit, useCellSelection, useConvertTreeData, useGlideTable };
|
package/dist/core.d.ts
CHANGED
|
@@ -55,29 +55,50 @@ type DragState = {
|
|
|
55
55
|
fillAnchor: CellPosition | null;
|
|
56
56
|
fillEnd: CellPosition | null;
|
|
57
57
|
};
|
|
58
|
+
/**
|
|
59
|
+
* Reads per-row heights for a merged cell span from the table body.
|
|
60
|
+
* Expand + merge yields unequal row heights (e.g. parent 25px / child 22px);
|
|
61
|
+
* step borders must use these instead of equal-ratio splits.
|
|
62
|
+
*/
|
|
63
|
+
declare function measureMergedSpanRowHeights(rowIndex: number, rowSpan: number, cellElement?: HTMLElement | null): number[] | undefined;
|
|
58
64
|
declare function getRowIndexInMergedCell(clientY: number, cellElement: HTMLElement, rowIndex: number, rowSpan: number): number;
|
|
59
65
|
declare function isCellInSelection(rowIndex: number, colIndex: number, bounds: CellSelectionBounds | null, rowSpan?: number): boolean;
|
|
60
66
|
declare const CELL_SELECTION_EDGES_CLASS = "cell-selection-edges";
|
|
61
67
|
type CellSelectionEdgeStyle = {
|
|
62
68
|
/** Full-side inset border applied via ::after */
|
|
63
69
|
["--selection-edge-shadows"]?: string;
|
|
64
|
-
/** Partial
|
|
70
|
+
/** Partial borders for merged-cell step (stair) segments */
|
|
65
71
|
["--selection-edge-gradients"]?: string;
|
|
66
72
|
["--selection-edge-sizes"]?: string;
|
|
67
73
|
["--selection-edge-positions"]?: string;
|
|
68
74
|
/** Hide internal horizontal grid lines inside the selection with background color */
|
|
69
75
|
borderBottomColor?: string;
|
|
70
76
|
};
|
|
77
|
+
/**
|
|
78
|
+
* Maps a logical row range inside a merged cell to height ratios / pixels.
|
|
79
|
+
* When `rowHeights` is omitted, falls back to equal row splits.
|
|
80
|
+
*/
|
|
81
|
+
declare function rowRangeToHeightRatios(rowIndex: number, span: number, fromRow: number, toRowExclusive: number, rowHeights?: number[]): {
|
|
82
|
+
offsetRatio: number;
|
|
83
|
+
lengthRatio: number;
|
|
84
|
+
offsetPx?: number;
|
|
85
|
+
lengthPx?: number;
|
|
86
|
+
};
|
|
71
87
|
/**
|
|
72
88
|
* Draws the selection border with ::after (extended -1px at the bottom) plus
|
|
73
89
|
* inset box-shadow / partial gradients so it does not break on the cell's
|
|
74
90
|
* border-bottom.
|
|
75
91
|
*
|
|
92
|
+
* Merged cells that overlap the selection are filled as a whole, so top/bottom
|
|
93
|
+
* edges follow the cell's visual box when it contains the selection start/end.
|
|
94
|
+
* Step (stair) segments use height-weighted ratios when expand makes rows unequal.
|
|
95
|
+
*
|
|
76
96
|
* @param isVisuallySelectedAt Whether a logical cell is visually selected
|
|
77
97
|
* (including adjacent merged cells). Used to suppress internal step borders
|
|
78
98
|
* when multiple columns have rowSpan.
|
|
99
|
+
* @param rowHeights Per-row heights inside this merged span.
|
|
79
100
|
*/
|
|
80
|
-
declare function getCellSelectionEdgeStyle(rowIndex: number, colIndex: number, bounds: CellSelectionBounds | null, rowSpan?: number, isVisuallySelectedAt?: (row: number, col: number) => boolean): CellSelectionEdgeStyle | undefined;
|
|
101
|
+
declare function getCellSelectionEdgeStyle(rowIndex: number, colIndex: number, bounds: CellSelectionBounds | null, rowSpan?: number, isVisuallySelectedAt?: (row: number, col: number) => boolean, rowHeights?: number[]): CellSelectionEdgeStyle | undefined;
|
|
81
102
|
declare function hasCellSelectionEdges(style: CellSelectionEdgeStyle | undefined): boolean;
|
|
82
103
|
|
|
83
104
|
type RowSpanInfo = {
|
|
@@ -253,4 +274,4 @@ declare const useConvertTreeData: <T extends Record<string, unknown>>({ data, en
|
|
|
253
274
|
declare function resolveRowSelection(mode: RowSelectionMode, controlledSelection: RowSelectionState | undefined, internalSelection: RowSelectionState): RowSelectionState;
|
|
254
275
|
declare function applySelectionUpdater(mode: RowSelectionMode, updater: Updater<RowSelectionState>, previous: RowSelectionState): RowSelectionState;
|
|
255
276
|
|
|
256
|
-
export { CELL_SELECTION_EDGES_CLASS, type CellSelectionBounds, type ColumnRowSpanMap, DEFAULT_TREE_CHILDREN_FIELD, DEFAULT_TREE_ID_FIELD, DEFAULT_TREE_PARENT_ID_FIELD, DEFAULT_TREE_QTY_FIELD, DataTableLabels, DataTableProps, type DragState, type EditingCell, RowSelectionMode, type RowSpanInfo, type TreeRow, type UseConvertTreeDataParams, type UseGlideTableOptions, type UseGlideTableResult, applyCellEdit, applyFillData, applySelectionUpdater, buildColumnRowSpanMap, canExpandRow, collectFillChanges, collectRowSpanColumns, getCellEditDraftValue, getCellSelectionEdgeStyle, getColumnEditType, getRowIndexInMergedCell, hasCellSelectionEdges, isCellInSelection, isColumnEditable, parseCellEditValue, resolveRowSelection, resolveRowSpanAt, toggleExpandedRowId, useCellEdit, useCellSelection, useConvertTreeData, useGlideTable };
|
|
277
|
+
export { CELL_SELECTION_EDGES_CLASS, type CellSelectionBounds, type ColumnRowSpanMap, DEFAULT_TREE_CHILDREN_FIELD, DEFAULT_TREE_ID_FIELD, DEFAULT_TREE_PARENT_ID_FIELD, DEFAULT_TREE_QTY_FIELD, DataTableLabels, DataTableProps, type DragState, type EditingCell, RowSelectionMode, type RowSpanInfo, type TreeRow, type UseConvertTreeDataParams, type UseGlideTableOptions, type UseGlideTableResult, applyCellEdit, applyFillData, applySelectionUpdater, buildColumnRowSpanMap, canExpandRow, collectFillChanges, collectRowSpanColumns, getCellEditDraftValue, getCellSelectionEdgeStyle, getColumnEditType, getRowIndexInMergedCell, hasCellSelectionEdges, isCellInSelection, isColumnEditable, measureMergedSpanRowHeights, parseCellEditValue, resolveRowSelection, resolveRowSpanAt, rowRangeToHeightRatios, toggleExpandedRowId, useCellEdit, useCellSelection, useConvertTreeData, useGlideTable };
|
package/dist/core.js
CHANGED
|
@@ -187,10 +187,36 @@ function getCellSelectionBounds(start, end) {
|
|
|
187
187
|
endCol: Math.max(start.col, end.col)
|
|
188
188
|
};
|
|
189
189
|
}
|
|
190
|
+
function measureMergedSpanRowHeights(rowIndex, rowSpan, cellElement) {
|
|
191
|
+
if (rowSpan <= 1) return void 0;
|
|
192
|
+
const tbody = cellElement?.closest("tbody") ?? (typeof document !== "undefined" ? document.querySelector("tbody.data-table-body") : null);
|
|
193
|
+
if (!tbody) return void 0;
|
|
194
|
+
const rows = tbody.querySelectorAll(":scope > tr");
|
|
195
|
+
if (rows.length < rowIndex + rowSpan) return void 0;
|
|
196
|
+
const heights = [];
|
|
197
|
+
for (let i = 0; i < rowSpan; i++) {
|
|
198
|
+
const row = rows[rowIndex + i];
|
|
199
|
+
const height = row?.getBoundingClientRect().height ?? 0;
|
|
200
|
+
if (height <= 0) return void 0;
|
|
201
|
+
heights.push(height);
|
|
202
|
+
}
|
|
203
|
+
return heights;
|
|
204
|
+
}
|
|
190
205
|
function getRowIndexInMergedCell(clientY, cellElement, rowIndex, rowSpan) {
|
|
191
206
|
if (rowSpan <= 1) return rowIndex;
|
|
192
207
|
const rect = cellElement.getBoundingClientRect();
|
|
193
208
|
const relativeY = clientY - rect.top;
|
|
209
|
+
const heights = measureMergedSpanRowHeights(rowIndex, rowSpan, cellElement);
|
|
210
|
+
if (heights && heights.length === rowSpan) {
|
|
211
|
+
let accrued = 0;
|
|
212
|
+
for (let i = 0; i < rowSpan; i++) {
|
|
213
|
+
accrued += heights[i];
|
|
214
|
+
if (relativeY < accrued) {
|
|
215
|
+
return rowIndex + i;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return rowIndex + rowSpan - 1;
|
|
219
|
+
}
|
|
194
220
|
const rowHeight = rect.height / rowSpan;
|
|
195
221
|
const offset = Math.min(
|
|
196
222
|
Math.max(Math.floor(relativeY / rowHeight), 0),
|
|
@@ -212,7 +238,35 @@ function getActiveSelectionBounds(dragState, selectionBounds) {
|
|
|
212
238
|
var SELECTION_EDGE_WIDTH_PX = 2;
|
|
213
239
|
var SELECTION_EDGE_COLOR = "var(--color-brand-primary)";
|
|
214
240
|
var CELL_SELECTION_EDGES_CLASS = "cell-selection-edges";
|
|
215
|
-
function
|
|
241
|
+
function rowRangeToHeightRatios(rowIndex, span, fromRow, toRowExclusive, rowHeights) {
|
|
242
|
+
const clampedFrom = Math.max(fromRow, rowIndex);
|
|
243
|
+
const clampedTo = Math.min(toRowExclusive, rowIndex + span);
|
|
244
|
+
if (clampedTo <= clampedFrom) {
|
|
245
|
+
return { offsetRatio: 0, lengthRatio: 0 };
|
|
246
|
+
}
|
|
247
|
+
if (!rowHeights || rowHeights.length !== span) {
|
|
248
|
+
return {
|
|
249
|
+
offsetRatio: (clampedFrom - rowIndex) / span,
|
|
250
|
+
lengthRatio: (clampedTo - clampedFrom) / span
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
const total = rowHeights.reduce((sum, height) => sum + height, 0) || 1;
|
|
254
|
+
let offsetPx = 0;
|
|
255
|
+
for (let i = 0; i < clampedFrom - rowIndex; i++) {
|
|
256
|
+
offsetPx += rowHeights[i] ?? 0;
|
|
257
|
+
}
|
|
258
|
+
let lengthPx = 0;
|
|
259
|
+
for (let i = clampedFrom - rowIndex; i < clampedTo - rowIndex; i++) {
|
|
260
|
+
lengthPx += rowHeights[i] ?? 0;
|
|
261
|
+
}
|
|
262
|
+
return {
|
|
263
|
+
offsetRatio: offsetPx / total,
|
|
264
|
+
lengthRatio: lengthPx / total,
|
|
265
|
+
offsetPx,
|
|
266
|
+
lengthPx
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
function getMergedCellStepEdges(rowIndex, colIndex, bounds, rowSpan, isVisuallySelectedAt, rowHeights) {
|
|
216
270
|
const cellEndRow = rowIndex + rowSpan - 1;
|
|
217
271
|
const span = cellEndRow - rowIndex + 1;
|
|
218
272
|
if (span <= 1) return [];
|
|
@@ -231,20 +285,30 @@ function getMergedCellStepEdges(rowIndex, colIndex, bounds, rowSpan, isVisuallyS
|
|
|
231
285
|
continue;
|
|
232
286
|
}
|
|
233
287
|
if (runStart !== null) {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
288
|
+
const ratios = rowRangeToHeightRatios(
|
|
289
|
+
rowIndex,
|
|
290
|
+
span,
|
|
291
|
+
runStart,
|
|
292
|
+
row,
|
|
293
|
+
rowHeights
|
|
294
|
+
);
|
|
295
|
+
if (ratios.lengthRatio > 0) {
|
|
296
|
+
edges.push({ side, ...ratios });
|
|
297
|
+
}
|
|
239
298
|
runStart = null;
|
|
240
299
|
}
|
|
241
300
|
}
|
|
242
301
|
if (runStart !== null) {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
302
|
+
const ratios = rowRangeToHeightRatios(
|
|
303
|
+
rowIndex,
|
|
304
|
+
span,
|
|
305
|
+
runStart,
|
|
306
|
+
toRowExclusive,
|
|
307
|
+
rowHeights
|
|
308
|
+
);
|
|
309
|
+
if (ratios.lengthRatio > 0) {
|
|
310
|
+
edges.push({ side, ...ratios });
|
|
311
|
+
}
|
|
248
312
|
}
|
|
249
313
|
};
|
|
250
314
|
const collectSide = (side, neighborCol) => {
|
|
@@ -273,14 +337,17 @@ function getMergedCellStepEdges(rowIndex, colIndex, bounds, rowSpan, isVisuallyS
|
|
|
273
337
|
}
|
|
274
338
|
return edges;
|
|
275
339
|
}
|
|
276
|
-
function
|
|
340
|
+
function buildPartialEdgeGradient(edge) {
|
|
341
|
+
const usePx = edge.offsetPx != null && edge.lengthPx != null;
|
|
277
342
|
const startPct = edge.offsetRatio * 100;
|
|
278
|
-
const endPct = (edge.offsetRatio + edge.
|
|
279
|
-
const
|
|
280
|
-
const
|
|
281
|
-
const
|
|
282
|
-
const
|
|
283
|
-
const
|
|
343
|
+
const endPct = (edge.offsetRatio + edge.lengthRatio) * 100;
|
|
344
|
+
const startPx = edge.offsetPx ?? 0;
|
|
345
|
+
const endPx = (edge.offsetPx ?? 0) + (edge.lengthPx ?? 0);
|
|
346
|
+
const overlapPx = SELECTION_EDGE_WIDTH_PX;
|
|
347
|
+
const isTopProtrusion = (usePx ? startPx === 0 : edge.offsetRatio === 0) && edge.lengthRatio < 1;
|
|
348
|
+
const isBottomProtrusion = usePx ? startPx > 0 : edge.offsetRatio > 0;
|
|
349
|
+
const startStop = usePx ? isBottomProtrusion ? `${Math.max(0, startPx - overlapPx)}px` : `${startPx}px` : isBottomProtrusion ? `calc(${startPct}% - ${overlapPx}px)` : `${startPct}%`;
|
|
350
|
+
const endStop = usePx ? isTopProtrusion ? `${endPx + overlapPx}px` : `${endPx}px` : isTopProtrusion ? `calc(${endPct}% + ${overlapPx}px)` : `${endPct}%`;
|
|
284
351
|
const xPos = edge.side === "left" ? "0" : "100%";
|
|
285
352
|
const layers = [
|
|
286
353
|
{
|
|
@@ -290,7 +357,7 @@ function buildPartialVerticalGradient(edge) {
|
|
|
290
357
|
}
|
|
291
358
|
];
|
|
292
359
|
if (isTopProtrusion || isBottomProtrusion) {
|
|
293
|
-
const capTop = isTopProtrusion ? `calc(${endPct}% - ${SELECTION_EDGE_WIDTH_PX}px)` : `calc(${startPct}% - ${SELECTION_EDGE_WIDTH_PX}px)`;
|
|
360
|
+
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)`;
|
|
294
361
|
layers.push({
|
|
295
362
|
image: `linear-gradient(${SELECTION_EDGE_COLOR}, ${SELECTION_EDGE_COLOR})`,
|
|
296
363
|
size: `${SELECTION_EDGE_WIDTH_PX}px ${SELECTION_EDGE_WIDTH_PX}px`,
|
|
@@ -299,7 +366,7 @@ function buildPartialVerticalGradient(edge) {
|
|
|
299
366
|
}
|
|
300
367
|
return layers;
|
|
301
368
|
}
|
|
302
|
-
function getCellSelectionEdgeStyle(rowIndex, colIndex, bounds, rowSpan = 1, isVisuallySelectedAt) {
|
|
369
|
+
function getCellSelectionEdgeStyle(rowIndex, colIndex, bounds, rowSpan = 1, isVisuallySelectedAt, rowHeights) {
|
|
303
370
|
if (!isCellInSelection(rowIndex, colIndex, bounds, rowSpan) || !bounds)
|
|
304
371
|
return void 0;
|
|
305
372
|
const cellEndRow = rowIndex + rowSpan - 1;
|
|
@@ -308,39 +375,47 @@ function getCellSelectionEdgeStyle(rowIndex, colIndex, bounds, rowSpan = 1, isVi
|
|
|
308
375
|
const isLeftEdge = colIndex === bounds.startCol;
|
|
309
376
|
const isRightEdge = colIndex === bounds.endCol;
|
|
310
377
|
const selectionContinuesBelow = cellEndRow < bounds.endRow;
|
|
311
|
-
const shadows = [];
|
|
312
|
-
if (isTopEdge) {
|
|
313
|
-
shadows.push(
|
|
314
|
-
`inset 0 ${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`
|
|
315
|
-
);
|
|
316
|
-
}
|
|
317
|
-
if (isBottomEdge) {
|
|
318
|
-
shadows.push(
|
|
319
|
-
`inset 0 -${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`
|
|
320
|
-
);
|
|
321
|
-
}
|
|
322
|
-
if (isLeftEdge) {
|
|
323
|
-
shadows.push(
|
|
324
|
-
`inset ${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`
|
|
325
|
-
);
|
|
326
|
-
}
|
|
327
|
-
if (isRightEdge) {
|
|
328
|
-
shadows.push(
|
|
329
|
-
`inset -${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`
|
|
330
|
-
);
|
|
331
|
-
}
|
|
332
378
|
const stepEdges = getMergedCellStepEdges(
|
|
333
379
|
rowIndex,
|
|
334
380
|
colIndex,
|
|
335
381
|
bounds,
|
|
336
382
|
rowSpan,
|
|
337
|
-
isVisuallySelectedAt
|
|
383
|
+
isVisuallySelectedAt,
|
|
384
|
+
rowHeights
|
|
338
385
|
);
|
|
386
|
+
const shadows = [];
|
|
387
|
+
const hasFullPerimeter = isTopEdge && isBottomEdge && isLeftEdge && isRightEdge && stepEdges.length === 0;
|
|
388
|
+
if (hasFullPerimeter) {
|
|
389
|
+
shadows.push(
|
|
390
|
+
`inset 0 0 0 ${SELECTION_EDGE_WIDTH_PX}px ${SELECTION_EDGE_COLOR}`
|
|
391
|
+
);
|
|
392
|
+
} else {
|
|
393
|
+
if (isTopEdge) {
|
|
394
|
+
shadows.push(
|
|
395
|
+
`inset 0 ${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
if (isBottomEdge) {
|
|
399
|
+
shadows.push(
|
|
400
|
+
`inset 0 -${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
if (isLeftEdge) {
|
|
404
|
+
shadows.push(
|
|
405
|
+
`inset ${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
if (isRightEdge) {
|
|
409
|
+
shadows.push(
|
|
410
|
+
`inset -${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
339
414
|
const gradients = [];
|
|
340
415
|
const sizes = [];
|
|
341
416
|
const positions = [];
|
|
342
417
|
for (const edge of stepEdges) {
|
|
343
|
-
for (const partial of
|
|
418
|
+
for (const partial of buildPartialEdgeGradient(edge)) {
|
|
344
419
|
gradients.push(partial.image);
|
|
345
420
|
sizes.push(partial.size);
|
|
346
421
|
positions.push(partial.position);
|
|
@@ -1142,10 +1217,12 @@ export {
|
|
|
1142
1217
|
hasCellSelectionEdges,
|
|
1143
1218
|
isCellInSelection,
|
|
1144
1219
|
isColumnEditable,
|
|
1220
|
+
measureMergedSpanRowHeights,
|
|
1145
1221
|
parseCellEditValue,
|
|
1146
1222
|
resolveDataTableLabels,
|
|
1147
1223
|
resolveRowSelection,
|
|
1148
1224
|
resolveRowSpanAt,
|
|
1225
|
+
rowRangeToHeightRatios,
|
|
1149
1226
|
toggleExpandedRowId,
|
|
1150
1227
|
useCellEdit,
|
|
1151
1228
|
useCellSelection,
|