@keenmate/web-multiselect 1.0.0-rc05 → 1.0.0-rc08
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 +329 -56
- package/dist/multiselect.js +1367 -829
- package/dist/multiselect.umd.js +14 -14
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/scss/_css-variables.scss +379 -0
- package/src/scss/_input-dropdown.scss +9 -0
- package/src/scss/_options.scss +18 -0
- package/src/scss/_variables.scss +2 -0
- package/src/scss/main.scss +2 -0
package/README.md
CHANGED
|
@@ -7,7 +7,9 @@ A lightweight, accessible multiselect web component with typeahead search, RTL l
|
|
|
7
7
|
|
|
8
8
|
## Features
|
|
9
9
|
|
|
10
|
-
-
|
|
10
|
+
- 📝 **Declarative HTML** - Use standard `<option>` and `<optgroup>` elements - no JavaScript required for simple cases!
|
|
11
|
+
- ⚡ **Virtual Scrolling** - Handle 15,000+ options instantly (25× faster opening, 99.8% memory reduction)
|
|
12
|
+
- 🔍 **Flexible Search Modes** - Filter (hide non-matches) or navigate (jump to matches, keep all visible)
|
|
11
13
|
- ⌨️ **Keyboard Navigation** - Full keyboard support (arrows, Enter, Esc, Tab)
|
|
12
14
|
- 🎨 **Rich Content** - Icons, subtitles, and multiline text support
|
|
13
15
|
- 📊 **Multiple Display Modes** - Pills, count, compact, or partial (pills + threshold)
|
|
@@ -29,25 +31,51 @@ npm install @keenmate/web-multiselect
|
|
|
29
31
|
|
|
30
32
|
## Usage
|
|
31
33
|
|
|
32
|
-
###
|
|
34
|
+
### Declarative (No JavaScript!)
|
|
35
|
+
|
|
36
|
+
Perfect for simple forms - just use standard HTML `<option>` elements:
|
|
37
|
+
|
|
38
|
+
```html
|
|
39
|
+
<!-- Simple choice -->
|
|
40
|
+
<web-multiselect multiple="false">
|
|
41
|
+
<option value="yes">Yes</option>
|
|
42
|
+
<option value="no">No</option>
|
|
43
|
+
<option value="maybe" selected>Maybe</option>
|
|
44
|
+
</web-multiselect>
|
|
45
|
+
|
|
46
|
+
<!-- With icons -->
|
|
47
|
+
<web-multiselect>
|
|
48
|
+
<option value="apple" data-icon="🍎">Apple</option>
|
|
49
|
+
<option value="banana" data-icon="🍌" selected>Banana</option>
|
|
50
|
+
<option value="orange" data-icon="🍊">Orange</option>
|
|
51
|
+
</web-multiselect>
|
|
52
|
+
|
|
53
|
+
<!-- With groups -->
|
|
54
|
+
<web-multiselect>
|
|
55
|
+
<optgroup label="Frontend">
|
|
56
|
+
<option value="js" data-icon="🟨">JavaScript</option>
|
|
57
|
+
<option value="ts" data-icon="🔷">TypeScript</option>
|
|
58
|
+
</optgroup>
|
|
59
|
+
<optgroup label="Backend">
|
|
60
|
+
<option value="python" data-icon="🐍" selected>Python</option>
|
|
61
|
+
<option value="java" data-icon="☕">Java</option>
|
|
62
|
+
</optgroup>
|
|
63
|
+
</web-multiselect>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Programmatic (With JavaScript)
|
|
67
|
+
|
|
68
|
+
For dynamic data and advanced features:
|
|
33
69
|
|
|
34
70
|
```html
|
|
35
71
|
<!-- Multi-select -->
|
|
36
|
-
<
|
|
72
|
+
<web-multiselect
|
|
73
|
+
id="my-select"
|
|
37
74
|
search-placeholder="Search options..."
|
|
38
75
|
initial-values='["js","ts"]'>
|
|
39
|
-
</
|
|
40
|
-
|
|
41
|
-
<!-- Single-select -->
|
|
42
|
-
<multi-select
|
|
43
|
-
multiple="false"
|
|
44
|
-
search-placeholder="Select one..."
|
|
45
|
-
initial-values='["python"]'>
|
|
46
|
-
</multi-select>
|
|
76
|
+
</web-multiselect>
|
|
47
77
|
```
|
|
48
78
|
|
|
49
|
-
### With JavaScript/TypeScript
|
|
50
|
-
|
|
51
79
|
```typescript
|
|
52
80
|
// Import the component (includes styles)
|
|
53
81
|
import '@keenmate/web-multiselect';
|
|
@@ -55,7 +83,7 @@ import '@keenmate/web-multiselect';
|
|
|
55
83
|
// Or import styles separately if needed
|
|
56
84
|
import '@keenmate/web-multiselect/style.css';
|
|
57
85
|
|
|
58
|
-
const multiselect = document.querySelector('
|
|
86
|
+
const multiselect = document.querySelector('web-multiselect');
|
|
59
87
|
|
|
60
88
|
// Set options programmatically
|
|
61
89
|
multiselect.options = [
|
|
@@ -102,10 +130,12 @@ multiselect.setSelected(['js', 'ts']);
|
|
|
102
130
|
| `empty-message` | `string` | `'No results found'` | Message when no options found |
|
|
103
131
|
| `loading-message` | `string` | `'Loading...'` | Message while loading async data |
|
|
104
132
|
| `min-search-length` | `number` | `0` | Minimum search length for async |
|
|
133
|
+
| `keep-options-on-search` | `boolean` | `true` | Keep initial options visible when searchCallback is active (hybrid search) |
|
|
105
134
|
| `sticky-actions` | `boolean` | `true` | Keep Select All/Clear All buttons fixed at top while scrolling |
|
|
106
135
|
| `lock-placement` | `boolean` | `true` | Lock dropdown placement after first open to prevent flipping |
|
|
107
136
|
| `enable-search` | `boolean` | `true` | Enable/disable search functionality |
|
|
108
137
|
| `search-input-mode` | `'normal' \| 'readonly' \| 'hidden'` | `'normal'` | Search input display mode |
|
|
138
|
+
| `search-mode` | `'filter' \| 'navigate'` | `'filter'` | Search behavior: 'filter' hides non-matches, 'navigate' jumps to matches |
|
|
109
139
|
| `allow-add-new` | `boolean` | `false` | Allow adding new options not in the list |
|
|
110
140
|
| `value-member` | `string` | - | Property name for value/ID extraction from custom objects |
|
|
111
141
|
| `display-value-member` | `string` | - | Property name for display text extraction from custom objects |
|
|
@@ -117,6 +147,10 @@ multiselect.setSelected(['js', 'ts']);
|
|
|
117
147
|
| `name` | `string` | - | HTML form field name for form integration (creates hidden input) |
|
|
118
148
|
| `value-format` | `'json' \| 'csv' \| 'array'` | `'json'` | Format for form value serialization |
|
|
119
149
|
| `initial-values` | `string` (JSON array) | - | Pre-selected values |
|
|
150
|
+
| `enable-virtual-scroll` | `boolean` | `false` | Enable virtual scrolling for large datasets |
|
|
151
|
+
| `virtual-scroll-threshold` | `number` | `100` | Minimum items before virtual scroll activates |
|
|
152
|
+
| `option-height` | `number` | `50` | Fixed height for each option in pixels (required for virtual scroll) |
|
|
153
|
+
| `virtual-scroll-buffer` | `number` | `10` | Buffer size - extra items rendered above/below viewport |
|
|
120
154
|
|
|
121
155
|
## Properties
|
|
122
156
|
|
|
@@ -133,6 +167,17 @@ multiselect.onSearch = async (searchTerm) => {
|
|
|
133
167
|
return await response.json();
|
|
134
168
|
};
|
|
135
169
|
|
|
170
|
+
// Pre-process search terms before calling searchCallback
|
|
171
|
+
multiselect.beforeSearchCallback = (searchTerm) => {
|
|
172
|
+
// Remove accents: "café" → "cafe"
|
|
173
|
+
const normalized = searchTerm.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
|
|
174
|
+
|
|
175
|
+
// Block search if too short (return null to prevent search)
|
|
176
|
+
if (normalized.length < 2) return null;
|
|
177
|
+
|
|
178
|
+
return normalized; // Return transformed term
|
|
179
|
+
};
|
|
180
|
+
|
|
136
181
|
// Event callbacks
|
|
137
182
|
multiselect.onSelect = (option) => {
|
|
138
183
|
console.log('Selected:', option);
|
|
@@ -222,6 +267,7 @@ multiselect.addNewCallback = async (value) => {
|
|
|
222
267
|
## Keyboard Shortcuts
|
|
223
268
|
|
|
224
269
|
- **↑ ↓** - Navigate up/down through options
|
|
270
|
+
- **Ctrl+↑ Ctrl+↓** - Jump between matched items (navigate mode only)
|
|
225
271
|
- **Enter** - Select focused option
|
|
226
272
|
- **Escape** - Close dropdown
|
|
227
273
|
- **Tab** - Close dropdown and move to next field
|
|
@@ -234,7 +280,7 @@ multiselect.addNewCallback = async (value) => {
|
|
|
234
280
|
Icons support multiple formats - emojis, SVG markup, Font Awesome, images, or any HTML:
|
|
235
281
|
|
|
236
282
|
```html
|
|
237
|
-
<
|
|
283
|
+
<web-multiselect id="frameworks"></web-multiselect>
|
|
238
284
|
|
|
239
285
|
<script type="module">
|
|
240
286
|
const select = document.getElementById('frameworks');
|
|
@@ -281,12 +327,12 @@ select.options = [
|
|
|
281
327
|
### Async Data Loading
|
|
282
328
|
|
|
283
329
|
```html
|
|
284
|
-
<
|
|
330
|
+
<web-multiselect
|
|
285
331
|
id="async-select"
|
|
286
332
|
min-search-length="2"
|
|
287
333
|
loading-message="Searching..."
|
|
288
334
|
empty-message="No products found">
|
|
289
|
-
</
|
|
335
|
+
</web-multiselect>
|
|
290
336
|
|
|
291
337
|
<script type="module">
|
|
292
338
|
const select = document.getElementById('async-select');
|
|
@@ -299,33 +345,243 @@ select.options = [
|
|
|
299
345
|
</script>
|
|
300
346
|
```
|
|
301
347
|
|
|
348
|
+
### Hybrid Static + Dynamic Search
|
|
349
|
+
|
|
350
|
+
Show popular items initially, then switch to full database search when the user types. Perfect for showing "Top 10" items while supporting comprehensive search:
|
|
351
|
+
|
|
352
|
+
```html
|
|
353
|
+
<web-multiselect
|
|
354
|
+
id="hybrid-select"
|
|
355
|
+
min-search-length="3"
|
|
356
|
+
keep-options-on-search="true">
|
|
357
|
+
</web-multiselect>
|
|
358
|
+
|
|
359
|
+
<script type="module">
|
|
360
|
+
const select = document.getElementById('hybrid-select');
|
|
361
|
+
|
|
362
|
+
// Set initial popular items (shown when dropdown opens)
|
|
363
|
+
select.options = [
|
|
364
|
+
{ id: 1, name: 'React' },
|
|
365
|
+
{ id: 2, name: 'Vue' },
|
|
366
|
+
{ id: 3, name: 'Angular' },
|
|
367
|
+
{ id: 4, name: 'Svelte' },
|
|
368
|
+
{ id: 5, name: 'Solid' }
|
|
369
|
+
];
|
|
370
|
+
|
|
371
|
+
// Pre-process search terms (remove accents, validate, etc.)
|
|
372
|
+
select.beforeSearchCallback = (searchTerm) => {
|
|
373
|
+
// Remove accents: "café" → "cafe"
|
|
374
|
+
const normalized = searchTerm.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
|
|
375
|
+
|
|
376
|
+
// Block search if too short (return null to prevent search)
|
|
377
|
+
if (normalized.length < 2) return null;
|
|
378
|
+
|
|
379
|
+
return normalized;
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
// Search full database when user types 3+ characters
|
|
383
|
+
select.onSearch = async (searchTerm) => {
|
|
384
|
+
const response = await fetch(`/api/frameworks/search?q=${searchTerm}`);
|
|
385
|
+
return await response.json();
|
|
386
|
+
};
|
|
387
|
+
</script>
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
**How it works:**
|
|
391
|
+
1. **Dropdown opens** → Shows 5 popular frameworks
|
|
392
|
+
2. **User types "rea"** → Calls API, shows all matching results from database
|
|
393
|
+
3. **User clears search** → Shows 5 popular frameworks again
|
|
394
|
+
4. **User types "café"** → `beforeSearchCallback` converts to "cafe", then searches
|
|
395
|
+
|
|
396
|
+
**Key options:**
|
|
397
|
+
- `keep-options-on-search="true"` (default) - Keep initial options visible when search is empty/short
|
|
398
|
+
- `beforeSearchCallback` - Transform search text or block search by returning `null`
|
|
399
|
+
- `min-search-length` - Minimum characters before triggering search (shows initial options below this)
|
|
400
|
+
|
|
401
|
+
### Virtual Scrolling for Large Datasets
|
|
402
|
+
|
|
403
|
+
Handle 10,000+ options with smooth 60fps performance by rendering only visible items:
|
|
404
|
+
|
|
405
|
+
```html
|
|
406
|
+
<web-multiselect
|
|
407
|
+
id="large-dataset"
|
|
408
|
+
enable-virtual-scroll="true"
|
|
409
|
+
virtual-scroll-threshold="100"
|
|
410
|
+
option-height="50"
|
|
411
|
+
virtual-scroll-buffer="10"
|
|
412
|
+
search-mode="filter"
|
|
413
|
+
max-height="400px">
|
|
414
|
+
</web-multiselect>
|
|
415
|
+
|
|
416
|
+
<script type="module">
|
|
417
|
+
import '@keenmate/web-multiselect';
|
|
418
|
+
|
|
419
|
+
const select = document.getElementById('large-dataset');
|
|
420
|
+
|
|
421
|
+
// Generate 15,000 options
|
|
422
|
+
const largeDataset = Array.from({ length: 15000 }, (_, i) => ({
|
|
423
|
+
value: i,
|
|
424
|
+
label: `Item ${i.toString().padStart(5, '0')}`
|
|
425
|
+
}));
|
|
426
|
+
|
|
427
|
+
select.options = largeDataset;
|
|
428
|
+
</script>
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
**Performance Comparison (15,000 items):**
|
|
432
|
+
|
|
433
|
+
| Metric | Without Virtual Scroll | With Virtual Scroll | Improvement |
|
|
434
|
+
|--------|------------------------|---------------------|-------------|
|
|
435
|
+
| Initial render | 750ms | 30ms | **25× faster** |
|
|
436
|
+
| Search keystroke | 200-500ms | 15ms | **13-33× faster** |
|
|
437
|
+
| DOM nodes | 15,000 | ~30 | **99.8% reduction** |
|
|
438
|
+
| Memory usage | ~7.5 MB | ~15 KB | **500× less** |
|
|
439
|
+
|
|
440
|
+
**Configuration:**
|
|
441
|
+
|
|
442
|
+
- `enable-virtual-scroll="true"` - Enable virtual scrolling (default: `false`)
|
|
443
|
+
- `virtual-scroll-threshold="100"` - Auto-activate when this many items are present (default: `100`)
|
|
444
|
+
- `option-height="50"` - Fixed height per option in pixels (default: `50px`)
|
|
445
|
+
- `virtual-scroll-buffer="10"` - Extra items rendered above/below viewport for smooth scrolling (default: `10`)
|
|
446
|
+
|
|
447
|
+
**How it works:**
|
|
448
|
+
- Only renders ~30 visible items instead of all 15,000 DOM elements
|
|
449
|
+
- Uses absolute positioning with calculated offsets
|
|
450
|
+
- Maintains 10-item buffer zones above/below viewport for smooth scrolling
|
|
451
|
+
- Automatically calculates visible range based on scroll position
|
|
452
|
+
- Works seamlessly with search filtering and selection
|
|
453
|
+
|
|
454
|
+
**Requirements:**
|
|
455
|
+
- All options must have the same fixed height (enforced via CSS)
|
|
456
|
+
- Not compatible with grouped options (automatically falls back to normal rendering)
|
|
457
|
+
- Works with both filter and navigate search modes
|
|
458
|
+
|
|
459
|
+
**Example with search:**
|
|
460
|
+
```html
|
|
461
|
+
<!-- Virtual scroll + filter search for optimal large dataset performance -->
|
|
462
|
+
<web-multiselect
|
|
463
|
+
id="products"
|
|
464
|
+
enable-virtual-scroll="true"
|
|
465
|
+
search-mode="filter"
|
|
466
|
+
value-member="id"
|
|
467
|
+
display-value-member="name"
|
|
468
|
+
max-height="400px">
|
|
469
|
+
</web-multiselect>
|
|
470
|
+
|
|
471
|
+
<script type="module">
|
|
472
|
+
const select = document.getElementById('products');
|
|
473
|
+
|
|
474
|
+
// Load from API
|
|
475
|
+
const response = await fetch('/api/products');
|
|
476
|
+
const products = await response.json();
|
|
477
|
+
|
|
478
|
+
select.options = products; // Could be 10,000+ items
|
|
479
|
+
</script>
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
**Live Demo:**
|
|
483
|
+
See [examples-performance.html](examples-performance.html) for a working demo with 15,000 randomly generated options.
|
|
484
|
+
|
|
485
|
+
### Virtual Scrolling
|
|
486
|
+
|
|
487
|
+
Handle massive datasets (10,000+ items) with instant performance using virtual scrolling. Only visible items (~30) are rendered in the DOM, dramatically reducing memory usage and improving responsiveness.
|
|
488
|
+
|
|
489
|
+
**Enable virtual scrolling:**
|
|
490
|
+
```html
|
|
491
|
+
<web-multiselect
|
|
492
|
+
enable-virtual-scroll="true"
|
|
493
|
+
virtual-scroll-threshold="100"
|
|
494
|
+
option-height="50"
|
|
495
|
+
virtual-scroll-buffer="10">
|
|
496
|
+
</web-multiselect>
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
**Performance improvements with 15,000 items:**
|
|
500
|
+
- **Dropdown opening**: 750ms → 30ms (25× faster)
|
|
501
|
+
- **Search performance**: 200-500ms → 15ms per keystroke (13-33× faster)
|
|
502
|
+
- **Memory usage**: 7.5 MB → 15 KB (99.8% reduction)
|
|
503
|
+
- **DOM nodes**: 15,000 → ~30 visible items
|
|
504
|
+
|
|
505
|
+
**Configuration:**
|
|
506
|
+
- `enable-virtual-scroll="true"` - Opt-in to virtual scrolling
|
|
507
|
+
- `virtual-scroll-threshold="100"` - Auto-activates at 100+ items (default)
|
|
508
|
+
- `option-height="50"` - Fixed height per option in pixels (default: 50px)
|
|
509
|
+
- `virtual-scroll-buffer="10"` - Extra items rendered above/below viewport (default: 10)
|
|
510
|
+
|
|
511
|
+
**Features:**
|
|
512
|
+
- Full keyboard navigation (arrows, Page Up/Down, Home/End)
|
|
513
|
+
- Smooth mouse wheel scrolling
|
|
514
|
+
- Drag scrollbar support
|
|
515
|
+
- Works with search in both filter and navigate modes
|
|
516
|
+
- Automatic activation based on threshold
|
|
517
|
+
|
|
518
|
+
**Limitations:**
|
|
519
|
+
- Groups (`<optgroup>`) are disabled in virtual scroll mode (automatically falls back to standard rendering)
|
|
520
|
+
- All options must have consistent height (enforced via CSS)
|
|
521
|
+
|
|
522
|
+
**Live Demo:**
|
|
523
|
+
See [examples-performance.html](examples-performance.html) for a working demo testing virtual scroll with 15,000 randomly generated options.
|
|
524
|
+
|
|
525
|
+
### Search Modes: Filter vs Navigate
|
|
526
|
+
|
|
527
|
+
Choose between two search behaviors:
|
|
528
|
+
|
|
529
|
+
**Filter Mode** (default) - Hide non-matching options as you type:
|
|
530
|
+
```html
|
|
531
|
+
<web-multiselect search-mode="filter" id="countries"></web-multiselect>
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
**Navigate Mode** - Keep all options visible, jump to matches:
|
|
535
|
+
```html
|
|
536
|
+
<web-multiselect search-mode="navigate" id="states"></web-multiselect>
|
|
537
|
+
|
|
538
|
+
<script>
|
|
539
|
+
const select = document.getElementById('states');
|
|
540
|
+
select.options = [...50 US states...];
|
|
541
|
+
|
|
542
|
+
// User types "cal" → Jumps to "California", shows all states
|
|
543
|
+
// Matching options are highlighted with left border
|
|
544
|
+
</script>
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
**When to use each mode:**
|
|
548
|
+
- **Filter Mode**: Large datasets where narrowing down is essential (product catalogs, user lists, search results)
|
|
549
|
+
- **Navigate Mode**: Quick selection from familiar lists (countries, states, keyboard shortcuts, known options)
|
|
550
|
+
|
|
551
|
+
**Key differences:**
|
|
552
|
+
- Filter mode hides non-matches, navigate mode highlights matches with a left border
|
|
553
|
+
- Navigate mode keeps previous focus if no match is found (type "xyz" → stays on current option)
|
|
554
|
+
- Navigate mode only works with local data (automatically falls back to filter mode when using `searchCallback`)
|
|
555
|
+
- Both modes respect `beforeSearchCallback` for search term preprocessing (accent removal, validation)
|
|
556
|
+
- **Ctrl+↑/↓** jumps between matches only (navigate mode) - regular arrows navigate through all items
|
|
557
|
+
|
|
302
558
|
### Display Modes
|
|
303
559
|
|
|
304
560
|
Perfect for different use cases and space constraints:
|
|
305
561
|
|
|
306
562
|
```html
|
|
307
563
|
<!-- Pills mode (default) - Show all selections as removable pills -->
|
|
308
|
-
<
|
|
564
|
+
<web-multiselect pills-display-mode="pills"></web-multiselect>
|
|
309
565
|
|
|
310
566
|
<!-- Count mode - Show only count badge -->
|
|
311
|
-
<
|
|
567
|
+
<web-multiselect pills-display-mode="count" show-count-badge="true"></web-multiselect>
|
|
312
568
|
|
|
313
569
|
<!-- Compact mode - Show first item + count -->
|
|
314
|
-
<
|
|
570
|
+
<web-multiselect pills-display-mode="compact"></web-multiselect>
|
|
315
571
|
|
|
316
572
|
<!-- Auto-switch from pills to count at threshold -->
|
|
317
|
-
<
|
|
573
|
+
<web-multiselect
|
|
318
574
|
pills-threshold="3"
|
|
319
575
|
pills-threshold-mode="count"
|
|
320
576
|
show-count-badge="true">
|
|
321
|
-
</
|
|
577
|
+
</web-multiselect>
|
|
322
578
|
|
|
323
579
|
<!-- Partial mode - Show limited pills + "+X more" badge -->
|
|
324
|
-
<
|
|
580
|
+
<web-multiselect
|
|
325
581
|
pills-threshold="5"
|
|
326
582
|
pills-threshold-mode="partial"
|
|
327
583
|
pills-max-visible="3">
|
|
328
|
-
</
|
|
584
|
+
</web-multiselect>
|
|
329
585
|
```
|
|
330
586
|
|
|
331
587
|
### Pills Positioning
|
|
@@ -334,16 +590,16 @@ Control where selected item badges appear relative to the input:
|
|
|
334
590
|
|
|
335
591
|
```html
|
|
336
592
|
<!-- Pills below input (default) -->
|
|
337
|
-
<
|
|
593
|
+
<web-multiselect pills-position="bottom"></web-multiselect>
|
|
338
594
|
|
|
339
595
|
<!-- Pills above input -->
|
|
340
|
-
<
|
|
596
|
+
<web-multiselect pills-position="top"></web-multiselect>
|
|
341
597
|
|
|
342
598
|
<!-- Pills to the left of input -->
|
|
343
|
-
<
|
|
599
|
+
<web-multiselect pills-position="left"></web-multiselect>
|
|
344
600
|
|
|
345
601
|
<!-- Pills to the right of input -->
|
|
346
|
-
<
|
|
602
|
+
<web-multiselect pills-position="right"></web-multiselect>
|
|
347
603
|
```
|
|
348
604
|
|
|
349
605
|
**Note:** In RTL mode, left/right positions are automatically mirrored - `pills-position="left"` will appear on the physical right side in RTL languages.
|
|
@@ -354,19 +610,19 @@ Enable tooltips on selected item pills with customizable placement and delay:
|
|
|
354
610
|
|
|
355
611
|
```html
|
|
356
612
|
<!-- Basic tooltips -->
|
|
357
|
-
<
|
|
613
|
+
<web-multiselect
|
|
358
614
|
enable-pill-tooltips="true"
|
|
359
615
|
pill-tooltip-placement="top">
|
|
360
|
-
</
|
|
616
|
+
</web-multiselect>
|
|
361
617
|
|
|
362
618
|
<!-- Fast tooltips with custom delay -->
|
|
363
|
-
<
|
|
619
|
+
<web-multiselect
|
|
364
620
|
enable-pill-tooltips="true"
|
|
365
621
|
pill-tooltip-delay="100">
|
|
366
|
-
</
|
|
622
|
+
</web-multiselect>
|
|
367
623
|
|
|
368
624
|
<script type="module">
|
|
369
|
-
const select = document.querySelector('
|
|
625
|
+
const select = document.querySelector('web-multiselect');
|
|
370
626
|
|
|
371
627
|
// Custom tooltip content
|
|
372
628
|
select.getPillTooltipCallback = (item) => {
|
|
@@ -380,12 +636,12 @@ Enable tooltips on selected item pills with customizable placement and delay:
|
|
|
380
636
|
Customize count pill text for proper pluralization and localization:
|
|
381
637
|
|
|
382
638
|
```html
|
|
383
|
-
<
|
|
639
|
+
<web-multiselect
|
|
384
640
|
id="i18n-select"
|
|
385
641
|
pills-threshold="5"
|
|
386
642
|
pills-threshold-mode="partial"
|
|
387
643
|
pills-max-visible="3">
|
|
388
|
-
</
|
|
644
|
+
</web-multiselect>
|
|
389
645
|
|
|
390
646
|
<script type="module">
|
|
391
647
|
const select = document.getElementById('i18n-select');
|
|
@@ -408,11 +664,11 @@ Full RTL support for Arabic, Hebrew, Persian, Urdu, and other right-to-left lang
|
|
|
408
664
|
|
|
409
665
|
```html
|
|
410
666
|
<!-- Automatic RTL detection from dir attribute -->
|
|
411
|
-
<
|
|
667
|
+
<web-multiselect dir="rtl" search-placeholder="ابحث..."></web-multiselect>
|
|
412
668
|
|
|
413
669
|
<!-- RTL inherited from parent element -->
|
|
414
670
|
<div dir="rtl">
|
|
415
|
-
<
|
|
671
|
+
<web-multiselect search-placeholder="חיפוש..."></web-multiselect>
|
|
416
672
|
</div>
|
|
417
673
|
|
|
418
674
|
<!-- RTL on page level -->
|
|
@@ -438,14 +694,14 @@ The component supports **any data structure** through a member/callback pattern,
|
|
|
438
694
|
For objects with consistent property names, use member attributes:
|
|
439
695
|
|
|
440
696
|
```html
|
|
441
|
-
<
|
|
697
|
+
<web-multiselect
|
|
442
698
|
id="products"
|
|
443
699
|
value-member="productId"
|
|
444
700
|
display-value-member="productName"
|
|
445
701
|
icon-member="icon"
|
|
446
702
|
subtitle-member="description"
|
|
447
703
|
group-member="category">
|
|
448
|
-
</
|
|
704
|
+
</web-multiselect>
|
|
449
705
|
|
|
450
706
|
<script type="module">
|
|
451
707
|
const select = document.getElementById('products');
|
|
@@ -473,7 +729,7 @@ For objects with consistent property names, use member attributes:
|
|
|
473
729
|
For complex data extraction or conditional logic, use callbacks:
|
|
474
730
|
|
|
475
731
|
```javascript
|
|
476
|
-
const select = document.querySelector('
|
|
732
|
+
const select = document.querySelector('web-multiselect');
|
|
477
733
|
|
|
478
734
|
// Custom value extraction
|
|
479
735
|
select.getValueCallback = (item) => item.id || item.code || item.value;
|
|
@@ -546,7 +802,7 @@ interface Product {
|
|
|
546
802
|
category: string;
|
|
547
803
|
}
|
|
548
804
|
|
|
549
|
-
const select = document.querySelector<MultiSelectElement<Product>>('
|
|
805
|
+
const select = document.querySelector<MultiSelectElement<Product>>('web-multiselect');
|
|
550
806
|
select.options = [
|
|
551
807
|
{ id: 'p1', name: 'Laptop', price: 999, category: 'Electronics' }
|
|
552
808
|
];
|
|
@@ -561,11 +817,11 @@ The component seamlessly integrates with standard HTML forms by automatically cr
|
|
|
561
817
|
```html
|
|
562
818
|
<form id="userForm" action="/submit" method="POST">
|
|
563
819
|
<label>Select Skills:</label>
|
|
564
|
-
<
|
|
820
|
+
<web-multiselect
|
|
565
821
|
name="skills"
|
|
566
822
|
value-format="json"
|
|
567
823
|
multiple="true">
|
|
568
|
-
</
|
|
824
|
+
</web-multiselect>
|
|
569
825
|
|
|
570
826
|
<button type="submit">Submit</button>
|
|
571
827
|
</form>
|
|
@@ -574,7 +830,7 @@ The component seamlessly integrates with standard HTML forms by automatically cr
|
|
|
574
830
|
import '@keenmate/web-multiselect';
|
|
575
831
|
|
|
576
832
|
const form = document.getElementById('userForm');
|
|
577
|
-
const select = form.querySelector('
|
|
833
|
+
const select = form.querySelector('web-multiselect');
|
|
578
834
|
|
|
579
835
|
select.options = [
|
|
580
836
|
{ value: 'js', label: 'JavaScript' },
|
|
@@ -600,19 +856,19 @@ Choose how selected values are serialized in forms:
|
|
|
600
856
|
|
|
601
857
|
**JSON Format** (default):
|
|
602
858
|
```html
|
|
603
|
-
<
|
|
859
|
+
<web-multiselect name="items" value-format="json"></web-multiselect>
|
|
604
860
|
<!-- FormData result: items = ["item1","item2","item3"] -->
|
|
605
861
|
```
|
|
606
862
|
|
|
607
863
|
**CSV Format**:
|
|
608
864
|
```html
|
|
609
|
-
<
|
|
865
|
+
<web-multiselect name="items" value-format="csv"></web-multiselect>
|
|
610
866
|
<!-- FormData result: items = "item1,item2,item3" -->
|
|
611
867
|
```
|
|
612
868
|
|
|
613
869
|
**Array Format** (multiple inputs):
|
|
614
870
|
```html
|
|
615
|
-
<
|
|
871
|
+
<web-multiselect name="items" value-format="array"></web-multiselect>
|
|
616
872
|
<!-- FormData result:
|
|
617
873
|
items[] = "item1"
|
|
618
874
|
items[] = "item2"
|
|
@@ -625,7 +881,7 @@ Choose how selected values are serialized in forms:
|
|
|
625
881
|
For advanced use cases, provide a custom formatting function:
|
|
626
882
|
|
|
627
883
|
```javascript
|
|
628
|
-
const select = document.querySelector('
|
|
884
|
+
const select = document.querySelector('web-multiselect');
|
|
629
885
|
|
|
630
886
|
select.name = 'product_ids';
|
|
631
887
|
select.getValueFormatCallback = (values) => {
|
|
@@ -750,15 +1006,32 @@ You can customize the component using CSS variables even with just a `<script>`
|
|
|
750
1006
|
|
|
751
1007
|
### Available CSS Variables
|
|
752
1008
|
|
|
753
|
-
The component exposes
|
|
1009
|
+
The component exposes **150+ CSS custom properties** defined at the `:host` level, making them inspectable and overridable. Below are the **50+ most commonly customized variables** organized by category.
|
|
1010
|
+
|
|
1011
|
+
#### Inspecting Variables in DevTools
|
|
1012
|
+
|
|
1013
|
+
All CSS custom properties are now defined at the `:host` level in the compiled CSS, making them visible in browser DevTools:
|
|
1014
|
+
|
|
1015
|
+
1. Open DevTools (F12) and select the `<web-multiselect>` element
|
|
1016
|
+
2. In the **Styles** panel, look for the `:host` selector
|
|
1017
|
+
3. You'll see all 150+ variables with their default values
|
|
1018
|
+
4. Edit values live to preview changes instantly
|
|
1019
|
+
|
|
1020
|
+
**CSS variables work with Shadow DOM** because they inherit through the shadow boundary. This means you can customize the component from outside:
|
|
1021
|
+
|
|
1022
|
+
```html
|
|
1023
|
+
<style>
|
|
1024
|
+
/* These variables will penetrate into the Shadow DOM */
|
|
1025
|
+
multi-select {
|
|
1026
|
+
--ml-accent-color: #10b981; /* Changes primary color */
|
|
1027
|
+
--ml-input-border-radius: 0.5rem; /* Rounds input corners */
|
|
1028
|
+
}
|
|
1029
|
+
</style>
|
|
1030
|
+
```
|
|
754
1031
|
|
|
755
|
-
For the complete list of all available CSS variables, see
|
|
756
|
-
- [
|
|
757
|
-
- [
|
|
758
|
-
- [_pills-display.scss](./src/scss/_pills-display.scss) - Pills and count display
|
|
759
|
-
- [_options.scss](./src/scss/_options.scss) - Options and groups
|
|
760
|
-
- [_tooltip.scss](./src/scss/_tooltip.scss) - Tooltip styles
|
|
761
|
-
- [_rtl.scss](./src/scss/_rtl.scss) - RTL overrides
|
|
1032
|
+
For the complete list of all available CSS variables, see:
|
|
1033
|
+
- [_css-variables.scss](./src/scss/_css-variables.scss) - All 150+ CSS custom properties at `:host` level
|
|
1034
|
+
- [_variables.scss](./src/scss/_variables.scss) - Foundation SCSS variables (colors, spacing, typography)
|
|
762
1035
|
|
|
763
1036
|
#### Colors
|
|
764
1037
|
|