@lensui/lens-table 0.1.10 → 0.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -89,7 +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
- | `layout` | `{ rowHeight?: number; headerHeight?: number }` | `{ rowHeight: 36, headerHeight: 40 }` | 表格布局尺寸配置。推荐把行高、表头高度等样式相关参数放在这里;自定义多行表头会以 `headerHeight` 为最小高度自动撑开。 |
92
+ | `layout` | `{ rowHeight?: number; headerHeight?: number; headerActionSize?: number }` | `{ rowHeight: 36, headerHeight: 40, headerActionSize: 14 }` | 表格布局尺寸配置。`headerActionSize` 同步控制表头搜索、排序和拖拽图标的尺寸、占位及点击区域;自定义多行表头会以 `headerHeight` 为最小高度自动撑开。 |
93
93
  | `rowHeight` | `number` | `36` | 已兼容保留,建议改用 `layout.rowHeight`。 |
94
94
  | `headerHeight` | `number` | `40` | 已兼容保留,建议改用 `layout.headerHeight`。 |
95
95
  | `fixedHeader` | `boolean` | `true` | 表头是否固定在顶部。 |
@@ -97,6 +97,7 @@ export function Example() {
97
97
  | `borderless` | `boolean` | `false` | 是否弱化竖向边框。开启后隐藏单元格之间的竖线,但保留横向分隔线。 |
98
98
  | `verticalBorderless` | `boolean` | `false` | 已兼容保留,建议改用 `borderless`。 |
99
99
  | `horizontalBorderless` | `boolean` | `false` | 是否隐藏横向行分隔线及上下边框,同时保留纵向列分隔线。可与 `striped` 配合使用。 |
100
+ | `frameBorderless` | `boolean` | `false` | 是否隐藏表格四周外框,同时保留内部行列分隔线。 |
100
101
  | `striped` | `boolean \| string` | `false` | 是否显示斑马纹行背景。传`true` 使用主题变量,传颜色字符串可自定义斑马纹颜色,例如 `'#f6faff'`。 |
101
102
  | `highlight` | `{ editedCells?: boolean \| string; insertedRows?: boolean \| string }` | `{}` | 高亮反馈配置。`editedCells` 控制编辑过的单元格底色,`insertedRows` 控制乐观插入行底色;传 `true` 使用默认色,传颜色字符串可自定义。 |
102
103
  | `cellSpans` | `TableCellSpan[] \| ((ctx) => TableCellSpan \| { rowSpan?: number; colSpan?: number } \| null)` | `[]` | 合并主体单元格配置。可传数组,也可传函数按单元格生成规则。推荐使用 `rowKey` 定位;横向合并会限制在同一个固定列分区内,工具列不会参与合并。 |
@@ -579,6 +580,7 @@ const columns: GridColumn<Row>[] = [
579
580
  '--rvg-color-bg': '#0f172a',
580
581
  '--rvg-color-text': '#e5e7eb',
581
582
  '--rvg-color-grid': '#334155',
583
+ '--rvg-font-size-body': '12px',
582
584
  } as React.CSSProperties}
583
585
  />
