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/LICENSE +21 -21
- package/dist/index.cjs +200 -122
- 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 +207 -129
- package/dist/index.js.map +1 -1
- package/dist/styles.css +957 -862
- package/package.json +83 -79
- package/readme.md +440 -440
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,
|
|
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
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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
|
-
|
|
229
|
+
selectionManager.toggleRow(row);
|
|
221
230
|
const selectedRows = selectionManager.getSelectedRows();
|
|
222
231
|
setCurrentSelections(selectedRows);
|
|
223
|
-
|
|
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
|
-
|
|
231
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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,
|
|
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(
|
|
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 =
|
|
537
|
-
const previousFocusRef =
|
|
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
|
|
542
|
-
|
|
570
|
+
const autoFocusElement = modalRef.current?.querySelector(
|
|
571
|
+
"[data-autofocus]"
|
|
543
572
|
);
|
|
544
|
-
if (
|
|
545
|
-
|
|
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
|
|
590
|
+
const focusableElements = modalRef.current?.querySelectorAll(
|
|
555
591
|
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
556
592
|
);
|
|
557
|
-
if (!
|
|
558
|
-
const firstElement =
|
|
559
|
-
const lastElement =
|
|
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:
|
|
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__ */
|
|
747
|
-
"
|
|
748
|
-
|
|
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
|
|
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
|
|
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 <
|
|
996
|
+
const hasUnselected = selectedRows.length < selectableCount;
|
|
943
997
|
headerCheckboxRef.current.indeterminate = hasSelected && hasUnselected;
|
|
944
998
|
}
|
|
945
|
-
}, [selectedRows.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 ===
|
|
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 {
|
|
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 ?
|
|
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": `
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
1367
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
1445
|
-
sortDir:
|
|
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(
|
|
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
|
|
1503
|
-
const
|
|
1504
|
-
|
|
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
|
-
|
|
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:
|
|
1523
|
-
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: (
|
|
1617
|
+
onPageSizeChange: (_newSize) => {
|
|
1540
1618
|
},
|
|
1541
1619
|
totalCount,
|
|
1542
1620
|
loading
|