react-lookup-select 1.0.5 → 1.1.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/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 Onur Altuntaş
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Onur Altuntaş
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/index.cjs CHANGED
@@ -210,12 +210,16 @@ function useLookupSelectState(props) {
210
210
  },
211
211
  [open, onOpenChange]
212
212
  );
213
- const selectionManager = (0, import_react.useMemo)(() => {
214
- return new SelectionManager(mode, mapper);
215
- }, [mode, mapper]);
216
- const queryManager = (0, import_react.useMemo)(() => {
217
- return new QueryManager({ pageSize });
218
- }, [pageSize]);
213
+ const selectionManagerRef = (0, import_react.useRef)(null);
214
+ if (!selectionManagerRef.current) {
215
+ selectionManagerRef.current = new SelectionManager(mode, mapper);
216
+ }
217
+ const selectionManager = selectionManagerRef.current;
218
+ const queryManagerRef = (0, import_react.useRef)(null);
219
+ if (!queryManagerRef.current) {
220
+ queryManagerRef.current = new QueryManager({ pageSize });
221
+ }
222
+ const queryManager = queryManagerRef.current;
219
223
  const [currentSelections, setCurrentSelections] = (0, import_react.useState)([]);
220
224
  const [backupSelections, setBackupSelections] = (0, import_react.useState)([]);
221
225
  const resolveValueToRows = (0, import_react.useCallback)(
@@ -233,6 +237,10 @@ function useLookupSelectState(props) {
233
237
  );
234
238
  if (matchingRow) {
235
239
  resolvedRows.push(matchingRow);
240
+ } else if (typeof console !== "undefined") {
241
+ console.warn(
242
+ `[react-lookup-select] Could not resolve value with id "${searchId}" from data. Ensure the value matches an item in the data array.`
243
+ );
236
244
  }
237
245
  }
238
246
  }
@@ -254,7 +262,7 @@ function useLookupSelectState(props) {
254
262
  }, [value, defaultValue, data, resolveValueToRows, selectionManager]);
