@oscarpalmer/oui 0.33.0 → 0.34.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.
Files changed (58) hide show
  1. package/dist/js/combobox/constants.combobox.d.mts +2 -0
  2. package/dist/js/combobox/constants.combobox.mjs +46 -0
  3. package/dist/js/combobox/data.combobox.d.mts +18 -0
  4. package/dist/js/combobox/data.combobox.mjs +2 -0
  5. package/dist/js/combobox/dom.combobox.d.mts +110 -0
  6. package/dist/js/combobox/dom.combobox.mjs +2 -0
  7. package/dist/js/combobox/event.combobox.d.mts +17 -0
  8. package/dist/js/combobox/event.combobox.mjs +2 -0
  9. package/dist/js/combobox/form.combobox.d.mts +6 -0
  10. package/dist/js/combobox/form.combobox.mjs +2 -0
  11. package/dist/js/combobox/i18n.combobox.d.mts +2 -0
  12. package/dist/js/combobox/i18n.combobox.mjs +2 -0
  13. package/dist/js/combobox/index.d.mts +2 -0
  14. package/dist/js/combobox/index.mjs +194 -0
  15. package/dist/js/combobox/misc.combobox.d.mts +14 -0
  16. package/dist/js/combobox/misc.combobox.mjs +2 -0
  17. package/dist/js/combobox/models.combobox.d.mts +2 -0
  18. package/dist/js/combobox/models.combobox.mjs +1 -0
  19. package/dist/js/combobox/state.combobox.d.mts +11 -0
  20. package/dist/js/combobox/state.combobox.mjs +2 -0
  21. package/dist/js/constants.combobox-DqNUGBLS.d.mts +243 -0
  22. package/dist/js/data.combobox-8RwBv5ey.mjs +1077 -0
  23. package/dist/js/dialog.mjs +3 -2
  24. package/dist/js/floatable/floatable.embedded.mjs +4 -4
  25. package/dist/js/focus-trap/focus-trap.embedded.mjs +3 -3
  26. package/dist/js/index.d.mts +14 -643
  27. package/dist/js/index.mjs +19 -4875
  28. package/dist/js/internal/draggable.mjs +6 -6
  29. package/dist/js/internal/navigable.mjs +3 -3
  30. package/dist/js/menu.d.mts +1 -1
  31. package/dist/js/menu.mjs +3 -2
  32. package/dist/js/movable/movable.embedded.mjs +3 -3
  33. package/dist/js/popover.mjs +1 -1
  34. package/dist/js/resizable/resizable.embedded.mjs +7 -7
  35. package/dist/js/sortable/sortable.embedded.mjs +7 -6
  36. package/dist/js/splitter.mjs +7 -13
  37. package/dist/js/toolbar.d.mts +1 -1
  38. package/dist/js/tooltip.mjs +3 -3
  39. package/package.json +4 -4
  40. package/src/js/combobox/constants.combobox.ts +62 -0
  41. package/src/js/combobox/data.combobox.ts +236 -0
  42. package/src/js/combobox/dom.combobox.ts +794 -0
  43. package/src/js/combobox/event.combobox.ts +454 -0
  44. package/src/js/combobox/form.combobox.ts +115 -0
  45. package/src/js/combobox/i18n.combobox.ts +179 -0
  46. package/src/js/combobox/index.ts +307 -0
  47. package/src/js/combobox/misc.combobox.ts +138 -0
  48. package/src/js/combobox/models.combobox.ts +94 -0
  49. package/src/js/combobox/state.combobox.ts +235 -0
  50. package/src/js/index.ts +1 -1
  51. package/src/js/internal/draggable.ts +3 -3
  52. package/src/js/movable/movable.embedded.ts +2 -2
  53. package/src/js/resizable/resizable.embedded.ts +3 -3
  54. package/src/js/sortable/sortable.embedded.ts +2 -2
  55. package/src/js/splitter.ts +2 -2
  56. package/dist/js/combobox.d.mts +0 -91
  57. package/dist/js/combobox.mjs +0 -797
  58. package/src/js/combobox.ts +0 -2410
