@oscarpalmer/oui 0.32.0 → 0.33.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.
@@ -161,8 +161,30 @@ var OuiComboboxElement = class extends HTMLElement {
161
161
  this.clear();
162
162
  }
163
163
  };
164
- function createAnchor(type, id, label, select, autocomplete, multiple) {
165
- const [anchor, selection] = type === "autocomplete" ? [createInput(id, select)] : createBox(id, select, multiple);
164
+ function filterOptions(state, options, clear) {
165
+ if (clear || state.autocomplete.none) state.filter = "";
166
+ const filter = state.filter.toLocaleLowerCase();
167
+ const all = options ?? getOptions(state)[0];
168
+ const { length } = all;
169
+ const visible = [];
170
+ for (let index = 0; index < length; index += 1) {
171
+ const option = all[index];
172
+ option.hidden = option.textContent?.toLocaleLowerCase().includes(filter) === false;
173
+ if (!option.hidden) visible.push(option);
174
+ }
175
+ state.empty.hidden = visible.length > 0;
176
+ if (state.empty.hidden) return state.active != null && visible.includes(state.active) ? state.active : visible[0];
177
+ }
178
+ function generateIds(select, prefix) {
179
+ const options = [...select.options];
180
+ const { length } = options;
181
+ for (let index = 0; index < length; index += 1) {
182
+ const option = options[index];
183
+ if (option.id === "") option.id = `${prefix}_option_${index}`;
184
+ }
185
+ }
186
+ function getAnchor(type, id, label, select, autocomplete, multiple) {
187
+ const anchor = type === "autocomplete" ? getInput(id, select) : getBox(id, select, multiple);
166
188
  setAria(anchor, {
167
189
  autocomplete: type === "autocomplete" ? autocomplete.value : void 0,
168
190
  controls: `${id}_content`,
@@ -172,186 +194,80 @@ function createAnchor(type, id, label, select, autocomplete, multiple) {
172
194
  });
173
195
  setAttribute(anchor, ATTRIBUTE_ANCHOR, "");
174
196
  setRole(anchor, "combobox");
175
- return [anchor, selection];
197
+ return anchor;
198
+ }
199
+ function getAttributeValue(element, attribute, possible, defaultValue) {
200
+ const value = getAttribute(element, attribute);
201
+ return possible.has(value) ? value : defaultValue;
202
+ }
203
+ function getAutoComplete(element, select) {
204
+ let value;
205
+ if (select) value = "list";
206
+ else value = getAttributeValue(element, "autocomplete", AUTOCOMPLETES, "none");
207
+ return {
208
+ value,
209
+ both: value === "both",
210
+ inline: value === "inline",
211
+ list: value === "list",
212
+ none: value === "none"
213
+ };
176
214
  }
177
- function createBox(id, select, multiple) {
215
+ function getBox(id, select, multiple) {
178
216
  const box = createElement("div", {
179
217
  id,
180
218
  tabIndex: 0,
181
219
  textContent: multiple ? "" : select.selectedOptions[0]?.textContent ?? ""
182
220
  });
183
- let selection;
184
221
  if (multiple) {
185
- selection = createSelection(select);
222
+ const selection = createElement("ul", {}, { [ATTRIBUTE_SELECTION]: "" });
223
+ for (const option of select.selectedOptions) selection.append(getSelectionItem(option));
186
224
  box.append(selection);
187
225
  }
188
- return [box, selection];
226
+ return box;
189
227
  }
190
- function createContent(type, id, label, select, autocomplete, multiple) {
228
+ function getContent(id, label, select, autocomplete, multiple) {
191
229
  const content = createElement("div", { id: `${id}_content` }, {
192
230
  [ATTRIBUTE_CONTENT]: "",
193
231
  popover: "",
194
232
  tabindex: -1,
195
233
  "aria-labelledby": label.id
196
234
  });
197
- const [filter, input] = createFilter(type, id) ?? [];
198
- const [list, headers, selected] = createList(label, select, autocomplete, multiple);
199
- const empty = createEmpty();
200
- if (filter != null) content.append(filter);
201
- content.append(list, empty);
202
- return [
203
- content,
204
- input,
205
- headers,
206
- selected,
207
- empty
208
- ];
209
- }
210
- function createEmpty() {
211
- return createElement("div", {
212
- hidden: true,
213
- role: "status",
214
- textContent: "No results found"
215
- }, {
216
- [ATTRIBUTE_EMPTY]: "",
217
- "aria-live": "polite"
218
- });
219
- }
220
- function createFilter(type, id) {
221
- if (type === "autocomplete") return;
222
- const container = createElement("div", {}, { [ATTRIBUTE_FILTER]: "" });
223
- const label = createElement("label", {
224
- htmlFor: `${id}_filter`,
225
- textContent: "Filter options"
226
- }, { [ATTRIBUTE_FILTER_LABEL]: "" });
227
- const filter = createElement("input", { id: `${id}_filter` }, { [ATTRIBUTE_FILTER_INPUT]: "" });
228
- container.append(label, filter);
229
- return [container, filter];
230
- }
231
- function createHeader(group) {
232
- return createElement("li", {
233
- id: group.id,
234
- role: "presentation",
235
- textContent: group.label
236
- }, { [ATTRIBUTE_HEADER]: "" });
237
- }
238
- function createInput(id, select) {
239
- return createElement("input", {
240
- id,
241
- value: select.selectedOptions[0]?.textContent ?? ""
242
- }, {
243
- [ATTRIBUTE_ANCHOR]: "",
244
- autocomplete: "off",
245
- type: "text"
246
- });
247
- }
248
- function createList(label, select, autocomplete, multiple) {
249
235
  const list = createElement("ul", { role: "listbox" }, {
250
236
  [ATTRIBUTE_LIST]: "",
251
237
  "aria-labelledby": label.id,
252
238
  "aria-multiselectable": multiple
253
239
  });
254
- const groups = [...select.querySelectorAll("optgroup")];
255
- const groupsLength = groups.length;
240
+ const options = [...select.options];
256
241
  const selected = [];
257
- let headers;
258
- if (groupsLength > 0) {
259
- headers = [];
260
- let firstGroup;
261
- for (let groupIndex = 0; groupIndex < groupsLength; groupIndex += 1) {
262
- const group = groups[groupIndex];
263
- const options = [...group.querySelectorAll("option")];
264
- if (options.length === 0) continue;
265
- firstGroup ??= groupIndex;
266
- const header = createHeader(group);
267
- headers.push(header);
268
- list.append(header);
269
- createOptions(options, autocomplete, list, selected, groupIndex === firstGroup, header.id);
270
- }
271
- } else createOptions([...select.options], autocomplete, list, selected, true);
272
- return [
273
- list,
274
- headers,
275
- selected
276
- ];
277
- }
278
- function createOption(index, option, autocomplete, active, group) {
279
- return createElement("li", {
280
- id: option.id,
281
- role: "option",
282
- textContent: option.textContent
283
- }, {
284
- group,
285
- [ATTRIBUTE_OPTION]: "",
286
- [ATTRIBUTE_OPTION_ACTIVE]: active && index === 0 ? "" : void 0,
287
- value: option.value,
288
- "aria-selected": !autocomplete.none && option.selected
289
- });
290
- }
291
- function createOptions(options, autocomplete, list, selected, active, group) {
292
242
  for (let index = 0; index < options.length; index += 1) {
293
243
  const option = options[index];
294
- const item = createOption(index, option, autocomplete, active, group);
244
+ const item = createElement("li", {
245
+ id: option.id,
246
+ textContent: option.textContent
247
+ }, {
248
+ [ATTRIBUTE_OPTION]: "",
249
+ [ATTRIBUTE_OPTION_ACTIVE]: index === 0 ? "" : void 0,
250
+ role: "option",
251
+ value: option.value,
252
+ "aria-selected": !autocomplete.none && option.selected
253
+ });
295
254
  if (option.selected) selected.push(item);
296
255
  list.append(item);
297
256
  }
298
- }
299
- function createSelection(select) {
300
- const selection = createElement("ul", {}, { [ATTRIBUTE_SELECTION]: "" });
301
- for (const option of select.selectedOptions) selection.append(getSelectionItem(option));
302
- return selection;
303
- }
304
- function filterOptions(state, options, clear) {
305
- if (clear || state.autocomplete.none) state.filter.value = "";
306
- const filter = state.filter.value.toLocaleLowerCase();
307
- const groups = state.headers?.reduce((record, header) => {
308
- record[header.id] = 0;
309
- return record;
310
- }, {}) ?? {};
311
- const all = options ?? getOptions(state)[0];
312
- let { length } = all;
313
- const visible = [];
314
- for (let index = 0; index < length; index += 1) {
315
- const option = all[index];
316
- option.hidden = option.textContent?.toLocaleLowerCase().includes(filter) === false;
317
- if (!option.hidden) {
318
- groups[getAttribute(option, "group") ?? ""] += 1;
319
- visible.push(option);
320
- }
321
- }
322
- if (state.headers != null) {
323
- length = state.headers.length;
324
- for (let index = 0; index < length; index += 1) {
325
- const header = state.headers[index];
326
- header.hidden = groups[header.id] === 0;
327
- }
328
- }
329
- state.empty.hidden = visible.length > 0;
330
- if (state.empty.hidden) return state.active != null && visible.includes(state.active) ? state.active : visible[0];
331
- }
332
- function generateIds(select, prefix) {
333
- const elements = [...select.querySelectorAll("optgroup"), ...select.options];
334
- const { length } = elements;
335
- for (let index = 0; index < length; index += 1) {
336
- const element = elements[index];
337
- if (element.id === "") element.id = `${prefix}${element instanceof HTMLOptGroupElement ? "_group_" : "_option_"}${index}`;
338
- }
339
- }
340
- function getAttributeValue(element, attribute, possible, defaultValue) {
341
- const value = getAttribute(element, attribute);
342
- return possible.has(value) ? value : defaultValue;
343
- }
344
- function getAutoComplete(element, select) {
345
- let value;
346
- if (select) value = "list";
347
- else value = getAttributeValue(element, "autocomplete", AUTOCOMPLETES, "none");
348
- return {
349
- value,
350
- both: value === "both",
351
- inline: value === "inline",
352
- list: value === "list",
353
- none: value === "none"
354
- };
257
+ const empty = createElement("div", {
258
+ hidden: true,
259
+ role: "status",
260
+ textContent: "No results found"
261
+ }, {
262
+ [ATTRIBUTE_EMPTY]: "",
263
+ "aria-live": "polite"
264
+ });
265
+ content.append(list, empty);
266
+ return [
267
+ content,
268
+ selected,
269
+ empty
270
+ ];
355
271
  }
356
272
  function getFormValue(state, values) {
357
273
  if (values.length === 0 || !state.multiple) return values[0];
@@ -371,6 +287,16 @@ function getId() {
371
287
  index += 1;
372
288
  return `oui_combobox_${index}`;
373
289
  }
290
+ function getInput(id, select) {
291
+ return createElement("input", {
292
+ id,
293
+ value: select.selectedOptions[0]?.textContent ?? ""
294
+ }, {
295
+ [ATTRIBUTE_ANCHOR]: "",
296
+ autocomplete: "off",
297
+ type: "text"
298
+ });
299
+ }
374
300
  function getOption(state, value) {
375
301
  const [all] = getOptions(state);
376
302
  const valueAsString = getString(value).toLocaleLowerCase();
@@ -429,8 +355,8 @@ function getState(element, internals) {
429
355
  label.htmlFor = id;
430
356
  label.id = `${id}_label`;
431
357
  generateIds(select, id);
432
- const [anchor, selection] = createAnchor(type, id, label, select, autocomplete, multiple);
433
- const [content, filter, headers, selected, empty] = createContent(type, id, label, select, autocomplete, multiple);
358
+ const anchor = getAnchor(type, id, label, select, autocomplete, multiple);
359
+ const [content, selected, empty] = getContent(id, label, select, autocomplete, multiple);
434
360
  const values = selected.length === 0 ? [""] : selected.map((option) => getAttribute(option, "value") ?? "");
435
361
  const floatable = createEmbeddedFloatable(anchor, content, {
436
362
  position: "below-start",
@@ -447,7 +373,6 @@ function getState(element, internals) {
447
373
  disabled,
448
374
  empty,
449
375
  floatable,
450
- headers,
451
376
  id,
452
377
  internals,
453
378
  label,
@@ -456,19 +381,14 @@ function getState(element, internals) {
456
381
  required,
457
382
  select,
458
383
  selected,
459
- selection,
460
384
  type,
461
385
  values,
462
- active: selected[0],
463
- filter: {
464
- element: filter,
465
- value: ""
466
- },
386
+ filter: "",
467
387
  listeners: [],
468
388
  name: element.name,
469
389
  touched: false
470
390
  };
471
- state.listeners.push(on(element, "focusout", () => {
391
+ state.listeners.push(on(anchor, "blur", () => {
472
392
  state.touched = true;
473
393
  updateValidity(state);
474
394
  }), on(content, "click", (event) => {
@@ -478,21 +398,84 @@ function getState(element, internals) {
478
398
  activedescendant: floatable.open ? state.active?.id : void 0,
479
399
  expanded: floatable.open
480
400
  });
481
- if (floatable.open) {
482
- state.filter.element?.focus();
483
- updateWidth(state);
484
- }
401
+ if (floatable.open) updateWidth(state);
485
402
  }));
486
403
  setDisabled(element, state, disabled);
487
- setLimit("max", element, state, getAttribute(element, "max"));
488
- setLimit("min", element, state, getAttribute(element, "min"));
489
- setReadonly(element, state, readonly);
490
- setRequired(element, state, required);
491
404
  return state;
492
405
  }
493
406
  function hasTextSelection(element) {
494
407
  return element instanceof HTMLInputElement && element.selectionStart !== element.selectionEnd;
495
408
  }
409
+ function onAnchor(event, handler) {
410
+ if (event.ctrlKey || event.metaKey || event.shiftKey) return;
411
+ const combobox = findAncestor(event, TAGNAME);
412
+ const state = states.get(combobox);
413
+ if (combobox == null || state == null) return;
414
+ if (findAncestor(event, SELECTOR_ANCHOR) != null) handler(event, combobox, state);
415
+ }
416
+ function onAnchorKeydown(event, combobox, state) {
417
+ const isSelect = state.type === "select";
418
+ let preventDefault = true;
419
+ switch (true) {
420
+ case event.key === " " && state.type === "select":
421
+ case event.key === "Enter":
422
+ onEnter(combobox, state);
423
+ break;
424
+ case event.key === "Escape":
425
+ onEscape(state);
426
+ break;
427
+ case isSelect && KEYS_ABSOLUTE.has(event.key):
428
+ onNavigation(event, combobox, state);
429
+ break;
430
+ case !isSelect && KEYS_SELECTION.has(event.key):
431
+ preventDefault = false;
432
+ if (hasTextSelection(state.anchor)) onSelection(combobox, state);
433
+ break;
434
+ case KEYS_NAVIGATION.has(event.key):
435
+ onNavigation(event, combobox, state);
436
+ break;
437
+ default:
438
+ preventDefault = false;
439
+ if (event.key === "Tab") onTab(combobox, state);
440
+ break;
441
+ }
442
+ if (preventDefault) {
443
+ event.preventDefault();
444
+ event.stopPropagation();
445
+ }
446
+ }
447
+ function onAnchorKeyup(event, combobox, state) {
448
+ const typed = event.key.length === 1 && EXPRESSION_TYPED.test(event.key);
449
+ if (state.type === "select") {
450
+ if (typed) {
451
+ event.preventDefault();
452
+ event.stopPropagation();
453
+ onScrollTo(event, combobox, state);
454
+ }
455
+ return;
456
+ }
457
+ let preventDefault = true;
458
+ if (typed) state.filter += event.key;
459
+ if (state.anchor instanceof HTMLInputElement && (KEYS_REMOVE.has(event.key) || state.anchor.value.length < state.filter.length)) {
460
+ state.filter = state.anchor.value;
461
+ filterOptions(state);
462
+ }
463
+ switch (true) {
464
+ case typed:
465
+ onTyped(combobox, state);
466
+ break;
467
+ case KEYS_REMOVE.has(event.key):
468
+ onRemove(combobox, state);
469
+ break;
470
+ default:
471
+ preventDefault = false;
472
+ break;
473
+ }
474
+ if (preventDefault) {
475
+ event.preventDefault();
476
+ event.stopPropagation();
477
+ }
478
+ }
496
479
  function onClick(event) {
497
480
  const combobox = findAncestor(event, TAGNAME);
498
481
  const state = states.get(combobox);
@@ -521,7 +504,7 @@ function onContent(event, content) {
521
504
  if (combobox == null || state == null) return;
522
505
  const option = findAncestor(event, SELECTOR_OPTION);
523
506
  if (option instanceof HTMLElement) setNext("set", combobox, state, getOptions(state)[0], option);
524
- (state.filter.element ?? state.anchor).focus();
507
+ state.anchor.focus();
525
508
  }
526
509
  function onEnter(combobox, state) {
527
510
  if (hasTextSelection(state.anchor)) {
@@ -537,58 +520,11 @@ function onEnter(combobox, state) {
537
520
  function onEscape(state) {
538
521
  state.floatable.hide();
539
522
  }
540
- function onInput(event, handler) {
541
- if (event.ctrlKey || event.metaKey) return;
542
- const combobox = findAncestor(event, TAGNAME);
543
- const state = states.get(combobox);
544
- if (combobox == null || state == null) return;
545
- if ((findAncestor(event, SELECTOR_ANCHOR) ?? findAncestor(event, SELECTOR_FILTER)) != null) handler(event, combobox, state);
546
- }
547
- function onInputKeydown(event, combobox, state) {
548
- const isSelect = state.type === "select";
549
- let preventDefault = true;
550
- switch (true) {
551
- case event.key === "Enter":
552
- onEnter(combobox, state);
553
- break;
554
- case event.key === "Escape":
555
- onEscape(state);
556
- break;
557
- case !isSelect && KEYS_SELECTION.has(event.key):
558
- preventDefault = false;
559
- if (hasTextSelection(state.anchor)) onSelection(combobox, state);
560
- break;
561
- case KEYS_NAVIGATION.has(event.key):
562
- onNavigation(event, combobox, state);
563
- break;
564
- default:
565
- preventDefault = false;
566
- if (event.key === "Tab") onTab(combobox, state);
567
- break;
568
- }
569
- if (preventDefault) {
570
- event.preventDefault();
571
- event.stopPropagation();
572
- }
573
- }
574
- function onInputKeyup(event, combobox, state) {
575
- const filter = state.filter.element ?? state.anchor;
576
- const typed = event.key.length === 1 && EXPRESSION_TYPED.test(event.key);
577
- if (!(filter instanceof HTMLInputElement) || !(typed || KEYS_REMOVE.has(event.key) || filter.value.length < state.filter.value.length)) return;
578
- event.preventDefault();
579
- event.stopPropagation();
580
- if (typed) state.filter.value += event.key;
581
- else state.filter.value = filter.value;
582
- const [options] = getOptions(state);
583
- const next = filterOptions(state, options);
584
- if (typed) onTyped(combobox, state, options, next);
585
- else setNext("remove", combobox, state, options, next);
586
- }
587
523
  function onKeydown(event) {
588
- onInput(event, onInputKeydown);
524
+ onAnchor(event, onAnchorKeydown);
589
525
  }
590
526
  function onKeyup(event) {
591
- onInput(event, onInputKeyup);
527
+ onAnchor(event, onAnchorKeyup);
592
528
  }
593
529
  function onNavigation(event, combobox, state) {
594
530
  let skip = false;
@@ -624,22 +560,34 @@ function onNavigation(event, combobox, state) {
624
560
  }
625
561
  setNext(state.autocomplete.none ? "none" : "navigation", combobox, state, all, next);
626
562
  }
563
+ function onRemove(combobox, state) {
564
+ const [all] = getOptions(state);
565
+ setNext("remove", combobox, state, all, filterOptions(state, all));
566
+ }
567
+ function onScrollTo(event, combobox, state) {
568
+ const [all] = getOptions(state);
569
+ const key = event.key.toLocaleLowerCase();
570
+ const next = all.find((option) => option.textContent?.toLocaleLowerCase().startsWith(key) === true) ?? all.find((option) => option.textContent?.toLocaleLowerCase().includes(key) === true);
571
+ if (next != null) setNext("navigation", combobox, state, all, next);
572
+ }
627
573
  function onSelection(combobox, state) {
628
- state.filter.value = state.anchor instanceof HTMLInputElement ? state.anchor.value : "";
574
+ state.filter = state.anchor instanceof HTMLInputElement ? state.anchor.value : "";
629
575
  filterOptions(state);
630
576
  setNext("set", combobox, state, getOptions(state)[0], state.active);
631
577
  }
632
578
  function onTab(combobox, state) {
633
579
  if (combobox !== document.activeElement || !combobox.contains(document.activeElement)) state.floatable.hide();
634
580
  }
635
- function onTyped(combobox, state, options, next) {
636
- const filter = state.filter.element ?? state.anchor;
637
- if (!state.autocomplete.inline && filter instanceof HTMLInputElement && filter.value.length > 0) state.floatable.show();
638
- const all = options ?? getOptions(state)[0];
639
- setNext("typed", combobox, state, all, state.autocomplete.none ? state.active ?? all[0] : next ?? filterOptions(state, options));
581
+ function onTyped(combobox, state) {
582
+ if (!state.autocomplete.inline && state.anchor instanceof HTMLInputElement && state.anchor.value.length > 0) state.floatable.show();
583
+ const options = getOptions(state)[0];
584
+ setNext("typed", combobox, state, options, state.autocomplete.none ? state.active ?? options[0] : filterOptions(state));
640
585
  }
641
586
  function setDisabled(combobox, state, value) {
642
- setProperties("disabled", "disabled", combobox, state, value);
587
+ state.disabled = value;
588
+ setAria(state.anchor, "disabled", value);
589
+ setProperty(combobox, "disabled", value);
590
+ if (state.anchor instanceof HTMLInputElement) state.anchor.readOnly = value;
643
591
  }
644
592
  function setLimit(type, combobox, state, value) {
645
593
  if (typeof value === "number" && !Number.isNaN(value) ? value !== state[type] : value == null) {
@@ -655,18 +603,16 @@ function setNext(update, combobox, state, all, next) {
655
603
  next?.scrollIntoView(scrollOptions);
656
604
  setValue(update, combobox, state, next);
657
605
  }
658
- function setProperties(key, property, combobox, state, value) {
659
- state[key] = value;
660
- setAria(state.anchor, key, value);
661
- setProperty(combobox, key, value);
662
- if (state.filter.element != null) state.filter.element[property] = value;
663
- if (state.anchor instanceof HTMLInputElement) state.anchor[property] = value;
664
- }
665
606
  function setReadonly(combobox, state, value) {
666
- setProperties("readonly", "readOnly", combobox, state, value);
607
+ state.readonly = value;
608
+ setAria(state.anchor, "readonly", value);
609
+ setProperty(combobox, "readonly", value);
610
+ if (state.anchor instanceof HTMLInputElement) state.anchor.readOnly = value;
667
611
  }
668
612
  function setRequired(combobox, state, value) {
669
- setProperties("required", "required", combobox, state, value);
613
+ state.required = value;
614
+ setAria(state.anchor, "required", value);
615
+ setProperty(combobox, "required", value);
670
616
  updateValidity(state);
671
617
  }
672
618
  function setValue(update, combobox, state, next) {
@@ -719,11 +665,11 @@ function setValues(combobox, state, value) {
719
665
  }
720
666
  for (const option of state.selected) {
721
667
  setAria(option, "selected", false);
722
- state.selection?.querySelector(`[option="${option.id}"]`)?.remove();
668
+ state.anchor.querySelector(`[${ATTRIBUTE_SELECTION_ITEM}][option="${option.id}"]`)?.remove();
723
669
  }
724
670
  for (const option of selected) {
725
671
  setAria(option, "selected", true);
726
- state.selection?.append(getSelectionItem(option));
672
+ state.anchor.querySelector(`[${ATTRIBUTE_SELECTION}]`)?.append(getSelectionItem(option));
727
673
  }
728
674
  state.selected = selected.slice();
729
675
  setNext("navigation", combobox, state, all, next);
@@ -732,11 +678,11 @@ function setValues(combobox, state, value) {
732
678
  function updateAutoComplete(update, state, next) {
733
679
  if (!(update === "set" || state.autocomplete.both || state.autocomplete.inline)) return;
734
680
  const content = next.textContent ?? "";
735
- if (update === "typed" && !content.toLocaleLowerCase().startsWith(state.filter.value.toLocaleLowerCase())) return;
681
+ if (update === "typed" && !content.toLocaleLowerCase().startsWith(state.filter.toLocaleLowerCase())) return;
736
682
  const { length } = content;
737
683
  if (state.anchor instanceof HTMLInputElement) {
738
684
  state.anchor.value = content;
739
- state.anchor.setSelectionRange(update === "typed" ? state.filter.value.length : length, length);
685
+ state.anchor.setSelectionRange(update === "typed" ? state.filter.length : length, length);
740
686
  } else state.anchor.textContent = content;
741
687
  }
742
688
  function updateForm(combobox, state, values) {
@@ -754,8 +700,8 @@ function updateSelect(update, state, next, added) {
754
700
  state.anchor.textContent = next.textContent ?? "";
755
701
  return;
756
702
  }
757
- if (added === true) state.selection?.append(getSelectionItem(next));
758
- else if (added === false) state.selection?.querySelector(`[option="${next.id}"]`)?.remove();
703
+ if (added === true) state.anchor.querySelector(`[${ATTRIBUTE_SELECTION}]`)?.append(getSelectionItem(next));
704
+ else if (added === false) state.anchor.querySelector(`[${ATTRIBUTE_SELECTION_ITEM}][option="${next.id}"]`)?.remove();
759
705
  }
760
706
  function updateSelected(update, state, next) {
761
707
  const [all] = getOptions(state);
@@ -804,10 +750,6 @@ const TAGNAME = "oui-combobox";
804
750
  const ATTRIBUTE_ANCHOR = `${TAGNAME}-anchor`;
805
751
  const ATTRIBUTE_CONTENT = `${TAGNAME}-content`;
806
752
  const ATTRIBUTE_EMPTY = `${TAGNAME}-empty`;
807
- const ATTRIBUTE_FILTER = `${TAGNAME}-filter`;
808
- const ATTRIBUTE_FILTER_INPUT = `${ATTRIBUTE_FILTER}-input`;
809
- const ATTRIBUTE_FILTER_LABEL = `${ATTRIBUTE_FILTER}-label`;
810
- const ATTRIBUTE_HEADER = `${TAGNAME}-header`;
811
753
  const ATTRIBUTE_LIST = `${TAGNAME}-list`;
812
754
  const ATTRIBUTE_OPTION = `${TAGNAME}-option`;
813
755
  const ATTRIBUTE_OPTION_ACTIVE = `${TAGNAME}-option-active`;
@@ -824,6 +766,7 @@ const INVALID_BAD = "Please select an option from the list";
824
766
  const INVALID_INPUT = "Please enter a value";
825
767
  const INVALID_OVERFLOW = "Please select fewer options (maximum is {max})";
826
768
  const INVALID_UNDERFLOW = "Please select more options (minimum is {min})";
769
+ const KEYS_ABSOLUTE = /* @__PURE__ */ new Set(["End", "Home"]);
827
770
  const KEYS_NAVIGATION = /* @__PURE__ */ new Set([
828
771
  "ArrowDown",
829
772
  "ArrowUp",
@@ -836,7 +779,6 @@ const MESSAGE_LABEL = `<${TAGNAME}> must contain a <label> element for the <sele
836
779
  const MESSAGE_SELECT = `<${TAGNAME}> must contain a valid <select> element`;
837
780
  const PROPERTY_STYLE = "--oui-combobox-width";
838
781
  const SELECTOR_ANCHOR = `[${ATTRIBUTE_ANCHOR}]`;
839
- const SELECTOR_FILTER = `[${ATTRIBUTE_FILTER_INPUT}]`;
840
782
  const SELECTOR_OPTION = `[${ATTRIBUTE_OPTION}]`;
841
783
  const TYPES = /* @__PURE__ */ new Set(["autocomplete", "select"]);
842
784
  const comboboxes = /* @__PURE__ */ new WeakMap();