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