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/dist/index.cjs CHANGED
@@ -43,10 +43,12 @@ __export(src_exports, {
43
43
  hasCellSelectionEdges: () => hasCellSelectionEdges,
44
44
  isCellInSelection: () => isCellInSelection,
45
45
  isColumnEditable: () => isColumnEditable,
46
+ measureMergedSpanRowHeights: () => measureMergedSpanRowHeights,
46
47
  parseCellEditValue: () => parseCellEditValue,
47
48
  resolveDataTableLabels: () => resolveDataTableLabels,
48
49
  resolveRowSelection: () => resolveRowSelection,
49
50
  resolveRowSpanAt: () => resolveRowSpanAt,
51
+ rowRangeToHeightRatios: () => rowRangeToHeightRatios,
50
52
  toggleExpandedRowId: () => toggleExpandedRowId,
51
53
  useCellEdit: () => useCellEdit,
52
54
  useCellSelection: () => useCellSelection,
@@ -241,10 +243,36 @@ function getCellSelectionBounds(start, end) {
241
243
  endCol: Math.max(start.col, end.col)
242
244
  };
243
245
  }
246
+ function measureMergedSpanRowHeights(rowIndex, rowSpan, cellElement) {
247
+ if (rowSpan <= 1) return void 0;
248
+ const tbody = cellElement?.closest("tbody") ?? (typeof document !== "undefined" ? document.querySelector("tbody.data-table-body") : null);
249
+ if (!tbody) return void 0;
250
+ const rows = tbody.querySelectorAll(":scope > tr");
251
+ if (rows.length < rowIndex + rowSpan) return void 0;
252
+ const heights = [];
253
+ for (let i = 0; i < rowSpan; i++) {
254
+ const row = rows[rowIndex + i];
255
+ const height = row?.getBoundingClientRect().height ?? 0;
256
+ if (height <= 0) return void 0;
257
+ heights.push(height);
258
+ }
259
+ return heights;
260
+ }
244
261
  function getRowIndexInMergedCell(clientY, cellElement, rowIndex, rowSpan) {
245
262
  if (rowSpan <= 1) return rowIndex;
246
263
  const rect = cellElement.getBoundingClientRect();
247
264
  const relativeY = clientY - rect.top;
265
+ const heights = measureMergedSpanRowHeights(rowIndex, rowSpan, cellElement);
266
+ if (heights && heights.length === rowSpan) {
267
+ let accrued = 0;
268
+ for (let i = 0; i < rowSpan; i++) {
269
+ accrued += heights[i];
270
+ if (relativeY < accrued) {
271
+ return rowIndex + i;
272
+ }
273
+ }
274
+ return rowIndex + rowSpan - 1;
275
+ }
248
276
  const rowHeight = rect.height / rowSpan;
249
277
  const offset = Math.min(
250
278
  Math.max(Math.floor(relativeY / rowHeight), 0),
@@ -266,7 +294,35 @@ function getActiveSelectionBounds(dragState, selectionBounds) {
266
294
  var SELECTION_EDGE_WIDTH_PX = 2;
267
295
  var SELECTION_EDGE_COLOR = "var(--color-brand-primary)";
268
296
  var CELL_SELECTION_EDGES_CLASS = "cell-selection-edges";
269
- function getMergedCellStepEdges(rowIndex, colIndex, bounds, rowSpan, isVisuallySelectedAt) {
297
+ function rowRangeToHeightRatios(rowIndex, span, fromRow, toRowExclusive, rowHeights) {
298
+ const clampedFrom = Math.max(fromRow, rowIndex);
299
+ const clampedTo = Math.min(toRowExclusive, rowIndex + span);
300
+ if (clampedTo <= clampedFrom) {
301
+ return { offsetRatio: 0, lengthRatio: 0 };
302
+ }
303
+ if (!rowHeights || rowHeights.length !== span) {
304
+ return {
305
+ offsetRatio: (clampedFrom - rowIndex) / span,
306
+ lengthRatio: (clampedTo - clampedFrom) / span
307
+ };
308
+ }
309
+ const total = rowHeights.reduce((sum, height) => sum + height, 0) || 1;
310
+ let offsetPx = 0;
311
+ for (let i = 0; i < clampedFrom - rowIndex; i++) {
312
+ offsetPx += rowHeights[i] ?? 0;
313
+ }
314
+ let lengthPx = 0;
315
+ for (let i = clampedFrom - rowIndex; i < clampedTo - rowIndex; i++) {
316
+ lengthPx += rowHeights[i] ?? 0;
317
+ }
318
+ return {
319
+ offsetRatio: offsetPx / total,
320
+ lengthRatio: lengthPx / total,
321
+ offsetPx,
322
+ lengthPx
323
+ };
324
+ }
325
+ function getMergedCellStepEdges(rowIndex, colIndex, bounds, rowSpan, isVisuallySelectedAt, rowHeights) {
270
326
  const cellEndRow = rowIndex + rowSpan - 1;
271
327
  const span = cellEndRow - rowIndex + 1;
272
328
  if (span <= 1) return [];
@@ -285,20 +341,30 @@ function getMergedCellStepEdges(rowIndex, colIndex, bounds, rowSpan, isVisuallyS
285
341
  continue;
286
342
  }
287
343
  if (runStart !== null) {
288
- edges.push({
289
- side,
290
- offsetRatio: (runStart - rowIndex) / span,
291
- heightRatio: (row - runStart) / span
292
- });
344
+ const ratios = rowRangeToHeightRatios(
345
+ rowIndex,
346
+ span,
347
+ runStart,
348
+ row,
349
+ rowHeights
350
+ );
351
+ if (ratios.lengthRatio > 0) {
352
+ edges.push({ side, ...ratios });
353
+ }
293
354
  runStart = null;
294
355
  }
295
356
  }
296
357
  if (runStart !== null) {
297
- edges.push({
298
- side,
299
- offsetRatio: (runStart - rowIndex) / span,
300
- heightRatio: (toRowExclusive - runStart) / span
301
- });
358
+ const ratios = rowRangeToHeightRatios(
359
+ rowIndex,
360
+ span,
361
+ runStart,
362
+ toRowExclusive,
363
+ rowHeights
364
+ );
365
+ if (ratios.lengthRatio > 0) {
366
+ edges.push({ side, ...ratios });
367
+ }
302
368
  }
303
369
  };
304
370
  const collectSide = (side, neighborCol) => {
@@ -327,14 +393,17 @@ function getMergedCellStepEdges(rowIndex, colIndex, bounds, rowSpan, isVisuallyS
327
393
  }
328
394
  return edges;
329
395
  }
330
- function buildPartialVerticalGradient(edge) {
396
+ function buildPartialEdgeGradient(edge) {
397
+ const usePx = edge.offsetPx != null && edge.lengthPx != null;
331
398
  const startPct = edge.offsetRatio * 100;
332
- const endPct = (edge.offsetRatio + edge.heightRatio) * 100;
333
- const overlapPx = SELECTION_EDGE_WIDTH_PX + 1;
334
- const isTopProtrusion = edge.offsetRatio === 0 && edge.heightRatio < 1;
335
- const isBottomProtrusion = edge.offsetRatio > 0;
336
- const startStop = isBottomProtrusion ? `calc(${startPct}% - ${overlapPx}px)` : `${startPct}%`;
337
- const endStop = isTopProtrusion ? `calc(${endPct}% + ${overlapPx}px)` : `${endPct}%`;
399
+ const endPct = (edge.offsetRatio + edge.lengthRatio) * 100;
400
+ const startPx = edge.offsetPx ?? 0;
401
+ const endPx = (edge.offsetPx ?? 0) + (edge.lengthPx ?? 0);
402
+ const overlapPx = SELECTION_EDGE_WIDTH_PX;
403
+ const isTopProtrusion = (usePx ? startPx === 0 : edge.offsetRatio === 0) && edge.lengthRatio < 1;
404
+ const isBottomProtrusion = usePx ? startPx > 0 : edge.offsetRatio > 0;
405
+ const startStop = usePx ? isBottomProtrusion ? `${Math.max(0, startPx - overlapPx)}px` : `${startPx}px` : isBottomProtrusion ? `calc(${startPct}% - ${overlapPx}px)` : `${startPct}%`;
406
+ const endStop = usePx ? isTopProtrusion ? `${endPx + overlapPx}px` : `${endPx}px` : isTopProtrusion ? `calc(${endPct}% + ${overlapPx}px)` : `${endPct}%`;
338
407
  const xPos = edge.side === "left" ? "0" : "100%";
339
408
  const layers = [
340
409
  {
@@ -344,7 +413,7 @@ function buildPartialVerticalGradient(edge) {
344
413
  }
345
414
  ];
346
415
  if (isTopProtrusion || isBottomProtrusion) {
347
- const capTop = isTopProtrusion ? `calc(${endPct}% - ${SELECTION_EDGE_WIDTH_PX}px)` : `calc(${startPct}% - ${SELECTION_EDGE_WIDTH_PX}px)`;
416
+ const capTop = usePx ? isTopProtrusion ? `${Math.max(0, endPx - SELECTION_EDGE_WIDTH_PX)}px` : `${Math.max(0, startPx - SELECTION_EDGE_WIDTH_PX)}px` : isTopProtrusion ? `calc(${endPct}% - ${SELECTION_EDGE_WIDTH_PX}px)` : `calc(${startPct}% - ${SELECTION_EDGE_WIDTH_PX}px)`;
348
417
  layers.push({
349
418
  image: `linear-gradient(${SELECTION_EDGE_COLOR}, ${SELECTION_EDGE_COLOR})`,
350
419
  size: `${SELECTION_EDGE_WIDTH_PX}px ${SELECTION_EDGE_WIDTH_PX}px`,
@@ -353,7 +422,7 @@ function buildPartialVerticalGradient(edge) {
353
422
  }
354
423
  return layers;
355
424
  }
356
- function getCellSelectionEdgeStyle(rowIndex, colIndex, bounds, rowSpan = 1, isVisuallySelectedAt) {
425
+ function getCellSelectionEdgeStyle(rowIndex, colIndex, bounds, rowSpan = 1, isVisuallySelectedAt, rowHeights) {
357
426
  if (!isCellInSelection(rowIndex, colIndex, bounds, rowSpan) || !bounds)
358
427
  return void 0;
359
428
  const cellEndRow = rowIndex + rowSpan - 1;
@@ -362,39 +431,47 @@ function getCellSelectionEdgeStyle(rowIndex, colIndex, bounds, rowSpan = 1, isVi
362
431
  const isLeftEdge = colIndex === bounds.startCol;
363
432
  const isRightEdge = colIndex === bounds.endCol;
364
433
  const selectionContinuesBelow = cellEndRow < bounds.endRow;
365
- const shadows = [];
366
- if (isTopEdge) {
367
- shadows.push(
368
- `inset 0 ${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`
369
- );
370
- }
371
- if (isBottomEdge) {
372
- shadows.push(
373
- `inset 0 -${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`
374
- );
375
- }
376
- if (isLeftEdge) {
377
- shadows.push(
378
- `inset ${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`
379
- );
380
- }
381
- if (isRightEdge) {
382
- shadows.push(
383
- `inset -${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`
384
- );
385
- }
386
434
  const stepEdges = getMergedCellStepEdges(
387
435
  rowIndex,
388
436
  colIndex,
389
437
  bounds,
390
438
  rowSpan,
391
- isVisuallySelectedAt
439
+ isVisuallySelectedAt,
440
+ rowHeights
392
441
  );
442
+ const shadows = [];
443
+ const hasFullPerimeter = isTopEdge && isBottomEdge && isLeftEdge && isRightEdge && stepEdges.length === 0;
444
+ if (hasFullPerimeter) {
445
+ shadows.push(
446
+ `inset 0 0 0 ${SELECTION_EDGE_WIDTH_PX}px ${SELECTION_EDGE_COLOR}`
447
+ );
448
+ } else {
449
+ if (isTopEdge) {
450
+ shadows.push(
451
+ `inset 0 ${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`
452
+ );
453
+ }
454
+ if (isBottomEdge) {
455
+ shadows.push(
456
+ `inset 0 -${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`
457
+ );
458
+ }
459
+ if (isLeftEdge) {
460
+ shadows.push(
461
+ `inset ${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`
462
+ );
463
+ }
464
+ if (isRightEdge) {
465
+ shadows.push(
466
+ `inset -${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`
467
+ );
468
+ }
469
+ }
393
470
  const gradients = [];
394
471
  const sizes = [];
395
472
  const positions = [];
396
473
  for (const edge of stepEdges) {
397
- for (const partial of buildPartialVerticalGradient(edge)) {
474
+ for (const partial of buildPartialEdgeGradient(edge)) {
398
475
  gradients.push(partial.image);
399
476
  sizes.push(partial.size);
400
477
  positions.push(partial.position);
@@ -1485,9 +1562,10 @@ function DataTableRow({
1485
1562
  }
1486
1563
  }
1487
1564
  const isEditing = editingCell?.rowIndex === rowIndex && editingCell?.colIndex === cellIndex;
1488
- const showCellHover = isRowSpanColumn ? isGroupHovered : isRowHovered;
1489
- const showCellSelected = isRowSpanColumn ? isGroupSelected : isRowSelected;
1490
1565
  const cellRowSpan = rowSpanInfo?.rowSpan ?? 1;
1566
+ const isMergedCellHovered = hoveredRowIndex !== null && hoveredRowIndex >= rowIndex && hoveredRowIndex <= rowIndex + cellRowSpan - 1;
1567
+ const showCellHover = isRowSpanColumn ? isMergedCellHovered : isRowHovered;
1568
+ const showCellSelected = isRowSpanColumn ? isGroupSelected : isRowSelected;
1491
1569
  const isMerged = cellRowSpan > 1;
1492
1570
  const showMergedRightEdge = isMerged && (cellIndex === visibleCells.length - 1 || resolveRowSpanAt(
1493
1571
  columnRowSpanMap.get(columnIdsByIndex[cellIndex + 1]),
@@ -1500,12 +1578,14 @@ function DataTableRow({
1500
1578
  cellRowSpan
1501
1579
  );
1502
1580
  const isBottomRightCell = !isEditing && activeSelectionBounds && !dragState.isSelecting && activeSelectionBounds.endRow >= rowIndex && activeSelectionBounds.endRow <= rowIndex + cellRowSpan - 1 && cellIndex === activeSelectionBounds.endCol;
1581
+ const spanRowHeights = enableCellSelection && activeSelectionBounds && isCellDragSelected && cellRowSpan > 1 ? measureMergedSpanRowHeights(rowIndex, cellRowSpan) : void 0;
1503
1582
  const selectionEdgeStyle = getCellSelectionEdgeStyle(
1504
1583
  rowIndex,
1505
1584
  cellIndex,
1506
1585
  activeSelectionBounds,
1507
1586
  cellRowSpan,
1508
- isVisuallySelectedAt
1587
+ isVisuallySelectedAt,
1588
+ spanRowHeights
1509
1589
  );
1510
1590
  const resolveCellRowIndex = (clientY, element) => getRowIndexInMergedCell(clientY, element, rowIndex, cellRowSpan);
1511
1591
  const hasSelectionEdges = hasCellSelectionEdges(selectionEdgeStyle);
@@ -1515,6 +1595,7 @@ function DataTableRow({
1515
1595
  rowSpan: rowSpanInfo && rowSpanInfo.rowSpan > 1 ? rowSpanInfo.rowSpan : void 0,
1516
1596
  "data-merged": isMerged && cellIndex > 0 ? "" : void 0,
1517
1597
  "data-merged-edge-right": showMergedRightEdge ? "" : void 0,
1598
+ "data-merged-row-first": isMerged && cellIndex === 0 && showMergedRightEdge ? "" : void 0,
1518
1599
  "data-group-selected": enableRowSpan && showCellSelected ? "" : void 0,
1519
1600
  "data-group-hovered": enableRowSpan && showCellHover && !showCellSelected ? "" : void 0,
1520
1601
  "data-selection-fill": isCellDragSelected ? "" : void 0,
@@ -1559,7 +1640,8 @@ function DataTableRow({
1559
1640
  "data-table-cell",
1560
1641
  CELL_ALIGN_CLASS[align],
1561
1642
  cellClassName,
1562
- isMerged && cellIndex > 0 && "is-merged",
1643
+ isMerged && "is-merged",
1644
+ isMerged && cellIndex === 0 && showMergedRightEdge && "is-merged-row-first",
1563
1645
  showMergedRightEdge && "is-merged-edge-right",
1564
1646
  enableRowSpan && showCellSelected && "is-group-selected",
1565
1647
  enableRowSpan && showCellHover && !showCellSelected && "is-group-hovered",
@@ -2065,10 +2147,26 @@ function parseTableChildren(children) {
2065
2147
  }
2066
2148
  return slots;
2067
2149
  }
2150
+ function flattenColumnElements(children) {
2151
+ const result = [];
2152
+ for (const child of import_react9.Children.toArray(children)) {
2153
+ if (isTableColumnElement(child)) {
2154
+ result.push(child);
2155
+ continue;
2156
+ }
2157
+ if ((0, import_react9.isValidElement)(child)) {
2158
+ const nested = child.props.children;
2159
+ if (nested != null) {
2160
+ result.push(...flattenColumnElements(nested));
2161
+ }
2162
+ }
2163
+ }
2164
+ return result;
2165
+ }
2068
2166
  function extractColumnElements(header) {
2069
2167
  if (!header) return [];
2070
2168
  const { children } = header.props;
2071
- return import_react9.Children.toArray(children).filter(isTableColumnElement);
2169
+ return flattenColumnElements(children);
2072
2170
  }
2073
2171
 
2074
2172
  // src/components/ui/table/components/Table/TableBody.tsx
@@ -2281,10 +2379,12 @@ var Table = Object.assign(TableRoot, {
2281
2379
  hasCellSelectionEdges,
2282
2380
  isCellInSelection,
2283
2381
  isColumnEditable,
2382
+ measureMergedSpanRowHeights,
2284
2383
  parseCellEditValue,
2285
2384
  resolveDataTableLabels,
2286
2385
  resolveRowSelection,
2287
2386
  resolveRowSpanAt,
2387
+ rowRangeToHeightRatios,
2288
2388
  toggleExpandedRowId,
2289
2389
  useCellEdit,
2290
2390
  useCellSelection,
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { D as DEFAULT_DATA_TABLE_LABELS, a as DataTableClassNames, b as DataTableLabels, c as DataTableProps, d as DataTableSlots, R as RowSelectionMode, T as TableColumnProps, e as TableProps, r as resolveDataTableLabels } from './types-BfthylVR.cjs';
2
- export { CELL_SELECTION_EDGES_CLASS, CellSelectionBounds, ColumnRowSpanMap, DEFAULT_TREE_CHILDREN_FIELD, DEFAULT_TREE_ID_FIELD, DEFAULT_TREE_PARENT_ID_FIELD, DEFAULT_TREE_QTY_FIELD, DragState, EditingCell, RowSpanInfo, TreeRow, UseConvertTreeDataParams, UseGlideTableOptions, UseGlideTableResult, applyCellEdit, applyFillData, applySelectionUpdater, buildColumnRowSpanMap, canExpandRow, collectFillChanges, collectRowSpanColumns, getCellEditDraftValue, getCellSelectionEdgeStyle, getColumnEditType, getRowIndexInMergedCell, hasCellSelectionEdges, isCellInSelection, isColumnEditable, parseCellEditValue, resolveRowSelection, resolveRowSpanAt, toggleExpandedRowId, useCellEdit, useCellSelection, useConvertTreeData, useGlideTable } from './core.cjs';
2
+ export { CELL_SELECTION_EDGES_CLASS, CellSelectionBounds, ColumnRowSpanMap, DEFAULT_TREE_CHILDREN_FIELD, DEFAULT_TREE_ID_FIELD, DEFAULT_TREE_PARENT_ID_FIELD, DEFAULT_TREE_QTY_FIELD, DragState, EditingCell, RowSpanInfo, TreeRow, UseConvertTreeDataParams, UseGlideTableOptions, 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 } from './core.cjs';
3
3
  export { DataTable, Table, TableCompoundComponent, createTable } from './compound.cjs';
4
4
  export { ColumnDef, RowSelectionState } from '@tanstack/react-table';
5
5
  import 'react';
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { D as DEFAULT_DATA_TABLE_LABELS, a as DataTableClassNames, b as DataTableLabels, c as DataTableProps, d as DataTableSlots, R as RowSelectionMode, T as TableColumnProps, e as TableProps, r as resolveDataTableLabels } from './types-BfthylVR.js';
2
- export { CELL_SELECTION_EDGES_CLASS, CellSelectionBounds, ColumnRowSpanMap, DEFAULT_TREE_CHILDREN_FIELD, DEFAULT_TREE_ID_FIELD, DEFAULT_TREE_PARENT_ID_FIELD, DEFAULT_TREE_QTY_FIELD, DragState, EditingCell, RowSpanInfo, TreeRow, UseConvertTreeDataParams, UseGlideTableOptions, UseGlideTableResult, applyCellEdit, applyFillData, applySelectionUpdater, buildColumnRowSpanMap, canExpandRow, collectFillChanges, collectRowSpanColumns, getCellEditDraftValue, getCellSelectionEdgeStyle, getColumnEditType, getRowIndexInMergedCell, hasCellSelectionEdges, isCellInSelection, isColumnEditable, parseCellEditValue, resolveRowSelection, resolveRowSpanAt, toggleExpandedRowId, useCellEdit, useCellSelection, useConvertTreeData, useGlideTable } from './core.js';
2
+ export { CELL_SELECTION_EDGES_CLASS, CellSelectionBounds, ColumnRowSpanMap, DEFAULT_TREE_CHILDREN_FIELD, DEFAULT_TREE_ID_FIELD, DEFAULT_TREE_PARENT_ID_FIELD, DEFAULT_TREE_QTY_FIELD, DragState, EditingCell, RowSpanInfo, TreeRow, UseConvertTreeDataParams, UseGlideTableOptions, 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 } from './core.js';
3
3
  export { DataTable, Table, TableCompoundComponent, createTable } from './compound.js';
4
4
  export { ColumnDef, RowSelectionState } from '@tanstack/react-table';
5
5
  import 'react';
package/dist/index.js CHANGED
@@ -195,10 +195,36 @@ function getCellSelectionBounds(start, end) {
195
195
  endCol: Math.max(start.col, end.col)
196
196
  };
197
197
  }
198
+ function measureMergedSpanRowHeights(rowIndex, rowSpan, cellElement) {
199
+ if (rowSpan <= 1) return void 0;
200
+ const tbody = cellElement?.closest("tbody") ?? (typeof document !== "undefined" ? document.querySelector("tbody.data-table-body") : null);
201
+ if (!tbody) return void 0;
202
+ const rows = tbody.querySelectorAll(":scope > tr");
203
+ if (rows.length < rowIndex + rowSpan) return void 0;
204
+ const heights = [];
205
+ for (let i = 0; i < rowSpan; i++) {
206
+ const row = rows[rowIndex + i];
207
+ const height = row?.getBoundingClientRect().height ?? 0;
208
+ if (height <= 0) return void 0;
209
+ heights.push(height);
210
+ }
211
+ return heights;
212
+ }
198
213
  function getRowIndexInMergedCell(clientY, cellElement, rowIndex, rowSpan) {
199
214
  if (rowSpan <= 1) return rowIndex;
200
215
  const rect = cellElement.getBoundingClientRect();
201
216
  const relativeY = clientY - rect.top;
217
+ const heights = measureMergedSpanRowHeights(rowIndex, rowSpan, cellElement);
218
+ if (heights && heights.length === rowSpan) {
219
+ let accrued = 0;
220
+ for (let i = 0; i < rowSpan; i++) {
221
+ accrued += heights[i];
222
+ if (relativeY < accrued) {
223
+ return rowIndex + i;
224
+ }
225
+ }
226
+ return rowIndex + rowSpan - 1;
227
+ }
202
228
  const rowHeight = rect.height / rowSpan;
203
229
  const offset = Math.min(
204
230
  Math.max(Math.floor(relativeY / rowHeight), 0),
@@ -220,7 +246,35 @@ function getActiveSelectionBounds(dragState, selectionBounds) {
220
246
  var SELECTION_EDGE_WIDTH_PX = 2;
221
247
  var SELECTION_EDGE_COLOR = "var(--color-brand-primary)";
222
248
  var CELL_SELECTION_EDGES_CLASS = "cell-selection-edges";
223
- function getMergedCellStepEdges(rowIndex, colIndex, bounds, rowSpan, isVisuallySelectedAt) {
249
+ function rowRangeToHeightRatios(rowIndex, span, fromRow, toRowExclusive, rowHeights) {
250
+ const clampedFrom = Math.max(fromRow, rowIndex);
251
+ const clampedTo = Math.min(toRowExclusive, rowIndex + span);
252
+ if (clampedTo <= clampedFrom) {
253
+ return { offsetRatio: 0, lengthRatio: 0 };
254
+ }
255
+ if (!rowHeights || rowHeights.length !== span) {
256
+ return {
257
+ offsetRatio: (clampedFrom - rowIndex) / span,
258
+ lengthRatio: (clampedTo - clampedFrom) / span
259
+ };
260
+ }
261
+ const total = rowHeights.reduce((sum, height) => sum + height, 0) || 1;
262
+ let offsetPx = 0;
263
+ for (let i = 0; i < clampedFrom - rowIndex; i++) {
264
+ offsetPx += rowHeights[i] ?? 0;
265
+ }
266
+ let lengthPx = 0;
267
+ for (let i = clampedFrom - rowIndex; i < clampedTo - rowIndex; i++) {
268
+ lengthPx += rowHeights[i] ?? 0;
269
+ }
270
+ return {
271
+ offsetRatio: offsetPx / total,
272
+ lengthRatio: lengthPx / total,
273
+ offsetPx,
274
+ lengthPx
275
+ };
276
+ }
277
+ function getMergedCellStepEdges(rowIndex, colIndex, bounds, rowSpan, isVisuallySelectedAt, rowHeights) {
224
278
  const cellEndRow = rowIndex + rowSpan - 1;
225
279
  const span = cellEndRow - rowIndex + 1;
226
280
  if (span <= 1) return [];
@@ -239,20 +293,30 @@ function getMergedCellStepEdges(rowIndex, colIndex, bounds, rowSpan, isVisuallyS
239
293
  continue;
240
294
  }
241
295
  if (runStart !== null) {
242
- edges.push({
243
- side,
244
- offsetRatio: (runStart - rowIndex) / span,
245
- heightRatio: (row - runStart) / span
246
- });
296
+ const ratios = rowRangeToHeightRatios(
297
+ rowIndex,
298
+ span,
299
+ runStart,
300
+ row,
301
+ rowHeights
302
+ );
303
+ if (ratios.lengthRatio > 0) {
304
+ edges.push({ side, ...ratios });
305
+ }
247
306
  runStart = null;
248
307
  }
249
308
  }
250
309
  if (runStart !== null) {
251
- edges.push({
252
- side,
253
- offsetRatio: (runStart - rowIndex) / span,
254
- heightRatio: (toRowExclusive - runStart) / span
255
- });
310
+ const ratios = rowRangeToHeightRatios(
311
+ rowIndex,
312
+ span,
313
+ runStart,
314
+ toRowExclusive,
315
+ rowHeights
316
+ );
317
+ if (ratios.lengthRatio > 0) {
318
+ edges.push({ side, ...ratios });
319
+ }
256
320
  }
257
321
  };
258
322
  const collectSide = (side, neighborCol) => {
@@ -281,14 +345,17 @@ function getMergedCellStepEdges(rowIndex, colIndex, bounds, rowSpan, isVisuallyS
281
345
  }
282
346
  return edges;
283
347
  }
284
- function buildPartialVerticalGradient(edge) {
348
+ function buildPartialEdgeGradient(edge) {
349
+ const usePx = edge.offsetPx != null && edge.lengthPx != null;
285
350
  const startPct = edge.offsetRatio * 100;
286
- const endPct = (edge.offsetRatio + edge.heightRatio) * 100;
287
- const overlapPx = SELECTION_EDGE_WIDTH_PX + 1;
288
- const isTopProtrusion = edge.offsetRatio === 0 && edge.heightRatio < 1;
289
- const isBottomProtrusion = edge.offsetRatio > 0;
290
- const startStop = isBottomProtrusion ? `calc(${startPct}% - ${overlapPx}px)` : `${startPct}%`;
291
- const endStop = isTopProtrusion ? `calc(${endPct}% + ${overlapPx}px)` : `${endPct}%`;
351
+ const endPct = (edge.offsetRatio + edge.lengthRatio) * 100;
352
+ const startPx = edge.offsetPx ?? 0;
353
+ const endPx = (edge.offsetPx ?? 0) + (edge.lengthPx ?? 0);
354
+ const overlapPx = SELECTION_EDGE_WIDTH_PX;
355
+ const isTopProtrusion = (usePx ? startPx === 0 : edge.offsetRatio === 0) && edge.lengthRatio < 1;
356
+ const isBottomProtrusion = usePx ? startPx > 0 : edge.offsetRatio > 0;
357
+ const startStop = usePx ? isBottomProtrusion ? `${Math.max(0, startPx - overlapPx)}px` : `${startPx}px` : isBottomProtrusion ? `calc(${startPct}% - ${overlapPx}px)` : `${startPct}%`;
358
+ const endStop = usePx ? isTopProtrusion ? `${endPx + overlapPx}px` : `${endPx}px` : isTopProtrusion ? `calc(${endPct}% + ${overlapPx}px)` : `${endPct}%`;
292
359
  const xPos = edge.side === "left" ? "0" : "100%";
293
360
  const layers = [
294
361
  {
@@ -298,7 +365,7 @@ function buildPartialVerticalGradient(edge) {
298
365
  }
299
366
  ];
300
367
  if (isTopProtrusion || isBottomProtrusion) {
301
- const capTop = isTopProtrusion ? `calc(${endPct}% - ${SELECTION_EDGE_WIDTH_PX}px)` : `calc(${startPct}% - ${SELECTION_EDGE_WIDTH_PX}px)`;
368
+ const capTop = usePx ? isTopProtrusion ? `${Math.max(0, endPx - SELECTION_EDGE_WIDTH_PX)}px` : `${Math.max(0, startPx - SELECTION_EDGE_WIDTH_PX)}px` : isTopProtrusion ? `calc(${endPct}% - ${SELECTION_EDGE_WIDTH_PX}px)` : `calc(${startPct}% - ${SELECTION_EDGE_WIDTH_PX}px)`;
302
369
  layers.push({
303
370
  image: `linear-gradient(${SELECTION_EDGE_COLOR}, ${SELECTION_EDGE_COLOR})`,
304
371
  size: `${SELECTION_EDGE_WIDTH_PX}px ${SELECTION_EDGE_WIDTH_PX}px`,
@@ -307,7 +374,7 @@ function buildPartialVerticalGradient(edge) {
307
374
  }
308
375
  return layers;
309
376
  }
310
- function getCellSelectionEdgeStyle(rowIndex, colIndex, bounds, rowSpan = 1, isVisuallySelectedAt) {
377
+ function getCellSelectionEdgeStyle(rowIndex, colIndex, bounds, rowSpan = 1, isVisuallySelectedAt, rowHeights) {
311
378
  if (!isCellInSelection(rowIndex, colIndex, bounds, rowSpan) || !bounds)
312
379
  return void 0;
313
380
  const cellEndRow = rowIndex + rowSpan - 1;
@@ -316,39 +383,47 @@ function getCellSelectionEdgeStyle(rowIndex, colIndex, bounds, rowSpan = 1, isVi
316
383
  const isLeftEdge = colIndex === bounds.startCol;
317
384
  const isRightEdge = colIndex === bounds.endCol;
318
385
  const selectionContinuesBelow = cellEndRow < bounds.endRow;
319
- const shadows = [];
320
- if (isTopEdge) {
321
- shadows.push(
322
- `inset 0 ${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`
323
- );
324
- }
325
- if (isBottomEdge) {
326
- shadows.push(
327
- `inset 0 -${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`
328
- );
329
- }
330
- if (isLeftEdge) {
331
- shadows.push(
332
- `inset ${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`
333
- );
334
- }
335
- if (isRightEdge) {
336
- shadows.push(
337
- `inset -${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`
338
- );
339
- }
340
386
  const stepEdges = getMergedCellStepEdges(
341
387
  rowIndex,
342
388
  colIndex,
343
389
  bounds,
344
390
  rowSpan,
345
- isVisuallySelectedAt
391
+ isVisuallySelectedAt,
392
+ rowHeights
346
393
  );
394
+ const shadows = [];
395
+ const hasFullPerimeter = isTopEdge && isBottomEdge && isLeftEdge && isRightEdge && stepEdges.length === 0;
396
+ if (hasFullPerimeter) {
397
+ shadows.push(
398
+ `inset 0 0 0 ${SELECTION_EDGE_WIDTH_PX}px ${SELECTION_EDGE_COLOR}`
399
+ );
400
+ } else {
401
+ if (isTopEdge) {
402
+ shadows.push(
403
+ `inset 0 ${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`
404
+ );
405
+ }
406
+ if (isBottomEdge) {
407
+ shadows.push(
408
+ `inset 0 -${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`
409
+ );
410
+ }
411
+ if (isLeftEdge) {
412
+ shadows.push(
413
+ `inset ${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`
414
+ );
415
+ }
416
+ if (isRightEdge) {
417
+ shadows.push(
418
+ `inset -${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`
419
+ );
420
+ }
421
+ }
347
422
  const gradients = [];
348
423
  const sizes = [];
349
424
  const positions = [];
350
425
  for (const edge of stepEdges) {
351
- for (const partial of buildPartialVerticalGradient(edge)) {
426
+ for (const partial of buildPartialEdgeGradient(edge)) {
352
427
  gradients.push(partial.image);
353
428
  sizes.push(partial.size);
354
429
  positions.push(partial.position);
@@ -1439,9 +1514,10 @@ function DataTableRow({
1439
1514
  }
1440
1515
  }
1441
1516
  const isEditing = editingCell?.rowIndex === rowIndex && editingCell?.colIndex === cellIndex;
1442
- const showCellHover = isRowSpanColumn ? isGroupHovered : isRowHovered;
1443
- const showCellSelected = isRowSpanColumn ? isGroupSelected : isRowSelected;
1444
1517
  const cellRowSpan = rowSpanInfo?.rowSpan ?? 1;
1518
+ const isMergedCellHovered = hoveredRowIndex !== null && hoveredRowIndex >= rowIndex && hoveredRowIndex <= rowIndex + cellRowSpan - 1;
1519
+ const showCellHover = isRowSpanColumn ? isMergedCellHovered : isRowHovered;
1520
+ const showCellSelected = isRowSpanColumn ? isGroupSelected : isRowSelected;
1445
1521
  const isMerged = cellRowSpan > 1;
1446
1522
  const showMergedRightEdge = isMerged && (cellIndex === visibleCells.length - 1 || resolveRowSpanAt(
1447
1523
  columnRowSpanMap.get(columnIdsByIndex[cellIndex + 1]),
@@ -1454,12 +1530,14 @@ function DataTableRow({
1454
1530
  cellRowSpan
1455
1531
  );
1456
1532
  const isBottomRightCell = !isEditing && activeSelectionBounds && !dragState.isSelecting && activeSelectionBounds.endRow >= rowIndex && activeSelectionBounds.endRow <= rowIndex + cellRowSpan - 1 && cellIndex === activeSelectionBounds.endCol;
1533
+ const spanRowHeights = enableCellSelection && activeSelectionBounds && isCellDragSelected && cellRowSpan > 1 ? measureMergedSpanRowHeights(rowIndex, cellRowSpan) : void 0;
1457
1534
  const selectionEdgeStyle = getCellSelectionEdgeStyle(
1458
1535
  rowIndex,
1459
1536
  cellIndex,
1460
1537
  activeSelectionBounds,
1461
1538
  cellRowSpan,
1462
- isVisuallySelectedAt
1539
+ isVisuallySelectedAt,
1540
+ spanRowHeights
1463
1541
  );
1464
1542
  const resolveCellRowIndex = (clientY, element) => getRowIndexInMergedCell(clientY, element, rowIndex, cellRowSpan);
1465
1543
  const hasSelectionEdges = hasCellSelectionEdges(selectionEdgeStyle);
@@ -1469,6 +1547,7 @@ function DataTableRow({
1469
1547
  rowSpan: rowSpanInfo && rowSpanInfo.rowSpan > 1 ? rowSpanInfo.rowSpan : void 0,
1470
1548
  "data-merged": isMerged && cellIndex > 0 ? "" : void 0,
1471
1549
  "data-merged-edge-right": showMergedRightEdge ? "" : void 0,
1550
+ "data-merged-row-first": isMerged && cellIndex === 0 && showMergedRightEdge ? "" : void 0,
1472
1551
  "data-group-selected": enableRowSpan && showCellSelected ? "" : void 0,
1473
1552
  "data-group-hovered": enableRowSpan && showCellHover && !showCellSelected ? "" : void 0,
1474
1553
  "data-selection-fill": isCellDragSelected ? "" : void 0,
@@ -1513,7 +1592,8 @@ function DataTableRow({
1513
1592
  "data-table-cell",
1514
1593
  CELL_ALIGN_CLASS[align],
1515
1594
  cellClassName,
1516
- isMerged && cellIndex > 0 && "is-merged",
1595
+ isMerged && "is-merged",
1596
+ isMerged && cellIndex === 0 && showMergedRightEdge && "is-merged-row-first",
1517
1597
  showMergedRightEdge && "is-merged-edge-right",
1518
1598
  enableRowSpan && showCellSelected && "is-group-selected",
1519
1599
  enableRowSpan && showCellHover && !showCellSelected && "is-group-hovered",
@@ -1970,7 +2050,7 @@ function buildColumnDef(props, sort, onSort) {
1970
2050
  }
1971
2051
 
1972
2052
  // src/components/ui/table/components/Table/parseTableChildren.ts
1973
- import { Children } from "react";
2053
+ import { Children, isValidElement as isValidElement2 } from "react";
1974
2054
 
1975
2055
  // src/components/ui/table/components/Table/tableChildTypes.ts
1976
2056
  import { isValidElement } from "react";
@@ -2019,10 +2099,26 @@ function parseTableChildren(children) {
2019
2099
  }
2020
2100
  return slots;
2021
2101
  }
2102
+ function flattenColumnElements(children) {
2103
+ const result = [];
2104
+ for (const child of Children.toArray(children)) {
2105
+ if (isTableColumnElement(child)) {
2106
+ result.push(child);
2107
+ continue;
2108
+ }
2109
+ if (isValidElement2(child)) {
2110
+ const nested = child.props.children;
2111
+ if (nested != null) {
2112
+ result.push(...flattenColumnElements(nested));
2113
+ }
2114
+ }
2115
+ }
2116
+ return result;
2117
+ }
2022
2118
  function extractColumnElements(header) {
2023
2119
  if (!header) return [];
2024
2120
  const { children } = header.props;
2025
- return Children.toArray(children).filter(isTableColumnElement);
2121
+ return flattenColumnElements(children);
2026
2122
  }
2027
2123
 
2028
2124
  // src/components/ui/table/components/Table/TableBody.tsx
@@ -2234,10 +2330,12 @@ export {
2234
2330
  hasCellSelectionEdges,
2235
2331
  isCellInSelection,
2236
2332
  isColumnEditable,
2333
+ measureMergedSpanRowHeights,
2237
2334
  parseCellEditValue,
2238
2335
  resolveDataTableLabels,
2239
2336
  resolveRowSelection,
2240
2337
  resolveRowSpanAt,
2338
+ rowRangeToHeightRatios,
2241
2339
  toggleExpandedRowId,
2242
2340
  useCellEdit,
2243
2341
  useCellSelection,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-glide-table",
3
- "version": "1.1.4",
3
+ "version": "1.1.5",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/zpxlffjrm/react-glide-table.git"