kr-elements 0.0.1-alpha.3 → 0.0.1-alpha.30
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/dist/cjs/combobox/Boolean.attribute.value.normalizer.js +2 -2
- package/dist/cjs/combobox/Combobox.markup.js +113 -62
- package/dist/cjs/combobox/HTML.combobox.element.js +40 -15
- package/dist/cjs/combobox/HTML.combobox.option.element.js +41 -37
- package/dist/cjs/combobox/HTML.combobox.tag.element.js +0 -3
- package/dist/cjs/index.js +67 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -0
- package/dist/esm/combobox/Boolean.attribute.value.normalizer.js +1 -0
- package/dist/esm/combobox/Combobox.markup.js +113 -62
- package/dist/esm/combobox/HTML.combobox.element.js +26 -3
- package/dist/esm/combobox/HTML.combobox.option.element.js +33 -31
- package/dist/esm/combobox/HTML.combobox.tag.element.js +0 -3
- package/dist/esm/index.js +67 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -0
- package/dist/types/combobox/Boolean.attribute.value.normalizer.d.ts +2 -0
- package/dist/types/combobox/Boolean.attribute.value.normalizer.d.ts.map +1 -0
- package/dist/types/combobox/Combobox.markup.d.ts +6 -1
- package/dist/types/combobox/Combobox.markup.d.ts.map +1 -0
- package/dist/types/combobox/HTML.combobox.element.d.ts +1 -0
- package/dist/types/combobox/HTML.combobox.element.d.ts.map +1 -0
- package/dist/types/combobox/HTML.combobox.option.element.d.ts +1 -0
- package/dist/types/combobox/HTML.combobox.option.element.d.ts.map +1 -0
- package/dist/types/combobox/HTML.combobox.tag.element.d.ts +1 -0
- package/dist/types/combobox/HTML.combobox.tag.element.d.ts.map +1 -0
- package/dist/types/combobox/index.d.ts +1 -0
- package/dist/types/combobox/index.d.ts.map +1 -0
- package/dist/types/index.d.ts +213 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/tsconfig.types.tsbuildinfo +1 -0
- package/dist/types/types.d.ts +198 -0
- package/package.json +14 -14
- package/dist/cjs/Boolean.attribute.value.normalizer.js +0 -11
- package/dist/cjs/Combobox.markup.js +0 -315
- package/dist/cjs/HTML.combobox.element.js +0 -317
- package/dist/cjs/HTML.combobox.option.element.js +0 -95
- package/dist/cjs/HTML.combobox.tag.element.js +0 -22
- package/dist/esm/Boolean.attribute.value.normalizer.js +0 -7
- package/dist/esm/Combobox.markup.js +0 -311
- package/dist/esm/HTML.combobox.element.js +0 -313
- package/dist/esm/HTML.combobox.option.element.js +0 -91
- package/dist/esm/HTML.combobox.tag.element.js +0 -18
- package/dist/types/Boolean.attribute.value.normalizer.d.ts +0 -2
- package/dist/types/Boolean.attribute.value.normalizer.d.ts.map +0 -1
- package/dist/types/Combobox.markup.d.ts +0 -26
- package/dist/types/Combobox.markup.d.ts.map +0 -1
- package/dist/types/HTML.combobox.element.d.ts +0 -50
- package/dist/types/HTML.combobox.element.d.ts.map +0 -1
- package/dist/types/HTML.combobox.option.element.d.ts +0 -15
- package/dist/types/HTML.combobox.option.element.d.ts.map +0 -1
- package/dist/types/HTML.combobox.tag.element.d.ts +0 -4
- package/dist/types/HTML.combobox.tag.element.d.ts.map +0 -1
package/dist/cjs/index.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
/// <reference types="solid-js" />
|
|
3
|
+
/// <reference types="preact" />
|
|
4
|
+
/// <reference types="react" />
|
|
5
|
+
/// <reference types="vue" />
|
|
2
6
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
7
|
if (k2 === undefined) k2 = k;
|
|
4
8
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
@@ -14,4 +18,66 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
18
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
19
|
};
|
|
16
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./
|
|
21
|
+
__exportStar(require("./combobox/index.js"), exports);
|
|
22
|
+
// declare global {
|
|
23
|
+
// namespace React {
|
|
24
|
+
// namespace JSX {
|
|
25
|
+
// interface IntrinsicElements {
|
|
26
|
+
// 'combo-box': React.DetailedHTMLProps<Omit<React.HTMLAttributes<HTMLComboboxElement>, 'defaultValue'>,
|
|
27
|
+
// HTMLComboboxElement
|
|
28
|
+
// > & ComboboxJsxAttributes;
|
|
29
|
+
//
|
|
30
|
+
// 'box-option': React.DetailedHTMLProps<
|
|
31
|
+
// React.HTMLAttributes<HTMLComboboxOptionElement>,
|
|
32
|
+
// HTMLComboboxOptionElement>
|
|
33
|
+
// & ComboboxOptionJsxAttributes;
|
|
34
|
+
//
|
|
35
|
+
// 'box-tag': React.DetailedHTMLProps<React.HTMLAttributes<HTMLComboboxTagElement>, HTMLComboboxTagElement>;
|
|
36
|
+
// }
|
|
37
|
+
// }
|
|
38
|
+
// }
|
|
39
|
+
//
|
|
40
|
+
//
|
|
41
|
+
// namespace preact {
|
|
42
|
+
// namespace JSX {
|
|
43
|
+
// interface IntrinsicElements {
|
|
44
|
+
// 'combo-box': preact.HTMLAttributes<HTMLComboboxElement> & ComboboxJsxAttributes;
|
|
45
|
+
// 'box-option': preact.HTMLAttributes<HTMLComboboxOptionElement> & ComboboxOptionJsxAttributes;
|
|
46
|
+
// 'box-tag': preact.HTMLAttributes<HTMLComboboxTagElement>;
|
|
47
|
+
// }
|
|
48
|
+
// }
|
|
49
|
+
// }
|
|
50
|
+
//
|
|
51
|
+
// }
|
|
52
|
+
//
|
|
53
|
+
// declare module 'preact' {
|
|
54
|
+
// namespace JSX {
|
|
55
|
+
// interface IntrinsicElements {
|
|
56
|
+
// 'combo-box': preact.HTMLAttributes<HTMLComboboxElement> & ComboboxJsxAttributes;
|
|
57
|
+
// 'box-option': preact.HTMLAttributes<HTMLComboboxOptionElement> & ComboboxOptionJsxAttributes;
|
|
58
|
+
// 'box-tag': preact.HTMLAttributes<HTMLComboboxTagElement>;
|
|
59
|
+
// }
|
|
60
|
+
// }
|
|
61
|
+
// }
|
|
62
|
+
//
|
|
63
|
+
// // Solid
|
|
64
|
+
// declare module 'solid-js' {
|
|
65
|
+
// namespace JSX {
|
|
66
|
+
// interface IntrinsicElements {
|
|
67
|
+
// 'combo-box': HTMLAttributes<HTMLComboboxElement> & ComboboxJsxAttributes
|
|
68
|
+
// 'box-option': HTMLAttributes<HTMLComboboxOptionElement> & ComboboxOptionJsxAttributes
|
|
69
|
+
// 'box-tag': HTMLAttributes<HTMLElement>
|
|
70
|
+
// }
|
|
71
|
+
// }
|
|
72
|
+
// }
|
|
73
|
+
//
|
|
74
|
+
//
|
|
75
|
+
// declare global {
|
|
76
|
+
// namespace JSX {
|
|
77
|
+
// interface IntrinsicElements {
|
|
78
|
+
// 'combo-box': ComboboxJsxAttributes;
|
|
79
|
+
// 'box-option': ComboboxOptionJsxAttributes;
|
|
80
|
+
// 'box-tag': HTMLElement;
|
|
81
|
+
// }
|
|
82
|
+
// }
|
|
83
|
+
// }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"root":["../../src/index.ts","../../src/combobox/boolean.attribute.value.normalizer.ts","../../src/combobox/combobox.markup.ts","../../src/combobox/html.combobox.element.ts","../../src/combobox/html.combobox.option.element.ts","../../src/combobox/html.combobox.tag.element.ts","../../src/combobox/index.ts"],"version":"5.9.3"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { HTMLComboboxTagElement } from './HTML.combobox.tag.element.js';
|
|
2
2
|
export class ComboboxMarkup {
|
|
3
|
+
static scrollToTopOptions = { top: 0, behavior: 'smooth' };
|
|
3
4
|
#shadowRoot;
|
|
4
5
|
#internals;
|
|
5
6
|
tagsContainer = null;
|
|
@@ -8,6 +9,8 @@ export class ComboboxMarkup {
|
|
|
8
9
|
dropdown = null;
|
|
9
10
|
placeholder = null;
|
|
10
11
|
searchInput = null;
|
|
12
|
+
tagTemplate = null;
|
|
13
|
+
options;
|
|
11
14
|
connected = false;
|
|
12
15
|
constructor(shadowRoot, internals) {
|
|
13
16
|
this.#shadowRoot = shadowRoot;
|
|
@@ -21,30 +24,37 @@ export class ComboboxMarkup {
|
|
|
21
24
|
connect() {
|
|
22
25
|
const placeholder = this.#shadowRoot.host.getAttribute('placeholder') || '';
|
|
23
26
|
this.tagsContainer = this.#shadowRoot.querySelector('#tags');
|
|
24
|
-
this.optionsContainer = this.#shadowRoot.querySelector('[part
|
|
25
|
-
this.clearAllButton = this.#shadowRoot.querySelector('[part
|
|
27
|
+
this.optionsContainer = this.#shadowRoot.querySelector('[part*="options"]');
|
|
28
|
+
this.clearAllButton = this.#shadowRoot.querySelector('[part*="clear-all-button"]');
|
|
26
29
|
this.dropdown = this.#shadowRoot.querySelector('#dropdown');
|
|
27
30
|
this.placeholder = this.#shadowRoot.querySelector('#placeholder');
|
|
28
31
|
this.placeholder.innerText = placeholder;
|
|
29
|
-
this.searchInput = this.#shadowRoot.querySelector('[part
|
|
30
|
-
this.searchInput.value = this.#shadowRoot.host.getAttribute('
|
|
32
|
+
this.searchInput = this.#shadowRoot.querySelector('[part*="search-input"]');
|
|
33
|
+
this.searchInput.value = this.#shadowRoot.host.getAttribute('query');
|
|
31
34
|
this.searchInput.placeholder = placeholder;
|
|
35
|
+
const innerTemplate = this.#shadowRoot.querySelector('#tag-template');
|
|
36
|
+
const doc = document.importNode(innerTemplate.content, true);
|
|
37
|
+
this.tagTemplate = doc.querySelector('box-tag');
|
|
32
38
|
this.connected = true;
|
|
33
39
|
}
|
|
40
|
+
invalidateOptionsCache() {
|
|
41
|
+
this.options = this.optionsContainer.querySelectorAll('box-option');
|
|
42
|
+
}
|
|
43
|
+
#timer = undefined;
|
|
34
44
|
sort(query) {
|
|
35
|
-
|
|
36
|
-
this
|
|
37
|
-
.
|
|
38
|
-
|
|
39
|
-
option.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
});
|
|
45
|
+
clearTimeout(this.#timer);
|
|
46
|
+
this.#timer = setTimeout(() => {
|
|
47
|
+
const regex = new RegExp(query.trim(), 'i');
|
|
48
|
+
this.options.forEach(option => {
|
|
49
|
+
if (!regex.test(option.textContent)) {
|
|
50
|
+
option.style.display = "none";
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
option.style.display = "flex";
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
this.dropdown.scrollTo(ComboboxMarkup.scrollToTopOptions);
|
|
57
|
+
}, 200);
|
|
48
58
|
}
|
|
49
59
|
disconnect() {
|
|
50
60
|
this.#shadowRoot.host.removeEventListener('focus', this.showDropdown);
|
|
@@ -82,42 +92,75 @@ export class ComboboxMarkup {
|
|
|
82
92
|
try {
|
|
83
93
|
this.setDropdownPosition(this.#shadowRoot.host.getBoundingClientRect());
|
|
84
94
|
this.dropdown.style.display = 'flex';
|
|
95
|
+
// @ts-ignore
|
|
85
96
|
this.dropdown.showPopover();
|
|
86
97
|
this.#internals.ariaExpanded = "true";
|
|
98
|
+
if (this.tagsContainer?.children.length === 0) {
|
|
99
|
+
this.searchInput?.focus();
|
|
100
|
+
}
|
|
101
|
+
this.placeholder.innerText = '';
|
|
87
102
|
}
|
|
88
103
|
catch {
|
|
89
104
|
this.#internals.ariaExpanded = "false";
|
|
90
105
|
}
|
|
91
106
|
};
|
|
92
|
-
|
|
93
|
-
if (event.composedPath().includes(this.#shadowRoot.host))
|
|
94
|
-
return;
|
|
107
|
+
closeDropdown() {
|
|
95
108
|
try {
|
|
109
|
+
// @ts-ignore
|
|
96
110
|
this.dropdown.hidePopover();
|
|
97
111
|
this.dropdown.style.display = 'none';
|
|
98
112
|
this.#internals.ariaExpanded = "false";
|
|
113
|
+
this.placeholder.innerText = this.#shadowRoot.host.getAttribute('placeholder');
|
|
99
114
|
}
|
|
100
|
-
catch {
|
|
115
|
+
catch (e) {
|
|
101
116
|
this.#internals.ariaExpanded = "true";
|
|
102
117
|
}
|
|
118
|
+
}
|
|
119
|
+
hideDropdown = (event) => {
|
|
120
|
+
if (event.composedPath().includes(this.#shadowRoot.host))
|
|
121
|
+
return;
|
|
122
|
+
this.closeDropdown();
|
|
103
123
|
};
|
|
104
124
|
createAndAppendTag(option) {
|
|
105
|
-
const
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
125
|
+
const value = option.value;
|
|
126
|
+
const userTagTemplate = this.#shadowRoot.host.firstElementChild;
|
|
127
|
+
let tag;
|
|
128
|
+
let button;
|
|
129
|
+
if (userTagTemplate && userTagTemplate instanceof HTMLComboboxTagElement) {
|
|
130
|
+
tag = userTagTemplate.cloneNode(true);
|
|
131
|
+
tag.querySelectorAll('[part]')
|
|
132
|
+
.forEach(node => {
|
|
133
|
+
const tokens = Array.from(node.part.values());
|
|
134
|
+
for (const token of tokens) {
|
|
135
|
+
const relatedPart = option.querySelector(`[part*="${token}"]`);
|
|
136
|
+
if (relatedPart) {
|
|
137
|
+
const newNode = relatedPart.cloneNode(true);
|
|
138
|
+
newNode.part.add(...tokens);
|
|
139
|
+
const exportedParts = option.getAttribute('exportparts');
|
|
140
|
+
if (exportedParts) {
|
|
141
|
+
tag.part.add(exportedParts);
|
|
142
|
+
}
|
|
143
|
+
tag.replaceChild(newNode, node);
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
button = tag.querySelector('[part*="tag-clear-button"]');
|
|
149
|
+
if (!button && this.#shadowRoot.host.hasAttribute('multiple')) {
|
|
150
|
+
throw new Error(`A button with part "tag-clear-button"`);
|
|
117
151
|
}
|
|
118
|
-
}
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
const template = this.tagTemplate;
|
|
155
|
+
tag = template.cloneNode(true);
|
|
156
|
+
const label = tag.querySelector('[part="tag-label"]');
|
|
157
|
+
label.textContent = option.label;
|
|
158
|
+
button = tag.querySelector('[part="tag-clear-button"]');
|
|
159
|
+
}
|
|
160
|
+
button.setAttribute('value', value);
|
|
161
|
+
tag.setAttribute('value', value);
|
|
119
162
|
this.tagsContainer.appendChild(tag);
|
|
120
|
-
return
|
|
163
|
+
return button;
|
|
121
164
|
}
|
|
122
165
|
getTagByValue(value) {
|
|
123
166
|
return this.tagsContainer.querySelector(`box-tag[value="${value}"]`);
|
|
@@ -125,15 +168,6 @@ export class ComboboxMarkup {
|
|
|
125
168
|
getOptionByValue(value) {
|
|
126
169
|
return this.optionsContainer.querySelector(`box-option[value="${value}"]`);
|
|
127
170
|
}
|
|
128
|
-
get tagTemplate() {
|
|
129
|
-
let template = this.#shadowRoot.host.firstElementChild;
|
|
130
|
-
if (!template || !(template instanceof HTMLComboboxTagElement)) {
|
|
131
|
-
const innerTemplate = this.#shadowRoot.querySelector('#tag-template');
|
|
132
|
-
const doc = document.importNode(innerTemplate.content, true);
|
|
133
|
-
template = doc.querySelector('box-tag');
|
|
134
|
-
}
|
|
135
|
-
return template;
|
|
136
|
-
}
|
|
137
171
|
get selectedOptions() {
|
|
138
172
|
return this.optionsContainer
|
|
139
173
|
.querySelectorAll('box-option[selected]');
|
|
@@ -162,24 +196,34 @@ export class ComboboxMarkup {
|
|
|
162
196
|
overflow-y: scroll;
|
|
163
197
|
flex-direction: column;
|
|
164
198
|
border-radius: inherit;
|
|
199
|
+
border-color: ButtonFace;
|
|
200
|
+
border-width: inherit;
|
|
165
201
|
}
|
|
166
202
|
|
|
167
203
|
[part="options"] {
|
|
168
204
|
display: flex;
|
|
169
205
|
flex-direction: column;
|
|
206
|
+
justify-content: start;
|
|
170
207
|
gap: 2px;
|
|
171
208
|
padding-block: .5rem;
|
|
172
209
|
border-radius: inherit;
|
|
173
210
|
}
|
|
174
211
|
|
|
175
|
-
|
|
212
|
+
box-option {
|
|
176
213
|
display: flex;
|
|
177
214
|
border-radius: inherit;
|
|
178
215
|
content-visibility: auto;
|
|
216
|
+
cursor: pointer;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
box-option:hover {
|
|
220
|
+
background-color: color-mix(in srgb, Highlight, transparent 70%);
|
|
179
221
|
}
|
|
180
222
|
|
|
181
|
-
|
|
223
|
+
box-option[selected] {
|
|
182
224
|
background-color: Highlight;
|
|
225
|
+
cursor: not-allowed;
|
|
226
|
+
pointer-events: none;
|
|
183
227
|
}
|
|
184
228
|
|
|
185
229
|
[part="search-input"] {
|
|
@@ -190,6 +234,7 @@ export class ComboboxMarkup {
|
|
|
190
234
|
border-radius: inherit;
|
|
191
235
|
border-style: inherit;
|
|
192
236
|
border-width: inherit;
|
|
237
|
+
border-color: inherit;
|
|
193
238
|
padding: inherit;
|
|
194
239
|
}
|
|
195
240
|
|
|
@@ -199,7 +244,11 @@ export class ComboboxMarkup {
|
|
|
199
244
|
}
|
|
200
245
|
|
|
201
246
|
#placeholder {
|
|
247
|
+
text-align: left;
|
|
202
248
|
overflow: hidden;
|
|
249
|
+
padding-inline-start: 2px;
|
|
250
|
+
font-size: smaller;
|
|
251
|
+
color: dimgrey;
|
|
203
252
|
}
|
|
204
253
|
|
|
205
254
|
#tags:not(:empty) + #placeholder {
|
|
@@ -219,10 +268,9 @@ export class ComboboxMarkup {
|
|
|
219
268
|
border-radius: inherit;
|
|
220
269
|
}
|
|
221
270
|
|
|
222
|
-
[part
|
|
271
|
+
[part*="box-tag"] {
|
|
223
272
|
width: 100%;
|
|
224
273
|
justify-self: start;
|
|
225
|
-
font-size: inherit;
|
|
226
274
|
box-sizing: border-box;
|
|
227
275
|
display: flex;
|
|
228
276
|
align-items: center;
|
|
@@ -231,15 +279,17 @@ export class ComboboxMarkup {
|
|
|
231
279
|
padding-inline-end: .2rem;
|
|
232
280
|
background-color: transparent;
|
|
233
281
|
gap: 5px;
|
|
282
|
+
font-size: medium;
|
|
283
|
+
text-transform: uppercase;
|
|
234
284
|
}
|
|
235
285
|
|
|
236
|
-
:host([multiple]) [part
|
|
286
|
+
:host([multiple]) [part*="box-tag"] {
|
|
237
287
|
background-color: Highlight;
|
|
238
288
|
width: fit-content;
|
|
239
289
|
max-width: 100%;
|
|
240
290
|
}
|
|
241
291
|
|
|
242
|
-
[part
|
|
292
|
+
[part*="tag-label"] {
|
|
243
293
|
white-space: nowrap;
|
|
244
294
|
text-overflow: ellipsis;
|
|
245
295
|
overflow: hidden;
|
|
@@ -248,11 +298,11 @@ export class ComboboxMarkup {
|
|
|
248
298
|
flex-grow: 1;
|
|
249
299
|
}
|
|
250
300
|
|
|
251
|
-
:host([multiple]) [part
|
|
301
|
+
:host([multiple]) [part*="tag-label"] {
|
|
252
302
|
flex-grow: unset;
|
|
253
303
|
}
|
|
254
304
|
|
|
255
|
-
[part
|
|
305
|
+
[part*="tag-clear-button"], [part*="clear-all-button"] {
|
|
256
306
|
border-radius: 100%;
|
|
257
307
|
border: none;
|
|
258
308
|
aspect-ratio: 1;
|
|
@@ -262,39 +312,40 @@ export class ComboboxMarkup {
|
|
|
262
312
|
background-color: transparent;
|
|
263
313
|
}
|
|
264
314
|
|
|
265
|
-
[part
|
|
315
|
+
[part*="tag-clear-button"] {
|
|
266
316
|
inline-size: 1em;
|
|
267
317
|
block-size: 1em;
|
|
268
318
|
font-size: 80%;
|
|
269
319
|
display: none;
|
|
270
320
|
}
|
|
271
321
|
|
|
272
|
-
:host([multiple]) [part
|
|
273
|
-
:host([clearable]) [part
|
|
322
|
+
:host([multiple]) [part*="tag-clear-button"],
|
|
323
|
+
:host([clearable]) [part*="tag-clear-button"] {
|
|
274
324
|
display: block;
|
|
275
325
|
}
|
|
276
326
|
|
|
277
|
-
[part
|
|
327
|
+
[part*="clear-all-button"] {
|
|
278
328
|
font-size: inherit;
|
|
279
329
|
inline-size: 1.2em;
|
|
280
330
|
block-size: 1.2em;
|
|
281
331
|
display: none;
|
|
282
332
|
}
|
|
283
333
|
|
|
284
|
-
:host([multiple]) [part
|
|
334
|
+
:host([multiple]) [part*="clear-all-button"] {
|
|
285
335
|
display: block;
|
|
286
336
|
}
|
|
287
337
|
|
|
288
|
-
[part
|
|
289
|
-
[part
|
|
338
|
+
[part*="clear-all-button"]:hover,
|
|
339
|
+
[part*="tag-clear-button"]:hover {
|
|
290
340
|
color: ActiveText;
|
|
341
|
+
cursor: pointer;
|
|
291
342
|
}
|
|
292
343
|
|
|
293
|
-
[part
|
|
344
|
+
[part*="clear-all-button"]:hover {
|
|
294
345
|
background-color: ButtonFace;
|
|
295
346
|
}
|
|
296
347
|
|
|
297
|
-
:host:has(#tags:empty) [part
|
|
348
|
+
:host:has(#tags:empty) [part*="clear-all-button"] {
|
|
298
349
|
pointer-events: none;
|
|
299
350
|
color: darkgrey;
|
|
300
351
|
}
|
|
@@ -302,7 +353,7 @@ export class ComboboxMarkup {
|
|
|
302
353
|
</style>
|
|
303
354
|
|
|
304
355
|
<div id="tags"></div>
|
|
305
|
-
<div id="placeholder"
|
|
356
|
+
<div id="placeholder"> </div>
|
|
306
357
|
<button part="clear-all-button">✕</button>
|
|
307
358
|
<div id="dropdown" popover="manual">
|
|
308
359
|
<input name="search-input" part="search-input" />
|
|
@@ -22,6 +22,7 @@ export class HTMLComboboxElement extends HTMLElement {
|
|
|
22
22
|
this.shadowRoot.adoptedStyleSheets = HTMLComboboxElement.styleSheet;
|
|
23
23
|
this.#observer = new MutationObserver(this.#onOptionsChanges);
|
|
24
24
|
}
|
|
25
|
+
// Lifecycle callbacks
|
|
25
26
|
connectedCallback() {
|
|
26
27
|
this.#markup.connect();
|
|
27
28
|
this.#initialAttributesSynchronization();
|
|
@@ -44,6 +45,8 @@ export class HTMLComboboxElement extends HTMLElement {
|
|
|
44
45
|
formDisabledCallback(isDisabled) {
|
|
45
46
|
this.disabled = isDisabled;
|
|
46
47
|
}
|
|
48
|
+
// Instance properties
|
|
49
|
+
// readonly
|
|
47
50
|
get valueAsArray() {
|
|
48
51
|
return Array.from(this.#values);
|
|
49
52
|
}
|
|
@@ -56,6 +59,7 @@ export class HTMLComboboxElement extends HTMLElement {
|
|
|
56
59
|
get willValidate() {
|
|
57
60
|
return this.internals.willValidate;
|
|
58
61
|
}
|
|
62
|
+
// configurable
|
|
59
63
|
get value() {
|
|
60
64
|
return this.valueAsArray.join(',');
|
|
61
65
|
}
|
|
@@ -151,6 +155,7 @@ export class HTMLComboboxElement extends HTMLElement {
|
|
|
151
155
|
this.#markup.searchInput.placeholder = value;
|
|
152
156
|
}
|
|
153
157
|
}
|
|
158
|
+
// Instance methods
|
|
154
159
|
setAttribute(name, value) {
|
|
155
160
|
if (HTMLComboboxElement.booleanAttributes.has(name)) {
|
|
156
161
|
Reflect.set(this, name, toBoolean(value));
|
|
@@ -186,6 +191,7 @@ export class HTMLComboboxElement extends HTMLElement {
|
|
|
186
191
|
this.internals.setValidity({ customError: true }, message);
|
|
187
192
|
}
|
|
188
193
|
}
|
|
194
|
+
// Internal
|
|
189
195
|
#onInput = (event) => {
|
|
190
196
|
if (this.filterable) {
|
|
191
197
|
if (event.target && event.target instanceof HTMLInputElement) {
|
|
@@ -212,6 +218,7 @@ export class HTMLComboboxElement extends HTMLElement {
|
|
|
212
218
|
}
|
|
213
219
|
});
|
|
214
220
|
});
|
|
221
|
+
this.#markup.invalidateOptionsCache();
|
|
215
222
|
this.#setValidityAndFormValue();
|
|
216
223
|
};
|
|
217
224
|
#selectOption(option) {
|
|
@@ -222,6 +229,9 @@ export class HTMLComboboxElement extends HTMLElement {
|
|
|
222
229
|
option.toggleAttribute('selected', true);
|
|
223
230
|
const control = this.#markup.createAndAppendTag(option);
|
|
224
231
|
control.addEventListener('click', this.#onClickTagClearButton);
|
|
232
|
+
if (!this.multiple) {
|
|
233
|
+
this.#markup.closeDropdown();
|
|
234
|
+
}
|
|
225
235
|
}
|
|
226
236
|
#onSelectOption = (event) => {
|
|
227
237
|
let option;
|
|
@@ -243,18 +253,31 @@ export class HTMLComboboxElement extends HTMLElement {
|
|
|
243
253
|
this.#values.clear();
|
|
244
254
|
this.#markup.tagsContainer.replaceChildren();
|
|
245
255
|
}
|
|
256
|
+
if (!this.multiple) {
|
|
257
|
+
event.stopPropagation();
|
|
258
|
+
}
|
|
246
259
|
this.#selectOption(option);
|
|
247
260
|
this.#setValidityAndFormValue();
|
|
248
261
|
this.dispatchEvent(new Event('change'));
|
|
249
262
|
}
|
|
250
263
|
};
|
|
251
264
|
#onClickTagClearButton = (event) => {
|
|
252
|
-
|
|
253
|
-
|
|
265
|
+
let button;
|
|
266
|
+
if (event.target instanceof HTMLButtonElement) {
|
|
267
|
+
button = event.target;
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
button = event.composedPath()
|
|
271
|
+
.find(el => {
|
|
272
|
+
return el instanceof HTMLElement && el.part.contains('tag-clear-button');
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
if (button) {
|
|
276
|
+
const value = button.value;
|
|
254
277
|
const option = this.#markup.getOptionByValue(value);
|
|
255
278
|
const tag = this.#markup.getTagByValue(value);
|
|
256
279
|
option.removeAttribute('selected');
|
|
257
|
-
this.#values.delete(
|
|
280
|
+
this.#values.delete(value);
|
|
258
281
|
tag.remove();
|
|
259
282
|
this.#setValidityAndFormValue();
|
|
260
283
|
this.dispatchEvent(new Event('change'));
|
|
@@ -7,23 +7,47 @@ export class HTMLComboboxOptionElement extends HTMLElement {
|
|
|
7
7
|
super.setAttribute('part', 'box-option');
|
|
8
8
|
super.setAttribute('tabindex', "0");
|
|
9
9
|
super.setAttribute('role', "option");
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
const value = this.value;
|
|
11
|
+
const label = this.label;
|
|
12
|
+
const hasNoMarkup = this.children.length === 0;
|
|
13
|
+
const text = this.textContent.trim();
|
|
14
|
+
const hasText = text.length > 0;
|
|
15
|
+
if (!value && !label) {
|
|
16
|
+
if (hasNoMarkup && hasText) {
|
|
17
|
+
this.value = text;
|
|
18
|
+
this.label = text;
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
throw new Error('box-option must have value and label attributes');
|
|
22
|
+
}
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (value && !label) {
|
|
26
|
+
if (hasNoMarkup && hasText) {
|
|
27
|
+
this.label = text;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
this.label = value;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (!value && label) {
|
|
34
|
+
this.value = label;
|
|
35
|
+
}
|
|
36
|
+
if (hasNoMarkup && !hasText) {
|
|
37
|
+
this.textContent = label || value;
|
|
12
38
|
}
|
|
13
39
|
}
|
|
14
40
|
get value() {
|
|
15
41
|
return this.getAttribute('value');
|
|
16
42
|
}
|
|
17
43
|
set value(value) {
|
|
18
|
-
|
|
19
|
-
super.setAttribute('value', value);
|
|
44
|
+
super.setAttribute('value', String(value));
|
|
20
45
|
}
|
|
21
46
|
get label() {
|
|
22
|
-
return this.getAttribute('
|
|
47
|
+
return this.getAttribute('label');
|
|
23
48
|
}
|
|
24
49
|
set label(value) {
|
|
25
|
-
|
|
26
|
-
super.setAttribute('label', value);
|
|
50
|
+
super.setAttribute('label', String(value));
|
|
27
51
|
}
|
|
28
52
|
get selected() {
|
|
29
53
|
return this.hasAttribute('selected');
|
|
@@ -37,32 +61,10 @@ export class HTMLComboboxOptionElement extends HTMLElement {
|
|
|
37
61
|
Reflect.set(this, key, value);
|
|
38
62
|
}
|
|
39
63
|
for (const key of HTMLComboboxOptionElement.stringAttributes) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
get #optionHasOnlyTextNode() {
|
|
44
|
-
return this.children.length === 0 && this.textContent.length > 0;
|
|
45
|
-
}
|
|
46
|
-
#getOrExtractValue(value, name) {
|
|
47
|
-
if (typeof value === 'string')
|
|
48
|
-
return value;
|
|
49
|
-
if (value == null) {
|
|
50
|
-
const opposite = name === 'label' ? 'value' : 'label';
|
|
51
|
-
const oppositeValue = this.getAttribute(opposite);
|
|
52
|
-
if (oppositeValue) {
|
|
53
|
-
value = oppositeValue;
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
if (this.#optionHasOnlyTextNode) {
|
|
57
|
-
value = this.textContent;
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
60
|
-
throw new TypeError('No value, or label or textContent found');
|
|
61
|
-
}
|
|
64
|
+
if (this.hasAttribute(key)) {
|
|
65
|
+
Reflect.set(this, key, this.getAttribute(key));
|
|
62
66
|
}
|
|
63
|
-
return value;
|
|
64
67
|
}
|
|
65
|
-
throw new TypeError('Invalid value');
|
|
66
68
|
}
|
|
67
69
|
setAttribute(name, value) {
|
|
68
70
|
if (HTMLComboboxOptionElement.booleanAttributes.has(name)) {
|
|
@@ -7,9 +7,6 @@ export class HTMLComboboxTagElement extends HTMLElement {
|
|
|
7
7
|
throw new Error(`A <button> with part="tag-clear-button" is required for <combo-box> with multiple attribute`);
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
|
-
if (!this.querySelector('[part="tag-label"]')) {
|
|
11
|
-
throw new Error(`Invalid <box-tag-template>, an element with part="tag-label" is required as a descendant`);
|
|
12
|
-
}
|
|
13
10
|
}
|
|
14
11
|
}
|
|
15
12
|
}
|