@adobe/spectrum-wc-core 2.0.0-beta.2 → 2.0.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/CHANGELOG.md +75 -9
  2. package/dist/components/accordion/Accordion.base.js +28 -22
  3. package/dist/components/accordion/Accordion.base.js.map +1 -1
  4. package/dist/components/accordion/AccordionItem.base.d.ts +16 -6
  5. package/dist/components/accordion/AccordionItem.base.js +6 -3
  6. package/dist/components/accordion/AccordionItem.base.js.map +1 -1
  7. package/dist/components/action-group/ActionGroup.base.d.ts +108 -0
  8. package/dist/components/action-group/ActionGroup.base.js +53 -0
  9. package/dist/components/action-group/ActionGroup.base.js.map +1 -0
  10. package/dist/components/action-group/ActionGroup.types.d.ts +17 -0
  11. package/dist/components/action-group/ActionGroup.types.js +12 -0
  12. package/dist/components/action-group/ActionGroup.types.js.map +1 -0
  13. package/dist/components/action-group/index.d.ts +13 -0
  14. package/dist/components/action-group/index.js +3 -0
  15. package/dist/components/alert-banner/AlertBanner.base.js +11 -10
  16. package/dist/components/alert-banner/AlertBanner.base.js.map +1 -1
  17. package/dist/components/asset/Asset.base.d.ts +1 -1
  18. package/dist/components/asset/Asset.base.js +14 -8
  19. package/dist/components/asset/Asset.base.js.map +1 -1
  20. package/dist/components/avatar/Avatar.base.js +18 -22
  21. package/dist/components/avatar/Avatar.base.js.map +1 -1
  22. package/dist/components/badge/Badge.base.d.ts +24 -8
  23. package/dist/components/badge/Badge.base.js +22 -17
  24. package/dist/components/badge/Badge.base.js.map +1 -1
  25. package/dist/components/button/Button.base.d.ts +20 -10
  26. package/dist/components/button/Button.base.js +17 -12
  27. package/dist/components/button/Button.base.js.map +1 -1
  28. package/dist/components/button-group/ButtonGroup.base.js +19 -12
  29. package/dist/components/button-group/ButtonGroup.base.js.map +1 -1
  30. package/dist/components/card/Card.base.d.ts +95 -46
  31. package/dist/components/card/Card.base.js +71 -23
  32. package/dist/components/card/Card.base.js.map +1 -1
  33. package/dist/components/card/Card.types.d.ts +1 -0
  34. package/dist/components/card/Card.types.js +2 -2
  35. package/dist/components/card/Card.types.js.map +1 -1
  36. package/dist/components/card/index.js +3 -3
  37. package/dist/components/divider/Divider.base.js +13 -8
  38. package/dist/components/divider/Divider.base.js.map +1 -1
  39. package/dist/components/dropzone/Dropzone.base.d.ts +74 -0
  40. package/dist/components/dropzone/Dropzone.base.js +82 -0
  41. package/dist/components/dropzone/Dropzone.base.js.map +1 -0
  42. package/dist/components/dropzone/Dropzone.types.d.ts +43 -0
  43. package/dist/components/dropzone/Dropzone.types.js +15 -0
  44. package/dist/components/dropzone/Dropzone.types.js.map +1 -0
  45. package/dist/components/dropzone/index.d.ts +13 -0
  46. package/dist/components/dropzone/index.js +3 -0
  47. package/dist/components/icon/Icon.base.d.ts +11 -7
  48. package/dist/components/icon/Icon.base.js +13 -20
  49. package/dist/components/icon/Icon.base.js.map +1 -1
  50. package/dist/components/illustrated-message/IllustratedMessage.base.d.ts +22 -8
  51. package/dist/components/illustrated-message/IllustratedMessage.base.js +33 -20
  52. package/dist/components/illustrated-message/IllustratedMessage.base.js.map +1 -1
  53. package/dist/components/infield-button/InfieldButton.base.d.ts +28 -0
  54. package/dist/components/infield-button/InfieldButton.base.js +22 -0
  55. package/dist/components/infield-button/InfieldButton.base.js.map +1 -0
  56. package/dist/components/infield-button/InfieldButton.types.d.ts +13 -0
  57. package/dist/components/infield-button/InfieldButton.types.js +11 -0
  58. package/dist/components/infield-button/InfieldButton.types.js.map +1 -0
  59. package/dist/components/infield-button/index.d.ts +13 -0
  60. package/dist/components/infield-button/index.js +3 -0
  61. package/dist/components/meter/Meter.base.js +11 -9
  62. package/dist/components/meter/Meter.base.js.map +1 -1
  63. package/dist/components/popover/Popover.base.js +46 -42
  64. package/dist/components/popover/Popover.base.js.map +1 -1
  65. package/dist/components/progress-circle/ProgressCircle.base.d.ts +0 -1
  66. package/dist/components/progress-circle/ProgressCircle.base.js +26 -22
  67. package/dist/components/progress-circle/ProgressCircle.base.js.map +1 -1
  68. package/dist/components/status-light/StatusLight.base.d.ts +1 -1
  69. package/dist/components/status-light/StatusLight.base.js +12 -9
  70. package/dist/components/status-light/StatusLight.base.js.map +1 -1
  71. package/dist/components/tabs/Tabs.base.js +44 -32
  72. package/dist/components/tabs/Tabs.base.js.map +1 -1
  73. package/dist/components/tooltip/Tooltip.base.js +57 -22
  74. package/dist/components/tooltip/Tooltip.base.js.map +1 -1
  75. package/dist/controllers/color-controller/src/color-controller.js +45 -44
  76. package/dist/controllers/color-controller/src/color-controller.js.map +1 -1
  77. package/dist/controllers/drag-and-drop-controller/index.d.ts +12 -0
  78. package/dist/controllers/drag-and-drop-controller/index.js +2 -0
  79. package/dist/controllers/drag-and-drop-controller/src/drag-and-drop-controller.d.ts +68 -0
  80. package/dist/controllers/drag-and-drop-controller/src/drag-and-drop-controller.js +56 -0
  81. package/dist/controllers/drag-and-drop-controller/src/drag-and-drop-controller.js.map +1 -0
  82. package/dist/controllers/index.d.ts +4 -1
  83. package/dist/controllers/index.js +14 -11
  84. package/dist/controllers/page-scroll-lock-controller/index.d.ts +12 -0
  85. package/dist/controllers/page-scroll-lock-controller/index.js +2 -0
  86. package/dist/controllers/{page-scroll-lock.js → page-scroll-lock-controller/src/page-scroll-lock.js} +1 -1
  87. package/dist/controllers/page-scroll-lock-controller/src/page-scroll-lock.js.map +1 -0
  88. package/dist/controllers/slot-presence-controller/index.d.ts +12 -0
  89. package/dist/controllers/slot-presence-controller/index.js +2 -0
  90. package/dist/controllers/slot-presence-controller/src/slot-presence-controller.d.ts +57 -0
  91. package/dist/controllers/slot-presence-controller/src/slot-presence-controller.js +44 -0
  92. package/dist/controllers/slot-presence-controller/src/slot-presence-controller.js.map +1 -0
  93. package/dist/controllers/slot-text-controller/index.d.ts +12 -0
  94. package/dist/controllers/slot-text-controller/index.js +2 -0
  95. package/dist/controllers/slot-text-controller/src/slot-text-controller.d.ts +92 -0
  96. package/dist/controllers/slot-text-controller/src/slot-text-controller.js +53 -0
  97. package/dist/controllers/slot-text-controller/src/slot-text-controller.js.map +1 -0
  98. package/dist/element/index.d.ts +1 -1
  99. package/dist/element/index.js +2 -2
  100. package/dist/element/spectrum-element.d.ts +58 -15
  101. package/dist/element/spectrum-element.js +55 -36
  102. package/dist/element/spectrum-element.js.map +1 -1
  103. package/dist/element/version.d.ts +2 -2
  104. package/dist/element/version.js +1 -1
  105. package/dist/element/version.js.map +1 -1
  106. package/dist/mixins/index.d.ts +0 -2
  107. package/dist/mixins/index.js +4 -6
  108. package/dist/mixins/linear-progress-mixin.d.ts +1 -2
  109. package/dist/mixins/linear-progress-mixin.js +35 -34
  110. package/dist/mixins/linear-progress-mixin.js.map +1 -1
  111. package/dist/mixins/pending-mixin.js +12 -11
  112. package/dist/mixins/pending-mixin.js.map +1 -1
  113. package/dist/mixins/sized-mixin.d.ts +28 -0
  114. package/dist/mixins/sized-mixin.js +6 -6
  115. package/dist/mixins/sized-mixin.js.map +1 -1
  116. package/dist/utils/dev-validation.d.ts +88 -0
  117. package/dist/utils/dev-validation.js +33 -0
  118. package/dist/utils/dev-validation.js.map +1 -0
  119. package/dist/utils/index.d.ts +2 -0
  120. package/dist/utils/index.js +12 -10
  121. package/dist/utils/is-focus-visible-in-tree.d.ts +33 -0
  122. package/dist/utils/is-focus-visible-in-tree.js +11 -0
  123. package/dist/utils/is-focus-visible-in-tree.js.map +1 -0
  124. package/package.json +68 -15
  125. package/dist/_virtual/_@oxc-project_runtime@0.124.0/helpers/defineProperty.js +0 -12
  126. package/dist/_virtual/_@oxc-project_runtime@0.124.0/helpers/toPrimitive.js +0 -14
  127. package/dist/_virtual/_@oxc-project_runtime@0.124.0/helpers/toPropertyKey.js +0 -9
  128. package/dist/_virtual/_@oxc-project_runtime@0.124.0/helpers/typeof.js +0 -11
  129. package/dist/controllers/page-scroll-lock.js.map +0 -1
  130. package/dist/mixins/observe-slot-presence.d.ts +0 -12
  131. package/dist/mixins/observe-slot-presence.js +0 -40
  132. package/dist/mixins/observe-slot-presence.js.map +0 -1
  133. package/dist/mixins/observe-slot-text.d.ts +0 -11
  134. package/dist/mixins/observe-slot-text.js +0 -60
  135. package/dist/mixins/observe-slot-text.js.map +0 -1
  136. /package/dist/controllers/{page-scroll-lock.d.ts → page-scroll-lock-controller/src/page-scroll-lock.d.ts} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"Tabs.base.js","names":[],"sources":["../../../components/tabs/Tabs.base.ts"],"sourcesContent":["/**\n * Copyright 2026 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\nimport { PropertyValues } from 'lit';\nimport { property, state } from 'lit/decorators.js';\n\nimport {\n focusgroupNavigationActiveChange,\n type FocusgroupNavigationActiveChangeDetail,\n FocusgroupNavigationController,\n} from '@adobe/spectrum-wc-core/controllers/index.js';\nimport { SpectrumElement } from '@adobe/spectrum-wc-core/element/index.js';\n\nimport {\n KEYBOARD_ACTIVATION_DEFAULT,\n KEYBOARD_ACTIVATIONS,\n type KeyboardActivation,\n TAB_DENSITIES,\n TAB_DENSITY_DEFAULT,\n type TabDensity,\n type TabLike,\n type TabPanelLike,\n TABS_DEFAULT_DIRECTION,\n TABS_DIRECTIONS,\n type TabsDirection,\n} from './Tabs.types.js';\n\n/**\n * Base class for a tabbed interface container.\n *\n * Manages selection state, tab/panel association, ARIA wiring,\n * keyboard navigation, and event dispatching. Concrete classes\n * supply the stylesheet, render template, and visual behaviors\n * such as the selection indicator.\n *\n * Public API follows the Spectrum 2 / Spectrum Design–aligned surface\n * from the tabs migration plan: `keyboard-activation` and `density`\n * instead of legacy `auto`, `compact`, `quiet`, `emphasized`, and\n * t-shirt `size` attributes.\n *\n * @slot - Tab items (elements with `role=\"tab\"`)\n * @slot tab-panel - Tab panel content (elements with `role=\"tabpanel\"`)\n *\n * @fires change - The selected tab has changed. Cancelable —\n * calling `preventDefault()` reverts the selection.\n */\nexport abstract class TabsBase extends SpectrumElement {\n // ─────────────────────────\n // SHARED API\n // ─────────────────────────\n\n /**\n * @internal\n *\n * Valid direction values for validation and CEM/stories.\n */\n static readonly VALID_DIRECTIONS: readonly TabsDirection[] = TABS_DIRECTIONS;\n\n /**\n * @internal\n */\n static readonly VALID_KEYBOARD_ACTIVATIONS: readonly KeyboardActivation[] =\n KEYBOARD_ACTIVATIONS;\n\n /**\n * @internal\n */\n static readonly VALID_DENSITIES: readonly TabDensity[] = TAB_DENSITIES;\n\n /**\n * Whether selection follows keyboard focus (`automatic`, default) or the\n * user must press Enter or Space to activate (`manual`). Prefer `manual`\n * when tab panels are expensive to render or not fully present in the DOM.\n *\n * @see https://w3c.github.io/aria-practices/#kbd_selection_follows_focus\n */\n @property({ type: String, reflect: true, attribute: 'keyboard-activation' })\n public get keyboardActivation(): KeyboardActivation {\n return this._keyboardActivation;\n }\n\n public set keyboardActivation(value: string) {\n const isValid = (KEYBOARD_ACTIVATIONS as readonly string[]).includes(value);\n\n if (!isValid && window.__swc?.DEBUG) {\n window.__swc.warn(\n this,\n `<${this.localName}> expects \"keyboard-activation\" to be one of:`,\n 'https://opensource.adobe.com/spectrum-web-components/components/tabs/',\n { issues: [...KEYBOARD_ACTIVATIONS] }\n );\n }\n\n const valid = isValid\n ? (value as KeyboardActivation)\n : KEYBOARD_ACTIVATION_DEFAULT;\n\n if (this._keyboardActivation === valid) {\n return;\n }\n\n const old = this._keyboardActivation;\n this._keyboardActivation = valid;\n this.requestUpdate('keyboardActivation', old);\n }\n\n /** @internal */\n private _keyboardActivation: KeyboardActivation = KEYBOARD_ACTIVATION_DEFAULT;\n\n /**\n * Layout density: `regular` (default) or `compact` (reduced tab spacing).\n */\n @property({ type: String, reflect: true })\n public get density(): TabDensity {\n return this._density;\n }\n\n public set density(value: string) {\n const isValid = (TAB_DENSITIES as readonly string[]).includes(value);\n\n if (!isValid && window.__swc?.DEBUG) {\n window.__swc.warn(\n this,\n `<${this.localName}> expects \"density\" to be one of:`,\n 'https://opensource.adobe.com/spectrum-web-components/components/tabs/',\n { issues: [...TAB_DENSITIES] }\n );\n }\n\n const valid = isValid ? (value as TabDensity) : TAB_DENSITY_DEFAULT;\n\n if (this._density === valid) {\n return;\n }\n\n const old = this._density;\n this._density = valid;\n this.requestUpdate('density', old);\n }\n\n /** @internal */\n private _density: TabDensity = TAB_DENSITY_DEFAULT;\n\n /**\n * The layout direction of the tab list: `horizontal` (default) or `vertical`.\n *\n * @default 'horizontal'\n */\n @property({ type: String, reflect: true })\n public get direction(): TabsDirection {\n return this._direction;\n }\n\n public set direction(value: TabsDirection) {\n const isValid = (TABS_DIRECTIONS as readonly string[]).includes(value);\n\n if (!isValid && window.__swc?.DEBUG) {\n window.__swc.warn(\n this,\n `<${this.localName}> expects the \"direction\" attribute to be one of the following:`,\n 'https://opensource.adobe.com/spectrum-web-components/components/tabs/',\n {\n issues: [...TABS_DIRECTIONS],\n }\n );\n }\n\n const validDirection = isValid\n ? (value as TabsDirection)\n : TABS_DEFAULT_DIRECTION;\n\n if (this._direction === validDirection) {\n return;\n }\n\n const oldDirection = this._direction;\n this._direction = validDirection;\n this.requestUpdate('direction', oldDirection);\n }\n\n /** @internal */\n private _direction: TabsDirection = TABS_DEFAULT_DIRECTION;\n\n /**\n * Whether the entire tab list is disabled. When `true`,\n * `aria-disabled=\"true\"` is applied to the tablist element and\n * all interaction is suppressed.\n */\n @property({ type: Boolean, reflect: true })\n public disabled = false;\n\n /**\n * Accessible label for the tablist. Rendered as `aria-label` on the\n * element with `role=\"tablist\"` in the concrete template.\n */\n @property({ type: String, attribute: 'accessible-label' })\n public accessibleLabel = '';\n\n /**\n * The `tab-id` of the currently selected tab. Setting this property\n * updates which tab appears selected and which panel is visible.\n */\n @property({ type: String, reflect: true })\n public selected = '';\n\n // ──────────────────────\n // IMPLEMENTATION\n // ──────────────────────\n\n /** @internal */\n private static readonly INDICATOR_BASE_SIZE = 100;\n\n /**\n * @internal\n *\n * Inline style applied to the selection indicator element.\n * Computed from the selected tab's position and dimensions.\n */\n @state()\n protected selectionIndicatorStyle = '';\n\n /**\n * @internal\n *\n * Suppresses the transition on the very first indicator placement\n * so it doesn't animate from the origin.\n */\n @state()\n protected shouldAnimate = false;\n\n /**\n * @internal\n *\n * Cached list of tab elements managed by this container. Updated\n * via `handleTabSlotChange`.\n */\n private _tabs: TabLike[] = [];\n\n /**\n * @internal\n *\n * Manages roving tabindex and arrow-key / Home / End focus movement within\n * the tab list. Direction is kept in sync with `this.direction` via\n * `setOptions` in `willUpdate`. Disabled tabs remain in the navigation\n * sequence (per APG) but are not activatable.\n *\n * `getItems` returns an empty array when the container is disabled so all\n * tabs lose their tab stop, matching the `aria-disabled` tablist behavior.\n */\n private readonly _navigation = new FocusgroupNavigationController(this, {\n direction: this._direction,\n wrap: true,\n memory: true,\n getItems: () => (this.disabled ? [] : (this._tabs as HTMLElement[])),\n });\n\n /**\n * Handles `focusgroupNavigationActiveChange` events dispatched by\n * `_navigation`. In automatic activation mode, selects the newly focused\n * tab (unless it is disabled).\n *\n * Only `source: 'keyboard'` (arrow keys, Home/End) and `source: 'focus'`\n * (pointer click, Tab-key entry) represent a real focus move that\n * \"selection follows focus\" should react to. `'refresh'` and\n * `'programmatic'` fire when `_navigation` re-parks the roving tab stop\n * without anyone actually moving focus there — for example on mount or when\n * re-enabling after `disabled` — and must not trigger a spurious selection\n * or `change` event.\n */\n private readonly _handleNavigationActiveChange = (event: Event): void => {\n if (this._keyboardActivation !== 'automatic') {\n return;\n }\n const { activeElement, source } = (\n event as CustomEvent<FocusgroupNavigationActiveChangeDetail>\n ).detail;\n if (source !== 'keyboard' && source !== 'focus') {\n return;\n }\n if (!activeElement) {\n return;\n }\n const tab = activeElement as TabLike;\n if (tab.disabled) {\n return;\n }\n this.selectTarget(tab);\n };\n\n /**\n * Whether an assigned node is treated as a tab. `role=\"tab\"` is set in\n * each tab's `firstUpdated`, so `slotchange` may run before that — accept\n * known tab host tag names so the tab list and selection indicator sync.\n */\n private static isTabSlotNode(el: Element): el is TabLike {\n if (!(el instanceof HTMLElement)) {\n return false;\n }\n if (el.getAttribute('role') === 'tab') {\n return true;\n }\n return el.localName === 'swc-tab';\n }\n\n /**\n * Called by the concrete class when the default slot's content\n * changes. Rebuilds the internal tab list and syncs selection\n * state.\n */\n protected handleTabSlotChange(event: Event): void {\n const slot = event.target as HTMLSlotElement;\n this._tabs = slot\n .assignedElements()\n .filter(TabsBase.isTabSlotNode) as TabLike[];\n\n this._navigation.refresh();\n this.updateCheckedState();\n this.updateSelectionIndicator();\n }\n\n /**\n * Called by the concrete class when the `tab-panel` slot's content\n * changes. Wires up `aria-controls` / `aria-labelledby` between\n * each tab and its panel.\n */\n protected handlePanelSlotChange(event: Event): void {\n const slot = event.target as HTMLSlotElement;\n const panels = slot.assignedElements() as TabPanelLike[];\n this.managePanels(panels);\n }\n\n /**\n * Click handler bound to the tablist wrapper in the concrete\n * template. Activates the clicked tab.\n */\n protected handleClick(event: Event): void {\n if (this.disabled) {\n return;\n }\n\n const target = event\n .composedPath()\n .find((el) => (el as TabLike).parentElement === this) as\n | TabLike\n | undefined;\n\n if (!target || target.disabled) {\n return;\n }\n\n this.selectTarget(target);\n }\n\n /**\n * Keyboard handler for Enter and Space activation. Arrow-key, Home, and\n * End navigation is handled by `FocusgroupNavigationController` in capture\n * phase on the host element.\n */\n protected handleKeyDown(event: KeyboardEvent): void {\n if (this.disabled) {\n return;\n }\n\n const { code } = event;\n if (code === 'Enter' || code === 'Space') {\n event.preventDefault();\n const target = event.target as TabLike | null;\n if (target && !target.disabled) {\n this.selectTarget(target);\n }\n }\n }\n\n /**\n * Attempts to select the given tab element. Dispatches a cancelable\n * `change` event — if the consumer calls `preventDefault()`, the\n * selection reverts to the previous value.\n */\n private selectTarget(target: TabLike): void {\n const id = target.tabId;\n if (!id || id === this.selected) {\n return;\n }\n\n const previous = this.selected;\n this.selected = id;\n\n // Sync child tab.selected eagerly so that change-event handlers read the\n // correct state on individual tab elements without waiting for the next\n // Lit render cycle (tabs.selected is already updated synchronously above).\n this.updateCheckedState();\n\n const applyDefault = this.dispatchEvent(\n new Event('change', {\n cancelable: true,\n })\n );\n\n if (!applyDefault) {\n this.selected = previous;\n }\n }\n\n /**\n * Synchronizes the `selected` property on each child tab to match\n * `this.selected`, then parks the roving tab stop on the selected tab via\n * `_navigation.setActiveItem`. Tabindex management is otherwise owned by\n * `FocusgroupNavigationController` — for example, `_navigation.refresh()`\n * resets all tabindex values when the tab list or `disabled` state changes.\n */\n private updateCheckedState(): void {\n for (const tab of this._tabs) {\n tab.selected = false;\n }\n\n if (this.selected) {\n const currentTab = this._tabs.find((el) => el.tabId === this.selected);\n\n if (currentTab) {\n currentTab.selected = true;\n this._navigation.setActiveItem(currentTab as HTMLElement);\n } else {\n this.selected = '';\n }\n }\n }\n\n /**\n * Wires up cross-element ARIA relationships between tabs and\n * panels. Each tab gets `aria-controls` pointing at its panel's\n * `id`, and each panel gets `aria-labelledby` pointing at its\n * tab's `id`.\n */\n private managePanels(panels: TabPanelLike[]): void {\n for (const panel of panels) {\n const { tabId, id } = panel;\n const tab = this.querySelector(`[role=\"tab\"][tab-id=\"${tabId}\"]`);\n\n if (tab) {\n tab.setAttribute('aria-controls', id);\n panel.setAttribute('aria-labelledby', tab.id);\n }\n\n panel.selected = tabId === this.selected;\n }\n }\n\n // ───────────────────────────────────\n // SELECTION INDICATOR\n // ───────────────────────────────────\n\n /**\n * @internal\n *\n * Recalculates the selection indicator's position and size based\n * on the currently selected tab element. Uses CSS transforms for\n * smooth animation between tab positions.\n *\n * The indicator is a fixed-size element (100px base) that gets\n * `scaleX`/`scaleY` to match the selected tab's width/height,\n * and `translateX`/`translateY` to match its offset position.\n */\n protected updateSelectionIndicator = async (): Promise<void> => {\n const selectedElement = this._tabs.find((el) => el.selected);\n if (!selectedElement) {\n this.selectionIndicatorStyle =\n 'transform: translateX(0px) scaleX(0) scaleY(0)';\n return;\n }\n\n const tablist = this.renderRoot?.querySelector('.tablist');\n if (!tablist) {\n return;\n }\n\n await Promise.all([\n (selectedElement as HTMLElement & { updateComplete?: Promise<boolean> })\n .updateComplete ?? Promise.resolve(),\n document.fonts ? document.fonts.ready : Promise.resolve(),\n ]);\n\n const tabRect = selectedElement.getBoundingClientRect();\n const listRect = tablist.getBoundingClientRect();\n\n if (this._direction === 'horizontal') {\n const isRtl = getComputedStyle(this).direction === 'rtl';\n const offset = isRtl\n ? tabRect.right - listRect.right\n : tabRect.left - listRect.left;\n const scale = tabRect.width / TabsBase.INDICATOR_BASE_SIZE;\n this.selectionIndicatorStyle = `transform: translateX(${offset}px) scaleX(${scale})`;\n } else {\n const top = tabRect.top - listRect.top;\n const scale = tabRect.height / TabsBase.INDICATOR_BASE_SIZE;\n this.selectionIndicatorStyle = `transform: translateY(${top}px) scaleY(${scale})`;\n }\n\n if (!this.shouldAnimate) {\n await this.updateComplete;\n this.shouldAnimate = true;\n }\n };\n\n /** @internal */\n private _resizeObserver?: ResizeObserver;\n\n /**\n * @internal\n *\n * Watches for `dir` attribute changes so the indicator recalculates\n * when writing direction flips at runtime. `getComputedStyle` in\n * `updateSelectionIndicator` resolves inherited direction correctly,\n * but nothing else observes it: a `dir` flip on the document root or\n * an ancestor doesn't resize this element, so the `ResizeObserver`\n * below won't catch it. Mirrors the `dir`-watching pattern in\n * `placement-controller.ts`.\n */\n private _directionObserver?: MutationObserver;\n\n // ───────────────────────────────────\n // LIFECYCLE\n // ───────────────────────────────────\n\n protected override willUpdate(changes: PropertyValues): void {\n if (!this.hasUpdated) {\n const selectedChild = this.querySelector(\n ':scope > [selected]'\n ) as TabLike | null;\n\n if (selectedChild) {\n this.selectTarget(selectedChild);\n }\n }\n\n super.willUpdate(changes);\n\n if (changes.has('selected')) {\n if (this._tabs.length) {\n this.updateCheckedState();\n }\n\n const previousValue = changes.get('selected') as string | undefined;\n if (previousValue) {\n const previous = this.querySelector(\n `[role=\"tabpanel\"][tab-id=\"${previousValue}\"]`\n ) as TabPanelLike | null;\n\n if (previous) {\n previous.selected = false;\n }\n }\n\n const next = this.querySelector(\n `[role=\"tabpanel\"][tab-id=\"${this.selected}\"]`\n ) as TabPanelLike | null;\n\n if (next) {\n next.selected = true;\n }\n\n this.updateSelectionIndicator();\n }\n\n if (changes.has('disabled') && this._tabs.length) {\n this._navigation.refresh();\n if (this.disabled) {\n // When disabled, getItems() returns [] so the controller's cleanup\n // loop in refresh() iterates nothing. Explicitly remove all tabs from\n // the tab order so the tablist is fully inert.\n for (const tab of this._tabs) {\n (tab as HTMLElement).tabIndex = -1;\n }\n } else {\n // After re-enabling, restore the roving tab stop to the selected tab.\n this.updateCheckedState();\n }\n }\n\n if (changes.has('direction')) {\n this._navigation.setOptions({ direction: this.direction });\n this.updateSelectionIndicator();\n }\n }\n\n public override connectedCallback(): void {\n super.connectedCallback();\n this.addEventListener(\n focusgroupNavigationActiveChange,\n this._handleNavigationActiveChange\n );\n window.addEventListener('resize', this.updateSelectionIndicator);\n if ('fonts' in document) {\n document.fonts.addEventListener(\n 'loadingdone',\n this.updateSelectionIndicator\n );\n }\n this._resizeObserver = new ResizeObserver(() => {\n this.updateSelectionIndicator();\n });\n this._resizeObserver.observe(this);\n\n this._directionObserver = new MutationObserver(() => {\n this.updateSelectionIndicator();\n });\n const observeDir = (node: Element | null | undefined) => {\n if (node) {\n this._directionObserver?.observe(node, {\n attributes: true,\n attributeFilter: ['dir'],\n });\n }\n };\n observeDir(this.ownerDocument.documentElement);\n observeDir(this.closest('[dir]'));\n }\n\n public override disconnectedCallback(): void {\n this.removeEventListener(\n focusgroupNavigationActiveChange,\n this._handleNavigationActiveChange\n );\n window.removeEventListener('resize', this.updateSelectionIndicator);\n if ('fonts' in document) {\n document.fonts.removeEventListener(\n 'loadingdone',\n this.updateSelectionIndicator\n );\n }\n this._resizeObserver?.disconnect();\n this._resizeObserver = undefined;\n this._directionObserver?.disconnect();\n this._directionObserver = undefined;\n super.disconnectedCallback();\n }\n\n /**\n * Focuses the selected tab, or the first tab when none is selected yet.\n * Slotted tabs live in the light DOM; this is more reliable than relying\n * only on shadow `delegatesFocus` across browsers and test harnesses.\n */\n public override focus(options?: FocusOptions): void {\n if (this.disabled) {\n return;\n }\n\n const selectedTab = this._tabs.find((tab) => tab.selected);\n if (selectedTab) {\n (selectedTab as HTMLElement).focus(options);\n return;\n }\n\n if (this._tabs.length) {\n (this._tabs[0] as HTMLElement).focus(options);\n return;\n }\n\n super.focus(options);\n }\n\n protected override firstUpdated(changes: PropertyValues): void {\n super.firstUpdated(changes);\n\n if (!this.hasAttribute('direction')) {\n this.setAttribute('direction', this.direction);\n }\n\n if (window.__swc?.DEBUG && !this.accessibleLabel) {\n window.__swc.warn(\n this,\n `<${this.localName}> requires an \"accessible-label\" attribute to provide an accessible name for the tablist.`,\n 'https://opensource.adobe.com/spectrum-web-components/components/tabs/',\n { type: 'accessibility', level: 'high' }\n );\n }\n }\n\n /**\n * Waits for all child tab elements to finish their update cycle\n * before resolving so layout-dependent callers see stable geometry.\n */\n public override async getUpdateComplete(): Promise<boolean> {\n const complete = await super.getUpdateComplete();\n\n const children = [...this.children] as HTMLElement[];\n const childUpdates = children.map((child) => {\n if ('updateComplete' in child) {\n return (child as unknown as { updateComplete: Promise<boolean> })\n .updateComplete;\n }\n return Promise.resolve(true);\n });\n\n await Promise.all(childUpdates);\n return complete;\n }\n}\n"],"mappings":";;;;;;OAsDsB,IAAtB,MAAsB,UAAiB,EAAgB;;0CA6DH,mBAkCnB,qBAwCK,mBAQlB,2BAOO,oBAOP,mCAgBkB,yBASV,iBAQC,EAAE,qBAaE,IAAI,EAA+B,MAAM;GACtE,WAAW,KAAK;GAChB,MAAM;GACN,QAAQ;GACR,gBAAiB,KAAK,WAAW,EAAE,GAAI,KAAK;GAC7C,CAAC,wCAegD,MAAuB;AACvE,OAAI,KAAK,wBAAwB,YAC/B;GAEF,IAAM,EAAE,kBAAe,cACrB,EACA;AAIF,OAHI,MAAW,cAAc,MAAW,WAGpC,CAAC,EACH;GAEF,IAAM,IAAM;AACR,KAAI,YAGR,KAAK,aAAa,EAAI;qCAgLa,YAA2B;;GAC9D,IAAM,IAAkB,KAAK,MAAM,MAAM,MAAO,EAAG,SAAS;AAC5D,OAAI,CAAC,GAAiB;AACpB,SAAK,0BACH;AACF;;GAGF,IAAM,KAAA,IAAU,KAAK,eAAA,OAAA,KAAA,IAAA,EAAY,cAAc,WAAW;AAC1D,OAAI,CAAC,EACH;AAGF,SAAM,QAAQ,IAAI,EAAA,IACf,EACE,mBAAA,OAAkB,QAAQ,SAAS,GAAnC,GACH,SAAS,QAAQ,SAAS,MAAM,QAAQ,QAAQ,SAAS,CAC1D,CAAC;GAEF,IAAM,IAAU,EAAgB,uBAAuB,EACjD,IAAW,EAAQ,uBAAuB;AAehD,GAbI,KAAK,eAAe,eAMtB,KAAK,0BAA0B,yBALjB,iBAAiB,KAAK,CAAC,cAAc,QAE/C,EAAQ,QAAQ,EAAS,QACzB,EAAQ,OAAO,EAAS,KAEmC,aADjD,EAAQ,QAAQ,EAAS,oBAC2C,KAIlF,KAAK,0BAA0B,yBAFnB,EAAQ,MAAM,EAAS,IAEyB,aAD9C,EAAQ,SAAS,EAAS,oBACuC,IAG5E,KAAK,kBACR,MAAM,KAAK,gBACX,KAAK,gBAAgB;;;CAvazB,IACW,qBAAyC;AAClD,SAAO,KAAK;;CAGd,IAAW,mBAAmB,GAAe;;EAC3C,IAAM,IAAW,EAA2C,SAAS,EAAM;AAE3E,EAAI,CAAC,MAAA,IAAW,OAAO,UAAA,QAAA,EAAO,SAC5B,OAAO,MAAM,KACX,MACA,IAAI,KAAK,UAAU,gDACnB,yEACA,EAAE,QAAQ,CAAC,GAAG,EAAqB,EAAE,CACtC;EAGH,IAAM,IAAQ,IACT,IACD;AAEJ,MAAI,KAAK,wBAAwB,EAC/B;EAGF,IAAM,IAAM,KAAK;AAEjB,EADA,KAAK,sBAAsB,GAC3B,KAAK,cAAc,sBAAsB,EAAI;;CAS/C,IACW,UAAsB;AAC/B,SAAO,KAAK;;CAGd,IAAW,QAAQ,GAAe;;EAChC,IAAM,IAAW,EAAoC,SAAS,EAAM;AAEpE,EAAI,CAAC,MAAA,IAAW,OAAO,UAAA,QAAA,EAAO,SAC5B,OAAO,MAAM,KACX,MACA,IAAI,KAAK,UAAU,oCACnB,yEACA,EAAE,QAAQ,CAAC,GAAG,EAAc,EAAE,CAC/B;EAGH,IAAM,IAAQ,IAAW,IAAuB;AAEhD,MAAI,KAAK,aAAa,EACpB;EAGF,IAAM,IAAM,KAAK;AAEjB,EADA,KAAK,WAAW,GAChB,KAAK,cAAc,WAAW,EAAI;;CAWpC,IACW,YAA2B;AACpC,SAAO,KAAK;;CAGd,IAAW,UAAU,GAAsB;;EACzC,IAAM,IAAW,EAAsC,SAAS,EAAM;AAEtE,EAAI,CAAC,MAAA,IAAW,OAAO,UAAA,QAAA,EAAO,SAC5B,OAAO,MAAM,KACX,MACA,IAAI,KAAK,UAAU,kEACnB,yEACA,EACE,QAAQ,CAAC,GAAG,EAAgB,EAC7B,CACF;EAGH,IAAM,IAAiB,IAClB,IACD;AAEJ,MAAI,KAAK,eAAe,EACtB;EAGF,IAAM,IAAe,KAAK;AAE1B,EADA,KAAK,aAAa,GAClB,KAAK,cAAc,aAAa,EAAa;;CAqH/C,OAAe,cAAc,GAA4B;AAOvD,SANM,aAAc,cAGhB,EAAG,aAAa,OAAO,KAAK,QACvB,KAEF,EAAG,cAAc,YALf;;CAaX,oBAA8B,GAAoB;AAQhD,EANA,KAAK,QADQ,EAAM,OAEhB,kBAAkB,CAClB,OAAO,EAAS,cAAc,EAEjC,KAAK,YAAY,SAAS,EAC1B,KAAK,oBAAoB,EACzB,KAAK,0BAA0B;;CAQjC,sBAAgC,GAAoB;EAElD,IAAM,IADO,EAAM,OACC,kBAAkB;AACtC,OAAK,aAAa,EAAO;;CAO3B,YAAsB,GAAoB;AACxC,MAAI,KAAK,SACP;EAGF,IAAM,IAAS,EACZ,cAAc,CACd,MAAM,MAAQ,EAAe,kBAAkB,KAAK;AAInD,GAAC,KAAU,EAAO,YAItB,KAAK,aAAa,EAAO;;CAQ3B,cAAwB,GAA4B;AAClD,MAAI,KAAK,SACP;EAGF,IAAM,EAAE,YAAS;AACjB,MAAI,MAAS,WAAW,MAAS,SAAS;AACxC,KAAM,gBAAgB;GACtB,IAAM,IAAS,EAAM;AACrB,GAAI,KAAU,CAAC,EAAO,YACpB,KAAK,aAAa,EAAO;;;CAU/B,aAAqB,GAAuB;EAC1C,IAAM,IAAK,EAAO;AAClB,MAAI,CAAC,KAAM,MAAO,KAAK,SACrB;EAGF,IAAM,IAAW,KAAK;AActB,EAbA,KAAK,WAAW,GAKhB,KAAK,oBAAoB,EAEJ,KAAK,cACxB,IAAI,MAAM,UAAU,EAClB,YAAY,IACb,CAAC,CACH,KAGC,KAAK,WAAW;;CAWpB,qBAAmC;AACjC,OAAK,IAAM,KAAO,KAAK,MACrB,GAAI,WAAW;AAGjB,MAAI,KAAK,UAAU;GACjB,IAAM,IAAa,KAAK,MAAM,MAAM,MAAO,EAAG,UAAU,KAAK,SAAS;AAEtE,GAAI,KACF,EAAW,WAAW,IACtB,KAAK,YAAY,cAAc,EAA0B,IAEzD,KAAK,WAAW;;;CAWtB,aAAqB,GAA8B;AACjD,OAAK,IAAM,KAAS,GAAQ;GAC1B,IAAM,EAAE,UAAO,UAAO,GAChB,IAAM,KAAK,cAAc,wBAAwB,EAAM,IAAI;AAOjE,GALI,MACF,EAAI,aAAa,iBAAiB,EAAG,EACrC,EAAM,aAAa,mBAAmB,EAAI,GAAG,GAG/C,EAAM,WAAW,MAAU,KAAK;;;CAgFpC,WAA8B,GAA+B;AAC3D,MAAI,CAAC,KAAK,YAAY;GACpB,IAAM,IAAgB,KAAK,cACzB,sBACD;AAED,GAAI,KACF,KAAK,aAAa,EAAc;;AAMpC,MAFA,MAAM,WAAW,EAAQ,EAErB,EAAQ,IAAI,WAAW,EAAE;AAC3B,GAAI,KAAK,MAAM,UACb,KAAK,oBAAoB;GAG3B,IAAM,IAAgB,EAAQ,IAAI,WAAW;AAC7C,OAAI,GAAe;IACjB,IAAM,IAAW,KAAK,cACpB,6BAA6B,EAAc,IAC5C;AAED,IAAI,MACF,EAAS,WAAW;;GAIxB,IAAM,IAAO,KAAK,cAChB,6BAA6B,KAAK,SAAS,IAC5C;AAMD,GAJI,MACF,EAAK,WAAW,KAGlB,KAAK,0BAA0B;;AAGjC,MAAI,EAAQ,IAAI,WAAW,IAAI,KAAK,MAAM,OAExC,KADA,KAAK,YAAY,SAAS,EACtB,KAAK,SAIP,MAAK,IAAM,KAAO,KAAK,MACpB,GAAoB,WAAW;MAIlC,MAAK,oBAAoB;AAI7B,EAAI,EAAQ,IAAI,YAAY,KAC1B,KAAK,YAAY,WAAW,EAAE,WAAW,KAAK,WAAW,CAAC,EAC1D,KAAK,0BAA0B;;CAInC,oBAA0C;AAkBxC,EAjBA,MAAM,mBAAmB,EACzB,KAAK,iBACH,GACA,KAAK,8BACN,EACD,OAAO,iBAAiB,UAAU,KAAK,yBAAyB,EAC5D,WAAW,YACb,SAAS,MAAM,iBACb,eACA,KAAK,yBACN,EAEH,KAAK,kBAAkB,IAAI,qBAAqB;AAC9C,QAAK,0BAA0B;IAC/B,EACF,KAAK,gBAAgB,QAAQ,KAAK,EAElC,KAAK,qBAAqB,IAAI,uBAAuB;AACnD,QAAK,0BAA0B;IAC/B;EACF,IAAM,KAAc,MAAqC;AACvD,OAAI,GAAM;;AACR,KAAA,IAAA,KAAK,uBAAA,QAAA,EAAoB,QAAQ,GAAM;KACrC,YAAY;KACZ,iBAAiB,CAAC,MAAM;KACzB,CAAC;;;AAIN,EADA,EAAW,KAAK,cAAc,gBAAgB,EAC9C,EAAW,KAAK,QAAQ,QAAQ,CAAC;;CAGnC,uBAA6C;;AAgB3C,EAfA,KAAK,oBACH,GACA,KAAK,8BACN,EACD,OAAO,oBAAoB,UAAU,KAAK,yBAAyB,EAC/D,WAAW,YACb,SAAS,MAAM,oBACb,eACA,KAAK,yBACN,GAEH,IAAA,KAAK,oBAAA,QAAA,EAAiB,YAAY,EAClC,KAAK,kBAAkB,KAAA,IACvB,IAAA,KAAK,uBAAA,QAAA,EAAoB,YAAY,EACrC,KAAK,qBAAqB,KAAA,GAC1B,MAAM,sBAAsB;;CAQ9B,MAAsB,GAA8B;AAClD,MAAI,KAAK,SACP;EAGF,IAAM,IAAc,KAAK,MAAM,MAAM,MAAQ,EAAI,SAAS;AAC1D,MAAI,GAAa;AACd,KAA4B,MAAM,EAAQ;AAC3C;;AAGF,MAAI,KAAK,MAAM,QAAQ;AACpB,QAAK,MAAM,GAAmB,MAAM,EAAQ;AAC7C;;AAGF,QAAM,MAAM,EAAQ;;CAGtB,aAAgC,GAA+B;;AAO7D,EANA,MAAM,aAAa,EAAQ,EAEtB,KAAK,aAAa,YAAY,IACjC,KAAK,aAAa,aAAa,KAAK,UAAU,GAGhD,IAAI,OAAO,UAAA,QAAA,EAAO,SAAS,CAAC,KAAK,mBAC/B,OAAO,MAAM,KACX,MACA,IAAI,KAAK,UAAU,4FACnB,yEACA;GAAE,MAAM;GAAiB,OAAO;GAAQ,CACzC;;CAQL,MAAsB,oBAAsC;EAC1D,IAAM,IAAW,MAAM,MAAM,mBAAmB,EAG1C,IADW,CAAC,GAAG,KAAK,SAAS,CACL,KAAK,MAC7B,oBAAoB,IACd,EACL,iBAEE,QAAQ,QAAQ,GAAK,CAC5B;AAGF,SADA,MAAM,QAAQ,IAAI,EAAa,EACxB;;;4BA9nBoD,kCAM3D,uBAKuD,2BA+IX,QAtI7C,EAAS;CAAE,MAAM;CAAQ,SAAS;CAAM,WAAW;CAAuB,CAAC,CAAA,EAAA,EAAA,WAAA,sBAAA,KAAA,KAoC3E,EAAS;CAAE,MAAM;CAAQ,SAAS;CAAM,CAAC,CAAA,EAAA,EAAA,WAAA,WAAA,KAAA,KAoCzC,EAAS;CAAE,MAAM;CAAQ,SAAS;CAAM,CAAC,CAAA,EAAA,EAAA,WAAA,aAAA,KAAA,KAwCzC,EAAS;CAAE,MAAM;CAAS,SAAS;CAAM,CAAC,CAAA,EAAA,EAAA,WAAA,YAAA,KAAA,EAAA,KAO1C,EAAS;CAAE,MAAM;CAAQ,WAAW;CAAoB,CAAC,CAAA,EAAA,EAAA,WAAA,mBAAA,KAAA,EAAA,KAOzD,EAAS;CAAE,MAAM;CAAQ,SAAS;CAAM,CAAC,CAAA,EAAA,EAAA,WAAA,YAAA,KAAA,EAAA,KAgBzC,GAAO,CAAA,EAAA,EAAA,WAAA,2BAAA,KAAA,EAAA,KASP,GAAO,CAAA,EAAA,EAAA,WAAA,iBAAA,KAAA,EAAA"}
