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

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