@@ -1,9 +1,9 @@
1
- import { clamp } from "@oscarpalmer/atoms/number";
1
+ import { dispatch, getEventPosition, on } from "@oscarpalmer/toretto/event";
2
2
  import { getAttribute, setAttribute } from "@oscarpalmer/toretto/attribute";
3
- import { dispatch, getPosition, on } from "@oscarpalmer/toretto/event";
4
- import { findAncestor } from "@oscarpalmer/toretto/find";
5
- import { setStyle, setStyles, toggleStyles } from "@oscarpalmer/toretto/style";
6
3
  import { isHTMLOrSVGElement } from "@oscarpalmer/toretto/is";
4
+ import { setStyle, setStyles, toggleStyles } from "@oscarpalmer/toretto/style";
5
+ import { clamp } from "@oscarpalmer/atoms/number";
6
+ import { findAncestor } from "@oscarpalmer/toretto/find";
7
7
  import supportsTouch from "@oscarpalmer/toretto/touch";
8
8
  //#region src/js/internal/draggable.ts
9
9
  var OuiDraggable = class {
@@ -118,7 +118,7 @@ function onPointerdown(event) {
118
118
  if (handle == null) return;
119
119
  const dragged = item.state.options.getDragged();
120
120
  const rectangle = dragged.getBoundingClientRect();
121
- const position = getPosition(event) ?? {
121
+ const position = getEventPosition(event) ?? {
122
122
  x: 0,
123
123
  y: 0
124
124
  };
@@ -153,7 +153,7 @@ function onPointerdown(event) {
153
153
  }
154
154
  function onPointermove(event) {
155
155
  if (current == null) return;
156
- const position = getPosition(event);
156
+ const position = getEventPosition(event);
157
157
  if (position == null) return;
158
158
  const next = getNextPosition(current, position);
159
159
  if (globals.element != null) setStyle(globals.element.node, "inset", `${next.y}px auto auto ${next.x}px`);
@@ -1,8 +1,8 @@
1
- import { clamp } from "@oscarpalmer/atoms/number";
2
1
  import { getAria, setRole } from "@oscarpalmer/toretto/aria";
3
2
  import { dispatch, on } from "@oscarpalmer/toretto/event";
4
- import { findAncestor } from "@oscarpalmer/toretto/find";
5
3
  import { isHTMLOrSVGElement } from "@oscarpalmer/toretto/is";
4
+ import { clamp } from "@oscarpalmer/atoms/number";
5
+ import { findAncestor } from "@oscarpalmer/toretto/find";
6
6
  //#region src/js/internal/navigable.ts
7
7
  var OuiNavigable = class {
8
8
  active = { index: 0 };
@@ -17,7 +17,7 @@ var OuiNavigable = class {
17
17
  onFocusin(this, event);
18
18
  }), on(element, "keydown", (event) => {
19
19
  onKeydown(this, event);
20
- })];
20
+ }, { passive: false })];
21
21
  this.orientation = getOrientation(element, options);
22
22
  if (options.role != null) setRole(element, options.role);
23
23
  }
@@ -1 +1 @@
1
- export { };
1
+ export {}
package/dist/js/menu.mjs CHANGED
@@ -2,8 +2,8 @@ import { createEmbeddedFloatable, getOnBeforeToggleListener } from "./floatable/
2
2
  import { attributable } from "./internal/attributable.mjs";
3
3
  import { NAVIGABLE_KEYS_HORIZONTAL, NAVIGABLE_KEYS_VERTICAL, getNavigable, updateNavigableFocus } from "./internal/navigable.mjs";
4
4
  import { setAria, setRole } from "@oscarpalmer/toretto/aria";
5
- import { setAttribute, setAttributes } from "@oscarpalmer/toretto/attribute";
6
5
  import { on } from "@oscarpalmer/toretto/event";
