@lensui/lens-table 0.1.16 → 0.1.18

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
@@ -111,7 +111,7 @@ export function Example() {
111
111
  | `defaultSelectedCell` | `GridSelectionTarget \| null` | `null` | 默认选中单元格。传 `columnKey`,行可使用 `rowKey` 或 `rowIndex` 定位。 |
112
112
  | `onSelectedCellChange` | `(selection) => void` | - | 单元格选中变化回调,`selection.value` 为当前单元格的原始值。 |
113
113
  | `rangeSelection` | `boolean` | `false` | 是否允许鼠标拖动选择多个单元格。开启后支持范围复制、范围右键菜单和右下角拖拽扩展范围。 |
114
- | `rowSelection` | `boolean \| RowSelectionConfig` | `false` | 开启行选择。支持 `mode`、`showCheckbox`、`indicator: 'checkbox' \| 'arrow'` 和 `columnWidth`;箭头样式未选中时为空白。默认自动生成左侧选择列。 |
114
+ | `rowSelection` | `boolean \| RowSelectionConfig` | `false` | 开启行选择。支持 `mode`、`showCheckbox`、`indicator: 'checkbox' \| 'arrow' \| 'none'` 和 `columnWidth`;`none` 保留点击区域但不绘制标记。默认自动生成左侧选择列。 |
115
115
  | `selectedRowKeys` | `GridKey[]` | 非受控 | 受控行选择 key 列表。 |
116
116
  | `defaultSelectedRowKeys` | `GridKey[]` | `[]` | 非受控模式下默认选中的行 key。 |
117
117
  | `onSelectedRowChange` | `(keys, rows, indices) => void` | - | 行选择变化回调。 |
package/dist/index.cjs CHANGED
@@ -1115,7 +1115,8 @@ function paintGrid(options) {
1115
1115
  const checked = selectedRowKeys.has(rowKey);
1116
1116
  ctx.save();
1117
1117
  ctx.lineWidth = 1;
1118
- if (column.rowSelectionIndicator === "arrow") {
1118
+ if (column.rowSelectionIndicator === "none") {
1119
+ } else if (column.rowSelectionIndicator === "arrow") {
1119
1120
  if (checked) {
1120
1121
  ctx.beginPath();
1121
1122
  ctx.moveTo(iconLeft + 3.5, iconTop + 8);
@@ -1201,9 +1202,25 @@ function paintGrid(options) {
1201
1202
  paintBodyColumns("scroll");
1202
1203
  paintBodyColumns("left");
1203
1204
  paintBodyColumns("right");
1204
- if (extendVerticalGridLines && !verticalBorderless) {
1205
+ if (extendVerticalGridLines) {
1205
1206
  const extensionTop = Math.max(bodyTop, Math.min(height, bodyTop + rows.length * rowHeight - scrollTop));
1206
1207
  const extensionHeight = Math.max(0, height - extensionTop);
1208
+ const paintExtendedColumnSelection = (layer) => {
1209
+ ctx.save();
1210
+ if (layer === "scroll") {
1211
+ ctx.beginPath();
1212
+ ctx.rect(leftFixedWidth, extensionTop, Math.max(0, rightFixedLeft - leftFixedWidth), extensionHeight);
1213
+ ctx.clip();
1214
+ }
1215
+ ctx.fillStyle = colors.axisSelectionFill;
1216
+ for (let columnIndex = 0; columnIndex < columns.length; columnIndex += 1) {
1217
+ if (!selectedColumnKeys.has(columns[columnIndex].key)) continue;
1218
+ if (layer === "scroll" ? columns[columnIndex].fixed !== void 0 : columns[columnIndex].fixed !== layer) continue;
1219
+ if (layer === "scroll" && (columnIndex < range.columnStart || columnIndex >= range.columnEnd)) continue;
1220
+ ctx.fillRect(getColumnX(columnIndex), extensionTop, metrics[columnIndex].width, extensionHeight);
1221
+ }
1222
+ ctx.restore();
1223
+ };
1207
1224
  const paintExtendedColumnLines = (layer) => {
1208
1225
  ctx.save();
1209
1226
  if (layer === "scroll") {
@@ -1221,9 +1238,14 @@ function paintGrid(options) {
1221
1238
  }
1222
1239
  ctx.restore();
1223
1240
  };
1224
- paintExtendedColumnLines("scroll");
1225
- paintExtendedColumnLines("left");
1226
- paintExtendedColumnLines("right");
1241
+ paintExtendedColumnSelection("scroll");
1242
+ paintExtendedColumnSelection("left");
1243
+ paintExtendedColumnSelection("right");
1244
+ if (!verticalBorderless) {
1245
+ paintExtendedColumnLines("scroll");
1246
+ paintExtendedColumnLines("left");
1247
+ paintExtendedColumnLines("right");
1248
+ }
1227
1249
  }
1228
1250
  const headerY = fixedHeader ? 0 : -scrollTop;
1229
1251
  ctx.fillStyle = colors.header;
@@ -1239,8 +1261,9 @@ function paintGrid(options) {
1239
1261
  ctx.fillStyle = colors.header;
1240
1262
  ctx.fillRect(x, headerY, metric.width, headerHeight);
1241
1263
  }
1242
- if (selectedColumnKeys.has(columns[columnIndex].key)) {
1243
- ctx.fillStyle = colors.selectionFill;
1264
+ const isAxisSelected = selectedColumnKeys.has(columns[columnIndex].key);
1265
+ if (isAxisSelected) {
1266
+ ctx.fillStyle = colors.axisSelectionFill;
1244
1267
  ctx.fillRect(x, headerY, metric.width, headerHeight);
1245
1268
  }
1246
1269
  if (columnDropTarget && columnIndex >= columnDropTarget.startIndex && columnIndex <= columnDropTarget.endIndex) {
@@ -1272,7 +1295,7 @@ function paintGrid(options) {
1272
1295
  ctx.beginPath();
1273
1296
  ctx.rect(x + 2, headerY + headerLeafTop + 1, Math.max(0, clipWidth - 4), headerLeafHeight - 2);
1274
1297
  ctx.clip();
1275
- ctx.fillStyle = colors.muted;
1298
+ ctx.fillStyle = isAxisSelected ? colors.axisSelectionText : colors.muted;
1276
1299
  ctx.textAlign = titleAlign === "right" ? "right" : titleAlign === "center" ? "center" : "left";
1277
1300
  ctx.fillText(title, textX, headerY + headerLeafTop + headerLeafHeight / 2);
1278
1301
  ctx.restore();
@@ -1634,6 +1657,7 @@ function TableInner({
1634
1657
  onSortChange,
1635
1658
  onFilterChange,
1636
1659
  onCellChange,
1660
+ editTrigger = "double-click",
1637
1661
  onCellClick,
1638
1662
  onCellDoubleClick,
1639
1663
  onCellContextMenu,
@@ -3378,6 +3402,9 @@ function TableInner({
3378
3402
  }, [setSelection]);
3379
3403
  const selectRow = (0, import_react5.useCallback)((rowIndex, event, forceToggle = false) => {
3380
3404
  if (!rowSelection) return;
3405
+ clickedCellRef.current = null;
3406
+ setSelectionRange(null);
3407
+ setSelection(null);
3381
3408
  const mode = typeof rowSelection === "object" ? rowSelection.mode ?? "multiple" : "multiple";
3382
3409
  const key = getRowKey(rows[rowIndex], rowIndex);
3383
3410
  let next;
@@ -3392,9 +3419,12 @@ function TableInner({
3392
3419
  }
3393
3420
  rowAnchorRef.current = rowIndex;
3394
3421
  setRowKeys(next);
3395
- }, [getRowKey, rowKeys, rowSelection, rows, selectedRowKeySet, setRowKeys]);
3422
+ }, [getRowKey, rowKeys, rowSelection, rows, selectedRowKeySet, setRowKeys, setSelection]);
3396
3423
  const selectColumn = (0, import_react5.useCallback)((columnIndex, event) => {
3397
3424
  if (!columnSelection || columnIndex < 0) return;
3425
+ clickedCellRef.current = null;
3426
+ setSelectionRange(null);
3427
+ setSelection(null);
3398
3428
  const mode = typeof columnSelection === "object" ? columnSelection.mode ?? "multiple" : "multiple";
3399
3429
  const key = columns[columnIndex].key;
3400
3430
  let next;
@@ -3402,23 +3432,26 @@ function TableInner({
3402
3432
  const start = Math.min(columnAnchorRef.current, columnIndex);
3403
3433
  const end = Math.max(columnAnchorRef.current, columnIndex);
3404
3434
  next = columns.slice(start, end + 1).map((column) => column.key);
3405
- } else if (mode === "multiple") {
3435
+ } else if (mode === "multiple" && (event.ctrlKey || event.metaKey)) {
3406
3436
  next = selectedColumnKeySet.has(key) ? columnKeys.filter((item) => item !== key) : [...columnKeys, key];
3407
3437
  } else {
3408
3438
  next = columnKeys.length === 1 && selectedColumnKeySet.has(key) ? [] : [key];
3409
3439
  }
3410
3440
  columnAnchorRef.current = columnIndex;
3411
3441
  setColumnKeys(next);
3412
- }, [columnKeys, columnSelection, columns, selectedColumnKeySet, setColumnKeys]);
3442
+ }, [columnKeys, columnSelection, columns, selectedColumnKeySet, setColumnKeys, setSelection]);
3413
3443
  const toggleColumnFromMenu = (0, import_react5.useCallback)((columnIndex) => {
3414
3444
  if (!columnSelection || columnIndex < 0) return;
3445
+ clickedCellRef.current = null;
3446
+ setSelectionRange(null);
3447
+ setSelection(null);
3415
3448
  const key = columns[columnIndex].key;
3416
3449
  const mode = typeof columnSelection === "object" ? columnSelection.mode ?? "multiple" : "multiple";
3417
3450
  const selected = selectedColumnKeySet.has(key);
3418
3451
  const next = selected ? columnKeys.filter((value) => value !== key) : mode === "multiple" ? [...columnKeys, key] : [key];
3419
3452
  columnAnchorRef.current = columnIndex;
3420
3453
  setColumnKeys(next);
3421
- }, [columnKeys, columnSelection, columns, selectedColumnKeySet, setColumnKeys]);
3454
+ }, [columnKeys, columnSelection, columns, selectedColumnKeySet, setColumnKeys, setSelection]);
3422
3455
  const revealCell = (0, import_react5.useCallback)((cell) => {
3423
3456
  const scroller = scrollerRef.current;
3424
3457
  const metric = metrics[cell.columnIndex];
@@ -3770,6 +3803,8 @@ function TableInner({
3770
3803
  const renderHeaderIcons = (columnIndex, left) => {
3771
3804
  const column = columns[columnIndex];
3772
3805
  if (column.rowSelection || column.rowDragHandle || column.rowNumber) return null;
3806
+ const isAxisSelected = selectedColumnKeySet.has(column.key);
3807
+ const selectedClassName = isAxisSelected ? " is-axis-selected" : "";
3773
3808
  const top = headerLeafTop + (headerLeafHeight - headerActionSize) / 2;
3774
3809
  const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex), headerActionSlotWidth);
3775
3810
  let right = left + metrics[columnIndex].width - (visibleActions.drag ? headerActionSlotWidth : 2);
@@ -3777,21 +3812,21 @@ function TableInner({
3777
3812
  if (visibleActions.drag) {
3778
3813
  const hovered = hoveredHeaderAction?.columnIndex === columnIndex && hoveredHeaderAction.action === "drag";
3779
3814
  icons.push(
3780
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(HeaderDragIcon, { className: `rvg-header-icon rvg-header-drag-icon${hovered ? " is-hovered" : ""}`, style: { left: left + metrics[columnIndex].width - headerActionSlotWidth, top } }, "drag")
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")
3781
3816
  );
3782
3817
  }
3783
3818
  if (visibleActions.sort) {
3784
3819
  const hovered = hoveredHeaderAction?.columnIndex === columnIndex && hoveredHeaderAction.action === "sort";
3785
3820
  const direction = sortState?.columnKey === column.key ? sortState.direction : null;
3786
3821
  icons.push(
3787
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(HeaderSortIcon, { className: `rvg-header-icon${hovered ? " is-hovered" : ""}`, style: { left: right - headerActionSlotWidth, top }, direction }, "sort")
3822
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(HeaderSortIcon, { className: `rvg-header-icon${selectedClassName}${hovered ? " is-hovered" : ""}`, style: { left: right - headerActionSlotWidth, top }, direction }, "sort")
3788
3823
  );
3789
3824
  right -= headerActionSlotWidth;
3790
3825
  }
3791
3826
  if (visibleActions.filter) {
3792
3827
  const hovered = hoveredHeaderAction?.columnIndex === columnIndex && hoveredHeaderAction.action === "filter";
3793
3828
  icons.push(
3794
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(HeaderSearchIcon, { className: `rvg-header-icon rvg-header-search-icon${hovered ? " is-hovered" : ""}${filterValues[column.key] ? " is-active" : ""}`, style: { left: right - headerActionSlotWidth, top: top + 1 } }, "filter")
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")
3795
3830
  );
3796
3831
  }
3797
3832
  return icons;
@@ -4171,6 +4206,7 @@ function TableInner({
4171
4206
  };
4172
4207
  const renderHeaderTitle = (columnIndex, left) => {
4173
4208
  const column = columns[columnIndex];
4209
+ const isAxisSelected = selectedColumnKeySet.has(column.key);
4174
4210
  const isDropTarget = Boolean(columnDropTarget && columnIndex >= columnDropTarget.startIndex && columnIndex <= columnDropTarget.endIndex);
4175
4211
  const currentScrollTop = scrollRef.current.top;
4176
4212
  const headerTop = fixedHeader ? 0 : -currentScrollTop;
@@ -4187,7 +4223,7 @@ function TableInner({
4187
4223
  );
4188
4224
  }
4189
4225
  if (column.rowSelection) {
4190
- if (rowSelectionMode === "single" || column.rowSelectionIndicator === "arrow") return null;
4226
+ if (rowSelectionMode === "single" || column.rowSelectionIndicator === "arrow" || column.rowSelectionIndicator === "none") return null;
4191
4227
  const allSelected = rows.length > 0 && selectedRowKeySet.size === rows.length;
4192
4228
  const indeterminate = selectedRowKeySet.size > 0 && !allSelected;
4193
4229
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
@@ -4206,7 +4242,7 @@ function TableInner({
4206
4242
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4207
4243
  "div",
4208
4244
  {
4209
- className: `rvg-header-title is-custom${isDropTarget ? " is-drop-target" : ""}`,
4245
+ className: `rvg-header-title is-custom${isAxisSelected ? " is-axis-selected" : ""}${isDropTarget ? " is-drop-target" : ""}`,
4210
4246
  "data-level": headerDepth - 1,
4211
4247
  "data-measure-key": column.key,
4212
4248
  style: {
@@ -4249,10 +4285,11 @@ function TableInner({
4249
4285
  const visibleActions = cell.leaf ? getVisibleHeaderActions(column, startMetric.width, columnDraggable, measureHeaderTitleWidth(cell.startIndex), headerActionSlotWidth) : null;
4250
4286
  const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * headerActionSlotWidth : columnDraggable && !cell.leaf ? headerActionSlotWidth : 0;
4251
4287
  const isDropTarget = Boolean(columnDropTarget && cell.endIndex >= columnDropTarget.startIndex && cell.startIndex <= columnDropTarget.endIndex);
4288
+ const isAxisSelected = cell.leaf && selectedColumnKeySet.has(column.key);
4252
4289
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
4253
4290
  "div",
4254
4291
  {
4255
- className: `rvg-header-title rvg-header-group${column.renderHeader ? " is-custom" : ""}${cell.leaf ? " is-leaf" : ""}${cell.endIndex === columns.length - 1 ? " is-frame-right" : ""}${isDropTarget ? " is-drop-target" : ""}`,
4292
+ className: `rvg-header-title rvg-header-group${column.renderHeader ? " is-custom" : ""}${cell.leaf ? " is-leaf" : ""}${isAxisSelected ? " is-axis-selected" : ""}${cell.endIndex === columns.length - 1 ? " is-frame-right" : ""}${isDropTarget ? " is-drop-target" : ""}`,
4256
4293
  "data-level": cell.level,
4257
4294
  "data-measure-key": `group:${cell.key}:${cell.level}`,
4258
4295
  style: {
@@ -4516,12 +4553,16 @@ function TableInner({
4516
4553
  getSelectedRows: () => rows.filter((row, index) => selectedRowKeySet.has(getRowKey(row, index))),
4517
4554
  setSelectedRowKeys: (keys) => {
4518
4555
  if (!rowSelection) return;
4556
+ setSelectionRange(null);
4557
+ setSelection(null);
4519
4558
  const next = [...new Set(keys)];
4520
4559
  setRowKeys(rowSelectionMode === "single" ? next.slice(0, 1) : next);
4521
4560
  },
4522
4561
  getSelectedColumnKeys: () => [...columnKeys],
4523
4562
  setSelectedColumnKeys: (keys) => {
4524
4563
  if (!columnSelection) return;
4564
+ setSelectionRange(null);
4565
+ setSelection(null);
4525
4566
  const next = [...new Set(keys)].filter((key) => columns.some((column) => column.key === key && !column.rowNumber && !column.rowSelection && !column.rowDragHandle));
4526
4567
  setColumnKeys(typeof columnSelection === "object" && columnSelection.mode === "single" ? next.slice(0, 1) : next);
4527
4568
  },
@@ -4724,7 +4765,9 @@ function TableInner({
4724
4765
  }
4725
4766
  if (locateHeaderDragHandle(event.clientX, columnIndex)) return;
4726
4767
  if (columnIndex >= 0 && columns[columnIndex].rowSelection && rowSelection) {
4727
- if (columns[columnIndex].rowSelectionIndicator === "arrow") return;
4768
+ if (columns[columnIndex].rowSelectionIndicator === "arrow" || columns[columnIndex].rowSelectionIndicator === "none") return;
4769
+ setSelectionRange(null);
4770
+ setSelection(null);
4728
4771
  setRowKeys(rowKeys.length === rows.length ? [] : rows.map(getRowKey));
4729
4772
  } else if (columnIndex >= 0 && (columns[columnIndex].rowDragHandle || columns[columnIndex].rowNumber)) {
4730
4773
  return;
@@ -4743,7 +4786,7 @@ function TableInner({
4743
4786
  const clickedRowSelector = Boolean(columns[cell.columnIndex].rowSelection);
4744
4787
  if (clickedRowSelector) {
4745
4788
  clickedCellRef.current = null;
4746
- selectRow(cell.rowIndex, event, columns[cell.columnIndex].rowSelectionIndicator !== "arrow");
4789
+ selectRow(cell.rowIndex, event, !["arrow", "none"].includes(columns[cell.columnIndex].rowSelectionIndicator ?? "checkbox"));
4747
4790
  return;
4748
4791
  }
4749
4792
  if (event.detail > 1) return;
@@ -4759,6 +4802,7 @@ function TableInner({
4759
4802
  selectRow(cell.rowIndex, event);
4760
4803
  }
4761
4804
  revealCell(cell);
4805
+ if (editTrigger === "single-click") beginEdit(cell);
4762
4806
  },
4763
4807
  onDoubleClick: (event) => {
4764
4808
  const cell = clickedCellRef.current ?? locateCell(event.clientX, event.clientY);
@@ -4767,7 +4811,7 @@ function TableInner({
4767
4811
  if (column.rowNumber || column.rowSelection || column.rowDragHandle) return;
4768
4812
  const row = rows[cell.rowIndex];
4769
4813
  onCellDoubleClick?.({ ...cell, row, column, value: column.dataIndex === void 0 ? void 0 : row[column.dataIndex] }, event);
4770
- if (!event.defaultPrevented) beginEdit(cell);
4814
+ if (!event.defaultPrevented && editTrigger === "double-click") beginEdit(cell);
4771
4815
  },
4772
4816
  onContextMenu: handleContextMenu
4773
4817
  }
@@ -5042,6 +5086,8 @@ function TableInner({
5042
5086
  if (item === "select-row") {
5043
5087
  if (!rowSelection) return null;
5044
5088
  return renderContextMenuButton("cell-select-row", cellContext, selectedRowKeySet.has(cell.rowKey) ? labels.deselectRow : labels.selectRow, () => {
5089
+ setSelectionRange(null);
5090
+ setSelection(null);
5045
5091
  if (selectedRowKeySet.has(cell.rowKey)) setRowKeys(rowKeys.filter((key) => key !== cell.rowKey));
5046
5092
  else setRowKeys(rowSelectionMode === "single" ? [cell.rowKey] : [...rowKeys, cell.rowKey]);
5047
5093
  }, override, { icon: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ContextMenuIcon, { type: "select-row" }) });
package/dist/index.css CHANGED
@@ -773,6 +773,10 @@
773
773
  .rvg-header-title.is-drop-target {
774
774
  background: var(--rvg-color-column-drop-target-fill);
775
775
  }
776
+ .rvg-header-title.is-axis-selected {
777
+ background: var(--rvg-color-axis-selection-fill);
778
+ color: var(--rvg-color-axis-selection-text);
779
+ }
776
780
  .rvg-text-fixed-shadow {
777
781
  position: absolute;
778
782
  z-index: 4;
@@ -854,6 +858,11 @@
854
858
  .rvg-header-icon.is-hovered {
855
859
  color: var(--rvg-color-muted);
856
860
  }
861
+ .rvg-header-icon.is-axis-selected,
862
+ .rvg-header-icon.is-axis-selected.is-hovered,
863
+ .rvg-header-icon.is-axis-selected.is-active {
864
+ color: var(--rvg-color-axis-selection-text);
865
+ }
857
866
  .rvg-root.is-initial-loading .rvg-header-icon {
858
867
  color: #d6dbe0;
859
868
  animation: rvg-loading-icon-pulse 1.15s ease-in-out infinite alternate;
package/dist/index.d.cts CHANGED
@@ -160,7 +160,7 @@ interface RowSelectionConfig extends AxisSelectionConfig {
160
160
  /** Show the selection column. Defaults to true; when false, click body cells to select rows. */
161
161
  showCheckbox?: boolean;
162
162
  /** Selection marker style. Arrow stays blank until selected. Defaults to checkbox. */
163
- indicator?: 'checkbox' | 'arrow';
163
+ indicator?: 'checkbox' | 'arrow' | 'none';
164
164
  /** Width of the generated selection column. Defaults to 44. */
165
165
  columnWidth?: number;
166
166
  }
@@ -413,6 +413,8 @@ interface TableProps<Row extends object> {
413
413
  /** Overrides the built-in text filtering. Return the rows that match the internally managed filter values. */
414
414
  onFilterChange?: (rows: Row[], values: Readonly<Record<string, string>>) => Row[];
415
415
  onCellChange?: (change: CellChange<Row>) => void | Promise<void>;
416
+ /** Starts editing from a single or double click. Defaults to double-click. */
417
+ editTrigger?: 'single-click' | 'double-click';
416
418
  /** Fires on a data cell click, before the default selection behavior. */
417
419
  onCellClick?: (cell: TableCellContext<Row>, event: MouseEvent<HTMLElement>) => void;
418
420
  /** Fires before double-click editing. preventDefault() cancels editing. */
package/dist/index.d.ts CHANGED
@@ -160,7 +160,7 @@ interface RowSelectionConfig extends AxisSelectionConfig {
160
160
  /** Show the selection column. Defaults to true; when false, click body cells to select rows. */
161
161
  showCheckbox?: boolean;
162
162
  /** Selection marker style. Arrow stays blank until selected. Defaults to checkbox. */
163
- indicator?: 'checkbox' | 'arrow';
163
+ indicator?: 'checkbox' | 'arrow' | 'none';
164
164
  /** Width of the generated selection column. Defaults to 44. */
165
165
  columnWidth?: number;
166
166
  }
@@ -413,6 +413,8 @@ interface TableProps<Row extends object> {
413
413
  /** Overrides the built-in text filtering. Return the rows that match the internally managed filter values. */
414
414
  onFilterChange?: (rows: Row[], values: Readonly<Record<string, string>>) => Row[];
415
415
  onCellChange?: (change: CellChange<Row>) => void | Promise<void>;
416
+ /** Starts editing from a single or double click. Defaults to double-click. */
417
+ editTrigger?: 'single-click' | 'double-click';
416
418
  /** Fires on a data cell click, before the default selection behavior. */
417
419
  onCellClick?: (cell: TableCellContext<Row>, event: MouseEvent<HTMLElement>) => void;
418
420
  /** Fires before double-click editing. preventDefault() cancels editing. */
package/dist/index.js CHANGED
@@ -1085,7 +1085,8 @@ function paintGrid(options) {
1085
1085
  const checked = selectedRowKeys.has(rowKey);
1086
1086
  ctx.save();
1087
1087
  ctx.lineWidth = 1;
1088
- if (column.rowSelectionIndicator === "arrow") {
1088
+ if (column.rowSelectionIndicator === "none") {
1089
+ } else if (column.rowSelectionIndicator === "arrow") {
1089
1090
  if (checked) {
1090
1091
  ctx.beginPath();
1091
1092
  ctx.moveTo(iconLeft + 3.5, iconTop + 8);
@@ -1171,9 +1172,25 @@ function paintGrid(options) {
1171
1172
  paintBodyColumns("scroll");
1172
1173
  paintBodyColumns("left");
1173
1174
  paintBodyColumns("right");
1174
- if (extendVerticalGridLines && !verticalBorderless) {
1175
+ if (extendVerticalGridLines) {
1175
1176
  const extensionTop = Math.max(bodyTop, Math.min(height, bodyTop + rows.length * rowHeight - scrollTop));
1176
1177
  const extensionHeight = Math.max(0, height - extensionTop);
1178
+ const paintExtendedColumnSelection = (layer) => {
1179
+ ctx.save();
1180
+ if (layer === "scroll") {
1181
+ ctx.beginPath();
1182
+ ctx.rect(leftFixedWidth, extensionTop, Math.max(0, rightFixedLeft - leftFixedWidth), extensionHeight);
1183
+ ctx.clip();
1184
+ }
1185
+ ctx.fillStyle = colors.axisSelectionFill;
1186
+ for (let columnIndex = 0; columnIndex < columns.length; columnIndex += 1) {
1187
+ if (!selectedColumnKeys.has(columns[columnIndex].key)) continue;
1188
+ if (layer === "scroll" ? columns[columnIndex].fixed !== void 0 : columns[columnIndex].fixed !== layer) continue;
1189
+ if (layer === "scroll" && (columnIndex < range.columnStart || columnIndex >= range.columnEnd)) continue;
1190
+ ctx.fillRect(getColumnX(columnIndex), extensionTop, metrics[columnIndex].width, extensionHeight);
1191
+ }
1192
+ ctx.restore();
1193
+ };
1177
1194
  const paintExtendedColumnLines = (layer) => {
1178
1195
  ctx.save();
1179
1196
  if (layer === "scroll") {
@@ -1191,9 +1208,14 @@ function paintGrid(options) {
1191
1208
  }
1192
1209
  ctx.restore();
1193
1210
  };
1194
- paintExtendedColumnLines("scroll");
1195
- paintExtendedColumnLines("left");
1196
- paintExtendedColumnLines("right");
1211
+ paintExtendedColumnSelection("scroll");
1212
+ paintExtendedColumnSelection("left");
1213
+ paintExtendedColumnSelection("right");
1214
+ if (!verticalBorderless) {
1215
+ paintExtendedColumnLines("scroll");
1216
+ paintExtendedColumnLines("left");
1217
+ paintExtendedColumnLines("right");
1218
+ }
1197
1219
  }
1198
1220
  const headerY = fixedHeader ? 0 : -scrollTop;
1199
1221
  ctx.fillStyle = colors.header;
@@ -1209,8 +1231,9 @@ function paintGrid(options) {
1209
1231
  ctx.fillStyle = colors.header;
1210
1232
  ctx.fillRect(x, headerY, metric.width, headerHeight);
1211
1233
  }
1212
- if (selectedColumnKeys.has(columns[columnIndex].key)) {
1213
- ctx.fillStyle = colors.selectionFill;
1234
+ const isAxisSelected = selectedColumnKeys.has(columns[columnIndex].key);
1235
+ if (isAxisSelected) {
1236
+ ctx.fillStyle = colors.axisSelectionFill;
1214
1237
  ctx.fillRect(x, headerY, metric.width, headerHeight);
1215
1238
  }
1216
1239
  if (columnDropTarget && columnIndex >= columnDropTarget.startIndex && columnIndex <= columnDropTarget.endIndex) {
@@ -1242,7 +1265,7 @@ function paintGrid(options) {
1242
1265
  ctx.beginPath();
1243
1266
  ctx.rect(x + 2, headerY + headerLeafTop + 1, Math.max(0, clipWidth - 4), headerLeafHeight - 2);
1244
1267
  ctx.clip();
1245
- ctx.fillStyle = colors.muted;
1268
+ ctx.fillStyle = isAxisSelected ? colors.axisSelectionText : colors.muted;
1246
1269
  ctx.textAlign = titleAlign === "right" ? "right" : titleAlign === "center" ? "center" : "left";
1247
1270
  ctx.fillText(title, textX, headerY + headerLeafTop + headerLeafHeight / 2);
1248
1271
  ctx.restore();
@@ -1604,6 +1627,7 @@ function TableInner({
1604
1627
  onSortChange,
1605
1628
  onFilterChange,
1606
1629
  onCellChange,
1630
+ editTrigger = "double-click",
1607
1631
  onCellClick,
1608
1632
  onCellDoubleClick,
1609
1633
  onCellContextMenu,
@@ -3348,6 +3372,9 @@ function TableInner({
3348
3372
  }, [setSelection]);
3349
3373
  const selectRow = useCallback2((rowIndex, event, forceToggle = false) => {
3350
3374
  if (!rowSelection) return;
3375
+ clickedCellRef.current = null;
3376
+ setSelectionRange(null);
3377
+ setSelection(null);
3351
3378
  const mode = typeof rowSelection === "object" ? rowSelection.mode ?? "multiple" : "multiple";
3352
3379
  const key = getRowKey(rows[rowIndex], rowIndex);
3353
3380
  let next;
@@ -3362,9 +3389,12 @@ function TableInner({
3362
3389
  }
3363
3390
  rowAnchorRef.current = rowIndex;
3364
3391
  setRowKeys(next);
3365
- }, [getRowKey, rowKeys, rowSelection, rows, selectedRowKeySet, setRowKeys]);
3392
+ }, [getRowKey, rowKeys, rowSelection, rows, selectedRowKeySet, setRowKeys, setSelection]);
3366
3393
  const selectColumn = useCallback2((columnIndex, event) => {
3367
3394
  if (!columnSelection || columnIndex < 0) return;
3395
+ clickedCellRef.current = null;
3396
+ setSelectionRange(null);
3397
+ setSelection(null);
3368
3398
  const mode = typeof columnSelection === "object" ? columnSelection.mode ?? "multiple" : "multiple";
3369
3399
  const key = columns[columnIndex].key;
3370
3400
  let next;
@@ -3372,23 +3402,26 @@ function TableInner({
3372
3402
  const start = Math.min(columnAnchorRef.current, columnIndex);
3373
3403
  const end = Math.max(columnAnchorRef.current, columnIndex);
3374
3404
  next = columns.slice(start, end + 1).map((column) => column.key);
3375
- } else if (mode === "multiple") {
3405
+ } else if (mode === "multiple" && (event.ctrlKey || event.metaKey)) {
3376
3406
  next = selectedColumnKeySet.has(key) ? columnKeys.filter((item) => item !== key) : [...columnKeys, key];
3377
3407
  } else {
3378
3408
  next = columnKeys.length === 1 && selectedColumnKeySet.has(key) ? [] : [key];
3379
3409
  }
3380
3410
  columnAnchorRef.current = columnIndex;
3381
3411
  setColumnKeys(next);
3382
- }, [columnKeys, columnSelection, columns, selectedColumnKeySet, setColumnKeys]);
3412
+ }, [columnKeys, columnSelection, columns, selectedColumnKeySet, setColumnKeys, setSelection]);
3383
3413
  const toggleColumnFromMenu = useCallback2((columnIndex) => {
3384
3414
  if (!columnSelection || columnIndex < 0) return;
3415
+ clickedCellRef.current = null;
3416
+ setSelectionRange(null);
3417
+ setSelection(null);
3385
3418
  const key = columns[columnIndex].key;
3386
3419
  const mode = typeof columnSelection === "object" ? columnSelection.mode ?? "multiple" : "multiple";
3387
3420
  const selected = selectedColumnKeySet.has(key);
3388
3421
  const next = selected ? columnKeys.filter((value) => value !== key) : mode === "multiple" ? [...columnKeys, key] : [key];
3389
3422
  columnAnchorRef.current = columnIndex;
3390
3423
  setColumnKeys(next);
3391
- }, [columnKeys, columnSelection, columns, selectedColumnKeySet, setColumnKeys]);
3424
+ }, [columnKeys, columnSelection, columns, selectedColumnKeySet, setColumnKeys, setSelection]);
3392
3425
  const revealCell = useCallback2((cell) => {
3393
3426
  const scroller = scrollerRef.current;
3394
3427
  const metric = metrics[cell.columnIndex];
@@ -3740,6 +3773,8 @@ function TableInner({
3740
3773
  const renderHeaderIcons = (columnIndex, left) => {
3741
3774
  const column = columns[columnIndex];
3742
3775
  if (column.rowSelection || column.rowDragHandle || column.rowNumber) return null;
3776
+ const isAxisSelected = selectedColumnKeySet.has(column.key);
3777
+ const selectedClassName = isAxisSelected ? " is-axis-selected" : "";
3743
3778
  const top = headerLeafTop + (headerLeafHeight - headerActionSize) / 2;
3744
3779
  const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex), headerActionSlotWidth);
3745
3780
  let right = left + metrics[columnIndex].width - (visibleActions.drag ? headerActionSlotWidth : 2);
@@ -3747,21 +3782,21 @@ function TableInner({
3747
3782
  if (visibleActions.drag) {
3748
3783
  const hovered = hoveredHeaderAction?.columnIndex === columnIndex && hoveredHeaderAction.action === "drag";
3749
3784
  icons.push(
3750
- /* @__PURE__ */ jsx7(HeaderDragIcon, { className: `rvg-header-icon rvg-header-drag-icon${hovered ? " is-hovered" : ""}`, style: { left: left + metrics[columnIndex].width - headerActionSlotWidth, top } }, "drag")
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")
3751
3786
  );
3752
3787
  }
3753
3788
  if (visibleActions.sort) {
3754
3789
  const hovered = hoveredHeaderAction?.columnIndex === columnIndex && hoveredHeaderAction.action === "sort";
3755
3790
  const direction = sortState?.columnKey === column.key ? sortState.direction : null;
3756
3791
  icons.push(
3757
- /* @__PURE__ */ jsx7(HeaderSortIcon, { className: `rvg-header-icon${hovered ? " is-hovered" : ""}`, style: { left: right - headerActionSlotWidth, top }, direction }, "sort")
3792
+ /* @__PURE__ */ jsx7(HeaderSortIcon, { className: `rvg-header-icon${selectedClassName}${hovered ? " is-hovered" : ""}`, style: { left: right - headerActionSlotWidth, top }, direction }, "sort")
3758
3793
  );
3759
3794
  right -= headerActionSlotWidth;
3760
3795
  }
3761
3796
  if (visibleActions.filter) {
3762
3797
  const hovered = hoveredHeaderAction?.columnIndex === columnIndex && hoveredHeaderAction.action === "filter";
3763
3798
  icons.push(
3764
- /* @__PURE__ */ jsx7(HeaderSearchIcon, { className: `rvg-header-icon rvg-header-search-icon${hovered ? " is-hovered" : ""}${filterValues[column.key] ? " is-active" : ""}`, style: { left: right - headerActionSlotWidth, top: top + 1 } }, "filter")
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")
3765
3800
  );
3766
3801
  }
3767
3802
  return icons;
@@ -4141,6 +4176,7 @@ function TableInner({
4141
4176
  };
4142
4177
  const renderHeaderTitle = (columnIndex, left) => {
4143
4178
  const column = columns[columnIndex];
4179
+ const isAxisSelected = selectedColumnKeySet.has(column.key);
4144
4180
  const isDropTarget = Boolean(columnDropTarget && columnIndex >= columnDropTarget.startIndex && columnIndex <= columnDropTarget.endIndex);
4145
4181
  const currentScrollTop = scrollRef.current.top;
4146
4182
  const headerTop = fixedHeader ? 0 : -currentScrollTop;
@@ -4157,7 +4193,7 @@ function TableInner({
4157
4193
  );
4158
4194
  }
4159
4195
  if (column.rowSelection) {
4160
- if (rowSelectionMode === "single" || column.rowSelectionIndicator === "arrow") return null;
4196
+ if (rowSelectionMode === "single" || column.rowSelectionIndicator === "arrow" || column.rowSelectionIndicator === "none") return null;
4161
4197
  const allSelected = rows.length > 0 && selectedRowKeySet.size === rows.length;
4162
4198
  const indeterminate = selectedRowKeySet.size > 0 && !allSelected;
4163
4199
  return /* @__PURE__ */ jsx7(
@@ -4176,7 +4212,7 @@ function TableInner({
4176
4212
  return /* @__PURE__ */ jsx7(
4177
4213
  "div",
4178
4214
  {
4179
- className: `rvg-header-title is-custom${isDropTarget ? " is-drop-target" : ""}`,
4215
+ className: `rvg-header-title is-custom${isAxisSelected ? " is-axis-selected" : ""}${isDropTarget ? " is-drop-target" : ""}`,
4180
4216
  "data-level": headerDepth - 1,
4181
4217
  "data-measure-key": column.key,
4182
4218
  style: {
@@ -4219,10 +4255,11 @@ function TableInner({
4219
4255
  const visibleActions = cell.leaf ? getVisibleHeaderActions(column, startMetric.width, columnDraggable, measureHeaderTitleWidth(cell.startIndex), headerActionSlotWidth) : null;
4220
4256
  const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * headerActionSlotWidth : columnDraggable && !cell.leaf ? headerActionSlotWidth : 0;
4221
4257
  const isDropTarget = Boolean(columnDropTarget && cell.endIndex >= columnDropTarget.startIndex && cell.startIndex <= columnDropTarget.endIndex);
4258
+ const isAxisSelected = cell.leaf && selectedColumnKeySet.has(column.key);
4222
4259
  return /* @__PURE__ */ jsxs7(
4223
4260
  "div",
4224
4261
  {
4225
- className: `rvg-header-title rvg-header-group${column.renderHeader ? " is-custom" : ""}${cell.leaf ? " is-leaf" : ""}${cell.endIndex === columns.length - 1 ? " is-frame-right" : ""}${isDropTarget ? " is-drop-target" : ""}`,
4262
+ className: `rvg-header-title rvg-header-group${column.renderHeader ? " is-custom" : ""}${cell.leaf ? " is-leaf" : ""}${isAxisSelected ? " is-axis-selected" : ""}${cell.endIndex === columns.length - 1 ? " is-frame-right" : ""}${isDropTarget ? " is-drop-target" : ""}`,
4226
4263
  "data-level": cell.level,
4227
4264
  "data-measure-key": `group:${cell.key}:${cell.level}`,
4228
4265
  style: {
@@ -4486,12 +4523,16 @@ function TableInner({
4486
4523
  getSelectedRows: () => rows.filter((row, index) => selectedRowKeySet.has(getRowKey(row, index))),
4487
4524
  setSelectedRowKeys: (keys) => {
4488
4525
  if (!rowSelection) return;
4526
+ setSelectionRange(null);
4527
+ setSelection(null);
4489
4528
  const next = [...new Set(keys)];
4490
4529
  setRowKeys(rowSelectionMode === "single" ? next.slice(0, 1) : next);
4491
4530
  },
4492
4531
  getSelectedColumnKeys: () => [...columnKeys],
4493
4532
  setSelectedColumnKeys: (keys) => {
4494
4533
  if (!columnSelection) return;
4534
+ setSelectionRange(null);
4535
+ setSelection(null);
4495
4536
  const next = [...new Set(keys)].filter((key) => columns.some((column) => column.key === key && !column.rowNumber && !column.rowSelection && !column.rowDragHandle));
4496
4537
  setColumnKeys(typeof columnSelection === "object" && columnSelection.mode === "single" ? next.slice(0, 1) : next);
4497
4538
  },
@@ -4694,7 +4735,9 @@ function TableInner({
4694
4735
  }
4695
4736
  if (locateHeaderDragHandle(event.clientX, columnIndex)) return;
4696
4737
  if (columnIndex >= 0 && columns[columnIndex].rowSelection && rowSelection) {
4697
- if (columns[columnIndex].rowSelectionIndicator === "arrow") return;
4738
+ if (columns[columnIndex].rowSelectionIndicator === "arrow" || columns[columnIndex].rowSelectionIndicator === "none") return;
4739
+ setSelectionRange(null);
4740
+ setSelection(null);
4698
4741
  setRowKeys(rowKeys.length === rows.length ? [] : rows.map(getRowKey));
4699
4742
  } else if (columnIndex >= 0 && (columns[columnIndex].rowDragHandle || columns[columnIndex].rowNumber)) {
4700
4743
  return;
@@ -4713,7 +4756,7 @@ function TableInner({
4713
4756
  const clickedRowSelector = Boolean(columns[cell.columnIndex].rowSelection);
4714
4757
  if (clickedRowSelector) {
4715
4758
  clickedCellRef.current = null;
4716
- selectRow(cell.rowIndex, event, columns[cell.columnIndex].rowSelectionIndicator !== "arrow");
4759
+ selectRow(cell.rowIndex, event, !["arrow", "none"].includes(columns[cell.columnIndex].rowSelectionIndicator ?? "checkbox"));
4717
4760
  return;
4718
4761
  }
4719
4762
  if (event.detail > 1) return;
@@ -4729,6 +4772,7 @@ function TableInner({
4729
4772
  selectRow(cell.rowIndex, event);
4730
4773
  }
4731
4774
  revealCell(cell);
4775
+ if (editTrigger === "single-click") beginEdit(cell);
4732
4776
  },
4733
4777
  onDoubleClick: (event) => {
4734
4778
  const cell = clickedCellRef.current ?? locateCell(event.clientX, event.clientY);
@@ -4737,7 +4781,7 @@ function TableInner({
4737
4781
  if (column.rowNumber || column.rowSelection || column.rowDragHandle) return;
4738
4782
  const row = rows[cell.rowIndex];
4739
4783
  onCellDoubleClick?.({ ...cell, row, column, value: column.dataIndex === void 0 ? void 0 : row[column.dataIndex] }, event);
4740
- if (!event.defaultPrevented) beginEdit(cell);
4784
+ if (!event.defaultPrevented && editTrigger === "double-click") beginEdit(cell);
4741
4785
  },
4742
4786
  onContextMenu: handleContextMenu
4743
4787
  }
@@ -5012,6 +5056,8 @@ function TableInner({
5012
5056
  if (item === "select-row") {
5013
5057
  if (!rowSelection) return null;
5014
5058
  return renderContextMenuButton("cell-select-row", cellContext, selectedRowKeySet.has(cell.rowKey) ? labels.deselectRow : labels.selectRow, () => {
5059
+ setSelectionRange(null);
5060
+ setSelection(null);
5015
5061
  if (selectedRowKeySet.has(cell.rowKey)) setRowKeys(rowKeys.filter((key) => key !== cell.rowKey));
5016
5062
  else setRowKeys(rowSelectionMode === "single" ? [cell.rowKey] : [...rowKeys, cell.rowKey]);
5017
5063
  }, override, { icon: /* @__PURE__ */ jsx7(ContextMenuIcon, { type: "select-row" }) });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lensui/lens-table",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "description": "A lightweight, canvas-rendered React table component for LensUI.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",