@lekoala/combobox 0.1.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 +21 -0
- package/README.md +566 -0
- package/custom-elements.json +247 -0
- package/dist/combobox.css +447 -0
- package/dist/combobox.js +2220 -0
- package/dist/combobox.min.css +1 -0
- package/dist/combobox.min.js +2 -0
- package/dist/types/combo-box.d.ts +112 -0
- package/dist/types/combo-box.d.ts.map +1 -0
- package/dist/types/combobox.d.ts +530 -0
- package/dist/types/combobox.d.ts.map +1 -0
- package/dist/types/define.d.ts +2 -0
- package/dist/types/define.d.ts.map +1 -0
- package/dist/types/helpers.d.ts +251 -0
- package/dist/types/helpers.d.ts.map +1 -0
- package/dist/types/index.d.ts +24 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/locales/de.d.ts +7 -0
- package/dist/types/locales/de.d.ts.map +1 -0
- package/dist/types/locales/en.d.ts +8 -0
- package/dist/types/locales/en.d.ts.map +1 -0
- package/dist/types/locales/es.d.ts +7 -0
- package/dist/types/locales/es.d.ts.map +1 -0
- package/dist/types/locales/fr.d.ts +7 -0
- package/dist/types/locales/fr.d.ts.map +1 -0
- package/dist/types/locales/it.d.ts +7 -0
- package/dist/types/locales/it.d.ts.map +1 -0
- package/dist/types/locales/nl.d.ts +7 -0
- package/dist/types/locales/nl.d.ts.map +1 -0
- package/dist/types/locales/pt.d.ts +7 -0
- package/dist/types/locales/pt.d.ts.map +1 -0
- package/dist/types/locales/ru.d.ts +7 -0
- package/dist/types/locales/ru.d.ts.map +1 -0
- package/dist/types/locales/zh-CN.d.ts +7 -0
- package/dist/types/locales/zh-CN.d.ts.map +1 -0
- package/dist/types/messages.d.ts +48 -0
- package/dist/types/messages.d.ts.map +1 -0
- package/package.json +92 -0
- package/src/combo-box.js +316 -0
- package/src/combobox.css +447 -0
- package/src/combobox.js +2975 -0
- package/src/define.js +20 -0
- package/src/helpers.js +377 -0
- package/src/index.js +35 -0
- package/src/locales/de.js +17 -0
- package/src/locales/en.js +18 -0
- package/src/locales/es.js +17 -0
- package/src/locales/fr.js +17 -0
- package/src/locales/it.js +17 -0
- package/src/locales/nl.js +17 -0
- package/src/locales/pt.js +17 -0
- package/src/locales/ru.js +17 -0
- package/src/locales/zh-CN.js +17 -0
- package/src/messages.js +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 LeKoala
|
|
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,566 @@
|
|
|
1
|
+
# @lekoala/combobox
|
|
2
|
+
|
|
3
|
+
A small, native-first combobox library for searchable selects, multiple values, tags and remote suggestions.
|
|
4
|
+
|
|
5
|
+
It enhances regular `<input>`, `<datalist>` and `<select>` controls instead of replacing them with a custom form model.
|
|
6
|
+
|
|
7
|
+
```html
|
|
8
|
+
<script type="module">
|
|
9
|
+
import "@lekoala/combobox/define";
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<combo-box create placeholder="Search or create a framework…">
|
|
13
|
+
<select name="frameworks[]" multiple>
|
|
14
|
+
<option value="react">React</option>
|
|
15
|
+
<option value="vue">Vue</option>
|
|
16
|
+
</select>
|
|
17
|
+
</combo-box>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The library uses the browser where it can:
|
|
21
|
+
|
|
22
|
+
* native form controls keep owning the value;
|
|
23
|
+
* Popover handles the picker top layer;
|
|
24
|
+
* CSS Anchor Positioning handles placement;
|
|
25
|
+
* ARIA combobox/listbox semantics handle keyboard interaction;
|
|
26
|
+
* native `input`, `change`, validation and form reset keep working.
|
|
27
|
+
|
|
28
|
+
There is no JavaScript positioning engine, no Bootstrap dependency and no global `window.*` API.
|
|
29
|
+
|
|
30
|
+
## Install
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm install @lekoala/combobox
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
or:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
bun add @lekoala/combobox
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Three ways to use it
|
|
43
|
+
|
|
44
|
+
### `<combo-box>`
|
|
45
|
+
|
|
46
|
+
This is the simplest option for most applications.
|
|
47
|
+
|
|
48
|
+
```html
|
|
49
|
+
<script type="module">
|
|
50
|
+
import "@lekoala/combobox/define";
|
|
51
|
+
</script>
|
|
52
|
+
|
|
53
|
+
<combo-box search="fuzzy" placeholder="Choose a country…">
|
|
54
|
+
<select name="country">
|
|
55
|
+
<option value="">Choose…</option>
|
|
56
|
+
<option value="be">Belgium</option>
|
|
57
|
+
<option value="fr">France</option>
|
|
58
|
+
</select>
|
|
59
|
+
</combo-box>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Importing `@lekoala/combobox/define` registers `<combo-box>`.
|
|
63
|
+
|
|
64
|
+
Importing the main package does **not** register anything automatically.
|
|
65
|
+
|
|
66
|
+
### JavaScript
|
|
67
|
+
|
|
68
|
+
You can enhance a native control directly:
|
|
69
|
+
|
|
70
|
+
```js
|
|
71
|
+
import Combobox from "@lekoala/combobox";
|
|
72
|
+
|
|
73
|
+
const combo = new Combobox(document.querySelector("select"), {
|
|
74
|
+
search: "fuzzy",
|
|
75
|
+
minChars: 1,
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Classic script / `file://`
|
|
80
|
+
|
|
81
|
+
A standalone build is also available:
|
|
82
|
+
|
|
83
|
+
```html
|
|
84
|
+
<script src="dist/combobox.js"></script>
|
|
85
|
+
|
|
86
|
+
<combo-box>
|
|
87
|
+
<select>
|
|
88
|
+
...
|
|
89
|
+
</select>
|
|
90
|
+
</combo-box>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The classic build registers `<combo-box>`, but still does not expose a global `Combobox` object.
|
|
94
|
+
|
|
95
|
+
## Native controls stay native
|
|
96
|
+
|
|
97
|
+
The original control remains the source of truth.
|
|
98
|
+
|
|
99
|
+
For an input:
|
|
100
|
+
|
|
101
|
+
```text
|
|
102
|
+
<input list="cities">
|
|
103
|
+
│
|
|
104
|
+
└── owns the submitted value
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
For selects:
|
|
108
|
+
|
|
109
|
+
```text
|
|
110
|
+
<select>
|
|
111
|
+
<select multiple>
|
|
112
|
+
│
|
|
113
|
+
└── own the submitted value(s)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The searchable input added around a `<select>` is only there for interaction. It has no `name` and never replaces the select in `FormData`.
|
|
117
|
+
|
|
118
|
+
This also means things such as:
|
|
119
|
+
|
|
120
|
+
* `required`;
|
|
121
|
+
* `disabled`;
|
|
122
|
+
* `form.reset()`;
|
|
123
|
+
* native `input` and `change`;
|
|
124
|
+
* server-rendered selections;
|
|
125
|
+
|
|
126
|
+
continue to behave like normal form controls.
|
|
127
|
+
|
|
128
|
+
## Searchable selects
|
|
129
|
+
|
|
130
|
+
A regular select can be filtered without changing its value model:
|
|
131
|
+
|
|
132
|
+
```html
|
|
133
|
+
<combo-box search="includes">
|
|
134
|
+
<select name="doctor">
|
|
135
|
+
<option value="1">Dr Jane Smith</option>
|
|
136
|
+
<option value="2">Dr John Martin</option>
|
|
137
|
+
</select>
|
|
138
|
+
</combo-box>
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
You can also provide your own interaction input:
|
|
142
|
+
|
|
143
|
+
```html
|
|
144
|
+
<input data-filter-for="doctor" placeholder="Search doctors…">
|
|
145
|
+
|
|
146
|
+
<select id="doctor" name="doctor">
|
|
147
|
+
...
|
|
148
|
+
</select>
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
The input is only used for filtering. The select still owns the value.
|
|
152
|
+
|
|
153
|
+
## Multiple values and tags
|
|
154
|
+
|
|
155
|
+
Multiple selects are rendered as removable chips:
|
|
156
|
+
|
|
157
|
+
```html
|
|
158
|
+
<combo-box>
|
|
159
|
+
<select name="specialties[]" multiple>
|
|
160
|
+
<option value="cardiology">Cardiology</option>
|
|
161
|
+
<option value="neurology">Neurology</option>
|
|
162
|
+
</select>
|
|
163
|
+
</combo-box>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Enable creation when users may enter new values:
|
|
167
|
+
|
|
168
|
+
```html
|
|
169
|
+
<combo-box create>
|
|
170
|
+
<select name="tags[]" multiple></select>
|
|
171
|
+
</combo-box>
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Created options are added to the native `<select>` just like normal options.
|
|
175
|
+
|
|
176
|
+
## Matching
|
|
177
|
+
|
|
178
|
+
Built-in search modes are:
|
|
179
|
+
|
|
180
|
+
```text
|
|
181
|
+
includes
|
|
182
|
+
startswith
|
|
183
|
+
fuzzy
|
|
184
|
+
pattern
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Search can cover several fields:
|
|
188
|
+
|
|
189
|
+
```html
|
|
190
|
+
<combo-box
|
|
191
|
+
search="fuzzy"
|
|
192
|
+
search-fields="label city specialty"
|
|
193
|
+
>
|
|
194
|
+
...
|
|
195
|
+
</combo-box>
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Each field is matched independently. Search never matches by accidentally joining fields together.
|
|
199
|
+
|
|
200
|
+
Matching is case- and accent-friendly where appropriate, so values such as:
|
|
201
|
+
|
|
202
|
+
```text
|
|
203
|
+
Liège
|
|
204
|
+
liege
|
|
205
|
+
LIEGE
|
|
206
|
+
liège
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
behave as expected.
|
|
210
|
+
|
|
211
|
+
More specialized matching can be provided from JavaScript.
|
|
212
|
+
|
|
213
|
+
## Remote results
|
|
214
|
+
|
|
215
|
+
Remote search stays deliberately simple:
|
|
216
|
+
|
|
217
|
+
```js
|
|
218
|
+
combo.configure({
|
|
219
|
+
minChars: 2,
|
|
220
|
+
|
|
221
|
+
async load(query, { signal }) {
|
|
222
|
+
const response = await fetch(`/api/patients?q=${encodeURIComponent(query)}`, {
|
|
223
|
+
signal,
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
return response.json();
|
|
227
|
+
},
|
|
228
|
+
});
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Remote results are **temporary suggestions**. They do not immediately become native `<option>` elements.
|
|
232
|
+
|
|
233
|
+
Once a remote result is selected, it is added to the select so normal form submission continues to work.
|
|
234
|
+
|
|
235
|
+
That distinction is intentional:
|
|
236
|
+
|
|
237
|
+
```text
|
|
238
|
+
catalogue persistent native options
|
|
239
|
+
results temporary search results
|
|
240
|
+
selection native selected options
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
`setResults()` and `clearResults()` only deal with temporary results.
|
|
244
|
+
|
|
245
|
+
`setOptions()` replaces the catalogue while keeping currently selected native options, including selected values that originally came from remote results or creation.
|
|
246
|
+
|
|
247
|
+
## Empty values
|
|
248
|
+
|
|
249
|
+
Empty option values are supported when explicitly enabled:
|
|
250
|
+
|
|
251
|
+
```html
|
|
252
|
+
<combo-box allow-empty-option>
|
|
253
|
+
<select>
|
|
254
|
+
<option value="">None</option>
|
|
255
|
+
<option value="a">Option A</option>
|
|
256
|
+
</select>
|
|
257
|
+
</combo-box>
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Without `allow-empty-option`, `""` is not treated as a normal selectable item when options are added programmatically.
|
|
261
|
+
|
|
262
|
+
## JavaScript-only behavior
|
|
263
|
+
|
|
264
|
+
Simple options have HTML attributes where that makes sense:
|
|
265
|
+
|
|
266
|
+
```html
|
|
267
|
+
<combo-box
|
|
268
|
+
create
|
|
269
|
+
search="fuzzy"
|
|
270
|
+
min-chars="2"
|
|
271
|
+
max-items="5"
|
|
272
|
+
max-options="20"
|
|
273
|
+
tab-select
|
|
274
|
+
>
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Behavior that requires functions stays in JavaScript:
|
|
278
|
+
|
|
279
|
+
```js
|
|
280
|
+
element.configure({
|
|
281
|
+
async load(query, context) {
|
|
282
|
+
// ...
|
|
283
|
+
},
|
|
284
|
+
|
|
285
|
+
guards: {
|
|
286
|
+
async remove(item) {
|
|
287
|
+
return confirm(`Remove ${item.label}?`);
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
|
|
291
|
+
render: {
|
|
292
|
+
option(item) {
|
|
293
|
+
const strong = document.createElement("strong");
|
|
294
|
+
strong.textContent = item.label;
|
|
295
|
+
return strong;
|
|
296
|
+
},
|
|
297
|
+
},
|
|
298
|
+
});
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
Strings are always rendered as text. Rich rendering uses DOM nodes rather than an `allowHtml` switch.
|
|
302
|
+
|
|
303
|
+
See [API](docs/API.md) for the full option and method reference.
|
|
304
|
+
|
|
305
|
+
## Filtering events
|
|
306
|
+
|
|
307
|
+
Filtering can be intercepted:
|
|
308
|
+
|
|
309
|
+
```js
|
|
310
|
+
combo.input.addEventListener("beforefilter", (event) => {
|
|
311
|
+
if (somethingSpecial) {
|
|
312
|
+
event.preventDefault();
|
|
313
|
+
|
|
314
|
+
// Application-defined behavior...
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
`beforefilter` is cancellable and exposes the current query.
|
|
320
|
+
|
|
321
|
+
Filtering events belong to the interaction input. `combobox:*` lifecycle events belong to the native source control.
|
|
322
|
+
|
|
323
|
+
The full event table is documented in [API](docs/API.md).
|
|
324
|
+
|
|
325
|
+
## Selection order
|
|
326
|
+
|
|
327
|
+
For multiple selects, source order and selection order do not have to mean the same thing.
|
|
328
|
+
|
|
329
|
+
When explicit selection order is enabled, values can be reordered with:
|
|
330
|
+
|
|
331
|
+
```js
|
|
332
|
+
combo.move(fromIndex, toIndex);
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
Drag-and-drop is intentionally not built into the core. An application can add whatever UI it wants and call `move()`.
|
|
336
|
+
|
|
337
|
+
## Progressive fallback
|
|
338
|
+
|
|
339
|
+
If the browser does not support the Popover and CSS Anchor features needed by the enhanced picker, the original controls remain usable.
|
|
340
|
+
|
|
341
|
+
* `input + datalist` stays a native datalist;
|
|
342
|
+
* `select` stays a native select;
|
|
343
|
+
* `select multiple` stays a native multiple select;
|
|
344
|
+
* creatable multiple selects get a small native Add input/button.
|
|
345
|
+
|
|
346
|
+
There is no second JavaScript picker implementation for older browsers.
|
|
347
|
+
|
|
348
|
+
You can force this mode in the demo with:
|
|
349
|
+
|
|
350
|
+
```text
|
|
351
|
+
?native=1
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
## Styling
|
|
355
|
+
|
|
356
|
+
The component ships with a small default stylesheet and is designed to be easy to theme with CSS custom properties.
|
|
357
|
+
|
|
358
|
+
For example:
|
|
359
|
+
|
|
360
|
+
```css
|
|
361
|
+
combo-box.compact {
|
|
362
|
+
--cb-chip-font-size: 0.75em;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
combo-box.pills {
|
|
366
|
+
--cb-chip-border-radius: 999px;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
combo-box.solid {
|
|
370
|
+
--cb-chip-bg: #6d28d9;
|
|
371
|
+
--cb-chip-color: white;
|
|
372
|
+
}
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
Applications can also return marker elements from renderers and style them with normal CSS:
|
|
376
|
+
|
|
377
|
+
```js
|
|
378
|
+
render: {
|
|
379
|
+
item(item) {
|
|
380
|
+
const label = document.createElement("span");
|
|
381
|
+
label.className = `tag-tone-${item.data.tone}`;
|
|
382
|
+
label.textContent = item.label;
|
|
383
|
+
return label;
|
|
384
|
+
},
|
|
385
|
+
}
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
```css
|
|
389
|
+
.cb-chip:has(.tag-tone-success) {
|
|
390
|
+
--cb-chip-bg: #dcfce7;
|
|
391
|
+
--cb-chip-color: #15803d;
|
|
392
|
+
}
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
`demo/actual-css.html` shows the same component themed entirely with [Actual CSS](https://github.com/lekoala/actual-css) tokens.
|
|
396
|
+
|
|
397
|
+
## Demo
|
|
398
|
+
|
|
399
|
+
The main demo covers:
|
|
400
|
+
|
|
401
|
+
* input + datalist;
|
|
402
|
+
* searchable single selects;
|
|
403
|
+
* multiple values and chips;
|
|
404
|
+
* created values;
|
|
405
|
+
* fuzzy and multi-field search;
|
|
406
|
+
* remote loading;
|
|
407
|
+
* custom renderers;
|
|
408
|
+
* selection order;
|
|
409
|
+
* guards;
|
|
410
|
+
* separators;
|
|
411
|
+
* maximum items/results;
|
|
412
|
+
* RTL;
|
|
413
|
+
* runtime disabled states;
|
|
414
|
+
* form reset;
|
|
415
|
+
* custom clear controls.
|
|
416
|
+
|
|
417
|
+
Run it locally with:
|
|
418
|
+
|
|
419
|
+
```bash
|
|
420
|
+
bun install
|
|
421
|
+
bun run dev
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
Then open:
|
|
425
|
+
|
|
426
|
+
```text
|
|
427
|
+
http://127.0.0.1:4173/
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
The demo uses the generated distribution build, not a special development-only version.
|
|
431
|
+
|
|
432
|
+
## What is in 0.1
|
|
433
|
+
|
|
434
|
+
The main 0.1 API covers:
|
|
435
|
+
|
|
436
|
+
* input + datalist;
|
|
437
|
+
* single and multiple selects;
|
|
438
|
+
* `<combo-box>`;
|
|
439
|
+
* filtering and matching;
|
|
440
|
+
* multiple search fields;
|
|
441
|
+
* creation;
|
|
442
|
+
* remote loading;
|
|
443
|
+
* chips;
|
|
444
|
+
* separators;
|
|
445
|
+
* `maxItems` and `maxOptions`;
|
|
446
|
+
* selection order and `move()`;
|
|
447
|
+
* `clear()`;
|
|
448
|
+
* form semantics;
|
|
449
|
+
* exact cleanup with `dispose()`.
|
|
450
|
+
|
|
451
|
+
A few more advanced APIs are intentionally still experimental in 0.x:
|
|
452
|
+
|
|
453
|
+
* `observeSource`;
|
|
454
|
+
* custom `tokenize`;
|
|
455
|
+
* cursor pagination / `loadMore()`;
|
|
456
|
+
* rich `render.*` customization.
|
|
457
|
+
|
|
458
|
+
Things that are deliberately **not** part of the library:
|
|
459
|
+
|
|
460
|
+
* a plugin framework;
|
|
461
|
+
* virtualization;
|
|
462
|
+
* built-in drag/drop;
|
|
463
|
+
* checkbox dropdowns;
|
|
464
|
+
* Bootstrap JavaScript;
|
|
465
|
+
* automatic DOM observation by default;
|
|
466
|
+
* a built-in clear button;
|
|
467
|
+
* a JavaScript positioning fallback.
|
|
468
|
+
|
|
469
|
+
The goal is not to become another all-purpose Select2 clone. The library should stay small enough that native controls and browser APIs remain visible underneath it.
|
|
470
|
+
|
|
471
|
+
## Custom element registration
|
|
472
|
+
|
|
473
|
+
Registration is explicit:
|
|
474
|
+
|
|
475
|
+
```js
|
|
476
|
+
import { defineCombobox } from "@lekoala/combobox";
|
|
477
|
+
|
|
478
|
+
defineCombobox();
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
Calling `defineCombobox()` more than once is safe.
|
|
482
|
+
|
|
483
|
+
You can also build your own element name:
|
|
484
|
+
|
|
485
|
+
```js
|
|
486
|
+
import { ComboBoxElement } from "@lekoala/combobox";
|
|
487
|
+
|
|
488
|
+
customElements.define(
|
|
489
|
+
"app-combobox",
|
|
490
|
+
class extends ComboBoxElement {},
|
|
491
|
+
);
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
`<combo-box>` uses no Shadow DOM and does not become the form control itself.
|
|
495
|
+
|
|
496
|
+
## Development
|
|
497
|
+
|
|
498
|
+
The source is pure ESM and has no runtime dependencies.
|
|
499
|
+
|
|
500
|
+
```bash
|
|
501
|
+
bun install
|
|
502
|
+
bunx playwright install chromium firefox webkit
|
|
503
|
+
|
|
504
|
+
bun run check
|
|
505
|
+
bun run test:browser
|
|
506
|
+
bun run sync
|
|
507
|
+
bun run verify
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
A few useful commands:
|
|
511
|
+
|
|
512
|
+
```text
|
|
513
|
+
bun run check
|
|
514
|
+
lint + typecheck + unit tests
|
|
515
|
+
|
|
516
|
+
bun run test:browser
|
|
517
|
+
browser behavior tests against the ESM source
|
|
518
|
+
|
|
519
|
+
bun run sync
|
|
520
|
+
regenerate dist JS/CSS, declarations and custom-elements.json
|
|
521
|
+
|
|
522
|
+
bun run verify
|
|
523
|
+
run the full consistency/package checks
|
|
524
|
+
|
|
525
|
+
bun run check:all
|
|
526
|
+
bun run test:browser:all
|
|
527
|
+
include Firefox and WebKit
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
Generated distribution files are committed so the demo, package contents and published artifacts can be checked directly.
|
|
531
|
+
|
|
532
|
+
The package ships:
|
|
533
|
+
|
|
534
|
+
* pure ESM entry points;
|
|
535
|
+
* an opt-in `<combo-box>` registration entry;
|
|
536
|
+
* a classic self-registering build;
|
|
537
|
+
* CSS;
|
|
538
|
+
* generated TypeScript declarations;
|
|
539
|
+
* `custom-elements.json`.
|
|
540
|
+
|
|
541
|
+
There are no runtime source maps in 0.1. Declaration maps are kept for TypeScript editor navigation.
|
|
542
|
+
|
|
543
|
+
## Documentation
|
|
544
|
+
|
|
545
|
+
More detail lives here:
|
|
546
|
+
|
|
547
|
+
* [API](docs/API.md) — options, methods, attributes and events
|
|
548
|
+
* [Architecture](docs/ARCHITECTURE.md) — internal model and design decisions
|
|
549
|
+
* [Use cases](docs/USE_CASES.md) — practical examples
|
|
550
|
+
* [Migration](docs/MIGRATION.md) — moving from `bootstrap5-tags` / `bootstrap5-autocomplete`
|
|
551
|
+
* [Testing](docs/TESTING.md) — browser and behavior coverage
|
|
552
|
+
* [References](docs/REFERENCES.md) — related browser and Open UI work
|
|
553
|
+
|
|
554
|
+
## Design principles
|
|
555
|
+
|
|
556
|
+
A few rules keep the library intentionally small:
|
|
557
|
+
|
|
558
|
+
1. The native control owns the value.
|
|
559
|
+
2. Remote results stay temporary until selected.
|
|
560
|
+
3. Option identity comes from the actual `<option>`, not only its string value.
|
|
561
|
+
4. Form behavior should remain native whenever possible.
|
|
562
|
+
5. The browser handles placement and top-layer behavior.
|
|
563
|
+
6. Rich rendering uses DOM nodes, not HTML strings.
|
|
564
|
+
7. Application-specific transport and UI stay application-specific.
|
|
565
|
+
|
|
566
|
+
That is most of the design.
|