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 +1604 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +293 -0
- package/dist/index.d.ts +293 -0
- package/dist/index.js +1567 -0
- package/dist/index.js.map +1 -0
- package/dist/styles.css +873 -0
- package/package.json +75 -0
- package/readme.md +274 -0
package/package.json
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-lookup-select",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": [
|
|
7
|
+
"dist/styles.css"
|
|
8
|
+
],
|
|
9
|
+
"main": "dist/index.cjs",
|
|
10
|
+
"module": "dist/index.mjs",
|
|
11
|
+
"types": "dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.mjs",
|
|
16
|
+
"require": "./dist/index.cjs"
|
|
17
|
+
},
|
|
18
|
+
"./styles.css": "./dist/styles.css"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist"
|
|
22
|
+
],
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"react": ">=18",
|
|
25
|
+
"react-dom": ">=18"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsup src/index.ts --dts --format cjs,esm --sourcemap",
|
|
29
|
+
"dev": "tsup src/index.ts --dts --format cjs,esm --sourcemap --watch",
|
|
30
|
+
"lint": "eslint .",
|
|
31
|
+
"test": "vitest",
|
|
32
|
+
"prepare": "husky install",
|
|
33
|
+
"storybook": "storybook dev -p 6006",
|
|
34
|
+
"build-storybook": "storybook build"
|
|
35
|
+
},
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "git+https://github.com/onurlulardanlulardan/react-lookup-select.git"
|
|
42
|
+
},
|
|
43
|
+
"keywords": [
|
|
44
|
+
"react",
|
|
45
|
+
"select",
|
|
46
|
+
"lookup",
|
|
47
|
+
"modal",
|
|
48
|
+
"grid",
|
|
49
|
+
"combobox",
|
|
50
|
+
"picker",
|
|
51
|
+
"headless",
|
|
52
|
+
"a11y"
|
|
53
|
+
],
|
|
54
|
+
"description": "A headless, customizable React lookup select component with modal and grid support",
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@storybook/addon-docs": "^9.1.7",
|
|
57
|
+
"@storybook/react-vite": "^9.1.7",
|
|
58
|
+
"@testing-library/jest-dom": "^6.8.0",
|
|
59
|
+
"@testing-library/react": "^16.3.0",
|
|
60
|
+
"@testing-library/user-event": "^14.6.1",
|
|
61
|
+
"@types/react": "^19.1.13",
|
|
62
|
+
"@types/react-dom": "^19.1.9",
|
|
63
|
+
"@typescript-eslint/eslint-plugin": "^8.44.0",
|
|
64
|
+
"@typescript-eslint/parser": "^8.44.0",
|
|
65
|
+
"eslint": "^9.35.0",
|
|
66
|
+
"eslint-plugin-storybook": "^9.1.7",
|
|
67
|
+
"husky": "^9.1.7",
|
|
68
|
+
"jsdom": "^27.0.0",
|
|
69
|
+
"prettier": "^3.6.2",
|
|
70
|
+
"storybook": "^9.1.7",
|
|
71
|
+
"tsup": "^8.5.0",
|
|
72
|
+
"typescript": "^5.9.2",
|
|
73
|
+
"vitest": "^3.2.4"
|
|
74
|
+
}
|
|
75
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
# react-lookup-select
|
|
2
|
+
|
|
3
|
+
A headless, customizable React lookup select component with modal and grid support for single/multiple selection.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Trigger (ComboBox appearance)**: Click to open modal
|
|
8
|
+
- **Grid inside Modal**: Single/multiple row selection (with checkboxes or row clicks)
|
|
9
|
+
- **Selection Modes**: single | multiple
|
|
10
|
+
- **Return Values**: id and text fields are user-mappable
|
|
11
|
+
- **Full Customization**: themes, icons, grid columns, cell renderers
|
|
12
|
+
- **Data Sources**: data (array) or dataSource (async: pagination/sort/search)
|
|
13
|
+
- **Accessibility**: keyboard navigation, ARIA roles, focus trap
|
|
14
|
+
- **Performance**: virtualization option for large datasets
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm i react-lookup-select
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
import { LookupSelect } from 'react-lookup-select';
|
|
24
|
+
import 'react-lookup-select/styles.css';
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Example Usage
|
|
28
|
+
|
|
29
|
+
### Single Selection – text = name + ' ' + surname
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
<LookupSelect
|
|
33
|
+
mode="single"
|
|
34
|
+
data={users}
|
|
35
|
+
columns={[
|
|
36
|
+
{ key: 'name', title: 'Name' },
|
|
37
|
+
{ key: 'surname', title: 'Surname' },
|
|
38
|
+
{ key: 'email', title: 'Email' },
|
|
39
|
+
]}
|
|
40
|
+
mapper={{
|
|
41
|
+
getId: (u) => u.userId,
|
|
42
|
+
getText: (u) => `${u.name} ${u.surname}`,
|
|
43
|
+
}}
|
|
44
|
+
returnShape="id-text"
|
|
45
|
+
onChange={(val) => console.log(val)}
|
|
46
|
+
/>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Multiple Selection – Custom return
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
<LookupSelect
|
|
53
|
+
mode="multiple"
|
|
54
|
+
data={products}
|
|
55
|
+
columns={[
|
|
56
|
+
{ key: 'sku', title: 'SKU' },
|
|
57
|
+
{ key: 'title', title: 'Product' },
|
|
58
|
+
{ key: 'price', title: 'Price' },
|
|
59
|
+
]}
|
|
60
|
+
mapper={{ getId: (p) => p.id, getText: (p) => p.title }}
|
|
61
|
+
returnShape="custom"
|
|
62
|
+
returnMap={{ map: (p) => ({ key: p.id, label: p.title, p }) }}
|
|
63
|
+
onChange={(vals) => save(vals)}
|
|
64
|
+
/>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Server-side data + search/pagination
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
const dataSource = async (q: QueryState) => {
|
|
71
|
+
const res = await fetch(
|
|
72
|
+
`/api/users?page=${q.page}&size=${q.pageSize}&search=${q.search ?? ''}`
|
|
73
|
+
);
|
|
74
|
+
const json = await res.json();
|
|
75
|
+
return { rows: json.items, total: json.total };
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
<LookupSelect
|
|
79
|
+
mode="multiple"
|
|
80
|
+
dataSource={dataSource}
|
|
81
|
+
pageSize={50}
|
|
82
|
+
columns={[
|
|
83
|
+
{ key: 'name', title: 'Name', sortable: true },
|
|
84
|
+
{ key: 'surname', title: 'Surname', sortable: true },
|
|
85
|
+
{ key: 'department', title: 'Department' },
|
|
86
|
+
]}
|
|
87
|
+
mapper={{ getId: (u) => u.id, getText: (u) => `${u.name} ${u.surname}` }}
|
|
88
|
+
onQueryChange={(q) => console.log('query changed', q)}
|
|
89
|
+
/>;
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Theming and Customization
|
|
93
|
+
|
|
94
|
+
### Pre-built Themes
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
{
|
|
98
|
+
/* Default theme */
|
|
99
|
+
}
|
|
100
|
+
<LookupSelect variant="default" {...props} />;
|
|
101
|
+
|
|
102
|
+
{
|
|
103
|
+
/* Dark theme */
|
|
104
|
+
}
|
|
105
|
+
<LookupSelect variant="dark" {...props} />;
|
|
106
|
+
|
|
107
|
+
{
|
|
108
|
+
/* Minimal theme */
|
|
109
|
+
}
|
|
110
|
+
<LookupSelect variant="minimal" {...props} />;
|
|
111
|
+
|
|
112
|
+
{
|
|
113
|
+
/* Compact theme */
|
|
114
|
+
}
|
|
115
|
+
<LookupSelect variant="compact" {...props} />;
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Size Options
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
{
|
|
122
|
+
/* Small size */
|
|
123
|
+
}
|
|
124
|
+
<LookupSelect size="small" {...props} />;
|
|
125
|
+
|
|
126
|
+
{
|
|
127
|
+
/* Medium size (default) */
|
|
128
|
+
}
|
|
129
|
+
<LookupSelect size="medium" {...props} />;
|
|
130
|
+
|
|
131
|
+
{
|
|
132
|
+
/* Large size */
|
|
133
|
+
}
|
|
134
|
+
<LookupSelect size="large" {...props} />;
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Customization with CSS Variables
|
|
138
|
+
|
|
139
|
+
```tsx
|
|
140
|
+
<LookupSelect
|
|
141
|
+
theme={{
|
|
142
|
+
colorPrimary: '#8b5cf6',
|
|
143
|
+
colorBg: '#faf5ff',
|
|
144
|
+
colorText: '#4c1d95',
|
|
145
|
+
borderRadius: 12,
|
|
146
|
+
spacing: 10,
|
|
147
|
+
}}
|
|
148
|
+
{...props}
|
|
149
|
+
/>
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Customization with CSS Classes
|
|
153
|
+
|
|
154
|
+
```tsx
|
|
155
|
+
<LookupSelect
|
|
156
|
+
classNames={{
|
|
157
|
+
root: 'my-custom-lookup',
|
|
158
|
+
trigger: 'my-custom-trigger',
|
|
159
|
+
modal: 'my-custom-modal',
|
|
160
|
+
grid: 'my-custom-grid',
|
|
161
|
+
}}
|
|
162
|
+
{...props}
|
|
163
|
+
/>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
```css
|
|
167
|
+
.my-custom-lookup {
|
|
168
|
+
--lookup-select-color-primary: #10b981;
|
|
169
|
+
--lookup-select-border-radius: 8px;
|
|
170
|
+
--lookup-select-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.my-custom-trigger {
|
|
174
|
+
border: 2px solid #10b981;
|
|
175
|
+
background: linear-gradient(135deg, #ecfdf5 0%, #d1fae5 100%);
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Inline Styles
|
|
180
|
+
|
|
181
|
+
```tsx
|
|
182
|
+
<LookupSelect
|
|
183
|
+
styles={{
|
|
184
|
+
root: { border: '2px solid #f59e0b', borderRadius: '8px' },
|
|
185
|
+
trigger: { background: '#fef3c7', color: '#92400e' },
|
|
186
|
+
modal: { boxShadow: '0 25px 50px -12px rgba(0, 0, 0, 0.25)' },
|
|
187
|
+
}}
|
|
188
|
+
{...props}
|
|
189
|
+
/>
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Virtualization - Large Data Performance
|
|
193
|
+
|
|
194
|
+
### Auto Virtualization
|
|
195
|
+
|
|
196
|
+
```tsx
|
|
197
|
+
<LookupSelect
|
|
198
|
+
data={largeDataArray} // 1000+ records
|
|
199
|
+
virtualization={true} // Auto-enable when data > 100 items
|
|
200
|
+
{...props}
|
|
201
|
+
/>
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Manual Virtualization Configuration
|
|
205
|
+
|
|
206
|
+
```tsx
|
|
207
|
+
<LookupSelect
|
|
208
|
+
data={tenThousandItems}
|
|
209
|
+
virtualization={true}
|
|
210
|
+
virtualRowHeight={48} // Fixed row height
|
|
211
|
+
virtualContainerHeight={500} // Scroll container height
|
|
212
|
+
virtualOverscan={10} // Buffer for smooth scrolling
|
|
213
|
+
virtualThreshold={100} // Enable when data exceeds this
|
|
214
|
+
{...props}
|
|
215
|
+
/>
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Hybrid Mode - Server + Client Virtualization
|
|
219
|
+
|
|
220
|
+
```tsx
|
|
221
|
+
<LookupSelect
|
|
222
|
+
dataSource={serverDataSource}
|
|
223
|
+
virtualization={true}
|
|
224
|
+
pageSize={100} // Fetch 500 records from server (5x buffer)
|
|
225
|
+
virtualContainerHeight={400}
|
|
226
|
+
virtualRowHeight={40}
|
|
227
|
+
{...props}
|
|
228
|
+
/>
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Performance Comparison
|
|
232
|
+
|
|
233
|
+
| Data Size | Virtualization | DOM Elements | Render Time | Memory |
|
|
234
|
+
| ----------- | -------------- | ------------ | ----------- | ------ |
|
|
235
|
+
| 10,000 item | ❌ Disabled | 10,000 rows | ~2000ms | ~200MB |
|
|
236
|
+
| 10,000 item | ✅ Enabled | ~20 rows | ~50ms | ~15MB |
|
|
237
|
+
|
|
238
|
+
### Usage Recommendations
|
|
239
|
+
|
|
240
|
+
- **100+ records:** Auto virtualization
|
|
241
|
+
- **1,000+ records:** Client-side virtualization
|
|
242
|
+
- **10,000+ records:** Hybrid mode (server + client)
|
|
243
|
+
- **100,000+ records:** Pure server pagination
|
|
244
|
+
|
|
245
|
+
### All CSS Customization Variables
|
|
246
|
+
|
|
247
|
+
```css
|
|
248
|
+
:root {
|
|
249
|
+
/* Colors */
|
|
250
|
+
--lookup-select-color-primary: #0066cc;
|
|
251
|
+
--lookup-select-color-primary-hover: #0052a3;
|
|
252
|
+
--lookup-select-color-bg: #ffffff;
|
|
253
|
+
--lookup-select-color-text: #333333;
|
|
254
|
+
--lookup-select-color-border: #d1d5db;
|
|
255
|
+
|
|
256
|
+
/* Layout */
|
|
257
|
+
--lookup-select-border-radius: 6px;
|
|
258
|
+
--lookup-select-spacing: 8px;
|
|
259
|
+
--lookup-select-font-size: 14px;
|
|
260
|
+
|
|
261
|
+
/* Component specific sizes */
|
|
262
|
+
--lookup-select-trigger-height: 36px;
|
|
263
|
+
--lookup-select-modal-width: 600px;
|
|
264
|
+
--lookup-select-grid-row-height: 40px;
|
|
265
|
+
|
|
266
|
+
/* Shadows */
|
|
267
|
+
--lookup-select-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
|
|
268
|
+
--lookup-select-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
## License
|
|
273
|
+
|
|
274
|
+
MIT
|