react-lookup-select 1.0.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 ADDED
@@ -0,0 +1,1567 @@
1
+ // src/components/LookupSelect.tsx
2
+ import React3 from "react";
3
+
4
+ // src/internal/state.ts
5
+ import { useState, useCallback, useMemo, useEffect } from "react";
6
+
7
+ // src/internal/core.ts
8
+ function createSelectionState() {
9
+ return {
10
+ selectedIds: /* @__PURE__ */ new Set(),
11
+ selectedRows: /* @__PURE__ */ new Map()
12
+ };
13
+ }
14
+ var SelectionManager = class {
15
+ constructor(mode, mapper, initialState) {
16
+ this.mode = mode;
17
+ this.mapper = mapper;
18
+ this.state = initialState || createSelectionState();
19
+ }
20
+ /**
21
+ * Row selection/deselection
22
+ */
23
+ toggleRow(row) {
24
+ const id = this.mapper.getId(row);
25
+ const newState = {
26
+ selectedIds: new Set(this.state.selectedIds),
27
+ selectedRows: new Map(this.state.selectedRows)
28
+ };
29
+ if (this.state.selectedIds.has(id)) {
30
+ newState.selectedIds.delete(id);
31
+ newState.selectedRows.delete(id);
32
+ } else {
33
+ if (this.mode === "single") {
34
+ newState.selectedIds.clear();
35
+ newState.selectedRows.clear();
36
+ }
37
+ newState.selectedIds.add(id);
38
+ newState.selectedRows.set(id, row);
39
+ }
40
+ this.state = newState;
41
+ return newState;
42
+ }
43
+ /**
44
+ * Return selected rows as array
45
+ */
46
+ getSelectedRows() {
47
+ return Array.from(this.state.selectedRows.values());
48
+ }
49
+ /**
50
+ * Check if row is selected
51
+ */
52
+ isRowSelected(row) {
53
+ const id = this.mapper.getId(row);
54
+ return this.state.selectedIds.has(id);
55
+ }
56
+ /**
57
+ * Clear all selections
58
+ */
59
+ clearSelection() {
60
+ this.state = createSelectionState();
61
+ return this.state;
62
+ }
63
+ /**
64
+ * Get current state
65
+ */
66
+ getState() {
67
+ return this.state;
68
+ }
69
+ };
70
+ function createQueryState(pageSize = 20) {
71
+ return {
72
+ page: 1,
73
+ pageSize,
74
+ search: void 0,
75
+ sortBy: void 0,
76
+ sortDir: void 0
77
+ };
78
+ }
79
+ var QueryManager = class {
80
+ constructor(initialState) {
81
+ this.state = { ...createQueryState(), ...initialState };
82
+ }
83
+ /**
84
+ * Update search term - returns to page 1
85
+ */
86
+ updateSearch(search) {
87
+ this.state = { ...this.state, search, page: 1 };
88
+ return this.state;
89
+ }
90
+ /**
91
+ * Update pagination
92
+ */
93
+ updatePage(page) {
94
+ this.state = { ...this.state, page };
95
+ return this.state;
96
+ }
97
+ /**
98
+ * Update sorting - returns to page 1
99
+ */
100
+ updateSort(sortBy, sortDir) {
101
+ this.state = { ...this.state, sortBy, sortDir, page: 1 };
102
+ return this.state;
103
+ }
104
+ /**
105
+ * Update page size - returns to page 1
106
+ */
107
+ updatePageSize(pageSize) {
108
+ this.state = { ...this.state, pageSize, page: 1 };
109
+ return this.state;
110
+ }
111
+ /**
112
+ * Get current state
113
+ */
114
+ getState() {
115
+ return this.state;
116
+ }
117
+ };
118
+ function mapReturnValue(selectedRows, props) {
119
+ const { returnShape = "id-text", returnMap, mapper, mode = "single" } = props;
120
+ if (selectedRows.length === 0) {
121
+ return mode === "single" ? null : [];
122
+ }
123
+ let mappedValues;
124
+ switch (returnShape) {
125
+ case "id-text":
126
+ mappedValues = selectedRows.map((row) => ({
127
+ id: mapper.getId(row),
128
+ text: mapper.getText(row)
129
+ }));
130
+ break;
131
+ case "row":
132
+ mappedValues = selectedRows;
133
+ break;
134
+ case "custom":
135
+ if (!returnMap) {
136
+ throw new Error('returnMap is required when returnShape is "custom"');
137
+ }
138
+ mappedValues = selectedRows.map((row) => returnMap.map(row));
139
+ break;
140
+ default:
141
+ throw new Error(`Unknown returnShape: ${returnShape}`);
142
+ }
143
+ return mode === "single" ? mappedValues[0] : mappedValues;
144
+ }
145
+
146
+ // src/internal/state.ts
147
+ function useLookupSelectState(props) {
148
+ const {
149
+ mode = "single",
150
+ mapper,
151
+ returnShape = "id-text",
152
+ returnMap,
153
+ value,
154
+ defaultValue,
155
+ onChange,
156
+ open,
157
+ defaultOpen = false,
158
+ onOpenChange,
159
+ pageSize = 20,
160
+ onQueryChange,
161
+ onSelectionChange,
162
+ onConfirm,
163
+ onCancel,
164
+ data
165
+ } = props;
166
+ const [internalModalOpen, setInternalModalOpen] = useState(defaultOpen);
167
+ const modalOpen = open !== void 0 ? open : internalModalOpen;
168
+ const handleModalOpenChange = useCallback(
169
+ (newOpen) => {
170
+ if (open === void 0) {
171
+ setInternalModalOpen(newOpen);
172
+ }
173
+ onOpenChange?.(newOpen);
174
+ },
175
+ [open, onOpenChange]
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]);
183
+ const [currentSelections, setCurrentSelections] = useState([]);
184
+ const resolveValueToRows = useCallback(
185
+ (valueToResolve) => {
186
+ if (!valueToResolve || !data) return [];
187
+ const values = Array.isArray(valueToResolve) ? valueToResolve : [valueToResolve];
188
+ const resolvedRows = [];
189
+ for (const val of values) {
190
+ if (typeof val === "object" && val !== null && mapper.getId(val) !== void 0) {
191
+ resolvedRows.push(val);
192
+ } else {
193
+ const searchId = typeof val === "object" && val !== null && "id" in val ? val.id : val;
194
+ const matchingRow = data.find(
195
+ (row) => mapper.getId(row) === searchId
196
+ );
197
+ if (matchingRow) {
198
+ resolvedRows.push(matchingRow);
199
+ }
200
+ }
201
+ }
202
+ return resolvedRows;
203
+ },
204
+ [data, mapper]
205
+ );
206
+ useEffect(() => {
207
+ const initialValue = value !== void 0 ? value : defaultValue;
208
+ if (!initialValue || !data) return;
209
+ const resolvedSelections = resolveValueToRows(initialValue);
210
+ if (resolvedSelections.length > 0) {
211
+ selectionManager.clearSelection();
212
+ for (const row of resolvedSelections) {
213
+ selectionManager.toggleRow(row);
214
+ }
215
+ setCurrentSelections(resolvedSelections);
216
+ }
217
+ }, [value, defaultValue, data, resolveValueToRows, selectionManager]);
218
+ const toggleRowSelection = useCallback(
219
+ (row) => {
220
+ const newState = selectionManager.toggleRow(row);
221
+ const selectedRows = selectionManager.getSelectedRows();
222
+ setCurrentSelections(selectedRows);
223
+ onSelectionChange?.(selectedRows);
224
+ },
225
+ [selectionManager, onSelectionChange]
226
+ );
227
+ const clearSelections = useCallback(() => {
228
+ selectionManager.clearSelection();
229
+ setCurrentSelections([]);
230
+ onSelectionChange?.([]);
231
+ }, [selectionManager, onSelectionChange]);
232
+ const isRowSelected = useCallback(
233
+ (row) => {
234
+ return selectionManager.isRowSelected(row);
235
+ },
236
+ [selectionManager]
237
+ );
238
+ const updateQuery = useCallback(
239
+ (updates) => {
240
+ const currentState = queryManager.getState();
241
+ const newState = { ...currentState, ...updates };
242
+ if (updates.search !== void 0) {
243
+ queryManager.updateSearch(updates.search);
244
+ }
245
+ if (updates.page !== void 0) {
246
+ queryManager.updatePage(updates.page);
247
+ }
248
+ if (updates.sortBy !== void 0 && updates.sortDir !== void 0) {
249
+ queryManager.updateSort(updates.sortBy, updates.sortDir);
250
+ }
251
+ if (updates.pageSize !== void 0) {
252
+ queryManager.updatePageSize(updates.pageSize);
253
+ }
254
+ onQueryChange?.(queryManager.getState());
255
+ },
256
+ [queryManager, onQueryChange]
257
+ );
258
+ const confirmSelection = useCallback(() => {
259
+ const selectedRows = selectionManager.getSelectedRows();
260
+ const returnValue = mapReturnValue(selectedRows, {
261
+ mode,
262
+ mapper,
263
+ returnShape,
264
+ returnMap
265
+ });
266
+ onChange?.(returnValue);
267
+ onConfirm?.(returnValue);
268
+ handleModalOpenChange(false);
269
+ }, [
270
+ selectionManager,
271
+ mode,
272
+ mapper,
273
+ returnShape,
274
+ returnMap,
275
+ onChange,
276
+ onConfirm,
277
+ handleModalOpenChange
278
+ ]);
279
+ const cancelSelection = useCallback(() => {
280
+ selectionManager.clearSelection();
281
+ setCurrentSelections([]);
282
+ onCancel?.();
283
+ handleModalOpenChange(false);
284
+ }, [selectionManager, onCancel, handleModalOpenChange]);
285
+ const getCurrentQuery = useCallback(() => {
286
+ return queryManager.getState();
287
+ }, [queryManager]);
288
+ return {
289
+ // Modal state
290
+ modalOpen,
291
+ openModal: () => handleModalOpenChange(true),
292
+ closeModal: () => handleModalOpenChange(false),
293
+ // Selection state
294
+ currentSelections,
295
+ toggleRowSelection,
296
+ clearSelections,
297
+ isRowSelected,
298
+ // Query state
299
+ updateQuery,
300
+ getCurrentQuery,
301
+ // Actions
302
+ confirmSelection,
303
+ cancelSelection
304
+ };
305
+ }
306
+
307
+ // src/hooks/useAccessibility.ts
308
+ import { useEffect as useEffect2, useCallback as useCallback2 } from "react";
309
+ function useKeyboardNavigation({
310
+ isModalOpen,
311
+ onClose,
312
+ onConfirm,
313
+ onRowSelect,
314
+ currentData,
315
+ selectedRows,
316
+ mode
317
+ }) {
318
+ const handleKeyDown = useCallback2(
319
+ (event) => {
320
+ if (!isModalOpen) return;
321
+ switch (event.key) {
322
+ case "Escape":
323
+ event.preventDefault();
324
+ onClose();
325
+ break;
326
+ case "Enter":
327
+ if (event.ctrlKey || event.metaKey) {
328
+ event.preventDefault();
329
+ onConfirm?.();
330
+ }
331
+ break;
332
+ case "ArrowDown":
333
+ case "ArrowUp":
334
+ break;
335
+ default:
336
+ break;
337
+ }
338
+ },
339
+ [isModalOpen, onClose, onConfirm]
340
+ );
341
+ useEffect2(() => {
342
+ if (isModalOpen) {
343
+ document.addEventListener("keydown", handleKeyDown);
344
+ return () => {
345
+ document.removeEventListener("keydown", handleKeyDown);
346
+ };
347
+ }
348
+ }, [isModalOpen, handleKeyDown]);
349
+ return {
350
+ handleKeyDown
351
+ };
352
+ }
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]);
383
+ }
384
+ function useScreenReaderAnnouncements() {
385
+ const announce = useCallback2(
386
+ (message, priority = "polite") => {
387
+ const announcement = document.createElement("div");
388
+ announcement.setAttribute("aria-live", priority);
389
+ announcement.setAttribute("aria-atomic", "true");
390
+ announcement.className = "sr-only";
391
+ announcement.textContent = message;
392
+ document.body.appendChild(announcement);
393
+ setTimeout(() => {
394
+ document.body.removeChild(announcement);
395
+ }, 1e3);
396
+ },
397
+ []
398
+ );
399
+ return { announce };
400
+ }
401
+
402
+ // src/components/Trigger.tsx
403
+ import { jsx, jsxs } from "react/jsx-runtime";
404
+ function Trigger({
405
+ isOpen,
406
+ onToggle,
407
+ selectedItems,
408
+ placeholder = "Please select",
409
+ icon,
410
+ mapper,
411
+ mode = "single",
412
+ className,
413
+ style,
414
+ renderTrigger,
415
+ onClear,
416
+ onRemoveTag
417
+ }) {
418
+ const selectedValue = mode === "single" ? selectedItems[0] || null : selectedItems;
419
+ if (renderTrigger) {
420
+ return /* @__PURE__ */ jsx(
421
+ "div",
422
+ {
423
+ role: "button",
424
+ tabIndex: 0,
425
+ "aria-haspopup": "dialog",
426
+ "aria-expanded": isOpen,
427
+ onClick: onToggle,
428
+ onKeyDown: (e) => {
429
+ if (e.key === "Enter" || e.key === " ") {
430
+ e.preventDefault();
431
+ onToggle();
432
+ }
433
+ },
434
+ className,
435
+ style,
436
+ children: renderTrigger(selectedValue)
437
+ }
438
+ );
439
+ }
440
+ return /* @__PURE__ */ jsxs(
441
+ "div",
442
+ {
443
+ role: "button",
444
+ tabIndex: 0,
445
+ "aria-haspopup": "dialog",
446
+ "aria-expanded": isOpen,
447
+ className: `lookup-select__trigger ${className || ""}`,
448
+ style,
449
+ onClick: onToggle,
450
+ onKeyDown: (e) => {
451
+ if (e.key === "Enter" || e.key === " ") {
452
+ e.preventDefault();
453
+ onToggle();
454
+ }
455
+ },
456
+ children: [
457
+ /* @__PURE__ */ jsx("div", { className: "lookup-select__trigger-content", children: selectedItems.length === 0 ? /* @__PURE__ */ jsx("span", { className: "lookup-select__placeholder", children: placeholder }) : mode === "single" ? /* @__PURE__ */ jsx("span", { className: "lookup-select__selected-text", children: mapper.getText(selectedItems[0]) }) : /* @__PURE__ */ jsxs("div", { className: "lookup-select__tags", children: [
458
+ selectedItems.slice(0, 3).map((item) => /* @__PURE__ */ jsxs("span", { className: "lookup-select__tag", children: [
459
+ mapper.getText(item),
460
+ /* @__PURE__ */ jsx(
461
+ "button",
462
+ {
463
+ type: "button",
464
+ className: "lookup-select__tag-remove",
465
+ onClick: (e) => {
466
+ e.stopPropagation();
467
+ onRemoveTag?.(item);
468
+ },
469
+ "aria-label": `Remove ${mapper.getText(item)}`,
470
+ children: "\xD7"
471
+ }
472
+ )
473
+ ] }, mapper.getId(item))),
474
+ selectedItems.length > 3 && /* @__PURE__ */ jsxs("span", { className: "lookup-select__tag lookup-select__tag--more", children: [
475
+ "+",
476
+ selectedItems.length - 3,
477
+ " more"
478
+ ] })
479
+ ] }) }),
480
+ /* @__PURE__ */ jsxs("div", { className: "lookup-select__trigger-actions", children: [
481
+ selectedItems.length > 0 && onClear && /* @__PURE__ */ jsx(
482
+ "button",
483
+ {
484
+ type: "button",
485
+ className: "lookup-select__clear-button",
486
+ onClick: (e) => {
487
+ e.stopPropagation();
488
+ onClear();
489
+ },
490
+ "aria-label": "Clear selection",
491
+ children: /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ jsx(
492
+ "path",
493
+ {
494
+ d: "M10.5 5.5L5.5 10.5M5.5 5.5L10.5 10.5",
495
+ stroke: "currentColor",
496
+ strokeWidth: "1.5",
497
+ strokeLinecap: "round",
498
+ strokeLinejoin: "round"
499
+ }
500
+ ) })
501
+ }
502
+ ),
503
+ /* @__PURE__ */ jsx("div", { className: "lookup-select__trigger-icon", children: icon || /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ jsx(
504
+ "path",
505
+ {
506
+ d: "M4 6L8 10L12 6",
507
+ stroke: "currentColor",
508
+ strokeWidth: "1.5",
509
+ fill: "none",
510
+ strokeLinecap: "round",
511
+ strokeLinejoin: "round"
512
+ }
513
+ ) }) })
514
+ ] })
515
+ ]
516
+ }
517
+ );
518
+ }
519
+
520
+ // src/components/Modal.tsx
521
+ import { useEffect as useEffect3, useRef } from "react";
522
+ import { createPortal } from "react-dom";
523
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
524
+ function Modal({
525
+ isOpen,
526
+ onClose,
527
+ title = "Select Record",
528
+ className,
529
+ style,
530
+ children,
531
+ ariaLabel,
532
+ ariaLabelledBy,
533
+ ariaDescribedBy,
534
+ closeButtonLabel = "Close modal"
535
+ }) {
536
+ const modalRef = useRef(null);
537
+ const previousFocusRef = useRef(null);
538
+ useEffect3(() => {
539
+ if (!isOpen) return;
540
+ previousFocusRef.current = document.activeElement;
541
+ const focusableElements = modalRef.current?.querySelectorAll(
542
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
543
+ );
544
+ if (focusableElements && focusableElements.length > 0) {
545
+ focusableElements[0].focus();
546
+ }
547
+ const handleEscape = (e) => {
548
+ if (e.key === "Escape") {
549
+ onClose();
550
+ }
551
+ };
552
+ const handleTab = (e) => {
553
+ if (e.key !== "Tab") return;
554
+ const focusableElements2 = modalRef.current?.querySelectorAll(
555
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
556
+ );
557
+ if (!focusableElements2 || focusableElements2.length === 0) return;
558
+ const firstElement = focusableElements2[0];
559
+ const lastElement = focusableElements2[focusableElements2.length - 1];
560
+ if (e.shiftKey) {
561
+ if (document.activeElement === firstElement) {
562
+ lastElement.focus();
563
+ e.preventDefault();
564
+ }
565
+ } else {
566
+ if (document.activeElement === lastElement) {
567
+ firstElement.focus();
568
+ e.preventDefault();
569
+ }
570
+ }
571
+ };
572
+ document.addEventListener("keydown", handleEscape);
573
+ document.addEventListener("keydown", handleTab);
574
+ return () => {
575
+ document.removeEventListener("keydown", handleEscape);
576
+ document.removeEventListener("keydown", handleTab);
577
+ if (previousFocusRef.current) {
578
+ previousFocusRef.current.focus();
579
+ }
580
+ };
581
+ }, [isOpen, onClose]);
582
+ useEffect3(() => {
583
+ if (isOpen) {
584
+ document.body.style.overflow = "hidden";
585
+ } else {
586
+ document.body.style.overflow = "";
587
+ }
588
+ return () => {
589
+ document.body.style.overflow = "";
590
+ };
591
+ }, [isOpen]);
592
+ if (!isOpen) return null;
593
+ return createPortal(
594
+ /* @__PURE__ */ jsx2(
595
+ "div",
596
+ {
597
+ className: "lookup-select__modal-overlay",
598
+ onClick: onClose,
599
+ role: "presentation",
600
+ children: /* @__PURE__ */ jsxs2(
601
+ "div",
602
+ {
603
+ ref: modalRef,
604
+ role: "dialog",
605
+ "aria-modal": "true",
606
+ "aria-label": ariaLabel,
607
+ "aria-labelledby": ariaLabelledBy || (title ? "lookup-select-modal-title" : void 0),
608
+ "aria-describedby": ariaDescribedBy,
609
+ className: `lookup-select__modal ${className || ""}`,
610
+ style,
611
+ onClick: (e) => e.stopPropagation(),
612
+ tabIndex: -1,
613
+ children: [
614
+ /* @__PURE__ */ jsxs2("div", { className: "lookup-select__modal-header", children: [
615
+ /* @__PURE__ */ jsx2(
616
+ "h2",
617
+ {
618
+ id: "lookup-select-modal-title",
619
+ className: "lookup-select__modal-title",
620
+ children: title
621
+ }
622
+ ),
623
+ /* @__PURE__ */ jsx2(
624
+ "button",
625
+ {
626
+ type: "button",
627
+ className: "lookup-select__modal-close",
628
+ onClick: onClose,
629
+ "aria-label": closeButtonLabel,
630
+ tabIndex: 0,
631
+ children: "\xD7"
632
+ }
633
+ )
634
+ ] }),
635
+ /* @__PURE__ */ jsx2("div", { className: "lookup-select__modal-body", role: "document", children })
636
+ ]
637
+ }
638
+ )
639
+ }
640
+ ),
641
+ document.body
642
+ );
643
+ }
644
+ function SearchInput({
645
+ value,
646
+ onChange,
647
+ placeholder = "Search...",
648
+ className
649
+ }) {
650
+ const inputRef = useRef(null);
651
+ useEffect3(() => {
652
+ if (inputRef.current) {
653
+ inputRef.current.focus();
654
+ }
655
+ }, []);
656
+ return /* @__PURE__ */ jsxs2("div", { className: `lookup-select__search ${className || ""}`, children: [
657
+ /* @__PURE__ */ jsx2(
658
+ "input",
659
+ {
660
+ ref: inputRef,
661
+ type: "text",
662
+ value,
663
+ onChange: (e) => onChange(e.target.value),
664
+ placeholder,
665
+ className: "lookup-select__search-input",
666
+ "aria-label": "Search records"
667
+ }
668
+ ),
669
+ /* @__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" }) }) })
670
+ ] });
671
+ }
672
+ function ModalFooter({
673
+ onConfirm,
674
+ onCancel,
675
+ confirmText = "Apply",
676
+ cancelText = "Cancel",
677
+ selectedCount = 0,
678
+ className
679
+ }) {
680
+ return /* @__PURE__ */ jsxs2("div", { className: `lookup-select__modal-footer ${className || ""}`, children: [
681
+ /* @__PURE__ */ jsx2("div", { className: "lookup-select__selected-count", children: selectedCount > 0 ? `${selectedCount} selected` : "No selection" }),
682
+ /* @__PURE__ */ jsxs2("div", { className: "lookup-select__modal-actions", children: [
683
+ /* @__PURE__ */ jsx2(
684
+ "button",
685
+ {
686
+ type: "button",
687
+ className: "lookup-select__button lookup-select__button--secondary",
688
+ onClick: onCancel,
689
+ children: cancelText
690
+ }
691
+ ),
692
+ /* @__PURE__ */ jsx2(
693
+ "button",
694
+ {
695
+ type: "button",
696
+ className: "lookup-select__button lookup-select__button--primary",
697
+ onClick: onConfirm,
698
+ disabled: selectedCount === 0,
699
+ children: confirmText
700
+ }
701
+ )
702
+ ] })
703
+ ] });
704
+ }
705
+
706
+ // src/components/Grid.tsx
707
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
708
+ function Grid({
709
+ data,
710
+ columns,
711
+ mode,
712
+ mapper,
713
+ selectedRows,
714
+ onRowToggle,
715
+ onRowClick,
716
+ isRowSelected,
717
+ selectableRow,
718
+ onSort,
719
+ currentSort,
720
+ loading = false,
721
+ error,
722
+ emptyText = "No records found",
723
+ className,
724
+ style
725
+ }) {
726
+ const handleSort = (columnKey) => {
727
+ if (!onSort) return;
728
+ const currentSortBy = currentSort?.sortBy;
729
+ const currentSortDir = currentSort?.sortDir;
730
+ let newSortDir = "asc";
731
+ if (currentSortBy === columnKey) {
732
+ newSortDir = currentSortDir === "asc" ? "desc" : "asc";
733
+ }
734
+ onSort(columnKey, newSortDir);
735
+ };
736
+ const getSortIcon = (columnKey) => {
737
+ if (currentSort?.sortBy !== columnKey) {
738
+ return "\u2195";
739
+ }
740
+ return currentSort.sortDir === "asc" ? "\u2191" : "\u2193";
741
+ };
742
+ 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..." }) }) });
744
+ }
745
+ 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
+ ] }) }) });
750
+ }
751
+ if (data.length === 0) {
752
+ return /* @__PURE__ */ jsx3("div", { className: "lookup-select__grid-state", children: /* @__PURE__ */ jsx3("div", { className: "lookup-select__empty", children: /* @__PURE__ */ jsx3("p", { children: emptyText }) }) });
753
+ }
754
+ const handleRowClick = (row, event) => {
755
+ if (event.target.closest(".lookup-select__checkbox")) {
756
+ return;
757
+ }
758
+ if (selectableRow && !selectableRow(row)) {
759
+ return;
760
+ }
761
+ if (mapper.getDisabled?.(row)) {
762
+ return;
763
+ }
764
+ onRowClick?.(row);
765
+ onRowToggle(row);
766
+ };
767
+ const handleCheckboxChange = (row, event) => {
768
+ event.stopPropagation();
769
+ if (selectableRow && !selectableRow(row)) {
770
+ return;
771
+ }
772
+ if (mapper.getDisabled?.(row)) {
773
+ return;
774
+ }
775
+ onRowToggle(row);
776
+ };
777
+ const isRowDisabled = (row) => {
778
+ if (mapper.getDisabled?.(row)) return true;
779
+ if (selectableRow && !selectableRow(row)) return true;
780
+ return false;
781
+ };
782
+ return /* @__PURE__ */ jsx3("div", { className: `lookup-select__grid ${className || ""}`, style, children: /* @__PURE__ */ jsxs3(
783
+ "table",
784
+ {
785
+ className: "lookup-select__table",
786
+ role: "grid",
787
+ "aria-label": "Record list",
788
+ "aria-rowcount": data.length,
789
+ "aria-multiselectable": mode === "multiple",
790
+ children: [
791
+ /* @__PURE__ */ jsx3("thead", { className: "lookup-select__table-head", children: /* @__PURE__ */ jsxs3("tr", { children: [
792
+ mode === "multiple" && /* @__PURE__ */ jsx3("th", { className: "lookup-select__table-header lookup-select__table-header--checkbox", children: /* @__PURE__ */ jsx3("div", { className: "lookup-select__checkbox", children: /* @__PURE__ */ jsx3(
793
+ "input",
794
+ {
795
+ type: "checkbox",
796
+ checked: selectedRows.length > 0 && selectedRows.length === data.filter((row) => !isRowDisabled(row)).length,
797
+ ref: (input) => {
798
+ if (input) {
799
+ input.indeterminate = selectedRows.length > 0 && selectedRows.length < data.filter((row) => !isRowDisabled(row)).length;
800
+ }
801
+ },
802
+ onChange: (e) => {
803
+ const selectableRows = data.filter(
804
+ (row) => !isRowDisabled(row)
805
+ );
806
+ if (e.target.checked) {
807
+ selectableRows.forEach((row) => {
808
+ if (!isRowSelected(row)) {
809
+ onRowToggle(row);
810
+ }
811
+ });
812
+ } else {
813
+ selectedRows.forEach((row) => {
814
+ onRowToggle(row);
815
+ });
816
+ }
817
+ },
818
+ "aria-label": "Select all"
819
+ }
820
+ ) }) }),
821
+ columns.map((column, index) => /* @__PURE__ */ jsx3(
822
+ "th",
823
+ {
824
+ className: "lookup-select__table-header",
825
+ style: { width: column.width },
826
+ children: /* @__PURE__ */ jsxs3("div", { className: "lookup-select__table-header-content", children: [
827
+ column.title,
828
+ column.sortable && /* @__PURE__ */ jsx3(
829
+ "button",
830
+ {
831
+ type: "button",
832
+ className: "lookup-select__sort-button",
833
+ "aria-label": `Sort by ${column.title} column`,
834
+ onClick: () => handleSort(String(column.key)),
835
+ children: getSortIcon(String(column.key))
836
+ }
837
+ )
838
+ ] })
839
+ },
840
+ typeof column.key === "string" ? column.key : String(column.key)
841
+ ))
842
+ ] }) }),
843
+ /* @__PURE__ */ jsx3("tbody", { className: "lookup-select__table-body", children: data.map((row, rowIndex) => {
844
+ const rowId = mapper.getId(row);
845
+ const selected = isRowSelected(row);
846
+ const disabled = isRowDisabled(row);
847
+ return /* @__PURE__ */ jsxs3(
848
+ "tr",
849
+ {
850
+ className: `
851
+ lookup-select__table-row
852
+ ${selected ? "lookup-select__table-row--selected" : ""}
853
+ ${disabled ? "lookup-select__table-row--disabled" : ""}
854
+ `,
855
+ onClick: (e) => !disabled && handleRowClick(row, e),
856
+ role: disabled ? void 0 : "button",
857
+ tabIndex: disabled ? void 0 : 0,
858
+ onKeyDown: (e) => {
859
+ if (disabled) return;
860
+ if (e.key === "Enter" || e.key === " ") {
861
+ e.preventDefault();
862
+ onRowToggle(row);
863
+ }
864
+ },
865
+ "aria-label": disabled ? void 0 : `Select ${mapper.getText(row)} row`,
866
+ children: [
867
+ mode === "multiple" && /* @__PURE__ */ jsx3("td", { className: "lookup-select__table-cell lookup-select__table-cell--checkbox", children: /* @__PURE__ */ jsx3("div", { className: "lookup-select__checkbox", children: /* @__PURE__ */ jsx3(
868
+ "input",
869
+ {
870
+ type: "checkbox",
871
+ checked: selected,
872
+ onChange: (e) => handleCheckboxChange(row, e),
873
+ disabled,
874
+ "aria-label": `Select ${mapper.getText(row)}`
875
+ }
876
+ ) }) }),
877
+ columns.map((column, columnIndex) => {
878
+ const cellKey = `${rowId}-${typeof column.key === "string" ? column.key : String(column.key)}`;
879
+ let cellContent;
880
+ if (column.render) {
881
+ cellContent = column.render(row, rowIndex);
882
+ } else {
883
+ const value = row[column.key];
884
+ cellContent = value?.toString() || "";
885
+ }
886
+ return /* @__PURE__ */ jsx3("td", { className: "lookup-select__table-cell", children: cellContent }, cellKey);
887
+ })
888
+ ]
889
+ },
890
+ rowId
891
+ );
892
+ }) })
893
+ ]
894
+ }
895
+ ) });
896
+ }
897
+
898
+ // src/components/VirtualGrid.tsx
899
+ import React2 from "react";
900
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
901
+ function VirtualGrid(props) {
902
+ const {
903
+ data,
904
+ columns,
905
+ mode,
906
+ mapper,
907
+ selectedRows,
908
+ onRowToggle,
909
+ isRowSelected,
910
+ selectableRow,
911
+ loading,
912
+ error,
913
+ emptyText,
914
+ className,
915
+ style,
916
+ currentSort,
917
+ onSort,
918
+ virtualization
919
+ } = props;
920
+ const containerRef = React2.useRef(null);
921
+ const headerCheckboxRef = React2.useRef(null);
922
+ const [scrollTop, setScrollTop] = React2.useState(0);
923
+ const [actualContainerHeight, setActualContainerHeight] = React2.useState(
924
+ virtualization.containerHeight
925
+ );
926
+ React2.useEffect(() => {
927
+ if (containerRef.current) {
928
+ const updateHeight = () => {
929
+ if (containerRef.current) {
930
+ setActualContainerHeight(containerRef.current.clientHeight);
931
+ }
932
+ };
933
+ updateHeight();
934
+ const resizeObserver = new ResizeObserver(updateHeight);
935
+ resizeObserver.observe(containerRef.current);
936
+ return () => resizeObserver.disconnect();
937
+ }
938
+ }, []);
939
+ React2.useEffect(() => {
940
+ if (headerCheckboxRef.current) {
941
+ const hasSelected = selectedRows.length > 0;
942
+ const hasUnselected = selectedRows.length < data.length;
943
+ headerCheckboxRef.current.indeterminate = hasSelected && hasUnselected;
944
+ }
945
+ }, [selectedRows.length, data.length]);
946
+ const itemCount = data.length;
947
+ const { rowHeight, overscan } = virtualization;
948
+ const startIndex = Math.max(0, Math.floor(scrollTop / rowHeight) - overscan);
949
+ const endIndex = Math.min(
950
+ itemCount - 1,
951
+ Math.floor((scrollTop + actualContainerHeight) / rowHeight) + overscan
952
+ );
953
+ const visibleItems = React2.useMemo(() => {
954
+ const items = [];
955
+ for (let i = startIndex; i <= endIndex; i++) {
956
+ items.push({
957
+ index: i,
958
+ row: data[i],
959
+ style: {
960
+ position: "absolute",
961
+ top: i * rowHeight,
962
+ left: 0,
963
+ right: 0,
964
+ height: rowHeight
965
+ }
966
+ });
967
+ }
968
+ return items;
969
+ }, [startIndex, endIndex, data, rowHeight]);
970
+ const handleScroll = React2.useCallback((e) => {
971
+ setScrollTop(e.currentTarget.scrollTop);
972
+ }, []);
973
+ const totalHeight = itemCount * rowHeight;
974
+ if (loading) {
975
+ return /* @__PURE__ */ jsx4("div", { className: "lookup-select__grid-state", children: /* @__PURE__ */ jsx4("div", { className: "lookup-select__loading", children: /* @__PURE__ */ jsx4("p", { children: "Loading..." }) }) });
976
+ }
977
+ if (error) {
978
+ return /* @__PURE__ */ jsx4("div", { className: "lookup-select__grid-state", children: /* @__PURE__ */ jsx4("div", { className: "lookup-select__error", children: /* @__PURE__ */ jsxs4("p", { children: [
979
+ "Error: ",
980
+ error
981
+ ] }) }) });
982
+ }
983
+ if (data.length === 0) {
984
+ return /* @__PURE__ */ jsx4("div", { className: "lookup-select__grid-state", children: /* @__PURE__ */ jsx4("div", { className: "lookup-select__empty", children: /* @__PURE__ */ jsx4("p", { children: emptyText }) }) });
985
+ }
986
+ const handleSort = (columnKey) => {
987
+ if (onSort) {
988
+ const newSortDir = currentSort?.sortBy === columnKey && currentSort.sortDir === "asc" ? "desc" : "asc";
989
+ onSort(columnKey, newSortDir);
990
+ }
991
+ };
992
+ const getSortIcon = (columnKey) => {
993
+ if (currentSort?.sortBy !== columnKey) {
994
+ return "\u2195";
995
+ }
996
+ return currentSort.sortDir === "asc" ? "\u2191" : "\u2193";
997
+ };
998
+ const handleCheckboxChange = (row, event) => {
999
+ event.stopPropagation();
1000
+ onRowToggle(row);
1001
+ };
1002
+ const handleRowClick = (row) => {
1003
+ if (mode === "single") {
1004
+ onRowToggle(row);
1005
+ }
1006
+ };
1007
+ return /* @__PURE__ */ jsxs4(
1008
+ "div",
1009
+ {
1010
+ className: `lookup-select__virtual-grid ${className || ""}`,
1011
+ style,
1012
+ children: [
1013
+ /* @__PURE__ */ jsx4("div", { className: "lookup-select__grid-header", children: /* @__PURE__ */ jsx4("table", { className: "lookup-select__table", children: /* @__PURE__ */ jsx4("thead", { children: /* @__PURE__ */ jsxs4("tr", { children: [
1014
+ mode === "multiple" && /* @__PURE__ */ jsx4("th", { className: "lookup-select__header-cell lookup-select__header-cell--checkbox", children: /* @__PURE__ */ jsx4(
1015
+ "input",
1016
+ {
1017
+ ref: headerCheckboxRef,
1018
+ type: "checkbox",
1019
+ className: "lookup-select__checkbox",
1020
+ checked: selectedRows.length === data.length && data.length > 0,
1021
+ onChange: (e) => {
1022
+ if (e.target.checked) {
1023
+ data.forEach((row) => {
1024
+ if (!isRowSelected(row) && (!selectableRow || selectableRow(row))) {
1025
+ onRowToggle(row);
1026
+ }
1027
+ });
1028
+ } else {
1029
+ selectedRows.forEach((row) => onRowToggle(row));
1030
+ }
1031
+ },
1032
+ "aria-label": "Select all"
1033
+ }
1034
+ ) }),
1035
+ columns.map((column) => /* @__PURE__ */ jsx4(
1036
+ "th",
1037
+ {
1038
+ className: `lookup-select__header-cell ${column.sortable ? "lookup-select__header-cell--sortable" : ""}`,
1039
+ style: { width: column.width },
1040
+ children: /* @__PURE__ */ jsxs4("div", { className: "lookup-select__header-content", children: [
1041
+ /* @__PURE__ */ jsx4("span", { children: column.title }),
1042
+ column.sortable && /* @__PURE__ */ jsx4(
1043
+ "button",
1044
+ {
1045
+ type: "button",
1046
+ className: "lookup-select__sort-button",
1047
+ "aria-label": `Sort by ${column.title} column`,
1048
+ onClick: () => handleSort(String(column.key)),
1049
+ children: getSortIcon(String(column.key))
1050
+ }
1051
+ )
1052
+ ] })
1053
+ },
1054
+ String(column.key)
1055
+ ))
1056
+ ] }) }) }) }),
1057
+ /* @__PURE__ */ jsx4(
1058
+ "div",
1059
+ {
1060
+ ref: containerRef,
1061
+ className: "lookup-select__virtual-container",
1062
+ style: {
1063
+ flex: 1,
1064
+ overflow: "auto",
1065
+ position: "relative"
1066
+ },
1067
+ onScroll: handleScroll,
1068
+ children: /* @__PURE__ */ jsx4(
1069
+ "div",
1070
+ {
1071
+ className: "lookup-select__virtual-content",
1072
+ style: {
1073
+ height: totalHeight,
1074
+ position: "relative"
1075
+ },
1076
+ children: visibleItems.map((item) => /* @__PURE__ */ jsx4(
1077
+ VirtualRow,
1078
+ {
1079
+ item,
1080
+ columns,
1081
+ mode,
1082
+ mapper,
1083
+ isSelected: isRowSelected(item.row),
1084
+ isSelectable: !selectableRow || selectableRow(item.row),
1085
+ onToggle: () => onRowToggle(item.row),
1086
+ onCheckboxChange: (e) => handleCheckboxChange(item.row, e),
1087
+ onClick: () => handleRowClick(item.row)
1088
+ },
1089
+ item.index
1090
+ ))
1091
+ }
1092
+ )
1093
+ }
1094
+ )
1095
+ ]
1096
+ }
1097
+ );
1098
+ }
1099
+ function VirtualRowComponent({
1100
+ item,
1101
+ columns,
1102
+ mode,
1103
+ mapper,
1104
+ isSelected,
1105
+ isSelectable,
1106
+ onToggle,
1107
+ onCheckboxChange,
1108
+ onClick
1109
+ }) {
1110
+ const { row, style } = item;
1111
+ return /* @__PURE__ */ jsx4(
1112
+ "div",
1113
+ {
1114
+ className: `lookup-select__virtual-row ${isSelected ? "lookup-select__row--selected" : ""} ${!isSelectable ? "lookup-select__row--disabled" : ""}`,
1115
+ style,
1116
+ onClick: isSelectable ? onClick : void 0,
1117
+ children: /* @__PURE__ */ jsx4("table", { className: "lookup-select__table", children: /* @__PURE__ */ jsx4("tbody", { children: /* @__PURE__ */ jsxs4("tr", { children: [
1118
+ mode === "multiple" && /* @__PURE__ */ jsx4("td", { className: "lookup-select__cell lookup-select__cell--checkbox", children: /* @__PURE__ */ jsx4(
1119
+ "input",
1120
+ {
1121
+ type: "checkbox",
1122
+ className: "lookup-select__checkbox",
1123
+ checked: isSelected,
1124
+ disabled: !isSelectable,
1125
+ onChange: onCheckboxChange,
1126
+ onClick: (e) => e.stopPropagation()
1127
+ }
1128
+ ) }),
1129
+ columns.map((column) => /* @__PURE__ */ jsx4(
1130
+ "td",
1131
+ {
1132
+ className: "lookup-select__cell",
1133
+ style: { width: column.width },
1134
+ children: column.render ? column.render(row, item.index) : String(row[column.key] || "")
1135
+ },
1136
+ String(column.key)
1137
+ ))
1138
+ ] }) }) })
1139
+ }
1140
+ );
1141
+ }
1142
+ var VirtualRow = React2.memo(
1143
+ VirtualRowComponent
1144
+ );
1145
+
1146
+ // src/components/Pagination.tsx
1147
+ import { Fragment, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1148
+ function Pagination({
1149
+ currentPage,
1150
+ totalCount,
1151
+ pageSize,
1152
+ onPageChange,
1153
+ className
1154
+ }) {
1155
+ const totalPages = Math.ceil(totalCount / pageSize);
1156
+ if (totalPages <= 1) {
1157
+ return null;
1158
+ }
1159
+ const getVisiblePages = () => {
1160
+ const delta = 2;
1161
+ const range = [];
1162
+ const rangeWithDots = [];
1163
+ for (let i = Math.max(2, currentPage - delta); i <= Math.min(totalPages - 1, currentPage + delta); i++) {
1164
+ range.push(i);
1165
+ }
1166
+ if (currentPage - delta > 2) {
1167
+ rangeWithDots.push(1, "...");
1168
+ } else {
1169
+ rangeWithDots.push(1);
1170
+ }
1171
+ rangeWithDots.push(...range);
1172
+ if (currentPage + delta < totalPages - 1) {
1173
+ rangeWithDots.push("...", totalPages);
1174
+ } else if (totalPages > 1) {
1175
+ rangeWithDots.push(totalPages);
1176
+ }
1177
+ return rangeWithDots;
1178
+ };
1179
+ const visiblePages = getVisiblePages();
1180
+ const startRecord = (currentPage - 1) * pageSize + 1;
1181
+ const endRecord = Math.min(currentPage * pageSize, totalCount);
1182
+ 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" }),
1191
+ /* @__PURE__ */ jsxs5("div", { className: "lookup-select__pagination-controls", children: [
1192
+ /* @__PURE__ */ jsx5(
1193
+ "button",
1194
+ {
1195
+ type: "button",
1196
+ className: "lookup-select__pagination-button",
1197
+ disabled: currentPage === 1,
1198
+ onClick: () => onPageChange(currentPage - 1),
1199
+ "aria-label": "Previous page",
1200
+ children: "\u2039"
1201
+ }
1202
+ ),
1203
+ visiblePages.map((page, index) => {
1204
+ if (page === "...") {
1205
+ return /* @__PURE__ */ jsx5(
1206
+ "span",
1207
+ {
1208
+ className: "lookup-select__pagination-dots",
1209
+ children: "..."
1210
+ },
1211
+ `dots-${index}`
1212
+ );
1213
+ }
1214
+ const pageNum = page;
1215
+ return /* @__PURE__ */ jsx5(
1216
+ "button",
1217
+ {
1218
+ type: "button",
1219
+ className: `
1220
+ lookup-select__pagination-button
1221
+ ${currentPage === pageNum ? "lookup-select__pagination-button--active" : ""}
1222
+ `,
1223
+ onClick: () => onPageChange(pageNum),
1224
+ "aria-label": `Sayfa ${pageNum}`,
1225
+ "aria-current": currentPage === pageNum ? "page" : void 0,
1226
+ children: pageNum
1227
+ },
1228
+ pageNum
1229
+ );
1230
+ }),
1231
+ /* @__PURE__ */ jsx5(
1232
+ "button",
1233
+ {
1234
+ type: "button",
1235
+ className: "lookup-select__pagination-button",
1236
+ disabled: currentPage === totalPages,
1237
+ onClick: () => onPageChange(currentPage + 1),
1238
+ "aria-label": "Next page",
1239
+ children: "\u203A"
1240
+ }
1241
+ )
1242
+ ] })
1243
+ ] });
1244
+ }
1245
+
1246
+ // src/components/LookupSelect.tsx
1247
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1248
+ function LookupSelect(props) {
1249
+ const {
1250
+ columns,
1251
+ mapper,
1252
+ mode = "single",
1253
+ data = [],
1254
+ dataSource,
1255
+ variant = "default",
1256
+ size = "medium",
1257
+ icon,
1258
+ renderTrigger,
1259
+ modalTitle,
1260
+ classNames,
1261
+ styles,
1262
+ theme,
1263
+ i18n,
1264
+ pageSize = 20,
1265
+ selectableRow,
1266
+ virtualization = false,
1267
+ virtualRowHeight = 40,
1268
+ virtualOverscan = 5,
1269
+ virtualContainerHeight = 400,
1270
+ virtualThreshold = 100
1271
+ } = props;
1272
+ const {
1273
+ modalOpen,
1274
+ openModal,
1275
+ closeModal,
1276
+ currentSelections,
1277
+ toggleRowSelection,
1278
+ clearSelections,
1279
+ isRowSelected,
1280
+ updateQuery,
1281
+ getCurrentQuery,
1282
+ confirmSelection,
1283
+ cancelSelection
1284
+ } = useLookupSelectState(props);
1285
+ const texts = {
1286
+ triggerPlaceholder: "Please select",
1287
+ searchPlaceholder: "Search...",
1288
+ confirmText: "Apply",
1289
+ cancelText: "Cancel",
1290
+ modalTitle: "Select Record",
1291
+ emptyText: "No records found",
1292
+ loadingText: "Loading...",
1293
+ errorPrefix: "Error:",
1294
+ selectedCount: (n) => `${n} selected`,
1295
+ clearText: "Clear",
1296
+ selectAllLabel: "Select all",
1297
+ selectRowLabel: (rowText) => `Select ${rowText} option`,
1298
+ sortColumnLabel: (columnTitle) => `Sort by ${columnTitle} column`,
1299
+ closeModalLabel: "Close modal",
1300
+ paginationInfo: (current, total) => `Page ${current} / ${total}`,
1301
+ totalRecords: (total) => `${total} records`,
1302
+ searchResults: (count) => `${count} results found`,
1303
+ ...i18n
1304
+ };
1305
+ const optimizedPageSize = React3.useMemo(() => {
1306
+ if (dataSource && virtualization) {
1307
+ const containerCapacity = Math.ceil(
1308
+ virtualContainerHeight / virtualRowHeight
1309
+ );
1310
+ return Math.max(pageSize, containerCapacity * 5);
1311
+ }
1312
+ return pageSize;
1313
+ }, [
1314
+ dataSource,
1315
+ virtualization,
1316
+ virtualContainerHeight,
1317
+ virtualRowHeight,
1318
+ pageSize
1319
+ ]);
1320
+ const { announce } = useScreenReaderAnnouncements();
1321
+ useFocusManagement(modalOpen);
1322
+ useKeyboardNavigation({
1323
+ isModalOpen: modalOpen,
1324
+ onClose: closeModal,
1325
+ onConfirm: confirmSelection,
1326
+ currentData: [],
1327
+ selectedRows: currentSelections,
1328
+ mode
1329
+ });
1330
+ const [searchValue, setSearchValue] = React3.useState("");
1331
+ const [loading, setLoading] = React3.useState(false);
1332
+ const [error, setError] = React3.useState();
1333
+ const [serverData, setServerData] = React3.useState([]);
1334
+ const [totalCount, setTotalCount] = React3.useState(0);
1335
+ const [currentPage, setCurrentPage] = React3.useState(1);
1336
+ const handleSearchChange = React3.useCallback(
1337
+ (value) => {
1338
+ setSearchValue(value);
1339
+ updateQuery({ search: value, page: 1 });
1340
+ },
1341
+ [updateQuery]
1342
+ );
1343
+ const filteredData = React3.useMemo(() => {
1344
+ if (!searchValue || dataSource) {
1345
+ return data;
1346
+ }
1347
+ return data.filter((item) => {
1348
+ return columns.some((column) => {
1349
+ const cellValue = item[column.key];
1350
+ return cellValue?.toString().toLowerCase().includes(searchValue.toLowerCase());
1351
+ });
1352
+ });
1353
+ }, [data, searchValue, columns, dataSource]);
1354
+ const currentData = dataSource ? serverData : filteredData;
1355
+ const loadServerData = React3.useCallback(async () => {
1356
+ if (!dataSource) return;
1357
+ setLoading(true);
1358
+ setError(void 0);
1359
+ try {
1360
+ const query = getCurrentQuery();
1361
+ const result = await dataSource({
1362
+ ...query,
1363
+ search: searchValue,
1364
+ page: currentPage,
1365
+ pageSize: optimizedPageSize
1366
+ });
1367
+ setServerData(result.rows);
1368
+ setTotalCount(result.total);
1369
+ setLoading(false);
1370
+ } catch (err) {
1371
+ setError(
1372
+ err instanceof Error ? err.message : "Error occurred while loading data"
1373
+ );
1374
+ setLoading(false);
1375
+ }
1376
+ }, [
1377
+ dataSource,
1378
+ getCurrentQuery,
1379
+ searchValue,
1380
+ currentPage,
1381
+ optimizedPageSize
1382
+ ]);
1383
+ React3.useEffect(() => {
1384
+ if (dataSource && modalOpen) {
1385
+ loadServerData();
1386
+ }
1387
+ }, [dataSource, modalOpen, loadServerData]);
1388
+ const handlePageChange = React3.useCallback(
1389
+ (newPage) => {
1390
+ setCurrentPage(newPage);
1391
+ updateQuery({ page: newPage });
1392
+ },
1393
+ [updateQuery]
1394
+ );
1395
+ const handleSortChange = React3.useCallback(
1396
+ (sortBy, sortDir) => {
1397
+ updateQuery({ sortBy, sortDir, page: 1 });
1398
+ setCurrentPage(1);
1399
+ },
1400
+ [updateQuery]
1401
+ );
1402
+ const shouldUseVirtualization = React3.useMemo(() => {
1403
+ if (!virtualization) return false;
1404
+ return currentData.length >= virtualThreshold;
1405
+ }, [virtualization, currentData.length, virtualThreshold]);
1406
+ const virtualizationConfig = React3.useMemo(
1407
+ () => ({
1408
+ enabled: shouldUseVirtualization,
1409
+ rowHeight: virtualRowHeight,
1410
+ overscan: virtualOverscan,
1411
+ containerHeight: virtualContainerHeight,
1412
+ threshold: virtualThreshold
1413
+ }),
1414
+ [
1415
+ shouldUseVirtualization,
1416
+ virtualRowHeight,
1417
+ virtualOverscan,
1418
+ virtualContainerHeight,
1419
+ virtualThreshold
1420
+ ]
1421
+ );
1422
+ const renderGrid = React3.useCallback(() => {
1423
+ const commonGridProps = {
1424
+ data: currentData,
1425
+ columns,
1426
+ mode,
1427
+ mapper,
1428
+ selectedRows: currentSelections,
1429
+ onRowToggle: toggleRowSelection,
1430
+ isRowSelected,
1431
+ selectableRow,
1432
+ loading,
1433
+ error,
1434
+ emptyText: texts.emptyText,
1435
+ className: classNames?.grid,
1436
+ style: styles?.grid,
1437
+ currentSort: dataSource ? {
1438
+ sortBy: getCurrentQuery().sortBy || "",
1439
+ sortDir: getCurrentQuery().sortDir || "asc"
1440
+ } : void 0,
1441
+ onSort: dataSource ? handleSortChange : void 0
1442
+ };
1443
+ if (shouldUseVirtualization) {
1444
+ return /* @__PURE__ */ jsx6(
1445
+ VirtualGrid,
1446
+ {
1447
+ ...commonGridProps,
1448
+ virtualization: virtualizationConfig
1449
+ }
1450
+ );
1451
+ }
1452
+ return /* @__PURE__ */ jsx6(Grid, { ...commonGridProps });
1453
+ }, [
1454
+ currentData,
1455
+ columns,
1456
+ mode,
1457
+ mapper,
1458
+ currentSelections,
1459
+ toggleRowSelection,
1460
+ isRowSelected,
1461
+ selectableRow,
1462
+ loading,
1463
+ error,
1464
+ texts.emptyText,
1465
+ classNames?.grid,
1466
+ styles?.grid,
1467
+ dataSource,
1468
+ getCurrentQuery,
1469
+ handleSortChange,
1470
+ shouldUseVirtualization,
1471
+ virtualizationConfig
1472
+ ]);
1473
+ const getThemeClasses = () => {
1474
+ const classes = ["lookup-select"];
1475
+ if (variant && variant !== "default") {
1476
+ classes.push(`lookup-select--theme-${variant}`);
1477
+ }
1478
+ if (size && size !== "medium") {
1479
+ classes.push(`lookup-select--size-${size}`);
1480
+ }
1481
+ if (classNames?.root) {
1482
+ classes.push(classNames.root);
1483
+ }
1484
+ return classes.join(" ");
1485
+ };
1486
+ const getThemeStyles = () => {
1487
+ const themeStyles = {};
1488
+ if (theme) {
1489
+ if (theme.colorPrimary)
1490
+ themeStyles["--lookup-select-color-primary"] = theme.colorPrimary;
1491
+ if (theme.colorBg)
1492
+ themeStyles["--lookup-select-color-bg"] = theme.colorBg;
1493
+ if (theme.colorText)
1494
+ themeStyles["--lookup-select-color-text"] = theme.colorText;
1495
+ if (theme.borderRadius)
1496
+ themeStyles["--lookup-select-border-radius"] = typeof theme.borderRadius === "number" ? `${theme.borderRadius}px` : theme.borderRadius;
1497
+ if (theme.spacing)
1498
+ themeStyles["--lookup-select-spacing"] = `${theme.spacing}px`;
1499
+ }
1500
+ return { ...themeStyles, ...styles?.root };
1501
+ };
1502
+ return /* @__PURE__ */ jsxs6("div", { className: getThemeClasses(), style: getThemeStyles(), children: [
1503
+ /* @__PURE__ */ jsx6(
1504
+ Trigger,
1505
+ {
1506
+ isOpen: modalOpen,
1507
+ onToggle: openModal,
1508
+ selectedItems: currentSelections,
1509
+ placeholder: texts.triggerPlaceholder,
1510
+ icon,
1511
+ mapper,
1512
+ mode,
1513
+ className: classNames?.trigger,
1514
+ style: styles?.trigger,
1515
+ renderTrigger,
1516
+ onClear: clearSelections,
1517
+ onRemoveTag: (item) => toggleRowSelection(item)
1518
+ }
1519
+ ),
1520
+ /* @__PURE__ */ jsxs6(
1521
+ Modal,
1522
+ {
1523
+ isOpen: modalOpen,
1524
+ onClose: cancelSelection,
1525
+ title: modalTitle || texts.modalTitle,
1526
+ className: classNames?.modal,
1527
+ style: styles?.modal,
1528
+ children: [
1529
+ /* @__PURE__ */ jsx6(
1530
+ SearchInput,
1531
+ {
1532
+ value: searchValue,
1533
+ onChange: handleSearchChange,
1534
+ placeholder: texts.searchPlaceholder
1535
+ }
1536
+ ),
1537
+ /* @__PURE__ */ jsxs6("div", { className: "lookup-select__modal-content", children: [
1538
+ renderGrid(),
1539
+ dataSource && /* @__PURE__ */ jsx6(
1540
+ Pagination,
1541
+ {
1542
+ currentPage,
1543
+ totalCount,
1544
+ pageSize: optimizedPageSize,
1545
+ onPageChange: handlePageChange
1546
+ }
1547
+ )
1548
+ ] }),
1549
+ /* @__PURE__ */ jsx6(
1550
+ ModalFooter,
1551
+ {
1552
+ onConfirm: confirmSelection,
1553
+ onCancel: cancelSelection,
1554
+ confirmText: texts.confirmText,
1555
+ cancelText: texts.cancelText,
1556
+ selectedCount: currentSelections.length
1557
+ }
1558
+ )
1559
+ ]
1560
+ }
1561
+ )
1562
+ ] });
1563
+ }
1564
+ export {
1565
+ LookupSelect
1566
+ };
1567
+ //# sourceMappingURL=index.js.map