@lensui/lens-table 0.1.1 → 0.1.3
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 +21 -6
- package/dist/index.cjs +160 -42
- package/dist/index.css +25 -7
- package/dist/index.d.cts +29 -5
- package/dist/index.d.ts +29 -5
- package/dist/index.js +160 -42
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -88,16 +88,17 @@ export function Example() {
|
|
|
88
88
|
| `rows` | `Row[]` | 必填 | 表格数据。组件不会直接修改数据,编辑后通过事件通知外部更新。 |
|
|
89
89
|
| `rowKey` | `keyof Row \| ((row, index) => string \| number)` | `id` | 行唯一标识。默认读取每行的`id` 字段;如果数据不是 `id` 主键,请显式传入字段名或函数。 |
|
|
90
90
|
| `width` | `number \| string` | `100%` | 表格宽度。数字按像素处理,字符串可传`100%`、`80vw` 等。 |
|
|
91
|
-
| `height` | `number \| string` | `100%` |
|
|
92
|
-
| `
|
|
93
|
-
| `rowHeight` | `number` | `
|
|
94
|
-
| `headerHeight` | `number` | `
|
|
91
|
+
| `height` | `number \| string` | `100%` | 表格最大高度。默认继承父容器高度;如果父容器没有可计算高度,则回落到`480px`。数据较少时默认按真实内容高度收缩,数据较多时不超过该高度。 |
|
|
92
|
+
| `layout` | `{ rowHeight?: number; headerHeight?: number }` | `{ rowHeight: 36, headerHeight: 40 }` | 表格布局尺寸配置。推荐把行高、表头高度等样式相关参数放在这里;自定义多行表头会以 `headerHeight` 为最小高度自动撑开。 |
|
|
93
|
+
| `rowHeight` | `number` | `36` | 已兼容保留,建议改用 `layout.rowHeight`。 |
|
|
94
|
+
| `headerHeight` | `number` | `40` | 已兼容保留,建议改用 `layout.headerHeight`。 |
|
|
95
95
|
| `fixedHeader` | `boolean` | `true` | 表头是否固定在顶部。 |
|
|
96
96
|
| `locale` | `'zh-CN' \| 'en-US' \| LocaleConfig` | `'zh-CN'` | 国际化配置。传字符串时使用内置中英文文案;传对象时可通过`language` 指定语言,并通过 `labels` 覆盖文案。 |
|
|
97
|
-
| `
|
|
97
|
+
| `borderless` | `boolean` | `false` | 是否弱化竖向边框。开启后隐藏单元格之间的竖线,但保留横向分隔线。 |
|
|
98
|
+
| `verticalBorderless` | `boolean` | `false` | 已兼容保留,建议改用 `borderless`。 |
|
|
98
99
|
| `striped` | `boolean \| string` | `false` | 是否显示斑马纹行背景。传`true` 使用主题变量,传颜色字符串可自定义斑马纹颜色,例如 `'#f6faff'`。 |
|
|
99
100
|
| `highlight` | `{ editedCells?: boolean \| string; insertedRows?: boolean \| string }` | `{}` | 高亮反馈配置。`editedCells` 控制编辑过的单元格底色,`insertedRows` 控制乐观插入行底色;传 `true` 使用默认色,传颜色字符串可自定义。 |
|
|
100
|
-
| `cellSpans` | `
|
|
101
|
+
| `cellSpans` | `TableCellSpan[] \| ((ctx) => TableCellSpan \| { rowSpan?: number; colSpan?: number } \| null)` | `[]` | 合并主体单元格配置。可传数组,也可传函数按单元格生成规则。推荐使用 `rowKey` 定位;横向合并会限制在同一个固定列分区内,工具列不会参与合并。 |
|
|
101
102
|
| `loading` | `boolean` | `false` | 显示加载态。 |
|
|
102
103
|
| `loadingContent` | `ReactNode` | - | 自定义加载内容。传入后,首次加载和刷新加载不再区分,都会统一展示这个内容。 |
|
|
103
104
|
| `virtualized` | `boolean \| { enabled?: boolean; overscan?: number }` | `true` | 虚拟渲染配置。传`false` 时渲染全部行列;传对象时可通过 `enabled` 开关,并用 `overscan` 配置可见范围外的缓冲行列数量,默认 `4`。 |
|
|
@@ -169,6 +170,19 @@ export function Example() {
|
|
|
169
170
|
/>
|
|
170
171
|
```
|
|
171
172
|
|
|
173
|
+
也可以传函数按单元格声明规则。函数返回值会默认使用当前单元格作为锚点,因此只需要返回跨度:
|
|
174
|
+
|
|
175
|
+
```tsx
|
|
176
|
+
<Table
|
|
177
|
+
columns={columns}
|
|
178
|
+
rows={rows}
|
|
179
|
+
cellSpans={({ rowIndex, column }) => {
|
|
180
|
+
if (column.key === 'department' && rowIndex % 3 === 0) return { rowSpan: 2 };
|
|
181
|
+
return null;
|
|
182
|
+
}}
|
|
183
|
+
/>
|
|
184
|
+
```
|
|
185
|
+
|
|
172
186
|
`rowKey` 优先于 `rowIndex`,在排序、过滤或插入删除行后更稳定。被合并覆盖的单元格不会单独绘制或命中,点击覆盖区域会选中合并区域的锚点单元格。横向合并不会跨越固定左列、滚动列、固定右列这三个区域;如果声明跨区,组件会自动收缩到当前区域内。
|
|
173
187
|
|
|
174
188
|
## Column 参数
|
|
@@ -177,6 +191,7 @@ export function Example() {
|
|
|
177
191
|
| -------------- | -------------------------------------------------------------------------- | -------------------- | ------------------------------------------------------------------------------------------- |
|
|
178
192
|
| `key` | `string` | 必填 | 列唯一标识。排序、筛选、选择、编辑回调都使用它。 |
|
|
179
193
|
| `title` | `string` | 必填 | 表头展示文本。 |
|
|
194
|
+
| `renderHeader` | `(column) => ReactNode` | - | 自定义表头 React 内容。多行内容会自动撑开表头高度;`title` 仍用于 tooltip、拖拽预览和回退文本。 |
|
|
180
195
|
| `dataIndex` | `keyof Row` | - | 从行数据中读取和写入的字段。工具列可不传。 |
|
|
181
196
|
| `width` | `number` | `140` | 列宽,单位 px。最小宽度由内部布局保护。 |
|
|
182
197
|
| `fixed` | `'left' \| 'right'` | - | 固定列位置。左/右固定列会覆盖滚动列并显示阴影。 |
|
package/dist/index.cjs
CHANGED
|
@@ -814,8 +814,9 @@ function buildColumnMetrics(columns, columnDraggableOrOptions = false) {
|
|
|
814
814
|
});
|
|
815
815
|
const viewportWidth = options.viewportWidth ?? 0;
|
|
816
816
|
if (viewportWidth <= left || columns.length === 0) return metrics;
|
|
817
|
-
const
|
|
818
|
-
const
|
|
817
|
+
const unsetWidthStretchable = columns.map((column, index) => ({ column, index })).filter(({ column }) => column.width === void 0 && column.fixed === void 0 && !column.rowSelection && !column.rowDragHandle && !column.rowNumber);
|
|
818
|
+
const dataStretchable = columns.map((column, index) => ({ column, index })).filter(({ column }) => column.fixed === void 0 && !column.rowSelection && !column.rowDragHandle && !column.rowNumber);
|
|
819
|
+
const targets = unsetWidthStretchable.length > 0 ? unsetWidthStretchable : dataStretchable.length > 0 ? dataStretchable : [];
|
|
819
820
|
if (targets.length === 0) return metrics;
|
|
820
821
|
const extra = viewportWidth - left;
|
|
821
822
|
const baseTotal = targets.reduce((sum, { index }) => sum + metrics[index].width, 0);
|
|
@@ -1069,7 +1070,7 @@ function paintGrid(options) {
|
|
|
1069
1070
|
if (!verticalBorderless) ctx.fillRect(x + metric.width - 1, headerY, 1, headerHeight);
|
|
1070
1071
|
ctx.fillRect(x, headerY + headerHeight - 1, metric.width, 1);
|
|
1071
1072
|
const column = columns[columnIndex];
|
|
1072
|
-
if (!column.rowSelection && !column.rowDragHandle && !column.rowNumber && column.title) {
|
|
1073
|
+
if (!column.rowSelection && !column.rowDragHandle && !column.rowNumber && !column.renderHeader && column.title) {
|
|
1073
1074
|
ctx.font = "600 13px Inter, ui-sans-serif, system-ui, sans-serif";
|
|
1074
1075
|
const titleWidth = ctx.measureText(column.title).width;
|
|
1075
1076
|
const visibleActions = getVisibleHeaderActions(column, metric.width, columnDraggable, titleWidth);
|
|
@@ -1328,12 +1329,14 @@ function Table({
|
|
|
1328
1329
|
rowKey,
|
|
1329
1330
|
width = "100%",
|
|
1330
1331
|
height = "100%",
|
|
1331
|
-
autoHeight =
|
|
1332
|
-
|
|
1333
|
-
|
|
1332
|
+
autoHeight = true,
|
|
1333
|
+
layout,
|
|
1334
|
+
rowHeight: rowHeightProp,
|
|
1335
|
+
headerHeight: headerHeightProp,
|
|
1334
1336
|
fixedHeader = true,
|
|
1335
1337
|
locale = "zh-CN",
|
|
1336
|
-
|
|
1338
|
+
borderless,
|
|
1339
|
+
verticalBorderless,
|
|
1337
1340
|
striped = false,
|
|
1338
1341
|
highlight,
|
|
1339
1342
|
cellSpans = [],
|
|
@@ -1375,8 +1378,13 @@ function Table({
|
|
|
1375
1378
|
style,
|
|
1376
1379
|
ariaLabel = "Data grid"
|
|
1377
1380
|
}) {
|
|
1381
|
+
const rowHeight = layout?.rowHeight ?? rowHeightProp ?? 36;
|
|
1382
|
+
const baseHeaderHeight = layout?.headerHeight ?? headerHeightProp ?? 40;
|
|
1378
1383
|
const { language, labels } = (0, import_react5.useMemo)(() => resolveGridLocale(locale), [locale]);
|
|
1379
|
-
const
|
|
1384
|
+
const customHeaderContentRefs = (0, import_react5.useRef)(/* @__PURE__ */ new Map());
|
|
1385
|
+
const [measuredHeaderHeight, setMeasuredHeaderHeight] = (0, import_react5.useState)(0);
|
|
1386
|
+
const headerHeight = Math.max(baseHeaderHeight, measuredHeaderHeight);
|
|
1387
|
+
const hasVerticalBorders = !(borderless ?? verticalBorderless ?? false);
|
|
1380
1388
|
const hasStripedRows = Boolean(striped);
|
|
1381
1389
|
const stripedColor = typeof striped === "string" ? striped : void 0;
|
|
1382
1390
|
const editedCellsHighlight = highlight?.editedCells ?? false;
|
|
@@ -1440,7 +1448,7 @@ function Table({
|
|
|
1440
1448
|
const hoveredRowIndexRef = (0, import_react5.useRef)(null);
|
|
1441
1449
|
const [viewport, setViewport] = (0, import_react5.useState)({ width: 0, height: typeof height === "number" ? height : FALLBACK_HEIGHT });
|
|
1442
1450
|
const [horizontalScrollbarHeight, setHorizontalScrollbarHeight] = (0, import_react5.useState)(0);
|
|
1443
|
-
const resolvedHeight = viewport.height || FALLBACK_HEIGHT;
|
|
1451
|
+
const resolvedHeight = typeof height === "number" ? height : viewport.height || FALLBACK_HEIGHT;
|
|
1444
1452
|
const [scrollPosition, setScrollPosition] = (0, import_react5.useState)({ left: 0, top: 0 });
|
|
1445
1453
|
const scrollRef = (0, import_react5.useRef)({ left: 0, top: 0 });
|
|
1446
1454
|
const [selection, setSelection] = useControllableValue(selectedCell, defaultSelectedCell, onSelectedCellChange);
|
|
@@ -1482,6 +1490,28 @@ function Table({
|
|
|
1482
1490
|
if (headerTooltipTimerRef.current !== null) clearTimeout(headerTooltipTimerRef.current);
|
|
1483
1491
|
if (cellTooltipTimerRef.current !== null) clearTimeout(cellTooltipTimerRef.current);
|
|
1484
1492
|
}, []);
|
|
1493
|
+
const measureCustomHeaderHeight = (0, import_react5.useCallback)(() => {
|
|
1494
|
+
let nextHeight = 0;
|
|
1495
|
+
customHeaderContentRefs.current.forEach((node) => {
|
|
1496
|
+
nextHeight = Math.max(nextHeight, Math.ceil(node.scrollHeight) + 16);
|
|
1497
|
+
});
|
|
1498
|
+
setMeasuredHeaderHeight((current) => current === nextHeight ? current : nextHeight);
|
|
1499
|
+
}, []);
|
|
1500
|
+
(0, import_react5.useLayoutEffect)(() => {
|
|
1501
|
+
const activeCustomHeaderKeys = new Set(columns.filter((column) => column.renderHeader).map((column) => column.key));
|
|
1502
|
+
customHeaderContentRefs.current.forEach((_, key) => {
|
|
1503
|
+
if (!activeCustomHeaderKeys.has(key)) customHeaderContentRefs.current.delete(key);
|
|
1504
|
+
});
|
|
1505
|
+
if (customHeaderContentRefs.current.size === 0) {
|
|
1506
|
+
setMeasuredHeaderHeight(0);
|
|
1507
|
+
return;
|
|
1508
|
+
}
|
|
1509
|
+
measureCustomHeaderHeight();
|
|
1510
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
1511
|
+
const observer = new ResizeObserver(measureCustomHeaderHeight);
|
|
1512
|
+
customHeaderContentRefs.current.forEach((node) => observer.observe(node));
|
|
1513
|
+
return () => observer.disconnect();
|
|
1514
|
+
}, [columns, measureCustomHeaderHeight, viewport.width]);
|
|
1485
1515
|
const [filterEditor, setFilterEditor] = (0, import_react5.useState)(null);
|
|
1486
1516
|
const [contextMenu, setContextMenu] = (0, import_react5.useState)(null);
|
|
1487
1517
|
const [insertCounts, setInsertCounts] = (0, import_react5.useState)({ before: 1, after: 1 });
|
|
@@ -1490,6 +1520,7 @@ function Table({
|
|
|
1490
1520
|
const [insertBusy, setInsertBusy] = (0, import_react5.useState)(false);
|
|
1491
1521
|
const [deleteBusy, setDeleteBusy] = (0, import_react5.useState)(false);
|
|
1492
1522
|
const [toast, setToast] = (0, import_react5.useState)(null);
|
|
1523
|
+
const [resizeGuideX, setResizeGuideX] = (0, import_react5.useState)(null);
|
|
1493
1524
|
const [hoveredHeaderAction, setHoveredHeaderAction] = (0, import_react5.useState)(null);
|
|
1494
1525
|
const [visibleHeaderTooltip, setVisibleHeaderTooltip] = (0, import_react5.useState)(null);
|
|
1495
1526
|
const [hoveredCellTooltip, setHoveredCellTooltip] = (0, import_react5.useState)(null);
|
|
@@ -1588,14 +1619,15 @@ function Table({
|
|
|
1588
1619
|
}, [columns, metrics]);
|
|
1589
1620
|
const hasRowSelectionColumn = (0, import_react5.useMemo)(() => columns.some((column) => column.rowSelection), [columns]);
|
|
1590
1621
|
const rowSelectionMode = typeof rowSelection === "object" ? rowSelection.mode ?? "multiple" : "multiple";
|
|
1591
|
-
const hasSummaryRow = (0, import_react5.useMemo)(() => summaryEnabled && columns.some((column) => Boolean(column.summary)), [columns, summaryEnabled]);
|
|
1622
|
+
const hasSummaryRow = (0, import_react5.useMemo)(() => rows.length > 0 && summaryEnabled && columns.some((column) => Boolean(column.summary)), [columns, rows.length, summaryEnabled]);
|
|
1592
1623
|
const topSummaryHeight = hasSummaryRow && summaryPosition === "top" ? rowHeight : 0;
|
|
1593
1624
|
const bottomSummaryHeight = hasSummaryRow && summaryPosition === "bottom" ? rowHeight : 0;
|
|
1594
1625
|
const bodyTop = headerHeight + topSummaryHeight;
|
|
1595
1626
|
const bodyContentHeight = rows.length === 0 ? EMPTY_BODY_HEIGHT : contentHeight;
|
|
1596
1627
|
const realContentHeight = bodyTop + bodyContentHeight + bottomSummaryHeight + (contentWidth > viewport.width ? horizontalScrollbarHeight : 0);
|
|
1597
1628
|
const effectiveHeight = autoHeight ? Math.max(bodyTop + bottomSummaryHeight, Math.min(resolvedHeight, realContentHeight)) : resolvedHeight;
|
|
1598
|
-
const
|
|
1629
|
+
const renderHeight = Math.max(0, effectiveHeight - bottomSummaryHeight);
|
|
1630
|
+
const bodyViewportHeight = Math.max(0, renderHeight - (fixedHeader ? headerHeight : 0) - topSummaryHeight);
|
|
1599
1631
|
const [summaryState, setSummaryState] = (0, import_react5.useState)(() => ({ columns: null, rows: null, values: /* @__PURE__ */ new Map(), pending: false }));
|
|
1600
1632
|
const summaryPending = hasSummaryRow && (loading || summaryState.pending || summaryState.columns !== columns || summaryState.rows !== rows);
|
|
1601
1633
|
const summaryValues = hasSummaryRow && summaryState.columns === columns && summaryState.rows === rows ? summaryState.values : /* @__PURE__ */ new Map();
|
|
@@ -1642,14 +1674,30 @@ function Table({
|
|
|
1642
1674
|
rowIndexByKeyRef.current.set(key, index);
|
|
1643
1675
|
return key;
|
|
1644
1676
|
}, [getDataRowKey]);
|
|
1677
|
+
const resolvedCellSpans = (0, import_react5.useMemo)(() => {
|
|
1678
|
+
if (Array.isArray(cellSpans)) return cellSpans;
|
|
1679
|
+
const spans = [];
|
|
1680
|
+
rows.forEach((row, rowIndex) => {
|
|
1681
|
+
columns.forEach((column, columnIndex) => {
|
|
1682
|
+
const span = cellSpans({ row, rowIndex, column, columnIndex, rows, columns });
|
|
1683
|
+
if (!span) return;
|
|
1684
|
+
spans.push({
|
|
1685
|
+
rowIndex,
|
|
1686
|
+
columnKey: column.key,
|
|
1687
|
+
...span
|
|
1688
|
+
});
|
|
1689
|
+
});
|
|
1690
|
+
});
|
|
1691
|
+
return spans;
|
|
1692
|
+
}, [cellSpans, columns, rows]);
|
|
1645
1693
|
const cellSpanLookup = (0, import_react5.useMemo)(() => {
|
|
1646
|
-
if (
|
|
1694
|
+
if (resolvedCellSpans.length === 0) return createEmptyCellSpanLookup();
|
|
1647
1695
|
const rowIndexByKey = /* @__PURE__ */ new Map();
|
|
1648
1696
|
rows.forEach((row, index) => rowIndexByKey.set(getRowKey(row, index), index));
|
|
1649
1697
|
const columnIndexByKey = new Map(columns.map((column, index) => [column.key, index]));
|
|
1650
1698
|
const lookup = createEmptyCellSpanLookup();
|
|
1651
1699
|
const occupied = /* @__PURE__ */ new Set();
|
|
1652
|
-
for (const span of
|
|
1700
|
+
for (const span of resolvedCellSpans) {
|
|
1653
1701
|
const rowIndex = span.rowKey !== void 0 ? rowIndexByKey.get(span.rowKey) : span.rowIndex;
|
|
1654
1702
|
const columnIndex = columnIndexByKey.get(span.columnKey);
|
|
1655
1703
|
if (rowIndex === void 0 || columnIndex === void 0) continue;
|
|
@@ -1685,7 +1733,7 @@ function Table({
|
|
|
1685
1733
|
}
|
|
1686
1734
|
}
|
|
1687
1735
|
return lookup;
|
|
1688
|
-
}, [
|
|
1736
|
+
}, [columns, getRowKey, resolvedCellSpans, rows]);
|
|
1689
1737
|
const selectedRowKeySet = (0, import_react5.useMemo)(() => new Set(rowKeys), [rowKeys]);
|
|
1690
1738
|
const selectedColumnKeySet = (0, import_react5.useMemo)(() => new Set(columnKeys), [columnKeys]);
|
|
1691
1739
|
const insertedRowKeySet = (0, import_react5.useMemo)(() => insertedRowKeys, [insertedRowKeys]);
|
|
@@ -1732,7 +1780,7 @@ function Table({
|
|
|
1732
1780
|
if (!canvas || viewport.width <= 0) return;
|
|
1733
1781
|
const ratio = Math.min(window.devicePixelRatio || 1, 2);
|
|
1734
1782
|
const physicalWidth = Math.round(viewport.width * ratio);
|
|
1735
|
-
const physicalHeight = Math.round(
|
|
1783
|
+
const physicalHeight = Math.round(renderHeight * ratio);
|
|
1736
1784
|
if (canvas.width !== physicalWidth) canvas.width = physicalWidth;
|
|
1737
1785
|
if (canvas.height !== physicalHeight) canvas.height = physicalHeight;
|
|
1738
1786
|
const context = canvas.getContext("2d");
|
|
@@ -1755,8 +1803,8 @@ function Table({
|
|
|
1755
1803
|
const scroll = scrollRef.current;
|
|
1756
1804
|
const rowScrollTop = fixedHeader ? scroll.top : Math.max(0, scroll.top - bodyTop);
|
|
1757
1805
|
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 };
|
|
1758
|
-
paintGrid({ context, width: viewport.width, height:
|
|
1759
|
-
}, [bodyTop, bodyViewportHeight, bottomSummaryHeight, cellAnnotations, cellSpanLookup, columnDraggable, columns, editedCellHighlightColor, editedCellKeys, editing,
|
|
1806
|
+
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 });
|
|
1807
|
+
}, [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]);
|
|
1760
1808
|
const scheduleDraw = (0, import_react5.useCallback)(() => {
|
|
1761
1809
|
if (frameRef.current === null) frameRef.current = requestAnimationFrame(draw);
|
|
1762
1810
|
}, [draw]);
|
|
@@ -1865,14 +1913,14 @@ function Table({
|
|
|
1865
1913
|
const horizontalStart = column.fixed === void 0 ? fixedWidth : 0;
|
|
1866
1914
|
const horizontalEnd = column.fixed === void 0 ? viewport.width - rightFixedWidth : viewport.width;
|
|
1867
1915
|
const verticalStart = fixedHeader ? bodyTop : 0;
|
|
1868
|
-
if (rawRight <= horizontalStart || rawLeft >= horizontalEnd || rawBottom <= verticalStart || rawTop >=
|
|
1916
|
+
if (rawRight <= horizontalStart || rawLeft >= horizontalEnd || rawBottom <= verticalStart || rawTop >= renderHeight) {
|
|
1869
1917
|
focus.style.display = "none";
|
|
1870
1918
|
} else {
|
|
1871
1919
|
const borderWidth = 2;
|
|
1872
1920
|
const left = Math.round(Math.max(rawLeft - borderWidth, horizontalStart - 1));
|
|
1873
1921
|
const right = Math.round(Math.min(rawRight + 1, horizontalEnd, viewport.width));
|
|
1874
1922
|
const top = Math.round(Math.max(rawTop - borderWidth, verticalStart - 1));
|
|
1875
|
-
const bottom = Math.round(Math.min(rawBottom + 1,
|
|
1923
|
+
const bottom = Math.round(Math.min(rawBottom + 1, renderHeight));
|
|
1876
1924
|
focus.style.display = "block";
|
|
1877
1925
|
focus.style.left = `${left}px`;
|
|
1878
1926
|
focus.style.top = `${top}px`;
|
|
@@ -1897,7 +1945,7 @@ function Table({
|
|
|
1897
1945
|
setHoveredCellTooltip(null);
|
|
1898
1946
|
scheduleDraw();
|
|
1899
1947
|
setEditing(null);
|
|
1900
|
-
}, [bodyTop, columns,
|
|
1948
|
+
}, [bodyTop, columns, renderHeight, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, metrics, rightFixedOffsets, rightFixedWidth, rowHeight, rows, scheduleDraw, selection, selectionRange, viewport.width]);
|
|
1901
1949
|
const locateColumn = (0, import_react5.useCallback)((clientX) => {
|
|
1902
1950
|
const canvas = canvasRef.current;
|
|
1903
1951
|
if (!canvas) return -1;
|
|
@@ -1938,6 +1986,16 @@ function Table({
|
|
|
1938
1986
|
}
|
|
1939
1987
|
return closest;
|
|
1940
1988
|
}, [columnResizable, columns, getDisplayedColumnLeft, metrics]);
|
|
1989
|
+
const findResizeNeighbor = (0, import_react5.useCallback)((columnIndex) => {
|
|
1990
|
+
const resizedColumn = columns[columnIndex];
|
|
1991
|
+
for (let index = columnIndex + 1; index < columns.length; index += 1) {
|
|
1992
|
+
const column = columns[index];
|
|
1993
|
+
if (column.rowSelection || column.rowDragHandle || column.rowNumber) continue;
|
|
1994
|
+
if (resizedColumn?.fixed && column.fixed !== resizedColumn.fixed) return -1;
|
|
1995
|
+
return index;
|
|
1996
|
+
}
|
|
1997
|
+
return -1;
|
|
1998
|
+
}, [columns]);
|
|
1941
1999
|
const locateHeaderAction = (0, import_react5.useCallback)((clientX, columnIndex) => {
|
|
1942
2000
|
if (columnIndex < 0 || !canvasRef.current) return null;
|
|
1943
2001
|
const column = columns[columnIndex];
|
|
@@ -2485,13 +2543,13 @@ function Table({
|
|
|
2485
2543
|
const rowTop = (fixedHeader ? 0 : bodyTop) + rowIndex * rowHeight;
|
|
2486
2544
|
const rowBottom = rowTop + rowHeight * (span?.rowSpan ?? 1);
|
|
2487
2545
|
const cellWidth = getCellDisplayWidth(columnIndex, span?.colSpan ?? 1);
|
|
2488
|
-
const visibleRowHeight = fixedHeader ?
|
|
2546
|
+
const visibleRowHeight = fixedHeader ? renderHeight - bodyTop : renderHeight;
|
|
2489
2547
|
if (rowTop < scroller.scrollTop) scroller.scrollTop = rowTop;
|
|
2490
2548
|
if (rowBottom > scroller.scrollTop + visibleRowHeight) scroller.scrollTop = rowBottom - visibleRowHeight;
|
|
2491
2549
|
if (columns[columnIndex].fixed === void 0 && metric.left < scroller.scrollLeft + fixedWidth) scroller.scrollLeft = Math.max(0, metric.left - fixedWidth);
|
|
2492
2550
|
if (columns[columnIndex].fixed === void 0 && metric.left + cellWidth > scroller.scrollLeft + viewport.width - rightFixedWidth) scroller.scrollLeft = metric.left + cellWidth - viewport.width + rightFixedWidth;
|
|
2493
2551
|
}
|
|
2494
|
-
}, [beginEdit, bodyTop, columns, editing,
|
|
2552
|
+
}, [beginEdit, bodyTop, columns, editing, renderHeight, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, metrics, rightFixedWidth, rowHeight, rows, selection, setSelection, viewport.width]);
|
|
2495
2553
|
const editorStyle = (0, import_react5.useMemo)(() => {
|
|
2496
2554
|
if (!editing) return void 0;
|
|
2497
2555
|
const metric = metrics[editing.columnIndex];
|
|
@@ -2516,26 +2574,48 @@ function Table({
|
|
|
2516
2574
|
if (resizeIndex >= 0) {
|
|
2517
2575
|
event.preventDefault();
|
|
2518
2576
|
event.currentTarget.setPointerCapture(event.pointerId);
|
|
2519
|
-
|
|
2577
|
+
const neighborIndex = findResizeNeighbor(resizeIndex);
|
|
2578
|
+
const startGuideX = getDisplayedColumnLeft(resizeIndex) + metrics[resizeIndex].width;
|
|
2579
|
+
columnDragRef.current = {
|
|
2580
|
+
type: "resize",
|
|
2581
|
+
columnIndex: resizeIndex,
|
|
2582
|
+
startX: event.clientX,
|
|
2583
|
+
startWidth: metrics[resizeIndex].width,
|
|
2584
|
+
startGuideX,
|
|
2585
|
+
neighborIndex,
|
|
2586
|
+
startNeighborWidth: neighborIndex >= 0 ? metrics[neighborIndex].width : 0
|
|
2587
|
+
};
|
|
2520
2588
|
suppressClickRef.current = true;
|
|
2521
|
-
|
|
2589
|
+
setResizeGuideX(startGuideX);
|
|
2522
2590
|
return;
|
|
2523
2591
|
}
|
|
2524
|
-
}, [fixedHeader, getDisplayedColumnLeft, headerHeight, locateResizeHandle, metrics
|
|
2592
|
+
}, [findResizeNeighbor, fixedHeader, getDisplayedColumnLeft, headerHeight, locateResizeHandle, metrics]);
|
|
2525
2593
|
const handleColumnPointerMove = (0, import_react5.useCallback)((event) => {
|
|
2526
2594
|
const drag = columnDragRef.current;
|
|
2527
2595
|
if (!drag) return;
|
|
2528
|
-
const
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2596
|
+
const column = columns[drag.columnIndex];
|
|
2597
|
+
const minWidth = getMinimumColumnWidth(column, columnDraggable);
|
|
2598
|
+
let delta = Math.round(event.clientX - drag.startX);
|
|
2599
|
+
if (drag.neighborIndex >= 0) {
|
|
2600
|
+
const neighbor = columns[drag.neighborIndex];
|
|
2601
|
+
const minNeighborWidth = getMinimumColumnWidth(neighbor, columnDraggable);
|
|
2602
|
+
delta = Math.max(minWidth - drag.startWidth, Math.min(drag.startNeighborWidth - minNeighborWidth, delta));
|
|
2603
|
+
onColumnResize?.(column.key, drag.startWidth + delta);
|
|
2604
|
+
onColumnResize?.(neighbor.key, drag.startNeighborWidth - delta);
|
|
2605
|
+
setResizeGuideX(drag.startGuideX + delta);
|
|
2606
|
+
return;
|
|
2607
|
+
}
|
|
2608
|
+
const width2 = Math.max(minWidth, Math.round(drag.startWidth + delta));
|
|
2609
|
+
onColumnResize?.(column.key, width2);
|
|
2610
|
+
setResizeGuideX(drag.startGuideX + width2 - drag.startWidth);
|
|
2611
|
+
}, [columnDraggable, columns, onColumnResize]);
|
|
2532
2612
|
const handleColumnPointerUp = (0, import_react5.useCallback)((event) => {
|
|
2533
2613
|
const drag = columnDragRef.current;
|
|
2534
2614
|
if (!drag) return;
|
|
2535
2615
|
if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
|
|
2536
2616
|
columnDragRef.current = null;
|
|
2537
|
-
|
|
2538
|
-
}, [
|
|
2617
|
+
setResizeGuideX(null);
|
|
2618
|
+
}, []);
|
|
2539
2619
|
const queueSortStateChange = (0, import_react5.useCallback)((next) => {
|
|
2540
2620
|
setDisplaySortState(next);
|
|
2541
2621
|
if (sortDebounceRef.current !== null) clearTimeout(sortDebounceRef.current);
|
|
@@ -2715,7 +2795,7 @@ function Table({
|
|
|
2715
2795
|
type: "cell",
|
|
2716
2796
|
...cell,
|
|
2717
2797
|
left: Math.min(Math.max(0, clientX - root.left), Math.max(0, viewport.width - 190)),
|
|
2718
|
-
top: Math.min(Math.max(0, clientY - root.top), Math.max(0,
|
|
2798
|
+
top: Math.min(Math.max(0, clientY - root.top), Math.max(0, renderHeight - 350))
|
|
2719
2799
|
});
|
|
2720
2800
|
onCellContextMenu?.({ ...cell, row: rows[cell.rowIndex], clientX, clientY });
|
|
2721
2801
|
};
|
|
@@ -2726,7 +2806,7 @@ function Table({
|
|
|
2726
2806
|
type: "header",
|
|
2727
2807
|
columnIndex,
|
|
2728
2808
|
left: Math.min(Math.max(0, clientX - root.left), Math.max(0, viewport.width - 180)),
|
|
2729
|
-
top: Math.min(Math.max(0, clientY - root.top), Math.max(0,
|
|
2809
|
+
top: Math.min(Math.max(0, clientY - root.top), Math.max(0, renderHeight - 68))
|
|
2730
2810
|
});
|
|
2731
2811
|
};
|
|
2732
2812
|
const getRowDragOffset = (rowIndex) => {
|
|
@@ -2942,6 +3022,35 @@ function Table({
|
|
|
2942
3022
|
column.key
|
|
2943
3023
|
);
|
|
2944
3024
|
}
|
|
3025
|
+
if (column.renderHeader) {
|
|
3026
|
+
const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex));
|
|
3027
|
+
const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * 18;
|
|
3028
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3029
|
+
"div",
|
|
3030
|
+
{
|
|
3031
|
+
className: "rvg-header-title is-custom",
|
|
3032
|
+
style: {
|
|
3033
|
+
left,
|
|
3034
|
+
top: fixedHeader ? 0 : -currentScrollTop,
|
|
3035
|
+
width: Math.max(0, metrics[columnIndex].width - actionWidth),
|
|
3036
|
+
height: headerHeight,
|
|
3037
|
+
justifyContent: column.align === "right" ? "flex-end" : column.align === "center" ? "center" : "flex-start",
|
|
3038
|
+
textAlign: column.align === "right" ? "right" : column.align === "center" ? "center" : "left"
|
|
3039
|
+
},
|
|
3040
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3041
|
+
"span",
|
|
3042
|
+
{
|
|
3043
|
+
ref: (node) => {
|
|
3044
|
+
if (node) customHeaderContentRefs.current.set(column.key, node);
|
|
3045
|
+
else customHeaderContentRefs.current.delete(column.key);
|
|
3046
|
+
},
|
|
3047
|
+
children: column.renderHeader(column)
|
|
3048
|
+
}
|
|
3049
|
+
)
|
|
3050
|
+
},
|
|
3051
|
+
column.key
|
|
3052
|
+
);
|
|
3053
|
+
}
|
|
2945
3054
|
return null;
|
|
2946
3055
|
};
|
|
2947
3056
|
const rangeHandlePosition = (() => {
|
|
@@ -2956,7 +3065,7 @@ function Table({
|
|
|
2956
3065
|
const horizontalStart = column.fixed === void 0 ? fixedWidth : 0;
|
|
2957
3066
|
const horizontalEnd = column.fixed === void 0 ? viewport.width - rightFixedWidth : viewport.width;
|
|
2958
3067
|
const verticalStart = fixedHeader ? bodyTop : 0;
|
|
2959
|
-
if (rawLeft <= horizontalStart || rawLeft > horizontalEnd || rawTop <= verticalStart || rawTop >
|
|
3068
|
+
if (rawLeft <= horizontalStart || rawLeft > horizontalEnd || rawTop <= verticalStart || rawTop > renderHeight) return null;
|
|
2960
3069
|
return { left: rawLeft, top: rawTop, bounds };
|
|
2961
3070
|
})();
|
|
2962
3071
|
const selectionOutlineStyle = (() => {
|
|
@@ -2973,12 +3082,12 @@ function Table({
|
|
|
2973
3082
|
const horizontalStart = column.fixed === void 0 ? fixedWidth : 0;
|
|
2974
3083
|
const horizontalEnd = column.fixed === void 0 ? viewport.width - rightFixedWidth : viewport.width;
|
|
2975
3084
|
const verticalStart = fixedHeader ? bodyTop : 0;
|
|
2976
|
-
if (rawRight <= horizontalStart || rawLeft >= horizontalEnd || rawBottom <= verticalStart || rawTop >=
|
|
3085
|
+
if (rawRight <= horizontalStart || rawLeft >= horizontalEnd || rawBottom <= verticalStart || rawTop >= renderHeight) return null;
|
|
2977
3086
|
const borderWidth = 2;
|
|
2978
3087
|
const left = Math.round(Math.max(rawLeft - borderWidth, horizontalStart - 1));
|
|
2979
3088
|
const right = Math.round(Math.min(rawRight + 1, horizontalEnd, viewport.width));
|
|
2980
3089
|
const top = Math.round(Math.max(rawTop - borderWidth, verticalStart - 1));
|
|
2981
|
-
const bottom = Math.round(Math.min(rawBottom + 1,
|
|
3090
|
+
const bottom = Math.round(Math.min(rawBottom + 1, renderHeight));
|
|
2982
3091
|
return {
|
|
2983
3092
|
left,
|
|
2984
3093
|
top,
|
|
@@ -3093,8 +3202,10 @@ function Table({
|
|
|
3093
3202
|
const renderSummaryCell = (columnIndex, left) => {
|
|
3094
3203
|
const column = columns[columnIndex];
|
|
3095
3204
|
const isUtility = column.rowSelection || column.rowDragHandle || column.rowNumber;
|
|
3096
|
-
const
|
|
3097
|
-
const
|
|
3205
|
+
const hasSummaryValue = summaryValues.has(column.key);
|
|
3206
|
+
const summaryContent = hasSummaryValue ? summaryValues.get(column.key) : summaryEmptyValue;
|
|
3207
|
+
const showSummarySkeleton = Boolean(column.summary) && summaryPending;
|
|
3208
|
+
const content = columnIndex === 0 ? language === "zh-CN" ? "\u5408\u8BA1" : "Total" : showSummarySkeleton ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "rvg-summary-skeleton" }) : summaryContent;
|
|
3098
3209
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3099
3210
|
"div",
|
|
3100
3211
|
{
|
|
@@ -3125,7 +3236,7 @@ function Table({
|
|
|
3125
3236
|
{
|
|
3126
3237
|
ref: canvasRef,
|
|
3127
3238
|
className: "rvg-canvas",
|
|
3128
|
-
style: { width: viewport.width, height:
|
|
3239
|
+
style: { width: viewport.width, height: renderHeight },
|
|
3129
3240
|
onMouseMove: (event) => {
|
|
3130
3241
|
if (columnDragRef.current) return;
|
|
3131
3242
|
const rect = event.currentTarget.getBoundingClientRect();
|
|
@@ -3303,7 +3414,7 @@ function Table({
|
|
|
3303
3414
|
onContextMenu: handleContextMenu
|
|
3304
3415
|
}
|
|
3305
3416
|
),
|
|
3306
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rvg-text-layer", style: { width: viewport.width, height:
|
|
3417
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rvg-text-layer", style: { width: viewport.width, height: renderHeight, marginTop: -renderHeight }, onContextMenu: handleContextMenu, children: [
|
|
3307
3418
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rvg-text-scroll-clip", style: { left: fixedWidth, right: rightFixedWidth }, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
3308
3419
|
"div",
|
|
3309
3420
|
{
|
|
@@ -3327,7 +3438,7 @@ function Table({
|
|
|
3327
3438
|
columns.map((column, columnIndex) => column.fixed === "left" ? renderHeaderTitle(columnIndex, metrics[columnIndex].left) : null),
|
|
3328
3439
|
columns.map((column, columnIndex) => column.fixed === "right" ? renderHeaderTitle(columnIndex, viewport.width - (rightFixedOffsets.get(columnIndex) ?? 0) - metrics[columnIndex].width) : null)
|
|
3329
3440
|
] }),
|
|
3330
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rvg-spacer", style: { width: contentWidth, height: contentHeight + bodyTop, marginTop: -
|
|
3441
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rvg-spacer", style: { width: contentWidth, height: contentHeight + bodyTop, marginTop: -renderHeight } })
|
|
3331
3442
|
] }),
|
|
3332
3443
|
hasSummaryRow && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
3333
3444
|
"div",
|
|
@@ -3643,6 +3754,13 @@ function Table({
|
|
|
3643
3754
|
] }) }),
|
|
3644
3755
|
toast && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: `rvg-toast is-${toast.type}`, role: "status", children: toast.message }),
|
|
3645
3756
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { ref: dragGuideRef, className: "rvg-column-guide" }),
|
|
3757
|
+
resizeGuideX !== null && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3758
|
+
"div",
|
|
3759
|
+
{
|
|
3760
|
+
className: "rvg-column-guide is-resizing",
|
|
3761
|
+
style: { display: "block", transform: `translateX(${Math.round(resizeGuideX)}px)` }
|
|
3762
|
+
}
|
|
3763
|
+
),
|
|
3646
3764
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { ref: rowDragGuideRef, className: "rvg-row-guide" }),
|
|
3647
3765
|
filterEditor && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { ref: filterRef, className: "rvg-filter", style: { left: filterEditor.left, top: filterEditor.top }, children: [
|
|
3648
3766
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
@@ -3691,7 +3809,7 @@ function Table({
|
|
|
3691
3809
|
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 }) }),
|
|
3692
3810
|
loading && hasCustomLoading && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rvg-custom-loading", "aria-label": labels.refreshing, children: loadingContent }),
|
|
3693
3811
|
loading && !hasCustomLoading && !hasCompletedLoadRef.current && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rvg-initial-loading", style: { top: headerHeight }, "aria-label": labels.initialLoading, children: [
|
|
3694
|
-
Array.from({ length: Math.ceil((
|
|
3812
|
+
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) => {
|
|
3695
3813
|
const left = getDisplayedColumnLeft(columnIndex);
|
|
3696
3814
|
const columnWidth = metrics[columnIndex].width;
|
|
3697
3815
|
if (left + columnWidth <= 0 || left >= viewport.width) return null;
|
package/dist/index.css
CHANGED
|
@@ -150,6 +150,15 @@
|
|
|
150
150
|
white-space: nowrap;
|
|
151
151
|
user-select: text;
|
|
152
152
|
}
|
|
153
|
+
.rvg-header-title.is-custom {
|
|
154
|
+
overflow: visible;
|
|
155
|
+
white-space: normal;
|
|
156
|
+
}
|
|
157
|
+
.rvg-header-title.is-custom > span {
|
|
158
|
+
overflow: visible;
|
|
159
|
+
white-space: normal;
|
|
160
|
+
text-overflow: clip;
|
|
161
|
+
}
|
|
153
162
|
.rvg-row-drag-icon {
|
|
154
163
|
position: absolute;
|
|
155
164
|
display: grid;
|
|
@@ -1154,14 +1163,18 @@
|
|
|
1154
1163
|
z-index: 6;
|
|
1155
1164
|
background: var(--rvg-color-bg);
|
|
1156
1165
|
}
|
|
1166
|
+
.rvg-choice-editor {
|
|
1167
|
+
line-height: 1;
|
|
1168
|
+
}
|
|
1157
1169
|
.rvg-time-editor {
|
|
1158
1170
|
position: absolute;
|
|
1159
1171
|
z-index: 6;
|
|
1160
1172
|
display: flex;
|
|
1161
|
-
padding: 0 28px 0
|
|
1173
|
+
padding: 0 28px 0 9px;
|
|
1162
1174
|
align-items: center;
|
|
1163
1175
|
gap: 3px;
|
|
1164
1176
|
background: var(--rvg-color-bg);
|
|
1177
|
+
line-height: 1;
|
|
1165
1178
|
}
|
|
1166
1179
|
.rvg-time-field-group {
|
|
1167
1180
|
display: contents;
|
|
@@ -1169,6 +1182,7 @@
|
|
|
1169
1182
|
.rvg-time-editor input {
|
|
1170
1183
|
min-width: 0;
|
|
1171
1184
|
height: 100%;
|
|
1185
|
+
transform: translateY(-1px);
|
|
1172
1186
|
flex: 1;
|
|
1173
1187
|
padding: 0;
|
|
1174
1188
|
border: 0;
|
|
@@ -1180,17 +1194,18 @@
|
|
|
1180
1194
|
ui-sans-serif,
|
|
1181
1195
|
system-ui,
|
|
1182
1196
|
sans-serif;
|
|
1183
|
-
line-height:
|
|
1197
|
+
line-height: normal;
|
|
1184
1198
|
}
|
|
1185
1199
|
.rvg-native-editor {
|
|
1186
1200
|
position: absolute;
|
|
1187
1201
|
z-index: 6;
|
|
1188
1202
|
display: flex;
|
|
1189
|
-
padding: 0
|
|
1203
|
+
padding: 0 9px;
|
|
1190
1204
|
align-items: center;
|
|
1191
1205
|
gap: 6px;
|
|
1192
1206
|
overflow: hidden;
|
|
1193
1207
|
background: var(--rvg-color-bg);
|
|
1208
|
+
line-height: 1;
|
|
1194
1209
|
}
|
|
1195
1210
|
.rvg-native-editor.has-clear {
|
|
1196
1211
|
padding-right: 32px;
|
|
@@ -1198,18 +1213,20 @@
|
|
|
1198
1213
|
.rvg-native-editor input {
|
|
1199
1214
|
min-width: 0;
|
|
1200
1215
|
width: 100%;
|
|
1201
|
-
height:
|
|
1216
|
+
height: 100%;
|
|
1202
1217
|
padding: 0;
|
|
1218
|
+
transform: translateY(-1px);
|
|
1203
1219
|
flex: 1;
|
|
1204
1220
|
border: 0;
|
|
1205
1221
|
outline: 0;
|
|
1206
1222
|
background: transparent;
|
|
1207
1223
|
color: var(--rvg-color-text);
|
|
1208
1224
|
font:
|
|
1209
|
-
13px
|
|
1225
|
+
13px Inter,
|
|
1210
1226
|
ui-sans-serif,
|
|
1211
1227
|
system-ui,
|
|
1212
1228
|
sans-serif;
|
|
1229
|
+
line-height: normal;
|
|
1213
1230
|
box-sizing: border-box;
|
|
1214
1231
|
}
|
|
1215
1232
|
.rvg-native-editor span {
|
|
@@ -1448,13 +1465,14 @@
|
|
|
1448
1465
|
}
|
|
1449
1466
|
.rvg-choice-editor [data-part=trigger] {
|
|
1450
1467
|
padding: 0 28px 0 9px;
|
|
1468
|
+
line-height: 1;
|
|
1451
1469
|
}
|
|
1452
1470
|
.rvg-date-value {
|
|
1453
|
-
padding
|
|
1454
|
-
padding-bottom: 2px;
|
|
1471
|
+
padding: 0 8px 2px 9px;
|
|
1455
1472
|
}
|
|
1456
1473
|
.rvg-choice-editor [data-part=value-text] {
|
|
1457
1474
|
min-width: 0;
|
|
1475
|
+
transform: translateY(-1px);
|
|
1458
1476
|
overflow: hidden;
|
|
1459
1477
|
font: inherit;
|
|
1460
1478
|
line-height: normal;
|
package/dist/index.d.cts
CHANGED
|
@@ -116,6 +116,12 @@ interface TableHighlightConfig {
|
|
|
116
116
|
/** Highlight rows inserted optimistically through the built-in insert action. Pass a color string to customize the fill. Defaults to false. */
|
|
117
117
|
insertedRows?: boolean | string;
|
|
118
118
|
}
|
|
119
|
+
interface TableLayoutConfig {
|
|
120
|
+
/** Fixed body row height in pixels. Defaults to 36. */
|
|
121
|
+
rowHeight?: number;
|
|
122
|
+
/** Header height in pixels. Defaults to 40. */
|
|
123
|
+
headerHeight?: number;
|
|
124
|
+
}
|
|
119
125
|
interface TableCellSpan {
|
|
120
126
|
/** Row index in the current rows array. Prefer rowKey when row order can change. */
|
|
121
127
|
rowIndex?: number;
|
|
@@ -128,6 +134,16 @@ interface TableCellSpan {
|
|
|
128
134
|
/** Number of columns covered by the merged cell. Defaults to 1. */
|
|
129
135
|
colSpan?: number;
|
|
130
136
|
}
|
|
137
|
+
interface TableCellSpanRuleContext<Row extends object> {
|
|
138
|
+
row: Row;
|
|
139
|
+
rowIndex: number;
|
|
140
|
+
column: GridColumn<Row>;
|
|
141
|
+
columnIndex: number;
|
|
142
|
+
rows: Row[];
|
|
143
|
+
columns: GridColumn<Row>[];
|
|
144
|
+
}
|
|
145
|
+
type TableCellSpanRule<Row extends object> = (context: TableCellSpanRuleContext<Row>) => TableCellSpan | Omit<TableCellSpan, 'rowIndex' | 'rowKey' | 'columnKey'> | false | null | undefined;
|
|
146
|
+
type TableCellSpans<Row extends object> = TableCellSpan[] | TableCellSpanRule<Row>;
|
|
131
147
|
interface TableResolvedCellSpan {
|
|
132
148
|
rowIndex: number;
|
|
133
149
|
columnIndex: number;
|
|
@@ -149,6 +165,8 @@ interface GridColumn<Row> {
|
|
|
149
165
|
key: string;
|
|
150
166
|
/** Header label painted in the text layer. */
|
|
151
167
|
title: string;
|
|
168
|
+
/** Custom React content rendered in the header DOM overlay. */
|
|
169
|
+
renderHeader?: (column: GridColumn<Row>) => ReactNode;
|
|
152
170
|
/** Row field used as this column's cell value. Omit for utility columns. */
|
|
153
171
|
dataIndex?: keyof Row;
|
|
154
172
|
/** Preferred column width in pixels. Layout helpers enforce minimums. */
|
|
@@ -282,20 +300,26 @@ interface TableProps<Row extends object> {
|
|
|
282
300
|
width?: number | string;
|
|
283
301
|
/** Grid height. Defaults to 100%; falls back to 480px when the parent has no height. */
|
|
284
302
|
height?: number | string;
|
|
285
|
-
/** Shrink the grid to its real content height when there are fewer rows than the configured height can display. */
|
|
303
|
+
/** Shrink the grid to its real content height when there are fewer rows than the configured max height can display. Defaults to true. */
|
|
286
304
|
autoHeight?: boolean;
|
|
305
|
+
/** Layout and sizing configuration. Prefer this over rowHeight/headerHeight for new code. */
|
|
306
|
+
layout?: TableLayoutConfig;
|
|
307
|
+
/** @deprecated Use layout.rowHeight instead. */
|
|
287
308
|
rowHeight?: number;
|
|
309
|
+
/** @deprecated Use layout.headerHeight instead. */
|
|
288
310
|
headerHeight?: number;
|
|
289
311
|
fixedHeader?: boolean;
|
|
290
312
|
locale?: TableLocaleConfig;
|
|
291
|
-
/** Hide vertical grid lines while keeping horizontal row separators. */
|
|
313
|
+
/** Hide vertical grid lines while keeping horizontal row separators. Prefer borderless for new code. */
|
|
314
|
+
borderless?: boolean;
|
|
315
|
+
/** @deprecated Use borderless instead. */
|
|
292
316
|
verticalBorderless?: boolean;
|
|
293
317
|
/** Paint alternating row backgrounds. Pass a color string to customize the stripe color. */
|
|
294
318
|
striped?: boolean | string;
|
|
295
319
|
/** Visual highlight feedback configuration. All entries default to false. */
|
|
296
320
|
highlight?: TableHighlightConfig;
|
|
297
321
|
/** Merged body cell configuration. Utility columns are ignored and horizontal spans are limited to the same fixed column region. */
|
|
298
|
-
cellSpans?:
|
|
322
|
+
cellSpans?: TableCellSpans<Row>;
|
|
299
323
|
loading?: boolean;
|
|
300
324
|
/** Custom loading content. When provided, it is used for both initial loading and refresh loading. */
|
|
301
325
|
loadingContent?: ReactNode;
|
|
@@ -348,7 +372,7 @@ interface ViewportRange {
|
|
|
348
372
|
columnEnd: number;
|
|
349
373
|
}
|
|
350
374
|
|
|
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;
|
|
375
|
+
declare function Table<Row extends object>({ columns: sourceColumns, rows: sourceRows, rowKey, width, height, autoHeight, layout, rowHeight: rowHeightProp, headerHeight: headerHeightProp, fixedHeader, locale, borderless, 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;
|
|
352
376
|
|
|
353
377
|
type LayoutColumn<Row> = GridColumn<Row> & {
|
|
354
378
|
rowSelection?: boolean;
|
|
@@ -395,4 +419,4 @@ declare function hitTestColumn(metrics: ColumnMetric[], contentX: number): numbe
|
|
|
395
419
|
|
|
396
420
|
type TableFilterValues = Record<string, string>;
|
|
397
421
|
|
|
398
|
-
export { type AxisSelectionConfig, type CellChange, type ContextMenuEvent, type DeleteRowsEvent, type GridCellPosition, type GridColumn, type GridKey, type GridSelection, type GridSortState, type InsertRowsEvent, type SelectionMode, type SortDirection, Table, type CellChange as TableCellChange, type GridCellPosition as TableCellPosition, type TableCellSpan, type GridColumn as TableColumn, type TableContextMenu, type TableContextMenuConfig, type ContextMenuEvent as TableContextMenuEvent, type DeleteRowsEvent as TableDeleteRowsEvent, type GridSelection as TableEditingCell, type TableFilterValues, type TableHighlightConfig, type InsertRowsEvent as TableInsertRowsEvent, type GridKey as TableKey, type TableLabels, type TableLocaleCode, type TableLocaleConfig, type TableProps, type TableResolvedCellSpan, type GridSortState as TableSorter, type TableSummaryConfig, type TableTooltipConfig, type ViewportRange as TableViewportRange, type TableVirtualizedConfig, Table as VelocityGrid, type TableCellSpan as VelocityGridCellSpan, type TableContextMenu as VelocityGridContextMenu, type TableContextMenuConfig as VelocityGridContextMenuConfig, type TableHighlightConfig as VelocityGridHighlightConfig, type TableLabels as VelocityGridLabels, type TableLocaleCode as VelocityGridLocaleCode, type TableLocaleConfig as VelocityGridLocaleConfig, type TableProps as VelocityGridProps, type TableResolvedCellSpan as VelocityGridResolvedCellSpan, type TableSummaryConfig as VelocityGridSummaryConfig, type TableTooltipConfig as VelocityGridTooltipConfig, type TableVirtualizedConfig as VelocityGridVirtualizedConfig, type ViewportRange, buildColumnMetrics, getViewportRange, hitTestColumn };
|
|
422
|
+
export { type AxisSelectionConfig, type CellChange, type ContextMenuEvent, type DeleteRowsEvent, type GridCellPosition, type GridColumn, type GridKey, type GridSelection, type GridSortState, type InsertRowsEvent, type SelectionMode, type SortDirection, Table, type CellChange as TableCellChange, type GridCellPosition as TableCellPosition, type TableCellSpan, type TableCellSpanRule, type TableCellSpanRuleContext, type TableCellSpans, type GridColumn as TableColumn, type TableContextMenu, type TableContextMenuConfig, type ContextMenuEvent as TableContextMenuEvent, type DeleteRowsEvent as TableDeleteRowsEvent, type GridSelection as TableEditingCell, type TableFilterValues, type TableHighlightConfig, type InsertRowsEvent as TableInsertRowsEvent, type GridKey as TableKey, type TableLabels, type TableLayoutConfig, type TableLocaleCode, type TableLocaleConfig, type TableProps, type TableResolvedCellSpan, type GridSortState as TableSorter, type TableSummaryConfig, type TableTooltipConfig, type ViewportRange as TableViewportRange, type TableVirtualizedConfig, Table as VelocityGrid, type TableCellSpan as VelocityGridCellSpan, type TableCellSpanRule as VelocityGridCellSpanRule, type TableCellSpanRuleContext as VelocityGridCellSpanRuleContext, type TableCellSpans as VelocityGridCellSpans, type TableContextMenu as VelocityGridContextMenu, type TableContextMenuConfig as VelocityGridContextMenuConfig, type TableHighlightConfig as VelocityGridHighlightConfig, type TableLabels as VelocityGridLabels, type TableLayoutConfig as VelocityGridLayoutConfig, type TableLocaleCode as VelocityGridLocaleCode, type TableLocaleConfig as VelocityGridLocaleConfig, type TableProps as VelocityGridProps, type TableResolvedCellSpan as VelocityGridResolvedCellSpan, type TableSummaryConfig as VelocityGridSummaryConfig, type TableTooltipConfig as VelocityGridTooltipConfig, type TableVirtualizedConfig as VelocityGridVirtualizedConfig, type ViewportRange, buildColumnMetrics, getViewportRange, hitTestColumn };
|
package/dist/index.d.ts
CHANGED
|
@@ -116,6 +116,12 @@ interface TableHighlightConfig {
|
|
|
116
116
|
/** Highlight rows inserted optimistically through the built-in insert action. Pass a color string to customize the fill. Defaults to false. */
|
|
117
117
|
insertedRows?: boolean | string;
|
|
118
118
|
}
|
|
119
|
+
interface TableLayoutConfig {
|
|
120
|
+
/** Fixed body row height in pixels. Defaults to 36. */
|
|
121
|
+
rowHeight?: number;
|
|
122
|
+
/** Header height in pixels. Defaults to 40. */
|
|
123
|
+
headerHeight?: number;
|
|
124
|
+
}
|
|
119
125
|
interface TableCellSpan {
|
|
120
126
|
/** Row index in the current rows array. Prefer rowKey when row order can change. */
|
|
121
127
|
rowIndex?: number;
|
|
@@ -128,6 +134,16 @@ interface TableCellSpan {
|
|
|
128
134
|
/** Number of columns covered by the merged cell. Defaults to 1. */
|
|
129
135
|
colSpan?: number;
|
|
130
136
|
}
|
|
137
|
+
interface TableCellSpanRuleContext<Row extends object> {
|
|
138
|
+
row: Row;
|
|
139
|
+
rowIndex: number;
|
|
140
|
+
column: GridColumn<Row>;
|
|
141
|
+
columnIndex: number;
|
|
142
|
+
rows: Row[];
|
|
143
|
+
columns: GridColumn<Row>[];
|
|
144
|
+
}
|
|
145
|
+
type TableCellSpanRule<Row extends object> = (context: TableCellSpanRuleContext<Row>) => TableCellSpan | Omit<TableCellSpan, 'rowIndex' | 'rowKey' | 'columnKey'> | false | null | undefined;
|
|
146
|
+
type TableCellSpans<Row extends object> = TableCellSpan[] | TableCellSpanRule<Row>;
|
|
131
147
|
interface TableResolvedCellSpan {
|
|
132
148
|
rowIndex: number;
|
|
133
149
|
columnIndex: number;
|
|
@@ -149,6 +165,8 @@ interface GridColumn<Row> {
|
|
|
149
165
|
key: string;
|
|
150
166
|
/** Header label painted in the text layer. */
|
|
151
167
|
title: string;
|
|
168
|
+
/** Custom React content rendered in the header DOM overlay. */
|
|
169
|
+
renderHeader?: (column: GridColumn<Row>) => ReactNode;
|
|
152
170
|
/** Row field used as this column's cell value. Omit for utility columns. */
|
|
153
171
|
dataIndex?: keyof Row;
|
|
154
172
|
/** Preferred column width in pixels. Layout helpers enforce minimums. */
|
|
@@ -282,20 +300,26 @@ interface TableProps<Row extends object> {
|
|
|
282
300
|
width?: number | string;
|
|
283
301
|
/** Grid height. Defaults to 100%; falls back to 480px when the parent has no height. */
|
|
284
302
|
height?: number | string;
|
|
285
|
-
/** Shrink the grid to its real content height when there are fewer rows than the configured height can display. */
|
|
303
|
+
/** Shrink the grid to its real content height when there are fewer rows than the configured max height can display. Defaults to true. */
|
|
286
304
|
autoHeight?: boolean;
|
|
305
|
+
/** Layout and sizing configuration. Prefer this over rowHeight/headerHeight for new code. */
|
|
306
|
+
layout?: TableLayoutConfig;
|
|
307
|
+
/** @deprecated Use layout.rowHeight instead. */
|
|
287
308
|
rowHeight?: number;
|
|
309
|
+
/** @deprecated Use layout.headerHeight instead. */
|
|
288
310
|
headerHeight?: number;
|
|
289
311
|
fixedHeader?: boolean;
|
|
290
312
|
locale?: TableLocaleConfig;
|
|
291
|
-
/** Hide vertical grid lines while keeping horizontal row separators. */
|
|
313
|
+
/** Hide vertical grid lines while keeping horizontal row separators. Prefer borderless for new code. */
|
|
314
|
+
borderless?: boolean;
|
|
315
|
+
/** @deprecated Use borderless instead. */
|
|
292
316
|
verticalBorderless?: boolean;
|
|
293
317
|
/** Paint alternating row backgrounds. Pass a color string to customize the stripe color. */
|
|
294
318
|
striped?: boolean | string;
|
|
295
319
|
/** Visual highlight feedback configuration. All entries default to false. */
|
|
296
320
|
highlight?: TableHighlightConfig;
|
|
297
321
|
/** Merged body cell configuration. Utility columns are ignored and horizontal spans are limited to the same fixed column region. */
|
|
298
|
-
cellSpans?:
|
|
322
|
+
cellSpans?: TableCellSpans<Row>;
|
|
299
323
|
loading?: boolean;
|
|
300
324
|
/** Custom loading content. When provided, it is used for both initial loading and refresh loading. */
|
|
301
325
|
loadingContent?: ReactNode;
|
|
@@ -348,7 +372,7 @@ interface ViewportRange {
|
|
|
348
372
|
columnEnd: number;
|
|
349
373
|
}
|
|
350
374
|
|
|
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;
|
|
375
|
+
declare function Table<Row extends object>({ columns: sourceColumns, rows: sourceRows, rowKey, width, height, autoHeight, layout, rowHeight: rowHeightProp, headerHeight: headerHeightProp, fixedHeader, locale, borderless, 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;
|
|
352
376
|
|
|
353
377
|
type LayoutColumn<Row> = GridColumn<Row> & {
|
|
354
378
|
rowSelection?: boolean;
|
|
@@ -395,4 +419,4 @@ declare function hitTestColumn(metrics: ColumnMetric[], contentX: number): numbe
|
|
|
395
419
|
|
|
396
420
|
type TableFilterValues = Record<string, string>;
|
|
397
421
|
|
|
398
|
-
export { type AxisSelectionConfig, type CellChange, type ContextMenuEvent, type DeleteRowsEvent, type GridCellPosition, type GridColumn, type GridKey, type GridSelection, type GridSortState, type InsertRowsEvent, type SelectionMode, type SortDirection, Table, type CellChange as TableCellChange, type GridCellPosition as TableCellPosition, type TableCellSpan, type GridColumn as TableColumn, type TableContextMenu, type TableContextMenuConfig, type ContextMenuEvent as TableContextMenuEvent, type DeleteRowsEvent as TableDeleteRowsEvent, type GridSelection as TableEditingCell, type TableFilterValues, type TableHighlightConfig, type InsertRowsEvent as TableInsertRowsEvent, type GridKey as TableKey, type TableLabels, type TableLocaleCode, type TableLocaleConfig, type TableProps, type TableResolvedCellSpan, type GridSortState as TableSorter, type TableSummaryConfig, type TableTooltipConfig, type ViewportRange as TableViewportRange, type TableVirtualizedConfig, Table as VelocityGrid, type TableCellSpan as VelocityGridCellSpan, type TableContextMenu as VelocityGridContextMenu, type TableContextMenuConfig as VelocityGridContextMenuConfig, type TableHighlightConfig as VelocityGridHighlightConfig, type TableLabels as VelocityGridLabels, type TableLocaleCode as VelocityGridLocaleCode, type TableLocaleConfig as VelocityGridLocaleConfig, type TableProps as VelocityGridProps, type TableResolvedCellSpan as VelocityGridResolvedCellSpan, type TableSummaryConfig as VelocityGridSummaryConfig, type TableTooltipConfig as VelocityGridTooltipConfig, type TableVirtualizedConfig as VelocityGridVirtualizedConfig, type ViewportRange, buildColumnMetrics, getViewportRange, hitTestColumn };
|
|
422
|
+
export { type AxisSelectionConfig, type CellChange, type ContextMenuEvent, type DeleteRowsEvent, type GridCellPosition, type GridColumn, type GridKey, type GridSelection, type GridSortState, type InsertRowsEvent, type SelectionMode, type SortDirection, Table, type CellChange as TableCellChange, type GridCellPosition as TableCellPosition, type TableCellSpan, type TableCellSpanRule, type TableCellSpanRuleContext, type TableCellSpans, type GridColumn as TableColumn, type TableContextMenu, type TableContextMenuConfig, type ContextMenuEvent as TableContextMenuEvent, type DeleteRowsEvent as TableDeleteRowsEvent, type GridSelection as TableEditingCell, type TableFilterValues, type TableHighlightConfig, type InsertRowsEvent as TableInsertRowsEvent, type GridKey as TableKey, type TableLabels, type TableLayoutConfig, type TableLocaleCode, type TableLocaleConfig, type TableProps, type TableResolvedCellSpan, type GridSortState as TableSorter, type TableSummaryConfig, type TableTooltipConfig, type ViewportRange as TableViewportRange, type TableVirtualizedConfig, Table as VelocityGrid, type TableCellSpan as VelocityGridCellSpan, type TableCellSpanRule as VelocityGridCellSpanRule, type TableCellSpanRuleContext as VelocityGridCellSpanRuleContext, type TableCellSpans as VelocityGridCellSpans, type TableContextMenu as VelocityGridContextMenu, type TableContextMenuConfig as VelocityGridContextMenuConfig, type TableHighlightConfig as VelocityGridHighlightConfig, type TableLabels as VelocityGridLabels, type TableLayoutConfig as VelocityGridLayoutConfig, type TableLocaleCode as VelocityGridLocaleCode, type TableLocaleConfig as VelocityGridLocaleConfig, type TableProps as VelocityGridProps, type TableResolvedCellSpan as VelocityGridResolvedCellSpan, type TableSummaryConfig as VelocityGridSummaryConfig, type TableTooltipConfig as VelocityGridTooltipConfig, type TableVirtualizedConfig as VelocityGridVirtualizedConfig, type ViewportRange, buildColumnMetrics, getViewportRange, hitTestColumn };
|
package/dist/index.js
CHANGED
|
@@ -784,8 +784,9 @@ function buildColumnMetrics(columns, columnDraggableOrOptions = false) {
|
|
|
784
784
|
});
|
|
785
785
|
const viewportWidth = options.viewportWidth ?? 0;
|
|
786
786
|
if (viewportWidth <= left || columns.length === 0) return metrics;
|
|
787
|
-
const
|
|
788
|
-
const
|
|
787
|
+
const unsetWidthStretchable = columns.map((column, index) => ({ column, index })).filter(({ column }) => column.width === void 0 && column.fixed === void 0 && !column.rowSelection && !column.rowDragHandle && !column.rowNumber);
|
|
788
|
+
const dataStretchable = columns.map((column, index) => ({ column, index })).filter(({ column }) => column.fixed === void 0 && !column.rowSelection && !column.rowDragHandle && !column.rowNumber);
|
|
789
|
+
const targets = unsetWidthStretchable.length > 0 ? unsetWidthStretchable : dataStretchable.length > 0 ? dataStretchable : [];
|
|
789
790
|
if (targets.length === 0) return metrics;
|
|
790
791
|
const extra = viewportWidth - left;
|
|
791
792
|
const baseTotal = targets.reduce((sum, { index }) => sum + metrics[index].width, 0);
|
|
@@ -1039,7 +1040,7 @@ function paintGrid(options) {
|
|
|
1039
1040
|
if (!verticalBorderless) ctx.fillRect(x + metric.width - 1, headerY, 1, headerHeight);
|
|
1040
1041
|
ctx.fillRect(x, headerY + headerHeight - 1, metric.width, 1);
|
|
1041
1042
|
const column = columns[columnIndex];
|
|
1042
|
-
if (!column.rowSelection && !column.rowDragHandle && !column.rowNumber && column.title) {
|
|
1043
|
+
if (!column.rowSelection && !column.rowDragHandle && !column.rowNumber && !column.renderHeader && column.title) {
|
|
1043
1044
|
ctx.font = "600 13px Inter, ui-sans-serif, system-ui, sans-serif";
|
|
1044
1045
|
const titleWidth = ctx.measureText(column.title).width;
|
|
1045
1046
|
const visibleActions = getVisibleHeaderActions(column, metric.width, columnDraggable, titleWidth);
|
|
@@ -1298,12 +1299,14 @@ function Table({
|
|
|
1298
1299
|
rowKey,
|
|
1299
1300
|
width = "100%",
|
|
1300
1301
|
height = "100%",
|
|
1301
|
-
autoHeight =
|
|
1302
|
-
|
|
1303
|
-
|
|
1302
|
+
autoHeight = true,
|
|
1303
|
+
layout,
|
|
1304
|
+
rowHeight: rowHeightProp,
|
|
1305
|
+
headerHeight: headerHeightProp,
|
|
1304
1306
|
fixedHeader = true,
|
|
1305
1307
|
locale = "zh-CN",
|
|
1306
|
-
|
|
1308
|
+
borderless,
|
|
1309
|
+
verticalBorderless,
|
|
1307
1310
|
striped = false,
|
|
1308
1311
|
highlight,
|
|
1309
1312
|
cellSpans = [],
|
|
@@ -1345,8 +1348,13 @@ function Table({
|
|
|
1345
1348
|
style,
|
|
1346
1349
|
ariaLabel = "Data grid"
|
|
1347
1350
|
}) {
|
|
1351
|
+
const rowHeight = layout?.rowHeight ?? rowHeightProp ?? 36;
|
|
1352
|
+
const baseHeaderHeight = layout?.headerHeight ?? headerHeightProp ?? 40;
|
|
1348
1353
|
const { language, labels } = useMemo(() => resolveGridLocale(locale), [locale]);
|
|
1349
|
-
const
|
|
1354
|
+
const customHeaderContentRefs = useRef2(/* @__PURE__ */ new Map());
|
|
1355
|
+
const [measuredHeaderHeight, setMeasuredHeaderHeight] = useState4(0);
|
|
1356
|
+
const headerHeight = Math.max(baseHeaderHeight, measuredHeaderHeight);
|
|
1357
|
+
const hasVerticalBorders = !(borderless ?? verticalBorderless ?? false);
|
|
1350
1358
|
const hasStripedRows = Boolean(striped);
|
|
1351
1359
|
const stripedColor = typeof striped === "string" ? striped : void 0;
|
|
1352
1360
|
const editedCellsHighlight = highlight?.editedCells ?? false;
|
|
@@ -1410,7 +1418,7 @@ function Table({
|
|
|
1410
1418
|
const hoveredRowIndexRef = useRef2(null);
|
|
1411
1419
|
const [viewport, setViewport] = useState4({ width: 0, height: typeof height === "number" ? height : FALLBACK_HEIGHT });
|
|
1412
1420
|
const [horizontalScrollbarHeight, setHorizontalScrollbarHeight] = useState4(0);
|
|
1413
|
-
const resolvedHeight = viewport.height || FALLBACK_HEIGHT;
|
|
1421
|
+
const resolvedHeight = typeof height === "number" ? height : viewport.height || FALLBACK_HEIGHT;
|
|
1414
1422
|
const [scrollPosition, setScrollPosition] = useState4({ left: 0, top: 0 });
|
|
1415
1423
|
const scrollRef = useRef2({ left: 0, top: 0 });
|
|
1416
1424
|
const [selection, setSelection] = useControllableValue(selectedCell, defaultSelectedCell, onSelectedCellChange);
|
|
@@ -1452,6 +1460,28 @@ function Table({
|
|
|
1452
1460
|
if (headerTooltipTimerRef.current !== null) clearTimeout(headerTooltipTimerRef.current);
|
|
1453
1461
|
if (cellTooltipTimerRef.current !== null) clearTimeout(cellTooltipTimerRef.current);
|
|
1454
1462
|
}, []);
|
|
1463
|
+
const measureCustomHeaderHeight = useCallback2(() => {
|
|
1464
|
+
let nextHeight = 0;
|
|
1465
|
+
customHeaderContentRefs.current.forEach((node) => {
|
|
1466
|
+
nextHeight = Math.max(nextHeight, Math.ceil(node.scrollHeight) + 16);
|
|
1467
|
+
});
|
|
1468
|
+
setMeasuredHeaderHeight((current) => current === nextHeight ? current : nextHeight);
|
|
1469
|
+
}, []);
|
|
1470
|
+
useLayoutEffect2(() => {
|
|
1471
|
+
const activeCustomHeaderKeys = new Set(columns.filter((column) => column.renderHeader).map((column) => column.key));
|
|
1472
|
+
customHeaderContentRefs.current.forEach((_, key) => {
|
|
1473
|
+
if (!activeCustomHeaderKeys.has(key)) customHeaderContentRefs.current.delete(key);
|
|
1474
|
+
});
|
|
1475
|
+
if (customHeaderContentRefs.current.size === 0) {
|
|
1476
|
+
setMeasuredHeaderHeight(0);
|
|
1477
|
+
return;
|
|
1478
|
+
}
|
|
1479
|
+
measureCustomHeaderHeight();
|
|
1480
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
1481
|
+
const observer = new ResizeObserver(measureCustomHeaderHeight);
|
|
1482
|
+
customHeaderContentRefs.current.forEach((node) => observer.observe(node));
|
|
1483
|
+
return () => observer.disconnect();
|
|
1484
|
+
}, [columns, measureCustomHeaderHeight, viewport.width]);
|
|
1455
1485
|
const [filterEditor, setFilterEditor] = useState4(null);
|
|
1456
1486
|
const [contextMenu, setContextMenu] = useState4(null);
|
|
1457
1487
|
const [insertCounts, setInsertCounts] = useState4({ before: 1, after: 1 });
|
|
@@ -1460,6 +1490,7 @@ function Table({
|
|
|
1460
1490
|
const [insertBusy, setInsertBusy] = useState4(false);
|
|
1461
1491
|
const [deleteBusy, setDeleteBusy] = useState4(false);
|
|
1462
1492
|
const [toast, setToast] = useState4(null);
|
|
1493
|
+
const [resizeGuideX, setResizeGuideX] = useState4(null);
|
|
1463
1494
|
const [hoveredHeaderAction, setHoveredHeaderAction] = useState4(null);
|
|
1464
1495
|
const [visibleHeaderTooltip, setVisibleHeaderTooltip] = useState4(null);
|
|
1465
1496
|
const [hoveredCellTooltip, setHoveredCellTooltip] = useState4(null);
|
|
@@ -1558,14 +1589,15 @@ function Table({
|
|
|
1558
1589
|
}, [columns, metrics]);
|
|
1559
1590
|
const hasRowSelectionColumn = useMemo(() => columns.some((column) => column.rowSelection), [columns]);
|
|
1560
1591
|
const rowSelectionMode = typeof rowSelection === "object" ? rowSelection.mode ?? "multiple" : "multiple";
|
|
1561
|
-
const hasSummaryRow = useMemo(() => summaryEnabled && columns.some((column) => Boolean(column.summary)), [columns, summaryEnabled]);
|
|
1592
|
+
const hasSummaryRow = useMemo(() => rows.length > 0 && summaryEnabled && columns.some((column) => Boolean(column.summary)), [columns, rows.length, summaryEnabled]);
|
|
1562
1593
|
const topSummaryHeight = hasSummaryRow && summaryPosition === "top" ? rowHeight : 0;
|
|
1563
1594
|
const bottomSummaryHeight = hasSummaryRow && summaryPosition === "bottom" ? rowHeight : 0;
|
|
1564
1595
|
const bodyTop = headerHeight + topSummaryHeight;
|
|
1565
1596
|
const bodyContentHeight = rows.length === 0 ? EMPTY_BODY_HEIGHT : contentHeight;
|
|
1566
1597
|
const realContentHeight = bodyTop + bodyContentHeight + bottomSummaryHeight + (contentWidth > viewport.width ? horizontalScrollbarHeight : 0);
|
|
1567
1598
|
const effectiveHeight = autoHeight ? Math.max(bodyTop + bottomSummaryHeight, Math.min(resolvedHeight, realContentHeight)) : resolvedHeight;
|
|
1568
|
-
const
|
|
1599
|
+
const renderHeight = Math.max(0, effectiveHeight - bottomSummaryHeight);
|
|
1600
|
+
const bodyViewportHeight = Math.max(0, renderHeight - (fixedHeader ? headerHeight : 0) - topSummaryHeight);
|
|
1569
1601
|
const [summaryState, setSummaryState] = useState4(() => ({ columns: null, rows: null, values: /* @__PURE__ */ new Map(), pending: false }));
|
|
1570
1602
|
const summaryPending = hasSummaryRow && (loading || summaryState.pending || summaryState.columns !== columns || summaryState.rows !== rows);
|
|
1571
1603
|
const summaryValues = hasSummaryRow && summaryState.columns === columns && summaryState.rows === rows ? summaryState.values : /* @__PURE__ */ new Map();
|
|
@@ -1612,14 +1644,30 @@ function Table({
|
|
|
1612
1644
|
rowIndexByKeyRef.current.set(key, index);
|
|
1613
1645
|
return key;
|
|
1614
1646
|
}, [getDataRowKey]);
|
|
1647
|
+
const resolvedCellSpans = useMemo(() => {
|
|
1648
|
+
if (Array.isArray(cellSpans)) return cellSpans;
|
|
1649
|
+
const spans = [];
|
|
1650
|
+
rows.forEach((row, rowIndex) => {
|
|
1651
|
+
columns.forEach((column, columnIndex) => {
|
|
1652
|
+
const span = cellSpans({ row, rowIndex, column, columnIndex, rows, columns });
|
|
1653
|
+
if (!span) return;
|
|
1654
|
+
spans.push({
|
|
1655
|
+
rowIndex,
|
|
1656
|
+
columnKey: column.key,
|
|
1657
|
+
...span
|
|
1658
|
+
});
|
|
1659
|
+
});
|
|
1660
|
+
});
|
|
1661
|
+
return spans;
|
|
1662
|
+
}, [cellSpans, columns, rows]);
|
|
1615
1663
|
const cellSpanLookup = useMemo(() => {
|
|
1616
|
-
if (
|
|
1664
|
+
if (resolvedCellSpans.length === 0) return createEmptyCellSpanLookup();
|
|
1617
1665
|
const rowIndexByKey = /* @__PURE__ */ new Map();
|
|
1618
1666
|
rows.forEach((row, index) => rowIndexByKey.set(getRowKey(row, index), index));
|
|
1619
1667
|
const columnIndexByKey = new Map(columns.map((column, index) => [column.key, index]));
|
|
1620
1668
|
const lookup = createEmptyCellSpanLookup();
|
|
1621
1669
|
const occupied = /* @__PURE__ */ new Set();
|
|
1622
|
-
for (const span of
|
|
1670
|
+
for (const span of resolvedCellSpans) {
|
|
1623
1671
|
const rowIndex = span.rowKey !== void 0 ? rowIndexByKey.get(span.rowKey) : span.rowIndex;
|
|
1624
1672
|
const columnIndex = columnIndexByKey.get(span.columnKey);
|
|
1625
1673
|
if (rowIndex === void 0 || columnIndex === void 0) continue;
|
|
@@ -1655,7 +1703,7 @@ function Table({
|
|
|
1655
1703
|
}
|
|
1656
1704
|
}
|
|
1657
1705
|
return lookup;
|
|
1658
|
-
}, [
|
|
1706
|
+
}, [columns, getRowKey, resolvedCellSpans, rows]);
|
|
1659
1707
|
const selectedRowKeySet = useMemo(() => new Set(rowKeys), [rowKeys]);
|
|
1660
1708
|
const selectedColumnKeySet = useMemo(() => new Set(columnKeys), [columnKeys]);
|
|
1661
1709
|
const insertedRowKeySet = useMemo(() => insertedRowKeys, [insertedRowKeys]);
|
|
@@ -1702,7 +1750,7 @@ function Table({
|
|
|
1702
1750
|
if (!canvas || viewport.width <= 0) return;
|
|
1703
1751
|
const ratio = Math.min(window.devicePixelRatio || 1, 2);
|
|
1704
1752
|
const physicalWidth = Math.round(viewport.width * ratio);
|
|
1705
|
-
const physicalHeight = Math.round(
|
|
1753
|
+
const physicalHeight = Math.round(renderHeight * ratio);
|
|
1706
1754
|
if (canvas.width !== physicalWidth) canvas.width = physicalWidth;
|
|
1707
1755
|
if (canvas.height !== physicalHeight) canvas.height = physicalHeight;
|
|
1708
1756
|
const context = canvas.getContext("2d");
|
|
@@ -1725,8 +1773,8 @@ function Table({
|
|
|
1725
1773
|
const scroll = scrollRef.current;
|
|
1726
1774
|
const rowScrollTop = fixedHeader ? scroll.top : Math.max(0, scroll.top - bodyTop);
|
|
1727
1775
|
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 };
|
|
1728
|
-
paintGrid({ context, width: viewport.width, height:
|
|
1729
|
-
}, [bodyTop, bodyViewportHeight, bottomSummaryHeight, cellAnnotations, cellSpanLookup, columnDraggable, columns, editedCellHighlightColor, editedCellKeys, editing,
|
|
1776
|
+
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 });
|
|
1777
|
+
}, [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]);
|
|
1730
1778
|
const scheduleDraw = useCallback2(() => {
|
|
1731
1779
|
if (frameRef.current === null) frameRef.current = requestAnimationFrame(draw);
|
|
1732
1780
|
}, [draw]);
|
|
@@ -1835,14 +1883,14 @@ function Table({
|
|
|
1835
1883
|
const horizontalStart = column.fixed === void 0 ? fixedWidth : 0;
|
|
1836
1884
|
const horizontalEnd = column.fixed === void 0 ? viewport.width - rightFixedWidth : viewport.width;
|
|
1837
1885
|
const verticalStart = fixedHeader ? bodyTop : 0;
|
|
1838
|
-
if (rawRight <= horizontalStart || rawLeft >= horizontalEnd || rawBottom <= verticalStart || rawTop >=
|
|
1886
|
+
if (rawRight <= horizontalStart || rawLeft >= horizontalEnd || rawBottom <= verticalStart || rawTop >= renderHeight) {
|
|
1839
1887
|
focus.style.display = "none";
|
|
1840
1888
|
} else {
|
|
1841
1889
|
const borderWidth = 2;
|
|
1842
1890
|
const left = Math.round(Math.max(rawLeft - borderWidth, horizontalStart - 1));
|
|
1843
1891
|
const right = Math.round(Math.min(rawRight + 1, horizontalEnd, viewport.width));
|
|
1844
1892
|
const top = Math.round(Math.max(rawTop - borderWidth, verticalStart - 1));
|
|
1845
|
-
const bottom = Math.round(Math.min(rawBottom + 1,
|
|
1893
|
+
const bottom = Math.round(Math.min(rawBottom + 1, renderHeight));
|
|
1846
1894
|
focus.style.display = "block";
|
|
1847
1895
|
focus.style.left = `${left}px`;
|
|
1848
1896
|
focus.style.top = `${top}px`;
|
|
@@ -1867,7 +1915,7 @@ function Table({
|
|
|
1867
1915
|
setHoveredCellTooltip(null);
|
|
1868
1916
|
scheduleDraw();
|
|
1869
1917
|
setEditing(null);
|
|
1870
|
-
}, [bodyTop, columns,
|
|
1918
|
+
}, [bodyTop, columns, renderHeight, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, metrics, rightFixedOffsets, rightFixedWidth, rowHeight, rows, scheduleDraw, selection, selectionRange, viewport.width]);
|
|
1871
1919
|
const locateColumn = useCallback2((clientX) => {
|
|
1872
1920
|
const canvas = canvasRef.current;
|
|
1873
1921
|
if (!canvas) return -1;
|
|
@@ -1908,6 +1956,16 @@ function Table({
|
|
|
1908
1956
|
}
|
|
1909
1957
|
return closest;
|
|
1910
1958
|
}, [columnResizable, columns, getDisplayedColumnLeft, metrics]);
|
|
1959
|
+
const findResizeNeighbor = useCallback2((columnIndex) => {
|
|
1960
|
+
const resizedColumn = columns[columnIndex];
|
|
1961
|
+
for (let index = columnIndex + 1; index < columns.length; index += 1) {
|
|
1962
|
+
const column = columns[index];
|
|
1963
|
+
if (column.rowSelection || column.rowDragHandle || column.rowNumber) continue;
|
|
1964
|
+
if (resizedColumn?.fixed && column.fixed !== resizedColumn.fixed) return -1;
|
|
1965
|
+
return index;
|
|
1966
|
+
}
|
|
1967
|
+
return -1;
|
|
1968
|
+
}, [columns]);
|
|
1911
1969
|
const locateHeaderAction = useCallback2((clientX, columnIndex) => {
|
|
1912
1970
|
if (columnIndex < 0 || !canvasRef.current) return null;
|
|
1913
1971
|
const column = columns[columnIndex];
|
|
@@ -2455,13 +2513,13 @@ function Table({
|
|
|
2455
2513
|
const rowTop = (fixedHeader ? 0 : bodyTop) + rowIndex * rowHeight;
|
|
2456
2514
|
const rowBottom = rowTop + rowHeight * (span?.rowSpan ?? 1);
|
|
2457
2515
|
const cellWidth = getCellDisplayWidth(columnIndex, span?.colSpan ?? 1);
|
|
2458
|
-
const visibleRowHeight = fixedHeader ?
|
|
2516
|
+
const visibleRowHeight = fixedHeader ? renderHeight - bodyTop : renderHeight;
|
|
2459
2517
|
if (rowTop < scroller.scrollTop) scroller.scrollTop = rowTop;
|
|
2460
2518
|
if (rowBottom > scroller.scrollTop + visibleRowHeight) scroller.scrollTop = rowBottom - visibleRowHeight;
|
|
2461
2519
|
if (columns[columnIndex].fixed === void 0 && metric.left < scroller.scrollLeft + fixedWidth) scroller.scrollLeft = Math.max(0, metric.left - fixedWidth);
|
|
2462
2520
|
if (columns[columnIndex].fixed === void 0 && metric.left + cellWidth > scroller.scrollLeft + viewport.width - rightFixedWidth) scroller.scrollLeft = metric.left + cellWidth - viewport.width + rightFixedWidth;
|
|
2463
2521
|
}
|
|
2464
|
-
}, [beginEdit, bodyTop, columns, editing,
|
|
2522
|
+
}, [beginEdit, bodyTop, columns, editing, renderHeight, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, metrics, rightFixedWidth, rowHeight, rows, selection, setSelection, viewport.width]);
|
|
2465
2523
|
const editorStyle = useMemo(() => {
|
|
2466
2524
|
if (!editing) return void 0;
|
|
2467
2525
|
const metric = metrics[editing.columnIndex];
|
|
@@ -2486,26 +2544,48 @@ function Table({
|
|
|
2486
2544
|
if (resizeIndex >= 0) {
|
|
2487
2545
|
event.preventDefault();
|
|
2488
2546
|
event.currentTarget.setPointerCapture(event.pointerId);
|
|
2489
|
-
|
|
2547
|
+
const neighborIndex = findResizeNeighbor(resizeIndex);
|
|
2548
|
+
const startGuideX = getDisplayedColumnLeft(resizeIndex) + metrics[resizeIndex].width;
|
|
2549
|
+
columnDragRef.current = {
|
|
2550
|
+
type: "resize",
|
|
2551
|
+
columnIndex: resizeIndex,
|
|
2552
|
+
startX: event.clientX,
|
|
2553
|
+
startWidth: metrics[resizeIndex].width,
|
|
2554
|
+
startGuideX,
|
|
2555
|
+
neighborIndex,
|
|
2556
|
+
startNeighborWidth: neighborIndex >= 0 ? metrics[neighborIndex].width : 0
|
|
2557
|
+
};
|
|
2490
2558
|
suppressClickRef.current = true;
|
|
2491
|
-
|
|
2559
|
+
setResizeGuideX(startGuideX);
|
|
2492
2560
|
return;
|
|
2493
2561
|
}
|
|
2494
|
-
}, [fixedHeader, getDisplayedColumnLeft, headerHeight, locateResizeHandle, metrics
|
|
2562
|
+
}, [findResizeNeighbor, fixedHeader, getDisplayedColumnLeft, headerHeight, locateResizeHandle, metrics]);
|
|
2495
2563
|
const handleColumnPointerMove = useCallback2((event) => {
|
|
2496
2564
|
const drag = columnDragRef.current;
|
|
2497
2565
|
if (!drag) return;
|
|
2498
|
-
const
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2566
|
+
const column = columns[drag.columnIndex];
|
|
2567
|
+
const minWidth = getMinimumColumnWidth(column, columnDraggable);
|
|
2568
|
+
let delta = Math.round(event.clientX - drag.startX);
|
|
2569
|
+
if (drag.neighborIndex >= 0) {
|
|
2570
|
+
const neighbor = columns[drag.neighborIndex];
|
|
2571
|
+
const minNeighborWidth = getMinimumColumnWidth(neighbor, columnDraggable);
|
|
2572
|
+
delta = Math.max(minWidth - drag.startWidth, Math.min(drag.startNeighborWidth - minNeighborWidth, delta));
|
|
2573
|
+
onColumnResize?.(column.key, drag.startWidth + delta);
|
|
2574
|
+
onColumnResize?.(neighbor.key, drag.startNeighborWidth - delta);
|
|
2575
|
+
setResizeGuideX(drag.startGuideX + delta);
|
|
2576
|
+
return;
|
|
2577
|
+
}
|
|
2578
|
+
const width2 = Math.max(minWidth, Math.round(drag.startWidth + delta));
|
|
2579
|
+
onColumnResize?.(column.key, width2);
|
|
2580
|
+
setResizeGuideX(drag.startGuideX + width2 - drag.startWidth);
|
|
2581
|
+
}, [columnDraggable, columns, onColumnResize]);
|
|
2502
2582
|
const handleColumnPointerUp = useCallback2((event) => {
|
|
2503
2583
|
const drag = columnDragRef.current;
|
|
2504
2584
|
if (!drag) return;
|
|
2505
2585
|
if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
|
|
2506
2586
|
columnDragRef.current = null;
|
|
2507
|
-
|
|
2508
|
-
}, [
|
|
2587
|
+
setResizeGuideX(null);
|
|
2588
|
+
}, []);
|
|
2509
2589
|
const queueSortStateChange = useCallback2((next) => {
|
|
2510
2590
|
setDisplaySortState(next);
|
|
2511
2591
|
if (sortDebounceRef.current !== null) clearTimeout(sortDebounceRef.current);
|
|
@@ -2685,7 +2765,7 @@ function Table({
|
|
|
2685
2765
|
type: "cell",
|
|
2686
2766
|
...cell,
|
|
2687
2767
|
left: Math.min(Math.max(0, clientX - root.left), Math.max(0, viewport.width - 190)),
|
|
2688
|
-
top: Math.min(Math.max(0, clientY - root.top), Math.max(0,
|
|
2768
|
+
top: Math.min(Math.max(0, clientY - root.top), Math.max(0, renderHeight - 350))
|
|
2689
2769
|
});
|
|
2690
2770
|
onCellContextMenu?.({ ...cell, row: rows[cell.rowIndex], clientX, clientY });
|
|
2691
2771
|
};
|
|
@@ -2696,7 +2776,7 @@ function Table({
|
|
|
2696
2776
|
type: "header",
|
|
2697
2777
|
columnIndex,
|
|
2698
2778
|
left: Math.min(Math.max(0, clientX - root.left), Math.max(0, viewport.width - 180)),
|
|
2699
|
-
top: Math.min(Math.max(0, clientY - root.top), Math.max(0,
|
|
2779
|
+
top: Math.min(Math.max(0, clientY - root.top), Math.max(0, renderHeight - 68))
|
|
2700
2780
|
});
|
|
2701
2781
|
};
|
|
2702
2782
|
const getRowDragOffset = (rowIndex) => {
|
|
@@ -2912,6 +2992,35 @@ function Table({
|
|
|
2912
2992
|
column.key
|
|
2913
2993
|
);
|
|
2914
2994
|
}
|
|
2995
|
+
if (column.renderHeader) {
|
|
2996
|
+
const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex));
|
|
2997
|
+
const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * 18;
|
|
2998
|
+
return /* @__PURE__ */ jsx7(
|
|
2999
|
+
"div",
|
|
3000
|
+
{
|
|
3001
|
+
className: "rvg-header-title is-custom",
|
|
3002
|
+
style: {
|
|
3003
|
+
left,
|
|
3004
|
+
top: fixedHeader ? 0 : -currentScrollTop,
|
|
3005
|
+
width: Math.max(0, metrics[columnIndex].width - actionWidth),
|
|
3006
|
+
height: headerHeight,
|
|
3007
|
+
justifyContent: column.align === "right" ? "flex-end" : column.align === "center" ? "center" : "flex-start",
|
|
3008
|
+
textAlign: column.align === "right" ? "right" : column.align === "center" ? "center" : "left"
|
|
3009
|
+
},
|
|
3010
|
+
children: /* @__PURE__ */ jsx7(
|
|
3011
|
+
"span",
|
|
3012
|
+
{
|
|
3013
|
+
ref: (node) => {
|
|
3014
|
+
if (node) customHeaderContentRefs.current.set(column.key, node);
|
|
3015
|
+
else customHeaderContentRefs.current.delete(column.key);
|
|
3016
|
+
},
|
|
3017
|
+
children: column.renderHeader(column)
|
|
3018
|
+
}
|
|
3019
|
+
)
|
|
3020
|
+
},
|
|
3021
|
+
column.key
|
|
3022
|
+
);
|
|
3023
|
+
}
|
|
2915
3024
|
return null;
|
|
2916
3025
|
};
|
|
2917
3026
|
const rangeHandlePosition = (() => {
|
|
@@ -2926,7 +3035,7 @@ function Table({
|
|
|
2926
3035
|
const horizontalStart = column.fixed === void 0 ? fixedWidth : 0;
|
|
2927
3036
|
const horizontalEnd = column.fixed === void 0 ? viewport.width - rightFixedWidth : viewport.width;
|
|
2928
3037
|
const verticalStart = fixedHeader ? bodyTop : 0;
|
|
2929
|
-
if (rawLeft <= horizontalStart || rawLeft > horizontalEnd || rawTop <= verticalStart || rawTop >
|
|
3038
|
+
if (rawLeft <= horizontalStart || rawLeft > horizontalEnd || rawTop <= verticalStart || rawTop > renderHeight) return null;
|
|
2930
3039
|
return { left: rawLeft, top: rawTop, bounds };
|
|
2931
3040
|
})();
|
|
2932
3041
|
const selectionOutlineStyle = (() => {
|
|
@@ -2943,12 +3052,12 @@ function Table({
|
|
|
2943
3052
|
const horizontalStart = column.fixed === void 0 ? fixedWidth : 0;
|
|
2944
3053
|
const horizontalEnd = column.fixed === void 0 ? viewport.width - rightFixedWidth : viewport.width;
|
|
2945
3054
|
const verticalStart = fixedHeader ? bodyTop : 0;
|
|
2946
|
-
if (rawRight <= horizontalStart || rawLeft >= horizontalEnd || rawBottom <= verticalStart || rawTop >=
|
|
3055
|
+
if (rawRight <= horizontalStart || rawLeft >= horizontalEnd || rawBottom <= verticalStart || rawTop >= renderHeight) return null;
|
|
2947
3056
|
const borderWidth = 2;
|
|
2948
3057
|
const left = Math.round(Math.max(rawLeft - borderWidth, horizontalStart - 1));
|
|
2949
3058
|
const right = Math.round(Math.min(rawRight + 1, horizontalEnd, viewport.width));
|
|
2950
3059
|
const top = Math.round(Math.max(rawTop - borderWidth, verticalStart - 1));
|
|
2951
|
-
const bottom = Math.round(Math.min(rawBottom + 1,
|
|
3060
|
+
const bottom = Math.round(Math.min(rawBottom + 1, renderHeight));
|
|
2952
3061
|
return {
|
|
2953
3062
|
left,
|
|
2954
3063
|
top,
|
|
@@ -3063,8 +3172,10 @@ function Table({
|
|
|
3063
3172
|
const renderSummaryCell = (columnIndex, left) => {
|
|
3064
3173
|
const column = columns[columnIndex];
|
|
3065
3174
|
const isUtility = column.rowSelection || column.rowDragHandle || column.rowNumber;
|
|
3066
|
-
const
|
|
3067
|
-
const
|
|
3175
|
+
const hasSummaryValue = summaryValues.has(column.key);
|
|
3176
|
+
const summaryContent = hasSummaryValue ? summaryValues.get(column.key) : summaryEmptyValue;
|
|
3177
|
+
const showSummarySkeleton = Boolean(column.summary) && summaryPending;
|
|
3178
|
+
const content = columnIndex === 0 ? language === "zh-CN" ? "\u5408\u8BA1" : "Total" : showSummarySkeleton ? /* @__PURE__ */ jsx7("span", { className: "rvg-summary-skeleton" }) : summaryContent;
|
|
3068
3179
|
return /* @__PURE__ */ jsx7(
|
|
3069
3180
|
"div",
|
|
3070
3181
|
{
|
|
@@ -3095,7 +3206,7 @@ function Table({
|
|
|
3095
3206
|
{
|
|
3096
3207
|
ref: canvasRef,
|
|
3097
3208
|
className: "rvg-canvas",
|
|
3098
|
-
style: { width: viewport.width, height:
|
|
3209
|
+
style: { width: viewport.width, height: renderHeight },
|
|
3099
3210
|
onMouseMove: (event) => {
|
|
3100
3211
|
if (columnDragRef.current) return;
|
|
3101
3212
|
const rect = event.currentTarget.getBoundingClientRect();
|
|
@@ -3273,7 +3384,7 @@ function Table({
|
|
|
3273
3384
|
onContextMenu: handleContextMenu
|
|
3274
3385
|
}
|
|
3275
3386
|
),
|
|
3276
|
-
/* @__PURE__ */ jsxs7("div", { className: "rvg-text-layer", style: { width: viewport.width, height:
|
|
3387
|
+
/* @__PURE__ */ jsxs7("div", { className: "rvg-text-layer", style: { width: viewport.width, height: renderHeight, marginTop: -renderHeight }, onContextMenu: handleContextMenu, children: [
|
|
3277
3388
|
/* @__PURE__ */ jsx7("div", { className: "rvg-text-scroll-clip", style: { left: fixedWidth, right: rightFixedWidth }, children: /* @__PURE__ */ jsxs7(
|
|
3278
3389
|
"div",
|
|
3279
3390
|
{
|
|
@@ -3297,7 +3408,7 @@ function Table({
|
|
|
3297
3408
|
columns.map((column, columnIndex) => column.fixed === "left" ? renderHeaderTitle(columnIndex, metrics[columnIndex].left) : null),
|
|
3298
3409
|
columns.map((column, columnIndex) => column.fixed === "right" ? renderHeaderTitle(columnIndex, viewport.width - (rightFixedOffsets.get(columnIndex) ?? 0) - metrics[columnIndex].width) : null)
|
|
3299
3410
|
] }),
|
|
3300
|
-
/* @__PURE__ */ jsx7("div", { className: "rvg-spacer", style: { width: contentWidth, height: contentHeight + bodyTop, marginTop: -
|
|
3411
|
+
/* @__PURE__ */ jsx7("div", { className: "rvg-spacer", style: { width: contentWidth, height: contentHeight + bodyTop, marginTop: -renderHeight } })
|
|
3301
3412
|
] }),
|
|
3302
3413
|
hasSummaryRow && /* @__PURE__ */ jsxs7(
|
|
3303
3414
|
"div",
|
|
@@ -3613,6 +3724,13 @@ function Table({
|
|
|
3613
3724
|
] }) }),
|
|
3614
3725
|
toast && /* @__PURE__ */ jsx7("div", { className: `rvg-toast is-${toast.type}`, role: "status", children: toast.message }),
|
|
3615
3726
|
/* @__PURE__ */ jsx7("div", { ref: dragGuideRef, className: "rvg-column-guide" }),
|
|
3727
|
+
resizeGuideX !== null && /* @__PURE__ */ jsx7(
|
|
3728
|
+
"div",
|
|
3729
|
+
{
|
|
3730
|
+
className: "rvg-column-guide is-resizing",
|
|
3731
|
+
style: { display: "block", transform: `translateX(${Math.round(resizeGuideX)}px)` }
|
|
3732
|
+
}
|
|
3733
|
+
),
|
|
3616
3734
|
/* @__PURE__ */ jsx7("div", { ref: rowDragGuideRef, className: "rvg-row-guide" }),
|
|
3617
3735
|
filterEditor && /* @__PURE__ */ jsxs7("div", { ref: filterRef, className: "rvg-filter", style: { left: filterEditor.left, top: filterEditor.top }, children: [
|
|
3618
3736
|
/* @__PURE__ */ jsx7(
|
|
@@ -3661,7 +3779,7 @@ function Table({
|
|
|
3661
3779
|
rows.length === 0 && !loading && /* @__PURE__ */ jsx7("div", { className: "rvg-empty", style: { top: bodyTop }, children: emptyContent ?? /* @__PURE__ */ jsx7(DefaultEmptyState, { label: labels.empty }) }),
|
|
3662
3780
|
loading && hasCustomLoading && /* @__PURE__ */ jsx7("div", { className: "rvg-custom-loading", "aria-label": labels.refreshing, children: loadingContent }),
|
|
3663
3781
|
loading && !hasCustomLoading && !hasCompletedLoadRef.current && /* @__PURE__ */ jsxs7("div", { className: "rvg-initial-loading", style: { top: headerHeight }, "aria-label": labels.initialLoading, children: [
|
|
3664
|
-
Array.from({ length: Math.ceil((
|
|
3782
|
+
Array.from({ length: Math.ceil((renderHeight - headerHeight) / rowHeight) }, (_, rowIndex) => /* @__PURE__ */ jsx7("div", { className: "rvg-loading-row", style: { height: rowHeight }, children: columns.map((column, columnIndex) => {
|
|
3665
3783
|
const left = getDisplayedColumnLeft(columnIndex);
|
|
3666
3784
|
const columnWidth = metrics[columnIndex].width;
|
|
3667
3785
|
if (left + columnWidth <= 0 || left >= viewport.width) return null;
|