react-glide-table 1.4.1 → 1.7.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/index.js CHANGED
@@ -5,7 +5,12 @@ var DEFAULT_DATA_TABLE_LABELS = {
5
5
  selection: (selectedCount) => selectedCount > 0 ? `\u2713 ${selectedCount} selected` : null,
6
6
  expandRow: "Expand row",
7
7
  collapseRow: "Collapse row",
8
- resizeColumn: "Resize column"
8
+ resizeColumn: "Resize column",
9
+ searchPlaceholder: "Search\u2026",
10
+ searchResultHint: "Type to search",
11
+ searchPrevious: "Previous result",
12
+ searchNext: "Next result",
13
+ searchClose: "Close search"
9
14
  };
10
15
  function resolveDataTableLabels(partial) {
11
16
  return {
@@ -29,11 +34,11 @@ import {
29
34
  useVirtualizer
30
35
  } from "@tanstack/react-virtual";
31
36
  import {
32
- useCallback as useCallback3,
33
- useEffect as useEffect4,
34
- useMemo as useMemo2,
35
- useRef as useRef4,
36
- useState as useState3
37
+ useCallback as useCallback4,
38
+ useEffect as useEffect5,
39
+ useMemo as useMemo3,
40
+ useRef as useRef5,
41
+ useState as useState4
37
42
  } from "react";
38
43
 
39
44
  // src/components/ui/table/constants.ts
@@ -46,6 +51,7 @@ var ROW_HOVER_CLASS = "row-hoverable";
46
51
  var ROW_HOVERED_BG_CLASS = "row-hovered";
47
52
  var CELL_SELECTION_FILL_CLASS = "cell-selection-fill";
48
53
  var DATA_TABLE_ROW_HEIGHT = 44;
54
+ var DATA_TABLE_HEADER_ROW_HEIGHT = 40;
49
55
  var DATA_TABLE_VIRTUAL_OVERSCAN = 8;
50
56
  var DATA_TABLE_COLUMN_SIZE = 150;
51
57
  var DATA_TABLE_COLUMN_MIN_SIZE = 40;
@@ -199,6 +205,36 @@ function getCellSelectionBounds(start, end) {
199
205
  endCol: Math.max(start.col, end.col)
200
206
  };
201
207
  }
208
+ function getCellNavigationDelta(key) {
209
+ switch (key) {
210
+ case "ArrowUp":
211
+ case "w":
212
+ case "W":
213
+ return { row: -1, col: 0 };
214
+ case "ArrowDown":
215
+ case "s":
216
+ case "S":
217
+ return { row: 1, col: 0 };
218
+ case "ArrowLeft":
219
+ case "a":
220
+ case "A":
221
+ return { row: 0, col: -1 };
222
+ case "ArrowRight":
223
+ case "d":
224
+ case "D":
225
+ return { row: 0, col: 1 };
226
+ default:
227
+ return null;
228
+ }
229
+ }
230
+ function clampCellPosition(position, rowCount, columnCount) {
231
+ const maxRow = Math.max(rowCount - 1, 0);
232
+ const maxCol = Math.max(columnCount - 1, 0);
233
+ return {
234
+ row: Math.min(Math.max(position.row, 0), maxRow),
235
+ col: Math.min(Math.max(position.col, 0), maxCol)
236
+ };
237
+ }
202
238
  function measureMergedSpanRowHeights(rowIndex, rowSpan, cellElement) {
203
239
  if (rowSpan <= 1) return void 0;
204
240
  const tbody = cellElement?.closest("tbody") ?? (typeof document !== "undefined" ? document.querySelector("tbody.data-table-body") : null);
@@ -722,26 +758,44 @@ function useCellSelection({
722
758
  data,
723
759
  rows,
724
760
  enabled = true,
761
+ columnCount = 0,
725
762
  enableSubtreeCopy = false,
726
763
  enableInsertPaste = true,
727
764
  onDataChange,
728
765
  onBatchChange,
729
- onRowsPaste
766
+ onRowsPaste,
767
+ onCellNavigate
730
768
  }) {
731
769
  const [dragState, setDragState] = useState2(INITIAL_DRAG_STATE);
732
770
  const pendingPasteModeRef = useRef2(null);
771
+ const dragStateRef = useRef2(dragState);
772
+ const onCellNavigateRef = useRef2(onCellNavigate);
773
+ dragStateRef.current = dragState;
774
+ onCellNavigateRef.current = onCellNavigate;
733
775
  const cellSelectionBounds = getCellSelectionBounds(dragState.start, dragState.end);
734
776
  const activeSelectionBounds = enabled ? getActiveSelectionBounds(dragState, cellSelectionBounds) : null;
735
777
  const handleCellMouseDown = useCallback2(
736
- (rowIndex, colIndex) => {
778
+ (rowIndex, colIndex, options) => {
737
779
  if (!enabled) return;
738
- setDragState({
739
- isSelecting: true,
740
- isFillDragging: false,
741
- start: { row: rowIndex, col: colIndex },
742
- end: { row: rowIndex, col: colIndex },
743
- fillAnchor: null,
744
- fillEnd: null
780
+ setDragState((prev) => {
781
+ if (options?.shiftKey && prev.start) {
782
+ return {
783
+ ...prev,
784
+ isSelecting: true,
785
+ isFillDragging: false,
786
+ end: { row: rowIndex, col: colIndex },
787
+ fillAnchor: null,
788
+ fillEnd: null
789
+ };
790
+ }
791
+ return {
792
+ isSelecting: true,
793
+ isFillDragging: false,
794
+ start: { row: rowIndex, col: colIndex },
795
+ end: { row: rowIndex, col: colIndex },
796
+ fillAnchor: null,
797
+ fillEnd: null
798
+ };
745
799
  });
746
800
  },
747
801
  [enabled]
@@ -783,6 +837,53 @@ function useCellSelection({
783
837
  setDragState(INITIAL_DRAG_STATE);
784
838
  }
785
839
  }, [enabled]);
840
+ useEffect2(() => {
841
+ if (!enabled) return;
842
+ const handleKeyDown = (e) => {
843
+ if (e.ctrlKey || e.metaKey || e.altKey) return;
844
+ if (isEditablePasteTarget(e.target) || isEditablePasteTarget(document.activeElement)) {
845
+ return;
846
+ }
847
+ const delta = getCellNavigationDelta(e.key);
848
+ if (!delta) return;
849
+ const prev = dragStateRef.current;
850
+ if (!prev.start || !prev.end) return;
851
+ if (prev.isSelecting || prev.isFillDragging) return;
852
+ const rowCount = rows.length;
853
+ const resolvedColumnCount = columnCount > 0 ? columnCount : rows[0]?.getVisibleCells().length ?? 0;
854
+ if (rowCount <= 0 || resolvedColumnCount <= 0) return;
855
+ const nextEnd = clampCellPosition(
856
+ {
857
+ row: prev.end.row + delta.row,
858
+ col: prev.end.col + delta.col
859
+ },
860
+ rowCount,
861
+ resolvedColumnCount
862
+ );
863
+ if (nextEnd.row === prev.end.row && nextEnd.col === prev.end.col) return;
864
+ e.preventDefault();
865
+ const nextState = e.shiftKey ? {
866
+ ...prev,
867
+ isSelecting: false,
868
+ isFillDragging: false,
869
+ end: nextEnd,
870
+ fillAnchor: null,
871
+ fillEnd: null
872
+ } : {
873
+ isSelecting: false,
874
+ isFillDragging: false,
875
+ start: nextEnd,
876
+ end: nextEnd,
877
+ fillAnchor: null,
878
+ fillEnd: null
879
+ };
880
+ dragStateRef.current = nextState;
881
+ setDragState(nextState);
882
+ onCellNavigateRef.current?.(nextEnd);
883
+ };
884
+ window.addEventListener("keydown", handleKeyDown);
885
+ return () => window.removeEventListener("keydown", handleKeyDown);
886
+ }, [columnCount, enabled, rows]);
786
887
  const copySelection = useCallback2(
787
888
  async (options) => {
788
889
  if (!enabled || !activeSelectionBounds) return false;
@@ -1019,12 +1120,473 @@ function getColumnFreezeStyle(offset, options) {
1019
1120
  position: "sticky",
1020
1121
  ...offset.side === "left" ? { left: offset.offset } : { right: offset.offset },
1021
1122
  zIndex: zBase + offset.stack,
1022
- ...options?.isHeader ? { top: 0 } : {}
1123
+ ...options?.isHeader ? { top: options.headerTop ?? 0 } : {}
1124
+ };
1125
+ }
1126
+
1127
+ // src/components/ui/table/features/inline-search/inlineSearch.ts
1128
+ var INLINE_SEARCH_MAX_RESULTS = 1e3;
1129
+ var INLINE_SEARCH_TARGET_TICK_MS = 10;
1130
+ var INLINE_SEARCH_INITIAL_STRIDE = 10;
1131
+ function escapeSearchRegex(value) {
1132
+ return value.replace(/([$()*+.?[\\\]^{|}-])/g, "\\$1");
1133
+ }
1134
+ function createSearchRegex(query) {
1135
+ const trimmed = query.trim();
1136
+ if (!trimmed) return null;
1137
+ return new RegExp(escapeSearchRegex(trimmed), "i");
1138
+ }
1139
+ function cellValueToSearchText(value) {
1140
+ if (value == null) return void 0;
1141
+ if (typeof value === "string") return value;
1142
+ if (typeof value === "number" || typeof value === "boolean") {
1143
+ return String(value);
1144
+ }
1145
+ if (Array.isArray(value)) {
1146
+ return value.map((item) => cellValueToSearchText(item) ?? "").join(" ");
1147
+ }
1148
+ if (typeof value === "object") {
1149
+ try {
1150
+ return JSON.stringify(value);
1151
+ } catch {
1152
+ return String(value);
1153
+ }
1154
+ }
1155
+ return String(value);
1156
+ }
1157
+ function formatSearchResultLabel(status) {
1158
+ const countLabel = status.results >= INLINE_SEARCH_MAX_RESULTS ? `over ${INLINE_SEARCH_MAX_RESULTS}` : `${status.results} result${status.results !== 1 ? "s" : ""}`;
1159
+ if (status.selectedIndex >= 0 && status.results > 0) {
1160
+ return `${status.selectedIndex + 1} of ${countLabel}`;
1161
+ }
1162
+ return countLabel;
1163
+ }
1164
+ function nextSearchIndex(selectedIndex, results) {
1165
+ if (results <= 0) return -1;
1166
+ if (selectedIndex < 0) return 0;
1167
+ return (selectedIndex + 1) % results;
1168
+ }
1169
+ function previousSearchIndex(selectedIndex, results) {
1170
+ if (results <= 0) return -1;
1171
+ if (selectedIndex < 0) return results - 1;
1172
+ let next = (selectedIndex - 1) % results;
1173
+ if (next < 0) next += results;
1174
+ return next;
1175
+ }
1176
+ function buildSearchMatchKey(colIndex, rowIndex) {
1177
+ return `${colIndex}:${rowIndex}`;
1178
+ }
1179
+ function buildSearchMatchKeys(results) {
1180
+ const keys = /* @__PURE__ */ new Set();
1181
+ for (const [colIndex, rowIndex] of results) {
1182
+ keys.add(buildSearchMatchKey(colIndex, rowIndex));
1183
+ }
1184
+ return keys;
1185
+ }
1186
+ function collectSearchMatchesInRange(options) {
1187
+ const {
1188
+ query,
1189
+ startRow,
1190
+ rowCount,
1191
+ columnCount,
1192
+ getCellValue,
1193
+ maxResults = INLINE_SEARCH_MAX_RESULTS
1194
+ } = options;
1195
+ const regex = createSearchRegex(query);
1196
+ if (!regex || rowCount <= 0 || columnCount <= 0) return [];
1197
+ const matches = [];
1198
+ for (let rowOffset = 0; rowOffset < rowCount; rowOffset += 1) {
1199
+ const rowIndex = startRow + rowOffset;
1200
+ for (let colIndex = 0; colIndex < columnCount; colIndex += 1) {
1201
+ const text = cellValueToSearchText(getCellValue(rowIndex, colIndex));
1202
+ if (text !== void 0 && regex.test(text)) {
1203
+ matches.push([colIndex, rowIndex]);
1204
+ if (matches.length >= maxResults) {
1205
+ return matches;
1206
+ }
1207
+ }
1208
+ }
1209
+ }
1210
+ return matches;
1211
+ }
1212
+ function nextSearchStride(currentStride, elapsedMs, targetMs = INLINE_SEARCH_TARGET_TICK_MS) {
1213
+ const rounded = Math.max(elapsedMs, 1);
1214
+ const scalar = targetMs / rounded;
1215
+ return Math.max(1, Math.ceil(currentStride * scalar));
1216
+ }
1217
+ function buildFlatSearchCorpus(rows, getRowId) {
1218
+ return rows.map((data, index) => ({
1219
+ id: getRowId(data, index),
1220
+ data,
1221
+ ancestorToggleKeys: []
1222
+ }));
1223
+ }
1224
+ function buildTreeSearchCorpus(visibleRows, options) {
1225
+ const { toggleField, getRowId } = options;
1226
+ const corpus = [];
1227
+ const seen = /* @__PURE__ */ new Set();
1228
+ const walk = (node, ancestorToggleKeys) => {
1229
+ const id = getRowId(node, corpus.length);
1230
+ if (seen.has(id)) return;
1231
+ seen.add(id);
1232
+ corpus.push({
1233
+ id,
1234
+ data: node,
1235
+ ancestorToggleKeys
1236
+ });
1237
+ const children = node.children;
1238
+ if (!Array.isArray(children) || children.length === 0) return;
1239
+ const toggleValue = node[toggleField];
1240
+ const childAncestors = typeof toggleValue === "string" && toggleValue.length > 0 ? [...ancestorToggleKeys, toggleValue] : ancestorToggleKeys;
1241
+ for (const child of children) {
1242
+ if (child && typeof child === "object") {
1243
+ walk(child, childAncestors);
1244
+ }
1245
+ }
1246
+ };
1247
+ for (const row of visibleRows) {
1248
+ const level = row.level;
1249
+ if (level === 0 || level === void 0) {
1250
+ walk(row, []);
1251
+ }
1252
+ }
1253
+ for (const row of visibleRows) {
1254
+ const id = getRowId(row, corpus.length);
1255
+ if (seen.has(id)) continue;
1256
+ walk(row, []);
1257
+ }
1258
+ return corpus;
1259
+ }
1260
+ function mapSearchResultsToVisibleKeys(results, corpus, visibleRowIndexById) {
1261
+ const keys = /* @__PURE__ */ new Set();
1262
+ for (const [colIndex, corpusRowIndex] of results) {
1263
+ const corpusRow = corpus[corpusRowIndex];
1264
+ if (!corpusRow) continue;
1265
+ const visibleRowIndex = visibleRowIndexById.get(corpusRow.id);
1266
+ if (visibleRowIndex === void 0) continue;
1267
+ keys.add(buildSearchMatchKey(colIndex, visibleRowIndex));
1268
+ }
1269
+ return keys;
1270
+ }
1271
+ function mapSearchResultToVisibleItem(item, corpus, visibleRowIndexById) {
1272
+ const [colIndex, corpusRowIndex] = item;
1273
+ const corpusRow = corpus[corpusRowIndex];
1274
+ if (!corpusRow) return null;
1275
+ const visibleRowIndex = visibleRowIndexById.get(corpusRow.id);
1276
+ if (visibleRowIndex === void 0) return null;
1277
+ return [colIndex, visibleRowIndex];
1278
+ }
1279
+ function collectAncestorKeysToExpand(corpusRow, expandedRows) {
1280
+ return corpusRow.ancestorToggleKeys.filter((key) => !expandedRows.has(key));
1281
+ }
1282
+
1283
+ // src/components/ui/table/features/inline-search/useInlineSearch.ts
1284
+ import {
1285
+ useCallback as useCallback3,
1286
+ useEffect as useEffect3,
1287
+ useId,
1288
+ useMemo,
1289
+ useRef as useRef3,
1290
+ useState as useState3
1291
+ } from "react";
1292
+ var EMPTY_MATCH_KEYS = /* @__PURE__ */ new Set();
1293
+ function useInlineSearch({
1294
+ enabled = false,
1295
+ rowCount,
1296
+ columnCount,
1297
+ getCellValue,
1298
+ initialStartRow = 0,
1299
+ showSearch: controlledShowSearch,
1300
+ searchValue: controlledSearchValue,
1301
+ searchResults: controlledSearchResults,
1302
+ onSearchValueChange,
1303
+ onSearchClose,
1304
+ onSearchResultsChanged,
1305
+ onNavigateToResult,
1306
+ rootRef
1307
+ }) {
1308
+ const searchInputId = useId();
1309
+ const searchInputRef = useRef3(null);
1310
+ const [internalShowSearch, setInternalShowSearch] = useState3(false);
1311
+ const [internalSearchValue, setInternalSearchValue] = useState3("");
1312
+ const [internalResults, setInternalResults] = useState3(
1313
+ []
1314
+ );
1315
+ const [searchStatus, setSearchStatus] = useState3();
1316
+ const searchStatusRef = useRef3(searchStatus);
1317
+ searchStatusRef.current = searchStatus;
1318
+ const abortControllerRef = useRef3(null);
1319
+ const searchHandleRef = useRef3(void 0);
1320
+ const initialStartRowRef = useRef3(initialStartRow);
1321
+ initialStartRowRef.current = initialStartRow;
1322
+ const getCellValueRef = useRef3(getCellValue);
1323
+ getCellValueRef.current = getCellValue;
1324
+ const showSearch = controlledShowSearch ?? internalShowSearch;
1325
+ const searchValue = controlledSearchValue ?? internalSearchValue;
1326
+ const searchResults = controlledSearchResults ?? internalResults;
1327
+ const setSearchValue = useCallback3(
1328
+ (value) => {
1329
+ setInternalSearchValue(value);
1330
+ onSearchValueChange?.(value);
1331
+ },
1332
+ [onSearchValueChange]
1333
+ );
1334
+ const cancelSearch = useCallback3(() => {
1335
+ if (searchHandleRef.current !== void 0) {
1336
+ window.cancelAnimationFrame(searchHandleRef.current);
1337
+ searchHandleRef.current = void 0;
1338
+ }
1339
+ abortControllerRef.current?.abort();
1340
+ }, []);
1341
+ const emitResultsChanged = useCallback3(
1342
+ (results, navIndex) => {
1343
+ onSearchResultsChanged?.(results, navIndex);
1344
+ },
1345
+ [onSearchResultsChanged]
1346
+ );
1347
+ const navigateToIndex = useCallback3(
1348
+ (results, navIndex) => {
1349
+ if (onSearchResultsChanged) return;
1350
+ if (navIndex < 0 || navIndex >= results.length) return;
1351
+ const item = results[navIndex];
1352
+ if (!item) return;
1353
+ onNavigateToResult?.(item);
1354
+ },
1355
+ [onNavigateToResult, onSearchResultsChanged]
1356
+ );
1357
+ const beginSearch = useCallback3(
1358
+ (query) => {
1359
+ if (controlledSearchResults !== void 0) return;
1360
+ const totalRows = rowCount;
1361
+ if (totalRows === 0 || columnCount === 0) {
1362
+ setSearchStatus(void 0);
1363
+ setInternalResults([]);
1364
+ emitResultsChanged([], -1);
1365
+ return;
1366
+ }
1367
+ let startY = Math.min(
1368
+ Math.max(0, initialStartRowRef.current),
1369
+ totalRows - 1
1370
+ );
1371
+ let searchStride = Math.min(INLINE_SEARCH_INITIAL_STRIDE, totalRows);
1372
+ let rowsSearched = 0;
1373
+ const runningResult = [];
1374
+ setSearchStatus(void 0);
1375
+ setInternalResults([]);
1376
+ const tick = () => {
1377
+ if (abortControllerRef.current?.signal.aborted) return;
1378
+ const tStart = performance.now();
1379
+ const rowsLeft = totalRows - rowsSearched;
1380
+ const height = Math.min(searchStride, rowsLeft, totalRows - startY);
1381
+ if (height <= 0) {
1382
+ return;
1383
+ }
1384
+ const chunk = collectSearchMatchesInRange({
1385
+ query,
1386
+ startRow: startY,
1387
+ rowCount: height,
1388
+ columnCount,
1389
+ getCellValue: (rowIndex, colIndex) => getCellValueRef.current(rowIndex, colIndex),
1390
+ maxResults: INLINE_SEARCH_MAX_RESULTS - runningResult.length
1391
+ });
1392
+ if (chunk.length > 0) {
1393
+ runningResult.push(...chunk);
1394
+ setInternalResults([...runningResult]);
1395
+ }
1396
+ rowsSearched += height;
1397
+ const selectedIndex = searchStatusRef.current?.selectedIndex ?? -1;
1398
+ setSearchStatus({
1399
+ results: runningResult.length,
1400
+ rowsSearched,
1401
+ selectedIndex
1402
+ });
1403
+ emitResultsChanged(runningResult, selectedIndex);
1404
+ if (startY + height >= totalRows) {
1405
+ startY = 0;
1406
+ } else {
1407
+ startY += height;
1408
+ }
1409
+ searchStride = nextSearchStride(
1410
+ searchStride,
1411
+ performance.now() - tStart
1412
+ );
1413
+ if (rowsSearched < totalRows && runningResult.length < INLINE_SEARCH_MAX_RESULTS) {
1414
+ searchHandleRef.current = window.requestAnimationFrame(tick);
1415
+ }
1416
+ };
1417
+ cancelSearch();
1418
+ abortControllerRef.current = new AbortController();
1419
+ searchHandleRef.current = window.requestAnimationFrame(tick);
1420
+ },
1421
+ [
1422
+ cancelSearch,
1423
+ columnCount,
1424
+ controlledSearchResults,
1425
+ emitResultsChanged,
1426
+ rowCount
1427
+ ]
1428
+ );
1429
+ const openSearch = useCallback3(() => {
1430
+ if (controlledShowSearch === void 0) {
1431
+ setInternalShowSearch(true);
1432
+ }
1433
+ }, [controlledShowSearch]);
1434
+ const closeSearch = useCallback3(() => {
1435
+ if (controlledShowSearch === void 0) {
1436
+ setInternalShowSearch(false);
1437
+ }
1438
+ onSearchClose?.();
1439
+ setSearchStatus(void 0);
1440
+ setInternalResults([]);
1441
+ emitResultsChanged([], -1);
1442
+ cancelSearch();
1443
+ }, [
1444
+ cancelSearch,
1445
+ controlledShowSearch,
1446
+ emitResultsChanged,
1447
+ onSearchClose
1448
+ ]);
1449
+ const goToNext = useCallback3(() => {
1450
+ if (!searchStatus || searchStatus.results === 0) return;
1451
+ const newIndex = nextSearchIndex(
1452
+ searchStatus.selectedIndex,
1453
+ searchStatus.results
1454
+ );
1455
+ setSearchStatus({ ...searchStatus, selectedIndex: newIndex });
1456
+ emitResultsChanged(searchResults, newIndex);
1457
+ navigateToIndex(searchResults, newIndex);
1458
+ }, [emitResultsChanged, navigateToIndex, searchResults, searchStatus]);
1459
+ const goToPrevious = useCallback3(() => {
1460
+ if (!searchStatus || searchStatus.results === 0) return;
1461
+ const newIndex = previousSearchIndex(
1462
+ searchStatus.selectedIndex,
1463
+ searchStatus.results
1464
+ );
1465
+ setSearchStatus({ ...searchStatus, selectedIndex: newIndex });
1466
+ emitResultsChanged(searchResults, newIndex);
1467
+ navigateToIndex(searchResults, newIndex);
1468
+ }, [emitResultsChanged, navigateToIndex, searchResults, searchStatus]);
1469
+ useEffect3(() => {
1470
+ if (controlledSearchResults === void 0) return;
1471
+ if (controlledSearchResults.length > 0) {
1472
+ setSearchStatus((current) => ({
1473
+ rowsSearched: rowCount,
1474
+ results: controlledSearchResults.length,
1475
+ selectedIndex: current?.selectedIndex ?? -1
1476
+ }));
1477
+ } else {
1478
+ setSearchStatus(void 0);
1479
+ }
1480
+ }, [controlledSearchResults, rowCount]);
1481
+ useEffect3(() => {
1482
+ if (!enabled) return;
1483
+ setSearchStatus(void 0);
1484
+ setInternalResults([]);
1485
+ emitResultsChanged([], -1);
1486
+ if (showSearch) {
1487
+ queueMicrotask(() => {
1488
+ searchInputRef.current?.focus({ preventScroll: true });
1489
+ });
1490
+ } else {
1491
+ cancelSearch();
1492
+ }
1493
+ }, [enabled, showSearch]);
1494
+ useEffect3(() => {
1495
+ if (!enabled || !showSearch) return;
1496
+ if (controlledSearchResults !== void 0) return;
1497
+ if (searchValue.trim() === "") {
1498
+ setSearchStatus(void 0);
1499
+ setInternalResults([]);
1500
+ cancelSearch();
1501
+ emitResultsChanged([], -1);
1502
+ return;
1503
+ }
1504
+ beginSearch(searchValue);
1505
+ }, [
1506
+ beginSearch,
1507
+ cancelSearch,
1508
+ controlledSearchResults,
1509
+ emitResultsChanged,
1510
+ enabled,
1511
+ searchValue,
1512
+ showSearch
1513
+ ]);
1514
+ useEffect3(() => {
1515
+ if (!enabled) return;
1516
+ const handleKeyDown = (event) => {
1517
+ if (!(event.ctrlKey || event.metaKey)) return;
1518
+ if (event.key.toLowerCase() !== "f") return;
1519
+ const root = rootRef?.current;
1520
+ if (root) {
1521
+ const active = document.activeElement;
1522
+ const focusInside = active === root || active instanceof Node && root.contains(active);
1523
+ if (!focusInside && active !== document.body) {
1524
+ return;
1525
+ }
1526
+ }
1527
+ event.preventDefault();
1528
+ event.stopPropagation();
1529
+ if (showSearch) {
1530
+ searchInputRef.current?.focus({ preventScroll: true });
1531
+ searchInputRef.current?.select();
1532
+ return;
1533
+ }
1534
+ if (controlledShowSearch === void 0) {
1535
+ setInternalShowSearch(true);
1536
+ }
1537
+ };
1538
+ window.addEventListener("keydown", handleKeyDown, true);
1539
+ return () => window.removeEventListener("keydown", handleKeyDown, true);
1540
+ }, [controlledShowSearch, enabled, rootRef, showSearch]);
1541
+ useEffect3(() => () => cancelSearch(), [cancelSearch]);
1542
+ const searchMatchKeys = useMemo(
1543
+ () => buildSearchMatchKeys(searchResults),
1544
+ [searchResults]
1545
+ );
1546
+ const activeMatch = useMemo(() => {
1547
+ if (!searchStatus || searchStatus.selectedIndex < 0) return null;
1548
+ return searchResults[searchStatus.selectedIndex] ?? null;
1549
+ }, [searchResults, searchStatus]);
1550
+ if (!enabled) {
1551
+ return {
1552
+ enabled: false,
1553
+ showSearch: false,
1554
+ searchValue: "",
1555
+ searchResults: [],
1556
+ searchStatus: void 0,
1557
+ searchMatchKeys: EMPTY_MATCH_KEYS,
1558
+ activeMatch: null,
1559
+ searchInputRef,
1560
+ searchInputId,
1561
+ canClose: false,
1562
+ openSearch,
1563
+ closeSearch,
1564
+ setSearchValue,
1565
+ goToNext,
1566
+ goToPrevious
1567
+ };
1568
+ }
1569
+ return {
1570
+ enabled: true,
1571
+ showSearch,
1572
+ searchValue,
1573
+ searchResults,
1574
+ searchStatus,
1575
+ searchMatchKeys,
1576
+ activeMatch,
1577
+ searchInputRef,
1578
+ searchInputId,
1579
+ canClose: controlledShowSearch === void 0 || onSearchClose !== void 0,
1580
+ openSearch,
1581
+ closeSearch,
1582
+ setSearchValue,
1583
+ goToNext,
1584
+ goToPrevious
1023
1585
  };
1024
1586
  }
1025
1587
 
1026
1588
  // src/components/ui/table/features/row-expand/row-expand.ts
1027
- import { useEffect as useEffect3, useMemo, useRef as useRef3 } from "react";
1589
+ import { useEffect as useEffect4, useMemo as useMemo2, useRef as useRef4 } from "react";
1028
1590
  function getFieldValue(row, key) {
1029
1591
  return row[key];
1030
1592
  }
@@ -1054,12 +1616,12 @@ var useConvertTreeData = ({
1054
1616
  expandedRows,
1055
1617
  onExpandedRowsChange
1056
1618
  }) => {
1057
- const onExpandedRowsChangeRef = useRef3(onExpandedRowsChange);
1058
- const hasInitializedRef = useRef3(false);
1059
- useEffect3(() => {
1619
+ const onExpandedRowsChangeRef = useRef4(onExpandedRowsChange);
1620
+ const hasInitializedRef = useRef4(false);
1621
+ useEffect4(() => {
1060
1622
  onExpandedRowsChangeRef.current = onExpandedRowsChange;
1061
1623
  }, [onExpandedRowsChange]);
1062
- useEffect3(() => {
1624
+ useEffect4(() => {
1063
1625
  if (!data || data.length === 0) {
1064
1626
  hasInitializedRef.current = false;
1065
1627
  return;
@@ -1069,7 +1631,7 @@ var useConvertTreeData = ({
1069
1631
  onExpandedRowsChangeRef.current?.(new Set(ids));
1070
1632
  hasInitializedRef.current = true;
1071
1633
  }, [enabled, data, toggleField]);
1072
- const processedData = useMemo(() => {
1634
+ const processedData = useMemo2(() => {
1073
1635
  if (!enabled || !data || data.length === 0) return [];
1074
1636
  const flattenedData = [];
1075
1637
  const flattenItems = (items) => {
@@ -1133,7 +1695,7 @@ var useConvertTreeData = ({
1133
1695
  });
1134
1696
  return rootItems;
1135
1697
  }, [enabled, data, toggleField, childField, flattenField]);
1136
- const flattenTree = useMemo(() => {
1698
+ const flattenTree = useMemo2(() => {
1137
1699
  if (!enabled) return [];
1138
1700
  const flatten = (nodes, result = [], level = 0) => {
1139
1701
  nodes.forEach((node, index) => {
@@ -1183,7 +1745,7 @@ var useConvertTreeData = ({
1183
1745
  preventExpand,
1184
1746
  expandedRows
1185
1747
  ]);
1186
- const sortedData = useMemo(() => {
1748
+ const sortedData = useMemo2(() => {
1187
1749
  if (!enabled) {
1188
1750
  return data ?? [];
1189
1751
  }
@@ -1289,6 +1851,7 @@ function collectRowSpanColumns(columns) {
1289
1851
 
1290
1852
  // src/core/useGlideTable.ts
1291
1853
  var EMPTY_COLUMN_FREEZE_OFFSETS = /* @__PURE__ */ new Map();
1854
+ var EMPTY_SEARCH_MATCH_KEYS = /* @__PURE__ */ new Set();
1292
1855
  function useGlideTable(options) {
1293
1856
  const {
1294
1857
  data,
@@ -1329,9 +1892,16 @@ function useGlideTable(options) {
1329
1892
  columnSizing: controlledColumnSizing,
1330
1893
  onColumnSizingChange,
1331
1894
  columnResizeMode = "onChange",
1332
- enableColumnFreeze = false
1895
+ enableColumnFreeze = false,
1896
+ enableInlineSearch = false,
1897
+ showSearch,
1898
+ searchValue,
1899
+ onSearchValueChange,
1900
+ onSearchClose,
1901
+ searchResults,
1902
+ onSearchResultsChanged
1333
1903
  } = options;
1334
- const labels = useMemo2(() => {
1904
+ const labels = useMemo3(() => {
1335
1905
  const resolved = resolveDataTableLabels(labelsProp);
1336
1906
  return {
1337
1907
  ...resolved,
@@ -1342,15 +1912,16 @@ function useGlideTable(options) {
1342
1912
  }, [labelsProp, emptyText, loadingText, selectionLabel]);
1343
1913
  const enableExpand = Boolean(toggleField);
1344
1914
  const resolvedEnableSubtreeCopy = enableSubtreeCopy ?? enableExpand;
1345
- const [internalRowSelection, setInternalRowSelection] = useState3({});
1346
- const [internalColumnSizing, setInternalColumnSizing] = useState3({});
1347
- const [internalExpandedRows, setInternalExpandedRows] = useState3(
1915
+ const [internalRowSelection, setInternalRowSelection] = useState4({});
1916
+ const [internalColumnSizing, setInternalColumnSizing] = useState4({});
1917
+ const [internalExpandedRows, setInternalExpandedRows] = useState4(
1348
1918
  () => /* @__PURE__ */ new Set()
1349
1919
  );
1350
- const [hoveredRowIndex, setHoveredRowIndex] = useState3(null);
1351
- const scrollRef = useRef4(null);
1920
+ const [hoveredRowIndex, setHoveredRowIndex] = useState4(null);
1921
+ const scrollRef = useRef5(null);
1922
+ const rootRef = useRef5(null);
1352
1923
  const shouldVirtualize = enableVirtualization && !enableRowSpan;
1353
- useEffect4(() => {
1924
+ useEffect5(() => {
1354
1925
  if (enableVirtualization && enableRowSpan) {
1355
1926
  console.warn(
1356
1927
  "[useGlideTable] enableRowSpan is on; virtualization is disabled to preserve cell merges."
@@ -1364,7 +1935,7 @@ function useGlideTable(options) {
1364
1935
  );
1365
1936
  const columnSizing = controlledColumnSizing ?? internalColumnSizing;
1366
1937
  const expandedRows = controlledExpandedRows ?? internalExpandedRows;
1367
- const handleExpandedRowsChange = useCallback3(
1938
+ const handleExpandedRowsChange = useCallback4(
1368
1939
  (next) => {
1369
1940
  if (onExpandedRowsChange) {
1370
1941
  onExpandedRowsChange(next);
@@ -1425,13 +1996,13 @@ function useGlideTable(options) {
1425
1996
  getCoreRowModel: getCoreRowModel(),
1426
1997
  getRowId: getRowId ? (originalRow, index) => getRowId(originalRow, index) : (_originalRow, index) => String(index)
1427
1998
  });
1428
- const rowSpanColumnKeys = useMemo2(() => {
1999
+ const rowSpanColumnKeys = useMemo3(() => {
1429
2000
  if (!enableRowSpan) return [];
1430
2001
  return collectRowSpanColumns(columns);
1431
2002
  }, [enableRowSpan, columns]);
1432
2003
  const primaryRowSpanKey = rowSpanColumnKeys[0]?.rowSpanKey;
1433
2004
  const primaryRowSpanColumnId = rowSpanColumnKeys[0]?.columnId;
1434
- const columnRowSpanMap = useMemo2(
2005
+ const columnRowSpanMap = useMemo3(
1435
2006
  () => buildColumnRowSpanMap(tableData, rowSpanColumnKeys),
1436
2007
  [tableData, rowSpanColumnKeys]
1437
2008
  );
@@ -1440,7 +2011,7 @@ function useGlideTable(options) {
1440
2011
  const rows = table.getRowModel().rows;
1441
2012
  const columnCount = table.getAllLeafColumns().length || 1;
1442
2013
  const visibleLeafColumns = table.getVisibleLeafColumns();
1443
- const columnFreezeOffsets = useMemo2(() => {
2014
+ const columnFreezeOffsets = useMemo3(() => {
1444
2015
  if (!enableColumnFreeze) return EMPTY_COLUMN_FREEZE_OFFSETS;
1445
2016
  return buildColumnFreezeOffsets(
1446
2017
  visibleLeafColumns.map((column) => ({
@@ -1460,13 +2031,46 @@ function useGlideTable(options) {
1460
2031
  const totalSize = rowVirtualizer.getTotalSize();
1461
2032
  const paddingTop = virtualRows.length > 0 ? virtualRows[0]?.start ?? 0 : 0;
1462
2033
  const paddingBottom = virtualRows.length > 0 ? totalSize - (virtualRows[virtualRows.length - 1]?.end ?? 0) : 0;
1463
- const selectedRowIndices = useMemo2(() => {
2034
+ const selectedRowIndices = useMemo3(() => {
1464
2035
  const indices = /* @__PURE__ */ new Set();
1465
2036
  for (const selectedRow of selectedRows) {
1466
2037
  indices.add(selectedRow.index);
1467
2038
  }
1468
2039
  return indices;
1469
2040
  }, [selectedRows]);
2041
+ const scrollCellIntoView = useCallback4(
2042
+ (rowIndex, colIndex, options2) => {
2043
+ const align = options2?.align ?? "nearest";
2044
+ const blockAlign = align === "center" ? "center" : "nearest";
2045
+ if (shouldVirtualize) {
2046
+ rowVirtualizer.scrollToIndex(rowIndex, {
2047
+ align: align === "nearest" ? "auto" : align
2048
+ });
2049
+ }
2050
+ const scrollElement = scrollRef.current;
2051
+ if (!scrollElement) return;
2052
+ const scrollToMatchedCell = () => {
2053
+ const cell = scrollElement.querySelector(
2054
+ `[data-row-index="${rowIndex}"][data-col-index="${colIndex}"]`
2055
+ );
2056
+ if (cell instanceof HTMLElement) {
2057
+ cell.scrollIntoView({ block: blockAlign, inline: "nearest" });
2058
+ }
2059
+ };
2060
+ if (shouldVirtualize) {
2061
+ requestAnimationFrame(scrollToMatchedCell);
2062
+ return;
2063
+ }
2064
+ scrollToMatchedCell();
2065
+ },
2066
+ [rowVirtualizer, shouldVirtualize]
2067
+ );
2068
+ const handleCellNavigate = useCallback4(
2069
+ (position) => {
2070
+ scrollCellIntoView(position.row, position.col, { align: "nearest" });
2071
+ },
2072
+ [scrollCellIntoView]
2073
+ );
1470
2074
  const {
1471
2075
  dragState,
1472
2076
  activeSelectionBounds,
@@ -1478,11 +2082,13 @@ function useGlideTable(options) {
1478
2082
  data: tableData,
1479
2083
  rows,
1480
2084
  enabled: enableCellSelection,
2085
+ columnCount: visibleLeafColumns.length,
1481
2086
  enableSubtreeCopy: resolvedEnableSubtreeCopy,
1482
2087
  enableInsertPaste: enableInsertPaste ?? true,
1483
2088
  onDataChange,
1484
2089
  onBatchChange,
1485
- onRowsPaste
2090
+ onRowsPaste,
2091
+ onCellNavigate: handleCellNavigate
1486
2092
  });
1487
2093
  const {
1488
2094
  editingCell,
@@ -1492,23 +2098,193 @@ function useGlideTable(options) {
1492
2098
  commitEdit,
1493
2099
  cancelEdit
1494
2100
  } = useCellEdit({ data: tableData, rows, onDataChange, onCellChange });
1495
- const handleCellMouseDownWithCommit = useCallback3(
1496
- (rowIndex, colIndex) => {
2101
+ const handleCellMouseDownWithCommit = useCallback4(
2102
+ (rowIndex, colIndex, options2) => {
1497
2103
  const isSameEditingCell = editingCell?.rowIndex === rowIndex && editingCell?.colIndex === colIndex;
1498
2104
  if (editingCell && !isSameEditingCell && !commitEdit()) {
1499
2105
  return;
1500
2106
  }
1501
- handleCellMouseDown(rowIndex, colIndex);
2107
+ handleCellMouseDown(rowIndex, colIndex, options2);
1502
2108
  },
1503
2109
  [commitEdit, editingCell, handleCellMouseDown]
1504
2110
  );
1505
- const clearHover = useCallback3(() => {
2111
+ const navigateToSearchResult = useCallback4(
2112
+ (item) => {
2113
+ const [colIndex, rowIndex] = item;
2114
+ handleCellMouseDownWithCommit(rowIndex, colIndex);
2115
+ scrollCellIntoView(rowIndex, colIndex, { align: "center" });
2116
+ },
2117
+ [handleCellMouseDownWithCommit, scrollCellIntoView]
2118
+ );
2119
+ const resolveSearchRowId = useCallback4(
2120
+ (row, index) => {
2121
+ if (getRowId) return getRowId(row, index);
2122
+ if (enableExpand) {
2123
+ const record = row;
2124
+ const idValue = record.id;
2125
+ if (idValue != null && String(idValue).length > 0) {
2126
+ return String(idValue);
2127
+ }
2128
+ const uniqueId = record.uniqueId;
2129
+ if (uniqueId != null && String(uniqueId).length > 0) {
2130
+ return String(uniqueId);
2131
+ }
2132
+ if (toggleField) {
2133
+ const toggleValue = record[toggleField];
2134
+ if (toggleValue != null && String(toggleValue).length > 0) {
2135
+ return String(toggleValue);
2136
+ }
2137
+ }
2138
+ }
2139
+ return String(index);
2140
+ },
2141
+ [enableExpand, getRowId, toggleField]
2142
+ );
2143
+ const searchCorpus = useMemo3(() => {
2144
+ if (!enableInlineSearch) return [];
2145
+ if (enableExpand && toggleField) {
2146
+ return buildTreeSearchCorpus(tableData, {
2147
+ toggleField,
2148
+ getRowId: resolveSearchRowId
2149
+ });
2150
+ }
2151
+ return buildFlatSearchCorpus(tableData, resolveSearchRowId);
2152
+ }, [
2153
+ enableExpand,
2154
+ enableInlineSearch,
2155
+ resolveSearchRowId,
2156
+ tableData,
2157
+ toggleField
2158
+ ]);
2159
+ const searchCorpusRef = useRef5(searchCorpus);
2160
+ searchCorpusRef.current = searchCorpus;
2161
+ const visibleRowIndexById = useMemo3(() => {
2162
+ const map = /* @__PURE__ */ new Map();
2163
+ for (const row of rows) {
2164
+ map.set(resolveSearchRowId(row.original, row.index), row.index);
2165
+ }
2166
+ return map;
2167
+ }, [resolveSearchRowId, rows]);
2168
+ const getSearchCellValue = useCallback4(
2169
+ (rowIndex, colIndex) => {
2170
+ const corpusRow = searchCorpusRef.current[rowIndex];
2171
+ const column = visibleLeafColumns[colIndex];
2172
+ if (!corpusRow || !column) return void 0;
2173
+ const visibleIndex = visibleRowIndexById.get(corpusRow.id);
2174
+ if (visibleIndex !== void 0) {
2175
+ const visibleRow = rows[visibleIndex];
2176
+ if (visibleRow) {
2177
+ return visibleRow.getValue(column.id);
2178
+ }
2179
+ }
2180
+ const columnDef = column.columnDef;
2181
+ if ("accessorFn" in columnDef && typeof columnDef.accessorFn === "function") {
2182
+ return columnDef.accessorFn(corpusRow.data, rowIndex);
2183
+ }
2184
+ if ("accessorKey" in columnDef && columnDef.accessorKey != null && columnDef.accessorKey !== "") {
2185
+ return corpusRow.data[String(columnDef.accessorKey)];
2186
+ }
2187
+ return corpusRow.data[column.id];
2188
+ },
2189
+ [rows, visibleLeafColumns, visibleRowIndexById]
2190
+ );
2191
+ const pendingSearchNavRef = useRef5(null);
2192
+ const focusSearchResult = useCallback4(
2193
+ (colIndex, visibleRowIndex) => {
2194
+ navigateToSearchResult([colIndex, visibleRowIndex]);
2195
+ },
2196
+ [navigateToSearchResult]
2197
+ );
2198
+ const navigateToCorpusSearchResult = useCallback4(
2199
+ (item) => {
2200
+ const [colIndex, corpusRowIndex] = item;
2201
+ const corpusRow = searchCorpusRef.current[corpusRowIndex];
2202
+ if (!corpusRow) return;
2203
+ const missingKeys = collectAncestorKeysToExpand(corpusRow, expandedRows);
2204
+ if (missingKeys.length > 0) {
2205
+ pendingSearchNavRef.current = {
2206
+ colIndex,
2207
+ rowId: corpusRow.id
2208
+ };
2209
+ const next = new Set(expandedRows);
2210
+ for (const key of corpusRow.ancestorToggleKeys) {
2211
+ next.add(key);
2212
+ }
2213
+ handleExpandedRowsChange(next);
2214
+ return;
2215
+ }
2216
+ const visibleItem = mapSearchResultToVisibleItem(
2217
+ item,
2218
+ searchCorpusRef.current,
2219
+ visibleRowIndexById
2220
+ );
2221
+ if (!visibleItem) return;
2222
+ focusSearchResult(visibleItem[0], visibleItem[1]);
2223
+ },
2224
+ [
2225
+ expandedRows,
2226
+ focusSearchResult,
2227
+ handleExpandedRowsChange,
2228
+ visibleRowIndexById
2229
+ ]
2230
+ );
2231
+ useEffect5(() => {
2232
+ const pending = pendingSearchNavRef.current;
2233
+ if (!pending) return;
2234
+ const visibleRowIndex = visibleRowIndexById.get(pending.rowId);
2235
+ if (visibleRowIndex === void 0) return;
2236
+ pendingSearchNavRef.current = null;
2237
+ focusSearchResult(pending.colIndex, visibleRowIndex);
2238
+ }, [focusSearchResult, rows, visibleRowIndexById]);
2239
+ const initialSearchStartRow = virtualRows[0]?.index ?? 0;
2240
+ const inlineSearch = useInlineSearch({
2241
+ enabled: enableInlineSearch,
2242
+ rowCount: searchCorpus.length,
2243
+ columnCount: visibleLeafColumns.length,
2244
+ getCellValue: getSearchCellValue,
2245
+ initialStartRow: initialSearchStartRow,
2246
+ showSearch,
2247
+ searchValue,
2248
+ searchResults,
2249
+ onSearchValueChange,
2250
+ onSearchClose,
2251
+ onSearchResultsChanged,
2252
+ onNavigateToResult: navigateToCorpusSearchResult,
2253
+ rootRef
2254
+ });
2255
+ const visibleSearchMatchKeys = useMemo3(() => {
2256
+ if (!enableInlineSearch) return EMPTY_SEARCH_MATCH_KEYS;
2257
+ return mapSearchResultsToVisibleKeys(
2258
+ inlineSearch.searchResults,
2259
+ searchCorpus,
2260
+ visibleRowIndexById
2261
+ );
2262
+ }, [
2263
+ enableInlineSearch,
2264
+ inlineSearch.searchResults,
2265
+ searchCorpus,
2266
+ visibleRowIndexById
2267
+ ]);
2268
+ const visibleActiveMatch = useMemo3(() => {
2269
+ if (!enableInlineSearch || !inlineSearch.activeMatch) return null;
2270
+ return mapSearchResultToVisibleItem(
2271
+ inlineSearch.activeMatch,
2272
+ searchCorpus,
2273
+ visibleRowIndexById
2274
+ );
2275
+ }, [
2276
+ enableInlineSearch,
2277
+ inlineSearch.activeMatch,
2278
+ searchCorpus,
2279
+ visibleRowIndexById
2280
+ ]);
2281
+ const clearHover = useCallback4(() => {
1506
2282
  setHoveredRowIndex(null);
1507
2283
  }, []);
1508
- const handleRowHover = useCallback3((rowIndex, _rowData) => {
2284
+ const handleRowHover = useCallback4((rowIndex, _rowData) => {
1509
2285
  setHoveredRowIndex(rowIndex);
1510
2286
  }, []);
1511
- const handleToggleSelect = useCallback3(
2287
+ const handleToggleSelect = useCallback4(
1512
2288
  (row) => {
1513
2289
  if (!row.getCanSelect()) return;
1514
2290
  if (preserveRowSelection && row.getIsSelected()) {
@@ -1518,14 +2294,14 @@ function useGlideTable(options) {
1518
2294
  },
1519
2295
  [preserveRowSelection]
1520
2296
  );
1521
- const handleToggleExpand = useCallback3(
2297
+ const handleToggleExpand = useCallback4(
1522
2298
  (rowKey) => {
1523
2299
  if (preventExpand) return;
1524
2300
  handleExpandedRowsChange(toggleExpandedRowId(rowKey, expandedRows));
1525
2301
  },
1526
2302
  [preventExpand, handleExpandedRowsChange, expandedRows]
1527
2303
  );
1528
- const rowContextValue = useMemo2(() => {
2304
+ const rowContextValue = useMemo3(() => {
1529
2305
  return {
1530
2306
  rowSpan: {
1531
2307
  enableRowSpan,
@@ -1573,6 +2349,11 @@ function useGlideTable(options) {
1573
2349
  columnFreeze: {
1574
2350
  enableColumnFreeze,
1575
2351
  offsets: columnFreezeOffsets
2352
+ },
2353
+ inlineSearch: {
2354
+ enabled: enableInlineSearch,
2355
+ matchKeys: visibleSearchMatchKeys,
2356
+ activeMatch: visibleActiveMatch
1576
2357
  }
1577
2358
  };
1578
2359
  }, [
@@ -1608,14 +2389,17 @@ function useGlideTable(options) {
1608
2389
  labels.collapseRow,
1609
2390
  enableColumnResize,
1610
2391
  enableColumnFreeze,
1611
- columnFreezeOffsets
2392
+ columnFreezeOffsets,
2393
+ enableInlineSearch,
2394
+ visibleSearchMatchKeys,
2395
+ visibleActiveMatch
1612
2396
  ]);
1613
- const copySelectionRef = useRef4(copySelection);
1614
- useEffect4(() => {
2397
+ const copySelectionRef = useRef5(copySelection);
2398
+ useEffect5(() => {
1615
2399
  copySelectionRef.current = copySelection;
1616
2400
  }, [copySelection]);
1617
- const stableCopySelection = useCallback3((options2) => copySelectionRef.current(options2), []);
1618
- useEffect4(() => {
2401
+ const stableCopySelection = useCallback4((options2) => copySelectionRef.current(options2), []);
2402
+ useEffect5(() => {
1619
2403
  onCopyActionsReady?.({ copySelection: stableCopySelection });
1620
2404
  }, [onCopyActionsReady, stableCopySelection]);
1621
2405
  return {
@@ -1631,8 +2415,10 @@ function useGlideTable(options) {
1631
2415
  enableCellSelection,
1632
2416
  enableColumnResize,
1633
2417
  enableColumnFreeze,
2418
+ enableInlineSearch,
1634
2419
  shouldVirtualize,
1635
2420
  scrollRef,
2421
+ rootRef,
1636
2422
  rowVirtualizer,
1637
2423
  virtualRows,
1638
2424
  paddingTop,
@@ -1640,7 +2426,21 @@ function useGlideTable(options) {
1640
2426
  rowContextValue,
1641
2427
  handleToggleSelect,
1642
2428
  clearHover,
1643
- copySelection: stableCopySelection
2429
+ copySelection: stableCopySelection,
2430
+ inlineSearch: {
2431
+ showSearch: inlineSearch.showSearch,
2432
+ searchValue: inlineSearch.searchValue,
2433
+ searchStatus: inlineSearch.searchStatus,
2434
+ searchInputRef: inlineSearch.searchInputRef,
2435
+ searchInputId: inlineSearch.searchInputId,
2436
+ canClose: inlineSearch.canClose,
2437
+ searchRowCount: searchCorpus.length,
2438
+ setSearchValue: inlineSearch.setSearchValue,
2439
+ closeSearch: inlineSearch.closeSearch,
2440
+ goToNext: inlineSearch.goToNext,
2441
+ goToPrevious: inlineSearch.goToPrevious,
2442
+ openSearch: inlineSearch.openSearch
2443
+ }
1644
2444
  };
1645
2445
  }
1646
2446
 
@@ -1659,11 +2459,11 @@ function getColumnSizeStyle(size, options) {
1659
2459
 
1660
2460
  // src/components/ui/table/components/DataTable/DataTable.tsx
1661
2461
  import { flexRender as flexRender2 } from "@tanstack/react-table";
1662
- import { useMemo as useMemo3 } from "react";
2462
+ import { useMemo as useMemo4 } from "react";
1663
2463
 
1664
2464
  // src/components/ui/table/components/DataTable/DataTableRow.tsx
1665
2465
  import { flexRender } from "@tanstack/react-table";
1666
- import { useEffect as useEffect5, useRef as useRef5 } from "react";
2466
+ import { useEffect as useEffect6, useRef as useRef6 } from "react";
1667
2467
 
1668
2468
  // src/components/ui/table/DataTableContext.tsx
1669
2469
  import { createContext, use } from "react";
@@ -1871,10 +2671,16 @@ function DataTableRow({
1871
2671
  cellEdit,
1872
2672
  expand,
1873
2673
  columnResize,
1874
- columnFreeze
2674
+ columnFreeze,
2675
+ inlineSearch
1875
2676
  } = useDataTableRowContext();
1876
2677
  const { enableColumnResize } = columnResize;
1877
2678
  const { enableColumnFreeze, offsets: freezeOffsets } = columnFreeze;
2679
+ const {
2680
+ enabled: enableInlineSearch,
2681
+ matchKeys: searchMatchKeys,
2682
+ activeMatch
2683
+ } = inlineSearch;
1878
2684
  const {
1879
2685
  enableRowSpan,
1880
2686
  primaryRowSpanColumnId,
@@ -1968,9 +2774,9 @@ function DataTableRow({
1968
2774
  const expandKey = toggleField && rowData[toggleField] !== null && rowData[toggleField] !== void 0 ? String(rowData[toggleField]) : null;
1969
2775
  const isExpanded = expandKey !== null && Boolean(expandedRows?.has(expandKey));
1970
2776
  const rowLevel = typeof rowData.level === "number" ? rowData.level : 0;
1971
- const editInputRef = useRef5(null);
2777
+ const editInputRef = useRef6(null);
1972
2778
  const isRowEditing = editingCell?.rowIndex === rowIndex;
1973
- useEffect5(() => {
2779
+ useEffect6(() => {
1974
2780
  if (!isRowEditing) return;
1975
2781
  editInputRef.current?.focus();
1976
2782
  editInputRef.current?.select();
@@ -2066,9 +2872,14 @@ function DataTableRow({
2066
2872
  ...freezeStyle,
2067
2873
  ...selectionEdgeStyle
2068
2874
  };
2875
+ const searchMatchKey = buildSearchMatchKey(cellIndex, rowIndex);
2876
+ const isSearchMatch = enableInlineSearch && searchMatchKeys.has(searchMatchKey);
2877
+ const isSearchActive = isSearchMatch && activeMatch !== null && activeMatch[0] === cellIndex && activeMatch[1] === rowIndex;
2069
2878
  return /* @__PURE__ */ jsxs2(
2070
2879
  "td",
2071
2880
  {
2881
+ "data-row-index": rowIndex,
2882
+ "data-col-index": cellIndex,
2072
2883
  rowSpan: rowSpanInfo && rowSpanInfo.rowSpan > 1 ? rowSpanInfo.rowSpan : void 0,
2073
2884
  "data-merged": isMerged && cellIndex > 0 ? "" : void 0,
2074
2885
  "data-merged-edge-right": showMergedRightEdge ? "" : void 0,
@@ -2078,6 +2889,8 @@ function DataTableRow({
2078
2889
  "data-group-hovered": enableRowSpan && showCellHover && !showCellSelected ? "" : void 0,
2079
2890
  "data-selection-fill": isCellDragSelected ? "" : void 0,
2080
2891
  "data-selection-edges": hasSelectionEdges ? "" : void 0,
2892
+ "data-search-match": isSearchMatch ? "" : void 0,
2893
+ "data-search-active": isSearchActive ? "" : void 0,
2081
2894
  "data-editable": editable ? "" : void 0,
2082
2895
  "data-editing": isEditing ? "" : void 0,
2083
2896
  "data-frozen": freezeOffset?.side,
@@ -2092,7 +2905,8 @@ function DataTableRow({
2092
2905
  event.preventDefault();
2093
2906
  onCellMouseDown(
2094
2907
  resolveCellRowIndex(event.clientY, event.currentTarget),
2095
- cellIndex
2908
+ cellIndex,
2909
+ { shiftKey: event.shiftKey }
2096
2910
  );
2097
2911
  },
2098
2912
  onMouseEnter: (event) => {
@@ -2129,6 +2943,8 @@ function DataTableRow({
2129
2943
  enableRowSpan && showCellHover && !showCellSelected && "is-group-hovered",
2130
2944
  isCellDragSelected && CELL_SELECTION_FILL_CLASS,
2131
2945
  hasSelectionEdges && CELL_SELECTION_EDGES_CLASS,
2946
+ isSearchMatch && "is-search-match",
2947
+ isSearchActive && "is-search-active",
2132
2948
  editable && "is-editable",
2133
2949
  classNames?.cell
2134
2950
  ),
@@ -2263,8 +3079,169 @@ function DataTableRow({
2263
3079
  );
2264
3080
  }
2265
3081
 
3082
+ // src/components/ui/table/components/DataTable/DataTableSearch.tsx
3083
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
3084
+ function SearchCloseIcon({ className }) {
3085
+ return /* @__PURE__ */ jsxs3(
3086
+ "svg",
3087
+ {
3088
+ className,
3089
+ "aria-hidden": true,
3090
+ width: "16",
3091
+ height: "16",
3092
+ viewBox: "0 0 24 24",
3093
+ fill: "none",
3094
+ stroke: "currentColor",
3095
+ strokeWidth: "2",
3096
+ strokeLinecap: "round",
3097
+ strokeLinejoin: "round",
3098
+ children: [
3099
+ /* @__PURE__ */ jsx4("path", { d: "M18 6 6 18" }),
3100
+ /* @__PURE__ */ jsx4("path", { d: "m6 6 12 12" })
3101
+ ]
3102
+ }
3103
+ );
3104
+ }
3105
+ function DataTableSearch({
3106
+ showSearch,
3107
+ searchValue,
3108
+ searchStatus,
3109
+ searchInputId,
3110
+ searchInputRef,
3111
+ canClose,
3112
+ placeholder,
3113
+ resultHint,
3114
+ previousLabel,
3115
+ nextLabel,
3116
+ closeLabel,
3117
+ rowsTotal,
3118
+ classNames,
3119
+ onSearchValueChange,
3120
+ onClose,
3121
+ onNext,
3122
+ onPrevious
3123
+ }) {
3124
+ if (!showSearch) return null;
3125
+ const resultString = searchStatus ? formatSearchResultLabel(searchStatus) : resultHint;
3126
+ const progress = rowsTotal > 0 ? Math.floor((searchStatus?.rowsSearched ?? 0) / rowsTotal * 100) : 0;
3127
+ const handleKeyDown = (event) => {
3128
+ if ((event.ctrlKey || event.metaKey) && event.code === "KeyF" || event.key === "Escape") {
3129
+ event.preventDefault();
3130
+ event.stopPropagation();
3131
+ if (canClose) {
3132
+ onClose();
3133
+ }
3134
+ return;
3135
+ }
3136
+ if (event.key === "ArrowDown" || event.key === "Enter" && !event.shiftKey) {
3137
+ event.preventDefault();
3138
+ onNext();
3139
+ return;
3140
+ }
3141
+ if (event.key === "ArrowUp" || event.key === "Enter" && event.shiftKey) {
3142
+ event.preventDefault();
3143
+ onPrevious();
3144
+ }
3145
+ };
3146
+ return /* @__PURE__ */ jsxs3(
3147
+ "div",
3148
+ {
3149
+ className: cn("data-table-search", classNames?.search),
3150
+ role: "search",
3151
+ onMouseDown: (event) => event.stopPropagation(),
3152
+ children: [
3153
+ /* @__PURE__ */ jsxs3("div", { className: "data-table-search-row", children: [
3154
+ /* @__PURE__ */ jsx4(
3155
+ "input",
3156
+ {
3157
+ ref: searchInputRef,
3158
+ id: searchInputId,
3159
+ type: "search",
3160
+ value: searchValue,
3161
+ placeholder,
3162
+ autoComplete: "off",
3163
+ spellCheck: false,
3164
+ "aria-label": placeholder,
3165
+ className: cn("data-table-search-input", classNames?.searchInput),
3166
+ onChange: (event) => onSearchValueChange(event.target.value),
3167
+ onKeyDown: handleKeyDown
3168
+ }
3169
+ ),
3170
+ /* @__PURE__ */ jsx4(
3171
+ "button",
3172
+ {
3173
+ type: "button",
3174
+ "aria-label": previousLabel,
3175
+ className: cn("data-table-search-button", classNames?.searchButton),
3176
+ onClick: (event) => {
3177
+ event.stopPropagation();
3178
+ onPrevious();
3179
+ },
3180
+ children: /* @__PURE__ */ jsx4(ChevronUp, { className: "data-table-search-icon" })
3181
+ }
3182
+ ),
3183
+ /* @__PURE__ */ jsx4(
3184
+ "button",
3185
+ {
3186
+ type: "button",
3187
+ "aria-label": nextLabel,
3188
+ className: cn("data-table-search-button", classNames?.searchButton),
3189
+ onClick: (event) => {
3190
+ event.stopPropagation();
3191
+ onNext();
3192
+ },
3193
+ children: /* @__PURE__ */ jsx4(ChevronDown, { className: "data-table-search-icon" })
3194
+ }
3195
+ ),
3196
+ canClose ? /* @__PURE__ */ jsx4(
3197
+ "button",
3198
+ {
3199
+ type: "button",
3200
+ "aria-label": closeLabel,
3201
+ className: cn("data-table-search-button", classNames?.searchButton),
3202
+ onClick: (event) => {
3203
+ event.stopPropagation();
3204
+ onClose();
3205
+ },
3206
+ children: /* @__PURE__ */ jsx4(SearchCloseIcon, { className: "data-table-search-icon" })
3207
+ }
3208
+ ) : null
3209
+ ] }),
3210
+ /* @__PURE__ */ jsx4(
3211
+ "div",
3212
+ {
3213
+ className: cn("data-table-search-status", classNames?.searchStatus),
3214
+ "aria-live": "polite",
3215
+ children: resultString
3216
+ }
3217
+ ),
3218
+ searchStatus !== void 0 ? /* @__PURE__ */ jsx4(
3219
+ "div",
3220
+ {
3221
+ className: cn(
3222
+ "data-table-search-progress",
3223
+ classNames?.searchProgress
3224
+ ),
3225
+ role: "progressbar",
3226
+ "aria-valuemin": 0,
3227
+ "aria-valuemax": 100,
3228
+ "aria-valuenow": progress,
3229
+ children: /* @__PURE__ */ jsx4(
3230
+ "div",
3231
+ {
3232
+ className: "data-table-search-progress-bar",
3233
+ style: { width: `${progress}%` }
3234
+ }
3235
+ )
3236
+ }
3237
+ ) : null
3238
+ ]
3239
+ }
3240
+ );
3241
+ }
3242
+
2266
3243
  // src/components/ui/table/components/DataTable/DataTableToolbar.tsx
2267
- import { Fragment, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
3244
+ import { Fragment, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
2268
3245
  function DataTableToolbar({
2269
3246
  filteredCount,
2270
3247
  totalCount,
@@ -2282,39 +3259,71 @@ function DataTableToolbar({
2282
3259
  const selectionContent = selectionLabel?.(selectedCount) ?? null;
2283
3260
  const hasSelectionContent = selectionContent !== null && selectionContent !== false && selectionContent !== void 0 && typeof selectionContent !== "boolean";
2284
3261
  if (!hasLeftContent && !hasToolbar && !hasSelectionContent) return null;
2285
- return /* @__PURE__ */ jsxs3("div", { className: cn("DataTableToolbarJSX", classNames?.toolbar, className), children: [
2286
- /* @__PURE__ */ jsxs3("div", { className: cn("toolbar-left", classNames?.toolbarLeft), children: [
2287
- hasCount && /* @__PURE__ */ jsx4("span", { className: cn("toolbar-count", classNames?.toolbarCount), children: displayFiltered !== void 0 && totalCount !== void 0 ? /* @__PURE__ */ jsxs3(Fragment, { children: [
2288
- /* @__PURE__ */ jsx4("span", { className: "toolbar-count-primary", children: displayFiltered }),
2289
- /* @__PURE__ */ jsxs3("span", { className: "toolbar-count-placeholder", children: [
3262
+ return /* @__PURE__ */ jsxs4("div", { className: cn("DataTableToolbarJSX", classNames?.toolbar, className), children: [
3263
+ /* @__PURE__ */ jsxs4("div", { className: cn("toolbar-left", classNames?.toolbarLeft), children: [
3264
+ hasCount && /* @__PURE__ */ jsx5("span", { className: cn("toolbar-count", classNames?.toolbarCount), children: displayFiltered !== void 0 && totalCount !== void 0 ? /* @__PURE__ */ jsxs4(Fragment, { children: [
3265
+ /* @__PURE__ */ jsx5("span", { className: "toolbar-count-primary", children: displayFiltered }),
3266
+ /* @__PURE__ */ jsxs4("span", { className: "toolbar-count-placeholder", children: [
2290
3267
  " / ",
2291
3268
  totalCount
2292
3269
  ] })
2293
- ] }) : /* @__PURE__ */ jsx4("span", { className: "toolbar-count-primary", children: displayFiltered ?? totalCount }) }),
3270
+ ] }) : /* @__PURE__ */ jsx5("span", { className: "toolbar-count-primary", children: displayFiltered ?? totalCount }) }),
2294
3271
  summary
2295
3272
  ] }),
2296
- /* @__PURE__ */ jsxs3("div", { className: cn("toolbar-right", classNames?.toolbarRight), children: [
2297
- hasSelectionContent && (typeof selectionContent === "string" || typeof selectionContent === "number" ? /* @__PURE__ */ jsx4("span", { className: cn("toolbar-selection", classNames?.toolbarSelection), children: selectionContent }) : selectionContent),
2298
- hasToolbar && /* @__PURE__ */ jsx4("div", { className: cn("toolbar-actions", classNames?.toolbarActions), children: toolbar })
3273
+ /* @__PURE__ */ jsxs4("div", { className: cn("toolbar-right", classNames?.toolbarRight), children: [
3274
+ hasSelectionContent && (typeof selectionContent === "string" || typeof selectionContent === "number" ? /* @__PURE__ */ jsx5("span", { className: cn("toolbar-selection", classNames?.toolbarSelection), children: selectionContent }) : selectionContent),
3275
+ hasToolbar && /* @__PURE__ */ jsx5("div", { className: cn("toolbar-actions", classNames?.toolbarActions), children: toolbar })
2299
3276
  ] })
2300
3277
  ] });
2301
3278
  }
2302
3279
 
3280
+ // src/components/ui/table/features/column-groups/mergeHeaderGroups.ts
3281
+ function getMergedHeaderGroups(headerGroups) {
3282
+ if (headerGroups.length <= 1) {
3283
+ return headerGroups.map((group) => ({
3284
+ ...group,
3285
+ headers: group.headers.map((header) => ({
3286
+ ...header,
3287
+ mergedRowSpan: 1
3288
+ }))
3289
+ }));
3290
+ }
3291
+ const seenColumnIds = /* @__PURE__ */ new Set();
3292
+ const fullDepth = headerGroups.length;
3293
+ return headerGroups.map((group, depth) => ({
3294
+ ...group,
3295
+ headers: group.headers.filter((header) => !seenColumnIds.has(header.column.id)).map((header) => {
3296
+ seenColumnIds.add(header.column.id);
3297
+ if (header.isPlaceholder) {
3298
+ return {
3299
+ ...header,
3300
+ isPlaceholder: false,
3301
+ mergedRowSpan: fullDepth - depth
3302
+ };
3303
+ }
3304
+ return {
3305
+ ...header,
3306
+ mergedRowSpan: 1
3307
+ };
3308
+ })
3309
+ }));
3310
+ }
3311
+
2303
3312
  // src/components/ui/table/components/DataTable/DataTable.tsx
2304
- import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
3313
+ import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
2305
3314
  function DefaultScroll({
2306
3315
  scrollRef,
2307
3316
  children,
2308
3317
  className
2309
3318
  }) {
2310
- return /* @__PURE__ */ jsx5("div", { ref: scrollRef, className: cn("data-table-scroll", className), children });
3319
+ return /* @__PURE__ */ jsx6("div", { ref: scrollRef, className: cn("data-table-scroll", className), children });
2311
3320
  }
2312
3321
  function DefaultPending({
2313
3322
  loadingText,
2314
3323
  className,
2315
3324
  classNames
2316
3325
  }) {
2317
- return /* @__PURE__ */ jsx5(
3326
+ return /* @__PURE__ */ jsx6(
2318
3327
  "div",
2319
3328
  {
2320
3329
  className: cn(
@@ -2324,7 +3333,7 @@ function DefaultPending({
2324
3333
  classNames?.pending,
2325
3334
  className
2326
3335
  ),
2327
- children: /* @__PURE__ */ jsx5("span", { className: cn("data-table-loading-text", classNames?.loadingText), children: loadingText })
3336
+ children: /* @__PURE__ */ jsx6("span", { className: cn("data-table-loading-text", classNames?.loadingText), children: loadingText })
2328
3337
  }
2329
3338
  );
2330
3339
  }
@@ -2333,7 +3342,7 @@ function DefaultEmpty({
2333
3342
  columnCount,
2334
3343
  classNames
2335
3344
  }) {
2336
- return /* @__PURE__ */ jsx5("tr", { children: /* @__PURE__ */ jsx5(
3345
+ return /* @__PURE__ */ jsx6("tr", { children: /* @__PURE__ */ jsx6(
2337
3346
  "td",
2338
3347
  {
2339
3348
  colSpan: columnCount,
@@ -2366,15 +3375,18 @@ function DataTable({
2366
3375
  enableCellSelection,
2367
3376
  enableColumnResize,
2368
3377
  enableColumnFreeze,
3378
+ enableInlineSearch,
2369
3379
  shouldVirtualize,
2370
3380
  scrollRef,
3381
+ rootRef,
2371
3382
  rowVirtualizer,
2372
3383
  virtualRows,
2373
3384
  paddingTop,
2374
3385
  paddingBottom,
2375
3386
  rowContextValue,
2376
3387
  handleToggleSelect,
2377
- clearHover
3388
+ clearHover,
3389
+ inlineSearch
2378
3390
  } = useGlideTable(glideOptions);
2379
3391
  const ToolbarSlot = slots?.Toolbar ?? DataTableToolbar;
2380
3392
  const ScrollSlot = slots?.Scroll ?? DefaultScroll;
@@ -2382,12 +3394,13 @@ function DataTable({
2382
3394
  const PendingSlot = slots?.Pending ?? DefaultPending;
2383
3395
  const EmptySlot = slots?.Empty ?? DefaultEmpty;
2384
3396
  const freezeOffsets = rowContextValue.columnFreeze.offsets;
2385
- const contextValue = useMemo3(
3397
+ const headerGroups = getMergedHeaderGroups(table.getHeaderGroups());
3398
+ const contextValue = useMemo4(
2386
3399
  () => ({ ...rowContextValue, classNames }),
2387
3400
  [rowContextValue, classNames]
2388
3401
  );
2389
3402
  if (isPending) {
2390
- return /* @__PURE__ */ jsx5(
3403
+ return /* @__PURE__ */ jsx6(
2391
3404
  PendingSlot,
2392
3405
  {
2393
3406
  loadingText,
@@ -2396,19 +3409,21 @@ function DataTable({
2396
3409
  }
2397
3410
  );
2398
3411
  }
2399
- return /* @__PURE__ */ jsxs4(
3412
+ return /* @__PURE__ */ jsxs5(
2400
3413
  "div",
2401
3414
  {
3415
+ ref: rootRef,
2402
3416
  className: cn(
2403
3417
  "DataTableJSX",
2404
3418
  !enableCellSelection && "DataTableJSX--no-cell-selection",
2405
3419
  enableColumnResize && "DataTableJSX--column-resize",
2406
3420
  enableColumnFreeze && "DataTableJSX--column-freeze",
3421
+ enableInlineSearch && "DataTableJSX--inline-search",
2407
3422
  classNames?.root,
2408
3423
  className
2409
3424
  ),
2410
3425
  children: [
2411
- /* @__PURE__ */ jsx5(
3426
+ /* @__PURE__ */ jsx6(
2412
3427
  ToolbarSlot,
2413
3428
  {
2414
3429
  filteredCount: filteredCount ?? tableData.length,
@@ -2420,14 +3435,36 @@ function DataTable({
2420
3435
  classNames
2421
3436
  }
2422
3437
  ),
2423
- /* @__PURE__ */ jsx5(ScrollSlot, { scrollRef, className: classNames?.scroll, children: /* @__PURE__ */ jsxs4(
3438
+ enableInlineSearch ? /* @__PURE__ */ jsx6(
3439
+ DataTableSearch,
3440
+ {
3441
+ showSearch: inlineSearch.showSearch,
3442
+ searchValue: inlineSearch.searchValue,
3443
+ searchStatus: inlineSearch.searchStatus,
3444
+ searchInputId: inlineSearch.searchInputId,
3445
+ searchInputRef: inlineSearch.searchInputRef,
3446
+ canClose: inlineSearch.canClose,
3447
+ placeholder: labels.searchPlaceholder,
3448
+ resultHint: labels.searchResultHint,
3449
+ previousLabel: labels.searchPrevious,
3450
+ nextLabel: labels.searchNext,
3451
+ closeLabel: labels.searchClose,
3452
+ rowsTotal: inlineSearch.searchRowCount,
3453
+ classNames,
3454
+ onSearchValueChange: inlineSearch.setSearchValue,
3455
+ onClose: inlineSearch.closeSearch,
3456
+ onNext: inlineSearch.goToNext,
3457
+ onPrevious: inlineSearch.goToPrevious
3458
+ }
3459
+ ) : null,
3460
+ /* @__PURE__ */ jsx6(ScrollSlot, { scrollRef, className: classNames?.scroll, children: /* @__PURE__ */ jsxs5(
2424
3461
  "table",
2425
3462
  {
2426
3463
  className: cn("data-table", classNames?.table),
2427
3464
  style: enableColumnResize ? { width: table.getTotalSize() } : void 0,
2428
3465
  onDragStart: enableCellSelection ? (event) => event.preventDefault() : void 0,
2429
3466
  children: [
2430
- /* @__PURE__ */ jsx5("thead", { className: cn("data-table-head", classNames?.head), children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ jsx5(
3467
+ /* @__PURE__ */ jsx6("thead", { className: cn("data-table-head", classNames?.head), children: headerGroups.map((headerGroup) => /* @__PURE__ */ jsx6(
2431
3468
  "tr",
2432
3469
  {
2433
3470
  className: cn("data-table-head-row", classNames?.headRow),
@@ -2441,15 +3478,18 @@ function DataTable({
2441
3478
  });
2442
3479
  const freezeOffset = enableColumnFreeze ? freezeOffsets.get(header.column.id) : void 0;
2443
3480
  const freezeStyle = getColumnFreezeStyle(freezeOffset, {
2444
- isHeader: true
3481
+ isHeader: true,
3482
+ headerTop: header.depth * DATA_TABLE_HEADER_ROW_HEIGHT
2445
3483
  });
2446
3484
  const headerStyle = {
2447
3485
  ...sizeStyle,
2448
3486
  ...freezeStyle
2449
3487
  };
2450
- return /* @__PURE__ */ jsxs4(
3488
+ return /* @__PURE__ */ jsxs5(
2451
3489
  "th",
2452
3490
  {
3491
+ colSpan: header.colSpan,
3492
+ rowSpan: header.mergedRowSpan,
2453
3493
  "data-resizing": header.column.getIsResizing() ? "" : void 0,
2454
3494
  "data-frozen": freezeOffset?.side,
2455
3495
  "data-freeze-edge": getColumnFreezeEdgeAttr(freezeOffset),
@@ -2463,7 +3503,7 @@ function DataTable({
2463
3503
  ),
2464
3504
  children: [
2465
3505
  header.isPlaceholder ? null : flexRender2(header.column.columnDef.header, header.getContext()),
2466
- canResize ? /* @__PURE__ */ jsx5(
3506
+ canResize ? /* @__PURE__ */ jsx6(
2467
3507
  "div",
2468
3508
  {
2469
3509
  role: "separator",
@@ -2489,20 +3529,20 @@ function DataTable({
2489
3529
  },
2490
3530
  headerGroup.id
2491
3531
  )) }),
2492
- /* @__PURE__ */ jsx5(DataTableContextProvider, { value: contextValue, children: /* @__PURE__ */ jsx5(
3532
+ /* @__PURE__ */ jsx6(DataTableContextProvider, { value: contextValue, children: /* @__PURE__ */ jsx6(
2493
3533
  "tbody",
2494
3534
  {
2495
3535
  onMouseLeave: clearHover,
2496
3536
  className: cn("data-table-body", classNames?.body),
2497
- children: rows.length === 0 ? /* @__PURE__ */ jsx5(
3537
+ children: rows.length === 0 ? /* @__PURE__ */ jsx6(
2498
3538
  EmptySlot,
2499
3539
  {
2500
3540
  emptyText,
2501
3541
  columnCount,
2502
3542
  classNames
2503
3543
  }
2504
- ) : shouldVirtualize ? /* @__PURE__ */ jsxs4(Fragment2, { children: [
2505
- paddingTop > 0 && /* @__PURE__ */ jsx5(
3544
+ ) : shouldVirtualize ? /* @__PURE__ */ jsxs5(Fragment2, { children: [
3545
+ paddingTop > 0 && /* @__PURE__ */ jsx6(
2506
3546
  "tr",
2507
3547
  {
2508
3548
  "aria-hidden": true,
@@ -2510,7 +3550,7 @@ function DataTable({
2510
3550
  "data-table-virtual-spacer",
2511
3551
  classNames?.virtualSpacer
2512
3552
  ),
2513
- children: /* @__PURE__ */ jsx5(
3553
+ children: /* @__PURE__ */ jsx6(
2514
3554
  "td",
2515
3555
  {
2516
3556
  colSpan: columnCount,
@@ -2526,7 +3566,7 @@ function DataTable({
2526
3566
  virtualRows.map((virtualRow) => {
2527
3567
  const row = rows[virtualRow.index];
2528
3568
  if (!row) return null;
2529
- return /* @__PURE__ */ jsx5(
3569
+ return /* @__PURE__ */ jsx6(
2530
3570
  RowSlot,
2531
3571
  {
2532
3572
  row,
@@ -2537,7 +3577,7 @@ function DataTable({
2537
3577
  row.id
2538
3578
  );
2539
3579
  }),
2540
- paddingBottom > 0 && /* @__PURE__ */ jsx5(
3580
+ paddingBottom > 0 && /* @__PURE__ */ jsx6(
2541
3581
  "tr",
2542
3582
  {
2543
3583
  "aria-hidden": true,
@@ -2545,7 +3585,7 @@ function DataTable({
2545
3585
  "data-table-virtual-spacer",
2546
3586
  classNames?.virtualSpacer
2547
3587
  ),
2548
- children: /* @__PURE__ */ jsx5(
3588
+ children: /* @__PURE__ */ jsx6(
2549
3589
  "td",
2550
3590
  {
2551
3591
  colSpan: columnCount,
@@ -2558,7 +3598,7 @@ function DataTable({
2558
3598
  )
2559
3599
  }
2560
3600
  )
2561
- ] }) : rows.map((row) => /* @__PURE__ */ jsx5(
3601
+ ] }) : rows.map((row) => /* @__PURE__ */ jsx6(
2562
3602
  RowSlot,
2563
3603
  {
2564
3604
  row,
@@ -2577,10 +3617,10 @@ function DataTable({
2577
3617
  }
2578
3618
 
2579
3619
  // src/components/ui/table/components/Table/Table.tsx
2580
- import { useCallback as useCallback4, useMemo as useMemo4, useState as useState4 } from "react";
3620
+ import { useCallback as useCallback5, useMemo as useMemo5, useState as useState5 } from "react";
2581
3621
 
2582
3622
  // src/components/ui/table/components/Table/buildColumnDef.tsx
2583
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
3623
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
2584
3624
  function SortableHeader({
2585
3625
  label,
2586
3626
  field,
@@ -2589,15 +3629,15 @@ function SortableHeader({
2589
3629
  }) {
2590
3630
  const isActive = sort?.field === field;
2591
3631
  const Icon = isActive ? sort.direction === "asc" ? ArrowUp : ArrowDown : ArrowUpDown;
2592
- return /* @__PURE__ */ jsxs5(
3632
+ return /* @__PURE__ */ jsxs6(
2593
3633
  "button",
2594
3634
  {
2595
3635
  type: "button",
2596
3636
  className: cn("SortableHeaderJSX", isActive ? "is-active" : "is-inactive"),
2597
3637
  onClick: () => onSort(field),
2598
3638
  children: [
2599
- /* @__PURE__ */ jsx6("span", { children: label }),
2600
- /* @__PURE__ */ jsx6(Icon, { className: "sortable-header-icon" })
3639
+ /* @__PURE__ */ jsx7("span", { children: label }),
3640
+ /* @__PURE__ */ jsx7(Icon, { className: "sortable-header-icon" })
2601
3641
  ]
2602
3642
  }
2603
3643
  );
@@ -2630,7 +3670,7 @@ function buildColumnDef(props, sort, onSort) {
2630
3670
  ...minWidth != null ? { minSize: minWidth } : {},
2631
3671
  ...maxWidth != null ? { maxSize: maxWidth } : {},
2632
3672
  ...resizable === false ? { enableResizing: false } : {},
2633
- header: sortable ? () => /* @__PURE__ */ jsx6(SortableHeader, { label: children, field, sort, onSort }) : (
3673
+ header: sortable ? () => /* @__PURE__ */ jsx7(SortableHeader, { label: children, field, sort, onSort }) : (
2634
3674
  // eslint-disable-next-line @typescript-eslint/promise-function-async
2635
3675
  () => children
2636
3676
  ),
@@ -2655,6 +3695,46 @@ function buildColumnDef(props, sort, onSort) {
2655
3695
  }
2656
3696
  };
2657
3697
  }
3698
+ function resolveGroupId(props, index) {
3699
+ if (props.id) return props.id;
3700
+ if (typeof props.header === "string" || typeof props.header === "number") {
3701
+ return `group:${props.header}:${index}`;
3702
+ }
3703
+ return `group:${index}`;
3704
+ }
3705
+ function buildColumnDefsFromTree(nodes, sort, onSort) {
3706
+ return nodes.map((node, index) => {
3707
+ if (node.type === "leaf") {
3708
+ return buildColumnDef(node.props, sort, onSort);
3709
+ }
3710
+ const childDefs = buildColumnDefsFromTree(node.columns, sort, onSort);
3711
+ const { header, align, headerClassName } = node.props;
3712
+ return {
3713
+ id: resolveGroupId(node.props, index),
3714
+ header: (
3715
+ // eslint-disable-next-line @typescript-eslint/promise-function-async
3716
+ () => header
3717
+ ),
3718
+ columns: childDefs,
3719
+ enableResizing: false,
3720
+ meta: {
3721
+ align,
3722
+ headerClassName
3723
+ }
3724
+ };
3725
+ });
3726
+ }
3727
+ function countLeafColumns(nodes) {
3728
+ let count = 0;
3729
+ for (const node of nodes) {
3730
+ if (node.type === "leaf") {
3731
+ count += 1;
3732
+ } else {
3733
+ count += countLeafColumns(node.columns);
3734
+ }
3735
+ }
3736
+ return count;
3737
+ }
2658
3738
 
2659
3739
  // src/components/ui/table/components/Table/parseTableChildren.ts
2660
3740
  import { Children, isValidElement as isValidElement2 } from "react";
@@ -2664,6 +3744,7 @@ import { isValidElement } from "react";
2664
3744
  var TABLE_HEADER_DISPLAY_NAME = "Table.Header";
2665
3745
  var TABLE_BODY_DISPLAY_NAME = "Table.Body";
2666
3746
  var TABLE_COLUMN_DISPLAY_NAME = "Table.Column";
3747
+ var TABLE_COLUMN_GROUP_DISPLAY_NAME = "Table.ColumnGroup";
2667
3748
  var TABLE_PAGINATION_DISPLAY_NAME = "Table.Pagination";
2668
3749
  function getComponentDisplayName(type) {
2669
3750
  if (typeof type === "function" || typeof type === "object" && type !== null) {
@@ -2680,6 +3761,9 @@ function isTableBodyElement(child) {
2680
3761
  function isTableColumnElement(child) {
2681
3762
  return isValidElement(child) && getComponentDisplayName(child.type) === TABLE_COLUMN_DISPLAY_NAME;
2682
3763
  }
3764
+ function isTableColumnGroupElement(child) {
3765
+ return isValidElement(child) && getComponentDisplayName(child.type) === TABLE_COLUMN_GROUP_DISPLAY_NAME;
3766
+ }
2683
3767
  function isTablePaginationElement(child) {
2684
3768
  return isValidElement(child) && getComponentDisplayName(child.type) === TABLE_PAGINATION_DISPLAY_NAME;
2685
3769
  }
@@ -2706,26 +3790,38 @@ function parseTableChildren(children) {
2706
3790
  }
2707
3791
  return slots;
2708
3792
  }
2709
- function flattenColumnElements(children) {
3793
+ function walkColumnTreeNodes(children) {
2710
3794
  const result = [];
2711
3795
  for (const child of Children.toArray(children)) {
2712
3796
  if (isTableColumnElement(child)) {
2713
- result.push(child);
3797
+ result.push({
3798
+ type: "leaf",
3799
+ props: child.props
3800
+ });
3801
+ continue;
3802
+ }
3803
+ if (isTableColumnGroupElement(child)) {
3804
+ const groupProps = child.props;
3805
+ result.push({
3806
+ type: "group",
3807
+ props: groupProps,
3808
+ columns: walkColumnTreeNodes(groupProps.children)
3809
+ });
2714
3810
  continue;
2715
3811
  }
2716
3812
  if (isValidElement2(child)) {
2717
3813
  const nested = child.props.children;
2718
3814
  if (nested != null) {
2719
- result.push(...flattenColumnElements(nested));
3815
+ result.push(...walkColumnTreeNodes(nested));
2720
3816
  }
2721
3817
  }
2722
3818
  }
2723
3819
  return result;
2724
3820
  }
2725
- function extractColumnElements(header) {
3821
+ function extractColumnTree(header) {
2726
3822
  if (!header) return [];
2727
3823
  const { children } = header.props;
2728
- return flattenColumnElements(children);
3824
+ return walkColumnTreeNodes(children);
2729
3825
  }
2730
3826
 
2731
3827
  // src/components/ui/table/components/Table/TableBody.tsx
@@ -2741,6 +3837,13 @@ function TableColumn(props) {
2741
3837
  }
2742
3838
  TableColumn.displayName = TABLE_COLUMN_DISPLAY_NAME;
2743
3839
 
3840
+ // src/components/ui/table/components/Table/TableColumnGroup.tsx
3841
+ function TableColumnGroup(props) {
3842
+ void props;
3843
+ return null;
3844
+ }
3845
+ TableColumnGroup.displayName = TABLE_COLUMN_GROUP_DISPLAY_NAME;
3846
+
2744
3847
  // src/components/ui/table/components/Table/tableDataPipeline.ts
2745
3848
  function sortTableData(data, sort) {
2746
3849
  if (!sort) return data;
@@ -2778,7 +3881,7 @@ function TableHeader(props) {
2778
3881
  TableHeader.displayName = TABLE_HEADER_DISPLAY_NAME;
2779
3882
 
2780
3883
  // src/components/ui/table/components/Table/TablePagination.tsx
2781
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
3884
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
2782
3885
  function TablePagination({
2783
3886
  page,
2784
3887
  pageSize = 10,
@@ -2790,8 +3893,8 @@ function TablePagination({
2790
3893
  const safePage = Math.min(Math.max(1, page), totalPages);
2791
3894
  const canGoPrev = safePage > 1;
2792
3895
  const canGoNext = safePage < totalPages;
2793
- return /* @__PURE__ */ jsxs6("div", { className: cn("TablePaginationJSX", className), children: [
2794
- /* @__PURE__ */ jsx7(
3896
+ return /* @__PURE__ */ jsxs7("div", { className: cn("TablePaginationJSX", className), children: [
3897
+ /* @__PURE__ */ jsx8(
2795
3898
  "button",
2796
3899
  {
2797
3900
  type: "button",
@@ -2799,15 +3902,15 @@ function TablePagination({
2799
3902
  disabled: !canGoPrev,
2800
3903
  onClick: () => onChange(safePage - 1),
2801
3904
  "aria-label": "Previous page",
2802
- children: /* @__PURE__ */ jsx7(ChevronLeft, { className: "pagination-button-icon" })
3905
+ children: /* @__PURE__ */ jsx8(ChevronLeft, { className: "pagination-button-icon" })
2803
3906
  }
2804
3907
  ),
2805
- /* @__PURE__ */ jsxs6("span", { className: "pagination-label", children: [
3908
+ /* @__PURE__ */ jsxs7("span", { className: "pagination-label", children: [
2806
3909
  safePage,
2807
3910
  " / ",
2808
3911
  totalPages
2809
3912
  ] }),
2810
- /* @__PURE__ */ jsx7(
3913
+ /* @__PURE__ */ jsx8(
2811
3914
  "button",
2812
3915
  {
2813
3916
  type: "button",
@@ -2815,7 +3918,7 @@ function TablePagination({
2815
3918
  disabled: !canGoNext,
2816
3919
  onClick: () => onChange(safePage + 1),
2817
3920
  "aria-label": "Next page",
2818
- children: /* @__PURE__ */ jsx7(ChevronRight, { className: "pagination-button-icon" })
3921
+ children: /* @__PURE__ */ jsx8(ChevronRight, { className: "pagination-button-icon" })
2819
3922
  }
2820
3923
  )
2821
3924
  ] });
@@ -2823,7 +3926,7 @@ function TablePagination({
2823
3926
  TablePagination.displayName = TABLE_PAGINATION_DISPLAY_NAME;
2824
3927
 
2825
3928
  // src/components/ui/table/components/Table/Table.tsx
2826
- import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
3929
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
2827
3930
  function TableRoot({
2828
3931
  data,
2829
3932
  children,
@@ -2832,12 +3935,12 @@ function TableRoot({
2832
3935
  filteredCount,
2833
3936
  ...dataTableProps
2834
3937
  }) {
2835
- const { header, pagination: paginationElement } = useMemo4(
3938
+ const { header, pagination: paginationElement } = useMemo5(
2836
3939
  () => parseTableChildren(children),
2837
3940
  [children]
2838
3941
  );
2839
- const [sort, setSort] = useState4(null);
2840
- const handleSort = useCallback4((field) => {
3942
+ const [sort, setSort] = useState5(null);
3943
+ const handleSort = useCallback5((field) => {
2841
3944
  setSort((previous) => {
2842
3945
  if (previous?.field !== field) {
2843
3946
  return { field, direction: "asc" };
@@ -2848,25 +3951,25 @@ function TableRoot({
2848
3951
  return null;
2849
3952
  });
2850
3953
  }, []);
2851
- const columns = useMemo4(() => {
2852
- return extractColumnElements(header).map(
2853
- (columnElement) => buildColumnDef(columnElement.props, sort, handleSort)
2854
- );
2855
- }, [header, sort, handleSort]);
3954
+ const columnTree = useMemo5(() => extractColumnTree(header), [header]);
3955
+ const columns = useMemo5(
3956
+ () => buildColumnDefsFromTree(columnTree, sort, handleSort),
3957
+ [columnTree, sort, handleSort]
3958
+ );
2856
3959
  const paginationProps = paginationElement?.props;
2857
3960
  const pageSize = paginationProps?.pageSize ?? 10;
2858
3961
  const page = paginationProps?.page ?? 1;
2859
3962
  const resolvedTotalCount = paginationProps?.totalCount ?? totalCount ?? data.length;
2860
- const tableData = useMemo4(() => {
3963
+ const tableData = useMemo5(() => {
2861
3964
  const sortedData = sortTableData(data, sort);
2862
3965
  if (!paginationProps) return sortedData;
2863
3966
  return paginateTableData(sortedData, page, pageSize);
2864
3967
  }, [data, sort, paginationProps, page, pageSize]);
2865
- if (columns.length === 0) {
3968
+ if (countLeafColumns(columnTree) === 0) {
2866
3969
  console.warn("[Table] Declare at least one Table.Column inside Table.Header.");
2867
3970
  }
2868
- return /* @__PURE__ */ jsxs7("div", { className: "TableJSX", children: [
2869
- /* @__PURE__ */ jsx8(
3971
+ return /* @__PURE__ */ jsxs8("div", { className: "TableJSX", children: [
3972
+ /* @__PURE__ */ jsx9(
2870
3973
  DataTable,
2871
3974
  {
2872
3975
  ...dataTableProps,
@@ -2877,7 +3980,7 @@ function TableRoot({
2877
3980
  className: cn(className, paginationProps && "DataTableJSX--with-pagination")
2878
3981
  }
2879
3982
  ),
2880
- paginationProps && /* @__PURE__ */ jsx8(
3983
+ paginationProps && /* @__PURE__ */ jsx9(
2881
3984
  TablePagination,
2882
3985
  {
2883
3986
  page,
@@ -2895,13 +3998,19 @@ function createTable() {
2895
3998
  return null;
2896
3999
  }
2897
4000
  Column.displayName = TABLE_COLUMN_DISPLAY_NAME;
4001
+ function ColumnGroup(props) {
4002
+ void props;
4003
+ return null;
4004
+ }
4005
+ ColumnGroup.displayName = TABLE_COLUMN_GROUP_DISPLAY_NAME;
2898
4006
  return Object.assign(
2899
4007
  function BoundTable(props) {
2900
- return /* @__PURE__ */ jsx8(TableRoot, { ...props });
4008
+ return /* @__PURE__ */ jsx9(TableRoot, { ...props });
2901
4009
  },
2902
4010
  {
2903
4011
  Header: TableHeader,
2904
4012
  Column,
4013
+ ColumnGroup,
2905
4014
  Body: TableBody,
2906
4015
  Pagination: TablePagination
2907
4016
  }
@@ -2910,6 +4019,7 @@ function createTable() {
2910
4019
  var Table = Object.assign(TableRoot, {
2911
4020
  Header: TableHeader,
2912
4021
  Column: TableColumn,
4022
+ ColumnGroup: TableColumnGroup,
2913
4023
  Body: TableBody,
2914
4024
  Pagination: TablePagination
2915
4025
  });
@@ -2921,20 +4031,31 @@ export {
2921
4031
  DEFAULT_TREE_PARENT_ID_FIELD,
2922
4032
  DEFAULT_TREE_QTY_FIELD,
2923
4033
  DataTable,
4034
+ INLINE_SEARCH_MAX_RESULTS,
2924
4035
  Table,
2925
4036
  applyCellEdit,
2926
4037
  applyFillData,
2927
4038
  applySelectionUpdater,
2928
4039
  buildColumnFreezeOffsets,
2929
4040
  buildColumnRowSpanMap,
4041
+ buildFlatSearchCorpus,
2930
4042
  buildRowsPastePayload,
4043
+ buildSearchMatchKey,
4044
+ buildSearchMatchKeys,
4045
+ buildTreeSearchCorpus,
2931
4046
  canExpandRow,
4047
+ cellValueToSearchText,
4048
+ collectAncestorKeysToExpand,
2932
4049
  collectCopyRowEntries,
2933
4050
  collectCopyRows,
2934
4051
  collectFillChanges,
2935
4052
  collectRowSpanColumns,
4053
+ collectSearchMatchesInRange,
4054
+ createSearchRegex,
2936
4055
  createTable,
4056
+ escapeSearchRegex,
2937
4057
  flattenSubtreeRows,
4058
+ formatSearchResultLabel,
2938
4059
  getCellEditDraftValue,
2939
4060
  getCellSelectionEdgeStyle,
2940
4061
  getColumnEditType,
@@ -2946,10 +4067,15 @@ export {
2946
4067
  isCellInSelection,
2947
4068
  isColumnEditable,
2948
4069
  isEditablePasteTarget,
4070
+ mapSearchResultToVisibleItem,
4071
+ mapSearchResultsToVisibleKeys,
2949
4072
  measureMergedSpanRowHeights,
4073
+ nextSearchIndex,
4074
+ nextSearchStride,
2950
4075
  parseCellEditValue,
2951
4076
  parseClipboardTSV,
2952
4077
  parseClipboardTSVWithDepths,
4078
+ previousSearchIndex,
2953
4079
  resolveColumnFreezeSide,
2954
4080
  resolveDataTableLabels,
2955
4081
  resolvePasteColumnIds,
@@ -2963,5 +4089,6 @@ export {
2963
4089
  useCellSelection,
2964
4090
  useConvertTreeData,
2965
4091
  useGlideTable,
4092
+ useInlineSearch,
2966
4093
  writeSelectionToClipboard
2967
4094
  };