react-lookup-select 1.0.4 → 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/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import React3 from "react";
3
3
 
4
4
  // src/internal/state.ts
5
- import { useState, useCallback, useMemo, useEffect } from "react";
5
+ import { useState, useCallback, useRef, useEffect } from "react";
6
6
 
7
7
  // src/internal/core.ts
8
8
  function createSelectionState() {
@@ -174,13 +174,18 @@ function useLookupSelectState(props) {
174
174
  },
175
175
  [open, onOpenChange]
176
176
  );
177
- const selectionManager = useMemo(() => {
178
- return new SelectionManager(mode, mapper);
179
- }, [mode, mapper]);
180
- const queryManager = useMemo(() => {
181
- return new QueryManager({ pageSize });
182
- }, [pageSize]);
177
+ const selectionManagerRef = useRef(null);
178
+ if (!selectionManagerRef.current) {
179
+ selectionManagerRef.current = new SelectionManager(mode, mapper);
180
+ }
181
+ const selectionManager = selectionManagerRef.current;
182
+ const queryManagerRef = useRef(null);
183
+ if (!queryManagerRef.current) {
184
+ queryManagerRef.current = new QueryManager({ pageSize });
185
+ }
186
+ const queryManager = queryManagerRef.current;
183
187
  const [currentSelections, setCurrentSelections] = useState([]);
188
+ const [backupSelections, setBackupSelections] = useState([]);
184
189
  const resolveValueToRows = useCallback(
185
190
  (valueToResolve) => {
186
191
  if (!valueToResolve || !data) return [];
@@ -196,6 +201,10 @@ function useLookupSelectState(props) {
196
201
  );
197
202
  if (matchingRow) {
198
203
  resolvedRows.push(matchingRow);
204
+ } else if (typeof console !== "undefined") {
205
+ console.warn(
206
+ `[react-lookup-select] Could not resolve value with id "${searchId}" from data. Ensure the value matches an item in the data array.`
207
+ );
199
208
  }
200
209
  }
201
210
  }
@@ -217,18 +226,22 @@ function useLookupSelectState(props) {
217
226
  }, [value, defaultValue, data, resolveValueToRows, selectionManager]);
218
227
  const toggleRowSelection = useCallback(
219
228
  (row) => {
220
- const newState = selectionManager.toggleRow(row);
229
+ selectionManager.toggleRow(row);
221
230
  const selectedRows = selectionManager.getSelectedRows();
222
231
  setCurrentSelections(selectedRows);
223
- onSelectionChange?.(selectedRows);
232
+ if (!modalOpen) {
233
+ onSelectionChange?.(selectedRows);
234
+ }
224
235
  },
225
- [selectionManager, onSelectionChange]
236
+ [selectionManager, onSelectionChange, modalOpen]
226
237
  );
