html-combobox-element 0.0.2-beta.6 → 0.0.2-beta.7
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 +1 -0
- package/dist/cjs/combobox/Combobox.markup.js +188 -84
- package/dist/cjs/combobox/HTML.combobox.element.js +148 -105
- package/dist/cjs/combobox/HTML.combobox.option.element.js +71 -5
- package/dist/cjs/combobox/HTML.combobox.tag.element.js +26 -7
- package/dist/cjs/index.js +0 -62
- package/dist/esm/combobox/Combobox.markup.js +188 -84
- package/dist/esm/combobox/HTML.combobox.element.js +148 -105
- package/dist/esm/combobox/HTML.combobox.option.element.js +71 -5
- package/dist/esm/combobox/HTML.combobox.tag.element.js +26 -7
- package/dist/esm/index.js +0 -62
- package/dist/types/combobox/Combobox.markup.d.ts +11 -8
- package/dist/types/combobox/Combobox.markup.d.ts.map +1 -1
- package/dist/types/combobox/HTML.combobox.element.d.ts +3 -1
- package/dist/types/combobox/HTML.combobox.element.d.ts.map +1 -1
- package/dist/types/combobox/HTML.combobox.option.element.d.ts +11 -0
- package/dist/types/combobox/HTML.combobox.option.element.d.ts.map +1 -1
- package/dist/types/combobox/HTML.combobox.tag.element.d.ts +9 -1
- package/dist/types/combobox/HTML.combobox.tag.element.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -11,41 +11,70 @@ export class HTMLComboboxElement extends HTMLElement {
|
|
|
11
11
|
#observer;
|
|
12
12
|
#markup;
|
|
13
13
|
#values = new Set;
|
|
14
|
+
#prevValue = '';
|
|
15
|
+
#changed = false;
|
|
14
16
|
constructor() {
|
|
15
17
|
super();
|
|
16
18
|
this.#internals = this.attachInternals();
|
|
17
19
|
this.#internals.role = "combobox";
|
|
18
20
|
this.#internals.ariaHasPopup = "dialog";
|
|
19
|
-
this.shadowRoot = this.attachShadow({ mode: '
|
|
21
|
+
this.shadowRoot = this.attachShadow({ mode: 'open', delegatesFocus: true }); //delegatesFocus: true
|
|
20
22
|
this.#markup = new ComboboxMarkup(this.shadowRoot, this.#internals);
|
|
21
23
|
this.shadowRoot.innerHTML = ComboboxMarkup.template;
|
|
22
24
|
this.shadowRoot.adoptedStyleSheets = HTMLComboboxElement.styleSheet;
|
|
23
25
|
this.#observer = new MutationObserver(this.#onOptionsChanges);
|
|
26
|
+
this.#markup.connect();
|
|
24
27
|
}
|
|
25
28
|
// Lifecycle callbacks
|
|
26
29
|
connectedCallback() {
|
|
27
|
-
|
|
28
|
-
this.#
|
|
30
|
+
// super.setAttribute('tabindex', '0');
|
|
31
|
+
this.shadowRoot.addEventListener('optionselectedstatechange', this.#onOptionSelectedStateChanges);
|
|
32
|
+
this.shadowRoot.addEventListener('tagcleared', this.#onClearTag);
|
|
29
33
|
this.#onOptionsChanges([{ addedNodes: Array.from(this.children) }]);
|
|
34
|
+
this.#initialAttributesSynchronization();
|
|
30
35
|
this.#observer.observe(this, HTMLComboboxElement.observerOptions);
|
|
31
|
-
this
|
|
36
|
+
this.addEventListener('keydown', this.#onKeyDown);
|
|
37
|
+
this.#markup.clearAllButton.addEventListener('click', this.#onClearAll);
|
|
32
38
|
this.#markup.searchInput.addEventListener('input', this.#onInput);
|
|
39
|
+
this.#markup.dropdown.addEventListener('toggle', this.#onPickerToggle);
|
|
40
|
+
this.#setValidityAndFormValue();
|
|
41
|
+
}
|
|
42
|
+
insertBefore = (node, referenceNode) => {
|
|
43
|
+
// Since we just move options to the #markup.optionsContainer, they are no more part of this children
|
|
44
|
+
// next time, when someone is calling this(combo-box).insertBefore, an error will be thrown:
|
|
45
|
+
// Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.
|
|
46
|
+
// Here we overwrite this method, and if new node is HTMLComboboxOptionElement,
|
|
47
|
+
// we just delegate this job to the real option parent: optionsContainer
|
|
48
|
+
if (node instanceof HTMLComboboxOptionElement || node instanceof HTMLOptGroupElement) {
|
|
49
|
+
this.#markup.optionsContainer?.insertBefore(node, referenceNode);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
super.insertBefore(node, referenceNode);
|
|
53
|
+
}
|
|
54
|
+
return node;
|
|
55
|
+
};
|
|
56
|
+
get changed() {
|
|
57
|
+
return this.#changed;
|
|
33
58
|
}
|
|
34
59
|
disconnectedCallback() {
|
|
35
60
|
this.#observer.disconnect();
|
|
36
61
|
this.#markup.disconnect();
|
|
37
|
-
this
|
|
62
|
+
this.removeEventListener('keydown', this.#onKeyDown);
|
|
63
|
+
this.#markup.clearAllButton.removeEventListener('click', this.#onClearAll);
|
|
38
64
|
this.#markup.searchInput.removeEventListener('input', this.#onInput);
|
|
65
|
+
this.#markup.dropdown.removeEventListener('toggle', this.#onPickerToggle);
|
|
66
|
+
this.shadowRoot.removeEventListener('optionselectedstatechange', this.#onOptionSelectedStateChanges);
|
|
67
|
+
this.shadowRoot.removeEventListener('tagcleared', this.#onClearTag);
|
|
39
68
|
}
|
|
40
69
|
formResetCallback() {
|
|
41
|
-
this.#values = new Set;
|
|
42
70
|
this.selectedOptions.forEach(option => option.selected = false);
|
|
43
|
-
this
|
|
44
|
-
this.#
|
|
45
|
-
this.dispatchEvent(new Event('change'));
|
|
71
|
+
this.dispatchEvent(new Event('reset'));
|
|
72
|
+
requestAnimationFrame(() => this.#markup.searchInput?.focus());
|
|
46
73
|
}
|
|
47
|
-
formDisabledCallback(
|
|
48
|
-
|
|
74
|
+
formDisabledCallback() {
|
|
75
|
+
// toDo
|
|
76
|
+
// when inside fieldset, this callback is only invoked for the first time
|
|
77
|
+
// isDisabled: boolean
|
|
49
78
|
}
|
|
50
79
|
// Instance properties
|
|
51
80
|
// <combo-box> specific properties
|
|
@@ -62,9 +91,7 @@ export class HTMLComboboxElement extends HTMLElement {
|
|
|
62
91
|
value = '';
|
|
63
92
|
value = String(value);
|
|
64
93
|
super.setAttribute('query', value);
|
|
65
|
-
|
|
66
|
-
this.#markup.searchInput.value = value;
|
|
67
|
-
}
|
|
94
|
+
this.#markup?.setInputValue(value);
|
|
68
95
|
}
|
|
69
96
|
get placeholder() {
|
|
70
97
|
return this.getAttribute('placeholder');
|
|
@@ -74,10 +101,7 @@ export class HTMLComboboxElement extends HTMLElement {
|
|
|
74
101
|
value = ' ';
|
|
75
102
|
value = String(value);
|
|
76
103
|
super.setAttribute('placeholder', value);
|
|
77
|
-
|
|
78
|
-
this.#markup.placeholder.innerText = value;
|
|
79
|
-
this.#markup.searchInput.placeholder = value;
|
|
80
|
-
}
|
|
104
|
+
this.#markup?.setPlaceholder(value);
|
|
81
105
|
}
|
|
82
106
|
get clearable() {
|
|
83
107
|
return this.hasAttribute('clearable');
|
|
@@ -103,7 +127,9 @@ export class HTMLComboboxElement extends HTMLElement {
|
|
|
103
127
|
}
|
|
104
128
|
set disabled(value) {
|
|
105
129
|
this.#internals.ariaDisabled = String(value);
|
|
106
|
-
|
|
130
|
+
value = toBoolean(value);
|
|
131
|
+
super.toggleAttribute('disabled', value);
|
|
132
|
+
super.toggleAttribute('inert', value);
|
|
107
133
|
}
|
|
108
134
|
get form() {
|
|
109
135
|
return this.#internals.form;
|
|
@@ -154,34 +180,11 @@ export class HTMLComboboxElement extends HTMLElement {
|
|
|
154
180
|
set value(value) {
|
|
155
181
|
if (this.value === value || typeof value !== 'string')
|
|
156
182
|
return;
|
|
157
|
-
const prevValue = new Set(this.#values);
|
|
158
|
-
this.#values = new Set;
|
|
159
183
|
const values = value.split(',').filter(Boolean);
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
if (this.#values.size === 0) {
|
|
165
|
-
values.length = 1;
|
|
166
|
-
}
|
|
167
|
-
else {
|
|
168
|
-
values.length = 0;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
for (const key of values) {
|
|
172
|
-
const option = this.#markup.getOptionByValue(key);
|
|
173
|
-
if (option)
|
|
174
|
-
this.#selectOption(option);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
for (const key of prevValue) {
|
|
178
|
-
if (this.#values.has(key))
|
|
179
|
-
continue;
|
|
180
|
-
const option = this.#markup.getOptionByValue(key);
|
|
181
|
-
const tag = this.#markup.getTagByValue(key);
|
|
182
|
-
tag?.remove();
|
|
183
|
-
option?.toggleAttribute('selected', false);
|
|
184
|
-
}
|
|
184
|
+
this.#values = new Set(values);
|
|
185
|
+
this.#markup.options.forEach(option => {
|
|
186
|
+
const value = option.value;
|
|
187
|
+
option.selected = this.#values.has(value);
|
|
185
188
|
});
|
|
186
189
|
}
|
|
187
190
|
// Instance methods
|
|
@@ -228,64 +231,38 @@ export class HTMLComboboxElement extends HTMLElement {
|
|
|
228
231
|
}
|
|
229
232
|
}
|
|
230
233
|
// Internal
|
|
231
|
-
#onInput = (event) => {
|
|
232
|
-
if (!this.searchable && this.filterable) {
|
|
233
|
-
if (event.target && event.target instanceof HTMLInputElement) {
|
|
234
|
-
this.#markup.sort(event.target.value);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
};
|
|
238
234
|
#onOptionsChanges = (records) => {
|
|
239
235
|
records.forEach(record => {
|
|
240
236
|
record.addedNodes.forEach(node => {
|
|
241
|
-
if (node instanceof HTMLComboboxOptionElement) {
|
|
242
|
-
node.addEventListener('click', this.#onSelectOption);
|
|
243
|
-
if (node.selected) {
|
|
244
|
-
if (this.multiple) {
|
|
245
|
-
this.#selectOption(node);
|
|
246
|
-
}
|
|
247
|
-
else if (this.#values.size === 0) {
|
|
248
|
-
this.#selectOption(node);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
237
|
if (node instanceof HTMLComboboxOptionElement || node instanceof HTMLOptGroupElement) {
|
|
253
238
|
this.#markup.optionsContainer.append(node);
|
|
254
239
|
}
|
|
255
240
|
});
|
|
256
241
|
});
|
|
257
|
-
this.#markup.invalidateOptionsCache();
|
|
258
|
-
this.#setValidityAndFormValue();
|
|
259
242
|
};
|
|
260
|
-
#
|
|
261
|
-
|
|
262
|
-
return;
|
|
243
|
+
#onOptionSelectedStateChanges = (event) => {
|
|
244
|
+
const option = event.target;
|
|
263
245
|
const value = option.value;
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
246
|
+
if (option.selected) {
|
|
247
|
+
if (!this.multiple) {
|
|
248
|
+
this.#values.forEach(value => {
|
|
249
|
+
const prevOption = this.#markup.getOptionByValue(value);
|
|
250
|
+
if (prevOption)
|
|
251
|
+
prevOption.selected = false;
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
if (!this.#values.has(value)) {
|
|
255
|
+
this.#values.add(value);
|
|
256
|
+
this.#markup.createAndAppendTag(option);
|
|
257
|
+
this.#setValidityAndFormValue();
|
|
258
|
+
this.dispatchEvent(new Event('change'));
|
|
259
|
+
}
|
|
270
260
|
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
if (option) {
|
|
277
|
-
if (this.#values.has(option.value))
|
|
278
|
-
return;
|
|
279
|
-
if (!this.multiple) {
|
|
280
|
-
event.stopPropagation();
|
|
281
|
-
this.#values.forEach(value => {
|
|
282
|
-
this.#markup.getTagByValue(value)?.remove();
|
|
283
|
-
this.#markup.getOptionByValue(value)?.toggleAttribute('selected', false);
|
|
284
|
-
});
|
|
285
|
-
this.#values.clear();
|
|
286
|
-
this.#markup.tagsContainer.replaceChildren();
|
|
287
|
-
}
|
|
288
|
-
this.#selectOption(option);
|
|
261
|
+
else {
|
|
262
|
+
if (this.#values.has(value)) {
|
|
263
|
+
this.#values.delete(value);
|
|
264
|
+
const control = this.#markup.getTagByValue(value);
|
|
265
|
+
control?.remove();
|
|
289
266
|
this.#setValidityAndFormValue();
|
|
290
267
|
this.dispatchEvent(new Event('change'));
|
|
291
268
|
}
|
|
@@ -294,25 +271,90 @@ export class HTMLComboboxElement extends HTMLElement {
|
|
|
294
271
|
#onClearTag = (event) => {
|
|
295
272
|
const target = event.target;
|
|
296
273
|
if (target) {
|
|
297
|
-
const
|
|
298
|
-
if (
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
this
|
|
305
|
-
|
|
306
|
-
|
|
274
|
+
const option = this.#markup.getOptionByValue(target.value);
|
|
275
|
+
if (option)
|
|
276
|
+
option.selected = false;
|
|
277
|
+
requestAnimationFrame(() => {
|
|
278
|
+
if (this.#values.size) {
|
|
279
|
+
this.focus();
|
|
280
|
+
}
|
|
281
|
+
else if (this.searchable || this.filterable) {
|
|
282
|
+
this.#markup.searchInput?.focus();
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
this.focus();
|
|
286
|
+
}
|
|
287
|
+
});
|
|
307
288
|
}
|
|
308
289
|
};
|
|
309
|
-
#
|
|
290
|
+
#onClearAll = () => {
|
|
310
291
|
this.formResetCallback();
|
|
311
292
|
};
|
|
293
|
+
#onPickerToggle = (event) => {
|
|
294
|
+
const state = Reflect.get(event, 'newState');
|
|
295
|
+
const oldState = Reflect.get(event, 'oldState');
|
|
296
|
+
if (state === oldState)
|
|
297
|
+
return;
|
|
298
|
+
if (state === 'open') {
|
|
299
|
+
this.#prevValue = this.value;
|
|
300
|
+
this.#changed = true;
|
|
301
|
+
}
|
|
302
|
+
else {
|
|
303
|
+
this.#changed = this.#prevValue !== this.value;
|
|
304
|
+
this.dispatchEvent(new Event('close'));
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
#onKeyDown = (event) => {
|
|
308
|
+
if (this.#internals.ariaExpanded !== 'true')
|
|
309
|
+
return;
|
|
310
|
+
const activeElement = this.shadowRoot.activeElement;
|
|
311
|
+
if (!activeElement) {
|
|
312
|
+
// ??? how we should react ???
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
if (activeElement === this.#markup.searchInput && event.key === 'Enter') {
|
|
316
|
+
event.stopPropagation();
|
|
317
|
+
if (!this.searchable && this.filterable) {
|
|
318
|
+
this.#markup.sort(this.#markup.searchInput.value);
|
|
319
|
+
}
|
|
320
|
+
if (this.searchable) {
|
|
321
|
+
this.dispatchEvent(new Event('search'));
|
|
322
|
+
}
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
if (event.key === 'Enter' && activeElement instanceof HTMLComboboxOptionElement) {
|
|
326
|
+
if (!activeElement.selected) {
|
|
327
|
+
event.stopPropagation();
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
if (event.key === 'Enter' && activeElement instanceof HTMLElement && activeElement.part.contains('clear-tag')) {
|
|
332
|
+
activeElement.click();
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
if (event.key === 'Escape' || event.key === 'Enter') {
|
|
336
|
+
this.#markup.closeDropdown();
|
|
337
|
+
this.blur();
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
#onInput = () => {
|
|
341
|
+
this.query = this.#markup.searchInput.value;
|
|
342
|
+
if (!this.searchable && this.filterable) {
|
|
343
|
+
this.#markup.sort(this.#markup.searchInput?.value || '');
|
|
344
|
+
}
|
|
345
|
+
};
|
|
312
346
|
#setValidityAndFormValue() {
|
|
347
|
+
this.#markup.clearAllButton?.toggleAttribute('disabled', !Boolean(this.#values.size));
|
|
348
|
+
let empty = false;
|
|
349
|
+
if (this.#values.size === 0)
|
|
350
|
+
empty = true;
|
|
351
|
+
if (this.#values.size > 0 && !this.clearable && !this.multiple && (this.searchable || this.filterable)) {
|
|
352
|
+
empty = true;
|
|
353
|
+
}
|
|
354
|
+
this.#markup.searchInput?.toggleAttribute('autofocus', empty);
|
|
313
355
|
this.#internals.setFormValue(this.value);
|
|
314
356
|
if (this.required && this.value === '') {
|
|
315
|
-
this.#internals.setValidity({ valueMissing: true });
|
|
357
|
+
this.#internals.setValidity({ valueMissing: true }, 'Value missing');
|
|
316
358
|
}
|
|
317
359
|
else {
|
|
318
360
|
this.#internals.setValidity({});
|
|
@@ -320,6 +362,7 @@ export class HTMLComboboxElement extends HTMLElement {
|
|
|
320
362
|
}
|
|
321
363
|
#initialAttributesSynchronization() {
|
|
322
364
|
for (const key of HTMLComboboxElement.OWN_IDL) {
|
|
365
|
+
// @ts-ignore
|
|
323
366
|
this[key] = this.getAttribute(key);
|
|
324
367
|
}
|
|
325
368
|
}
|
|
@@ -1,15 +1,56 @@
|
|
|
1
1
|
import { toBoolean } from './Boolean.attribute.value.normalizer.js';
|
|
2
2
|
export class HTMLComboboxOptionElement extends HTMLElement {
|
|
3
|
-
static OWN_IDL = new Set(['value', 'label', 'selected']);
|
|
3
|
+
static OWN_IDL = new Set(['value', 'label', 'selected', 'disabled']);
|
|
4
|
+
static EventInit = { bubbles: true, cancelable: true, composed: true };
|
|
5
|
+
static captureFocus(event) {
|
|
6
|
+
if (event.relatedTarget && event.relatedTarget instanceof HTMLComboboxOptionElement) {
|
|
7
|
+
event.stopPropagation();
|
|
8
|
+
event.preventDefault();
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
static onNavigate(event) {
|
|
12
|
+
if (!event.target ||
|
|
13
|
+
!(event.target instanceof HTMLComboboxOptionElement) ||
|
|
14
|
+
event.key === 'Tab' ||
|
|
15
|
+
event.key === 'Enter')
|
|
16
|
+
return;
|
|
17
|
+
event.preventDefault();
|
|
18
|
+
if (event.key === 'ArrowDown') {
|
|
19
|
+
const next = event.target.nextElementSibling;
|
|
20
|
+
if (next && next instanceof HTMLComboboxOptionElement) {
|
|
21
|
+
next.focus();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
if (event.key === 'ArrowUp') {
|
|
25
|
+
const prev = event.target.previousElementSibling;
|
|
26
|
+
if (prev && prev instanceof HTMLComboboxOptionElement) {
|
|
27
|
+
prev.focus();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
4
31
|
connectedCallback() {
|
|
32
|
+
this.addEventListener('keydown', HTMLComboboxOptionElement.onNavigate);
|
|
33
|
+
this.addEventListener('focusout', HTMLComboboxOptionElement.captureFocus);
|
|
34
|
+
this.addEventListener('click', this.#toggleSelection);
|
|
5
35
|
this.#initialAttributesSynchronization();
|
|
6
36
|
this.part.add('option');
|
|
7
37
|
super.setAttribute('tabindex', "0");
|
|
8
38
|
super.setAttribute('role', "option");
|
|
9
|
-
if (!this.value)
|
|
10
|
-
this.value = this.
|
|
11
|
-
|
|
39
|
+
if (!this.value)
|
|
40
|
+
this.value = this.label;
|
|
41
|
+
if (!this.textContent)
|
|
42
|
+
this.textContent = this.value;
|
|
43
|
+
this.dispatchEvent(new Event('optionselectedstatechange', HTMLComboboxOptionElement.EventInit));
|
|
12
44
|
}
|
|
45
|
+
disconnectedCallback() {
|
|
46
|
+
this.removeEventListener('click', this.#toggleSelection);
|
|
47
|
+
this.removeEventListener('keydown', HTMLComboboxOptionElement.onNavigate);
|
|
48
|
+
this.removeEventListener('focusout', HTMLComboboxOptionElement.captureFocus);
|
|
49
|
+
}
|
|
50
|
+
#toggleSelection = (event) => {
|
|
51
|
+
event.stopPropagation();
|
|
52
|
+
this.selected = !this.selected;
|
|
53
|
+
};
|
|
13
54
|
get value() {
|
|
14
55
|
return this.getAttribute('value');
|
|
15
56
|
}
|
|
@@ -30,15 +71,34 @@ export class HTMLComboboxOptionElement extends HTMLElement {
|
|
|
30
71
|
return this.hasAttribute('selected');
|
|
31
72
|
}
|
|
32
73
|
set selected(value) {
|
|
33
|
-
|
|
74
|
+
const prev = this.selected;
|
|
75
|
+
const force = toBoolean(value);
|
|
76
|
+
if (prev === force)
|
|
77
|
+
return;
|
|
78
|
+
super.toggleAttribute('selected', force);
|
|
79
|
+
this.part.toggle('selected', force);
|
|
80
|
+
if (this.isConnected) {
|
|
81
|
+
this.dispatchEvent(new Event('optionselectedstatechange', HTMLComboboxOptionElement.EventInit));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
get disabled() {
|
|
85
|
+
return this.hasAttribute('disabled');
|
|
86
|
+
}
|
|
87
|
+
set disabled(value) {
|
|
88
|
+
const force = toBoolean(value);
|
|
89
|
+
super.toggleAttribute('disabled', force);
|
|
90
|
+
this.part.toggle('disabled', force);
|
|
91
|
+
super.toggleAttribute('inert', force);
|
|
34
92
|
}
|
|
35
93
|
#initialAttributesSynchronization() {
|
|
36
94
|
for (const key of HTMLComboboxOptionElement.OWN_IDL) {
|
|
95
|
+
// @ts-ignore
|
|
37
96
|
this[key] = this.getAttribute(key);
|
|
38
97
|
}
|
|
39
98
|
}
|
|
40
99
|
setAttribute(name, value) {
|
|
41
100
|
if (HTMLComboboxOptionElement.OWN_IDL.has(name)) {
|
|
101
|
+
// @ts-ignore
|
|
42
102
|
this[name] = value;
|
|
43
103
|
}
|
|
44
104
|
else {
|
|
@@ -47,12 +107,18 @@ export class HTMLComboboxOptionElement extends HTMLElement {
|
|
|
47
107
|
}
|
|
48
108
|
removeAttribute(name) {
|
|
49
109
|
if (HTMLComboboxOptionElement.OWN_IDL.has(name)) {
|
|
110
|
+
// @ts-ignore
|
|
50
111
|
this[name] = null;
|
|
51
112
|
}
|
|
52
113
|
else {
|
|
53
114
|
super.removeAttribute(name);
|
|
54
115
|
}
|
|
55
116
|
}
|
|
117
|
+
toggleAttribute(name, force) {
|
|
118
|
+
if (name === 'selected')
|
|
119
|
+
this.part.toggle(name, force);
|
|
120
|
+
return super.toggleAttribute(name, force);
|
|
121
|
+
}
|
|
56
122
|
}
|
|
57
123
|
if (!window.customElements.get('box-option')) {
|
|
58
124
|
window.customElements.define('box-option', HTMLComboboxOptionElement);
|
|
@@ -1,17 +1,36 @@
|
|
|
1
1
|
export class HTMLComboboxTagElement extends HTMLElement {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
static EventInit = { bubbles: true, cancelable: true, composed: true };
|
|
3
|
+
get value() {
|
|
4
|
+
return this.getAttribute('value') || '';
|
|
4
5
|
}
|
|
5
6
|
connectedCallback() {
|
|
6
7
|
this.part.add('tag');
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
const control = this.querySelector('[part*="clear-tag"]');
|
|
9
|
+
if (!control?.checkVisibility()) {
|
|
10
|
+
super.setAttribute('tabindex', '0');
|
|
11
|
+
}
|
|
12
|
+
control?.addEventListener('focusout', HTMLComboboxTagElement.captureFocus);
|
|
13
|
+
control?.addEventListener('click', HTMLComboboxTagElement.onClearTag);
|
|
14
|
+
const root = this.getRootNode();
|
|
15
|
+
if (root?.host.hasAttribute('multiple')) {
|
|
16
|
+
if (!control) {
|
|
17
|
+
throw new Error(`A <button> with part="clear-tag" is required for <combo-box> with multiple attribute`);
|
|
12
18
|
}
|
|
13
19
|
}
|
|
14
20
|
}
|
|
21
|
+
disconnectedCallback() {
|
|
22
|
+
const control = this.querySelector('[part*="clear-tag"]');
|
|
23
|
+
control?.removeEventListener('focusout', HTMLComboboxTagElement.captureFocus);
|
|
24
|
+
control?.removeEventListener('click', HTMLComboboxTagElement.onClearTag);
|
|
25
|
+
}
|
|
26
|
+
static onClearTag(event) {
|
|
27
|
+
const target = event.target;
|
|
28
|
+
target?.parentElement?.dispatchEvent(new Event('tagcleared', HTMLComboboxTagElement.EventInit));
|
|
29
|
+
}
|
|
30
|
+
static captureFocus(event) {
|
|
31
|
+
event.stopPropagation();
|
|
32
|
+
event.preventDefault();
|
|
33
|
+
}
|
|
15
34
|
}
|
|
16
35
|
if (!window.customElements.get('box-tag')) {
|
|
17
36
|
window.customElements.define('box-tag', HTMLComboboxTagElement);
|
package/dist/esm/index.js
CHANGED
|
@@ -3,65 +3,3 @@
|
|
|
3
3
|
/// <reference types="react" />
|
|
4
4
|
/// <reference types="vue" />
|
|
5
5
|
export * from './combobox/index.js';
|
|
6
|
-
// declare global {
|
|
7
|
-
// namespace React {
|
|
8
|
-
// namespace JSX {
|
|
9
|
-
// interface IntrinsicElements {
|
|
10
|
-
// 'combo-box': React.DetailedHTMLProps<Omit<React.HTMLAttributes<HTMLComboboxElement>, 'defaultValue'>,
|
|
11
|
-
// HTMLComboboxElement
|
|
12
|
-
// > & ComboboxJsxAttributes;
|
|
13
|
-
//
|
|
14
|
-
// 'box-option': React.DetailedHTMLProps<
|
|
15
|
-
// React.HTMLAttributes<HTMLComboboxOptionElement>,
|
|
16
|
-
// HTMLComboboxOptionElement>
|
|
17
|
-
// & ComboboxOptionJsxAttributes;
|
|
18
|
-
//
|
|
19
|
-
// 'box-tag': React.DetailedHTMLProps<React.HTMLAttributes<HTMLComboboxTagElement>, HTMLComboboxTagElement>;
|
|
20
|
-
// }
|
|
21
|
-
// }
|
|
22
|
-
// }
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
// namespace preact {
|
|
26
|
-
// namespace JSX {
|
|
27
|
-
// interface IntrinsicElements {
|
|
28
|
-
// 'combo-box': preact.HTMLAttributes<HTMLComboboxElement> & ComboboxJsxAttributes;
|
|
29
|
-
// 'box-option': preact.HTMLAttributes<HTMLComboboxOptionElement> & ComboboxOptionJsxAttributes;
|
|
30
|
-
// 'box-tag': preact.HTMLAttributes<HTMLComboboxTagElement>;
|
|
31
|
-
// }
|
|
32
|
-
// }
|
|
33
|
-
// }
|
|
34
|
-
//
|
|
35
|
-
// }
|
|
36
|
-
//
|
|
37
|
-
// declare module 'preact' {
|
|
38
|
-
// namespace JSX {
|
|
39
|
-
// interface IntrinsicElements {
|
|
40
|
-
// 'combo-box': preact.HTMLAttributes<HTMLComboboxElement> & ComboboxJsxAttributes;
|
|
41
|
-
// 'box-option': preact.HTMLAttributes<HTMLComboboxOptionElement> & ComboboxOptionJsxAttributes;
|
|
42
|
-
// 'box-tag': preact.HTMLAttributes<HTMLComboboxTagElement>;
|
|
43
|
-
// }
|
|
44
|
-
// }
|
|
45
|
-
// }
|
|
46
|
-
//
|
|
47
|
-
// // Solid
|
|
48
|
-
// declare module 'solid-js' {
|
|
49
|
-
// namespace JSX {
|
|
50
|
-
// interface IntrinsicElements {
|
|
51
|
-
// 'combo-box': HTMLAttributes<HTMLComboboxElement> & ComboboxJsxAttributes
|
|
52
|
-
// 'box-option': HTMLAttributes<HTMLComboboxOptionElement> & ComboboxOptionJsxAttributes
|
|
53
|
-
// 'box-tag': HTMLAttributes<HTMLElement>
|
|
54
|
-
// }
|
|
55
|
-
// }
|
|
56
|
-
// }
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
// declare global {
|
|
60
|
-
// namespace JSX {
|
|
61
|
-
// interface IntrinsicElements {
|
|
62
|
-
// 'combo-box': ComboboxJsxAttributes;
|
|
63
|
-
// 'box-option': ComboboxOptionJsxAttributes;
|
|
64
|
-
// 'box-tag': HTMLElement;
|
|
65
|
-
// }
|
|
66
|
-
// }
|
|
67
|
-
// }
|
|
@@ -9,18 +9,21 @@ export declare class ComboboxMarkup {
|
|
|
9
9
|
placeholder: HTMLDivElement | null;
|
|
10
10
|
searchInput: HTMLInputElement | null;
|
|
11
11
|
tagTemplate: HTMLComboboxTagElement | null;
|
|
12
|
-
options: NodeListOf<HTMLComboboxOptionElement> | null;
|
|
13
|
-
connected: boolean;
|
|
14
12
|
constructor(shadowRoot: ShadowRoot, internals: ElementInternals);
|
|
15
13
|
connect(): void;
|
|
16
|
-
invalidateOptionsCache(): void;
|
|
17
|
-
sort(query: string): void;
|
|
18
14
|
disconnect(): void;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
setPlaceholder(value: string): void;
|
|
16
|
+
setInputValue(value: string): void;
|
|
17
|
+
onClearAllFocusOut: (event: FocusEvent) => void;
|
|
18
|
+
onClickOutside: (ev: MouseEvent) => void;
|
|
19
|
+
sort(query: string): void;
|
|
20
|
+
get options(): NodeListOf<HTMLComboboxOptionElement>;
|
|
21
|
+
getTagControl(tag: HTMLComboboxTagElement): HTMLButtonElement;
|
|
22
|
+
setDropdownPosition(): void;
|
|
23
|
+
showDropdown(): void;
|
|
24
|
+
onFocusIn: () => void;
|
|
22
25
|
closeDropdown(): void;
|
|
23
|
-
|
|
26
|
+
onFocusOut: () => void;
|
|
24
27
|
createAndAppendTag(option: HTMLComboboxOptionElement): HTMLButtonElement;
|
|
25
28
|
getTagByValue(value: string): HTMLComboboxTagElement;
|
|
26
29
|
getOptionByValue(value: string): HTMLComboboxOptionElement;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Combobox.markup.d.ts","sourceRoot":"","sources":["../../../src/combobox/Combobox.markup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AAE9E,qBAAa,cAAc;;IAGzB,aAAa,EAAE,cAAc,GAAG,IAAI,CAAQ;IAC5C,gBAAgB,EAAE,cAAc,GAAG,IAAI,CAAQ;IAC/C,cAAc,EAAE,iBAAiB,GAAG,IAAI,CAAQ;IAChD,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAQ;IACvC,WAAW,EAAE,cAAc,GAAG,IAAI,CAAQ;IAC1C,WAAW,EAAE,gBAAgB,GAAG,IAAI,CAAQ;IAC5C,WAAW,EAAE,sBAAsB,GAAG,IAAI,CAAQ;
|
|
1
|
+
{"version":3,"file":"Combobox.markup.d.ts","sourceRoot":"","sources":["../../../src/combobox/Combobox.markup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AAE9E,qBAAa,cAAc;;IAGzB,aAAa,EAAE,cAAc,GAAG,IAAI,CAAQ;IAC5C,gBAAgB,EAAE,cAAc,GAAG,IAAI,CAAQ;IAC/C,cAAc,EAAE,iBAAiB,GAAG,IAAI,CAAQ;IAChD,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAQ;IACvC,WAAW,EAAE,cAAc,GAAG,IAAI,CAAQ;IAC1C,WAAW,EAAE,gBAAgB,GAAG,IAAI,CAAQ;IAC5C,WAAW,EAAE,sBAAsB,GAAG,IAAI,CAAQ;gBAOtC,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,gBAAgB;IAa/D,OAAO;IAyBP,UAAU;IAQV,cAAc,CAAC,KAAK,EAAE,MAAM;IAM5B,aAAa,CAAC,KAAK,EAAE,MAAM;IAM3B,kBAAkB,GAAI,OAAO,UAAU,UAStC;IAED,cAAc,GAAI,IAAI,UAAU,UAI/B;IAID,IAAI,CAAC,KAAK,EAAE,MAAM;IAiBlB,IAAI,OAAO,0CAEV;IAED,aAAa,CAAC,GAAG,EAAE,sBAAsB;IAczC,mBAAmB;IAqBnB,YAAY;IAIZ,SAAS,aAWR;IAED,aAAa;IAWb,UAAU,aAGT;IAGD,kBAAkB,CAAC,MAAM,EAAE,yBAAyB;IAyCpD,aAAa,CAAC,KAAK,EAAE,MAAM;IAI3B,gBAAgB,CAAC,KAAK,EAAE,MAAM;IAI9B,IAAI,eAAe,0CAElB;IAED,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE;IAQ/B,MAAM,CAAC,QAAQ,SAkQhB;CACA"}
|
|
@@ -12,9 +12,11 @@ export declare class HTMLComboboxElement extends HTMLElement {
|
|
|
12
12
|
shadowRoot: ShadowRoot;
|
|
13
13
|
constructor();
|
|
14
14
|
connectedCallback(): void;
|
|
15
|
+
insertBefore: (node: Node, referenceNode: Node | null) => any;
|
|
16
|
+
get changed(): boolean;
|
|
15
17
|
disconnectedCallback(): void;
|
|
16
18
|
formResetCallback(): void;
|
|
17
|
-
formDisabledCallback(
|
|
19
|
+
formDisabledCallback(): void;
|
|
18
20
|
get valueAsArray(): string[];
|
|
19
21
|
get query(): string;
|
|
20
22
|
set query(value: string);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HTML.combobox.element.d.ts","sourceRoot":"","sources":["../../../src/combobox/HTML.combobox.element.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AAI9E,qBAAa,mBAAoB,SAAQ,WAAW;;IAClD,MAAM,CAAC,OAAO,cAA0H;IACxI,MAAM,CAAC,eAAe;;;;MAA0D;IAChF,MAAM,CAAC,UAAU,kBAAuB;IACxC,MAAM,CAAC,cAAc,UAAQ;IAE7B,UAAU,EAAE,UAAU,CAAC;;
|
|
1
|
+
{"version":3,"file":"HTML.combobox.element.d.ts","sourceRoot":"","sources":["../../../src/combobox/HTML.combobox.element.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AAI9E,qBAAa,mBAAoB,SAAQ,WAAW;;IAClD,MAAM,CAAC,OAAO,cAA0H;IACxI,MAAM,CAAC,eAAe;;;;MAA0D;IAChF,MAAM,CAAC,UAAU,kBAAuB;IACxC,MAAM,CAAC,cAAc,UAAQ;IAE7B,UAAU,EAAE,UAAU,CAAC;;IAyBvB,iBAAiB;IAejB,YAAY,GAAI,MAAM,IAAI,EAAE,eAAe,IAAI,GAAG,IAAI,KAWrC,GAAG,CACnB;IAGD,IAAI,OAAO,YAEV;IAED,oBAAoB;IAWpB,iBAAiB;IAMjB,oBAAoB;IASpB,IAAI,YAAY,aAEf;IAED,IAAI,KAAK,IAGQ,MAAM,CADtB;IACD,IAAI,KAAK,CAAC,KAAK,EAAE,MAAM,EAMtB;IAED,IAAI,WAAW,WAEd;IACD,IAAI,WAAW,CAAC,KAAK,QAAA,EAKpB;IAED,IAAI,SAAS,YAEZ;IACD,IAAI,SAAS,CAAC,KAAK,SAAA,EAElB;IAED,IAAI,UAAU,YAEb;IACD,IAAI,UAAU,CAAC,KAAK,SAAA,EAEnB;IAED,IAAI,UAAU,YAEb;IACD,IAAI,UAAU,CAAC,KAAK,SAAA,EAEnB;IAGD,IAAI,QAAQ,YAEX;IACD,IAAI,QAAQ,CAAC,KAAK,SAAA,EAKjB;IAED,IAAI,IAAI,oBAEP;IAED,IAAI,MAAM,aAET;IAED,IAAI,MAAM,WAET;IAED,IAAI,QAAQ,YAEX;IACD,IAAI,QAAQ,CAAC,KAAK,SAAA,EAEjB;IAKD,IAAI,QAAQ,YAEX;IACD,IAAI,QAAQ,CAAC,KAAK,SAAA,EAGjB;IAID,IAAI,eAAe,0CAElB;IAMD,IAAI,IAAI,qCAEP;IAED,IAAI,iBAAiB,WAEpB;IAED,IAAI,QAAQ,kBAEX;IAED,IAAI,YAAY,YAEf;IAED,IAAI,KAAK,IAGQ,MAAM,CADtB;IACD,IAAI,KAAK,CAAC,KAAK,EAAE,MAAM,EAQtB;IAQD,aAAa;IAIb,IAAI,CAAC,KAAK,EAAE,MAAM;IASlB,cAAc;IAId,iBAAiB,CAAC,OAAO,EAAE,MAAM;IAQjC,UAAU;IAKV,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG;IAQrC,eAAe,CAAC,IAAI,EAAE,MAAM;IAkK5B,MAAM,CAAC,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE;IAIrC,MAAM,CAAC,8BAA8B;CAoBtC"}
|
|
@@ -1,14 +1,25 @@
|
|
|
1
1
|
export declare class HTMLComboboxOptionElement extends HTMLElement {
|
|
2
2
|
#private;
|
|
3
3
|
static OWN_IDL: Set<string>;
|
|
4
|
+
static EventInit: {
|
|
5
|
+
bubbles: boolean;
|
|
6
|
+
cancelable: boolean;
|
|
7
|
+
composed: boolean;
|
|
8
|
+
};
|
|
9
|
+
static captureFocus(event: FocusEvent): void;
|
|
10
|
+
static onNavigate(event: KeyboardEvent): void;
|
|
4
11
|
connectedCallback(): void;
|
|
12
|
+
disconnectedCallback(): void;
|
|
5
13
|
get value(): string;
|
|
6
14
|
set value(value: string);
|
|
7
15
|
get label(): string;
|
|
8
16
|
set label(value: string);
|
|
9
17
|
get selected(): boolean;
|
|
10
18
|
set selected(value: boolean);
|
|
19
|
+
get disabled(): boolean;
|
|
20
|
+
set disabled(value: boolean);
|
|
11
21
|
setAttribute(name: string, value: string): void;
|
|
12
22
|
removeAttribute(name: string): void;
|
|
23
|
+
toggleAttribute(name: string, force?: boolean): boolean;
|
|
13
24
|
}
|
|
14
25
|
//# sourceMappingURL=HTML.combobox.option.element.d.ts.map
|