@lensui/lens-table 0.1.0 → 0.1.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/README.md CHANGED
@@ -89,6 +89,7 @@ export function Example() {
89
89
  | `rowKey` | `keyof Row \| ((row, index) => string \| number)` | `id` | 行唯一标识。默认读取每行的`id` 字段;如果数据不是 `id` 主键,请显式传入字段名或函数。 |
90
90
  | `width` | `number \| string` | `100%` | 表格宽度。数字按像素处理,字符串可传`100%`、`80vw` 等。 |
91
91
  | `height` | `number \| string` | `100%` | 表格高度。默认继承父容器高度;如果父容器没有可计算高度,则回落到`480px`。 |
92
+ | `autoHeight` | `boolean` | `false` | 是否按真实内容高度收缩表格。开启后数据较少时使用 `表头 + 行数 * 行高 + 合计行` 的高度,数据较多时不超过 `height`。 |
92
93
  | `rowHeight` | `number` | `40` | 每行高度,固定行高用于高性能虚拟滚动。 |
93
94
  | `headerHeight` | `number` | `44` | 表头高度,单位 px。 |
94
95
  | `fixedHeader` | `boolean` | `true` | 表头是否固定在顶部。 |
package/dist/index.cjs CHANGED
@@ -333,6 +333,7 @@ function LoadingSpinnerIcon() {
333
333
  // src/components/editors/ChoiceEditor.tsx
334
334
  var import_jsx_runtime3 = require("react/jsx-runtime");
335
335
  function ChoiceEditor({ value, options, style, labels, onChange, onCommit, onCancel }) {
336
+ const editorStyle = typeof style.width === "number" ? { ...style, width: Math.max(0, style.width - 1) } : style;
336
337
  const collection = (0, import_select.createListCollection)({
337
338
  items: options,
338
339
  itemToValue: (item) => item.value,
@@ -342,7 +343,7 @@ function ChoiceEditor({ value, options, style, labels, onChange, onCommit, onCan
342
343
  import_select.Select.Root,
343
344
  {
344
345
  className: "rvg-choice-editor",
345
- style,
346
+ style: editorStyle,
346
347
  collection,
347
348
  value: value ? [value] : [],
348
349
  defaultOpen: true,
@@ -801,14 +802,38 @@ function getMinimumColumnWidth(column, columnDraggable = false) {
801
802
  if (column.rowSelection || column.rowNumber) return 44;
802
803
  return 60;
803
804
  }
804
- function buildColumnMetrics(columns, columnDraggable = false) {
805
+ function buildColumnMetrics(columns, columnDraggableOrOptions = false) {
806
+ const options = typeof columnDraggableOrOptions === "boolean" ? { columnDraggable: columnDraggableOrOptions } : columnDraggableOrOptions;
807
+ const columnDraggable = options.columnDraggable ?? false;
805
808
  let left = 0;
806
- return columns.map((column) => {
809
+ const metrics = columns.map((column) => {
807
810
  const width = Math.max(getMinimumColumnWidth(column, columnDraggable), column.width ?? DEFAULT_COLUMN_WIDTH);
808
811
  const metric = { left, width, right: left + width };
809
812
  left += width;
810
813
  return metric;
811
814
  });
815
+ const viewportWidth = options.viewportWidth ?? 0;
816
+ if (viewportWidth <= left || columns.length === 0) return metrics;
817
+ const stretchable = columns.map((column, index) => ({ column, index })).filter(({ column }) => column.fixed !== "right" && !column.rowSelection && !column.rowDragHandle && !column.rowNumber);
818
+ const targets = stretchable.length > 0 ? stretchable : columns.map((column, index) => ({ column, index })).filter(({ column }) => column.fixed !== "right");
819
+ if (targets.length === 0) return metrics;
820
+ const extra = viewportWidth - left;
821
+ const baseTotal = targets.reduce((sum, { index }) => sum + metrics[index].width, 0);
822
+ let assigned = 0;
823
+ const targetIndexSet = new Set(targets.map(({ index }) => index));
824
+ const nextWidths = metrics.map((metric, index) => {
825
+ if (!targetIndexSet.has(index)) return metric.width;
826
+ const isLastTarget = index === targets[targets.length - 1].index;
827
+ const addition = isLastTarget ? extra - assigned : Math.floor(extra * (metric.width / baseTotal));
828
+ assigned += addition;
829
+ return metric.width + addition;
830
+ });
831
+ left = 0;
832
+ return nextWidths.map((width) => {
833
+ const metric = { left, width, right: left + width };
834
+ left += width;
835
+ return metric;
836
+ });
812
837
  }
813
838
  function lowerBound(metrics, value) {
814
839
  let low = 0;
@@ -1229,6 +1254,7 @@ function useControllableValue(value, defaultValue, onChange) {
1229
1254
  var import_jsx_runtime7 = require("react/jsx-runtime");
1230
1255
  var ANNOTATION_COLORS = ["#f44336", "#ff7a45", "#f5a623", "#f2c94c", "#52c41a", "#13a8a8", "#1677ff", "#597ef7", "#9254de", "#eb2f96"];
1231
1256
  var FALLBACK_HEIGHT = 480;
1257
+ var EMPTY_BODY_HEIGHT = 240;
1232
1258
  var VIRTUAL_OVERSCAN = 4;
1233
1259
  var TOOLTIP_DELAY = 1e3;
1234
1260
  var SUMMARY_CHUNK_SIZE = 5e3;
@@ -1302,6 +1328,7 @@ function Table({
1302
1328
  rowKey,
1303
1329
  width = "100%",
1304
1330
  height = "100%",
1331
+ autoHeight = false,
1305
1332
  rowHeight = 36,
1306
1333
  headerHeight = 40,
1307
1334
  fixedHeader = true,
@@ -1544,7 +1571,7 @@ function Table({
1544
1571
  if (selection?.rowKey === editing.rowKey && selection.columnKey === editing.columnKey) return;
1545
1572
  setEditing(null);
1546
1573
  }, [editing, selection]);
1547
- const metrics = (0, import_react5.useMemo)(() => buildColumnMetrics(columns, columnDraggable), [columnDraggable, columns]);
1574
+ const metrics = (0, import_react5.useMemo)(() => buildColumnMetrics(columns, { columnDraggable, viewportWidth: viewport.width }), [columnDraggable, columns, viewport.width]);
1548
1575
  const contentWidth = metrics.length > 0 ? metrics[metrics.length - 1].right : 0;
1549
1576
  const contentHeight = rows.length * rowHeight;
1550
1577
  const fixedWidth = (0, import_react5.useMemo)(() => columns.reduce((width2, column, index) => column.fixed === "left" ? Math.max(width2, metrics[index].right) : width2, 0), [columns, metrics]);
@@ -1565,7 +1592,10 @@ function Table({
1565
1592
  const topSummaryHeight = hasSummaryRow && summaryPosition === "top" ? rowHeight : 0;
1566
1593
  const bottomSummaryHeight = hasSummaryRow && summaryPosition === "bottom" ? rowHeight : 0;
1567
1594
  const bodyTop = headerHeight + topSummaryHeight;
1568
- const bodyViewportHeight = Math.max(0, resolvedHeight - (fixedHeader ? headerHeight : 0) - topSummaryHeight);
1595
+ const bodyContentHeight = rows.length === 0 ? EMPTY_BODY_HEIGHT : contentHeight;
1596
+ const realContentHeight = bodyTop + bodyContentHeight + bottomSummaryHeight + (contentWidth > viewport.width ? horizontalScrollbarHeight : 0);
1597
+ const effectiveHeight = autoHeight ? Math.max(bodyTop + bottomSummaryHeight, Math.min(resolvedHeight, realContentHeight)) : resolvedHeight;
1598
+ const bodyViewportHeight = Math.max(0, effectiveHeight - (fixedHeader ? headerHeight : 0) - topSummaryHeight);
1569
1599
  const [summaryState, setSummaryState] = (0, import_react5.useState)(() => ({ columns: null, rows: null, values: /* @__PURE__ */ new Map(), pending: false }));
1570
1600
  const summaryPending = hasSummaryRow && (loading || summaryState.pending || summaryState.columns !== columns || summaryState.rows !== rows);
1571
1601
  const summaryValues = hasSummaryRow && summaryState.columns === columns && summaryState.rows === rows ? summaryState.values : /* @__PURE__ */ new Map();
@@ -1701,9 +1731,8 @@ function Table({
1701
1731
  const canvas = canvasRef.current;
1702
1732
  if (!canvas || viewport.width <= 0) return;
1703
1733
  const ratio = Math.min(window.devicePixelRatio || 1, 2);
1704
- const displayHeight = resolvedHeight;
1705
1734
  const physicalWidth = Math.round(viewport.width * ratio);
1706
- const physicalHeight = Math.round(displayHeight * ratio);
1735
+ const physicalHeight = Math.round(effectiveHeight * ratio);
1707
1736
  if (canvas.width !== physicalWidth) canvas.width = physicalWidth;
1708
1737
  if (canvas.height !== physicalHeight) canvas.height = physicalHeight;
1709
1738
  const context = canvas.getContext("2d");
@@ -1726,8 +1755,8 @@ function Table({
1726
1755
  const scroll = scrollRef.current;
1727
1756
  const rowScrollTop = fixedHeader ? scroll.top : Math.max(0, scroll.top - bodyTop);
1728
1757
  const range = isVirtualized ? getViewportRange(scroll.left, rowScrollTop, viewport.width, bodyViewportHeight, rows.length, rowHeight, metrics, virtualOverscan) : { rowStart: 0, rowEnd: rows.length, columnStart: 0, columnEnd: columns.length };
1729
- paintGrid({ context, width: viewport.width, height: resolvedHeight, pixelRatio: ratio, scrollLeft: scroll.left, scrollTop: scroll.top, rowHeight, headerHeight, bodyTop, suppressLastRowBottomBorder: bottomSummaryHeight > 0, suppressFrameBottomBorder: bottomSummaryHeight > 0, fixedHeader, verticalBorderless: !hasVerticalBorders, striped: hasStripedRows, columnDraggable, sortState, filterValues, hoveredHeaderAction: hoveredHeaderActionRef.current, rows, columns, metrics, range, selection, editing, hoveredRowIndex: hoveredRowIndexRef.current, selectionRange, selectedRowKeys: selectedRowKeySet, selectedColumnKeys: selectedColumnKeySet, cellSpans: cellSpanLookup.covered, maxRowSpan: cellSpanLookup.maxRowSpan, highlightEditedCells: highlightsEditedCells, highlightInsertedRows: highlightsInsertedRows, insertedRowKeys: insertedRowKeySet, editedCellKeys, cellAnnotations, getRowKey, rowDragPreview, colors: themeColors });
1730
- }, [bodyTop, bodyViewportHeight, bottomSummaryHeight, cellAnnotations, cellSpanLookup, columnDraggable, columns, editedCellHighlightColor, editedCellKeys, editing, filterValues, fixedHeader, getRowKey, hasStripedRows, hasVerticalBorders, headerHeight, highlightsEditedCells, highlightsInsertedRows, insertedRowHighlightColor, insertedRowKeySet, isVirtualized, metrics, resolvedHeight, rowDragPreview, rowHeight, rows, selectedColumnKeySet, selectedRowKeySet, selection, selectionRange, sortState, stripedColor, viewport.width, virtualOverscan]);
1758
+ paintGrid({ context, width: viewport.width, height: effectiveHeight, pixelRatio: ratio, scrollLeft: scroll.left, scrollTop: scroll.top, rowHeight, headerHeight, bodyTop, suppressLastRowBottomBorder: bottomSummaryHeight > 0, suppressFrameBottomBorder: bottomSummaryHeight > 0, fixedHeader, verticalBorderless: !hasVerticalBorders, striped: hasStripedRows, columnDraggable, sortState, filterValues, hoveredHeaderAction: hoveredHeaderActionRef.current, rows, columns, metrics, range, selection, editing, hoveredRowIndex: hoveredRowIndexRef.current, selectionRange, selectedRowKeys: selectedRowKeySet, selectedColumnKeys: selectedColumnKeySet, cellSpans: cellSpanLookup.covered, maxRowSpan: cellSpanLookup.maxRowSpan, highlightEditedCells: highlightsEditedCells, highlightInsertedRows: highlightsInsertedRows, insertedRowKeys: insertedRowKeySet, editedCellKeys, cellAnnotations, getRowKey, rowDragPreview, colors: themeColors });
1759
+ }, [bodyTop, bodyViewportHeight, bottomSummaryHeight, cellAnnotations, cellSpanLookup, columnDraggable, columns, editedCellHighlightColor, editedCellKeys, editing, effectiveHeight, filterValues, fixedHeader, getRowKey, hasStripedRows, hasVerticalBorders, headerHeight, highlightsEditedCells, highlightsInsertedRows, insertedRowHighlightColor, insertedRowKeySet, isVirtualized, metrics, rowDragPreview, rowHeight, rows, selectedColumnKeySet, selectedRowKeySet, selection, selectionRange, sortState, stripedColor, viewport.width, virtualOverscan]);
1731
1760
  const scheduleDraw = (0, import_react5.useCallback)(() => {
1732
1761
  if (frameRef.current === null) frameRef.current = requestAnimationFrame(draw);
1733
1762
  }, [draw]);
@@ -1836,14 +1865,14 @@ function Table({
1836
1865
  const horizontalStart = column.fixed === void 0 ? fixedWidth : 0;
1837
1866
  const horizontalEnd = column.fixed === void 0 ? viewport.width - rightFixedWidth : viewport.width;
1838
1867
  const verticalStart = fixedHeader ? bodyTop : 0;
1839
- if (rawRight <= horizontalStart || rawLeft >= horizontalEnd || rawBottom <= verticalStart || rawTop >= resolvedHeight) {
1868
+ if (rawRight <= horizontalStart || rawLeft >= horizontalEnd || rawBottom <= verticalStart || rawTop >= effectiveHeight) {
1840
1869
  focus.style.display = "none";
1841
1870
  } else {
1842
1871
  const borderWidth = 2;
1843
1872
  const left = Math.round(Math.max(rawLeft - borderWidth, horizontalStart - 1));
1844
1873
  const right = Math.round(Math.min(rawRight + 1, horizontalEnd, viewport.width));
1845
1874
  const top = Math.round(Math.max(rawTop - borderWidth, verticalStart - 1));
1846
- const bottom = Math.round(Math.min(rawBottom + 1, resolvedHeight));
1875
+ const bottom = Math.round(Math.min(rawBottom + 1, effectiveHeight));
1847
1876
  focus.style.display = "block";
1848
1877
  focus.style.left = `${left}px`;
1849
1878
  focus.style.top = `${top}px`;
@@ -1868,7 +1897,7 @@ function Table({
1868
1897
  setHoveredCellTooltip(null);
1869
1898
  scheduleDraw();
1870
1899
  setEditing(null);
1871
- }, [bodyTop, columns, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, metrics, resolvedHeight, rightFixedOffsets, rightFixedWidth, rowHeight, rows, scheduleDraw, selection, selectionRange, viewport.width]);
1900
+ }, [bodyTop, columns, effectiveHeight, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, metrics, rightFixedOffsets, rightFixedWidth, rowHeight, rows, scheduleDraw, selection, selectionRange, viewport.width]);
1872
1901
  const locateColumn = (0, import_react5.useCallback)((clientX) => {
1873
1902
  const canvas = canvasRef.current;
1874
1903
  if (!canvas) return -1;
@@ -2456,13 +2485,13 @@ function Table({
2456
2485
  const rowTop = (fixedHeader ? 0 : bodyTop) + rowIndex * rowHeight;
2457
2486
  const rowBottom = rowTop + rowHeight * (span?.rowSpan ?? 1);
2458
2487
  const cellWidth = getCellDisplayWidth(columnIndex, span?.colSpan ?? 1);
2459
- const visibleRowHeight = fixedHeader ? resolvedHeight - bodyTop : resolvedHeight;
2488
+ const visibleRowHeight = fixedHeader ? effectiveHeight - bodyTop : effectiveHeight;
2460
2489
  if (rowTop < scroller.scrollTop) scroller.scrollTop = rowTop;
2461
2490
  if (rowBottom > scroller.scrollTop + visibleRowHeight) scroller.scrollTop = rowBottom - visibleRowHeight;
2462
2491
  if (columns[columnIndex].fixed === void 0 && metric.left < scroller.scrollLeft + fixedWidth) scroller.scrollLeft = Math.max(0, metric.left - fixedWidth);
2463
2492
  if (columns[columnIndex].fixed === void 0 && metric.left + cellWidth > scroller.scrollLeft + viewport.width - rightFixedWidth) scroller.scrollLeft = metric.left + cellWidth - viewport.width + rightFixedWidth;
2464
2493
  }
2465
- }, [beginEdit, bodyTop, columns, editing, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, metrics, resolvedHeight, rightFixedWidth, rowHeight, rows, selection, setSelection, viewport.width]);
2494
+ }, [beginEdit, bodyTop, columns, editing, effectiveHeight, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, metrics, rightFixedWidth, rowHeight, rows, selection, setSelection, viewport.width]);
2466
2495
  const editorStyle = (0, import_react5.useMemo)(() => {
2467
2496
  if (!editing) return void 0;
2468
2497
  const metric = metrics[editing.columnIndex];
@@ -2686,7 +2715,7 @@ function Table({
2686
2715
  type: "cell",
2687
2716
  ...cell,
2688
2717
  left: Math.min(Math.max(0, clientX - root.left), Math.max(0, viewport.width - 190)),
2689
- top: Math.min(Math.max(0, clientY - root.top), Math.max(0, resolvedHeight - 350))
2718
+ top: Math.min(Math.max(0, clientY - root.top), Math.max(0, effectiveHeight - 350))
2690
2719
  });
2691
2720
  onCellContextMenu?.({ ...cell, row: rows[cell.rowIndex], clientX, clientY });
2692
2721
  };
@@ -2697,7 +2726,7 @@ function Table({
2697
2726
  type: "header",
2698
2727
  columnIndex,
2699
2728
  left: Math.min(Math.max(0, clientX - root.left), Math.max(0, viewport.width - 180)),
2700
- top: Math.min(Math.max(0, clientY - root.top), Math.max(0, resolvedHeight - 68))
2729
+ top: Math.min(Math.max(0, clientY - root.top), Math.max(0, effectiveHeight - 68))
2701
2730
  });
2702
2731
  };
2703
2732
  const getRowDragOffset = (rowIndex) => {
@@ -2927,7 +2956,7 @@ function Table({
2927
2956
  const horizontalStart = column.fixed === void 0 ? fixedWidth : 0;
2928
2957
  const horizontalEnd = column.fixed === void 0 ? viewport.width - rightFixedWidth : viewport.width;
2929
2958
  const verticalStart = fixedHeader ? bodyTop : 0;
2930
- if (rawLeft <= horizontalStart || rawLeft > horizontalEnd || rawTop <= verticalStart || rawTop > resolvedHeight) return null;
2959
+ if (rawLeft <= horizontalStart || rawLeft > horizontalEnd || rawTop <= verticalStart || rawTop > effectiveHeight) return null;
2931
2960
  return { left: rawLeft, top: rawTop, bounds };
2932
2961
  })();
2933
2962
  const selectionOutlineStyle = (() => {
@@ -2944,12 +2973,12 @@ function Table({
2944
2973
  const horizontalStart = column.fixed === void 0 ? fixedWidth : 0;
2945
2974
  const horizontalEnd = column.fixed === void 0 ? viewport.width - rightFixedWidth : viewport.width;
2946
2975
  const verticalStart = fixedHeader ? bodyTop : 0;
2947
- if (rawRight <= horizontalStart || rawLeft >= horizontalEnd || rawBottom <= verticalStart || rawTop >= resolvedHeight) return null;
2976
+ if (rawRight <= horizontalStart || rawLeft >= horizontalEnd || rawBottom <= verticalStart || rawTop >= effectiveHeight) return null;
2948
2977
  const borderWidth = 2;
2949
2978
  const left = Math.round(Math.max(rawLeft - borderWidth, horizontalStart - 1));
2950
2979
  const right = Math.round(Math.min(rawRight + 1, horizontalEnd, viewport.width));
2951
2980
  const top = Math.round(Math.max(rawTop - borderWidth, verticalStart - 1));
2952
- const bottom = Math.round(Math.min(rawBottom + 1, resolvedHeight));
2981
+ const bottom = Math.round(Math.min(rawBottom + 1, effectiveHeight));
2953
2982
  return {
2954
2983
  left,
2955
2984
  top,
@@ -3082,8 +3111,8 @@ function Table({
3082
3111
  };
3083
3112
  const rootStyle = {
3084
3113
  width,
3085
- height,
3086
- minHeight: typeof height === "number" ? void 0 : FALLBACK_HEIGHT,
3114
+ height: autoHeight ? effectiveHeight : height,
3115
+ minHeight: autoHeight || typeof height === "number" ? void 0 : FALLBACK_HEIGHT,
3087
3116
  ...stripedColor ? { "--rvg-color-stripe": stripedColor } : null,
3088
3117
  ...style
3089
3118
  };
@@ -3096,7 +3125,7 @@ function Table({
3096
3125
  {
3097
3126
  ref: canvasRef,
3098
3127
  className: "rvg-canvas",
3099
- style: { width: viewport.width, height: resolvedHeight },
3128
+ style: { width: viewport.width, height: effectiveHeight },
3100
3129
  onMouseMove: (event) => {
3101
3130
  if (columnDragRef.current) return;
3102
3131
  const rect = event.currentTarget.getBoundingClientRect();
@@ -3274,7 +3303,7 @@ function Table({
3274
3303
  onContextMenu: handleContextMenu
3275
3304
  }
3276
3305
  ),
3277
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rvg-text-layer", style: { width: viewport.width, height: resolvedHeight, marginTop: -resolvedHeight }, onContextMenu: handleContextMenu, children: [
3306
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rvg-text-layer", style: { width: viewport.width, height: effectiveHeight, marginTop: -effectiveHeight }, onContextMenu: handleContextMenu, children: [
3278
3307
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rvg-text-scroll-clip", style: { left: fixedWidth, right: rightFixedWidth }, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
3279
3308
  "div",
3280
3309
  {
@@ -3298,7 +3327,7 @@ function Table({
3298
3327
  columns.map((column, columnIndex) => column.fixed === "left" ? renderHeaderTitle(columnIndex, metrics[columnIndex].left) : null),
3299
3328
  columns.map((column, columnIndex) => column.fixed === "right" ? renderHeaderTitle(columnIndex, viewport.width - (rightFixedOffsets.get(columnIndex) ?? 0) - metrics[columnIndex].width) : null)
3300
3329
  ] }),
3301
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rvg-spacer", style: { width: contentWidth, height: contentHeight + bodyTop, marginTop: -resolvedHeight } })
3330
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rvg-spacer", style: { width: contentWidth, height: contentHeight + bodyTop, marginTop: -effectiveHeight } })
3302
3331
  ] }),
3303
3332
  hasSummaryRow && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
3304
3333
  "div",
@@ -3659,10 +3688,10 @@ function Table({
3659
3688
  ] })
3660
3689
  ] }),
3661
3690
  renderEditor(),
3662
- rows.length === 0 && !loading && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rvg-empty", children: emptyContent ?? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(DefaultEmptyState, { label: labels.empty }) }),
3691
+ rows.length === 0 && !loading && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rvg-empty", style: { top: bodyTop }, children: emptyContent ?? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(DefaultEmptyState, { label: labels.empty }) }),
3663
3692
  loading && hasCustomLoading && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rvg-custom-loading", "aria-label": labels.refreshing, children: loadingContent }),
3664
3693
  loading && !hasCustomLoading && !hasCompletedLoadRef.current && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rvg-initial-loading", style: { top: headerHeight }, "aria-label": labels.initialLoading, children: [
3665
- Array.from({ length: Math.ceil((resolvedHeight - headerHeight) / rowHeight) }, (_, rowIndex) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rvg-loading-row", style: { height: rowHeight }, children: columns.map((column, columnIndex) => {
3694
+ Array.from({ length: Math.ceil((effectiveHeight - headerHeight) / rowHeight) }, (_, rowIndex) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rvg-loading-row", style: { height: rowHeight }, children: columns.map((column, columnIndex) => {
3666
3695
  const left = getDisplayedColumnLeft(columnIndex);
3667
3696
  const columnWidth = metrics[columnIndex].width;
3668
3697
  if (left + columnWidth <= 0 || left >= viewport.width) return null;
package/dist/index.d.cts CHANGED
@@ -282,6 +282,8 @@ interface TableProps<Row extends object> {
282
282
  width?: number | string;
283
283
  /** Grid height. Defaults to 100%; falls back to 480px when the parent has no height. */
284
284
  height?: number | string;
285
+ /** Shrink the grid to its real content height when there are fewer rows than the configured height can display. */
286
+ autoHeight?: boolean;
285
287
  rowHeight?: number;
286
288
  headerHeight?: number;
287
289
  fixedHeader?: boolean;
@@ -346,8 +348,13 @@ interface ViewportRange {
346
348
  columnEnd: number;
347
349
  }
348
350
 
349
- declare function Table<Row extends object>({ columns: sourceColumns, rows: sourceRows, rowKey, width, height, rowHeight, headerHeight, fixedHeader, locale, verticalBorderless, striped, highlight, cellSpans, loading, loadingContent, virtualized, tooltip, summary, rowNumber, selectedCell, defaultSelectedCell, onSelectedCellChange, rangeSelection, rowSelection, selectedRowKeys, defaultSelectedRowKeys, onSelectedRowKeysChange, columnSelection, selectedColumnKeys, defaultSelectedColumnKeys, onSelectedColumnKeysChange, columnDraggable, columnResizable, onColumnOrderChange, onColumnResize, rowDraggable, onRowOrderChange, onInsertRows, onDeleteRows, sortState, onSortStateChange, filterValues, onFilterValuesChange, onCellChange, onCellContextMenu, contextMenu: contextMenuConfig, emptyContent, className, style, ariaLabel, }: TableProps<Row>): react.JSX.Element;
351
+ declare function Table<Row extends object>({ columns: sourceColumns, rows: sourceRows, rowKey, width, height, autoHeight, rowHeight, headerHeight, fixedHeader, locale, verticalBorderless, striped, highlight, cellSpans, loading, loadingContent, virtualized, tooltip, summary, rowNumber, selectedCell, defaultSelectedCell, onSelectedCellChange, rangeSelection, rowSelection, selectedRowKeys, defaultSelectedRowKeys, onSelectedRowKeysChange, columnSelection, selectedColumnKeys, defaultSelectedColumnKeys, onSelectedColumnKeysChange, columnDraggable, columnResizable, onColumnOrderChange, onColumnResize, rowDraggable, onRowOrderChange, onInsertRows, onDeleteRows, sortState, onSortStateChange, filterValues, onFilterValuesChange, onCellChange, onCellContextMenu, contextMenu: contextMenuConfig, emptyContent, className, style, ariaLabel, }: TableProps<Row>): react.JSX.Element;
350
352
 
353
+ type LayoutColumn<Row> = GridColumn<Row> & {
354
+ rowSelection?: boolean;
355
+ rowDragHandle?: boolean;
356
+ rowNumber?: boolean;
357
+ };
351
358
  /**
352
359
  * Cached horizontal geometry for one column.
353
360
  *
@@ -360,11 +367,15 @@ interface ColumnMetric {
360
367
  width: number;
361
368
  right: number;
362
369
  }
370
+ interface BuildColumnMetricsOptions {
371
+ columnDraggable?: boolean;
372
+ viewportWidth?: number;
373
+ }
363
374
  /**
364
375
  * Build cumulative column positions used by both canvas painting and DOM
365
376
  * overlay placement.
366
377
  */
367
- declare function buildColumnMetrics<Row>(columns: GridColumn<Row>[], columnDraggable?: boolean): ColumnMetric[];
378
+ declare function buildColumnMetrics<Row>(columns: LayoutColumn<Row>[], columnDraggableOrOptions?: boolean | BuildColumnMetricsOptions): ColumnMetric[];
368
379
  /**
369
380
  * Compute the row and column slice that should be painted for the current
370
381
  * viewport.
package/dist/index.d.ts CHANGED
@@ -282,6 +282,8 @@ interface TableProps<Row extends object> {
282
282
  width?: number | string;
283
283
  /** Grid height. Defaults to 100%; falls back to 480px when the parent has no height. */
284
284
  height?: number | string;
285
+ /** Shrink the grid to its real content height when there are fewer rows than the configured height can display. */
286
+ autoHeight?: boolean;
285
287
  rowHeight?: number;
286
288
  headerHeight?: number;
287
289
  fixedHeader?: boolean;
@@ -346,8 +348,13 @@ interface ViewportRange {
346
348
  columnEnd: number;
347
349
  }
348
350
 
349
- declare function Table<Row extends object>({ columns: sourceColumns, rows: sourceRows, rowKey, width, height, rowHeight, headerHeight, fixedHeader, locale, verticalBorderless, striped, highlight, cellSpans, loading, loadingContent, virtualized, tooltip, summary, rowNumber, selectedCell, defaultSelectedCell, onSelectedCellChange, rangeSelection, rowSelection, selectedRowKeys, defaultSelectedRowKeys, onSelectedRowKeysChange, columnSelection, selectedColumnKeys, defaultSelectedColumnKeys, onSelectedColumnKeysChange, columnDraggable, columnResizable, onColumnOrderChange, onColumnResize, rowDraggable, onRowOrderChange, onInsertRows, onDeleteRows, sortState, onSortStateChange, filterValues, onFilterValuesChange, onCellChange, onCellContextMenu, contextMenu: contextMenuConfig, emptyContent, className, style, ariaLabel, }: TableProps<Row>): react.JSX.Element;
351
+ declare function Table<Row extends object>({ columns: sourceColumns, rows: sourceRows, rowKey, width, height, autoHeight, rowHeight, headerHeight, fixedHeader, locale, verticalBorderless, striped, highlight, cellSpans, loading, loadingContent, virtualized, tooltip, summary, rowNumber, selectedCell, defaultSelectedCell, onSelectedCellChange, rangeSelection, rowSelection, selectedRowKeys, defaultSelectedRowKeys, onSelectedRowKeysChange, columnSelection, selectedColumnKeys, defaultSelectedColumnKeys, onSelectedColumnKeysChange, columnDraggable, columnResizable, onColumnOrderChange, onColumnResize, rowDraggable, onRowOrderChange, onInsertRows, onDeleteRows, sortState, onSortStateChange, filterValues, onFilterValuesChange, onCellChange, onCellContextMenu, contextMenu: contextMenuConfig, emptyContent, className, style, ariaLabel, }: TableProps<Row>): react.JSX.Element;
350
352
 
353
+ type LayoutColumn<Row> = GridColumn<Row> & {
354
+ rowSelection?: boolean;
355
+ rowDragHandle?: boolean;
356
+ rowNumber?: boolean;
357
+ };
351
358
  /**
352
359
  * Cached horizontal geometry for one column.
353
360
  *
@@ -360,11 +367,15 @@ interface ColumnMetric {
360
367
  width: number;
361
368
  right: number;
362
369
  }
370
+ interface BuildColumnMetricsOptions {
371
+ columnDraggable?: boolean;
372
+ viewportWidth?: number;
373
+ }
363
374
  /**
364
375
  * Build cumulative column positions used by both canvas painting and DOM
365
376
  * overlay placement.
366
377
  */
367
- declare function buildColumnMetrics<Row>(columns: GridColumn<Row>[], columnDraggable?: boolean): ColumnMetric[];
378
+ declare function buildColumnMetrics<Row>(columns: LayoutColumn<Row>[], columnDraggableOrOptions?: boolean | BuildColumnMetricsOptions): ColumnMetric[];
368
379
  /**
369
380
  * Compute the row and column slice that should be painted for the current
370
381
  * viewport.
package/dist/index.js CHANGED
@@ -303,6 +303,7 @@ function LoadingSpinnerIcon() {
303
303
  // src/components/editors/ChoiceEditor.tsx
304
304
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
305
305
  function ChoiceEditor({ value, options, style, labels, onChange, onCommit, onCancel }) {
306
+ const editorStyle = typeof style.width === "number" ? { ...style, width: Math.max(0, style.width - 1) } : style;
306
307
  const collection = createListCollection({
307
308
  items: options,
308
309
  itemToValue: (item) => item.value,
@@ -312,7 +313,7 @@ function ChoiceEditor({ value, options, style, labels, onChange, onCommit, onCan
312
313
  Select.Root,
313
314
  {
314
315
  className: "rvg-choice-editor",
315
- style,
316
+ style: editorStyle,
316
317
  collection,
317
318
  value: value ? [value] : [],
318
319
  defaultOpen: true,
@@ -771,14 +772,38 @@ function getMinimumColumnWidth(column, columnDraggable = false) {
771
772
  if (column.rowSelection || column.rowNumber) return 44;
772
773
  return 60;
773
774
  }
774
- function buildColumnMetrics(columns, columnDraggable = false) {
775
+ function buildColumnMetrics(columns, columnDraggableOrOptions = false) {
776
+ const options = typeof columnDraggableOrOptions === "boolean" ? { columnDraggable: columnDraggableOrOptions } : columnDraggableOrOptions;
777
+ const columnDraggable = options.columnDraggable ?? false;
775
778
  let left = 0;
776
- return columns.map((column) => {
779
+ const metrics = columns.map((column) => {
777
780
  const width = Math.max(getMinimumColumnWidth(column, columnDraggable), column.width ?? DEFAULT_COLUMN_WIDTH);
778
781
  const metric = { left, width, right: left + width };
779
782
  left += width;
780
783
  return metric;
781
784
  });
785
+ const viewportWidth = options.viewportWidth ?? 0;
786
+ if (viewportWidth <= left || columns.length === 0) return metrics;
787
+ const stretchable = columns.map((column, index) => ({ column, index })).filter(({ column }) => column.fixed !== "right" && !column.rowSelection && !column.rowDragHandle && !column.rowNumber);
788
+ const targets = stretchable.length > 0 ? stretchable : columns.map((column, index) => ({ column, index })).filter(({ column }) => column.fixed !== "right");
789
+ if (targets.length === 0) return metrics;
790
+ const extra = viewportWidth - left;
791
+ const baseTotal = targets.reduce((sum, { index }) => sum + metrics[index].width, 0);
792
+ let assigned = 0;
793
+ const targetIndexSet = new Set(targets.map(({ index }) => index));
794
+ const nextWidths = metrics.map((metric, index) => {
795
+ if (!targetIndexSet.has(index)) return metric.width;
796
+ const isLastTarget = index === targets[targets.length - 1].index;
797
+ const addition = isLastTarget ? extra - assigned : Math.floor(extra * (metric.width / baseTotal));
798
+ assigned += addition;
799
+ return metric.width + addition;
800
+ });
801
+ left = 0;
802
+ return nextWidths.map((width) => {
803
+ const metric = { left, width, right: left + width };
804
+ left += width;
805
+ return metric;
806
+ });
782
807
  }
783
808
  function lowerBound(metrics, value) {
784
809
  let low = 0;
@@ -1199,6 +1224,7 @@ function useControllableValue(value, defaultValue, onChange) {
1199
1224
  import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1200
1225
  var ANNOTATION_COLORS = ["#f44336", "#ff7a45", "#f5a623", "#f2c94c", "#52c41a", "#13a8a8", "#1677ff", "#597ef7", "#9254de", "#eb2f96"];
1201
1226
  var FALLBACK_HEIGHT = 480;
1227
+ var EMPTY_BODY_HEIGHT = 240;
1202
1228
  var VIRTUAL_OVERSCAN = 4;
1203
1229
  var TOOLTIP_DELAY = 1e3;
1204
1230
  var SUMMARY_CHUNK_SIZE = 5e3;
@@ -1272,6 +1298,7 @@ function Table({
1272
1298
  rowKey,
1273
1299
  width = "100%",
1274
1300
  height = "100%",
1301
+ autoHeight = false,
1275
1302
  rowHeight = 36,
1276
1303
  headerHeight = 40,
1277
1304
  fixedHeader = true,
@@ -1514,7 +1541,7 @@ function Table({
1514
1541
  if (selection?.rowKey === editing.rowKey && selection.columnKey === editing.columnKey) return;
1515
1542
  setEditing(null);
1516
1543
  }, [editing, selection]);
1517
- const metrics = useMemo(() => buildColumnMetrics(columns, columnDraggable), [columnDraggable, columns]);
1544
+ const metrics = useMemo(() => buildColumnMetrics(columns, { columnDraggable, viewportWidth: viewport.width }), [columnDraggable, columns, viewport.width]);
1518
1545
  const contentWidth = metrics.length > 0 ? metrics[metrics.length - 1].right : 0;
1519
1546
  const contentHeight = rows.length * rowHeight;
1520
1547
  const fixedWidth = useMemo(() => columns.reduce((width2, column, index) => column.fixed === "left" ? Math.max(width2, metrics[index].right) : width2, 0), [columns, metrics]);
@@ -1535,7 +1562,10 @@ function Table({
1535
1562
  const topSummaryHeight = hasSummaryRow && summaryPosition === "top" ? rowHeight : 0;
1536
1563
  const bottomSummaryHeight = hasSummaryRow && summaryPosition === "bottom" ? rowHeight : 0;
1537
1564
  const bodyTop = headerHeight + topSummaryHeight;
1538
- const bodyViewportHeight = Math.max(0, resolvedHeight - (fixedHeader ? headerHeight : 0) - topSummaryHeight);
1565
+ const bodyContentHeight = rows.length === 0 ? EMPTY_BODY_HEIGHT : contentHeight;
1566
+ const realContentHeight = bodyTop + bodyContentHeight + bottomSummaryHeight + (contentWidth > viewport.width ? horizontalScrollbarHeight : 0);
1567
+ const effectiveHeight = autoHeight ? Math.max(bodyTop + bottomSummaryHeight, Math.min(resolvedHeight, realContentHeight)) : resolvedHeight;
1568
+ const bodyViewportHeight = Math.max(0, effectiveHeight - (fixedHeader ? headerHeight : 0) - topSummaryHeight);
1539
1569
  const [summaryState, setSummaryState] = useState4(() => ({ columns: null, rows: null, values: /* @__PURE__ */ new Map(), pending: false }));
1540
1570
  const summaryPending = hasSummaryRow && (loading || summaryState.pending || summaryState.columns !== columns || summaryState.rows !== rows);
1541
1571
  const summaryValues = hasSummaryRow && summaryState.columns === columns && summaryState.rows === rows ? summaryState.values : /* @__PURE__ */ new Map();
@@ -1671,9 +1701,8 @@ function Table({
1671
1701
  const canvas = canvasRef.current;
1672
1702
  if (!canvas || viewport.width <= 0) return;
1673
1703
  const ratio = Math.min(window.devicePixelRatio || 1, 2);
1674
- const displayHeight = resolvedHeight;
1675
1704
  const physicalWidth = Math.round(viewport.width * ratio);
1676
- const physicalHeight = Math.round(displayHeight * ratio);
1705
+ const physicalHeight = Math.round(effectiveHeight * ratio);
1677
1706
  if (canvas.width !== physicalWidth) canvas.width = physicalWidth;
1678
1707
  if (canvas.height !== physicalHeight) canvas.height = physicalHeight;
1679
1708
  const context = canvas.getContext("2d");
@@ -1696,8 +1725,8 @@ function Table({
1696
1725
  const scroll = scrollRef.current;
1697
1726
  const rowScrollTop = fixedHeader ? scroll.top : Math.max(0, scroll.top - bodyTop);
1698
1727
  const range = isVirtualized ? getViewportRange(scroll.left, rowScrollTop, viewport.width, bodyViewportHeight, rows.length, rowHeight, metrics, virtualOverscan) : { rowStart: 0, rowEnd: rows.length, columnStart: 0, columnEnd: columns.length };
1699
- paintGrid({ context, width: viewport.width, height: resolvedHeight, pixelRatio: ratio, scrollLeft: scroll.left, scrollTop: scroll.top, rowHeight, headerHeight, bodyTop, suppressLastRowBottomBorder: bottomSummaryHeight > 0, suppressFrameBottomBorder: bottomSummaryHeight > 0, fixedHeader, verticalBorderless: !hasVerticalBorders, striped: hasStripedRows, columnDraggable, sortState, filterValues, hoveredHeaderAction: hoveredHeaderActionRef.current, rows, columns, metrics, range, selection, editing, hoveredRowIndex: hoveredRowIndexRef.current, selectionRange, selectedRowKeys: selectedRowKeySet, selectedColumnKeys: selectedColumnKeySet, cellSpans: cellSpanLookup.covered, maxRowSpan: cellSpanLookup.maxRowSpan, highlightEditedCells: highlightsEditedCells, highlightInsertedRows: highlightsInsertedRows, insertedRowKeys: insertedRowKeySet, editedCellKeys, cellAnnotations, getRowKey, rowDragPreview, colors: themeColors });
1700
- }, [bodyTop, bodyViewportHeight, bottomSummaryHeight, cellAnnotations, cellSpanLookup, columnDraggable, columns, editedCellHighlightColor, editedCellKeys, editing, filterValues, fixedHeader, getRowKey, hasStripedRows, hasVerticalBorders, headerHeight, highlightsEditedCells, highlightsInsertedRows, insertedRowHighlightColor, insertedRowKeySet, isVirtualized, metrics, resolvedHeight, rowDragPreview, rowHeight, rows, selectedColumnKeySet, selectedRowKeySet, selection, selectionRange, sortState, stripedColor, viewport.width, virtualOverscan]);
1728
+ paintGrid({ context, width: viewport.width, height: effectiveHeight, pixelRatio: ratio, scrollLeft: scroll.left, scrollTop: scroll.top, rowHeight, headerHeight, bodyTop, suppressLastRowBottomBorder: bottomSummaryHeight > 0, suppressFrameBottomBorder: bottomSummaryHeight > 0, fixedHeader, verticalBorderless: !hasVerticalBorders, striped: hasStripedRows, columnDraggable, sortState, filterValues, hoveredHeaderAction: hoveredHeaderActionRef.current, rows, columns, metrics, range, selection, editing, hoveredRowIndex: hoveredRowIndexRef.current, selectionRange, selectedRowKeys: selectedRowKeySet, selectedColumnKeys: selectedColumnKeySet, cellSpans: cellSpanLookup.covered, maxRowSpan: cellSpanLookup.maxRowSpan, highlightEditedCells: highlightsEditedCells, highlightInsertedRows: highlightsInsertedRows, insertedRowKeys: insertedRowKeySet, editedCellKeys, cellAnnotations, getRowKey, rowDragPreview, colors: themeColors });
1729
+ }, [bodyTop, bodyViewportHeight, bottomSummaryHeight, cellAnnotations, cellSpanLookup, columnDraggable, columns, editedCellHighlightColor, editedCellKeys, editing, effectiveHeight, filterValues, fixedHeader, getRowKey, hasStripedRows, hasVerticalBorders, headerHeight, highlightsEditedCells, highlightsInsertedRows, insertedRowHighlightColor, insertedRowKeySet, isVirtualized, metrics, rowDragPreview, rowHeight, rows, selectedColumnKeySet, selectedRowKeySet, selection, selectionRange, sortState, stripedColor, viewport.width, virtualOverscan]);
1701
1730
  const scheduleDraw = useCallback2(() => {
1702
1731
  if (frameRef.current === null) frameRef.current = requestAnimationFrame(draw);
1703
1732
  }, [draw]);
@@ -1806,14 +1835,14 @@ function Table({
1806
1835
  const horizontalStart = column.fixed === void 0 ? fixedWidth : 0;
1807
1836
  const horizontalEnd = column.fixed === void 0 ? viewport.width - rightFixedWidth : viewport.width;
1808
1837
  const verticalStart = fixedHeader ? bodyTop : 0;
1809
- if (rawRight <= horizontalStart || rawLeft >= horizontalEnd || rawBottom <= verticalStart || rawTop >= resolvedHeight) {
1838
+ if (rawRight <= horizontalStart || rawLeft >= horizontalEnd || rawBottom <= verticalStart || rawTop >= effectiveHeight) {
1810
1839
  focus.style.display = "none";
1811
1840
  } else {
1812
1841
  const borderWidth = 2;
1813
1842
  const left = Math.round(Math.max(rawLeft - borderWidth, horizontalStart - 1));
1814
1843
  const right = Math.round(Math.min(rawRight + 1, horizontalEnd, viewport.width));
1815
1844
  const top = Math.round(Math.max(rawTop - borderWidth, verticalStart - 1));
1816
- const bottom = Math.round(Math.min(rawBottom + 1, resolvedHeight));
1845
+ const bottom = Math.round(Math.min(rawBottom + 1, effectiveHeight));
1817
1846
  focus.style.display = "block";
1818
1847
  focus.style.left = `${left}px`;
1819
1848
  focus.style.top = `${top}px`;
@@ -1838,7 +1867,7 @@ function Table({
1838
1867
  setHoveredCellTooltip(null);
1839
1868
  scheduleDraw();
1840
1869
  setEditing(null);
1841
- }, [bodyTop, columns, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, metrics, resolvedHeight, rightFixedOffsets, rightFixedWidth, rowHeight, rows, scheduleDraw, selection, selectionRange, viewport.width]);
1870
+ }, [bodyTop, columns, effectiveHeight, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, metrics, rightFixedOffsets, rightFixedWidth, rowHeight, rows, scheduleDraw, selection, selectionRange, viewport.width]);
1842
1871
  const locateColumn = useCallback2((clientX) => {
1843
1872
  const canvas = canvasRef.current;
1844
1873
  if (!canvas) return -1;
@@ -2426,13 +2455,13 @@ function Table({
2426
2455
  const rowTop = (fixedHeader ? 0 : bodyTop) + rowIndex * rowHeight;
2427
2456
  const rowBottom = rowTop + rowHeight * (span?.rowSpan ?? 1);
2428
2457
  const cellWidth = getCellDisplayWidth(columnIndex, span?.colSpan ?? 1);
2429
- const visibleRowHeight = fixedHeader ? resolvedHeight - bodyTop : resolvedHeight;
2458
+ const visibleRowHeight = fixedHeader ? effectiveHeight - bodyTop : effectiveHeight;
2430
2459
  if (rowTop < scroller.scrollTop) scroller.scrollTop = rowTop;
2431
2460
  if (rowBottom > scroller.scrollTop + visibleRowHeight) scroller.scrollTop = rowBottom - visibleRowHeight;
2432
2461
  if (columns[columnIndex].fixed === void 0 && metric.left < scroller.scrollLeft + fixedWidth) scroller.scrollLeft = Math.max(0, metric.left - fixedWidth);
2433
2462
  if (columns[columnIndex].fixed === void 0 && metric.left + cellWidth > scroller.scrollLeft + viewport.width - rightFixedWidth) scroller.scrollLeft = metric.left + cellWidth - viewport.width + rightFixedWidth;
2434
2463
  }
2435
- }, [beginEdit, bodyTop, columns, editing, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, metrics, resolvedHeight, rightFixedWidth, rowHeight, rows, selection, setSelection, viewport.width]);
2464
+ }, [beginEdit, bodyTop, columns, editing, effectiveHeight, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, metrics, rightFixedWidth, rowHeight, rows, selection, setSelection, viewport.width]);
2436
2465
  const editorStyle = useMemo(() => {
2437
2466
  if (!editing) return void 0;
2438
2467
  const metric = metrics[editing.columnIndex];
@@ -2656,7 +2685,7 @@ function Table({
2656
2685
  type: "cell",
2657
2686
  ...cell,
2658
2687
  left: Math.min(Math.max(0, clientX - root.left), Math.max(0, viewport.width - 190)),
2659
- top: Math.min(Math.max(0, clientY - root.top), Math.max(0, resolvedHeight - 350))
2688
+ top: Math.min(Math.max(0, clientY - root.top), Math.max(0, effectiveHeight - 350))
2660
2689
  });
2661
2690
  onCellContextMenu?.({ ...cell, row: rows[cell.rowIndex], clientX, clientY });
2662
2691
  };
@@ -2667,7 +2696,7 @@ function Table({
2667
2696
  type: "header",
2668
2697
  columnIndex,
2669
2698
  left: Math.min(Math.max(0, clientX - root.left), Math.max(0, viewport.width - 180)),
2670
- top: Math.min(Math.max(0, clientY - root.top), Math.max(0, resolvedHeight - 68))
2699
+ top: Math.min(Math.max(0, clientY - root.top), Math.max(0, effectiveHeight - 68))
2671
2700
  });
2672
2701
  };
2673
2702
  const getRowDragOffset = (rowIndex) => {
@@ -2897,7 +2926,7 @@ function Table({
2897
2926
  const horizontalStart = column.fixed === void 0 ? fixedWidth : 0;
2898
2927
  const horizontalEnd = column.fixed === void 0 ? viewport.width - rightFixedWidth : viewport.width;
2899
2928
  const verticalStart = fixedHeader ? bodyTop : 0;
2900
- if (rawLeft <= horizontalStart || rawLeft > horizontalEnd || rawTop <= verticalStart || rawTop > resolvedHeight) return null;
2929
+ if (rawLeft <= horizontalStart || rawLeft > horizontalEnd || rawTop <= verticalStart || rawTop > effectiveHeight) return null;
2901
2930
  return { left: rawLeft, top: rawTop, bounds };
2902
2931
  })();
2903
2932
  const selectionOutlineStyle = (() => {
@@ -2914,12 +2943,12 @@ function Table({
2914
2943
  const horizontalStart = column.fixed === void 0 ? fixedWidth : 0;
2915
2944
  const horizontalEnd = column.fixed === void 0 ? viewport.width - rightFixedWidth : viewport.width;
2916
2945
  const verticalStart = fixedHeader ? bodyTop : 0;
2917
- if (rawRight <= horizontalStart || rawLeft >= horizontalEnd || rawBottom <= verticalStart || rawTop >= resolvedHeight) return null;
2946
+ if (rawRight <= horizontalStart || rawLeft >= horizontalEnd || rawBottom <= verticalStart || rawTop >= effectiveHeight) return null;
2918
2947
  const borderWidth = 2;
2919
2948
  const left = Math.round(Math.max(rawLeft - borderWidth, horizontalStart - 1));
2920
2949
  const right = Math.round(Math.min(rawRight + 1, horizontalEnd, viewport.width));
2921
2950
  const top = Math.round(Math.max(rawTop - borderWidth, verticalStart - 1));
2922
- const bottom = Math.round(Math.min(rawBottom + 1, resolvedHeight));
2951
+ const bottom = Math.round(Math.min(rawBottom + 1, effectiveHeight));
2923
2952
  return {
2924
2953
  left,
2925
2954
  top,
@@ -3052,8 +3081,8 @@ function Table({
3052
3081
  };
3053
3082
  const rootStyle = {
3054
3083
  width,
3055
- height,
3056
- minHeight: typeof height === "number" ? void 0 : FALLBACK_HEIGHT,
3084
+ height: autoHeight ? effectiveHeight : height,
3085
+ minHeight: autoHeight || typeof height === "number" ? void 0 : FALLBACK_HEIGHT,
3057
3086
  ...stripedColor ? { "--rvg-color-stripe": stripedColor } : null,
3058
3087
  ...style
3059
3088
  };
@@ -3066,7 +3095,7 @@ function Table({
3066
3095
  {
3067
3096
  ref: canvasRef,
3068
3097
  className: "rvg-canvas",
3069
- style: { width: viewport.width, height: resolvedHeight },
3098
+ style: { width: viewport.width, height: effectiveHeight },
3070
3099
  onMouseMove: (event) => {
3071
3100
  if (columnDragRef.current) return;
3072
3101
  const rect = event.currentTarget.getBoundingClientRect();
@@ -3244,7 +3273,7 @@ function Table({
3244
3273
  onContextMenu: handleContextMenu
3245
3274
  }
3246
3275
  ),
3247
- /* @__PURE__ */ jsxs7("div", { className: "rvg-text-layer", style: { width: viewport.width, height: resolvedHeight, marginTop: -resolvedHeight }, onContextMenu: handleContextMenu, children: [
3276
+ /* @__PURE__ */ jsxs7("div", { className: "rvg-text-layer", style: { width: viewport.width, height: effectiveHeight, marginTop: -effectiveHeight }, onContextMenu: handleContextMenu, children: [
3248
3277
  /* @__PURE__ */ jsx7("div", { className: "rvg-text-scroll-clip", style: { left: fixedWidth, right: rightFixedWidth }, children: /* @__PURE__ */ jsxs7(
3249
3278
  "div",
3250
3279
  {
@@ -3268,7 +3297,7 @@ function Table({
3268
3297
  columns.map((column, columnIndex) => column.fixed === "left" ? renderHeaderTitle(columnIndex, metrics[columnIndex].left) : null),
3269
3298
  columns.map((column, columnIndex) => column.fixed === "right" ? renderHeaderTitle(columnIndex, viewport.width - (rightFixedOffsets.get(columnIndex) ?? 0) - metrics[columnIndex].width) : null)
3270
3299
  ] }),
3271
- /* @__PURE__ */ jsx7("div", { className: "rvg-spacer", style: { width: contentWidth, height: contentHeight + bodyTop, marginTop: -resolvedHeight } })
3300
+ /* @__PURE__ */ jsx7("div", { className: "rvg-spacer", style: { width: contentWidth, height: contentHeight + bodyTop, marginTop: -effectiveHeight } })
3272
3301
  ] }),
3273
3302
  hasSummaryRow && /* @__PURE__ */ jsxs7(
3274
3303
  "div",
@@ -3629,10 +3658,10 @@ function Table({
3629
3658
  ] })
3630
3659
  ] }),
3631
3660
  renderEditor(),
3632
- rows.length === 0 && !loading && /* @__PURE__ */ jsx7("div", { className: "rvg-empty", children: emptyContent ?? /* @__PURE__ */ jsx7(DefaultEmptyState, { label: labels.empty }) }),
3661
+ rows.length === 0 && !loading && /* @__PURE__ */ jsx7("div", { className: "rvg-empty", style: { top: bodyTop }, children: emptyContent ?? /* @__PURE__ */ jsx7(DefaultEmptyState, { label: labels.empty }) }),
3633
3662
  loading && hasCustomLoading && /* @__PURE__ */ jsx7("div", { className: "rvg-custom-loading", "aria-label": labels.refreshing, children: loadingContent }),
3634
3663
  loading && !hasCustomLoading && !hasCompletedLoadRef.current && /* @__PURE__ */ jsxs7("div", { className: "rvg-initial-loading", style: { top: headerHeight }, "aria-label": labels.initialLoading, children: [
3635
- Array.from({ length: Math.ceil((resolvedHeight - headerHeight) / rowHeight) }, (_, rowIndex) => /* @__PURE__ */ jsx7("div", { className: "rvg-loading-row", style: { height: rowHeight }, children: columns.map((column, columnIndex) => {
3664
+ Array.from({ length: Math.ceil((effectiveHeight - headerHeight) / rowHeight) }, (_, rowIndex) => /* @__PURE__ */ jsx7("div", { className: "rvg-loading-row", style: { height: rowHeight }, children: columns.map((column, columnIndex) => {
3636
3665
  const left = getDisplayedColumnLeft(columnIndex);
3637
3666
  const columnWidth = metrics[columnIndex].width;
3638
3667
  if (left + columnWidth <= 0 || left >= viewport.width) return null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lensui/lens-table",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "A lightweight, canvas-rendered React table component for LensUI.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",