584
586
  ```
@@ -587,6 +589,7 @@ const columns: GridColumn<Row>[] = [
587
589
 
588
590
  | 变量 | 说明 |
589
591
  | ----------------------------------- | ---------------------------------------------------------------------------------- |
592
+ | `--rvg-font-size-body` | 数据正文及单元格编辑器字号,默认 `13px`;不影响表头字号。 |
590
593
  | `--rvg-color-bg` | 表格背景色。 |
591
594
  | `--rvg-color-header-bg` | 表头背景色。 |
592
595
  | `--rvg-color-text` | 主文本颜色。 |
package/dist/index.cjs CHANGED
@@ -692,18 +692,6 @@ function DateEditor({ editor, value: draft, style, locale, labels, onChange, onC
692
692
  );
693
693
  }
694
694
 
695
- // src/core/typography.ts
696
- var CELL_FONT = "13px Inter, ui-sans-serif, system-ui, sans-serif";
697
- function getCellBaselineOffset(context) {
698
- context.save();
699
- context.textBaseline = "alphabetic";
700
- const metrics = context.measureText("Mg");
701
- context.restore();
702
- const ascent = metrics.fontBoundingBoxAscent;
703
- const descent = metrics.fontBoundingBoxDescent;
704
- return Number.isFinite(ascent) && Number.isFinite(descent) ? (ascent - descent) / 2 : 0;
705
- }
706
-
707
695
  // src/components/editors/TextEditor.tsx
708
696
  var import_jsx_runtime6 = require("react/jsx-runtime");
709
697
  function TextEditor({ value, type, style, labels, onChange, onCommit, onCancel }) {
@@ -717,7 +705,7 @@ function TextEditor({ value, type, style, labels, onChange, onCommit, onCancel }
717
705
  "input",
718
706
  {
719
707
  type,
720
- style: { font: CELL_FONT, textAlign: style.textAlign },
708
+ style: { font: "var(--rvg-font-size-body) Inter, ui-sans-serif, system-ui, sans-serif", textAlign: style.textAlign },
721
709
  value,
722
710
  autoFocus: true,
723
711
  onChange: (event) => onChange(event.target.value),
@@ -834,8 +822,8 @@ function getHeaderTitleRequiredWidth(column, titleWidth) {
834
822
  const minimumReadableTitleWidth = Math.min(Math.ceil(titleWidth), 28);
835
823
  return minimumReadableTitleWidth + (column.align === "center" ? 20 : 22);
836
824
  }
837
- function getVisibleHeaderActions(column, width, columnDraggable = false, titleWidth = 26) {
838
- const capacity = Math.max(0, Math.floor((width - getHeaderTitleRequiredWidth(column, titleWidth)) / HEADER_ACTION_SLOT_WIDTH));
825
+ function getVisibleHeaderActions(column, width, columnDraggable = false, titleWidth = 26, actionSlotWidth = HEADER_ACTION_SLOT_WIDTH) {
826
+ const capacity = Math.max(0, Math.floor((width - getHeaderTitleRequiredWidth(column, titleWidth)) / actionSlotWidth));
839
827
  const candidates = [
840
828
  isColumnFilterable(column) ? "filter" : null,
841
829
  isColumnSortable(column) ? "sort" : null,
@@ -906,6 +894,18 @@ function hitTestColumn(metrics, contentX) {
906
894
  return index < metrics.length ? index : -1;
907
895
  }
908
896
 
897
+ // src/core/typography.ts
898
+ var createCellFont = (fontSize = 13) => `${fontSize}px Inter, ui-sans-serif, system-ui, sans-serif`;
899
+ function getCellBaselineOffset(context) {
900
+ context.save();
901
+ context.textBaseline = "alphabetic";
902
+ const metrics = context.measureText("Mg");
903
+ context.restore();
904
+ const ascent = metrics.fontBoundingBoxAscent;
905
+ const descent = metrics.fontBoundingBoxDescent;
906
+ return Number.isFinite(ascent) && Number.isFinite(descent) ? (ascent - descent) / 2 : 0;
907
+ }
908
+
909
909
  // src/core/render.ts
910
910
  var getCellCoordKey = (rowIndex, columnIndex) => `${rowIndex}:${columnIndex}`;
911
911
  var COLORS = {
@@ -942,7 +942,7 @@ function ellipsizeText(context, text, maxWidth) {
942
942
  return `${text.slice(0, start)}${ellipsis}`;
943
943
  }
944
944
  function paintGrid(options) {
945
- const { context: ctx, width, height, pixelRatio, scrollLeft, scrollTop, rowHeight, headerHeight, fixedHeader, verticalBorderless, horizontalBorderless, striped, columnDraggable, sortState, filterValues, hoveredHeaderAction, rows, columns, metrics, range, selection, editing, hoveredRowIndex, selectionRange, selectedRowKeys, rowSelectionMode, selectedColumnKeys, highlightEditedCells, highlightInsertedRows, insertedRowKeys, editedCellKeys, cellAnnotations, getRowKey, rowDragPreview, columnDropTarget } = options;
945
+ const { context: ctx, width, height, pixelRatio, scrollLeft, scrollTop, rowHeight, bodyFontSize, headerActionSlotWidth, headerHeight, fixedHeader, verticalBorderless, horizontalBorderless, frameBorderless, striped, columnDraggable, sortState, filterValues, hoveredHeaderAction, rows, columns, metrics, range, selection, editing, hoveredRowIndex, selectionRange, selectedRowKeys, rowSelectionMode, selectedColumnKeys, highlightEditedCells, highlightInsertedRows, insertedRowKeys, editedCellKeys, cellAnnotations, getRowKey, rowDragPreview, columnDropTarget } = options;
946
946
  const headerLeafTop = options.headerLeafTop ?? 0;
947
947
  const headerLeafHeight = options.headerLeafHeight ?? headerHeight;
948
948
  const colors = { ...COLORS, ...options.colors };
@@ -959,7 +959,7 @@ function paintGrid(options) {
959
959
  ctx.clearRect(0, 0, width, height);
960
960
  ctx.fillStyle = colors.background;
961
961
  ctx.fillRect(0, 0, width, height);
962
- ctx.font = CELL_FONT;
962
+ ctx.font = createCellFont(bodyFontSize);
963
963
  const cellBaselineOffset = getCellBaselineOffset(ctx);
964
964
  ctx.textBaseline = "middle";
965
965
  const leftFixedWidth = columns.reduce((value, column, index) => column.fixed === "left" ? value + metrics[index].width : value, 0);
@@ -1063,7 +1063,9 @@ function paintGrid(options) {
1063
1063
  ctx.fillRect(x, y, cellWidth, cellHeight);
1064
1064
  }
1065
1065
  ctx.fillStyle = colors.grid;
1066
- if (!verticalBorderless) ctx.fillRect(x + cellWidth - 1, y, 1, cellHeight);
1066
+ if (!verticalBorderless && !(frameBorderless && columnIndex + colSpan >= columns.length)) {
1067
+ ctx.fillRect(x + cellWidth - 1, y, 1, cellHeight);
1068
+ }
1067
1069
  if (rowIndex === rowBelowDragGap) ctx.fillRect(x, y, cellWidth, 1);
1068
1070
  if (!horizontalBorderless && (!options.suppressLastRowBottomBorder || rowIndex !== rows.length - 1)) {
1069
1071
  ctx.fillRect(x, y + cellHeight - 1, cellWidth, 1);
@@ -1159,7 +1161,7 @@ function paintGrid(options) {
1159
1161
  ctx.rect(x + 2, y + 1, Math.max(0, cellWidth - 4), cellHeight - 2);
1160
1162
  ctx.clip();
1161
1163
  ctx.fillStyle = cellStyle?.color ?? colors.text;
1162
- ctx.font = CELL_FONT;
1164
+ ctx.font = createCellFont(bodyFontSize);
1163
1165
  ctx.textBaseline = "alphabetic";
1164
1166
  ctx.textAlign = column.align === "right" ? "right" : column.align === "center" ? "center" : "left";
1165
1167
  ctx.fillText(label, textX, y + cellHeight / 2 + cellBaselineOffset);
@@ -1205,14 +1207,16 @@ function paintGrid(options) {
1205
1207
  ctx.fillRect(x, headerY, metric.width, headerHeight);
1206
1208
  }
1207
1209
  ctx.fillStyle = colors.grid;
1208
- if (!verticalBorderless) ctx.fillRect(x + metric.width - 1, headerY, 1, headerHeight);
1210
+ if (!verticalBorderless && !(frameBorderless && columnIndex === columns.length - 1)) {
1211
+ ctx.fillRect(x + metric.width - 1, headerY, 1, headerHeight);
1212
+ }
1209
1213
  if (!horizontalBorderless) ctx.fillRect(x, headerY + headerHeight - 1, metric.width, 1);
1210
1214
  const column = columns[columnIndex];
1211
1215
  if (headerLeafTop === 0 && !column.rowSelection && !column.rowDragHandle && !column.rowNumber && !column.renderHeader && column.title) {
1212
1216
  ctx.font = "600 13px Inter, ui-sans-serif, system-ui, sans-serif";
1213
1217
  const titleWidth = ctx.measureText(column.title).width;
1214
- const visibleActions = getVisibleHeaderActions(column, metric.width, columnDraggable, titleWidth);
1215
- const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * HEADER_ACTION_SLOT_WIDTH;
1218
+ const visibleActions = getVisibleHeaderActions(column, metric.width, columnDraggable, titleWidth, headerActionSlotWidth);
1219
+ const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * headerActionSlotWidth;
1216
1220
  const contentWidth = Math.max(0, metric.width - actionWidth);
1217
1221
  const padding = 10;
1218
1222
  const clipWidth = contentWidth;
@@ -1242,12 +1246,12 @@ function paintGrid(options) {
1242
1246
  ctx.fillRect(rightFixedLeft - 1, 0, 1, height);
1243
1247
  }
1244
1248
  ctx.fillStyle = colors.grid;
1245
- if (!horizontalBorderless) ctx.fillRect(0, 0, width, 1);
1246
- if (!verticalBorderless) {
1249
+ if (!horizontalBorderless && !frameBorderless) ctx.fillRect(0, 0, width, 1);
1250
+ if (!verticalBorderless && !frameBorderless) {
1247
1251
  ctx.fillRect(0, 0, 1, height);
1248
1252
  ctx.fillRect(width - 1, 0, 1, height);
1249
1253
  }
1250
- if (!horizontalBorderless && !options.suppressFrameBottomBorder) ctx.fillRect(0, height - 1, width, 1);
1254
+ if (!horizontalBorderless && !frameBorderless && !options.suppressFrameBottomBorder) ctx.fillRect(0, height - 1, width, 1);
1251
1255
  if (selectionRange) {
1252
1256
  const rowStart = Math.min(selectionRange.anchor.rowIndex, selectionRange.focus.rowIndex);
1253
1257
  const rowEnd = Math.max(selectionRange.anchor.rowIndex, selectionRange.focus.rowIndex);
@@ -1468,6 +1472,10 @@ var waitForPaint = () => new Promise((resolve) => {
1468
1472
  function readThemeColor(styles, name, fallback) {
1469
1473
  return styles.getPropertyValue(name).trim() || fallback;
1470
1474
  }
1475
+ function readThemePixel(styles, name, fallback) {
1476
+ const value = Number.parseFloat(styles.getPropertyValue(name));
1477
+ return Number.isFinite(value) && value > 0 ? value : fallback;
1478
+ }
1471
1479
  function getDefaultRowKey(row, index) {
1472
1480
  const id = row.id;
1473
1481
  return typeof id === "string" || typeof id === "number" ? id : index;
@@ -1552,6 +1560,7 @@ function TableInner({
1552
1560
  borderless,
1553
1561
  verticalBorderless,
1554
1562
  horizontalBorderless = false,
1563
+ frameBorderless = false,
1555
1564
  striped = false,
1556
1565
  highlight,
1557
1566
  cellSpans = [],
@@ -1598,6 +1607,8 @@ function TableInner({
1598
1607
  setSourceColumns((current) => reconcileColumns(current, columnProps));
1599
1608
  }, [columnProps]);
1600
1609
  const rowHeight = layout?.rowHeight ?? rowHeightProp ?? 36;
1610
+ const headerActionSize = Math.max(8, layout?.headerActionSize ?? 14);
1611
+ const headerActionSlotWidth = headerActionSize + 2;
1601
1612
  const baseHeaderHeight = layout?.headerHeight ?? headerHeightProp ?? 40;
1602
1613
  const headerDepth = (0, import_react5.useMemo)(() => Math.max(1, ...sourceColumns.map(getColumnDepth)), [sourceColumns]);
1603
1614
  const { language, labels } = (0, import_react5.useMemo)(() => resolveGridLocale(locale), [locale]);
@@ -2212,6 +2223,7 @@ function TableInner({
2212
2223
  const context = canvas.getContext("2d");
2213
2224
  if (!context) return;
2214
2225
  const themeStyles = getComputedStyle(canvas);
2226
+ const bodyFontSize = readThemePixel(themeStyles, "--rvg-font-size-body", 13);
2215
2227
  const themeColors = {
2216
2228
  background: readThemeColor(themeStyles, "--rvg-color-bg", "#ffffff"),
2217
2229
  header: readThemeColor(themeStyles, "--rvg-color-header-bg", "#f5f6f7"),
@@ -2232,8 +2244,8 @@ function TableInner({
2232
2244
  const scroll = scrollRef.current;
2233
2245
  const rowScrollTop = fixedHeader ? scroll.top : Math.max(0, scroll.top - bodyTop);
2234
2246
  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 };
2235
- paintGrid({ context, width: viewport.width, height: renderHeight, pixelRatio: ratio, scrollLeft: scroll.left, scrollTop: scroll.top, rowHeight, headerHeight, headerLeafTop, headerLeafHeight, bodyTop, suppressLastRowBottomBorder: bottomSummaryHeight > 0, suppressFrameBottomBorder: bottomSummaryHeight > 0, fixedHeader, verticalBorderless: !hasVerticalBorders, horizontalBorderless, striped: hasStripedRows, columnDraggable, sortState, filterValues, hoveredHeaderAction: hoveredHeaderActionRef.current, rows, columns, metrics, range, selection, editing, hoveredRowIndex: hoveredRowIndexRef.current, selectionRange, selectedRowKeys: selectedRowKeySet, rowSelectionMode, selectedColumnKeys: selectedColumnKeySet, cellSpans: cellSpanLookup.covered, maxRowSpan: cellSpanLookup.maxRowSpan, highlightEditedCells: highlightsEditedCells, highlightInsertedRows: highlightsInsertedRows, insertedRowKeys: insertedRowKeySet, editedCellKeys, cellAnnotations, getRowKey, rowDragPreview, columnDropTarget, colors: themeColors });
2236
- }, [bodyTop, bodyViewportHeight, bottomSummaryHeight, cellAnnotations, cellSpanLookup, columnDraggable, columnDropTarget, columns, editedCellHighlightColor, editedCellKeys, editing, renderHeight, filterValues, fixedHeader, getRowKey, hasStripedRows, hasVerticalBorders, headerDepth, headerHeight, headerLeafHeight, headerLeafTop, horizontalBorderless, highlightsEditedCells, highlightsInsertedRows, insertedRowHighlightColor, insertedRowKeySet, isVirtualized, metrics, rowDragPreview, rowHeight, rowSelectionMode, rows, selectedColumnKeySet, selectedRowKeySet, selection, selectionRange, sortState, stripedColor, viewport.width, virtualOverscan]);
2247
+ paintGrid({ context, width: viewport.width, height: renderHeight, pixelRatio: ratio, scrollLeft: scroll.left, scrollTop: scroll.top, rowHeight, bodyFontSize, headerActionSlotWidth, headerHeight, headerLeafTop, headerLeafHeight, bodyTop, suppressLastRowBottomBorder: bottomSummaryHeight > 0, suppressFrameBottomBorder: bottomSummaryHeight > 0, fixedHeader, verticalBorderless: !hasVerticalBorders, horizontalBorderless, frameBorderless, striped: hasStripedRows, columnDraggable, sortState, filterValues, hoveredHeaderAction: hoveredHeaderActionRef.current, rows, columns, metrics, range, selection, editing, hoveredRowIndex: hoveredRowIndexRef.current, selectionRange, selectedRowKeys: selectedRowKeySet, rowSelectionMode, selectedColumnKeys: selectedColumnKeySet, cellSpans: cellSpanLookup.covered, maxRowSpan: cellSpanLookup.maxRowSpan, highlightEditedCells: highlightsEditedCells, highlightInsertedRows: highlightsInsertedRows, insertedRowKeys: insertedRowKeySet, editedCellKeys, cellAnnotations, getRowKey, rowDragPreview, columnDropTarget, colors: themeColors });
2248
+ }, [bodyTop, bodyViewportHeight, bottomSummaryHeight, cellAnnotations, cellSpanLookup, columnDraggable, columnDropTarget, columns, editedCellHighlightColor, editedCellKeys, editing, renderHeight, filterValues, fixedHeader, frameBorderless, getRowKey, hasStripedRows, hasVerticalBorders, headerActionSlotWidth, headerDepth, headerHeight, headerLeafHeight, headerLeafTop, horizontalBorderless, highlightsEditedCells, highlightsInsertedRows, insertedRowHighlightColor, insertedRowKeySet, isVirtualized, metrics, rowDragPreview, rowHeight, rowSelectionMode, rows, selectedColumnKeySet, selectedRowKeySet, selection, selectionRange, sortState, stripedColor, viewport.width, virtualOverscan]);
2237
2249
  const scheduleDraw = (0, import_react5.useCallback)(() => {
2238
2250
  if (frameRef.current === null) frameRef.current = requestAnimationFrame(draw);
2239
2251
  }, [draw]);
@@ -2799,34 +2811,34 @@ function TableInner({
2799
2811
  if (column.rowSelection || column.rowDragHandle || column.rowNumber) return null;
2800
2812
  const localX = clientX - canvasRef.current.getBoundingClientRect().left;
2801
2813
  const columnRight = getDisplayedColumnLeft(columnIndex) + metrics[columnIndex].width;
2802
- const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex));
2803
- let right = columnRight - (visibleActions.drag ? HEADER_ACTION_SLOT_WIDTH : 2);
2814
+ const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex), headerActionSlotWidth);
2815
+ let right = columnRight - (visibleActions.drag ? headerActionSlotWidth : 2);
2804
2816
  if (visibleActions.sort) {
2805
- if (localX >= right - HEADER_ACTION_SLOT_WIDTH && localX < right) return "sort";
2806
- right -= HEADER_ACTION_SLOT_WIDTH;
2817
+ if (localX >= right - headerActionSlotWidth && localX < right) return "sort";
2818
+ right -= headerActionSlotWidth;
2807
2819
  }
2808
- if (visibleActions.filter && localX >= right - HEADER_ACTION_SLOT_WIDTH && localX < right) return "filter";
2820
+ if (visibleActions.filter && localX >= right - headerActionSlotWidth && localX < right) return "filter";
2809
2821
  return null;
2810
- }, [columnDraggable, columns, getDisplayedColumnLeft, measureHeaderTitleWidth, metrics]);
2822
+ }, [columnDraggable, columns, getDisplayedColumnLeft, headerActionSlotWidth, measureHeaderTitleWidth, metrics]);
2811
2823
  const isHeaderTitleTruncated = (0, import_react5.useCallback)((columnIndex) => {
2812
2824
  const canvas = canvasRef.current;
2813
2825
  if (columnIndex < 0 || !canvas) return false;
2814
2826
  const column = columns[columnIndex];
2815
2827
  const metric = metrics[columnIndex];
2816
2828
  const titleWidth = measureHeaderTitleWidth(columnIndex);
2817
- const visibleActions = getVisibleHeaderActions(column, metric.width, columnDraggable, titleWidth);
2818
- const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * HEADER_ACTION_SLOT_WIDTH;
2829
+ const visibleActions = getVisibleHeaderActions(column, metric.width, columnDraggable, titleWidth, headerActionSlotWidth);
2830
+ const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * headerActionSlotWidth;
2819
2831
  return getHeaderTitleRequiredWidth(column, titleWidth) > Math.max(0, metric.width - actionWidth);
2820
- }, [columnDraggable, columns, measureHeaderTitleWidth, metrics]);
2832
+ }, [columnDraggable, columns, headerActionSlotWidth, measureHeaderTitleWidth, metrics]);
2821
2833
  const locateHeaderDragHandle = (0, import_react5.useCallback)((clientX, columnIndex) => {
2822
2834
  if (!columnDraggable || columnIndex < 0 || !canvasRef.current) return false;
2823
2835
  const column = columns[columnIndex];
2824
2836
  if (column.rowSelection || column.rowDragHandle || column.rowNumber) return false;
2825
- if (!getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex)).drag) return false;
2837
+ if (!getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex), headerActionSlotWidth).drag) return false;
2826
2838
  const localX = clientX - canvasRef.current.getBoundingClientRect().left;
2827
2839
  const columnRight = getDisplayedColumnLeft(columnIndex) + metrics[columnIndex].width;
2828
- return localX >= columnRight - 16 && localX < columnRight - 5;
2829
- }, [columnDraggable, columns, getDisplayedColumnLeft, measureHeaderTitleWidth, metrics]);
2840
+ return localX >= columnRight - headerActionSlotWidth && localX < columnRight - 2;
2841
+ }, [columnDraggable, columns, getDisplayedColumnLeft, headerActionSlotWidth, measureHeaderTitleWidth, metrics]);
2830
2842
  const getHeaderCellLeft = (0, import_react5.useCallback)((cell) => {
2831
2843
  const startMetric = metrics[cell.startIndex];
2832
2844
  const fixedSide = columns[cell.startIndex]?.fixed;
@@ -2891,8 +2903,8 @@ function TableInner({
2891
2903
  if (cell.leaf) return locateHeaderDragHandle(clientX, cell.startIndex);
2892
2904
  const localX = clientX - canvasRef.current.getBoundingClientRect().left;
2893
2905
  const right = getHeaderCellLeft(cell) + getHeaderCellWidth(cell);
2894
- return localX >= right - 16 && localX < right - 5;
2895
- }, [columnDraggable, getHeaderCellLeft, getHeaderCellWidth, locateHeaderDragHandle]);
2906
+ return localX >= right - headerActionSlotWidth && localX < right - 2;
2907
+ }, [columnDraggable, getHeaderCellLeft, getHeaderCellWidth, headerActionSlotWidth, locateHeaderDragHandle]);
2896
2908
  const locateHeaderCell = (0, import_react5.useCallback)((clientX, clientY) => {
2897
2909
  const canvas = canvasRef.current;
2898
2910
  if (!canvas) return null;
@@ -3048,26 +3060,32 @@ function TableInner({
3048
3060
  header.style.height = `${headerHeight}px`;
3049
3061
  header.style.justifyContent = column.align === "right" ? "flex-end" : column.align === "center" ? "center" : "flex-start";
3050
3062
  header.textContent = column.title;
3051
- const visibleActions = getVisibleHeaderActions(column, columnWidth, columnDraggable, measureHeaderTitleWidth(sourceIndex));
3063
+ const visibleActions = getVisibleHeaderActions(column, columnWidth, columnDraggable, measureHeaderTitleWidth(sourceIndex), headerActionSlotWidth);
3052
3064
  let actionRight = 2;
3053
3065
  if (visibleActions.drag) {
3054
3066
  const icon = createHeaderDragHandleSvg();
3055
3067
  icon.classList.add("rvg-preview-header-icon");
3068
+ icon.style.width = `${headerActionSize}px`;
3069
+ icon.style.height = `${headerActionSize}px`;
3056
3070
  icon.style.right = `${actionRight}px`;
3057
3071
  header.appendChild(icon);
3058
- actionRight += HEADER_ACTION_SLOT_WIDTH;
3072
+ actionRight += headerActionSlotWidth;
3059
3073
  }
3060
3074
  if (visibleActions.sort) {
3061
3075
  const direction = sortState?.columnKey === column.key ? sortState.direction : null;
3062
3076
  const icon = createHeaderSortSvg(direction);
3063
3077
  icon.classList.add("rvg-preview-header-icon");
3078
+ icon.style.width = `${headerActionSize}px`;
3079
+ icon.style.height = `${headerActionSize}px`;
3064
3080
  icon.style.right = `${actionRight}px`;
3065
3081
  header.appendChild(icon);
3066
- actionRight += HEADER_ACTION_SLOT_WIDTH;
3082
+ actionRight += headerActionSlotWidth;
3067
3083
  }
3068
3084
  if (visibleActions.filter) {
3069
3085
  const icon = createHeaderSearchSvg(Boolean(filterValues[column.key]));
3070
3086
  icon.classList.add("rvg-preview-header-icon", "rvg-preview-header-search-icon");
3087
+ icon.style.width = `${headerActionSize}px`;
3088
+ icon.style.height = `${headerActionSize}px`;
3071
3089
  icon.style.right = `${actionRight}px`;
3072
3090
  header.appendChild(icon);
3073
3091
  }
@@ -3264,7 +3282,7 @@ function TableInner({
3264
3282
  cleanupDrag();
3265
3283
  cleanupDrop();
3266
3284
  };
3267
- }, [applyColumnOrder, applyRowOrder, bodyTop, columnDraggable, columns, filterValues, fixedHeader, getCellLabel, getDisplayedColumnLeft, getHeaderCellWidth, getRowKey, headerCells, headerHeight, hideDragTooltips, locateColumn, locateHeaderCell, locateHeaderCellDragHandle, locateHeaderResizeCell, locateRow, measureHeaderTitleWidth, metrics, rowDraggable, rowHeight, rows, selectedRowKeySet, selection, setDragGuide, setRowDragGuide, sortState, utilityColumnCount]);
3285
+ }, [applyColumnOrder, applyRowOrder, bodyTop, columnDraggable, columns, filterValues, fixedHeader, getCellLabel, getDisplayedColumnLeft, getHeaderCellWidth, getRowKey, headerActionSize, headerActionSlotWidth, headerCells, headerHeight, hideDragTooltips, locateColumn, locateHeaderCell, locateHeaderCellDragHandle, locateHeaderResizeCell, locateRow, measureHeaderTitleWidth, metrics, rowDraggable, rowHeight, rows, selectedRowKeySet, selection, setDragGuide, setRowDragGuide, sortState, utilityColumnCount]);
3268
3286
  const locateCell = (0, import_react5.useCallback)((clientX, clientY) => {
3269
3287
  const canvas = canvasRef.current;
3270
3288
  if (!canvas) return null;
@@ -3708,28 +3726,28 @@ function TableInner({
3708
3726
  const renderHeaderIcons = (columnIndex, left) => {
3709
3727
  const column = columns[columnIndex];
3710
3728
  if (column.rowSelection || column.rowDragHandle || column.rowNumber) return null;
3711
- const top = headerLeafTop + (headerLeafHeight - 14) / 2;
3712
- const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex));
3713
- let right = left + metrics[columnIndex].width - (visibleActions.drag ? HEADER_ACTION_SLOT_WIDTH : 2);
3729
+ const top = headerLeafTop + (headerLeafHeight - headerActionSize) / 2;
3730
+ const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex), headerActionSlotWidth);
3731
+ let right = left + metrics[columnIndex].width - (visibleActions.drag ? headerActionSlotWidth : 2);
3714
3732
  const icons = [];
3715
3733
  if (visibleActions.drag) {
3716
3734
  const hovered = hoveredHeaderAction?.columnIndex === columnIndex && hoveredHeaderAction.action === "drag";
3717
3735
  icons.push(
3718
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(HeaderDragIcon, { className: `rvg-header-icon rvg-header-drag-icon${hovered ? " is-hovered" : ""}`, style: { left: left + metrics[columnIndex].width - 16, top } }, "drag")
3736
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(HeaderDragIcon, { className: `rvg-header-icon rvg-header-drag-icon${hovered ? " is-hovered" : ""}`, style: { left: left + metrics[columnIndex].width - headerActionSlotWidth, top } }, "drag")
3719
3737
  );
3720
3738
  }
3721
3739
  if (visibleActions.sort) {
3722
3740
  const hovered = hoveredHeaderAction?.columnIndex === columnIndex && hoveredHeaderAction.action === "sort";
3723
3741
  const direction = sortState?.columnKey === column.key ? sortState.direction : null;
3724
3742
  icons.push(
3725
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(HeaderSortIcon, { className: `rvg-header-icon${hovered ? " is-hovered" : ""}`, style: { left: right - 16, top }, direction }, "sort")
3743
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(HeaderSortIcon, { className: `rvg-header-icon${hovered ? " is-hovered" : ""}`, style: { left: right - headerActionSlotWidth, top }, direction }, "sort")
3726
3744
  );
3727
- right -= HEADER_ACTION_SLOT_WIDTH;
3745
+ right -= headerActionSlotWidth;
3728
3746
  }
3729
3747
  if (visibleActions.filter) {
3730
3748
  const hovered = hoveredHeaderAction?.columnIndex === columnIndex && hoveredHeaderAction.action === "filter";
3731
3749
  icons.push(
3732
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(HeaderSearchIcon, { className: `rvg-header-icon rvg-header-search-icon${hovered ? " is-hovered" : ""}${filterValues[column.key] ? " is-active" : ""}`, style: { left: right - 16, top: top + 1 } }, "filter")
3750
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(HeaderSearchIcon, { className: `rvg-header-icon rvg-header-search-icon${hovered ? " is-hovered" : ""}${filterValues[column.key] ? " is-active" : ""}`, style: { left: right - headerActionSlotWidth, top: top + 1 } }, "filter")
3733
3751
  );
3734
3752
  }
3735
3753
  return icons;
@@ -3750,8 +3768,8 @@ function TableInner({
3750
3768
  const context = canvas?.getContext("2d");
3751
3769
  if (context) context.font = "600 13px Inter, ui-sans-serif, system-ui, sans-serif";
3752
3770
  const titleWidth = context?.measureText(column.title).width ?? column.title.length * 13;
3753
- const visibleActions = cell?.leaf || !cell ? getVisibleHeaderActions(column, cellWidth, columnDraggable, titleWidth) : null;
3754
- const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * HEADER_ACTION_SLOT_WIDTH : columnDraggable ? HEADER_ACTION_SLOT_WIDTH : 0;
3771
+ const visibleActions = cell?.leaf || !cell ? getVisibleHeaderActions(column, cellWidth, columnDraggable, titleWidth, headerActionSlotWidth) : null;
3772
+ const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * headerActionSlotWidth : columnDraggable ? headerActionSlotWidth : 0;
3755
3773
  const contentWidth2 = Math.max(0, cellWidth - actionWidth);
3756
3774
  const maxTextWidth = Math.max(0, contentWidth2 - 20);
3757
3775
  const textWidth = Math.min(titleWidth, maxTextWidth);
@@ -3772,12 +3790,17 @@ function TableInner({
3772
3790
  } else if (action === "sort") {
3773
3791
  const direction = sortState?.columnKey === column.key ? sortState.direction : null;
3774
3792
  label = direction === "asc" ? labels.sortAsc : direction === "desc" ? labels.sortDesc : labels.sortBoth;
3775
- anchorX = cellLeft + cellWidth - 22;
3793
+ const visibleActions = getVisibleHeaderActions(column, cellWidth, columnDraggable, measureHeaderTitleWidth(columnIndex), headerActionSlotWidth);
3794
+ const right = cellLeft + cellWidth - (visibleActions.drag ? headerActionSlotWidth : 2);
3795
+ anchorX = right - headerActionSlotWidth / 2;
3776
3796
  anchorLeft = anchorX;
3777
3797
  } else if (action === "filter") {
3778
3798
  const value = filterValues[column.key];
3779
3799
  label = value ? labels.filterWithValue(value) : labels.filter;
3780
- anchorX = cellLeft + cellWidth - 40;
3800
+ const visibleActions = getVisibleHeaderActions(column, cellWidth, columnDraggable, measureHeaderTitleWidth(columnIndex), headerActionSlotWidth);
3801
+ let right = cellLeft + cellWidth - (visibleActions.drag ? headerActionSlotWidth : 2);
3802
+ if (visibleActions.sort) right -= headerActionSlotWidth;
3803
+ anchorX = right - headerActionSlotWidth / 2;
3781
3804
  anchorLeft = anchorX;
3782
3805
  }
3783
3806
  const placement = anchorX + 328 > viewport.width ? "left" : "right";
@@ -3790,7 +3813,7 @@ function TableInner({
3790
3813
  label,
3791
3814
  placement
3792
3815
  };
3793
- }, [baseHeaderHeight, columnDraggable, columns, filterValues, fixedHeader, getDisplayedColumnLeft, getHeaderCellLeft, getHeaderCellWidth, headerDepth, headerRowHeights, headerRowOffsets, labels, metrics, scrollPosition.top, sortState, viewport.width]);
3816
+ }, [baseHeaderHeight, columnDraggable, columns, filterValues, fixedHeader, getDisplayedColumnLeft, getHeaderCellLeft, getHeaderCellWidth, headerActionSlotWidth, headerDepth, headerRowHeights, headerRowOffsets, labels, measureHeaderTitleWidth, metrics, scrollPosition.top, sortState, viewport.width]);
3794
3817
  const isPointerOnHeaderTitle = (0, import_react5.useCallback)((clientX, clientY, cell) => {
3795
3818
  const canvas = canvasRef.current;
3796
3819
  if (!canvas) return false;
@@ -3804,14 +3827,14 @@ function TableInner({
3804
3827
  const context = canvas.getContext("2d");
3805
3828
  if (context) context.font = "600 13px Inter, ui-sans-serif, system-ui, sans-serif";
3806
3829
  const titleWidth = context?.measureText(column.title).width ?? column.title.length * 13;
3807
- const visibleActions = cell.leaf ? getVisibleHeaderActions(column, cellWidth, columnDraggable, titleWidth) : null;
3808
- const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * HEADER_ACTION_SLOT_WIDTH : columnDraggable ? HEADER_ACTION_SLOT_WIDTH : 0;
3830
+ const visibleActions = cell.leaf ? getVisibleHeaderActions(column, cellWidth, columnDraggable, titleWidth, headerActionSlotWidth) : null;
3831
+ const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * headerActionSlotWidth : columnDraggable ? headerActionSlotWidth : 0;
3809
3832
  const contentWidth2 = Math.max(0, cellWidth - actionWidth);
3810
3833
  const textWidth = Math.min(titleWidth, Math.max(0, contentWidth2 - 20));
3811
3834
  const textLeft = column.align === "right" ? cellLeft + contentWidth2 - 10 - textWidth : column.align === "center" ? cellLeft + contentWidth2 / 2 - textWidth / 2 : cellLeft + 10;
3812
3835
  const textTop = rowTop + 8;
3813
3836
  return localX >= textLeft - 4 && localX <= textLeft + textWidth + 4 && localY >= textTop - 2 && localY <= textTop + 18;
3814
- }, [columnDraggable, fixedHeader, getHeaderCellLeft, getHeaderCellWidth, headerRowOffsets]);
3837
+ }, [columnDraggable, fixedHeader, getHeaderCellLeft, getHeaderCellWidth, headerActionSlotWidth, headerRowOffsets]);
3815
3838
  const headerTooltip = (() => {
3816
3839
  if (!visibleHeaderTooltip) return null;
3817
3840
  return visibleHeaderTooltip;
@@ -4134,8 +4157,8 @@ function TableInner({
4134
4157
  );
4135
4158
  }
4136
4159
  if (column.renderHeader) {
4137
- const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex));
4138
- const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * HEADER_ACTION_SLOT_WIDTH;
4160
+ const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex), headerActionSlotWidth);
4161
+ const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * headerActionSlotWidth;
4139
4162
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4140
4163
  "div",
4141
4164
  {
@@ -4179,13 +4202,13 @@ function TableInner({
4179
4202
  const headerTop = (fixedHeader ? 0 : -currentScrollTop) + (headerRowOffsets[cell.level] ?? 0);
4180
4203
  const left = fixedSide === "left" ? leftFixedOffsets.get(cell.startIndex) ?? 0 : fixedSide === "right" ? viewport.width - (rightFixedOffsets.get(cell.startIndex) ?? 0) - startMetric.width : getScrollableColumnLeft(cell.startIndex);
4181
4204
  const width2 = cell.leaf ? startMetric.width : getCellDisplayWidth(cell.startIndex, cell.endIndex - cell.startIndex + 1);
4182
- const visibleActions = cell.leaf ? getVisibleHeaderActions(column, startMetric.width, columnDraggable, measureHeaderTitleWidth(cell.startIndex)) : null;
4183
- const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * HEADER_ACTION_SLOT_WIDTH : columnDraggable && !cell.leaf ? HEADER_ACTION_SLOT_WIDTH : 0;
4205
+ const visibleActions = cell.leaf ? getVisibleHeaderActions(column, startMetric.width, columnDraggable, measureHeaderTitleWidth(cell.startIndex), headerActionSlotWidth) : null;
4206
+ const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * headerActionSlotWidth : columnDraggable && !cell.leaf ? headerActionSlotWidth : 0;
4184
4207
  const isDropTarget = Boolean(columnDropTarget && cell.endIndex >= columnDropTarget.startIndex && cell.startIndex <= columnDropTarget.endIndex);
4185
4208
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
4186
4209
  "div",
4187
4210
  {
4188
- className: `rvg-header-title rvg-header-group${column.renderHeader ? " is-custom" : ""}${cell.leaf ? " is-leaf" : ""}${isDropTarget ? " is-drop-target" : ""}`,
4211
+ className: `rvg-header-title rvg-header-group${column.renderHeader ? " is-custom" : ""}${cell.leaf ? " is-leaf" : ""}${cell.endIndex === columns.length - 1 ? " is-frame-right" : ""}${isDropTarget ? " is-drop-target" : ""}`,
4189
4212
  "data-level": cell.level,
4190
4213
  "data-measure-key": `group:${cell.key}:${cell.level}`,
4191
4214
  style: {
@@ -4216,7 +4239,7 @@ function TableInner({
4216
4239
  HeaderDragIcon,
4217
4240
  {
4218
4241
  className: `rvg-header-icon rvg-header-drag-icon${hoveredHeaderAction?.columnIndex === cell.startIndex && hoveredHeaderAction.action === "drag" ? " is-hovered" : ""}`,
4219
- style: { left: width2 - 16, top: (rowHeightForLevel - 14) / 2 }
4242
+ style: { left: width2 - headerActionSlotWidth, top: (rowHeightForLevel - headerActionSize) / 2 }
4220
4243
  }
4221
4244
  )
4222
4245
  ]
@@ -4482,12 +4505,13 @@ function TableInner({
4482
4505
  width,
4483
4506
  height: autoHeight ? effectiveHeight : height,
4484
4507
  minHeight: autoHeight || typeof height === "number" ? void 0 : FALLBACK_HEIGHT,
4508
+ "--rvg-header-action-size": `${headerActionSize}px`,
4485
4509
  ...stripedColor ? { "--rvg-color-stripe": stripedColor } : null,
4486
4510
  ...style
4487
4511
  };
4488
4512
  const currentScroll = scrollRef.current;
4489
4513
  const scrollerBottom = Math.max(0, bottomSummaryHeight - horizontalScrollbarHeight);
4490
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: `rvg-root${!hasVerticalBorders ? " is-vertical-borderless" : ""}${horizontalBorderless ? " is-horizontal-borderless" : ""}${hasStripedRows ? " is-striped" : ""}${insertBusy ? " is-inserting" : ""}${deleteBusy ? " is-deleting" : ""} ${loading && !hasCompletedLoadRef.current && !hasCustomLoading ? "is-initial-loading" : ""} ${className}`, style: rootStyle, children: [
4514
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: `rvg-root${!hasVerticalBorders ? " is-vertical-borderless" : ""}${horizontalBorderless ? " is-horizontal-borderless" : ""}${frameBorderless ? " is-frame-borderless" : ""}${hasStripedRows ? " is-striped" : ""}${insertBusy ? " is-inserting" : ""}${deleteBusy ? " is-deleting" : ""} ${loading && !hasCompletedLoadRef.current && !hasCustomLoading ? "is-initial-loading" : ""} ${className}`, style: rootStyle, children: [
4491
4515
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { ref: scrollerRef, className: `rvg-scroller${contextMenu ? " is-context-menu-open" : ""}`, style: { bottom: scrollerBottom }, onScroll: handleScroll, onCopy: handleCopy, onContextMenu: handleContextMenu, tabIndex: 0, role: "grid", "aria-label": ariaLabel, "aria-rowcount": rows.length, "aria-colcount": columns.length, onKeyDown: handleKeyDown, children: [
4492
4516
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4493
4517
  "canvas",
@@ -4547,7 +4571,10 @@ function TableInner({
4547
4571
  } else {
4548
4572
  const label = getCellLabel(cell.rowIndex, cell.columnIndex);
4549
4573
  const context = event.currentTarget.getContext("2d");
4550
- if (context) context.font = "13px Inter, ui-sans-serif, system-ui, sans-serif";
4574
+ if (context) {
4575
+ const bodyFontSize = readThemePixel(getComputedStyle(event.currentTarget), "--rvg-font-size-body", 13);
4576
+ context.font = `${bodyFontSize}px Inter, ui-sans-serif, system-ui, sans-serif`;
4577
+ }
4551
4578
  const truncated = Boolean(label && context && context.measureText(label).width + 20 > metrics[cell.columnIndex].width);
4552
4579
  if (!label || !tooltipConfig.cell && !truncated) {
4553
4580
  showCellTooltipAfterDelay(null);
package/dist/index.css CHANGED
@@ -1,5 +1,6 @@
1
1
  /* src/style.css */
2
2
  :root {
3
+ --rvg-font-size-body: 13px;
3
4
  --rvg-color-bg: #fff;
4
5
  --rvg-color-header-bg: #f5f6f7;
5
6
  --rvg-color-grid: #e3e6e8;
@@ -205,11 +206,12 @@
205
206
  padding: 0 10px;
206
207
  overflow: hidden;
207
208
  color: var(--rvg-color-text);
208
- font:
209
- 13px Inter,
209
+ font-family:
210
+ Inter,
210
211
  ui-sans-serif,
211
212
  system-ui,
212
213
  sans-serif;
214
+ font-size: 13px;
213
215
  white-space: nowrap;
214
216
  user-select: text;
215
217
  pointer-events: auto;
@@ -220,6 +222,7 @@
220
222
  cursor: default;
221
223
  }
222
224
  .rvg-cell-text {
225
+ font-size: var(--rvg-font-size-body);
223
226
  transition: transform 140ms ease, opacity 100ms ease;
224
227
  }
225
228
  .rvg-row-drop-settling .rvg-cell-text {
@@ -251,11 +254,12 @@
251
254
  align-items: center;
252
255
  overflow: hidden;
253
256
  color: var(--rvg-color-text);
254
- font:
255
- 13px Inter,
257
+ font-family:
258
+ Inter,
256
259
  ui-sans-serif,
257
260
  system-ui,
258
261
  sans-serif;
262
+ font-size: var(--rvg-font-size-body);
259
263
  white-space: nowrap;
260
264
  pointer-events: none;
261
265
  box-sizing: border-box;
@@ -753,6 +757,9 @@
753
757
  background: var(--rvg-color-header-bg);
754
758
  box-sizing: border-box;
755
759
  }
760
+ .rvg-root.is-frame-borderless .rvg-header-group.is-frame-right {
761
+ border-right: 0;
762
+ }
756
763
  .rvg-root.is-horizontal-borderless .rvg-header-group {
757
764
  border-bottom: 0;
758
765
  }
@@ -813,6 +820,9 @@
813
820
  content: "";
814
821
  pointer-events: none;
815
822
  }
823
+ .rvg-root.is-frame-borderless .rvg-header-icons::after {
824
+ display: none;
825
+ }
816
826
  .rvg-root.is-vertical-borderless .rvg-header-icons::after {
817
827
  display: none;
818
828
  }
@@ -832,8 +842,8 @@
832
842
  }
833
843
  .rvg-header-icon {
834
844
  position: absolute;
835
- width: 14px;
836
- height: 14px;
845
+ width: var(--rvg-header-action-size, 14px);
846
+ height: var(--rvg-header-action-size, 14px);
837
847
  overflow: visible;
838
848
  color: var(--rvg-color-icon);
839
849
  }
@@ -1587,7 +1597,7 @@
1587
1597
  background: var(--rvg-color-bg);
1588
1598
  color: var(--rvg-color-text);
1589
1599
  font:
1590
- 13px Inter,
1600
+ var(--rvg-font-size-body) Inter,
1591
1601
  ui-sans-serif,
1592
1602
  system-ui,
1593
1603
  sans-serif;
@@ -1635,7 +1645,7 @@
1635
1645
  background: transparent;
1636
1646
  color: var(--rvg-color-text);
1637
1647
  font:
1638
- 13px Inter,
1648
+ var(--rvg-font-size-body) Inter,
1639
1649
  ui-sans-serif,
1640
1650
  system-ui,
1641
1651
  sans-serif;
@@ -1670,7 +1680,7 @@
1670
1680
  background: transparent;
1671
1681
  color: var(--rvg-color-text);
1672
1682
  font:
1673
- 13px Inter,
1683
+ var(--rvg-font-size-body) Inter,
1674
1684
  ui-sans-serif,
1675
1685
  system-ui,
1676
1686
  sans-serif;
@@ -1903,7 +1913,7 @@
1903
1913
  background: var(--rvg-color-bg);
1904
1914
  color: var(--rvg-color-text);
1905
1915
  font:
1906
- 13px Inter,
1916
+ var(--rvg-font-size-body) Inter,
1907
1917
  ui-sans-serif,
1908
1918
  system-ui,
1909
1919
  sans-serif;
package/dist/index.d.cts CHANGED
@@ -121,6 +121,8 @@ interface TableLayoutConfig {
121
121
  rowHeight?: number;
122
122
  /** Header height in pixels. Defaults to 40. */
123
123
  headerHeight?: number;
124
+ /** Header action icon size in pixels. Defaults to 14. */
125
+ headerActionSize?: number;
124
126
  }
125
127
  interface TableCellSpan {
126
128
  /** Row index in the current rows array. Prefer rowKey when row order can change. */
@@ -349,6 +351,8 @@ interface TableProps<Row extends object> {
349
351
  verticalBorderless?: boolean;
350
352
  /** Hide horizontal row and frame separators while keeping vertical grid lines. */
351
353
  horizontalBorderless?: boolean;
354
+ /** Hide the outer table frame while keeping internal row and column separators. */
355
+ frameBorderless?: boolean;
352
356
  /** Paint alternating row backgrounds. Pass a color string to customize the stripe color. */
353
357
  striped?: boolean | string;
354
358
  /** Visual highlight feedback configuration. All entries default to false. */
package/dist/index.d.ts CHANGED
@@ -121,6 +121,8 @@ interface TableLayoutConfig {
121
121
  rowHeight?: number;
122
122
  /** Header height in pixels. Defaults to 40. */
123
123
  headerHeight?: number;
124
+ /** Header action icon size in pixels. Defaults to 14. */
125
+ headerActionSize?: number;
124
126
  }
125
127
  interface TableCellSpan {
126
128
  /** Row index in the current rows array. Prefer rowKey when row order can change. */
@@ -349,6 +351,8 @@ interface TableProps<Row extends object> {
349
351
  verticalBorderless?: boolean;
350
352
  /** Hide horizontal row and frame separators while keeping vertical grid lines. */
351
353
  horizontalBorderless?: boolean;
354
+ /** Hide the outer table frame while keeping internal row and column separators. */
355
+ frameBorderless?: boolean;
352
356
  /** Paint alternating row backgrounds. Pass a color string to customize the stripe color. */
353
357
  striped?: boolean | string;
354
358
  /** Visual highlight feedback configuration. All entries default to false. */
package/dist/index.js CHANGED
@@ -662,18 +662,6 @@ function DateEditor({ editor, value: draft, style, locale, labels, onChange, onC
662
662
  );
663
663
  }
664
664
 
665
- // src/core/typography.ts
666
- var CELL_FONT = "13px Inter, ui-sans-serif, system-ui, sans-serif";
667
- function getCellBaselineOffset(context) {
668
- context.save();
669
- context.textBaseline = "alphabetic";
670
- const metrics = context.measureText("Mg");
671
- context.restore();
672
- const ascent = metrics.fontBoundingBoxAscent;
673
- const descent = metrics.fontBoundingBoxDescent;
674
- return Number.isFinite(ascent) && Number.isFinite(descent) ? (ascent - descent) / 2 : 0;
675
- }
676
-
677
665
  // src/components/editors/TextEditor.tsx
678
666
  import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
679
667
  function TextEditor({ value, type, style, labels, onChange, onCommit, onCancel }) {
@@ -687,7 +675,7 @@ function TextEditor({ value, type, style, labels, onChange, onCommit, onCancel }
687
675
  "input",
688
676
  {
689
677
  type,
690
- style: { font: CELL_FONT, textAlign: style.textAlign },
678
+ style: { font: "var(--rvg-font-size-body) Inter, ui-sans-serif, system-ui, sans-serif", textAlign: style.textAlign },
691
679
  value,
692
680
  autoFocus: true,
693
681
  onChange: (event) => onChange(event.target.value),
@@ -804,8 +792,8 @@ function getHeaderTitleRequiredWidth(column, titleWidth) {
804
792
  const minimumReadableTitleWidth = Math.min(Math.ceil(titleWidth), 28);
805
793
  return minimumReadableTitleWidth + (column.align === "center" ? 20 : 22);
806
794
  }
807
- function getVisibleHeaderActions(column, width, columnDraggable = false, titleWidth = 26) {
808
- const capacity = Math.max(0, Math.floor((width - getHeaderTitleRequiredWidth(column, titleWidth)) / HEADER_ACTION_SLOT_WIDTH));
795
+ function getVisibleHeaderActions(column, width, columnDraggable = false, titleWidth = 26, actionSlotWidth = HEADER_ACTION_SLOT_WIDTH) {
796
+ const capacity = Math.max(0, Math.floor((width - getHeaderTitleRequiredWidth(column, titleWidth)) / actionSlotWidth));
809
797
  const candidates = [
810
798
  isColumnFilterable(column) ? "filter" : null,
811
799
  isColumnSortable(column) ? "sort" : null,
@@ -876,6 +864,18 @@ function hitTestColumn(metrics, contentX) {
876
864
  return index < metrics.length ? index : -1;
877
865
  }
878
866
 
867
+ // src/core/typography.ts
868
+ var createCellFont = (fontSize = 13) => `${fontSize}px Inter, ui-sans-serif, system-ui, sans-serif`;
869
+ function getCellBaselineOffset(context) {
870
+ context.save();
871
+ context.textBaseline = "alphabetic";
872
+ const metrics = context.measureText("Mg");
873
+ context.restore();
874
+ const ascent = metrics.fontBoundingBoxAscent;
875
+ const descent = metrics.fontBoundingBoxDescent;
876
+ return Number.isFinite(ascent) && Number.isFinite(descent) ? (ascent - descent) / 2 : 0;
877
+ }
878
+
879
879
  // src/core/render.ts
880
880
  var getCellCoordKey = (rowIndex, columnIndex) => `${rowIndex}:${columnIndex}`;
881
881
  var COLORS = {
@@ -912,7 +912,7 @@ function ellipsizeText(context, text, maxWidth) {
912
912
  return `${text.slice(0, start)}${ellipsis}`;
913
913
  }
914
914
  function paintGrid(options) {
915
- const { context: ctx, width, height, pixelRatio, scrollLeft, scrollTop, rowHeight, headerHeight, fixedHeader, verticalBorderless, horizontalBorderless, striped, columnDraggable, sortState, filterValues, hoveredHeaderAction, rows, columns, metrics, range, selection, editing, hoveredRowIndex, selectionRange, selectedRowKeys, rowSelectionMode, selectedColumnKeys, highlightEditedCells, highlightInsertedRows, insertedRowKeys, editedCellKeys, cellAnnotations, getRowKey, rowDragPreview, columnDropTarget } = options;
915
+ const { context: ctx, width, height, pixelRatio, scrollLeft, scrollTop, rowHeight, bodyFontSize, headerActionSlotWidth, headerHeight, fixedHeader, verticalBorderless, horizontalBorderless, frameBorderless, striped, columnDraggable, sortState, filterValues, hoveredHeaderAction, rows, columns, metrics, range, selection, editing, hoveredRowIndex, selectionRange, selectedRowKeys, rowSelectionMode, selectedColumnKeys, highlightEditedCells, highlightInsertedRows, insertedRowKeys, editedCellKeys, cellAnnotations, getRowKey, rowDragPreview, columnDropTarget } = options;
916
916
  const headerLeafTop = options.headerLeafTop ?? 0;
917
917
  const headerLeafHeight = options.headerLeafHeight ?? headerHeight;
918
918
  const colors = { ...COLORS, ...options.colors };
@@ -929,7 +929,7 @@ function paintGrid(options) {
929
929
  ctx.clearRect(0, 0, width, height);
930
930
  ctx.fillStyle = colors.background;
931
931
  ctx.fillRect(0, 0, width, height);
932
- ctx.font = CELL_FONT;
932
+ ctx.font = createCellFont(bodyFontSize);
933
933
  const cellBaselineOffset = getCellBaselineOffset(ctx);
934
934
  ctx.textBaseline = "middle";
935
935
  const leftFixedWidth = columns.reduce((value, column, index) => column.fixed === "left" ? value + metrics[index].width : value, 0);
@@ -1033,7 +1033,9 @@ function paintGrid(options) {
1033
1033
  ctx.fillRect(x, y, cellWidth, cellHeight);
1034
1034
  }
1035
1035
  ctx.fillStyle = colors.grid;
1036
- if (!verticalBorderless) ctx.fillRect(x + cellWidth - 1, y, 1, cellHeight);
1036
+ if (!verticalBorderless && !(frameBorderless && columnIndex + colSpan >= columns.length)) {
1037
+ ctx.fillRect(x + cellWidth - 1, y, 1, cellHeight);
1038
+ }
1037
1039
  if (rowIndex === rowBelowDragGap) ctx.fillRect(x, y, cellWidth, 1);
1038
1040
  if (!horizontalBorderless && (!options.suppressLastRowBottomBorder || rowIndex !== rows.length - 1)) {
1039
1041
  ctx.fillRect(x, y + cellHeight - 1, cellWidth, 1);
@@ -1129,7 +1131,7 @@ function paintGrid(options) {
1129
1131
  ctx.rect(x + 2, y + 1, Math.max(0, cellWidth - 4), cellHeight - 2);
1130
1132
  ctx.clip();
1131
1133
  ctx.fillStyle = cellStyle?.color ?? colors.text;
1132
- ctx.font = CELL_FONT;
1134
+ ctx.font = createCellFont(bodyFontSize);
1133
1135
  ctx.textBaseline = "alphabetic";
1134
1136
  ctx.textAlign = column.align === "right" ? "right" : column.align === "center" ? "center" : "left";
1135
1137
  ctx.fillText(label, textX, y + cellHeight / 2 + cellBaselineOffset);
@@ -1175,14 +1177,16 @@ function paintGrid(options) {
1175
1177
  ctx.fillRect(x, headerY, metric.width, headerHeight);
1176
1178
  }
1177
1179
  ctx.fillStyle = colors.grid;
1178
- if (!verticalBorderless) ctx.fillRect(x + metric.width - 1, headerY, 1, headerHeight);
1180
+ if (!verticalBorderless && !(frameBorderless && columnIndex === columns.length - 1)) {
1181
+ ctx.fillRect(x + metric.width - 1, headerY, 1, headerHeight);
1182
+ }
1179
1183
  if (!horizontalBorderless) ctx.fillRect(x, headerY + headerHeight - 1, metric.width, 1);
1180
1184
  const column = columns[columnIndex];
1181
1185
  if (headerLeafTop === 0 && !column.rowSelection && !column.rowDragHandle && !column.rowNumber && !column.renderHeader && column.title) {
1182
1186
  ctx.font = "600 13px Inter, ui-sans-serif, system-ui, sans-serif";
1183
1187
  const titleWidth = ctx.measureText(column.title).width;
1184
- const visibleActions = getVisibleHeaderActions(column, metric.width, columnDraggable, titleWidth);
1185
- const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * HEADER_ACTION_SLOT_WIDTH;
1188
+ const visibleActions = getVisibleHeaderActions(column, metric.width, columnDraggable, titleWidth, headerActionSlotWidth);
1189
+ const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * headerActionSlotWidth;
1186
1190
  const contentWidth = Math.max(0, metric.width - actionWidth);
1187
1191
  const padding = 10;
1188
1192
  const clipWidth = contentWidth;
@@ -1212,12 +1216,12 @@ function paintGrid(options) {
1212
1216
  ctx.fillRect(rightFixedLeft - 1, 0, 1, height);
1213
1217
  }
1214
1218
  ctx.fillStyle = colors.grid;
1215
- if (!horizontalBorderless) ctx.fillRect(0, 0, width, 1);
1216
- if (!verticalBorderless) {
1219
+ if (!horizontalBorderless && !frameBorderless) ctx.fillRect(0, 0, width, 1);
1220
+ if (!verticalBorderless && !frameBorderless) {
1217
1221
  ctx.fillRect(0, 0, 1, height);
1218
1222
  ctx.fillRect(width - 1, 0, 1, height);
1219
1223
  }
1220
- if (!horizontalBorderless && !options.suppressFrameBottomBorder) ctx.fillRect(0, height - 1, width, 1);
1224
+ if (!horizontalBorderless && !frameBorderless && !options.suppressFrameBottomBorder) ctx.fillRect(0, height - 1, width, 1);
1221
1225
  if (selectionRange) {
1222
1226
  const rowStart = Math.min(selectionRange.anchor.rowIndex, selectionRange.focus.rowIndex);
1223
1227
  const rowEnd = Math.max(selectionRange.anchor.rowIndex, selectionRange.focus.rowIndex);
@@ -1438,6 +1442,10 @@ var waitForPaint = () => new Promise((resolve) => {
1438
1442
  function readThemeColor(styles, name, fallback) {
1439
1443
  return styles.getPropertyValue(name).trim() || fallback;
1440
1444
  }
1445
+ function readThemePixel(styles, name, fallback) {
1446
+ const value = Number.parseFloat(styles.getPropertyValue(name));
1447
+ return Number.isFinite(value) && value > 0 ? value : fallback;
1448
+ }
1441
1449
  function getDefaultRowKey(row, index) {
1442
1450
  const id = row.id;
1443
1451
  return typeof id === "string" || typeof id === "number" ? id : index;
@@ -1522,6 +1530,7 @@ function TableInner({
1522
1530
  borderless,
1523
1531
  verticalBorderless,
1524
1532
  horizontalBorderless = false,
1533
+ frameBorderless = false,
1525
1534
  striped = false,
1526
1535
  highlight,
1527
1536
  cellSpans = [],
@@ -1568,6 +1577,8 @@ function TableInner({
1568
1577
  setSourceColumns((current) => reconcileColumns(current, columnProps));
1569
1578
  }, [columnProps]);
1570
1579
  const rowHeight = layout?.rowHeight ?? rowHeightProp ?? 36;
1580
+ const headerActionSize = Math.max(8, layout?.headerActionSize ?? 14);
1581
+ const headerActionSlotWidth = headerActionSize + 2;
1571
1582
  const baseHeaderHeight = layout?.headerHeight ?? headerHeightProp ?? 40;
1572
1583
  const headerDepth = useMemo(() => Math.max(1, ...sourceColumns.map(getColumnDepth)), [sourceColumns]);
1573
1584
  const { language, labels } = useMemo(() => resolveGridLocale(locale), [locale]);
@@ -2182,6 +2193,7 @@ function TableInner({
2182
2193
  const context = canvas.getContext("2d");
2183
2194
  if (!context) return;
2184
2195
  const themeStyles = getComputedStyle(canvas);
2196
+ const bodyFontSize = readThemePixel(themeStyles, "--rvg-font-size-body", 13);
2185
2197
  const themeColors = {
2186
2198
  background: readThemeColor(themeStyles, "--rvg-color-bg", "#ffffff"),
2187
2199
  header: readThemeColor(themeStyles, "--rvg-color-header-bg", "#f5f6f7"),
@@ -2202,8 +2214,8 @@ function TableInner({
2202
2214
  const scroll = scrollRef.current;
2203
2215
  const rowScrollTop = fixedHeader ? scroll.top : Math.max(0, scroll.top - bodyTop);
2204
2216
  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 };
2205
- paintGrid({ context, width: viewport.width, height: renderHeight, pixelRatio: ratio, scrollLeft: scroll.left, scrollTop: scroll.top, rowHeight, headerHeight, headerLeafTop, headerLeafHeight, bodyTop, suppressLastRowBottomBorder: bottomSummaryHeight > 0, suppressFrameBottomBorder: bottomSummaryHeight > 0, fixedHeader, verticalBorderless: !hasVerticalBorders, horizontalBorderless, striped: hasStripedRows, columnDraggable, sortState, filterValues, hoveredHeaderAction: hoveredHeaderActionRef.current, rows, columns, metrics, range, selection, editing, hoveredRowIndex: hoveredRowIndexRef.current, selectionRange, selectedRowKeys: selectedRowKeySet, rowSelectionMode, selectedColumnKeys: selectedColumnKeySet, cellSpans: cellSpanLookup.covered, maxRowSpan: cellSpanLookup.maxRowSpan, highlightEditedCells: highlightsEditedCells, highlightInsertedRows: highlightsInsertedRows, insertedRowKeys: insertedRowKeySet, editedCellKeys, cellAnnotations, getRowKey, rowDragPreview, columnDropTarget, colors: themeColors });
2206
- }, [bodyTop, bodyViewportHeight, bottomSummaryHeight, cellAnnotations, cellSpanLookup, columnDraggable, columnDropTarget, columns, editedCellHighlightColor, editedCellKeys, editing, renderHeight, filterValues, fixedHeader, getRowKey, hasStripedRows, hasVerticalBorders, headerDepth, headerHeight, headerLeafHeight, headerLeafTop, horizontalBorderless, highlightsEditedCells, highlightsInsertedRows, insertedRowHighlightColor, insertedRowKeySet, isVirtualized, metrics, rowDragPreview, rowHeight, rowSelectionMode, rows, selectedColumnKeySet, selectedRowKeySet, selection, selectionRange, sortState, stripedColor, viewport.width, virtualOverscan]);
2217
+ paintGrid({ context, width: viewport.width, height: renderHeight, pixelRatio: ratio, scrollLeft: scroll.left, scrollTop: scroll.top, rowHeight, bodyFontSize, headerActionSlotWidth, headerHeight, headerLeafTop, headerLeafHeight, bodyTop, suppressLastRowBottomBorder: bottomSummaryHeight > 0, suppressFrameBottomBorder: bottomSummaryHeight > 0, fixedHeader, verticalBorderless: !hasVerticalBorders, horizontalBorderless, frameBorderless, striped: hasStripedRows, columnDraggable, sortState, filterValues, hoveredHeaderAction: hoveredHeaderActionRef.current, rows, columns, metrics, range, selection, editing, hoveredRowIndex: hoveredRowIndexRef.current, selectionRange, selectedRowKeys: selectedRowKeySet, rowSelectionMode, selectedColumnKeys: selectedColumnKeySet, cellSpans: cellSpanLookup.covered, maxRowSpan: cellSpanLookup.maxRowSpan, highlightEditedCells: highlightsEditedCells, highlightInsertedRows: highlightsInsertedRows, insertedRowKeys: insertedRowKeySet, editedCellKeys, cellAnnotations, getRowKey, rowDragPreview, columnDropTarget, colors: themeColors });
2218
+ }, [bodyTop, bodyViewportHeight, bottomSummaryHeight, cellAnnotations, cellSpanLookup, columnDraggable, columnDropTarget, columns, editedCellHighlightColor, editedCellKeys, editing, renderHeight, filterValues, fixedHeader, frameBorderless, getRowKey, hasStripedRows, hasVerticalBorders, headerActionSlotWidth, headerDepth, headerHeight, headerLeafHeight, headerLeafTop, horizontalBorderless, highlightsEditedCells, highlightsInsertedRows, insertedRowHighlightColor, insertedRowKeySet, isVirtualized, metrics, rowDragPreview, rowHeight, rowSelectionMode, rows, selectedColumnKeySet, selectedRowKeySet, selection, selectionRange, sortState, stripedColor, viewport.width, virtualOverscan]);
2207
2219
  const scheduleDraw = useCallback2(() => {
2208
2220
  if (frameRef.current === null) frameRef.current = requestAnimationFrame(draw);
2209
2221
  }, [draw]);
@@ -2769,34 +2781,34 @@ function TableInner({
2769
2781
  if (column.rowSelection || column.rowDragHandle || column.rowNumber) return null;
2770
2782
  const localX = clientX - canvasRef.current.getBoundingClientRect().left;
2771
2783
  const columnRight = getDisplayedColumnLeft(columnIndex) + metrics[columnIndex].width;
2772
- const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex));
2773
- let right = columnRight - (visibleActions.drag ? HEADER_ACTION_SLOT_WIDTH : 2);
2784
+ const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex), headerActionSlotWidth);
2785
+ let right = columnRight - (visibleActions.drag ? headerActionSlotWidth : 2);
2774
2786
  if (visibleActions.sort) {
2775
- if (localX >= right - HEADER_ACTION_SLOT_WIDTH && localX < right) return "sort";
2776
- right -= HEADER_ACTION_SLOT_WIDTH;
2787
+ if (localX >= right - headerActionSlotWidth && localX < right) return "sort";
2788
+ right -= headerActionSlotWidth;
2777
2789
  }
2778
- if (visibleActions.filter && localX >= right - HEADER_ACTION_SLOT_WIDTH && localX < right) return "filter";
2790
+ if (visibleActions.filter && localX >= right - headerActionSlotWidth && localX < right) return "filter";
2779
2791
  return null;
2780
- }, [columnDraggable, columns, getDisplayedColumnLeft, measureHeaderTitleWidth, metrics]);
2792
+ }, [columnDraggable, columns, getDisplayedColumnLeft, headerActionSlotWidth, measureHeaderTitleWidth, metrics]);
2781
2793
  const isHeaderTitleTruncated = useCallback2((columnIndex) => {
2782
2794
  const canvas = canvasRef.current;
2783
2795
  if (columnIndex < 0 || !canvas) return false;
2784
2796
  const column = columns[columnIndex];
2785
2797
  const metric = metrics[columnIndex];
2786
2798
  const titleWidth = measureHeaderTitleWidth(columnIndex);
2787
- const visibleActions = getVisibleHeaderActions(column, metric.width, columnDraggable, titleWidth);
2788
- const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * HEADER_ACTION_SLOT_WIDTH;
2799
+ const visibleActions = getVisibleHeaderActions(column, metric.width, columnDraggable, titleWidth, headerActionSlotWidth);
2800
+ const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * headerActionSlotWidth;
2789
2801
  return getHeaderTitleRequiredWidth(column, titleWidth) > Math.max(0, metric.width - actionWidth);
2790
- }, [columnDraggable, columns, measureHeaderTitleWidth, metrics]);
2802
+ }, [columnDraggable, columns, headerActionSlotWidth, measureHeaderTitleWidth, metrics]);
2791
2803
  const locateHeaderDragHandle = useCallback2((clientX, columnIndex) => {
2792
2804
  if (!columnDraggable || columnIndex < 0 || !canvasRef.current) return false;
2793
2805
  const column = columns[columnIndex];
2794
2806
  if (column.rowSelection || column.rowDragHandle || column.rowNumber) return false;
2795
- if (!getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex)).drag) return false;
2807
+ if (!getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex), headerActionSlotWidth).drag) return false;
2796
2808
  const localX = clientX - canvasRef.current.getBoundingClientRect().left;
2797
2809
  const columnRight = getDisplayedColumnLeft(columnIndex) + metrics[columnIndex].width;
2798
- return localX >= columnRight - 16 && localX < columnRight - 5;
2799
- }, [columnDraggable, columns, getDisplayedColumnLeft, measureHeaderTitleWidth, metrics]);
2810
+ return localX >= columnRight - headerActionSlotWidth && localX < columnRight - 2;
2811
+ }, [columnDraggable, columns, getDisplayedColumnLeft, headerActionSlotWidth, measureHeaderTitleWidth, metrics]);
2800
2812
  const getHeaderCellLeft = useCallback2((cell) => {
2801
2813
  const startMetric = metrics[cell.startIndex];
2802
2814
  const fixedSide = columns[cell.startIndex]?.fixed;
@@ -2861,8 +2873,8 @@ function TableInner({
2861
2873
  if (cell.leaf) return locateHeaderDragHandle(clientX, cell.startIndex);
2862
2874
  const localX = clientX - canvasRef.current.getBoundingClientRect().left;
2863
2875
  const right = getHeaderCellLeft(cell) + getHeaderCellWidth(cell);
2864
- return localX >= right - 16 && localX < right - 5;
2865
- }, [columnDraggable, getHeaderCellLeft, getHeaderCellWidth, locateHeaderDragHandle]);
2876
+ return localX >= right - headerActionSlotWidth && localX < right - 2;
2877
+ }, [columnDraggable, getHeaderCellLeft, getHeaderCellWidth, headerActionSlotWidth, locateHeaderDragHandle]);
2866
2878
  const locateHeaderCell = useCallback2((clientX, clientY) => {
2867
2879
  const canvas = canvasRef.current;
2868
2880
  if (!canvas) return null;
@@ -3018,26 +3030,32 @@ function TableInner({
3018
3030
  header.style.height = `${headerHeight}px`;
3019
3031
  header.style.justifyContent = column.align === "right" ? "flex-end" : column.align === "center" ? "center" : "flex-start";
3020
3032
  header.textContent = column.title;
3021
- const visibleActions = getVisibleHeaderActions(column, columnWidth, columnDraggable, measureHeaderTitleWidth(sourceIndex));
3033
+ const visibleActions = getVisibleHeaderActions(column, columnWidth, columnDraggable, measureHeaderTitleWidth(sourceIndex), headerActionSlotWidth);
3022
3034
  let actionRight = 2;
3023
3035
  if (visibleActions.drag) {
3024
3036
  const icon = createHeaderDragHandleSvg();
3025
3037
  icon.classList.add("rvg-preview-header-icon");
3038
+ icon.style.width = `${headerActionSize}px`;
3039
+ icon.style.height = `${headerActionSize}px`;
3026
3040
  icon.style.right = `${actionRight}px`;
3027
3041
  header.appendChild(icon);
3028
- actionRight += HEADER_ACTION_SLOT_WIDTH;
3042
+ actionRight += headerActionSlotWidth;
3029
3043
  }
3030
3044
  if (visibleActions.sort) {
3031
3045
  const direction = sortState?.columnKey === column.key ? sortState.direction : null;
3032
3046
  const icon = createHeaderSortSvg(direction);
3033
3047
  icon.classList.add("rvg-preview-header-icon");
3048
+ icon.style.width = `${headerActionSize}px`;
3049
+ icon.style.height = `${headerActionSize}px`;
3034
3050
  icon.style.right = `${actionRight}px`;
3035
3051
  header.appendChild(icon);
3036
- actionRight += HEADER_ACTION_SLOT_WIDTH;
3052
+ actionRight += headerActionSlotWidth;
3037
3053
  }
3038
3054
  if (visibleActions.filter) {
3039
3055
  const icon = createHeaderSearchSvg(Boolean(filterValues[column.key]));
3040
3056
  icon.classList.add("rvg-preview-header-icon", "rvg-preview-header-search-icon");
3057
+ icon.style.width = `${headerActionSize}px`;
3058
+ icon.style.height = `${headerActionSize}px`;
3041
3059
  icon.style.right = `${actionRight}px`;
3042
3060
  header.appendChild(icon);
3043
3061
  }
@@ -3234,7 +3252,7 @@ function TableInner({
3234
3252
  cleanupDrag();
3235
3253
  cleanupDrop();
3236
3254
  };
3237
- }, [applyColumnOrder, applyRowOrder, bodyTop, columnDraggable, columns, filterValues, fixedHeader, getCellLabel, getDisplayedColumnLeft, getHeaderCellWidth, getRowKey, headerCells, headerHeight, hideDragTooltips, locateColumn, locateHeaderCell, locateHeaderCellDragHandle, locateHeaderResizeCell, locateRow, measureHeaderTitleWidth, metrics, rowDraggable, rowHeight, rows, selectedRowKeySet, selection, setDragGuide, setRowDragGuide, sortState, utilityColumnCount]);
3255
+ }, [applyColumnOrder, applyRowOrder, bodyTop, columnDraggable, columns, filterValues, fixedHeader, getCellLabel, getDisplayedColumnLeft, getHeaderCellWidth, getRowKey, headerActionSize, headerActionSlotWidth, headerCells, headerHeight, hideDragTooltips, locateColumn, locateHeaderCell, locateHeaderCellDragHandle, locateHeaderResizeCell, locateRow, measureHeaderTitleWidth, metrics, rowDraggable, rowHeight, rows, selectedRowKeySet, selection, setDragGuide, setRowDragGuide, sortState, utilityColumnCount]);
3238
3256
  const locateCell = useCallback2((clientX, clientY) => {
3239
3257
  const canvas = canvasRef.current;
3240
3258
  if (!canvas) return null;
@@ -3678,28 +3696,28 @@ function TableInner({
3678
3696
  const renderHeaderIcons = (columnIndex, left) => {
3679
3697
  const column = columns[columnIndex];
3680
3698
  if (column.rowSelection || column.rowDragHandle || column.rowNumber) return null;
3681
- const top = headerLeafTop + (headerLeafHeight - 14) / 2;
3682
- const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex));
3683
- let right = left + metrics[columnIndex].width - (visibleActions.drag ? HEADER_ACTION_SLOT_WIDTH : 2);
3699
+ const top = headerLeafTop + (headerLeafHeight - headerActionSize) / 2;
3700
+ const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex), headerActionSlotWidth);
3701
+ let right = left + metrics[columnIndex].width - (visibleActions.drag ? headerActionSlotWidth : 2);
3684
3702
  const icons = [];
3685
3703
  if (visibleActions.drag) {
3686
3704
  const hovered = hoveredHeaderAction?.columnIndex === columnIndex && hoveredHeaderAction.action === "drag";
3687
3705
  icons.push(
3688
- /* @__PURE__ */ jsx7(HeaderDragIcon, { className: `rvg-header-icon rvg-header-drag-icon${hovered ? " is-hovered" : ""}`, style: { left: left + metrics[columnIndex].width - 16, top } }, "drag")
3706
+ /* @__PURE__ */ jsx7(HeaderDragIcon, { className: `rvg-header-icon rvg-header-drag-icon${hovered ? " is-hovered" : ""}`, style: { left: left + metrics[columnIndex].width - headerActionSlotWidth, top } }, "drag")
3689
3707
  );
3690
3708
  }
3691
3709
  if (visibleActions.sort) {
3692
3710
  const hovered = hoveredHeaderAction?.columnIndex === columnIndex && hoveredHeaderAction.action === "sort";
3693
3711
  const direction = sortState?.columnKey === column.key ? sortState.direction : null;
3694
3712
  icons.push(
3695
- /* @__PURE__ */ jsx7(HeaderSortIcon, { className: `rvg-header-icon${hovered ? " is-hovered" : ""}`, style: { left: right - 16, top }, direction }, "sort")
3713
+ /* @__PURE__ */ jsx7(HeaderSortIcon, { className: `rvg-header-icon${hovered ? " is-hovered" : ""}`, style: { left: right - headerActionSlotWidth, top }, direction }, "sort")
3696
3714
  );
3697
- right -= HEADER_ACTION_SLOT_WIDTH;
3715
+ right -= headerActionSlotWidth;
3698
3716
  }
3699
3717
  if (visibleActions.filter) {
3700
3718
  const hovered = hoveredHeaderAction?.columnIndex === columnIndex && hoveredHeaderAction.action === "filter";
3701
3719
  icons.push(
3702
- /* @__PURE__ */ jsx7(HeaderSearchIcon, { className: `rvg-header-icon rvg-header-search-icon${hovered ? " is-hovered" : ""}${filterValues[column.key] ? " is-active" : ""}`, style: { left: right - 16, top: top + 1 } }, "filter")
3720
+ /* @__PURE__ */ jsx7(HeaderSearchIcon, { className: `rvg-header-icon rvg-header-search-icon${hovered ? " is-hovered" : ""}${filterValues[column.key] ? " is-active" : ""}`, style: { left: right - headerActionSlotWidth, top: top + 1 } }, "filter")
3703
3721
  );
3704
3722
  }
3705
3723
  return icons;
@@ -3720,8 +3738,8 @@ function TableInner({
3720
3738
  const context = canvas?.getContext("2d");
3721
3739
  if (context) context.font = "600 13px Inter, ui-sans-serif, system-ui, sans-serif";
3722
3740
  const titleWidth = context?.measureText(column.title).width ?? column.title.length * 13;
3723
- const visibleActions = cell?.leaf || !cell ? getVisibleHeaderActions(column, cellWidth, columnDraggable, titleWidth) : null;
3724
- const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * HEADER_ACTION_SLOT_WIDTH : columnDraggable ? HEADER_ACTION_SLOT_WIDTH : 0;
3741
+ const visibleActions = cell?.leaf || !cell ? getVisibleHeaderActions(column, cellWidth, columnDraggable, titleWidth, headerActionSlotWidth) : null;
3742
+ const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * headerActionSlotWidth : columnDraggable ? headerActionSlotWidth : 0;
3725
3743
  const contentWidth2 = Math.max(0, cellWidth - actionWidth);
3726
3744
  const maxTextWidth = Math.max(0, contentWidth2 - 20);
3727
3745
  const textWidth = Math.min(titleWidth, maxTextWidth);
@@ -3742,12 +3760,17 @@ function TableInner({
3742
3760
  } else if (action === "sort") {
3743
3761
  const direction = sortState?.columnKey === column.key ? sortState.direction : null;
3744
3762
  label = direction === "asc" ? labels.sortAsc : direction === "desc" ? labels.sortDesc : labels.sortBoth;
3745
- anchorX = cellLeft + cellWidth - 22;
3763
+ const visibleActions = getVisibleHeaderActions(column, cellWidth, columnDraggable, measureHeaderTitleWidth(columnIndex), headerActionSlotWidth);
3764
+ const right = cellLeft + cellWidth - (visibleActions.drag ? headerActionSlotWidth : 2);
3765
+ anchorX = right - headerActionSlotWidth / 2;
3746
3766
  anchorLeft = anchorX;
3747
3767
  } else if (action === "filter") {
3748
3768
  const value = filterValues[column.key];
3749
3769
  label = value ? labels.filterWithValue(value) : labels.filter;
3750
- anchorX = cellLeft + cellWidth - 40;
3770
+ const visibleActions = getVisibleHeaderActions(column, cellWidth, columnDraggable, measureHeaderTitleWidth(columnIndex), headerActionSlotWidth);
3771
+ let right = cellLeft + cellWidth - (visibleActions.drag ? headerActionSlotWidth : 2);
3772
+ if (visibleActions.sort) right -= headerActionSlotWidth;
3773
+ anchorX = right - headerActionSlotWidth / 2;
3751
3774
  anchorLeft = anchorX;
3752
3775
  }
3753
3776
  const placement = anchorX + 328 > viewport.width ? "left" : "right";
@@ -3760,7 +3783,7 @@ function TableInner({
3760
3783
  label,
3761
3784
  placement
3762
3785
  };
3763
- }, [baseHeaderHeight, columnDraggable, columns, filterValues, fixedHeader, getDisplayedColumnLeft, getHeaderCellLeft, getHeaderCellWidth, headerDepth, headerRowHeights, headerRowOffsets, labels, metrics, scrollPosition.top, sortState, viewport.width]);
3786
+ }, [baseHeaderHeight, columnDraggable, columns, filterValues, fixedHeader, getDisplayedColumnLeft, getHeaderCellLeft, getHeaderCellWidth, headerActionSlotWidth, headerDepth, headerRowHeights, headerRowOffsets, labels, measureHeaderTitleWidth, metrics, scrollPosition.top, sortState, viewport.width]);
3764
3787
  const isPointerOnHeaderTitle = useCallback2((clientX, clientY, cell) => {
3765
3788
  const canvas = canvasRef.current;
3766
3789
  if (!canvas) return false;
@@ -3774,14 +3797,14 @@ function TableInner({
3774
3797
  const context = canvas.getContext("2d");
3775
3798
  if (context) context.font = "600 13px Inter, ui-sans-serif, system-ui, sans-serif";
3776
3799
  const titleWidth = context?.measureText(column.title).width ?? column.title.length * 13;
3777
- const visibleActions = cell.leaf ? getVisibleHeaderActions(column, cellWidth, columnDraggable, titleWidth) : null;
3778
- const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * HEADER_ACTION_SLOT_WIDTH : columnDraggable ? HEADER_ACTION_SLOT_WIDTH : 0;
3800
+ const visibleActions = cell.leaf ? getVisibleHeaderActions(column, cellWidth, columnDraggable, titleWidth, headerActionSlotWidth) : null;
3801
+ const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * headerActionSlotWidth : columnDraggable ? headerActionSlotWidth : 0;
3779
3802
  const contentWidth2 = Math.max(0, cellWidth - actionWidth);
3780
3803
  const textWidth = Math.min(titleWidth, Math.max(0, contentWidth2 - 20));
3781
3804
  const textLeft = column.align === "right" ? cellLeft + contentWidth2 - 10 - textWidth : column.align === "center" ? cellLeft + contentWidth2 / 2 - textWidth / 2 : cellLeft + 10;
3782
3805
  const textTop = rowTop + 8;
3783
3806
  return localX >= textLeft - 4 && localX <= textLeft + textWidth + 4 && localY >= textTop - 2 && localY <= textTop + 18;
3784
- }, [columnDraggable, fixedHeader, getHeaderCellLeft, getHeaderCellWidth, headerRowOffsets]);
3807
+ }, [columnDraggable, fixedHeader, getHeaderCellLeft, getHeaderCellWidth, headerActionSlotWidth, headerRowOffsets]);
3785
3808
  const headerTooltip = (() => {
3786
3809
  if (!visibleHeaderTooltip) return null;
3787
3810
  return visibleHeaderTooltip;
@@ -4104,8 +4127,8 @@ function TableInner({
4104
4127
  );
4105
4128
  }
4106
4129
  if (column.renderHeader) {
4107
- const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex));
4108
- const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * HEADER_ACTION_SLOT_WIDTH;
4130
+ const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex), headerActionSlotWidth);
4131
+ const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * headerActionSlotWidth;
4109
4132
  return /* @__PURE__ */ jsx7(
4110
4133
  "div",
4111
4134
  {
@@ -4149,13 +4172,13 @@ function TableInner({
4149
4172
  const headerTop = (fixedHeader ? 0 : -currentScrollTop) + (headerRowOffsets[cell.level] ?? 0);
4150
4173
  const left = fixedSide === "left" ? leftFixedOffsets.get(cell.startIndex) ?? 0 : fixedSide === "right" ? viewport.width - (rightFixedOffsets.get(cell.startIndex) ?? 0) - startMetric.width : getScrollableColumnLeft(cell.startIndex);
4151
4174
  const width2 = cell.leaf ? startMetric.width : getCellDisplayWidth(cell.startIndex, cell.endIndex - cell.startIndex + 1);
4152
- const visibleActions = cell.leaf ? getVisibleHeaderActions(column, startMetric.width, columnDraggable, measureHeaderTitleWidth(cell.startIndex)) : null;
4153
- const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * HEADER_ACTION_SLOT_WIDTH : columnDraggable && !cell.leaf ? HEADER_ACTION_SLOT_WIDTH : 0;
4175
+ const visibleActions = cell.leaf ? getVisibleHeaderActions(column, startMetric.width, columnDraggable, measureHeaderTitleWidth(cell.startIndex), headerActionSlotWidth) : null;
4176
+ const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * headerActionSlotWidth : columnDraggable && !cell.leaf ? headerActionSlotWidth : 0;
4154
4177
  const isDropTarget = Boolean(columnDropTarget && cell.endIndex >= columnDropTarget.startIndex && cell.startIndex <= columnDropTarget.endIndex);
4155
4178
  return /* @__PURE__ */ jsxs7(
4156
4179
  "div",
4157
4180
  {
4158
- className: `rvg-header-title rvg-header-group${column.renderHeader ? " is-custom" : ""}${cell.leaf ? " is-leaf" : ""}${isDropTarget ? " is-drop-target" : ""}`,
4181
+ className: `rvg-header-title rvg-header-group${column.renderHeader ? " is-custom" : ""}${cell.leaf ? " is-leaf" : ""}${cell.endIndex === columns.length - 1 ? " is-frame-right" : ""}${isDropTarget ? " is-drop-target" : ""}`,
4159
4182
  "data-level": cell.level,
4160
4183
  "data-measure-key": `group:${cell.key}:${cell.level}`,
4161
4184
  style: {
@@ -4186,7 +4209,7 @@ function TableInner({
4186
4209
  HeaderDragIcon,
4187
4210
  {
4188
4211
  className: `rvg-header-icon rvg-header-drag-icon${hoveredHeaderAction?.columnIndex === cell.startIndex && hoveredHeaderAction.action === "drag" ? " is-hovered" : ""}`,
4189
- style: { left: width2 - 16, top: (rowHeightForLevel - 14) / 2 }
4212
+ style: { left: width2 - headerActionSlotWidth, top: (rowHeightForLevel - headerActionSize) / 2 }
4190
4213
  }
4191
4214
  )
4192
4215
  ]
@@ -4452,12 +4475,13 @@ function TableInner({
4452
4475
  width,
4453
4476
  height: autoHeight ? effectiveHeight : height,
4454
4477
  minHeight: autoHeight || typeof height === "number" ? void 0 : FALLBACK_HEIGHT,
4478
+ "--rvg-header-action-size": `${headerActionSize}px`,
4455
4479
  ...stripedColor ? { "--rvg-color-stripe": stripedColor } : null,
4456
4480
  ...style
4457
4481
  };
4458
4482
  const currentScroll = scrollRef.current;
4459
4483
  const scrollerBottom = Math.max(0, bottomSummaryHeight - horizontalScrollbarHeight);
4460
- return /* @__PURE__ */ jsxs7("div", { className: `rvg-root${!hasVerticalBorders ? " is-vertical-borderless" : ""}${horizontalBorderless ? " is-horizontal-borderless" : ""}${hasStripedRows ? " is-striped" : ""}${insertBusy ? " is-inserting" : ""}${deleteBusy ? " is-deleting" : ""} ${loading && !hasCompletedLoadRef.current && !hasCustomLoading ? "is-initial-loading" : ""} ${className}`, style: rootStyle, children: [
4484
+ return /* @__PURE__ */ jsxs7("div", { className: `rvg-root${!hasVerticalBorders ? " is-vertical-borderless" : ""}${horizontalBorderless ? " is-horizontal-borderless" : ""}${frameBorderless ? " is-frame-borderless" : ""}${hasStripedRows ? " is-striped" : ""}${insertBusy ? " is-inserting" : ""}${deleteBusy ? " is-deleting" : ""} ${loading && !hasCompletedLoadRef.current && !hasCustomLoading ? "is-initial-loading" : ""} ${className}`, style: rootStyle, children: [
4461
4485
  /* @__PURE__ */ jsxs7("div", { ref: scrollerRef, className: `rvg-scroller${contextMenu ? " is-context-menu-open" : ""}`, style: { bottom: scrollerBottom }, onScroll: handleScroll, onCopy: handleCopy, onContextMenu: handleContextMenu, tabIndex: 0, role: "grid", "aria-label": ariaLabel, "aria-rowcount": rows.length, "aria-colcount": columns.length, onKeyDown: handleKeyDown, children: [
4462
4486
  /* @__PURE__ */ jsx7(
4463
4487
  "canvas",
@@ -4517,7 +4541,10 @@ function TableInner({
4517
4541
  } else {
4518
4542
  const label = getCellLabel(cell.rowIndex, cell.columnIndex);
4519
4543
  const context = event.currentTarget.getContext("2d");
4520
- if (context) context.font = "13px Inter, ui-sans-serif, system-ui, sans-serif";
4544
+ if (context) {
4545
+ const bodyFontSize = readThemePixel(getComputedStyle(event.currentTarget), "--rvg-font-size-body", 13);
4546
+ context.font = `${bodyFontSize}px Inter, ui-sans-serif, system-ui, sans-serif`;
4547
+ }
4521
4548
  const truncated = Boolean(label && context && context.measureText(label).width + 20 > metrics[cell.columnIndex].width);
4522
4549
  if (!label || !tooltipConfig.cell && !truncated) {
4523
4550
  showCellTooltipAfterDelay(null);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lensui/lens-table",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
4
4
  "description": "A lightweight, canvas-rendered React table component for LensUI.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",