255
263
  const toggleRowSelection = (0, import_react.useCallback)(
256
264
  (row) => {
257
- const newState = selectionManager.toggleRow(row);
265
+ selectionManager.toggleRow(row);
258
266
  const selectedRows = selectionManager.getSelectedRows();
259
267
  setCurrentSelections(selectedRows);
260
268
  if (!modalOpen) {
@@ -278,8 +286,6 @@ function useLookupSelectState(props) {
278
286
  );
279
287
  const updateQuery = (0, import_react.useCallback)(
280
288
  (updates) => {
281
- const currentState = queryManager.getState();
282
- const newState = { ...currentState, ...updates };
283
289
  if (updates.search !== void 0) {
284
290
  queryManager.updateSearch(updates.search);
285
291
  }
@@ -356,21 +362,12 @@ function useLookupSelectState(props) {
356
362
  var import_react2 = require("react");
357
363
  function useKeyboardNavigation({
358
364
  isModalOpen,
359
- onClose,
360
- onConfirm,
361
- onRowSelect,
362
- currentData,
363
- selectedRows,
364
- mode
365
+ onConfirm
365
366
  }) {
366
367
  const handleKeyDown = (0, import_react2.useCallback)(
367
368
  (event) => {
368
369
  if (!isModalOpen) return;
369
370
  switch (event.key) {
370
- case "Escape":
371
- event.preventDefault();
372
- onClose();
373
- break;
374
371
  case "Enter":
375
372
  if (event.ctrlKey || event.metaKey) {
376
373
  event.preventDefault();
@@ -378,13 +375,62 @@ function useKeyboardNavigation({
378
375
  }
379
376
  break;
380
377
  case "ArrowDown":
381
- case "ArrowUp":
378
+ case "ArrowUp": {
379
+ const grid = document.querySelector(
380
+ ".lookup-select__grid tbody, .lookup-select__virtual-grid-body"
381
+ );
382
+ if (!grid) break;
383
+ const rows = grid.querySelectorAll(
384
+ 'tr[tabindex="0"], tr[role="row"]'
385
+ );
386
+ if (rows.length === 0) break;
387
+ const currentIndex = Array.from(rows).findIndex(
388
+ (row) => row === document.activeElement
389
+ );
390
+ let nextIndex;
391
+ if (event.key === "ArrowDown") {
392
+ nextIndex = currentIndex < 0 ? 0 : Math.min(currentIndex + 1, rows.length - 1);
393
+ } else {
394
+ nextIndex = currentIndex < 0 ? rows.length - 1 : Math.max(currentIndex - 1, 0);
395
+ }
396
+ event.preventDefault();
397
+ rows[nextIndex]?.focus();
398
+ break;
399
+ }
400
+ case "Home": {
401
+ const gridHome = document.querySelector(
402
+ ".lookup-select__grid tbody, .lookup-select__virtual-grid-body"
403
+ );
404
+ if (!gridHome) break;
405
+ const firstRow = gridHome.querySelector(
406
+ 'tr[tabindex="0"], tr[role="row"]'
407
+ );
408
+ if (firstRow) {
409
+ event.preventDefault();
410
+ firstRow.focus();
411
+ }
382
412
  break;
413
+ }
414
+ case "End": {
415
+ const gridEnd = document.querySelector(
416
+ ".lookup-select__grid tbody, .lookup-select__virtual-grid-body"
417
+ );
418
+ if (!gridEnd) break;
419
+ const allRows = gridEnd.querySelectorAll(
420
+ 'tr[tabindex="0"], tr[role="row"]'
421
+ );
422
+ const lastRow = allRows[allRows.length - 1];
423
+ if (lastRow) {
424
+ event.preventDefault();
425
+ lastRow.focus();
426
+ }
427
+ break;
428
+ }
383
429
  default:
384
430
  break;
385
431
  }
386
432
  },
387
- [isModalOpen, onClose, onConfirm]
433
+ [isModalOpen, onConfirm]
388
434
  );
389
435
  (0, import_react2.useEffect)(() => {
390
436
  if (isModalOpen) {
@@ -398,36 +444,7 @@ function useKeyboardNavigation({
398
444
  handleKeyDown
399
445
  };
400
446
  }
401
- function useFocusManagement(isOpen) {
402
- (0, import_react2.useEffect)(() => {
403
- if (!isOpen) return;
404
- const modal = document.querySelector('[role="dialog"]');
405
- if (!modal) return;
406
- const focusableElements = modal.querySelectorAll(
407
- 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
408
- );
409
- const firstFocusable = focusableElements[0];
410
- const lastFocusable = focusableElements[focusableElements.length - 1];
411
- firstFocusable?.focus();
412
- const handleTabKey = (e) => {
413
- if (e.key !== "Tab") return;
414
- if (e.shiftKey) {
415
- if (document.activeElement === firstFocusable) {
416
- e.preventDefault();
417
- lastFocusable?.focus();
418
- }
419
- } else {
420
- if (document.activeElement === lastFocusable) {
421
- e.preventDefault();
422
- firstFocusable?.focus();
423
- }
424
- }
425
- };
426
- document.addEventListener("keydown", handleTabKey);
427
- return () => {
428
- document.removeEventListener("keydown", handleTabKey);
429
- };
430
- }, [isOpen]);
447
+ function useFocusManagement(_isOpen) {
431
448
  }
432
449
  function useScreenReaderAnnouncements() {
433
450
  const announce = (0, import_react2.useCallback)(
@@ -586,11 +603,18 @@ function Modal({
586
603
  (0, import_react3.useEffect)(() => {
587
604
  if (!isOpen) return;
588
605
  previousFocusRef.current = document.activeElement;
589
- const focusableElements = modalRef.current?.querySelectorAll(
590
- 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
606
+ const autoFocusElement = modalRef.current?.querySelector(
607
+ "[data-autofocus]"
591
608
  );
592
- if (focusableElements && focusableElements.length > 0) {
593
- focusableElements[0].focus();
609
+ if (autoFocusElement) {
610
+ autoFocusElement.focus();
611
+ } else {
612
+ const focusableElements = modalRef.current?.querySelectorAll(
613
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
614
+ );
615
+ if (focusableElements && focusableElements.length > 0) {
616
+ focusableElements[0].focus();
617
+ }
594
618
  }
595
619
  const handleEscape = (e) => {
596
620
  if (e.key === "Escape") {
@@ -599,12 +623,12 @@ function Modal({
599
623
  };
600
624
  const handleTab = (e) => {
601
625
  if (e.key !== "Tab") return;
602
- const focusableElements2 = modalRef.current?.querySelectorAll(
626
+ const focusableElements = modalRef.current?.querySelectorAll(
603
627
  'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
604
628
  );
605
- if (!focusableElements2 || focusableElements2.length === 0) return;
606
- const firstElement = focusableElements2[0];
607
- const lastElement = focusableElements2[focusableElements2.length - 1];
629
+ if (!focusableElements || focusableElements.length === 0) return;
630
+ const firstElement = focusableElements[0];
631
+ const lastElement = focusableElements[focusableElements.length - 1];
608
632
  if (e.shiftKey) {
609
633
  if (document.activeElement === firstElement) {
610
634
  lastElement.focus();
@@ -695,23 +719,17 @@ function SearchInput({
695
719
  placeholder = "Search...",
696
720
  className
697
721
  }) {
698
- const inputRef = (0, import_react3.useRef)(null);
699
- (0, import_react3.useEffect)(() => {
700
- if (inputRef.current) {
701
- inputRef.current.focus();
702
- }
703
- }, []);
704
722
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: `lookup-select__search ${className || ""}`, children: [
705
723
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
706
724
  "input",
707
725
  {
708
- ref: inputRef,
709
726
  type: "text",
710
727
  value,
711
728
  onChange: (e) => onChange(e.target.value),
712
729
  placeholder,
713
730
  className: "lookup-select__search-input",
714
- "aria-label": "Search records"
731
+ "aria-label": "Search records",
732
+ "data-autofocus": true
715
733
  }
716
734
  ),
717
735
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "lookup-select__search-icon", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001c.03.04.062.078.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1.007 1.007 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z" }) }) })
@@ -768,6 +786,7 @@ function Grid({
768
786
  loading = false,
769
787
  error,
770
788
  emptyText = "No records found",
789
+ onRetry,
771
790
  className,
772
791
  style
773
792
  }) {
@@ -788,13 +807,33 @@ function Grid({
788
807
  return currentSort.sortDir === "asc" ? "\u2191" : "\u2193";
789
808
  };
790
809
  if (loading) {
791
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "lookup-select__grid-state", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "lookup-select__loading", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { children: "Loading..." }) }) });
810
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: `lookup-select__grid ${className || ""}`, style, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("table", { className: "lookup-select__table", children: [
811
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("tr", { children: [
812
+ mode === "multiple" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("th", { className: "lookup-select__header-cell lookup-select__header-cell--checkbox" }),
813
+ columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("th", { className: "lookup-select__header-cell", children: column.title }, String(column.key)))
814
+ ] }) }),
815
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("tbody", { children: Array.from({ length: 5 }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("tr", { className: "lookup-select__grid-row lookup-select__grid-row--skeleton", children: [
816
+ mode === "multiple" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("td", { className: "lookup-select__cell" }),
817
+ columns.map((col, j) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("td", { className: "lookup-select__cell", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "lookup-select__skeleton-line" }) }, j))
818
+ ] }, i)) })
819
+ ] }) });
792
820
  }
793
821
  if (error) {
794
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "lookup-select__grid-state", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "lookup-select__error", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { children: [
795
- "Error: ",
796
- error
797
- ] }) }) });
822
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "lookup-select__grid-state", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "lookup-select__error", children: [
823
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { children: [
824
+ "Error: ",
825
+ error
826
+ ] }),
827
+ onRetry && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
828
+ "button",
829
+ {
830
+ type: "button",
831
+ className: "lookup-select__button lookup-select__button--secondary",
832
+ onClick: onRetry,
833
+ children: "Retry"
834
+ }
835
+ )
836
+ ] }) });
798
837
  }
799
838
  if (data.length === 0) {
800
839
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "lookup-select__grid-state", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "lookup-select__empty", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { children: emptyText }) }) });
@@ -866,7 +905,7 @@ function Grid({
866
905
  "aria-label": "Select all"
867
906
  }
868
907
  ) }) }),
