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 +21 -21
- package/dist/index.cjs +181 -115
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -16
- package/dist/index.d.ts +3 -16
- package/dist/index.js +188 -122
- package/dist/index.js.map +1 -1
- package/dist/styles.css +957 -862
- package/package.json +83 -79
- package/readme.md +440 -440
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
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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(
|
|
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
|
|
590
|
-
|
|
606
|
+
const autoFocusElement = modalRef.current?.querySelector(
|
|
607
|
+
"[data-autofocus]"
|
|
591
608
|
);
|
|
592
|
-
if (
|
|
593
|
-
|
|
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
|
|
626
|
+
const focusableElements = modalRef.current?.querySelectorAll(
|
|
603
627
|
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
604
628
|
);
|
|
605
|
-
if (!
|
|
606
|
-
const firstElement =
|
|
607
|
-
const lastElement =
|
|
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:
|
|
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.
|
|
795
|
-
"
|
|
796
|
-
|
|
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
|
|
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
|
|
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 <
|
|
1032
|
+
const hasUnselected = selectedRows.length < selectableCount;
|
|
991
1033
|
headerCheckboxRef.current.indeterminate = hasSelected && hasUnselected;
|
|
992
1034
|
}
|
|
993
|
-
}, [selectedRows.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 ===
|
|
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 ?
|
|
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": `
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
1415
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
1493
|
-
sortDir:
|
|
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
|
|
1551
|
-
const
|
|
1552
|
-
|
|
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
|
-
|
|
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:
|
|
1571
|
-
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: (
|
|
1653
|
+
onPageSizeChange: (_newSize) => {
|
|
1588
1654
|
},
|
|
1589
1655
|
totalCount,
|
|
1590
1656
|
loading
|