1
+ {"version":3,"file":"Tabs.base.js","names":[],"sources":["../../../components/tabs/Tabs.base.ts"],"sourcesContent":["/**\n * Copyright 2026 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\nimport { PropertyValues } from 'lit';\nimport { property, state } from 'lit/decorators.js';\n\nimport {\n focusgroupNavigationActiveChange,\n type FocusgroupNavigationActiveChangeDetail,\n FocusgroupNavigationController,\n} from '@adobe/spectrum-wc-core/controllers/index.js';\nimport { SpectrumElement } from '@adobe/spectrum-wc-core/element/index.js';\nimport { validateEnum, warnIf } from '@adobe/spectrum-wc-core/utils/index.js';\n\nimport {\n KEYBOARD_ACTIVATION_DEFAULT,\n KEYBOARD_ACTIVATIONS,\n type KeyboardActivation,\n TAB_DENSITIES,\n TAB_DENSITY_DEFAULT,\n type TabDensity,\n type TabLike,\n type TabPanelLike,\n TABS_DEFAULT_DIRECTION,\n TABS_DIRECTIONS,\n type TabsDirection,\n} from './Tabs.types.js';\n\n/**\n * Base class for a tabbed interface container.\n *\n * Manages selection state, tab/panel association, ARIA wiring,\n * keyboard navigation, and event dispatching. Concrete classes\n * supply the stylesheet, render template, and visual behaviors\n * such as the selection indicator.\n *\n * Public API follows the Spectrum 2 / Spectrum Design–aligned surface\n * from the tabs migration plan: `keyboard-activation` and `density`\n * instead of legacy `auto`, `compact`, `quiet`, `emphasized`, and\n * t-shirt `size` attributes.\n *\n * @slot - Tab items (elements with `role=\"tab\"`)\n * @slot tab-panel - Tab panel content (elements with `role=\"tabpanel\"`)\n *\n * @fires change - The selected tab has changed. Cancelable —\n * calling `preventDefault()` reverts the selection.\n */\nexport abstract class TabsBase extends SpectrumElement {\n // ─────────────────────────\n // SHARED API\n // ─────────────────────────\n\n /**\n * @internal\n *\n * Valid direction values for validation and CEM/stories.\n */\n static readonly VALID_DIRECTIONS: readonly TabsDirection[] = TABS_DIRECTIONS;\n\n /**\n * @internal\n */\n static readonly VALID_KEYBOARD_ACTIVATIONS: readonly KeyboardActivation[] =\n KEYBOARD_ACTIVATIONS;\n\n /**\n * @internal\n */\n static readonly VALID_DENSITIES: readonly TabDensity[] = TAB_DENSITIES;\n\n /**\n * Whether selection follows keyboard focus (`automatic`, default) or the\n * user must press Enter or Space to activate (`manual`). Prefer `manual`\n * when tab panels are expensive to render or not fully present in the DOM.\n *\n * @see https://w3c.github.io/aria-practices/#kbd_selection_follows_focus\n */\n @property({ type: String, reflect: true, attribute: 'keyboard-activation' })\n public get keyboardActivation(): KeyboardActivation {\n return this._keyboardActivation;\n }\n\n public set keyboardActivation(value: string) {\n const isValid = (KEYBOARD_ACTIVATIONS as readonly string[]).includes(value);\n\n validateEnum(this, {\n prop: 'keyboard-activation',\n value,\n valid: KEYBOARD_ACTIVATIONS,\n url: 'https://spectrum-web-components.adobe.com/?path=/docs/components-tabs--docs',\n });\n\n const valid = isValid\n ? (value as KeyboardActivation)\n : KEYBOARD_ACTIVATION_DEFAULT;\n\n if (this._keyboardActivation === valid) {\n return;\n }\n\n const old = this._keyboardActivation;\n this._keyboardActivation = valid;\n this.requestUpdate('keyboardActivation', old);\n }\n\n /** @internal */\n private _keyboardActivation: KeyboardActivation = KEYBOARD_ACTIVATION_DEFAULT;\n\n /**\n * Layout density: `regular` (default) or `compact` (reduced tab spacing).\n */\n @property({ type: String, reflect: true })\n public get density(): TabDensity {\n return this._density;\n }\n\n public set density(value: string) {\n const isValid = (TAB_DENSITIES as readonly string[]).includes(value);\n\n validateEnum(this, {\n prop: 'density',\n value,\n valid: TAB_DENSITIES,\n url: 'https://spectrum-web-components.adobe.com/?path=/docs/components-tabs--docs',\n });\n\n const valid = isValid ? (value as TabDensity) : TAB_DENSITY_DEFAULT;\n\n if (this._density === valid) {\n return;\n }\n\n const old = this._density;\n this._density = valid;\n this.requestUpdate('density', old);\n }\n\n /** @internal */\n private _density: TabDensity = TAB_DENSITY_DEFAULT;\n\n /**\n * The layout direction of the tab list: `horizontal` (default) or `vertical`.\n *\n * @default 'horizontal'\n */\n @property({ type: String, reflect: true })\n public get direction(): TabsDirection {\n return this._direction;\n }\n\n public set direction(value: TabsDirection) {\n const isValid = (TABS_DIRECTIONS as readonly string[]).includes(value);\n\n validateEnum(this, {\n prop: 'direction',\n value,\n valid: TABS_DIRECTIONS,\n url: 'https://spectrum-web-components.adobe.com/?path=/docs/components-tabs--docs',\n });\n\n const validDirection = isValid\n ? (value as TabsDirection)\n : TABS_DEFAULT_DIRECTION;\n\n if (this._direction === validDirection) {\n return;\n }\n\n const oldDirection = this._direction;\n this._direction = validDirection;\n this.requestUpdate('direction', oldDirection);\n }\n\n /** @internal */\n private _direction: TabsDirection = TABS_DEFAULT_DIRECTION;\n\n /**\n * Whether the entire tab list is disabled. When `true`,\n * `aria-disabled=\"true\"` is applied to the tablist element and\n * all interaction is suppressed.\n */\n @property({ type: Boolean, reflect: true })\n public disabled = false;\n\n /**\n * Accessible label for the tablist. Rendered as `aria-label` on the\n * element with `role=\"tablist\"` in the concrete template.\n */\n @property({ type: String, attribute: 'accessible-label' })\n public accessibleLabel = '';\n\n /**\n * The `tab-id` of the currently selected tab. Setting this property\n * updates which tab appears selected and which panel is visible.\n */\n @property({ type: String, reflect: true })\n public selected = '';\n\n // ──────────────────────\n // IMPLEMENTATION\n // ──────────────────────\n\n /** @internal */\n private static readonly INDICATOR_BASE_SIZE = 100;\n\n /**\n * @internal\n *\n * Inline style applied to the selection indicator element.\n * Computed from the selected tab's position and dimensions.\n */\n @state()\n protected selectionIndicatorStyle = '';\n\n /**\n * @internal\n *\n * Suppresses the transition on the very first indicator placement\n * so it doesn't animate from the origin.\n */\n @state()\n protected shouldAnimate = false;\n\n /**\n * @internal\n *\n * Cached list of tab elements managed by this container. Updated\n * via `handleTabSlotChange`.\n */\n private _tabs: TabLike[] = [];\n\n /**\n * @internal\n *\n * Manages roving tabindex and arrow-key / Home / End focus movement within\n * the tab list. Direction is kept in sync with `this.direction` via\n * `setOptions` in `willUpdate`. Disabled tabs remain in the navigation\n * sequence (per APG) but are not activatable.\n *\n * `getItems` returns an empty array when the container is disabled so all\n * tabs lose their tab stop, matching the `aria-disabled` tablist behavior.\n */\n private readonly _navigation = new FocusgroupNavigationController(this, {\n direction: this._direction,\n wrap: true,\n memory: true,\n getItems: () => (this.disabled ? [] : (this._tabs as HTMLElement[])),\n });\n\n /**\n * Handles `focusgroupNavigationActiveChange` events dispatched by\n * `_navigation`. In automatic activation mode, selects the newly focused\n * tab (unless it is disabled).\n *\n * Only `source: 'keyboard'` (arrow keys, Home/End) and `source: 'focus'`\n * (pointer click, Tab-key entry) represent a real focus move that\n * \"selection follows focus\" should react to. `'refresh'` and\n * `'programmatic'` fire when `_navigation` re-parks the roving tab stop\n * without anyone actually moving focus there — for example on mount or when\n * re-enabling after `disabled` — and must not trigger a spurious selection\n * or `change` event.\n */\n private readonly _handleNavigationActiveChange = (event: Event): void => {\n if (this._keyboardActivation !== 'automatic') {\n return;\n }\n const { activeElement, source } = (\n event as CustomEvent<FocusgroupNavigationActiveChangeDetail>\n ).detail;\n if (source !== 'keyboard' && source !== 'focus') {\n return;\n }\n if (!activeElement) {\n return;\n }\n const tab = activeElement as TabLike;\n if (tab.disabled) {\n return;\n }\n this.selectTarget(tab);\n };\n\n /**\n * Whether an assigned node is treated as a tab. `role=\"tab\"` is set in\n * each tab's `firstUpdated`, so `slotchange` may run before that — accept\n * known tab host tag names so the tab list and selection indicator sync.\n */\n private static isTabSlotNode(el: Element): el is TabLike {\n if (!(el instanceof HTMLElement)) {\n return false;\n }\n if (el.getAttribute('role') === 'tab') {\n return true;\n }\n return el.localName === 'swc-tab';\n }\n\n /**\n * Called by the concrete class when the default slot's content\n * changes. Rebuilds the internal tab list and syncs selection\n * state.\n */\n protected handleTabSlotChange(event: Event): void {\n const slot = event.target as HTMLSlotElement;\n this._tabs = slot\n .assignedElements()\n .filter(TabsBase.isTabSlotNode) as TabLike[];\n\n this._navigation.refresh();\n this.updateCheckedState();\n this.updateSelectionIndicator();\n }\n\n /**\n * Called by the concrete class when the `tab-panel` slot's content\n * changes. Wires up `aria-controls` / `aria-labelledby` between\n * each tab and its panel.\n */\n protected handlePanelSlotChange(event: Event): void {\n const slot = event.target as HTMLSlotElement;\n const panels = slot.assignedElements() as TabPanelLike[];\n this.managePanels(panels);\n }\n\n /**\n * Click handler bound to the tablist wrapper in the concrete\n * template. Activates the clicked tab.\n */\n protected handleClick(event: Event): void {\n if (this.disabled) {\n return;\n }\n\n const target = event\n .composedPath()\n .find((el) => (el as TabLike).parentElement === this) as\n | TabLike\n | undefined;\n\n if (!target || target.disabled) {\n return;\n }\n\n this.selectTarget(target);\n }\n\n /**\n * Keyboard handler for Enter and Space activation. Arrow-key, Home, and\n * End navigation is handled by `FocusgroupNavigationController` in capture\n * phase on the host element.\n */\n protected handleKeyDown(event: KeyboardEvent): void {\n if (this.disabled) {\n return;\n }\n\n const { code } = event;\n if (code === 'Enter' || code === 'Space') {\n event.preventDefault();\n const target = event.target as TabLike | null;\n if (target && !target.disabled) {\n this.selectTarget(target);\n }\n }\n }\n\n /**\n * Attempts to select the given tab element. Dispatches a cancelable\n * `change` event — if the consumer calls `preventDefault()`, the\n * selection reverts to the previous value.\n */\n private selectTarget(target: TabLike): void {\n const id = target.tabId;\n if (!id || id === this.selected) {\n return;\n }\n\n const previous = this.selected;\n this.selected = id;\n\n // Sync child tab.selected eagerly so that change-event handlers read the\n // correct state on individual tab elements without waiting for the next\n // Lit render cycle (tabs.selected is already updated synchronously above).\n this.updateCheckedState();\n\n const applyDefault = this.dispatchEvent(\n new Event('change', {\n cancelable: true,\n })\n );\n\n if (!applyDefault) {\n this.selected = previous;\n }\n }\n\n /**\n * Synchronizes the `selected` property on each child tab to match\n * `this.selected`, then parks the roving tab stop on the selected tab via\n * `_navigation.setActiveItem`. Tabindex management is otherwise owned by\n * `FocusgroupNavigationController` — for example, `_navigation.refresh()`\n * resets all tabindex values when the tab list or `disabled` state changes.\n */\n private updateCheckedState(): void {\n for (const tab of this._tabs) {\n tab.selected = false;\n }\n\n if (this.selected) {\n const currentTab = this._tabs.find((el) => el.tabId === this.selected);\n\n if (currentTab) {\n currentTab.selected = true;\n this._navigation.setActiveItem(currentTab as HTMLElement);\n } else {\n this.selected = '';\n }\n }\n }\n\n /**\n * Wires up cross-element ARIA relationships between tabs and\n * panels. Each tab gets `aria-controls` pointing at its panel's\n * `id`, and each panel gets `aria-labelledby` pointing at its\n * tab's `id`.\n */\n private managePanels(panels: TabPanelLike[]): void {\n for (const panel of panels) {\n const { tabId, id } = panel;\n const tab = this.querySelector(`[role=\"tab\"][tab-id=\"${tabId}\"]`);\n\n if (tab) {\n tab.setAttribute('aria-controls', id);\n panel.setAttribute('aria-labelledby', tab.id);\n }\n\n panel.selected = tabId === this.selected;\n }\n }\n\n // ───────────────────────────────────\n // SELECTION INDICATOR\n // ───────────────────────────────────\n\n /**\n * @internal\n *\n * Recalculates the selection indicator's position and size based\n * on the currently selected tab element. Uses CSS transforms for\n * smooth animation between tab positions.\n *\n * The indicator is a fixed-size element (100px base) that gets\n * `scaleX`/`scaleY` to match the selected tab's width/height,\n * and `translateX`/`translateY` to match its offset position.\n */\n protected updateSelectionIndicator = async (): Promise<void> => {\n const selectedElement = this._tabs.find((el) => el.selected);\n if (!selectedElement) {\n this.selectionIndicatorStyle =\n 'transform: translateX(0px) scaleX(0) scaleY(0)';\n return;\n }\n\n const tablist = this.renderRoot?.querySelector('.tablist');\n if (!tablist) {\n return;\n }\n\n await Promise.all([\n (selectedElement as HTMLElement & { updateComplete?: Promise<boolean> })\n .updateComplete ?? Promise.resolve(),\n document.fonts ? document.fonts.ready : Promise.resolve(),\n ]);\n\n const tabRect = selectedElement.getBoundingClientRect();\n const listRect = tablist.getBoundingClientRect();\n\n if (this._direction === 'horizontal') {\n const isRtl = getComputedStyle(this).direction === 'rtl';\n const offset = isRtl\n ? tabRect.right - listRect.right\n : tabRect.left - listRect.left;\n const scale = tabRect.width / TabsBase.INDICATOR_BASE_SIZE;\n this.selectionIndicatorStyle = `transform: translateX(${offset}px) scaleX(${scale})`;\n } else {\n const top = tabRect.top - listRect.top;\n const scale = tabRect.height / TabsBase.INDICATOR_BASE_SIZE;\n this.selectionIndicatorStyle = `transform: translateY(${top}px) scaleY(${scale})`;\n }\n\n if (!this.shouldAnimate) {\n await this.updateComplete;\n this.shouldAnimate = true;\n }\n };\n\n /** @internal */\n private _resizeObserver?: ResizeObserver;\n\n /**\n * @internal\n *\n * Watches for `dir` attribute changes so the indicator recalculates\n * when writing direction flips at runtime. `getComputedStyle` in\n * `updateSelectionIndicator` resolves inherited direction correctly,\n * but nothing else observes it: a `dir` flip on the document root or\n * an ancestor doesn't resize this element, so the `ResizeObserver`\n * below won't catch it. Mirrors the `dir`-watching pattern in\n * `placement-controller.ts`.\n */\n private _directionObserver?: MutationObserver;\n\n // ───────────────────────────────────\n // LIFECYCLE\n // ───────────────────────────────────\n\n protected override willUpdate(changes: PropertyValues): void {\n if (!this.hasUpdated) {\n const selectedChild = this.querySelector(\n ':scope > [selected]'\n ) as TabLike | null;\n\n if (selectedChild) {\n this.selectTarget(selectedChild);\n }\n }\n\n super.willUpdate(changes);\n\n if (changes.has('selected')) {\n if (this._tabs.length) {\n this.updateCheckedState();\n }\n\n const previousValue = changes.get('selected') as string | undefined;\n if (previousValue) {\n const previous = this.querySelector(\n `[role=\"tabpanel\"][tab-id=\"${previousValue}\"]`\n ) as TabPanelLike | null;\n\n if (previous) {\n previous.selected = false;\n }\n }\n\n const next = this.querySelector(\n `[role=\"tabpanel\"][tab-id=\"${this.selected}\"]`\n ) as TabPanelLike | null;\n\n if (next) {\n next.selected = true;\n }\n\n this.updateSelectionIndicator();\n }\n\n if (changes.has('disabled') && this._tabs.length) {\n this._navigation.refresh();\n if (this.disabled) {\n // When disabled, getItems() returns [] so the controller's cleanup\n // loop in refresh() iterates nothing. Explicitly remove all tabs from\n // the tab order so the tablist is fully inert.\n for (const tab of this._tabs) {\n (tab as HTMLElement).tabIndex = -1;\n }\n } else {\n // After re-enabling, restore the roving tab stop to the selected tab.\n this.updateCheckedState();\n }\n }\n\n if (changes.has('direction')) {\n this._navigation.setOptions({ direction: this.direction });\n this.updateSelectionIndicator();\n }\n }\n\n public override connectedCallback(): void {\n super.connectedCallback();\n this.addEventListener(\n focusgroupNavigationActiveChange,\n this._handleNavigationActiveChange\n );\n window.addEventListener('resize', this.updateSelectionIndicator);\n if ('fonts' in document) {\n document.fonts.addEventListener(\n 'loadingdone',\n this.updateSelectionIndicator\n );\n }\n this._resizeObserver = new ResizeObserver(() => {\n this.updateSelectionIndicator();\n });\n this._resizeObserver.observe(this);\n\n this._directionObserver = new MutationObserver(() => {\n this.updateSelectionIndicator();\n });\n const observeDir = (node: Element | null | undefined) => {\n if (node) {\n this._directionObserver?.observe(node, {\n attributes: true,\n attributeFilter: ['dir'],\n });\n }\n };\n observeDir(this.ownerDocument.documentElement);\n observeDir(this.closest('[dir]'));\n }\n\n public override disconnectedCallback(): void {\n this.removeEventListener(\n focusgroupNavigationActiveChange,\n this._handleNavigationActiveChange\n );\n window.removeEventListener('resize', this.updateSelectionIndicator);\n if ('fonts' in document) {\n document.fonts.removeEventListener(\n 'loadingdone',\n this.updateSelectionIndicator\n );\n }\n this._resizeObserver?.disconnect();\n this._resizeObserver = undefined;\n this._directionObserver?.disconnect();\n this._directionObserver = undefined;\n super.disconnectedCallback();\n }\n\n /**\n * Focuses the selected tab, or the first tab when none is selected yet.\n * Slotted tabs live in the light DOM; this is more reliable than relying\n * only on shadow `delegatesFocus` across browsers and test harnesses.\n */\n public override focus(options?: FocusOptions): void {\n if (this.disabled) {\n return;\n }\n\n const selectedTab = this._tabs.find((tab) => tab.selected);\n if (selectedTab) {\n (selectedTab as HTMLElement).focus(options);\n return;\n }\n\n if (this._tabs.length) {\n (this._tabs[0] as HTMLElement).focus(options);\n return;\n }\n\n super.focus(options);\n }\n\n protected override firstUpdated(changes: PropertyValues): void {\n super.firstUpdated(changes);\n\n if (!this.hasAttribute('direction')) {\n this.setAttribute('direction', this.direction);\n }\n\n warnIf(\n this,\n !this.accessibleLabel,\n `<${this.localName}> requires an \"accessible-label\" attribute to provide an accessible name for the tablist.`,\n 'https://spectrum-web-components.adobe.com/?path=/docs/components-tabs--docs',\n { type: 'accessibility', level: 'high' }\n );\n }\n\n /**\n * Waits for all child tab elements to finish their update cycle\n * before resolving so layout-dependent callers see stable geometry.\n */\n public override async getUpdateComplete(): Promise<boolean> {\n const complete = await super.getUpdateComplete();\n\n const children = [...this.children] as HTMLElement[];\n const childUpdates = children.map((child) => {\n if ('updateComplete' in child) {\n return (child as unknown as { updateComplete: Promise<boolean> })\n .updateComplete;\n }\n return Promise.resolve(true);\n });\n\n await Promise.all(childUpdates);\n return complete;\n }\n}\n"],"mappings":"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"}
@@ -6,8 +6,9 @@ import "../../controllers/placement-controller/index.js";
6
6
  import { TOOLTIP_PLACEMENTS as a, TOOLTIP_VARIANTS as o } from "./Tooltip.types.js";