869
- columns.map((column, index) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
908
+ columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
870
909
  "th",
871
910
  {
872
911
  className: "lookup-select__table-header",
@@ -922,7 +961,7 @@ function Grid({
922
961
  "aria-label": `Select ${mapper.getText(row)}`
923
962
  }
924
963
  ) }) }),
925
- columns.map((column, columnIndex) => {
964
+ columns.map((column) => {
926
965
  const cellKey = `${rowId}-${typeof column.key === "string" ? column.key : String(column.key)}`;
927
966
  let cellContent;
928
967
  if (column.render) {
@@ -984,13 +1023,16 @@ function VirtualGrid(props) {
984
1023
  return () => resizeObserver.disconnect();
985
1024
  }
986
1025
  }, []);
1026
+ const selectableCount = import_react4.default.useMemo(() => {
1027
+ return selectableRow ? data.filter(selectableRow).length : data.length;
1028
+ }, [data, selectableRow]);
987
1029
  import_react4.default.useEffect(() => {
988
1030
  if (headerCheckboxRef.current) {
989
1031
  const hasSelected = selectedRows.length > 0;
990
- const hasUnselected = selectedRows.length < data.length;
1032
+ const hasUnselected = selectedRows.length < selectableCount;
991
1033
  headerCheckboxRef.current.indeterminate = hasSelected && hasUnselected;
992
1034
  }
993
- }, [selectedRows.length, data.length]);
1035
+ }, [selectedRows.length, selectableCount]);
994
1036
  const itemCount = data.length;
995
1037
  const { rowHeight, overscan } = virtualization;
996
1038
  const startIndex = Math.max(0, Math.floor(scrollTop / rowHeight) - overscan);
@@ -1065,7 +1107,7 @@ function VirtualGrid(props) {
1065
1107
  ref: headerCheckboxRef,
1066
1108
  type: "checkbox",
1067
1109
  className: "lookup-select__checkbox",
1068
- checked: selectedRows.length === data.length && data.length > 0,
1110
+ checked: selectedRows.length === selectableCount && selectableCount > 0,
1069
1111
  onChange: (e) => {
1070
1112
  if (e.target.checked) {
1071
1113
  data.forEach((row) => {
@@ -1148,9 +1190,11 @@ function VirtualRowComponent({
1148
1190
  item,
1149
1191
  columns,
1150
1192
  mode,
1193
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1151
1194
  mapper,
1152
1195
  isSelected,
1153
1196
  isSelectable,
1197
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1154
1198
  onToggle,
1155
1199
  onCheckboxChange,
1156
1200
  onClick
@@ -1198,7 +1242,8 @@ function Pagination({
1198
1242
  totalCount,
1199
1243
  pageSize,
1200
1244
  onPageChange,
1201
- className
1245
+ className,
1246
+ i18n
1202
1247
  }) {
1203
1248
  const totalPages = Math.ceil(totalCount / pageSize);
1204
1249
  if (totalPages <= 1) {
@@ -1228,14 +1273,7 @@ function Pagination({
1228
1273
  const startRecord = (currentPage - 1) * pageSize + 1;
1229
1274
  const endRecord = Math.min(currentPage * pageSize, totalCount);
1230
1275
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: `lookup-select__pagination ${className || ""}`, children: [
1231
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "lookup-select__pagination-info", children: totalCount > 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1232
- startRecord,
1233
- "-",
1234
- endRecord,
1235
- " / ",
1236
- totalCount,
1237
- " records"
1238
- ] }) : "0 records" }),
1276
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "lookup-select__pagination-info", children: totalCount > 0 ? i18n?.recordsInfo ? i18n.recordsInfo(startRecord, endRecord, totalCount) : `${startRecord}-${endRecord} / ${totalCount} records` : "0 records" }),
1239
1277
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "lookup-select__pagination-controls", children: [
1240
1278
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1241
1279
  "button",
@@ -1244,7 +1282,7 @@ function Pagination({
1244
1282
  className: "lookup-select__pagination-button",
1245
1283
  disabled: currentPage === 1,
1246
1284
  onClick: () => onPageChange(currentPage - 1),
1247
- "aria-label": "Previous page",
1285
+ "aria-label": i18n?.previousPage || "Previous page",
1248
1286
  children: "\u2039"
1249
1287
  }
1250
1288
  ),
@@ -1269,7 +1307,7 @@ function Pagination({
1269
1307
  ${currentPage === pageNum ? "lookup-select__pagination-button--active" : ""}
1270
1308
  `,
1271
1309
  onClick: () => onPageChange(pageNum),
1272
- "aria-label": `Sayfa ${pageNum}`,
1310
+ "aria-label": i18n?.pageLabel ? i18n.pageLabel(pageNum) : `Page ${pageNum}`,
1273
1311
  "aria-current": currentPage === pageNum ? "page" : void 0,
1274
1312
  children: pageNum
1275
1313
  },
@@ -1283,7 +1321,7 @@ function Pagination({
1283
1321
  className: "lookup-select__pagination-button",
1284
1322
  disabled: currentPage === totalPages,
1285
1323
  onClick: () => onPageChange(currentPage + 1),
1286
- "aria-label": "Next page",
1324
+ "aria-label": i18n?.nextPage || "Next page",
1287
1325
  children: "\u203A"
1288
1326
  }
1289
1327
  )
@@ -1306,7 +1344,6 @@ function LookupSelect(props) {
1306
1344
  renderTrigger,
1307
1345
  renderModal,
1308
1346
  renderGrid,
1309
- renderHeader,
1310
1347
  renderFooter,
1311
1348
  renderSearch,
1312
1349
  renderPagination,
@@ -1326,7 +1363,6 @@ function LookupSelect(props) {
1326
1363
  const {
1327
1364
  modalOpen,
1328
1365
  openModal,
1329
- closeModal,
1330
1366
  currentSelections,
1331
1367
  toggleRowSelection,
1332
1368
  clearSelections,
@@ -1371,11 +1407,10 @@ function LookupSelect(props) {
1371
1407
  virtualRowHeight,
1372
1408
  pageSize
1373
1409
  ]);
1374
- const { announce } = useScreenReaderAnnouncements();
1410
+ useScreenReaderAnnouncements();
1375
1411
  useFocusManagement(modalOpen);
1376
1412
  useKeyboardNavigation({
1377
1413
  isModalOpen: modalOpen,
1378
- onClose: closeModal,
1379
1414
  onConfirm: confirmSelection,
1380
1415
  currentData: [],
1381
1416
  selectedRows: currentSelections,
@@ -1387,13 +1422,34 @@ function LookupSelect(props) {
1387
1422
  const [serverData, setServerData] = import_react5.default.useState([]);
1388
1423
  const [totalCount, setTotalCount] = import_react5.default.useState(0);
1389
1424
  const [currentPage, setCurrentPage] = import_react5.default.useState(1);
1425
+ const [sortState, setSortState] = import_react5.default.useState({});
1426
+ const [debouncedSearch, setDebouncedSearch] = import_react5.default.useState("");
1427
+ const debounceTimerRef = import_react5.default.useRef(null);
1390
1428
  const handleSearchChange = import_react5.default.useCallback(
1391
1429
  (value) => {
1392
1430
  setSearchValue(value);
1393
- updateQuery({ search: value, page: 1 });
1431
+ if (dataSource) {
1432
+ if (debounceTimerRef.current) {
1433
+ clearTimeout(debounceTimerRef.current);
1434
+ }
1435
+ debounceTimerRef.current = setTimeout(() => {
1436
+ setDebouncedSearch(value);
1437
+ setCurrentPage(1);
1438
+ updateQuery({ search: value, page: 1 });
1439
+ }, 300);
1440
+ } else {
1441
+ updateQuery({ search: value, page: 1 });
1442
+ }
1394
1443
  },
1395
- [updateQuery]
1444
+ [updateQuery, dataSource]
1396
1445
  );
1446
+ import_react5.default.useEffect(() => {
1447
+ return () => {
1448
+ if (debounceTimerRef.current) {
1449
+ clearTimeout(debounceTimerRef.current);
1450
+ }
1451
+ };
1452
+ }, []);
1397
1453
  const filteredData = import_react5.default.useMemo(() => {
1398
1454
  if (!searchValue || dataSource) {
1399
1455
  return data;
@@ -1411,13 +1467,14 @@ function LookupSelect(props) {
1411
1467
  setLoading(true);
1412
1468
  setError(void 0);
1413
1469
  try {
1414
- const query = getCurrentQuery();
1415
- const result = await dataSource({
1416
- ...query,
1417
- search: searchValue,
1470
+ const query = {
1471
+ search: debouncedSearch,
1418
1472
  page: currentPage,
1419
- pageSize: optimizedPageSize
1420
- });
1473
+ pageSize: optimizedPageSize,
1474
+ sortBy: sortState.sortBy,
1475
+ sortDir: sortState.sortDir
1476
+ };
1477
+ const result = await dataSource(query);
1421
1478
  setServerData(result.rows);
1422
1479
  setTotalCount(result.total);
1423
1480
  setLoading(false);
@@ -1429,10 +1486,10 @@ function LookupSelect(props) {
1429
1486
  }
1430
1487
  }, [
1431
1488
  dataSource,
1432
- getCurrentQuery,
1433
- searchValue,
1489
+ debouncedSearch,
1434
1490
  currentPage,
1435
- optimizedPageSize
1491
+ optimizedPageSize,
1492
+ sortState
1436
1493
  ]);
1437
1494
  import_react5.default.useEffect(() => {
1438
1495
  if (dataSource && modalOpen) {
@@ -1448,8 +1505,9 @@ function LookupSelect(props) {
1448
1505
  );
1449
1506
  const handleSortChange = import_react5.default.useCallback(
1450
1507
  (sortBy, sortDir) => {
1451
- updateQuery({ sortBy, sortDir, page: 1 });
1508
+ setSortState({ sortBy, sortDir });
1452
1509
  setCurrentPage(1);
1510
+ updateQuery({ sortBy, sortDir, page: 1 });
1453
1511
  },
1454
1512
  [updateQuery]
1455
1513
  );
@@ -1486,11 +1544,12 @@ function LookupSelect(props) {
1486
1544
  loading,
1487
1545
  error,
1488
1546
  emptyText: texts.emptyText,
1547
+ onRetry: dataSource ? loadServerData : void 0,
1489
1548
  className: classNames?.grid,
1490
1549
  style: styles?.grid,
1491
1550
  currentSort: dataSource ? {
1492
- sortBy: getCurrentQuery().sortBy || "",
1493
- sortDir: getCurrentQuery().sortDir || "asc"
1551
+ sortBy: sortState.sortBy || "",
1552
+ sortDir: sortState.sortDir || "asc"
1494
1553
  } : void 0,
1495
1554
  onSort: dataSource ? handleSortChange : void 0
1496
1555
  };
@@ -1547,12 +1606,14 @@ function LookupSelect(props) {
1547
1606
  selectedIds: currentSelections.map(mapper.getId),
1548
1607
  onRowSelect: toggleRowSelection,
1549
1608
  onSelectAll: mode === "multiple" ? () => {
1550
- const allIds = currentData.map(mapper.getId);
1551
- const selectedIds = currentSelections.map(mapper.getId);
1552
- if (allIds.length === selectedIds.length) {
1609
+ const selectableData = selectableRow ? currentData.filter(selectableRow) : currentData;
1610
+ const allSelected = selectableData.every(
1611
+ (row) => isRowSelected(row)
1612
+ );
1613
+ if (allSelected) {
1553
1614
  clearSelections();
1554
1615
  } else {
1555
- currentData.forEach((row) => {
1616
+ selectableData.forEach((row) => {
1556
1617
  if (!isRowSelected(row)) {
1557
1618
  toggleRowSelection(row);
1558
1619
  }
@@ -1567,8 +1628,8 @@ function LookupSelect(props) {
1567
1628
  virtualization: shouldUseVirtualization ? virtualizationConfig : void 0,
1568
1629
  selectableRow,
1569
1630
  onSortChange: handleSortChange,
1570
- sortBy: getCurrentQuery().sortBy,
1571
- sortDir: getCurrentQuery().sortDir
1631
+ sortBy: sortState.sortBy,
1632
+ sortDir: sortState.sortDir
1572
1633
  }) : renderGridComponent(),
1573
1634
  dataSource && !renderPagination && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1574
1635
  Pagination,
@@ -1576,7 +1637,12 @@ function LookupSelect(props) {
1576
1637
  currentPage,
1577
1638
  totalCount,
1578
1639
  pageSize: optimizedPageSize,
1579
- onPageChange: handlePageChange
1640
+ onPageChange: handlePageChange,
1641
+ i18n: {
1642
+ previousPage: i18n?.previousPage,
1643
+ nextPage: i18n?.nextPage,
1644
+ pageLabel: i18n?.paginationInfo ? (pageNum) => i18n.paginationInfo(pageNum, Math.ceil(totalCount / optimizedPageSize)) : void 0
1645
+ }
1580
1646
  }
1581
1647
  ),
1582
1648
  dataSource && renderPagination && renderPagination({
@@ -1584,7 +1650,7 @@ function LookupSelect(props) {
1584
1650
  totalPages: Math.ceil(totalCount / optimizedPageSize),
1585
1651
  onPageChange: handlePageChange,
1586
1652
  pageSize: optimizedPageSize,
1587
- onPageSizeChange: (newSize) => {
1653
+ onPageSizeChange: (_newSize) => {
1588
1654
  },
1589
1655
  totalCount,
1590
1656
  loading