@vonage/vivid 3.9.0 → 3.10.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 (123) hide show
  1. package/alert/index.js +28 -0
  2. package/avatar/index.js +1 -1
  3. package/badge/index.js +1 -1
  4. package/banner/index.js +3 -3
  5. package/breadcrumb/index.js +1 -1
  6. package/breadcrumb-item/index.js +1 -1
  7. package/button/index.js +2 -2
  8. package/calendar/index.js +1 -1
  9. package/calendar-event/index.js +1 -1
  10. package/card/index.js +2 -2
  11. package/checkbox/index.js +1 -1
  12. package/combobox/index.js +6 -6
  13. package/custom-elements.json +269 -27
  14. package/data-grid/index.js +1 -1
  15. package/dialog/index.js +4 -4
  16. package/divider/index.js +1 -1
  17. package/elevation/index.js +1 -1
  18. package/fab/index.js +1 -1
  19. package/header/index.js +2 -2
  20. package/index.js +51 -50
  21. package/layout/index.js +1 -1
  22. package/lib/alert/alert.d.ts +24 -0
  23. package/lib/alert/alert.template.d.ts +4 -0
  24. package/lib/alert/definition.d.ts +2 -0
  25. package/lib/alert/index.d.ts +1 -0
  26. package/lib/components.d.ts +1 -0
  27. package/lib/text-area/text-area.d.ts +2 -1
  28. package/lib/tooltip/tooltip.d.ts +1 -0
  29. package/listbox/index.js +2 -2
  30. package/menu/index.js +6 -6
  31. package/menu-item/index.js +1 -1
  32. package/nav/index.js +1 -1
  33. package/nav-disclosure/index.js +1 -1
  34. package/nav-item/index.js +1 -1
  35. package/note/index.js +1 -1
  36. package/number-field/index.js +4 -4
  37. package/option/index.js +1 -1
  38. package/package.json +1 -1
  39. package/popup/index.js +4 -4
  40. package/progress/index.js +1 -1
  41. package/progress-ring/index.js +1 -1
  42. package/radio/index.js +1 -1
  43. package/radio-group/index.js +1 -1
  44. package/select/index.js +6 -6
  45. package/shared/definition.js +1 -1
  46. package/shared/definition10.js +46 -63
  47. package/shared/definition11.js +38 -92
  48. package/shared/definition12.js +96 -31
  49. package/shared/definition13.js +84 -757
  50. package/shared/definition14.js +32 -95
  51. package/shared/definition15.js +758 -100
  52. package/shared/definition16.js +103 -24
  53. package/shared/definition17.js +96 -154
  54. package/shared/definition18.js +108 -663
  55. package/shared/definition19.js +667 -1532
  56. package/shared/definition2.js +1 -1
  57. package/shared/definition20.js +1544 -223
  58. package/shared/definition21.js +183 -964
  59. package/shared/definition22.js +1037 -222
  60. package/shared/definition23.js +226 -67
  61. package/shared/definition24.js +68 -77
  62. package/shared/definition25.js +76 -47
  63. package/shared/definition26.js +46 -32
  64. package/shared/definition27.js +35 -49
  65. package/shared/definition28.js +48 -344
  66. package/shared/definition29.js +273 -282
  67. package/shared/definition30.js +356 -14
  68. package/shared/definition31.js +13 -67
  69. package/shared/definition32.js +65 -21
  70. package/shared/definition33.js +21 -39
  71. package/shared/definition34.js +31 -432
  72. package/shared/definition35.js +432 -76
  73. package/shared/definition36.js +82 -33
  74. package/shared/definition37.js +31 -422
  75. package/shared/definition38.js +357 -564
  76. package/shared/definition39.js +628 -75
  77. package/shared/definition4.js +1 -1
  78. package/shared/definition40.js +70 -573
  79. package/shared/definition41.js +538 -81
  80. package/shared/definition42.js +127 -47
  81. package/shared/definition43.js +51 -16
  82. package/shared/definition44.js +17 -425
  83. package/shared/definition45.js +421 -103
  84. package/shared/definition46.js +114 -19
  85. package/shared/definition47.js +19 -269
  86. package/shared/definition48.js +244 -86
  87. package/shared/definition49.js +110 -70
  88. package/shared/definition5.js +1 -1
  89. package/shared/definition50.js +80 -67
  90. package/shared/definition51.js +69 -295
  91. package/shared/definition52.js +305 -0
  92. package/shared/definition6.js +142 -45
  93. package/shared/definition7.js +95 -23
  94. package/shared/definition8.js +22 -103
  95. package/shared/definition9.js +62 -102
  96. package/shared/es.object.assign.js +1 -1
  97. package/shared/form-associated.js +1 -1
  98. package/shared/form-elements.js +2 -2
  99. package/shared/index.js +1 -1
  100. package/shared/listbox.js +2 -2
  101. package/shared/patterns/form-elements/form-elements.d.ts +4 -4
  102. package/shared/repeat.js +1 -1
  103. package/shared/text-field.js +1 -1
  104. package/shared/text-field2.js +1 -1
  105. package/side-drawer/index.js +1 -1
  106. package/slider/index.js +1 -1
  107. package/styles/core/all.css +1 -1
  108. package/styles/core/theme.css +1 -1
  109. package/styles/core/typography.css +1 -1
  110. package/styles/tokens/theme-dark.css +4 -4
  111. package/styles/tokens/theme-light.css +4 -4
  112. package/switch/index.js +1 -1
  113. package/tab/index.js +1 -1
  114. package/tab-panel/index.js +1 -1
  115. package/tabs/index.js +3 -3
  116. package/tag/index.js +1 -1
  117. package/tag-group/index.js +1 -1
  118. package/text-area/index.js +1 -1
  119. package/text-field/index.js +1 -1
  120. package/tooltip/index.js +5 -5
  121. package/tree-item/index.js +1 -1
  122. package/tree-view/index.js +1 -1
  123. package/vivid.api.json +91 -0
@@ -1,1592 +1,727 @@
1
- import { F as FoundationElement, U as __classPrivateFieldGet, a5 as __classPrivateFieldSet, _ as __decorate, a as attr, b as __metadata, h as html, r as registerFactory } from './index.js';
2
- import { B as Button, b as buttonRegistries } from './definition9.js';
3
- import { E as Elevation, e as elevationRegistries } from './definition16.js';
4
- import './icon.js';
5
- import './es.object.assign.js';
6
- import { w as when } from './when.js';
1
+ import { Y as DOM, O as Observable, _ as __decorate, a as attr, o as observable, b as __metadata, h as html, r as registerFactory } from './index.js';
2
+ import { i as iconRegistries } from './definition3.js';
3
+ import { P as Popup, p as popupRegistries } from './definition20.js';
4
+ import { f as focusRegistries } from './definition4.js';
5
+ import { l as listboxOptionRegistries } from './definition21.js';
6
+ import { c as css_248z$1 } from './text-field.js';
7
+ import { b as AffixIcon, a as affixIconTemplateFactory } from './affix.js';
8
+ import './focus.js';
9
+ import { f as formElements } from './form-elements.js';
10
+ import { L as Listbox, D as DelegatesARIAListbox, a as Listbox$1 } from './listbox.js';
11
+ import { S as StartEnd } from './start-end.js';
12
+ import { S as SelectPosition } from './select.options.js';
13
+ import { a as applyMixins } from './apply-mixins.js';
14
+ import { F as FormAssociated } from './form-associated.js';
15
+ import { l as limit } from './numbers.js';
16
+ import { u as uniqueId } from './strings.js';
17
+ import { f as focusTemplateFactory } from './focus2.js';
18
+ import { s as slotted } from './slotted.js';
7
19
  import { r as ref } from './ref.js';
20
+ import { w as when } from './when.js';
8
21
  import { c as classNames } from './class-names.js';
9
22
 
