@vonage/vivid 3.0.0-next.92 → 3.0.0-next.93

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 (52) hide show
  1. package/accordion/index.js +1 -1
  2. package/accordion-item/index.js +1 -1
  3. package/action-group/index.js +1 -1
  4. package/avatar/index.js +1 -1
  5. package/badge/index.js +1 -1
  6. package/banner/index.js +1 -1
  7. package/breadcrumb-item/index.js +1 -1
  8. package/calendar/index.js +1 -1
  9. package/calendar-event/index.js +1 -1
  10. package/card/index.js +1 -1
  11. package/checkbox/index.js +1 -1
  12. package/dialog/index.js +1 -1
  13. package/fab/index.js +1 -1
  14. package/focus/index.js +1 -1
  15. package/header/index.js +1 -1
  16. package/index.js +5 -3
  17. package/lib/components.d.ts +1 -0
  18. package/lib/listbox/index.d.ts +4 -0
  19. package/lib/listbox/listbox.d.ts +9 -0
  20. package/lib/listbox/listbox.template.d.ts +4 -0
  21. package/listbox/index.js +1105 -0
  22. package/listbox-option/index.js +12 -239
  23. package/menu/index.js +4 -10
  24. package/menu-item/index.js +1 -1
  25. package/nav-disclosure/index.js +1 -1
  26. package/nav-item/index.js +1 -1
  27. package/note/index.js +1 -1
  28. package/number-field/index.js +2 -2
  29. package/package.json +2 -1
  30. package/popup/index.js +1 -1
  31. package/progress/index.js +1 -1
  32. package/progress-ring/index.js +1 -1
  33. package/radio/index.js +1 -1
  34. package/radio-group/index.js +2 -2
  35. package/shared/dom.js +8 -0
  36. package/shared/form-elements.js +1 -1
  37. package/shared/index2.js +1 -1
  38. package/shared/index4.js +1 -1
  39. package/shared/index5.js +220 -1492
  40. package/shared/index6.js +1490 -314
  41. package/shared/index7.js +349 -0
  42. package/shared/key-codes.js +2 -1
  43. package/shared/patterns/form-elements/form-elements.d.ts +1 -1
  44. package/side-drawer/index.js +1 -1
  45. package/styles/core/all.css +1 -1
  46. package/styles/core/theme.css +1 -1
  47. package/styles/core/typography.css +1 -1
  48. package/styles/tokens/theme-dark.css +4 -4
  49. package/styles/tokens/theme-light.css +4 -4
  50. package/text-area/index.js +1 -1
  51. package/text-field/index.js +1 -1
  52. package/tooltip/index.js +2 -2
