@lensui/lens-table 0.1.9 → 0.1.10

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
@@ -96,6 +96,7 @@ export function Example() {
96
96
  | `locale` | `'zh-CN' \| 'en-US' \| LocaleConfig` | `'zh-CN'` | 国际化配置。传字符串时使用内置中英文文案;传对象时可通过`language` 指定语言,并通过 `labels` 覆盖文案。 |
97
97
  | `borderless` | `boolean` | `false` | 是否弱化竖向边框。开启后隐藏单元格之间的竖线,但保留横向分隔线。 |
98
98
  | `verticalBorderless` | `boolean` | `false` | 已兼容保留,建议改用 `borderless`。 |
99
+ | `horizontalBorderless` | `boolean` | `false` | 是否隐藏横向行分隔线及上下边框,同时保留纵向列分隔线。可与 `striped` 配合使用。 |
99
100
  | `striped` | `boolean \| string` | `false` | 是否显示斑马纹行背景。传`true` 使用主题变量,传颜色字符串可自定义斑马纹颜色,例如 `'#f6faff'`。 |
100
101
  | `highlight` | `{ editedCells?: boolean \| string; insertedRows?: boolean \| string }` | `{}` | 高亮反馈配置。`editedCells` 控制编辑过的单元格底色,`insertedRows` 控制乐观插入行底色;传 `true` 使用默认色,传颜色字符串可自定义。 |
101
102
  | `cellSpans` | `TableCellSpan[] \| ((ctx) => TableCellSpan \| { rowSpan?: number; colSpan?: number } \| null)` | `[]` | 合并主体单元格配置。可传数组,也可传函数按单元格生成规则。推荐使用 `rowKey` 定位;横向合并会限制在同一个固定列分区内,工具列不会参与合并。 |
package/dist/index.cjs CHANGED
@@ -692,6 +692,18 @@ 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
+
695
707
  // src/components/editors/TextEditor.tsx
696
708
  var import_jsx_runtime6 = require("react/jsx-runtime");
697
709
  function TextEditor({ value, type, style, labels, onChange, onCommit, onCancel }) {
@@ -705,6 +717,7 @@ function TextEditor({ value, type, style, labels, onChange, onCommit, onCancel }
705
717
  "input",
706
718
  {
707
719
  type,
720
+ style: { font: CELL_FONT, textAlign: style.textAlign },
708
721
  value,
709
722
  autoFocus: true,
710
723
  onChange: (event) => onChange(event.target.value),
@@ -929,7 +942,7 @@ function ellipsizeText(context, text, maxWidth) {
929
942
  return `${text.slice(0, start)}${ellipsis}`;
930
943
  }
931
944
  function paintGrid(options) {
932
- const { context: ctx, width, height, pixelRatio, scrollLeft, scrollTop, rowHeight, headerHeight, fixedHeader, verticalBorderless, 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, 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;
933
946
  const headerLeafTop = options.headerLeafTop ?? 0;
934
947
  const headerLeafHeight = options.headerLeafHeight ?? headerHeight;
935
948
  const colors = { ...COLORS, ...options.colors };
@@ -946,7 +959,8 @@ function paintGrid(options) {
946
959
  ctx.clearRect(0, 0, width, height);
947
960
  ctx.fillStyle = colors.background;
948
961
  ctx.fillRect(0, 0, width, height);
949
- ctx.font = "13px Inter, ui-sans-serif, system-ui, sans-serif";
962
+ ctx.font = CELL_FONT;
963
+ const cellBaselineOffset = getCellBaselineOffset(ctx);
950
964
  ctx.textBaseline = "middle";
951
965
  const leftFixedWidth = columns.reduce((value, column, index) => column.fixed === "left" ? value + metrics[index].width : value, 0);
952
966
  const rightFixedWidth = columns.reduce((value, column, index) => column.fixed === "right" ? value + metrics[index].width : value, 0);
@@ -1051,7 +1065,7 @@ function paintGrid(options) {
1051
1065
  ctx.fillStyle = colors.grid;
1052
1066
  if (!verticalBorderless) ctx.fillRect(x + cellWidth - 1, y, 1, cellHeight);
1053
1067
  if (rowIndex === rowBelowDragGap) ctx.fillRect(x, y, cellWidth, 1);
1054
- if (!options.suppressLastRowBottomBorder || rowIndex !== rows.length - 1) {
1068
+ if (!horizontalBorderless && (!options.suppressLastRowBottomBorder || rowIndex !== rows.length - 1)) {
1055
1069
  ctx.fillRect(x, y + cellHeight - 1, cellWidth, 1);
1056
1070
  }
1057
1071
  if (rowSpan > 1) {
@@ -1145,9 +1159,10 @@ function paintGrid(options) {
1145
1159
  ctx.rect(x + 2, y + 1, Math.max(0, cellWidth - 4), cellHeight - 2);
1146
1160
  ctx.clip();
1147
1161
  ctx.fillStyle = cellStyle?.color ?? colors.text;
1148
- ctx.font = "13px Inter, ui-sans-serif, system-ui, sans-serif";
1162
+ ctx.font = CELL_FONT;
1163
+ ctx.textBaseline = "alphabetic";
1149
1164
  ctx.textAlign = column.align === "right" ? "right" : column.align === "center" ? "center" : "left";
1150
- ctx.fillText(label, textX, y + cellHeight / 2);
1165
+ ctx.fillText(label, textX, y + cellHeight / 2 + cellBaselineOffset);
1151
1166
  ctx.restore();
1152
1167
  }
1153
1168
  }
@@ -1191,7 +1206,7 @@ function paintGrid(options) {
1191
1206
  }
1192
1207
  ctx.fillStyle = colors.grid;
1193
1208
  if (!verticalBorderless) ctx.fillRect(x + metric.width - 1, headerY, 1, headerHeight);
1194
- ctx.fillRect(x, headerY + headerHeight - 1, metric.width, 1);
1209
+ if (!horizontalBorderless) ctx.fillRect(x, headerY + headerHeight - 1, metric.width, 1);
1195
1210
  const column = columns[columnIndex];
1196
1211
  if (headerLeafTop === 0 && !column.rowSelection && !column.rowDragHandle && !column.rowNumber && !column.renderHeader && column.title) {
1197
1212
  ctx.font = "600 13px Inter, ui-sans-serif, system-ui, sans-serif";
@@ -1227,12 +1242,12 @@ function paintGrid(options) {
1227
1242
  ctx.fillRect(rightFixedLeft - 1, 0, 1, height);
1228
1243
  }
1229
1244
  ctx.fillStyle = colors.grid;
1230
- ctx.fillRect(0, 0, width, 1);
1245
+ if (!horizontalBorderless) ctx.fillRect(0, 0, width, 1);
1231
1246
  if (!verticalBorderless) {
1232
1247
  ctx.fillRect(0, 0, 1, height);
1233
1248
  ctx.fillRect(width - 1, 0, 1, height);
1234
1249
  }
1235
- if (!options.suppressFrameBottomBorder) ctx.fillRect(0, height - 1, width, 1);
1250
+ if (!horizontalBorderless && !options.suppressFrameBottomBorder) ctx.fillRect(0, height - 1, width, 1);
1236
1251
  if (selectionRange) {
1237
1252
  const rowStart = Math.min(selectionRange.anchor.rowIndex, selectionRange.focus.rowIndex);
1238
1253
  const rowEnd = Math.max(selectionRange.anchor.rowIndex, selectionRange.focus.rowIndex);
@@ -1536,6 +1551,7 @@ function TableInner({
1536
1551
  locale = "zh-CN",
1537
1552
  borderless,
1538
1553
  verticalBorderless,
1554
+ horizontalBorderless = false,
1539
1555
  striped = false,
1540
1556
  highlight,
1541
1557
  cellSpans = [],
@@ -2216,8 +2232,8 @@ function TableInner({
2216
2232
  const scroll = scrollRef.current;
2217
2233
  const rowScrollTop = fixedHeader ? scroll.top : Math.max(0, scroll.top - bodyTop);
2218
2234
  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 };
2219
- 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, 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 });
2220
- }, [bodyTop, bodyViewportHeight, bottomSummaryHeight, cellAnnotations, cellSpanLookup, columnDraggable, columnDropTarget, columns, editedCellHighlightColor, editedCellKeys, editing, renderHeight, filterValues, fixedHeader, getRowKey, hasStripedRows, hasVerticalBorders, headerDepth, headerHeight, headerLeafHeight, headerLeafTop, highlightsEditedCells, highlightsInsertedRows, insertedRowHighlightColor, insertedRowKeySet, isVirtualized, metrics, rowDragPreview, rowHeight, rowSelectionMode, rows, selectedColumnKeySet, selectedRowKeySet, selection, selectionRange, sortState, stripedColor, viewport.width, virtualOverscan]);
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]);
2221
2237
  const scheduleDraw = (0, import_react5.useCallback)(() => {
2222
2238
  if (frameRef.current === null) frameRef.current = requestAnimationFrame(draw);
2223
2239
  }, [draw]);
@@ -3581,11 +3597,21 @@ function TableInner({
3581
3597
  const visibleRight = Math.min(cellLeft + cellWidth, horizontalEnd, viewport.width);
3582
3598
  const visibleTop = Math.max(cellTop, verticalStart);
3583
3599
  const visibleBottom = Math.min(cellTop + cellHeight, renderHeight);
3600
+ if (column.editor && column.editor.type !== "text") {
3601
+ return {
3602
+ left: visibleLeft,
3603
+ top: visibleTop,
3604
+ width: Math.max(0, visibleRight - visibleLeft),
3605
+ height: Math.max(0, visibleBottom - visibleTop)
3606
+ };
3607
+ }
3584
3608
  return {
3585
- left: visibleLeft,
3586
- top: visibleTop,
3587
- width: Math.max(0, visibleRight - visibleLeft),
3588
- height: Math.max(0, visibleBottom - visibleTop)
3609
+ left: cellLeft,
3610
+ top: cellTop,
3611
+ width: cellWidth,
3612
+ height: cellHeight,
3613
+ textAlign: column.align ?? "left",
3614
+ clipPath: `inset(${Math.max(0, visibleTop - cellTop)}px ${Math.max(0, cellLeft + cellWidth - visibleRight)}px ${Math.max(0, cellTop + cellHeight - visibleBottom)}px ${Math.max(0, visibleLeft - cellLeft)}px)`
3589
3615
  };
3590
3616
  }, [bodyTop, columns, editing, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getScrollableColumnLeft, leftFixedOffsets, metrics, renderHeight, rightFixedOffsets, rightFixedWidth, rowHeight, viewport.width]);
3591
3617
  const handleColumnPointerDown = (0, import_react5.useCallback)((event) => {
@@ -4461,7 +4487,7 @@ function TableInner({
4461
4487
  };
4462
4488
  const currentScroll = scrollRef.current;
4463
4489
  const scrollerBottom = Math.max(0, bottomSummaryHeight - horizontalScrollbarHeight);
4464
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: `rvg-root${!hasVerticalBorders ? " is-vertical-borderless" : ""}${hasStripedRows ? " is-striped" : ""}${insertBusy ? " is-inserting" : ""}${deleteBusy ? " is-deleting" : ""} ${loading && !hasCompletedLoadRef.current && !hasCustomLoading ? "is-initial-loading" : ""} ${className}`, style: rootStyle, children: [
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: [
4465
4491
  /* @__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: [
4466
4492
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4467
4493
  "canvas",
package/dist/index.css CHANGED
@@ -105,6 +105,9 @@
105
105
  content: "";
106
106
  pointer-events: none;
107
107
  }
108
+ .rvg-root.is-horizontal-borderless .rvg-text-layer::before {
109
+ display: none;
110
+ }
108
111
  .rvg-vertical-scrollbar,
109
112
  .rvg-horizontal-scrollbar {
110
113
  position: absolute;
@@ -294,6 +297,10 @@
294
297
  .rvg-summary-row.is-top {
295
298
  border-bottom: 1px solid var(--rvg-color-grid);
296
299
  }
300
+ .rvg-root.is-horizontal-borderless .rvg-summary-row {
301
+ border-top: 0;
302
+ border-bottom: 0;
303
+ }
297
304
  .rvg-summary-scroll-clip {
298
305
  position: absolute;
299
306
  top: 0;
@@ -746,6 +753,9 @@
746
753
  background: var(--rvg-color-header-bg);
747
754
  box-sizing: border-box;
748
755
  }
756
+ .rvg-root.is-horizontal-borderless .rvg-header-group {
757
+ border-bottom: 0;
758
+ }
749
759
  .rvg-header-group.is-leaf {
750
760
  z-index: 2;
751
761
  }
@@ -1644,15 +1654,17 @@
1644
1654
  box-sizing: border-box;
1645
1655
  }
1646
1656
  .rvg-native-editor.has-clear {
1647
- padding-right: 32px;
1657
+ padding-right: 10px;
1648
1658
  }
1649
1659
  .rvg-native-editor input {
1650
1660
  min-width: 0;
1651
1661
  width: 100%;
1652
1662
  height: 100%;
1663
+ margin: 0;
1653
1664
  padding: 0;
1654
- transform: translateY(-1px);
1655
1665
  flex: 1;
1666
+ appearance: none;
1667
+ border-radius: 0;
1656
1668
  border: 0;
1657
1669
  outline: 0;
1658
1670
  background: transparent;
@@ -2419,12 +2431,18 @@
2419
2431
  .rvg-root.is-vertical-borderless .rvg-loading-row span {
2420
2432
  border-right: 0;
2421
2433
  }
2434
+ .rvg-root.is-horizontal-borderless .rvg-loading-row span {
2435
+ border-bottom: 0;
2436
+ }
2422
2437
  .rvg-root.is-striped .rvg-loading-row:nth-child(even) span {
2423
2438
  background: var(--rvg-color-stripe) !important;
2424
2439
  }
2425
2440
  .rvg-root.is-vertical-borderless .rvg-initial-loading::after {
2426
2441
  border-left: 0;
2427
2442
  }
2443
+ .rvg-root.is-horizontal-borderless .rvg-initial-loading::after {
2444
+ border-bottom: 0;
2445
+ }
2428
2446
  .rvg-loading-row i {
2429
2447
  display: block;
2430
2448
  height: 7px;
@@ -2528,3 +2546,14 @@
2528
2546
  opacity: 1;
2529
2547
  }
2530
2548
  }
2549
+ .rvg-native-editor input[type=number] {
2550
+ -moz-appearance: textfield;
2551
+ }
2552
+ .rvg-native-editor input::-webkit-inner-spin-button,
2553
+ .rvg-native-editor input::-webkit-outer-spin-button {
2554
+ margin: 0;
2555
+ -webkit-appearance: none;
2556
+ }
2557
+ .rvg-native-editor .rvg-clear-trigger {
2558
+ background: var(--rvg-color-bg);
2559
+ }
package/dist/index.d.cts CHANGED
@@ -347,6 +347,8 @@ interface TableProps<Row extends object> {
347
347
  borderless?: boolean;
348
348
  /** @deprecated Use borderless instead. */
349
349
  verticalBorderless?: boolean;
350
+ /** Hide horizontal row and frame separators while keeping vertical grid lines. */
351
+ horizontalBorderless?: boolean;
350
352
  /** Paint alternating row backgrounds. Pass a color string to customize the stripe color. */
351
353
  striped?: boolean | string;
352
354
  /** Visual highlight feedback configuration. All entries default to false. */
package/dist/index.d.ts CHANGED
@@ -347,6 +347,8 @@ interface TableProps<Row extends object> {
347
347
  borderless?: boolean;
348
348
  /** @deprecated Use borderless instead. */
349
349
  verticalBorderless?: boolean;
350
+ /** Hide horizontal row and frame separators while keeping vertical grid lines. */
351
+ horizontalBorderless?: boolean;
350
352
  /** Paint alternating row backgrounds. Pass a color string to customize the stripe color. */
351
353
  striped?: boolean | string;
352
354
  /** Visual highlight feedback configuration. All entries default to false. */
package/dist/index.js CHANGED
@@ -662,6 +662,18 @@ 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
+
665
677
  // src/components/editors/TextEditor.tsx
666
678
  import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
667
679
  function TextEditor({ value, type, style, labels, onChange, onCommit, onCancel }) {
@@ -675,6 +687,7 @@ function TextEditor({ value, type, style, labels, onChange, onCommit, onCancel }
675
687
  "input",
676
688
  {
677
689
  type,
690
+ style: { font: CELL_FONT, textAlign: style.textAlign },
678
691
  value,
679
692
  autoFocus: true,
680
693
  onChange: (event) => onChange(event.target.value),
@@ -899,7 +912,7 @@ function ellipsizeText(context, text, maxWidth) {
899
912
  return `${text.slice(0, start)}${ellipsis}`;
900
913
  }
901
914
  function paintGrid(options) {
902
- const { context: ctx, width, height, pixelRatio, scrollLeft, scrollTop, rowHeight, headerHeight, fixedHeader, verticalBorderless, 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, 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;
903
916
  const headerLeafTop = options.headerLeafTop ?? 0;
904
917
  const headerLeafHeight = options.headerLeafHeight ?? headerHeight;
905
918
  const colors = { ...COLORS, ...options.colors };
@@ -916,7 +929,8 @@ function paintGrid(options) {
916
929
  ctx.clearRect(0, 0, width, height);
917
930
  ctx.fillStyle = colors.background;
918
931
  ctx.fillRect(0, 0, width, height);
919
- ctx.font = "13px Inter, ui-sans-serif, system-ui, sans-serif";
932
+ ctx.font = CELL_FONT;
933
+ const cellBaselineOffset = getCellBaselineOffset(ctx);
920
934
  ctx.textBaseline = "middle";
921
935
  const leftFixedWidth = columns.reduce((value, column, index) => column.fixed === "left" ? value + metrics[index].width : value, 0);
922
936
  const rightFixedWidth = columns.reduce((value, column, index) => column.fixed === "right" ? value + metrics[index].width : value, 0);
@@ -1021,7 +1035,7 @@ function paintGrid(options) {
1021
1035
  ctx.fillStyle = colors.grid;
1022
1036
  if (!verticalBorderless) ctx.fillRect(x + cellWidth - 1, y, 1, cellHeight);
1023
1037
  if (rowIndex === rowBelowDragGap) ctx.fillRect(x, y, cellWidth, 1);
1024
- if (!options.suppressLastRowBottomBorder || rowIndex !== rows.length - 1) {
1038
+ if (!horizontalBorderless && (!options.suppressLastRowBottomBorder || rowIndex !== rows.length - 1)) {
1025
1039
  ctx.fillRect(x, y + cellHeight - 1, cellWidth, 1);
1026
1040
  }
1027
1041
  if (rowSpan > 1) {
@@ -1115,9 +1129,10 @@ function paintGrid(options) {
1115
1129
  ctx.rect(x + 2, y + 1, Math.max(0, cellWidth - 4), cellHeight - 2);
1116
1130
  ctx.clip();
1117
1131
  ctx.fillStyle = cellStyle?.color ?? colors.text;
1118
- ctx.font = "13px Inter, ui-sans-serif, system-ui, sans-serif";
1132
+ ctx.font = CELL_FONT;
1133
+ ctx.textBaseline = "alphabetic";
1119
1134
  ctx.textAlign = column.align === "right" ? "right" : column.align === "center" ? "center" : "left";
1120
- ctx.fillText(label, textX, y + cellHeight / 2);
1135
+ ctx.fillText(label, textX, y + cellHeight / 2 + cellBaselineOffset);
1121
1136
  ctx.restore();
1122
1137
  }
1123
1138
  }
@@ -1161,7 +1176,7 @@ function paintGrid(options) {
1161
1176
  }
1162
1177
  ctx.fillStyle = colors.grid;
1163
1178
  if (!verticalBorderless) ctx.fillRect(x + metric.width - 1, headerY, 1, headerHeight);
1164
- ctx.fillRect(x, headerY + headerHeight - 1, metric.width, 1);
1179
+ if (!horizontalBorderless) ctx.fillRect(x, headerY + headerHeight - 1, metric.width, 1);
1165
1180
  const column = columns[columnIndex];
1166
1181
  if (headerLeafTop === 0 && !column.rowSelection && !column.rowDragHandle && !column.rowNumber && !column.renderHeader && column.title) {
1167
1182
  ctx.font = "600 13px Inter, ui-sans-serif, system-ui, sans-serif";
@@ -1197,12 +1212,12 @@ function paintGrid(options) {
1197
1212
  ctx.fillRect(rightFixedLeft - 1, 0, 1, height);
1198
1213
  }
1199
1214
  ctx.fillStyle = colors.grid;
1200
- ctx.fillRect(0, 0, width, 1);
1215
+ if (!horizontalBorderless) ctx.fillRect(0, 0, width, 1);
1201
1216
  if (!verticalBorderless) {
1202
1217
  ctx.fillRect(0, 0, 1, height);
1203
1218
  ctx.fillRect(width - 1, 0, 1, height);
1204
1219
  }
1205
- if (!options.suppressFrameBottomBorder) ctx.fillRect(0, height - 1, width, 1);
1220
+ if (!horizontalBorderless && !options.suppressFrameBottomBorder) ctx.fillRect(0, height - 1, width, 1);
1206
1221
  if (selectionRange) {
1207
1222
  const rowStart = Math.min(selectionRange.anchor.rowIndex, selectionRange.focus.rowIndex);
1208
1223
  const rowEnd = Math.max(selectionRange.anchor.rowIndex, selectionRange.focus.rowIndex);
@@ -1506,6 +1521,7 @@ function TableInner({
1506
1521
  locale = "zh-CN",
1507
1522
  borderless,
1508
1523
  verticalBorderless,
1524
+ horizontalBorderless = false,
1509
1525
  striped = false,
1510
1526
  highlight,
1511
1527
  cellSpans = [],
@@ -2186,8 +2202,8 @@ function TableInner({
2186
2202
  const scroll = scrollRef.current;
2187
2203
  const rowScrollTop = fixedHeader ? scroll.top : Math.max(0, scroll.top - bodyTop);
2188
2204
  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 };
2189
- 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, 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 });
2190
- }, [bodyTop, bodyViewportHeight, bottomSummaryHeight, cellAnnotations, cellSpanLookup, columnDraggable, columnDropTarget, columns, editedCellHighlightColor, editedCellKeys, editing, renderHeight, filterValues, fixedHeader, getRowKey, hasStripedRows, hasVerticalBorders, headerDepth, headerHeight, headerLeafHeight, headerLeafTop, highlightsEditedCells, highlightsInsertedRows, insertedRowHighlightColor, insertedRowKeySet, isVirtualized, metrics, rowDragPreview, rowHeight, rowSelectionMode, rows, selectedColumnKeySet, selectedRowKeySet, selection, selectionRange, sortState, stripedColor, viewport.width, virtualOverscan]);
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]);
2191
2207
  const scheduleDraw = useCallback2(() => {
2192
2208
  if (frameRef.current === null) frameRef.current = requestAnimationFrame(draw);
2193
2209
  }, [draw]);
@@ -3551,11 +3567,21 @@ function TableInner({
3551
3567
  const visibleRight = Math.min(cellLeft + cellWidth, horizontalEnd, viewport.width);
3552
3568
  const visibleTop = Math.max(cellTop, verticalStart);
3553
3569
  const visibleBottom = Math.min(cellTop + cellHeight, renderHeight);
3570
+ if (column.editor && column.editor.type !== "text") {
3571
+ return {
3572
+ left: visibleLeft,
3573
+ top: visibleTop,
3574
+ width: Math.max(0, visibleRight - visibleLeft),
3575
+ height: Math.max(0, visibleBottom - visibleTop)
3576
+ };
3577
+ }
3554
3578
  return {
3555
- left: visibleLeft,
3556
- top: visibleTop,
3557
- width: Math.max(0, visibleRight - visibleLeft),
3558
- height: Math.max(0, visibleBottom - visibleTop)
3579
+ left: cellLeft,
3580
+ top: cellTop,
3581
+ width: cellWidth,
3582
+ height: cellHeight,
3583
+ textAlign: column.align ?? "left",
3584
+ clipPath: `inset(${Math.max(0, visibleTop - cellTop)}px ${Math.max(0, cellLeft + cellWidth - visibleRight)}px ${Math.max(0, cellTop + cellHeight - visibleBottom)}px ${Math.max(0, visibleLeft - cellLeft)}px)`
3559
3585
  };
3560
3586
  }, [bodyTop, columns, editing, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getScrollableColumnLeft, leftFixedOffsets, metrics, renderHeight, rightFixedOffsets, rightFixedWidth, rowHeight, viewport.width]);
3561
3587
  const handleColumnPointerDown = useCallback2((event) => {
@@ -4431,7 +4457,7 @@ function TableInner({
4431
4457
  };
4432
4458
  const currentScroll = scrollRef.current;
4433
4459
  const scrollerBottom = Math.max(0, bottomSummaryHeight - horizontalScrollbarHeight);
4434
- return /* @__PURE__ */ jsxs7("div", { className: `rvg-root${!hasVerticalBorders ? " is-vertical-borderless" : ""}${hasStripedRows ? " is-striped" : ""}${insertBusy ? " is-inserting" : ""}${deleteBusy ? " is-deleting" : ""} ${loading && !hasCompletedLoadRef.current && !hasCustomLoading ? "is-initial-loading" : ""} ${className}`, style: rootStyle, children: [
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: [
4435
4461
  /* @__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: [
4436
4462
  /* @__PURE__ */ jsx7(
4437
4463
  "canvas",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lensui/lens-table",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "A lightweight, canvas-rendered React table component for LensUI.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",