@kamleshchandel/react-select-kc 1.0.4 → 1.0.5
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 +283 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1 +1,283 @@
|
|
|
1
|
-
|
|
1
|
+
# Universal Select
|
|
2
|
+
|
|
3
|
+
A powerful, flexible, and feature-rich React select component with support for single/multi-select, async options, search, keyboard navigation, and infinite scrolling.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
**Single & Multi-Select** - Support for both selection modes
|
|
8
|
+
**Search & Filter** - Built-in search with customizable filtering
|
|
9
|
+
**Async Options** - Load options dynamically with infinite scroll
|
|
10
|
+
**Keyboard Navigation** - Full keyboard support (Arrow keys, Enter, Escape)
|
|
11
|
+
**Customizable Styling** - Override default styles easily
|
|
12
|
+
**Persistent State** - Automatic localStorage integration
|
|
13
|
+
**Custom Renderers** - Render custom option and chip components
|
|
14
|
+
**Accessible** - Keyboard navigation and focus management
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm i @kamleshchandel/react-select-kc
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
or
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
yarn add @kamleshchandel/react-select-kc
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Basic Usage
|
|
29
|
+
|
|
30
|
+
### Single Select
|
|
31
|
+
|
|
32
|
+
```jsx
|
|
33
|
+
import UniversalSelect from "@kamleshchandel/react-select-kc";
|
|
34
|
+
import "@kamleshchandel/react-select-kc/style.css";
|
|
35
|
+
|
|
36
|
+
const options = [
|
|
37
|
+
{ id: 1, label: 'Option 1' },
|
|
38
|
+
{ id: 2, label: 'Option 2' },
|
|
39
|
+
{ id: 3, label: 'Option 3' },
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
function App() {
|
|
43
|
+
const [value, setValue] = useState(null);
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<UniversalSelect
|
|
47
|
+
options={options}
|
|
48
|
+
value={value}
|
|
49
|
+
onChange={setValue}
|
|
50
|
+
label="Select an option"
|
|
51
|
+
/>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Multi-Select
|
|
57
|
+
|
|
58
|
+
```jsx
|
|
59
|
+
const [selectedValues, setSelectedValues] = useState([]);
|
|
60
|
+
|
|
61
|
+
<UniversalSelect
|
|
62
|
+
options={options}
|
|
63
|
+
value={selectedValues}
|
|
64
|
+
onChange={setSelectedValues}
|
|
65
|
+
label="Select multiple options"
|
|
66
|
+
isMultiSelectAllow={true}
|
|
67
|
+
/>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Async Options with Infinite Scroll
|
|
71
|
+
|
|
72
|
+
```jsx
|
|
73
|
+
const loadAsyncOptions = async (searchQuery) => {
|
|
74
|
+
const response = await fetch(`/api/options?search=${searchQuery}`);
|
|
75
|
+
const data = await response.json();
|
|
76
|
+
return data;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
<UniversalSelect
|
|
80
|
+
loadAsyncOptions={loadAsyncOptions}
|
|
81
|
+
value={value}
|
|
82
|
+
onChange={setValue}
|
|
83
|
+
label="Search and select"
|
|
84
|
+
/>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Props
|
|
88
|
+
|
|
89
|
+
| Prop | Type | Default | Description |
|
|
90
|
+
|------|------|---------|-------------|
|
|
91
|
+
| `options` | `Array<{id: string\|number, label: string, disabled?: boolean}>` | `[]` | Array of options for static select |
|
|
92
|
+
| `loadAsyncOptions` | `(search: string) => Promise<Array>` | `undefined` | Async function to load options dynamically |
|
|
93
|
+
| `value` | `Object \| Array \| null` | `undefined` | Controlled value (single object or array for multi-select) |
|
|
94
|
+
| `onChange` | `(value) => void` | `undefined` | Callback when selection changes |
|
|
95
|
+
| `label` | `string` | `undefined` | Label text for the select |
|
|
96
|
+
| `isMultiSelectAllow` | `boolean` | `false` | Enable multi-select mode |
|
|
97
|
+
| `closeOnOutsideClick` | `boolean` | `true` | Close dropdown when clicking outside |
|
|
98
|
+
| `isClearOptionAllow` | `boolean` | `true` | Show clear button when value is selected |
|
|
99
|
+
| `isSearchAllow` | `boolean` | `true` | Enable search functionality |
|
|
100
|
+
| `selectStyle` | `object` | `{}` | Custom style overrides |
|
|
101
|
+
| `renderOption` | `(option) => ReactNode` | `undefined` | Custom renderer for options |
|
|
102
|
+
| `renderSelectedOptionChip` | `(option) => ReactNode` | `undefined` | Custom renderer for selected chips (multi-select) |
|
|
103
|
+
|
|
104
|
+
## Option Object Structure
|
|
105
|
+
|
|
106
|
+
Each option should follow this structure:
|
|
107
|
+
|
|
108
|
+
```javascript
|
|
109
|
+
{
|
|
110
|
+
id: string | number, // Unique identifier (required)
|
|
111
|
+
label: string, // Display text (required)
|
|
112
|
+
disabled?: boolean // Disable selection (optional)
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Advanced Examples
|
|
117
|
+
|
|
118
|
+
### Custom Option Rendering
|
|
119
|
+
|
|
120
|
+
```jsx
|
|
121
|
+
const renderOption = (option) => (
|
|
122
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
|
123
|
+
<img src={option.avatar} alt="" style={{ width: 24, height: 24 }} />
|
|
124
|
+
<div>
|
|
125
|
+
<div>{option.label}</div>
|
|
126
|
+
<small style={{ color: '#666' }}>{option.email}</small>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
<UniversalSelect
|
|
132
|
+
options={users}
|
|
133
|
+
renderOption={renderOption}
|
|
134
|
+
value={selectedUser}
|
|
135
|
+
onChange={setSelectedUser}
|
|
136
|
+
/>
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Custom Selected Chip Rendering
|
|
140
|
+
|
|
141
|
+
```jsx
|
|
142
|
+
const renderSelectedChip = (option) => (
|
|
143
|
+
<span style={{
|
|
144
|
+
background: option.color,
|
|
145
|
+
padding: '2px 8px',
|
|
146
|
+
borderRadius: '4px'
|
|
147
|
+
}}>
|
|
148
|
+
{option.label}
|
|
149
|
+
</span>
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
<UniversalSelect
|
|
153
|
+
options={tags}
|
|
154
|
+
isMultiSelectAllow={true}
|
|
155
|
+
renderSelectedOptionChip={renderSelectedChip}
|
|
156
|
+
value={selectedTags}
|
|
157
|
+
onChange={setSelectedTags}
|
|
158
|
+
/>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Custom Styling
|
|
162
|
+
|
|
163
|
+
```jsx
|
|
164
|
+
const customStyles = {
|
|
165
|
+
selectWrapper: {
|
|
166
|
+
maxWidth: '400px',
|
|
167
|
+
margin: '20px 0'
|
|
168
|
+
},
|
|
169
|
+
optionsList: {
|
|
170
|
+
maxHeight: '300px',
|
|
171
|
+
borderRadius: '8px'
|
|
172
|
+
},
|
|
173
|
+
highlightOption: {
|
|
174
|
+
backgroundColor: '#e3f2fd'
|
|
175
|
+
},
|
|
176
|
+
selectedOption: {
|
|
177
|
+
backgroundColor: '#bbdefb',
|
|
178
|
+
fontWeight: 'bold'
|
|
179
|
+
},
|
|
180
|
+
disabledOption: {
|
|
181
|
+
opacity: 0.5,
|
|
182
|
+
cursor: 'not-allowed'
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
<UniversalSelect
|
|
187
|
+
options={options}
|
|
188
|
+
selectStyle={customStyles}
|
|
189
|
+
value={value}
|
|
190
|
+
onChange={setValue}
|
|
191
|
+
/>
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Disabled Options
|
|
195
|
+
|
|
196
|
+
```jsx
|
|
197
|
+
const options = [
|
|
198
|
+
{ id: 1, label: 'Available Option' },
|
|
199
|
+
{ id: 2, label: 'Disabled Option', disabled: true },
|
|
200
|
+
{ id: 3, label: 'Another Available' },
|
|
201
|
+
];
|
|
202
|
+
|
|
203
|
+
<UniversalSelect options={options} />
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Uncontrolled Component (Internal State)
|
|
207
|
+
|
|
208
|
+
```jsx
|
|
209
|
+
// Value is automatically managed and persisted in localStorage
|
|
210
|
+
<UniversalSelect
|
|
211
|
+
options={options}
|
|
212
|
+
onChange={(value) => console.log('Selection changed:', value)}
|
|
213
|
+
label="Uncontrolled Select"
|
|
214
|
+
/>
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Keyboard Navigation
|
|
218
|
+
|
|
219
|
+
- **Arrow Down** - Move focus to next option
|
|
220
|
+
- **Arrow Up** - Move focus to previous option
|
|
221
|
+
- **Enter** - Select focused option
|
|
222
|
+
- **Escape** - Close dropdown
|
|
223
|
+
|
|
224
|
+
## CSS Classes
|
|
225
|
+
|
|
226
|
+
You can override these CSS classes for custom styling:
|
|
227
|
+
|
|
228
|
+
- `.custom-select` - Main wrapper
|
|
229
|
+
- `.custom-select-label` - Label element
|
|
230
|
+
- `.custom-select-trigger` - Trigger button
|
|
231
|
+
- `.custom-select-options` - Options list
|
|
232
|
+
- `.custom-select-option` - Individual option
|
|
233
|
+
- `.custom-select-option.selected` - Selected option
|
|
234
|
+
- `.custom-select-option.highlight` - Focused option
|
|
235
|
+
- `.custom-select-option.disabled` - Disabled option
|
|
236
|
+
- `.multiple-options-container` - Multi-select container
|
|
237
|
+
- `.multiple-options` - Individual selected chip
|
|
238
|
+
- `.no-custom-select-options` - No options message
|
|
239
|
+
|
|
240
|
+
## LocalStorage
|
|
241
|
+
|
|
242
|
+
The component automatically persists the selected value to localStorage under the key `UNIVERSAL_SELECT_VALUE`. This works for both controlled and uncontrolled modes.
|
|
243
|
+
|
|
244
|
+
## TypeScript Support
|
|
245
|
+
|
|
246
|
+
For TypeScript users, you can define your option type:
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
interface Option {
|
|
250
|
+
id: string | number;
|
|
251
|
+
label: string;
|
|
252
|
+
disabled?: boolean;
|
|
253
|
+
// Add your custom properties
|
|
254
|
+
[key: string]: any;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const options: Option[] = [
|
|
258
|
+
{ id: 1, label: 'Option 1' }
|
|
259
|
+
];
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
## Browser Support
|
|
263
|
+
|
|
264
|
+
- Chrome (latest)
|
|
265
|
+
- Firefox (latest)
|
|
266
|
+
- Safari (latest)
|
|
267
|
+
- Edge (latest)
|
|
268
|
+
|
|
269
|
+
## Contributing
|
|
270
|
+
|
|
271
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
272
|
+
|
|
273
|
+
## License
|
|
274
|
+
|
|
275
|
+
MIT
|
|
276
|
+
|
|
277
|
+
## Author
|
|
278
|
+
Kamlesh Chandel
|
|
279
|
+
MERN Stack Developer
|
|
280
|
+
|
|
281
|
+
## Support
|
|
282
|
+
|
|
283
|
+
For issues and questions, please open an issue on the GitHub repository.
|
package/package.json
CHANGED