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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 송찬영
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/compound.cjs CHANGED
@@ -128,10 +128,36 @@ function getCellSelectionBounds(start, end) {
128
128
  endCol: Math.max(start.col, end.col)
129
129
  };
130
130
  }
131
+ function measureMergedSpanRowHeights(rowIndex, rowSpan, cellElement) {
132
+ if (rowSpan <= 1) return void 0;
133
+ const tbody = cellElement?.closest("tbody") ?? (typeof document !== "undefined" ? document.querySelector("tbody.data-table-body") : null);
134
+ if (!tbody) return void 0;
135
+ const rows = tbody.querySelectorAll(":scope > tr");
136
+ if (rows.length < rowIndex + rowSpan) return void 0;
137
+ const heights = [];
138
+ for (let i = 0; i < rowSpan; i++) {
139
+ const row = rows[rowIndex + i];
140
+ const height = row?.getBoundingClientRect().height ?? 0;
141
+ if (height <= 0) return void 0;
142
+ heights.push(height);
143
+ }
144
+ return heights;
145
+ }
131
146
  function getRowIndexInMergedCell(clientY, cellElement, rowIndex, rowSpan) {
132
147
  if (rowSpan <= 1) return rowIndex;
133
148
  const rect = cellElement.getBoundingClientRect();
134
149
  const relativeY = clientY - rect.top;
150
+ const heights = measureMergedSpanRowHeights(rowIndex, rowSpan, cellElement);
151
+ if (heights && heights.length === rowSpan) {
152
+ let accrued = 0;
153
+ for (let i = 0; i < rowSpan; i++) {
154
+ accrued += heights[i];
155
+ if (relativeY < accrued) {
156
+ return rowIndex + i;
157
+ }
158
+ }
159
+ return rowIndex + rowSpan - 1;
160
+ }
135
161
  const rowHeight = rect.height / rowSpan;
136
162
  const offset = Math.min(
137
163
  Math.max(Math.floor(relativeY / rowHeight), 0),
@@ -153,7 +179,35 @@ function getActiveSelectionBounds(dragState, selectionBounds) {
153
179
  var SELECTION_EDGE_WIDTH_PX = 2;
154
180
  var SELECTION_EDGE_COLOR = "var(--color-brand-primary)";
155
181
  var CELL_SELECTION_EDGES_CLASS = "cell-selection-edges";
156
- function getMergedCellStepEdges(rowIndex, colIndex, bounds, rowSpan, isVisuallySelectedAt) {
182
+ function rowRangeToHeightRatios(rowIndex, span, fromRow, toRowExclusive, rowHeights) {
183
+ const clampedFrom = Math.max(fromRow, rowIndex);
184
+ const clampedTo = Math.min(toRowExclusive, rowIndex + span);
185
+ if (clampedTo <= clampedFrom) {
186
+ return { offsetRatio: 0, lengthRatio: 0 };
187
+ }
188
+ if (!rowHeights || rowHeights.length !== span) {
189
+ return {
190
+ offsetRatio: (clampedFrom - rowIndex) / span,
191
+ lengthRatio: (clampedTo - clampedFrom) / span
192
+ };
193
+ }
194
+ const total = rowHeights.reduce((sum, height) => sum + height, 0) || 1;
195
+ let offsetPx = 0;
196
+ for (let i = 0; i < clampedFrom - rowIndex; i++) {
197
+ offsetPx += rowHeights[i] ?? 0;
198
+ }
199
+ let lengthPx = 0;
200
+ for (let i = clampedFrom - rowIndex; i < clampedTo - rowIndex; i++) {
201
+ lengthPx += rowHeights[i] ?? 0;
202
+ }
203
+ return {
204
+ offsetRatio: offsetPx / total,
205
+ lengthRatio: lengthPx / total,
206
+ offsetPx,
207
+ lengthPx
208
+ };
209
+ }
210
+ function getMergedCellStepEdges(rowIndex, colIndex, bounds, rowSpan, isVisuallySelectedAt, rowHeights) {
157
211
  const cellEndRow = rowIndex + rowSpan - 1;
158
212
  const span = cellEndRow - rowIndex + 1;
159
213
  if (span <= 1) return [];
@@ -172,20 +226,30 @@ function getMergedCellStepEdges(rowIndex, colIndex, bounds, rowSpan, isVisuallyS
172
226
  continue;
173
227
  }
174
228
  if (runStart !== null) {
175
- edges.push({
176
- side,
177
- offsetRatio: (runStart - rowIndex) / span,
178
- heightRatio: (row - runStart) / span
179
- });
229
+ const ratios = rowRangeToHeightRatios(
230
+ rowIndex,
231
+ span,
232
+ runStart,
233
+ row,
234
+ rowHeights
235
+ );
236
+ if (ratios.lengthRatio > 0) {
237
+ edges.push({ side, ...ratios });
238
+ }
180
239
  runStart = null;
181
240
  }
182
241
  }
183
242
  if (runStart !== null) {
184
- edges.push({
185
- side,
186
- offsetRatio: (runStart - rowIndex) / span,
187
- heightRatio: (toRowExclusive - runStart) / span
188
- });
243
+ const ratios = rowRangeToHeightRatios(
244
+ rowIndex,
245
+ span,
246
+ runStart,
247
+ toRowExclusive,
248
+ rowHeights
249
+ );
250
+ if (ratios.lengthRatio > 0) {
251
+ edges.push({ side, ...ratios });
252
+ }
189
253
  }
190
254
  };
191
255
  const collectSide = (side, neighborCol) => {
@@ -214,14 +278,17 @@ function getMergedCellStepEdges(rowIndex, colIndex, bounds, rowSpan, isVisuallyS
214
278
  }
215
279
  return edges;
216
280
  }
217
- function buildPartialVerticalGradient(edge) {
281
+ function buildPartialEdgeGradient(edge) {
282
+ const usePx = edge.offsetPx != null && edge.lengthPx != null;
218
283
  const startPct = edge.offsetRatio * 100;
219
- const endPct = (edge.offsetRatio + edge.heightRatio) * 100;
220
- const overlapPx = SELECTION_EDGE_WIDTH_PX + 1;
221
- const isTopProtrusion = edge.offsetRatio === 0 && edge.heightRatio < 1;
222
- const isBottomProtrusion = edge.offsetRatio > 0;
223
- const startStop = isBottomProtrusion ? `calc(${startPct}% - ${overlapPx}px)` : `${startPct}%`;
224
- const endStop = isTopProtrusion ? `calc(${endPct}% + ${overlapPx}px)` : `${endPct}%`;
284
+ const endPct = (edge.offsetRatio + edge.lengthRatio) * 100;
285
+ const startPx = edge.offsetPx ?? 0;
286
+ const endPx = (edge.offsetPx ?? 0) + (edge.lengthPx ?? 0);
287
+ const overlapPx = SELECTION_EDGE_WIDTH_PX;
288
+ const isTopProtrusion = (usePx ? startPx === 0 : edge.offsetRatio === 0) && edge.lengthRatio < 1;
289
+ const isBottomProtrusion = usePx ? startPx > 0 : edge.offsetRatio > 0;
290
+ const startStop = usePx ? isBottomProtrusion ? `${Math.max(0, startPx - overlapPx)}px` : `${startPx}px` : isBottomProtrusion ? `calc(${startPct}% - ${overlapPx}px)` : `${startPct}%`;
291
+ const endStop = usePx ? isTopProtrusion ? `${endPx + overlapPx}px` : `${endPx}px` : isTopProtrusion ? `calc(${endPct}% + ${overlapPx}px)` : `${endPct}%`;
225
292
  const xPos = edge.side === "left" ? "0" : "100%";
226
293
  const layers = [
227
294
  {
@@ -231,7 +298,7 @@ function buildPartialVerticalGradient(edge) {
231
298
  }
232
299
  ];
233
300
  if (isTopProtrusion || isBottomProtrusion) {
234
- const capTop = isTopProtrusion ? `calc(${endPct}% - ${SELECTION_EDGE_WIDTH_PX}px)` : `calc(${startPct}% - ${SELECTION_EDGE_WIDTH_PX}px)`;
301
+ const capTop = usePx ? isTopProtrusion ? `${Math.max(0, endPx - SELECTION_EDGE_WIDTH_PX)}px` : `${Math.max(0, startPx - SELECTION_EDGE_WIDTH_PX)}px` : isTopProtrusion ? `calc(${endPct}% - ${SELECTION_EDGE_WIDTH_PX}px)` : `calc(${startPct}% - ${SELECTION_EDGE_WIDTH_PX}px)`;
235
302
  layers.push({
236
303
  image: `linear-gradient(${SELECTION_EDGE_COLOR}, ${SELECTION_EDGE_COLOR})`,
237
304
  size: `${SELECTION_EDGE_WIDTH_PX}px ${SELECTION_EDGE_WIDTH_PX}px`,
@@ -240,7 +307,7 @@ function buildPartialVerticalGradient(edge) {
240
307
  }
241
308
  return layers;
242
309
  }
243
- function getCellSelectionEdgeStyle(rowIndex, colIndex, bounds, rowSpan = 1, isVisuallySelectedAt) {
310
+ function getCellSelectionEdgeStyle(rowIndex, colIndex, bounds, rowSpan = 1, isVisuallySelectedAt, rowHeights) {
244
311
  if (!isCellInSelection(rowIndex, colIndex, bounds, rowSpan) || !bounds)
245
312
  return void 0;
246
313
  const cellEndRow = rowIndex + rowSpan - 1;
@@ -249,39 +316,47 @@ function getCellSelectionEdgeStyle(rowIndex, colIndex, bounds, rowSpan = 1, isVi
249
316
  const isLeftEdge = colIndex === bounds.startCol;
250
317
  const isRightEdge = colIndex === bounds.endCol;
251
318
  const selectionContinuesBelow = cellEndRow < bounds.endRow;
252
- const shadows = [];
253
- if (isTopEdge) {
254
- shadows.push(
255
- `inset 0 ${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`
256
- );
257
- }
258
- if (isBottomEdge) {
259
- shadows.push(
260
- `inset 0 -${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`
261
- );
262
- }
263
- if (isLeftEdge) {
264
- shadows.push(
265
- `inset ${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`
266
- );
267
- }
268
- if (isRightEdge) {
269
- shadows.push(
270
- `inset -${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`
271
- );
272
- }
273
319
  const stepEdges = getMergedCellStepEdges(
274
320
  rowIndex,
275
321
  colIndex,
276
322
  bounds,
277
323
  rowSpan,
278
- isVisuallySelectedAt
324
+ isVisuallySelectedAt,
325
+ rowHeights
279
326
  );
327
+ const shadows = [];
328
+ const hasFullPerimeter = isTopEdge && isBottomEdge && isLeftEdge && isRightEdge && stepEdges.length === 0;
329
+ if (hasFullPerimeter) {
330
+ shadows.push(
331
+ `inset 0 0 0 ${SELECTION_EDGE_WIDTH_PX}px ${SELECTION_EDGE_COLOR}`
332
+ );
333
+ } else {
334
+ if (isTopEdge) {
335
+ shadows.push(
336
+ `inset 0 ${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`
337
+ );
338
+ }
339
+ if (isBottomEdge) {
340
+ shadows.push(
341
+ `inset 0 -${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`
342
+ );
343
+ }
344
+ if (isLeftEdge) {
345
+ shadows.push(
346
+ `inset ${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`
347
+ );
348
+ }
349
+ if (isRightEdge) {
350
+ shadows.push(
351
+ `inset -${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`
352
+ );
353
+ }
354
+ }
280
355
  const gradients = [];
281
356
  const sizes = [];
282
357
  const positions = [];
283
358
  for (const edge of stepEdges) {
284
- for (const partial of buildPartialVerticalGradient(edge)) {
359
+ for (const partial of buildPartialEdgeGradient(edge)) {
285
360
  gradients.push(partial.image);
286
361
  sizes.push(partial.size);
287
362
  positions.push(partial.position);
@@ -853,9 +928,10 @@ function DataTableRow({
853
928
  }
854
929
  }
855
930
  const isEditing = editingCell?.rowIndex === rowIndex && editingCell?.colIndex === cellIndex;
856
- const showCellHover = isRowSpanColumn ? isGroupHovered : isRowHovered;
857
- const showCellSelected = isRowSpanColumn ? isGroupSelected : isRowSelected;
858
931
  const cellRowSpan = rowSpanInfo?.rowSpan ?? 1;
932
+ const isMergedCellHovered = hoveredRowIndex !== null && hoveredRowIndex >= rowIndex && hoveredRowIndex <= rowIndex + cellRowSpan - 1;
933
+ const showCellHover = isRowSpanColumn ? isMergedCellHovered : isRowHovered;
934
+ const showCellSelected = isRowSpanColumn ? isGroupSelected : isRowSelected;
859
935
  const isMerged = cellRowSpan > 1;
860
936
  const showMergedRightEdge = isMerged && (cellIndex === visibleCells.length - 1 || resolveRowSpanAt(
861
937
  columnRowSpanMap.get(columnIdsByIndex[cellIndex + 1]),
@@ -868,12 +944,14 @@ function DataTableRow({
868
944
  cellRowSpan
869
945
  );
870
946
  const isBottomRightCell = !isEditing && activeSelectionBounds && !dragState.isSelecting && activeSelectionBounds.endRow >= rowIndex && activeSelectionBounds.endRow <= rowIndex + cellRowSpan - 1 && cellIndex === activeSelectionBounds.endCol;
947
+ const spanRowHeights = enableCellSelection && activeSelectionBounds && isCellDragSelected && cellRowSpan > 1 ? measureMergedSpanRowHeights(rowIndex, cellRowSpan) : void 0;
871
948
  const selectionEdgeStyle = getCellSelectionEdgeStyle(
872
949
  rowIndex,
873
950
  cellIndex,
874
951
  activeSelectionBounds,
875
952
  cellRowSpan,
876
- isVisuallySelectedAt
953
+ isVisuallySelectedAt,
954
+ spanRowHeights
877
955
  );
878
956
  const resolveCellRowIndex = (clientY, element) => getRowIndexInMergedCell(clientY, element, rowIndex, cellRowSpan);
879
957
  const hasSelectionEdges = hasCellSelectionEdges(selectionEdgeStyle);
@@ -883,6 +961,7 @@ function DataTableRow({
883
961
  rowSpan: rowSpanInfo && rowSpanInfo.rowSpan > 1 ? rowSpanInfo.rowSpan : void 0,
884
962
  "data-merged": isMerged && cellIndex > 0 ? "" : void 0,
885
963
  "data-merged-edge-right": showMergedRightEdge ? "" : void 0,
964
+ "data-merged-row-first": isMerged && cellIndex === 0 && showMergedRightEdge ? "" : void 0,
886
965
  "data-group-selected": enableRowSpan && showCellSelected ? "" : void 0,
887
966
  "data-group-hovered": enableRowSpan && showCellHover && !showCellSelected ? "" : void 0,
888
967
  "data-selection-fill": isCellDragSelected ? "" : void 0,
@@ -927,7 +1006,8 @@ function DataTableRow({
927
1006
  "data-table-cell",
928
1007
  CELL_ALIGN_CLASS[align],
929
1008
  cellClassName,
930
- isMerged && cellIndex > 0 && "is-merged",
1009
+ isMerged && "is-merged",
1010
+ isMerged && cellIndex === 0 && showMergedRightEdge && "is-merged-row-first",
931
1011
  showMergedRightEdge && "is-merged-edge-right",
932
1012
  enableRowSpan && showCellSelected && "is-group-selected",
933
1013
  enableRowSpan && showCellHover && !showCellSelected && "is-group-hovered",
@@ -2036,10 +2116,26 @@ function parseTableChildren(children) {
2036
2116
  }
2037
2117
  return slots;
2038
2118
  }
2119
+ function flattenColumnElements(children) {
2120
+ const result = [];
2121
+ for (const child of import_react9.Children.toArray(children)) {
2122
+ if (isTableColumnElement(child)) {
2123
+ result.push(child);
2124
+ continue;
2125
+ }
2126
+ if ((0, import_react9.isValidElement)(child)) {
2127
+ const nested = child.props.children;
2128
+ if (nested != null) {
2129
+ result.push(...flattenColumnElements(nested));
2130
+ }
2131
+ }
2132
+ }
2133
+ return result;
2134
+ }
2039
2135
  function extractColumnElements(header) {
2040
2136
  if (!header) return [];
2041
2137
  const { children } = header.props;
2042
- return import_react9.Children.toArray(children).filter(isTableColumnElement);
2138
+ return flattenColumnElements(children);
2043
2139
  }
2044
2140
 
2045
2141
  // src/components/ui/table/components/Table/TableBody.tsx
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 getMergedCellStepEdges(rowIndex, colIndex, bounds, rowSpan, isVisuallySelectedAt) {
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
- edges.push({
148
- side,
149
- offsetRatio: (runStart - rowIndex) / span,
150
- heightRatio: (row - runStart) / span
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
- edges.push({
157
- side,
158
- offsetRatio: (runStart - rowIndex) / span,
159
- heightRatio: (toRowExclusive - runStart) / span
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 buildPartialVerticalGradient(edge) {
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.heightRatio) * 100;
192
- const overlapPx = SELECTION_EDGE_WIDTH_PX + 1;
193
- const isTopProtrusion = edge.offsetRatio === 0 && edge.heightRatio < 1;
194
- const isBottomProtrusion = edge.offsetRatio > 0;
195
- const startStop = isBottomProtrusion ? `calc(${startPct}% - ${overlapPx}px)` : `${startPct}%`;
196
- const endStop = isTopProtrusion ? `calc(${endPct}% + ${overlapPx}px)` : `${endPct}%`;
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 buildPartialVerticalGradient(edge)) {
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);
@@ -825,9 +900,10 @@ function DataTableRow({
825
900
  }
826
901
  }
827
902
  const isEditing = editingCell?.rowIndex === rowIndex && editingCell?.colIndex === cellIndex;
828
- const showCellHover = isRowSpanColumn ? isGroupHovered : isRowHovered;
829
- const showCellSelected = isRowSpanColumn ? isGroupSelected : isRowSelected;
830
903
  const cellRowSpan = rowSpanInfo?.rowSpan ?? 1;
904
+ const isMergedCellHovered = hoveredRowIndex !== null && hoveredRowIndex >= rowIndex && hoveredRowIndex <= rowIndex + cellRowSpan - 1;
905
+ const showCellHover = isRowSpanColumn ? isMergedCellHovered : isRowHovered;
906
+ const showCellSelected = isRowSpanColumn ? isGroupSelected : isRowSelected;
831
907
  const isMerged = cellRowSpan > 1;
832
908
  const showMergedRightEdge = isMerged && (cellIndex === visibleCells.length - 1 || resolveRowSpanAt(
833
909
  columnRowSpanMap.get(columnIdsByIndex[cellIndex + 1]),
@@ -840,12 +916,14 @@ function DataTableRow({
840
916
  cellRowSpan
841
917
  );
842
918
  const isBottomRightCell = !isEditing && activeSelectionBounds && !dragState.isSelecting && activeSelectionBounds.endRow >= rowIndex && activeSelectionBounds.endRow <= rowIndex + cellRowSpan - 1 && cellIndex === activeSelectionBounds.endCol;
919
+ const spanRowHeights = enableCellSelection && activeSelectionBounds && isCellDragSelected && cellRowSpan > 1 ? measureMergedSpanRowHeights(rowIndex, cellRowSpan) : void 0;
843
920
  const selectionEdgeStyle = getCellSelectionEdgeStyle(
844
921
  rowIndex,
845
922
  cellIndex,
846
923
  activeSelectionBounds,
847
924
  cellRowSpan,
848
- isVisuallySelectedAt
925
+ isVisuallySelectedAt,
926
+ spanRowHeights
849
927
  );
850
928
  const resolveCellRowIndex = (clientY, element) => getRowIndexInMergedCell(clientY, element, rowIndex, cellRowSpan);
851
929
  const hasSelectionEdges = hasCellSelectionEdges(selectionEdgeStyle);
@@ -855,6 +933,7 @@ function DataTableRow({
855
933
  rowSpan: rowSpanInfo && rowSpanInfo.rowSpan > 1 ? rowSpanInfo.rowSpan : void 0,
856
934
  "data-merged": isMerged && cellIndex > 0 ? "" : void 0,
857
935
  "data-merged-edge-right": showMergedRightEdge ? "" : void 0,
936
+ "data-merged-row-first": isMerged && cellIndex === 0 && showMergedRightEdge ? "" : void 0,
858
937
  "data-group-selected": enableRowSpan && showCellSelected ? "" : void 0,
859
938
  "data-group-hovered": enableRowSpan && showCellHover && !showCellSelected ? "" : void 0,
860
939
  "data-selection-fill": isCellDragSelected ? "" : void 0,
@@ -899,7 +978,8 @@ function DataTableRow({
899
978
  "data-table-cell",
900
979
  CELL_ALIGN_CLASS[align],
901
980
  cellClassName,
902
- isMerged && cellIndex > 0 && "is-merged",
981
+ isMerged && "is-merged",
982
+ isMerged && cellIndex === 0 && showMergedRightEdge && "is-merged-row-first",
903
983
  showMergedRightEdge && "is-merged-edge-right",
904
984
  enableRowSpan && showCellSelected && "is-group-selected",
905
985
  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