@lensui/lens-table 0.1.82 → 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
@@ -1144,6 +1159,24 @@ function ellipsizeText(context, text, maxWidth) {
1144
1159
  }
1145
1160
  return `${text.slice(0, start)}${ellipsis}`;
1146
1161
  }
1162
+ function getSelectionOutlineBounds({
1163
+ left,
1164
+ right,
1165
+ top,
1166
+ bottom,
1167
+ horizontalStart,
1168
+ horizontalEnd,
1169
+ verticalStart,
1170
+ verticalEnd,
1171
+ borderWidth = 2
1172
+ }) {
1173
+ return {
1174
+ left: Math.max(left - borderWidth, horizontalStart - borderWidth),
1175
+ right: Math.min(right + 1, horizontalEnd + 1),
1176
+ top: Math.max(top - borderWidth, verticalStart - borderWidth),
1177
+ bottom: Math.min(bottom + borderWidth, verticalEnd + borderWidth)
1178
+ };
1179
+ }
1147
1180
  function paintGrid(options) {
1148
1181
  const { context: ctx, width, height, pixelRatio, scrollLeft, scrollTop, rowHeight, bodyFontSize, headerActionSlotWidth, headerHeight, fixedHeader, verticalBorderless, horizontalBorderless, frameBorderless, extendVerticalGridLines, striped, columnDraggable, sortState, filterValues, hoveredHeaderAction, rows, columns, metrics, range, selection, editing, hoveredRowIndex, selectionRange, selectedRowKeys, rowSelectionMode, selectedColumnKeys, highlightEditedCells, highlightInsertedRows, insertedRowKeys, editedCellKeys, cellAnnotations, getRowKey, rowDragPreview, columnDropTarget } = options;
1149
1182
  const headerLeafTop = options.headerLeafTop ?? 0;
@@ -1632,51 +1665,65 @@ function paintGrid(options) {
1632
1665
  const visibleRight = x + selectionWidth;
1633
1666
  const visibleBottom = y + selectionHeight;
1634
1667
  const selectionTop = fixedHeader ? bodyTop : 0;
1635
- const selectionClipTop = selectionTop;
1636
1668
  const hiddenAboveHeader = visibleBottom <= selectionTop;
1637
1669
  const hiddenBelowViewport = y >= height;
1638
1670
  const hiddenBehindLeft = !isFixed && visibleRight <= visibleLeft;
1639
1671
  const hiddenBehindRight = !isFixed && x >= selectionAreaRight;
1640
- const leftMarkerX = Math.max(0, leftFixedWidth);
1672
+ const leftMarkerX = leftFixedWidth - borderWidth;
1673
+ const rightMarkerX = selectionAreaRight - 1;
1674
+ const topMarkerY = selectionTop - borderWidth;
1675
+ const bottomMarkerY = height;
1641
1676
  if ((hiddenAboveHeader || hiddenBelowViewport) && (hiddenBehindLeft || hiddenBehindRight)) {
1642
1677
  const markerSize = 2;
1643
- const markerX = hiddenBehindLeft ? leftMarkerX : selectionAreaRight - borderWidth - rightFixedBoundaryInset;
1644
- const markerY = hiddenAboveHeader ? selectionClipTop : height - markerSize;
1678
+ const markerX = hiddenBehindLeft ? leftMarkerX : rightMarkerX;
1679
+ const markerY = hiddenAboveHeader ? topMarkerY : bottomMarkerY;
1645
1680
  ctx.fillStyle = colors.selection;
1646
1681
  ctx.fillRect(markerX, markerY, markerSize, markerSize);
1647
1682
  return;
1648
1683
  }
1684
+ const outline = getSelectionOutlineBounds({
1685
+ left: x,
1686
+ right: visibleRight,
1687
+ top: y,
1688
+ bottom: visibleBottom,
1689
+ horizontalStart: visibleLeft,
1690
+ horizontalEnd: selectionAreaRight,
1691
+ verticalStart: selectionTop,
1692
+ verticalEnd: height,
1693
+ borderWidth
1694
+ });
1649
1695
  ctx.save();
1650
1696
  ctx.beginPath();
1651
- const clipLeft = fixedSide === "left" ? 0 : Math.max(0, leftFixedWidth);
1697
+ const clipLeft = Math.max(0, outline.left);
1652
1698
  const clipRight = Math.min(width, selectionAreaRight + 1);
1653
- ctx.rect(clipLeft, selectionClipTop, clipRight - clipLeft, height - selectionClipTop);
1699
+ const clipTop = Math.max(0, outline.top);
1700
+ ctx.rect(clipLeft, clipTop, clipRight - clipLeft, height - clipTop);
1654
1701
  ctx.clip();
1655
1702
  ctx.fillStyle = colors.selection;
1656
1703
  if (hiddenAboveHeader) {
1657
- const markerLeft = Math.max(x - borderWidth, visibleLeft - 1);
1658
- const markerRight = Math.min(visibleRight + 1, selectionAreaRight);
1659
- if (markerRight > markerLeft) ctx.fillRect(markerLeft, selectionClipTop, markerRight - markerLeft, borderWidth);
1704
+ const markerLeft = Math.max(x - borderWidth, visibleLeft - borderWidth);
1705
+ const markerRight = Math.min(visibleRight + 1, selectionAreaRight + 1);
1706
+ if (markerRight > markerLeft) ctx.fillRect(markerLeft, topMarkerY, markerRight - markerLeft, borderWidth);
1660
1707
  }
1661
1708
  if (hiddenBelowViewport) {
1662
- const markerLeft = Math.max(x - borderWidth, visibleLeft - 1);
1663
- const markerRight = Math.min(visibleRight + 1, selectionAreaRight);
1664
- if (markerRight > markerLeft) ctx.fillRect(markerLeft, height - borderWidth, markerRight - markerLeft, borderWidth);
1709
+ const markerLeft = Math.max(x - borderWidth, visibleLeft - borderWidth);
1710
+ const markerRight = Math.min(visibleRight + 1, selectionAreaRight + 1);
1711
+ if (markerRight > markerLeft) ctx.fillRect(markerLeft, bottomMarkerY, markerRight - markerLeft, borderWidth);
1665
1712
  }
1666
1713
  if (hiddenBehindLeft || hiddenBehindRight) {
1667
- const markerTop = Math.max(y - borderWidth, selectionTop);
1668
- const markerBottom = Math.min(y + selectionHeight + 1, height);
1669
- const markerX = hiddenBehindLeft ? leftMarkerX : selectionAreaRight - borderWidth - rightFixedBoundaryInset;
1714
+ const markerTop = Math.max(y - borderWidth, selectionTop - borderWidth);
1715
+ const markerBottom = Math.min(y + selectionHeight + borderWidth, height + borderWidth);
1716
+ const markerX = hiddenBehindLeft ? leftMarkerX : rightMarkerX;
1670
1717
  ctx.fillRect(markerX, markerTop, borderWidth, Math.max(0, markerBottom - markerTop));
1671
1718
  }
1672
1719
  if (hiddenAboveHeader || hiddenBelowViewport || hiddenBehindLeft || hiddenBehindRight) {
1673
1720
  ctx.restore();
1674
1721
  return;
1675
1722
  }
1676
- const borderLeft = x <= 0 ? 0 : Math.max(x - borderWidth, visibleLeft);
1677
- const borderRight = Math.min(x + selectionWidth - 1, selectionAreaRight - borderWidth, width - borderWidth);
1678
- const borderTop = Math.max(y - borderWidth, selectionClipTop);
1679
- const borderBottom = Math.min(y + selectionHeight, height - borderWidth);
1723
+ const borderLeft = outline.left;
1724
+ const borderRight = outline.right - borderWidth;
1725
+ const borderTop = outline.top;
1726
+ const borderBottom = outline.bottom - borderWidth;
1680
1727
  const borderOuterWidth = borderRight + borderWidth - borderLeft;
1681
1728
  ctx.fillRect(borderLeft, borderTop, borderOuterWidth, borderWidth);
1682
1729
  ctx.fillRect(borderLeft, borderBottom, borderOuterWidth, borderWidth);
@@ -2696,14 +2743,17 @@ function TableInner({
2696
2743
  const cornerLength = 10;
2697
2744
  const hitSlop = 6;
2698
2745
  const targetSize = borderWidth + hitSlop * 2;
2699
- const rightBoundaryInset = rightFixedWidth > 0 && hasVerticalBorders ? 1 : 0;
2700
- const markerX = hiddenLeft ? horizontalStart : horizontalEnd - borderWidth - rightBoundaryInset;
2701
- const markerY = hiddenTop ? verticalStart : renderHeight - borderWidth;
2746
+ const leftBoundary = horizontalStart - borderWidth;
2747
+ const rightBoundary = horizontalEnd - 1;
2748
+ const topBoundary = verticalStart - borderWidth;
2749
+ const bottomBoundary = renderHeight;
2750
+ const markerX = hiddenLeft ? leftBoundary : rightBoundary;
2751
+ const markerY = hiddenTop ? topBoundary : bottomBoundary;
2702
2752
  if (hiddenLeft || hiddenRight) {
2703
- let markerTop = Math.max(rawTop - borderWidth, verticalStart);
2704
- let markerBottom = Math.min(rawBottom + 1, renderHeight);
2705
- const topClip = Math.max(0, verticalStart - (rawTop - borderWidth));
2706
- const bottomClip = Math.max(0, rawBottom + 1 - renderHeight);
2753
+ const markerTop = Math.max(rawTop - borderWidth, topBoundary);
2754
+ const markerBottom = Math.min(rawBottom + borderWidth, renderHeight + borderWidth);
2755
+ const topClip = Math.max(0, verticalStart - rawTop);
2756
+ const bottomClip = Math.max(0, rawBottom - renderHeight);
2707
2757
  if (topClip > 0 || bottomClip > 0) {
2708
2758
  const foldAtTop = topClip > 0;
2709
2759
  const fold = Math.min(cornerLength, foldAtTop ? topClip : bottomClip);
@@ -2712,7 +2762,7 @@ function TableInner({
2712
2762
  const cornerHeight = Math.max(cornerLength, markerBottom - markerTop);
2713
2763
  const cornerOnLeft = hiddenLeft;
2714
2764
  const shapeLeft = cornerOnLeft ? markerX : markerX + borderWidth - cornerWidth;
2715
- const shapeTop = foldAtTop ? verticalStart : renderHeight - cornerHeight;
2765
+ const shapeTop = foldAtTop ? topBoundary : renderHeight + borderWidth - cornerHeight;
2716
2766
  return {
2717
2767
  side: `${foldAtTop ? "top" : "bottom"}-${cornerOnLeft ? "left" : "right"}`,
2718
2768
  orientation: "corner",
@@ -2739,19 +2789,19 @@ function TableInner({
2739
2789
  }
2740
2790
  };
2741
2791
  }
2742
- const horizontalMarkerEnd = horizontalEnd - rightBoundaryInset;
2743
- let markerLeft = Math.max(rawLeft - borderWidth, horizontalStart);
2744
- let markerRight = Math.min(rawRight + 1, horizontalMarkerEnd);
2745
- const leftClip = Math.max(0, horizontalStart - (rawLeft - borderWidth));
2746
- const rightClip = Math.max(0, rawRight + 1 - horizontalMarkerEnd);
2792
+ const horizontalMarkerEnd = horizontalEnd + 1;
2793
+ const markerLeft = Math.max(rawLeft - borderWidth, leftBoundary);
2794
+ const markerRight = Math.min(rawRight + 1, horizontalMarkerEnd);
2795
+ const leftClip = Math.max(0, horizontalStart - rawLeft);
2796
+ const rightClip = Math.max(0, rawRight - horizontalEnd);
2747
2797
  if (leftClip > 0 || rightClip > 0) {
2748
2798
  const foldAtLeft = leftClip > 0;
2749
2799
  const fold = Math.min(cornerLength, foldAtLeft ? leftClip : rightClip);
2750
2800
  if (fold > borderWidth) {
2751
2801
  const cornerWidth = Math.max(cornerLength, markerRight - markerLeft);
2752
2802
  const cornerHeight = fold;
2753
- const shapeLeft = foldAtLeft ? horizontalStart : horizontalMarkerEnd - cornerWidth;
2754
- const shapeTop = hiddenTop ? verticalStart : renderHeight - cornerHeight;
2803
+ const shapeLeft = foldAtLeft ? leftBoundary : horizontalMarkerEnd - cornerWidth;
2804
+ const shapeTop = hiddenTop ? topBoundary : renderHeight + borderWidth - cornerHeight;
2755
2805
  return {
2756
2806
  side: `${hiddenTop ? "top" : "bottom"}-${foldAtLeft ? "left" : "right"}`,
2757
2807
  orientation: "corner",
@@ -2777,7 +2827,7 @@ function TableInner({
2777
2827
  height: targetSize
2778
2828
  }
2779
2829
  };
2780
- }, [bodyTop, columns, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, getScrollableColumnLeft, hasVerticalBorders, leftFixedOffsets, metrics, renderHeight, rightFixedOffsets, rightFixedWidth, rowHeight, rows, selection, selectionRange, viewport.width]);
2830
+ }, [bodyTop, columns, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, getScrollableColumnLeft, leftFixedOffsets, metrics, renderHeight, rightFixedOffsets, rightFixedWidth, rowHeight, rows, selection, selectionRange, viewport.width]);
2781
2831
  const syncSelectionEdgeTarget = (0, import_react8.useCallback)((scrollLeft, scrollTop) => {
2782
2832
  const target = selectionEdgeTargetRef.current;
2783
2833
  if (!target) return;
@@ -3147,11 +3197,20 @@ function TableInner({
3147
3197
  if (rawRight <= horizontalStart || rawLeft >= horizontalEnd || rawBottom <= verticalStart || rawTop >= renderHeight) {
3148
3198
  focus.style.display = "none";
3149
3199
  } else {
3150
- const borderWidth = 2;
3151
- const left = Math.round(Math.max(rawLeft - borderWidth, horizontalStart));
3152
- const right = Math.round(Math.min(rawRight + 1, horizontalEnd, viewport.width));
3153
- const top = Math.round(Math.max(rawTop - borderWidth, verticalStart));
3154
- const bottom = Math.round(Math.min(rawBottom + borderWidth, renderHeight));
3200
+ const bounds = getSelectionOutlineBounds({
3201
+ left: rawLeft,
3202
+ right: rawRight,
3203
+ top: rawTop,
3204
+ bottom: rawBottom,
3205
+ horizontalStart,
3206
+ horizontalEnd,
3207
+ verticalStart,
3208
+ verticalEnd: renderHeight
3209
+ });
3210
+ const left = Math.round(bounds.left);
3211
+ const right = Math.round(bounds.right);
3212
+ const top = Math.round(bounds.top);
3213
+ const bottom = Math.round(bounds.bottom);
3155
3214
  focus.style.display = "block";
3156
3215
  focus.style.left = `${left}px`;
3157
3216
  focus.style.top = `${top}px`;
@@ -3678,6 +3737,7 @@ function TableInner({
3678
3737
  options: column.editor.options,
3679
3738
  searchable: column.editor.searchable,
3680
3739
  allowCustomValue: column.editor.allowCustomValue,
3740
+ strictOptions: column.editor.strictOptions,
3681
3741
  maxDropdownHeight: column.editor.maxDropdownHeight,
3682
3742
  style: editorStyle,
3683
3743
  labels,
@@ -4687,11 +4747,20 @@ function TableInner({
4687
4747
  const horizontalEnd = column.fixed === void 0 ? viewport.width - rightFixedWidth : viewport.width;
4688
4748
  const verticalStart = fixedHeader ? bodyTop : 0;
4689
4749
  if (rawRight <= horizontalStart || rawLeft >= horizontalEnd || rawBottom + dragOffset <= verticalStart || rawTop + dragOffset >= renderHeight) return null;
4690
- const borderWidth = 2;
4691
- const left = Math.round(Math.max(rawLeft - borderWidth, horizontalStart));
4692
- const right = Math.round(Math.min(rawRight + 1, horizontalEnd, viewport.width));
4693
- const top = Math.round(Math.max(rawTop - borderWidth, verticalStart));
4694
- const bottom = Math.round(Math.min(rawBottom + borderWidth, renderHeight));
4750
+ const bounds = getSelectionOutlineBounds({
4751
+ left: rawLeft,
4752
+ right: rawRight,
4753
+ top: rawTop,
4754
+ bottom: rawBottom,
4755
+ horizontalStart,
4756
+ horizontalEnd,
4757
+ verticalStart,
4758
+ verticalEnd: renderHeight
4759
+ });
4760
+ const left = Math.round(bounds.left);
4761
+ const right = Math.round(bounds.right);
4762
+ const top = Math.round(bounds.top);
4763
+ const bottom = Math.round(bounds.bottom);
4695
4764
  return {
4696
4765
  left,
4697
4766
  top,
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';