10
- function getAlignment(placement) {
11
- return placement.split('-')[1];
12
- }
13
-
14
- function getLengthFromAxis(axis) {
15
- return axis === 'y' ? 'height' : 'width';
16
- }
17
-
18
- function getSide(placement) {
19
- return placement.split('-')[0];
20
- }
21
-
22
- function getMainAxisFromPlacement(placement) {
23
- return ['top', 'bottom'].includes(getSide(placement)) ? 'x' : 'y';
24
- }
25
-
26
- function computeCoordsFromPlacement(_ref, placement, rtl) {
27
- let {
28
- reference,
29
- floating
30
- } = _ref;
31
- const commonX = reference.x + reference.width / 2 - floating.width / 2;
32
- const commonY = reference.y + reference.height / 2 - floating.height / 2;
33
- const mainAxis = getMainAxisFromPlacement(placement);
34
- const length = getLengthFromAxis(mainAxis);
35
- const commonAlign = reference[length] / 2 - floating[length] / 2;
36
- const side = getSide(placement);
37
- const isVertical = mainAxis === 'x';
38
- let coords;
39
- switch (side) {
40
- case 'top':
41
- coords = {
42
- x: commonX,
43
- y: reference.y - floating.height
44
- };
45
- break;
46
- case 'bottom':
47
- coords = {
48
- x: commonX,
49
- y: reference.y + reference.height
50
- };
51
- break;
52
- case 'right':
53
- coords = {
54
- x: reference.x + reference.width,
55
- y: commonY
56
- };
57
- break;
58
- case 'left':
59
- coords = {
60
- x: reference.x - floating.width,
61
- y: commonY
62
- };
63
- break;
64
- default:
65
- coords = {
66
- x: reference.x,
67
- y: reference.y
68
- };
69
- }
70
- switch (getAlignment(placement)) {
71
- case 'start':
72
- coords[mainAxis] -= commonAlign * (rtl && isVertical ? -1 : 1);
73
- break;
74
- case 'end':
75
- coords[mainAxis] += commonAlign * (rtl && isVertical ? -1 : 1);
76
- break;
77
- }
78
- return coords;
23
+ class _Combobox extends Listbox {
79
24
  }
80
-
81
25
  /**
82
- * Computes the `x` and `y` coordinates that will place the floating element
83
- * next to a reference element when it is given a certain positioning strategy.
26
+ * A form-associated base class for the {@link (Combobox:class)} component.
84
27
  *
85
- * This export does not have any `platform` interface logic. You will need to
86
- * write one for the platform you are using Floating UI with.
28
+ * @internal
87
29
  */
88
- const computePosition$1 = async (reference, floating, config) => {
89
- const {
90
- placement = 'bottom',
91
- strategy = 'absolute',
92
- middleware = [],
93
- platform
94
- } = config;
95
- const validMiddleware = middleware.filter(Boolean);
96
- const rtl = await (platform.isRTL == null ? void 0 : platform.isRTL(floating));
97
- let rects = await platform.getElementRects({
98
- reference,
99
- floating,
100
- strategy
101
- });
102
- let {
103
- x,
104
- y
105
- } = computeCoordsFromPlacement(rects, placement, rtl);
106
- let statefulPlacement = placement;
107
- let middlewareData = {};
108
- let resetCount = 0;
109
- for (let i = 0; i < validMiddleware.length; i++) {
110
- const {
111
- name,
112
- fn
113
- } = validMiddleware[i];
114
- const {
115
- x: nextX,
116
- y: nextY,
117
- data,
118
- reset
119
- } = await fn({
120
- x,
121
- y,
122
- initialPlacement: placement,
123
- placement: statefulPlacement,
124
- strategy,
125
- middlewareData,
126
- rects,
127
- platform,
128
- elements: {
129
- reference,
130
- floating
131
- }
132
- });
133
- x = nextX != null ? nextX : x;
134
- y = nextY != null ? nextY : y;
135
- middlewareData = {
136
- ...middlewareData,
137
- [name]: {
138
- ...middlewareData[name],
139
- ...data
140
- }
141
- };
142
- if (reset && resetCount <= 50) {
143
- resetCount++;
144
- if (typeof reset === 'object') {
145
- if (reset.placement) {
146
- statefulPlacement = reset.placement;
147
- }
148
- if (reset.rects) {
149
- rects = reset.rects === true ? await platform.getElementRects({
150
- reference,
151
- floating,
152
- strategy
153
- }) : reset.rects;
154
- }
155
- ({
156
- x,
157
- y
158
- } = computeCoordsFromPlacement(rects, statefulPlacement, rtl));
159
- }
160
- i = -1;
161
- continue;
30
+ class FormAssociatedCombobox extends FormAssociated(_Combobox) {
31
+ constructor() {
32
+ super(...arguments);
33
+ this.proxy = document.createElement("input");
162
34
  }
163
- }
164
- return {
165
- x,
166
- y,
167
- placement: statefulPlacement,
168
- strategy,
169
- middlewareData
170
- };
171
- };
172
-
173
- function expandPaddingObject(padding) {
174
- return {
175
- top: 0,
176
- right: 0,
177
- bottom: 0,
178
- left: 0,
179
- ...padding
180
- };
181
- }
182
-
183
- function getSideObjectFromPadding(padding) {
184
- return typeof padding !== 'number' ? expandPaddingObject(padding) : {
185
- top: padding,
186
- right: padding,
187
- bottom: padding,
188
- left: padding
189
- };
190
- }
191
-
192
- function rectToClientRect(rect) {
193
- return {
194
- ...rect,
195
- top: rect.y,
196
- left: rect.x,
197
- right: rect.x + rect.width,
198
- bottom: rect.y + rect.height
199
- };
200
35
  }
201
36
 
202
37
  /**
203
- * Resolves with an object of overflow side offsets that determine how much the
204
- * element is overflowing a given clipping boundary.
205
- * - positive = overflowing the boundary by that number of pixels
206
- * - negative = how many pixels left before it will overflow
207
- * - 0 = lies flush with the boundary
208
- * @see https://floating-ui.com/docs/detectOverflow
38
+ * Autocomplete values for combobox.
39
+ * @public
209
40
  */
210
- async function detectOverflow(middlewareArguments, options) {
211
- var _await$platform$isEle;
212
- if (options === void 0) {
213
- options = {};
214
- }
215
- const {
216
- x,
217
- y,
218
- platform,
219
- rects,
220
- elements,
221
- strategy
222
- } = middlewareArguments;
223
- const {
224
- boundary = 'clippingAncestors',
225
- rootBoundary = 'viewport',
226
- elementContext = 'floating',
227
- altBoundary = false,
228
- padding = 0
229
- } = options;
230
- const paddingObject = getSideObjectFromPadding(padding);
231
- const altContext = elementContext === 'floating' ? 'reference' : 'floating';
232
- const element = elements[altBoundary ? altContext : elementContext];
233
- const clippingClientRect = rectToClientRect(await platform.getClippingRect({
234
- element: ((_await$platform$isEle = await (platform.isElement == null ? void 0 : platform.isElement(element))) != null ? _await$platform$isEle : true) ? element : element.contextElement || (await (platform.getDocumentElement == null ? void 0 : platform.getDocumentElement(elements.floating))),
235
- boundary,
236
- rootBoundary,
237
- strategy
238
- }));
239
- const rect = elementContext === 'floating' ? {
240
- ...rects.floating,
241
- x,
242
- y
243
- } : rects.reference;
244
- const offsetParent = await (platform.getOffsetParent == null ? void 0 : platform.getOffsetParent(elements.floating));
245
- const offsetScale = (await (platform.isElement == null ? void 0 : platform.isElement(offsetParent))) ? (await (platform.getScale == null ? void 0 : platform.getScale(offsetParent))) || {
246
- x: 1,
247
- y: 1
248
- } : {
249
- x: 1,
250
- y: 1
251
- };
252
- const elementClientRect = rectToClientRect(platform.convertOffsetParentRelativeRectToViewportRelativeRect ? await platform.convertOffsetParentRelativeRectToViewportRelativeRect({
253
- rect,
254
- offsetParent,
255
- strategy
256
- }) : rect);
257
- return {
258
- top: (clippingClientRect.top - elementClientRect.top + paddingObject.top) / offsetScale.y,
259
- bottom: (elementClientRect.bottom - clippingClientRect.bottom + paddingObject.bottom) / offsetScale.y,
260
- left: (clippingClientRect.left - elementClientRect.left + paddingObject.left) / offsetScale.x,
261
- right: (elementClientRect.right - clippingClientRect.right + paddingObject.right) / offsetScale.x
262
- };
263
- }
264
-
265
- const min$1 = Math.min;
266
- const max$1 = Math.max;
267
-
268
- function within(min$1$1, value, max$1$1) {
269
- return max$1(min$1$1, min$1(value, max$1$1));
270
- }
41
+ const ComboboxAutocomplete = {
42
+ inline: "inline",
43
+ list: "list",
44
+ both: "both",
45
+ none: "none",
46
+ };
271
47
 
272
48
  /**
273
- * A data provider that provides data to position an inner element of the
274
- * floating element (usually a triangle or caret) so that it is centered to the
275
- * reference element.
276
- * @see https://floating-ui.com/docs/arrow
49
+ * A Combobox Custom HTML Element.
50
+ * Implements the {@link https://w3c.github.io/aria-practices/#combobox | ARIA combobox }.
51
+ *
52
+ * @slot start - Content which can be provided before the input
53
+ * @slot end - Content which can be provided after the input
54
+ * @slot control - Used to replace the input element representing the combobox
55
+ * @slot indicator - The visual indicator representing the expanded state
56
+ * @slot - The default slot for the options
57
+ * @csspart control - The wrapper element containing the input area, including start and end
58
+ * @csspart selected-value - The input element representing the selected value
59
+ * @csspart indicator - The element wrapping the indicator slot
60
+ * @csspart listbox - The wrapper for the listbox slotted options
61
+ * @fires change - Fires a custom 'change' event when the value updates
62
+ *
63
+ * @public
277
64
  */
278
- const arrow = options => ({
279
- name: 'arrow',
280
- options,
281
- async fn(middlewareArguments) {
282
- // Since `element` is required, we don't Partial<> the type.
283
- const {
284
- element,
285
- padding = 0
286
- } = options || {};
287
- const {
288
- x,
289
- y,
290
- placement,
291
- rects,
292
- platform
293
- } = middlewareArguments;
294
- if (element == null) {
295
- return {};
65
+ class Combobox$1 extends FormAssociatedCombobox {
66
+ constructor() {
67
+ super(...arguments);
68
+ /**
69
+ * The internal value property.
70
+ *
71
+ * @internal
72
+ */
73
+ this._value = "";
74
+ /**
75
+ * The collection of currently filtered options.
76
+ *
77
+ * @public
78
+ */
79
+ this.filteredOptions = [];
80
+ /**
81
+ * The current filter value.
82
+ *
83
+ * @internal
84
+ */
85
+ this.filter = "";
86
+ /**
87
+ * The initial state of the position attribute.
88
+ *
89
+ * @internal
90
+ */
91
+ this.forcedPosition = false;
92
+ /**
93
+ * The unique id for the internal listbox element.
94
+ *
95
+ * @internal
96
+ */
97
+ this.listboxId = uniqueId("listbox-");
98
+ /**
99
+ * The max height for the listbox when opened.
100
+ *
101
+ * @internal
102
+ */
103
+ this.maxHeight = 0;
104
+ /**
105
+ * The open attribute.
106
+ *
107
+ * @public
108
+ * @remarks
109
+ * HTML Attribute: open
110
+ */
111
+ this.open = false;
296
112
  }
297
- const paddingObject = getSideObjectFromPadding(padding);
298
- const coords = {
299
- x,
300
- y
301
- };
302
- const axis = getMainAxisFromPlacement(placement);
303
- const length = getLengthFromAxis(axis);
304
- const arrowDimensions = await platform.getDimensions(element);
305
- const minProp = axis === 'y' ? 'top' : 'left';
306
- const maxProp = axis === 'y' ? 'bottom' : 'right';
307
- const endDiff = rects.reference[length] + rects.reference[axis] - coords[axis] - rects.floating[length];
308
- const startDiff = coords[axis] - rects.reference[axis];
309
- const arrowOffsetParent = await (platform.getOffsetParent == null ? void 0 : platform.getOffsetParent(element));
310
- let clientSize = arrowOffsetParent ? axis === 'y' ? arrowOffsetParent.clientHeight || 0 : arrowOffsetParent.clientWidth || 0 : 0;
311
- if (clientSize === 0) {
312
- clientSize = rects.floating[length];
113
+ /**
114
+ * Reset the element to its first selectable option when its parent form is reset.
115
+ *
116
+ * @internal
117
+ */
118
+ formResetCallback() {
119
+ super.formResetCallback();
120
+ this.setDefaultSelectedOption();
121
+ this.updateValue();
313
122
  }
314
- const centerToReference = endDiff / 2 - startDiff / 2;
315
-
316
- // Make sure the arrow doesn't overflow the floating element if the center
317
- // point is outside the floating element's bounds.
318
- const min = paddingObject[minProp];
319
- const max = clientSize - arrowDimensions[length] - paddingObject[maxProp];
320
- const center = clientSize / 2 - arrowDimensions[length] / 2 + centerToReference;
321
- const offset = within(min, center, max);
322
-
323
- // If the reference is small enough that the arrow's padding causes it to
324
- // to point to nothing for an aligned placement, adjust the offset of the
325
- // floating element itself. This stops `shift()` from taking action, but can
326
- // be worked around by calling it again after the `arrow()` if desired.
327
- const shouldAddOffset = getAlignment(placement) != null && center != offset && rects.reference[length] / 2 - (center < min ? paddingObject[minProp] : paddingObject[maxProp]) - arrowDimensions[length] / 2 < 0;
328
- const alignmentOffset = shouldAddOffset ? center < min ? min - center : max - center : 0;
329
- return {
330
- [axis]: coords[axis] - alignmentOffset,
331
- data: {
332
- [axis]: offset,
333
- centerOffset: center - offset
334
- }
335
- };
336
- }
337
- });
338
-
339
- const sides = ['top', 'right', 'bottom', 'left'];
340
-
341
- const oppositeSideMap = {
342
- left: 'right',
343
- right: 'left',
344
- bottom: 'top',
345
- top: 'bottom'
346
- };
347
- function getOppositePlacement(placement) {
348
- return placement.replace(/left|right|bottom|top/g, side => oppositeSideMap[side]);
349
- }
350
-
351
- function getAlignmentSides(placement, rects, rtl) {
352
- if (rtl === void 0) {
353
- rtl = false;
354
- }
355
- const alignment = getAlignment(placement);
356
- const mainAxis = getMainAxisFromPlacement(placement);
357
- const length = getLengthFromAxis(mainAxis);
358
- let mainAlignmentSide = mainAxis === 'x' ? alignment === (rtl ? 'end' : 'start') ? 'right' : 'left' : alignment === 'start' ? 'bottom' : 'top';
359
- if (rects.reference[length] > rects.floating[length]) {
360
- mainAlignmentSide = getOppositePlacement(mainAlignmentSide);
361
- }
362
- return {
363
- main: mainAlignmentSide,
364
- cross: getOppositePlacement(mainAlignmentSide)
365
- };
366
- }
367
-
368
- const oppositeAlignmentMap = {
369
- start: 'end',
370
- end: 'start'
371
- };
372
- function getOppositeAlignmentPlacement(placement) {
373
- return placement.replace(/start|end/g, alignment => oppositeAlignmentMap[alignment]);
374
- }
375
-
376
- function getExpandedPlacements(placement) {
377
- const oppositePlacement = getOppositePlacement(placement);
378
- return [getOppositeAlignmentPlacement(placement), oppositePlacement, getOppositeAlignmentPlacement(oppositePlacement)];
379
- }
380
-
381
- function getSideList(side, isStart, rtl) {
382
- const lr = ['left', 'right'];
383
- const rl = ['right', 'left'];
384
- const tb = ['top', 'bottom'];
385
- const bt = ['bottom', 'top'];
386
- switch (side) {
387
- case 'top':
388
- case 'bottom':
389
- if (rtl) return isStart ? rl : lr;
390
- return isStart ? lr : rl;
391
- case 'left':
392
- case 'right':
393
- return isStart ? tb : bt;
394
- default:
395
- return [];
396
- }
397
- }
398
- function getOppositeAxisPlacements(placement, flipAlignment, direction, rtl) {
399
- const alignment = getAlignment(placement);
400
- let list = getSideList(getSide(placement), direction === 'start', rtl);
401
- if (alignment) {
402
- list = list.map(side => side + "-" + alignment);
403
- if (flipAlignment) {
404
- list = list.concat(list.map(getOppositeAlignmentPlacement));
123
+ /** {@inheritDoc (FormAssociated:interface).validate} */
124
+ validate() {
125
+ super.validate(this.control);
405
126
  }
406
- }
407
- return list;
408
- }
409
-
410
- /**
411
- * A visibility optimizer that changes the placement of the floating element in
412
- * order to keep it in view. By default, only the opposite placement is tried.
413
- *
414
- * It has the ability to flip to any placement, not just the opposite one. You
415
- * can use a series of “fallback” placements, where each placement is
416
- * progressively tried until the one that fits can be used.
417
- * @see https://floating-ui.com/docs/flip
418
- */
419
- const flip = function (options) {
420
- if (options === void 0) {
421
- options = {};
422
- }
423
- return {
424
- name: 'flip',
425
- options,
426
- async fn(middlewareArguments) {
427
- var _middlewareData$flip;
428
- const {
429
- placement,
430
- middlewareData,
431
- rects,
432
- initialPlacement,
433
- platform,
434
- elements
435
- } = middlewareArguments;
436
- const {
437
- mainAxis: checkMainAxis = true,
438
- crossAxis: checkCrossAxis = true,
439
- fallbackPlacements: specifiedFallbackPlacements,
440
- fallbackStrategy = 'bestFit',
441
- fallbackAxisSideDirection = 'none',
442
- flipAlignment = true,
443
- ...detectOverflowOptions
444
- } = options;
445
- const side = getSide(placement);
446
- const isBasePlacement = getSide(initialPlacement) === initialPlacement;
447
- const rtl = await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating));
448
- const fallbackPlacements = specifiedFallbackPlacements || (isBasePlacement || !flipAlignment ? [getOppositePlacement(initialPlacement)] : getExpandedPlacements(initialPlacement));
449
- if (!specifiedFallbackPlacements && fallbackAxisSideDirection !== 'none') {
450
- fallbackPlacements.push(...getOppositeAxisPlacements(initialPlacement, flipAlignment, fallbackAxisSideDirection, rtl));
451
- }
452
- const placements = [initialPlacement, ...fallbackPlacements];
453
- const overflow = await detectOverflow(middlewareArguments, detectOverflowOptions);
454
- const overflows = [];
455
- let overflowsData = ((_middlewareData$flip = middlewareData.flip) == null ? void 0 : _middlewareData$flip.overflows) || [];
456
- if (checkMainAxis) {
457
- overflows.push(overflow[side]);
458
- }
459
- if (checkCrossAxis) {
460
- const {
461
- main,
462
- cross
463
- } = getAlignmentSides(placement, rects, rtl);
464
- overflows.push(overflow[main], overflow[cross]);
465
- }
466
- overflowsData = [...overflowsData, {
467
- placement,
468
- overflows
469
- }];
470
-
471
- // One or more sides is overflowing.
472
- if (!overflows.every(side => side <= 0)) {
473
- var _middlewareData$flip2, _overflowsData$filter;
474
- const nextIndex = (((_middlewareData$flip2 = middlewareData.flip) == null ? void 0 : _middlewareData$flip2.index) || 0) + 1;
475
- const nextPlacement = placements[nextIndex];
476
- if (nextPlacement) {
477
- // Try next placement and re-run the lifecycle.
478
- return {
479
- data: {
480
- index: nextIndex,
481
- overflows: overflowsData
482
- },
483
- reset: {
484
- placement: nextPlacement
485
- }
486
- };
127
+ get isAutocompleteInline() {
128
+ return (this.autocomplete === ComboboxAutocomplete.inline || this.isAutocompleteBoth);
129
+ }
130
+ get isAutocompleteList() {
131
+ return this.autocomplete === ComboboxAutocomplete.list || this.isAutocompleteBoth;
132
+ }
133
+ get isAutocompleteBoth() {
134
+ return this.autocomplete === ComboboxAutocomplete.both;
135
+ }
136
+ /**
137
+ * Sets focus and synchronize ARIA attributes when the open property changes.
138
+ *
139
+ * @param prev - the previous open value
140
+ * @param next - the current open value
141
+ *
142
+ * @internal
143
+ */
144
+ openChanged() {
145
+ if (this.open) {
146
+ this.ariaControls = this.listboxId;
147
+ this.ariaExpanded = "true";
148
+ this.setPositioning();
149
+ this.focusAndScrollOptionIntoView();
150
+ // focus is directed to the element when `open` is changed programmatically
151
+ DOM.queueUpdate(() => this.focus());
152
+ return;
487
153
  }
488
-
489
- // First, find the candidates that fit on the mainAxis side of overflow,
490
- // then find the placement that fits the best on the main crossAxis side.
491
- let resetPlacement = (_overflowsData$filter = overflowsData.filter(d => d.overflows[0] <= 0).sort((a, b) => a.overflows[1] - b.overflows[1])[0]) == null ? void 0 : _overflowsData$filter.placement;
492
-
493
- // Otherwise fallback.
494
- if (!resetPlacement) {
495
- switch (fallbackStrategy) {
496
- case 'bestFit':
497
- {
498
- var _overflowsData$map$so;
499
- const placement = (_overflowsData$map$so = overflowsData.map(d => [d.placement, d.overflows.filter(overflow => overflow > 0).reduce((acc, overflow) => acc + overflow, 0)]).sort((a, b) => a[1] - b[1])[0]) == null ? void 0 : _overflowsData$map$so[0];
500
- if (placement) {
501
- resetPlacement = placement;
502
- }
503
- break;
504
- }
505
- case 'initialPlacement':
506
- resetPlacement = initialPlacement;
507
- break;
508
- }
154
+ this.ariaControls = "";
155
+ this.ariaExpanded = "false";
156
+ }
157
+ /**
158
+ * The list of options.
159
+ *
160
+ * @public
161
+ * @remarks
162
+ * Overrides `Listbox.options`.
163
+ */
164
+ get options() {
165
+ Observable.track(this, "options");
166
+ return this.filteredOptions.length ? this.filteredOptions : this._options;
167
+ }
168
+ set options(value) {
169
+ this._options = value;
170
+ Observable.notify(this, "options");
171
+ }
172
+ /**
173
+ * Updates the placeholder on the proxy element.
174
+ * @internal
175
+ */
176
+ placeholderChanged() {
177
+ if (this.proxy instanceof HTMLInputElement) {
178
+ this.proxy.placeholder = this.placeholder;
179
+ }
180
+ }
181
+ positionChanged(prev, next) {
182
+ this.positionAttribute = next;
183
+ this.setPositioning();
184
+ }
185
+ /**
186
+ * The value property.
187
+ *
188
+ * @public
189
+ */
190
+ get value() {
191
+ Observable.track(this, "value");
192
+ return this._value;
193
+ }
194
+ set value(next) {
195
+ var _a, _b, _c;
196
+ const prev = `${this._value}`;
197
+ if (this.$fastController.isConnected && this.options) {
198
+ const selectedIndex = this.options.findIndex(el => el.text.toLowerCase() === next.toLowerCase());
199
+ const prevSelectedValue = (_a = this.options[this.selectedIndex]) === null || _a === void 0 ? void 0 : _a.text;
200
+ const nextSelectedValue = (_b = this.options[selectedIndex]) === null || _b === void 0 ? void 0 : _b.text;
201
+ this.selectedIndex =
202
+ prevSelectedValue !== nextSelectedValue
203
+ ? selectedIndex
204
+ : this.selectedIndex;
205
+ next = ((_c = this.firstSelectedOption) === null || _c === void 0 ? void 0 : _c.text) || next;
206
+ }
207
+ if (prev !== next) {
208
+ this._value = next;
209
+ super.valueChanged(prev, next);
210
+ Observable.notify(this, "value");
509
211
  }
510
- if (placement !== resetPlacement) {
511
- return {
512
- reset: {
513
- placement: resetPlacement
212
+ }
213
+ /**
214
+ * Handle opening and closing the listbox when the combobox is clicked.
215
+ *
216
+ * @param e - the mouse event
217
+ * @internal
218
+ */
219
+ clickHandler(e) {
220
+ if (this.disabled) {
221
+ return;
222
+ }
223
+ if (this.open) {
224
+ const captured = e.target.closest(`option,[role=option]`);
225
+ if (!captured || captured.disabled) {
226
+ return;
514
227
  }
515
- };
228
+ this.selectedOptions = [captured];
229
+ this.control.value = captured.text;
230
+ this.clearSelectionRange();
231
+ this.updateValue(true);
232
+ }
233
+ this.open = !this.open;
234
+ if (this.open) {
235
+ this.control.focus();
516
236
  }
517
- }
518
- return {};
237
+ return true;
519
238
  }
520
- };
521
- };
522
-
523
- function getSideOffsets(overflow, rect) {
524
- return {
525
- top: overflow.top - rect.height,
526
- right: overflow.right - rect.width,
527
- bottom: overflow.bottom - rect.height,
528
- left: overflow.left - rect.width
529
- };
530
- }
531
- function isAnySideFullyClipped(overflow) {
532
- return sides.some(side => overflow[side] >= 0);
533
- }
534
- /**
535
- * A data provider that allows you to hide the floating element in applicable
536
- * situations, usually when it’s not within the same clipping context as the
537
- * reference element.
538
- * @see https://floating-ui.com/docs/hide
539
- */
540
- const hide = function (options) {
541
- if (options === void 0) {
542
- options = {};
543
- }
544
- return {
545
- name: 'hide',
546
- options,
547
- async fn(middlewareArguments) {
548
- const {
549
- strategy = 'referenceHidden',
550
- ...detectOverflowOptions
551
- } = options;
552
- const {
553
- rects
554
- } = middlewareArguments;
555
- switch (strategy) {
556
- case 'referenceHidden':
557
- {
558
- const overflow = await detectOverflow(middlewareArguments, {
559
- ...detectOverflowOptions,
560
- elementContext: 'reference'
561
- });
562
- const offsets = getSideOffsets(overflow, rects.reference);
563
- return {
564
- data: {
565
- referenceHiddenOffsets: offsets,
566
- referenceHidden: isAnySideFullyClipped(offsets)
567
- }
568
- };
569
- }
570
- case 'escaped':
571
- {
572
- const overflow = await detectOverflow(middlewareArguments, {
573
- ...detectOverflowOptions,
574
- altBoundary: true
575
- });
576
- const offsets = getSideOffsets(overflow, rects.floating);
577
- return {
578
- data: {
579
- escapedOffsets: offsets,
580
- escaped: isAnySideFullyClipped(offsets)
581
- }
582
- };
583
- }
584
- default:
585
- {
586
- return {};
587
- }
588
- }
239
+ connectedCallback() {
240
+ super.connectedCallback();
241
+ this.forcedPosition = !!this.positionAttribute;
242
+ if (this.value) {
243
+ this.initialValue = this.value;
244
+ }
589
245
  }
590
- };
591
- };
592
-
593
- /**
594
- * Provides improved positioning for inline reference elements that can span
595
- * over multiple lines, such as hyperlinks or range selections.
596
- * @see https://floating-ui.com/docs/inline
597
- */
598
- const inline = function (options) {
599
- if (options === void 0) {
600
- options = {};
601
- }
602
- return {
603
- name: 'inline',
604
- options,
605
- async fn(middlewareArguments) {
606
- const {
607
- placement,
608
- elements,
609
- rects,
610
- platform,
611
- strategy
612
- } = middlewareArguments;
613
- // A MouseEvent's client{X,Y} coords can be up to 2 pixels off a
614
- // ClientRect's bounds, despite the event listener being triggered. A
615
- // padding of 2 seems to handle this issue.
616
- const {
617
- padding = 2,
618
- x,
619
- y
620
- } = options;
621
- const fallback = rectToClientRect(platform.convertOffsetParentRelativeRectToViewportRelativeRect ? await platform.convertOffsetParentRelativeRectToViewportRelativeRect({
622
- rect: rects.reference,
623
- offsetParent: await (platform.getOffsetParent == null ? void 0 : platform.getOffsetParent(elements.floating)),
624
- strategy
625
- }) : rects.reference);
626
- const clientRects = (await (platform.getClientRects == null ? void 0 : platform.getClientRects(elements.reference))) || [];
627
- const paddingObject = getSideObjectFromPadding(padding);
628
- function getBoundingClientRect() {
629
- // There are two rects and they are disjoined.
630
- if (clientRects.length === 2 && clientRects[0].left > clientRects[1].right && x != null && y != null) {
631
- // Find the first rect in which the point is fully inside.
632
- return clientRects.find(rect => x > rect.left - paddingObject.left && x < rect.right + paddingObject.right && y > rect.top - paddingObject.top && y < rect.bottom + paddingObject.bottom) || fallback;
246
+ /**
247
+ * Synchronize the `aria-disabled` property when the `disabled` property changes.
248
+ *
249
+ * @param prev - The previous disabled value
250
+ * @param next - The next disabled value
251
+ *
252
+ * @internal
253
+ */
254
+ disabledChanged(prev, next) {
255
+ if (super.disabledChanged) {
256
+ super.disabledChanged(prev, next);
633
257
  }
634
-
635
- // There are 2 or more connected rects.
636
- if (clientRects.length >= 2) {
637
- if (getMainAxisFromPlacement(placement) === 'x') {
638
- const firstRect = clientRects[0];
639
- const lastRect = clientRects[clientRects.length - 1];
640
- const isTop = getSide(placement) === 'top';
641
- const top = firstRect.top;
642
- const bottom = lastRect.bottom;
643
- const left = isTop ? firstRect.left : lastRect.left;
644
- const right = isTop ? firstRect.right : lastRect.right;
645
- const width = right - left;
646
- const height = bottom - top;
647
- return {
648
- top,
649
- bottom,
650
- left,
651
- right,
652
- width,
653
- height,
654
- x: left,
655
- y: top
656
- };
657
- }
658
- const isLeftSide = getSide(placement) === 'left';
659
- const maxRight = max$1(...clientRects.map(rect => rect.right));
660
- const minLeft = min$1(...clientRects.map(rect => rect.left));
661
- const measureRects = clientRects.filter(rect => isLeftSide ? rect.left === minLeft : rect.right === maxRight);
662
- const top = measureRects[0].top;
663
- const bottom = measureRects[measureRects.length - 1].bottom;
664
- const left = minLeft;
665
- const right = maxRight;
666
- const width = right - left;
667
- const height = bottom - top;
668
- return {
669
- top,
670
- bottom,
671
- left,
672
- right,
673
- width,
674
- height,
675
- x: left,
676
- y: top
677
- };
258
+ this.ariaDisabled = this.disabled ? "true" : "false";
259
+ }
260
+ /**
261
+ * Filter available options by text value.
262
+ *
263
+ * @public
264
+ */
265
+ filterOptions() {
266
+ if (!this.autocomplete || this.autocomplete === ComboboxAutocomplete.none) {
267
+ this.filter = "";
268
+ }
269
+ const filter = this.filter.toLowerCase();
270
+ this.filteredOptions = this._options.filter(o => o.text.toLowerCase().startsWith(this.filter.toLowerCase()));
271
+ if (this.isAutocompleteList) {
272
+ if (!this.filteredOptions.length && !filter) {
273
+ this.filteredOptions = this._options;
274
+ }
275
+ this._options.forEach(o => {
276
+ o.hidden = !this.filteredOptions.includes(o);
277
+ });
678
278
  }
679
- return fallback;
680
- }
681
- const resetRects = await platform.getElementRects({
682
- reference: {
683
- getBoundingClientRect
684
- },
685
- floating: elements.floating,
686
- strategy
687
- });
688
- if (rects.reference.x !== resetRects.reference.x || rects.reference.y !== resetRects.reference.y || rects.reference.width !== resetRects.reference.width || rects.reference.height !== resetRects.reference.height) {
689
- return {
690
- reset: {
691
- rects: resetRects
692
- }
693
- };
694
- }
695
- return {};
696
279
  }
697
- };
698
- };
699
-
700
- async function convertValueToCoords(middlewareArguments, value) {
701
- const {
702
- placement,
703
- platform,
704
- elements
705
- } = middlewareArguments;
706
- const rtl = await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating));
707
- const side = getSide(placement);
708
- const alignment = getAlignment(placement);
709
- const isVertical = getMainAxisFromPlacement(placement) === 'x';
710
- const mainAxisMulti = ['left', 'top'].includes(side) ? -1 : 1;
711
- const crossAxisMulti = rtl && isVertical ? -1 : 1;
712
- const rawValue = typeof value === 'function' ? value(middlewareArguments) : value;
713
-
714
- // eslint-disable-next-line prefer-const
715
- let {
716
- mainAxis,
717
- crossAxis,
718
- alignmentAxis
719
- } = typeof rawValue === 'number' ? {
720
- mainAxis: rawValue,
721
- crossAxis: 0,
722
- alignmentAxis: null
723
- } : {
724
- mainAxis: 0,
725
- crossAxis: 0,
726
- alignmentAxis: null,
727
- ...rawValue
728
- };
729
- if (alignment && typeof alignmentAxis === 'number') {
730
- crossAxis = alignment === 'end' ? alignmentAxis * -1 : alignmentAxis;
731
- }
732
- return isVertical ? {
733
- x: crossAxis * crossAxisMulti,
734
- y: mainAxis * mainAxisMulti
735
- } : {
736
- x: mainAxis * mainAxisMulti,
737
- y: crossAxis * crossAxisMulti
738
- };
739
- }
740
-
741
- /**
742
- * A placement modifier that translates the floating element along the specified
743
- * axes.
744
- * A number (shorthand for `mainAxis` or distance), or an axes configuration
745
- * object may be passed.
746
- * @see https://floating-ui.com/docs/offset
747
- */
748
- const offset = function (value) {
749
- if (value === void 0) {
750
- value = 0;
751
- }
752
- return {
753
- name: 'offset',
754
- options: value,
755
- async fn(middlewareArguments) {
756
- const {
757
- x,
758
- y
759
- } = middlewareArguments;
760
- const diffCoords = await convertValueToCoords(middlewareArguments, value);
761
- return {
762
- x: x + diffCoords.x,
763
- y: y + diffCoords.y,
764
- data: diffCoords
765
- };
280
+ /**
281
+ * Focus the control and scroll the first selected option into view.
282
+ *
283
+ * @internal
284
+ * @remarks
285
+ * Overrides: `Listbox.focusAndScrollOptionIntoView`
286
+ */
287
+ focusAndScrollOptionIntoView() {
288
+ if (this.contains(document.activeElement)) {
289
+ this.control.focus();
290
+ if (this.firstSelectedOption) {
291
+ requestAnimationFrame(() => {
292
+ var _a;
293
+ (_a = this.firstSelectedOption) === null || _a === void 0 ? void 0 : _a.scrollIntoView({ block: "nearest" });
294
+ });
295
+ }
296
+ }
766
297
  }
767
- };
768
- };
769
-
770
- function getWindow(node) {
771
- var _node$ownerDocument;
772
- return ((_node$ownerDocument = node.ownerDocument) == null ? void 0 : _node$ownerDocument.defaultView) || window;
773
- }
774
-
775
- function getComputedStyle$1(element) {
776
- return getWindow(element).getComputedStyle(element);
777
- }
778
-
779
- const min = Math.min;
780
- const max = Math.max;
781
- const round = Math.round;
782
-
783
- function getCssDimensions(element) {
784
- const css = getComputedStyle$1(element);
785
- let width = parseFloat(css.width);
786
- let height = parseFloat(css.height);
787
- const offsetWidth = element.offsetWidth;
788
- const offsetHeight = element.offsetHeight;
789
- const shouldFallback = round(width) !== offsetWidth || round(height) !== offsetHeight;
790
- if (shouldFallback) {
791
- width = offsetWidth;
792
- height = offsetHeight;
793
- }
794
- return {
795
- width,
796
- height,
797
- fallback: shouldFallback
798
- };
799
- }
800
-
801
- function getNodeName(node) {
802
- return isNode(node) ? (node.nodeName || '').toLowerCase() : '';
803
- }
804
-
805
- let uaString;
806
- function getUAString() {
807
- if (uaString) {
808
- return uaString;
809
- }
810
- const uaData = navigator.userAgentData;
811
- if (uaData && Array.isArray(uaData.brands)) {
812
- uaString = uaData.brands.map(item => item.brand + "/" + item.version).join(' ');
813
- return uaString;
814
- }
815
- return navigator.userAgent;
816
- }
817
-
818
- function isHTMLElement(value) {
819
- return value instanceof getWindow(value).HTMLElement;
820
- }
821
- function isElement(value) {
822
- return value instanceof getWindow(value).Element;
823
- }
824
- function isNode(value) {
825
- return value instanceof getWindow(value).Node;
826
- }
827
- function isShadowRoot(node) {
828
- // Browsers without `ShadowRoot` support.
829
- if (typeof ShadowRoot === 'undefined') {
830
- return false;
831
- }
832
- const OwnElement = getWindow(node).ShadowRoot;
833
- return node instanceof OwnElement || node instanceof ShadowRoot;
834
- }
835
- function isOverflowElement(element) {
836
- const {
837
- overflow,
838
- overflowX,
839
- overflowY,
840
- display
841
- } = getComputedStyle$1(element);
842
- return /auto|scroll|overlay|hidden|clip/.test(overflow + overflowY + overflowX) && !['inline', 'contents'].includes(display);
843
- }
844
- function isTableElement(element) {
845
- return ['table', 'td', 'th'].includes(getNodeName(element));
846
- }
847
- function isContainingBlock(element) {
848
- // TODO: Try to use feature detection here instead.
849
- const isFirefox = /firefox/i.test(getUAString());
850
- const css = getComputedStyle$1(element);
851
- const backdropFilter = css.backdropFilter || css.WebkitBackdropFilter;
852
-
853
- // This is non-exhaustive but covers the most common CSS properties that
854
- // create a containing block.
855
- // https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block
856
- return css.transform !== 'none' || css.perspective !== 'none' || (backdropFilter ? backdropFilter !== 'none' : false) || isFirefox && css.willChange === 'filter' || isFirefox && (css.filter ? css.filter !== 'none' : false) || ['transform', 'perspective'].some(value => css.willChange.includes(value)) || ['paint', 'layout', 'strict', 'content'].some(value => {
857
- // Add type check for old browsers.
858
- const contain = css.contain;
859
- return contain != null ? contain.includes(value) : false;
860
- });
861
- }
862
-
863
- /**
864
- * Determines whether or not `.getBoundingClientRect()` is affected by visual
865
- * viewport offsets. In Safari, the `x`/`y` offsets are values relative to the
866
- * visual viewport, while in other engines, they are values relative to the
867
- * layout viewport.
868
- */
869
- function isClientRectVisualViewportBased() {
870
- // TODO: Try to use feature detection here instead. Feature detection for
871
- // this can fail in various ways, making the userAgent check the most
872
- // reliable:
873
- // • Always-visible scrollbar or not
874
- // • Width of <html>
875
-
876
- // Is Safari.
877
- return /^((?!chrome|android).)*safari/i.test(getUAString());
878
- }
879
- function isLastTraversableNode(node) {
880
- return ['html', 'body', '#document'].includes(getNodeName(node));
881
- }
882
-
883
- function unwrapElement(element) {
884
- return !isElement(element) ? element.contextElement : element;
885
- }
886
-
887
- const FALLBACK_SCALE = {
888
- x: 1,
889
- y: 1
890
- };
891
- function getScale(element) {
892
- const domElement = unwrapElement(element);
893
- if (!isHTMLElement(domElement)) {
894
- return FALLBACK_SCALE;
895
- }
896
- const rect = domElement.getBoundingClientRect();
897
- const {
898
- width,
899
- height,
900
- fallback
901
- } = getCssDimensions(domElement);
902
- let x = (fallback ? round(rect.width) : rect.width) / width;
903
- let y = (fallback ? round(rect.height) : rect.height) / height;
904
-
905
- // 0, NaN, or Infinity should always fallback to 1.
906
-
907
- if (!x || !Number.isFinite(x)) {
908
- x = 1;
909
- }
910
- if (!y || !Number.isFinite(y)) {
911
- y = 1;
912
- }
913
- return {
914
- x,
915
- y
916
- };
917
- }
918
-
919
- function getBoundingClientRect(element, includeScale, isFixedStrategy, offsetParent) {
920
- var _win$visualViewport, _win$visualViewport2;
921
- if (includeScale === void 0) {
922
- includeScale = false;
923
- }
924
- if (isFixedStrategy === void 0) {
925
- isFixedStrategy = false;
926
- }
927
- const clientRect = element.getBoundingClientRect();
928
- const domElement = unwrapElement(element);
929
- let scale = FALLBACK_SCALE;
930
- if (includeScale) {
931
- if (offsetParent) {
932
- if (isElement(offsetParent)) {
933
- scale = getScale(offsetParent);
934
- }
935
- } else {
936
- scale = getScale(element);
298
+ /**
299
+ * Handle focus state when the element or its children lose focus.
300
+ *
301
+ * @param e - The focus event
302
+ * @internal
303
+ */
304
+ focusoutHandler(e) {
305
+ this.syncValue();
306
+ if (!this.open) {
307
+ return true;
308
+ }
309
+ const focusTarget = e.relatedTarget;
310
+ if (this.isSameNode(focusTarget)) {
311
+ this.focus();
312
+ return;
313
+ }
314
+ if (!this.options || !this.options.includes(focusTarget)) {
315
+ this.open = false;
316
+ }
937
317
  }
938
- }
939
- const win = domElement ? getWindow(domElement) : window;
940
- const addVisualOffsets = isClientRectVisualViewportBased() && isFixedStrategy;
941
- let x = (clientRect.left + (addVisualOffsets ? ((_win$visualViewport = win.visualViewport) == null ? void 0 : _win$visualViewport.offsetLeft) || 0 : 0)) / scale.x;
942
- let y = (clientRect.top + (addVisualOffsets ? ((_win$visualViewport2 = win.visualViewport) == null ? void 0 : _win$visualViewport2.offsetTop) || 0 : 0)) / scale.y;
943
- let width = clientRect.width / scale.x;
944
- let height = clientRect.height / scale.y;
945
- if (domElement) {
946
- const win = getWindow(domElement);
947
- const offsetWin = offsetParent && isElement(offsetParent) ? getWindow(offsetParent) : offsetParent;
948
- let currentIFrame = win.frameElement;
949
- while (currentIFrame && offsetParent && offsetWin !== win) {
950
- const iframeScale = getScale(currentIFrame);
951
- const iframeRect = currentIFrame.getBoundingClientRect();
952
- const css = getComputedStyle(currentIFrame);
953
- iframeRect.x += (currentIFrame.clientLeft + parseFloat(css.paddingLeft)) * iframeScale.x;
954
- iframeRect.y += (currentIFrame.clientTop + parseFloat(css.paddingTop)) * iframeScale.y;
955
- x *= iframeScale.x;
956
- y *= iframeScale.y;
957
- width *= iframeScale.x;
958
- height *= iframeScale.y;
959
- x += iframeRect.x;
960
- y += iframeRect.y;
961
- currentIFrame = getWindow(currentIFrame).frameElement;
318
+ /**
319
+ * Handle content changes on the control input.
320
+ *
321
+ * @param e - the input event
322
+ * @internal
323
+ */
324
+ inputHandler(e) {
325
+ this.filter = this.control.value;
326
+ this.filterOptions();
327
+ if (!this.isAutocompleteInline) {
328
+ this.selectedIndex = this.options
329
+ .map(option => option.text)
330
+ .indexOf(this.control.value);
331
+ }
332
+ if (e.inputType.includes("deleteContent") || !this.filter.length) {
333
+ return true;
334
+ }
335
+ if (this.isAutocompleteList && !this.open) {
336
+ this.open = true;
337
+ }
338
+ if (this.isAutocompleteInline) {
339
+ if (this.filteredOptions.length) {
340
+ this.selectedOptions = [this.filteredOptions[0]];
341
+ this.selectedIndex = this.options.indexOf(this.firstSelectedOption);
342
+ this.setInlineSelection();
343
+ }
344
+ else {
345
+ this.selectedIndex = -1;
346
+ }
347
+ }
348
+ return;
962
349
  }
963
- }
964
- return {
965
- width,
966
- height,
967
- top: y,
968
- right: x + width,
969
- bottom: y + height,
970
- left: x,
971
- x,
972
- y
973
- };
974
- }
975
-
976
- function getDocumentElement(node) {
977
- return ((isNode(node) ? node.ownerDocument : node.document) || window.document).documentElement;
978
- }
979
-
980
- function getNodeScroll(element) {
981
- if (isElement(element)) {
982
- return {
983
- scrollLeft: element.scrollLeft,
984
- scrollTop: element.scrollTop
985
- };
986
- }
987
- return {
988
- scrollLeft: element.pageXOffset,
989
- scrollTop: element.pageYOffset
990
- };
991
- }
992
-
993
- function convertOffsetParentRelativeRectToViewportRelativeRect(_ref) {
994
- let {
995
- rect,
996
- offsetParent,
997
- strategy
998
- } = _ref;
999
- const isOffsetParentAnElement = isHTMLElement(offsetParent);
1000
- const documentElement = getDocumentElement(offsetParent);
1001
- if (offsetParent === documentElement) {
1002
- return rect;
1003
- }
1004
- let scroll = {
1005
- scrollLeft: 0,
1006
- scrollTop: 0
1007
- };
1008
- let scale = {
1009
- x: 1,
1010
- y: 1
1011
- };
1012
- const offsets = {
1013
- x: 0,
1014
- y: 0
1015
- };
1016
- if (isOffsetParentAnElement || !isOffsetParentAnElement && strategy !== 'fixed') {
1017
- if (getNodeName(offsetParent) !== 'body' || isOverflowElement(documentElement)) {
1018
- scroll = getNodeScroll(offsetParent);
350
+ /**
351
+ * Handle keydown actions for listbox navigation.
352
+ *
353
+ * @param e - the keyboard event
354
+ * @internal
355
+ */
356
+ keydownHandler(e) {
357
+ const key = e.key;
358
+ if (e.ctrlKey || e.shiftKey) {
359
+ return true;
360
+ }
361
+ switch (key) {
362
+ case "Enter": {
363
+ this.syncValue();
364
+ if (this.isAutocompleteInline) {
365
+ this.filter = this.value;
366
+ }
367
+ this.open = false;
368
+ this.clearSelectionRange();
369
+ break;
370
+ }
371
+ case "Escape": {
372
+ if (!this.isAutocompleteInline) {
373
+ this.selectedIndex = -1;
374
+ }
375
+ if (this.open) {
376
+ this.open = false;
377
+ break;
378
+ }
379
+ this.value = "";
380
+ this.control.value = "";
381
+ this.filter = "";
382
+ this.filterOptions();
383
+ break;
384
+ }
385
+ case "Tab": {
386
+ this.setInputToSelection();
387
+ if (!this.open) {
388
+ return true;
389
+ }
390
+ e.preventDefault();
391
+ this.open = false;
392
+ break;
393
+ }
394
+ case "ArrowUp":
395
+ case "ArrowDown": {
396
+ this.filterOptions();
397
+ if (!this.open) {
398
+ this.open = true;
399
+ break;
400
+ }
401
+ if (this.filteredOptions.length > 0) {
402
+ super.keydownHandler(e);
403
+ }
404
+ if (this.isAutocompleteInline) {
405
+ this.setInlineSelection();
406
+ }
407
+ break;
408
+ }
409
+ default: {
410
+ return true;
411
+ }
412
+ }
1019
413
  }
1020
- if (isHTMLElement(offsetParent)) {
1021
- const offsetRect = getBoundingClientRect(offsetParent);
1022
- scale = getScale(offsetParent);
1023
- offsets.x = offsetRect.x + offsetParent.clientLeft;
1024
- offsets.y = offsetRect.y + offsetParent.clientTop;
414
+ /**
415
+ * Handle keyup actions for value input and text field manipulations.
416
+ *
417
+ * @param e - the keyboard event
418
+ * @internal
419
+ */
420
+ keyupHandler(e) {
421
+ const key = e.key;
422
+ switch (key) {
423
+ case "ArrowLeft":
424
+ case "ArrowRight":
425
+ case "Backspace":
426
+ case "Delete":
427
+ case "Home":
428
+ case "End": {
429
+ this.filter = this.control.value;
430
+ this.selectedIndex = -1;
431
+ this.filterOptions();
432
+ break;
433
+ }
434
+ }
1025
435
  }
1026
- }
1027
- return {
1028
- width: rect.width * scale.x,
1029
- height: rect.height * scale.y,
1030
- x: rect.x * scale.x - scroll.scrollLeft * scale.x + offsets.x,
1031
- y: rect.y * scale.y - scroll.scrollTop * scale.y + offsets.y
1032
- };
1033
- }
1034
-
1035
- function getWindowScrollBarX(element) {
1036
- // If <html> has a CSS width greater than the viewport, then this will be
1037
- // incorrect for RTL.
1038
- return getBoundingClientRect(getDocumentElement(element)).left + getNodeScroll(element).scrollLeft;
1039
- }
1040
-
1041
- // Gets the entire size of the scrollable document area, even extending outside
1042
- // of the `<html>` and `<body>` rect bounds if horizontally scrollable.
1043
- function getDocumentRect(element) {
1044
- const html = getDocumentElement(element);
1045
- const scroll = getNodeScroll(element);
1046
- const body = element.ownerDocument.body;
1047
- const width = max(html.scrollWidth, html.clientWidth, body.scrollWidth, body.clientWidth);
1048
- const height = max(html.scrollHeight, html.clientHeight, body.scrollHeight, body.clientHeight);
1049
- let x = -scroll.scrollLeft + getWindowScrollBarX(element);
1050
- const y = -scroll.scrollTop;
1051
- if (getComputedStyle$1(body).direction === 'rtl') {
1052
- x += max(html.clientWidth, body.clientWidth) - width;
1053
- }
1054
- return {
1055
- width,
1056
- height,
1057
- x,
1058
- y
1059
- };
1060
- }
1061
-
1062
- function getParentNode(node) {
1063
- if (getNodeName(node) === 'html') {
1064
- return node;
1065
- }
1066
- const result =
1067
- // Step into the shadow DOM of the parent of a slotted node.
1068
- node.assignedSlot ||
1069
- // DOM Element detected.
1070
- node.parentNode ||
1071
- // ShadowRoot detected.
1072
- isShadowRoot(node) && node.host ||
1073
- // Fallback.
1074
- getDocumentElement(node);
1075
- return isShadowRoot(result) ? result.host : result;
1076
- }
1077
-
1078
- function getNearestOverflowAncestor(node) {
1079
- const parentNode = getParentNode(node);
1080
- if (isLastTraversableNode(parentNode)) {
1081
- // `getParentNode` will never return a `Document` due to the fallback
1082
- // check, so it's either the <html> or <body> element.
1083
- return parentNode.ownerDocument.body;
1084
- }
1085
- if (isHTMLElement(parentNode) && isOverflowElement(parentNode)) {
1086
- return parentNode;
1087
- }
1088
- return getNearestOverflowAncestor(parentNode);
1089
- }
1090
-
1091
- function getOverflowAncestors(node, list) {
1092
- var _node$ownerDocument;
1093
- if (list === void 0) {
1094
- list = [];
1095
- }
1096
- const scrollableAncestor = getNearestOverflowAncestor(node);
1097
- const isBody = scrollableAncestor === ((_node$ownerDocument = node.ownerDocument) == null ? void 0 : _node$ownerDocument.body);
1098
- const win = getWindow(scrollableAncestor);
1099
- if (isBody) {
1100
- return list.concat(win, win.visualViewport || [], isOverflowElement(scrollableAncestor) ? scrollableAncestor : []);
1101
- }
1102
- return list.concat(scrollableAncestor, getOverflowAncestors(scrollableAncestor));
1103
- }
1104
-
1105
- function getViewportRect(element, strategy) {
1106
- const win = getWindow(element);
1107
- const html = getDocumentElement(element);
1108
- const visualViewport = win.visualViewport;
1109
- let width = html.clientWidth;
1110
- let height = html.clientHeight;
1111
- let x = 0;
1112
- let y = 0;
1113
- if (visualViewport) {
1114
- width = visualViewport.width;
1115
- height = visualViewport.height;
1116
- const visualViewportBased = isClientRectVisualViewportBased();
1117
- if (!visualViewportBased || visualViewportBased && strategy === 'fixed') {
1118
- x = visualViewport.offsetLeft;
1119
- y = visualViewport.offsetTop;
436
+ /**
437
+ * Ensure that the selectedIndex is within the current allowable filtered range.
438
+ *
439
+ * @param prev - the previous selected index value
440
+ * @param next - the current selected index value
441
+ *
442
+ * @internal
443
+ */
444
+ selectedIndexChanged(prev, next) {
445
+ if (this.$fastController.isConnected) {
446
+ next = limit(-1, this.options.length - 1, next);
447
+ // we only want to call the super method when the selectedIndex is in range
448
+ if (next !== this.selectedIndex) {
449
+ this.selectedIndex = next;
450
+ return;
451
+ }
452
+ super.selectedIndexChanged(prev, next);
453
+ }
1120
454
  }
1121
- }
1122
- return {
1123
- width,
1124
- height,
1125
- x,
1126
- y
1127
- };
1128
- }
1129
-
1130
- // Returns the inner client rect, subtracting scrollbars if present.
1131
- function getInnerBoundingClientRect(element, strategy) {
1132
- const clientRect = getBoundingClientRect(element, true, strategy === 'fixed');
1133
- const top = clientRect.top + element.clientTop;
1134
- const left = clientRect.left + element.clientLeft;
1135
- const scale = isHTMLElement(element) ? getScale(element) : {
1136
- x: 1,
1137
- y: 1
1138
- };
1139
- const width = element.clientWidth * scale.x;
1140
- const height = element.clientHeight * scale.y;
1141
- const x = left * scale.x;
1142
- const y = top * scale.y;
1143
- return {
1144
- width,
1145
- height,
1146
- x,
1147
- y
1148
- };
1149
- }
1150
- function getClientRectFromClippingAncestor(element, clippingAncestor, strategy) {
1151
- let rect;
1152
- if (clippingAncestor === 'viewport') {
1153
- rect = getViewportRect(element, strategy);
1154
- } else if (clippingAncestor === 'document') {
1155
- rect = getDocumentRect(getDocumentElement(element));
1156
- } else if (isElement(clippingAncestor)) {
1157
- rect = getInnerBoundingClientRect(clippingAncestor, strategy);
1158
- } else {
1159
- const mutableRect = {
1160
- ...clippingAncestor
1161
- };
1162
- if (isClientRectVisualViewportBased()) {
1163
- var _win$visualViewport, _win$visualViewport2;
1164
- const win = getWindow(element);
1165
- mutableRect.x -= ((_win$visualViewport = win.visualViewport) == null ? void 0 : _win$visualViewport.offsetLeft) || 0;
1166
- mutableRect.y -= ((_win$visualViewport2 = win.visualViewport) == null ? void 0 : _win$visualViewport2.offsetTop) || 0;
455
+ /**
456
+ * Move focus to the previous selectable option.
457
+ *
458
+ * @internal
459
+ * @remarks
460
+ * Overrides `Listbox.selectPreviousOption`
461
+ */
462
+ selectPreviousOption() {
463
+ if (!this.disabled && this.selectedIndex >= 0) {
464
+ this.selectedIndex = this.selectedIndex - 1;
465
+ }
1167
466
  }
1168
- rect = mutableRect;
1169
- }
1170
- return rectToClientRect(rect);
1171
- }
1172
-
1173
- // A "clipping ancestor" is an `overflow` element with the characteristic of
1174
- // clipping (or hiding) child elements. This returns all clipping ancestors
1175
- // of the given element up the tree.
1176
- function getClippingElementAncestors(element, cache) {
1177
- const cachedResult = cache.get(element);
1178
- if (cachedResult) {
1179
- return cachedResult;
1180
- }
1181
- let result = getOverflowAncestors(element).filter(el => isElement(el) && getNodeName(el) !== 'body');
1182
- let currentContainingBlockComputedStyle = null;
1183
- const elementIsFixed = getComputedStyle$1(element).position === 'fixed';
1184
- let currentNode = elementIsFixed ? getParentNode(element) : element;
1185
-
1186
- // https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block
1187
- while (isElement(currentNode) && !isLastTraversableNode(currentNode)) {
1188
- const computedStyle = getComputedStyle$1(currentNode);
1189
- const containingBlock = isContainingBlock(currentNode);
1190
- const shouldDropCurrentNode = elementIsFixed ? !containingBlock && !currentContainingBlockComputedStyle : !containingBlock && computedStyle.position === 'static' && !!currentContainingBlockComputedStyle && ['absolute', 'fixed'].includes(currentContainingBlockComputedStyle.position);
1191
- if (shouldDropCurrentNode) {
1192
- // Drop non-containing blocks.
1193
- result = result.filter(ancestor => ancestor !== currentNode);
1194
- } else {
1195
- // Record last containing block for next iteration.
1196
- currentContainingBlockComputedStyle = computedStyle;
467
+ /**
468
+ * Set the default selected options at initialization or reset.
469
+ *
470
+ * @internal
471
+ * @remarks
472
+ * Overrides `Listbox.setDefaultSelectedOption`
473
+ */
474
+ setDefaultSelectedOption() {
475
+ if (this.$fastController.isConnected && this.options) {
476
+ const selectedIndex = this.options.findIndex(el => el.getAttribute("selected") !== null || el.selected);
477
+ this.selectedIndex = selectedIndex;
478
+ if (!this.dirtyValue && this.firstSelectedOption) {
479
+ this.value = this.firstSelectedOption.text;
480
+ }
481
+ this.setSelectedOptions();
482
+ }
1197
483
  }
1198
- currentNode = getParentNode(currentNode);
1199
- }
1200
- cache.set(element, result);
1201
- return result;
1202
- }
1203
-
1204
- // Gets the maximum area that the element is visible in due to any number of
1205
- // clipping ancestors.
1206
- function getClippingRect(_ref) {
1207
- let {
1208
- element,
1209
- boundary,
1210
- rootBoundary,
1211
- strategy
1212
- } = _ref;
1213
- const elementClippingAncestors = boundary === 'clippingAncestors' ? getClippingElementAncestors(element, this._c) : [].concat(boundary);
1214
- const clippingAncestors = [...elementClippingAncestors, rootBoundary];
1215
- const firstClippingAncestor = clippingAncestors[0];
1216
- const clippingRect = clippingAncestors.reduce((accRect, clippingAncestor) => {
1217
- const rect = getClientRectFromClippingAncestor(element, clippingAncestor, strategy);
1218
- accRect.top = max(rect.top, accRect.top);
1219
- accRect.right = min(rect.right, accRect.right);
1220
- accRect.bottom = min(rect.bottom, accRect.bottom);
1221
- accRect.left = max(rect.left, accRect.left);
1222
- return accRect;
1223
- }, getClientRectFromClippingAncestor(element, firstClippingAncestor, strategy));
1224
- return {
1225
- width: clippingRect.right - clippingRect.left,
1226
- height: clippingRect.bottom - clippingRect.top,
1227
- x: clippingRect.left,
1228
- y: clippingRect.top
1229
- };
1230
- }
1231
-
1232
- function getDimensions(element) {
1233
- if (isHTMLElement(element)) {
1234
- return getCssDimensions(element);
1235
- }
1236
- return element.getBoundingClientRect();
1237
- }
1238
-
1239
- function getTrueOffsetParent(element, polyfill) {
1240
- if (!isHTMLElement(element) || getComputedStyle$1(element).position === 'fixed') {
1241
- return null;
1242
- }
1243
- if (polyfill) {
1244
- return polyfill(element);
1245
- }
1246
- return element.offsetParent;
1247
- }
1248
- function getContainingBlock(element) {
1249
- let currentNode = getParentNode(element);
1250
- while (isHTMLElement(currentNode) && !isLastTraversableNode(currentNode)) {
1251
- if (isContainingBlock(currentNode)) {
1252
- return currentNode;
1253
- } else {
1254
- currentNode = getParentNode(currentNode);
484
+ /**
485
+ * Focus and set the content of the control based on the first selected option.
486
+ *
487
+ * @internal
488
+ */
489
+ setInputToSelection() {
490
+ if (this.firstSelectedOption) {
491
+ this.control.value = this.firstSelectedOption.text;
492
+ this.control.focus();
493
+ }
1255
494
  }
1256
- }
1257
- return null;
1258
- }
1259
-
1260
- // Gets the closest ancestor positioned element. Handles some edge cases,
1261
- // such as table ancestors and cross browser bugs.
1262
- function getOffsetParent(element, polyfill) {
1263
- const window = getWindow(element);
1264
- let offsetParent = getTrueOffsetParent(element, polyfill);
1265
- while (offsetParent && isTableElement(offsetParent) && getComputedStyle$1(offsetParent).position === 'static') {
1266
- offsetParent = getTrueOffsetParent(offsetParent, polyfill);
1267
- }
1268
- if (offsetParent && (getNodeName(offsetParent) === 'html' || getNodeName(offsetParent) === 'body' && getComputedStyle$1(offsetParent).position === 'static' && !isContainingBlock(offsetParent))) {
1269
- return window;
1270
- }
1271
- return offsetParent || getContainingBlock(element) || window;
1272
- }
1273
-
1274
- function getRectRelativeToOffsetParent(element, offsetParent, strategy) {
1275
- const isOffsetParentAnElement = isHTMLElement(offsetParent);
1276
- const documentElement = getDocumentElement(offsetParent);
1277
- const rect = getBoundingClientRect(element, true, strategy === 'fixed', offsetParent);
1278
- let scroll = {
1279
- scrollLeft: 0,
1280
- scrollTop: 0
1281
- };
1282
- const offsets = {
1283
- x: 0,
1284
- y: 0
1285
- };
1286
- if (isOffsetParentAnElement || !isOffsetParentAnElement && strategy !== 'fixed') {
1287
- if (getNodeName(offsetParent) !== 'body' || isOverflowElement(documentElement)) {
1288
- scroll = getNodeScroll(offsetParent);
495
+ /**
496
+ * Focus, set and select the content of the control based on the first selected option.
497
+ *
498
+ * @internal
499
+ */
500
+ setInlineSelection() {
501
+ if (this.firstSelectedOption) {
502
+ this.setInputToSelection();
503
+ this.control.setSelectionRange(this.filter.length, this.control.value.length, "backward");
504
+ }
1289
505
  }
1290
- if (isHTMLElement(offsetParent)) {
1291
- const offsetRect = getBoundingClientRect(offsetParent, true);
1292
- offsets.x = offsetRect.x + offsetParent.clientLeft;
1293
- offsets.y = offsetRect.y + offsetParent.clientTop;
1294
- } else if (documentElement) {
1295
- offsets.x = getWindowScrollBarX(documentElement);
506
+ /**
507
+ * Determines if a value update should involve emitting a change event, then updates the value.
508
+ *
509
+ * @internal
510
+ */
511
+ syncValue() {
512
+ var _a;
513
+ const newValue = this.selectedIndex > -1 ? (_a = this.firstSelectedOption) === null || _a === void 0 ? void 0 : _a.text : this.control.value;
514
+ this.updateValue(this.value !== newValue);
1296
515
  }
1297
- }
1298
- return {
1299
- x: rect.left + scroll.scrollLeft - offsets.x,
1300
- y: rect.top + scroll.scrollTop - offsets.y,
1301
- width: rect.width,
1302
- height: rect.height
1303
- };
1304
- }
1305
-
1306
- const platform = {
1307
- getClippingRect,
1308
- convertOffsetParentRelativeRectToViewportRelativeRect,
1309
- isElement,
1310
- getDimensions,
1311
- getOffsetParent,
1312
- getDocumentElement,
1313
- getScale,
1314
- async getElementRects(_ref) {
1315
- let {
1316
- reference,
1317
- floating,
1318
- strategy
1319
- } = _ref;
1320
- const getOffsetParentFn = this.getOffsetParent || getOffsetParent;
1321
- const getDimensionsFn = this.getDimensions;
1322
- return {
1323
- reference: getRectRelativeToOffsetParent(reference, await getOffsetParentFn(floating), strategy),
1324
- floating: {
1325
- x: 0,
1326
- y: 0,
1327
- ...(await getDimensionsFn(floating))
1328
- }
1329
- };
1330
- },
1331
- getClientRects: element => Array.from(element.getClientRects()),
1332
- isRTL: element => getComputedStyle$1(element).direction === 'rtl'
1333
- };
1334
-
1335
- /**
1336
- * Automatically updates the position of the floating element when necessary.
1337
- * @see https://floating-ui.com/docs/autoUpdate
1338
- */
1339
- function autoUpdate(reference, floating, update, options) {
1340
- if (options === void 0) {
1341
- options = {};
1342
- }
1343
- const {
1344
- ancestorScroll: _ancestorScroll = true,
1345
- ancestorResize = true,
1346
- elementResize = true,
1347
- animationFrame = false
1348
- } = options;
1349
- const ancestorScroll = _ancestorScroll && !animationFrame;
1350
- const ancestors = ancestorScroll || ancestorResize ? [...(isElement(reference) ? getOverflowAncestors(reference) : reference.contextElement ? getOverflowAncestors(reference.contextElement) : []), ...getOverflowAncestors(floating)] : [];
1351
- ancestors.forEach(ancestor => {
1352
- ancestorScroll && ancestor.addEventListener('scroll', update, {
1353
- passive: true
1354
- });
1355
- ancestorResize && ancestor.addEventListener('resize', update);
1356
- });
1357
- let observer = null;
1358
- if (elementResize) {
1359
- let initialUpdate = true;
1360
- observer = new ResizeObserver(() => {
1361
- if (!initialUpdate) {
1362
- update();
1363
- }
1364
- initialUpdate = false;
1365
- });
1366
- isElement(reference) && !animationFrame && observer.observe(reference);
1367
- if (!isElement(reference) && reference.contextElement && !animationFrame) {
1368
- observer.observe(reference.contextElement);
516
+ /**
517
+ * Calculate and apply listbox positioning based on available viewport space.
518
+ *
519
+ * @param force - direction to force the listbox to display
520
+ * @public
521
+ */
522
+ setPositioning() {
523
+ const currentBox = this.getBoundingClientRect();
524
+ const viewportHeight = window.innerHeight;
525
+ const availableBottom = viewportHeight - currentBox.bottom;
526
+ this.position = this.forcedPosition
527
+ ? this.positionAttribute
528
+ : currentBox.top > availableBottom
529
+ ? SelectPosition.above
530
+ : SelectPosition.below;
531
+ this.positionAttribute = this.forcedPosition
532
+ ? this.positionAttribute
533
+ : this.position;
534
+ this.maxHeight =
535
+ this.position === SelectPosition.above ? ~~currentBox.top : ~~availableBottom;
1369
536
  }
1370
- observer.observe(floating);
1371
- }
1372
- let frameId;
1373
- let prevRefRect = animationFrame ? getBoundingClientRect(reference) : null;
1374
- if (animationFrame) {
1375
- frameLoop();
1376
- }
1377
- function frameLoop() {
1378
- const nextRefRect = getBoundingClientRect(reference);
1379
- if (prevRefRect && (nextRefRect.x !== prevRefRect.x || nextRefRect.y !== prevRefRect.y || nextRefRect.width !== prevRefRect.width || nextRefRect.height !== prevRefRect.height)) {
1380
- update();
537
+ /**
538
+ * Ensure that the entire list of options is used when setting the selected property.
539
+ *
540
+ * @param prev - the previous list of selected options
541
+ * @param next - the current list of selected options
542
+ *
543
+ * @internal
544
+ * @remarks
545
+ * Overrides: `Listbox.selectedOptionsChanged`
546
+ */
547
+ selectedOptionsChanged(prev, next) {
548
+ if (this.$fastController.isConnected) {
549
+ this._options.forEach(o => {
550
+ o.selected = next.includes(o);
551
+ });
552
+ }
1381
553
  }
1382
- prevRefRect = nextRefRect;
1383
- frameId = requestAnimationFrame(frameLoop);
1384
- }
1385
- update();
1386
- return () => {
1387
- var _observer;
1388
- ancestors.forEach(ancestor => {
1389
- ancestorScroll && ancestor.removeEventListener('scroll', update);
1390
- ancestorResize && ancestor.removeEventListener('resize', update);
1391
- });
1392
- (_observer = observer) == null ? void 0 : _observer.disconnect();
1393
- observer = null;
1394
- if (animationFrame) {
1395
- cancelAnimationFrame(frameId);
554
+ /**
555
+ * Synchronize the form-associated proxy and update the value property of the element.
556
+ *
557
+ * @param prev - the previous collection of slotted option elements
558
+ * @param next - the next collection of slotted option elements
559
+ *
560
+ * @internal
561
+ */
562
+ slottedOptionsChanged(prev, next) {
563
+ super.slottedOptionsChanged(prev, next);
564
+ this.updateValue();
1396
565
  }
1397
- };
1398
- }
1399
-
1400
- /**
1401
- * Computes the `x` and `y` coordinates that will place the floating element
1402
- * next to a reference element when it is given a certain CSS positioning
1403
- * strategy.
1404
- */
1405
- const computePosition = (reference, floating, options) => {
1406
- // This caches the expensive `getClippingElementAncestors` function so that
1407
- // multiple lifecycle resets re-use the same result. It only lives for a
1408
- // single call. If other functions become expensive, we can add them as well.
1409
- const cache = new Map();
1410
- const mergedOptions = {
1411
- platform,
1412
- ...options
1413
- };
1414
- const platformWithCache = {
1415
- ...mergedOptions.platform,
1416
- _c: cache
1417
- };
1418
- return computePosition$1(reference, floating, {
1419
- ...mergedOptions,
1420
- platform: platformWithCache
1421
- });
1422
- };
1423
-
1424
- var _Popup_instances, _Popup_arrowPosition_get, _Popup_padding_get, _Popup_distance_get, _Popup_middleware_get, _Popup_cleanup, _Popup_assignPopupPosition, _Popup_assignArrowPosition, _Popup_getAnchor;
1425
- class Popup extends FoundationElement {
1426
- constructor() {
1427
- super(...arguments);
1428
- _Popup_instances.add(this);
1429
- _Popup_cleanup.set(this, void 0);
1430
- this.open = false;
1431
- this.dismissible = false;
1432
- this.arrow = false;
1433
- this.alternate = false;
1434
- this.strategy = 'fixed';
1435
- }
1436
- openChanged(_, newValue) {
1437
- newValue ? this.$emit('open') : this.$emit('close');
1438
- }
1439
- disconnectedCallback() {
1440
- var _a;
1441
- super.disconnectedCallback();
1442
- (_a = __classPrivateFieldGet(this, _Popup_cleanup, "f")) === null || _a === void 0 ? void 0 : _a.call(this);
1443
- }
1444
- attributeChangedCallback(name, oldValue, newValue) {
1445
- var _a;
1446
- super.attributeChangedCallback(name, oldValue, newValue);
1447
- switch (name) {
1448
- case 'anchor':
1449
- {
1450
- this.anchorEl = __classPrivateFieldGet(this, _Popup_instances, "m", _Popup_getAnchor).call(this);
1451
- break;
566
+ /**
567
+ * Sets the value and to match the first selected option.
568
+ *
569
+ * @param shouldEmit - if true, the change event will be emitted
570
+ *
571
+ * @internal
572
+ */
573
+ updateValue(shouldEmit) {
574
+ var _a;
575
+ if (this.$fastController.isConnected) {
576
+ this.value = ((_a = this.firstSelectedOption) === null || _a === void 0 ? void 0 : _a.text) || this.control.value;
577
+ this.control.value = this.value;
1452
578
  }
1453
- case 'open':
1454
- {
1455
- this.open ? this.show() : this.hide();
1456
- break;
579
+ if (shouldEmit) {
580
+ this.$emit("change");
1457
581
  }
1458
582
  }
1459
- if (this.anchorEl && this.popupEl) {
1460
- __classPrivateFieldSet(this, _Popup_cleanup, autoUpdate(this.anchorEl, this.popupEl, () => this.updatePosition()), "f");
1461
- } else {
1462
- (_a = __classPrivateFieldGet(this, _Popup_cleanup, "f")) === null || _a === void 0 ? void 0 : _a.call(this);
583
+ /**
584
+ * @internal
585
+ */
586
+ clearSelectionRange() {
587
+ const controlValueLength = this.control.value.length;
588
+ this.control.setSelectionRange(controlValueLength, controlValueLength);
1463
589
  }
1464
- }
1465
- async updatePosition() {
1466
- if (!this.open || !this.anchorEl) {
1467
- return;
1468
- }
1469
- const positionData = await computePosition(this.anchorEl, this.popupEl, {
1470
- placement: this.placement,
1471
- strategy: this.strategy,
1472
- middleware: __classPrivateFieldGet(this, _Popup_instances, "a", _Popup_middleware_get)
1473
- });
1474
- __classPrivateFieldGet(this, _Popup_instances, "m", _Popup_assignPopupPosition).call(this, positionData);
1475
- if (this.arrow) {
1476
- __classPrivateFieldGet(this, _Popup_instances, "m", _Popup_assignArrowPosition).call(this, positionData);
1477
- }
1478
- }
1479
- show() {
1480
- this.open = true;
1481
- }
1482
- hide() {
1483
- this.open = false;
1484
- }
1485
590
  }
1486
- _Popup_cleanup = new WeakMap(), _Popup_instances = new WeakSet(), _Popup_arrowPosition_get = function _Popup_arrowPosition_get() {
1487
- return {
1488
- top: 'bottom',
1489
- right: 'left',
1490
- bottom: 'top',
1491
- left: 'right'
1492
- };
1493
- }, _Popup_padding_get = function _Popup_padding_get() {
1494
- return 0;
1495
- }, _Popup_distance_get = function _Popup_distance_get() {
1496
- return 12;
1497
- }, _Popup_middleware_get = function _Popup_middleware_get() {
1498
- const middleware = [flip(), hide(), inline()];
1499
- if (this.arrow) {
1500
- middleware.push(arrow({
1501
- element: this.arrowEl,
1502
- padding: __classPrivateFieldGet(this, _Popup_instances, "a", _Popup_padding_get)
1503
- }), offset(__classPrivateFieldGet(this, _Popup_instances, "a", _Popup_distance_get)));
591
+ __decorate([
592
+ attr({ attribute: "autocomplete", mode: "fromView" })
593
+ ], Combobox$1.prototype, "autocomplete", void 0);
594
+ __decorate([
595
+ observable
596
+ ], Combobox$1.prototype, "maxHeight", void 0);
597
+ __decorate([
598
+ attr({ attribute: "open", mode: "boolean" })
599
+ ], Combobox$1.prototype, "open", void 0);
600
+ __decorate([
601
+ attr
602
+ ], Combobox$1.prototype, "placeholder", void 0);
603
+ __decorate([
604
+ attr({ attribute: "position" })
605
+ ], Combobox$1.prototype, "positionAttribute", void 0);
606
+ __decorate([
607
+ observable
608
+ ], Combobox$1.prototype, "position", void 0);
609
+ /**
610
+ * Includes ARIA states and properties relating to the ARIA combobox role.
611
+ *
612
+ * @public
613
+ */
614
+ class DelegatesARIACombobox {
615
+ }
616
+ __decorate([
617
+ observable
618
+ ], DelegatesARIACombobox.prototype, "ariaAutoComplete", void 0);
619
+ __decorate([
620
+ observable
621
+ ], DelegatesARIACombobox.prototype, "ariaControls", void 0);
622
+ applyMixins(DelegatesARIACombobox, DelegatesARIAListbox);
623
+ applyMixins(Combobox$1, StartEnd, DelegatesARIACombobox);
624
+
625
+ var css_248z = "/**\n * Do not edit directly\n * Generated on Thu, 27 Apr 2023 09:20:45 GMT\n */\n:host {\n position: relative;\n}\n\n.control {\n display: flex;\n padding-inline-end: 44px;\n}\n\n.icon {\n inset-inline-end: 16px;\n inset-inline-start: unset;\n}\n:not(.disabled) .icon {\n cursor: pointer;\n}\n.disabled .icon {\n cursor: not-allowed;\n}\n\n.listbox {\n padding: 4px;\n background-color: var(--_appearance-color-fill);\n border-radius: 6px;\n box-shadow: inset 0 0 0 1px var(--_appearance-color-outline);\n contain: paint;\n}\n.listbox {\n --_connotation-color-backdrop: var(--vvd-color-canvas);\n --_connotation-color-intermediate: var(--vvd-color-neutral-500);\n --_connotation-color-primary: var(--vvd-color-canvas-text);\n --_connotation-color-soft: var(--vvd-color-neutral-100);\n}\n.listbox {\n --_appearance-color-text: var(--vvd-color-canvas-text);\n --_appearance-color-fill: var(--_connotation-color-backdrop);\n --_appearance-color-outline: var(--_connotation-color-intermediate);\n}\n.listbox.appearance-ghost {\n --_appearance-color-text: var(--_connotation-color-primary);\n --_appearance-color-fill: transparent;\n --_appearance-color-outline: transparent;\n}\n.listbox:where(:disabled, .disabled) {\n --_appearance-color-text: var(--vvd-color-neutral-400);\n --_appearance-color-fill: var(--vvd-color-neutral-200);\n --_appearance-color-outline: var(--vvd-color-neutral-400);\n}\n.listbox:where(:disabled, .disabled).appearance-ghost {\n --_appearance-color-text: var(--vvd-color-neutral-400);\n --_appearance-color-fill: transparent;\n --_appearance-color-outline: transparent;\n}\n\n::part(popup-base) {\n inline-size: 100%;\n}";
626
+
627
+ let Combobox = class Combobox extends Combobox$1 {
628
+ connectedCallback() {
629
+ super.connectedCallback();
630
+ this._popup.anchor = this._anchor;
1504
631
  }
1505
- return middleware;
1506
- }, _Popup_assignPopupPosition = function _Popup_assignPopupPosition(data) {
1507
- const {
1508
- x: popupX,
1509
- y: popupY
1510
- } = data;
1511
- const {
1512
- referenceHidden
1513
- } = data.middlewareData.hide;
1514
- Object.assign(this.popupEl.style, {
1515
- left: `${popupX}px`,
1516
- top: `${popupY}px`,
1517
- visibility: referenceHidden ? 'hidden' : 'visible'
1518
- });
1519
- }, _Popup_assignArrowPosition = function _Popup_assignArrowPosition(data) {
1520
- const {
1521
- x: arrowX,
1522
- y: arrowY
1523
- } = data.middlewareData.arrow;
1524
- const side = __classPrivateFieldGet(this, _Popup_instances, "a", _Popup_arrowPosition_get)[data.placement.split('-')[0]];
1525
- Object.assign(this.arrowEl.style, {
1526
- left: `${arrowX}px`,
1527
- top: `${arrowY}px`,
1528
- [__classPrivateFieldGet(this, _Popup_instances, "a", _Popup_arrowPosition_get)[side]]: '',
1529
- [side]: '-4px'
1530
- });
1531
- }, _Popup_getAnchor = function _Popup_getAnchor() {
1532
- return this.anchor instanceof HTMLElement ? this.anchor : document.getElementById(this.anchor);
1533
632
  };
1534
- __decorate([attr({
1535
- mode: 'boolean'
1536
- }), __metadata("design:type", Object)], Popup.prototype, "open", void 0);
1537
- __decorate([attr({
1538
- mode: 'boolean'
1539
- }), __metadata("design:type", Object)], Popup.prototype, "dismissible", void 0);
1540
- __decorate([attr({
1541
- mode: 'boolean'
1542
- }), __metadata("design:type", Object)], Popup.prototype, "arrow", void 0);
1543
- __decorate([attr({
1544
- mode: 'boolean'
1545
- }), __metadata("design:type", Object)], Popup.prototype, "alternate", void 0);
1546
- __decorate([attr({
1547
- mode: 'fromView'
1548
- }), __metadata("design:type", String)], Popup.prototype, "placement", void 0);
1549
- __decorate([attr({
1550
- mode: 'fromView'
1551
- }), __metadata("design:type", String)], Popup.prototype, "strategy", void 0);
1552
- __decorate([attr, __metadata("design:type", Object)], Popup.prototype, "anchor", void 0);
1553
-
1554
- var css_248z = ".control {\n background: var(--vvd-color-surface-4dp);\n border-radius: inherit;\n contain: layout;\n}\n.control:not(.open) {\n display: none;\n}\n\n.popup-wrapper {\n z-index: 10;\n border-radius: 6px;\n inline-size: fit-content;\n}\n.popup-wrapper:not(.absolute) {\n position: fixed;\n}\n.popup-wrapper.absolute {\n position: absolute;\n}\n\n.popup-content {\n display: grid;\n color: var(--vvd-color-canvas-text); /* neutral-100 */\n}\n.dismissible .popup-content {\n align-content: start;\n grid-template-columns: 1fr auto;\n}\n\n.arrow {\n position: absolute;\n z-index: -1;\n width: 8px;\n height: 8px;\n background: var(--vvd-color-surface-4dp);\n transform: rotate(45deg);\n}\n\n.dismissible-button {\n align-self: flex-start;\n margin-block-start: 4px;\n margin-inline-end: 4px;\n}";
633
+ __decorate([attr, __metadata("design:type", String)], Combobox.prototype, "placement", void 0);
634
+ Combobox = __decorate([formElements], Combobox);
635
+ applyMixins(Combobox, AffixIcon);
1555
636
 
1556
637
  let _ = t => t,
1557
638
  _t,
1558
639
  _t2,
1559
640
  _t3;
1560
- const getClasses = ({
1561
- open,
1562
- dismissible,
1563
- alternate
1564
- }) => classNames('control', ['open', Boolean(open)], ['dismissible', Boolean(dismissible)], ['alternate', Boolean(alternate)]);
1565
- const popupTemplate = context => {
1566
- const elevationTag = context.tagFor(Elevation);
1567
- const buttonTag = context.tagFor(Button);
641
+ function renderLabel() {
1568
642
  return html(_t || (_t = _`
1569
- <${0}>
1570
- <div class="popup-wrapper ${0}" ${0} part="popup-base">
1571
- <div class="${0}" aria-hidden="${0}"
1572
- part="${0}">
1573
- <div class="popup-content">
1574
- <slot></slot>
643
+ <label for="control" class="label">
644
+ ${0}
645
+ </label>`), x => x.label);
646
+ }
647
+ const getStateClasses = ({
648
+ disabled,
649
+ placeholder,
650
+ label
651
+ }) => classNames('base', ['disabled', disabled], ['placeholder', Boolean(placeholder)], ['no-label', !label]);
652
+ function renderInput(context) {
653
+ const affixIconTemplate = affixIconTemplateFactory(context);
654
+ const focusTemplate = focusTemplateFactory(context);
655
+ return html(_t2 || (_t2 = _`
656
+ <div class="${0}" ${0}>
657
+ ${0}
658
+ <div class="fieldset">
659
+ <input
660
+ id="control"
661
+ class="control"
662
+ aria-activedescendant="${0}"
663
+ aria-autocomplete="${0}"
664
+ aria-controls="${0}"
665
+ aria-disabled="${0}"
666
+ aria-expanded="${0}"
667
+ aria-haspopup="listbox"
668
+ placeholder="${0}"
669
+ role="combobox"
670
+ type="text"
671
+ ?disabled="${0}"
672
+ :value="${0}"
673
+ @input="${0}"
674
+ @keyup="${0}"
1575
675
  ${0}
1576
- </div>
676
+ />
677
+ ${0}
1577
678
  ${0}
1578
679
  </div>
1579
- </div>
1580
- </${0}>`), elevationTag, x => x.strategy, ref('popupEl'), getClasses, x => x.open ? 'false' : 'true', x => x.alternate ? 'vvd-theme-alternate' : '', when(x => x.dismissible, html(_t2 || (_t2 = _`<${0} size="condensed" @click="${0}"
1581
- class="dismissible-button" icon="close-small-solid" shape="pill"></${0}>`), buttonTag, x => x.open = false, buttonTag)), when(x => x.arrow, html(_t3 || (_t3 = _`<div class="arrow" ${0}></div>`), ref('arrowEl'))), elevationTag);
680
+ </div>`), getStateClasses, ref('_anchor'), when(x => x.label, renderLabel()), x => x.open ? x.ariaActiveDescendant : null, x => x.ariaAutoComplete, x => x.ariaControls, x => x.ariaDisabled, x => x.ariaExpanded, x => x.placeholder, x => x.disabled, x => x.value, (x, c) => x.inputHandler(c.event), (x, c) => x.keyupHandler(c.event), ref('control'), () => affixIconTemplate('chevron-down-line'), () => focusTemplate);
681
+ }
682
+ const comboboxTemplate = context => {
683
+ const popupTag = context.tagFor(Popup);
684
+ return html(_t3 || (_t3 = _`
685
+ <template
686
+ aria-disabled="${0}"
687
+ autocomplete="${0}"
688
+ tabindex="${0}"
689
+ @click="${0}"
690
+ @focusout="${0}"
691
+ @keydown="${0}"
692
+ >
693
+ ${0}
694
+ <${0} class="popup"
695
+ ?open="${0}"
696
+ placement="${0}"
697
+ strategy="absolute"
698
+ ${0}>
699
+ <div id="${0}"
700
+ class="listbox"
701
+ role="listbox"
702
+ ?disabled="${0}"
703
+ ${0}>
704
+ <slot ${0}>
705
+ </slot>
706
+ </div>
707
+ </${0}>
708
+ </template>
709
+ `), x => x.ariaDisabled, x => x.autocomplete, x => !x.disabled ? '0' : null, (x, c) => x.clickHandler(c.event), (x, c) => x.focusoutHandler(c.event), (x, c) => x.keydownHandler(c.event), () => renderInput(context), popupTag, x => x.open, x => x.placement, ref('_popup'), x => x.listboxId, x => x.disabled, ref('listbox'), slotted({
710
+ filter: Listbox$1.slottedOptionFilter,
711
+ flatten: true,
712
+ property: 'slottedOptions'
713
+ }), popupTag);
1582
714
  };
1583
715
 
1584
- const popupDefinition = Popup.compose({
1585
- baseName: 'popup',
1586
- template: popupTemplate,
1587
- styles: css_248z
1588
- });
1589
- const popupRegistries = [popupDefinition(), ...elevationRegistries, ...buttonRegistries];
1590
- const registerPopup = registerFactory(popupRegistries);
716
+ const combobox = Combobox.compose({
717
+ baseName: 'combobox',
718
+ template: comboboxTemplate,
719
+ styles: [css_248z$1, css_248z],
720
+ shadowOptions: {
721
+ delegatesFocus: true
722
+ }
723
+ })();
724
+ const comboboxRegistries = [combobox, ...iconRegistries, ...popupRegistries, ...focusRegistries, ...listboxOptionRegistries];
725
+ const registerCombobox = registerFactory(comboboxRegistries);
1591
726
 
1592
- export { Popup as P, popupDefinition as a, popupRegistries as p, registerPopup as r };
727
+ export { comboboxRegistries as a, combobox as c, registerCombobox as r };