227
238
  const clearSelections = useCallback(() => {
228
239
  selectionManager.clearSelection();
229
240
  setCurrentSelections([]);
230
- onSelectionChange?.([]);
231
- }, [selectionManager, onSelectionChange]);
241
+ if (!modalOpen) {
242
+ onSelectionChange?.([]);
243
+ }
244
+ }, [selectionManager, onSelectionChange, modalOpen]);
232
245
  const isRowSelected = useCallback(
233
246
  (row) => {
234
247
  return selectionManager.isRowSelected(row);
@@ -237,8 +250,6 @@ function useLookupSelectState(props) {
237
250
  );
238
251
  const updateQuery = useCallback(
239
252
  (updates) => {
240
- const currentState = queryManager.getState();
241
- const newState = { ...currentState, ...updates };
242
253
  if (updates.search !== void 0) {
243
254
  queryManager.updateSearch(updates.search);
244
255
  }
@@ -278,17 +289,24 @@ function useLookupSelectState(props) {
278
289
  ]);
279
290
  const cancelSelection = useCallback(() => {
280
291
  selectionManager.clearSelection();
281
- setCurrentSelections([]);
292
+ for (const row of backupSelections) {
293
+ selectionManager.toggleRow(row);
294
+ }
295
+ setCurrentSelections([...backupSelections]);
282
296
  onCancel?.();
283
297
  handleModalOpenChange(false);
284
- }, [selectionManager, onCancel, handleModalOpenChange]);
298
+ }, [selectionManager, backupSelections, onCancel, handleModalOpenChange]);
285
299
  const getCurrentQuery = useCallback(() => {
286
300
  return queryManager.getState();
287
301
  }, [queryManager]);
302
+ const openModal = useCallback(() => {
303
+ setBackupSelections([...currentSelections]);
304
+ handleModalOpenChange(true);
305
+ }, [currentSelections, handleModalOpenChange]);
288
306
  return {
289
307
  // Modal state
290
308
  modalOpen,
291
- openModal: () => handleModalOpenChange(true),
309
+ openModal,
292
310
  closeModal: () => handleModalOpenChange(false),
293
311
  // Selection state
294
312
  currentSelections,
@@ -308,21 +326,12 @@ function useLookupSelectState(props) {
308
326
  import { useEffect as useEffect2, useCallback as useCallback2 } from "react";
309
327
  function useKeyboardNavigation({
310
328
  isModalOpen,
311
- onClose,
312
- onConfirm,
313
- onRowSelect,
314
- currentData,
315
- selectedRows,
316
- mode
329
+ onConfirm
317
330
  }) {
318
331
  const handleKeyDown = useCallback2(
319
332
  (event) => {
320
333
  if (!isModalOpen) return;
321
334
  switch (event.key) {
322
- case "Escape":
323
- event.preventDefault();
324
- onClose();
325
- break;
326
335
  case "Enter":
327
336
  if (event.ctrlKey || event.metaKey) {
328
337
  event.preventDefault();
@@ -330,13 +339,62 @@ function useKeyboardNavigation({
330
339
  }
331
340
  break;
332
341
  case "ArrowDown":
333
- case "ArrowUp":
342
+ case "ArrowUp": {
343
+ const grid = document.querySelector(
344
+ ".lookup-select__grid tbody, .lookup-select__virtual-grid-body"
345
+ );
346
+ if (!grid) break;
347
+ const rows = grid.querySelectorAll(
348
+ 'tr[tabindex="0"], tr[role="row"]'
349
+ );
350
+ if (rows.length === 0) break;
351
+ const currentIndex = Array.from(rows).findIndex(
352
+ (row) => row === document.activeElement
353
+ );
354
+ let nextIndex;
355
+ if (event.key === "ArrowDown") {
356
+ nextIndex = currentIndex < 0 ? 0 : Math.min(currentIndex + 1, rows.length - 1);
357
+ } else {
358
+ nextIndex = currentIndex < 0 ? rows.length - 1 : Math.max(currentIndex - 1, 0);
359
+ }
360
+ event.preventDefault();
361
+ rows[nextIndex]?.focus();
362
+ break;
363
+ }
364
+ case "Home": {
365
+ const gridHome = document.querySelector(
366
+ ".lookup-select__grid tbody, .lookup-select__virtual-grid-body"
367
+ );
368
+ if (!gridHome) break;
369
+ const firstRow = gridHome.querySelector(
370
+ 'tr[tabindex="0"], tr[role="row"]'
371
+ );
372
+ if (firstRow) {
373
+ event.preventDefault();
374
+ firstRow.focus();
375
+ }
376
+ break;
377
+ }
378
+ case "End": {
379
+ const gridEnd = document.querySelector(
380
+ ".lookup-select__grid tbody, .lookup-select__virtual-grid-body"
381
+ );
382
+ if (!gridEnd) break;
383
+ const allRows = gridEnd.querySelectorAll(
384
+ 'tr[tabindex="0"], tr[role="row"]'
385
+ );
386
+ const lastRow = allRows[allRows.length - 1];
387
+ if (lastRow) {
388
+ event.preventDefault();
389
+ lastRow.focus();
390
+ }
334
391
  break;
392
+ }
335
393
  default:
336
394
  break;
337
395
  }
338
396
  },
339
- [isModalOpen, onClose, onConfirm]
397
+ [isModalOpen, onConfirm]
340
398
  );
341
399
  useEffect2(() => {
342
400
  if (isModalOpen) {
@@ -350,36 +408,7 @@ function useKeyboardNavigation({
350
408
  handleKeyDown
351
409
  };
352
410
  }
353
- function useFocusManagement(isOpen) {
354
- useEffect2(() => {
355
- if (!isOpen) return;
356
- const modal = document.querySelector('[role="dialog"]');
357
- if (!modal) return;
358
- const focusableElements = modal.querySelectorAll(
359
- 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
360
- );
361
- const firstFocusable = focusableElements[0];
362
- const lastFocusable = focusableElements[focusableElements.length - 1];
363
- firstFocusable?.focus();
364
- const handleTabKey = (e) => {
365
- if (e.key !== "Tab") return;
366
- if (e.shiftKey) {
367
- if (document.activeElement === firstFocusable) {
368
- e.preventDefault();
369
- lastFocusable?.focus();
370
- }
371
- } else {
372
- if (document.activeElement === lastFocusable) {
373
- e.preventDefault();
374
- firstFocusable?.focus();
375
- }
376
- }
377
- };
378
- document.addEventListener("keydown", handleTabKey);
379
- return () => {
380
- document.removeEventListener("keydown", handleTabKey);
381
- };
382
- }, [isOpen]);
411
+ function useFocusManagement(_isOpen) {
383
412
  }
384
413
  function useScreenReaderAnnouncements() {
385
414
  const announce = useCallback2(
@@ -518,7 +547,7 @@ function Trigger({
518
547
  }
519
548
 
520
549
  // src/components/Modal.tsx
521
- import { useEffect as useEffect3, useRef } from "react";
550
+ import { useEffect as useEffect3, useRef as useRef2 } from "react";
522
551
  import { createPortal } from "react-dom";
523
552
  import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
524
553
  function Modal({
@@ -533,16 +562,23 @@ function Modal({
533
562
  ariaDescribedBy,
534
563
  closeButtonLabel = "Close modal"
535
564
  }) {
536
- const modalRef = useRef(null);
537
- const previousFocusRef = useRef(null);
565
+ const modalRef = useRef2(null);
566
+ const previousFocusRef = useRef2(null);
538
567
  useEffect3(() => {
539
568
  if (!isOpen) return;
540
569
  previousFocusRef.current = document.activeElement;
541
- const focusableElements = modalRef.current?.querySelectorAll(
542
- 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
570
+ const autoFocusElement = modalRef.current?.querySelector(
571
+ "[data-autofocus]"
543
572
  );
544
- if (focusableElements && focusableElements.length > 0) {
545
- focusableElements[0].focus();
573
+ if (autoFocusElement) {
574
+ autoFocusElement.focus();
575
+ } else {
576
+ const focusableElements = modalRef.current?.querySelectorAll(
577
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
578
+ );
579
+ if (focusableElements && focusableElements.length > 0) {
580
+ focusableElements[0].focus();
581
+ }
546
582
  }
547
583
  const handleEscape = (e) => {
548
584
  if (e.key === "Escape") {
@@ -551,12 +587,12 @@ function Modal({
551
587
  };
552
588
  const handleTab = (e) => {
553
589
  if (e.key !== "Tab") return;
554
- const focusableElements2 = modalRef.current?.querySelectorAll(
590
+ const focusableElements = modalRef.current?.querySelectorAll(
555
591
  'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
556
592
  );
557
- if (!focusableElements2 || focusableElements2.length === 0) return;
558
- const firstElement = focusableElements2[0];
559
- const lastElement = focusableElements2[focusableElements2.length - 1];
593
+ if (!focusableElements || focusableElements.length === 0) return;
594
+ const firstElement = focusableElements[0];
595
+ const lastElement = focusableElements[focusableElements.length - 1];
560
596
  if (e.shiftKey) {
561
597
  if (document.activeElement === firstElement) {
562
598
  lastElement.focus();
@@ -647,23 +683,17 @@ function SearchInput({
647
683
  placeholder = "Search...",
648
684
  className
649
685
  }) {
650
- const inputRef = useRef(null);
651
- useEffect3(() => {
652
- if (inputRef.current) {
653
- inputRef.current.focus();
654
- }
655
- }, []);
656
686
  return /* @__PURE__ */ jsxs2("div", { className: `lookup-select__search ${className || ""}`, children: [
657
687
  /* @__PURE__ */ jsx2(
658
688
  "input",
659
689
  {
660
- ref: inputRef,
661
690
  type: "text",
662
691
  value,
663
692
  onChange: (e) => onChange(e.target.value),
664
693
  placeholder,
665
694
  className: "lookup-select__search-input",
666
- "aria-label": "Search records"
695
+ "aria-label": "Search records",
696
+ "data-autofocus": true
667
697
  }
668
698
  ),
669
699
  /* @__PURE__ */ jsx2("div", { className: "lookup-select__search-icon", children: /* @__PURE__ */ jsx2("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ jsx2("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" }) }) })
@@ -720,6 +750,7 @@ function Grid({
720
750
  loading = false,
721
751
  error,
722
752
  emptyText = "No records found",
753
+ onRetry,
723
754
  className,
724
755
  style
725
756
  }) {
@@ -740,13 +771,33 @@ function Grid({
740
771
  return currentSort.sortDir === "asc" ? "\u2191" : "\u2193";
741
772
  };
742
773
  if (loading) {
743
- return /* @__PURE__ */ jsx3("div", { className: "lookup-select__grid-state", children: /* @__PURE__ */ jsx3("div", { className: "lookup-select__loading", children: /* @__PURE__ */ jsx3("p", { children: "Loading..." }) }) });
774
+ return /* @__PURE__ */ jsx3("div", { className: `lookup-select__grid ${className || ""}`, style, children: /* @__PURE__ */ jsxs3("table", { className: "lookup-select__table", children: [
775
+ /* @__PURE__ */ jsx3("thead", { children: /* @__PURE__ */ jsxs3("tr", { children: [
776
+ mode === "multiple" && /* @__PURE__ */ jsx3("th", { className: "lookup-select__header-cell lookup-select__header-cell--checkbox" }),
777
+ columns.map((column) => /* @__PURE__ */ jsx3("th", { className: "lookup-select__header-cell", children: column.title }, String(column.key)))
778
+ ] }) }),
779
+ /* @__PURE__ */ jsx3("tbody", { children: Array.from({ length: 5 }).map((_, i) => /* @__PURE__ */ jsxs3("tr", { className: "lookup-select__grid-row lookup-select__grid-row--skeleton", children: [
780
+ mode === "multiple" && /* @__PURE__ */ jsx3("td", { className: "lookup-select__cell" }),
781
+ columns.map((col, j) => /* @__PURE__ */ jsx3("td", { className: "lookup-select__cell", children: /* @__PURE__ */ jsx3("div", { className: "lookup-select__skeleton-line" }) }, j))
782
+ ] }, i)) })
783
+ ] }) });
744
784
  }
745
785
  if (error) {
746
- return /* @__PURE__ */ jsx3("div", { className: "lookup-select__grid-state", children: /* @__PURE__ */ jsx3("div", { className: "lookup-select__error", children: /* @__PURE__ */ jsxs3("p", { children: [
747
- "Error: ",
748
- error
749
- ] }) }) });
786
+ return /* @__PURE__ */ jsx3("div", { className: "lookup-select__grid-state", children: /* @__PURE__ */ jsxs3("div", { className: "lookup-select__error", children: [
787
+ /* @__PURE__ */ jsxs3("p", { children: [
788
+ "Error: ",
789
+ error
790
+ ] }),
791
+ onRetry && /* @__PURE__ */ jsx3(
792
+ "button",
793
+ {
794
+ type: "button",
795
+ className: "lookup-select__button lookup-select__button--secondary",
796
+ onClick: onRetry,
797
+ children: "Retry"
798
+ }
799
+ )
800
+ ] }) });
750
801
  }
751
802
  if (data.length === 0) {
752
803
  return /* @__PURE__ */ jsx3("div", { className: "lookup-select__grid-state", children: /* @__PURE__ */ jsx3("div", { className: "lookup-select__empty", children: /* @__PURE__ */ jsx3("p", { children: emptyText }) }) });
@@ -818,7 +869,7 @@ function Grid({
818
869
  "aria-label": "Select all"
819
870
  }
820
871
  ) }) }),
821
- columns.map((column, index) => /* @__PURE__ */ jsx3(
872
+ columns.map((column) => /* @__PURE__ */ jsx3(
822
873
  "th",
823
874
  {
824
875
  className: "lookup-select__table-header",
@@ -874,7 +925,7 @@ function Grid({
874
925
  "aria-label": `Select ${mapper.getText(row)}`
875
926
  }
876
927
  ) }) }),
877
- columns.map((column, columnIndex) => {
928
+ columns.map((column) => {
878
929
  const cellKey = `${rowId}-${typeof column.key === "string" ? column.key : String(column.key)}`;
879
930
  let cellContent;
880
931
  if (column.render) {
@@ -936,13 +987,16 @@ function VirtualGrid(props) {
936
987
  return () => resizeObserver.disconnect();
937
988
  }
938
989
  }, []);
990
+ const selectableCount = React2.useMemo(() => {
991
+ return selectableRow ? data.filter(selectableRow).length : data.length;
992
+ }, [data, selectableRow]);
939
993
  React2.useEffect(() => {
940
994
  if (headerCheckboxRef.current) {
941
995
  const hasSelected = selectedRows.length > 0;
942
- const hasUnselected = selectedRows.length < data.length;
996
+ const hasUnselected = selectedRows.length < selectableCount;
943
997
  headerCheckboxRef.current.indeterminate = hasSelected && hasUnselected;
944
998
  }
945
- }, [selectedRows.length, data.length]);
999
+ }, [selectedRows.length, selectableCount]);
946
1000
  const itemCount = data.length;
947
1001
  const { rowHeight, overscan } = virtualization;
948
1002
  const startIndex = Math.max(0, Math.floor(scrollTop / rowHeight) - overscan);
@@ -1017,7 +1071,7 @@ function VirtualGrid(props) {
1017
1071
  ref: headerCheckboxRef,
1018
1072
  type: "checkbox",
1019
1073
  className: "lookup-select__checkbox",
1020
- checked: selectedRows.length === data.length && data.length > 0,
1074
+ checked: selectedRows.length === selectableCount && selectableCount > 0,
1021
1075
  onChange: (e) => {
1022
1076
  if (e.target.checked) {
1023
1077
  data.forEach((row) => {
@@ -1100,9 +1154,11 @@ function VirtualRowComponent({
1100
1154
  item,
1101
1155
  columns,
1102
1156
  mode,
1157
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1103
1158
  mapper,
1104
1159
  isSelected,
1105
1160
  isSelectable,
1161
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1106
1162
  onToggle,
1107
1163
  onCheckboxChange,
1108
1164
  onClick
@@ -1144,13 +1200,14 @@ var VirtualRow = React2.memo(
1144
1200
  );
1145
1201
 
1146
1202
  // src/components/Pagination.tsx
1147
- import { Fragment, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1203
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1148
1204
  function Pagination({
1149
1205
  currentPage,
1150
1206
  totalCount,
1151
1207
  pageSize,
1152
1208
  onPageChange,
1153
- className
1209
+ className,
1210
+ i18n
1154
1211
  }) {
1155
1212
  const totalPages = Math.ceil(totalCount / pageSize);
1156
1213
  if (totalPages <= 1) {
@@ -1180,14 +1237,7 @@ function Pagination({
1180
1237
  const startRecord = (currentPage - 1) * pageSize + 1;
1181
1238
  const endRecord = Math.min(currentPage * pageSize, totalCount);
1182
1239
  return /* @__PURE__ */ jsxs5("div", { className: `lookup-select__pagination ${className || ""}`, children: [
1183
- /* @__PURE__ */ jsx5("div", { className: "lookup-select__pagination-info", children: totalCount > 0 ? /* @__PURE__ */ jsxs5(Fragment, { children: [
1184
- startRecord,
1185
- "-",
1186
- endRecord,
1187
- " / ",
1188
- totalCount,
1189
- " records"
1190
- ] }) : "0 records" }),
1240
+ /* @__PURE__ */ jsx5("div", { className: "lookup-select__pagination-info", children: totalCount > 0 ? i18n?.recordsInfo ? i18n.recordsInfo(startRecord, endRecord, totalCount) : `${startRecord}-${endRecord} / ${totalCount} records` : "0 records" }),
1191
1241
  /* @__PURE__ */ jsxs5("div", { className: "lookup-select__pagination-controls", children: [
1192
1242
  /* @__PURE__ */ jsx5(
1193
1243
  "button",
@@ -1196,7 +1246,7 @@ function Pagination({
1196
1246
  className: "lookup-select__pagination-button",
1197
1247
  disabled: currentPage === 1,
1198
1248
  onClick: () => onPageChange(currentPage - 1),
1199
- "aria-label": "Previous page",
1249
+ "aria-label": i18n?.previousPage || "Previous page",
1200
1250
  children: "\u2039"
1201
1251
  }
1202
1252
  ),
@@ -1221,7 +1271,7 @@ function Pagination({
1221
1271
  ${currentPage === pageNum ? "lookup-select__pagination-button--active" : ""}
1222
1272
  `,
1223
1273
  onClick: () => onPageChange(pageNum),
1224
- "aria-label": `Sayfa ${pageNum}`,
1274
+ "aria-label": i18n?.pageLabel ? i18n.pageLabel(pageNum) : `Page ${pageNum}`,
1225
1275
  "aria-current": currentPage === pageNum ? "page" : void 0,
1226
1276
  children: pageNum
1227
1277
  },
@@ -1235,7 +1285,7 @@ function Pagination({
1235
1285
  className: "lookup-select__pagination-button",
1236
1286
  disabled: currentPage === totalPages,
1237
1287
  onClick: () => onPageChange(currentPage + 1),
1238
- "aria-label": "Next page",
1288
+ "aria-label": i18n?.nextPage || "Next page",
1239
1289
  children: "\u203A"
1240
1290
  }
1241
1291
  )
@@ -1244,7 +1294,7 @@ function Pagination({
1244
1294
  }
1245
1295
 
1246
1296
  // src/components/LookupSelect.tsx
1247
- import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1297
+ import { Fragment, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1248
1298
  function LookupSelect(props) {
1249
1299
  const {
1250
1300
  columns,
@@ -1258,7 +1308,6 @@ function LookupSelect(props) {
1258
1308
  renderTrigger,
1259
1309
  renderModal,
1260
1310
  renderGrid,
1261
- renderHeader,
1262
1311
  renderFooter,
1263
1312
  renderSearch,
1264
1313
  renderPagination,
@@ -1278,7 +1327,6 @@ function LookupSelect(props) {
1278
1327
  const {
1279
1328
  modalOpen,
1280
1329
  openModal,
1281
- closeModal,
1282
1330
  currentSelections,
1283
1331
  toggleRowSelection,
1284
1332
  clearSelections,
@@ -1323,11 +1371,10 @@ function LookupSelect(props) {
1323
1371
  virtualRowHeight,
1324
1372
  pageSize
1325
1373
  ]);
1326
- const { announce } = useScreenReaderAnnouncements();
1374
+ useScreenReaderAnnouncements();
1327
1375
  useFocusManagement(modalOpen);
1328
1376
  useKeyboardNavigation({
1329
1377
  isModalOpen: modalOpen,
1330
- onClose: closeModal,
1331
1378
  onConfirm: confirmSelection,
1332
1379
  currentData: [],
1333
1380
  selectedRows: currentSelections,
@@ -1339,13 +1386,34 @@ function LookupSelect(props) {
1339
1386
  const [serverData, setServerData] = React3.useState([]);
1340
1387
  const [totalCount, setTotalCount] = React3.useState(0);
1341
1388
  const [currentPage, setCurrentPage] = React3.useState(1);
1389
+ const [sortState, setSortState] = React3.useState({});
1390
+ const [debouncedSearch, setDebouncedSearch] = React3.useState("");
1391
+ const debounceTimerRef = React3.useRef(null);
1342
1392
  const handleSearchChange = React3.useCallback(
1343
1393
  (value) => {
1344
1394
  setSearchValue(value);
1345
- updateQuery({ search: value, page: 1 });
1395
+ if (dataSource) {
1396
+ if (debounceTimerRef.current) {
1397
+ clearTimeout(debounceTimerRef.current);
1398
+ }
1399
+ debounceTimerRef.current = setTimeout(() => {
1400
+ setDebouncedSearch(value);
1401
+ setCurrentPage(1);
1402
+ updateQuery({ search: value, page: 1 });
1403
+ }, 300);
1404
+ } else {
1405
+ updateQuery({ search: value, page: 1 });
1406
+ }
1346
1407
  },
1347
- [updateQuery]
1408
+ [updateQuery, dataSource]
1348
1409
  );
1410
+ React3.useEffect(() => {
1411
+ return () => {
1412
+ if (debounceTimerRef.current) {
1413
+ clearTimeout(debounceTimerRef.current);
1414
+ }
1415
+ };
1416
+ }, []);
1349
1417
  const filteredData = React3.useMemo(() => {
1350
1418
  if (!searchValue || dataSource) {
1351
1419
  return data;
@@ -1363,13 +1431,14 @@ function LookupSelect(props) {
1363
1431
  setLoading(true);
1364
1432
  setError(void 0);
1365
1433
  try {
1366
- const query = getCurrentQuery();
1367
- const result = await dataSource({
1368
- ...query,
1369
- search: searchValue,
1434
+ const query = {
1435
+ search: debouncedSearch,
1370
1436
  page: currentPage,
1371
- pageSize: optimizedPageSize
1372
- });
1437
+ pageSize: optimizedPageSize,
1438
+ sortBy: sortState.sortBy,
1439
+ sortDir: sortState.sortDir
1440
+ };
1441
+ const result = await dataSource(query);
1373
1442
  setServerData(result.rows);
1374
1443
  setTotalCount(result.total);
1375
1444
  setLoading(false);
@@ -1381,10 +1450,10 @@ function LookupSelect(props) {
1381
1450
  }
1382
1451
  }, [
1383
1452
  dataSource,
1384
- getCurrentQuery,
1385
- searchValue,
1453
+ debouncedSearch,
1386
1454
  currentPage,
1387
- optimizedPageSize
1455
+ optimizedPageSize,
1456
+ sortState
1388
1457
  ]);
1389
1458
  React3.useEffect(() => {
1390
1459
  if (dataSource && modalOpen) {
@@ -1400,8 +1469,9 @@ function LookupSelect(props) {
1400
1469
  );
1401
1470
  const handleSortChange = React3.useCallback(
1402
1471
  (sortBy, sortDir) => {
1403
- updateQuery({ sortBy, sortDir, page: 1 });
1472
+ setSortState({ sortBy, sortDir });
1404
1473
  setCurrentPage(1);
1474
+ updateQuery({ sortBy, sortDir, page: 1 });
1405
1475
  },
1406
1476
  [updateQuery]
1407
1477
  );
@@ -1438,11 +1508,12 @@ function LookupSelect(props) {
1438
1508
  loading,
1439
1509
  error,
1440
1510
  emptyText: texts.emptyText,
1511
+ onRetry: dataSource ? loadServerData : void 0,
1441
1512
  className: classNames?.grid,
1442
1513
  style: styles?.grid,
1443
1514
  currentSort: dataSource ? {
1444
- sortBy: getCurrentQuery().sortBy || "",
1445
- sortDir: getCurrentQuery().sortDir || "asc"
1515
+ sortBy: sortState.sortBy || "",
1516
+ sortDir: sortState.sortDir || "asc"
1446
1517
  } : void 0,
1447
1518
  onSort: dataSource ? handleSortChange : void 0
1448
1519
  };
@@ -1477,7 +1548,7 @@ function LookupSelect(props) {
1477
1548
  virtualizationConfig
1478
1549
  ]);
1479
1550
  const renderModalContent = () => {
1480
- const defaultContent = /* @__PURE__ */ jsxs6(Fragment2, { children: [
1551
+ const defaultContent = /* @__PURE__ */ jsxs6(Fragment, { children: [
1481
1552
  renderSearch ? renderSearch({
1482
1553
  value: searchValue,
1483
1554
  onChange: handleSearchChange,
@@ -1499,12 +1570,14 @@ function LookupSelect(props) {
1499
1570
  selectedIds: currentSelections.map(mapper.getId),
1500
1571
  onRowSelect: toggleRowSelection,
1501
1572
  onSelectAll: mode === "multiple" ? () => {
1502
- const allIds = currentData.map(mapper.getId);
1503
- const selectedIds = currentSelections.map(mapper.getId);
1504
- if (allIds.length === selectedIds.length) {
1573
+ const selectableData = selectableRow ? currentData.filter(selectableRow) : currentData;
1574
+ const allSelected = selectableData.every(
1575
+ (row) => isRowSelected(row)
1576
+ );
1577
+ if (allSelected) {
1505
1578
  clearSelections();
1506
1579
  } else {
1507
- currentData.forEach((row) => {
1580
+ selectableData.forEach((row) => {
1508
1581
  if (!isRowSelected(row)) {
1509
1582
  toggleRowSelection(row);
1510
1583
  }
@@ -1519,8 +1592,8 @@ function LookupSelect(props) {
1519
1592
  virtualization: shouldUseVirtualization ? virtualizationConfig : void 0,
1520
1593
  selectableRow,
1521
1594
  onSortChange: handleSortChange,
1522
- sortBy: getCurrentQuery().sortBy,
1523
- sortDir: getCurrentQuery().sortDir
1595
+ sortBy: sortState.sortBy,
1596
+ sortDir: sortState.sortDir
1524
1597
  }) : renderGridComponent(),
1525
1598
  dataSource && !renderPagination && /* @__PURE__ */ jsx6(
1526
1599
  Pagination,
@@ -1528,7 +1601,12 @@ function LookupSelect(props) {
1528
1601
  currentPage,
1529
1602
  totalCount,
1530
1603
  pageSize: optimizedPageSize,
1531
- onPageChange: handlePageChange
1604
+ onPageChange: handlePageChange,
1605
+ i18n: {
1606
+ previousPage: i18n?.previousPage,
1607
+ nextPage: i18n?.nextPage,
1608
+ pageLabel: i18n?.paginationInfo ? (pageNum) => i18n.paginationInfo(pageNum, Math.ceil(totalCount / optimizedPageSize)) : void 0
1609
+ }
1532
1610
  }
1533
1611
  ),
1534
1612
  dataSource && renderPagination && renderPagination({
@@ -1536,7 +1614,7 @@ function LookupSelect(props) {
1536
1614
  totalPages: Math.ceil(totalCount / optimizedPageSize),
1537
1615
  onPageChange: handlePageChange,
1538
1616
  pageSize: optimizedPageSize,
1539
- onPageSizeChange: (newSize) => {
1617
+ onPageSizeChange: (_newSize) => {
1540
1618
  },
1541
1619
  totalCount,
1542
1620
  loading