@keenmate/web-multiselect 1.0.0-rc06 → 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 CHANGED
@@ -7,7 +7,9 @@ A lightweight, accessible multiselect web component with typeahead search, RTL l
7
7
 
8
8
  ## Features
9
9
 
10
- - 🔍 **Typeahead Search** - Real-time filtering as you type
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
- ### Basic HTML
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
- <multi-select
72
+ <web-multiselect
73
+ id="my-select"
37
74
  search-placeholder="Search options..."
38
75
  initial-values='["js","ts"]'>
39
- </multi-select>
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('multi-select');
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
- <multi-select id="frameworks"></multi-select>
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
- <multi-select
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
- </multi-select>
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
- <multi-select pills-display-mode="pills"></multi-select>
564
+ <web-multiselect pills-display-mode="pills"></web-multiselect>
309
565
 
310
566
  <!-- Count mode - Show only count badge -->
311
- <multi-select pills-display-mode="count" show-count-badge="true"></multi-select>
567
+ <web-multiselect pills-display-mode="count" show-count-badge="true"></web-multiselect>
312
568
 
313
569
  <!-- Compact mode - Show first item + count -->
314
- <multi-select pills-display-mode="compact"></multi-select>
570
+ <web-multiselect pills-display-mode="compact"></web-multiselect>
315
571
 
316
572
  <!-- Auto-switch from pills to count at threshold -->
317
- <multi-select
573
+ <web-multiselect
318
574
  pills-threshold="3"
319
575
  pills-threshold-mode="count"
320
576
  show-count-badge="true">
321
- </multi-select>
577
+ </web-multiselect>
322
578
 
323
579
  <!-- Partial mode - Show limited pills + "+X more" badge -->
324
- <multi-select
580
+ <web-multiselect
325
581
  pills-threshold="5"
326
582
  pills-threshold-mode="partial"
327
583
  pills-max-visible="3">
328
- </multi-select>
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
- <multi-select pills-position="bottom"></multi-select>
593
+ <web-multiselect pills-position="bottom"></web-multiselect>
338
594
 
339
595
  <!-- Pills above input -->
340
- <multi-select pills-position="top"></multi-select>
596
+ <web-multiselect pills-position="top"></web-multiselect>
341
597
 
342
598
  <!-- Pills to the left of input -->
343
- <multi-select pills-position="left"></multi-select>
599
+ <web-multiselect pills-position="left"></web-multiselect>
344
600
 
345
601
  <!-- Pills to the right of input -->
346
- <multi-select pills-position="right"></multi-select>
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
- <multi-select
613
+ <web-multiselect
358
614
  enable-pill-tooltips="true"
359
615
  pill-tooltip-placement="top">
360
- </multi-select>
616
+ </web-multiselect>
361
617
 
362
618
  <!-- Fast tooltips with custom delay -->
363
- <multi-select
619
+ <web-multiselect
364
620
  enable-pill-tooltips="true"
365
621
  pill-tooltip-delay="100">
366
- </multi-select>
622
+ </web-multiselect>
367
623
 
368
624
  <script type="module">
369
- const select = document.querySelector('multi-select');
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
- <multi-select
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
- </multi-select>
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
- <multi-select dir="rtl" search-placeholder="ابحث..."></multi-select>
667
+ <web-multiselect dir="rtl" search-placeholder="ابحث..."></web-multiselect>
412
668
 
413
669
  <!-- RTL inherited from parent element -->
414
670
  <div dir="rtl">
415
- <multi-select search-placeholder="חיפוש..."></multi-select>
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
- <multi-select
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
- </multi-select>
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('multi-select');
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>>('multi-select');
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
- <multi-select
820
+ <web-multiselect
565
821
  name="skills"
566
822
  value-format="json"
567
823
  multiple="true">
568
- </multi-select>
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('multi-select');
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
- <multi-select name="items" value-format="json"></multi-select>
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
- <multi-select name="items" value-format="csv"></multi-select>
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
- <multi-select name="items" value-format="array"></multi-select>
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('multi-select');
884
+ const select = document.querySelector('web-multiselect');
629
885
 
630
886
  select.name = 'product_ids';
631
887
  select.getValueFormatCallback = (values) => {
@@ -756,7 +1012,7 @@ The component exposes **150+ CSS custom properties** defined at the `:host` leve
756
1012
 
757
1013
  All CSS custom properties are now defined at the `:host` level in the compiled CSS, making them visible in browser DevTools:
758
1014
 
759
- 1. Open DevTools (F12) and select the `<multi-select>` element
1015
+ 1. Open DevTools (F12) and select the `<web-multiselect>` element
760
1016
  2. In the **Styles** panel, look for the `:host` selector
761
1017
  3. You'll see all 150+ variables with their default values
762
1018
  4. Edit values live to preview changes instantly