@fluentui/web-components 2.2.2 → 2.3.1

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.
@@ -3405,7 +3405,10 @@ class ArrayObserver extends SubscriberSet {
3405
3405
  this.splices = void 0;
3406
3406
  this.needsQueue = true;
3407
3407
  this.call = this.flush;
3408
- source.$fastController = this;
3408
+ Reflect.defineProperty(source, "$fastController", {
3409
+ value: this,
3410
+ enumerable: false
3411
+ });
3409
3412
  }
3410
3413
 
3411
3414
  addSplice(splice) {
@@ -3469,16 +3472,26 @@ function enableArrayObservation() {
3469
3472
  Observable.setArrayObserverFactory(collection => {
3470
3473
  return new ArrayObserver(collection);
3471
3474
  });
3472
- const arrayProto = Array.prototype;
3473
- const pop = arrayProto.pop;
3474
- const push = arrayProto.push;
3475
- const reverse = arrayProto.reverse;
3476
- const shift = arrayProto.shift;
3477
- const sort = arrayProto.sort;
3478
- const splice = arrayProto.splice;
3479
- const unshift = arrayProto.unshift;
3480
-
3481
- arrayProto.pop = function () {
3475
+ const proto = Array.prototype; // Don't patch Array if it has already been patched
3476
+ // by another copy of fast-element.
3477
+
3478
+ if (proto.$fastPatch) {
3479
+ return;
3480
+ }
3481
+
3482
+ Reflect.defineProperty(proto, "$fastPatch", {
3483
+ value: 1,
3484
+ enumerable: false
3485
+ });
3486
+ const pop = proto.pop;
3487
+ const push = proto.push;
3488
+ const reverse = proto.reverse;
3489
+ const shift = proto.shift;
3490
+ const sort = proto.sort;
3491
+ const splice = proto.splice;
3492
+ const unshift = proto.unshift;
3493
+
3494
+ proto.pop = function () {
3482
3495
  const notEmpty = this.length > 0;
3483
3496
  const methodCallResult = pop.apply(this, arguments);
3484
3497
  const o = this.$fastController;
@@ -3490,7 +3503,7 @@ function enableArrayObservation() {
3490
3503
  return methodCallResult;
3491
3504
  };
3492
3505
 
3493
- arrayProto.push = function () {
3506
+ proto.push = function () {
3494
3507
  const methodCallResult = push.apply(this, arguments);
3495
3508
  const o = this.$fastController;
3496
3509
 
@@ -3501,7 +3514,7 @@ function enableArrayObservation() {
3501
3514
  return methodCallResult;
3502
3515
  };
3503
3516
 
3504
- arrayProto.reverse = function () {
3517
+ proto.reverse = function () {
3505
3518
  let oldArray;
3506
3519
  const o = this.$fastController;
3507
3520
 
@@ -3519,7 +3532,7 @@ function enableArrayObservation() {
3519
3532
  return methodCallResult;
3520
3533
  };
3521
3534
 
3522
- arrayProto.shift = function () {
3535
+ proto.shift = function () {
3523
3536
  const notEmpty = this.length > 0;
3524
3537
  const methodCallResult = shift.apply(this, arguments);
3525
3538
  const o = this.$fastController;
@@ -3531,7 +3544,7 @@ function enableArrayObservation() {
3531
3544
  return methodCallResult;
3532
3545
  };
3533
3546
 
3534
- arrayProto.sort = function () {
3547
+ proto.sort = function () {
3535
3548
  let oldArray;
3536
3549
  const o = this.$fastController;
3537
3550
 
@@ -3549,7 +3562,7 @@ function enableArrayObservation() {
3549
3562
  return methodCallResult;
3550
3563
  };
3551
3564
 
3552
- arrayProto.splice = function () {
3565
+ proto.splice = function () {
3553
3566
  const methodCallResult = splice.apply(this, arguments);
3554
3567
  const o = this.$fastController;
3555
3568
 
@@ -3560,7 +3573,7 @@ function enableArrayObservation() {
3560
3573
  return methodCallResult;
3561
3574
  };
3562
3575
 
3563
- arrayProto.unshift = function () {
3576
+ proto.unshift = function () {
3564
3577
  const methodCallResult = unshift.apply(this, arguments);
3565
3578
  const o = this.$fastController;
3566
3579
 
@@ -5919,6 +5932,24 @@ var Orientation;
5919
5932
  Orientation["vertical"] = "vertical";
5920
5933
  })(Orientation || (Orientation = {}));
5921
5934
 
5935
+ /**
5936
+ * Returns the index of the last element in the array where predicate is true, and -1 otherwise.
5937
+ *
5938
+ * @param array - the array to test
5939
+ * @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.
5940
+ */
5941
+ function findLastIndex(array, predicate) {
5942
+ let k = array.length;
5943
+
5944
+ while (k--) {
5945
+ if (predicate(array[k], k, array)) {
5946
+ return k;
5947
+ }
5948
+ }
5949
+
5950
+ return -1;
5951
+ }
5952
+
5922
5953
  /**
5923
5954
  * Checks if the DOM is available to access and use
5924
5955
  */
@@ -6147,6 +6178,18 @@ function wrapInBounds(min, max, value) {
6147
6178
  function limit(min, max, value) {
6148
6179
  return Math.min(Math.max(value, min), max);
6149
6180
  }
6181
+ /**
6182
+ * Determines if a number value is within a specified range.
6183
+ *
6184
+ * @param value - the value to check
6185
+ * @param min - the range start
6186
+ * @param max - the range end
6187
+ */
6188
+
6189
+ function inRange(value, min, max = 0) {
6190
+ [min, max] = [min, max].sort((a, b) => a - b);
6191
+ return min <= value && value < max;
6192
+ }
6150
6193
 
6151
6194
  let uniqueIdCounter = 0;
6152
6195
  /**
@@ -6266,7 +6309,7 @@ class Accordion extends FoundationElement {
6266
6309
 
6267
6310
  this.activeItemChange = event => {
6268
6311
  const selectedItem = event.target;
6269
- this.activeid = event.target.getAttribute("id");
6312
+ this.activeid = selectedItem.getAttribute("id");
6270
6313
 
6271
6314
  if (this.isSingleExpandMode()) {
6272
6315
  this.resetItems();
@@ -10452,7 +10495,6 @@ class ListboxOption extends FoundationElement {
10452
10495
  */
10453
10496
 
10454
10497
  this.dirtyValue = false;
10455
- this.initialValue = this.initialValue || "";
10456
10498
 
10457
10499
  if (text) {
10458
10500
  this.textContent = text;
@@ -10540,7 +10582,9 @@ class ListboxOption extends FoundationElement {
10540
10582
  }
10541
10583
 
10542
10584
  get label() {
10543
- return this.value ? this.value : this.textContent ? this.textContent : "";
10585
+ var _a, _b;
10586
+
10587
+ return (_b = (_a = this.value) !== null && _a !== void 0 ? _a : this.textContent) !== null && _b !== void 0 ? _b : "";
10544
10588
  }
10545
10589
 
10546
10590
  get text() {
@@ -10559,8 +10603,10 @@ class ListboxOption extends FoundationElement {
10559
10603
  }
10560
10604
 
10561
10605
  get value() {
10606
+ var _a, _b;
10607
+
10562
10608
  Observable.track(this, "value");
10563
- return this._value ? this._value : this.text;
10609
+ return (_b = (_a = this._value) !== null && _a !== void 0 ? _a : this.textContent) !== null && _b !== void 0 ? _b : "";
10564
10610
  }
10565
10611
 
10566
10612
  get form() {
@@ -10681,6 +10727,16 @@ class Listbox$1 extends FoundationElement {
10681
10727
 
10682
10728
  return (_a = this.selectedOptions[0]) !== null && _a !== void 0 ? _a : null;
10683
10729
  }
10730
+ /**
10731
+ * Returns true if there is one or more selectable option.
10732
+ *
10733
+ * @internal
10734
+ */
10735
+
10736
+
10737
+ get hasSelectableOptions() {
10738
+ return this.options.length > 0 && !this.options.every(o => o.disabled);
10739
+ }
10684
10740
  /**
10685
10741
  * The number of options.
10686
10742
  *
@@ -10689,11 +10745,9 @@ class Listbox$1 extends FoundationElement {
10689
10745
 
10690
10746
 
10691
10747
  get length() {
10692
- if (this.options) {
10693
- return this.options.length;
10694
- }
10748
+ var _a, _b;
10695
10749
 
10696
- return 0;
10750
+ return (_b = (_a = this.options) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0;
10697
10751
  }
10698
10752
  /**
10699
10753
  * The list of options.
@@ -10742,17 +10796,23 @@ class Listbox$1 extends FoundationElement {
10742
10796
  }
10743
10797
  }
10744
10798
  /**
10745
- * Focus the first selected option and scroll it into view.
10799
+ * Ensures that the provided option is focused and scrolled into view.
10746
10800
  *
10801
+ * @param optionToFocus - The option to focus
10747
10802
  * @internal
10748
10803
  */
10749
10804
 
10750
10805
 
10751
- focusAndScrollOptionIntoView() {
10752
- if (this.contains(document.activeElement) && this.firstSelectedOption) {
10753
- this.firstSelectedOption.focus();
10806
+ focusAndScrollOptionIntoView(optionToFocus = this.firstSelectedOption) {
10807
+ // To ensure that the browser handles both `focus()` and `scrollIntoView()`, the
10808
+ // timing here needs to guarantee that they happen on different frames. Since this
10809
+ // function is typically called from the `openChanged` observer, `DOM.queueUpdate`
10810
+ // causes the calls to be grouped into the same frame. To prevent this,
10811
+ // `requestAnimationFrame` is used instead of `DOM.queueUpdate`.
10812
+ if (this.contains(document.activeElement) && optionToFocus !== null) {
10813
+ optionToFocus.focus();
10754
10814
  requestAnimationFrame(() => {
10755
- this.firstSelectedOption.scrollIntoView({
10815
+ optionToFocus.scrollIntoView({
10756
10816
  block: "nearest"
10757
10817
  });
10758
10818
  });
@@ -10775,6 +10835,72 @@ class Listbox$1 extends FoundationElement {
10775
10835
 
10776
10836
  this.shouldSkipFocus = false;
10777
10837
  }
10838
+ /**
10839
+ * Returns the options which match the current typeahead buffer.
10840
+ *
10841
+ * @internal
10842
+ */
10843
+
10844
+
10845
+ getTypeaheadMatches() {
10846
+ const pattern = this.typeaheadBuffer.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&");
10847
+ const re = new RegExp(`^${pattern}`, "gi");
10848
+ return this.options.filter(o => o.text.trim().match(re));
10849
+ }
10850
+ /**
10851
+ * Determines the index of the next option which is selectable, if any.
10852
+ *
10853
+ * @param prev - the previous selected index
10854
+ * @param next - the next index to select
10855
+ *
10856
+ * @internal
10857
+ */
10858
+
10859
+
10860
+ getSelectableIndex(prev = this.selectedIndex, next) {
10861
+ const direction = prev > next ? -1 : prev < next ? 1 : 0;
10862
+ const potentialDirection = prev + direction;
10863
+ let nextSelectableOption = null;
10864
+
10865
+ switch (direction) {
10866
+ case -1:
10867
+ {
10868
+ nextSelectableOption = this.options.reduceRight((nextSelectableOption, thisOption, index) => !nextSelectableOption && !thisOption.disabled && index < potentialDirection ? thisOption : nextSelectableOption, nextSelectableOption);
10869
+ break;
10870
+ }
10871
+
10872
+ case 1:
10873
+ {
10874
+ nextSelectableOption = this.options.reduce((nextSelectableOption, thisOption, index) => !nextSelectableOption && !thisOption.disabled && index > potentialDirection ? thisOption : nextSelectableOption, nextSelectableOption);
10875
+ break;
10876
+ }
10877
+ }
10878
+
10879
+ return this.options.indexOf(nextSelectableOption);
10880
+ }
10881
+ /**
10882
+ * Handles external changes to child options.
10883
+ *
10884
+ * @param source - the source object
10885
+ * @param propertyName - the property
10886
+ *
10887
+ * @internal
10888
+ */
10889
+
10890
+
10891
+ handleChange(source, propertyName) {
10892
+ switch (propertyName) {
10893
+ case "selected":
10894
+ {
10895
+ if (Listbox$1.slottedOptionFilter(source)) {
10896
+ this.selectedIndex = this.options.indexOf(source);
10897
+ }
10898
+
10899
+ this.setSelectedOptions();
10900
+ break;
10901
+ }
10902
+ }
10903
+ }
10778
10904
  /**
10779
10905
  * Moves focus to an option whose label matches characters typed by the user.
10780
10906
  * Consecutive keystrokes are batched into a buffer of search text used
@@ -10782,6 +10908,8 @@ class Listbox$1 extends FoundationElement {
10782
10908
  * between consecutive keystrokes, the search restarts.
10783
10909
  *
10784
10910
  * @param key - the key to be evaluated
10911
+ *
10912
+ * @internal
10785
10913
  */
10786
10914
 
10787
10915
 
@@ -10897,6 +11025,19 @@ class Listbox$1 extends FoundationElement {
10897
11025
  this.shouldSkipFocus = !this.contains(document.activeElement);
10898
11026
  return true;
10899
11027
  }
11028
+ /**
11029
+ * Switches between single-selection and multi-selection mode.
11030
+ *
11031
+ * @param prev - the previous value of the `multiple` attribute
11032
+ * @param next - the next value of the `multiple` attribute
11033
+ *
11034
+ * @internal
11035
+ */
11036
+
11037
+
11038
+ multipleChanged(prev, next) {
11039
+ this.ariaMultiSelectable = next ? "true" : undefined;
11040
+ }
10900
11041
  /**
10901
11042
  * Updates the list of selected options when the `selectedIndex` changes.
10902
11043
  *
@@ -10908,6 +11049,25 @@ class Listbox$1 extends FoundationElement {
10908
11049
 
10909
11050
 
10910
11051
  selectedIndexChanged(prev, next) {
11052
+ var _a;
11053
+
11054
+ if (!this.hasSelectableOptions) {
11055
+ this.selectedIndex = -1;
11056
+ return;
11057
+ }
11058
+
11059
+ if (((_a = this.options[this.selectedIndex]) === null || _a === void 0 ? void 0 : _a.disabled) && typeof prev === "number") {
11060
+ const selectableIndex = this.getSelectableIndex(prev, next);
11061
+ const newNext = selectableIndex > -1 ? selectableIndex : prev;
11062
+ this.selectedIndex = newNext;
11063
+
11064
+ if (next === newNext) {
11065
+ this.selectedIndexChanged(next, newNext);
11066
+ }
11067
+
11068
+ return;
11069
+ }
11070
+
10911
11071
  this.setSelectedOptions();
10912
11072
  }
10913
11073
  /**
@@ -10921,11 +11081,15 @@ class Listbox$1 extends FoundationElement {
10921
11081
 
10922
11082
 
10923
11083
  selectedOptionsChanged(prev, next) {
10924
- if (this.$fastController.isConnected) {
10925
- this.options.forEach(o => {
10926
- o.selected = next.includes(o);
10927
- });
10928
- }
11084
+ var _a;
11085
+
11086
+ const filteredNext = next.filter(Listbox$1.slottedOptionFilter);
11087
+ (_a = this.options) === null || _a === void 0 ? void 0 : _a.forEach(o => {
11088
+ const notifier = Observable.getNotifier(o);
11089
+ notifier.unsubscribe(this, "selected");
11090
+ o.selected = filteredNext.includes(o);
11091
+ notifier.subscribe(this, "selected");
11092
+ });
10929
11093
  }
10930
11094
  /**
10931
11095
  * Moves focus to the first selectable option.
@@ -10935,8 +11099,10 @@ class Listbox$1 extends FoundationElement {
10935
11099
 
10936
11100
 
10937
11101
  selectFirstOption() {
11102
+ var _a, _b;
11103
+
10938
11104
  if (!this.disabled) {
10939
- this.selectedIndex = 0;
11105
+ this.selectedIndex = (_b = (_a = this.options) === null || _a === void 0 ? void 0 : _a.findIndex(o => !o.disabled)) !== null && _b !== void 0 ? _b : -1;
10940
11106
  }
10941
11107
  }
10942
11108
  /**
@@ -10948,7 +11114,7 @@ class Listbox$1 extends FoundationElement {
10948
11114
 
10949
11115
  selectLastOption() {
10950
11116
  if (!this.disabled) {
10951
- this.selectedIndex = this.options.length - 1;
11117
+ this.selectedIndex = findLastIndex(this.options, o => !o.disabled);
10952
11118
  }
10953
11119
  }
10954
11120
  /**
@@ -10959,7 +11125,7 @@ class Listbox$1 extends FoundationElement {
10959
11125
 
10960
11126
 
10961
11127
  selectNextOption() {
10962
- if (!this.disabled && this.options && this.selectedIndex < this.options.length - 1) {
11128
+ if (!this.disabled && this.selectedIndex < this.options.length - 1) {
10963
11129
  this.selectedIndex += 1;
10964
11130
  }
10965
11131
  }
@@ -10983,8 +11149,10 @@ class Listbox$1 extends FoundationElement {
10983
11149
 
10984
11150
 
10985
11151
  setDefaultSelectedOption() {
10986
- if (this.options && this.$fastController.isConnected) {
10987
- const selectedIndex = this.options.findIndex(el => el.getAttribute("selected") !== null);
11152
+ var _a;
11153
+
11154
+ if (this.$fastController.isConnected) {
11155
+ const selectedIndex = (_a = this.options) === null || _a === void 0 ? void 0 : _a.findIndex(el => el.getAttribute("selected") !== null);
10988
11156
 
10989
11157
  if (selectedIndex !== -1) {
10990
11158
  this.selectedIndex = selectedIndex;
@@ -10995,7 +11163,7 @@ class Listbox$1 extends FoundationElement {
10995
11163
  }
10996
11164
  }
10997
11165
  /**
10998
- * Sets the selected option and gives it focus.
11166
+ * Sets an option as selected and gives it focus.
10999
11167
  *
11000
11168
  * @public
11001
11169
  */
@@ -11004,9 +11172,8 @@ class Listbox$1 extends FoundationElement {
11004
11172
  setSelectedOptions() {
11005
11173
  var _a, _b, _c;
11006
11174
 
11007
- if (this.$fastController.isConnected && this.options) {
11008
- const selectedOption = (_a = this.options[this.selectedIndex]) !== null && _a !== void 0 ? _a : null;
11009
- this.selectedOptions = this.options.filter(el => el.isSameNode(selectedOption));
11175
+ if (((_a = this.options) === null || _a === void 0 ? void 0 : _a.length) && !this.disabled) {
11176
+ this.selectedOptions = [this.options[this.selectedIndex]];
11010
11177
  this.ariaActiveDescendant = (_c = (_b = this.firstSelectedOption) === null || _b === void 0 ? void 0 : _b.id) !== null && _c !== void 0 ? _c : "";
11011
11178
  this.focusAndScrollOptionIntoView();
11012
11179
  }
@@ -11056,12 +11223,10 @@ class Listbox$1 extends FoundationElement {
11056
11223
 
11057
11224
  typeaheadBufferChanged(prev, next) {
11058
11225
  if (this.$fastController.isConnected) {
11059
- const pattern = this.typeaheadBuffer.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&");
11060
- const re = new RegExp(`^${pattern}`, "gi");
11061
- const filteredOptions = this.options.filter(o => o.text.trim().match(re));
11226
+ const typeaheadMatches = this.getTypeaheadMatches();
11062
11227
 
11063
- if (filteredOptions.length) {
11064
- const selectedIndex = this.options.indexOf(filteredOptions[0]);
11228
+ if (typeaheadMatches.length) {
11229
+ const selectedIndex = this.options.indexOf(typeaheadMatches[0]);
11065
11230
 
11066
11231
  if (selectedIndex > -1) {
11067
11232
  this.selectedIndex = selectedIndex;
@@ -11094,6 +11259,10 @@ __decorate$1([attr({
11094
11259
  mode: "boolean"
11095
11260
  })], Listbox$1.prototype, "disabled", void 0);
11096
11261
 
11262
+ __decorate$1([attr({
11263
+ mode: "boolean"
11264
+ })], Listbox$1.prototype, "multiple", void 0);
11265
+
11097
11266
  __decorate$1([observable], Listbox$1.prototype, "selectedIndex", void 0);
11098
11267
 
11099
11268
  __decorate$1([observable], Listbox$1.prototype, "selectedOptions", void 0);
@@ -11116,6 +11285,8 @@ __decorate$1([observable], DelegatesARIAListbox.prototype, "ariaDisabled", void
11116
11285
 
11117
11286
  __decorate$1([observable], DelegatesARIAListbox.prototype, "ariaExpanded", void 0);
11118
11287
 
11288
+ __decorate$1([observable], DelegatesARIAListbox.prototype, "ariaMultiSelectable", void 0);
11289
+
11119
11290
  applyMixins(DelegatesARIAListbox, ARIAGlobalStatesAndProperties);
11120
11291
  applyMixins(Listbox$1, DelegatesARIAListbox);
11121
11292
 
@@ -11127,6 +11298,400 @@ applyMixins(Listbox$1, DelegatesARIAListbox);
11127
11298
  */
11128
11299
 
11129
11300
  class ListboxElement extends Listbox$1 {
11301
+ constructor() {
11302
+ super(...arguments);
11303
+ /**
11304
+ * The index of the most recently checked option.
11305
+ *
11306
+ * @internal
11307
+ * @remarks
11308
+ * Multiple-selection mode only.
11309
+ */
11310
+
11311
+ this.activeIndex = -1;
11312
+ /**
11313
+ * The start index when checking a range of options.
11314
+ *
11315
+ * @internal
11316
+ */
11317
+
11318
+ this.rangeStartIndex = -1;
11319
+ }
11320
+ /**
11321
+ * Returns the last checked option.
11322
+ *
11323
+ * @internal
11324
+ */
11325
+
11326
+
11327
+ get activeOption() {
11328
+ return this.options[this.activeIndex];
11329
+ }
11330
+ /**
11331
+ * Returns the list of checked options.
11332
+ *
11333
+ * @internal
11334
+ */
11335
+
11336
+
11337
+ get checkedOptions() {
11338
+ var _a;
11339
+
11340
+ return (_a = this.options) === null || _a === void 0 ? void 0 : _a.filter(o => o.checked);
11341
+ }
11342
+ /**
11343
+ * Returns the index of the first selected option.
11344
+ *
11345
+ * @internal
11346
+ */
11347
+
11348
+
11349
+ get firstSelectedOptionIndex() {
11350
+ return this.options.indexOf(this.firstSelectedOption);
11351
+ }
11352
+ /**
11353
+ * Updates the `ariaActiveDescendant` property when the active index changes.
11354
+ *
11355
+ * @param prev - the previous active index
11356
+ * @param next - the next active index
11357
+ *
11358
+ * @internal
11359
+ */
11360
+
11361
+
11362
+ activeIndexChanged(prev, next) {
11363
+ var _a, _b;
11364
+
11365
+ this.ariaActiveDescendant = (_b = (_a = this.options[next]) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : "";
11366
+ this.focusAndScrollOptionIntoView();
11367
+ }
11368
+ /**
11369
+ * Toggles the checked state for the currently active option.
11370
+ *
11371
+ * @remarks
11372
+ * Multiple-selection mode only.
11373
+ *
11374
+ * @internal
11375
+ */
11376
+
11377
+
11378
+ checkActiveIndex() {
11379
+ if (!this.multiple) {
11380
+ return;
11381
+ }
11382
+
11383
+ const activeItem = this.activeOption;
11384
+
11385
+ if (activeItem) {
11386
+ activeItem.checked = true;
11387
+ }
11388
+ }
11389
+ /**
11390
+ * Sets the active index to the first option and marks it as checked.
11391
+ *
11392
+ * @remarks
11393
+ * Multi-selection mode only.
11394
+ *
11395
+ * @param preserveChecked - mark all options unchecked before changing the active index
11396
+ *
11397
+ * @internal
11398
+ */
11399
+
11400
+
11401
+ checkFirstOption(preserveChecked = false) {
11402
+ if (preserveChecked) {
11403
+ if (this.rangeStartIndex === -1) {
11404
+ this.rangeStartIndex = this.activeIndex + 1;
11405
+ }
11406
+
11407
+ this.options.forEach((o, i) => {
11408
+ o.checked = inRange(i, this.rangeStartIndex);
11409
+ });
11410
+ } else {
11411
+ this.uncheckAllOptions();
11412
+ }
11413
+
11414
+ this.activeIndex = 0;
11415
+ this.checkActiveIndex();
11416
+ }
11417
+ /**
11418
+ * Decrements the active index and sets the matching option as checked.
11419
+ *
11420
+ * @remarks
11421
+ * Multi-selection mode only.
11422
+ *
11423
+ * @param preserveChecked - mark all options unchecked before changing the active index
11424
+ *
11425
+ * @internal
11426
+ */
11427
+
11428
+
11429
+ checkLastOption(preserveChecked = false) {
11430
+ if (preserveChecked) {
11431
+ if (this.rangeStartIndex === -1) {
11432
+ this.rangeStartIndex = this.activeIndex;
11433
+ }
11434
+
11435
+ this.options.forEach((o, i) => {
11436
+ o.checked = inRange(i, this.rangeStartIndex, this.options.length);
11437
+ });
11438
+ } else {
11439
+ this.uncheckAllOptions();
11440
+ }
11441
+
11442
+ this.activeIndex = this.options.length - 1;
11443
+ this.checkActiveIndex();
11444
+ }
11445
+ /**
11446
+ * @override
11447
+ * @internal
11448
+ */
11449
+
11450
+
11451
+ connectedCallback() {
11452
+ super.connectedCallback();
11453
+ this.addEventListener("focusout", this.focusoutHandler);
11454
+ }
11455
+ /**
11456
+ * @override
11457
+ * @internal
11458
+ */
11459
+
11460
+
11461
+ disconnectedCallback() {
11462
+ this.removeEventListener("focusout", this.focusoutHandler);
11463
+ super.disconnectedCallback();
11464
+ }
11465
+ /**
11466
+ * Increments the active index and marks the matching option as checked.
11467
+ *
11468
+ * @remarks
11469
+ * Multiple-selection mode only.
11470
+ *
11471
+ * @param preserveChecked - mark all options unchecked before changing the active index
11472
+ *
11473
+ * @internal
11474
+ */
11475
+
11476
+
11477
+ checkNextOption(preserveChecked = false) {
11478
+ if (preserveChecked) {
11479
+ if (this.rangeStartIndex === -1) {
11480
+ this.rangeStartIndex = this.activeIndex;
11481
+ }
11482
+
11483
+ this.options.forEach((o, i) => {
11484
+ o.checked = inRange(i, this.rangeStartIndex, this.activeIndex + 1);
11485
+ });
11486
+ } else {
11487
+ this.uncheckAllOptions();
11488
+ }
11489
+
11490
+ this.activeIndex += this.activeIndex < this.options.length - 1 ? 1 : 0;
11491
+ this.checkActiveIndex();
11492
+ }
11493
+ /**
11494
+ * Decrements the active index and marks the matching option as checked.
11495
+ *
11496
+ * @remarks
11497
+ * Multiple-selection mode only.
11498
+ *
11499
+ * @param preserveChecked - mark all options unchecked before changing the active index
11500
+ *
11501
+ * @internal
11502
+ */
11503
+
11504
+
11505
+ checkPreviousOption(preserveChecked = false) {
11506
+ if (preserveChecked) {
11507
+ if (this.rangeStartIndex === -1) {
11508
+ this.rangeStartIndex = this.activeIndex;
11509
+ }
11510
+
11511
+ if (this.checkedOptions.length === 1) {
11512
+ this.rangeStartIndex += 1;
11513
+ }
11514
+
11515
+ this.options.forEach((o, i) => {
11516
+ o.checked = inRange(i, this.activeIndex, this.rangeStartIndex);
11517
+ });
11518
+ } else {
11519
+ this.uncheckAllOptions();
11520
+ }
11521
+
11522
+ this.activeIndex -= this.activeIndex > 0 ? 1 : 0;
11523
+ this.checkActiveIndex();
11524
+ }
11525
+ /**
11526
+ * Handles click events for listbox options.
11527
+ *
11528
+ * @param e - the event object
11529
+ *
11530
+ * @override
11531
+ * @internal
11532
+ */
11533
+
11534
+
11535
+ clickHandler(e) {
11536
+ var _a;
11537
+
11538
+ if (!this.multiple) {
11539
+ return super.clickHandler(e);
11540
+ }
11541
+
11542
+ const captured = (_a = e.target) === null || _a === void 0 ? void 0 : _a.closest(`[role=option]`);
11543
+
11544
+ if (!captured || captured.disabled) {
11545
+ return;
11546
+ }
11547
+
11548
+ this.uncheckAllOptions();
11549
+ this.activeIndex = this.options.indexOf(captured);
11550
+ this.checkActiveIndex();
11551
+ this.toggleSelectedForAllCheckedOptions();
11552
+ return true;
11553
+ }
11554
+ /**
11555
+ * @override
11556
+ * @internal
11557
+ */
11558
+
11559
+
11560
+ focusAndScrollOptionIntoView() {
11561
+ super.focusAndScrollOptionIntoView(this.activeOption);
11562
+ }
11563
+ /**
11564
+ * In multiple-selection mode:
11565
+ * If any options are selected, the first selected option is checked when
11566
+ * the listbox receives focus. If no options are selected, the first
11567
+ * selectable option is checked.
11568
+ *
11569
+ * @override
11570
+ * @internal
11571
+ */
11572
+
11573
+
11574
+ focusinHandler(e) {
11575
+ if (!this.multiple) {
11576
+ return super.focusinHandler(e);
11577
+ }
11578
+
11579
+ if (!this.shouldSkipFocus && e.target === e.currentTarget) {
11580
+ this.uncheckAllOptions();
11581
+
11582
+ if (this.activeIndex === -1) {
11583
+ this.activeIndex = this.firstSelectedOptionIndex !== -1 ? this.firstSelectedOptionIndex : 0;
11584
+ }
11585
+
11586
+ this.checkActiveIndex();
11587
+ this.setSelectedOptions();
11588
+ this.focusAndScrollOptionIntoView();
11589
+ }
11590
+
11591
+ this.shouldSkipFocus = false;
11592
+ }
11593
+ /**
11594
+ * Unchecks all options when the listbox loses focus.
11595
+ *
11596
+ * @internal
11597
+ */
11598
+
11599
+
11600
+ focusoutHandler(e) {
11601
+ if (this.multiple) {
11602
+ this.uncheckAllOptions();
11603
+ }
11604
+ }
11605
+ /**
11606
+ * Handles keydown actions for listbox navigation and typeahead
11607
+ *
11608
+ * @override
11609
+ * @internal
11610
+ */
11611
+
11612
+
11613
+ keydownHandler(e) {
11614
+ if (!this.multiple) {
11615
+ return super.keydownHandler(e);
11616
+ }
11617
+
11618
+ if (this.disabled) {
11619
+ return true;
11620
+ }
11621
+
11622
+ const {
11623
+ key,
11624
+ shiftKey
11625
+ } = e;
11626
+ this.shouldSkipFocus = false;
11627
+
11628
+ switch (key) {
11629
+ // Select the first available option
11630
+ case keyHome:
11631
+ {
11632
+ this.checkFirstOption(shiftKey);
11633
+ return;
11634
+ }
11635
+ // Select the next selectable option
11636
+
11637
+ case keyArrowDown:
11638
+ {
11639
+ this.checkNextOption(shiftKey);
11640
+ return;
11641
+ }
11642
+ // Select the previous selectable option
11643
+
11644
+ case keyArrowUp:
11645
+ {
11646
+ this.checkPreviousOption(shiftKey);
11647
+ return;
11648
+ }
11649
+ // Select the last available option
11650
+
11651
+ case keyEnd:
11652
+ {
11653
+ this.checkLastOption(shiftKey);
11654
+ return;
11655
+ }
11656
+
11657
+ case keyTab:
11658
+ {
11659
+ this.focusAndScrollOptionIntoView();
11660
+ return true;
11661
+ }
11662
+
11663
+ case keyEscape:
11664
+ {
11665
+ if (this.multiple) {
11666
+ this.uncheckAllOptions();
11667
+ this.checkActiveIndex();
11668
+ }
11669
+
11670
+ return true;
11671
+ }
11672
+
11673
+ case keySpace:
11674
+ {
11675
+ e.preventDefault();
11676
+
11677
+ if (this.typeAheadExpired) {
11678
+ this.toggleSelectedForAllCheckedOptions();
11679
+ }
11680
+
11681
+ return;
11682
+ }
11683
+ // Send key to Typeahead handler
11684
+
11685
+ default:
11686
+ {
11687
+ if (key.length === 1) {
11688
+ this.handleTypeAhead(`${key}`);
11689
+ }
11690
+
11691
+ return true;
11692
+ }
11693
+ }
11694
+ }
11130
11695
  /**
11131
11696
  * Prevents `focusin` events from firing before `click` events when the
11132
11697
  * element is unfocused.
@@ -11134,11 +11699,53 @@ class ListboxElement extends Listbox$1 {
11134
11699
  * @override
11135
11700
  * @internal
11136
11701
  */
11702
+
11703
+
11137
11704
  mousedownHandler(e) {
11138
11705
  if (e.offsetX >= 0 && e.offsetX <= this.scrollWidth) {
11139
11706
  return super.mousedownHandler(e);
11140
11707
  }
11141
11708
  }
11709
+ /**
11710
+ * Switches between single-selection and multi-selection mode.
11711
+ *
11712
+ * @override
11713
+ * @internal
11714
+ */
11715
+
11716
+
11717
+ multipleChanged(prev, next) {
11718
+ var _a;
11719
+
11720
+ super.multipleChanged(prev, next);
11721
+ (_a = this.options) === null || _a === void 0 ? void 0 : _a.forEach(o => {
11722
+ o.checked = next ? false : undefined;
11723
+ });
11724
+ this.setSelectedOptions();
11725
+
11726
+ if (next && !this.size) {
11727
+ this.size = 0;
11728
+ }
11729
+ }
11730
+ /**
11731
+ * Sets an option as selected and gives it focus.
11732
+ *
11733
+ * @override
11734
+ * @public
11735
+ */
11736
+
11737
+
11738
+ setSelectedOptions() {
11739
+ if (!this.multiple) {
11740
+ super.setSelectedOptions();
11741
+ return;
11742
+ }
11743
+
11744
+ if (this.$fastController.isConnected && this.options) {
11745
+ this.selectedOptions = this.options.filter(o => o.selected);
11746
+ this.focusAndScrollOptionIntoView();
11747
+ }
11748
+ }
11142
11749
  /**
11143
11750
  * Ensures the size is a positive integer when the property is updated.
11144
11751
  *
@@ -11158,9 +11765,74 @@ class ListboxElement extends Listbox$1 {
11158
11765
  });
11159
11766
  }
11160
11767
  }
11768
+ /**
11769
+ * Toggles the selected state of the provided options. If any provided items
11770
+ * are in an unselected state, all items are set to selected. If every
11771
+ * provided item is selected, they are all unselected.
11772
+ *
11773
+ * @internal
11774
+ */
11775
+
11776
+
11777
+ toggleSelectedForAllCheckedOptions() {
11778
+ const enabledCheckedOptions = this.checkedOptions.filter(o => !o.disabled);
11779
+ const force = !enabledCheckedOptions.every(o => o.selected);
11780
+ enabledCheckedOptions.forEach(o => o.selected = force);
11781
+ this.selectedIndex = this.options.indexOf(enabledCheckedOptions[enabledCheckedOptions.length - 1]);
11782
+ this.setSelectedOptions();
11783
+ }
11784
+ /**
11785
+ * @override
11786
+ * @internal
11787
+ */
11788
+
11789
+
11790
+ typeaheadBufferChanged(prev, next) {
11791
+ if (!this.multiple) {
11792
+ super.typeaheadBufferChanged(prev, next);
11793
+ return;
11794
+ }
11795
+
11796
+ if (this.$fastController.isConnected) {
11797
+ const typeaheadMatches = this.getTypeaheadMatches();
11798
+
11799
+ if (typeaheadMatches) {
11800
+ const activeIndex = this.options.indexOf(this.getTypeaheadMatches[0]);
11801
+
11802
+ if (activeIndex > -1) {
11803
+ this.activeIndex = activeIndex;
11804
+ this.uncheckAllOptions();
11805
+ this.checkActiveIndex();
11806
+ }
11807
+ }
11808
+
11809
+ this.typeAheadExpired = false;
11810
+ }
11811
+ }
11812
+ /**
11813
+ * Unchecks all options.
11814
+ *
11815
+ * @remarks
11816
+ * Multiple-selection mode only.
11817
+ *
11818
+ * @param preserveChecked - reset the rangeStartIndex
11819
+ *
11820
+ * @internal
11821
+ */
11822
+
11823
+
11824
+ uncheckAllOptions(preserveChecked = false) {
11825
+ this.options.forEach(o => o.checked = this.multiple ? false : undefined);
11826
+
11827
+ if (!preserveChecked) {
11828
+ this.rangeStartIndex = -1;
11829
+ }
11830
+ }
11161
11831
 
11162
11832
  }
11163
11833
 
11834
+ __decorate$1([observable], ListboxElement.prototype, "activeIndex", void 0);
11835
+
11164
11836
  __decorate$1([attr({
11165
11837
  converter: nullableNumberConverter
11166
11838
  })], ListboxElement.prototype, "size", void 0);
@@ -11170,7 +11842,7 @@ __decorate$1([attr({
11170
11842
  * @public
11171
11843
  */
11172
11844
 
11173
- const listboxTemplate = (context, definition) => html`<template aria-activedescendant="${x => x.ariaActiveDescendant}" class="listbox" role="listbox" tabindex="${x => !x.disabled ? "0" : null}" @click="${(x, c) => x.clickHandler(c.event)}" @focusin="${(x, c) => x.focusinHandler(c.event)}" @keydown="${(x, c) => x.keydownHandler(c.event)}" @mousedown="${(x, c) => x.mousedownHandler(c.event)}"><slot ${slotted({
11845
+ const listboxTemplate = (context, definition) => html`<template aria-activedescendant="${x => x.ariaActiveDescendant}" aria-multiselectable="${x => x.ariaMultiSelectable}" class="listbox" role="listbox" tabindex="${x => !x.disabled ? "0" : null}" @click="${(x, c) => x.clickHandler(c.event)}" @focusin="${(x, c) => x.focusinHandler(c.event)}" @keydown="${(x, c) => x.keydownHandler(c.event)}" @mousedown="${(x, c) => x.mousedownHandler(c.event)}"><slot ${slotted({
11174
11846
  filter: ListboxElement.slottedOptionFilter,
11175
11847
  flatten: true,
11176
11848
  property: "slottedOptions"
@@ -11255,7 +11927,7 @@ class Combobox$1 extends FormAssociatedCombobox {
11255
11927
 
11256
11928
  this.forcedPosition = false;
11257
11929
  /**
11258
- * The unique id of the internal listbox.
11930
+ * The unique id for the internal listbox element.
11259
11931
  *
11260
11932
  * @internal
11261
11933
  */
@@ -11318,10 +11990,12 @@ class Combobox$1 extends FormAssociatedCombobox {
11318
11990
 
11319
11991
  openChanged() {
11320
11992
  if (this.open) {
11321
- this.ariaControls = this.listbox.id;
11993
+ this.ariaControls = this.listboxId;
11322
11994
  this.ariaExpanded = "true";
11323
11995
  this.setPositioning();
11324
- this.focusAndScrollOptionIntoView();
11996
+ this.focusAndScrollOptionIntoView(); // focus is directed to the element when `open` is changed programmatically
11997
+
11998
+ DOM.queueUpdate(() => this.focus());
11325
11999
  return;
11326
12000
  }
11327
12001
 
@@ -11357,6 +12031,11 @@ class Combobox$1 extends FormAssociatedCombobox {
11357
12031
  this.proxy.placeholder = this.placeholder;
11358
12032
  }
11359
12033
  }
12034
+
12035
+ positionChanged() {
12036
+ this.positionAttribute = this.position;
12037
+ this.setPositioning();
12038
+ }
11360
12039
  /**
11361
12040
  * The value property.
11362
12041
  *
@@ -11429,10 +12108,6 @@ class Combobox$1 extends FormAssociatedCombobox {
11429
12108
  if (this.value) {
11430
12109
  this.initialValue = this.value;
11431
12110
  }
11432
-
11433
- if (!this.listbox.id) {
11434
- this.listbox.id = uniqueId("listbox-");
11435
- }
11436
12111
  }
11437
12112
  /**
11438
12113
  * Synchronize the `aria-disabled` property when the `disabled` property changes.
@@ -11491,7 +12166,9 @@ class Combobox$1 extends FormAssociatedCombobox {
11491
12166
 
11492
12167
  if (this.firstSelectedOption) {
11493
12168
  requestAnimationFrame(() => {
11494
- this.firstSelectedOption.scrollIntoView({
12169
+ var _a;
12170
+
12171
+ (_a = this.firstSelectedOption) === null || _a === void 0 ? void 0 : _a.scrollIntoView({
11495
12172
  block: "nearest"
11496
12173
  });
11497
12174
  });
@@ -11843,7 +12520,7 @@ applyMixins(Combobox$1, StartEnd, DelegatesARIACombobox);
11843
12520
  * @public
11844
12521
  */
11845
12522
 
11846
- const comboboxTemplate = (context, definition) => html`<template aria-disabled="${x => x.ariaDisabled}" autocomplete="${x => x.autocomplete}" class="${x => x.open ? "open" : ""} ${x => x.disabled ? "disabled" : ""} ${x => x.position}" tabindex="${x => !x.disabled ? "0" : null}" @click="${(x, c) => x.clickHandler(c.event)}" @focusout="${(x, c) => x.focusoutHandler(c.event)}" @keydown="${(x, c) => x.keydownHandler(c.event)}"><div class="control" part="control">${startSlotTemplate(context, definition)}<slot name="control"><input aria-activedescendant="${x => x.open ? x.ariaActiveDescendant : null}" aria-autocomplete="${x => x.ariaAutoComplete}" aria-controls="${x => x.ariaControls}" aria-disabled="${x => x.ariaDisabled}" aria-expanded="${x => x.ariaExpanded}" aria-haspopup="listbox" class="selected-value" part="selected-value" placeholder="${x => x.placeholder}" role="combobox" type="text" ?disabled="${x => x.disabled}" :value="${x => x.value}" @input="${(x, c) => x.inputHandler(c.event)}" @keyup="${(x, c) => x.keyupHandler(c.event)}" ${ref("control")} /><div class="indicator" part="indicator" aria-hidden="true"><slot name="indicator">${definition.indicator || ""}</slot></div></slot>${endSlotTemplate(context, definition)}</div><div class="listbox" part="listbox" role="listbox" ?disabled="${x => x.disabled}" ?hidden="${x => !x.open}" ${ref("listbox")}><slot ${slotted({
12523
+ const comboboxTemplate = (context, definition) => html`<template aria-disabled="${x => x.ariaDisabled}" autocomplete="${x => x.autocomplete}" class="${x => x.open ? "open" : ""} ${x => x.disabled ? "disabled" : ""} ${x => x.position}" ?open="${x => x.open}" tabindex="${x => !x.disabled ? "0" : null}" @click="${(x, c) => x.clickHandler(c.event)}" @focusout="${(x, c) => x.focusoutHandler(c.event)}" @keydown="${(x, c) => x.keydownHandler(c.event)}"><div class="control" part="control">${startSlotTemplate(context, definition)}<slot name="control"><input aria-activedescendant="${x => x.open ? x.ariaActiveDescendant : null}" aria-autocomplete="${x => x.ariaAutoComplete}" aria-controls="${x => x.ariaControls}" aria-disabled="${x => x.ariaDisabled}" aria-expanded="${x => x.ariaExpanded}" aria-haspopup="listbox" class="selected-value" part="selected-value" placeholder="${x => x.placeholder}" role="combobox" type="text" ?disabled="${x => x.disabled}" :value="${x => x.value}" @input="${(x, c) => x.inputHandler(c.event)}" @keyup="${(x, c) => x.keyupHandler(c.event)}" ${ref("control")} /><div class="indicator" part="indicator" aria-hidden="true"><slot name="indicator">${definition.indicator || ""}</slot></div></slot>${endSlotTemplate(context, definition)}</div><div class="listbox" id="${x => x.listboxId}" part="listbox" role="listbox" ?disabled="${x => x.disabled}" ?hidden="${x => !x.open}" ${ref("listbox")}><slot ${slotted({
11847
12524
  filter: Listbox$1.slottedOptionFilter,
11848
12525
  flatten: true,
11849
12526
  property: "slottedOptions"
@@ -12107,6 +12784,7 @@ class PropertyStyleSheetBehavior {
12107
12784
  /**
12108
12785
  * Change event for the provided element.
12109
12786
  * @param source - the element for which to attach or detach styles.
12787
+ * @param key - the key to lookup to know if the element already has the styles
12110
12788
  * @internal
12111
12789
  */
12112
12790
 
@@ -14448,7 +15126,7 @@ class Menu$1 extends FoundationElement {
14448
15126
 
14449
15127
 
14450
15128
  this.handleFocusOut = e => {
14451
- if (!this.contains(e.relatedTarget)) {
15129
+ if (!this.contains(e.relatedTarget) && this.menuItems !== undefined) {
14452
15130
  this.collapseExpandedItem(); // find our first focusable element
14453
15131
 
14454
15132
  const focusIndex = this.menuItems.findIndex(this.isFocusableElement); // set the current focus index's tabindex to -1
@@ -14464,7 +15142,7 @@ class Menu$1 extends FoundationElement {
14464
15142
  this.handleItemFocus = e => {
14465
15143
  const targetItem = e.target;
14466
15144
 
14467
- if (targetItem !== this.menuItems[this.focusIndex]) {
15145
+ if (this.menuItems !== undefined && targetItem !== this.menuItems[this.focusIndex]) {
14468
15146
  this.menuItems[this.focusIndex].setAttribute("tabindex", "-1");
14469
15147
  this.focusIndex = this.menuItems.indexOf(targetItem);
14470
15148
  targetItem.setAttribute("tabindex", "0");
@@ -14472,7 +15150,7 @@ class Menu$1 extends FoundationElement {
14472
15150
  };
14473
15151
 
14474
15152
  this.handleExpandedChanged = e => {
14475
- if (e.defaultPrevented || e.target === null || this.menuItems.indexOf(e.target) < 0) {
15153
+ if (e.defaultPrevented || e.target === null || this.menuItems === undefined || this.menuItems.indexOf(e.target) < 0) {
14476
15154
  return;
14477
15155
  }
14478
15156
 
@@ -14496,7 +15174,19 @@ class Menu$1 extends FoundationElement {
14496
15174
  }
14497
15175
  };
14498
15176
 
15177
+ this.removeItemListeners = () => {
15178
+ if (this.menuItems !== undefined) {
15179
+ this.menuItems.forEach(item => {
15180
+ item.removeEventListener("expanded-change", this.handleExpandedChanged);
15181
+ item.removeEventListener("focus", this.handleItemFocus);
15182
+ });
15183
+ }
15184
+ };
15185
+
14499
15186
  this.setItems = () => {
15187
+ const newItems = this.domChildren();
15188
+ this.removeItemListeners();
15189
+ this.menuItems = newItems;
14500
15190
  const menuItems = this.menuItems.filter(this.isMenuItemElement); // if our focus index is not -1 we have items
14501
15191
 
14502
15192
  if (menuItems.length) {
@@ -14533,19 +15223,16 @@ class Menu$1 extends FoundationElement {
14533
15223
  }
14534
15224
  });
14535
15225
  };
14536
-
14537
- this.resetItems = oldValue => {
14538
- oldValue.forEach(item => {
14539
- item.removeEventListener("expanded-change", this.handleExpandedChanged);
14540
- item.removeEventListener("focus", this.handleItemFocus);
14541
- });
14542
- };
14543
15226
  /**
14544
15227
  * handle change from child element
14545
15228
  */
14546
15229
 
14547
15230
 
14548
15231
  this.changeHandler = e => {
15232
+ if (this.menuItems === undefined) {
15233
+ return;
15234
+ }
15235
+
14549
15236
  const changedMenuItem = e.target;
14550
15237
  const changeItemIndex = this.menuItems.indexOf(changedMenuItem);
14551
15238
 
@@ -14602,9 +15289,10 @@ class Menu$1 extends FoundationElement {
14602
15289
  }
14603
15290
 
14604
15291
  itemsChanged(oldValue, newValue) {
14605
- if (this.$fastController.isConnected) {
14606
- this.menuItems = this.domChildren();
14607
- this.resetItems(oldValue);
15292
+ // only update children after the component is connected and
15293
+ // the setItems has run on connectedCallback
15294
+ // (menuItems is undefined until then)
15295
+ if (this.$fastController.isConnected && this.menuItems !== undefined) {
14608
15296
  this.setItems();
14609
15297
  }
14610
15298
  }
@@ -14615,7 +15303,11 @@ class Menu$1 extends FoundationElement {
14615
15303
 
14616
15304
  connectedCallback() {
14617
15305
  super.connectedCallback();
14618
- this.menuItems = this.domChildren();
15306
+ DOM.queueUpdate(() => {
15307
+ // wait until children have had a chance to
15308
+ // connect before setting/checking their props/attributes
15309
+ this.setItems();
15310
+ });
14619
15311
  this.addEventListener("change", this.changeHandler);
14620
15312
  }
14621
15313
  /**
@@ -14625,7 +15317,8 @@ class Menu$1 extends FoundationElement {
14625
15317
 
14626
15318
  disconnectedCallback() {
14627
15319
  super.disconnectedCallback();
14628
- this.menuItems = [];
15320
+ this.removeItemListeners();
15321
+ this.menuItems = undefined;
14629
15322
  this.removeEventListener("change", this.changeHandler);
14630
15323
  }
14631
15324
  /**
@@ -14657,7 +15350,7 @@ class Menu$1 extends FoundationElement {
14657
15350
 
14658
15351
 
14659
15352
  handleMenuKeyDown(e) {
14660
- if (e.defaultPrevented) {
15353
+ if (e.defaultPrevented || this.menuItems === undefined) {
14661
15354
  return;
14662
15355
  }
14663
15356
 
@@ -16305,7 +16998,7 @@ __decorate$1([attr], HorizontalScroll$1.prototype, "duration", void 0);
16305
16998
  __decorate$1([attr], HorizontalScroll$1.prototype, "easing", void 0);
16306
16999
 
16307
17000
  __decorate$1([attr({
16308
- attribute: "aria-hidden",
17001
+ attribute: "flippers-hidden-from-at",
16309
17002
  converter: booleanConverter
16310
17003
  })], HorizontalScroll$1.prototype, "flippersHiddenFromAT", void 0);
16311
17004
 
@@ -16554,6 +17247,13 @@ class Select$1 extends FormAssociatedSelect {
16554
17247
  */
16555
17248
 
16556
17249
  this.position = SelectPosition.below;
17250
+ /**
17251
+ * The unique id for the internal listbox element.
17252
+ *
17253
+ * @internal
17254
+ */
17255
+
17256
+ this.listboxId = uniqueId("listbox-");
16557
17257
  /**
16558
17258
  * The max height for the listbox when opened.
16559
17259
  *
@@ -16572,11 +17272,13 @@ class Select$1 extends FormAssociatedSelect {
16572
17272
 
16573
17273
  openChanged() {
16574
17274
  if (this.open) {
16575
- this.ariaControls = this.listbox.id;
17275
+ this.ariaControls = this.listboxId;
16576
17276
  this.ariaExpanded = "true";
16577
17277
  this.setPositioning();
16578
17278
  this.focusAndScrollOptionIntoView();
16579
- this.indexWhenOpened = this.selectedIndex;
17279
+ this.indexWhenOpened = this.selectedIndex; // focus is directed to the element when `open` is changed programmatically
17280
+
17281
+ DOM.queueUpdate(() => this.focus());
16580
17282
  return;
16581
17283
  }
16582
17284
 
@@ -16596,9 +17298,11 @@ class Select$1 extends FormAssociatedSelect {
16596
17298
  }
16597
17299
 
16598
17300
  set value(next) {
17301
+ var _a;
17302
+
16599
17303
  const prev = `${this._value}`;
16600
17304
 
16601
- if (this.$fastController.isConnected && this.options) {
17305
+ if ((_a = this.options) === null || _a === void 0 ? void 0 : _a.length) {
16602
17306
  const selectedIndex = this.options.findIndex(el => el.value === next);
16603
17307
  const prevSelectedOption = this.options[this.selectedIndex];
16604
17308
  const nextSelectedOption = this.options[selectedIndex];
@@ -16650,6 +17354,11 @@ class Select$1 extends FormAssociatedSelect {
16650
17354
  super.selectedIndexChanged(prev, next);
16651
17355
  this.updateValue();
16652
17356
  }
17357
+
17358
+ positionChanged() {
17359
+ this.positionAttribute = this.position;
17360
+ this.setPositioning();
17361
+ }
16653
17362
  /**
16654
17363
  * Calculate and apply listbox positioning based on available viewport space.
16655
17364
  *
@@ -16697,8 +17406,10 @@ class Select$1 extends FormAssociatedSelect {
16697
17406
 
16698
17407
 
16699
17408
  formResetCallback() {
16700
- this.setProxyOptions();
16701
- this.setDefaultSelectedOption();
17409
+ this.setProxyOptions(); // Call the base class's implementation setDefaultSelectedOption instead of the select's
17410
+ // override, in order to reset the selectedIndex without using the value property.
17411
+
17412
+ super.setDefaultSelectedOption();
16702
17413
  this.value = this.firstSelectedOption.value;
16703
17414
  }
16704
17415
  /**
@@ -16777,6 +17488,20 @@ class Select$1 extends FormAssociatedSelect {
16777
17488
  this.setProxyOptions();
16778
17489
  this.updateValue();
16779
17490
  }
17491
+
17492
+ setDefaultSelectedOption() {
17493
+ var _a;
17494
+
17495
+ const options = (_a = this.options) !== null && _a !== void 0 ? _a : Array.from(this.children).filter(Listbox$1.slottedOptionFilter);
17496
+ const selectedIndex = options === null || options === void 0 ? void 0 : options.findIndex(el => el.hasAttribute("selected") || el.selected || el.value === this.value);
17497
+
17498
+ if (selectedIndex !== -1) {
17499
+ this.selectedIndex = selectedIndex;
17500
+ return;
17501
+ }
17502
+
17503
+ this.selectedIndex = 0;
17504
+ }
16780
17505
  /**
16781
17506
  * Reset and fill the proxy to match the component's options.
16782
17507
  *
@@ -16858,10 +17583,6 @@ class Select$1 extends FormAssociatedSelect {
16858
17583
  connectedCallback() {
16859
17584
  super.connectedCallback();
16860
17585
  this.forcedPosition = !!this.positionAttribute;
16861
-
16862
- if (!this.listbox.id) {
16863
- this.listbox.id = uniqueId("listbox-");
16864
- }
16865
17586
  }
16866
17587
 
16867
17588
  }
@@ -16899,7 +17620,7 @@ applyMixins(Select$1, StartEnd, DelegatesARIASelect);
16899
17620
  * @public
16900
17621
  */
16901
17622
 
16902
- const selectTemplate = (context, definition) => html`<template class="${x => x.open ? "open" : ""} ${x => x.disabled ? "disabled" : ""} ${x => x.position}" aria-activedescendant="${x => x.ariaActiveDescendant}" aria-controls="${x => x.ariaControls}" aria-disabled="${x => x.ariaDisabled}" aria-expanded="${x => x.ariaExpanded}" aria-haspopup="listbox" role="combobox" tabindex="${x => !x.disabled ? "0" : null}" @click="${(x, c) => x.clickHandler(c.event)}" @focusout="${(x, c) => x.focusoutHandler(c.event)}" @keydown="${(x, c) => x.keydownHandler(c.event)}"><div class="control" part="control" ?disabled="${x => x.disabled}">${startSlotTemplate(context, definition)}<slot name="button-container"><div class="selected-value" part="selected-value"><slot name="selected-value">${x => x.displayValue}</slot></div><div aria-hidden="true" class="indicator" part="indicator"><slot name="indicator">${definition.indicator || ""}</slot></div></slot>${endSlotTemplate(context, definition)}</div><div class="listbox" part="listbox" role="listbox" ?disabled="${x => x.disabled}" ?hidden="${x => !x.open}" ${ref("listbox")}><slot ${slotted({
17623
+ const selectTemplate = (context, definition) => html`<template class="${x => x.open ? "open" : ""} ${x => x.disabled ? "disabled" : ""} ${x => x.position}" aria-activedescendant="${x => x.ariaActiveDescendant}" aria-controls="${x => x.ariaControls}" aria-disabled="${x => x.ariaDisabled}" aria-expanded="${x => x.ariaExpanded}" aria-haspopup="listbox" ?open="${x => x.open}" role="combobox" tabindex="${x => !x.disabled ? "0" : null}" @click="${(x, c) => x.clickHandler(c.event)}" @focusout="${(x, c) => x.focusoutHandler(c.event)}" @keydown="${(x, c) => x.keydownHandler(c.event)}"><div class="control" part="control" ?disabled="${x => x.disabled}">${startSlotTemplate(context, definition)}<slot name="button-container"><div class="selected-value" part="selected-value"><slot name="selected-value">${x => x.displayValue}</slot></div><div aria-hidden="true" class="indicator" part="indicator"><slot name="indicator">${definition.indicator || ""}</slot></div></slot>${endSlotTemplate(context, definition)}</div><div class="listbox" id="${x => x.listboxId}" part="listbox" role="listbox" ?disabled="${x => x.disabled}" ?hidden="${x => !x.open}" ${ref("listbox")}><slot ${slotted({
16903
17624
  filter: Listbox$1.slottedOptionFilter,
16904
17625
  flatten: true,
16905
17626
  property: "slottedOptions"
@@ -17552,7 +18273,7 @@ class Slider extends FormAssociatedSlider {
17552
18273
 
17553
18274
  setThumbPositionForOrientation(direction) {
17554
18275
  const newPct = convertPixelToPercent(Number(this.value), Number(this.min), Number(this.max), direction);
17555
- const percentage = Math.round((1 - newPct) * 100);
18276
+ const percentage = (1 - newPct) * 100;
17556
18277
 
17557
18278
  if (this.orientation === Orientation.horizontal) {
17558
18279
  this.position = this.isDragging ? `right: ${percentage}%; transition: none;` : `right: ${percentage}%; transition: all 0.2s ease;`;
@@ -18969,7 +19690,7 @@ class Tooltip$1 extends FoundationElement {
18969
19690
 
18970
19691
  anchorChanged() {
18971
19692
  if (this.$fastController.isConnected) {
18972
- this.updateLayout();
19693
+ this.anchorElement = this.getAnchor();
18973
19694
  }
18974
19695
  }
18975
19696
 
@@ -19031,7 +19752,6 @@ class Tooltip$1 extends FoundationElement {
19031
19752
  connectedCallback() {
19032
19753
  super.connectedCallback();
19033
19754
  this.anchorElement = this.getAnchor();
19034
- this.updateLayout();
19035
19755
  this.updateTooltipVisibility();
19036
19756
  }
19037
19757
 
@@ -19127,7 +19847,7 @@ __decorate$1([observable], Tooltip$1.prototype, "currentDirection", void 0);
19127
19847
  * @public
19128
19848
  */
19129
19849
 
19130
- const treeItemTemplate = (context, definition) => html`<template role="treeitem" slot="${x => x.isNestedItem() ? "item" : void 0}" tabindex="${x => !x.focusable ? -1 : 0}" class="${x => x.expanded ? "expanded" : ""} ${x => x.selected ? "selected" : ""} ${x => x.nested ? "nested" : ""} ${x => x.disabled ? "disabled" : ""}" aria-expanded="${x => x.childItems && x.childItemLength() > 0 ? x.expanded : void 0}" aria-selected="${x => x.selected}" aria-disabled="${x => x.disabled}" @click="${(x, c) => x.handleClick(c.event)}" ${children({
19850
+ const treeItemTemplate = (context, definition) => html`<template role="treeitem" slot="${x => x.isNestedItem() ? "item" : void 0}" tabindex="-1" class="${x => x.expanded ? "expanded" : ""} ${x => x.selected ? "selected" : ""} ${x => x.nested ? "nested" : ""} ${x => x.disabled ? "disabled" : ""}" aria-expanded="${x => x.childItems && x.childItemLength() > 0 ? x.expanded : void 0}" aria-selected="${x => x.selected}" aria-disabled="${x => x.disabled}" @focusin="${(x, c) => x.handleFocus(c.event)}" @focusout="${(x, c) => x.handleBlur(c.event)}" ${children({
19131
19851
  property: "childItems",
19132
19852
  filter: elements()
19133
19853
  })}><div class="positioning-region" part="positioning-region"><div class="content-region" part="content-region">${when(x => x.childItems && x.childItemLength() > 0, html`<div aria-hidden="true" class="expand-collapse-button" part="expand-collapse-button" @click="${(x, c) => x.handleExpandCollapseButtonClick(c.event)}" ${ref("expandCollapseButton")}><slot name="expand-collapse-glyph">${definition.expandCollapseGlyph || ""}</slot></div>`)} ${startSlotTemplate(context, definition)}<slot></slot>${endSlotTemplate(context, definition)}</div></div>${when(x => x.childItems && x.childItemLength() > 0 && (x.expanded || x.renderCollapsedChildren), html`<div role="group" class="items" part="items"><slot name="item" ${slotted("items")}></slot></div>`)}</template>`;
@@ -19159,32 +19879,66 @@ class TreeItem extends FoundationElement {
19159
19879
  */
19160
19880
 
19161
19881
  this.expanded = false;
19882
+ /**
19883
+ * Whether the item is focusable
19884
+ *
19885
+ * @internal
19886
+ */
19887
+
19162
19888
  this.focusable = false;
19889
+ /**
19890
+ * Whether the tree is nested
19891
+ *
19892
+ * @public
19893
+ */
19894
+
19895
+ this.isNestedItem = () => {
19896
+ return isTreeItemElement(this.parentElement);
19897
+ };
19898
+ /**
19899
+ * Handle expand button click
19900
+ *
19901
+ * @internal
19902
+ */
19903
+
19163
19904
 
19164
19905
  this.handleExpandCollapseButtonClick = e => {
19165
- if (!this.disabled) {
19166
- e.preventDefault();
19167
- this.setExpanded(!this.expanded);
19906
+ if (!this.disabled && !e.defaultPrevented) {
19907
+ this.expanded = !this.expanded;
19168
19908
  }
19169
19909
  };
19910
+ /**
19911
+ * Handle focus events
19912
+ *
19913
+ * @internal
19914
+ */
19170
19915
 
19171
- this.handleClick = e => {
19172
- if (!e.defaultPrevented) {
19173
- const target = e.composedPath();
19174
- const clickedTreeItem = target.find(t => t instanceof HTMLElement && isTreeItemElement(t));
19175
19916
 
19176
- if (clickedTreeItem === this) {
19177
- this.handleSelected();
19178
- } // do not prevent default as it completely eats the click
19917
+ this.handleFocus = e => {
19918
+ this.setAttribute("tabindex", "0");
19919
+ };
19920
+ /**
19921
+ * Handle blur events
19922
+ *
19923
+ * @internal
19924
+ */
19179
19925
 
19180
19926
 
19181
- return true;
19182
- }
19927
+ this.handleBlur = e => {
19928
+ this.setAttribute("tabindex", "-1");
19183
19929
  };
19930
+ }
19184
19931
 
19185
- this.isNestedItem = () => {
19186
- return isTreeItemElement(this.parentElement);
19187
- };
19932
+ expandedChanged() {
19933
+ if (this.$fastController.isConnected) {
19934
+ this.$emit("expanded-change", this);
19935
+ }
19936
+ }
19937
+
19938
+ selectedChanged() {
19939
+ if (this.$fastController.isConnected) {
19940
+ this.$emit("selected-change", this);
19941
+ }
19188
19942
  }
19189
19943
 
19190
19944
  itemsChanged(oldValue, newValue) {
@@ -19199,6 +19953,8 @@ class TreeItem extends FoundationElement {
19199
19953
  }
19200
19954
  /**
19201
19955
  * Places document focus on a tree item
19956
+ *
19957
+ * @public
19202
19958
  * @param el - the element to focus
19203
19959
  */
19204
19960
 
@@ -19207,6 +19963,12 @@ class TreeItem extends FoundationElement {
19207
19963
  el.focusable = true;
19208
19964
  el.focus();
19209
19965
  }
19966
+ /**
19967
+ * Gets number of children
19968
+ *
19969
+ * @internal
19970
+ */
19971
+
19210
19972
 
19211
19973
  childItemLength() {
19212
19974
  const treeChildren = this.childItems.filter(item => {
@@ -19215,23 +19977,6 @@ class TreeItem extends FoundationElement {
19215
19977
  return treeChildren ? treeChildren.length : 0;
19216
19978
  }
19217
19979
 
19218
- handleSelected(e) {
19219
- if (e === null || e === void 0 ? void 0 : e.defaultPrevented) {
19220
- return;
19221
- }
19222
-
19223
- e === null || e === void 0 ? void 0 : e.preventDefault();
19224
-
19225
- if (!this.disabled) {
19226
- this.$emit("selected-change", e);
19227
- }
19228
- }
19229
-
19230
- setExpanded(expanded) {
19231
- this.expanded = expanded;
19232
- this.$emit("expanded-change", this);
19233
- }
19234
-
19235
19980
  }
19236
19981
 
19237
19982
  __decorate$1([attr({
@@ -19263,7 +20008,7 @@ applyMixins(TreeItem, StartEnd);
19263
20008
  * @public
19264
20009
  */
19265
20010
 
19266
- const treeViewTemplate = (context, definition) => html`<template role="tree" ${ref("treeView")} @keydown="${(x, c) => x.handleKeyDown(c.event)}" @focusout="${(x, c) => x.handleBlur(c.event)}" @focusin="${(x, c) => x.handleFocus(c.event)}"><slot ${slotted("slottedTreeItems")}></slot></template>`;
20011
+ const treeViewTemplate = (context, definition) => html`<template role="tree" ${ref("treeView")} @keydown="${(x, c) => x.handleKeyDown(c.event)}" @focusin="${(x, c) => x.handleFocus(c.event)}" @focusout="${(x, c) => x.handleBlur(c.event)}" @click="${(x, c) => x.handleClick(c.event)}" @selected-change="${(x, c) => x.handleSelectedChange(c.event)}"><slot ${slotted("slottedTreeItems")}></slot></template>`;
19267
20012
 
19268
20013
  /**
19269
20014
  * A Tree view Custom HTML Element.
@@ -19275,57 +20020,86 @@ const treeViewTemplate = (context, definition) => html`<template role="tree" ${r
19275
20020
  class TreeView extends FoundationElement {
19276
20021
  constructor() {
19277
20022
  super(...arguments);
20023
+ /**
20024
+ * The tree item that is designated to be in the tab queue.
20025
+ *
20026
+ * @internal
20027
+ */
19278
20028
 
19279
- this.handleBlur = e => {
19280
- const {
19281
- relatedTarget,
19282
- target
19283
- } = e;
19284
-
19285
- if (target instanceof HTMLElement && (relatedTarget === null || !this.contains(relatedTarget))) {
19286
- this.setAttribute("tabindex", "0");
19287
- }
19288
- };
20029
+ this.currentFocused = null;
20030
+ /**
20031
+ * Handle focus events
20032
+ *
20033
+ * @internal
20034
+ */
19289
20035
 
19290
20036
  this.handleFocus = e => {
19291
- const {
19292
- relatedTarget,
19293
- target
19294
- } = e;
20037
+ if (this.slottedTreeItems.length < 1) {
20038
+ // no child items, nothing to do
20039
+ return;
20040
+ }
19295
20041
 
19296
- if (target instanceof HTMLElement && (relatedTarget === null || !this.contains(relatedTarget))) {
19297
- const treeView = this;
20042
+ if (e.target === this) {
20043
+ if (this.currentFocused === null) {
20044
+ this.currentFocused = this.getValidFocusableItem();
20045
+ }
19298
20046
 
19299
- if (target === this && this.currentFocused instanceof TreeItem) {
20047
+ if (this.currentFocused !== null) {
19300
20048
  TreeItem.focusItem(this.currentFocused);
19301
- this.currentFocused.setAttribute("tabindex", "0");
19302
20049
  }
19303
20050
 
19304
- treeView.setAttribute("tabindex", "-1");
20051
+ return;
20052
+ }
20053
+
20054
+ if (this.contains(e.target)) {
20055
+ this.setAttribute("tabindex", "-1");
20056
+ this.currentFocused = e.target;
20057
+ }
20058
+ };
20059
+ /**
20060
+ * Handle blur events
20061
+ *
20062
+ * @internal
20063
+ */
20064
+
20065
+
20066
+ this.handleBlur = e => {
20067
+ if (e.target instanceof HTMLElement && (e.relatedTarget === null || !this.contains(e.relatedTarget))) {
20068
+ this.setAttribute("tabindex", "0");
19305
20069
  }
19306
20070
  };
20071
+ /**
20072
+ * KeyDown handler
20073
+ *
20074
+ * @internal
20075
+ */
20076
+
19307
20077
 
19308
20078
  this.handleKeyDown = e => {
19309
- if (!this.treeItems) {
20079
+ if (e.defaultPrevented) {
20080
+ return;
20081
+ }
20082
+
20083
+ if (this.slottedTreeItems.length < 1) {
19310
20084
  return true;
19311
20085
  }
19312
20086
 
20087
+ const treeItems = this.getVisibleNodes();
20088
+
19313
20089
  switch (e.key) {
19314
20090
  case keyHome:
19315
- if (this.treeItems && this.treeItems.length) {
19316
- TreeItem.focusItem(this.treeItems[0]);
19317
- this.treeItems[0].setAttribute("tabindex", "0");
20091
+ if (treeItems.length) {
20092
+ TreeItem.focusItem(treeItems[0]);
19318
20093
  }
19319
20094
 
19320
- break;
20095
+ return;
19321
20096
 
19322
20097
  case keyEnd:
19323
- if (this.treeItems && this.treeItems.length) {
19324
- TreeItem.focusItem(this.treeItems[this.treeItems.length - 1]);
19325
- this.treeItems[this.treeItems.length - 1].setAttribute("tabindex", "0");
20098
+ if (treeItems.length) {
20099
+ TreeItem.focusItem(treeItems[treeItems.length - 1]);
19326
20100
  }
19327
20101
 
19328
- break;
20102
+ return;
19329
20103
 
19330
20104
  case keyArrowLeft:
19331
20105
  if (e.target && this.isFocusableElement(e.target)) {
@@ -19336,7 +20110,7 @@ class TreeView extends FoundationElement {
19336
20110
  }
19337
20111
  }
19338
20112
 
19339
- break;
20113
+ return false;
19340
20114
 
19341
20115
  case keyArrowRight:
19342
20116
  if (e.target && this.isFocusableElement(e.target)) {
@@ -19347,62 +20121,84 @@ class TreeView extends FoundationElement {
19347
20121
  }
19348
20122
  }
19349
20123
 
19350
- break;
20124
+ return;
19351
20125
 
19352
20126
  case keyArrowDown:
19353
20127
  if (e.target && this.isFocusableElement(e.target)) {
19354
20128
  this.focusNextNode(1, e.target);
19355
20129
  }
19356
20130
 
19357
- break;
20131
+ return;
19358
20132
 
19359
20133
  case keyArrowUp:
19360
20134
  if (e.target && this.isFocusableElement(e.target)) {
19361
20135
  this.focusNextNode(-1, e.target);
19362
20136
  }
19363
20137
 
19364
- break;
20138
+ return;
19365
20139
 
19366
20140
  case keyEnter:
19367
20141
  // In single-select trees where selection does not follow focus (see note below),
19368
20142
  // the default action is typically to select the focused node.
19369
- this.handleSelected(e.target);
19370
- break;
20143
+ this.handleClick(e);
20144
+ return;
20145
+ } // don't prevent default if we took no action
19371
20146
 
19372
- default:
19373
- return true;
19374
- }
20147
+
20148
+ return true;
19375
20149
  };
20150
+ /**
20151
+ * Handles the selected-changed events bubbling up
20152
+ * from child tree items
20153
+ *
20154
+ * @internal
20155
+ */
19376
20156
 
19377
- this.setItems = () => {
19378
- let focusIndex = this.treeItems.findIndex(this.isSelectedElement);
19379
20157
 
19380
- if (focusIndex === -1) {
19381
- focusIndex = this.treeItems.findIndex(this.isFocusableElement);
20158
+ this.handleSelectedChange = e => {
20159
+ if (e.defaultPrevented) {
20160
+ return;
19382
20161
  }
19383
20162
 
19384
- for (let item = 0; item < this.treeItems.length; item++) {
19385
- if (item === focusIndex) {
19386
- this.treeItems[item].setAttribute("tabindex", "0");
19387
- this.currentFocused = this.treeItems[item];
19388
- }
19389
-
19390
- this.treeItems[item].addEventListener("selected-change", this.handleItemSelected);
20163
+ if (!(e.target instanceof Element) || !isTreeItemElement(e.target)) {
20164
+ return true;
19391
20165
  }
19392
- };
19393
20166
 
19394
- this.resetItems = () => {
19395
- for (let item = 0; item < this.treeItems.length; item++) {
19396
- this.treeItems[item].removeEventListener("selected-change", this.handleItemSelected);
20167
+ const item = e.target;
20168
+
20169
+ if (item.selected && this.currentSelected !== item) {
20170
+ // new selected item
20171
+ this.currentSelected = item;
20172
+ } else if (!item.selected && this.currentSelected === item) {
20173
+ // selected item deselected
20174
+ this.currentSelected = null;
19397
20175
  }
20176
+
20177
+ return;
19398
20178
  };
20179
+ /**
20180
+ * Updates the tree view when slottedTreeItems changes
20181
+ */
19399
20182
 
19400
- this.handleItemSelected = e => {
19401
- const newSelection = e.target;
19402
20183
 
19403
- if (newSelection !== this.currentSelected) {
19404
- this.handleSelected(newSelection);
19405
- }
20184
+ this.setItems = () => {
20185
+ // force single selection
20186
+ // defaults to first one found
20187
+ const selectedItem = this.treeView.querySelector("[aria-selected='true']");
20188
+ this.currentSelected = selectedItem; // invalidate the current focused item if it is no longer valid
20189
+
20190
+ if (this.currentFocused === null || !this.contains(this.currentFocused)) {
20191
+ this.currentFocused = this.getValidFocusableItem();
20192
+ } // toggle properties on child elements
20193
+
20194
+
20195
+ this.nested = this.checkForNestedItems();
20196
+ const treeItems = this.getVisibleNodes();
20197
+ treeItems.forEach(node => {
20198
+ if (isTreeItemElement(node)) {
20199
+ node.nested = this.nested;
20200
+ }
20201
+ });
19406
20202
  };
19407
20203
  /**
19408
20204
  * check if the item is focusable
@@ -19418,95 +20214,94 @@ class TreeView extends FoundationElement {
19418
20214
  };
19419
20215
  }
19420
20216
 
19421
- slottedTreeItemsChanged(oldValue, newValue) {
20217
+ slottedTreeItemsChanged() {
19422
20218
  if (this.$fastController.isConnected) {
19423
- // filter the tree items until that's done for us in the framework
19424
- this.resetItems();
19425
- this.treeItems = this.getVisibleNodes();
19426
- this.setItems(); // check if any tree items have nested items
19427
- // if they do, apply the nested attribute
19428
-
19429
- if (this.checkForNestedItems()) {
19430
- this.slottedTreeItems.forEach(node => {
19431
- if (isTreeItemElement(node)) {
19432
- node.nested = true;
19433
- }
19434
- });
19435
- }
20219
+ // update for slotted children change
20220
+ this.setItems();
19436
20221
  }
19437
20222
  }
19438
20223
 
19439
- checkForNestedItems() {
19440
- return this.slottedTreeItems.some(node => {
19441
- return isTreeItemElement(node) && node.querySelector("[role='treeitem']");
19442
- });
19443
- }
19444
-
19445
20224
  connectedCallback() {
19446
20225
  super.connectedCallback();
19447
- this.treeItems = this.getVisibleNodes();
20226
+ this.setAttribute("tabindex", "0");
19448
20227
  DOM.queueUpdate(() => {
19449
- //only supporting single select
19450
- const node = this.treeView.querySelector("[aria-selected='true']");
19451
-
19452
- if (node) {
19453
- this.currentSelected = node;
19454
- }
20228
+ this.setItems();
19455
20229
  });
19456
20230
  }
20231
+ /**
20232
+ * Handles click events bubbling up
20233
+ *
20234
+ * @internal
20235
+ */
19457
20236
 
19458
- focusNextNode(delta, item) {
19459
- const visibleNodes = this.getVisibleNodes();
19460
20237
 
19461
- if (!visibleNodes) {
20238
+ handleClick(e) {
20239
+ if (e.defaultPrevented) {
20240
+ // handled, do nothing
19462
20241
  return;
19463
20242
  }
19464
20243
 
19465
- const index = visibleNodes.indexOf(item);
19466
- const lastItem = visibleNodes[index];
20244
+ if (!(e.target instanceof Element) || !isTreeItemElement(e.target)) {
20245
+ // not a tree item, ignore
20246
+ return true;
20247
+ }
20248
+
20249
+ const item = e.target;
20250
+
20251
+ if (!item.disabled) {
20252
+ item.selected = !item.selected;
20253
+ }
20254
+
20255
+ return;
20256
+ }
20257
+ /**
20258
+ * Move focus to a tree item based on its offset from the provided item
20259
+ */
20260
+
20261
+
20262
+ focusNextNode(delta, item) {
20263
+ const visibleNodes = this.getVisibleNodes();
19467
20264
 
19468
- if (delta < 0 && index > 0) {
19469
- lastItem.setAttribute("tabindex", "-1");
19470
- } else if (delta > 0 && index < visibleNodes.length - 1) {
19471
- lastItem.setAttribute("tabindex", "-1");
20265
+ if (!visibleNodes) {
20266
+ return;
19472
20267
  }
19473
20268
 
19474
20269
  const focusItem = visibleNodes[visibleNodes.indexOf(item) + delta];
19475
20270
 
19476
20271
  if (isHTMLElement(focusItem)) {
19477
20272
  TreeItem.focusItem(focusItem);
19478
- focusItem.setAttribute("tabindex", "0");
19479
- this.currentFocused = focusItem;
19480
20273
  }
19481
20274
  }
20275
+ /**
20276
+ * checks if there are any nested tree items
20277
+ */
19482
20278
 
19483
- handleSelected(item) {
19484
- if (this.currentSelected !== item) {
19485
- item.setAttribute("tabindex", "0");
19486
-
19487
- if (this.currentSelected instanceof TreeItem && this.currentFocused) {
19488
- if (!item.disabled) {
19489
- this.currentSelected.selected = false;
19490
- }
19491
20279
 
19492
- this.currentFocused.setAttribute("tabindex", "-1");
19493
- }
20280
+ getValidFocusableItem() {
20281
+ const treeItems = this.getVisibleNodes(); // default to selected element if there is one
19494
20282
 
19495
- if (!this.currentSelected) {
19496
- this.slottedTreeItems.forEach(item => {
19497
- if (item instanceof TreeItem) {
19498
- item.setAttribute("tabindex", "-1");
19499
- }
19500
- });
19501
- }
20283
+ let focusIndex = treeItems.findIndex(this.isSelectedElement);
19502
20284
 
19503
- if (!item.disabled) {
19504
- item.selected = true;
19505
- this.currentSelected = item;
19506
- }
20285
+ if (focusIndex === -1) {
20286
+ // otherwise first focusable tree item
20287
+ focusIndex = treeItems.findIndex(this.isFocusableElement);
20288
+ }
19507
20289
 
19508
- this.currentFocused = item;
20290
+ if (focusIndex !== -1) {
20291
+ return treeItems[focusIndex];
19509
20292
  }
20293
+
20294
+ return null;
20295
+ }
20296
+ /**
20297
+ * checks if there are any nested tree items
20298
+ */
20299
+
20300
+
20301
+ checkForNestedItems() {
20302
+ return this.slottedTreeItems.some(node => {
20303
+ return isTreeItemElement(node) && node.querySelector("[role='treeitem']");
20304
+ });
19510
20305
  }
19511
20306
 
19512
20307
  getVisibleNodes() {
@@ -19521,8 +20316,6 @@ __decorate$1([attr({
19521
20316
 
19522
20317
  __decorate$1([observable], TreeView.prototype, "currentSelected", void 0);
19523
20318
 
19524
- __decorate$1([observable], TreeView.prototype, "nested", void 0);
19525
-
19526
20319
  __decorate$1([observable], TreeView.prototype, "slottedTreeItems", void 0);
19527
20320
 
19528
20321
  /**
@@ -19965,22 +20758,50 @@ function rgbToRelativeLuminance(rgb) {
19965
20758
 
19966
20759
  return rgbToLinearLuminance(new ColorRGBA64(luminanceHelper(rgb.r), luminanceHelper(rgb.g), luminanceHelper(rgb.b), 1));
19967
20760
  }
20761
+
20762
+ function calcChannelOverlay(match, background, overlay) {
20763
+ if (overlay - background === 0) {
20764
+ return 0;
20765
+ } else {
20766
+ return (match - background) / (overlay - background);
20767
+ }
20768
+ }
20769
+
20770
+ function calcRgbOverlay(rgbMatch, rgbBackground, rgbOverlay) {
20771
+ const rChannel = calcChannelOverlay(rgbMatch.r, rgbBackground.r, rgbOverlay.r);
20772
+ const gChannel = calcChannelOverlay(rgbMatch.g, rgbBackground.g, rgbOverlay.g);
20773
+ const bChannel = calcChannelOverlay(rgbMatch.b, rgbBackground.b, rgbOverlay.b);
20774
+ return (rChannel + gChannel + bChannel) / 3;
20775
+ }
19968
20776
  /**
19969
- * Calculate an overlay color that uses rgba (rgb + alpha) that matches the appareance of a given solid color when placed on the same background
20777
+ * Calculate an overlay color that uses rgba (rgb + alpha) that matches the appearance of a given solid color when placed on the same background
19970
20778
  * @param rgbMatch - The solid color the overlay should match in appearance when placed over the rgbBackground
19971
20779
  * @param rgbBackground - The background on which the overlay rests
19972
- * @param rgbOverlay - The rgb color of the overlay. Typically this is either pure white or pure black. This color will be used in the returned output
20780
+ * @param rgbOverlay - The rgb color of the overlay. Typically this is either pure white or pure black and when not provided will be determined automatically. This color will be used in the returned output
19973
20781
  * @returns The rgba (rgb + alpha) color of the overlay
19974
20782
  *
19975
20783
  * @public
19976
20784
  */
19977
20785
 
19978
- function calculateOverlayColor(rgbMatch, rgbBackground, rgbOverlay) {
19979
- const rChannel = (rgbMatch.r - rgbBackground.r) / (rgbOverlay.r - rgbBackground.r);
19980
- const gChannel = (rgbMatch.g - rgbBackground.g) / (rgbOverlay.g - rgbBackground.g);
19981
- const bChannel = (rgbMatch.b - rgbBackground.b) / (rgbOverlay.b - rgbBackground.b);
19982
- const alpha = (rChannel + gChannel + bChannel) / 3;
19983
- return new ColorRGBA64(rgbOverlay.r, rgbOverlay.g, rgbOverlay.b, alpha);
20786
+
20787
+ function calculateOverlayColor(rgbMatch, rgbBackground, rgbOverlay = null) {
20788
+ let alpha = 0;
20789
+ let overlay = rgbOverlay;
20790
+
20791
+ if (overlay !== null) {
20792
+ alpha = calcRgbOverlay(rgbMatch, rgbBackground, overlay);
20793
+ } else {
20794
+ overlay = new ColorRGBA64(0, 0, 0, 1);
20795
+ alpha = calcRgbOverlay(rgbMatch, rgbBackground, overlay);
20796
+
20797
+ if (alpha <= 0) {
20798
+ overlay = new ColorRGBA64(1, 1, 1, 1);
20799
+ alpha = calcRgbOverlay(rgbMatch, rgbBackground, overlay);
20800
+ }
20801
+ }
20802
+
20803
+ alpha = Math.round(alpha * 1000) / 1000;
20804
+ return new ColorRGBA64(overlay.r, overlay.g, overlay.b, alpha);
19984
20805
  }
19985
20806
  /**
19986
20807
  * Converts a {@link @microsoft/fast-colors#ColorRGBA64} to a {@link @microsoft/fast-colors#ColorHSL}
@@ -21148,7 +21969,7 @@ const typeRampPlus1LineHeight = create('type-ramp-plus-1-line-height').withDefau
21148
21969
  const typeRampPlus2FontSize = create('type-ramp-plus-2-font-size').withDefault('20px');
21149
21970
  /** @public */
21150
21971
 
21151
- const typeRampPlus2LineHeight = create('type-ramp-plus-2-line-height').withDefault('28px');
21972
+ const typeRampPlus2LineHeight = create('type-ramp-plus-2-line-height').withDefault('26px');
21152
21973
  /** @public */
21153
21974
 
21154
21975
  const typeRampPlus3FontSize = create('type-ramp-plus-3-font-size').withDefault('24px');
@@ -22906,6 +23727,9 @@ __decorate([attr({
22906
23727
  const fluentCombobox = Combobox.compose({
22907
23728
  baseName: 'combobox',
22908
23729
  baseClass: Combobox$1,
23730
+ shadowOptions: {
23731
+ delegatesFocus: true
23732
+ },
22909
23733
  template: comboboxTemplate,
22910
23734
  styles: comboboxStyles$1,
22911
23735
  indicator: `