@lensui/lens-table 0.1.18 → 0.1.20

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
@@ -98,6 +98,7 @@ export function Example() {
98
98
  | `verticalBorderless` | `boolean` | `false` | 已兼容保留,建议改用 `borderless`。 |
99
99
  | `horizontalBorderless` | `boolean` | `false` | 是否隐藏横向行分隔线及上下边框,同时保留纵向列分隔线。可与 `striped` 配合使用。 |
100
100
  | `frameBorderless` | `boolean` | `false` | 是否隐藏表格四周外框,同时保留内部行列分隔线。 |
101
+ | `rowStyle` | `(row, rowIndex) => { color?: string; backgroundColor?: string }` | - | 自定义整行绘制样式,覆盖数据列及自动生成的选择列、序号列和拖拽列。 |
101
102
  | `striped` | `boolean \| string` | `false` | 是否显示斑马纹行背景。传`true` 使用主题变量,传颜色字符串可自定义斑马纹颜色,例如 `'#f6faff'`。 |
102
103
  | `highlight` | `{ editedCells?: boolean \| string; insertedRows?: boolean \| string }` | `{}` | 高亮反馈配置。`editedCells` 控制编辑过的单元格底色,`insertedRows` 控制乐观插入行底色;传 `true` 使用默认色,传颜色字符串可自定义。 |
103
104
  | `cellSpans` | `TableCellSpan[] \| ((ctx) => TableCellSpan \| { rowSpan?: number; colSpan?: number } \| null)` | `[]` | 合并主体单元格配置。可传数组,也可传函数按单元格生成规则。推荐使用 `rowKey` 定位;横向合并会限制在同一个固定列分区内,工具列不会参与合并。 |
package/dist/index.cjs CHANGED
@@ -1007,6 +1007,7 @@ function paintGrid(options) {
1007
1007
  const y = bodyTop + rowIndex * rowHeight - scrollTop + shift;
1008
1008
  const row = rows[rowIndex];
1009
1009
  const rowKey = getRowKey(row, rowIndex);
1010
+ const rowStyle = options.rowStyle?.(row, rowIndex);
1010
1011
  for (let columnIndex = 0; columnIndex < columns.length; columnIndex += 1) {
1011
1012
  if (layer === "scroll" ? columns[columnIndex].fixed !== void 0 : columns[columnIndex].fixed !== layer) continue;
1012
1013
  if (layer === "scroll" && (columnIndex < range.columnStart || columnIndex >= range.columnEnd)) continue;
@@ -1029,6 +1030,10 @@ function paintGrid(options) {
1029
1030
  ctx.fillStyle = striped && rowIndex % 2 === 1 ? colors.stripe : colors.background;
1030
1031
  ctx.fillRect(x, y, cellWidth, cellHeight);
1031
1032
  }
1033
+ if (rowStyle?.backgroundColor) {
1034
+ ctx.fillStyle = rowStyle.backgroundColor;
1035
+ ctx.fillRect(x, y, cellWidth, cellHeight);
1036
+ }
1032
1037
  if (isAxisSelected) {
1033
1038
  ctx.fillStyle = colors.axisSelectionFill;
1034
1039
  ctx.fillRect(x, y, cellWidth, cellHeight);
@@ -1074,14 +1079,14 @@ function paintGrid(options) {
1074
1079
  ctx.fillRect(x, y + cellHeight - 1, cellWidth, 1);
1075
1080
  }
1076
1081
  if (rowSpan > 1) {
1077
- const mergedFill = annotation?.type === "background" ? annotation.color : isSelectedCell || isRangeCell ? colors.selectionFill : highlightEditedCells && !insertedRowKeys.has(rowKey) && editedCellKeys.has(`${typeof rowKey}:${String(rowKey)}\0${column.key}`) ? colors.editedFill : highlightInsertedRows && insertedRowKeys.has(rowKey) ? colors.insertedFill : hoveredRowIndex === rowIndex ? colors.rowHoverFill : isAxisSelected ? colors.axisSelectionFill : cellStyle?.backgroundColor ?? (striped && rowIndex % 2 === 1 ? colors.stripe : colors.background);
1082
+ const mergedFill = annotation?.type === "background" ? annotation.color : isSelectedCell || isRangeCell ? colors.selectionFill : highlightEditedCells && !insertedRowKeys.has(rowKey) && editedCellKeys.has(`${typeof rowKey}:${String(rowKey)}\0${column.key}`) ? colors.editedFill : highlightInsertedRows && insertedRowKeys.has(rowKey) ? colors.insertedFill : hoveredRowIndex === rowIndex ? colors.rowHoverFill : isAxisSelected ? colors.axisSelectionFill : cellStyle?.backgroundColor ?? rowStyle?.backgroundColor ?? (striped && rowIndex % 2 === 1 ? colors.stripe : colors.background);
1078
1083
  ctx.fillStyle = mergedFill;
1079
1084
  for (let offset = 1; offset < rowSpan; offset += 1) {
1080
1085
  ctx.fillRect(x, y + offset * rowHeight - 1, cellWidth, 2);
1081
1086
  }
1082
1087
  }
1083
1088
  if (colSpan > 1 && !verticalBorderless) {
1084
- const mergedFill = annotation?.type === "background" ? annotation.color : isSelectedCell || isRangeCell ? colors.selectionFill : highlightEditedCells && !insertedRowKeys.has(rowKey) && editedCellKeys.has(`${typeof rowKey}:${String(rowKey)}\0${column.key}`) ? colors.editedFill : highlightInsertedRows && insertedRowKeys.has(rowKey) ? colors.insertedFill : hoveredRowIndex === rowIndex ? colors.rowHoverFill : isAxisSelected ? colors.axisSelectionFill : cellStyle?.backgroundColor ?? (striped && rowIndex % 2 === 1 ? colors.stripe : colors.background);
1089
+ const mergedFill = annotation?.type === "background" ? annotation.color : isSelectedCell || isRangeCell ? colors.selectionFill : highlightEditedCells && !insertedRowKeys.has(rowKey) && editedCellKeys.has(`${typeof rowKey}:${String(rowKey)}\0${column.key}`) ? colors.editedFill : highlightInsertedRows && insertedRowKeys.has(rowKey) ? colors.insertedFill : hoveredRowIndex === rowIndex ? colors.rowHoverFill : isAxisSelected ? colors.axisSelectionFill : cellStyle?.backgroundColor ?? rowStyle?.backgroundColor ?? (striped && rowIndex % 2 === 1 ? colors.stripe : colors.background);
1085
1090
  ctx.fillStyle = mergedFill;
1086
1091
  let offsetLeft = 0;
1087
1092
  for (let offset = 1; offset < colSpan; offset += 1) {
@@ -1178,7 +1183,7 @@ function paintGrid(options) {
1178
1183
  ctx.beginPath();
1179
1184
  ctx.rect(x + 2, y + 1, Math.max(0, cellWidth - 4), cellHeight - 2);
1180
1185
  ctx.clip();
1181
- ctx.fillStyle = isAxisSelected && !isSelectedCell && !isRangeCell ? colors.axisSelectionText : cellStyle?.color ?? colors.text;
1186
+ ctx.fillStyle = isAxisSelected && !isSelectedCell && !isRangeCell ? colors.axisSelectionText : cellStyle?.color ?? rowStyle?.color ?? colors.text;
1182
1187
  ctx.font = createCellFont(bodyFontSize);
1183
1188
  ctx.textBaseline = "alphabetic";
1184
1189
  ctx.textAlign = column.align === "right" ? "right" : column.align === "center" ? "center" : "left";
@@ -1626,6 +1631,7 @@ function TableInner({
1626
1631
  horizontalBorderless = false,
1627
1632
  frameBorderless = false,
1628
1633
  striped = false,
1634
+ rowStyle,
1629
1635
  highlight,
1630
1636
  cellSpans = [],
1631
1637
  loading = false,
@@ -1754,6 +1760,7 @@ function TableInner({
1754
1760
  }, [columns, headerCells, headerDepth]);
1755
1761
  const canvasRef = (0, import_react5.useRef)(null);
1756
1762
  const scrollerRef = (0, import_react5.useRef)(null);
1763
+ const rootRef = (0, import_react5.useRef)(null);
1757
1764
  const frameRef = (0, import_react5.useRef)(null);
1758
1765
  const textFrameRef = (0, import_react5.useRef)(null);
1759
1766
  const toastTimerRef = (0, import_react5.useRef)(null);
@@ -1904,6 +1911,7 @@ function TableInner({
1904
1911
  const [toast, setToast] = (0, import_react5.useState)(null);
1905
1912
  const [resizeGuideX, setResizeGuideX] = (0, import_react5.useState)(null);
1906
1913
  const [resizeGuideTop, setResizeGuideTop] = (0, import_react5.useState)(0);
1914
+ const [hoveredHeaderColumnIndex, setHoveredHeaderColumnIndex] = (0, import_react5.useState)(null);
1907
1915
  const [hoveredHeaderAction, setHoveredHeaderAction] = (0, import_react5.useState)(null);
1908
1916
  const [visibleHeaderTooltip, setVisibleHeaderTooltip] = (0, import_react5.useState)(null);
1909
1917
  const [hoveredCellTooltip, setHoveredCellTooltip] = (0, import_react5.useState)(null);
@@ -2037,6 +2045,18 @@ function TableInner({
2037
2045
  const [selection, setSelection] = useControllableValue(resolvedSelectedCell, resolvedDefaultSelectedCell, handleSelectedCellChange);
2038
2046
  const [selectionRange, setSelectionRange] = (0, import_react5.useState)(null);
2039
2047
  const rangeDragRef = (0, import_react5.useRef)(null);
2048
+ (0, import_react5.useEffect)(() => {
2049
+ if (!selection) return void 0;
2050
+ const handleOutsidePointerDown = (event) => {
2051
+ const target = event.target;
2052
+ if (!(target instanceof Node) || rootRef.current?.contains(target)) return;
2053
+ clickedCellRef.current = null;
2054
+ setSelectionRange(null);
2055
+ setSelection(null);
2056
+ };
2057
+ document.addEventListener("pointerdown", handleOutsidePointerDown);
2058
+ return () => document.removeEventListener("pointerdown", handleOutsidePointerDown);
2059
+ }, [selection, setSelection]);
2040
2060
  const applyRowOrder = (0, import_react5.useCallback)((sourceIndex, targetIndex) => {
2041
2061
  if (sourceIndex === targetIndex || sourceIndex < 0 || targetIndex < 0 || sourceIndex >= rows.length || targetIndex >= rows.length) return;
2042
2062
  const nextEntries = rows.map((row, index) => ({ row, key: getDataRowKey(row, index) }));
@@ -2312,8 +2332,8 @@ function TableInner({
2312
2332
  const scroll = scrollRef.current;
2313
2333
  const rowScrollTop = fixedHeader ? scroll.top : Math.max(0, scroll.top - bodyTop);
2314
2334
  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 };
2315
- 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, extendVerticalGridLines: !autoHeight, 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 });
2316
- }, [autoHeight, 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]);
2335
+ 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, extendVerticalGridLines: !autoHeight, striped: hasStripedRows, columnDraggable, sortState, filterValues, hoveredHeaderAction: hoveredHeaderActionRef.current, rows, rowStyle, 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 });
2336
+ }, [autoHeight, 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, rowStyle, rows, selectedColumnKeySet, selectedRowKeySet, selection, selectionRange, sortState, stripedColor, viewport.width, virtualOverscan]);
2317
2337
  const scheduleDraw = (0, import_react5.useCallback)(() => {
2318
2338
  if (frameRef.current === null) frameRef.current = requestAnimationFrame(draw);
2319
2339
  }, [draw]);
@@ -2797,7 +2817,7 @@ function TableInner({
2797
2817
  const left = Math.round(Math.max(rawLeft - borderWidth, horizontalStart));
2798
2818
  const right = Math.round(Math.min(rawRight + 1, horizontalEnd, viewport.width));
2799
2819
  const top = Math.round(Math.max(rawTop - borderWidth, verticalStart));
2800
- const bottom = Math.round(Math.min(rawBottom + 1, renderHeight));
2820
+ const bottom = Math.round(Math.min(rawBottom + borderWidth, renderHeight));
2801
2821
  focus.style.display = "block";
2802
2822
  focus.style.left = `${left}px`;
2803
2823
  focus.style.top = `${top}px`;
@@ -3805,6 +3825,7 @@ function TableInner({
3805
3825
  if (column.rowSelection || column.rowDragHandle || column.rowNumber) return null;
3806
3826
  const isAxisSelected = selectedColumnKeySet.has(column.key);
3807
3827
  const selectedClassName = isAxisSelected ? " is-axis-selected" : "";
3828
+ const hoveredColumnClassName = hoveredHeaderColumnIndex === columnIndex ? " is-header-hovered" : "";
3808
3829
  const top = headerLeafTop + (headerLeafHeight - headerActionSize) / 2;
3809
3830
  const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex), headerActionSlotWidth);
3810
3831
  let right = left + metrics[columnIndex].width - (visibleActions.drag ? headerActionSlotWidth : 2);
@@ -3812,21 +3833,21 @@ function TableInner({
3812
3833
  if (visibleActions.drag) {
3813
3834
  const hovered = hoveredHeaderAction?.columnIndex === columnIndex && hoveredHeaderAction.action === "drag";
3814
3835
  icons.push(
3815
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(HeaderDragIcon, { className: `rvg-header-icon rvg-header-drag-icon${selectedClassName}${hovered ? " is-hovered" : ""}`, style: { left: left + metrics[columnIndex].width - headerActionSlotWidth, top } }, "drag")
3836
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(HeaderDragIcon, { className: `rvg-header-icon rvg-header-drag-icon${selectedClassName}${hoveredColumnClassName}${hovered ? " is-hovered" : ""}`, style: { left: left + metrics[columnIndex].width - headerActionSlotWidth, top } }, "drag")
3816
3837
  );
3817
3838
  }
3818
3839
  if (visibleActions.sort) {
3819
3840
  const hovered = hoveredHeaderAction?.columnIndex === columnIndex && hoveredHeaderAction.action === "sort";
3820
3841
  const direction = sortState?.columnKey === column.key ? sortState.direction : null;
3821
3842
  icons.push(
3822
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(HeaderSortIcon, { className: `rvg-header-icon${selectedClassName}${hovered ? " is-hovered" : ""}`, style: { left: right - headerActionSlotWidth, top }, direction }, "sort")
3843
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(HeaderSortIcon, { className: `rvg-header-icon${selectedClassName}${hoveredColumnClassName}${direction ? " is-active" : ""}${hovered ? " is-hovered" : ""}`, style: { left: right - headerActionSlotWidth, top }, direction }, "sort")
3823
3844
  );
3824
3845
  right -= headerActionSlotWidth;
3825
3846
  }
3826
3847
  if (visibleActions.filter) {
3827
3848
  const hovered = hoveredHeaderAction?.columnIndex === columnIndex && hoveredHeaderAction.action === "filter";
3828
3849
  icons.push(
3829
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(HeaderSearchIcon, { className: `rvg-header-icon rvg-header-search-icon${selectedClassName}${hovered ? " is-hovered" : ""}${filterValues[column.key] ? " is-active" : ""}`, style: { left: right - headerActionSlotWidth, top: top + 1 } }, "filter")
3850
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(HeaderSearchIcon, { className: `rvg-header-icon rvg-header-search-icon${selectedClassName}${hoveredColumnClassName}${hovered ? " is-hovered" : ""}${filterValues[column.key] ? " is-active" : ""}`, style: { left: right - headerActionSlotWidth, top: top + 1 } }, "filter")
3830
3851
  );
3831
3852
  }
3832
3853
  return icons;
@@ -4364,7 +4385,7 @@ function TableInner({
4364
4385
  const left = Math.round(Math.max(rawLeft - borderWidth, horizontalStart));
4365
4386
  const right = Math.round(Math.min(rawRight + 1, horizontalEnd, viewport.width));
4366
4387
  const top = Math.round(Math.max(rawTop - borderWidth, verticalStart));
4367
- const bottom = Math.round(Math.min(rawBottom + 1, renderHeight));
4388
+ const bottom = Math.round(Math.min(rawBottom + borderWidth, renderHeight));
4368
4389
  return {
4369
4390
  left,
4370
4391
  top,
@@ -4596,7 +4617,7 @@ function TableInner({
4596
4617
  };
4597
4618
  const currentScroll = scrollRef.current;
4598
4619
  const scrollerBottom = Math.max(0, bottomSummaryHeight - horizontalScrollbarHeight);
4599
- 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: [
4620
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { ref: rootRef, 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: [
4600
4621
  /* @__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: [
4601
4622
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4602
4623
  "canvas",
@@ -4616,6 +4637,8 @@ function TableInner({
4616
4637
  const columnIndex = locateColumn(event.clientX);
4617
4638
  const inHeader = localY >= headerY && localY < headerY + headerHeight;
4618
4639
  const inLeafHeader = inHeader && localY >= headerY + headerLeafTop;
4640
+ const nextHoveredHeaderColumnIndex = inLeafHeader && columnIndex >= 0 ? columnIndex : null;
4641
+ setHoveredHeaderColumnIndex((current) => current === nextHoveredHeaderColumnIndex ? current : nextHoveredHeaderColumnIndex);
4619
4642
  const hoveredHeaderCell = inHeader ? locateHeaderCell(event.clientX, event.clientY) : null;
4620
4643
  const hoveredAction = inLeafHeader ? locateHeaderAction(event.clientX, columnIndex) : null;
4621
4644
  const hoveredDragHandle = Boolean(hoveredHeaderCell && locateHeaderCellDragHandle(event.clientX, hoveredHeaderCell));
@@ -4701,6 +4724,7 @@ function TableInner({
4701
4724
  },
4702
4725
  onMouseLeave: (event) => {
4703
4726
  event.currentTarget.style.cursor = "default";
4727
+ setHoveredHeaderColumnIndex(null);
4704
4728
  showCellTooltip(null);
4705
4729
  showHeaderTooltipAfterDelay(null);
4706
4730
  if (hoveredHeaderActionRef.current) {
@@ -4778,7 +4802,12 @@ function TableInner({
4778
4802
  return;
4779
4803
  }
4780
4804
  const cell = locateCell(event.clientX, event.clientY);
4781
- if (!cell) return;
4805
+ if (!cell) {
4806
+ clickedCellRef.current = null;
4807
+ setSelectionRange(null);
4808
+ setSelection(null);
4809
+ return;
4810
+ }
4782
4811
  if (columns[cell.columnIndex].rowDragHandle || columns[cell.columnIndex].rowNumber) {
4783
4812
  clickedCellRef.current = null;
4784
4813
  return;
package/dist/index.css CHANGED
@@ -853,7 +853,12 @@
853
853
  width: var(--rvg-header-action-size, 14px);
854
854
  height: var(--rvg-header-action-size, 14px);
855
855
  overflow: visible;
856
+ opacity: 0;
856
857
  color: var(--rvg-color-icon);
858
+ transition: opacity 120ms ease, color 120ms ease;
859
+ }
860
+ .rvg-header-icon.is-header-hovered {
861
+ opacity: 1;
857
862
  }
858
863
  .rvg-header-icon.is-hovered {
859
864
  color: var(--rvg-color-muted);
@@ -864,6 +869,7 @@
864
869
  color: var(--rvg-color-axis-selection-text);
865
870
  }
866
871
  .rvg-root.is-initial-loading .rvg-header-icon {
872
+ opacity: 1;
867
873
  color: #d6dbe0;
868
874
  animation: rvg-loading-icon-pulse 1.15s ease-in-out infinite alternate;
869
875
  }
package/dist/index.d.cts CHANGED
@@ -334,6 +334,11 @@ type TableContextMenu<Row> = boolean | TableContextMenuConfig<Row>;
334
334
  interface TableProps<Row extends object> {
335
335
  columns: GridColumn<Row>[];
336
336
  rows: Row[];
337
+ /** Custom row paint style. Applies to data and generated utility columns. */
338
+ rowStyle?: (row: Row, rowIndex: number) => {
339
+ color?: string;
340
+ backgroundColor?: string;
341
+ };
337
342
  /** Stable row id. Defaults to the row's `id` field when omitted. */
338
343
  rowKey?: keyof Row | ((row: Row, index: number) => GridKey);
339
344
  width?: number | string;
package/dist/index.d.ts CHANGED
@@ -334,6 +334,11 @@ type TableContextMenu<Row> = boolean | TableContextMenuConfig<Row>;
334
334
  interface TableProps<Row extends object> {
335
335
  columns: GridColumn<Row>[];
336
336
  rows: Row[];
337
+ /** Custom row paint style. Applies to data and generated utility columns. */
338
+ rowStyle?: (row: Row, rowIndex: number) => {
339
+ color?: string;
340
+ backgroundColor?: string;
341
+ };
337
342
  /** Stable row id. Defaults to the row's `id` field when omitted. */
338
343
  rowKey?: keyof Row | ((row: Row, index: number) => GridKey);
339
344
  width?: number | string;
package/dist/index.js CHANGED
@@ -977,6 +977,7 @@ function paintGrid(options) {
977
977
  const y = bodyTop + rowIndex * rowHeight - scrollTop + shift;
978
978
  const row = rows[rowIndex];
979
979
  const rowKey = getRowKey(row, rowIndex);
980
+ const rowStyle = options.rowStyle?.(row, rowIndex);
980
981
  for (let columnIndex = 0; columnIndex < columns.length; columnIndex += 1) {
981
982
  if (layer === "scroll" ? columns[columnIndex].fixed !== void 0 : columns[columnIndex].fixed !== layer) continue;
982
983
  if (layer === "scroll" && (columnIndex < range.columnStart || columnIndex >= range.columnEnd)) continue;
@@ -999,6 +1000,10 @@ function paintGrid(options) {
999
1000
  ctx.fillStyle = striped && rowIndex % 2 === 1 ? colors.stripe : colors.background;
1000
1001
  ctx.fillRect(x, y, cellWidth, cellHeight);
1001
1002
  }
1003
+ if (rowStyle?.backgroundColor) {
1004
+ ctx.fillStyle = rowStyle.backgroundColor;
1005
+ ctx.fillRect(x, y, cellWidth, cellHeight);
1006
+ }
1002
1007
  if (isAxisSelected) {
1003
1008
  ctx.fillStyle = colors.axisSelectionFill;
1004
1009
  ctx.fillRect(x, y, cellWidth, cellHeight);
@@ -1044,14 +1049,14 @@ function paintGrid(options) {
1044
1049
  ctx.fillRect(x, y + cellHeight - 1, cellWidth, 1);
1045
1050
  }
1046
1051
  if (rowSpan > 1) {
1047
- const mergedFill = annotation?.type === "background" ? annotation.color : isSelectedCell || isRangeCell ? colors.selectionFill : highlightEditedCells && !insertedRowKeys.has(rowKey) && editedCellKeys.has(`${typeof rowKey}:${String(rowKey)}\0${column.key}`) ? colors.editedFill : highlightInsertedRows && insertedRowKeys.has(rowKey) ? colors.insertedFill : hoveredRowIndex === rowIndex ? colors.rowHoverFill : isAxisSelected ? colors.axisSelectionFill : cellStyle?.backgroundColor ?? (striped && rowIndex % 2 === 1 ? colors.stripe : colors.background);
1052
+ const mergedFill = annotation?.type === "background" ? annotation.color : isSelectedCell || isRangeCell ? colors.selectionFill : highlightEditedCells && !insertedRowKeys.has(rowKey) && editedCellKeys.has(`${typeof rowKey}:${String(rowKey)}\0${column.key}`) ? colors.editedFill : highlightInsertedRows && insertedRowKeys.has(rowKey) ? colors.insertedFill : hoveredRowIndex === rowIndex ? colors.rowHoverFill : isAxisSelected ? colors.axisSelectionFill : cellStyle?.backgroundColor ?? rowStyle?.backgroundColor ?? (striped && rowIndex % 2 === 1 ? colors.stripe : colors.background);
1048
1053
  ctx.fillStyle = mergedFill;
1049
1054
  for (let offset = 1; offset < rowSpan; offset += 1) {
1050
1055
  ctx.fillRect(x, y + offset * rowHeight - 1, cellWidth, 2);
1051
1056
  }
1052
1057
  }
1053
1058
  if (colSpan > 1 && !verticalBorderless) {
1054
- const mergedFill = annotation?.type === "background" ? annotation.color : isSelectedCell || isRangeCell ? colors.selectionFill : highlightEditedCells && !insertedRowKeys.has(rowKey) && editedCellKeys.has(`${typeof rowKey}:${String(rowKey)}\0${column.key}`) ? colors.editedFill : highlightInsertedRows && insertedRowKeys.has(rowKey) ? colors.insertedFill : hoveredRowIndex === rowIndex ? colors.rowHoverFill : isAxisSelected ? colors.axisSelectionFill : cellStyle?.backgroundColor ?? (striped && rowIndex % 2 === 1 ? colors.stripe : colors.background);
1059
+ const mergedFill = annotation?.type === "background" ? annotation.color : isSelectedCell || isRangeCell ? colors.selectionFill : highlightEditedCells && !insertedRowKeys.has(rowKey) && editedCellKeys.has(`${typeof rowKey}:${String(rowKey)}\0${column.key}`) ? colors.editedFill : highlightInsertedRows && insertedRowKeys.has(rowKey) ? colors.insertedFill : hoveredRowIndex === rowIndex ? colors.rowHoverFill : isAxisSelected ? colors.axisSelectionFill : cellStyle?.backgroundColor ?? rowStyle?.backgroundColor ?? (striped && rowIndex % 2 === 1 ? colors.stripe : colors.background);
1055
1060
  ctx.fillStyle = mergedFill;
1056
1061
  let offsetLeft = 0;
1057
1062
  for (let offset = 1; offset < colSpan; offset += 1) {
@@ -1148,7 +1153,7 @@ function paintGrid(options) {
1148
1153
  ctx.beginPath();
1149
1154
  ctx.rect(x + 2, y + 1, Math.max(0, cellWidth - 4), cellHeight - 2);
1150
1155
  ctx.clip();
1151
- ctx.fillStyle = isAxisSelected && !isSelectedCell && !isRangeCell ? colors.axisSelectionText : cellStyle?.color ?? colors.text;
1156
+ ctx.fillStyle = isAxisSelected && !isSelectedCell && !isRangeCell ? colors.axisSelectionText : cellStyle?.color ?? rowStyle?.color ?? colors.text;
1152
1157
  ctx.font = createCellFont(bodyFontSize);
1153
1158
  ctx.textBaseline = "alphabetic";
1154
1159
  ctx.textAlign = column.align === "right" ? "right" : column.align === "center" ? "center" : "left";
@@ -1596,6 +1601,7 @@ function TableInner({
1596
1601
  horizontalBorderless = false,
1597
1602
  frameBorderless = false,
1598
1603
  striped = false,
1604
+ rowStyle,
1599
1605
  highlight,
1600
1606
  cellSpans = [],
1601
1607
  loading = false,
@@ -1724,6 +1730,7 @@ function TableInner({
1724
1730
  }, [columns, headerCells, headerDepth]);
1725
1731
  const canvasRef = useRef2(null);
1726
1732
  const scrollerRef = useRef2(null);
1733
+ const rootRef = useRef2(null);
1727
1734
  const frameRef = useRef2(null);
1728
1735
  const textFrameRef = useRef2(null);
1729
1736
  const toastTimerRef = useRef2(null);
@@ -1874,6 +1881,7 @@ function TableInner({
1874
1881
  const [toast, setToast] = useState4(null);
1875
1882
  const [resizeGuideX, setResizeGuideX] = useState4(null);
1876
1883
  const [resizeGuideTop, setResizeGuideTop] = useState4(0);
1884
+ const [hoveredHeaderColumnIndex, setHoveredHeaderColumnIndex] = useState4(null);
1877
1885
  const [hoveredHeaderAction, setHoveredHeaderAction] = useState4(null);
1878
1886
  const [visibleHeaderTooltip, setVisibleHeaderTooltip] = useState4(null);
1879
1887
  const [hoveredCellTooltip, setHoveredCellTooltip] = useState4(null);
@@ -2007,6 +2015,18 @@ function TableInner({
2007
2015
  const [selection, setSelection] = useControllableValue(resolvedSelectedCell, resolvedDefaultSelectedCell, handleSelectedCellChange);
2008
2016
  const [selectionRange, setSelectionRange] = useState4(null);
2009
2017
  const rangeDragRef = useRef2(null);
2018
+ useEffect2(() => {
2019
+ if (!selection) return void 0;
2020
+ const handleOutsidePointerDown = (event) => {
2021
+ const target = event.target;
2022
+ if (!(target instanceof Node) || rootRef.current?.contains(target)) return;
2023
+ clickedCellRef.current = null;
2024
+ setSelectionRange(null);
2025
+ setSelection(null);
2026
+ };
2027
+ document.addEventListener("pointerdown", handleOutsidePointerDown);
2028
+ return () => document.removeEventListener("pointerdown", handleOutsidePointerDown);
2029
+ }, [selection, setSelection]);
2010
2030
  const applyRowOrder = useCallback2((sourceIndex, targetIndex) => {
2011
2031
  if (sourceIndex === targetIndex || sourceIndex < 0 || targetIndex < 0 || sourceIndex >= rows.length || targetIndex >= rows.length) return;
2012
2032
  const nextEntries = rows.map((row, index) => ({ row, key: getDataRowKey(row, index) }));
@@ -2282,8 +2302,8 @@ function TableInner({
2282
2302
  const scroll = scrollRef.current;
2283
2303
  const rowScrollTop = fixedHeader ? scroll.top : Math.max(0, scroll.top - bodyTop);
2284
2304
  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 };
2285
- 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, extendVerticalGridLines: !autoHeight, 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 });
2286
- }, [autoHeight, 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]);
2305
+ 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, extendVerticalGridLines: !autoHeight, striped: hasStripedRows, columnDraggable, sortState, filterValues, hoveredHeaderAction: hoveredHeaderActionRef.current, rows, rowStyle, 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 });
2306
+ }, [autoHeight, 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, rowStyle, rows, selectedColumnKeySet, selectedRowKeySet, selection, selectionRange, sortState, stripedColor, viewport.width, virtualOverscan]);
2287
2307
  const scheduleDraw = useCallback2(() => {
2288
2308
  if (frameRef.current === null) frameRef.current = requestAnimationFrame(draw);
2289
2309
  }, [draw]);
@@ -2767,7 +2787,7 @@ function TableInner({
2767
2787
  const left = Math.round(Math.max(rawLeft - borderWidth, horizontalStart));
2768
2788
  const right = Math.round(Math.min(rawRight + 1, horizontalEnd, viewport.width));
2769
2789
  const top = Math.round(Math.max(rawTop - borderWidth, verticalStart));
2770
- const bottom = Math.round(Math.min(rawBottom + 1, renderHeight));
2790
+ const bottom = Math.round(Math.min(rawBottom + borderWidth, renderHeight));
2771
2791
  focus.style.display = "block";
2772
2792
  focus.style.left = `${left}px`;
2773
2793
  focus.style.top = `${top}px`;
@@ -3775,6 +3795,7 @@ function TableInner({
3775
3795
  if (column.rowSelection || column.rowDragHandle || column.rowNumber) return null;
3776
3796
  const isAxisSelected = selectedColumnKeySet.has(column.key);
3777
3797
  const selectedClassName = isAxisSelected ? " is-axis-selected" : "";
3798
+ const hoveredColumnClassName = hoveredHeaderColumnIndex === columnIndex ? " is-header-hovered" : "";
3778
3799
  const top = headerLeafTop + (headerLeafHeight - headerActionSize) / 2;
3779
3800
  const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex), headerActionSlotWidth);
3780
3801
  let right = left + metrics[columnIndex].width - (visibleActions.drag ? headerActionSlotWidth : 2);
@@ -3782,21 +3803,21 @@ function TableInner({
3782
3803
  if (visibleActions.drag) {
3783
3804
  const hovered = hoveredHeaderAction?.columnIndex === columnIndex && hoveredHeaderAction.action === "drag";
3784
3805
  icons.push(
3785
- /* @__PURE__ */ jsx7(HeaderDragIcon, { className: `rvg-header-icon rvg-header-drag-icon${selectedClassName}${hovered ? " is-hovered" : ""}`, style: { left: left + metrics[columnIndex].width - headerActionSlotWidth, top } }, "drag")
3806
+ /* @__PURE__ */ jsx7(HeaderDragIcon, { className: `rvg-header-icon rvg-header-drag-icon${selectedClassName}${hoveredColumnClassName}${hovered ? " is-hovered" : ""}`, style: { left: left + metrics[columnIndex].width - headerActionSlotWidth, top } }, "drag")
3786
3807
  );
3787
3808
  }
3788
3809
  if (visibleActions.sort) {
3789
3810
  const hovered = hoveredHeaderAction?.columnIndex === columnIndex && hoveredHeaderAction.action === "sort";
3790
3811
  const direction = sortState?.columnKey === column.key ? sortState.direction : null;
3791
3812
  icons.push(
3792
- /* @__PURE__ */ jsx7(HeaderSortIcon, { className: `rvg-header-icon${selectedClassName}${hovered ? " is-hovered" : ""}`, style: { left: right - headerActionSlotWidth, top }, direction }, "sort")
3813
+ /* @__PURE__ */ jsx7(HeaderSortIcon, { className: `rvg-header-icon${selectedClassName}${hoveredColumnClassName}${direction ? " is-active" : ""}${hovered ? " is-hovered" : ""}`, style: { left: right - headerActionSlotWidth, top }, direction }, "sort")
3793
3814
  );
3794
3815
  right -= headerActionSlotWidth;
3795
3816
  }
3796
3817
  if (visibleActions.filter) {
3797
3818
  const hovered = hoveredHeaderAction?.columnIndex === columnIndex && hoveredHeaderAction.action === "filter";
3798
3819
  icons.push(
3799
- /* @__PURE__ */ jsx7(HeaderSearchIcon, { className: `rvg-header-icon rvg-header-search-icon${selectedClassName}${hovered ? " is-hovered" : ""}${filterValues[column.key] ? " is-active" : ""}`, style: { left: right - headerActionSlotWidth, top: top + 1 } }, "filter")
3820
+ /* @__PURE__ */ jsx7(HeaderSearchIcon, { className: `rvg-header-icon rvg-header-search-icon${selectedClassName}${hoveredColumnClassName}${hovered ? " is-hovered" : ""}${filterValues[column.key] ? " is-active" : ""}`, style: { left: right - headerActionSlotWidth, top: top + 1 } }, "filter")
3800
3821
  );
3801
3822
  }
3802
3823
  return icons;
@@ -4334,7 +4355,7 @@ function TableInner({
4334
4355
  const left = Math.round(Math.max(rawLeft - borderWidth, horizontalStart));
4335
4356
  const right = Math.round(Math.min(rawRight + 1, horizontalEnd, viewport.width));
4336
4357
  const top = Math.round(Math.max(rawTop - borderWidth, verticalStart));
4337
- const bottom = Math.round(Math.min(rawBottom + 1, renderHeight));
4358
+ const bottom = Math.round(Math.min(rawBottom + borderWidth, renderHeight));
4338
4359
  return {
4339
4360
  left,
4340
4361
  top,
@@ -4566,7 +4587,7 @@ function TableInner({
4566
4587
  };
4567
4588
  const currentScroll = scrollRef.current;
4568
4589
  const scrollerBottom = Math.max(0, bottomSummaryHeight - horizontalScrollbarHeight);
4569
- 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: [
4590
+ return /* @__PURE__ */ jsxs7("div", { ref: rootRef, 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: [
4570
4591
  /* @__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: [
4571
4592
  /* @__PURE__ */ jsx7(
4572
4593
  "canvas",
@@ -4586,6 +4607,8 @@ function TableInner({
4586
4607
  const columnIndex = locateColumn(event.clientX);
4587
4608
  const inHeader = localY >= headerY && localY < headerY + headerHeight;
4588
4609
  const inLeafHeader = inHeader && localY >= headerY + headerLeafTop;
4610
+ const nextHoveredHeaderColumnIndex = inLeafHeader && columnIndex >= 0 ? columnIndex : null;
4611
+ setHoveredHeaderColumnIndex((current) => current === nextHoveredHeaderColumnIndex ? current : nextHoveredHeaderColumnIndex);
4589
4612
  const hoveredHeaderCell = inHeader ? locateHeaderCell(event.clientX, event.clientY) : null;
4590
4613
  const hoveredAction = inLeafHeader ? locateHeaderAction(event.clientX, columnIndex) : null;
4591
4614
  const hoveredDragHandle = Boolean(hoveredHeaderCell && locateHeaderCellDragHandle(event.clientX, hoveredHeaderCell));
@@ -4671,6 +4694,7 @@ function TableInner({
4671
4694
  },
4672
4695
  onMouseLeave: (event) => {
4673
4696
  event.currentTarget.style.cursor = "default";
4697
+ setHoveredHeaderColumnIndex(null);
4674
4698
  showCellTooltip(null);
4675
4699
  showHeaderTooltipAfterDelay(null);
4676
4700
  if (hoveredHeaderActionRef.current) {
@@ -4748,7 +4772,12 @@ function TableInner({
4748
4772
  return;
4749
4773
  }
4750
4774
  const cell = locateCell(event.clientX, event.clientY);
4751
- if (!cell) return;
4775
+ if (!cell) {
4776
+ clickedCellRef.current = null;
4777
+ setSelectionRange(null);
4778
+ setSelection(null);
4779
+ return;
4780
+ }
4752
4781
  if (columns[cell.columnIndex].rowDragHandle || columns[cell.columnIndex].rowNumber) {
4753
4782
  clickedCellRef.current = null;
4754
4783
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lensui/lens-table",
3
- "version": "0.1.18",
3
+ "version": "0.1.20",
4
4
  "description": "A lightweight, canvas-rendered React table component for LensUI.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",