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.
Files changed (51) hide show
  1. package/dist/cjs/combobox/Boolean.attribute.value.normalizer.js +2 -2
  2. package/dist/cjs/combobox/Combobox.markup.js +113 -62
  3. package/dist/cjs/combobox/HTML.combobox.element.js +40 -15
  4. package/dist/cjs/combobox/HTML.combobox.option.element.js +41 -37
  5. package/dist/cjs/combobox/HTML.combobox.tag.element.js +0 -3
  6. package/dist/cjs/index.js +67 -1
  7. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -0
  8. package/dist/esm/combobox/Boolean.attribute.value.normalizer.js +1 -0
  9. package/dist/esm/combobox/Combobox.markup.js +113 -62
  10. package/dist/esm/combobox/HTML.combobox.element.js +26 -3
  11. package/dist/esm/combobox/HTML.combobox.option.element.js +33 -31
  12. package/dist/esm/combobox/HTML.combobox.tag.element.js +0 -3
  13. package/dist/esm/index.js +67 -1
  14. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -0
  15. package/dist/types/combobox/Boolean.attribute.value.normalizer.d.ts +2 -0
  16. package/dist/types/combobox/Boolean.attribute.value.normalizer.d.ts.map +1 -0
  17. package/dist/types/combobox/Combobox.markup.d.ts +6 -1
  18. package/dist/types/combobox/Combobox.markup.d.ts.map +1 -0
  19. package/dist/types/combobox/HTML.combobox.element.d.ts +1 -0
  20. package/dist/types/combobox/HTML.combobox.element.d.ts.map +1 -0
  21. package/dist/types/combobox/HTML.combobox.option.element.d.ts +1 -0
  22. package/dist/types/combobox/HTML.combobox.option.element.d.ts.map +1 -0
  23. package/dist/types/combobox/HTML.combobox.tag.element.d.ts +1 -0
  24. package/dist/types/combobox/HTML.combobox.tag.element.d.ts.map +1 -0
  25. package/dist/types/combobox/index.d.ts +1 -0
  26. package/dist/types/combobox/index.d.ts.map +1 -0
  27. package/dist/types/index.d.ts +213 -1
  28. package/dist/types/index.d.ts.map +1 -1
  29. package/dist/types/tsconfig.types.tsbuildinfo +1 -0
  30. package/dist/types/types.d.ts +198 -0
  31. package/package.json +14 -14
  32. package/dist/cjs/Boolean.attribute.value.normalizer.js +0 -11
  33. package/dist/cjs/Combobox.markup.js +0 -315
  34. package/dist/cjs/HTML.combobox.element.js +0 -317
  35. package/dist/cjs/HTML.combobox.option.element.js +0 -95
  36. package/dist/cjs/HTML.combobox.tag.element.js +0 -22
  37. package/dist/esm/Boolean.attribute.value.normalizer.js +0 -7
  38. package/dist/esm/Combobox.markup.js +0 -311
  39. package/dist/esm/HTML.combobox.element.js +0 -313
  40. package/dist/esm/HTML.combobox.option.element.js +0 -91
  41. package/dist/esm/HTML.combobox.tag.element.js +0 -18
  42. package/dist/types/Boolean.attribute.value.normalizer.d.ts +0 -2
  43. package/dist/types/Boolean.attribute.value.normalizer.d.ts.map +0 -1
  44. package/dist/types/Combobox.markup.d.ts +0 -26
  45. package/dist/types/Combobox.markup.d.ts.map +0 -1
  46. package/dist/types/HTML.combobox.element.d.ts +0 -50
  47. package/dist/types/HTML.combobox.element.d.ts.map +0 -1
  48. package/dist/types/HTML.combobox.option.element.d.ts +0 -15
  49. package/dist/types/HTML.combobox.option.element.d.ts.map +0 -1
  50. package/dist/types/HTML.combobox.tag.element.d.ts +0 -4
  51. 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("./HTML.combobox.element.js"), exports);
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,3 +1,4 @@
1
+ /** Transform invalid (nullish or stringed) values to correct boolean value */
1
2
  export function toBoolean(value) {
2
3
  if (value == null || value === 'false' || value === false)
3
4
  value = false;
@@ -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="options"]');
25
- this.clearAllButton = this.#shadowRoot.querySelector('[part="clear-all-button"]');
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="search-input"]');
30
- this.searchInput.value = this.#shadowRoot.host.getAttribute('value');
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
- const regex = new RegExp(query, 'i');
36
- this.optionsContainer.querySelectorAll('box-option')
37
- .forEach(option => {
38
- if (query === '') {
39
- option.style.display = "initial";
40
- }
41
- else if (!regex.test(option.label)) {
42
- option.style.display = "none";
43
- }
44
- else {
45
- option.style.order = "initial";
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
- hideDropdown = (event) => {
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 template = this.tagTemplate;
106
- const tag = template.cloneNode(true);
107
- const label = tag.querySelector('[part="tag-label"]');
108
- label.textContent = option.label;
109
- const clearButton = tag.querySelector('[part="tag-clear-button"]');
110
- clearButton.setAttribute('value', option.value);
111
- tag.setAttribute('value', option.value);
112
- option.querySelectorAll('[part]')
113
- .forEach(node => {
114
- const relatedPart = tag.querySelector(`[part=${node.getAttribute('part')}]`);
115
- if (relatedPart) {
116
- tag.replaceChild(node.cloneNode(true), relatedPart);
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 clearButton;
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
- [part="options"] box-option {
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
- [part="options"] box-option[selected] {
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="box-tag"] {
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="box-tag"] {
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="tag-label"] {
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="tag-label"] {
301
+ :host([multiple]) [part*="tag-label"] {
252
302
  flex-grow: unset;
253
303
  }
254
304
 
255
- [part="tag-clear-button"], [part="clear-all-button"] {
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="tag-clear-button"] {
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="tag-clear-button"],
273
- :host([clearable]) [part="tag-clear-button"] {
322
+ :host([multiple]) [part*="tag-clear-button"],
323
+ :host([clearable]) [part*="tag-clear-button"] {
274
324
  display: block;
275
325
  }
276
326
 
277
- [part="clear-all-button"] {
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="clear-all-button"] {
334
+ :host([multiple]) [part*="clear-all-button"] {
285
335
  display: block;
286
336
  }
287
337
 
288
- [part="clear-all-button"]:hover,
289
- [part="tag-clear-button"]:hover {
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="clear-all-button"]:hover {
344
+ [part*="clear-all-button"]:hover {
294
345
  background-color: ButtonFace;
295
346
  }
296
347
 
297
- :host:has(#tags:empty) [part="clear-all-button"] {
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" ></div>
356
+ <div id="placeholder">&nbsp;</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
- if (event.target && event.target instanceof HTMLButtonElement) {
253
- const value = event.target.value;
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(event.target.value);
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
- if (this.children.length === 0) {
11
- this.textContent = this.label;
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
- value = this.#getOrExtractValue(value, 'value');
19
- super.setAttribute('value', value);
44
+ super.setAttribute('value', String(value));
20
45
  }
21
46
  get label() {
22
- return this.getAttribute('value');
47
+ return this.getAttribute('label');
23
48
  }
24
49
  set label(value) {
25
- value = this.#getOrExtractValue(value, 'label');
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
- Reflect.set(this, key, this.getAttribute(key));
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
  }