@lensui/lens-table 0.1.0 → 0.1.2
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 +1 -0
- package/dist/index.cjs +62 -30
- package/dist/index.d.cts +13 -2
- package/dist/index.d.ts +13 -2
- package/dist/index.js +62 -30
- package/package.json +1 -1
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,
|
|
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
|
-
|
|
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,
|
|
@@ -1413,7 +1440,7 @@ function Table({
|
|
|
1413
1440
|
const hoveredRowIndexRef = (0, import_react5.useRef)(null);
|
|
1414
1441
|
const [viewport, setViewport] = (0, import_react5.useState)({ width: 0, height: typeof height === "number" ? height : FALLBACK_HEIGHT });
|
|
1415
1442
|
const [horizontalScrollbarHeight, setHorizontalScrollbarHeight] = (0, import_react5.useState)(0);
|
|
1416
|
-
const resolvedHeight = viewport.height || FALLBACK_HEIGHT;
|
|
1443
|
+
const resolvedHeight = typeof height === "number" ? height : viewport.height || FALLBACK_HEIGHT;
|
|
1417
1444
|
const [scrollPosition, setScrollPosition] = (0, import_react5.useState)({ left: 0, top: 0 });
|
|
1418
1445
|
const scrollRef = (0, import_react5.useRef)({ left: 0, top: 0 });
|
|
1419
1446
|
const [selection, setSelection] = useControllableValue(selectedCell, defaultSelectedCell, onSelectedCellChange);
|
|
@@ -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]);
|
|
@@ -1561,11 +1588,15 @@ function Table({
|
|
|
1561
1588
|
}, [columns, metrics]);
|
|
1562
1589
|
const hasRowSelectionColumn = (0, import_react5.useMemo)(() => columns.some((column) => column.rowSelection), [columns]);
|
|
1563
1590
|
const rowSelectionMode = typeof rowSelection === "object" ? rowSelection.mode ?? "multiple" : "multiple";
|
|
1564
|
-
const hasSummaryRow = (0, import_react5.useMemo)(() => summaryEnabled && columns.some((column) => Boolean(column.summary)), [columns, summaryEnabled]);
|
|
1591
|
+
const hasSummaryRow = (0, import_react5.useMemo)(() => rows.length > 0 && summaryEnabled && columns.some((column) => Boolean(column.summary)), [columns, rows.length, summaryEnabled]);
|
|
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
|
|
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 renderHeight = Math.max(0, effectiveHeight - bottomSummaryHeight);
|
|
1599
|
+
const bodyViewportHeight = Math.max(0, renderHeight - (fixedHeader ? headerHeight : 0) - topSummaryHeight);
|
|
1569
1600
|
const [summaryState, setSummaryState] = (0, import_react5.useState)(() => ({ columns: null, rows: null, values: /* @__PURE__ */ new Map(), pending: false }));
|
|
1570
1601
|
const summaryPending = hasSummaryRow && (loading || summaryState.pending || summaryState.columns !== columns || summaryState.rows !== rows);
|
|
1571
1602
|
const summaryValues = hasSummaryRow && summaryState.columns === columns && summaryState.rows === rows ? summaryState.values : /* @__PURE__ */ new Map();
|
|
@@ -1701,9 +1732,8 @@ function Table({
|
|
|
1701
1732
|
const canvas = canvasRef.current;
|
|
1702
1733
|
if (!canvas || viewport.width <= 0) return;
|
|
1703
1734
|
const ratio = Math.min(window.devicePixelRatio || 1, 2);
|
|
1704
|
-
const displayHeight = resolvedHeight;
|
|
1705
1735
|
const physicalWidth = Math.round(viewport.width * ratio);
|
|
1706
|
-
const physicalHeight = Math.round(
|
|
1736
|
+
const physicalHeight = Math.round(renderHeight * ratio);
|
|
1707
1737
|
if (canvas.width !== physicalWidth) canvas.width = physicalWidth;
|
|
1708
1738
|
if (canvas.height !== physicalHeight) canvas.height = physicalHeight;
|
|
1709
1739
|
const context = canvas.getContext("2d");
|
|
@@ -1726,8 +1756,8 @@ function Table({
|
|
|
1726
1756
|
const scroll = scrollRef.current;
|
|
1727
1757
|
const rowScrollTop = fixedHeader ? scroll.top : Math.max(0, scroll.top - bodyTop);
|
|
1728
1758
|
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:
|
|
1730
|
-
}, [bodyTop, bodyViewportHeight, bottomSummaryHeight, cellAnnotations, cellSpanLookup, columnDraggable, columns, editedCellHighlightColor, editedCellKeys, editing, filterValues, fixedHeader, getRowKey, hasStripedRows, hasVerticalBorders, headerHeight, highlightsEditedCells, highlightsInsertedRows, insertedRowHighlightColor, insertedRowKeySet, isVirtualized, metrics,
|
|
1759
|
+
paintGrid({ context, width: viewport.width, height: renderHeight, 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 });
|
|
1760
|
+
}, [bodyTop, bodyViewportHeight, bottomSummaryHeight, cellAnnotations, cellSpanLookup, columnDraggable, columns, editedCellHighlightColor, editedCellKeys, editing, renderHeight, 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
1761
|
const scheduleDraw = (0, import_react5.useCallback)(() => {
|
|
1732
1762
|
if (frameRef.current === null) frameRef.current = requestAnimationFrame(draw);
|
|
1733
1763
|
}, [draw]);
|
|
@@ -1836,14 +1866,14 @@ function Table({
|
|
|
1836
1866
|
const horizontalStart = column.fixed === void 0 ? fixedWidth : 0;
|
|
1837
1867
|
const horizontalEnd = column.fixed === void 0 ? viewport.width - rightFixedWidth : viewport.width;
|
|
1838
1868
|
const verticalStart = fixedHeader ? bodyTop : 0;
|
|
1839
|
-
if (rawRight <= horizontalStart || rawLeft >= horizontalEnd || rawBottom <= verticalStart || rawTop >=
|
|
1869
|
+
if (rawRight <= horizontalStart || rawLeft >= horizontalEnd || rawBottom <= verticalStart || rawTop >= renderHeight) {
|
|
1840
1870
|
focus.style.display = "none";
|
|
1841
1871
|
} else {
|
|
1842
1872
|
const borderWidth = 2;
|
|
1843
1873
|
const left = Math.round(Math.max(rawLeft - borderWidth, horizontalStart - 1));
|
|
1844
1874
|
const right = Math.round(Math.min(rawRight + 1, horizontalEnd, viewport.width));
|
|
1845
1875
|
const top = Math.round(Math.max(rawTop - borderWidth, verticalStart - 1));
|
|
1846
|
-
const bottom = Math.round(Math.min(rawBottom + 1,
|
|
1876
|
+
const bottom = Math.round(Math.min(rawBottom + 1, renderHeight));
|
|
1847
1877
|
focus.style.display = "block";
|
|
1848
1878
|
focus.style.left = `${left}px`;
|
|
1849
1879
|
focus.style.top = `${top}px`;
|
|
@@ -1868,7 +1898,7 @@ function Table({
|
|
|
1868
1898
|
setHoveredCellTooltip(null);
|
|
1869
1899
|
scheduleDraw();
|
|
1870
1900
|
setEditing(null);
|
|
1871
|
-
}, [bodyTop, columns, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, metrics,
|
|
1901
|
+
}, [bodyTop, columns, renderHeight, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, metrics, rightFixedOffsets, rightFixedWidth, rowHeight, rows, scheduleDraw, selection, selectionRange, viewport.width]);
|
|
1872
1902
|
const locateColumn = (0, import_react5.useCallback)((clientX) => {
|
|
1873
1903
|
const canvas = canvasRef.current;
|
|
1874
1904
|
if (!canvas) return -1;
|
|
@@ -2456,13 +2486,13 @@ function Table({
|
|
|
2456
2486
|
const rowTop = (fixedHeader ? 0 : bodyTop) + rowIndex * rowHeight;
|
|
2457
2487
|
const rowBottom = rowTop + rowHeight * (span?.rowSpan ?? 1);
|
|
2458
2488
|
const cellWidth = getCellDisplayWidth(columnIndex, span?.colSpan ?? 1);
|
|
2459
|
-
const visibleRowHeight = fixedHeader ?
|
|
2489
|
+
const visibleRowHeight = fixedHeader ? renderHeight - bodyTop : renderHeight;
|
|
2460
2490
|
if (rowTop < scroller.scrollTop) scroller.scrollTop = rowTop;
|
|
2461
2491
|
if (rowBottom > scroller.scrollTop + visibleRowHeight) scroller.scrollTop = rowBottom - visibleRowHeight;
|
|
2462
2492
|
if (columns[columnIndex].fixed === void 0 && metric.left < scroller.scrollLeft + fixedWidth) scroller.scrollLeft = Math.max(0, metric.left - fixedWidth);
|
|
2463
2493
|
if (columns[columnIndex].fixed === void 0 && metric.left + cellWidth > scroller.scrollLeft + viewport.width - rightFixedWidth) scroller.scrollLeft = metric.left + cellWidth - viewport.width + rightFixedWidth;
|
|
2464
2494
|
}
|
|
2465
|
-
}, [beginEdit, bodyTop, columns, editing, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, metrics,
|
|
2495
|
+
}, [beginEdit, bodyTop, columns, editing, renderHeight, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, metrics, rightFixedWidth, rowHeight, rows, selection, setSelection, viewport.width]);
|
|
2466
2496
|
const editorStyle = (0, import_react5.useMemo)(() => {
|
|
2467
2497
|
if (!editing) return void 0;
|
|
2468
2498
|
const metric = metrics[editing.columnIndex];
|
|
@@ -2686,7 +2716,7 @@ function Table({
|
|
|
2686
2716
|
type: "cell",
|
|
2687
2717
|
...cell,
|
|
2688
2718
|
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,
|
|
2719
|
+
top: Math.min(Math.max(0, clientY - root.top), Math.max(0, renderHeight - 350))
|
|
2690
2720
|
});
|
|
2691
2721
|
onCellContextMenu?.({ ...cell, row: rows[cell.rowIndex], clientX, clientY });
|
|
2692
2722
|
};
|
|
@@ -2697,7 +2727,7 @@ function Table({
|
|
|
2697
2727
|
type: "header",
|
|
2698
2728
|
columnIndex,
|
|
2699
2729
|
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,
|
|
2730
|
+
top: Math.min(Math.max(0, clientY - root.top), Math.max(0, renderHeight - 68))
|
|
2701
2731
|
});
|
|
2702
2732
|
};
|
|
2703
2733
|
const getRowDragOffset = (rowIndex) => {
|
|
@@ -2927,7 +2957,7 @@ function Table({
|
|
|
2927
2957
|
const horizontalStart = column.fixed === void 0 ? fixedWidth : 0;
|
|
2928
2958
|
const horizontalEnd = column.fixed === void 0 ? viewport.width - rightFixedWidth : viewport.width;
|
|
2929
2959
|
const verticalStart = fixedHeader ? bodyTop : 0;
|
|
2930
|
-
if (rawLeft <= horizontalStart || rawLeft > horizontalEnd || rawTop <= verticalStart || rawTop >
|
|
2960
|
+
if (rawLeft <= horizontalStart || rawLeft > horizontalEnd || rawTop <= verticalStart || rawTop > renderHeight) return null;
|
|
2931
2961
|
return { left: rawLeft, top: rawTop, bounds };
|
|
2932
2962
|
})();
|
|
2933
2963
|
const selectionOutlineStyle = (() => {
|
|
@@ -2944,12 +2974,12 @@ function Table({
|
|
|
2944
2974
|
const horizontalStart = column.fixed === void 0 ? fixedWidth : 0;
|
|
2945
2975
|
const horizontalEnd = column.fixed === void 0 ? viewport.width - rightFixedWidth : viewport.width;
|
|
2946
2976
|
const verticalStart = fixedHeader ? bodyTop : 0;
|
|
2947
|
-
if (rawRight <= horizontalStart || rawLeft >= horizontalEnd || rawBottom <= verticalStart || rawTop >=
|
|
2977
|
+
if (rawRight <= horizontalStart || rawLeft >= horizontalEnd || rawBottom <= verticalStart || rawTop >= renderHeight) return null;
|
|
2948
2978
|
const borderWidth = 2;
|
|
2949
2979
|
const left = Math.round(Math.max(rawLeft - borderWidth, horizontalStart - 1));
|
|
2950
2980
|
const right = Math.round(Math.min(rawRight + 1, horizontalEnd, viewport.width));
|
|
2951
2981
|
const top = Math.round(Math.max(rawTop - borderWidth, verticalStart - 1));
|
|
2952
|
-
const bottom = Math.round(Math.min(rawBottom + 1,
|
|
2982
|
+
const bottom = Math.round(Math.min(rawBottom + 1, renderHeight));
|
|
2953
2983
|
return {
|
|
2954
2984
|
left,
|
|
2955
2985
|
top,
|
|
@@ -3064,8 +3094,10 @@ function Table({
|
|
|
3064
3094
|
const renderSummaryCell = (columnIndex, left) => {
|
|
3065
3095
|
const column = columns[columnIndex];
|
|
3066
3096
|
const isUtility = column.rowSelection || column.rowDragHandle || column.rowNumber;
|
|
3067
|
-
const
|
|
3068
|
-
const
|
|
3097
|
+
const hasSummaryValue = summaryValues.has(column.key);
|
|
3098
|
+
const summaryContent = hasSummaryValue ? summaryValues.get(column.key) : summaryEmptyValue;
|
|
3099
|
+
const showSummarySkeleton = Boolean(column.summary) && summaryPending;
|
|
3100
|
+
const content = columnIndex === 0 ? language === "zh-CN" ? "\u5408\u8BA1" : "Total" : showSummarySkeleton ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "rvg-summary-skeleton" }) : summaryContent;
|
|
3069
3101
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3070
3102
|
"div",
|
|
3071
3103
|
{
|
|
@@ -3082,8 +3114,8 @@ function Table({
|
|
|
3082
3114
|
};
|
|
3083
3115
|
const rootStyle = {
|
|
3084
3116
|
width,
|
|
3085
|
-
height,
|
|
3086
|
-
minHeight: typeof height === "number" ? void 0 : FALLBACK_HEIGHT,
|
|
3117
|
+
height: autoHeight ? effectiveHeight : height,
|
|
3118
|
+
minHeight: autoHeight || typeof height === "number" ? void 0 : FALLBACK_HEIGHT,
|
|
3087
3119
|
...stripedColor ? { "--rvg-color-stripe": stripedColor } : null,
|
|
3088
3120
|
...style
|
|
3089
3121
|
};
|
|
@@ -3096,7 +3128,7 @@ function Table({
|
|
|
3096
3128
|
{
|
|
3097
3129
|
ref: canvasRef,
|
|
3098
3130
|
className: "rvg-canvas",
|
|
3099
|
-
style: { width: viewport.width, height:
|
|
3131
|
+
style: { width: viewport.width, height: renderHeight },
|
|
3100
3132
|
onMouseMove: (event) => {
|
|
3101
3133
|
if (columnDragRef.current) return;
|
|
3102
3134
|
const rect = event.currentTarget.getBoundingClientRect();
|
|
@@ -3274,7 +3306,7 @@ function Table({
|
|
|
3274
3306
|
onContextMenu: handleContextMenu
|
|
3275
3307
|
}
|
|
3276
3308
|
),
|
|
3277
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rvg-text-layer", style: { width: viewport.width, height:
|
|
3309
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rvg-text-layer", style: { width: viewport.width, height: renderHeight, marginTop: -renderHeight }, onContextMenu: handleContextMenu, children: [
|
|
3278
3310
|
/* @__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
3311
|
"div",
|
|
3280
3312
|
{
|
|
@@ -3298,7 +3330,7 @@ function Table({
|
|
|
3298
3330
|
columns.map((column, columnIndex) => column.fixed === "left" ? renderHeaderTitle(columnIndex, metrics[columnIndex].left) : null),
|
|
3299
3331
|
columns.map((column, columnIndex) => column.fixed === "right" ? renderHeaderTitle(columnIndex, viewport.width - (rightFixedOffsets.get(columnIndex) ?? 0) - metrics[columnIndex].width) : null)
|
|
3300
3332
|
] }),
|
|
3301
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rvg-spacer", style: { width: contentWidth, height: contentHeight + bodyTop, marginTop: -
|
|
3333
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rvg-spacer", style: { width: contentWidth, height: contentHeight + bodyTop, marginTop: -renderHeight } })
|
|
3302
3334
|
] }),
|
|
3303
3335
|
hasSummaryRow && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
3304
3336
|
"div",
|
|
@@ -3659,10 +3691,10 @@ function Table({
|
|
|
3659
3691
|
] })
|
|
3660
3692
|
] }),
|
|
3661
3693
|
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 }) }),
|
|
3694
|
+
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
3695
|
loading && hasCustomLoading && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rvg-custom-loading", "aria-label": labels.refreshing, children: loadingContent }),
|
|
3664
3696
|
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((
|
|
3697
|
+
Array.from({ length: Math.ceil((renderHeight - headerHeight) / rowHeight) }, (_, rowIndex) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rvg-loading-row", style: { height: rowHeight }, children: columns.map((column, columnIndex) => {
|
|
3666
3698
|
const left = getDisplayedColumnLeft(columnIndex);
|
|
3667
3699
|
const columnWidth = metrics[columnIndex].width;
|
|
3668
3700
|
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:
|
|
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:
|
|
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,
|
|
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
|
-
|
|
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,
|
|
@@ -1383,7 +1410,7 @@ function Table({
|
|
|
1383
1410
|
const hoveredRowIndexRef = useRef2(null);
|
|
1384
1411
|
const [viewport, setViewport] = useState4({ width: 0, height: typeof height === "number" ? height : FALLBACK_HEIGHT });
|
|
1385
1412
|
const [horizontalScrollbarHeight, setHorizontalScrollbarHeight] = useState4(0);
|
|
1386
|
-
const resolvedHeight = viewport.height || FALLBACK_HEIGHT;
|
|
1413
|
+
const resolvedHeight = typeof height === "number" ? height : viewport.height || FALLBACK_HEIGHT;
|
|
1387
1414
|
const [scrollPosition, setScrollPosition] = useState4({ left: 0, top: 0 });
|
|
1388
1415
|
const scrollRef = useRef2({ left: 0, top: 0 });
|
|
1389
1416
|
const [selection, setSelection] = useControllableValue(selectedCell, defaultSelectedCell, onSelectedCellChange);
|
|
@@ -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]);
|
|
@@ -1531,11 +1558,15 @@ function Table({
|
|
|
1531
1558
|
}, [columns, metrics]);
|
|
1532
1559
|
const hasRowSelectionColumn = useMemo(() => columns.some((column) => column.rowSelection), [columns]);
|
|
1533
1560
|
const rowSelectionMode = typeof rowSelection === "object" ? rowSelection.mode ?? "multiple" : "multiple";
|
|
1534
|
-
const hasSummaryRow = useMemo(() => summaryEnabled && columns.some((column) => Boolean(column.summary)), [columns, summaryEnabled]);
|
|
1561
|
+
const hasSummaryRow = useMemo(() => rows.length > 0 && summaryEnabled && columns.some((column) => Boolean(column.summary)), [columns, rows.length, summaryEnabled]);
|
|
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
|
|
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 renderHeight = Math.max(0, effectiveHeight - bottomSummaryHeight);
|
|
1569
|
+
const bodyViewportHeight = Math.max(0, renderHeight - (fixedHeader ? headerHeight : 0) - topSummaryHeight);
|
|
1539
1570
|
const [summaryState, setSummaryState] = useState4(() => ({ columns: null, rows: null, values: /* @__PURE__ */ new Map(), pending: false }));
|
|
1540
1571
|
const summaryPending = hasSummaryRow && (loading || summaryState.pending || summaryState.columns !== columns || summaryState.rows !== rows);
|
|
1541
1572
|
const summaryValues = hasSummaryRow && summaryState.columns === columns && summaryState.rows === rows ? summaryState.values : /* @__PURE__ */ new Map();
|
|
@@ -1671,9 +1702,8 @@ function Table({
|
|
|
1671
1702
|
const canvas = canvasRef.current;
|
|
1672
1703
|
if (!canvas || viewport.width <= 0) return;
|
|
1673
1704
|
const ratio = Math.min(window.devicePixelRatio || 1, 2);
|
|
1674
|
-
const displayHeight = resolvedHeight;
|
|
1675
1705
|
const physicalWidth = Math.round(viewport.width * ratio);
|
|
1676
|
-
const physicalHeight = Math.round(
|
|
1706
|
+
const physicalHeight = Math.round(renderHeight * ratio);
|
|
1677
1707
|
if (canvas.width !== physicalWidth) canvas.width = physicalWidth;
|
|
1678
1708
|
if (canvas.height !== physicalHeight) canvas.height = physicalHeight;
|
|
1679
1709
|
const context = canvas.getContext("2d");
|
|
@@ -1696,8 +1726,8 @@ function Table({
|
|
|
1696
1726
|
const scroll = scrollRef.current;
|
|
1697
1727
|
const rowScrollTop = fixedHeader ? scroll.top : Math.max(0, scroll.top - bodyTop);
|
|
1698
1728
|
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:
|
|
1700
|
-
}, [bodyTop, bodyViewportHeight, bottomSummaryHeight, cellAnnotations, cellSpanLookup, columnDraggable, columns, editedCellHighlightColor, editedCellKeys, editing, filterValues, fixedHeader, getRowKey, hasStripedRows, hasVerticalBorders, headerHeight, highlightsEditedCells, highlightsInsertedRows, insertedRowHighlightColor, insertedRowKeySet, isVirtualized, metrics,
|
|
1729
|
+
paintGrid({ context, width: viewport.width, height: renderHeight, 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, renderHeight, 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
1731
|
const scheduleDraw = useCallback2(() => {
|
|
1702
1732
|
if (frameRef.current === null) frameRef.current = requestAnimationFrame(draw);
|
|
1703
1733
|
}, [draw]);
|
|
@@ -1806,14 +1836,14 @@ function Table({
|
|
|
1806
1836
|
const horizontalStart = column.fixed === void 0 ? fixedWidth : 0;
|
|
1807
1837
|
const horizontalEnd = column.fixed === void 0 ? viewport.width - rightFixedWidth : viewport.width;
|
|
1808
1838
|
const verticalStart = fixedHeader ? bodyTop : 0;
|
|
1809
|
-
if (rawRight <= horizontalStart || rawLeft >= horizontalEnd || rawBottom <= verticalStart || rawTop >=
|
|
1839
|
+
if (rawRight <= horizontalStart || rawLeft >= horizontalEnd || rawBottom <= verticalStart || rawTop >= renderHeight) {
|
|
1810
1840
|
focus.style.display = "none";
|
|
1811
1841
|
} else {
|
|
1812
1842
|
const borderWidth = 2;
|
|
1813
1843
|
const left = Math.round(Math.max(rawLeft - borderWidth, horizontalStart - 1));
|
|
1814
1844
|
const right = Math.round(Math.min(rawRight + 1, horizontalEnd, viewport.width));
|
|
1815
1845
|
const top = Math.round(Math.max(rawTop - borderWidth, verticalStart - 1));
|
|
1816
|
-
const bottom = Math.round(Math.min(rawBottom + 1,
|
|
1846
|
+
const bottom = Math.round(Math.min(rawBottom + 1, renderHeight));
|
|
1817
1847
|
focus.style.display = "block";
|
|
1818
1848
|
focus.style.left = `${left}px`;
|
|
1819
1849
|
focus.style.top = `${top}px`;
|
|
@@ -1838,7 +1868,7 @@ function Table({
|
|
|
1838
1868
|
setHoveredCellTooltip(null);
|
|
1839
1869
|
scheduleDraw();
|
|
1840
1870
|
setEditing(null);
|
|
1841
|
-
}, [bodyTop, columns, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, metrics,
|
|
1871
|
+
}, [bodyTop, columns, renderHeight, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, metrics, rightFixedOffsets, rightFixedWidth, rowHeight, rows, scheduleDraw, selection, selectionRange, viewport.width]);
|
|
1842
1872
|
const locateColumn = useCallback2((clientX) => {
|
|
1843
1873
|
const canvas = canvasRef.current;
|
|
1844
1874
|
if (!canvas) return -1;
|
|
@@ -2426,13 +2456,13 @@ function Table({
|
|
|
2426
2456
|
const rowTop = (fixedHeader ? 0 : bodyTop) + rowIndex * rowHeight;
|
|
2427
2457
|
const rowBottom = rowTop + rowHeight * (span?.rowSpan ?? 1);
|
|
2428
2458
|
const cellWidth = getCellDisplayWidth(columnIndex, span?.colSpan ?? 1);
|
|
2429
|
-
const visibleRowHeight = fixedHeader ?
|
|
2459
|
+
const visibleRowHeight = fixedHeader ? renderHeight - bodyTop : renderHeight;
|
|
2430
2460
|
if (rowTop < scroller.scrollTop) scroller.scrollTop = rowTop;
|
|
2431
2461
|
if (rowBottom > scroller.scrollTop + visibleRowHeight) scroller.scrollTop = rowBottom - visibleRowHeight;
|
|
2432
2462
|
if (columns[columnIndex].fixed === void 0 && metric.left < scroller.scrollLeft + fixedWidth) scroller.scrollLeft = Math.max(0, metric.left - fixedWidth);
|
|
2433
2463
|
if (columns[columnIndex].fixed === void 0 && metric.left + cellWidth > scroller.scrollLeft + viewport.width - rightFixedWidth) scroller.scrollLeft = metric.left + cellWidth - viewport.width + rightFixedWidth;
|
|
2434
2464
|
}
|
|
2435
|
-
}, [beginEdit, bodyTop, columns, editing, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, metrics,
|
|
2465
|
+
}, [beginEdit, bodyTop, columns, editing, renderHeight, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, metrics, rightFixedWidth, rowHeight, rows, selection, setSelection, viewport.width]);
|
|
2436
2466
|
const editorStyle = useMemo(() => {
|
|
2437
2467
|
if (!editing) return void 0;
|
|
2438
2468
|
const metric = metrics[editing.columnIndex];
|
|
@@ -2656,7 +2686,7 @@ function Table({
|
|
|
2656
2686
|
type: "cell",
|
|
2657
2687
|
...cell,
|
|
2658
2688
|
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,
|
|
2689
|
+
top: Math.min(Math.max(0, clientY - root.top), Math.max(0, renderHeight - 350))
|
|
2660
2690
|
});
|
|
2661
2691
|
onCellContextMenu?.({ ...cell, row: rows[cell.rowIndex], clientX, clientY });
|
|
2662
2692
|
};
|
|
@@ -2667,7 +2697,7 @@ function Table({
|
|
|
2667
2697
|
type: "header",
|
|
2668
2698
|
columnIndex,
|
|
2669
2699
|
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,
|
|
2700
|
+
top: Math.min(Math.max(0, clientY - root.top), Math.max(0, renderHeight - 68))
|
|
2671
2701
|
});
|
|
2672
2702
|
};
|
|
2673
2703
|
const getRowDragOffset = (rowIndex) => {
|
|
@@ -2897,7 +2927,7 @@ function Table({
|
|
|
2897
2927
|
const horizontalStart = column.fixed === void 0 ? fixedWidth : 0;
|
|
2898
2928
|
const horizontalEnd = column.fixed === void 0 ? viewport.width - rightFixedWidth : viewport.width;
|
|
2899
2929
|
const verticalStart = fixedHeader ? bodyTop : 0;
|
|
2900
|
-
if (rawLeft <= horizontalStart || rawLeft > horizontalEnd || rawTop <= verticalStart || rawTop >
|
|
2930
|
+
if (rawLeft <= horizontalStart || rawLeft > horizontalEnd || rawTop <= verticalStart || rawTop > renderHeight) return null;
|
|
2901
2931
|
return { left: rawLeft, top: rawTop, bounds };
|
|
2902
2932
|
})();
|
|
2903
2933
|
const selectionOutlineStyle = (() => {
|
|
@@ -2914,12 +2944,12 @@ function Table({
|
|
|
2914
2944
|
const horizontalStart = column.fixed === void 0 ? fixedWidth : 0;
|
|
2915
2945
|
const horizontalEnd = column.fixed === void 0 ? viewport.width - rightFixedWidth : viewport.width;
|
|
2916
2946
|
const verticalStart = fixedHeader ? bodyTop : 0;
|
|
2917
|
-
if (rawRight <= horizontalStart || rawLeft >= horizontalEnd || rawBottom <= verticalStart || rawTop >=
|
|
2947
|
+
if (rawRight <= horizontalStart || rawLeft >= horizontalEnd || rawBottom <= verticalStart || rawTop >= renderHeight) return null;
|
|
2918
2948
|
const borderWidth = 2;
|
|
2919
2949
|
const left = Math.round(Math.max(rawLeft - borderWidth, horizontalStart - 1));
|
|
2920
2950
|
const right = Math.round(Math.min(rawRight + 1, horizontalEnd, viewport.width));
|
|
2921
2951
|
const top = Math.round(Math.max(rawTop - borderWidth, verticalStart - 1));
|
|
2922
|
-
const bottom = Math.round(Math.min(rawBottom + 1,
|
|
2952
|
+
const bottom = Math.round(Math.min(rawBottom + 1, renderHeight));
|
|
2923
2953
|
return {
|
|
2924
2954
|
left,
|
|
2925
2955
|
top,
|
|
@@ -3034,8 +3064,10 @@ function Table({
|
|
|
3034
3064
|
const renderSummaryCell = (columnIndex, left) => {
|
|
3035
3065
|
const column = columns[columnIndex];
|
|
3036
3066
|
const isUtility = column.rowSelection || column.rowDragHandle || column.rowNumber;
|
|
3037
|
-
const
|
|
3038
|
-
const
|
|
3067
|
+
const hasSummaryValue = summaryValues.has(column.key);
|
|
3068
|
+
const summaryContent = hasSummaryValue ? summaryValues.get(column.key) : summaryEmptyValue;
|
|
3069
|
+
const showSummarySkeleton = Boolean(column.summary) && summaryPending;
|
|
3070
|
+
const content = columnIndex === 0 ? language === "zh-CN" ? "\u5408\u8BA1" : "Total" : showSummarySkeleton ? /* @__PURE__ */ jsx7("span", { className: "rvg-summary-skeleton" }) : summaryContent;
|
|
3039
3071
|
return /* @__PURE__ */ jsx7(
|
|
3040
3072
|
"div",
|
|
3041
3073
|
{
|
|
@@ -3052,8 +3084,8 @@ function Table({
|
|
|
3052
3084
|
};
|
|
3053
3085
|
const rootStyle = {
|
|
3054
3086
|
width,
|
|
3055
|
-
height,
|
|
3056
|
-
minHeight: typeof height === "number" ? void 0 : FALLBACK_HEIGHT,
|
|
3087
|
+
height: autoHeight ? effectiveHeight : height,
|
|
3088
|
+
minHeight: autoHeight || typeof height === "number" ? void 0 : FALLBACK_HEIGHT,
|
|
3057
3089
|
...stripedColor ? { "--rvg-color-stripe": stripedColor } : null,
|
|
3058
3090
|
...style
|
|
3059
3091
|
};
|
|
@@ -3066,7 +3098,7 @@ function Table({
|
|
|
3066
3098
|
{
|
|
3067
3099
|
ref: canvasRef,
|
|
3068
3100
|
className: "rvg-canvas",
|
|
3069
|
-
style: { width: viewport.width, height:
|
|
3101
|
+
style: { width: viewport.width, height: renderHeight },
|
|
3070
3102
|
onMouseMove: (event) => {
|
|
3071
3103
|
if (columnDragRef.current) return;
|
|
3072
3104
|
const rect = event.currentTarget.getBoundingClientRect();
|
|
@@ -3244,7 +3276,7 @@ function Table({
|
|
|
3244
3276
|
onContextMenu: handleContextMenu
|
|
3245
3277
|
}
|
|
3246
3278
|
),
|
|
3247
|
-
/* @__PURE__ */ jsxs7("div", { className: "rvg-text-layer", style: { width: viewport.width, height:
|
|
3279
|
+
/* @__PURE__ */ jsxs7("div", { className: "rvg-text-layer", style: { width: viewport.width, height: renderHeight, marginTop: -renderHeight }, onContextMenu: handleContextMenu, children: [
|
|
3248
3280
|
/* @__PURE__ */ jsx7("div", { className: "rvg-text-scroll-clip", style: { left: fixedWidth, right: rightFixedWidth }, children: /* @__PURE__ */ jsxs7(
|
|
3249
3281
|
"div",
|
|
3250
3282
|
{
|
|
@@ -3268,7 +3300,7 @@ function Table({
|
|
|
3268
3300
|
columns.map((column, columnIndex) => column.fixed === "left" ? renderHeaderTitle(columnIndex, metrics[columnIndex].left) : null),
|
|
3269
3301
|
columns.map((column, columnIndex) => column.fixed === "right" ? renderHeaderTitle(columnIndex, viewport.width - (rightFixedOffsets.get(columnIndex) ?? 0) - metrics[columnIndex].width) : null)
|
|
3270
3302
|
] }),
|
|
3271
|
-
/* @__PURE__ */ jsx7("div", { className: "rvg-spacer", style: { width: contentWidth, height: contentHeight + bodyTop, marginTop: -
|
|
3303
|
+
/* @__PURE__ */ jsx7("div", { className: "rvg-spacer", style: { width: contentWidth, height: contentHeight + bodyTop, marginTop: -renderHeight } })
|
|
3272
3304
|
] }),
|
|
3273
3305
|
hasSummaryRow && /* @__PURE__ */ jsxs7(
|
|
3274
3306
|
"div",
|
|
@@ -3629,10 +3661,10 @@ function Table({
|
|
|
3629
3661
|
] })
|
|
3630
3662
|
] }),
|
|
3631
3663
|
renderEditor(),
|
|
3632
|
-
rows.length === 0 && !loading && /* @__PURE__ */ jsx7("div", { className: "rvg-empty", children: emptyContent ?? /* @__PURE__ */ jsx7(DefaultEmptyState, { label: labels.empty }) }),
|
|
3664
|
+
rows.length === 0 && !loading && /* @__PURE__ */ jsx7("div", { className: "rvg-empty", style: { top: bodyTop }, children: emptyContent ?? /* @__PURE__ */ jsx7(DefaultEmptyState, { label: labels.empty }) }),
|
|
3633
3665
|
loading && hasCustomLoading && /* @__PURE__ */ jsx7("div", { className: "rvg-custom-loading", "aria-label": labels.refreshing, children: loadingContent }),
|
|
3634
3666
|
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((
|
|
3667
|
+
Array.from({ length: Math.ceil((renderHeight - headerHeight) / rowHeight) }, (_, rowIndex) => /* @__PURE__ */ jsx7("div", { className: "rvg-loading-row", style: { height: rowHeight }, children: columns.map((column, columnIndex) => {
|
|
3636
3668
|
const left = getDisplayedColumnLeft(columnIndex);
|
|
3637
3669
|
const columnWidth = metrics[columnIndex].width;
|
|
3638
3670
|
if (left + columnWidth <= 0 || left >= viewport.width) return null;
|