@lensui/lens-table 0.1.83 → 0.1.84

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
@@ -315,6 +315,11 @@ const columns: GridColumn<Row>[] = [
315
315
  | `time-range` | `'09:00 ~ 18:00'` | 时间范围选择器。 |
316
316
  | `date-time-range` | `'2026-01-01 09:00 ~ 2026-01-01 18:00'` | 日期时间范围选择器,包含日期和时间面板。 |
317
317
 
318
+ `select` 可配置 `searchable` 开启输入筛选。需要把输入限制为已有选项时,设置
319
+ `strictOptions: true`:首次打开显示完整列表;开始输入后始终按输入内容筛选;失焦时只提交
320
+ 精确匹配的选项,不匹配则恢复进入编辑前的值。若 `options` 包含 `{ label: '', value: '' }`,
321
+ 清空输入会提交该空选项。
322
+
318
323
  可选 `format` 用于控制提交到数据里的字符串格式,例如:
319
324
 
320
325
  ```tsx
package/dist/index.cjs CHANGED
@@ -345,7 +345,7 @@ function getSearchableChoicePopupHorizontalLayout(anchorLeft, anchorWidth, longe
345
345
  );
346
346
  return { left, width };
347
347
  }
348
- function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownHeight, style, labels, onChange, onCommit, onCancel }) {
348
+ function SearchableChoiceEditor({ value, options, allowCustomValue, strictOptions, maxDropdownHeight, style, labels, onChange, onCommit, onCancel }) {
349
349
  const editorRef = (0, import_react2.useRef)(null);
350
350
  const popupRef = (0, import_react2.useRef)(null);
351
351
  const activeOptionRef = (0, import_react2.useRef)(null);
@@ -355,6 +355,7 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
355
355
  const listboxId = (0, import_react2.useId)();
356
356
  const originalValue = originalValueRef.current;
357
357
  const [inputValue, setInputValue] = (0, import_react2.useState)(value);
358
+ const [hasTyped, setHasTyped] = (0, import_react2.useState)(false);
358
359
  const [activeIndex, setActiveIndex] = (0, import_react2.useState)(() => {
359
360
  const selectedIndex = options.findIndex((option) => option.value === value);
360
361
  return selectedIndex >= 0 ? selectedIndex : 0;
@@ -364,10 +365,23 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
364
365
  visibility: "hidden"
365
366
  });
366
367
  const filteredOptions = (0, import_react2.useMemo)(() => {
368
+ if (strictOptions) {
369
+ if (!hasTyped) return options;
370
+ if (inputValue === "") return options.filter((option) => option.value === "");
371
+ const strictQuery = inputValue.trim().toLocaleLowerCase();
372
+ if (!strictQuery) return [];
373
+ return options.filter((option) => option.label.toLocaleLowerCase().includes(strictQuery) || option.value.toLocaleLowerCase().includes(strictQuery));
374
+ }
367
375
  const query = inputValue.trim().toLocaleLowerCase();
368
376
  if (!query || query === originalValue.toLocaleLowerCase()) return options;
369
377
  return options.filter((option) => option.label.toLocaleLowerCase().includes(query) || option.value.toLocaleLowerCase().includes(query));
370
- }, [inputValue, options, originalValue]);
378
+ }, [hasTyped, inputValue, options, originalValue, strictOptions]);
379
+ const exactInputOption = options.find((option) => option.value === inputValue);
380
+ const selectedValue = exactInputOption?.value;
381
+ const resolveInputValue = (0, import_react2.useCallback)(() => {
382
+ if (strictOptions) return exactInputOption?.value ?? originalValue;
383
+ return allowCustomValue ? inputValue || originalValue : originalValue;
384
+ }, [allowCustomValue, exactInputOption, inputValue, originalValue, strictOptions]);
371
385
  (0, import_react2.useLayoutEffect)(() => {
372
386
  const positionPopup = () => {
373
387
  const rect = editorRef.current?.getBoundingClientRect();
@@ -434,11 +448,11 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
434
448
  const target = event.target;
435
449
  if (!(target instanceof Node)) return;
436
450
  if (editorRef.current?.contains(target) || popupRef.current?.contains(target)) return;
437
- onCommit(allowCustomValue ? inputValue || originalValue : originalValue);
451
+ onCommit(resolveInputValue());
438
452
  };
439
453
  document.addEventListener("pointerdown", handleOutsidePointer, true);
440
454
  return () => document.removeEventListener("pointerdown", handleOutsidePointer, true);
441
- }, [allowCustomValue, inputValue, onCommit, originalValue]);
455
+ }, [onCommit, resolveInputValue]);
442
456
  const selectOption = (option) => {
443
457
  onChange(option.value);
444
458
  onCommit(option.value);
@@ -460,7 +474,7 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
460
474
  event.preventDefault();
461
475
  const option = filteredOptions[activeIndex];
462
476
  if (option) selectOption(option);
463
- else if (allowCustomValue) onCommit(inputValue || originalValue);
477
+ else if (allowCustomValue || strictOptions) onCommit(resolveInputValue());
464
478
  }
465
479
  };
466
480
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { ref: editorRef, className: "rvg-choice-editor rvg-searchable-choice-editor", style, children: [
@@ -474,6 +488,7 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
474
488
  "aria-controls": listboxId,
475
489
  onFocus: (event) => event.currentTarget.select(),
476
490
  onChange: (event) => {
491
+ setHasTyped(true);
477
492
  setInputValue(event.target.value);
478
493
  onChange(event.target.value);
479
494
  navigationSourceRef.current = "filter";
@@ -489,8 +504,8 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
489
504
  ref: index === activeIndex ? activeOptionRef : void 0,
490
505
  type: "button",
491
506
  role: "option",
492
- "aria-selected": option.value === originalValue,
493
- className: `rvg-choice-item${index === activeIndex ? " is-active" : ""}${option.value === originalValue ? " is-selected" : ""}`,
507
+ "aria-selected": option.value === selectedValue,
508
+ className: `rvg-choice-item${index === activeIndex ? " is-active" : ""}${option.value === selectedValue ? " is-selected" : ""}`,
494
509
  onPointerDown: (event) => event.preventDefault(),
495
510
  onMouseEnter: () => {
496
511
  navigationSourceRef.current = "pointer";
@@ -499,7 +514,7 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
499
514
  onClick: () => selectOption(option),
500
515
  children: [
501
516
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: option.label }),
502
- option.value === originalValue && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChoiceCheckIcon, {})
517
+ option.value === selectedValue && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChoiceCheckIcon, {})
503
518
  ]
504
519
  },
505
520
  option.value
@@ -3722,6 +3737,7 @@ function TableInner({
3722
3737
  options: column.editor.options,
3723
3738
  searchable: column.editor.searchable,
3724
3739
  allowCustomValue: column.editor.allowCustomValue,
3740
+ strictOptions: column.editor.strictOptions,
3725
3741
  maxDropdownHeight: column.editor.maxDropdownHeight,
3726
3742
  style: editorStyle,
3727
3743
  labels,
package/dist/index.d.cts CHANGED
@@ -211,6 +211,8 @@ interface GridColumn<Row> {
211
211
  }>;
212
212
  searchable?: boolean;
213
213
  allowCustomValue?: boolean;
214
+ /** Keep typed input filtered and only commit values that exactly match an option. */
215
+ strictOptions?: boolean;
214
216
  maxDropdownHeight?: number;
215
217
  } | {
216
218
  type: 'textarea';
package/dist/index.d.ts CHANGED
@@ -211,6 +211,8 @@ interface GridColumn<Row> {
211
211
  }>;
212
212
  searchable?: boolean;
213
213
  allowCustomValue?: boolean;
214
+ /** Keep typed input filtered and only commit values that exactly match an option. */
215
+ strictOptions?: boolean;
214
216
  maxDropdownHeight?: number;
215
217
  } | {
216
218
  type: 'textarea';
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/Table.tsx
2
- import { forwardRef, useImperativeHandle, Fragment as Fragment2, useCallback as useCallback2, useEffect as useEffect3, useLayoutEffect as useLayoutEffect3, useMemo as useMemo2, useRef as useRef4, useState as useState6 } from "react";
2
+ import { forwardRef, useImperativeHandle, Fragment as Fragment2, useCallback as useCallback3, useEffect as useEffect3, useLayoutEffect as useLayoutEffect3, useMemo as useMemo2, useRef as useRef4, useState as useState6 } from "react";
3
3
  import { flushSync } from "react-dom";
4
4
 
5
5
  // src/components/EmptyState.tsx
@@ -199,7 +199,7 @@ function DefaultEmptyState({ label = ZH_CN_GRID_LABELS.empty }) {
199
199
  // src/components/editors/ChoiceEditor.tsx
200
200
  import { Portal } from "@ark-ui/react/portal";
201
201
  import { Select, createListCollection } from "@ark-ui/react/select";
202
- import { useEffect, useId as useId2, useLayoutEffect, useMemo, useRef, useState } from "react";
202
+ import { useCallback, useEffect, useId as useId2, useLayoutEffect, useMemo, useRef, useState } from "react";
203
203
 
204
204
  // src/icons/gridIcons.tsx
205
205
  import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
@@ -315,7 +315,7 @@ function getSearchableChoicePopupHorizontalLayout(anchorLeft, anchorWidth, longe
315
315
  );
316
316
  return { left, width };
317
317
  }
318
- function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownHeight, style, labels, onChange, onCommit, onCancel }) {
318
+ function SearchableChoiceEditor({ value, options, allowCustomValue, strictOptions, maxDropdownHeight, style, labels, onChange, onCommit, onCancel }) {
319
319
  const editorRef = useRef(null);
320
320
  const popupRef = useRef(null);
321
321
  const activeOptionRef = useRef(null);
@@ -325,6 +325,7 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
325
325
  const listboxId = useId2();
326
326
  const originalValue = originalValueRef.current;
327
327
  const [inputValue, setInputValue] = useState(value);
328
+ const [hasTyped, setHasTyped] = useState(false);
328
329
  const [activeIndex, setActiveIndex] = useState(() => {
329
330
  const selectedIndex = options.findIndex((option) => option.value === value);
330
331
  return selectedIndex >= 0 ? selectedIndex : 0;
@@ -334,10 +335,23 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
334
335
  visibility: "hidden"
335
336
  });
336
337
  const filteredOptions = useMemo(() => {
338
+ if (strictOptions) {
339
+ if (!hasTyped) return options;
340
+ if (inputValue === "") return options.filter((option) => option.value === "");
341
+ const strictQuery = inputValue.trim().toLocaleLowerCase();
342
+ if (!strictQuery) return [];
343
+ return options.filter((option) => option.label.toLocaleLowerCase().includes(strictQuery) || option.value.toLocaleLowerCase().includes(strictQuery));
344
+ }
337
345
  const query = inputValue.trim().toLocaleLowerCase();
338
346
  if (!query || query === originalValue.toLocaleLowerCase()) return options;
339
347
  return options.filter((option) => option.label.toLocaleLowerCase().includes(query) || option.value.toLocaleLowerCase().includes(query));
340
- }, [inputValue, options, originalValue]);
348
+ }, [hasTyped, inputValue, options, originalValue, strictOptions]);
349
+ const exactInputOption = options.find((option) => option.value === inputValue);
350
+ const selectedValue = exactInputOption?.value;
351
+ const resolveInputValue = useCallback(() => {
352
+ if (strictOptions) return exactInputOption?.value ?? originalValue;
353
+ return allowCustomValue ? inputValue || originalValue : originalValue;
354
+ }, [allowCustomValue, exactInputOption, inputValue, originalValue, strictOptions]);
341
355
  useLayoutEffect(() => {
342
356
  const positionPopup = () => {
343
357
  const rect = editorRef.current?.getBoundingClientRect();
@@ -404,11 +418,11 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
404
418
  const target = event.target;
405
419
  if (!(target instanceof Node)) return;
406
420
  if (editorRef.current?.contains(target) || popupRef.current?.contains(target)) return;
407
- onCommit(allowCustomValue ? inputValue || originalValue : originalValue);
421
+ onCommit(resolveInputValue());
408
422
  };
409
423
  document.addEventListener("pointerdown", handleOutsidePointer, true);
410
424
  return () => document.removeEventListener("pointerdown", handleOutsidePointer, true);
411
- }, [allowCustomValue, inputValue, onCommit, originalValue]);
425
+ }, [onCommit, resolveInputValue]);
412
426
  const selectOption = (option) => {
413
427
  onChange(option.value);
414
428
  onCommit(option.value);
@@ -430,7 +444,7 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
430
444
  event.preventDefault();
431
445
  const option = filteredOptions[activeIndex];
432
446
  if (option) selectOption(option);
433
- else if (allowCustomValue) onCommit(inputValue || originalValue);
447
+ else if (allowCustomValue || strictOptions) onCommit(resolveInputValue());
434
448
  }
435
449
  };
436
450
  return /* @__PURE__ */ jsxs3("div", { ref: editorRef, className: "rvg-choice-editor rvg-searchable-choice-editor", style, children: [
@@ -444,6 +458,7 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
444
458
  "aria-controls": listboxId,
445
459
  onFocus: (event) => event.currentTarget.select(),
446
460
  onChange: (event) => {
461
+ setHasTyped(true);
447
462
  setInputValue(event.target.value);
448
463
  onChange(event.target.value);
449
464
  navigationSourceRef.current = "filter";
@@ -459,8 +474,8 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
459
474
  ref: index === activeIndex ? activeOptionRef : void 0,
460
475
  type: "button",
461
476
  role: "option",
462
- "aria-selected": option.value === originalValue,
463
- className: `rvg-choice-item${index === activeIndex ? " is-active" : ""}${option.value === originalValue ? " is-selected" : ""}`,
477
+ "aria-selected": option.value === selectedValue,
478
+ className: `rvg-choice-item${index === activeIndex ? " is-active" : ""}${option.value === selectedValue ? " is-selected" : ""}`,
464
479
  onPointerDown: (event) => event.preventDefault(),
465
480
  onMouseEnter: () => {
466
481
  navigationSourceRef.current = "pointer";
@@ -469,7 +484,7 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
469
484
  onClick: () => selectOption(option),
470
485
  children: [
471
486
  /* @__PURE__ */ jsx3("span", { children: option.label }),
472
- option.value === originalValue && /* @__PURE__ */ jsx3(ChoiceCheckIcon, {})
487
+ option.value === selectedValue && /* @__PURE__ */ jsx3(ChoiceCheckIcon, {})
473
488
  ]
474
489
  },
475
490
  option.value
@@ -1690,11 +1705,11 @@ function paintGrid(options) {
1690
1705
  }
1691
1706
 
1692
1707
  // src/hooks/useControllable.ts
1693
- import { useCallback, useState as useState5 } from "react";
1708
+ import { useCallback as useCallback2, useState as useState5 } from "react";
1694
1709
  function useControllableKeys(value, defaultValue, onChange) {
1695
1710
  const [internal, setInternal] = useState5(defaultValue ?? []);
1696
1711
  const keys = value === void 0 ? internal : value;
1697
- const setKeys = useCallback((next) => {
1712
+ const setKeys = useCallback2((next) => {
1698
1713
  if (value === void 0) setInternal(next);
1699
1714
  onChange?.(next);
1700
1715
  }, [onChange, value]);
@@ -1703,7 +1718,7 @@ function useControllableKeys(value, defaultValue, onChange) {
1703
1718
  function useControllableValue(value, defaultValue, onChange) {
1704
1719
  const [internal, setInternal] = useState5(defaultValue ?? null);
1705
1720
  const selected = value === void 0 ? internal : value;
1706
- const setSelected = useCallback((next) => {
1721
+ const setSelected = useCallback2((next) => {
1707
1722
  if (value === void 0) setInternal(next);
1708
1723
  onChange?.(next);
1709
1724
  }, [onChange, value]);
@@ -2173,7 +2188,7 @@ function TableInner({
2173
2188
  if (headerTooltipTimerRef.current !== null) clearTimeout(headerTooltipTimerRef.current);
2174
2189
  if (cellTooltipTimerRef.current !== null) clearTimeout(cellTooltipTimerRef.current);
2175
2190
  }, []);
2176
- const measureCustomHeaderHeight = useCallback2(() => {
2191
+ const measureCustomHeaderHeight = useCallback3(() => {
2177
2192
  const nextHeights = Array.from({ length: headerDepth }, () => baseHeaderHeight);
2178
2193
  customHeaderContentRefs.current.forEach((node) => {
2179
2194
  const headerCell = node.closest(".rvg-header-title");
@@ -2224,7 +2239,7 @@ function TableInner({
2224
2239
  const [hoveredHeaderAction, setHoveredHeaderAction] = useState6(null);
2225
2240
  const [visibleHeaderTooltip, setVisibleHeaderTooltip] = useState6(null);
2226
2241
  const [hoveredCellTooltip, setHoveredCellTooltip] = useState6(null);
2227
- const hideDragTooltips = useCallback2(() => {
2242
+ const hideDragTooltips = useCallback3(() => {
2228
2243
  if (headerTooltipTimerRef.current !== null) {
2229
2244
  clearTimeout(headerTooltipTimerRef.current);
2230
2245
  headerTooltipTimerRef.current = null;
@@ -2251,10 +2266,10 @@ function TableInner({
2251
2266
  const [insertedRowKeys, setInsertedRowKeys] = useState6(() => /* @__PURE__ */ new Set());
2252
2267
  const [rowOrderKeys, setRowOrderKeys] = useState6(null);
2253
2268
  const settledPendingInsertIdsRef = useRef4(/* @__PURE__ */ new Set());
2254
- const getDataRowKey = useCallback2((row, index) => {
2269
+ const getDataRowKey = useCallback3((row, index) => {
2255
2270
  return typeof rowKey === "function" ? rowKey(row, index) : rowKey ? row[rowKey] : getDefaultRowKey(row, index);
2256
2271
  }, [rowKey]);
2257
- const getSourceRowIndexByKey = useCallback2((key, preferredIndex) => {
2272
+ const getSourceRowIndexByKey = useCallback3((key, preferredIndex) => {
2258
2273
  const preferredRow = sourceRows[preferredIndex];
2259
2274
  if (preferredRow && getDataRowKey(preferredRow, preferredIndex) === key) return preferredIndex;
2260
2275
  const start = Math.max(0, preferredIndex - 8);
@@ -2325,7 +2340,7 @@ function TableInner({
2325
2340
  return result * direction;
2326
2341
  });
2327
2342
  }, [columns, filteredRows, language, onSortChange, sortState]);
2328
- const resolveSelectionTarget = useCallback2((target) => {
2343
+ const resolveSelectionTarget = useCallback3((target) => {
2329
2344
  if (target == null) return target;
2330
2345
  const columnIndex = columns.findIndex((column) => column.key === target.columnKey);
2331
2346
  const rowIndex = target.rowKey !== void 0 ? rows.findIndex((row2, index) => getDataRowKey(row2, index) === target.rowKey) : target.rowIndex ?? -1;
@@ -2340,7 +2355,7 @@ function TableInner({
2340
2355
  }, [columns, getDataRowKey, rows]);
2341
2356
  const resolvedSelectedCell = useMemo2(() => resolveSelectionTarget(selectedCell), [resolveSelectionTarget, selectedCell]);
2342
2357
  const resolvedDefaultSelectedCell = useMemo2(() => resolveSelectionTarget(defaultSelectedCell), [defaultSelectedCell, resolveSelectionTarget]);
2343
- const handleSelectedCellChange = useCallback2((next) => {
2358
+ const handleSelectedCellChange = useCallback3((next) => {
2344
2359
  if (!onSelectedCellChange) return;
2345
2360
  if (!next) {
2346
2361
  onSelectedCellChange(null);
@@ -2369,7 +2384,7 @@ function TableInner({
2369
2384
  document.addEventListener("pointerdown", handleOutsidePointerDown);
2370
2385
  return () => document.removeEventListener("pointerdown", handleOutsidePointerDown);
2371
2386
  }, [selection, setSelection]);
2372
- const applyRowOrder = useCallback2((sourceIndex, targetIndex) => {
2387
+ const applyRowOrder = useCallback3((sourceIndex, targetIndex) => {
2373
2388
  if (sourceIndex === targetIndex || sourceIndex < 0 || targetIndex < 0 || sourceIndex >= rows.length || targetIndex >= rows.length) return;
2374
2389
  const nextEntries = rows.map((row, index) => ({ row, key: getDataRowKey(row, index) }));
2375
2390
  const [entry] = nextEntries.splice(sourceIndex, 1);
@@ -2378,7 +2393,7 @@ function TableInner({
2378
2393
  setRowOrderKeys(nextEntries.map(({ key }) => key));
2379
2394
  onRowsReorder?.(nextRows, { sourceIndex, targetIndex });
2380
2395
  }, [getDataRowKey, onRowsReorder, rows]);
2381
- const applyColumnOrder = useCallback2((sourceIndex, targetIndex, detail) => {
2396
+ const applyColumnOrder = useCallback3((sourceIndex, targetIndex, detail) => {
2382
2397
  const nextColumns = reorderColumns(sourceColumns, detail);
2383
2398
  setSourceColumns(nextColumns);
2384
2399
  onColumnsReorder?.(nextColumns, { sourceIndex, targetIndex, ...detail });
@@ -2451,7 +2466,7 @@ function TableInner({
2451
2466
  }
2452
2467
  return lefts;
2453
2468
  }, [columns, fixedWidth, metrics]);
2454
- const getScrollableColumnLeft = useCallback2((columnIndex) => scrollableColumnLefts[columnIndex] ?? fixedWidth, [fixedWidth, scrollableColumnLefts]);
2469
+ const getScrollableColumnLeft = useCallback3((columnIndex) => scrollableColumnLefts[columnIndex] ?? fixedWidth, [fixedWidth, scrollableColumnLefts]);
2455
2470
  const hasRowSelectionColumn = useMemo2(() => columns.some((column) => column.rowSelection), [columns]);
2456
2471
  const rowSelectionMode = typeof rowSelection === "object" ? rowSelection.mode ?? "multiple" : "multiple";
2457
2472
  const summarySignature = useMemo2(() => getSummarySignature(columns), [columns]);
@@ -2504,7 +2519,7 @@ function TableInner({
2504
2519
  const rowScrollTop = fixedHeader ? currentScroll2.top : Math.max(0, currentScroll2.top - bodyTop);
2505
2520
  return getViewportRange(currentScroll2.left, rowScrollTop, viewport.width, bodyViewportHeight, rows.length, rowHeight, metrics, virtualOverscan);
2506
2521
  }, [bodyTop, bodyViewportHeight, columns.length, fixedHeader, isVirtualized, metrics, rowHeight, rows.length, scrollPosition, viewport.width, virtualOverscan]);
2507
- const getRowKey = useCallback2((row, index) => {
2522
+ const getRowKey = useCallback3((row, index) => {
2508
2523
  const key = getDataRowKey(row, index);
2509
2524
  rowByKeyRef.current.set(key, row);
2510
2525
  rowIndexByKeyRef.current.set(key, index);
@@ -2573,7 +2588,7 @@ function TableInner({
2573
2588
  const selectedRowKeySet = useMemo2(() => new Set(rowKeys), [rowKeys]);
2574
2589
  const selectedColumnKeySet = useMemo2(() => new Set(columnKeys), [columnKeys]);
2575
2590
  const insertedRowKeySet = useMemo2(() => insertedRowKeys, [insertedRowKeys]);
2576
- const measureHeaderTitleWidth = useCallback2((columnIndex) => {
2591
+ const measureHeaderTitleWidth = useCallback3((columnIndex) => {
2577
2592
  const title = columns[columnIndex]?.title ?? "";
2578
2593
  const context = canvasRef.current?.getContext("2d");
2579
2594
  if (!context) return Array.from(title).reduce((width3, character) => width3 + (/^[\x00-\x7F]$/.test(character) ? 7 : 13), 0);
@@ -2583,7 +2598,7 @@ function TableInner({
2583
2598
  context.restore();
2584
2599
  return width2;
2585
2600
  }, [columns]);
2586
- const getCellLabel = useCallback2((rowIndex, columnIndex) => {
2601
+ const getCellLabel = useCallback3((rowIndex, columnIndex) => {
2587
2602
  const row = rows[rowIndex];
2588
2603
  const column = columns[columnIndex];
2589
2604
  if (!row || !column || column.rowSelection || column.rowDragHandle) return "";
@@ -2594,8 +2609,8 @@ function TableInner({
2594
2609
  return "Render error";
2595
2610
  }
2596
2611
  }, [columns, rows]);
2597
- const getCellSpan = useCallback2((rowIndex, columnIndex) => cellSpanLookup.covered.get(getCellCoordKey2(rowIndex, columnIndex)), [cellSpanLookup]);
2598
- const getCellDisplayWidth = useCallback2((columnIndex, colSpan = 1) => {
2612
+ const getCellSpan = useCallback3((rowIndex, columnIndex) => cellSpanLookup.covered.get(getCellCoordKey2(rowIndex, columnIndex)), [cellSpanLookup]);
2613
+ const getCellDisplayWidth = useCallback3((columnIndex, colSpan = 1) => {
2599
2614
  let width2 = 0;
2600
2615
  for (let index = columnIndex; index < Math.min(columns.length, columnIndex + colSpan); index += 1) {
2601
2616
  width2 += metrics[index]?.width ?? 0;
@@ -2610,7 +2625,7 @@ function TableInner({
2610
2625
  if (nextRowIndex === selection.rowIndex && nextColumnIndex === selection.columnIndex) return;
2611
2626
  setSelection({ ...selection, rowIndex: nextRowIndex, columnIndex: nextColumnIndex });
2612
2627
  }, [columns, getRowKey, rows, selection, setSelection]);
2613
- const draw = useCallback2(() => {
2628
+ const draw = useCallback3(() => {
2614
2629
  frameRef.current = null;
2615
2630
  const canvas = canvasRef.current;
2616
2631
  if (!canvas || viewport.width <= 0) return;
@@ -2656,15 +2671,15 @@ function TableInner({
2656
2671
  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 };
2657
2672
  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, resolveColor: (color) => resolveCanvasColor(themeStyles, color), colors: themeColors });
2658
2673
  }, [autoHeight, bodyTop, bodyViewportHeight, bottomSummaryHeight, cellAnnotations, cellSpanLookup, columnDragPreview, 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]);
2659
- const scheduleDraw = useCallback2(() => {
2674
+ const scheduleDraw = useCallback3(() => {
2660
2675
  if (frameRef.current === null) frameRef.current = requestAnimationFrame(draw);
2661
2676
  }, [draw]);
2662
- const drawImmediately = useCallback2(() => {
2677
+ const drawImmediately = useCallback3(() => {
2663
2678
  if (frameRef.current !== null) cancelAnimationFrame(frameRef.current);
2664
2679
  frameRef.current = null;
2665
2680
  draw();
2666
2681
  }, [draw]);
2667
- const getSelectionHorizontalVisibility = useCallback2((scrollLeft) => {
2682
+ const getSelectionHorizontalVisibility = useCallback3((scrollLeft) => {
2668
2683
  if (!selection || selectionRange) return "visible";
2669
2684
  const column = columns[selection.columnIndex];
2670
2685
  if (!column || column.fixed !== void 0) return "visible";
@@ -2675,7 +2690,7 @@ function TableInner({
2675
2690
  if (left >= viewport.width - rightFixedWidth) return "hidden-right";
2676
2691
  return "visible";
2677
2692
  }, [columns, fixedWidth, getCellDisplayWidth, getCellSpan, getScrollableColumnLeft, rightFixedWidth, selection, selectionRange, viewport.width]);
2678
- const getSelectionEdgeIndicator = useCallback2((scrollLeft, scrollTop) => {
2693
+ const getSelectionEdgeIndicator = useCallback3((scrollLeft, scrollTop) => {
2679
2694
  if (!selection || selectionRange) return null;
2680
2695
  const row = rows[selection.rowIndex];
2681
2696
  const metric = metrics[selection.columnIndex];
@@ -2783,7 +2798,7 @@ function TableInner({
2783
2798
  }
2784
2799
  };
2785
2800
  }, [bodyTop, columns, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, getScrollableColumnLeft, leftFixedOffsets, metrics, renderHeight, rightFixedOffsets, rightFixedWidth, rowHeight, rows, selection, selectionRange, viewport.width]);
2786
- const syncSelectionEdgeTarget = useCallback2((scrollLeft, scrollTop) => {
2801
+ const syncSelectionEdgeTarget = useCallback3((scrollLeft, scrollTop) => {
2787
2802
  const target = selectionEdgeTargetRef.current;
2788
2803
  if (!target) return;
2789
2804
  const indicator = getSelectionEdgeIndicator(scrollLeft, scrollTop);
@@ -2905,7 +2920,7 @@ function TableInner({
2905
2920
  scroller.addEventListener("scroll", lockScroll);
2906
2921
  return () => scroller.removeEventListener("scroll", lockScroll);
2907
2922
  }, [insertBusy]);
2908
- const showCustomScrollbar = useCallback2((axis, persist = false) => {
2923
+ const showCustomScrollbar = useCallback3((axis, persist = false) => {
2909
2924
  const showVertical = axis === "vertical" || axis === "both";
2910
2925
  const showHorizontal = axis === "horizontal" || axis === "both";
2911
2926
  if (showVertical) {
@@ -2929,15 +2944,15 @@ function TableInner({
2929
2944
  }
2930
2945
  }
2931
2946
  }, []);
2932
- const keepCustomScrollbarVisible = useCallback2((axis) => {
2947
+ const keepCustomScrollbarVisible = useCallback3((axis) => {
2933
2948
  showCustomScrollbar(axis, true);
2934
2949
  }, [showCustomScrollbar]);
2935
- const releaseCustomScrollbarVisible = useCallback2((axis) => {
2950
+ const releaseCustomScrollbarVisible = useCallback3((axis) => {
2936
2951
  if (axis === "vertical" && verticalScrollbarDragRef.current) return;
2937
2952
  if (axis === "horizontal" && horizontalScrollbarDragRef.current) return;
2938
2953
  showCustomScrollbar(axis);
2939
2954
  }, [showCustomScrollbar]);
2940
- const updateCustomScrollbars = useCallback2(() => {
2955
+ const updateCustomScrollbars = useCallback3(() => {
2941
2956
  const scroller = scrollerRef.current;
2942
2957
  const verticalTrack = verticalScrollbarRef.current;
2943
2958
  const verticalThumb = verticalScrollbarThumbRef.current;
@@ -2990,7 +3005,7 @@ function TableInner({
2990
3005
  useLayoutEffect3(() => {
2991
3006
  updateCustomScrollbars();
2992
3007
  }, [contentHeight, contentWidth, updateCustomScrollbars, viewport.height, viewport.width]);
2993
- const handleVerticalScrollbarPointerDown = useCallback2((event) => {
3008
+ const handleVerticalScrollbarPointerDown = useCallback3((event) => {
2994
3009
  const scroller = scrollerRef.current;
2995
3010
  const track = verticalScrollbarRef.current;
2996
3011
  const thumb = verticalScrollbarThumbRef.current;
@@ -3027,7 +3042,7 @@ function TableInner({
3027
3042
  maxScrollTop
3028
3043
  };
3029
3044
  }, [showCustomScrollbar, updateCustomScrollbars]);
3030
- const handleVerticalScrollbarPointerMove = useCallback2((event) => {
3045
+ const handleVerticalScrollbarPointerMove = useCallback3((event) => {
3031
3046
  const drag = verticalScrollbarDragRef.current;
3032
3047
  const scroller = scrollerRef.current;
3033
3048
  if (!drag || !scroller || drag.pointerId !== event.pointerId) return;
@@ -3037,14 +3052,14 @@ function TableInner({
3037
3052
  scroller.scrollTop = nextThumbTop / maxThumbTop * drag.maxScrollTop;
3038
3053
  updateCustomScrollbars();
3039
3054
  }, [updateCustomScrollbars]);
3040
- const handleVerticalScrollbarPointerUp = useCallback2((event) => {
3055
+ const handleVerticalScrollbarPointerUp = useCallback3((event) => {
3041
3056
  const drag = verticalScrollbarDragRef.current;
3042
3057
  if (!drag || drag.pointerId !== event.pointerId) return;
3043
3058
  if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
3044
3059
  verticalScrollbarDragRef.current = null;
3045
3060
  showCustomScrollbar("vertical");
3046
3061
  }, [showCustomScrollbar]);
3047
- const handleHorizontalScrollbarPointerDown = useCallback2((event) => {
3062
+ const handleHorizontalScrollbarPointerDown = useCallback3((event) => {
3048
3063
  const scroller = scrollerRef.current;
3049
3064
  const track = horizontalScrollbarRef.current;
3050
3065
  const thumb = horizontalScrollbarThumbRef.current;
@@ -3087,7 +3102,7 @@ function TableInner({
3087
3102
  maxScrollLeft
3088
3103
  };
3089
3104
  }, [drawImmediately, getSelectionHorizontalVisibility, showCustomScrollbar, syncSelectionEdgeTarget, updateCustomScrollbars]);
3090
- const handleHorizontalScrollbarPointerMove = useCallback2((event) => {
3105
+ const handleHorizontalScrollbarPointerMove = useCallback3((event) => {
3091
3106
  const drag = horizontalScrollbarDragRef.current;
3092
3107
  const scroller = scrollerRef.current;
3093
3108
  if (!drag || !scroller || drag.pointerId !== event.pointerId) return;
@@ -3103,7 +3118,7 @@ function TableInner({
3103
3118
  if (visibilityChanged) drawImmediately();
3104
3119
  updateCustomScrollbars();
3105
3120
  }, [drawImmediately, getSelectionHorizontalVisibility, syncSelectionEdgeTarget, updateCustomScrollbars]);
3106
- const handleHorizontalScrollbarPointerUp = useCallback2((event) => {
3121
+ const handleHorizontalScrollbarPointerUp = useCallback3((event) => {
3107
3122
  const drag = horizontalScrollbarDragRef.current;
3108
3123
  if (!drag || drag.pointerId !== event.pointerId) return;
3109
3124
  if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
@@ -3117,7 +3132,7 @@ function TableInner({
3117
3132
  clip.style.transform = "";
3118
3133
  });
3119
3134
  }, [scrollPosition.top]);
3120
- const handleScroll = useCallback2(() => {
3135
+ const handleScroll = useCallback3(() => {
3121
3136
  const element = scrollerRef.current;
3122
3137
  if (!element) return;
3123
3138
  const previousScroll = scrollRef.current;
@@ -3194,7 +3209,7 @@ function TableInner({
3194
3209
  setHoveredCellTooltip(null);
3195
3210
  scheduleDraw();
3196
3211
  }, [bodyTop, columns, renderHeight, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, getScrollableColumnLeft, metrics, rightFixedOffsets, rightFixedWidth, rowHeight, rows, scheduleDraw, selection, selectionRange, showCustomScrollbar, syncSelectionEdgeTarget, updateCustomScrollbars, viewport.width]);
3197
- const locateColumn = useCallback2((clientX) => {
3212
+ const locateColumn = useCallback3((clientX) => {
3198
3213
  const canvas = canvasRef.current;
3199
3214
  if (!canvas) return -1;
3200
3215
  const rect = canvas.getBoundingClientRect();
@@ -3222,7 +3237,7 @@ function TableInner({
3222
3237
  }
3223
3238
  return columnIndex;
3224
3239
  }, [columns, fixedWidth, getScrollableColumnLeft, leftFixedOffsets, metrics, rightFixedOffsets, rightFixedWidth, viewport.width]);
3225
- const getDisplayedColumnLeft = useCallback2((columnIndex) => {
3240
+ const getDisplayedColumnLeft = useCallback3((columnIndex) => {
3226
3241
  const metric = metrics[columnIndex];
3227
3242
  if (columns[columnIndex].fixed === "left") return leftFixedOffsets.get(columnIndex) ?? 0;
3228
3243
  if (columns[columnIndex].fixed === "right") return viewport.width - (rightFixedOffsets.get(columnIndex) ?? 0) - metric.width;
@@ -3232,7 +3247,7 @@ function TableInner({
3232
3247
  const displayedContentRight = columns.reduce((right, _column, columnIndex) => Math.max(right, getDisplayedColumnLeft(columnIndex) + metrics[columnIndex].width), 0);
3233
3248
  return displayedContentRight > 0 && displayedContentRight < viewport.width ? Math.round(displayedContentRight) - 1 : null;
3234
3249
  }, [columns, getDisplayedColumnLeft, metrics, scrollPosition.left, viewport.width]);
3235
- const getHeaderLevelAtY = useCallback2((localY, headerY) => {
3250
+ const getHeaderLevelAtY = useCallback3((localY, headerY) => {
3236
3251
  const y = localY - headerY;
3237
3252
  for (let level = 0; level < headerRowHeights.length; level += 1) {
3238
3253
  const top = headerRowOffsets[level] ?? 0;
@@ -3240,11 +3255,11 @@ function TableInner({
3240
3255
  }
3241
3256
  return headerDepth - 1;
3242
3257
  }, [headerDepth, headerRowHeights, headerRowOffsets]);
3243
- const resizeColumn = useCallback2((columnKey, width2) => {
3258
+ const resizeColumn = useCallback3((columnKey, width2) => {
3244
3259
  setResizedColumnWidths((current) => current[columnKey] === width2 ? current : { ...current, [columnKey]: width2 });
3245
3260
  onColumnResize?.(columnKey, width2);
3246
3261
  }, [onColumnResize]);
3247
- const locateHeaderAction = useCallback2((clientX, clientY, columnIndex) => {
3262
+ const locateHeaderAction = useCallback3((clientX, clientY, columnIndex) => {
3248
3263
  if (columnIndex < 0 || !canvasRef.current) return null;
3249
3264
  const column = columns[columnIndex];
3250
3265
  if (column.rowSelection || column.rowDragHandle || column.rowNumber) return null;
@@ -3264,7 +3279,7 @@ function TableInner({
3264
3279
  if (visibleActions.filter && localX >= right - headerActionSlotWidth && localX < right) return "filter";
3265
3280
  return null;
3266
3281
  }, [columnDraggable, columns, fixedHeader, getDisplayedColumnLeft, headerActionSize, headerActionSlotWidth, headerLeafHeight, headerLeafTop, measureHeaderTitleWidth, metrics]);
3267
- const isHeaderTitleTruncated = useCallback2((columnIndex) => {
3282
+ const isHeaderTitleTruncated = useCallback3((columnIndex) => {
3268
3283
  const canvas = canvasRef.current;
3269
3284
  if (columnIndex < 0 || !canvas) return false;
3270
3285
  const column = columns[columnIndex];
@@ -3274,7 +3289,7 @@ function TableInner({
3274
3289
  const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * headerActionSlotWidth;
3275
3290
  return getHeaderTitleRequiredWidth(column, titleWidth) > Math.max(0, metric.width - actionWidth);
3276
3291
  }, [columnDraggable, columns, headerActionSlotWidth, measureHeaderTitleWidth, metrics]);
3277
- const locateHeaderDragHandle = useCallback2((clientX, clientY, columnIndex) => {
3292
+ const locateHeaderDragHandle = useCallback3((clientX, clientY, columnIndex) => {
3278
3293
  if (!columnDraggable || columnIndex < 0 || !canvasRef.current) return false;
3279
3294
  const column = columns[columnIndex];
3280
3295
  if (column.rowSelection || column.rowDragHandle || column.rowNumber) return false;
@@ -3288,14 +3303,14 @@ function TableInner({
3288
3303
  const columnRight = getDisplayedColumnLeft(columnIndex) + metrics[columnIndex].width;
3289
3304
  return localX >= columnRight - headerActionSlotWidth && localX < columnRight - 2;
3290
3305
  }, [columnDraggable, columns, fixedHeader, getDisplayedColumnLeft, headerActionSize, headerActionSlotWidth, headerLeafHeight, headerLeafTop, measureHeaderTitleWidth, metrics]);
3291
- const getHeaderCellLeft = useCallback2((cell) => {
3306
+ const getHeaderCellLeft = useCallback3((cell) => {
3292
3307
  const startMetric = metrics[cell.startIndex];
3293
3308
  const fixedSide = columns[cell.startIndex]?.fixed;
3294
3309
  if (!startMetric) return 0;
3295
3310
  return fixedSide === "left" ? leftFixedOffsets.get(cell.startIndex) ?? 0 : fixedSide === "right" ? viewport.width - (rightFixedOffsets.get(cell.startIndex) ?? 0) - startMetric.width : getScrollableColumnLeft(cell.startIndex) - scrollRef.current.left;
3296
3311
  }, [columns, getScrollableColumnLeft, leftFixedOffsets, metrics, rightFixedOffsets, viewport.width]);
3297
- const getHeaderCellWidth = useCallback2((cell) => cell.leaf ? metrics[cell.startIndex]?.width ?? 0 : getCellDisplayWidth(cell.startIndex, cell.endIndex - cell.startIndex + 1), [getCellDisplayWidth, metrics]);
3298
- const getHeaderCellEdgeFromMetrics = useCallback2((startIndex, endIndex, edge, nextMetrics) => {
3312
+ const getHeaderCellWidth = useCallback3((cell) => cell.leaf ? metrics[cell.startIndex]?.width ?? 0 : getCellDisplayWidth(cell.startIndex, cell.endIndex - cell.startIndex + 1), [getCellDisplayWidth, metrics]);
3313
+ const getHeaderCellEdgeFromMetrics = useCallback3((startIndex, endIndex, edge, nextMetrics) => {
3299
3314
  const fixedSide = columns[startIndex]?.fixed;
3300
3315
  const width2 = nextMetrics.slice(startIndex, endIndex + 1).reduce((total, metric) => total + metric.width, 0);
3301
3316
  let left;
@@ -3319,7 +3334,7 @@ function TableInner({
3319
3334
  }
3320
3335
  return left + (edge === "right" ? width2 : 0);
3321
3336
  }, [columns, fixedWidth, viewport.width]);
3322
- const locateHeaderResizeHit = useCallback2((clientX, clientY) => {
3337
+ const locateHeaderResizeHit = useCallback3((clientX, clientY) => {
3323
3338
  if (!columnResizable || !canvasRef.current) return null;
3324
3339
  const rect = canvasRef.current.getBoundingClientRect();
3325
3340
  const localX = clientX - rect.left;
@@ -3346,15 +3361,15 @@ function TableInner({
3346
3361
  }
3347
3362
  return closest;
3348
3363
  }, [columnResizable, columns, fixedHeader, getHeaderCellLeft, getHeaderCellWidth, getHeaderLevelAtY, headerCells, headerHeight]);
3349
- const locateHeaderResizeCell = useCallback2((clientX, clientY) => locateHeaderResizeHit(clientX, clientY)?.cell ?? null, [locateHeaderResizeHit]);
3350
- const locateHeaderCellDragHandle = useCallback2((clientX, clientY, cell) => {
3364
+ const locateHeaderResizeCell = useCallback3((clientX, clientY) => locateHeaderResizeHit(clientX, clientY)?.cell ?? null, [locateHeaderResizeHit]);
3365
+ const locateHeaderCellDragHandle = useCallback3((clientX, clientY, cell) => {
3351
3366
  if (!columnDraggable || !canvasRef.current) return false;
3352
3367
  if (cell.leaf) return locateHeaderDragHandle(clientX, clientY, cell.startIndex);
3353
3368
  const localX = clientX - canvasRef.current.getBoundingClientRect().left;
3354
3369
  const right = getHeaderCellLeft(cell) + getHeaderCellWidth(cell);
3355
3370
  return localX >= right - headerActionSlotWidth && localX < right - 2;
3356
3371
  }, [columnDraggable, getHeaderCellLeft, getHeaderCellWidth, headerActionSlotWidth, locateHeaderDragHandle]);
3357
- const locateHeaderCell = useCallback2((clientX, clientY) => {
3372
+ const locateHeaderCell = useCallback3((clientX, clientY) => {
3358
3373
  const canvas = canvasRef.current;
3359
3374
  if (!canvas) return null;
3360
3375
  const rect = canvas.getBoundingClientRect();
@@ -3371,7 +3386,7 @@ function TableInner({
3371
3386
  }
3372
3387
  return null;
3373
3388
  }, [fixedHeader, getHeaderCellLeft, getHeaderCellWidth, getHeaderLevelAtY, headerCells, headerHeight]);
3374
- const setDragGuide = useCallback2((x, top = 0) => {
3389
+ const setDragGuide = useCallback3((x, top = 0) => {
3375
3390
  const guide = dragGuideRef.current;
3376
3391
  if (!guide) return;
3377
3392
  guide.style.display = x === null ? "none" : "block";
@@ -3380,20 +3395,20 @@ function TableInner({
3380
3395
  guide.style.transform = `translateX(${Math.round(x)}px)`;
3381
3396
  }
3382
3397
  }, []);
3383
- const setRowDragGuide = useCallback2((y) => {
3398
+ const setRowDragGuide = useCallback3((y) => {
3384
3399
  const guide = rowDragGuideRef.current;
3385
3400
  if (!guide) return;
3386
3401
  guide.style.display = y === null ? "none" : "block";
3387
3402
  if (y !== null) guide.style.transform = `translateY(${Math.round(y)}px)`;
3388
3403
  }, []);
3389
- const locateRow = useCallback2((clientY) => {
3404
+ const locateRow = useCallback3((clientY) => {
3390
3405
  const canvas = canvasRef.current;
3391
3406
  if (!canvas) return -1;
3392
3407
  const localY = clientY - canvas.getBoundingClientRect().top;
3393
3408
  const rowIndex = Math.floor((localY - bodyTop + scrollRef.current.top) / rowHeight);
3394
3409
  return rowIndex >= 0 && rowIndex < rows.length ? rowIndex : -1;
3395
3410
  }, [bodyTop, rowHeight, rows.length]);
3396
- const handleRowOrderPointerDown = useCallback2((event) => {
3411
+ const handleRowOrderPointerDown = useCallback3((event) => {
3397
3412
  if (!rowDraggable || event.button !== 0 || rowOrderDragRef.current) return;
3398
3413
  const columnIndex = locateColumn(event.clientX);
3399
3414
  const sourceIndex = locateRow(event.clientY);
@@ -3407,7 +3422,7 @@ function TableInner({
3407
3422
  targetIndex: sourceIndex
3408
3423
  };
3409
3424
  }, [columns, locateColumn, locateRow, rowDraggable]);
3410
- const handleRowOrderPointerMove = useCallback2((event) => {
3425
+ const handleRowOrderPointerMove = useCallback3((event) => {
3411
3426
  const drag = rowOrderDragRef.current;
3412
3427
  if (!drag || drag.pointerId !== event.pointerId) return;
3413
3428
  if (!drag.active) {
@@ -3435,7 +3450,7 @@ function TableInner({
3435
3450
  setRowDragPreview(null);
3436
3451
  setRowDragGuide(rowTop + (edge === "bottom" ? rowHeight : 0));
3437
3452
  }, [bodyTop, hideDragTooltips, locateRow, renderHeight, rowHeight, rows.length, setRowDragGuide]);
3438
- const finishRowOrderPointerDrag = useCallback2((event, apply) => {
3453
+ const finishRowOrderPointerDrag = useCallback3((event, apply) => {
3439
3454
  const drag = rowOrderDragRef.current;
3440
3455
  if (!drag || drag.pointerId !== event.pointerId) return;
3441
3456
  if (event.currentTarget.hasPointerCapture?.(event.pointerId)) event.currentTarget.releasePointerCapture?.(event.pointerId);
@@ -3453,7 +3468,7 @@ function TableInner({
3453
3468
  });
3454
3469
  }
3455
3470
  }, [applyRowOrder, setRowDragGuide]);
3456
- const locateCell = useCallback2((clientX, clientY) => {
3471
+ const locateCell = useCallback3((clientX, clientY) => {
3457
3472
  const canvas = canvasRef.current;
3458
3473
  if (!canvas) return null;
3459
3474
  const rect = canvas.getBoundingClientRect();
@@ -3473,7 +3488,7 @@ function TableInner({
3473
3488
  columnKey: columns[targetColumnIndex].key
3474
3489
  };
3475
3490
  }, [bodyTop, columns, fixedHeader, getCellSpan, getRowKey, locateColumn, rowHeight, rows]);
3476
- const beginRangeDrag = useCallback2((event) => {
3491
+ const beginRangeDrag = useCallback3((event) => {
3477
3492
  if (!rangeSelection) return;
3478
3493
  if (event.button !== 0 || editing || event.target !== event.currentTarget) return;
3479
3494
  const cell = locateCell(event.clientX, event.clientY);
@@ -3482,7 +3497,7 @@ function TableInner({
3482
3497
  setSelectionRange(null);
3483
3498
  event.currentTarget.setPointerCapture(event.pointerId);
3484
3499
  }, [columns, editing, locateCell, rangeSelection]);
3485
- const updateRangeDrag = useCallback2((event) => {
3500
+ const updateRangeDrag = useCallback3((event) => {
3486
3501
  const drag = rangeDragRef.current;
3487
3502
  if (!drag) return;
3488
3503
  const focus = locateCell(event.clientX, event.clientY);
@@ -3492,7 +3507,7 @@ function TableInner({
3492
3507
  event.preventDefault();
3493
3508
  setSelectionRange((current) => current?.focus.rowIndex === focus.rowIndex && current.focus.columnIndex === focus.columnIndex ? current : { anchor: drag.anchor, focus });
3494
3509
  }, [columns, locateCell]);
3495
- const endRangeDrag = useCallback2((event) => {
3510
+ const endRangeDrag = useCallback3((event) => {
3496
3511
  const drag = rangeDragRef.current;
3497
3512
  if (!drag) return;
3498
3513
  rangeDragRef.current = null;
@@ -3502,7 +3517,7 @@ function TableInner({
3502
3517
  setSelection(drag.anchor);
3503
3518
  scrollerRef.current?.focus({ preventScroll: true });
3504
3519
  }, [setSelection]);
3505
- const selectRow = useCallback2((rowIndex, event, forceToggle = false) => {
3520
+ const selectRow = useCallback3((rowIndex, event, forceToggle = false) => {
3506
3521
  if (!rowSelection) return;
3507
3522
  clickedCellRef.current = null;
3508
3523
  setSelectionRange(null);
@@ -3522,7 +3537,7 @@ function TableInner({
3522
3537
  rowAnchorRef.current = rowIndex;
3523
3538
  setRowKeys(next);
3524
3539
  }, [getRowKey, rowKeys, rowSelection, rows, selectedRowKeySet, setRowKeys, setSelection]);
3525
- const selectColumn = useCallback2((columnIndex, event) => {
3540
+ const selectColumn = useCallback3((columnIndex, event) => {
3526
3541
  if (!columnSelection || columnIndex < 0) return;
3527
3542
  clickedCellRef.current = null;
3528
3543
  setSelectionRange(null);
@@ -3542,7 +3557,7 @@ function TableInner({
3542
3557
  columnAnchorRef.current = columnIndex;
3543
3558
  setColumnKeys(next);
3544
3559
  }, [columnKeys, columnSelection, columns, selectedColumnKeySet, setColumnKeys, setSelection]);
3545
- const toggleColumnFromMenu = useCallback2((columnIndex) => {
3560
+ const toggleColumnFromMenu = useCallback3((columnIndex) => {
3546
3561
  if (!columnSelection || columnIndex < 0) return;
3547
3562
  clickedCellRef.current = null;
3548
3563
  setSelectionRange(null);
@@ -3554,7 +3569,7 @@ function TableInner({
3554
3569
  columnAnchorRef.current = columnIndex;
3555
3570
  setColumnKeys(next);
3556
3571
  }, [columnKeys, columnSelection, columns, selectedColumnKeySet, setColumnKeys, setSelection]);
3557
- const revealCell = useCallback2((cell) => {
3572
+ const revealCell = useCallback3((cell) => {
3558
3573
  const scroller = scrollerRef.current;
3559
3574
  const metric = metrics[cell.columnIndex];
3560
3575
  const column = columns[cell.columnIndex];
@@ -3584,13 +3599,13 @@ function TableInner({
3584
3599
  scrollRef.current = { left: scroller.scrollLeft, top: scroller.scrollTop };
3585
3600
  scheduleDraw();
3586
3601
  }, [bodyTop, columns, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getScrollableColumnLeft, metrics, rightFixedWidth, rowHeight, scheduleDraw, viewport.width]);
3587
- const isCellEditable = useCallback2((cell) => {
3602
+ const isCellEditable = useCallback3((cell) => {
3588
3603
  const column = columns[cell.columnIndex];
3589
3604
  const row = rows[cell.rowIndex];
3590
3605
  if (!column || !row || column.dataIndex === void 0 || column.rowNumber || column.rowSelection || column.rowDragHandle) return false;
3591
3606
  return typeof column.editable === "function" ? column.editable(row[column.dataIndex], row, cell.rowIndex) : column.editable === true;
3592
3607
  }, [columns, rows]);
3593
- const beginEdit = useCallback2((cell) => {
3608
+ const beginEdit = useCallback3((cell) => {
3594
3609
  const column = columns[cell.columnIndex];
3595
3610
  if (!isCellEditable(cell) || column.dataIndex === void 0) return;
3596
3611
  const value = rows[cell.rowIndex]?.[column.dataIndex];
@@ -3660,7 +3675,7 @@ function TableInner({
3660
3675
  return next;
3661
3676
  });
3662
3677
  };
3663
- const commitEdit = useCallback2((nextDraft, close = true) => {
3678
+ const commitEdit = useCallback3((nextDraft, close = true) => {
3664
3679
  if (!editing) return;
3665
3680
  if (!isCellEditable(editing)) {
3666
3681
  setEditing(null);
@@ -3692,6 +3707,7 @@ function TableInner({
3692
3707
  options: column.editor.options,
3693
3708
  searchable: column.editor.searchable,
3694
3709
  allowCustomValue: column.editor.allowCustomValue,
3710
+ strictOptions: column.editor.strictOptions,
3695
3711
  maxDropdownHeight: column.editor.maxDropdownHeight,
3696
3712
  style: editorStyle,
3697
3713
  labels,
@@ -3764,7 +3780,7 @@ function TableInner({
3764
3780
  }
3765
3781
  );
3766
3782
  };
3767
- const handleKeyDown = useCallback2((event) => {
3783
+ const handleKeyDown = useCallback3((event) => {
3768
3784
  if (editing || !selection) return;
3769
3785
  if (event.key === "Enter") {
3770
3786
  event.preventDefault();
@@ -3837,7 +3853,7 @@ function TableInner({
3837
3853
  clipPath: `inset(${Math.max(0, visibleTop - cellTop)}px ${Math.max(0, cellLeft + cellWidth - visibleRight)}px ${Math.max(0, cellTop + cellHeight - visibleBottom)}px ${Math.max(0, visibleLeft - cellLeft)}px)`
3838
3854
  };
3839
3855
  }, [bodyTop, columns, editing, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getScrollableColumnLeft, leftFixedOffsets, metrics, renderHeight, rightFixedOffsets, rightFixedWidth, rowHeight, rows, rowStyle, viewport.width]);
3840
- const handleColumnPointerDown = useCallback2((event) => {
3856
+ const handleColumnPointerDown = useCallback3((event) => {
3841
3857
  const rect = event.currentTarget.getBoundingClientRect();
3842
3858
  const localY = event.clientY - rect.top;
3843
3859
  const headerY = fixedHeader ? 0 : -scrollRef.current.top;
@@ -3881,7 +3897,7 @@ function TableInner({
3881
3897
  return;
3882
3898
  }
3883
3899
  }, [fixedHeader, getHeaderCellLeft, getHeaderCellWidth, headerHeight, headerRowOffsets, locateHeaderResizeHit, metrics]);
3884
- const handleColumnPointerMove = useCallback2((event) => {
3900
+ const handleColumnPointerMove = useCallback3((event) => {
3885
3901
  const drag = columnDragRef.current;
3886
3902
  if (!drag) return;
3887
3903
  const column = columns[drag.columnIndex];
@@ -3917,7 +3933,7 @@ function TableInner({
3917
3933
  resizeColumn(column.key, width2);
3918
3934
  setGuideFromWidths([width2]);
3919
3935
  }, [columnDraggable, columns, getHeaderCellEdgeFromMetrics, resizeColumn, viewport.width]);
3920
- const handleColumnPointerUp = useCallback2((event) => {
3936
+ const handleColumnPointerUp = useCallback3((event) => {
3921
3937
  const drag = columnDragRef.current;
3922
3938
  if (!drag) return;
3923
3939
  if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
@@ -3927,7 +3943,7 @@ function TableInner({
3927
3943
  document.documentElement.classList.remove("rvg-is-dragging");
3928
3944
  event.currentTarget.style.cursor = "default";
3929
3945
  }, []);
3930
- const clearColumnOrderPointerDrag = useCallback2((canvas) => {
3946
+ const clearColumnOrderPointerDrag = useCallback3((canvas) => {
3931
3947
  if (columnOrderLongPressTimerRef.current) {
3932
3948
  clearTimeout(columnOrderLongPressTimerRef.current);
3933
3949
  columnOrderLongPressTimerRef.current = null;
@@ -3940,7 +3956,7 @@ function TableInner({
3940
3956
  document.documentElement.classList.remove("rvg-is-dragging");
3941
3957
  canvas.style.cursor = "default";
3942
3958
  }, [setDragGuide]);
3943
- const updateColumnDragPreview = useCallback2((sourceCell, clientX, canvas) => {
3959
+ const updateColumnDragPreview = useCallback3((sourceCell, clientX, canvas) => {
3944
3960
  const rootRect = rootRef.current?.getBoundingClientRect() ?? canvas.getBoundingClientRect();
3945
3961
  const sourceWidth = metrics[sourceCell.startIndex]?.width ?? 100;
3946
3962
  const previewWidth = sourceWidth;
@@ -3952,7 +3968,7 @@ function TableInner({
3952
3968
  sourceLeft: getDisplayedColumnLeft(sourceCell.startIndex)
3953
3969
  });
3954
3970
  }, [getDisplayedColumnLeft, metrics, viewport.width]);
3955
- const activateColumnOrderPointerDrag = useCallback2((canvas, clientX) => {
3971
+ const activateColumnOrderPointerDrag = useCallback3((canvas, clientX) => {
3956
3972
  const drag = columnOrderDragRef.current;
3957
3973
  if (!drag || drag.active) return;
3958
3974
  if (columnOrderLongPressTimerRef.current) {
@@ -3966,7 +3982,7 @@ function TableInner({
3966
3982
  canvas.style.cursor = "move";
3967
3983
  updateColumnDragPreview(drag.sourceCell, clientX, canvas);
3968
3984
  }, [hideDragTooltips, updateColumnDragPreview]);
3969
- const handleColumnOrderPointerDown = useCallback2((event) => {
3985
+ const handleColumnOrderPointerDown = useCallback3((event) => {
3970
3986
  if (!columnDraggable || event.button !== 0 || columnDragRef.current) return;
3971
3987
  const sourceCell = locateHeaderCell(event.clientX, event.clientY);
3972
3988
  if (!sourceCell || locateHeaderResizeCell(event.clientX, event.clientY)) return;
@@ -3990,7 +4006,7 @@ function TableInner({
3990
4006
  activateColumnOrderPointerDrag(canvas, drag.startX);
3991
4007
  }, 320);
3992
4008
  }, [activateColumnOrderPointerDrag, columnDraggable, columns, locateHeaderAction, locateHeaderCell, locateHeaderResizeCell]);
3993
- const handleColumnOrderPointerMove = useCallback2((event) => {
4009
+ const handleColumnOrderPointerMove = useCallback3((event) => {
3994
4010
  const drag = columnOrderDragRef.current;
3995
4011
  if (!drag || drag.pointerId !== event.pointerId || columnDragRef.current) return;
3996
4012
  if (!drag.active) {
@@ -4061,7 +4077,7 @@ function TableInner({
4061
4077
  columnEdge
4062
4078
  };
4063
4079
  }, [activateColumnOrderPointerDrag, columns, getHeaderCellLeft, getHeaderCellWidth, headerCells, headerRowOffsets, locateColumn, locateHeaderCell, setDragGuide, updateColumnDragPreview]);
4064
- const finishColumnOrderPointerDrag = useCallback2((event, apply) => {
4080
+ const finishColumnOrderPointerDrag = useCallback3((event, apply) => {
4065
4081
  const drag = columnOrderDragRef.current;
4066
4082
  if (!drag || drag.pointerId !== event.pointerId) return;
4067
4083
  if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
@@ -4085,7 +4101,7 @@ function TableInner({
4085
4101
  suppressClickRef.current = false;
4086
4102
  }, 0);
4087
4103
  }, [applyColumnOrder, clearColumnOrderPointerDrag, utilityColumnCount]);
4088
- const queueSortStateChange = useCallback2((next) => {
4104
+ const queueSortStateChange = useCallback3((next) => {
4089
4105
  setSortState(next);
4090
4106
  }, []);
4091
4107
  const renderHeaderIcons = (columnIndex, left) => {
@@ -4120,7 +4136,7 @@ function TableInner({
4120
4136
  }
4121
4137
  return icons;
4122
4138
  };
4123
- const createHeaderTooltip = useCallback2((action, columnIndex, cell) => {
4139
+ const createHeaderTooltip = useCallback3((action, columnIndex, cell) => {
4124
4140
  const column = cell?.column ?? columns[columnIndex];
4125
4141
  if (!column) return null;
4126
4142
  const cellLeft = cell ? getHeaderCellLeft(cell) : getDisplayedColumnLeft(columnIndex);
@@ -4189,7 +4205,7 @@ function TableInner({
4189
4205
  placement
4190
4206
  };
4191
4207
  }, [baseHeaderHeight, columnDraggable, columns, filterValues, fixedHeader, getDisplayedColumnLeft, getHeaderCellLeft, getHeaderCellWidth, headerActionSize, headerActionSlotWidth, headerDepth, headerLeafHeight, headerLeafTop, headerRowHeights, headerRowOffsets, labels, measureHeaderTitleWidth, metrics, scrollPosition.top, sortState, viewport.width]);
4192
- const isPointerOnHeaderTitle = useCallback2((clientX, clientY, cell) => {
4208
+ const isPointerOnHeaderTitle = useCallback3((clientX, clientY, cell) => {
4193
4209
  const canvas = canvasRef.current;
4194
4210
  if (!canvas) return false;
4195
4211
  const rect = canvas.getBoundingClientRect();
@@ -4210,7 +4226,7 @@ function TableInner({
4210
4226
  const textTop = rowTop + 8;
4211
4227
  return localX >= textLeft - 4 && localX <= textLeft + textWidth + 4 && localY >= textTop - 2 && localY <= textTop + 18;
4212
4228
  }, [columnDraggable, fixedHeader, getHeaderCellLeft, getHeaderCellWidth, headerActionSlotWidth, headerRowOffsets]);
4213
- const locateCustomHeaderTooltip = useCallback2((clientX, clientY, columnIndex) => {
4229
+ const locateCustomHeaderTooltip = useCallback3((clientX, clientY, columnIndex) => {
4214
4230
  const root = rootRef.current;
4215
4231
  const canvas = canvasRef.current;
4216
4232
  if (!root || !canvas || columnIndex < 0) return null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lensui/lens-table",
3
- "version": "0.1.83",
3
+ "version": "0.1.84",
4
4
  "description": "A lightweight, canvas-rendered React table component for LensUI.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",