7
7
  import { property as s } from "lit/decorators.js";
8
8
  import { SpectrumElement as c } from "@adobe/spectrum-wc-core/element/index.js";
9
+ import { isTopDismissible as l, registerDismissible as u, unregisterDismissible as d, validateEnum as f, warnIf as p } from "@adobe/spectrum-wc-core/utils/index.js";
9
10
  //#region components/tooltip/Tooltip.base.ts
10
- var l, u = class extends c {
11
+ var m, h = null, g = class extends c {
11
12
  constructor(...e) {
12
13
  super(...e), this.variant = "neutral", this.placement = "top", this.open = !1, this.triggerElement = null, this.delay = 1500, this.disabled = !1, this.manual = !1, this.offset = 4, this.crossOffset = 0, this.containerPadding = 12, this.shouldFlip = !0, this.labeling = !1, this.hoverController = new t(this, { warmStateKey: "swc-tooltip" }), this.placementController = new i(this), this.afterEventPending = !1, this.afterEventFallbackTimer = null, this._lastWiredTrigger = null, this.handleBeforeToggle = (e) => {
13
14
  let { newState: t } = e, n = t === "open" ? "swc-open" : "swc-close";
@@ -33,7 +34,7 @@ var l, u = class extends c {
33
34
  }, this.handleTransitionEnd = (e) => {
34
35
  e.target !== this || !this.afterEventPending || (this.afterEventFallbackTimer !== null && (clearTimeout(this.afterEventFallbackTimer), this.afterEventFallbackTimer = null), this.afterEventPending = !1, this.dispatchAfterEvent(this.open));
35
36
  }, this.handleKeyDown = (e) => {
36
- e.key === "Escape" && this.open && (this.open = !1);
37
+ e.key !== "Escape" || !this.open || l(this) && (e.preventDefault(), this.open = !1);
37
38
  };
38
39
  }
39
40
  get tipElement() {
@@ -70,9 +71,8 @@ var l, u = class extends c {
70
71
  resolveTrigger() {
71
72
  if (this.triggerElement) return this.triggerElement;
72
73
  if (this.for) {
73
- var e;
74
- let t = this.getRootNode().getElementById(this.for);
75
- return !t && (e = window.__swc) != null && e.DEBUG && window.__swc.warn(this, `<${this.localName}> for="${this.for}" did not resolve to an element in the current tree root. Check that the referenced id exists in the same document tree root.`, "https://opensource.adobe.com/spectrum-web-components/components/tooltip/", { level: "high" }), t;
74
+ let e = this.getRootNode().getElementById(this.for);
75
+ return p(this, !e, `<${this.localName}> for="${this.for}" did not resolve to an element in the current tree root. Check that the referenced id exists in the same document tree root.`, "https://spectrum-web-components.adobe.com/?path=/docs/components-tooltip--docs", { level: "high" }), e;
76
76
  }
77
77
  return null;
78
78
  }
@@ -111,53 +111,88 @@ var l, u = class extends c {
111
111
  })), e || this.clearPositioningState();
112
112
  }
113
113
  willUpdate(e) {
114
- super.willUpdate(e), this.disabled && this.open && (this.open = !1);
114
+ if (super.willUpdate(e), this.disabled && this.open && (this.open = !1), e.has("variant")) {
115
+ let e = this.constructor;
116
+ f(this, {
117
+ prop: "variant",
118
+ value: this.variant,
119
+ valid: e.VARIANTS,
120
+ url: "https://spectrum-web-components.adobe.com/?path=/docs/components-tooltip--docs"
121
+ });
122
+ }
123
+ if (e.has("placement")) {
124
+ let e = this.constructor;
125
+ f(this, {
126
+ prop: "placement",
127
+ value: this.placement,
128
+ valid: e.PLACEMENTS,
129
+ url: "https://spectrum-web-components.adobe.com/?path=/docs/components-tooltip--docs"
130
+ });
131
+ }
115
132
  }
116
133
  updated(e) {
117
- super.updated(e), e.has("offset") && this.style.setProperty("--_swc-tooltip-animation-distance", `${this.offset}px`), (e.has("open") || e.has("labeling") || e.has("for") || e.has("triggerElement")) && this.syncAriaRelationship(), (e.has("for") || e.has("triggerElement")) && this.hoverController.setTarget(this.resolveTrigger()), e.has("open") ? this.open ? (document.addEventListener("keydown", this.handleKeyDown), this.setDeclaredActualPlacement(), this.startPlacement(), this.open !== this.isPopoverOpen && this.showPopover()) : (document.removeEventListener("keydown", this.handleKeyDown), this.open !== this.isPopoverOpen && this.hidePopover(), this.placementController.stop()) : this.open && (e.has("placement") || e.has("offset") || e.has("crossOffset") || e.has("containerPadding") || e.has("shouldFlip") || e.has("for") || e.has("triggerElement")) && this.startPlacement();
134
+ if (super.updated(e), e.has("variant")) {
135
+ let e = this.constructor;
136
+ f(this, {
137
+ prop: "variant",
138
+ value: this.variant,
139
+ valid: e.VARIANTS,
140
+ url: "https://spectrum-web-components.adobe.com/?path=/docs/components-tooltip--docs"
141
+ });
142
+ }
143
+ if (e.has("placement")) {
144
+ let e = this.constructor;
145
+ f(this, {
146
+ prop: "placement",
147
+ value: this.placement,
148
+ valid: e.PLACEMENTS,
149
+ url: "https://spectrum-web-components.adobe.com/?path=/docs/components-tooltip--docs"
150
+ });
151
+ }
152
+ e.has("offset") && this.style.setProperty("--_swc-tooltip-animation-distance", `${this.offset}px`), (e.has("open") || e.has("labeling") || e.has("for") || e.has("triggerElement")) && this.syncAriaRelationship(), (e.has("for") || e.has("triggerElement")) && this.hoverController.setTarget(this.resolveTrigger()), e.has("open") ? this.open ? (this.manual || (h && h !== this && h.requestClose(), h = this), u(this), document.addEventListener("keydown", this.handleKeyDown, { capture: !0 }), this.setDeclaredActualPlacement(), this.startPlacement(), this.open !== this.isPopoverOpen && this.showPopover()) : (h === this && (h = null), d(this), document.removeEventListener("keydown", this.handleKeyDown, { capture: !0 }), this.open !== this.isPopoverOpen && this.hidePopover(), this.placementController.stop()) : this.open && (e.has("placement") || e.has("offset") || e.has("crossOffset") || e.has("containerPadding") || e.has("shouldFlip") || e.has("for") || e.has("triggerElement")) && this.startPlacement();
118
153
  }
119
154
  connectedCallback() {
120
- super.connectedCallback(), this.setAttribute("role", "tooltip"), this.setAttribute("popover", "auto"), this.addEventListener("beforetoggle", this.handleBeforeToggle), this.addEventListener("toggle", this.handleToggle), this.addEventListener("transitionend", this.handleTransitionEnd);
155
+ super.connectedCallback(), this.setAttribute("role", "tooltip"), this.setAttribute("popover", "manual"), this.addEventListener("beforetoggle", this.handleBeforeToggle), this.addEventListener("toggle", this.handleToggle), this.addEventListener("transitionend", this.handleTransitionEnd);
121
156
  }
122
157
  disconnectedCallback() {
123
- super.disconnectedCallback(), this.removeEventListener("beforetoggle", this.handleBeforeToggle), this.removeEventListener("toggle", this.handleToggle), this.removeEventListener("transitionend", this.handleTransitionEnd), document.removeEventListener("keydown", this.handleKeyDown), this.afterEventFallbackTimer !== null && (clearTimeout(this.afterEventFallbackTimer), this.afterEventFallbackTimer = null), this.clearAriaRelationship();
158
+ super.disconnectedCallback(), h === this && (h = null), this.removeEventListener("beforetoggle", this.handleBeforeToggle), this.removeEventListener("toggle", this.handleToggle), this.removeEventListener("transitionend", this.handleTransitionEnd), d(this), document.removeEventListener("keydown", this.handleKeyDown, { capture: !0 }), this.afterEventFallbackTimer !== null && (clearTimeout(this.afterEventFallbackTimer), this.afterEventFallbackTimer = null), this.clearAriaRelationship();
124
159
  }
125
160
  };
126
- l = u, l.VARIANTS = o, l.PLACEMENTS = a, e([s({
161
+ m = g, m.VARIANTS = o, m.PLACEMENTS = a, e([s({
127
162
  type: String,
128
163
  reflect: !0
129
- })], u.prototype, "variant", void 0), e([s({
164
+ })], g.prototype, "variant", void 0), e([s({
130
165
  type: String,
131
166
  reflect: !0
132
- })], u.prototype, "placement", void 0), e([s({
167
+ })], g.prototype, "placement", void 0), e([s({
133
168
  type: Boolean,
134
169
  reflect: !0
135
- })], u.prototype, "open", void 0), e([s({
170
+ })], g.prototype, "open", void 0), e([s({
136
171
  attribute: "for",
137
172
  type: String
138
- })], u.prototype, "for", void 0), e([s({ attribute: !1 })], u.prototype, "triggerElement", void 0), e([s({
173
+ })], g.prototype, "for", void 0), e([s({ attribute: !1 })], g.prototype, "triggerElement", void 0), e([s({
139
174
  type: Number,
140
175
  reflect: !0
141
- })], u.prototype, "delay", void 0), e([s({
176
+ })], g.prototype, "delay", void 0), e([s({
142
177
  type: Boolean,
143
178
  reflect: !0
144
- })], u.prototype, "disabled", void 0), e([s({
179
+ })], g.prototype, "disabled", void 0), e([s({
145
180
  type: Boolean,
146
181
  reflect: !0
147
- })], u.prototype, "manual", void 0), e([s({ type: Number })], u.prototype, "offset", void 0), e([s({
182
+ })], g.prototype, "manual", void 0), e([s({ type: Number })], g.prototype, "offset", void 0), e([s({
148
183
  type: Number,
149
184
  attribute: "cross-offset"
150
- })], u.prototype, "crossOffset", void 0), e([s({
185
+ })], g.prototype, "crossOffset", void 0), e([s({
151
186
  type: Number,
152
187
  attribute: "container-padding"
153
- })], u.prototype, "containerPadding", void 0), e([s({
188
+ })], g.prototype, "containerPadding", void 0), e([s({
154
189
  type: Boolean,
155
190
  attribute: "should-flip"
156
- })], u.prototype, "shouldFlip", void 0), e([s({
191
+ })], g.prototype, "shouldFlip", void 0), e([s({
157
192
  type: Boolean,
158
193
  reflect: !0
159
- })], u.prototype, "labeling", void 0);
194
+ })], g.prototype, "labeling", void 0);
160
195
  //#endregion
161
- export { u as TooltipBase };
196
+ export { g as TooltipBase };
162
197
 
163
198
  //# sourceMappingURL=Tooltip.base.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Tooltip.base.js","names":[],"sources":["../../../components/tooltip/Tooltip.base.ts"],"sourcesContent":["/**\n * Copyright 2026 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\nimport { PropertyValues } from 'lit';\nimport { property } from 'lit/decorators.js';\n\nimport { SpectrumElement } from '@adobe/spectrum-wc-core/element/index.js';\n\nimport {\n HoverController,\n type HoverControllerHost,\n} from '../../controllers/hover-controller/index.js';\nimport {\n fromFloatingPlacement,\n type Placement as ControllerPlacement,\n PlacementController,\n toFloatingPlacement,\n} from '../../controllers/placement-controller/index.js';\nimport {\n TOOLTIP_PLACEMENTS,\n TOOLTIP_VARIANTS,\n type TooltipPlacement,\n type TooltipVariant,\n} from './Tooltip.types.js';\n\n/**\n * Abstract base class for the Tooltip component.\n * Handles all non-rendering logic: property declarations, popover lifecycle,\n * event dispatch, trigger resolution, ARIA wiring, `HoverController` integration\n * (hover/focus open/close), and `PlacementController` integration (pixel positioning).\n *\n * @slot - Text label displayed in the tooltip.\n *\n * @fires swc-open - Dispatched when the tooltip begins to open, before the transition plays.\n * @fires swc-close - Dispatched when the tooltip begins to close, before the transition plays.\n * @fires swc-after-open - Dispatched after the tooltip finishes opening, once the transition completes.\n * @fires swc-after-close - Dispatched after the tooltip finishes closing, once the transition completes.\n */\nexport abstract class TooltipBase\n extends SpectrumElement\n implements HoverControllerHost\n{\n // ──────────────────\n // SHARED API\n // ──────────────────\n\n /**\n * @internal\n *\n * All valid variants for the tooltip.\n */\n static readonly VARIANTS: readonly string[] = TOOLTIP_VARIANTS;\n\n /**\n * @internal\n *\n * All valid placement values: physical cardinals (`top`, `bottom`, `left`, `right`)\n * and logical inline values (`start`, `end`).\n */\n static readonly PLACEMENTS: readonly string[] = TOOLTIP_PLACEMENTS;\n\n /**\n * The semantic variant of the tooltip.\n *\n * @default 'neutral'\n */\n @property({ type: String, reflect: true })\n public variant: TooltipVariant = 'neutral';\n\n /**\n * Preferred placement of the tooltip relative to its trigger. This is always\n * the consumer's requested side; the resolved physical side (after any\n * viewport-driven flip) is reflected as `actual-placement`.\n *\n * @default 'top'\n */\n @property({ type: String, reflect: true })\n public placement: TooltipPlacement = 'top';\n\n /**\n * Whether the tooltip is visible.\n *\n * @default false\n */\n @property({ type: Boolean, reflect: true })\n public open: boolean = false;\n\n /**\n * The `id` of the trigger element in the same document tree root.\n */\n @property({ attribute: 'for', type: String })\n public for: string | undefined;\n\n /**\n * Explicit trigger element reference; overrides `for` when set.\n * Use when `getElementById` cannot reach the trigger, such as across a shadow boundary.\n *\n * @default null\n */\n @property({ attribute: false })\n public triggerElement: HTMLElement | null = null;\n\n /**\n * Warm-up delay in milliseconds before the tooltip opens on pointer hover.\n * Set to `0` to open immediately. Keyboard focus always opens immediately.\n *\n * @default 1500\n */\n @property({ type: Number, reflect: true })\n public delay: number = 1500;\n\n /**\n * When set, the tooltip does not respond to hover or focus events and cannot\n * be opened. `disabled` takes priority over `open`: setting `open` to `true`\n * while disabled is a no-op, and disabling an open tooltip closes it. Applies\n * in `manual` mode as well.\n *\n * @default false\n */\n @property({ type: Boolean, reflect: true })\n public disabled: boolean = false;\n\n /**\n * Suppresses automatic hover and focus wiring.\n * The consumer manages visibility via the `open` property or the popover API.\n *\n * @default false\n */\n @property({ type: Boolean, reflect: true })\n public manual: boolean = false;\n\n /**\n * Pixel gap along the placement axis between the trigger and the tooltip bubble.\n *\n * @default 4\n */\n @property({ type: Number })\n public offset: number = 4;\n\n /**\n * Slide along the trigger edge perpendicular to the placement direction, in pixels.\n *\n * @default 0\n */\n @property({ type: Number, attribute: 'cross-offset' })\n public crossOffset: number = 0;\n\n /**\n * Minimum inset from the viewport edge, in pixels, for collision detection.\n *\n * @default 12\n */\n @property({ type: Number, attribute: 'container-padding' })\n public containerPadding: number = 12;\n\n /**\n * Whether the tooltip may reposition to the opposite side when the requested\n * placement does not fit within the viewport.\n *\n * @default true\n */\n @property({ type: Boolean, attribute: 'should-flip' })\n public shouldFlip: boolean = true;\n\n /**\n * When set, the tooltip acts as the trigger's accessible name rather than its description.\n * Use for icon-only triggers where the tooltip text is the sole accessible name.\n *\n * @default false\n */\n @property({ type: Boolean, reflect: true })\n public labeling: boolean = false;\n\n // ──────────────────────\n // IMPLEMENTATION\n // ──────────────────────\n\n private readonly hoverController = new HoverController(this, {\n warmStateKey: 'swc-tooltip',\n });\n\n private readonly placementController = new PlacementController(this);\n\n // Guards dispatchAfterEvent so only the first transitionend per open/close cycle fires,\n // preventing one after event from firing for each CSS property that transitions.\n private afterEventPending = false;\n\n // Fallback timer for browsers that do not fire transitionend for\n // transition-behavior:allow-discrete discrete properties (e.g. Firefox in CI).\n // Cleared when transitionend fires or a new toggle starts.\n private afterEventFallbackTimer: ReturnType<typeof setTimeout> | null = null;\n\n // Tracks the most recently ARIA-wired target so syncAriaRelationship can\n // remove stale references when for or triggerElement changes while open.\n private _lastWiredTrigger:\n | (Element & {\n ariaDescribedByElements: Element[] | null;\n ariaLabelledByElements: Element[] | null;\n })\n | null = null;\n\n /**\n * Returns the tip arrow element to pass to `PlacementController` so the\n * `arrow` middleware keeps the tip aligned with the trigger when the bubble\n * is shifted (e.g. via `crossOffset` or viewport `shift`).\n *\n * Returns `null` in the base class; the SWC rendering layer overrides this\n * to return the actual `.swc-Tooltip-tip` element from the shadow DOM.\n */\n protected get tipElement(): HTMLElement | null {\n return null;\n }\n\n // Reflects the browser's actual popover state. Used in updated() to reconcile\n // `open` against native light-dismiss: when the browser closes the popover\n // (e.g. Escape or outside click), `open` is synced by the toggle listener\n // without re-invoking the Popover API, and the guard prevents a redundant\n // hidePopover() (which would throw on an already-closed popover).\n private get isPopoverOpen(): boolean {\n return this.matches(':popover-open');\n }\n\n /**\n * {@link HoverControllerHost} contract. `open` is the single source of truth\n * for visibility; the controller asks to open via this method rather than\n * driving the Popover API directly. `updated()` then reconciles the popover.\n */\n public requestOpen(): void {\n this.open = true;\n }\n\n /**\n * {@link HoverControllerHost} contract. Asks the tooltip to close by setting\n * `open`; `updated()` reconciles the popover.\n */\n public requestClose(): void {\n this.open = false;\n }\n\n // Writes actual-placement to the physical side of the declared `placement`,\n // resolving logical start/end against the trigger's writing direction. Used\n // synchronously before showPopover() so @starting-style has a direction; the\n // PlacementController overwrites it with the flip-resolved side afterwards.\n private setDeclaredActualPlacement(): void {\n const trigger = this.resolveTrigger();\n const direction =\n trigger && getComputedStyle(trigger).direction === 'rtl' ? 'rtl' : 'ltr';\n const side = fromFloatingPlacement(\n toFloatingPlacement(this.placement as ControllerPlacement, direction)\n ).split('-')[0];\n this.setAttribute('actual-placement', side);\n }\n\n private startPlacement(): void {\n const trigger = this.resolveTrigger();\n if (!trigger) {\n return;\n }\n this.placementController.start(trigger, this, {\n placement: this.placement as ControllerPlacement,\n offset: this.offset,\n crossOffset: this.crossOffset,\n containerPadding: this.containerPadding,\n shouldFlip: this.shouldFlip,\n tipElement: this.tipElement ?? undefined,\n onPlacementChange: (resolvedPlacement: ControllerPlacement) => {\n this.setAttribute('actual-placement', resolvedPlacement.split('-')[0]);\n },\n });\n }\n\n private resolveTrigger(): HTMLElement | null {\n if (this.triggerElement) {\n return this.triggerElement;\n }\n if (this.for) {\n const root = this.getRootNode() as Document | ShadowRoot;\n const trigger = root.getElementById(this.for);\n if (!trigger && window.__swc?.DEBUG) {\n window.__swc.warn(\n this,\n `<${this.localName}> for=\"${this.for}\" did not resolve to an element in the current tree root. Check that the referenced id exists in the same document tree root.`,\n 'https://opensource.adobe.com/spectrum-web-components/components/tooltip/',\n { level: 'high' }\n );\n }\n return trigger;\n }\n return null;\n }\n\n // Removes this tooltip from the previously wired target's ARIA element arrays\n // and forgets the reference. Called before re-wiring and on disconnect so a\n // trigger never retains a reference to a detached tooltip node.\n private clearAriaRelationship(): void {\n if (!this._lastWiredTrigger) {\n return;\n }\n const stale = this._lastWiredTrigger;\n stale.ariaDescribedByElements = (\n stale.ariaDescribedByElements ?? []\n ).filter((el) => el !== this);\n stale.ariaLabelledByElements = (stale.ariaLabelledByElements ?? []).filter(\n (el) => el !== this\n );\n this._lastWiredTrigger = null;\n }\n\n private syncAriaRelationship(): void {\n // Remove stale references from the previously wired target before resolving\n // the new one. Handles for/triggerElement changes while the tooltip is open.\n this.clearAriaRelationship();\n\n const trigger = this.resolveTrigger();\n if (!trigger) {\n return;\n }\n const target = (trigger.shadowRoot?.querySelector('button') ??\n trigger) as Element & {\n ariaDescribedByElements: Element[] | null;\n ariaLabelledByElements: Element[] | null;\n };\n\n if (this.labeling) {\n // Remove any stale describedby reference (e.g. if labeling changed while open).\n const described = target.ariaDescribedByElements ?? [];\n target.ariaDescribedByElements = described.filter((el) => el !== this);\n\n const labelled = target.ariaLabelledByElements ?? [];\n target.ariaLabelledByElements = this.open\n ? [...labelled.filter((el) => el !== this), this]\n : labelled.filter((el) => el !== this);\n } else {\n // Remove any stale labelledby reference (e.g. if labeling changed while open).\n const labelled = target.ariaLabelledByElements ?? [];\n target.ariaLabelledByElements = labelled.filter((el) => el !== this);\n\n const described = target.ariaDescribedByElements ?? [];\n target.ariaDescribedByElements = this.open\n ? [...described.filter((el) => el !== this), this]\n : described.filter((el) => el !== this);\n }\n\n if (this.open) {\n this._lastWiredTrigger = target;\n }\n }\n\n private clearPositioningState(): void {\n // Called after the exit transition. Clearing earlier would displace the\n // tooltip to 0,0 while still visible and fading out.\n this.removeAttribute('actual-placement');\n this.style.removeProperty('translate');\n this.style.removeProperty('top');\n this.style.removeProperty('left');\n this.style.removeProperty('--swc-placement-available-width');\n this.style.removeProperty('--swc-placement-available-height');\n if (this.tipElement) {\n this.tipElement.style.removeProperty('translate');\n this.tipElement.style.removeProperty('top');\n this.tipElement.style.removeProperty('left');\n }\n }\n\n private dispatchAfterEvent(isOpen: boolean): void {\n this.dispatchEvent(\n new CustomEvent(isOpen ? 'swc-after-open' : 'swc-after-close', {\n bubbles: true,\n composed: true,\n })\n );\n if (!isOpen) {\n this.clearPositioningState();\n }\n }\n\n private readonly handleBeforeToggle = (event: Event): void => {\n const { newState } = event as ToggleEvent;\n const eventName = newState === 'open' ? 'swc-open' : 'swc-close';\n this.dispatchEvent(\n new CustomEvent(eventName, { bubbles: true, composed: true })\n );\n // Set here so the flag is set regardless of whether this.open already matches\n // newState. handleToggle exits early when open was set externally, which would\n // otherwise leave the flag unset and suppress swc-after-open / swc-after-close.\n this.afterEventPending = true;\n };\n\n private readonly handleToggle = (event: Event): void => {\n // Cancel any in-flight close fallback; a new toggle resets the cycle.\n if (this.afterEventFallbackTimer !== null) {\n clearTimeout(this.afterEventFallbackTimer);\n this.afterEventFallbackTimer = null;\n }\n const { newState } = event as ToggleEvent;\n const isOpen = newState === 'open';\n if (isOpen !== this.open) {\n this.open = isOpen;\n }\n // When no CSS transition is active, dispatch after-* immediately since transitionend will not fire.\n // transitionDuration is comma-separated when multiple properties transition (\"0s, 0s, …\"),\n // so check that every value in the list is zero rather than comparing the full string.\n const durations = getComputedStyle(this).transitionDuration.split(',');\n if (durations.every((d) => d.trim() === '0s')) {\n this.afterEventPending = false;\n this.dispatchAfterEvent(isOpen);\n } else if (!isOpen) {\n // Some browsers (e.g. Firefox in CI) do not fire transitionend for\n // transition-behavior:allow-discrete discrete properties. Start a fallback\n // so positioning is always cleared after the exit transition window.\n const maxMs = Math.max(\n 0,\n ...durations.map((d) => {\n const trimmed = d.trim();\n const value = parseFloat(trimmed);\n return trimmed.endsWith('ms') ? value : value * 1000;\n })\n );\n this.afterEventFallbackTimer = setTimeout(() => {\n this.afterEventFallbackTimer = null;\n if (this.afterEventPending) {\n this.afterEventPending = false;\n this.dispatchAfterEvent(false);\n }\n }, maxMs + 100);\n }\n };\n\n private readonly handleTransitionEnd = (event: TransitionEvent): void => {\n if (event.target !== this || !this.afterEventPending) {\n return;\n }\n if (this.afterEventFallbackTimer !== null) {\n clearTimeout(this.afterEventFallbackTimer);\n this.afterEventFallbackTimer = null;\n }\n this.afterEventPending = false;\n this.dispatchAfterEvent(this.open);\n };\n\n // Escape-to-close. Registered on `document` only while open (see updated()),\n // not for the whole connected lifetime: the tooltip is non-interactive and\n // never receives focus, so a host-level keydown would not fire, and scoping to\n // the open state keeps at most one listener active at a time (popover=\"auto\"\n // permits one open tooltip). Under `popover=\"auto\"` this duplicates the\n // browser's native Escape light-dismiss; it is kept as a mode-independent\n // handler so a `manual` tooltip (which gets no native light-dismiss, e.g. one\n // that coexists with an open popover) still closes on Escape.\n private readonly handleKeyDown = (event: KeyboardEvent): void => {\n if (event.key === 'Escape' && this.open) {\n this.open = false;\n }\n };\n\n protected override willUpdate(changedProperties: PropertyValues): void {\n super.willUpdate(changedProperties);\n // `disabled` takes priority over `open`: a disabled tooltip cannot be shown,\n // regardless of how `open` became true (set programmatically, or `disabled`\n // toggled on while already open). Normalizing here — before render and\n // before updated() acts on `open` — keeps `open` and visibility consistent\n // and prevents the `open` attribute from briefly reflecting `true`. Hover\n // and focus opening are separately blocked by HoverController's guards.\n if (this.disabled && this.open) {\n this.open = false;\n }\n }\n\n protected override updated(changedProperties: PropertyValues): void {\n super.updated(changedProperties);\n if (changedProperties.has('offset')) {\n this.style.setProperty(\n '--_swc-tooltip-animation-distance',\n `${this.offset}px`\n );\n }\n if (\n changedProperties.has('open') ||\n changedProperties.has('labeling') ||\n changedProperties.has('for') ||\n changedProperties.has('triggerElement')\n ) {\n this.syncAriaRelationship();\n }\n if (\n changedProperties.has('for') ||\n changedProperties.has('triggerElement')\n ) {\n this.hoverController.setTarget(this.resolveTrigger());\n }\n\n // Placement controller: start when opening, stop when closing, restart\n // when positioning options or the trigger change while already open.\n const openChanged = changedProperties.has('open');\n if (openChanged) {\n if (this.open) {\n // Register Escape handling only while open; removed on close below.\n document.addEventListener('keydown', this.handleKeyDown);\n // Set actual-placement to the declared side synchronously, before\n // showPopover(). @starting-style is evaluated by the browser the moment\n // the popover enters the top layer, so the direction-bearing attribute\n // must already be present or the entrance animation has no direction to\n // animate from. PlacementController writes actual-placement again from\n // onPlacementChange, but that is async (it awaits document.fonts.ready),\n // so it lands after showPopover() — too late for @starting-style. The\n // popover is display:none until shown and therefore unmeasurable, so a\n // viewport flip cannot be known here; on a flip the resolved side\n // arrives from onPlacementChange and the slide direction corrects then.\n this.setDeclaredActualPlacement();\n this.startPlacement();\n if (this.open !== this.isPopoverOpen) {\n this.showPopover();\n }\n } else {\n document.removeEventListener('keydown', this.handleKeyDown);\n if (this.open !== this.isPopoverOpen) {\n this.hidePopover();\n }\n // Stop autoUpdate immediately so positioning stops tracking the trigger\n // during the exit transition. clearPositioningState() runs after the\n // transition completes via dispatchAfterEvent.\n this.placementController.stop();\n }\n } else if (this.open) {\n const placementOptsChanged =\n changedProperties.has('placement') ||\n changedProperties.has('offset') ||\n changedProperties.has('crossOffset') ||\n changedProperties.has('containerPadding') ||\n changedProperties.has('shouldFlip') ||\n changedProperties.has('for') ||\n changedProperties.has('triggerElement');\n if (placementOptsChanged) {\n this.startPlacement();\n }\n }\n }\n\n public override connectedCallback(): void {\n super.connectedCallback();\n this.setAttribute('role', 'tooltip');\n this.setAttribute('popover', 'auto');\n this.addEventListener('beforetoggle', this.handleBeforeToggle);\n this.addEventListener('toggle', this.handleToggle);\n this.addEventListener('transitionend', this.handleTransitionEnd);\n }\n\n public override disconnectedCallback(): void {\n super.disconnectedCallback();\n this.removeEventListener('beforetoggle', this.handleBeforeToggle);\n this.removeEventListener('toggle', this.handleToggle);\n this.removeEventListener('transitionend', this.handleTransitionEnd);\n // Defensive: the keydown listener is normally removed on close, but a\n // tooltip disconnected while open would still have it registered.\n document.removeEventListener('keydown', this.handleKeyDown);\n if (this.afterEventFallbackTimer !== null) {\n clearTimeout(this.afterEventFallbackTimer);\n this.afterEventFallbackTimer = null;\n }\n // If the tooltip is removed while open, the trigger would otherwise retain a\n // reference to this now-detached node in its ARIA element arrays.\n this.clearAriaRelationship();\n }\n}\n"],"mappings":";;;;;;;;;OA8CsB,IAAtB,cACU,EAEV;;8BA0BmC,4BAUI,mBAQd,0BAeqB,mBASrB,sBAWI,kBASF,kBAQD,sBAQK,2BAQK,sBASL,oBASF,2BAMQ,IAAI,EAAgB,MAAM,EAC3D,cAAc,eACf,CAAC,6BAEqC,IAAI,EAAoB,KAAK,2BAIxC,mCAK4C,+BAS7D,iCAiL4B,MAAuB;GAC5D,IAAM,EAAE,gBAAa,GACf,IAAY,MAAa,SAAS,aAAa;AAOrD,GANA,KAAK,cACH,IAAI,YAAY,GAAW;IAAE,SAAS;IAAM,UAAU;IAAM,CAAC,CAC9D,EAID,KAAK,oBAAoB;0BAGM,MAAuB;AAEtD,GAAI,KAAK,4BAA4B,SACnC,aAAa,KAAK,wBAAwB,EAC1C,KAAK,0BAA0B;GAEjC,IAAM,EAAE,gBAAa,GACf,IAAS,MAAa;AAC5B,GAAI,MAAW,KAAK,SAClB,KAAK,OAAO;GAKd,IAAM,IAAY,iBAAiB,KAAK,CAAC,mBAAmB,MAAM,IAAI;AACtE,OAAI,EAAU,OAAO,MAAM,EAAE,MAAM,KAAK,KAAK,CAE3C,CADA,KAAK,oBAAoB,IACzB,KAAK,mBAAmB,EAAO;YACtB,CAAC,GAAQ;IAIlB,IAAM,IAAQ,KAAK,IACjB,GACA,GAAG,EAAU,KAAK,MAAM;KACtB,IAAM,IAAU,EAAE,MAAM,EAClB,IAAQ,WAAW,EAAQ;AACjC,YAAO,EAAQ,SAAS,KAAK,GAAG,IAAQ,IAAQ;MAChD,CACH;AACD,SAAK,0BAA0B,iBAAiB;AAE9C,KADA,KAAK,0BAA0B,MAC3B,KAAK,sBACP,KAAK,oBAAoB,IACzB,KAAK,mBAAmB,GAAM;OAE/B,IAAQ,IAAI;;iCAIqB,MAAiC;AACnE,KAAM,WAAW,QAAQ,CAAC,KAAK,sBAG/B,KAAK,4BAA4B,SACnC,aAAa,KAAK,wBAAwB,EAC1C,KAAK,0BAA0B,OAEjC,KAAK,oBAAoB,IACzB,KAAK,mBAAmB,KAAK,KAAK;2BAWF,MAA+B;AAC/D,GAAI,EAAM,QAAQ,YAAY,KAAK,SACjC,KAAK,OAAO;;;CAjPhB,IAAc,aAAiC;AAC7C,SAAO;;CAQT,IAAY,gBAAyB;AACnC,SAAO,KAAK,QAAQ,gBAAgB;;CAQtC,cAA2B;AACzB,OAAK,OAAO;;CAOd,eAA4B;AAC1B,OAAK,OAAO;;CAOd,6BAA2C;EACzC,IAAM,IAAU,KAAK,gBAAgB,EAC/B,IACJ,KAAW,iBAAiB,EAAQ,CAAC,cAAc,QAAQ,QAAQ,OAC/D,IAAO,EACX,EAAoB,KAAK,WAAkC,EAAU,CACtE,CAAC,MAAM,IAAI,CAAC;AACb,OAAK,aAAa,oBAAoB,EAAK;;CAG7C,iBAA+B;;EAC7B,IAAM,IAAU,KAAK,gBAAgB;AAChC,OAGL,KAAK,oBAAoB,MAAM,GAAS,MAAM;GAC5C,WAAW,KAAK;GAChB,QAAQ,KAAK;GACb,aAAa,KAAK;GAClB,kBAAkB,KAAK;GACvB,YAAY,KAAK;GACjB,aAAA,IAAY,KAAK,eAAA,OAAc,KAAA,IAAd;GACjB,oBAAoB,MAA2C;AAC7D,SAAK,aAAa,oBAAoB,EAAkB,MAAM,IAAI,CAAC,GAAG;;GAEzE,CAAC;;CAGJ,iBAA6C;AAC3C,MAAI,KAAK,eACP,QAAO,KAAK;AAEd,MAAI,KAAK,KAAK;;GAEZ,IAAM,IADO,KAAK,aAAa,CACV,eAAe,KAAK,IAAI;AAS7C,UARI,CAAC,MAAA,IAAW,OAAO,UAAA,QAAA,EAAO,SAC5B,OAAO,MAAM,KACX,MACA,IAAI,KAAK,UAAU,SAAS,KAAK,IAAI,gIACrC,4EACA,EAAE,OAAO,QAAQ,CAClB,EAEI;;AAET,SAAO;;CAMT,wBAAsC;;AACpC,MAAI,CAAC,KAAK,kBACR;EAEF,IAAM,IAAQ,KAAK;AAOnB,EANA,EAAM,4BAAA,IACJ,EAAM,4BAAA,OAA2B,EAAE,GAA7B,GACN,QAAQ,MAAO,MAAO,KAAK,EAC7B,EAAM,2BAAA,IAA0B,EAAM,2BAAA,OAA0B,EAAE,GAA5B,GAA8B,QACjE,MAAO,MAAO,KAChB,EACD,KAAK,oBAAoB;;CAG3B,uBAAqC;;AAGnC,OAAK,uBAAuB;EAE5B,IAAM,IAAU,KAAK,gBAAgB;AACrC,MAAI,CAAC,EACH;EAEF,IAAM,KAAA,KAAA,IAAU,EAAQ,eAAA,OAAA,KAAA,IAAA,EAAY,cAAc,SAAS,KAAA,OACzD,IADyD;AAM3D,MAAI,KAAK,UAAU;;AAGjB,KAAO,4BAAA,IADW,EAAO,4BAAA,OAA2B,EAAE,GAA7B,GACkB,QAAQ,MAAO,MAAO,KAAK;GAEtE,IAAM,KAAA,IAAW,EAAO,2BAAA,OAA0B,EAAE,GAA5B;AACxB,KAAO,yBAAyB,KAAK,OACjC,CAAC,GAAG,EAAS,QAAQ,MAAO,MAAO,KAAK,EAAE,KAAK,GAC/C,EAAS,QAAQ,MAAO,MAAO,KAAK;SACnC;;AAGL,KAAO,2BAAA,IADU,EAAO,2BAAA,OAA0B,EAAE,GAA5B,GACiB,QAAQ,MAAO,MAAO,KAAK;GAEpE,IAAM,KAAA,IAAY,EAAO,4BAAA,OAA2B,EAAE,GAA7B;AACzB,KAAO,0BAA0B,KAAK,OAClC,CAAC,GAAG,EAAU,QAAQ,MAAO,MAAO,KAAK,EAAE,KAAK,GAChD,EAAU,QAAQ,MAAO,MAAO,KAAK;;AAG3C,EAAI,KAAK,SACP,KAAK,oBAAoB;;CAI7B,wBAAsC;AASpC,EANA,KAAK,gBAAgB,mBAAmB,EACxC,KAAK,MAAM,eAAe,YAAY,EACtC,KAAK,MAAM,eAAe,MAAM,EAChC,KAAK,MAAM,eAAe,OAAO,EACjC,KAAK,MAAM,eAAe,kCAAkC,EAC5D,KAAK,MAAM,eAAe,mCAAmC,EACzD,KAAK,eACP,KAAK,WAAW,MAAM,eAAe,YAAY,EACjD,KAAK,WAAW,MAAM,eAAe,MAAM,EAC3C,KAAK,WAAW,MAAM,eAAe,OAAO;;CAIhD,mBAA2B,GAAuB;AAOhD,EANA,KAAK,cACH,IAAI,YAAY,IAAS,mBAAmB,mBAAmB;GAC7D,SAAS;GACT,UAAU;GACX,CAAC,CACH,EACI,KACH,KAAK,uBAAuB;;CAkFhC,WAA8B,GAAyC;AAQrE,EAPA,MAAM,WAAW,EAAkB,EAO/B,KAAK,YAAY,KAAK,SACxB,KAAK,OAAO;;CAIhB,QAA2B,GAAyC;AA0BlE,EAzBA,MAAM,QAAQ,EAAkB,EAC5B,EAAkB,IAAI,SAAS,IACjC,KAAK,MAAM,YACT,qCACA,GAAG,KAAK,OAAO,IAChB,GAGD,EAAkB,IAAI,OAAO,IAC7B,EAAkB,IAAI,WAAW,IACjC,EAAkB,IAAI,MAAM,IAC5B,EAAkB,IAAI,iBAAiB,KAEvC,KAAK,sBAAsB,GAG3B,EAAkB,IAAI,MAAM,IAC5B,EAAkB,IAAI,iBAAiB,KAEvC,KAAK,gBAAgB,UAAU,KAAK,gBAAgB,CAAC,EAKnC,EAAkB,IAAI,OAAO,GAE3C,KAAK,QAEP,SAAS,iBAAiB,WAAW,KAAK,cAAc,EAWxD,KAAK,4BAA4B,EACjC,KAAK,gBAAgB,EACjB,KAAK,SAAS,KAAK,iBACrB,KAAK,aAAa,KAGpB,SAAS,oBAAoB,WAAW,KAAK,cAAc,EACvD,KAAK,SAAS,KAAK,iBACrB,KAAK,aAAa,EAKpB,KAAK,oBAAoB,MAAM,IAExB,KAAK,SAEZ,EAAkB,IAAI,YAAY,IAClC,EAAkB,IAAI,SAAS,IAC/B,EAAkB,IAAI,cAAc,IACpC,EAAkB,IAAI,mBAAmB,IACzC,EAAkB,IAAI,aAAa,IACnC,EAAkB,IAAI,MAAM,IAC5B,EAAkB,IAAI,iBAAiB,KAEvC,KAAK,gBAAgB;;CAK3B,oBAA0C;AAMxC,EALA,MAAM,mBAAmB,EACzB,KAAK,aAAa,QAAQ,UAAU,EACpC,KAAK,aAAa,WAAW,OAAO,EACpC,KAAK,iBAAiB,gBAAgB,KAAK,mBAAmB,EAC9D,KAAK,iBAAiB,UAAU,KAAK,aAAa,EAClD,KAAK,iBAAiB,iBAAiB,KAAK,oBAAoB;;CAGlE,uBAA6C;AAc3C,EAbA,MAAM,sBAAsB,EAC5B,KAAK,oBAAoB,gBAAgB,KAAK,mBAAmB,EACjE,KAAK,oBAAoB,UAAU,KAAK,aAAa,EACrD,KAAK,oBAAoB,iBAAiB,KAAK,oBAAoB,EAGnE,SAAS,oBAAoB,WAAW,KAAK,cAAc,EACvD,KAAK,4BAA4B,SACnC,aAAa,KAAK,wBAAwB,EAC1C,KAAK,0BAA0B,OAIjC,KAAK,uBAAuB;;;oBA7fgB,kBAQE,MAO/C,EAAS;CAAE,MAAM;CAAQ,SAAS;CAAM,CAAC,CAAA,EAAA,EAAA,WAAA,WAAA,KAAA,EAAA,KAUzC,EAAS;CAAE,MAAM;CAAQ,SAAS;CAAM,CAAC,CAAA,EAAA,EAAA,WAAA,aAAA,KAAA,EAAA,KAQzC,EAAS;CAAE,MAAM;CAAS,SAAS;CAAM,CAAC,CAAA,EAAA,EAAA,WAAA,QAAA,KAAA,EAAA,KAM1C,EAAS;CAAE,WAAW;CAAO,MAAM;CAAQ,CAAC,CAAA,EAAA,EAAA,WAAA,OAAA,KAAA,EAAA,KAS5C,EAAS,EAAE,WAAW,IAAO,CAAC,CAAA,EAAA,EAAA,WAAA,kBAAA,KAAA,EAAA,KAS9B,EAAS;CAAE,MAAM;CAAQ,SAAS;CAAM,CAAC,CAAA,EAAA,EAAA,WAAA,SAAA,KAAA,EAAA,KAWzC,EAAS;CAAE,MAAM;CAAS,SAAS;CAAM,CAAC,CAAA,EAAA,EAAA,WAAA,YAAA,KAAA,EAAA,KAS1C,EAAS;CAAE,MAAM;CAAS,SAAS;CAAM,CAAC,CAAA,EAAA,EAAA,WAAA,UAAA,KAAA,EAAA,KAQ1C,EAAS,EAAE,MAAM,QAAQ,CAAC,CAAA,EAAA,EAAA,WAAA,UAAA,KAAA,EAAA,KAQ1B,EAAS;CAAE,MAAM;CAAQ,WAAW;CAAgB,CAAC,CAAA,EAAA,EAAA,WAAA,eAAA,KAAA,EAAA,KAQrD,EAAS;CAAE,MAAM;CAAQ,WAAW;CAAqB,CAAC,CAAA,EAAA,EAAA,WAAA,oBAAA,KAAA,EAAA,KAS1D,EAAS;CAAE,MAAM;CAAS,WAAW;CAAe,CAAC,CAAA,EAAA,EAAA,WAAA,cAAA,KAAA,EAAA,KASrD,EAAS;CAAE,MAAM;CAAS,SAAS;CAAM,CAAC,CAAA,EAAA,EAAA,WAAA,YAAA,KAAA,EAAA"}
1
+ {"version":3,"file":"Tooltip.base.js","names":[],"sources":["../../../components/tooltip/Tooltip.base.ts"],"sourcesContent":["/**\n * Copyright 2026 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\nimport { PropertyValues } from 'lit';\nimport { property } from 'lit/decorators.js';\n\nimport { SpectrumElement } from '@adobe/spectrum-wc-core/element/index.js';\nimport {\n isTopDismissible,\n registerDismissible,\n unregisterDismissible,\n validateEnum,\n warnIf,\n} from '@adobe/spectrum-wc-core/utils/index.js';\n\nimport {\n HoverController,\n type HoverControllerHost,\n} from '../../controllers/hover-controller/index.js';\nimport {\n fromFloatingPlacement,\n type Placement as ControllerPlacement,\n PlacementController,\n toFloatingPlacement,\n} from '../../controllers/placement-controller/index.js';\nimport {\n TOOLTIP_PLACEMENTS,\n TOOLTIP_VARIANTS,\n type TooltipPlacement,\n type TooltipVariant,\n} from './Tooltip.types.js';\n\n// Currently shown tooltip. `popover=\"manual\"` drops the native auto singleton, so\n// restore it explicitly: one tooltip open at a time across all instances.\nlet openTooltip: TooltipBase | null = null;\n\n/**\n * Abstract base class for the Tooltip component.\n * Handles all non-rendering logic: property declarations, popover lifecycle,\n * event dispatch, trigger resolution, ARIA wiring, `HoverController` integration\n * (hover/focus open/close), and `PlacementController` integration (pixel positioning).\n *\n * @slot - Text label displayed in the tooltip.\n *\n * @fires swc-open - Dispatched when the tooltip begins to open, before the transition plays.\n * @fires swc-close - Dispatched when the tooltip begins to close, before the transition plays.\n * @fires swc-after-open - Dispatched after the tooltip finishes opening, once the transition completes.\n * @fires swc-after-close - Dispatched after the tooltip finishes closing, once the transition completes.\n */\nexport abstract class TooltipBase\n extends SpectrumElement\n implements HoverControllerHost\n{\n // ──────────────────\n // SHARED API\n // ──────────────────\n\n /**\n * @internal\n *\n * All valid variants for the tooltip.\n */\n static readonly VARIANTS: readonly string[] = TOOLTIP_VARIANTS;\n\n /**\n * @internal\n *\n * All valid placement values: physical cardinals (`top`, `bottom`, `left`, `right`)\n * and logical inline values (`start`, `end`).\n */\n static readonly PLACEMENTS: readonly string[] = TOOLTIP_PLACEMENTS;\n\n /**\n * The semantic variant of the tooltip.\n *\n * @default 'neutral'\n */\n @property({ type: String, reflect: true })\n public variant: TooltipVariant = 'neutral';\n\n /**\n * Preferred placement of the tooltip relative to its trigger. This is always\n * the consumer's requested side; the resolved physical side (after any\n * viewport-driven flip) is reflected as `actual-placement`.\n *\n * @default 'top'\n */\n @property({ type: String, reflect: true })\n public placement: TooltipPlacement = 'top';\n\n /**\n * Whether the tooltip is visible.\n *\n * @default false\n */\n @property({ type: Boolean, reflect: true })\n public open: boolean = false;\n\n /**\n * The `id` of the trigger element in the same document tree root.\n */\n @property({ attribute: 'for', type: String })\n public for: string | undefined;\n\n /**\n * Explicit trigger element reference; overrides `for` when set.\n * Use when `getElementById` cannot reach the trigger, such as across a shadow boundary.\n *\n * @default null\n */\n @property({ attribute: false })\n public triggerElement: HTMLElement | null = null;\n\n /**\n * Warm-up delay in milliseconds before the tooltip opens on pointer hover.\n * Set to `0` to open immediately. Keyboard focus always opens immediately.\n *\n * @default 1500\n */\n @property({ type: Number, reflect: true })\n public delay: number = 1500;\n\n /**\n * When set, the tooltip does not respond to hover or focus events and cannot\n * be opened. `disabled` takes priority over `open`: setting `open` to `true`\n * while disabled is a no-op, and disabling an open tooltip closes it. Applies\n * in `manual` mode as well.\n *\n * @default false\n */\n @property({ type: Boolean, reflect: true })\n public disabled: boolean = false;\n\n /**\n * Suppresses automatic hover and focus wiring.\n * The consumer manages visibility via the `open` property or the popover API.\n *\n * @default false\n */\n @property({ type: Boolean, reflect: true })\n public manual: boolean = false;\n\n /**\n * Pixel gap along the placement axis between the trigger and the tooltip bubble.\n *\n * @default 4\n */\n @property({ type: Number })\n public offset: number = 4;\n\n /**\n * Slide along the trigger edge perpendicular to the placement direction, in pixels.\n *\n * @default 0\n */\n @property({ type: Number, attribute: 'cross-offset' })\n public crossOffset: number = 0;\n\n /**\n * Minimum inset from the viewport edge, in pixels, for collision detection.\n *\n * @default 12\n */\n @property({ type: Number, attribute: 'container-padding' })\n public containerPadding: number = 12;\n\n /**\n * Whether the tooltip may reposition to the opposite side when the requested\n * placement does not fit within the viewport.\n *\n * @default true\n */\n @property({ type: Boolean, attribute: 'should-flip' })\n public shouldFlip: boolean = true;\n\n /**\n * When set, the tooltip acts as the trigger's accessible name rather than its description.\n * Use for icon-only triggers where the tooltip text is the sole accessible name.\n *\n * @default false\n */\n @property({ type: Boolean, reflect: true })\n public labeling: boolean = false;\n\n // ──────────────────────\n // IMPLEMENTATION\n // ──────────────────────\n\n private readonly hoverController = new HoverController(this, {\n warmStateKey: 'swc-tooltip',\n });\n\n private readonly placementController = new PlacementController(this);\n\n // Guards dispatchAfterEvent so only the first transitionend per open/close cycle fires,\n // preventing one after event from firing for each CSS property that transitions.\n private afterEventPending = false;\n\n // Fallback timer for browsers that do not fire transitionend for\n // transition-behavior:allow-discrete discrete properties (e.g. Firefox in CI).\n // Cleared when transitionend fires or a new toggle starts.\n private afterEventFallbackTimer: ReturnType<typeof setTimeout> | null = null;\n\n // Tracks the most recently ARIA-wired target so syncAriaRelationship can\n // remove stale references when for or triggerElement changes while open.\n private _lastWiredTrigger:\n | (Element & {\n ariaDescribedByElements: Element[] | null;\n ariaLabelledByElements: Element[] | null;\n })\n | null = null;\n\n /**\n * Returns the tip arrow element to pass to `PlacementController` so the\n * `arrow` middleware keeps the tip aligned with the trigger when the bubble\n * is shifted (e.g. via `crossOffset` or viewport `shift`).\n *\n * Returns `null` in the base class; the SWC rendering layer overrides this\n * to return the actual `.swc-Tooltip-tip` element from the shadow DOM.\n */\n protected get tipElement(): HTMLElement | null {\n return null;\n }\n\n // Reflects the browser's actual popover state. Used in updated() to reconcile\n // `open` against the native popover state: when a `toggle` event syncs `open`\n // (the toggle listener sets it without re-invoking the Popover API), this\n // guard prevents a redundant showPopover()/hidePopover() call in the following\n // update (hidePopover() would throw on an already-closed popover). Under\n // `popover=\"manual\"` the browser never closes the tooltip on its own, so the\n // only driver is the component itself, but the guard is still needed to keep\n // the `open`-driven and toggle-driven paths from double-invoking the API.\n private get isPopoverOpen(): boolean {\n return this.matches(':popover-open');\n }\n\n /**\n * {@link HoverControllerHost} contract. `open` is the single source of truth\n * for visibility; the controller asks to open via this method rather than\n * driving the Popover API directly. `updated()` then reconciles the popover.\n */\n public requestOpen(): void {\n this.open = true;\n }\n\n /**\n * {@link HoverControllerHost} contract. Asks the tooltip to close by setting\n * `open`; `updated()` reconciles the popover.\n */\n public requestClose(): void {\n this.open = false;\n }\n\n // Writes actual-placement to the physical side of the declared `placement`,\n // resolving logical start/end against the trigger's writing direction. Used\n // synchronously before showPopover() so @starting-style has a direction; the\n // PlacementController overwrites it with the flip-resolved side afterwards.\n private setDeclaredActualPlacement(): void {\n const trigger = this.resolveTrigger();\n const direction =\n trigger && getComputedStyle(trigger).direction === 'rtl' ? 'rtl' : 'ltr';\n const side = fromFloatingPlacement(\n toFloatingPlacement(this.placement as ControllerPlacement, direction)\n ).split('-')[0];\n this.setAttribute('actual-placement', side);\n }\n\n private startPlacement(): void {\n const trigger = this.resolveTrigger();\n if (!trigger) {\n return;\n }\n this.placementController.start(trigger, this, {\n placement: this.placement as ControllerPlacement,\n offset: this.offset,\n crossOffset: this.crossOffset,\n containerPadding: this.containerPadding,\n shouldFlip: this.shouldFlip,\n tipElement: this.tipElement ?? undefined,\n onPlacementChange: (resolvedPlacement: ControllerPlacement) => {\n this.setAttribute('actual-placement', resolvedPlacement.split('-')[0]);\n },\n });\n }\n\n private resolveTrigger(): HTMLElement | null {\n if (this.triggerElement) {\n return this.triggerElement;\n }\n if (this.for) {\n const root = this.getRootNode() as Document | ShadowRoot;\n const trigger = root.getElementById(this.for);\n warnIf(\n this,\n !trigger,\n `<${this.localName}> for=\"${this.for}\" did not resolve to an element in the current tree root. Check that the referenced id exists in the same document tree root.`,\n 'https://spectrum-web-components.adobe.com/?path=/docs/components-tooltip--docs',\n { level: 'high' }\n );\n return trigger;\n }\n return null;\n }\n\n // Removes this tooltip from the previously wired target's ARIA element arrays\n // and forgets the reference. Called before re-wiring and on disconnect so a\n // trigger never retains a reference to a detached tooltip node.\n private clearAriaRelationship(): void {\n if (!this._lastWiredTrigger) {\n return;\n }\n const stale = this._lastWiredTrigger;\n stale.ariaDescribedByElements = (\n stale.ariaDescribedByElements ?? []\n ).filter((el) => el !== this);\n stale.ariaLabelledByElements = (stale.ariaLabelledByElements ?? []).filter(\n (el) => el !== this\n );\n this._lastWiredTrigger = null;\n }\n\n private syncAriaRelationship(): void {\n // Remove stale references from the previously wired target before resolving\n // the new one. Handles for/triggerElement changes while the tooltip is open.\n this.clearAriaRelationship();\n\n const trigger = this.resolveTrigger();\n if (!trigger) {\n return;\n }\n const target = (trigger.shadowRoot?.querySelector('button') ??\n trigger) as Element & {\n ariaDescribedByElements: Element[] | null;\n ariaLabelledByElements: Element[] | null;\n };\n\n if (this.labeling) {\n // Remove any stale describedby reference (e.g. if labeling changed while open).\n const described = target.ariaDescribedByElements ?? [];\n target.ariaDescribedByElements = described.filter((el) => el !== this);\n\n const labelled = target.ariaLabelledByElements ?? [];\n target.ariaLabelledByElements = this.open\n ? [...labelled.filter((el) => el !== this), this]\n : labelled.filter((el) => el !== this);\n } else {\n // Remove any stale labelledby reference (e.g. if labeling changed while open).\n const labelled = target.ariaLabelledByElements ?? [];\n target.ariaLabelledByElements = labelled.filter((el) => el !== this);\n\n const described = target.ariaDescribedByElements ?? [];\n target.ariaDescribedByElements = this.open\n ? [...described.filter((el) => el !== this), this]\n : described.filter((el) => el !== this);\n }\n\n if (this.open) {\n this._lastWiredTrigger = target;\n }\n }\n\n private clearPositioningState(): void {\n // Called after the exit transition. Clearing earlier would displace the\n // tooltip to 0,0 while still visible and fading out.\n this.removeAttribute('actual-placement');\n this.style.removeProperty('translate');\n this.style.removeProperty('top');\n this.style.removeProperty('left');\n this.style.removeProperty('--swc-placement-available-width');\n this.style.removeProperty('--swc-placement-available-height');\n if (this.tipElement) {\n this.tipElement.style.removeProperty('translate');\n this.tipElement.style.removeProperty('top');\n this.tipElement.style.removeProperty('left');\n }\n }\n\n private dispatchAfterEvent(isOpen: boolean): void {\n this.dispatchEvent(\n new CustomEvent(isOpen ? 'swc-after-open' : 'swc-after-close', {\n bubbles: true,\n composed: true,\n })\n );\n if (!isOpen) {\n this.clearPositioningState();\n }\n }\n\n private readonly handleBeforeToggle = (event: Event): void => {\n const { newState } = event as ToggleEvent;\n const eventName = newState === 'open' ? 'swc-open' : 'swc-close';\n this.dispatchEvent(\n new CustomEvent(eventName, { bubbles: true, composed: true })\n );\n // Set here so the flag is set regardless of whether this.open already matches\n // newState. handleToggle exits early when open was set externally, which would\n // otherwise leave the flag unset and suppress swc-after-open / swc-after-close.\n this.afterEventPending = true;\n };\n\n private readonly handleToggle = (event: Event): void => {\n // Cancel any in-flight close fallback; a new toggle resets the cycle.\n if (this.afterEventFallbackTimer !== null) {\n clearTimeout(this.afterEventFallbackTimer);\n this.afterEventFallbackTimer = null;\n }\n const { newState } = event as ToggleEvent;\n const isOpen = newState === 'open';\n if (isOpen !== this.open) {\n this.open = isOpen;\n }\n // When no CSS transition is active, dispatch after-* immediately since transitionend will not fire.\n // transitionDuration is comma-separated when multiple properties transition (\"0s, 0s, …\"),\n // so check that every value in the list is zero rather than comparing the full string.\n const durations = getComputedStyle(this).transitionDuration.split(',');\n if (durations.every((d) => d.trim() === '0s')) {\n this.afterEventPending = false;\n this.dispatchAfterEvent(isOpen);\n } else if (!isOpen) {\n // Some browsers (e.g. Firefox in CI) do not fire transitionend for\n // transition-behavior:allow-discrete discrete properties. Start a fallback\n // so positioning is always cleared after the exit transition window.\n const maxMs = Math.max(\n 0,\n ...durations.map((d) => {\n const trimmed = d.trim();\n const value = parseFloat(trimmed);\n return trimmed.endsWith('ms') ? value : value * 1000;\n })\n );\n this.afterEventFallbackTimer = setTimeout(() => {\n this.afterEventFallbackTimer = null;\n if (this.afterEventPending) {\n this.afterEventPending = false;\n this.dispatchAfterEvent(false);\n }\n }, maxMs + 100);\n }\n };\n\n private readonly handleTransitionEnd = (event: TransitionEvent): void => {\n if (event.target !== this || !this.afterEventPending) {\n return;\n }\n if (this.afterEventFallbackTimer !== null) {\n clearTimeout(this.afterEventFallbackTimer);\n this.afterEventFallbackTimer = null;\n }\n this.afterEventPending = false;\n this.dispatchAfterEvent(this.open);\n };\n\n // Escape-to-close. Registered on `document` (capture) only while open (see\n // updated()); `popover=\"manual\"` gets no native light-dismiss, so this is the\n // sole Escape mechanism.\n private readonly handleKeyDown = (event: KeyboardEvent): void => {\n if (event.key !== 'Escape' || !this.open) {\n return;\n }\n // Only the topmost dismissible handles Escape; a surface above us gets it first.\n if (!isTopDismissible(this)) {\n return;\n }\n // A `manual` tooltip is outside the browser's auto-popover stack, so cancel\n // the native default (capture phase, before it runs) to keep an `auto`\n // popover underneath open; a later Escape, once we are gone, closes it.\n event.preventDefault();\n this.open = false;\n };\n\n protected override willUpdate(changedProperties: PropertyValues): void {\n super.willUpdate(changedProperties);\n // `disabled` takes priority over `open`: a disabled tooltip cannot be shown,\n // regardless of how `open` became true (set programmatically, or `disabled`\n // toggled on while already open). Normalizing here — before render and\n // before updated() acts on `open` — keeps `open` and visibility consistent\n // and prevents the `open` attribute from briefly reflecting `true`. Hover\n // and focus opening are separately blocked by HoverController's guards.\n if (this.disabled && this.open) {\n this.open = false;\n }\n\n if (changedProperties.has('variant')) {\n const constructor = this.constructor as typeof TooltipBase;\n validateEnum(this, {\n prop: 'variant',\n value: this.variant,\n valid: constructor.VARIANTS,\n url: 'https://spectrum-web-components.adobe.com/?path=/docs/components-tooltip--docs',\n });\n }\n if (changedProperties.has('placement')) {\n const constructor = this.constructor as typeof TooltipBase;\n validateEnum(this, {\n prop: 'placement',\n value: this.placement,\n valid: constructor.PLACEMENTS,\n url: 'https://spectrum-web-components.adobe.com/?path=/docs/components-tooltip--docs',\n });\n }\n }\n\n protected override updated(changedProperties: PropertyValues): void {\n super.updated(changedProperties);\n if (changedProperties.has('variant')) {\n const constructor = this.constructor as typeof TooltipBase;\n validateEnum(this, {\n prop: 'variant',\n value: this.variant,\n valid: constructor.VARIANTS,\n url: 'https://spectrum-web-components.adobe.com/?path=/docs/components-tooltip--docs',\n });\n }\n if (changedProperties.has('placement')) {\n const constructor = this.constructor as typeof TooltipBase;\n validateEnum(this, {\n prop: 'placement',\n value: this.placement,\n valid: constructor.PLACEMENTS,\n url: 'https://spectrum-web-components.adobe.com/?path=/docs/components-tooltip--docs',\n });\n }\n if (changedProperties.has('offset')) {\n this.style.setProperty(\n '--_swc-tooltip-animation-distance',\n `${this.offset}px`\n );\n }\n if (\n changedProperties.has('open') ||\n changedProperties.has('labeling') ||\n changedProperties.has('for') ||\n changedProperties.has('triggerElement')\n ) {\n this.syncAriaRelationship();\n }\n if (\n changedProperties.has('for') ||\n changedProperties.has('triggerElement')\n ) {\n this.hoverController.setTarget(this.resolveTrigger());\n }\n\n // Placement controller: start when opening, stop when closing, restart\n // when positioning options or the trigger change while already open.\n const openChanged = changedProperties.has('open');\n if (openChanged) {\n if (this.open) {\n // The single-open singleton restores popover=\"auto\"'s \"one open at a\n // time\" for automatic (hover/focus) tooltips. A `manual` tooltip is\n // consumer-managed and opts out, so several can coexist (e.g. a coach-mark\n // tour that points at multiple controls at once); it neither closes others\n // nor claims the singleton slot.\n if (!this.manual) {\n // Close any other open tooltip first. Assign after requestClose() so\n // the other's updated() (else branch) does not clear the reference we take.\n if (openTooltip && openTooltip !== this) {\n openTooltip.requestClose();\n }\n // eslint-disable-next-line @typescript-eslint/no-this-alias -- singleton registration, not a closure capture\n openTooltip = this;\n }\n // Join the dismissible stack and Escape handling while open (both torn\n // down on close below). Capture phase so preventDefault() beats the\n // native popover light-dismiss (see handleKeyDown).\n registerDismissible(this);\n document.addEventListener('keydown', this.handleKeyDown, {\n capture: true,\n });\n // Set actual-placement to the declared side synchronously, before\n // showPopover(). @starting-style is evaluated by the browser the moment\n // the popover enters the top layer, so the direction-bearing attribute\n // must already be present or the entrance animation has no direction to\n // animate from. PlacementController writes actual-placement again from\n // onPlacementChange, but that is async (it awaits document.fonts.ready),\n // so it lands after showPopover() — too late for @starting-style. The\n // popover is display:none until shown and therefore unmeasurable, so a\n // viewport flip cannot be known here; on a flip the resolved side\n // arrives from onPlacementChange and the slide direction corrects then.\n this.setDeclaredActualPlacement();\n this.startPlacement();\n if (this.open !== this.isPopoverOpen) {\n this.showPopover();\n }\n } else {\n if (openTooltip === this) {\n openTooltip = null;\n }\n unregisterDismissible(this);\n document.removeEventListener('keydown', this.handleKeyDown, {\n capture: true,\n });\n if (this.open !== this.isPopoverOpen) {\n this.hidePopover();\n }\n // Stop autoUpdate immediately so positioning stops tracking the trigger\n // during the exit transition. clearPositioningState() runs after the\n // transition completes via dispatchAfterEvent.\n this.placementController.stop();\n }\n } else if (this.open) {\n const placementOptsChanged =\n changedProperties.has('placement') ||\n changedProperties.has('offset') ||\n changedProperties.has('crossOffset') ||\n changedProperties.has('containerPadding') ||\n changedProperties.has('shouldFlip') ||\n changedProperties.has('for') ||\n changedProperties.has('triggerElement');\n if (placementOptsChanged) {\n this.startPlacement();\n }\n }\n }\n\n public override connectedCallback(): void {\n super.connectedCallback();\n this.setAttribute('role', 'tooltip');\n // `manual`, not `auto`: a tooltip must coexist with an open `swc-popover`\n // (or menu/picker/select). Under `popover=\"auto\"` the native light-dismiss\n // group closes every other open auto popover the moment a tooltip opens on\n // hover, tearing down an open popover the user is still working in. `manual`\n // takes the tooltip out of that group. The behaviors `manual` drops are\n // already handled internally: Escape via handleKeyDown (registered while\n // open) and close-on-leave via HoverController; show/hide is driven\n // explicitly through showPopover()/hidePopover() in updated().\n this.setAttribute('popover', 'manual');\n this.addEventListener('beforetoggle', this.handleBeforeToggle);\n this.addEventListener('toggle', this.handleToggle);\n this.addEventListener('transitionend', this.handleTransitionEnd);\n }\n\n public override disconnectedCallback(): void {\n super.disconnectedCallback();\n // Release the singleton so a detached node is not retained.\n if (openTooltip === this) {\n openTooltip = null;\n }\n this.removeEventListener('beforetoggle', this.handleBeforeToggle);\n this.removeEventListener('toggle', this.handleToggle);\n this.removeEventListener('transitionend', this.handleTransitionEnd);\n // Defensive: clear the stack entry and keydown listener in case the tooltip\n // is disconnected while still open (normally cleared on close).\n unregisterDismissible(this);\n document.removeEventListener('keydown', this.handleKeyDown, {\n capture: true,\n });\n if (this.afterEventFallbackTimer !== null) {\n clearTimeout(this.afterEventFallbackTimer);\n this.afterEventFallbackTimer = null;\n }\n // If the tooltip is removed while open, the trigger would otherwise retain a\n // reference to this now-detached node in its ARIA element arrays.\n this.clearAriaRelationship();\n }\n}\n"],"mappings":"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"}