@stackline/react-multiselect-dropdown 17.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alexandro Marques
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,374 @@
1
+ # @stackline/react-multiselect-dropdown
2
+
3
+ > A maintained React multiselect dropdown for React 17 applications, with controlled React state, searchable/grouped options, lazy loading hooks, custom render functions, skins, body-overlay positioning, and ADA-friendly keyboard/ARIA behavior.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@stackline/react-multiselect-dropdown.svg?style=flat-square)](https://www.npmjs.com/package/@stackline/react-multiselect-dropdown)
6
+ [![npm downloads](https://img.shields.io/npm/dt/@stackline/react-multiselect-dropdown.svg?style=flat-square)](https://www.npmjs.com/package/@stackline/react-multiselect-dropdown)
7
+ [![npm monthly](https://img.shields.io/npm/dm/@stackline/react-multiselect-dropdown.svg?style=flat-square)](https://www.npmjs.com/package/@stackline/react-multiselect-dropdown)
8
+ [![license](https://img.shields.io/npm/l/@stackline/react-multiselect-dropdown.svg?style=flat-square)](https://github.com/alexandroit/react-multiselect-dropdown/blob/main/LICENSE)
9
+ [![React 17](https://img.shields.io/badge/React-17.x-61dafb?style=flat-square&logo=react)](https://alexandro.net/docs/react/multiselect/react-17/)
10
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.9-blue?style=flat-square&logo=typescript)](https://www.typescriptlang.org)
11
+ [![GitHub stars](https://img.shields.io/github/stars/alexandroit/react-multiselect-dropdown.svg?style=flat-square)](https://github.com/alexandroit/react-multiselect-dropdown/stargazers)
12
+
13
+ **[Documentation & Live Demos](https://alexandro.net/docs/react/multiselect/)** | **[React 17 Demo](https://alexandro.net/docs/react/multiselect/react-17/)** | **[npm](https://www.npmjs.com/package/@stackline/react-multiselect-dropdown)** | **[Issues](https://github.com/alexandroit/react-multiselect-dropdown/issues)** | **[Repository](https://github.com/alexandroit/react-multiselect-dropdown)**
14
+
15
+ **Latest tested package release:** `17.0.0` for React `17.x`
16
+
17
+ ---
18
+
19
+ > **Credits:** Current maintenance, React line stewardship, publishing, and documentation by [Alexandro Paixao Marques](https://github.com/alexandroit/react-multiselect-dropdown).
20
+
21
+ ---
22
+
23
+ ## Why this library?
24
+
25
+ `@stackline/react-multiselect-dropdown` provides a maintained React multiselect component for applications that need predictable selection state, search, grouping, skins, keyboard support, and live tested examples.
26
+
27
+ The package is built around a controlled React API: pass `data`, bind `selectedItems`, receive updates through `onChange`, and customize behavior through a `settings` object. It also supports custom React render functions for option rows and selected badges, lazy loading callbacks, imperative `ref` methods, and body-overlay positioning for dialogs or clipped containers.
28
+
29
+ The current package release is `17.0.0` for React 17.x applications. It was tested in a clean React `17.0.2` application before publication to the local validation registry.
30
+
31
+ ## Features
32
+
33
+ | Feature | Supported |
34
+ | :--- | :---: |
35
+ | React 17 tested published release line | Yes |
36
+ | Multi-select and single-select modes | Yes |
37
+ | Controlled and uncontrolled selection | Yes |
38
+ | Search and filter | Yes |
39
+ | Group by field | Yes |
40
+ | Custom item render functions | Yes |
41
+ | Custom badge render functions | Yes |
42
+ | Lazy loading hooks | Yes |
43
+ | Add-new-item from search text | Yes |
44
+ | Ref methods for open, close, focus, select all, and clear | Yes |
45
+ | Built-in `classic`, `material`, `dark`, `custom`, and `brand` skins | Yes |
46
+ | ADA-friendly keyboard navigation, focus states, and ARIA labels | Yes |
47
+ | Dialog and overflow-container support through `appendToBody` / `tagToBody` | Yes |
48
+ | Versioned docs builds per React line | Yes |
49
+
50
+ ## Table of Contents
51
+
52
+ 1. [React Version Compatibility](#react-version-compatibility)
53
+ 2. [Installation](#installation)
54
+ 3. [Setup](#setup)
55
+ 4. [Styling and Skins](#styling-and-skins)
56
+ 5. [Basic Usage](#basic-usage)
57
+ 6. [Official React 17 Test Matrix](#official-react-17-test-matrix)
58
+ 7. [Custom Render Functions](#custom-render-functions)
59
+ 8. [Forms and Controlled State](#forms-and-controlled-state)
60
+ 9. [Lazy Loading and Dynamic Data](#lazy-loading-and-dynamic-data)
61
+ 10. [Dialogs and Overflow Containers](#dialogs-and-overflow-containers)
62
+ 11. [Events](#events)
63
+ 12. [Ref Methods](#ref-methods)
64
+ 13. [Run Locally](#run-locally)
65
+ 14. [License](#license)
66
+
67
+ ## React Version Compatibility
68
+
69
+ Each package family installs on its matching React family. Keep the package family aligned with the React major used by your application.
70
+
71
+ | Package family | React family | Peer range | Tested release window | Demo link |
72
+ | :---: | :---: | :---: | :---: | :--- |
73
+ | **17.x** | **React 17 only** | **`>=17.0.0 <18.0.0`** | **17.0.0 -> 17.0.2** | [React 17 family docs](https://alexandro.net/docs/react/multiselect/react-17/) |
74
+ | **18.x** | **React 18 only** | **`>=18.0.0 <19.0.0`** | Planned | [React docs](https://alexandro.net/docs/react/multiselect/) |
75
+ | **19.x** | **React 19 only** | **`>=19.0.0 <20.0.0`** | Planned | [React docs](https://alexandro.net/docs/react/multiselect/) |
76
+
77
+ ## Installation
78
+
79
+ ```bash
80
+ npm install @stackline/react-multiselect-dropdown@17.0.0 --save-exact
81
+ ```
82
+
83
+ Install `17.0.0` for React 17.x applications. The package includes its component styles and injects them at runtime, so no extra CSS import is required for the default experience.
84
+
85
+ ## Setup
86
+
87
+ ### 1. Import the component
88
+
89
+ ```tsx
90
+ import { MultiSelectDropdown } from '@stackline/react-multiselect-dropdown';
91
+ import type {
92
+ DropdownSettings,
93
+ MultiSelectDropdownHandle
94
+ } from '@stackline/react-multiselect-dropdown';
95
+ ```
96
+
97
+ ### 2. Keep selection in React state
98
+
99
+ ```tsx
100
+ const [selectedCountries, setSelectedCountries] = useState<Country[]>([]);
101
+ ```
102
+
103
+ ### 3. Pass a stable settings object
104
+
105
+ ```tsx
106
+ const settings = useMemo<DropdownSettings<Country>>(
107
+ () => ({
108
+ text: 'Select countries',
109
+ enableSearchFilter: true,
110
+ primaryKey: 'id',
111
+ labelKey: 'itemName',
112
+ badgeShowLimit: 3,
113
+ skin: 'classic'
114
+ }),
115
+ []
116
+ );
117
+ ```
118
+
119
+ ## Styling and Skins
120
+
121
+ Use `settings.skin` to switch the visual mode:
122
+
123
+ ```ts
124
+ setSettings((current) => ({
125
+ ...current,
126
+ skin: 'material'
127
+ }));
128
+ ```
129
+
130
+ Built-in skins:
131
+
132
+ | Skin | Usage |
133
+ | :--- | :--- |
134
+ | `classic` | Compact classic dropdown styling. |
135
+ | `material` | Material-style rounded controls and chips. |
136
+ | `dark` | Dark UI surfaces. |
137
+ | `custom` | CSS-variable starter skin for custom projects. |
138
+ | `brand` | Stackline brand skin. |
139
+
140
+ `settings.theme` is accepted as a legacy alias, but new React usage should configure only `settings.skin`.
141
+
142
+ ## Basic Usage
143
+
144
+ ```tsx
145
+ import { useMemo, useState } from 'react';
146
+ import {
147
+ MultiSelectDropdown,
148
+ type DropdownSettings
149
+ } from '@stackline/react-multiselect-dropdown';
150
+
151
+ type Country = {
152
+ id: number;
153
+ itemName: string;
154
+ capital: string;
155
+ region: string;
156
+ };
157
+
158
+ const countries: Country[] = [
159
+ { id: 1, itemName: 'Brazil', capital: 'Brasilia', region: 'South America' },
160
+ { id: 2, itemName: 'Canada', capital: 'Ottawa', region: 'North America' },
161
+ { id: 3, itemName: 'Portugal', capital: 'Lisbon', region: 'Europe' },
162
+ { id: 4, itemName: 'United States', capital: 'Washington, DC', region: 'North America' },
163
+ { id: 5, itemName: 'Argentina', capital: 'Buenos Aires', region: 'South America' },
164
+ { id: 6, itemName: 'Mexico', capital: 'Mexico City', region: 'North America' }
165
+ ];
166
+
167
+ export function CountrySelector() {
168
+ const [selectedCountries, setSelectedCountries] = useState<Country[]>([
169
+ countries[1]
170
+ ]);
171
+
172
+ const settings = useMemo<DropdownSettings<Country>>(
173
+ () => ({
174
+ singleSelection: false,
175
+ text: 'Select countries',
176
+ selectAllText: 'Select all',
177
+ unSelectAllText: 'Clear all',
178
+ enableSearchFilter: true,
179
+ searchPlaceholderText: 'Search',
180
+ primaryKey: 'id',
181
+ labelKey: 'itemName',
182
+ badgeShowLimit: 4,
183
+ maxHeight: 260,
184
+ showCheckbox: true,
185
+ noDataLabel: 'No data',
186
+ skin: 'classic',
187
+ appendToBody: false
188
+ }),
189
+ []
190
+ );
191
+
192
+ return (
193
+ <MultiSelectDropdown
194
+ data={countries}
195
+ selectedItems={selectedCountries}
196
+ onChange={setSelectedCountries}
197
+ settings={settings}
198
+ onSelect={(item) => console.log('selected', item)}
199
+ onDeSelect={(item) => console.log('removed', item)}
200
+ onSelectAll={(items) => console.log('selected all', items)}
201
+ onDeSelectAll={(items) => console.log('cleared all', items)}
202
+ />
203
+ );
204
+ }
205
+ ```
206
+
207
+ ## Official React 17 Test Matrix
208
+
209
+ The published React 17 release was tested in a clean React `17.0.2` application with `@stackline/react-multiselect-dropdown@17.0.0`. The docs use the same examples from that test app, including keyboard navigation, focus, ARIA behavior, badge counters, responsive action buttons, scrollable lists, and dialog-safe body overlays.
210
+
211
+ The same core scenarios are validated for the visual skins:
212
+
213
+ | # | Scenario | Main settings tested |
214
+ | :---: | :--- | :--- |
215
+ | 01 | Basic multi | `{ enableSearchFilter: true }` |
216
+ | 02 | All selected badges visible | `{ badgeShowLimit: 10 }` |
217
+ | 03 | Single selection | `{ singleSelection: true }` |
218
+ | 04 | Search by fields | `{ searchBy: ['itemName', 'capital'] }` |
219
+ | 05 | Grouped options | `{ groupBy: 'category', selectGroup: true }` |
220
+ | 06 | Selection limit | `{ limitSelection: 2, badgeShowLimit: 2 }` |
221
+ | 07 | Custom rendering | `renderItem` and `renderBadge` |
222
+ | 08 | Search and add item | `{ addNewItemOnFilter: true }` |
223
+ | 09 | Disabled state | `{ disabled: true }` |
224
+ | 10 | Controlled form validation | React state and derived validation |
225
+ | 11 | Long list with keyboard scroll | `{ maxHeight: 140 }` |
226
+ | 12 | Local lazy loading | `{ lazyLoading: true }` |
227
+ | 13 | Dialog and overflow container | `{ appendToBody: true, tagToBody: true }` |
228
+ | 14 | Body overlay auto direction | `{ autoPosition: true, position: 'top' }` |
229
+ | 15 | Ref methods | `openDropdown`, `closeDropdown`, `selectAll`, `clearSelection` |
230
+
231
+ ## Custom Render Functions
232
+
233
+ Use `renderItem` for option rows and `renderBadge` for selected chips:
234
+
235
+ ```tsx
236
+ <MultiSelectDropdown
237
+ data={countries}
238
+ selectedItems={selectedCountries}
239
+ onChange={setSelectedCountries}
240
+ settings={settings}
241
+ renderItem={(item, context) => (
242
+ <span>
243
+ <strong>{context.label}</strong>
244
+ <small>{item.capital}</small>
245
+ </span>
246
+ )}
247
+ renderBadge={(item) => (
248
+ <span>{item.itemName}</span>
249
+ )}
250
+ />
251
+ ```
252
+
253
+ ## Forms and Controlled State
254
+
255
+ Keep the selected array in React state and derive validity from that state:
256
+
257
+ ```tsx
258
+ const [name, setName] = useState('');
259
+ const [email, setEmail] = useState('');
260
+ const [selectedSkills, setSelectedSkills] = useState<Skill[]>([]);
261
+
262
+ const formIsValid = email.trim().length > 0 && selectedSkills.length > 0;
263
+
264
+ return (
265
+ <form onSubmit={(event) => event.preventDefault()}>
266
+ <input value={name} onChange={(event) => setName(event.target.value)} />
267
+ <input value={email} onChange={(event) => setEmail(event.target.value)} />
268
+
269
+ <MultiSelectDropdown
270
+ data={skills}
271
+ selectedItems={selectedSkills}
272
+ onChange={setSelectedSkills}
273
+ settings={skillSettings}
274
+ />
275
+
276
+ <button type="submit" disabled={!formIsValid}>
277
+ Submit
278
+ </button>
279
+ </form>
280
+ );
281
+ ```
282
+
283
+ ## Lazy Loading and Dynamic Data
284
+
285
+ Enable lazy loading through the settings object and append more rows when the list reaches the end:
286
+
287
+ ```tsx
288
+ const settings = {
289
+ text: 'Select people',
290
+ enableSearchFilter: true,
291
+ lazyLoading: true,
292
+ labelKey: 'name',
293
+ primaryKey: 'id',
294
+ maxHeight: 140
295
+ };
296
+
297
+ <MultiSelectDropdown
298
+ data={people}
299
+ selectedItems={selectedPeople}
300
+ onChange={setSelectedPeople}
301
+ settings={settings}
302
+ onScrollToEnd={() => {
303
+ setPeople((current) => current.concat(loadMorePeople()));
304
+ }}
305
+ />
306
+ ```
307
+
308
+ ## Dialogs and Overflow Containers
309
+
310
+ Use `appendToBody: true` or `tagToBody: true` when the dropdown is inside dialogs, modals, drawers, or containers that set `overflow: hidden` or `overflow: auto`.
311
+
312
+ ```tsx
313
+ const settings = {
314
+ text: 'Dialog dropdown',
315
+ enableSearchFilter: true,
316
+ skin: 'material',
317
+ appendToBody: true,
318
+ tagToBody: true,
319
+ autoPosition: true
320
+ };
321
+ ```
322
+
323
+ With body overlay enabled, the open panel is rendered against `document.body`, aligned to the original trigger, sized to the trigger, recalculated on open, scroll, resize, and content changes, and cleaned up when the dropdown closes or the component unmounts.
324
+
325
+ `autoPosition: true` treats `position` as a preferred direction. The menu opens upward only when there is meaningfully less room below and enough room above; otherwise it opens below and shrinks the scrollable list height to stay visible without covering the trigger.
326
+
327
+ ## Events
328
+
329
+ Available callbacks:
330
+
331
+ - `onChange`
332
+ - `onSelect`
333
+ - `onDeSelect`
334
+ - `onSelectAll`
335
+ - `onDeSelectAll`
336
+ - `onOpen`
337
+ - `onClose`
338
+ - `onScrollToEnd`
339
+ - `onAddFilterNewItem`
340
+ - `onGroupSelect`
341
+ - `onGroupDeSelect`
342
+
343
+ ## Ref Methods
344
+
345
+ ```tsx
346
+ const dropdownRef = useRef<MultiSelectDropdownHandle<Country>>(null);
347
+
348
+ dropdownRef.current?.openDropdown();
349
+ dropdownRef.current?.closeDropdown();
350
+ dropdownRef.current?.focusSearch();
351
+ dropdownRef.current?.selectAll();
352
+ dropdownRef.current?.deSelectAll();
353
+ dropdownRef.current?.clearSelection();
354
+ ```
355
+
356
+ ## Run Locally
357
+
358
+ ```bash
359
+ npm install
360
+ npm run build
361
+ npm test
362
+ ```
363
+
364
+ React 17 docs:
365
+
366
+ ```bash
367
+ cd docs-src/react-17
368
+ npm install
369
+ npm run build
370
+ ```
371
+
372
+ ## License
373
+
374
+ MIT