react-lookup-select 1.0.4 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/readme.md CHANGED
@@ -1,440 +1,440 @@
1
- # react-lookup-select
2
-
3
- [![NPM Version](https://img.shields.io/npm/v/react-lookup-select.svg)](https://www.npmjs.com/package/react-lookup-select)
4
- [![NPM Downloads](https://img.shields.io/npm/dm/react-lookup-select.svg)](https://www.npmjs.com/package/react-lookup-select)
5
- [![GitHub](https://img.shields.io/github/license/Onurlulardan/react-lookup-select.svg)](https://github.com/Onurlulardan/react-lookup-select)
6
-
7
- A headless, customizable React lookup select component with modal and grid support for single/multiple selection.
8
-
9
- ## Features
10
-
11
- - **Trigger (ComboBox appearance)**: Click to open modal
12
- - **Grid inside Modal**: Single/multiple row selection (with checkboxes or row clicks)
13
- - **Selection Modes**: single | multiple
14
- - **Return Values**: id and text fields are user-mappable
15
- - **Full Customization**: themes, icons, grid columns, cell renderers
16
- - **Data Sources**: data (array) or dataSource (async: pagination/sort/search)
17
- - **Accessibility**: keyboard navigation, ARIA roles, focus trap
18
- - **Performance**: virtualization option for large datasets
19
-
20
- ## Installation
21
-
22
- ```bash
23
- npm i react-lookup-select
24
- ```
25
-
26
- ```tsx
27
- import { LookupSelect } from 'react-lookup-select';
28
- import 'react-lookup-select/styles.css';
29
- ```
30
-
31
- ## Example Usage
32
-
33
- ### Single Selection – text = name + ' ' + surname
34
-
35
- ```tsx
36
- <LookupSelect
37
- mode="single"
38
- data={users}
39
- columns={[
40
- { key: 'name', title: 'Name' },
41
- { key: 'surname', title: 'Surname' },
42
- { key: 'email', title: 'Email' },
43
- ]}
44
- mapper={{
45
- getId: (u) => u.userId,
46
- getText: (u) => `${u.name} ${u.surname}`,
47
- }}
48
- returnShape="id-text"
49
- onChange={(val) => console.log(val)}
50
- />
51
- ```
52
-
53
- ### Multiple Selection – Custom return
54
-
55
- ```tsx
56
- <LookupSelect
57
- mode="multiple"
58
- data={products}
59
- columns={[
60
- { key: 'sku', title: 'SKU' },
61
- { key: 'title', title: 'Product' },
62
- { key: 'price', title: 'Price' },
63
- ]}
64
- mapper={{ getId: (p) => p.id, getText: (p) => p.title }}
65
- returnShape="custom"
66
- returnMap={{ map: (p) => ({ key: p.id, label: p.title, p }) }}
67
- onChange={(vals) => save(vals)}
68
- />
69
- ```
70
-
71
- ### Server-side data + search/pagination
72
-
73
- ```tsx
74
- const dataSource = async (q: QueryState) => {
75
- const res = await fetch(
76
- `/api/users?page=${q.page}&size=${q.pageSize}&search=${q.search ?? ''}`
77
- );
78
- const json = await res.json();
79
- return { rows: json.items, total: json.total };
80
- };
81
-
82
- <LookupSelect
83
- mode="multiple"
84
- dataSource={dataSource}
85
- pageSize={50}
86
- columns={[
87
- { key: 'name', title: 'Name', sortable: true },
88
- { key: 'surname', title: 'Surname', sortable: true },
89
- { key: 'department', title: 'Department' },
90
- ]}
91
- mapper={{ getId: (u) => u.id, getText: (u) => `${u.name} ${u.surname}` }}
92
- onQueryChange={(q) => console.log('query changed', q)}
93
- />;
94
- ```
95
-
96
- ## Theming and Customization
97
-
98
- ### Pre-built Themes
99
-
100
- ```tsx
101
- {
102
- /* Default theme */
103
- }
104
- <LookupSelect variant="default" {...props} />;
105
-
106
- {
107
- /* Dark theme */
108
- }
109
- <LookupSelect variant="dark" {...props} />;
110
-
111
- {
112
- /* Minimal theme */
113
- }
114
- <LookupSelect variant="minimal" {...props} />;
115
-
116
- {
117
- /* Compact theme */
118
- }
119
- <LookupSelect variant="compact" {...props} />;
120
- ```
121
-
122
- ### Size Options
123
-
124
- ```tsx
125
- {
126
- /* Small size */
127
- }
128
- <LookupSelect size="small" {...props} />;
129
-
130
- {
131
- /* Medium size (default) */
132
- }
133
- <LookupSelect size="medium" {...props} />;
134
-
135
- {
136
- /* Large size */
137
- }
138
- <LookupSelect size="large" {...props} />;
139
- ```
140
-
141
- ### Customization with CSS Variables
142
-
143
- ```tsx
144
- <LookupSelect
145
- theme={{
146
- colorPrimary: '#8b5cf6',
147
- colorBg: '#faf5ff',
148
- colorText: '#4c1d95',
149
- borderRadius: 12,
150
- spacing: 10,
151
- }}
152
- {...props}
153
- />
154
- ```
155
-
156
- ### Customization with CSS Classes
157
-
158
- ```tsx
159
- <LookupSelect
160
- classNames={{
161
- root: 'my-custom-lookup',
162
- trigger: 'my-custom-trigger',
163
- modal: 'my-custom-modal',
164
- grid: 'my-custom-grid',
165
- }}
166
- {...props}
167
- />
168
- ```
169
-
170
- ```css
171
- .my-custom-lookup {
172
- --lookup-select-color-primary: #10b981;
173
- --lookup-select-border-radius: 8px;
174
- --lookup-select-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
175
- }
176
-
177
- .my-custom-trigger {
178
- border: 2px solid #10b981;
179
- background: linear-gradient(135deg, #ecfdf5 0%, #d1fae5 100%);
180
- }
181
- ```
182
-
183
- ### Inline Styles
184
-
185
- ```tsx
186
- <LookupSelect
187
- styles={{
188
- root: { border: '2px solid #f59e0b', borderRadius: '8px' },
189
- trigger: { background: '#fef3c7', color: '#92400e' },
190
- modal: { boxShadow: '0 25px 50px -12px rgba(0, 0, 0, 0.25)' },
191
- }}
192
- {...props}
193
- />
194
- ```
195
-
196
- ## Custom Rendering with Render Props
197
-
198
- ### Overview
199
-
200
- The LookupSelect component supports complete customization through render props, allowing you to integrate with any UI library (Ant Design, Material-UI, etc.) or create custom designs.
201
-
202
- ### Available Render Props
203
-
204
- - `renderTrigger` - Customize the trigger button
205
- - `renderModal` - Customize the modal container
206
- - `renderGrid` - Customize the data grid
207
- - `renderHeader` - Customize the modal header
208
- - `renderFooter` - Customize the modal footer
209
- - `renderSearch` - Customize the search input
210
- - `renderPagination` - Customize pagination controls
211
-
212
- ### Custom Modal Example
213
-
214
- ```tsx
215
- <LookupSelect
216
- data={users}
217
- columns={columns}
218
- mapper={mapper}
219
- renderModal={({ isOpen, onClose, children, title, selectedCount }) => {
220
- if (!isOpen) return null;
221
-
222
- return (
223
- <div className="custom-modal-overlay" onClick={onClose}>
224
- <div className="custom-modal" onClick={(e) => e.stopPropagation()}>
225
- <div className="modal-header">
226
- <h2>{title}</h2>
227
- {selectedCount > 0 && (
228
- <span className="selection-count">{selectedCount} selected</span>
229
- )}
230
- <button onClick={onClose}>×</button>
231
- </div>
232
- {children}
233
- </div>
234
- </div>
235
- );
236
- }}
237
- />
238
- ```
239
-
240
- ### Custom Grid with Cards
241
-
242
- ```tsx
243
- <LookupSelect
244
- data={users}
245
- columns={columns}
246
- mapper={mapper}
247
- renderGrid={({ data, selectedIds, onRowSelect, mapper }) => (
248
- <div className="card-grid">
249
- {data.map((user) => {
250
- const isSelected = selectedIds.includes(mapper.getId(user));
251
- return (
252
- <div
253
- key={mapper.getId(user)}
254
- className={`user-card ${isSelected ? 'selected' : ''}`}
255
- onClick={() => onRowSelect(user)}
256
- >
257
- <img src={user.avatar} alt={user.name} />
258
- <h3>{user.name}</h3>
259
- <p>{user.email}</p>
260
- <span className="role">{user.role}</span>
261
- </div>
262
- );
263
- })}
264
- </div>
265
- )}
266
- />
267
- ```
268
-
269
- ### Ant Design Integration
270
-
271
- ```tsx
272
- import { Modal, Table, Input, Button } from 'antd';
273
-
274
- <LookupSelect
275
- data={users}
276
- columns={columns}
277
- mapper={mapper}
278
- renderModal={({ isOpen, onClose, children }) => (
279
- <Modal
280
- open={isOpen}
281
- onCancel={onClose}
282
- title="Select Users"
283
- width={800}
284
- footer={null}
285
- >
286
- {children}
287
- </Modal>
288
- )}
289
- renderGrid={({ data, columns, selectedRowKeys, onRowSelect }) => (
290
- <Table
291
- dataSource={data}
292
- columns={columns}
293
- rowSelection={{
294
- selectedRowKeys,
295
- onChange: onRowSelect,
296
- }}
297
- pagination={false}
298
- />
299
- )}
300
- renderSearch={({ value, onChange, placeholder }) => (
301
- <Input.Search
302
- value={value}
303
- onChange={(e) => onChange(e.target.value)}
304
- placeholder={placeholder}
305
- style={{ marginBottom: 16 }}
306
- />
307
- )}
308
- />;
309
- ```
310
-
311
- ### Material-UI Integration
312
-
313
- ```tsx
314
- import { Dialog, DataGrid, TextField, Chip } from '@mui/material';
315
-
316
- <LookupSelect
317
- data={users}
318
- columns={columns}
319
- mapper={mapper}
320
- renderModal={({ isOpen, onClose, children }) => (
321
- <Dialog open={isOpen} onClose={onClose} maxWidth="md" fullWidth>
322
- {children}
323
- </Dialog>
324
- )}
325
- renderGrid={({ data, columns, onRowSelect }) => (
326
- <DataGrid
327
- rows={data}
328
- columns={columns}
329
- onRowSelectionModelChange={onRowSelect}
330
- checkboxSelection
331
- />
332
- )}
333
- />;
334
- ```
335
-
336
- ### TypeScript Support for Render Props
337
-
338
- ```tsx
339
- import type {
340
- ModalRenderProps,
341
- GridRenderProps,
342
- SearchRenderProps,
343
- } from 'react-lookup-select';
344
-
345
- const CustomModal = ({ isOpen, onClose, children }: ModalRenderProps<User>) => {
346
- // Your custom modal implementation
347
- };
348
-
349
- const CustomGrid = ({
350
- data,
351
- selectedIds,
352
- onRowSelect,
353
- }: GridRenderProps<User>) => {
354
- // Your custom grid implementation
355
- };
356
- ```
357
-
358
- ## Virtualization - Large Data Performance
359
-
360
- ### Auto Virtualization
361
-
362
- ```tsx
363
- <LookupSelect
364
- data={largeDataArray} // 1000+ records
365
- virtualization={true} // Auto-enable when data > 100 items
366
- {...props}
367
- />
368
- ```
369
-
370
- ### Manual Virtualization Configuration
371
-
372
- ```tsx
373
- <LookupSelect
374
- data={tenThousandItems}
375
- virtualization={true}
376
- virtualRowHeight={48} // Fixed row height
377
- virtualContainerHeight={500} // Scroll container height
378
- virtualOverscan={10} // Buffer for smooth scrolling
379
- virtualThreshold={100} // Enable when data exceeds this
380
- {...props}
381
- />
382
- ```
383
-
384
- ### Hybrid Mode - Server + Client Virtualization
385
-
386
- ```tsx
387
- <LookupSelect
388
- dataSource={serverDataSource}
389
- virtualization={true}
390
- pageSize={100} // Fetch 500 records from server (5x buffer)
391
- virtualContainerHeight={400}
392
- virtualRowHeight={40}
393
- {...props}
394
- />
395
- ```
396
-
397
- ### Performance Comparison
398
-
399
- | Data Size | Virtualization | DOM Elements | Render Time | Memory |
400
- | ----------- | -------------- | ------------ | ----------- | ------ |
401
- | 10,000 item | ❌ Disabled | 10,000 rows | ~2000ms | ~200MB |
402
- | 10,000 item | ✅ Enabled | ~20 rows | ~50ms | ~15MB |
403
-
404
- ### Usage Recommendations
405
-
406
- - **100+ records:** Auto virtualization
407
- - **1,000+ records:** Client-side virtualization
408
- - **10,000+ records:** Hybrid mode (server + client)
409
- - **100,000+ records:** Pure server pagination
410
-
411
- ### All CSS Customization Variables
412
-
413
- ```css
414
- :root {
415
- /* Colors */
416
- --lookup-select-color-primary: #0066cc;
417
- --lookup-select-color-primary-hover: #0052a3;
418
- --lookup-select-color-bg: #ffffff;
419
- --lookup-select-color-text: #333333;
420
- --lookup-select-color-border: #d1d5db;
421
-
422
- /* Layout */
423
- --lookup-select-border-radius: 6px;
424
- --lookup-select-spacing: 8px;
425
- --lookup-select-font-size: 14px;
426
-
427
- /* Component specific sizes */
428
- --lookup-select-trigger-height: 36px;
429
- --lookup-select-modal-width: 600px;
430
- --lookup-select-grid-row-height: 40px;
431
-
432
- /* Shadows */
433
- --lookup-select-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
434
- --lookup-select-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
435
- }
436
- ```
437
-
438
- ## License
439
-
440
- MIT
1
+ # react-lookup-select
2
+
3
+ [![NPM Version](https://img.shields.io/npm/v/react-lookup-select.svg)](https://www.npmjs.com/package/react-lookup-select)
4
+ [![NPM Downloads](https://img.shields.io/npm/dm/react-lookup-select.svg)](https://www.npmjs.com/package/react-lookup-select)
5
+ [![GitHub](https://img.shields.io/github/license/Onurlulardan/react-lookup-select.svg)](https://github.com/Onurlulardan/react-lookup-select)
6
+
7
+ A headless, customizable React lookup select component with modal and grid support for single/multiple selection.
8
+
9
+ ## Features
10
+
11
+ - **Trigger (ComboBox appearance)**: Click to open modal
12
+ - **Grid inside Modal**: Single/multiple row selection (with checkboxes or row clicks)
13
+ - **Selection Modes**: single | multiple
14
+ - **Return Values**: id and text fields are user-mappable
15
+ - **Full Customization**: themes, icons, grid columns, cell renderers
16
+ - **Data Sources**: data (array) or dataSource (async: pagination/sort/search)
17
+ - **Accessibility**: keyboard navigation, ARIA roles, focus trap
18
+ - **Performance**: virtualization option for large datasets
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ npm i react-lookup-select
24
+ ```
25
+
26
+ ```tsx
27
+ import { LookupSelect } from 'react-lookup-select';
28
+ import 'react-lookup-select/styles.css';
29
+ ```
30
+
31
+ ## Example Usage
32
+
33
+ ### Single Selection – text = name + ' ' + surname
34
+
35
+ ```tsx
36
+ <LookupSelect
37
+ mode="single"
38
+ data={users}
39
+ columns={[
40
+ { key: 'name', title: 'Name' },
41
+ { key: 'surname', title: 'Surname' },
42
+ { key: 'email', title: 'Email' },
43
+ ]}
44
+ mapper={{
45
+ getId: (u) => u.userId,
46
+ getText: (u) => `${u.name} ${u.surname}`,
47
+ }}
48
+ returnShape="id-text"
49
+ onChange={(val) => console.log(val)}
50
+ />
51
+ ```
52
+
53
+ ### Multiple Selection – Custom return
54
+
55
+ ```tsx
56
+ <LookupSelect
57
+ mode="multiple"
58
+ data={products}
59
+ columns={[
60
+ { key: 'sku', title: 'SKU' },
61
+ { key: 'title', title: 'Product' },
62
+ { key: 'price', title: 'Price' },
63
+ ]}
64
+ mapper={{ getId: (p) => p.id, getText: (p) => p.title }}
65
+ returnShape="custom"
66
+ returnMap={{ map: (p) => ({ key: p.id, label: p.title, p }) }}
67
+ onChange={(vals) => save(vals)}
68
+ />
69
+ ```
70
+
71
+ ### Server-side data + search/pagination
72
+
73
+ ```tsx
74
+ const dataSource = async (q: QueryState) => {
75
+ const res = await fetch(
76
+ `/api/users?page=${q.page}&size=${q.pageSize}&search=${q.search ?? ''}`
77
+ );
78
+ const json = await res.json();
79
+ return { rows: json.items, total: json.total };
80
+ };
81
+
82
+ <LookupSelect
83
+ mode="multiple"
84
+ dataSource={dataSource}
85
+ pageSize={50}
86
+ columns={[
87
+ { key: 'name', title: 'Name', sortable: true },
88
+ { key: 'surname', title: 'Surname', sortable: true },
89
+ { key: 'department', title: 'Department' },
90
+ ]}
91
+ mapper={{ getId: (u) => u.id, getText: (u) => `${u.name} ${u.surname}` }}
92
+ onQueryChange={(q) => console.log('query changed', q)}
93
+ />;
94
+ ```
95
+
96
+ ## Theming and Customization
97
+
98
+ ### Pre-built Themes
99
+
100
+ ```tsx
101
+ {
102
+ /* Default theme */
103
+ }
104
+ <LookupSelect variant="default" {...props} />;
105
+
106
+ {
107
+ /* Dark theme */
108
+ }
109
+ <LookupSelect variant="dark" {...props} />;
110
+
111
+ {
112
+ /* Minimal theme */
113
+ }
114
+ <LookupSelect variant="minimal" {...props} />;
115
+
116
+ {
117
+ /* Compact theme */
118
+ }
119
+ <LookupSelect variant="compact" {...props} />;
120
+ ```
121
+
122
+ ### Size Options
123
+
124
+ ```tsx
125
+ {
126
+ /* Small size */
127
+ }
128
+ <LookupSelect size="small" {...props} />;
129
+
130
+ {
131
+ /* Medium size (default) */
132
+ }
133
+ <LookupSelect size="medium" {...props} />;
134
+
135
+ {
136
+ /* Large size */
137
+ }
138
+ <LookupSelect size="large" {...props} />;
139
+ ```
140
+
141
+ ### Customization with CSS Variables
142
+
143
+ ```tsx
144
+ <LookupSelect
145
+ theme={{
146
+ colorPrimary: '#8b5cf6',
147
+ colorBg: '#faf5ff',
148
+ colorText: '#4c1d95',
149
+ borderRadius: 12,
150
+ spacing: 10,
151
+ }}
152
+ {...props}
153
+ />
154
+ ```
155
+
156
+ ### Customization with CSS Classes
157
+
158
+ ```tsx
159
+ <LookupSelect
160
+ classNames={{
161
+ root: 'my-custom-lookup',
162
+ trigger: 'my-custom-trigger',
163
+ modal: 'my-custom-modal',
164
+ grid: 'my-custom-grid',
165
+ }}
166
+ {...props}
167
+ />
168
+ ```
169
+
170
+ ```css
171
+ .my-custom-lookup {
172
+ --lookup-select-color-primary: #10b981;
173
+ --lookup-select-border-radius: 8px;
174
+ --lookup-select-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
175
+ }
176
+
177
+ .my-custom-trigger {
178
+ border: 2px solid #10b981;
179
+ background: linear-gradient(135deg, #ecfdf5 0%, #d1fae5 100%);
180
+ }
181
+ ```
182
+
183
+ ### Inline Styles
184
+
185
+ ```tsx
186
+ <LookupSelect
187
+ styles={{
188
+ root: { border: '2px solid #f59e0b', borderRadius: '8px' },
189
+ trigger: { background: '#fef3c7', color: '#92400e' },
190
+ modal: { boxShadow: '0 25px 50px -12px rgba(0, 0, 0, 0.25)' },
191
+ }}
192
+ {...props}
193
+ />
194
+ ```
195
+
196
+ ## Custom Rendering with Render Props
197
+
198
+ ### Overview
199
+
200
+ The LookupSelect component supports complete customization through render props, allowing you to integrate with any UI library (Ant Design, Material-UI, etc.) or create custom designs.
201
+
202
+ ### Available Render Props
203
+
204
+ - `renderTrigger` - Customize the trigger button
205
+ - `renderModal` - Customize the modal container
206
+ - `renderGrid` - Customize the data grid
207
+ - `renderHeader` - Customize the modal header
208
+ - `renderFooter` - Customize the modal footer
209
+ - `renderSearch` - Customize the search input
210
+ - `renderPagination` - Customize pagination controls
211
+
212
+ ### Custom Modal Example
213
+
214
+ ```tsx
215
+ <LookupSelect
216
+ data={users}
217
+ columns={columns}
218
+ mapper={mapper}
219
+ renderModal={({ isOpen, onClose, children, title, selectedCount }) => {
220
+ if (!isOpen) return null;
221
+
222
+ return (
223
+ <div className="custom-modal-overlay" onClick={onClose}>
224
+ <div className="custom-modal" onClick={(e) => e.stopPropagation()}>
225
+ <div className="modal-header">
226
+ <h2>{title}</h2>
227
+ {selectedCount > 0 && (
228
+ <span className="selection-count">{selectedCount} selected</span>
229
+ )}
230
+ <button onClick={onClose}>×</button>
231
+ </div>
232
+ {children}
233
+ </div>
234
+ </div>
235
+ );
236
+ }}
237
+ />
238
+ ```
239
+
240
+ ### Custom Grid with Cards
241
+
242
+ ```tsx
243
+ <LookupSelect
244
+ data={users}
245
+ columns={columns}
246
+ mapper={mapper}
247
+ renderGrid={({ data, selectedIds, onRowSelect, mapper }) => (
248
+ <div className="card-grid">
249
+ {data.map((user) => {
250
+ const isSelected = selectedIds.includes(mapper.getId(user));
251
+ return (
252
+ <div
253
+ key={mapper.getId(user)}
254
+ className={`user-card ${isSelected ? 'selected' : ''}`}
255
+ onClick={() => onRowSelect(user)}
256
+ >
257
+ <img src={user.avatar} alt={user.name} />
258
+ <h3>{user.name}</h3>
259
+ <p>{user.email}</p>
260
+ <span className="role">{user.role}</span>
261
+ </div>
262
+ );
263
+ })}
264
+ </div>
265
+ )}
266
+ />
267
+ ```
268
+
269
+ ### Ant Design Integration
270
+
271
+ ```tsx
272
+ import { Modal, Table, Input, Button } from 'antd';
273
+
274
+ <LookupSelect
275
+ data={users}
276
+ columns={columns}
277
+ mapper={mapper}
278
+ renderModal={({ isOpen, onClose, children }) => (
279
+ <Modal
280
+ open={isOpen}
281
+ onCancel={onClose}
282
+ title="Select Users"
283
+ width={800}
284
+ footer={null}
285
+ >
286
+ {children}
287
+ </Modal>
288
+ )}
289
+ renderGrid={({ data, columns, selectedRowKeys, onRowSelect }) => (
290
+ <Table
291
+ dataSource={data}
292
+ columns={columns}
293
+ rowSelection={{
294
+ selectedRowKeys,
295
+ onChange: onRowSelect,
296
+ }}
297
+ pagination={false}
298
+ />
299
+ )}
300
+ renderSearch={({ value, onChange, placeholder }) => (
301
+ <Input.Search
302
+ value={value}
303
+ onChange={(e) => onChange(e.target.value)}
304
+ placeholder={placeholder}
305
+ style={{ marginBottom: 16 }}
306
+ />
307
+ )}
308
+ />;
309
+ ```
310
+
311
+ ### Material-UI Integration
312
+
313
+ ```tsx
314
+ import { Dialog, DataGrid, TextField, Chip } from '@mui/material';
315
+
316
+ <LookupSelect
317
+ data={users}
318
+ columns={columns}
319
+ mapper={mapper}
320
+ renderModal={({ isOpen, onClose, children }) => (
321
+ <Dialog open={isOpen} onClose={onClose} maxWidth="md" fullWidth>
322
+ {children}
323
+ </Dialog>
324
+ )}
325
+ renderGrid={({ data, columns, onRowSelect }) => (
326
+ <DataGrid
327
+ rows={data}
328
+ columns={columns}
329
+ onRowSelectionModelChange={onRowSelect}
330
+ checkboxSelection
331
+ />
332
+ )}
333
+ />;
334
+ ```
335
+
336
+ ### TypeScript Support for Render Props
337
+
338
+ ```tsx
339
+ import type {
340
+ ModalRenderProps,
341
+ GridRenderProps,
342
+ SearchRenderProps,
343
+ } from 'react-lookup-select';
344
+
345
+ const CustomModal = ({ isOpen, onClose, children }: ModalRenderProps<User>) => {
346
+ // Your custom modal implementation
347
+ };
348
+
349
+ const CustomGrid = ({
350
+ data,
351
+ selectedIds,
352
+ onRowSelect,
353
+ }: GridRenderProps<User>) => {
354
+ // Your custom grid implementation
355
+ };
356
+ ```
357
+
358
+ ## Virtualization - Large Data Performance
359
+
360
+ ### Auto Virtualization
361
+
362
+ ```tsx
363
+ <LookupSelect
364
+ data={largeDataArray} // 1000+ records
365
+ virtualization={true} // Auto-enable when data > 100 items
366
+ {...props}
367
+ />
368
+ ```
369
+
370
+ ### Manual Virtualization Configuration
371
+
372
+ ```tsx
373
+ <LookupSelect
374
+ data={tenThousandItems}
375
+ virtualization={true}
376
+ virtualRowHeight={48} // Fixed row height
377
+ virtualContainerHeight={500} // Scroll container height
378
+ virtualOverscan={10} // Buffer for smooth scrolling
379
+ virtualThreshold={100} // Enable when data exceeds this
380
+ {...props}
381
+ />
382
+ ```
383
+
384
+ ### Hybrid Mode - Server + Client Virtualization
385
+
386
+ ```tsx
387
+ <LookupSelect
388
+ dataSource={serverDataSource}
389
+ virtualization={true}
390
+ pageSize={100} // Fetch 500 records from server (5x buffer)
391
+ virtualContainerHeight={400}
392
+ virtualRowHeight={40}
393
+ {...props}
394
+ />
395
+ ```
396
+
397
+ ### Performance Comparison
398
+
399
+ | Data Size | Virtualization | DOM Elements | Render Time | Memory |
400
+ | ----------- | -------------- | ------------ | ----------- | ------ |
401
+ | 10,000 item | ❌ Disabled | 10,000 rows | ~2000ms | ~200MB |
402
+ | 10,000 item | ✅ Enabled | ~20 rows | ~50ms | ~15MB |
403
+
404
+ ### Usage Recommendations
405
+
406
+ - **100+ records:** Auto virtualization
407
+ - **1,000+ records:** Client-side virtualization
408
+ - **10,000+ records:** Hybrid mode (server + client)
409
+ - **100,000+ records:** Pure server pagination
410
+
411
+ ### All CSS Customization Variables
412
+
413
+ ```css
414
+ :root {
415
+ /* Colors */
416
+ --lookup-select-color-primary: #0066cc;
417
+ --lookup-select-color-primary-hover: #0052a3;
418
+ --lookup-select-color-bg: #ffffff;
419
+ --lookup-select-color-text: #333333;
420
+ --lookup-select-color-border: #d1d5db;
421
+
422
+ /* Layout */
423
+ --lookup-select-border-radius: 6px;
424
+ --lookup-select-spacing: 8px;
425
+ --lookup-select-font-size: 14px;
426
+
427
+ /* Component specific sizes */
428
+ --lookup-select-trigger-height: 36px;
429
+ --lookup-select-modal-width: 600px;
430
+ --lookup-select-grid-row-height: 40px;
431
+
432
+ /* Shadows */
433
+ --lookup-select-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
434
+ --lookup-select-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
435
+ }
436
+ ```
437
+
438
+ ## License
439
+
440
+ MIT