6
+ import { setAttribute, setAttributes } from "@oscarpalmer/toretto/attribute";
7
7
  import { findAncestor } from "@oscarpalmer/toretto/find";
8
8
  //#region src/js/menu.ts
9
9
  var OuiMenu = class {
@@ -120,7 +120,8 @@ function onDeactivate(_, navigable) {
120
120
  if (instance != null) updateNavigableFocus(instance.navigable, true);
121
121
  }
122
122
  function onPreventNavigation(_, navigable) {
123
- return findAncestor((menubars.get(navigable.element) ?? menus.get(navigable.element))?.element.parentElement, SELECTORS) != null;
123
+ const instance = menubars.get(navigable.element) ?? menus.get(navigable.element);
124
+ return findAncestor(instance?.element.parentElement, SELECTORS) != null;
124
125
  }
125
126
  function onShouldActivate(event, navigable) {
126
127
  let activate = false;
@@ -1,9 +1,9 @@
1
1
  import { attributable } from "../internal/attributable.mjs";
2
2
  import { OuiDraggable, addDraggable, getDraggableStates, removeDraggable } from "../internal/draggable.mjs";
3
+ import { dispatch, getEventPosition, on } from "@oscarpalmer/toretto/event";
3
4
  import { setAttribute } from "@oscarpalmer/toretto/attribute";
4
- import { dispatch, getPosition, on } from "@oscarpalmer/toretto/event";
5
- import { getStyles, setStyle, setStyles } from "@oscarpalmer/toretto/style";
6
5
  import { isHTMLOrSVGElement } from "@oscarpalmer/toretto/is";
6
+ import { getStyles, setStyle, setStyles } from "@oscarpalmer/toretto/style";
7
7
  //#region src/js/movable/movable.embedded.ts
8
8
  var OuiMovable = class extends OuiDraggable {
9
9
  #element;
@@ -112,7 +112,7 @@ function onEnd(event, globals, item, reset) {
112
112
  if (container != null) setOffset(state, container);
113
113
  const dispatched = dispatch(state.element, "movable:end", { detail: {
114
114
  from: { ...globals.original },
115
- to: getPosition(event)
115
+ to: getEventPosition(event)
116
116
  } });
117
117
  setTimeout(() => {
118
118
  if (dispatched.defaultPrevented) onCancel(globals, item);
@@ -2,8 +2,8 @@ import { createEmbeddedFloatable, getOnBeforeToggleListener } from "./floatable/
2
2
  import { attributable } from "./internal/attributable.mjs";
3
3
  import { createFocusTrap } from "./focus-trap/focus-trap.embedded.mjs";
4
4
  import { getAria, setAria } from "@oscarpalmer/toretto/aria";
5
- import { setAttribute } from "@oscarpalmer/toretto/attribute";
6
5
  import { on } from "@oscarpalmer/toretto/event";
6
+ import { setAttribute } from "@oscarpalmer/toretto/attribute";
7
7
  import { isHTMLOrSVGElement } from "@oscarpalmer/toretto/is";
8
8
  //#region src/js/popover.ts
9
9
  var OuiPopover = class {
@@ -1,11 +1,11 @@
1
1
  import { attributable } from "../internal/attributable.mjs";
2
- import { getNumber } from "@oscarpalmer/atoms/number";
3
- import { getAttribute, setAttribute } from "@oscarpalmer/toretto/attribute";
4
- import { dispatch, getPosition, on } from "@oscarpalmer/toretto/event";
5
- import { findAncestor } from "@oscarpalmer/toretto/find";
6
- import { setStyles, toggleStyles } from "@oscarpalmer/toretto/style";
2
+ import { dispatch, getEventPosition, on } from "@oscarpalmer/toretto/event";
7
3
  import { isPlainObject } from "@oscarpalmer/atoms/is";
4
+ import { getAttribute, setAttribute } from "@oscarpalmer/toretto/attribute";
8
5
  import { isHTMLOrSVGElement } from "@oscarpalmer/toretto/is";
6
+ import { setStyles, toggleStyles } from "@oscarpalmer/toretto/style";
7
+ import { getNumber } from "@oscarpalmer/atoms/number";
8
+ import { findAncestor } from "@oscarpalmer/toretto/find";
9
9
  import supportsTouch from "@oscarpalmer/toretto/touch";
10
10
  //#region src/js/resizable/resizable.embedded.ts
11
11
  var OuiResizable = class {
@@ -127,7 +127,7 @@ function onPointerdown(event) {
127
127
  const handle = findAncestor(event, SELECTOR_HANDLE);
128
128
  const element = handle && findAncestor(handle, SELECTOR);
129
129
  const stored = element && items.get(element);
130
- const position = getPosition(event);
130
+ const position = getEventPosition(event);
131
131
  if (stored == null || position == null) return;
132
132
  if (supportsTouch.value) event.preventDefault();
133
133
  const dispatched = dispatch(element, "resizable:begin");
@@ -141,7 +141,7 @@ function onPointerdown(event) {
141
141
  }
142
142
  function onPointermove(event) {
143
143
  if (current == null) return;
144
- const { x, y } = getPosition(event) ?? {};
144
+ const { x, y } = getEventPosition(event) ?? {};
145
145
  if (x == null || y == null) return;
146
146
  const width = current.state.width + (x - (start?.x ?? 0));
147
147
  const height = current.state.height + (y - (start?.y ?? 0));
@@ -1,11 +1,11 @@
1
1
  import { attributable } from "../internal/attributable.mjs";
2
2
  import { OuiDraggable, addDraggable, getDraggableStates, removeDraggable } from "../internal/draggable.mjs";
3
+ import { dispatch, getEventPosition } from "@oscarpalmer/toretto/event";
3
4
  import { getAttribute, setAttribute, setAttributes } from "@oscarpalmer/toretto/attribute";
4
- import { createElement } from "@oscarpalmer/toretto/create";
5
- import { dispatch, getPosition } from "@oscarpalmer/toretto/event";
6
- import { findAncestor, getElementFromPosition } from "@oscarpalmer/toretto/find";
7
- import { setStyle } from "@oscarpalmer/toretto/style";
8
5
  import { isHTMLOrSVGElement } from "@oscarpalmer/toretto/is";
6
+ import { setStyle } from "@oscarpalmer/toretto/style";
7
+ import { findAncestor, getElementFromPosition } from "@oscarpalmer/toretto/find";
8
+ import { createElement } from "@oscarpalmer/toretto/create";
9
9
  //#region src/js/sortable/sortable.embedded.ts
10
10
  var OuiSortable = class extends OuiDraggable {
11
11
  constructor(element, options) {
@@ -92,7 +92,7 @@ function onCancel(_, item) {
92
92
  }
93
93
  function onEnd(event, globals, item, reset) {
94
94
  const { origin } = item.state;
95
- const position = getPosition(event) ?? {
95
+ const position = getEventPosition(event) ?? {
96
96
  x: 0,
97
97
  y: 0
98
98
  };
@@ -258,6 +258,7 @@ const placeholder = {
258
258
  position: void 0
259
259
  };
260
260
  const addSortable = addDraggable.bind(bound);
261
- attributable(ATTRIBUTE, addSortable, removeDraggable.bind(bound));
261
+ const removeSortable = removeDraggable.bind(bound);
262
+ attributable(ATTRIBUTE, addSortable, removeSortable);
262
263
  //#endregion
263
264
  export { OuiSortable, createSortable };
@@ -1,10 +1,10 @@
1
- import { clamp } from "@oscarpalmer/atoms/number";
2
1
  import { setAria } from "@oscarpalmer/toretto/aria";
2
+ import { getEventPosition, on } from "@oscarpalmer/toretto/event";
3
+ import { isNullableOrWhitespace } from "@oscarpalmer/atoms/is";
3
4
  import { getAttribute, setAttribute } from "@oscarpalmer/toretto/attribute";
4
- import { getPosition, on } from "@oscarpalmer/toretto/event";
5
- import { findAncestor } from "@oscarpalmer/toretto/find";
6
5
  import { setStyle, toggleStyles } from "@oscarpalmer/toretto/style";
7
- import { isNullableOrWhitespace } from "@oscarpalmer/atoms/is";
6
+ import { clamp } from "@oscarpalmer/atoms/number";
7
+ import { findAncestor } from "@oscarpalmer/toretto/find";
8
8
  import supportsTouch from "@oscarpalmer/toretto/touch";
9
9
  import { round } from "@oscarpalmer/atoms/math";
10
10
  //#region src/js/splitter.ts
@@ -130,10 +130,7 @@ var OuiSplitterElement = class extends HTMLElement {
130
130
  case ATTRIBUTE_SIZE:
131
131
  this.#splitter.updateValues();
132
132
  break;
133
- case ATTRIBUTE_TYPE:
134
- this.#splitter.updateOrientation();
135
- break;
136
- default: break;
133
+ case ATTRIBUTE_TYPE: this.#splitter.updateOrientation();
137
134
  }
138
135
  }
139
136
  };
@@ -213,10 +210,7 @@ function onKeydown(event) {
213
210
  case KEYS_ALL.has(event.key):
214
211
  onNavigate(event);
215
212
  break;
216
- case event.key === "Escape":
217
- onEnd(true);
218
- break;
219
- default: break;
213
+ case event.key === "Escape": onEnd(true);
220
214
  }
221
215
  }
222
216
  function onNavigate(event) {
@@ -256,7 +250,7 @@ function onPointerdown(event) {
256
250
  }
257
251
  function onPointermove(event) {
258
252
  if (splitter == null) return;
259
- const position = getPosition(event);
253
+ const position = getEventPosition(event);
260
254
  if (position != null) {
261
255
  splitter.values.now.current = getSize(splitter, splitter.types.horizontal ? position.y : position.x);
262
256
  setSize(splitter, splitter.values.now.current);
@@ -1 +1 @@
1
- export { };
1
+ export {}
@@ -1,11 +1,11 @@
1
1
  import { createEmbeddedFloatable } from "./floatable/floatable.embedded.mjs";
2
2
  import { attributable } from "./internal/attributable.mjs";
3
3
  import { getAria } from "@oscarpalmer/toretto/aria";
4
- import { setAttribute } from "@oscarpalmer/toretto/attribute";
5
- import { createElement } from "@oscarpalmer/toretto/create";
6
4
  import { on } from "@oscarpalmer/toretto/event";
7
- import { findAncestor } from "@oscarpalmer/toretto/find";
8
5
  import { isNullableOrWhitespace } from "@oscarpalmer/atoms/is";
6
+ import { setAttribute } from "@oscarpalmer/toretto/attribute";
7
+ import { findAncestor } from "@oscarpalmer/toretto/find";
8
+ import { createElement } from "@oscarpalmer/toretto/create";
9
9
  //#region src/js/tooltip.ts
10
10
  var OuiTooltip = class {
11
11
  #state;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oscarpalmer/oui",
3
- "version": "0.33.0",
3
+ "version": "0.34.0",
4
4
  "description": "Styles, components, and utilities for building websites and applications.",
5
5
  "keywords": [
6
6
  "css",
@@ -34,8 +34,8 @@
34
34
  "default": "./dist/js/index.mjs"
35
35
  },
36
36
  "./combobox": {
37
- "types": "./dist/js/combobox.d.mts",
38
- "default": "./dist/js/combobox.mjs"
37
+ "types": "./dist/js/combobox/index.d.mts",
38
+ "default": "./dist/js/combobox/index.mjs"
39
39
  },
40
40
  "./dialog": {
41
41
  "types": "./dist/js/dialog.d.mts",
@@ -95,7 +95,7 @@
95
95
  },
96
96
  "dependencies": {
97
97
  "@oscarpalmer/atoms": "^0.188.1",
98
- "@oscarpalmer/toretto": "^0.49.1"
98
+ "@oscarpalmer/toretto": "^0.50.1"
99
99
  },
100
100
  "devDependencies": {
101
101
  "@oxlint/plugins": "^1.77",
@@ -0,0 +1,62 @@
1
+ import type {OuiComboboxAutoCompleteType, OuiComboboxType} from './models.combobox';
2
+
3
+ // #region Variables
4
+
5
+ export const COMBOBOX_TAGNAME = 'oui-combobox';
6
+
7
+ export const COMBOBOX_ATTRIBUTE_ANCHOR = `${COMBOBOX_TAGNAME}-anchor`;
8
+
9
+ export const COMBOBOX_ATTRIBUTE_CONTENT = `${COMBOBOX_TAGNAME}-content`;
10
+
11
+ export const COMBOBOX_ATTRIBUTE_FILTER = `${COMBOBOX_TAGNAME}-filter`;
12
+
13
+ export const COMBOBOX_ATTRIBUTE_FILTER_INPUT = `${COMBOBOX_ATTRIBUTE_FILTER}-input`;
14
+
15
+ export const COMBOBOX_ATTRIBUTE_HEADER = `${COMBOBOX_TAGNAME}-header`;
16
+
17
+ export const COMBOBOX_ATTRIBUTE_HIDDEN = `${COMBOBOX_TAGNAME}-hidden`;
18
+
19
+ export const COMBOBOX_ATTRIBUTE_LIST = `${COMBOBOX_TAGNAME}-list`;
20
+
21
+ export const COMBOBOX_ATTRIBUTE_MESSAGE = `${COMBOBOX_TAGNAME}-message`;
22
+
23
+ export const COMBOBOX_ATTRIBUTE_OPTION = `${COMBOBOX_TAGNAME}-option`;
24
+
25
+ export const COMBOBOX_ATTRIBUTE_OPTION_ACTIVE = `${COMBOBOX_TAGNAME}-option-active`;
26
+
27
+ export const COMBOBOX_ATTRIBUTE_SELECTION = `${COMBOBOX_TAGNAME}-selection`;
28
+
29
+ export const COMBOBOX_ATTRIBUTE_SELECTION_ITEM = `${COMBOBOX_TAGNAME}-selection-item`;
30
+
31
+ export const COMBOBOX_AUTOCOMPLETES = new Set<OuiComboboxAutoCompleteType>([
32
+ 'both',
33
+ 'inline',
34
+ 'list',
35
+ 'none',
36
+ ]);
37
+
38
+ export const COMBOBOX_KEYS_ACTIVATION = new Set([' ', 'Backspace', 'Delete', 'Enter']);
39
+
40
+ export const COMBOBOX_KEYS_NAVIGATION = new Set(['ArrowDown', 'ArrowUp', 'PageDown', 'PageUp']);
41
+
42
+ export const COMBOBOX_KEYS_SELECTION = new Set(['ArrowRight', 'End']);
43
+
44
+ export const COMBOBOX_MESSAGE_LABEL = `<${COMBOBOX_TAGNAME}> must contain a <label> element for the <select> element`;
45
+
46
+ export const COMBOBOX_MESSAGE_SELECT = `<${COMBOBOX_TAGNAME}> must contain a valid <select> element`;
47
+
48
+ export const COMBOBOX_SELECTOR_ANCHOR = `[${COMBOBOX_ATTRIBUTE_ANCHOR}]`;
49
+
50
+ export const COMBOBOX_SELECTOR_SELECTION_ITEM = `[${COMBOBOX_ATTRIBUTE_SELECTION_ITEM}]`;
51
+
52
+ export const COMBOBOX_SELECTOR_OPTION = `[${COMBOBOX_ATTRIBUTE_OPTION}]`;
53
+
54
+ export const COMBOBOX_TYPES = new Set<OuiComboboxType>(['autocomplete', 'select']);
55
+
56
+ export const comboboxScrollOptions: ScrollIntoViewOptions = {
57
+ behavior: 'auto',
58
+ block: 'nearest',
59
+ inline: 'nearest',
60
+ };
61
+
62
+ // #endregion
@@ -0,0 +1,236 @@
1
+ import {getString} from '@oscarpalmer/atoms/string';
2
+ import {setAria} from '@oscarpalmer/toretto/aria';
3
+ import {getData} from '@oscarpalmer/toretto/data';
4
+ import {
5
+ updateComboboxMessage,
6
+ updateComboboxPreview,
7
+ updateComboboxSelection,
8
+ } from './dom.combobox';
9
+ import {onComboboxFilter} from './event.combobox';
10
+ import {updateComboboxForm} from './form.combobox';
11
+ import type {OuiComboboxState} from './models.combobox';
12
+
13
+ // #region Types
14
+
15
+ type ApplySelectionParameters = {
16
+ added: boolean;
17
+ emit?: boolean;
18
+ option?: HTMLElement;
19
+ selected?: HTMLElement[];
20
+ state: OuiComboboxState;
21
+ values?: string[];
22
+ };
23
+
24
+ // #endregion
25
+
26
+ // #region Functions
27
+
28
+ export function applyComboboxSelection(parameters: ApplySelectionParameters): void {
29
+ const {added, emit, option, selected, state} = parameters;
30
+
31
+ const {length} = state.options.all;
32
+
33
+ for (let index = 0; index < length; index += 1) {
34
+ const option = state.options.all[index];
35
+
36
+ setAria(option, 'selected', (selected ?? state.options.selected).includes(option));
37
+ }
38
+
39
+ const values =
40
+ parameters.values ??
41
+ (state.autocomplete.none
42
+ ? [state.filter.element.value]
43
+ : (selected ?? state.options.selected).map(option => getData(option, 'value', false) ?? ''));
44
+
45
+ if (values.length === 0) {
46
+ values.push('');
47
+ }
48
+
49
+ if (selected != null) {
50
+ state.options.selected = selected;
51
+ }
52
+
53
+ const update =
54
+ values.length === state.values.current.length
55
+ ? values.some(value => !state.values.current.includes(value))
56
+ : true;
57
+
58
+ if (!update) {
59
+ return;
60
+ }
61
+
62
+ updateComboboxForm(state, values, emit);
63
+ updateComboboxPreview('set', state, option);
64
+ updateComboboxSelection(state, option, added, selected);
65
+ }
66
+
67
+ export function filterComboboxOption(
68
+ state: OuiComboboxState,
69
+ value: unknown,
70
+ textContent?: boolean,
71
+ ): HTMLElement | undefined {
72
+ const filterTextContent = textContent ?? false;
73
+
74
+ const filter = getString(value).toLocaleLowerCase();
75
+
76
+ filterComboboxOptions(state);
77
+
78
+ const {length} = state.options.all;
79
+ let match: HTMLElement | undefined;
80
+
81
+ for (let index = 0; index < length; index += 1) {
82
+ const option = state.options.all[index];
83
+
84
+ const value = (
85
+ filterTextContent ? option.textContent : (getData(option, 'value', false) ?? '')
86
+ ).toLocaleLowerCase();
87
+
88
+ if (value === filter) {
89
+ match = option;
90
+
91
+ break;
92
+ }
93
+ }
94
+
95
+ return match;
96
+ }
97
+
98
+ export function filterComboboxOptions(state: OuiComboboxState): HTMLElement | undefined {
99
+ function filter(options: HTMLElement[]): boolean {
100
+ const {length} = options;
101
+
102
+ let count = 0;
103
+
104
+ for (let index = 0; index < length; index += 1) {
105
+ const option = options[index];
106
+
107
+ option.hidden =
108
+ !state.autocomplete.none && !option.textContent.toLocaleLowerCase().includes(value);
109
+
110
+ if (!option.hidden) {
111
+ count += 1;
112
+
113
+ visible.push(option);
114
+ }
115
+ }
116
+
117
+ return count === 0;
118
+ }
119
+
120
+ const value = state.filter.element.value.toLocaleLowerCase();
121
+
122
+ const visible: HTMLElement[] = [];
123
+
124
+ if (state.groups == null) {
125
+ filter(state.options.all);
126
+ } else {
127
+ const groupsLength = state.groups.length;
128
+
129
+ for (let groupIndex = 0; groupIndex < groupsLength; groupIndex += 1) {
130
+ const {element, options} = state.groups[groupIndex];
131
+
132
+ element.hidden = filter(options);
133
+ }
134
+ }
135
+
136
+ updateComboboxMessage(state, visible.length);
137
+
138
+ return state.active != null && visible.includes(state.active) ? state.active : visible[0];
139
+ }
140
+
141
+ export function setComboboxValue(state: OuiComboboxState, value: unknown, emit?: boolean): void {
142
+ if (state.autocomplete.none) {
143
+ state.filter.element.value = getString(value);
144
+
145
+ onComboboxFilter('insertText', state, true, emit);
146
+
147
+ return;
148
+ }
149
+
150
+ const option = filterComboboxOption(state, value);
151
+
152
+ if (option != null) {
153
+ setComboboxValues(state, [value], emit);
154
+ }
155
+ }
156
+
157
+ export function setComboboxValues(state: OuiComboboxState, value: unknown, emit?: boolean): void {
158
+ if (!Array.isArray(value)) {
159
+ return;
160
+ }
161
+
162
+ const values = (state.multiple ? value : value.slice(0, 1)).map(value =>
163
+ getString(value).toLocaleLowerCase(),
164
+ );
165
+
166
+ if (values.length === 0) {
167
+ values.push('');
168
+ }
169
+
170
+ if (state.autocomplete.none) {
171
+ setComboboxValue(state, values[0], emit);
172
+
173
+ return;
174
+ }
175
+
176
+ const {length} = state.options.all;
177
+
178
+ const selected: HTMLElement[] = [];
179
+
180
+ for (let index = 0; index < length; index += 1) {
181
+ const option = state.options.all[index];
182
+
183
+ if (values.includes(getData(option, 'value', false)?.toLocaleLowerCase() ?? '')) {
184
+ selected.push(option);
185
+ }
186
+ }
187
+
188
+ applyComboboxSelection({
189
+ emit,
190
+ selected,
191
+ state,
192
+ added: true,
193
+ option: selected[0] ?? state.options.all[0],
194
+ values: state.autocomplete.none ? values : undefined,
195
+ });
196
+ }
197
+
198
+ export function toggleComboboxOption(state: OuiComboboxState, option?: HTMLElement): void {
199
+ if (option == null || (!state.multiple && state.options.selected.includes(option))) {
200
+ state.floatable.hide();
201
+
202
+ return;
203
+ }
204
+
205
+ const selected = state.options.selected.slice();
206
+
207
+ let added = true;
208
+
209
+ if (state.multiple) {
210
+ const index = selected.indexOf(option);
211
+
212
+ if (index === -1) {
213
+ selected.push(option);
214
+ } else {
215
+ added = false;
216
+
217
+ selected.splice(index, 1);
218
+ }
219
+ } else {
220
+ selected.splice(0, selected.length, option);
221
+ }
222
+
223
+ applyComboboxSelection({
224
+ added,
225
+ option,
226
+ selected,
227
+ state,
228
+ values: state.autocomplete.none && option != null ? [option.textContent] : undefined,
229
+ });
230
+
231
+ if (!state.multiple) {
232
+ state.floatable.hide();
233
+ }
234
+ }
235
+
236
+ // #endregion