@@ -0,0 +1,1105 @@
1
+ import '../focus/index.js';
2
+ import { i as isListboxOption } from '../shared/index5.js';
3
+ import { F as FoundationElement, O as Observable, _ as __decorate, a as attr, o as observable, D as DOM, n as nullableNumberConverter, c as __classPrivateFieldGet, b as __metadata, h as html, d as designSystem } from '../shared/index.js';
4
+ import '../shared/web.dom-collections.iterator.js';
5
+ import { A as ARIAGlobalStatesAndProperties } from '../shared/aria-global.js';
6
+ import { a as applyMixins } from '../shared/apply-mixins.js';
7
+ import { a as keySpace, b as keyEscape, k as keyEnter, c as keyTab, d as keyEnd, e as keyArrowUp, f as keyArrowDown, g as keyHome } from '../shared/key-codes.js';
8
+ import '../shared/affix.js';
9
+ import { f as focusTemplateFactory } from '../shared/focus2.js';
10
+ import '../shared/icon.js';
11
+ import { s as slotted } from '../shared/slotted.js';
12
+ import { c as classNames } from '../shared/class-names.js';
13
+ import '../shared/focus.js';
14
+ import '../icon/index.js';
15
+ import '../shared/when.js';
16
+ import '../shared/export.js';
17
+ import '../shared/iterators.js';
18
+ import '../shared/to-string.js';
19
+ import '../shared/_has.js';
20
+ import '../shared/start-end.js';
21
+ import '../shared/ref.js';
22
+ import '../shared/dom.js';
23
+ import '../shared/object-keys.js';
24
+
25
+ /**
26
+ * Returns the index of the last element in the array where predicate is true, and -1 otherwise.
27
+ *
28
+ * @param array - the array to test
29
+ * @param predicate - find calls predicate once for each element of the array, in descending order, until it finds one where predicate returns true. If such an element is found, findLastIndex immediately returns that element index. Otherwise, findIndex returns -1.
30
+ */
31
+ function findLastIndex(array, predicate) {
32
+ let k = array.length;
33
+ while (k--) {
34
+ if (predicate(array[k], k, array)) {
35
+ return k;
36
+ }
37
+ }
38
+ return -1;
39
+ }
40
+
41
+ /**
42
+ * This method keeps a given value within the bounds of a min and max value. If the value
43
+ * is larger than the max, the minimum value will be returned. If the value is smaller than the minimum,
44
+ * the maximum will be returned. Otherwise, the value is returned un-changed.
45
+ */
46
+ /**
47
+ * Determines if a number value is within a specified range.
48
+ *
49
+ * @param value - the value to check
50
+ * @param min - the range start
51
+ * @param max - the range end
52
+ */
53
+ function inRange(value, min, max = 0) {
54
+ [min, max] = [min, max].sort((a, b) => a - b);
55
+ return min <= value && value < max;
56
+ }
57
+
58
+ let uniqueIdCounter = 0;
59
+ /**
60
+ * Generates a unique ID based on incrementing a counter.
61
+ */
62
+ function uniqueId(prefix = "") {
63
+ return `${prefix}${uniqueIdCounter++}`;
64
+ }
65
+
66
+ /**
67
+ * A Listbox Custom HTML Element.
68
+ * Implements the {@link https://www.w3.org/TR/wai-aria-1.1/#listbox | ARIA listbox }.
69
+ *
70
+ * @slot - The default slot for the listbox options
71
+ *
72
+ * @public
73
+ */
74
+ class Listbox$1 extends FoundationElement {
75
+ constructor() {
76
+ super(...arguments);
77
+ /**
78
+ * The internal unfiltered list of selectable options.
79
+ *
80
+ * @internal
81
+ */
82
+ this._options = [];
83
+ /**
84
+ * The index of the selected option.
85
+ *
86
+ * @public
87
+ */
88
+ this.selectedIndex = -1;
89
+ /**
90
+ * A collection of the selected options.
91
+ *
92
+ * @public
93
+ */
94
+ this.selectedOptions = [];
95
+ /**
96
+ * A standard `click` event creates a `focus` event before firing, so a
97
+ * `mousedown` event is used to skip that initial focus.
98
+ *
99
+ * @internal
100
+ */
101
+ this.shouldSkipFocus = false;
102
+ /**
103
+ * The current typeahead buffer string.
104
+ *
105
+ * @internal
106
+ */
107
+ this.typeaheadBuffer = "";
108
+ /**
109
+ * Flag for the typeahead timeout expiration.
110
+ *
111
+ * @internal
112
+ */
113
+ this.typeaheadExpired = true;
114
+ /**
115
+ * The timeout ID for the typeahead handler.
116
+ *
117
+ * @internal
118
+ */
119
+ this.typeaheadTimeout = -1;
120
+ }
121
+ /**
122
+ * The first selected option.
123
+ *
124
+ * @internal
125
+ */
126
+ get firstSelectedOption() {
127
+ var _a;
128
+ return (_a = this.selectedOptions[0]) !== null && _a !== void 0 ? _a : null;
129
+ }
130
+ /**
131
+ * Returns true if there is one or more selectable option.
132
+ *
133
+ * @internal
134
+ */
135
+ get hasSelectableOptions() {
136
+ return this.options.length > 0 && !this.options.every(o => o.disabled);
137
+ }
138
+ /**
139
+ * The number of options.
140
+ *
141
+ * @public
142
+ */
143
+ get length() {
144
+ var _a, _b;
145
+ return (_b = (_a = this.options) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0;
146
+ }
147
+ /**
148
+ * The list of options.
149
+ *
150
+ * @public
151
+ */
152
+ get options() {
153
+ Observable.track(this, "options");
154
+ return this._options;
155
+ }
156
+ set options(value) {
157
+ this._options = value;
158
+ Observable.notify(this, "options");
159
+ }
160
+ /**
161
+ * Flag for the typeahead timeout expiration.
162
+ *
163
+ * @deprecated use `Listbox.typeaheadExpired`
164
+ * @internal
165
+ */
166
+ get typeAheadExpired() {
167
+ return this.typeaheadExpired;
168
+ }
169
+ set typeAheadExpired(value) {
170
+ this.typeaheadExpired = value;
171
+ }
172
+ /**
173
+ * Handle click events for listbox options.
174
+ *
175
+ * @internal
176
+ */
177
+ clickHandler(e) {
178
+ const captured = e.target.closest(`option,[role=option]`);
179
+ if (captured && !captured.disabled) {
180
+ this.selectedIndex = this.options.indexOf(captured);
181
+ return true;
182
+ }
183
+ }
184
+ /**
185
+ * Ensures that the provided option is focused and scrolled into view.
186
+ *
187
+ * @param optionToFocus - The option to focus
188
+ * @internal
189
+ */
190
+ focusAndScrollOptionIntoView(optionToFocus = this.firstSelectedOption) {
191
+ // To ensure that the browser handles both `focus()` and `scrollIntoView()`, the
192
+ // timing here needs to guarantee that they happen on different frames. Since this
193
+ // function is typically called from the `openChanged` observer, `DOM.queueUpdate`
194
+ // causes the calls to be grouped into the same frame. To prevent this,
195
+ // `requestAnimationFrame` is used instead of `DOM.queueUpdate`.
196
+ if (this.contains(document.activeElement) && optionToFocus !== null) {
197
+ optionToFocus.focus();
198
+ requestAnimationFrame(() => {
199
+ optionToFocus.scrollIntoView({ block: "nearest" });
200
+ });
201
+ }
202
+ }
203
+ /**
204
+ * Handles `focusin` actions for the component. When the component receives focus,
205
+ * the list of selected options is refreshed and the first selected option is scrolled
206
+ * into view.
207
+ *
208
+ * @internal
209
+ */
210
+ focusinHandler(e) {
211
+ if (!this.shouldSkipFocus && e.target === e.currentTarget) {
212
+ this.setSelectedOptions();
213
+ this.focusAndScrollOptionIntoView();
214
+ }
215
+ this.shouldSkipFocus = false;
216
+ }
217
+ /**
218
+ * Returns the options which match the current typeahead buffer.
219
+ *
220
+ * @internal
221
+ */
222
+ getTypeaheadMatches() {
223
+ const pattern = this.typeaheadBuffer.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&");
224
+ const re = new RegExp(`^${pattern}`, "gi");
225
+ return this.options.filter((o) => o.text.trim().match(re));
226
+ }
227
+ /**
228
+ * Determines the index of the next option which is selectable, if any.
229
+ *
230
+ * @param prev - the previous selected index
231
+ * @param next - the next index to select
232
+ *
233
+ * @internal
234
+ */
235
+ getSelectableIndex(prev = this.selectedIndex, next) {
236
+ const direction = prev > next ? -1 : prev < next ? 1 : 0;
237
+ const potentialDirection = prev + direction;
238
+ let nextSelectableOption = null;
239
+ switch (direction) {
240
+ case -1: {
241
+ nextSelectableOption = this.options.reduceRight((nextSelectableOption, thisOption, index) => !nextSelectableOption &&
242
+ !thisOption.disabled &&
243
+ index < potentialDirection
244
+ ? thisOption
245
+ : nextSelectableOption, nextSelectableOption);
246
+ break;
247
+ }
248
+ case 1: {
249
+ nextSelectableOption = this.options.reduce((nextSelectableOption, thisOption, index) => !nextSelectableOption &&
250
+ !thisOption.disabled &&
251
+ index > potentialDirection
252
+ ? thisOption
253
+ : nextSelectableOption, nextSelectableOption);
254
+ break;
255
+ }
256
+ }
257
+ return this.options.indexOf(nextSelectableOption);
258
+ }
259
+ /**
260
+ * Handles external changes to child options.
261
+ *
262
+ * @param source - the source object
263
+ * @param propertyName - the property
264
+ *
265
+ * @internal
266
+ */
267
+ handleChange(source, propertyName) {
268
+ switch (propertyName) {
269
+ case "selected": {
270
+ if (Listbox$1.slottedOptionFilter(source)) {
271
+ this.selectedIndex = this.options.indexOf(source);
272
+ }
273
+ this.setSelectedOptions();
274
+ break;
275
+ }
276
+ }
277
+ }
278
+ /**
279
+ * Moves focus to an option whose label matches characters typed by the user.
280
+ * Consecutive keystrokes are batched into a buffer of search text used
281
+ * to match against the set of options. If `TYPE_AHEAD_TIMEOUT_MS` passes
282
+ * between consecutive keystrokes, the search restarts.
283
+ *
284
+ * @param key - the key to be evaluated
285
+ *
286
+ * @internal
287
+ */
288
+ handleTypeAhead(key) {
289
+ if (this.typeaheadTimeout) {
290
+ window.clearTimeout(this.typeaheadTimeout);
291
+ }
292
+ this.typeaheadTimeout = window.setTimeout(() => (this.typeaheadExpired = true), Listbox$1.TYPE_AHEAD_TIMEOUT_MS);
293
+ if (key.length > 1) {
294
+ return;
295
+ }
296
+ this.typeaheadBuffer = `${this.typeaheadExpired ? "" : this.typeaheadBuffer}${key}`;
297
+ }
298
+ /**
299
+ * Handles `keydown` actions for listbox navigation and typeahead.
300
+ *
301
+ * @internal
302
+ */
303
+ keydownHandler(e) {
304
+ if (this.disabled) {
305
+ return true;
306
+ }
307
+ this.shouldSkipFocus = false;
308
+ const key = e.key;
309
+ switch (key) {
310
+ // Select the first available option
311
+ case keyHome: {
312
+ if (!e.shiftKey) {
313
+ e.preventDefault();
314
+ this.selectFirstOption();
315
+ }
316
+ break;
317
+ }
318
+ // Select the next selectable option
319
+ case keyArrowDown: {
320
+ if (!e.shiftKey) {
321
+ e.preventDefault();
322
+ this.selectNextOption();
323
+ }
324
+ break;
325
+ }
326
+ // Select the previous selectable option
327
+ case keyArrowUp: {
328
+ if (!e.shiftKey) {
329
+ e.preventDefault();
330
+ this.selectPreviousOption();
331
+ }
332
+ break;
333
+ }
334
+ // Select the last available option
335
+ case keyEnd: {
336
+ e.preventDefault();
337
+ this.selectLastOption();
338
+ break;
339
+ }
340
+ case keyTab: {
341
+ this.focusAndScrollOptionIntoView();
342
+ return true;
343
+ }
344
+ case keyEnter:
345
+ case keyEscape: {
346
+ return true;
347
+ }
348
+ case keySpace: {
349
+ if (this.typeaheadExpired) {
350
+ return true;
351
+ }
352
+ }
353
+ // Send key to Typeahead handler
354
+ default: {
355
+ if (key.length === 1) {
356
+ this.handleTypeAhead(`${key}`);
357
+ }
358
+ return true;
359
+ }
360
+ }
361
+ }
362
+ /**
363
+ * Prevents `focusin` events from firing before `click` events when the
364
+ * element is unfocused.
365
+ *
366
+ * @internal
367
+ */
368
+ mousedownHandler(e) {
369
+ this.shouldSkipFocus = !this.contains(document.activeElement);
370
+ return true;
371
+ }
372
+ /**
373
+ * Switches between single-selection and multi-selection mode.
374
+ *
375
+ * @param prev - the previous value of the `multiple` attribute
376
+ * @param next - the next value of the `multiple` attribute
377
+ *
378
+ * @internal
379
+ */
380
+ multipleChanged(prev, next) {
381
+ this.ariaMultiSelectable = next ? "true" : null;
382
+ }
383
+ /**
384
+ * Updates the list of selected options when the `selectedIndex` changes.
385
+ *
386
+ * @param prev - the previous selected index value
387
+ * @param next - the current selected index value
388
+ *
389
+ * @internal
390
+ */
391
+ selectedIndexChanged(prev, next) {
392
+ var _a;
393
+ if (!this.hasSelectableOptions) {
394
+ this.selectedIndex = -1;
395
+ return;
396
+ }
397
+ if (((_a = this.options[this.selectedIndex]) === null || _a === void 0 ? void 0 : _a.disabled) && typeof prev === "number") {
398
+ const selectableIndex = this.getSelectableIndex(prev, next);
399
+ const newNext = selectableIndex > -1 ? selectableIndex : prev;
400
+ this.selectedIndex = newNext;
401
+ if (next === newNext) {
402
+ this.selectedIndexChanged(next, newNext);
403
+ }
404
+ return;
405
+ }
406
+ this.setSelectedOptions();
407
+ }
408
+ /**
409
+ * Updates the selectedness of each option when the list of selected options changes.
410
+ *
411
+ * @param prev - the previous list of selected options
412
+ * @param next - the current list of selected options
413
+ *
414
+ * @internal
415
+ */
416
+ selectedOptionsChanged(prev, next) {
417
+ var _a;
418
+ const filteredNext = next.filter(Listbox$1.slottedOptionFilter);
419
+ (_a = this.options) === null || _a === void 0 ? void 0 : _a.forEach(o => {
420
+ const notifier = Observable.getNotifier(o);
421
+ notifier.unsubscribe(this, "selected");
422
+ o.selected = filteredNext.includes(o);
423
+ notifier.subscribe(this, "selected");
424
+ });
425
+ }
426
+ /**
427
+ * Moves focus to the first selectable option.
428
+ *
429
+ * @public
430
+ */
431
+ selectFirstOption() {
432
+ var _a, _b;
433
+ if (!this.disabled) {
434
+ this.selectedIndex = (_b = (_a = this.options) === null || _a === void 0 ? void 0 : _a.findIndex(o => !o.disabled)) !== null && _b !== void 0 ? _b : -1;
435
+ }
436
+ }
437
+ /**
438
+ * Moves focus to the last selectable option.
439
+ *
440
+ * @internal
441
+ */
442
+ selectLastOption() {
443
+ if (!this.disabled) {
444
+ this.selectedIndex = findLastIndex(this.options, o => !o.disabled);
445
+ }
446
+ }
447
+ /**
448
+ * Moves focus to the next selectable option.
449
+ *
450
+ * @internal
451
+ */
452
+ selectNextOption() {
453
+ if (!this.disabled && this.selectedIndex < this.options.length - 1) {
454
+ this.selectedIndex += 1;
455
+ }
456
+ }
457
+ /**
458
+ * Moves focus to the previous selectable option.
459
+ *
460
+ * @internal
461
+ */
462
+ selectPreviousOption() {
463
+ if (!this.disabled && this.selectedIndex > 0) {
464
+ this.selectedIndex = this.selectedIndex - 1;
465
+ }
466
+ }
467
+ /**
468
+ * Updates the selected index to match the first selected option.
469
+ *
470
+ * @internal
471
+ */
472
+ setDefaultSelectedOption() {
473
+ var _a, _b;
474
+ this.selectedIndex = (_b = (_a = this.options) === null || _a === void 0 ? void 0 : _a.findIndex(el => el.defaultSelected)) !== null && _b !== void 0 ? _b : -1;
475
+ }
476
+ /**
477
+ * Sets an option as selected and gives it focus.
478
+ *
479
+ * @public
480
+ */
481
+ setSelectedOptions() {
482
+ var _a, _b, _c;
483
+ if ((_a = this.options) === null || _a === void 0 ? void 0 : _a.length) {
484
+ this.selectedOptions = [this.options[this.selectedIndex]];
485
+ this.ariaActiveDescendant = (_c = (_b = this.firstSelectedOption) === null || _b === void 0 ? void 0 : _b.id) !== null && _c !== void 0 ? _c : "";
486
+ this.focusAndScrollOptionIntoView();
487
+ }
488
+ }
489
+ /**
490
+ * Updates the list of options and resets the selected option when the slotted option content changes.
491
+ *
492
+ * @param prev - the previous list of slotted options
493
+ * @param next - the current list of slotted options
494
+ *
495
+ * @internal
496
+ */
497
+ slottedOptionsChanged(prev, next) {
498
+ this.options = next.reduce((options, item) => {
499
+ if (isListboxOption(item)) {
500
+ options.push(item);
501
+ }
502
+ return options;
503
+ }, []);
504
+ const setSize = `${this.options.length}`;
505
+ this.options.forEach((option, index) => {
506
+ if (!option.id) {
507
+ option.id = uniqueId("option-");
508
+ }
509
+ option.ariaPosInSet = `${index + 1}`;
510
+ option.ariaSetSize = setSize;
511
+ });
512
+ if (this.$fastController.isConnected) {
513
+ this.setSelectedOptions();
514
+ this.setDefaultSelectedOption();
515
+ }
516
+ }
517
+ /**
518
+ * Updates the filtered list of options when the typeahead buffer changes.
519
+ *
520
+ * @param prev - the previous typeahead buffer value
521
+ * @param next - the current typeahead buffer value
522
+ *
523
+ * @internal
524
+ */
525
+ typeaheadBufferChanged(prev, next) {
526
+ if (this.$fastController.isConnected) {
527
+ const typeaheadMatches = this.getTypeaheadMatches();
528
+ if (typeaheadMatches.length) {
529
+ const selectedIndex = this.options.indexOf(typeaheadMatches[0]);
530
+ if (selectedIndex > -1) {
531
+ this.selectedIndex = selectedIndex;
532
+ }
533
+ }
534
+ this.typeaheadExpired = false;
535
+ }
536
+ }
537
+ }
538
+ /**
539
+ * A static filter to include only selectable options.
540
+ *
541
+ * @param n - element to filter
542
+ * @public
543
+ */
544
+ Listbox$1.slottedOptionFilter = (n) => isListboxOption(n) && !n.hidden;
545
+ /**
546
+ * Typeahead timeout in milliseconds.
547
+ *
548
+ * @internal
549
+ */
550
+ Listbox$1.TYPE_AHEAD_TIMEOUT_MS = 1000;
551
+ __decorate([
552
+ attr({ mode: "boolean" })
553
+ ], Listbox$1.prototype, "disabled", void 0);
554
+ __decorate([
555
+ observable
556
+ ], Listbox$1.prototype, "selectedIndex", void 0);
557
+ __decorate([
558
+ observable
559
+ ], Listbox$1.prototype, "selectedOptions", void 0);
560
+ __decorate([
561
+ observable
562
+ ], Listbox$1.prototype, "slottedOptions", void 0);
563
+ __decorate([
564
+ observable
565
+ ], Listbox$1.prototype, "typeaheadBuffer", void 0);
566
+ /**
567
+ * Includes ARIA states and properties relating to the ARIA listbox role
568
+ *
569
+ * @public
570
+ */
571
+ class DelegatesARIAListbox {
572
+ }
573
+ __decorate([
574
+ observable
575
+ ], DelegatesARIAListbox.prototype, "ariaActiveDescendant", void 0);
576
+ __decorate([
577
+ observable
578
+ ], DelegatesARIAListbox.prototype, "ariaDisabled", void 0);
579
+ __decorate([
580
+ observable
581
+ ], DelegatesARIAListbox.prototype, "ariaExpanded", void 0);
582
+ __decorate([
583
+ observable
584
+ ], DelegatesARIAListbox.prototype, "ariaMultiSelectable", void 0);
585
+ applyMixins(DelegatesARIAListbox, ARIAGlobalStatesAndProperties);
586
+ applyMixins(Listbox$1, DelegatesARIAListbox);
587
+
588
+ /**
589
+ * A Listbox Custom HTML Element.
590
+ * Implements the {@link https://w3c.github.io/aria/#listbox | ARIA listbox }.
591
+ *
592
+ * @public
593
+ */
594
+ class ListboxElement extends Listbox$1 {
595
+ constructor() {
596
+ super(...arguments);
597
+ /**
598
+ * The index of the most recently checked option.
599
+ *
600
+ * @internal
601
+ * @remarks
602
+ * Multiple-selection mode only.
603
+ */
604
+ this.activeIndex = -1;
605
+ /**
606
+ * The start index when checking a range of options.
607
+ *
608
+ * @internal
609
+ */
610
+ this.rangeStartIndex = -1;
611
+ }
612
+ /**
613
+ * Returns the last checked option.
614
+ *
615
+ * @internal
616
+ */
617
+ get activeOption() {
618
+ return this.options[this.activeIndex];
619
+ }
620
+ /**
621
+ * Returns the list of checked options.
622
+ *
623
+ * @internal
624
+ */
625
+ get checkedOptions() {
626
+ var _a;
627
+ return (_a = this.options) === null || _a === void 0 ? void 0 : _a.filter(o => o.checked);
628
+ }
629
+ /**
630
+ * Returns the index of the first selected option.
631
+ *
632
+ * @internal
633
+ */
634
+ get firstSelectedOptionIndex() {
635
+ return this.options.indexOf(this.firstSelectedOption);
636
+ }
637
+ /**
638
+ * Updates the `ariaActiveDescendant` property when the active index changes.
639
+ *
640
+ * @param prev - the previous active index
641
+ * @param next - the next active index
642
+ *
643
+ * @internal
644
+ */
645
+ activeIndexChanged(prev, next) {
646
+ var _a, _b;
647
+ this.ariaActiveDescendant = (_b = (_a = this.options[next]) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : "";
648
+ this.focusAndScrollOptionIntoView();
649
+ }
650
+ /**
651
+ * Toggles the checked state for the currently active option.
652
+ *
653
+ * @remarks
654
+ * Multiple-selection mode only.
655
+ *
656
+ * @internal
657
+ */
658
+ checkActiveIndex() {
659
+ if (!this.multiple) {
660
+ return;
661
+ }
662
+ const activeItem = this.activeOption;
663
+ if (activeItem) {
664
+ activeItem.checked = true;
665
+ }
666
+ }
667
+ /**
668
+ * Sets the active index to the first option and marks it as checked.
669
+ *
670
+ * @remarks
671
+ * Multi-selection mode only.
672
+ *
673
+ * @param preserveChecked - mark all options unchecked before changing the active index
674
+ *
675
+ * @internal
676
+ */
677
+ checkFirstOption(preserveChecked = false) {
678
+ if (preserveChecked) {
679
+ if (this.rangeStartIndex === -1) {
680
+ this.rangeStartIndex = this.activeIndex + 1;
681
+ }
682
+ this.options.forEach((o, i) => {
683
+ o.checked = inRange(i, this.rangeStartIndex);
684
+ });
685
+ }
686
+ else {
687
+ this.uncheckAllOptions();
688
+ }
689
+ this.activeIndex = 0;
690
+ this.checkActiveIndex();
691
+ }
692
+ /**
693
+ * Decrements the active index and sets the matching option as checked.
694
+ *
695
+ * @remarks
696
+ * Multi-selection mode only.
697
+ *
698
+ * @param preserveChecked - mark all options unchecked before changing the active index
699
+ *
700
+ * @internal
701
+ */
702
+ checkLastOption(preserveChecked = false) {
703
+ if (preserveChecked) {
704
+ if (this.rangeStartIndex === -1) {
705
+ this.rangeStartIndex = this.activeIndex;
706
+ }
707
+ this.options.forEach((o, i) => {
708
+ o.checked = inRange(i, this.rangeStartIndex, this.options.length);
709
+ });
710
+ }
711
+ else {
712
+ this.uncheckAllOptions();
713
+ }
714
+ this.activeIndex = this.options.length - 1;
715
+ this.checkActiveIndex();
716
+ }
717
+ /**
718
+ * @override
719
+ * @internal
720
+ */
721
+ connectedCallback() {
722
+ super.connectedCallback();
723
+ this.addEventListener("focusout", this.focusoutHandler);
724
+ }
725
+ /**
726
+ * @override
727
+ * @internal
728
+ */
729
+ disconnectedCallback() {
730
+ this.removeEventListener("focusout", this.focusoutHandler);
731
+ super.disconnectedCallback();
732
+ }
733
+ /**
734
+ * Increments the active index and marks the matching option as checked.
735
+ *
736
+ * @remarks
737
+ * Multiple-selection mode only.
738
+ *
739
+ * @param preserveChecked - mark all options unchecked before changing the active index
740
+ *
741
+ * @internal
742
+ */
743
+ checkNextOption(preserveChecked = false) {
744
+ if (preserveChecked) {
745
+ if (this.rangeStartIndex === -1) {
746
+ this.rangeStartIndex = this.activeIndex;
747
+ }
748
+ this.options.forEach((o, i) => {
749
+ o.checked = inRange(i, this.rangeStartIndex, this.activeIndex + 1);
750
+ });
751
+ }
752
+ else {
753
+ this.uncheckAllOptions();
754
+ }
755
+ this.activeIndex += this.activeIndex < this.options.length - 1 ? 1 : 0;
756
+ this.checkActiveIndex();
757
+ }
758
+ /**
759
+ * Decrements the active index and marks the matching option as checked.
760
+ *
761
+ * @remarks
762
+ * Multiple-selection mode only.
763
+ *
764
+ * @param preserveChecked - mark all options unchecked before changing the active index
765
+ *
766
+ * @internal
767
+ */
768
+ checkPreviousOption(preserveChecked = false) {
769
+ if (preserveChecked) {
770
+ if (this.rangeStartIndex === -1) {
771
+ this.rangeStartIndex = this.activeIndex;
772
+ }
773
+ if (this.checkedOptions.length === 1) {
774
+ this.rangeStartIndex += 1;
775
+ }
776
+ this.options.forEach((o, i) => {
777
+ o.checked = inRange(i, this.activeIndex, this.rangeStartIndex);
778
+ });
779
+ }
780
+ else {
781
+ this.uncheckAllOptions();
782
+ }
783
+ this.activeIndex -= this.activeIndex > 0 ? 1 : 0;
784
+ this.checkActiveIndex();
785
+ }
786
+ /**
787
+ * Handles click events for listbox options.
788
+ *
789
+ * @param e - the event object
790
+ *
791
+ * @override
792
+ * @internal
793
+ */
794
+ clickHandler(e) {
795
+ var _a;
796
+ if (!this.multiple) {
797
+ return super.clickHandler(e);
798
+ }
799
+ const captured = (_a = e.target) === null || _a === void 0 ? void 0 : _a.closest(`[role=option]`);
800
+ if (!captured || captured.disabled) {
801
+ return;
802
+ }
803
+ this.uncheckAllOptions();
804
+ this.activeIndex = this.options.indexOf(captured);
805
+ this.checkActiveIndex();
806
+ this.toggleSelectedForAllCheckedOptions();
807
+ return true;
808
+ }
809
+ /**
810
+ * @override
811
+ * @internal
812
+ */
813
+ focusAndScrollOptionIntoView() {
814
+ super.focusAndScrollOptionIntoView(this.activeOption);
815
+ }
816
+ /**
817
+ * In multiple-selection mode:
818
+ * If any options are selected, the first selected option is checked when
819
+ * the listbox receives focus. If no options are selected, the first
820
+ * selectable option is checked.
821
+ *
822
+ * @override
823
+ * @internal
824
+ */
825
+ focusinHandler(e) {
826
+ if (!this.multiple) {
827
+ return super.focusinHandler(e);
828
+ }
829
+ if (!this.shouldSkipFocus && e.target === e.currentTarget) {
830
+ this.uncheckAllOptions();
831
+ if (this.activeIndex === -1) {
832
+ this.activeIndex =
833
+ this.firstSelectedOptionIndex !== -1
834
+ ? this.firstSelectedOptionIndex
835
+ : 0;
836
+ }
837
+ this.checkActiveIndex();
838
+ this.setSelectedOptions();
839
+ this.focusAndScrollOptionIntoView();
840
+ }
841
+ this.shouldSkipFocus = false;
842
+ }
843
+ /**
844
+ * Unchecks all options when the listbox loses focus.
845
+ *
846
+ * @internal
847
+ */
848
+ focusoutHandler(e) {
849
+ if (this.multiple) {
850
+ this.uncheckAllOptions();
851
+ }
852
+ }
853
+ /**
854
+ * Handles keydown actions for listbox navigation and typeahead
855
+ *
856
+ * @override
857
+ * @internal
858
+ */
859
+ keydownHandler(e) {
860
+ if (!this.multiple) {
861
+ return super.keydownHandler(e);
862
+ }
863
+ if (this.disabled) {
864
+ return true;
865
+ }
866
+ const { key, shiftKey } = e;
867
+ this.shouldSkipFocus = false;
868
+ switch (key) {
869
+ // Select the first available option
870
+ case keyHome: {
871
+ this.checkFirstOption(shiftKey);
872
+ return;
873
+ }
874
+ // Select the next selectable option
875
+ case keyArrowDown: {
876
+ this.checkNextOption(shiftKey);
877
+ return;
878
+ }
879
+ // Select the previous selectable option
880
+ case keyArrowUp: {
881
+ this.checkPreviousOption(shiftKey);
882
+ return;
883
+ }
884
+ // Select the last available option
885
+ case keyEnd: {
886
+ this.checkLastOption(shiftKey);
887
+ return;
888
+ }
889
+ case keyTab: {
890
+ this.focusAndScrollOptionIntoView();
891
+ return true;
892
+ }
893
+ case keyEscape: {
894
+ this.uncheckAllOptions();
895
+ this.checkActiveIndex();
896
+ return true;
897
+ }
898
+ case keySpace: {
899
+ e.preventDefault();
900
+ if (this.typeAheadExpired) {
901
+ this.toggleSelectedForAllCheckedOptions();
902
+ return;
903
+ }
904
+ }
905
+ // Send key to Typeahead handler
906
+ default: {
907
+ if (key.length === 1) {
908
+ this.handleTypeAhead(`${key}`);
909
+ }
910
+ return true;
911
+ }
912
+ }
913
+ }
914
+ /**
915
+ * Prevents `focusin` events from firing before `click` events when the
916
+ * element is unfocused.
917
+ *
918
+ * @override
919
+ * @internal
920
+ */
921
+ mousedownHandler(e) {
922
+ if (e.offsetX >= 0 && e.offsetX <= this.scrollWidth) {
923
+ return super.mousedownHandler(e);
924
+ }
925
+ }
926
+ /**
927
+ * Switches between single-selection and multi-selection mode.
928
+ *
929
+ * @internal
930
+ */
931
+ multipleChanged(prev, next) {
932
+ var _a;
933
+ this.ariaMultiSelectable = next ? "true" : null;
934
+ (_a = this.options) === null || _a === void 0 ? void 0 : _a.forEach(o => {
935
+ o.checked = next ? false : undefined;
936
+ });
937
+ this.setSelectedOptions();
938
+ }
939
+ /**
940
+ * Sets an option as selected and gives it focus.
941
+ *
942
+ * @override
943
+ * @public
944
+ */
945
+ setSelectedOptions() {
946
+ if (!this.multiple) {
947
+ super.setSelectedOptions();
948
+ return;
949
+ }
950
+ if (this.$fastController.isConnected && this.options) {
951
+ this.selectedOptions = this.options.filter(o => o.selected);
952
+ this.focusAndScrollOptionIntoView();
953
+ }
954
+ }
955
+ /**
956
+ * Ensures the size is a positive integer when the property is updated.
957
+ *
958
+ * @param prev - the previous size value
959
+ * @param next - the current size value
960
+ *
961
+ * @internal
962
+ */
963
+ sizeChanged(prev, next) {
964
+ var _a;
965
+ const size = Math.max(0, parseInt((_a = next === null || next === void 0 ? void 0 : next.toFixed()) !== null && _a !== void 0 ? _a : "", 10));
966
+ if (size !== next) {
967
+ DOM.queueUpdate(() => {
968
+ this.size = size;
969
+ });
970
+ }
971
+ }
972
+ /**
973
+ * Toggles the selected state of the provided options. If any provided items
974
+ * are in an unselected state, all items are set to selected. If every
975
+ * provided item is selected, they are all unselected.
976
+ *
977
+ * @internal
978
+ */
979
+ toggleSelectedForAllCheckedOptions() {
980
+ const enabledCheckedOptions = this.checkedOptions.filter(o => !o.disabled);
981
+ const force = !enabledCheckedOptions.every(o => o.selected);
982
+ enabledCheckedOptions.forEach(o => (o.selected = force));
983
+ this.selectedIndex = this.options.indexOf(enabledCheckedOptions[enabledCheckedOptions.length - 1]);
984
+ this.setSelectedOptions();
985
+ }
986
+ /**
987
+ * @override
988
+ * @internal
989
+ */
990
+ typeaheadBufferChanged(prev, next) {
991
+ if (!this.multiple) {
992
+ super.typeaheadBufferChanged(prev, next);
993
+ return;
994
+ }
995
+ if (this.$fastController.isConnected) {
996
+ const typeaheadMatches = this.getTypeaheadMatches();
997
+ const activeIndex = this.options.indexOf(typeaheadMatches[0]);
998
+ if (activeIndex > -1) {
999
+ this.activeIndex = activeIndex;
1000
+ this.uncheckAllOptions();
1001
+ this.checkActiveIndex();
1002
+ }
1003
+ this.typeAheadExpired = false;
1004
+ }
1005
+ }
1006
+ /**
1007
+ * Unchecks all options.
1008
+ *
1009
+ * @remarks
1010
+ * Multiple-selection mode only.
1011
+ *
1012
+ * @param preserveChecked - reset the rangeStartIndex
1013
+ *
1014
+ * @internal
1015
+ */
1016
+ uncheckAllOptions(preserveChecked = false) {
1017
+ this.options.forEach(o => (o.checked = this.multiple ? false : undefined));
1018
+ if (!preserveChecked) {
1019
+ this.rangeStartIndex = -1;
1020
+ }
1021
+ }
1022
+ }
1023
+ __decorate([
1024
+ observable
1025
+ ], ListboxElement.prototype, "activeIndex", void 0);
1026
+ __decorate([
1027
+ attr({ mode: "boolean" })
1028
+ ], ListboxElement.prototype, "multiple", void 0);
1029
+ __decorate([
1030
+ attr({ converter: nullableNumberConverter })
1031
+ ], ListboxElement.prototype, "size", void 0);
1032
+
1033
+ var _Listbox_instances, _Listbox_disableSlottedChildren;
1034
+ class Listbox extends ListboxElement {
1035
+ constructor() {
1036
+ super(...arguments);
1037
+ _Listbox_instances.add(this);
1038
+ }
1039
+ slottedOptionsChanged(prev, next) {
1040
+ super.slottedOptionsChanged(prev, next);
1041
+ __classPrivateFieldGet(this, _Listbox_instances, "m", _Listbox_disableSlottedChildren).call(this);
1042
+ }
1043
+ attributeChangedCallback(name, oldValue, newValue) {
1044
+ super.attributeChangedCallback(name, oldValue, newValue);
1045
+ if (name === 'disabled') {
1046
+ __classPrivateFieldGet(this, _Listbox_instances, "m", _Listbox_disableSlottedChildren).call(this);
1047
+ }
1048
+ }
1049
+ }
1050
+ _Listbox_instances = new WeakSet(), _Listbox_disableSlottedChildren = function _Listbox_disableSlottedChildren() {
1051
+ this.options.forEach(optionElement => {
1052
+ if (!optionElement.disabled) {
1053
+ optionElement.disabled = this.disabled;
1054
+ }
1055
+ });
1056
+ };
1057
+ __decorate([attr, __metadata("design:type", String)], Listbox.prototype, "appearance", void 0);
1058
+
1059
+ let _ = t => t,
1060
+ _t;
1061
+ const getClasses = ({
1062
+ appearance,
1063
+ disabled
1064
+ }) => classNames('base', ['disabled', disabled], [`appearance-${appearance}`, Boolean(appearance)]);
1065
+ const ListboxTemplate = context => {
1066
+ const focusTemplate = focusTemplateFactory(context);
1067
+ return html(_t || (_t = _`
1068
+ <template
1069
+ aria-activedescendant="${0}"
1070
+ aria-multiselectable="${0}"
1071
+ aria-label="listbox"
1072
+ role="listbox"
1073
+ @click="${0}"
1074
+ @focusin="${0}"
1075
+ @keydown="${0}"
1076
+ @mousedown="${0}"
1077
+ tabindex="${0}"
1078
+ >
1079
+ <div
1080
+ class="${0}"
1081
+ >
1082
+ ${0}
1083
+
1084
+ <slot
1085
+ ${0}
1086
+ ></slot>
1087
+ </div>
1088
+ </template>
1089
+ `), x => x.ariaActiveDescendant, x => x.ariaMultiSelectable, (x, c) => x.clickHandler(c.event), (x, c) => x.focusinHandler(c.event), (x, c) => x.keydownHandler(c.event), (x, c) => x.mousedownHandler(c.event), x => !x.disabled ? '0' : null, getClasses, () => focusTemplate, slotted({
1090
+ filter: ListboxElement.slottedOptionFilter,
1091
+ flatten: true,
1092
+ property: 'slottedOptions'
1093
+ }));
1094
+ };
1095
+
1096
+ var css_248z = "/**\n * Do not edit directly\n * Generated on Mon, 07 Nov 2022 11:12:43 GMT\n */\n:host {\n display: block;\n}\n\n:host(:focus-within) {\n outline: none;\n}\n\n.base {\n position: relative;\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 gap: 4px;\n}\n.base {\n --_connotation-color-primary: var(--vvd-color-canvas-text);\n --_connotation-color-primary-text: var(--vvd-color-canvas);\n --_connotation-color-primary-increment: var(--vvd-color-neutral-800);\n --_connotation-color-contrast: var(--vvd-color-neutral-800);\n --_connotation-color-fierce: var(--vvd-color-neutral-700);\n --_connotation-color-firm: var(--vvd-color-canvas-text);\n --_connotation-color-soft: var(--vvd-color-neutral-100);\n --_connotation-color-faint: var(--vvd-color-neutral-50);\n}\n.base {\n --_appearance-color-text: var(--vvd-color-canvas-text);\n --_appearance-color-fill: var(--vvd-color-canvas);\n --_appearance-color-outline: var(--vvd-color-neutral-400);\n}\n.base.appearance-ghost {\n --_appearance-color-text: var(--_connotation-color-primary);\n --_appearance-color-fill: transparent;\n --_appearance-color-outline: transparent;\n}\n.base:where(:disabled, .disabled) {\n --_appearance-color-text: var(--vvd-color-neutral-400);\n --_appearance-color-fill: var(--vvd-color-neutral-100);\n --_appearance-color-outline: var(--vvd-color-neutral-400);\n}\n.base: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.focus-indicator {\n pointer-events: none;\n}\n:host(:not(:focus-within)) .focus-indicator {\n display: none;\n}";
1097
+
1098
+ const vividListbox = Listbox.compose({
1099
+ baseName: 'listbox',
1100
+ template: ListboxTemplate,
1101
+ styles: css_248z
1102
+ });
1103
+ designSystem.register(vividListbox());
1104
+
1105
+ export { vividListbox };