@fluentui/web-components 3.0.0-rc.13 → 3.0.0-rc.14

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.
@@ -4646,15 +4646,22 @@ function requestIdleCallback(callback, options) {
4646
4646
  didTimeout: options?.timeout ? Date.now() - start >= options.timeout : false,
4647
4647
  timeRemaining: () => 0
4648
4648
  });
4649
- }, 1);
4649
+ }, options?.timeout ?? 1);
4650
4650
  }
4651
4651
  function waitForConnectedDescendants(target, callback, options) {
4652
4652
  const shallow = options?.shallow ?? false;
4653
4653
  const timeout = options?.timeout ?? 50;
4654
+ const useIdleCallback = options?.idleCallback ?? false;
4654
4655
  const selector = `${shallow ? ":scope > " : ""}:not(:defined)`;
4655
4656
  const scheduleCheck = deadline => {
4656
4657
  if (target.querySelector(selector) === null || deadline && deadline.timeRemaining() <= 0) {
4657
- requestAnimationFrame(callback);
4658
+ if (useIdleCallback) {
4659
+ requestIdleCallback(callback, {
4660
+ timeout
4661
+ });
4662
+ } else {
4663
+ callback();
4664
+ }
4658
4665
  return;
4659
4666
  }
4660
4667
  requestIdleCallback(scheduleCheck, {
@@ -4684,6 +4691,11 @@ var __decorateClass$M = (decorators, target, key, kind) => {
4684
4691
  class Accordion extends FASTElement {
4685
4692
  constructor() {
4686
4693
  super(...arguments);
4694
+ /**
4695
+ * Guard flag to prevent re-entrant calls to setSingleExpandMode.
4696
+ * @internal
4697
+ */
4698
+ this._isAdjusting = false;
4687
4699
  this.activeItemIndex = 0;
4688
4700
  /**
4689
4701
  * Resets event listeners and sets the `accordionItems` property
@@ -4750,15 +4762,13 @@ class Accordion extends FASTElement {
4750
4762
  this.setSingleExpandMode(expandedItem);
4751
4763
  return;
4752
4764
  }
4753
- expandedItem?.expandbutton.removeAttribute("aria-disabled");
4765
+ expandedItem?.expandbutton?.removeAttribute("aria-disabled");
4754
4766
  }
4755
4767
  /**
4756
4768
  * @internal
4757
4769
  */
4758
4770
  slottedAccordionItemsChanged(oldValue, newValue) {
4759
- if (this.$fastController.isConnected) {
4760
- this.setItems();
4761
- }
4771
+ this.setItems();
4762
4772
  }
4763
4773
  /**
4764
4774
  * Watch for changes to the slotted accordion items `disabled` and `expanded` attributes
@@ -4768,8 +4778,10 @@ class Accordion extends FASTElement {
4768
4778
  if (propertyName === "disabled") {
4769
4779
  this.setItems();
4770
4780
  } else if (propertyName === "expanded") {
4771
- if (source.expanded && this.isSingleExpandMode()) {
4781
+ if (source.expanded && this.isSingleExpandMode() && !this._isAdjusting) {
4782
+ this._isAdjusting = true;
4772
4783
  this.setSingleExpandMode(source);
4784
+ this._isAdjusting = false;
4773
4785
  }
4774
4786
  }
4775
4787
  }
@@ -4796,33 +4808,29 @@ class Accordion extends FASTElement {
4796
4808
  * @returns {void}
4797
4809
  */
4798
4810
  setSingleExpandMode(expandedItem) {
4799
- requestAnimationFrame(() => {
4800
- if (this.accordionItems.length === 0) {
4801
- return;
4802
- }
4803
- const currentItems = Array.from(this.accordionItems);
4804
- this.activeItemIndex = currentItems.indexOf(expandedItem);
4805
- currentItems.forEach((item, index) => {
4806
- if (isAccordionItem(item)) {
4807
- if (this.activeItemIndex === index) {
4808
- item.expanded = true;
4809
- item.expandbutton.setAttribute("aria-disabled", "true");
4810
- } else {
4811
- item.expanded = false;
4812
- if (!item.hasAttribute("disabled")) {
4813
- item.expandbutton.removeAttribute("aria-disabled");
4814
- }
4811
+ if (this.accordionItems.length === 0) {
4812
+ return;
4813
+ }
4814
+ const currentItems = Array.from(this.accordionItems);
4815
+ this.activeItemIndex = currentItems.indexOf(expandedItem);
4816
+ currentItems.forEach((item, index) => {
4817
+ if (isAccordionItem(item)) {
4818
+ if (this.activeItemIndex === index) {
4819
+ item.expanded = true;
4820
+ item.expandbutton?.setAttribute("aria-disabled", "true");
4821
+ } else {
4822
+ item.expanded = false;
4823
+ if (!item.hasAttribute("disabled")) {
4824
+ item.expandbutton?.removeAttribute("aria-disabled");
4815
4825
  }
4816
4826
  }
4817
- });
4827
+ }
4818
4828
  });
4819
4829
  }
4820
4830
  connectedCallback() {
4821
4831
  super.connectedCallback();
4822
- requestAnimationFrame(() => {
4823
- this.expandmode = this.expandmode || AccordionExpandMode.multi;
4824
- this.setItems();
4825
- });
4832
+ this.expandmode = this.expandmode || AccordionExpandMode.multi;
4833
+ this.setItems();
4826
4834
  }
4827
4835
  }
4828
4836
  __decorateClass$M([attr({
@@ -7346,6 +7354,11 @@ const _BaseDropdown = class _BaseDropdown extends FASTElement {
7346
7354
  * @internal
7347
7355
  */
7348
7356
  this.elementInternals = this.attachInternals();
7357
+ /**
7358
+ * Guard flag to prevent reentrant calls to `insertControl`.
7359
+ * @internal
7360
+ */
7361
+ this._insertingControl = false;
7349
7362
  this.elementInternals.role = "presentation";
7350
7363
  }
7351
7364
  get activeDescendant() {
@@ -7440,7 +7453,7 @@ const _BaseDropdown = class _BaseDropdown extends FASTElement {
7440
7453
  const notifier = Observable.getNotifier(this);
7441
7454
  notifier.subscribe(next);
7442
7455
  notifier.notify("multiple");
7443
- Updates.enqueue(() => {
7456
+ waitForConnectedDescendants(next, () => {
7444
7457
  this.options.forEach(option => {
7445
7458
  option.disabled = option.disabledAttribute || this.disabled;
7446
7459
  option.name = this.name;
@@ -7449,6 +7462,8 @@ const _BaseDropdown = class _BaseDropdown extends FASTElement {
7449
7462
  x.selected = this.multiple || i === 0;
7450
7463
  });
7451
7464
  this.setValidity();
7465
+ }, {
7466
+ idleCallback: true
7452
7467
  });
7453
7468
  if (AnchorPositioningCSSSupported) {
7454
7469
  const anchorName = uniqueId("--dropdown-anchor-");
@@ -7792,12 +7807,17 @@ const _BaseDropdown = class _BaseDropdown extends FASTElement {
7792
7807
  * This method can be overridden in derived classes to provide custom control elements, though this is not recommended.
7793
7808
  */
7794
7809
  insertControl() {
7810
+ if (this._insertingControl) {
7811
+ return;
7812
+ }
7813
+ this._insertingControl = true;
7795
7814
  this.controlSlot?.assignedNodes().forEach(x => this.removeChild(x));
7796
7815
  if (this.type === DropdownType.combobox) {
7797
7816
  dropdownInputTemplate.render(this, this);
7798
7817
  return;
7799
7818
  }
7800
7819
  dropdownButtonTemplate.render(this, this);
7820
+ this._insertingControl = false;
7801
7821
  }
7802
7822
  /**
7803
7823
  * Handles the keydown events for the dropdown.
@@ -7896,7 +7916,9 @@ const _BaseDropdown = class _BaseDropdown extends FASTElement {
7896
7916
  */
7897
7917
  selectOption(index = this.selectedIndex, shouldEmit = false) {
7898
7918
  this.listbox.selectOption(index);
7899
- this.control.value = this.displayValue;
7919
+ if (this.control) {
7920
+ this.control.value = this.displayValue;
7921
+ }
7900
7922
  this.setValidity();
7901
7923
  this.updateFreeformOption();
7902
7924
  if (shouldEmit) {
@@ -7913,17 +7935,18 @@ const _BaseDropdown = class _BaseDropdown extends FASTElement {
7913
7935
  * @internal
7914
7936
  */
7915
7937
  setValidity(flags, message, anchor) {
7916
- if (this.$fastController.isConnected) {
7917
- if (this.disabled || !this.required) {
7918
- this.elementInternals.setValidity({});
7919
- return;
7920
- }
7921
- const valueMissing = this.required && this.listbox.selectedOptions.length === 0;
7922
- this.elementInternals.setValidity({
7923
- valueMissing,
7924
- ...flags
7925
- }, message ?? this.validationMessage, anchor ?? this.control);
7938
+ if (!this.elementInternals) {
7939
+ return;
7926
7940
  }
7941
+ if (this.disabled || !this.required) {
7942
+ this.elementInternals.setValidity({});
7943
+ return;
7944
+ }
7945
+ const valueMissing = this.required && this.listbox.selectedOptions.length === 0;
7946
+ this.elementInternals.setValidity({
7947
+ valueMissing,
7948
+ ...flags
7949
+ }, message ?? this.validationMessage, anchor ?? this.control);
7927
7950
  }
7928
7951
  /**
7929
7952
  * Handles the `slotchange` event for the dropdown.
@@ -8085,7 +8108,7 @@ __decorateClass$w([attr], Dropdown.prototype, "size", 2);
8085
8108
  const styles$q = css`
8086
8109
  ${display("inline-flex")}
8087
8110
 
8088
- :host{anchor-name:--dropdown-trigger;box-sizing:border-box;color:${colorNeutralForeground1};cursor:pointer}:host(${placeholderShownState}){color:${colorNeutralForeground4}}.control{appearance:none;background-color:${colorNeutralBackground1};border-radius:${borderRadiusMedium};border:${strokeWidthThin} solid ${colorTransparentStroke};box-shadow:inset 0 0 0 ${strokeWidthThin} var(--control-border-color);box-sizing:border-box;color:inherit;column-gap:${spacingHorizontalXXS};display:inline-flex;justify-content:space-between;min-width:160px;overflow:hidden;padding:${spacingVerticalSNudge} ${spacingHorizontalMNudge};position:relative;text-align:start;width:100%;z-index:1;${typographyBody1Styles}}:host([size='small']) .control{column-gap:${spacingHorizontalXXS};padding:${spacingVerticalXS} ${spacingHorizontalSNudge};${typographyCaption1Styles}}:host([size='large']) .control{column-gap:${spacingHorizontalS};padding:${spacingVerticalS} ${spacingHorizontalM};${typographyBody2Styles}}::slotted(:is(input,button)){all:unset;flex:1 1 auto}::slotted(button){cursor:pointer}::slotted(input){cursor:text}:where(slot[name='indicator'] > *,::slotted([slot='indicator'])){all:unset;align-items:center;appearance:none;aspect-ratio:1;color:${colorNeutralForeground3};display:inline-flex;justify-content:center;width:20px}:host([size='small']) :where(slot[name='indicator'] > *,::slotted([slot='indicator'])){width:16px}:host([size='large']) :where(slot[name='indicator'] > *,::slotted([slot='indicator'])){width:24px}.control::after,.control::before{content:'' / '';inset:auto 0 0;pointer-events:none;position:absolute}.control::before{height:${strokeWidthThin}}.control::after{background-color:${colorCompoundBrandStroke};height:${strokeWidthThick};scale:0 1;transition:scale ${durationUltraFast} ${curveDecelerateMid}}:host(:where(:focus-within)) .control{border-radius:${borderRadiusMedium};box-shadow:inset 0 0 0 1px ${colorStrokeFocus1};outline:${strokeWidthThick} solid ${colorStrokeFocus2}}:host(:where(${openState},:focus-within)) .control::after{scale:1 1;transition-duration:${durationNormal};transition-timing-function:${curveAccelerateMid}}:host(:where([appearance='outline'],[appearance='transparent'])) .control::before{background-color:${colorNeutralStrokeAccessible}}:host([appearance='transparent']) .control{--control-border-color:${colorTransparentStrokeInteractive};background-color:${colorTransparentBackground};border-radius:${borderRadiusNone}}:host([appearance='outline']) .control{--control-border-color:${colorNeutralStroke1}}:host([appearance='outline']) .control:hover{--control-border-color:${colorNeutralStroke1Hover}}:host(:where([appearance='outline'],[appearance='transparent'])) .control:hover::before{background-color:${colorNeutralStrokeAccessibleHover}}:host([appearance='outline']) .control:hover::after{background-color:${colorCompoundBrandBackgroundHover}}:host([appearance='outline']) .control:active{--control-border-color:${colorNeutralStroke1Pressed}}:host(:where([appearance='outline'],[appearance='transparent'])) .control:active::before{background-color:${colorNeutralStrokeAccessiblePressed}}:host(:where([appearance='outline'],[appearance='transparent'])) .control:active::after{background-color:${colorCompoundBrandBackgroundPressed}}:host([appearance='filled-darker']) .control{background-color:${colorNeutralBackground3}}:host(:where([appearance='filled-lighter'],[appearance='filled-darker'])) .control{--control-border-color:${colorTransparentStroke}}:host(:disabled),:host(:disabled) ::slotted(:where(button,input)){cursor:not-allowed}:host(:disabled) .control::before,:host(:disabled) .control::after{content:none}:host(:disabled) .control:is(*,:active,:hover),:host(:disabled) :where(slot[name='indicator'] > *,::slotted([slot='indicator'])){--control-border-color:${colorNeutralStrokeDisabled};background-color:${colorNeutralBackgroundDisabled};color:${colorNeutralForegroundDisabled}}::slotted([popover]:not(:popover-open)){display:none}@supports not (anchor-name:--anchor){:host{--listbox-max-height:50vh;--margin-offset:calc(${lineHeightBase300} + (${spacingVerticalSNudge} * 2) + ${strokeWidthThin})}:host([size='small']){--margin-offset:calc(${lineHeightBase200} + (${spacingVerticalXS} * 2) + ${strokeWidthThin})}:host([size='large']){--margin-offset:calc(${lineHeightBase400} + (${spacingVerticalS} * 2) + ${strokeWidthThin})}}@media (forced-colors:active){:host(:disabled) .control{border-color:GrayText}:host(:disabled) :where(slot[name='indicator'] > *,::slotted([slot='indicator'])){color:GrayText}`;
8111
+ :host{anchor-name:--dropdown-trigger;box-sizing:border-box;color:${colorNeutralForeground1};cursor:pointer}:host(${placeholderShownState}){color:${colorNeutralForeground4}}.control{appearance:none;background-color:${colorNeutralBackground1};border-radius:${borderRadiusMedium};border:${strokeWidthThin} solid ${colorTransparentStroke};box-shadow:inset 0 0 0 ${strokeWidthThin} var(--control-border-color);box-sizing:border-box;color:inherit;column-gap:${spacingHorizontalXXS};display:inline-flex;justify-content:space-between;min-width:160px;overflow:hidden;padding:${spacingVerticalSNudge} ${spacingHorizontalMNudge};position:relative;text-align:start;width:100%;z-index:1;${typographyBody1Styles}}:host([size='small']) .control{column-gap:${spacingHorizontalXXS};padding:${spacingVerticalXS} ${spacingHorizontalSNudge};${typographyCaption1Styles}}:host([size='large']) .control{column-gap:${spacingHorizontalS};padding:${spacingVerticalS} ${spacingHorizontalM};${typographyBody2Styles}}::slotted(:is(input,button)){all:unset;flex:1 1 auto}::slotted(button){cursor:pointer}::slotted(input){cursor:text}:where(slot[name='indicator'] > *,::slotted([slot='indicator'])){all:unset;align-items:center;appearance:none;aspect-ratio:1;color:${colorNeutralForeground3};display:inline-flex;justify-content:center;width:20px}:host([size='small']) :where(slot[name='indicator'] > *,::slotted([slot='indicator'])){width:16px}:host([size='large']) :where(slot[name='indicator'] > *,::slotted([slot='indicator'])){width:24px}.control::after,.control::before{content:'' / '';inset:auto 0 0;pointer-events:none;position:absolute}.control::before{height:${strokeWidthThin}}.control::after{background-color:${colorCompoundBrandStroke};height:${strokeWidthThick};scale:0 1;transition:scale ${durationUltraFast} ${curveDecelerateMid}}:host(:where(:focus-within)) .control{border-radius:${borderRadiusMedium};box-shadow:inset 0 0 0 1px ${colorStrokeFocus1};outline:${strokeWidthThick} solid ${colorStrokeFocus2}}:host(:where(${openState},:focus-within)) .control::after{scale:1 1;transition-duration:${durationNormal};transition-timing-function:${curveAccelerateMid}}:host(:where([appearance='outline'],[appearance='transparent'])) .control::before{background-color:${colorNeutralStrokeAccessible}}:host([appearance='transparent']) .control{--control-border-color:${colorTransparentStrokeInteractive};background-color:${colorTransparentBackground};border-radius:${borderRadiusNone}}:host([appearance='outline']) .control{--control-border-color:${colorNeutralStroke1}}:host([appearance='outline']) .control:hover{--control-border-color:${colorNeutralStroke1Hover}}:host(:where([appearance='outline'],[appearance='transparent'])) .control:hover::before{background-color:${colorNeutralStrokeAccessibleHover}}:host([appearance='outline']) .control:hover::after{background-color:${colorCompoundBrandBackgroundHover}}:host([appearance='outline']) .control:active{--control-border-color:${colorNeutralStroke1Pressed}}:host(:where([appearance='outline'],[appearance='transparent'])) .control:active::before{background-color:${colorNeutralStrokeAccessiblePressed}}:host(:where([appearance='outline'],[appearance='transparent'])) .control:active::after{background-color:${colorCompoundBrandBackgroundPressed}}:host([appearance='filled-darker']) .control{background-color:${colorNeutralBackground3}}:host(:where([appearance='filled-lighter'],[appearance='filled-darker'])) .control{--control-border-color:${colorTransparentStroke}}:host(:disabled),:host(:disabled) ::slotted(:where(button,input)){cursor:not-allowed}:host(:disabled) .control::before,:host(:disabled) .control::after{content:none}:host(:disabled) .control:is(*,:active,:hover),:host(:disabled) :where(slot[name='indicator'] > *,::slotted([slot='indicator'])){--control-border-color:${colorNeutralStrokeDisabled};background-color:${colorNeutralBackgroundDisabled};color:${colorNeutralForegroundDisabled}}::slotted(:not([slot]):not([popover])),::slotted([popover]:not(:popover-open)){display:none}@supports not (anchor-name:--anchor){:host{--listbox-max-height:50vh;--margin-offset:calc(${lineHeightBase300} + (${spacingVerticalSNudge} * 2) + ${strokeWidthThin})}:host([size='small']){--margin-offset:calc(${lineHeightBase200} + (${spacingVerticalXS} * 2) + ${strokeWidthThin})}:host([size='large']){--margin-offset:calc(${lineHeightBase400} + (${spacingVerticalS} * 2) + ${strokeWidthThin})}}@media (forced-colors:active){:host(:disabled) .control{border-color:GrayText}:host(:disabled) :where(slot[name='indicator'] > *,::slotted([slot='indicator'])){color:GrayText}`;
8089
8112
 
8090
8113
  const definition$r = Dropdown.compose({
8091
8114
  name: `${FluentDesignSystem.prefix}-dropdown`,
@@ -9276,7 +9299,33 @@ class Menu extends FASTElement {
9276
9299
  * @internal
9277
9300
  */
9278
9301
  slottedMenuListChanged(prev, next) {
9279
- this.setComponent();
9302
+ this._menuListAbortController?.abort();
9303
+ if (!next?.length) {
9304
+ return;
9305
+ }
9306
+ this._menuList = next[0];
9307
+ this._menuList.popover = this.openOnContext ? "manual" : "";
9308
+ this.addMenuListListeners();
9309
+ }
9310
+ /**
9311
+ * Ensures the trigger is properly set up when the slotted triggers change.
9312
+ * This includes setting ARIA attributes and adding event listeners based on the current property values.
9313
+ *
9314
+ * @param prev - The previous items in the slotted triggers list.
9315
+ * @param next - The current items in the slotted triggers list.
9316
+ * @internal
9317
+ */
9318
+ slottedTriggersChanged(prev, next) {
9319
+ this._triggerAbortController?.abort();
9320
+ if (next?.length) {
9321
+ const trigger = next[0];
9322
+ this._trigger = trigger;
9323
+ if (this._trigger?.isConnected) {
9324
+ this._trigger.setAttribute("aria-haspopup", "true");
9325
+ this._trigger.setAttribute("aria-expanded", `${this._open}`);
9326
+ this.addTriggerListeners();
9327
+ }
9328
+ }
9280
9329
  }
9281
9330
  /**
9282
9331
  * Called when the element is connected to the DOM.
@@ -9294,32 +9343,16 @@ class Menu extends FASTElement {
9294
9343
  */
9295
9344
  disconnectedCallback() {
9296
9345
  super.disconnectedCallback();
9297
- this.removeListeners();
9346
+ this._triggerAbortController?.abort();
9347
+ this._menuListAbortController?.abort();
9298
9348
  }
9299
9349
  /**
9300
9350
  * Sets the component.
9301
- * Sets the trigger and menu list elements and adds event listeners.
9351
+ * @deprecated This method is no longer used. Trigger and menu-list listeners are now
9352
+ * managed by their respective slot-changed callbacks.
9302
9353
  * @public
9303
9354
  */
9304
- setComponent() {
9305
- waitForConnectedDescendants(this, () => {
9306
- const trigger = this.slottedTriggers?.[0];
9307
- const menuList = this.slottedMenuList?.[0];
9308
- if (!trigger || !menuList) {
9309
- this.removeListeners();
9310
- return;
9311
- }
9312
- this._trigger = trigger;
9313
- this._menuList = menuList;
9314
- this._trigger.setAttribute("aria-haspopup", "true");
9315
- this._trigger.setAttribute("aria-expanded", `${this._open}`);
9316
- this._menuList.setAttribute("popover", this.openOnContext ? "manual" : "");
9317
- this.removeListeners();
9318
- this.addListeners();
9319
- }, {
9320
- shallow: true
9321
- });
9322
- }
9355
+ setComponent() {}
9323
9356
  /**
9324
9357
  * Focuses on the menu list.
9325
9358
  * @public
@@ -9347,10 +9380,9 @@ class Menu extends FASTElement {
9347
9380
  * @public
9348
9381
  */
9349
9382
  openOnHoverChanged(oldValue, newValue) {
9350
- if (newValue) {
9351
- this._trigger?.addEventListener("mouseover", this.openMenu);
9352
- } else {
9353
- this._trigger?.removeEventListener("mouseover", this.openMenu);
9383
+ if (this._trigger) {
9384
+ this._triggerAbortController?.abort();
9385
+ this.addTriggerListeners();
9354
9386
  }
9355
9387
  }
9356
9388
  /**
@@ -9361,10 +9393,9 @@ class Menu extends FASTElement {
9361
9393
  * @param newValue - The new value of 'persistOnItemClick'.
9362
9394
  */
9363
9395
  persistOnItemClickChanged(oldValue, newValue) {
9364
- if (!newValue) {
9365
- this._menuList?.addEventListener("change", this.closeMenu);
9366
- } else {
9367
- this._menuList?.removeEventListener("change", this.closeMenu);
9396
+ if (this._menuList) {
9397
+ this._menuListAbortController?.abort();
9398
+ this.addMenuListListeners();
9368
9399
  }
9369
9400
  }
9370
9401
  /**
@@ -9376,9 +9407,13 @@ class Menu extends FASTElement {
9376
9407
  */
9377
9408
  openOnContextChanged(oldValue, newValue) {
9378
9409
  if (newValue) {
9379
- this._trigger?.addEventListener("contextmenu", this.openMenu);
9410
+ this._menuList?.setAttribute("popover", "manual");
9380
9411
  } else {
9381
- this._trigger?.removeEventListener("contextmenu", this.openMenu);
9412
+ this._menuList?.setAttribute("popover", "");
9413
+ }
9414
+ if (this._trigger) {
9415
+ this._triggerAbortController?.abort();
9416
+ this.addTriggerListeners();
9382
9417
  }
9383
9418
  }
9384
9419
  /**
@@ -9396,50 +9431,50 @@ class Menu extends FASTElement {
9396
9431
  }
9397
9432
  }
9398
9433
  /**
9399
- * Adds event listeners.
9400
- * Adds click and keydown event listeners to the trigger.
9401
- * Adds a 'toggle' event listener to the menu list.
9402
- * If 'openOnHover' is true, adds a 'mouseover' event listener to the trigger.
9403
- * If 'openOnContext' is true, adds a 'contextmenu' event listener to the trigger and a document 'click' event listener.
9434
+ * Adds trigger-related event listeners.
9404
9435
  * @internal
9405
9436
  */
9406
- addListeners() {
9407
- this._menuList?.addEventListener("toggle", this.toggleHandler);
9408
- this._trigger?.addEventListener("keydown", this.triggerKeydownHandler);
9409
- if (!this.persistOnItemClick) {
9410
- this._menuList?.addEventListener("change", this.closeMenu);
9411
- }
9437
+ addTriggerListeners() {
9438
+ this._triggerAbortController = new AbortController();
9439
+ const {
9440
+ signal
9441
+ } = this._triggerAbortController;
9442
+ this._trigger?.addEventListener("keydown", this.triggerKeydownHandler, {
9443
+ signal
9444
+ });
9412
9445
  if (this.openOnHover) {
9413
- this._trigger?.addEventListener("mouseover", this.openMenu);
9446
+ this._trigger?.addEventListener("mouseover", this.openMenu, {
9447
+ signal
9448
+ });
9414
9449
  } else if (this.openOnContext) {
9415
- this._trigger?.addEventListener("contextmenu", this.openMenu);
9416
- document.addEventListener("click", this.documentClickHandler);
9450
+ this._trigger?.addEventListener("contextmenu", this.openMenu, {
9451
+ signal
9452
+ });
9453
+ document.addEventListener("click", this.documentClickHandler, {
9454
+ signal
9455
+ });
9417
9456
  } else {
9418
- this._trigger?.addEventListener("click", this.toggleMenu);
9457
+ this._trigger?.addEventListener("click", this.toggleMenu, {
9458
+ signal
9459
+ });
9419
9460
  }
9420
9461
  }
9421
9462
  /**
9422
- * Removes event listeners.
9423
- * Removes click and keydown event listeners from the trigger.
9424
- * Also removes toggle event listener from the menu list.
9425
- * Also removes 'mouseover' event listeners from the trigger.
9426
- * Also removes 'contextmenu' event listeners from the trigger and document 'click' event listeners.
9463
+ * Adds menu-list event listeners.
9427
9464
  * @internal
9428
9465
  */
9429
- removeListeners() {
9430
- this._menuList?.removeEventListener("toggle", this.toggleHandler);
9431
- this._trigger?.removeEventListener("keydown", this.triggerKeydownHandler);
9466
+ addMenuListListeners() {
9467
+ this._menuListAbortController = new AbortController();
9468
+ const {
9469
+ signal
9470
+ } = this._menuListAbortController;
9471
+ this._menuList?.addEventListener("toggle", this.toggleHandler, {
9472
+ signal
9473
+ });
9432
9474
  if (!this.persistOnItemClick) {
9433
- this._menuList?.removeEventListener("change", this.closeMenu);
9434
- }
9435
- if (this.openOnHover) {
9436
- this._trigger?.removeEventListener("mouseover", this.openMenu);
9437
- }
9438
- if (this.openOnContext) {
9439
- this._trigger?.removeEventListener("contextmenu", this.openMenu);
9440
- document.removeEventListener("click", this.documentClickHandler);
9441
- } else {
9442
- this._trigger?.removeEventListener("click", this.toggleMenu);
9475
+ this._menuList?.addEventListener("change", this.closeMenu, {
9476
+ signal
9477
+ });
9443
9478
  }
9444
9479
  }
9445
9480
  /**
@@ -11746,9 +11781,9 @@ class BaseTablist extends FASTElement {
11746
11781
  });
11747
11782
  }
11748
11783
  getTabIds() {
11749
- return this.tabs.map(tab => {
11784
+ return this.tabs?.map(tab => {
11750
11785
  return tab.getAttribute("id") ?? `tab-${uniqueId$1()}`;
11751
- });
11786
+ }) ?? [];
11752
11787
  }
11753
11788
  setComponent() {
11754
11789
  if (this.activeTabIndex !== this.prevActiveTabIndex) {
@@ -11793,6 +11828,8 @@ class BaseTablist extends FASTElement {
11793
11828
  waitForConnectedDescendants(this, () => {
11794
11829
  this.tabIds = this.getTabIds();
11795
11830
  this.setTabs();
11831
+ }, {
11832
+ shallow: true
11796
11833
  });
11797
11834
  }
11798
11835
  }