@keenmate/web-multiselect 1.0.0-rc02

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) 2024 Keenmate
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,562 @@
1
+ # MultiSelect Web Component
2
+
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
4
+ [![npm version](https://img.shields.io/npm/v/@keenmate/web-multiselect.svg)](https://www.npmjs.com/package/@keenmate/web-multiselect)
5
+
6
+ A lightweight, accessible multiselect web component with typeahead search, rich content support, and excellent keyboard navigation.
7
+
8
+ ## Features
9
+
10
+ - 🔍 **Typeahead Search** - Real-time filtering as you type
11
+ - ⌨️ **Keyboard Navigation** - Full keyboard support (arrows, Enter, Esc, Tab)
12
+ - 🎨 **Rich Content** - Icons, subtitles, and multiline text support
13
+ - 📊 **Multiple Display Modes** - Pills, count, compact, or partial (pills + threshold)
14
+ - 💬 **Pill Tooltips** - Customizable tooltips on selected items with placement control
15
+ - 🎯 **Single & Multi-Select** - Switch between single and multiple selection modes
16
+ - 🔄 **Async Data Loading** - On-demand data fetching support
17
+ - 📦 **Grouped Options** - Organize options into collapsible groups
18
+ - 🎉 **Smart Positioning** - Uses Floating UI for intelligent dropdown placement
19
+ - 🌍 **i18n Support** - Customizable callbacks for pluralization and localization
20
+ - ✨ **Modern** - Web Component with Shadow DOM, TypeScript, bundled with Vite
21
+ - 🌐 **Framework Agnostic** - Works with any framework or vanilla JS
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ npm install @keenmate/web-multiselect
27
+ ```
28
+
29
+ ## Usage
30
+
31
+ ### Basic HTML
32
+
33
+ ```html
34
+ <!-- Multi-select -->
35
+ <multi-select
36
+ search-placeholder="Search options..."
37
+ initial-values='["js","ts"]'>
38
+ </multi-select>
39
+
40
+ <!-- Single-select -->
41
+ <multi-select
42
+ multiple="false"
43
+ search-placeholder="Select one..."
44
+ initial-values='["python"]'>
45
+ </multi-select>
46
+ ```
47
+
48
+ ### With JavaScript/TypeScript
49
+
50
+ ```typescript
51
+ // Import the component (includes styles)
52
+ import '@keenmate/web-multiselect';
53
+
54
+ // Or import styles separately if needed
55
+ import '@keenmate/web-multiselect/style.css';
56
+
57
+ const multiselect = document.querySelector('multi-select');
58
+
59
+ // Set options programmatically
60
+ multiselect.options = [
61
+ { value: 'js', label: 'JavaScript', icon: '🟨' },
62
+ { value: 'ts', label: 'TypeScript', icon: '🔷' },
63
+ { value: 'py', label: 'Python', icon: '🐍' }
64
+ ];
65
+
66
+ // Listen for events
67
+ multiselect.addEventListener('change', (e) => {
68
+ console.log('Selected:', e.detail.selectedOptions);
69
+ console.log('Values:', e.detail.selectedValues);
70
+ });
71
+
72
+ // Public API
73
+ const selected = multiselect.getSelected();
74
+ multiselect.setSelected(['js', 'ts']);
75
+ ```
76
+
77
+ ## Attributes
78
+
79
+ | Attribute | Type | Default | Description |
80
+ |-----------|------|---------|-------------|
81
+ | `multiple` | `boolean` | `true` | Allow multiple selections |
82
+ | `search-placeholder` | `string` | `'Search...'` | Placeholder text for search input |
83
+ | `search-hint` | `string` | - | Hint text shown above input when focused |
84
+ | `allow-groups` | `boolean` | `true` | Enable option grouping |
85
+ | `allow-select-all` | `boolean` | `true` | Show "Select All" button |
86
+ | `allow-clear-all` | `boolean` | `true` | Show "Clear All" button |
87
+ | `show-checkboxes` | `boolean` | `true` | Show checkboxes next to options |
88
+ | `close-on-select` | `boolean` | `false` | Close dropdown after selecting |
89
+ | `dropdown-min-width` | `string` | - | Min width for dropdown (e.g., '20rem') |
90
+ | `pills-display-mode` | `'pills' \| 'count' \| 'compact'` | `'pills'` | How to display selected items |
91
+ | `pills-threshold` | `number` | - | Auto-switch mode when exceeded (see pills-threshold-mode) |
92
+ | `pills-threshold-mode` | `'count' \| 'partial'` | `'count'` | Mode after threshold: 'count' shows badge, 'partial' shows limited pills + more badge |
93
+ | `pills-max-visible` | `number` | `3` | Max pills shown in partial mode |
94
+ | `pills-position` | `'top' \| 'bottom' \| 'left' \| 'right'` | `'bottom'` | Position of pills container |
95
+ | `show-count-badge` | `boolean` | `false` | Show [3] badge next to toggle icon |
96
+ | `enable-pill-tooltips` | `boolean` | `false` | Enable tooltips on selected pills |
97
+ | `pill-tooltip-placement` | `'top' \| 'bottom' \| 'left' \| 'right'` | `'top'` | Tooltip placement relative to pill |
98
+ | `pill-tooltip-delay` | `number` | `300` | Delay in ms before showing tooltip |
99
+ | `pill-tooltip-offset` | `number` | `8` | Distance in pixels between pill and tooltip |
100
+ | `max-height` | `string` | `'20rem'` | Maximum height of dropdown |
101
+ | `empty-message` | `string` | `'No results found'` | Message when no options found |
102
+ | `loading-message` | `string` | `'Loading...'` | Message while loading async data |
103
+ | `min-search-length` | `number` | `0` | Minimum search length for async |
104
+ | `initial-values` | `string` (JSON array) | - | Pre-selected values |
105
+
106
+ ## Properties
107
+
108
+ ```typescript
109
+ // Get/set options
110
+ multiselect.options = [
111
+ { value: 'js', label: 'JavaScript' },
112
+ { value: 'ts', label: 'TypeScript' }
113
+ ];
114
+
115
+ // Async data loading
116
+ multiselect.onSearch = async (searchTerm) => {
117
+ const response = await fetch(`/api/search?q=${searchTerm}`);
118
+ return await response.json();
119
+ };
120
+
121
+ // Event callbacks
122
+ multiselect.onSelect = (option) => {
123
+ console.log('Selected:', option);
124
+ };
125
+
126
+ multiselect.onDeselect = (option) => {
127
+ console.log('Deselected:', option);
128
+ };
129
+
130
+ multiselect.onChange = (selectedOptions) => {
131
+ console.log('Changed:', selectedOptions);
132
+ };
133
+
134
+ // Pill tooltip customization
135
+ multiselect.getPillTooltipCallback = (item) => {
136
+ return `${item.label} - ${item.subtitle}`;
137
+ };
138
+
139
+ // Count pill i18n/pluralization
140
+ multiselect.getCountPillCallback = (count, moreCount) => {
141
+ if (moreCount !== undefined) {
142
+ return `+${moreCount} more`; // Partial mode badge
143
+ }
144
+ return `${count} selected`; // Count mode display
145
+ };
146
+ ```
147
+
148
+ ## Methods
149
+
150
+ | Method | Description |
151
+ |--------|-------------|
152
+ | `getSelected()` | Get currently selected options |
153
+ | `setSelected(values: string[])` | Set selected values |
154
+ | `destroy()` | Clean up and destroy instance |
155
+
156
+ ## Events
157
+
158
+ | Event | Detail | Description |
159
+ |-------|--------|-------------|
160
+ | `select` | `{ option, selectedOptions }` | Fired when an option is selected |
161
+ | `deselect` | `{ option, selectedOptions }` | Fired when an option is deselected |
162
+ | `change` | `{ selectedOptions, selectedValues }` | Fired when selection changes |
163
+
164
+ ## Keyboard Shortcuts
165
+
166
+ - **↑ ↓** - Navigate up/down through options
167
+ - **Enter** - Select focused option
168
+ - **Escape** - Close dropdown
169
+ - **Tab** - Close dropdown and move to next field
170
+ - **Type** - Filter options by search term
171
+
172
+ ## Advanced Features
173
+
174
+ ### Rich Content with Icons
175
+
176
+ Icons support multiple formats - emojis, SVG markup, Font Awesome, images, or any HTML:
177
+
178
+ ```html
179
+ <multi-select id="frameworks"></multi-select>
180
+
181
+ <script type="module">
182
+ const select = document.getElementById('frameworks');
183
+ select.options = [
184
+ {
185
+ value: 'react',
186
+ label: 'React',
187
+ icon: '⚛️', // Emoji
188
+ subtitle: 'A JavaScript library for building user interfaces'
189
+ },
190
+ {
191
+ value: 'vue',
192
+ label: 'Vue.js',
193
+ icon: '<svg viewBox="0 0 24 24"><path d="M2 3l10 18L22 3h-4l-6 10.5L6 3H2z"/></svg>', // SVG
194
+ subtitle: 'The Progressive JavaScript Framework'
195
+ },
196
+ {
197
+ value: 'angular',
198
+ label: 'Angular',
199
+ icon: '<i class="fab fa-angular"></i>', // Font Awesome
200
+ subtitle: 'Platform for building mobile and desktop apps'
201
+ },
202
+ {
203
+ value: 'svelte',
204
+ label: 'Svelte',
205
+ icon: '<img src="svelte-logo.png" alt="Svelte" />', // Image
206
+ subtitle: 'Cybernetically enhanced web apps'
207
+ }
208
+ ];
209
+ </script>
210
+ ```
211
+
212
+ ### Grouped Options
213
+
214
+ ```javascript
215
+ select.options = [
216
+ { value: 'js', label: 'JavaScript', group: 'Frontend' },
217
+ { value: 'ts', label: 'TypeScript', group: 'Frontend' },
218
+ { value: 'python', label: 'Python', group: 'Backend' },
219
+ { value: 'java', label: 'Java', group: 'Backend' }
220
+ ];
221
+ ```
222
+
223
+ ### Async Data Loading
224
+
225
+ ```html
226
+ <multi-select
227
+ id="async-select"
228
+ min-search-length="2"
229
+ loading-message="Searching..."
230
+ empty-message="No products found">
231
+ </multi-select>
232
+
233
+ <script type="module">
234
+ const select = document.getElementById('async-select');
235
+
236
+ select.onSearch = async (searchTerm) => {
237
+ const response = await fetch(`/api/products?q=${searchTerm}`);
238
+ const data = await response.json();
239
+ return data.products;
240
+ };
241
+ </script>
242
+ ```
243
+
244
+ ### Display Modes
245
+
246
+ Perfect for different use cases and space constraints:
247
+
248
+ ```html
249
+ <!-- Pills mode (default) - Show all selections as removable pills -->
250
+ <multi-select pills-display-mode="pills"></multi-select>
251
+
252
+ <!-- Count mode - Show only count badge -->
253
+ <multi-select pills-display-mode="count" show-count-badge="true"></multi-select>
254
+
255
+ <!-- Compact mode - Show first item + count -->
256
+ <multi-select pills-display-mode="compact"></multi-select>
257
+
258
+ <!-- Auto-switch from pills to count at threshold -->
259
+ <multi-select
260
+ pills-threshold="3"
261
+ pills-threshold-mode="count"
262
+ show-count-badge="true">
263
+ </multi-select>
264
+
265
+ <!-- Partial mode - Show limited pills + "+X more" badge -->
266
+ <multi-select
267
+ pills-threshold="5"
268
+ pills-threshold-mode="partial"
269
+ pills-max-visible="3">
270
+ </multi-select>
271
+ ```
272
+
273
+ ### Pills Positioning
274
+
275
+ Control where selected item badges appear:
276
+
277
+ ```html
278
+ <!-- Pills below input (default) -->
279
+ <multi-select pills-position="bottom"></multi-select>
280
+
281
+ <!-- Pills above input -->
282
+ <multi-select pills-position="top"></multi-select>
283
+
284
+ <!-- Pills to the left (RTL) -->
285
+ <multi-select pills-position="left"></multi-select>
286
+
287
+ <!-- Pills to the right (LTR) -->
288
+ <multi-select pills-position="right"></multi-select>
289
+ ```
290
+
291
+ ### Pill Tooltips
292
+
293
+ Enable tooltips on selected item pills with customizable placement and delay:
294
+
295
+ ```html
296
+ <!-- Basic tooltips -->
297
+ <multi-select
298
+ enable-pill-tooltips="true"
299
+ pill-tooltip-placement="top">
300
+ </multi-select>
301
+
302
+ <!-- Fast tooltips with custom delay -->
303
+ <multi-select
304
+ enable-pill-tooltips="true"
305
+ pill-tooltip-delay="100">
306
+ </multi-select>
307
+
308
+ <script type="module">
309
+ const select = document.querySelector('multi-select');
310
+
311
+ // Custom tooltip content
312
+ select.getPillTooltipCallback = (item) => {
313
+ return `${item.label} - ${item.subtitle}`;
314
+ };
315
+ </script>
316
+ ```
317
+
318
+ ### Internationalization (i18n)
319
+
320
+ Customize count pill text for proper pluralization and localization:
321
+
322
+ ```html
323
+ <multi-select
324
+ id="i18n-select"
325
+ pills-threshold="5"
326
+ pills-threshold-mode="partial"
327
+ pills-max-visible="3">
328
+ </multi-select>
329
+
330
+ <script type="module">
331
+ const select = document.getElementById('i18n-select');
332
+
333
+ // Spanish pluralization example
334
+ select.getCountPillCallback = (count, moreCount) => {
335
+ if (moreCount !== undefined) {
336
+ // Partial mode: "+X more" badge
337
+ return moreCount === 1 ? '+1 más' : `+${moreCount} más`;
338
+ }
339
+ // Count mode: total count
340
+ return count === 1 ? '1 elemento seleccionado' : `${count} elementos seleccionados`;
341
+ };
342
+ </script>
343
+ ```
344
+
345
+ ### Disabled Options
346
+
347
+ ```javascript
348
+ select.options = [
349
+ { value: 'basic', label: 'Basic License', subtitle: 'Free forever' },
350
+ { value: 'pro', label: 'Pro License', subtitle: 'Available for purchase' },
351
+ {
352
+ value: 'enterprise',
353
+ label: 'Enterprise License',
354
+ subtitle: 'Contact sales',
355
+ disabled: true
356
+ }
357
+ ];
358
+ ```
359
+
360
+ ## Option Structure
361
+
362
+ ```typescript
363
+ interface MultiSelectOption {
364
+ value: string; // Required: Unique identifier
365
+ label: string; // Required: Display text
366
+ icon?: string; // Optional: Icon or emoji
367
+ subtitle?: string; // Optional: Subtitle/description
368
+ group?: string; // Optional: Group name
369
+ disabled?: boolean; // Optional: Disable selection
370
+ }
371
+ ```
372
+
373
+ ## Styling
374
+
375
+ The component uses Shadow DOM for style encapsulation, but exposes CSS custom properties (CSS variables) that you can override to customize the appearance.
376
+
377
+ ### CSS Variables (No Build System Required)
378
+
379
+ You can customize the component using CSS variables even with just a `<script>` tag:
380
+
381
+ ```html
382
+ <style>
383
+ /* Override tooltip appearance */
384
+ multi-select {
385
+ --ml-tooltip-bg: #1f2937;
386
+ --ml-tooltip-color: #f9fafb;
387
+ --ml-tooltip-padding: 0.625rem 0.875rem;
388
+ --ml-tooltip-border-radius: 0.5rem;
389
+ --ml-tooltip-font-size: 0.8125rem;
390
+ --ml-tooltip-max-width: 24rem;
391
+ --ml-tooltip-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
392
+ --ml-tooltip-z-index: 10000;
393
+ }
394
+
395
+ /* Override "+X more" badge colors */
396
+ multi-select {
397
+ --ml-more-badge-bg: #dbeafe;
398
+ --ml-more-badge-hover-bg: #bfdbfe;
399
+ --ml-more-badge-active-bg: #93c5fd;
400
+ }
401
+
402
+ /* Size the component */
403
+ multi-select {
404
+ width: 100%;
405
+ max-width: 400px;
406
+ }
407
+ </style>
408
+ ```
409
+
410
+ ### Available CSS Variables
411
+
412
+ All 211 SCSS variables are exposed as CSS custom properties with fallbacks. Below are the most commonly customized variables organized by category. For the complete list, see [_multiselect.scss](./src/scss/_multiselect.scss).
413
+
414
+ #### Colors
415
+
416
+ | Variable | Default | Description |
417
+ |----------|---------|-------------|
418
+ | `--ml-accent-color` | `#3b82f6` | Primary accent color (blue) |
419
+ | `--ml-accent-color-hover` | `#2563eb` | Accent color on hover |
420
+ | `--ml-accent-color-active` | `#1d4ed8` | Accent color when active |
421
+ | `--ml-text-primary` | `#111827` | Primary text color |
422
+ | `--ml-text-secondary` | `#6b7280` | Secondary/muted text color |
423
+ | `--ml-border-color` | `#e5e7eb` | Default border color |
424
+
425
+ #### Input Component
426
+
427
+ | Variable | Default | Description |
428
+ |----------|---------|-------------|
429
+ | `--ml-input-bg` | `#ffffff` | Input background |
430
+ | `--ml-input-text` | `#111827` | Input text color |
431
+ | `--ml-input-border` | `#d1d5db` | Input border color |
432
+ | `--ml-input-focus-border-color` | `#3b82f6` | Border color when focused |
433
+ | `--ml-input-padding-v` | `0.5rem` | Input vertical padding |
434
+ | `--ml-input-padding-h` | `0.75rem` | Input horizontal padding |
435
+ | `--ml-input-font-size` | `0.875rem` | Input font size |
436
+ | `--ml-input-border-radius` | `0.375rem` | Input border radius |
437
+ | `--ml-input-placeholder-color` | `#6b7280` | Placeholder text color |
438
+
439
+ #### Dropdown & Options
440
+
441
+ | Variable | Default | Description |
442
+ |----------|---------|-------------|
443
+ | `--ml-dropdown-bg` | `#ffffff` | Dropdown background |
444
+ | `--ml-dropdown-border` | `#e5e7eb` | Dropdown border color |
445
+ | `--ml-dropdown-shadow` | (box shadow) | Dropdown shadow |
446
+ | `--ml-dropdown-max-height` | `20rem` | Max height of dropdown |
447
+ | `--ml-option-padding-v` | `0.5rem` | Option vertical padding |
448
+ | `--ml-option-padding-h` | `0.75rem` | Option horizontal padding |
449
+ | `--ml-option-hover-bg` | `#f9fafb` | Option background on hover |
450
+ | `--ml-option-bg-selected` | (rgba accent) | Selected option background |
451
+
452
+ #### Pills & Badges
453
+
454
+ | Variable | Default | Description |
455
+ |----------|---------|-------------|
456
+ | `--ml-pill-bg` | `#eff6ff` | Pill background color |
457
+ | `--ml-pill-text-color` | `#3b82f6` | Pill text color |
458
+ | `--ml-pill-gap` | `0.5rem` | Gap between pills |
459
+ | `--ml-pill-height` | `1.5rem` | Height of pills |
460
+ | `--ml-pill-font-size` | `0.75rem` | Pill font size |
461
+ | `--ml-pill-border-radius` | `0.375rem` | Pill border radius |
462
+ | `--ml-pill-remove-bg` | `#3b82f6` | Remove button background |
463
+ | `--ml-pill-remove-color` | `#ffffff` | Remove button color |
464
+ | `--ml-more-badge-bg` | (pill background) | "+X more" badge background |
465
+ | `--ml-more-badge-hover-bg` | `#ffffff` | "+X more" badge hover |
466
+ | `--ml-more-badge-active-bg` | `#e0f2fe` | "+X more" badge active |
467
+
468
+ #### Count Badge (in input)
469
+
470
+ | Variable | Default | Description |
471
+ |----------|---------|-------------|
472
+ | `--ml-count-badge-bg` | `#3b82f6` | Count badge background |
473
+ | `--ml-count-badge-color` | `#ffffff` | Count badge text color |
474
+ | `--ml-count-badge-font-size` | `0.75rem` | Count badge font size |
475
+ | `--ml-count-badge-bg-hover` | `#2563eb` | Hover background color |
476
+
477
+ #### Tooltips
478
+
479
+ | Variable | Default | Description |
480
+ |----------|---------|-------------|
481
+ | `--ml-tooltip-bg` | `#333` | Tooltip background color |
482
+ | `--ml-tooltip-color` | `#fff` | Tooltip text color |
483
+ | `--ml-tooltip-padding` | `0.5rem 0.75rem` | Tooltip padding |
484
+ | `--ml-tooltip-border-radius` | `0.375rem` | Tooltip border radius |
485
+ | `--ml-tooltip-font-size` | `0.875rem` | Tooltip font size |
486
+ | `--ml-tooltip-max-width` | `20rem` | Tooltip maximum width |
487
+ | `--ml-tooltip-shadow` | (box shadow) | Tooltip box shadow |
488
+ | `--ml-tooltip-z-index` | `10000` | Tooltip z-index |
489
+
490
+ #### Typography
491
+
492
+ | Variable | Default | Description |
493
+ |----------|---------|-------------|
494
+ | `--ml-font-size-xs` | `0.75rem` | Extra small font size |
495
+ | `--ml-font-size-sm` | `0.875rem` | Small font size |
496
+ | `--ml-font-size-base` | `1rem` | Base font size |
497
+ | `--ml-font-weight-medium` | `500` | Medium font weight |
498
+ | `--ml-font-weight-semibold` | `600` | Semibold font weight |
499
+
500
+ #### Effects & Transitions
501
+
502
+ | Variable | Default | Description |
503
+ |----------|---------|-------------|
504
+ | `--ml-transition-fast` | `150ms` | Fast transition duration |
505
+ | `--ml-transition-normal` | `200ms` | Normal transition duration |
506
+ | `--ml-easing-snappy` | (cubic-bezier) | Snappy easing function |
507
+ | `--ml-shadow-md` | (box shadow) | Medium shadow |
508
+ | `--ml-shadow-xl` | (box shadow) | Extra large shadow |
509
+ | `--ml-disabled-opacity` | `0.5` | Opacity for disabled state |
510
+
511
+ ### Advanced: Custom SCSS
512
+
513
+ For users with a build system, you can import and customize the SCSS:
514
+
515
+ ```scss
516
+ // Import and override SCSS variables
517
+ @use '@keenmate/web-multiselect/scss' with (
518
+ $ml-primary: #10b981,
519
+ $ml-border-radius: 0.5rem,
520
+ $ml-font-size: 1rem
521
+ );
522
+ ```
523
+
524
+ ## Browser Support
525
+
526
+ - Modern browsers with Web Components support
527
+ - Chrome/Edge 67+
528
+ - Firefox 63+
529
+ - Safari 10.1+
530
+
531
+ ## Development
532
+
533
+ ```bash
534
+ # Install dependencies
535
+ npm install
536
+
537
+ # Start dev server
538
+ npm run dev
539
+
540
+ # Build for production
541
+ npm run build
542
+
543
+ # Create package
544
+ npm run package
545
+ ```
546
+
547
+ ## License
548
+
549
+ Copyright (c) 2024 Keenmate
550
+
551
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
552
+
553
+ **What this means:**
554
+ - ✅ Free to use in commercial products
555
+ - ✅ Free to modify and distribute
556
+ - ✅ No licensing fees or restrictions
557
+ - ⚠️ Provided "as is" without warranty
558
+ - 📝 Must include copyright notice in copies
559
+
560
+ ## Credits
561
+
562
+ Created by [Keenmate](https://github.com/keenmate) as part of the Pure Admin design system.
@@ -0,0 +1,26 @@
1
+ import './scss/main.scss';
2
+ import { MultiSelectElement } from './web-component';
3
+ export { MultiSelectElement };
4
+ export { PureMultiSelect } from './multiselect';
5
+ export type { MultiSelectOption, MultiSelectOptions, MultiSelectEventDetail, PillsDisplayMode, PillsPosition, PillsThresholdMode, SearchInputMode, ValueFormat } from './types';
6
+ import './web-component';
7
+ export interface GlobalMultiSelectAPI {
8
+ version: () => string;
9
+ config: {
10
+ name: string;
11
+ version: string;
12
+ author: string;
13
+ license: string;
14
+ repository: string;
15
+ homepage: string;
16
+ };
17
+ register: () => void;
18
+ getInstances: () => HTMLElement[];
19
+ }
20
+ declare global {
21
+ interface Window {
22
+ keenmate?: {
23
+ multiselect?: GlobalMultiSelectAPI;
24
+ };
25
+ }
26
+ }