react-glide-table 2.1.0 → 2.2.1

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.js CHANGED
@@ -2225,23 +2225,49 @@ function applySelectionUpdater(mode, updater, previous) {
2225
2225
  function getRowFieldValue(row, key) {
2226
2226
  return row[key];
2227
2227
  }
2228
- function computeRowSpans(data, rowSpanKey) {
2228
+ function normalizeRowSpanParent(value) {
2229
+ if (!value) return [];
2230
+ return typeof value === "string" ? [value] : [...value];
2231
+ }
2232
+ function toParentSpanList(parentSpans) {
2233
+ if (!parentSpans?.length) return [];
2234
+ const first = parentSpans[0];
2235
+ if (!Array.isArray(first)) {
2236
+ return [parentSpans];
2237
+ }
2238
+ return parentSpans;
2239
+ }
2240
+ function buildStartRowLookup(spans) {
2241
+ const startRows = new Array(spans.length);
2242
+ let origin = 0;
2243
+ for (let i = 0; i < spans.length; i++) {
2244
+ if ((spans[i]?.rowSpan ?? 1) > 0) origin = i;
2245
+ startRows[i] = origin;
2246
+ }
2247
+ return startRows;
2248
+ }
2249
+ function sharesParentGroup(parentStartRows, rowIndex) {
2250
+ if (parentStartRows.length === 0 || rowIndex <= 0) return true;
2251
+ return parentStartRows.every(
2252
+ (startRows) => startRows[rowIndex - 1] === startRows[rowIndex]
2253
+ );
2254
+ }
2255
+ function computeRowSpans(data, rowSpanKey, parentSpans) {
2229
2256
  if (data.length === 0) return [];
2257
+ const parentStartRows = toParentSpanList(parentSpans).map(buildStartRowLookup);
2230
2258
  const result = [];
2231
2259
  for (let index = 0; index < data.length; index++) {
2232
2260
  const currentValue = getRowFieldValue(data[index], rowSpanKey);
2233
2261
  const previousValue = index > 0 ? getRowFieldValue(data[index - 1], rowSpanKey) : void 0;
2234
- if (index > 0 && currentValue === previousValue) {
2262
+ if (index > 0 && currentValue === previousValue && sharesParentGroup(parentStartRows, index)) {
2235
2263
  result.push({ rowSpan: 0, isFirstInGroup: false });
2236
2264
  continue;
2237
2265
  }
2238
2266
  let span = 1;
2239
2267
  for (let nextIndex = index + 1; nextIndex < data.length; nextIndex++) {
2240
- if (getRowFieldValue(data[nextIndex], rowSpanKey) === currentValue) {
2241
- span++;
2242
- } else {
2243
- break;
2244
- }
2268
+ if (getRowFieldValue(data[nextIndex], rowSpanKey) !== currentValue) break;
2269
+ if (!sharesParentGroup(parentStartRows, nextIndex)) break;
2270
+ span++;
2245
2271
  }
2246
2272
  result.push({ rowSpan: span, isFirstInGroup: true });
2247
2273
  }
@@ -2263,10 +2289,50 @@ function resolveRowSpanAt(rowSpans, rowIndex) {
2263
2289
  }
2264
2290
  return { startRow: rowIndex, rowSpan: 1 };
2265
2291
  }
2292
+ function findRowSpanColumn(spec, ref) {
2293
+ return spec.find((column) => column.columnId === ref) ?? spec.find((column) => column.rowSpanKey === ref);
2294
+ }
2266
2295
  function buildColumnRowSpanMap(data, columnKeys) {
2267
2296
  const map = /* @__PURE__ */ new Map();
2268
- for (const { columnId, rowSpanKey } of columnKeys) {
2269
- map.set(columnId, computeRowSpans(data, rowSpanKey));
2297
+ const visiting = /* @__PURE__ */ new Set();
2298
+ const warnedCycles = /* @__PURE__ */ new Set();
2299
+ const virtualParents = /* @__PURE__ */ new Map();
2300
+ const spansForColumn = (columnId) => {
2301
+ const cached = map.get(columnId);
2302
+ if (cached !== void 0) return cached;
2303
+ const column = columnKeys.find((item) => item.columnId === columnId);
2304
+ if (!column) return void 0;
2305
+ if (visiting.has(columnId)) {
2306
+ if (!warnedCycles.has(columnId)) {
2307
+ warnedCycles.add(columnId);
2308
+ console.warn(
2309
+ `[rowSpan] rowSpanParent cycle detected at column "${columnId}"; dropping the cyclic parent reference.`
2310
+ );
2311
+ }
2312
+ return void 0;
2313
+ }
2314
+ visiting.add(columnId);
2315
+ const parentSpans = (column.rowSpanParent ?? []).map((ref) => spansForParentRef(ref)).filter((spans2) => Boolean(spans2));
2316
+ visiting.delete(columnId);
2317
+ const spans = computeRowSpans(
2318
+ data,
2319
+ column.rowSpanKey,
2320
+ parentSpans.length > 0 ? parentSpans : void 0
2321
+ );
2322
+ map.set(columnId, spans);
2323
+ return spans;
2324
+ };
2325
+ const spansForParentRef = (ref) => {
2326
+ const parentColumn = findRowSpanColumn(columnKeys, ref);
2327
+ if (parentColumn) return spansForColumn(parentColumn.columnId);
2328
+ const cached = virtualParents.get(ref);
2329
+ if (cached !== void 0) return cached;
2330
+ const spans = computeRowSpans(data, ref);
2331
+ virtualParents.set(ref, spans);
2332
+ return spans;
2333
+ };
2334
+ for (const column of columnKeys) {
2335
+ spansForColumn(column.columnId);
2270
2336
  }
2271
2337
  return map;
2272
2338
  }
@@ -2282,7 +2348,8 @@ function collectRowSpanColumns(columns) {
2282
2348
  if (!columnId || !columnDef.meta?.rowSpan) continue;
2283
2349
  result.push({
2284
2350
  columnId,
2285
- rowSpanKey: columnDef.meta.rowSpanKey ?? columnId
2351
+ rowSpanKey: columnDef.meta.rowSpanKey ?? columnId,
2352
+ rowSpanParent: normalizeRowSpanParent(columnDef.meta.rowSpanParent)
2286
2353
  });
2287
2354
  }
2288
2355
  };
@@ -2990,15 +3057,29 @@ function ResolvedTableCell({
2990
3057
 
2991
3058
  // src/components/ui/table/features/column-resize/columnResize.ts
2992
3059
  function getColumnSizeStyle(size, options) {
2993
- const { force = false, lockMax = false } = options ?? {};
2994
- if (!force && size === DATA_TABLE_COLUMN_SIZE) {
3060
+ const { force = false, lockMax = false, minWidth, maxWidth } = options ?? {};
3061
+ if (lockMax) {
3062
+ return {
3063
+ width: size,
3064
+ minWidth: size,
3065
+ maxWidth: size
3066
+ };
3067
+ }
3068
+ const hasExplicitSize = force || size !== DATA_TABLE_COLUMN_SIZE;
3069
+ if (!hasExplicitSize && minWidth == null && maxWidth == null) {
2995
3070
  return void 0;
2996
3071
  }
2997
- return {
2998
- width: size,
2999
- minWidth: size,
3000
- ...lockMax ? { maxWidth: size } : {}
3001
- };
3072
+ const style = {};
3073
+ if (hasExplicitSize) {
3074
+ style.width = size;
3075
+ style.minWidth = minWidth ?? size;
3076
+ } else if (minWidth != null) {
3077
+ style.minWidth = minWidth;
3078
+ }
3079
+ if (maxWidth != null) {
3080
+ style.maxWidth = maxWidth;
3081
+ }
3082
+ return style;
3002
3083
  }
3003
3084
 
3004
3085
  // src/components/ui/table/features/column-reorder/useColumnReorder.ts
@@ -3607,7 +3688,9 @@ function DataTableRow({
3607
3688
  const hasSelectionEdges = hasCellSelectionEdges(selectionEdgeStyle);
3608
3689
  const sizeStyle = getColumnSizeStyle(cell.column.getSize(), {
3609
3690
  force: enableColumnResize,
3610
- lockMax: enableColumnResize
3691
+ lockMax: enableColumnResize,
3692
+ minWidth: meta?.minWidth,
3693
+ maxWidth: meta?.maxWidth
3611
3694
  });
3612
3695
  const freezeOffset = enableColumnFreeze ? freezeOffsets.get(columnId) : void 0;
3613
3696
  const freezeStyle = getColumnFreezeStyle(freezeOffset);
@@ -4228,7 +4311,9 @@ function DataTable({
4228
4311
  const canResize = enableColumnResize && header.column.getCanResize();
4229
4312
  const sizeStyle = getColumnSizeStyle(header.getSize(), {
4230
4313
  force: enableColumnResize,
4231
- lockMax: enableColumnResize
4314
+ lockMax: enableColumnResize,
4315
+ minWidth: header.column.columnDef.meta?.minWidth,
4316
+ maxWidth: header.column.columnDef.meta?.maxWidth
4232
4317
  });
4233
4318
  const freezeOffset = enableColumnFreeze ? resolveHeaderFreezeOffset(header.column, freezeOffsets) : void 0;
4234
4319
  const freezeStyle = getColumnFreezeStyle(freezeOffset, {
@@ -4434,12 +4519,15 @@ function buildColumnDef(props, sort, onSort) {
4434
4519
  width,
4435
4520
  minWidth,
4436
4521
  maxWidth,
4522
+ minResizeWidth,
4523
+ maxResizeWidth,
4437
4524
  resizable,
4438
4525
  reorderable,
4439
4526
  frozen,
4440
4527
  align,
4441
4528
  rowSpan,
4442
4529
  rowSpanKey,
4530
+ rowSpanParent,
4443
4531
  editable,
4444
4532
  editType,
4445
4533
  editInputProps,
@@ -4453,8 +4541,8 @@ function buildColumnDef(props, sort, onSort) {
4453
4541
  id: field,
4454
4542
  ...!virtual ? { accessorKey: field } : {},
4455
4543
  size: width ?? DATA_TABLE_COLUMN_SIZE,
4456
- ...minWidth != null ? { minSize: minWidth } : {},
4457
- ...maxWidth != null ? { maxSize: maxWidth } : {},
4544
+ ...minResizeWidth != null ? { minSize: minResizeWidth } : {},
4545
+ ...maxResizeWidth != null ? { maxSize: maxResizeWidth } : {},
4458
4546
  ...resizable === false ? { enableResizing: false } : {},
4459
4547
  header: sortable ? () => /* @__PURE__ */ jsx8(
4460
4548
  SortableHeader,
@@ -4474,6 +4562,7 @@ function buildColumnDef(props, sort, onSort) {
4474
4562
  align,
4475
4563
  rowSpan,
4476
4564
  rowSpanKey,
4565
+ rowSpanParent,
4477
4566
  editable,
4478
4567
  editType,
4479
4568
  editInputProps,
@@ -4482,6 +4571,8 @@ function buildColumnDef(props, sort, onSort) {
4482
4571
  cellRender: render,
4483
4572
  frozen,
4484
4573
  reorderable,
4574
+ minWidth,
4575
+ maxWidth,
4485
4576
  className,
4486
4577
  headerClassName
4487
4578
  }
@@ -180,10 +180,17 @@ type DataTableEditInputProps = Omit<InputHTMLAttributes<HTMLInputElement>, "type
180
180
 
181
181
  declare module "@tanstack/react-table" {
182
182
  interface ColumnMeta<TData, TValue> {
183
- /** Whether this column participates in vertical row spanning */
183
+ /**
184
+ * Whether this column participates in vertical row spanning.
185
+ */
184
186
  rowSpan?: boolean;
185
187
  /** Merge key field. Falls back to the column id when omitted */
186
188
  rowSpanKey?: string;
189
+ /**
190
+ * Parent column id, rowSpanKey, or row field(s) that bound this merge.
191
+ * The column only groups consecutive rows that stay inside those parent groups.
192
+ */
193
+ rowSpanParent?: string | readonly string[];
187
194
  align?: "left" | "center" | "right";
188
195
  className?: string;
189
196
  headerClassName?: string;
@@ -213,6 +220,10 @@ declare module "@tanstack/react-table" {
213
220
  * Defaults to true.
214
221
  */
215
222
  reorderable?: boolean;
223
+ /** CSS min-width for header/body cells. Independent of column resize. */
224
+ minWidth?: number;
225
+ /** CSS max-width for header/body cells. Independent of column resize. */
226
+ maxWidth?: number;
216
227
  }
217
228
  interface CellContext<TData, TValue> {
218
229
  /**
@@ -538,10 +549,20 @@ type TableColumnProps<T extends Record<string, unknown>, K extends string = keyo
538
549
  sortable?: boolean;
539
550
  /** Initial / default column width in px (TanStack `size`) */
540
551
  width?: number;
541
- /** Minimum resize width in px. Defaults to the table min when omitted */
552
+ /**
553
+ * CSS min-width for this column's cells.
554
+ * Independent of `enableColumnResize` / `minResizeWidth`.
555
+ */
542
556
  minWidth?: number;
543
- /** Maximum resize width in px. Defaults to the table max when omitted */
557
+ /**
558
+ * CSS max-width for this column's cells.
559
+ * Independent of `enableColumnResize` / `maxResizeWidth`.
560
+ */
544
561
  maxWidth?: number;
562
+ /** Minimum drag-resize width in px. Defaults to the table min when omitted */
563
+ minResizeWidth?: number;
564
+ /** Maximum drag-resize width in px. Defaults to the table max when omitted */
565
+ maxResizeWidth?: number;
545
566
  /**
546
567
  * When false, this column cannot be resized even if `enableColumnResize` is on.
547
568
  * Defaults to true.
@@ -559,8 +580,14 @@ type TableColumnProps<T extends Record<string, unknown>, K extends string = keyo
559
580
  */
560
581
  frozen?: ColumnFreezeMeta;
561
582
  align?: "left" | "center" | "right";
583
+ /** Vertical cell merge for consecutive identical `rowSpanKey` values. */
562
584
  rowSpan?: boolean;
563
585
  rowSpanKey?: string;
586
+ /**
587
+ * Parent field(s) this merge is nested under — a column `field`, `rowSpanKey`,
588
+ * or row data key. Omit to merge independently of other rowSpan columns.
589
+ */
590
+ rowSpanParent?: string | readonly string[];
564
591
  editable?: boolean;
565
592
  editType?: CellEditType;
566
593
  /** Inline editor input attribute overrides */
@@ -180,10 +180,17 @@ type DataTableEditInputProps = Omit<InputHTMLAttributes<HTMLInputElement>, "type
180
180
 
181
181
  declare module "@tanstack/react-table" {
182
182
  interface ColumnMeta<TData, TValue> {
183
- /** Whether this column participates in vertical row spanning */
183
+ /**
184
+ * Whether this column participates in vertical row spanning.
185
+ */
184
186
  rowSpan?: boolean;
185
187
  /** Merge key field. Falls back to the column id when omitted */
186
188
  rowSpanKey?: string;
189
+ /**
190
+ * Parent column id, rowSpanKey, or row field(s) that bound this merge.
191
+ * The column only groups consecutive rows that stay inside those parent groups.
192
+ */
193
+ rowSpanParent?: string | readonly string[];
187
194
  align?: "left" | "center" | "right";
188
195
  className?: string;
189
196
  headerClassName?: string;
@@ -213,6 +220,10 @@ declare module "@tanstack/react-table" {
213
220
  * Defaults to true.
214
221
  */
215
222
  reorderable?: boolean;
223
+ /** CSS min-width for header/body cells. Independent of column resize. */
224
+ minWidth?: number;
225
+ /** CSS max-width for header/body cells. Independent of column resize. */
226
+ maxWidth?: number;
216
227
  }
217
228
  interface CellContext<TData, TValue> {
218
229
  /**
@@ -538,10 +549,20 @@ type TableColumnProps<T extends Record<string, unknown>, K extends string = keyo
538
549
  sortable?: boolean;
539
550
  /** Initial / default column width in px (TanStack `size`) */
540
551
  width?: number;
541
- /** Minimum resize width in px. Defaults to the table min when omitted */
552
+ /**
553
+ * CSS min-width for this column's cells.
554
+ * Independent of `enableColumnResize` / `minResizeWidth`.
555
+ */
542
556
  minWidth?: number;
543
- /** Maximum resize width in px. Defaults to the table max when omitted */
557
+ /**
558
+ * CSS max-width for this column's cells.
559
+ * Independent of `enableColumnResize` / `maxResizeWidth`.
560
+ */
544
561
  maxWidth?: number;
562
+ /** Minimum drag-resize width in px. Defaults to the table min when omitted */
563
+ minResizeWidth?: number;
564
+ /** Maximum drag-resize width in px. Defaults to the table max when omitted */
565
+ maxResizeWidth?: number;
545
566
  /**
546
567
  * When false, this column cannot be resized even if `enableColumnResize` is on.
547
568
  * Defaults to true.
@@ -559,8 +580,14 @@ type TableColumnProps<T extends Record<string, unknown>, K extends string = keyo
559
580
  */
560
581
  frozen?: ColumnFreezeMeta;
561
582
  align?: "left" | "center" | "right";
583
+ /** Vertical cell merge for consecutive identical `rowSpanKey` values. */
562
584
  rowSpan?: boolean;
563
585
  rowSpanKey?: string;
586
+ /**
587
+ * Parent field(s) this merge is nested under — a column `field`, `rowSpanKey`,
588
+ * or row data key. Omit to merge independently of other rowSpan columns.
589
+ */
590
+ rowSpanParent?: string | readonly string[];
564
591
  editable?: boolean;
565
592
  editType?: CellEditType;
566
593
  /** Inline editor input attribute overrides */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-glide-table",
3
- "version": "2.1.0",
3
+ "version": "2.2.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/zpxlffjrm/react-glide-table.git"