react-glide-table 1.4.0 → 1.6.0

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/dist/core.cjs CHANGED
@@ -26,18 +26,29 @@ __export(core_exports, {
26
26
  DEFAULT_TREE_ID_FIELD: () => DEFAULT_TREE_ID_FIELD,
27
27
  DEFAULT_TREE_PARENT_ID_FIELD: () => DEFAULT_TREE_PARENT_ID_FIELD,
28
28
  DEFAULT_TREE_QTY_FIELD: () => DEFAULT_TREE_QTY_FIELD,
29
+ INLINE_SEARCH_MAX_RESULTS: () => INLINE_SEARCH_MAX_RESULTS,
29
30
  applyCellEdit: () => applyCellEdit,
30
31
  applyFillData: () => applyFillData,
31
32
  applySelectionUpdater: () => applySelectionUpdater,
32
33
  buildColumnFreezeOffsets: () => buildColumnFreezeOffsets,
33
34
  buildColumnRowSpanMap: () => buildColumnRowSpanMap,
35
+ buildFlatSearchCorpus: () => buildFlatSearchCorpus,
34
36
  buildRowsPastePayload: () => buildRowsPastePayload,
37
+ buildSearchMatchKey: () => buildSearchMatchKey,
38
+ buildSearchMatchKeys: () => buildSearchMatchKeys,
39
+ buildTreeSearchCorpus: () => buildTreeSearchCorpus,
35
40
  canExpandRow: () => canExpandRow,
41
+ cellValueToSearchText: () => cellValueToSearchText,
42
+ collectAncestorKeysToExpand: () => collectAncestorKeysToExpand,
36
43
  collectCopyRowEntries: () => collectCopyRowEntries,
37
44
  collectCopyRows: () => collectCopyRows,
38
45
  collectFillChanges: () => collectFillChanges,
39
46
  collectRowSpanColumns: () => collectRowSpanColumns,
47
+ collectSearchMatchesInRange: () => collectSearchMatchesInRange,
48
+ createSearchRegex: () => createSearchRegex,
49
+ escapeSearchRegex: () => escapeSearchRegex,
40
50
  flattenSubtreeRows: () => flattenSubtreeRows,
51
+ formatSearchResultLabel: () => formatSearchResultLabel,
41
52
  getCellEditDraftValue: () => getCellEditDraftValue,
42
53
  getCellSelectionEdgeStyle: () => getCellSelectionEdgeStyle,
43
54
  getColumnEditType: () => getColumnEditType,
@@ -49,10 +60,15 @@ __export(core_exports, {
49
60
  isCellInSelection: () => isCellInSelection,
50
61
  isColumnEditable: () => isColumnEditable,
51
62
  isEditablePasteTarget: () => isEditablePasteTarget,
63
+ mapSearchResultToVisibleItem: () => mapSearchResultToVisibleItem,
64
+ mapSearchResultsToVisibleKeys: () => mapSearchResultsToVisibleKeys,
52
65
  measureMergedSpanRowHeights: () => measureMergedSpanRowHeights,
66
+ nextSearchIndex: () => nextSearchIndex,
67
+ nextSearchStride: () => nextSearchStride,
53
68
  parseCellEditValue: () => parseCellEditValue,
54
69
  parseClipboardTSV: () => parseClipboardTSV,
55
70
  parseClipboardTSVWithDepths: () => parseClipboardTSVWithDepths,
71
+ previousSearchIndex: () => previousSearchIndex,
56
72
  resolveColumnFreezeSide: () => resolveColumnFreezeSide,
57
73
  resolveDataTableLabels: () => resolveDataTableLabels,
58
74
  resolvePasteColumnIds: () => resolvePasteColumnIds,
@@ -66,6 +82,7 @@ __export(core_exports, {
66
82
  useCellSelection: () => useCellSelection,
67
83
  useConvertTreeData: () => useConvertTreeData,
68
84
  useGlideTable: () => useGlideTable,
85
+ useInlineSearch: () => useInlineSearch,
69
86
  writeSelectionToClipboard: () => writeSelectionToClipboard
70
87
  });
71
88
  module.exports = __toCommonJS(core_exports);
@@ -77,7 +94,12 @@ var DEFAULT_DATA_TABLE_LABELS = {
77
94
  selection: (selectedCount) => selectedCount > 0 ? `\u2713 ${selectedCount} selected` : null,
78
95
  expandRow: "Expand row",
79
96
  collapseRow: "Collapse row",
80
- resizeColumn: "Resize column"
97
+ resizeColumn: "Resize column",
98
+ searchPlaceholder: "Search\u2026",
99
+ searchResultHint: "Type to search",
100
+ searchPrevious: "Previous result",
101
+ searchNext: "Next result",
102
+ searchClose: "Close search"
81
103
  };
82
104
  function resolveDataTableLabels(partial) {
83
105
  return {
@@ -95,7 +117,7 @@ var DEFAULT_TREE_QTY_FIELD = "qty";
95
117
  // src/core/useGlideTable.ts
96
118
  var import_react_table = require("@tanstack/react-table");
97
119
  var import_react_virtual = require("@tanstack/react-virtual");
98
- var import_react4 = require("react");
120
+ var import_react5 = require("react");
99
121
 
100
122
  // src/components/ui/table/constants.ts
101
123
  var DATA_TABLE_ROW_HEIGHT = 44;
@@ -252,6 +274,36 @@ function getCellSelectionBounds(start, end) {
252
274
  endCol: Math.max(start.col, end.col)
253
275
  };
254
276
  }
277
+ function getCellNavigationDelta(key) {
278
+ switch (key) {
279
+ case "ArrowUp":
280
+ case "w":
281
+ case "W":
282
+ return { row: -1, col: 0 };
283
+ case "ArrowDown":
284
+ case "s":
285
+ case "S":
286
+ return { row: 1, col: 0 };
287
+ case "ArrowLeft":
288
+ case "a":
289
+ case "A":
290
+ return { row: 0, col: -1 };
291
+ case "ArrowRight":
292
+ case "d":
293
+ case "D":
294
+ return { row: 0, col: 1 };
295
+ default:
296
+ return null;
297
+ }
298
+ }
299
+ function clampCellPosition(position, rowCount, columnCount) {
300
+ const maxRow = Math.max(rowCount - 1, 0);
301
+ const maxCol = Math.max(columnCount - 1, 0);
302
+ return {
303
+ row: Math.min(Math.max(position.row, 0), maxRow),
304
+ col: Math.min(Math.max(position.col, 0), maxCol)
305
+ };
306
+ }
255
307
  function measureMergedSpanRowHeights(rowIndex, rowSpan, cellElement) {
256
308
  if (rowSpan <= 1) return void 0;
257
309
  const tbody = cellElement?.closest("tbody") ?? (typeof document !== "undefined" ? document.querySelector("tbody.data-table-body") : null);
@@ -775,26 +827,44 @@ function useCellSelection({
775
827
  data,
776
828
  rows,
777
829
  enabled = true,
830
+ columnCount = 0,
778
831
  enableSubtreeCopy = false,
779
832
  enableInsertPaste = true,
780
833
  onDataChange,
781
834
  onBatchChange,
782
- onRowsPaste
835
+ onRowsPaste,
836
+ onCellNavigate
783
837
  }) {
784
838
  const [dragState, setDragState] = (0, import_react2.useState)(INITIAL_DRAG_STATE);
785
839
  const pendingPasteModeRef = (0, import_react2.useRef)(null);
840
+ const dragStateRef = (0, import_react2.useRef)(dragState);
841
+ const onCellNavigateRef = (0, import_react2.useRef)(onCellNavigate);
842
+ dragStateRef.current = dragState;
843
+ onCellNavigateRef.current = onCellNavigate;
786
844
  const cellSelectionBounds = getCellSelectionBounds(dragState.start, dragState.end);
787
845
  const activeSelectionBounds = enabled ? getActiveSelectionBounds(dragState, cellSelectionBounds) : null;
788
846
  const handleCellMouseDown = (0, import_react2.useCallback)(
789
- (rowIndex, colIndex) => {
847
+ (rowIndex, colIndex, options) => {
790
848
  if (!enabled) return;
791
- setDragState({
792
- isSelecting: true,
793
- isFillDragging: false,
794
- start: { row: rowIndex, col: colIndex },
795
- end: { row: rowIndex, col: colIndex },
796
- fillAnchor: null,
797
- fillEnd: null
849
+ setDragState((prev) => {
850
+ if (options?.shiftKey && prev.start) {
851
+ return {
852
+ ...prev,
853
+ isSelecting: true,
854
+ isFillDragging: false,
855
+ end: { row: rowIndex, col: colIndex },
856
+ fillAnchor: null,
857
+ fillEnd: null
858
+ };
859
+ }
860
+ return {
861
+ isSelecting: true,
862
+ isFillDragging: false,
863
+ start: { row: rowIndex, col: colIndex },
864
+ end: { row: rowIndex, col: colIndex },
865
+ fillAnchor: null,
866
+ fillEnd: null
867
+ };
798
868
  });
799
869
  },
800
870
  [enabled]
@@ -836,6 +906,53 @@ function useCellSelection({
836
906
  setDragState(INITIAL_DRAG_STATE);
837
907
  }
838
908
  }, [enabled]);
909
+ (0, import_react2.useEffect)(() => {
910
+ if (!enabled) return;
911
+ const handleKeyDown = (e) => {
912
+ if (e.ctrlKey || e.metaKey || e.altKey) return;
913
+ if (isEditablePasteTarget(e.target) || isEditablePasteTarget(document.activeElement)) {
914
+ return;
915
+ }
916
+ const delta = getCellNavigationDelta(e.key);
917
+ if (!delta) return;
918
+ const prev = dragStateRef.current;
919
+ if (!prev.start || !prev.end) return;
920
+ if (prev.isSelecting || prev.isFillDragging) return;
921
+ const rowCount = rows.length;
922
+ const resolvedColumnCount = columnCount > 0 ? columnCount : rows[0]?.getVisibleCells().length ?? 0;
923
+ if (rowCount <= 0 || resolvedColumnCount <= 0) return;
924
+ const nextEnd = clampCellPosition(
925
+ {
926
+ row: prev.end.row + delta.row,
927
+ col: prev.end.col + delta.col
928
+ },
929
+ rowCount,
930
+ resolvedColumnCount
931
+ );
932
+ if (nextEnd.row === prev.end.row && nextEnd.col === prev.end.col) return;
933
+ e.preventDefault();
934
+ const nextState = e.shiftKey ? {
935
+ ...prev,
936
+ isSelecting: false,
937
+ isFillDragging: false,
938
+ end: nextEnd,
939
+ fillAnchor: null,
940
+ fillEnd: null
941
+ } : {
942
+ isSelecting: false,
943
+ isFillDragging: false,
944
+ start: nextEnd,
945
+ end: nextEnd,
946
+ fillAnchor: null,
947
+ fillEnd: null
948
+ };
949
+ dragStateRef.current = nextState;
950
+ setDragState(nextState);
951
+ onCellNavigateRef.current?.(nextEnd);
952
+ };
953
+ window.addEventListener("keydown", handleKeyDown);
954
+ return () => window.removeEventListener("keydown", handleKeyDown);
955
+ }, [columnCount, enabled, rows]);
839
956
  const copySelection = (0, import_react2.useCallback)(
840
957
  async (options) => {
841
958
  if (!enabled || !activeSelectionBounds) return false;
@@ -1076,8 +1193,462 @@ function getColumnFreezeStyle(offset, options) {
1076
1193
  };
1077
1194
  }
1078
1195
 
1079
- // src/components/ui/table/features/row-expand/row-expand.ts
1196
+ // src/components/ui/table/features/inline-search/inlineSearch.ts
1197
+ var INLINE_SEARCH_MAX_RESULTS = 1e3;
1198
+ var INLINE_SEARCH_TARGET_TICK_MS = 10;
1199
+ var INLINE_SEARCH_INITIAL_STRIDE = 10;
1200
+ function escapeSearchRegex(value) {
1201
+ return value.replace(/([$()*+.?[\\\]^{|}-])/g, "\\$1");
1202
+ }
1203
+ function createSearchRegex(query) {
1204
+ const trimmed = query.trim();
1205
+ if (!trimmed) return null;
1206
+ return new RegExp(escapeSearchRegex(trimmed), "i");
1207
+ }
1208
+ function cellValueToSearchText(value) {
1209
+ if (value == null) return void 0;
1210
+ if (typeof value === "string") return value;
1211
+ if (typeof value === "number" || typeof value === "boolean") {
1212
+ return String(value);
1213
+ }
1214
+ if (Array.isArray(value)) {
1215
+ return value.map((item) => cellValueToSearchText(item) ?? "").join(" ");
1216
+ }
1217
+ if (typeof value === "object") {
1218
+ try {
1219
+ return JSON.stringify(value);
1220
+ } catch {
1221
+ return String(value);
1222
+ }
1223
+ }
1224
+ return String(value);
1225
+ }
1226
+ function formatSearchResultLabel(status) {
1227
+ const countLabel = status.results >= INLINE_SEARCH_MAX_RESULTS ? `over ${INLINE_SEARCH_MAX_RESULTS}` : `${status.results} result${status.results !== 1 ? "s" : ""}`;
1228
+ if (status.selectedIndex >= 0 && status.results > 0) {
1229
+ return `${status.selectedIndex + 1} of ${countLabel}`;
1230
+ }
1231
+ return countLabel;
1232
+ }
1233
+ function nextSearchIndex(selectedIndex, results) {
1234
+ if (results <= 0) return -1;
1235
+ if (selectedIndex < 0) return 0;
1236
+ return (selectedIndex + 1) % results;
1237
+ }
1238
+ function previousSearchIndex(selectedIndex, results) {
1239
+ if (results <= 0) return -1;
1240
+ if (selectedIndex < 0) return results - 1;
1241
+ let next = (selectedIndex - 1) % results;
1242
+ if (next < 0) next += results;
1243
+ return next;
1244
+ }
1245
+ function buildSearchMatchKey(colIndex, rowIndex) {
1246
+ return `${colIndex}:${rowIndex}`;
1247
+ }
1248
+ function buildSearchMatchKeys(results) {
1249
+ const keys = /* @__PURE__ */ new Set();
1250
+ for (const [colIndex, rowIndex] of results) {
1251
+ keys.add(buildSearchMatchKey(colIndex, rowIndex));
1252
+ }
1253
+ return keys;
1254
+ }
1255
+ function collectSearchMatchesInRange(options) {
1256
+ const {
1257
+ query,
1258
+ startRow,
1259
+ rowCount,
1260
+ columnCount,
1261
+ getCellValue,
1262
+ maxResults = INLINE_SEARCH_MAX_RESULTS
1263
+ } = options;
1264
+ const regex = createSearchRegex(query);
1265
+ if (!regex || rowCount <= 0 || columnCount <= 0) return [];
1266
+ const matches = [];
1267
+ for (let rowOffset = 0; rowOffset < rowCount; rowOffset += 1) {
1268
+ const rowIndex = startRow + rowOffset;
1269
+ for (let colIndex = 0; colIndex < columnCount; colIndex += 1) {
1270
+ const text = cellValueToSearchText(getCellValue(rowIndex, colIndex));
1271
+ if (text !== void 0 && regex.test(text)) {
1272
+ matches.push([colIndex, rowIndex]);
1273
+ if (matches.length >= maxResults) {
1274
+ return matches;
1275
+ }
1276
+ }
1277
+ }
1278
+ }
1279
+ return matches;
1280
+ }
1281
+ function nextSearchStride(currentStride, elapsedMs, targetMs = INLINE_SEARCH_TARGET_TICK_MS) {
1282
+ const rounded = Math.max(elapsedMs, 1);
1283
+ const scalar = targetMs / rounded;
1284
+ return Math.max(1, Math.ceil(currentStride * scalar));
1285
+ }
1286
+ function buildFlatSearchCorpus(rows, getRowId) {
1287
+ return rows.map((data, index) => ({
1288
+ id: getRowId(data, index),
1289
+ data,
1290
+ ancestorToggleKeys: []
1291
+ }));
1292
+ }
1293
+ function buildTreeSearchCorpus(visibleRows, options) {
1294
+ const { toggleField, getRowId } = options;
1295
+ const corpus = [];
1296
+ const seen = /* @__PURE__ */ new Set();
1297
+ const walk = (node, ancestorToggleKeys) => {
1298
+ const id = getRowId(node, corpus.length);
1299
+ if (seen.has(id)) return;
1300
+ seen.add(id);
1301
+ corpus.push({
1302
+ id,
1303
+ data: node,
1304
+ ancestorToggleKeys
1305
+ });
1306
+ const children = node.children;
1307
+ if (!Array.isArray(children) || children.length === 0) return;
1308
+ const toggleValue = node[toggleField];
1309
+ const childAncestors = typeof toggleValue === "string" && toggleValue.length > 0 ? [...ancestorToggleKeys, toggleValue] : ancestorToggleKeys;
1310
+ for (const child of children) {
1311
+ if (child && typeof child === "object") {
1312
+ walk(child, childAncestors);
1313
+ }
1314
+ }
1315
+ };
1316
+ for (const row of visibleRows) {
1317
+ const level = row.level;
1318
+ if (level === 0 || level === void 0) {
1319
+ walk(row, []);
1320
+ }
1321
+ }
1322
+ for (const row of visibleRows) {
1323
+ const id = getRowId(row, corpus.length);
1324
+ if (seen.has(id)) continue;
1325
+ walk(row, []);
1326
+ }
1327
+ return corpus;
1328
+ }
1329
+ function mapSearchResultsToVisibleKeys(results, corpus, visibleRowIndexById) {
1330
+ const keys = /* @__PURE__ */ new Set();
1331
+ for (const [colIndex, corpusRowIndex] of results) {
1332
+ const corpusRow = corpus[corpusRowIndex];
1333
+ if (!corpusRow) continue;
1334
+ const visibleRowIndex = visibleRowIndexById.get(corpusRow.id);
1335
+ if (visibleRowIndex === void 0) continue;
1336
+ keys.add(buildSearchMatchKey(colIndex, visibleRowIndex));
1337
+ }
1338
+ return keys;
1339
+ }
1340
+ function mapSearchResultToVisibleItem(item, corpus, visibleRowIndexById) {
1341
+ const [colIndex, corpusRowIndex] = item;
1342
+ const corpusRow = corpus[corpusRowIndex];
1343
+ if (!corpusRow) return null;
1344
+ const visibleRowIndex = visibleRowIndexById.get(corpusRow.id);
1345
+ if (visibleRowIndex === void 0) return null;
1346
+ return [colIndex, visibleRowIndex];
1347
+ }
1348
+ function collectAncestorKeysToExpand(corpusRow, expandedRows) {
1349
+ return corpusRow.ancestorToggleKeys.filter((key) => !expandedRows.has(key));
1350
+ }
1351
+
1352
+ // src/components/ui/table/features/inline-search/useInlineSearch.ts
1080
1353
  var import_react3 = require("react");
1354
+ var EMPTY_MATCH_KEYS = /* @__PURE__ */ new Set();
1355
+ function useInlineSearch({
1356
+ enabled = false,
1357
+ rowCount,
1358
+ columnCount,
1359
+ getCellValue,
1360
+ initialStartRow = 0,
1361
+ showSearch: controlledShowSearch,
1362
+ searchValue: controlledSearchValue,
1363
+ searchResults: controlledSearchResults,
1364
+ onSearchValueChange,
1365
+ onSearchClose,
1366
+ onSearchResultsChanged,
1367
+ onNavigateToResult,
1368
+ rootRef
1369
+ }) {
1370
+ const searchInputId = (0, import_react3.useId)();
1371
+ const searchInputRef = (0, import_react3.useRef)(null);
1372
+ const [internalShowSearch, setInternalShowSearch] = (0, import_react3.useState)(false);
1373
+ const [internalSearchValue, setInternalSearchValue] = (0, import_react3.useState)("");
1374
+ const [internalResults, setInternalResults] = (0, import_react3.useState)(
1375
+ []
1376
+ );
1377
+ const [searchStatus, setSearchStatus] = (0, import_react3.useState)();
1378
+ const searchStatusRef = (0, import_react3.useRef)(searchStatus);
1379
+ searchStatusRef.current = searchStatus;
1380
+ const abortControllerRef = (0, import_react3.useRef)(null);
1381
+ const searchHandleRef = (0, import_react3.useRef)(void 0);
1382
+ const initialStartRowRef = (0, import_react3.useRef)(initialStartRow);
1383
+ initialStartRowRef.current = initialStartRow;
1384
+ const getCellValueRef = (0, import_react3.useRef)(getCellValue);
1385
+ getCellValueRef.current = getCellValue;
1386
+ const showSearch = controlledShowSearch ?? internalShowSearch;
1387
+ const searchValue = controlledSearchValue ?? internalSearchValue;
1388
+ const searchResults = controlledSearchResults ?? internalResults;
1389
+ const setSearchValue = (0, import_react3.useCallback)(
1390
+ (value) => {
1391
+ setInternalSearchValue(value);
1392
+ onSearchValueChange?.(value);
1393
+ },
1394
+ [onSearchValueChange]
1395
+ );
1396
+ const cancelSearch = (0, import_react3.useCallback)(() => {
1397
+ if (searchHandleRef.current !== void 0) {
1398
+ window.cancelAnimationFrame(searchHandleRef.current);
1399
+ searchHandleRef.current = void 0;
1400
+ }
1401
+ abortControllerRef.current?.abort();
1402
+ }, []);
1403
+ const emitResultsChanged = (0, import_react3.useCallback)(
1404
+ (results, navIndex) => {
1405
+ onSearchResultsChanged?.(results, navIndex);
1406
+ },
1407
+ [onSearchResultsChanged]
1408
+ );
1409
+ const navigateToIndex = (0, import_react3.useCallback)(
1410
+ (results, navIndex) => {
1411
+ if (onSearchResultsChanged) return;
1412
+ if (navIndex < 0 || navIndex >= results.length) return;
1413
+ const item = results[navIndex];
1414
+ if (!item) return;
1415
+ onNavigateToResult?.(item);
1416
+ },
1417
+ [onNavigateToResult, onSearchResultsChanged]
1418
+ );
1419
+ const beginSearch = (0, import_react3.useCallback)(
1420
+ (query) => {
1421
+ if (controlledSearchResults !== void 0) return;
1422
+ const totalRows = rowCount;
1423
+ if (totalRows === 0 || columnCount === 0) {
1424
+ setSearchStatus(void 0);
1425
+ setInternalResults([]);
1426
+ emitResultsChanged([], -1);
1427
+ return;
1428
+ }
1429
+ let startY = Math.min(
1430
+ Math.max(0, initialStartRowRef.current),
1431
+ totalRows - 1
1432
+ );
1433
+ let searchStride = Math.min(INLINE_SEARCH_INITIAL_STRIDE, totalRows);
1434
+ let rowsSearched = 0;
1435
+ const runningResult = [];
1436
+ setSearchStatus(void 0);
1437
+ setInternalResults([]);
1438
+ const tick = () => {
1439
+ if (abortControllerRef.current?.signal.aborted) return;
1440
+ const tStart = performance.now();
1441
+ const rowsLeft = totalRows - rowsSearched;
1442
+ const height = Math.min(searchStride, rowsLeft, totalRows - startY);
1443
+ if (height <= 0) {
1444
+ return;
1445
+ }
1446
+ const chunk = collectSearchMatchesInRange({
1447
+ query,
1448
+ startRow: startY,
1449
+ rowCount: height,
1450
+ columnCount,
1451
+ getCellValue: (rowIndex, colIndex) => getCellValueRef.current(rowIndex, colIndex),
1452
+ maxResults: INLINE_SEARCH_MAX_RESULTS - runningResult.length
1453
+ });
1454
+ if (chunk.length > 0) {
1455
+ runningResult.push(...chunk);
1456
+ setInternalResults([...runningResult]);
1457
+ }
1458
+ rowsSearched += height;
1459
+ const selectedIndex = searchStatusRef.current?.selectedIndex ?? -1;
1460
+ setSearchStatus({
1461
+ results: runningResult.length,
1462
+ rowsSearched,
1463
+ selectedIndex
1464
+ });
1465
+ emitResultsChanged(runningResult, selectedIndex);
1466
+ if (startY + height >= totalRows) {
1467
+ startY = 0;
1468
+ } else {
1469
+ startY += height;
1470
+ }
1471
+ searchStride = nextSearchStride(
1472
+ searchStride,
1473
+ performance.now() - tStart
1474
+ );
1475
+ if (rowsSearched < totalRows && runningResult.length < INLINE_SEARCH_MAX_RESULTS) {
1476
+ searchHandleRef.current = window.requestAnimationFrame(tick);
1477
+ }
1478
+ };
1479
+ cancelSearch();
1480
+ abortControllerRef.current = new AbortController();
1481
+ searchHandleRef.current = window.requestAnimationFrame(tick);
1482
+ },
1483
+ [
1484
+ cancelSearch,
1485
+ columnCount,
1486
+ controlledSearchResults,
1487
+ emitResultsChanged,
1488
+ rowCount
1489
+ ]
1490
+ );
1491
+ const openSearch = (0, import_react3.useCallback)(() => {
1492
+ if (controlledShowSearch === void 0) {
1493
+ setInternalShowSearch(true);
1494
+ }
1495
+ }, [controlledShowSearch]);
1496
+ const closeSearch = (0, import_react3.useCallback)(() => {
1497
+ if (controlledShowSearch === void 0) {
1498
+ setInternalShowSearch(false);
1499
+ }
1500
+ onSearchClose?.();
1501
+ setSearchStatus(void 0);
1502
+ setInternalResults([]);
1503
+ emitResultsChanged([], -1);
1504
+ cancelSearch();
1505
+ }, [
1506
+ cancelSearch,
1507
+ controlledShowSearch,
1508
+ emitResultsChanged,
1509
+ onSearchClose
1510
+ ]);
1511
+ const goToNext = (0, import_react3.useCallback)(() => {
1512
+ if (!searchStatus || searchStatus.results === 0) return;
1513
+ const newIndex = nextSearchIndex(
1514
+ searchStatus.selectedIndex,
1515
+ searchStatus.results
1516
+ );
1517
+ setSearchStatus({ ...searchStatus, selectedIndex: newIndex });
1518
+ emitResultsChanged(searchResults, newIndex);
1519
+ navigateToIndex(searchResults, newIndex);
1520
+ }, [emitResultsChanged, navigateToIndex, searchResults, searchStatus]);
1521
+ const goToPrevious = (0, import_react3.useCallback)(() => {
1522
+ if (!searchStatus || searchStatus.results === 0) return;
1523
+ const newIndex = previousSearchIndex(
1524
+ searchStatus.selectedIndex,
1525
+ searchStatus.results
1526
+ );
1527
+ setSearchStatus({ ...searchStatus, selectedIndex: newIndex });
1528
+ emitResultsChanged(searchResults, newIndex);
1529
+ navigateToIndex(searchResults, newIndex);
1530
+ }, [emitResultsChanged, navigateToIndex, searchResults, searchStatus]);
1531
+ (0, import_react3.useEffect)(() => {
1532
+ if (controlledSearchResults === void 0) return;
1533
+ if (controlledSearchResults.length > 0) {
1534
+ setSearchStatus((current) => ({
1535
+ rowsSearched: rowCount,
1536
+ results: controlledSearchResults.length,
1537
+ selectedIndex: current?.selectedIndex ?? -1
1538
+ }));
1539
+ } else {
1540
+ setSearchStatus(void 0);
1541
+ }
1542
+ }, [controlledSearchResults, rowCount]);
1543
+ (0, import_react3.useEffect)(() => {
1544
+ if (!enabled) return;
1545
+ setSearchStatus(void 0);
1546
+ setInternalResults([]);
1547
+ emitResultsChanged([], -1);
1548
+ if (showSearch) {
1549
+ queueMicrotask(() => {
1550
+ searchInputRef.current?.focus({ preventScroll: true });
1551
+ });
1552
+ } else {
1553
+ cancelSearch();
1554
+ }
1555
+ }, [enabled, showSearch]);
1556
+ (0, import_react3.useEffect)(() => {
1557
+ if (!enabled || !showSearch) return;
1558
+ if (controlledSearchResults !== void 0) return;
1559
+ if (searchValue.trim() === "") {
1560
+ setSearchStatus(void 0);
1561
+ setInternalResults([]);
1562
+ cancelSearch();
1563
+ emitResultsChanged([], -1);
1564
+ return;
1565
+ }
1566
+ beginSearch(searchValue);
1567
+ }, [
1568
+ beginSearch,
1569
+ cancelSearch,
1570
+ controlledSearchResults,
1571
+ emitResultsChanged,
1572
+ enabled,
1573
+ searchValue,
1574
+ showSearch
1575
+ ]);
1576
+ (0, import_react3.useEffect)(() => {
1577
+ if (!enabled) return;
1578
+ const handleKeyDown = (event) => {
1579
+ if (!(event.ctrlKey || event.metaKey)) return;
1580
+ if (event.key.toLowerCase() !== "f") return;
1581
+ const root = rootRef?.current;
1582
+ if (root) {
1583
+ const active = document.activeElement;
1584
+ const focusInside = active === root || active instanceof Node && root.contains(active);
1585
+ if (!focusInside && active !== document.body) {
1586
+ return;
1587
+ }
1588
+ }
1589
+ event.preventDefault();
1590
+ event.stopPropagation();
1591
+ if (showSearch) {
1592
+ searchInputRef.current?.focus({ preventScroll: true });
1593
+ searchInputRef.current?.select();
1594
+ return;
1595
+ }
1596
+ if (controlledShowSearch === void 0) {
1597
+ setInternalShowSearch(true);
1598
+ }
1599
+ };
1600
+ window.addEventListener("keydown", handleKeyDown, true);
1601
+ return () => window.removeEventListener("keydown", handleKeyDown, true);
1602
+ }, [controlledShowSearch, enabled, rootRef, showSearch]);
1603
+ (0, import_react3.useEffect)(() => () => cancelSearch(), [cancelSearch]);
1604
+ const searchMatchKeys = (0, import_react3.useMemo)(
1605
+ () => buildSearchMatchKeys(searchResults),
1606
+ [searchResults]
1607
+ );
1608
+ const activeMatch = (0, import_react3.useMemo)(() => {
1609
+ if (!searchStatus || searchStatus.selectedIndex < 0) return null;
1610
+ return searchResults[searchStatus.selectedIndex] ?? null;
1611
+ }, [searchResults, searchStatus]);
1612
+ if (!enabled) {
1613
+ return {
1614
+ enabled: false,
1615
+ showSearch: false,
1616
+ searchValue: "",
1617
+ searchResults: [],
1618
+ searchStatus: void 0,
1619
+ searchMatchKeys: EMPTY_MATCH_KEYS,
1620
+ activeMatch: null,
1621
+ searchInputRef,
1622
+ searchInputId,
1623
+ canClose: false,
1624
+ openSearch,
1625
+ closeSearch,
1626
+ setSearchValue,
1627
+ goToNext,
1628
+ goToPrevious
1629
+ };
1630
+ }
1631
+ return {
1632
+ enabled: true,
1633
+ showSearch,
1634
+ searchValue,
1635
+ searchResults,
1636
+ searchStatus,
1637
+ searchMatchKeys,
1638
+ activeMatch,
1639
+ searchInputRef,
1640
+ searchInputId,
1641
+ canClose: controlledShowSearch === void 0 || onSearchClose !== void 0,
1642
+ openSearch,
1643
+ closeSearch,
1644
+ setSearchValue,
1645
+ goToNext,
1646
+ goToPrevious
1647
+ };
1648
+ }
1649
+
1650
+ // src/components/ui/table/features/row-expand/row-expand.ts
1651
+ var import_react4 = require("react");
1081
1652
  function getFieldValue(row, key) {
1082
1653
  return row[key];
1083
1654
  }
@@ -1107,12 +1678,12 @@ var useConvertTreeData = ({
1107
1678
  expandedRows,
1108
1679
  onExpandedRowsChange
1109
1680
  }) => {
1110
- const onExpandedRowsChangeRef = (0, import_react3.useRef)(onExpandedRowsChange);
1111
- const hasInitializedRef = (0, import_react3.useRef)(false);
1112
- (0, import_react3.useEffect)(() => {
1681
+ const onExpandedRowsChangeRef = (0, import_react4.useRef)(onExpandedRowsChange);
1682
+ const hasInitializedRef = (0, import_react4.useRef)(false);
1683
+ (0, import_react4.useEffect)(() => {
1113
1684
  onExpandedRowsChangeRef.current = onExpandedRowsChange;
1114
1685
  }, [onExpandedRowsChange]);
1115
- (0, import_react3.useEffect)(() => {
1686
+ (0, import_react4.useEffect)(() => {
1116
1687
  if (!data || data.length === 0) {
1117
1688
  hasInitializedRef.current = false;
1118
1689
  return;
@@ -1122,7 +1693,7 @@ var useConvertTreeData = ({
1122
1693
  onExpandedRowsChangeRef.current?.(new Set(ids));
1123
1694
  hasInitializedRef.current = true;
1124
1695
  }, [enabled, data, toggleField]);
1125
- const processedData = (0, import_react3.useMemo)(() => {
1696
+ const processedData = (0, import_react4.useMemo)(() => {
1126
1697
  if (!enabled || !data || data.length === 0) return [];
1127
1698
  const flattenedData = [];
1128
1699
  const flattenItems = (items) => {
@@ -1186,7 +1757,7 @@ var useConvertTreeData = ({
1186
1757
  });
1187
1758
  return rootItems;
1188
1759
  }, [enabled, data, toggleField, childField, flattenField]);
1189
- const flattenTree = (0, import_react3.useMemo)(() => {
1760
+ const flattenTree = (0, import_react4.useMemo)(() => {
1190
1761
  if (!enabled) return [];
1191
1762
  const flatten = (nodes, result = [], level = 0) => {
1192
1763
  nodes.forEach((node, index) => {
@@ -1236,7 +1807,7 @@ var useConvertTreeData = ({
1236
1807
  preventExpand,
1237
1808
  expandedRows
1238
1809
  ]);
1239
- const sortedData = (0, import_react3.useMemo)(() => {
1810
+ const sortedData = (0, import_react4.useMemo)(() => {
1240
1811
  if (!enabled) {
1241
1812
  return data ?? [];
1242
1813
  }
@@ -1342,6 +1913,7 @@ function collectRowSpanColumns(columns) {
1342
1913
 
1343
1914
  // src/core/useGlideTable.ts
1344
1915
  var EMPTY_COLUMN_FREEZE_OFFSETS = /* @__PURE__ */ new Map();
1916
+ var EMPTY_SEARCH_MATCH_KEYS = /* @__PURE__ */ new Set();
1345
1917
  function useGlideTable(options) {
1346
1918
  const {
1347
1919
  data,
@@ -1382,9 +1954,16 @@ function useGlideTable(options) {
1382
1954
  columnSizing: controlledColumnSizing,
1383
1955
  onColumnSizingChange,
1384
1956
  columnResizeMode = "onChange",
1385
- enableColumnFreeze = false
1957
+ enableColumnFreeze = false,
1958
+ enableInlineSearch = false,
1959
+ showSearch,
1960
+ searchValue,
1961
+ onSearchValueChange,
1962
+ onSearchClose,
1963
+ searchResults,
1964
+ onSearchResultsChanged
1386
1965
  } = options;
1387
- const labels = (0, import_react4.useMemo)(() => {
1966
+ const labels = (0, import_react5.useMemo)(() => {
1388
1967
  const resolved = resolveDataTableLabels(labelsProp);
1389
1968
  return {
1390
1969
  ...resolved,
@@ -1395,15 +1974,16 @@ function useGlideTable(options) {
1395
1974
  }, [labelsProp, emptyText, loadingText, selectionLabel]);
1396
1975
  const enableExpand = Boolean(toggleField);
1397
1976
  const resolvedEnableSubtreeCopy = enableSubtreeCopy ?? enableExpand;
1398
- const [internalRowSelection, setInternalRowSelection] = (0, import_react4.useState)({});
1399
- const [internalColumnSizing, setInternalColumnSizing] = (0, import_react4.useState)({});
1400
- const [internalExpandedRows, setInternalExpandedRows] = (0, import_react4.useState)(
1977
+ const [internalRowSelection, setInternalRowSelection] = (0, import_react5.useState)({});
1978
+ const [internalColumnSizing, setInternalColumnSizing] = (0, import_react5.useState)({});
1979
+ const [internalExpandedRows, setInternalExpandedRows] = (0, import_react5.useState)(
1401
1980
  () => /* @__PURE__ */ new Set()
1402
1981
  );
1403
- const [hoveredRowIndex, setHoveredRowIndex] = (0, import_react4.useState)(null);
1404
- const scrollRef = (0, import_react4.useRef)(null);
1982
+ const [hoveredRowIndex, setHoveredRowIndex] = (0, import_react5.useState)(null);
1983
+ const scrollRef = (0, import_react5.useRef)(null);
1984
+ const rootRef = (0, import_react5.useRef)(null);
1405
1985
  const shouldVirtualize = enableVirtualization && !enableRowSpan;
1406
- (0, import_react4.useEffect)(() => {
1986
+ (0, import_react5.useEffect)(() => {
1407
1987
  if (enableVirtualization && enableRowSpan) {
1408
1988
  console.warn(
1409
1989
  "[useGlideTable] enableRowSpan is on; virtualization is disabled to preserve cell merges."
@@ -1417,7 +1997,7 @@ function useGlideTable(options) {
1417
1997
  );
1418
1998
  const columnSizing = controlledColumnSizing ?? internalColumnSizing;
1419
1999
  const expandedRows = controlledExpandedRows ?? internalExpandedRows;
1420
- const handleExpandedRowsChange = (0, import_react4.useCallback)(
2000
+ const handleExpandedRowsChange = (0, import_react5.useCallback)(
1421
2001
  (next) => {
1422
2002
  if (onExpandedRowsChange) {
1423
2003
  onExpandedRowsChange(next);
@@ -1478,13 +2058,13 @@ function useGlideTable(options) {
1478
2058
  getCoreRowModel: (0, import_react_table.getCoreRowModel)(),
1479
2059
  getRowId: getRowId ? (originalRow, index) => getRowId(originalRow, index) : (_originalRow, index) => String(index)
1480
2060
  });
1481
- const rowSpanColumnKeys = (0, import_react4.useMemo)(() => {
2061
+ const rowSpanColumnKeys = (0, import_react5.useMemo)(() => {
1482
2062
  if (!enableRowSpan) return [];
1483
2063
  return collectRowSpanColumns(columns);
1484
2064
  }, [enableRowSpan, columns]);
1485
2065
  const primaryRowSpanKey = rowSpanColumnKeys[0]?.rowSpanKey;
1486
2066
  const primaryRowSpanColumnId = rowSpanColumnKeys[0]?.columnId;
1487
- const columnRowSpanMap = (0, import_react4.useMemo)(
2067
+ const columnRowSpanMap = (0, import_react5.useMemo)(
1488
2068
  () => buildColumnRowSpanMap(tableData, rowSpanColumnKeys),
1489
2069
  [tableData, rowSpanColumnKeys]
1490
2070
  );
@@ -1493,7 +2073,7 @@ function useGlideTable(options) {
1493
2073
  const rows = table.getRowModel().rows;
1494
2074
  const columnCount = table.getAllLeafColumns().length || 1;
1495
2075
  const visibleLeafColumns = table.getVisibleLeafColumns();
1496
- const columnFreezeOffsets = (0, import_react4.useMemo)(() => {
2076
+ const columnFreezeOffsets = (0, import_react5.useMemo)(() => {
1497
2077
  if (!enableColumnFreeze) return EMPTY_COLUMN_FREEZE_OFFSETS;
1498
2078
  return buildColumnFreezeOffsets(
1499
2079
  visibleLeafColumns.map((column) => ({
@@ -1513,13 +2093,46 @@ function useGlideTable(options) {
1513
2093
  const totalSize = rowVirtualizer.getTotalSize();
1514
2094
  const paddingTop = virtualRows.length > 0 ? virtualRows[0]?.start ?? 0 : 0;
1515
2095
  const paddingBottom = virtualRows.length > 0 ? totalSize - (virtualRows[virtualRows.length - 1]?.end ?? 0) : 0;
1516
- const selectedRowIndices = (0, import_react4.useMemo)(() => {
2096
+ const selectedRowIndices = (0, import_react5.useMemo)(() => {
1517
2097
  const indices = /* @__PURE__ */ new Set();
1518
2098
  for (const selectedRow of selectedRows) {
1519
2099
  indices.add(selectedRow.index);
1520
2100
  }
1521
2101
  return indices;
1522
2102
  }, [selectedRows]);
2103
+ const scrollCellIntoView = (0, import_react5.useCallback)(
2104
+ (rowIndex, colIndex, options2) => {
2105
+ const align = options2?.align ?? "nearest";
2106
+ const blockAlign = align === "center" ? "center" : "nearest";
2107
+ if (shouldVirtualize) {
2108
+ rowVirtualizer.scrollToIndex(rowIndex, {
2109
+ align: align === "nearest" ? "auto" : align
2110
+ });
2111
+ }
2112
+ const scrollElement = scrollRef.current;
2113
+ if (!scrollElement) return;
2114
+ const scrollToMatchedCell = () => {
2115
+ const cell = scrollElement.querySelector(
2116
+ `[data-row-index="${rowIndex}"][data-col-index="${colIndex}"]`
2117
+ );
2118
+ if (cell instanceof HTMLElement) {
2119
+ cell.scrollIntoView({ block: blockAlign, inline: "nearest" });
2120
+ }
2121
+ };
2122
+ if (shouldVirtualize) {
2123
+ requestAnimationFrame(scrollToMatchedCell);
2124
+ return;
2125
+ }
2126
+ scrollToMatchedCell();
2127
+ },
2128
+ [rowVirtualizer, shouldVirtualize]
2129
+ );
2130
+ const handleCellNavigate = (0, import_react5.useCallback)(
2131
+ (position) => {
2132
+ scrollCellIntoView(position.row, position.col, { align: "nearest" });
2133
+ },
2134
+ [scrollCellIntoView]
2135
+ );
1523
2136
  const {
1524
2137
  dragState,
1525
2138
  activeSelectionBounds,
@@ -1531,11 +2144,13 @@ function useGlideTable(options) {
1531
2144
  data: tableData,
1532
2145
  rows,
1533
2146
  enabled: enableCellSelection,
2147
+ columnCount: visibleLeafColumns.length,
1534
2148
  enableSubtreeCopy: resolvedEnableSubtreeCopy,
1535
2149
  enableInsertPaste: enableInsertPaste ?? true,
1536
2150
  onDataChange,
1537
2151
  onBatchChange,
1538
- onRowsPaste
2152
+ onRowsPaste,
2153
+ onCellNavigate: handleCellNavigate
1539
2154
  });
1540
2155
  const {
1541
2156
  editingCell,
@@ -1545,23 +2160,193 @@ function useGlideTable(options) {
1545
2160
  commitEdit,
1546
2161
  cancelEdit
1547
2162
  } = useCellEdit({ data: tableData, rows, onDataChange, onCellChange });
1548
- const handleCellMouseDownWithCommit = (0, import_react4.useCallback)(
1549
- (rowIndex, colIndex) => {
2163
+ const handleCellMouseDownWithCommit = (0, import_react5.useCallback)(
2164
+ (rowIndex, colIndex, options2) => {
1550
2165
  const isSameEditingCell = editingCell?.rowIndex === rowIndex && editingCell?.colIndex === colIndex;
1551
2166
  if (editingCell && !isSameEditingCell && !commitEdit()) {
1552
2167
  return;
1553
2168
  }
1554
- handleCellMouseDown(rowIndex, colIndex);
2169
+ handleCellMouseDown(rowIndex, colIndex, options2);
1555
2170
  },
1556
2171
  [commitEdit, editingCell, handleCellMouseDown]
1557
2172
  );
1558
- const clearHover = (0, import_react4.useCallback)(() => {
2173
+ const navigateToSearchResult = (0, import_react5.useCallback)(
2174
+ (item) => {
2175
+ const [colIndex, rowIndex] = item;
2176
+ handleCellMouseDownWithCommit(rowIndex, colIndex);
2177
+ scrollCellIntoView(rowIndex, colIndex, { align: "center" });
2178
+ },
2179
+ [handleCellMouseDownWithCommit, scrollCellIntoView]
2180
+ );
2181
+ const resolveSearchRowId = (0, import_react5.useCallback)(
2182
+ (row, index) => {
2183
+ if (getRowId) return getRowId(row, index);
2184
+ if (enableExpand) {
2185
+ const record = row;
2186
+ const idValue = record.id;
2187
+ if (idValue != null && String(idValue).length > 0) {
2188
+ return String(idValue);
2189
+ }
2190
+ const uniqueId = record.uniqueId;
2191
+ if (uniqueId != null && String(uniqueId).length > 0) {
2192
+ return String(uniqueId);
2193
+ }
2194
+ if (toggleField) {
2195
+ const toggleValue = record[toggleField];
2196
+ if (toggleValue != null && String(toggleValue).length > 0) {
2197
+ return String(toggleValue);
2198
+ }
2199
+ }
2200
+ }
2201
+ return String(index);
2202
+ },
2203
+ [enableExpand, getRowId, toggleField]
2204
+ );
2205
+ const searchCorpus = (0, import_react5.useMemo)(() => {
2206
+ if (!enableInlineSearch) return [];
2207
+ if (enableExpand && toggleField) {
2208
+ return buildTreeSearchCorpus(tableData, {
2209
+ toggleField,
2210
+ getRowId: resolveSearchRowId
2211
+ });
2212
+ }
2213
+ return buildFlatSearchCorpus(tableData, resolveSearchRowId);
2214
+ }, [
2215
+ enableExpand,
2216
+ enableInlineSearch,
2217
+ resolveSearchRowId,
2218
+ tableData,
2219
+ toggleField
2220
+ ]);
2221
+ const searchCorpusRef = (0, import_react5.useRef)(searchCorpus);
2222
+ searchCorpusRef.current = searchCorpus;
2223
+ const visibleRowIndexById = (0, import_react5.useMemo)(() => {
2224
+ const map = /* @__PURE__ */ new Map();
2225
+ for (const row of rows) {
2226
+ map.set(resolveSearchRowId(row.original, row.index), row.index);
2227
+ }
2228
+ return map;
2229
+ }, [resolveSearchRowId, rows]);
2230
+ const getSearchCellValue = (0, import_react5.useCallback)(
2231
+ (rowIndex, colIndex) => {
2232
+ const corpusRow = searchCorpusRef.current[rowIndex];
2233
+ const column = visibleLeafColumns[colIndex];
2234
+ if (!corpusRow || !column) return void 0;
2235
+ const visibleIndex = visibleRowIndexById.get(corpusRow.id);
2236
+ if (visibleIndex !== void 0) {
2237
+ const visibleRow = rows[visibleIndex];
2238
+ if (visibleRow) {
2239
+ return visibleRow.getValue(column.id);
2240
+ }
2241
+ }
2242
+ const columnDef = column.columnDef;
2243
+ if ("accessorFn" in columnDef && typeof columnDef.accessorFn === "function") {
2244
+ return columnDef.accessorFn(corpusRow.data, rowIndex);
2245
+ }
2246
+ if ("accessorKey" in columnDef && columnDef.accessorKey != null && columnDef.accessorKey !== "") {
2247
+ return corpusRow.data[String(columnDef.accessorKey)];
2248
+ }
2249
+ return corpusRow.data[column.id];
2250
+ },
2251
+ [rows, visibleLeafColumns, visibleRowIndexById]
2252
+ );
2253
+ const pendingSearchNavRef = (0, import_react5.useRef)(null);
2254
+ const focusSearchResult = (0, import_react5.useCallback)(
2255
+ (colIndex, visibleRowIndex) => {
2256
+ navigateToSearchResult([colIndex, visibleRowIndex]);
2257
+ },
2258
+ [navigateToSearchResult]
2259
+ );
2260
+ const navigateToCorpusSearchResult = (0, import_react5.useCallback)(
2261
+ (item) => {
2262
+ const [colIndex, corpusRowIndex] = item;
2263
+ const corpusRow = searchCorpusRef.current[corpusRowIndex];
2264
+ if (!corpusRow) return;
2265
+ const missingKeys = collectAncestorKeysToExpand(corpusRow, expandedRows);
2266
+ if (missingKeys.length > 0) {
2267
+ pendingSearchNavRef.current = {
2268
+ colIndex,
2269
+ rowId: corpusRow.id
2270
+ };
2271
+ const next = new Set(expandedRows);
2272
+ for (const key of corpusRow.ancestorToggleKeys) {
2273
+ next.add(key);
2274
+ }
2275
+ handleExpandedRowsChange(next);
2276
+ return;
2277
+ }
2278
+ const visibleItem = mapSearchResultToVisibleItem(
2279
+ item,
2280
+ searchCorpusRef.current,
2281
+ visibleRowIndexById
2282
+ );
2283
+ if (!visibleItem) return;
2284
+ focusSearchResult(visibleItem[0], visibleItem[1]);
2285
+ },
2286
+ [
2287
+ expandedRows,
2288
+ focusSearchResult,
2289
+ handleExpandedRowsChange,
2290
+ visibleRowIndexById
2291
+ ]
2292
+ );
2293
+ (0, import_react5.useEffect)(() => {
2294
+ const pending = pendingSearchNavRef.current;
2295
+ if (!pending) return;
2296
+ const visibleRowIndex = visibleRowIndexById.get(pending.rowId);
2297
+ if (visibleRowIndex === void 0) return;
2298
+ pendingSearchNavRef.current = null;
2299
+ focusSearchResult(pending.colIndex, visibleRowIndex);
2300
+ }, [focusSearchResult, rows, visibleRowIndexById]);
2301
+ const initialSearchStartRow = virtualRows[0]?.index ?? 0;
2302
+ const inlineSearch = useInlineSearch({
2303
+ enabled: enableInlineSearch,
2304
+ rowCount: searchCorpus.length,
2305
+ columnCount: visibleLeafColumns.length,
2306
+ getCellValue: getSearchCellValue,
2307
+ initialStartRow: initialSearchStartRow,
2308
+ showSearch,
2309
+ searchValue,
2310
+ searchResults,
2311
+ onSearchValueChange,
2312
+ onSearchClose,
2313
+ onSearchResultsChanged,
2314
+ onNavigateToResult: navigateToCorpusSearchResult,
2315
+ rootRef
2316
+ });
2317
+ const visibleSearchMatchKeys = (0, import_react5.useMemo)(() => {
2318
+ if (!enableInlineSearch) return EMPTY_SEARCH_MATCH_KEYS;
2319
+ return mapSearchResultsToVisibleKeys(
2320
+ inlineSearch.searchResults,
2321
+ searchCorpus,
2322
+ visibleRowIndexById
2323
+ );
2324
+ }, [
2325
+ enableInlineSearch,
2326
+ inlineSearch.searchResults,
2327
+ searchCorpus,
2328
+ visibleRowIndexById
2329
+ ]);
2330
+ const visibleActiveMatch = (0, import_react5.useMemo)(() => {
2331
+ if (!enableInlineSearch || !inlineSearch.activeMatch) return null;
2332
+ return mapSearchResultToVisibleItem(
2333
+ inlineSearch.activeMatch,
2334
+ searchCorpus,
2335
+ visibleRowIndexById
2336
+ );
2337
+ }, [
2338
+ enableInlineSearch,
2339
+ inlineSearch.activeMatch,
2340
+ searchCorpus,
2341
+ visibleRowIndexById
2342
+ ]);
2343
+ const clearHover = (0, import_react5.useCallback)(() => {
1559
2344
  setHoveredRowIndex(null);
1560
2345
  }, []);
1561
- const handleRowHover = (0, import_react4.useCallback)((rowIndex, _rowData) => {
2346
+ const handleRowHover = (0, import_react5.useCallback)((rowIndex, _rowData) => {
1562
2347
  setHoveredRowIndex(rowIndex);
1563
2348
  }, []);
1564
- const handleToggleSelect = (0, import_react4.useCallback)(
2349
+ const handleToggleSelect = (0, import_react5.useCallback)(
1565
2350
  (row) => {
1566
2351
  if (!row.getCanSelect()) return;
1567
2352
  if (preserveRowSelection && row.getIsSelected()) {
@@ -1571,14 +2356,14 @@ function useGlideTable(options) {
1571
2356
  },
1572
2357
  [preserveRowSelection]
1573
2358
  );
1574
- const handleToggleExpand = (0, import_react4.useCallback)(
2359
+ const handleToggleExpand = (0, import_react5.useCallback)(
1575
2360
  (rowKey) => {
1576
2361
  if (preventExpand) return;
1577
2362
  handleExpandedRowsChange(toggleExpandedRowId(rowKey, expandedRows));
1578
2363
  },
1579
2364
  [preventExpand, handleExpandedRowsChange, expandedRows]
1580
2365
  );
1581
- const rowContextValue = (0, import_react4.useMemo)(() => {
2366
+ const rowContextValue = (0, import_react5.useMemo)(() => {
1582
2367
  return {
1583
2368
  rowSpan: {
1584
2369
  enableRowSpan,
@@ -1626,6 +2411,11 @@ function useGlideTable(options) {
1626
2411
  columnFreeze: {
1627
2412
  enableColumnFreeze,
1628
2413
  offsets: columnFreezeOffsets
2414
+ },
2415
+ inlineSearch: {
2416
+ enabled: enableInlineSearch,
2417
+ matchKeys: visibleSearchMatchKeys,
2418
+ activeMatch: visibleActiveMatch
1629
2419
  }
1630
2420
  };
1631
2421
  }, [
@@ -1661,14 +2451,17 @@ function useGlideTable(options) {
1661
2451
  labels.collapseRow,
1662
2452
  enableColumnResize,
1663
2453
  enableColumnFreeze,
1664
- columnFreezeOffsets
2454
+ columnFreezeOffsets,
2455
+ enableInlineSearch,
2456
+ visibleSearchMatchKeys,
2457
+ visibleActiveMatch
1665
2458
  ]);
1666
- const copySelectionRef = (0, import_react4.useRef)(copySelection);
1667
- (0, import_react4.useEffect)(() => {
2459
+ const copySelectionRef = (0, import_react5.useRef)(copySelection);
2460
+ (0, import_react5.useEffect)(() => {
1668
2461
  copySelectionRef.current = copySelection;
1669
2462
  }, [copySelection]);
1670
- const stableCopySelection = (0, import_react4.useCallback)((options2) => copySelectionRef.current(options2), []);
1671
- (0, import_react4.useEffect)(() => {
2463
+ const stableCopySelection = (0, import_react5.useCallback)((options2) => copySelectionRef.current(options2), []);
2464
+ (0, import_react5.useEffect)(() => {
1672
2465
  onCopyActionsReady?.({ copySelection: stableCopySelection });
1673
2466
  }, [onCopyActionsReady, stableCopySelection]);
1674
2467
  return {
@@ -1684,8 +2477,10 @@ function useGlideTable(options) {
1684
2477
  enableCellSelection,
1685
2478
  enableColumnResize,
1686
2479
  enableColumnFreeze,
2480
+ enableInlineSearch,
1687
2481
  shouldVirtualize,
1688
2482
  scrollRef,
2483
+ rootRef,
1689
2484
  rowVirtualizer,
1690
2485
  virtualRows,
1691
2486
  paddingTop,
@@ -1693,7 +2488,21 @@ function useGlideTable(options) {
1693
2488
  rowContextValue,
1694
2489
  handleToggleSelect,
1695
2490
  clearHover,
1696
- copySelection: stableCopySelection
2491
+ copySelection: stableCopySelection,
2492
+ inlineSearch: {
2493
+ showSearch: inlineSearch.showSearch,
2494
+ searchValue: inlineSearch.searchValue,
2495
+ searchStatus: inlineSearch.searchStatus,
2496
+ searchInputRef: inlineSearch.searchInputRef,
2497
+ searchInputId: inlineSearch.searchInputId,
2498
+ canClose: inlineSearch.canClose,
2499
+ searchRowCount: searchCorpus.length,
2500
+ setSearchValue: inlineSearch.setSearchValue,
2501
+ closeSearch: inlineSearch.closeSearch,
2502
+ goToNext: inlineSearch.goToNext,
2503
+ goToPrevious: inlineSearch.goToPrevious,
2504
+ openSearch: inlineSearch.openSearch
2505
+ }
1697
2506
  };
1698
2507
  }
1699
2508
 
@@ -1717,18 +2526,29 @@ function getColumnSizeStyle(size, options) {
1717
2526
  DEFAULT_TREE_ID_FIELD,
1718
2527
  DEFAULT_TREE_PARENT_ID_FIELD,
1719
2528
  DEFAULT_TREE_QTY_FIELD,
2529
+ INLINE_SEARCH_MAX_RESULTS,
1720
2530
  applyCellEdit,
1721
2531
  applyFillData,
1722
2532
  applySelectionUpdater,
1723
2533
  buildColumnFreezeOffsets,
1724
2534
  buildColumnRowSpanMap,
2535
+ buildFlatSearchCorpus,
1725
2536
  buildRowsPastePayload,
2537
+ buildSearchMatchKey,
2538
+ buildSearchMatchKeys,
2539
+ buildTreeSearchCorpus,
1726
2540
  canExpandRow,
2541
+ cellValueToSearchText,
2542
+ collectAncestorKeysToExpand,
1727
2543
  collectCopyRowEntries,
1728
2544
  collectCopyRows,
1729
2545
  collectFillChanges,
1730
2546
  collectRowSpanColumns,
2547
+ collectSearchMatchesInRange,
2548
+ createSearchRegex,
2549
+ escapeSearchRegex,
1731
2550
  flattenSubtreeRows,
2551
+ formatSearchResultLabel,
1732
2552
  getCellEditDraftValue,
1733
2553
  getCellSelectionEdgeStyle,
1734
2554
  getColumnEditType,
@@ -1740,10 +2560,15 @@ function getColumnSizeStyle(size, options) {
1740
2560
  isCellInSelection,
1741
2561
  isColumnEditable,
1742
2562
  isEditablePasteTarget,
2563
+ mapSearchResultToVisibleItem,
2564
+ mapSearchResultsToVisibleKeys,
1743
2565
  measureMergedSpanRowHeights,
2566
+ nextSearchIndex,
2567
+ nextSearchStride,
1744
2568
  parseCellEditValue,
1745
2569
  parseClipboardTSV,
1746
2570
  parseClipboardTSVWithDepths,
2571
+ previousSearchIndex,
1747
2572
  resolveColumnFreezeSide,
1748
2573
  resolveDataTableLabels,
1749
2574
  resolvePasteColumnIds,
@@ -1757,5 +2582,6 @@ function getColumnSizeStyle(size, options) {
1757
2582
  useCellSelection,
1758
2583
  useConvertTreeData,
1759
2584
  useGlideTable,
2585
+ useInlineSearch,
1760
2586
  writeSelectionToClipboard
1761
2587
  });