@lensui/lens-table 0.1.18 → 0.1.19

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);
@@ -2037,6 +2044,18 @@ function TableInner({
2037
2044
  const [selection, setSelection] = useControllableValue(resolvedSelectedCell, resolvedDefaultSelectedCell, handleSelectedCellChange);
2038
2045
  const [selectionRange, setSelectionRange] = (0, import_react5.useState)(null);
2039
2046
  const rangeDragRef = (0, import_react5.useRef)(null);
2047
+ (0, import_react5.useEffect)(() => {
2048
+ if (!selection) return void 0;
2049
+ const handleOutsidePointerDown = (event) => {
2050
+ const target = event.target;
2051
+ if (!(target instanceof Node) || rootRef.current?.contains(target)) return;
2052
+ clickedCellRef.current = null;
2053
+ setSelectionRange(null);
2054
+ setSelection(null);
2055
+ };
2056
+ document.addEventListener("pointerdown", handleOutsidePointerDown);
2057
+ return () => document.removeEventListener("pointerdown", handleOutsidePointerDown);
2058
+ }, [selection, setSelection]);
2040
2059
  const applyRowOrder = (0, import_react5.useCallback)((sourceIndex, targetIndex) => {
2041
2060
  if (sourceIndex === targetIndex || sourceIndex < 0 || targetIndex < 0 || sourceIndex >= rows.length || targetIndex >= rows.length) return;
2042
2061
  const nextEntries = rows.map((row, index) => ({ row, key: getDataRowKey(row, index) }));
@@ -2312,8 +2331,8 @@ function TableInner({
2312
2331
  const scroll = scrollRef.current;
2313
2332
  const rowScrollTop = fixedHeader ? scroll.top : Math.max(0, scroll.top - bodyTop);
2314
2333
  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]);
2334
+ 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 });
2335
+ }, [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
2336
  const scheduleDraw = (0, import_react5.useCallback)(() => {
2318
2337
  if (frameRef.current === null) frameRef.current = requestAnimationFrame(draw);
2319
2338
  }, [draw]);
@@ -4596,7 +4615,7 @@ function TableInner({
4596
4615
  };
4597
4616
  const currentScroll = scrollRef.current;
4598
4617
  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: [
4618
+ 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
4619
  /* @__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
4620
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4602
4621
  "canvas",
@@ -4778,7 +4797,12 @@ function TableInner({
4778
4797
  return;
4779
4798
  }
4780
4799
  const cell = locateCell(event.clientX, event.clientY);
4781
- if (!cell) return;
4800
+ if (!cell) {
4801
+ clickedCellRef.current = null;
4802
+ setSelectionRange(null);
4803
+ setSelection(null);
4804
+ return;
4805
+ }
4782
4806
  if (columns[cell.columnIndex].rowDragHandle || columns[cell.columnIndex].rowNumber) {
4783
4807
  clickedCellRef.current = null;
4784
4808
  return;
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);
@@ -2007,6 +2014,18 @@ function TableInner({
2007
2014
  const [selection, setSelection] = useControllableValue(resolvedSelectedCell, resolvedDefaultSelectedCell, handleSelectedCellChange);
2008
2015
  const [selectionRange, setSelectionRange] = useState4(null);
2009
2016
  const rangeDragRef = useRef2(null);
2017
+ useEffect2(() => {
2018
+ if (!selection) return void 0;
2019
+ const handleOutsidePointerDown = (event) => {
2020
+ const target = event.target;
2021
+ if (!(target instanceof Node) || rootRef.current?.contains(target)) return;
2022
+ clickedCellRef.current = null;
2023
+ setSelectionRange(null);
2024
+ setSelection(null);
2025
+ };
2026
+ document.addEventListener("pointerdown", handleOutsidePointerDown);
2027
+ return () => document.removeEventListener("pointerdown", handleOutsidePointerDown);
2028
+ }, [selection, setSelection]);
2010
2029
  const applyRowOrder = useCallback2((sourceIndex, targetIndex) => {
2011
2030
  if (sourceIndex === targetIndex || sourceIndex < 0 || targetIndex < 0 || sourceIndex >= rows.length || targetIndex >= rows.length) return;
2012
2031
  const nextEntries = rows.map((row, index) => ({ row, key: getDataRowKey(row, index) }));
@@ -2282,8 +2301,8 @@ function TableInner({
2282
2301
  const scroll = scrollRef.current;
2283
2302
  const rowScrollTop = fixedHeader ? scroll.top : Math.max(0, scroll.top - bodyTop);
2284
2303
  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]);
2304
+ 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 });
2305
+ }, [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
2306
  const scheduleDraw = useCallback2(() => {
2288
2307
  if (frameRef.current === null) frameRef.current = requestAnimationFrame(draw);
2289
2308
  }, [draw]);
@@ -4566,7 +4585,7 @@ function TableInner({
4566
4585
  };
4567
4586
  const currentScroll = scrollRef.current;
4568
4587
  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: [
4588
+ 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
4589
  /* @__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
4590
  /* @__PURE__ */ jsx7(
4572
4591
  "canvas",
@@ -4748,7 +4767,12 @@ function TableInner({
4748
4767
  return;
4749
4768
  }
4750
4769
  const cell = locateCell(event.clientX, event.clientY);
4751
- if (!cell) return;
4770
+ if (!cell) {
4771
+ clickedCellRef.current = null;
4772
+ setSelectionRange(null);
4773
+ setSelection(null);
4774
+ return;
4775
+ }
4752
4776
  if (columns[cell.columnIndex].rowDragHandle || columns[cell.columnIndex].rowNumber) {
4753
4777
  clickedCellRef.current = null;
4754
4778
  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.19",
4
4
  "description": "A lightweight, canvas-rendered React table component for LensUI.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",