@adobe/spectrum-wc-core 2.0.0-beta.2 → 2.0.0-snapshot-test.20260728023631

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 (51) hide show
  1. package/dist/components/accordion/AccordionItem.base.d.ts +16 -6
  2. package/dist/components/accordion/AccordionItem.base.js +6 -3
  3. package/dist/components/accordion/AccordionItem.base.js.map +1 -1
  4. package/dist/components/badge/Badge.base.d.ts +24 -8
  5. package/dist/components/badge/Badge.base.js +8 -5
  6. package/dist/components/badge/Badge.base.js.map +1 -1
  7. package/dist/components/button/Button.base.d.ts +16 -9
  8. package/dist/components/button/Button.base.js +7 -5
  9. package/dist/components/button/Button.base.js.map +1 -1
  10. package/dist/controllers/index.d.ts +2 -0
  11. package/dist/controllers/index.js +8 -6
  12. package/dist/controllers/slot-presence-controller/index.d.ts +12 -0
  13. package/dist/controllers/slot-presence-controller/index.js +2 -0
  14. package/dist/controllers/slot-presence-controller/src/slot-presence-controller.d.ts +57 -0
  15. package/dist/controllers/slot-presence-controller/src/slot-presence-controller.js +44 -0
  16. package/dist/controllers/slot-presence-controller/src/slot-presence-controller.js.map +1 -0
  17. package/dist/controllers/slot-text-controller/index.d.ts +12 -0
  18. package/dist/controllers/slot-text-controller/index.js +2 -0
  19. package/dist/controllers/slot-text-controller/src/slot-text-controller.d.ts +92 -0
  20. package/dist/controllers/slot-text-controller/src/slot-text-controller.js +53 -0
  21. package/dist/controllers/slot-text-controller/src/slot-text-controller.js.map +1 -0
  22. package/dist/element/index.d.ts +1 -1
  23. package/dist/element/index.js +2 -2
  24. package/dist/element/spectrum-element.d.ts +2 -17
  25. package/dist/element/spectrum-element.js +9 -26
  26. package/dist/element/spectrum-element.js.map +1 -1
  27. package/dist/element/version.d.ts +2 -2
  28. package/dist/element/version.js +1 -1
  29. package/dist/element/version.js.map +1 -1
  30. package/dist/mixins/index.d.ts +0 -2
  31. package/dist/mixins/index.js +4 -6
  32. package/dist/mixins/linear-progress-mixin.d.ts +1 -2
  33. package/dist/mixins/linear-progress-mixin.js +5 -5
  34. package/dist/mixins/linear-progress-mixin.js.map +1 -1
  35. package/dist/utils/index.d.ts +1 -0
  36. package/dist/utils/index.js +10 -9
  37. package/dist/utils/is-focus-visible-in-tree.d.ts +33 -0
  38. package/dist/utils/is-focus-visible-in-tree.js +11 -0
  39. package/dist/utils/is-focus-visible-in-tree.js.map +1 -0
  40. package/package.json +30 -16
  41. package/CHANGELOG.md +0 -125
  42. package/dist/_virtual/_@oxc-project_runtime@0.124.0/helpers/defineProperty.js +0 -12
  43. package/dist/_virtual/_@oxc-project_runtime@0.124.0/helpers/toPrimitive.js +0 -14
  44. package/dist/_virtual/_@oxc-project_runtime@0.124.0/helpers/toPropertyKey.js +0 -9
  45. package/dist/_virtual/_@oxc-project_runtime@0.124.0/helpers/typeof.js +0 -11
  46. package/dist/mixins/observe-slot-presence.d.ts +0 -12
  47. package/dist/mixins/observe-slot-presence.js +0 -40
  48. package/dist/mixins/observe-slot-presence.js.map +0 -1
  49. package/dist/mixins/observe-slot-text.d.ts +0 -11
  50. package/dist/mixins/observe-slot-text.js +0 -60
  51. package/dist/mixins/observe-slot-text.js.map +0 -1
@@ -1,10 +1,7 @@
1
1
  import { PropertyValues } from 'lit';
2
+ import { SlotPresenceController } from '../../controllers/slot-presence-controller/index.js';
2
3
  import { SpectrumElement } from '../../element/index.js';
3
4
  import { AccordionHeadingLevel, AccordionSize } from './Accordion.types.js';
4
- declare const AccordionItemBase_base: typeof SpectrumElement & {
5
- new (...args: any[]): import('../../mixins/observe-slot-presence.js').SlotPresenceObservingInterface;
6
- prototype: import('../../mixins/observe-slot-presence.js').SlotPresenceObservingInterface;
7
- };
8
5
  /**
9
6
  * Base class for accordion item components. Manages open/disabled state,
10
7
  * heading level (set by the parent accordion), and the toggle event.
@@ -19,7 +16,14 @@ declare const AccordionItemBase_base: typeof SpectrumElement & {
19
16
  * the toggle button so they remain independently interactive.
20
17
  * @slot - The panel content revealed when the item is open.
21
18
  */
22
- export declare abstract class AccordionItemBase extends AccordionItemBase_base {
19
+ export declare abstract class AccordionItemBase extends SpectrumElement {
20
+ /**
21
+ * Observes whether content is slotted into `[slot="actions"]` so the header
22
+ * can conditionally render the actions container.
23
+ *
24
+ * @internal
25
+ */
26
+ protected slotPresence: SlotPresenceController;
23
27
  /**
24
28
  * Whether the accordion item panel is expanded.
25
29
  */
@@ -50,6 +54,13 @@ export declare abstract class AccordionItemBase extends AccordionItemBase_base {
50
54
  * when the accordion is re-enabled.
51
55
  */
52
56
  protected parentDisabled: boolean;
57
+ /**
58
+ * @internal
59
+ * Whether content is slotted into `[slot="actions"]`. Consumed by the
60
+ * concrete element's `render()` to conditionally render the actions
61
+ * container.
62
+ */
63
+ protected get hasActions(): boolean;
53
64
  /**
54
65
  * @internal
55
66
  * Whether the item may change `open` (expand or collapse). When false, the
@@ -83,4 +94,3 @@ export declare abstract class AccordionItemBase extends AccordionItemBase_base {
83
94
  connectedCallback(): void;
84
95
  disconnectedCallback(): void;
85
96
  }
86
- export {};
@@ -2,11 +2,11 @@ import { SWC_ACCORDION_ITEM_AFTER_CLOSE_EVENT as e, SWC_ACCORDION_ITEM_AFTER_OPE
2
2
  import { __decorate as a } from "../../_virtual/_@oxc-project_runtime@0.124.0/helpers/decorate.js";
3
3
  import { property as o, state as s } from "lit/decorators.js";
4
4
  import { SpectrumElement as c } from "@adobe/spectrum-wc-core/element/index.js";
5
- import { ObserveSlotPresence as l } from "@adobe/spectrum-wc-core/mixins/observe-slot-presence.js";
5
+ import { SlotPresenceController as l } from "@adobe/spectrum-wc-core/controllers/slot-presence-controller/index.js";
6
6
  //#region components/accordion/AccordionItem.base.ts
7
- var u = class extends l(c, "[slot=\"actions\"]") {
7
+ var u = class extends c {
8
8
  constructor(...e) {
9
- super(...e), this.disabled = !1, this._open = !1, this.headingLevel = 3, this.parentDisabled = !1, this.afterEventPending = !1, this.handleTransitionEnd = (e) => {
9
+ super(...e), this.slotPresence = new l(this, "[slot=\"actions\"]"), this.disabled = !1, this._open = !1, this.headingLevel = 3, this.parentDisabled = !1, this.afterEventPending = !1, this.handleTransitionEnd = (e) => {
10
10
  e.target !== this.contentPanel || e.propertyName !== "height" || !this.afterEventPending || (this.afterEventPending = !1, this.dispatchAfterEvent(this.open));
11
11
  }, this.handleTransitionCancel = (e) => {
12
12
  e.target !== this.contentPanel || e.propertyName !== "height" || !this.afterEventPending || (this.afterEventPending = !1, this.dispatchAfterEvent(this.open));
@@ -20,6 +20,9 @@ var u = class extends l(c, "[slot=\"actions\"]") {
20
20
  let t = this._open;
21
21
  this._open = e, e ? this.setAttribute("open", "") : this.removeAttribute("open"), this.requestUpdate("open", t);
22
22
  }
23
+ get hasActions() {
24
+ return this.slotPresence.isPresent;
25
+ }
23
26
  mayExpand() {
24
27
  return !this.disabled && !this.parentDisabled;
25
28
  }
@@ -1 +1 @@
1
- {"version":3,"file":"AccordionItem.base.js","names":[],"sources":["../../../components/accordion/AccordionItem.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 */\n\nimport { PropertyValues } from 'lit';\nimport { property, state } from 'lit/decorators.js';\n\nimport { SpectrumElement } from '@adobe/spectrum-wc-core/element/index.js';\nimport { ObserveSlotPresence } from '@adobe/spectrum-wc-core/mixins/observe-slot-presence.js';\n\nimport {\n type AccordionHeadingLevel,\n type AccordionSize,\n SWC_ACCORDION_ITEM_AFTER_CLOSE_EVENT,\n SWC_ACCORDION_ITEM_AFTER_OPEN_EVENT,\n SWC_ACCORDION_ITEM_CLOSE_EVENT,\n SWC_ACCORDION_ITEM_OPEN_EVENT,\n SWC_ACCORDION_ITEM_TOGGLE_EVENT,\n} from './Accordion.types.js';\n\n/**\n * Base class for accordion item components. Manages open/disabled state,\n * heading level (set by the parent accordion), and the toggle event.\n *\n * @attribute {boolean} open - Whether the accordion item panel is expanded.\n * @attribute {boolean} disabled - Whether the accordion item is disabled.\n * @attribute {AccordionSize} size - Size of the item. Inherited from the parent\n * accordion when slotted; controls the chevron icon when used standalone.\n *\n * @slot label - The heading text for this accordion item.\n * @slot actions - Optional actions rendered adjacent to the heading, outside\n * the toggle button so they remain independently interactive.\n * @slot - The panel content revealed when the item is open.\n */\nexport abstract class AccordionItemBase extends ObserveSlotPresence(\n SpectrumElement,\n '[slot=\"actions\"]'\n) {\n // ──────────────────\n // PUBLIC API\n // ──────────────────\n\n /**\n * Whether the accordion item panel is expanded.\n */\n @property({ type: Boolean, reflect: true })\n public get open(): boolean {\n return this._open;\n }\n\n public set open(value: boolean) {\n if (this.hasUpdated && !this.mayExpand() && value !== this._open) {\n return;\n }\n if (value === this._open) {\n return;\n }\n const oldValue = this._open;\n this._open = value;\n if (value) {\n this.setAttribute('open', '');\n } else {\n this.removeAttribute('open');\n }\n this.requestUpdate('open', oldValue);\n }\n\n /**\n * Whether the accordion item is disabled. A disabled item keeps its header\n * in the tab order but blocks toggling.\n */\n @property({ type: Boolean, reflect: true })\n public disabled: boolean = false;\n\n /**\n * The size of the item. Inherited from the parent accordion; controls which\n * chevron icon is displayed. Has no effect when the item is used standalone.\n */\n @property({ type: String, reflect: true })\n public size?: AccordionSize;\n\n private _open = false;\n\n // ──────────────────────\n // INTERNAL STATE\n // ──────────────────────\n\n /**\n * @internal\n * Heading level (2–6) propagated by the parent accordion. Defaults to 3\n * for standalone items.\n */\n @state()\n protected headingLevel: AccordionHeadingLevel = 3;\n\n /**\n * @internal\n * Set by the parent accordion when its own `disabled` is true. Causes the\n * item to render as disabled (aria-disabled + inert panel) without clobbering\n * the item's own `disabled` property, so the per-item state is preserved\n * when the accordion is re-enabled.\n */\n @state()\n protected parentDisabled: boolean = false;\n\n // ──────────────────────\n // IMPLEMENTATION\n // ──────────────────────\n\n /**\n * @internal\n * Whether the item may change `open` (expand or collapse). When false, the\n * `open` setter and `toggle()` leave state unchanged.\n */\n protected mayExpand(): boolean {\n return !this.disabled && !this.parentDisabled;\n }\n\n private get contentPanel(): HTMLElement | null {\n return this.shadowRoot?.getElementById('content') ?? null;\n }\n\n private dispatchAfterEvent(isOpen: boolean): void {\n this.dispatchEvent(\n new Event(\n isOpen\n ? SWC_ACCORDION_ITEM_AFTER_OPEN_EVENT\n : SWC_ACCORDION_ITEM_AFTER_CLOSE_EVENT,\n { bubbles: true, composed: true }\n )\n );\n }\n\n // Guards dispatchAfterEvent so only one after-event fires per open/close\n // cycle. transitionend fires once per CSS property; height is the only match.\n private afterEventPending = false;\n\n private readonly handleTransitionEnd = (event: TransitionEvent): void => {\n if (\n event.target !== this.contentPanel ||\n event.propertyName !== 'height' ||\n !this.afterEventPending\n ) {\n return;\n }\n this.afterEventPending = false;\n this.dispatchAfterEvent(this.open);\n };\n\n // Reused for transitioncancel: an externally interrupted transition must still\n // resolve afterEventPending so the after-event is not silently lost.\n private readonly handleTransitionCancel = (event: TransitionEvent): void => {\n if (\n event.target !== this.contentPanel ||\n event.propertyName !== 'height' ||\n !this.afterEventPending\n ) {\n return;\n }\n this.afterEventPending = false;\n this.dispatchAfterEvent(this.open);\n };\n\n /**\n * @internal\n * Toggles the item open state. Guards for disabled, flips `open`, dispatches\n * the toggle event, and reverts if the event is canceled. On success, dispatches\n * `swc-after-open` or `swc-after-close` after the panel height transition ends,\n * or immediately if no transition is active.\n */\n protected toggle(): void {\n if (!this.mayExpand()) {\n return;\n }\n this.open = !this.open;\n const toggleEvent = new Event(SWC_ACCORDION_ITEM_TOGGLE_EVENT, {\n bubbles: true,\n composed: true,\n cancelable: true,\n });\n if (!this.dispatchEvent(toggleEvent)) {\n this.open = !this.open;\n return;\n }\n const isOpen = this.open;\n this.dispatchEvent(\n new Event(\n isOpen ? SWC_ACCORDION_ITEM_OPEN_EVENT : SWC_ACCORDION_ITEM_CLOSE_EVENT,\n {\n bubbles: true,\n composed: true,\n }\n )\n );\n const panel = this.contentPanel;\n if (!panel || getComputedStyle(panel).transitionDuration === '0s') {\n this.dispatchAfterEvent(isOpen);\n } else {\n this.afterEventPending = true;\n }\n }\n\n /**\n * @internal\n * Synchronizes parent-managed heading level onto the item.\n */\n public setManagedHeading(heading: AccordionHeadingLevel): void {\n this.headingLevel = heading;\n }\n\n /**\n * @internal\n * Synchronizes parent-managed disabled state onto the item.\n */\n public setManagedParentDisabled(disabled: boolean): void {\n this.parentDisabled = disabled;\n }\n\n protected override firstUpdated(changedProperties: PropertyValues): void {\n super.firstUpdated(changedProperties);\n this.contentPanel?.addEventListener(\n 'transitionend',\n this.handleTransitionEnd\n );\n this.contentPanel?.addEventListener(\n 'transitioncancel',\n this.handleTransitionCancel\n );\n }\n\n public override connectedCallback(): void {\n super.connectedCallback();\n if (this.hasUpdated) {\n this.contentPanel?.addEventListener(\n 'transitionend',\n this.handleTransitionEnd\n );\n this.contentPanel?.addEventListener(\n 'transitioncancel',\n this.handleTransitionCancel\n );\n }\n }\n\n public override disconnectedCallback(): void {\n super.disconnectedCallback();\n this.contentPanel?.removeEventListener(\n 'transitionend',\n this.handleTransitionEnd\n );\n this.contentPanel?.removeEventListener(\n 'transitioncancel',\n this.handleTransitionCancel\n );\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AccordionItem.base.js","names":[],"sources":["../../../components/accordion/AccordionItem.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 */\n\nimport { PropertyValues } from 'lit';\nimport { property, state } from 'lit/decorators.js';\n\nimport { SlotPresenceController } from '@adobe/spectrum-wc-core/controllers/slot-presence-controller/index.js';\nimport { SpectrumElement } from '@adobe/spectrum-wc-core/element/index.js';\n\nimport {\n type AccordionHeadingLevel,\n type AccordionSize,\n SWC_ACCORDION_ITEM_AFTER_CLOSE_EVENT,\n SWC_ACCORDION_ITEM_AFTER_OPEN_EVENT,\n SWC_ACCORDION_ITEM_CLOSE_EVENT,\n SWC_ACCORDION_ITEM_OPEN_EVENT,\n SWC_ACCORDION_ITEM_TOGGLE_EVENT,\n} from './Accordion.types.js';\n\n/**\n * Base class for accordion item components. Manages open/disabled state,\n * heading level (set by the parent accordion), and the toggle event.\n *\n * @attribute {boolean} open - Whether the accordion item panel is expanded.\n * @attribute {boolean} disabled - Whether the accordion item is disabled.\n * @attribute {AccordionSize} size - Size of the item. Inherited from the parent\n * accordion when slotted; controls the chevron icon when used standalone.\n *\n * @slot label - The heading text for this accordion item.\n * @slot actions - Optional actions rendered adjacent to the heading, outside\n * the toggle button so they remain independently interactive.\n * @slot - The panel content revealed when the item is open.\n */\nexport abstract class AccordionItemBase extends SpectrumElement {\n // ──────────────────────\n // CONTROLLERS\n // ──────────────────────\n\n /**\n * Observes whether content is slotted into `[slot=\"actions\"]` so the header\n * can conditionally render the actions container.\n *\n * @internal\n */\n protected slotPresence = new SlotPresenceController(this, '[slot=\"actions\"]');\n\n // ──────────────────\n // PUBLIC API\n // ──────────────────\n\n /**\n * Whether the accordion item panel is expanded.\n */\n @property({ type: Boolean, reflect: true })\n public get open(): boolean {\n return this._open;\n }\n\n public set open(value: boolean) {\n if (this.hasUpdated && !this.mayExpand() && value !== this._open) {\n return;\n }\n if (value === this._open) {\n return;\n }\n const oldValue = this._open;\n this._open = value;\n if (value) {\n this.setAttribute('open', '');\n } else {\n this.removeAttribute('open');\n }\n this.requestUpdate('open', oldValue);\n }\n\n /**\n * Whether the accordion item is disabled. A disabled item keeps its header\n * in the tab order but blocks toggling.\n */\n @property({ type: Boolean, reflect: true })\n public disabled: boolean = false;\n\n /**\n * The size of the item. Inherited from the parent accordion; controls which\n * chevron icon is displayed. Has no effect when the item is used standalone.\n */\n @property({ type: String, reflect: true })\n public size?: AccordionSize;\n\n private _open = false;\n\n // ──────────────────────\n // INTERNAL STATE\n // ──────────────────────\n\n /**\n * @internal\n * Heading level (2–6) propagated by the parent accordion. Defaults to 3\n * for standalone items.\n */\n @state()\n protected headingLevel: AccordionHeadingLevel = 3;\n\n /**\n * @internal\n * Set by the parent accordion when its own `disabled` is true. Causes the\n * item to render as disabled (aria-disabled + inert panel) without clobbering\n * the item's own `disabled` property, so the per-item state is preserved\n * when the accordion is re-enabled.\n */\n @state()\n protected parentDisabled: boolean = false;\n\n // ──────────────────────\n // IMPLEMENTATION\n // ──────────────────────\n\n /**\n * @internal\n * Whether content is slotted into `[slot=\"actions\"]`. Consumed by the\n * concrete element's `render()` to conditionally render the actions\n * container.\n */\n protected get hasActions(): boolean {\n return this.slotPresence.isPresent;\n }\n\n /**\n * @internal\n * Whether the item may change `open` (expand or collapse). When false, the\n * `open` setter and `toggle()` leave state unchanged.\n */\n protected mayExpand(): boolean {\n return !this.disabled && !this.parentDisabled;\n }\n\n private get contentPanel(): HTMLElement | null {\n return this.shadowRoot?.getElementById('content') ?? null;\n }\n\n private dispatchAfterEvent(isOpen: boolean): void {\n this.dispatchEvent(\n new Event(\n isOpen\n ? SWC_ACCORDION_ITEM_AFTER_OPEN_EVENT\n : SWC_ACCORDION_ITEM_AFTER_CLOSE_EVENT,\n { bubbles: true, composed: true }\n )\n );\n }\n\n // Guards dispatchAfterEvent so only one after-event fires per open/close\n // cycle. transitionend fires once per CSS property; height is the only match.\n private afterEventPending = false;\n\n private readonly handleTransitionEnd = (event: TransitionEvent): void => {\n if (\n event.target !== this.contentPanel ||\n event.propertyName !== 'height' ||\n !this.afterEventPending\n ) {\n return;\n }\n this.afterEventPending = false;\n this.dispatchAfterEvent(this.open);\n };\n\n // Reused for transitioncancel: an externally interrupted transition must still\n // resolve afterEventPending so the after-event is not silently lost.\n private readonly handleTransitionCancel = (event: TransitionEvent): void => {\n if (\n event.target !== this.contentPanel ||\n event.propertyName !== 'height' ||\n !this.afterEventPending\n ) {\n return;\n }\n this.afterEventPending = false;\n this.dispatchAfterEvent(this.open);\n };\n\n /**\n * @internal\n * Toggles the item open state. Guards for disabled, flips `open`, dispatches\n * the toggle event, and reverts if the event is canceled. On success, dispatches\n * `swc-after-open` or `swc-after-close` after the panel height transition ends,\n * or immediately if no transition is active.\n */\n protected toggle(): void {\n if (!this.mayExpand()) {\n return;\n }\n this.open = !this.open;\n const toggleEvent = new Event(SWC_ACCORDION_ITEM_TOGGLE_EVENT, {\n bubbles: true,\n composed: true,\n cancelable: true,\n });\n if (!this.dispatchEvent(toggleEvent)) {\n this.open = !this.open;\n return;\n }\n const isOpen = this.open;\n this.dispatchEvent(\n new Event(\n isOpen ? SWC_ACCORDION_ITEM_OPEN_EVENT : SWC_ACCORDION_ITEM_CLOSE_EVENT,\n {\n bubbles: true,\n composed: true,\n }\n )\n );\n const panel = this.contentPanel;\n if (!panel || getComputedStyle(panel).transitionDuration === '0s') {\n this.dispatchAfterEvent(isOpen);\n } else {\n this.afterEventPending = true;\n }\n }\n\n /**\n * @internal\n * Synchronizes parent-managed heading level onto the item.\n */\n public setManagedHeading(heading: AccordionHeadingLevel): void {\n this.headingLevel = heading;\n }\n\n /**\n * @internal\n * Synchronizes parent-managed disabled state onto the item.\n */\n public setManagedParentDisabled(disabled: boolean): void {\n this.parentDisabled = disabled;\n }\n\n protected override firstUpdated(changedProperties: PropertyValues): void {\n super.firstUpdated(changedProperties);\n this.contentPanel?.addEventListener(\n 'transitionend',\n this.handleTransitionEnd\n );\n this.contentPanel?.addEventListener(\n 'transitioncancel',\n this.handleTransitionCancel\n );\n }\n\n public override connectedCallback(): void {\n super.connectedCallback();\n if (this.hasUpdated) {\n this.contentPanel?.addEventListener(\n 'transitionend',\n this.handleTransitionEnd\n );\n this.contentPanel?.addEventListener(\n 'transitioncancel',\n this.handleTransitionCancel\n );\n }\n }\n\n public override disconnectedCallback(): void {\n super.disconnectedCallback();\n this.contentPanel?.removeEventListener(\n 'transitionend',\n this.handleTransitionEnd\n );\n this.contentPanel?.removeEventListener(\n 'transitioncancel',\n this.handleTransitionCancel\n );\n }\n}\n"],"mappings":"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"}
@@ -1,13 +1,9 @@
1
1
  import { PropertyValues } from 'lit';
2
+ import { SlotPresenceController } from '../../controllers/slot-presence-controller/index.js';
3
+ import { SlotTextController } from '../../controllers/slot-text-controller/index.js';
2
4
  import { SpectrumElement } from '../../element/index.js';
3
5
  import { BadgeSize, BadgeVariant, FixedValues } from './Badge.types.js';
4
6
  declare const BadgeBase_base: typeof SpectrumElement & {
5
- new (...args: any[]): import('../../mixins/observe-slot-presence.js').SlotPresenceObservingInterface;
6
- prototype: import('../../mixins/observe-slot-presence.js').SlotPresenceObservingInterface;
7
- } & {
8
- new (...args: any[]): import('../../mixins/observe-slot-text.js').SlotTextObservingInterface;
9
- prototype: import('../../mixins/observe-slot-text.js').SlotTextObservingInterface;
10
- } & {
11
7
  new (...args: any[]): import('../../mixins/index.js').SizedElementInterface;
12
8
  prototype: import('../../mixins/index.js').SizedElementInterface;
13
9
  } & import('../../mixins/index.js').SizedElementConstructor;
@@ -19,8 +15,6 @@ declare const BadgeBase_base: typeof SpectrumElement & {
19
15
  *
20
16
  * @slot - Text label of the badge.
21
17
  * @slot icon - Optional icon that appears to the left of the label
22
- *
23
- * @todo review the mixin composition here. We currently have 3 levels of mixins on this class, but the mixin composition guide recommends a maximum of 2.
24
18
  */
25
19
  export declare abstract class BadgeBase extends BadgeBase_base {
26
20
  /**
@@ -87,12 +81,34 @@ export declare abstract class BadgeBase extends BadgeBase_base {
87
81
  * Can only be used with semantic variants.
88
82
  */
89
83
  outline: boolean;
84
+ /**
85
+ * Observes whether an icon is slotted into `[slot="icon"]`.
86
+ *
87
+ * @internal
88
+ */
89
+ protected slotPresence: SlotPresenceController;
90
+ /**
91
+ * Observes whether the default slot has meaningful text content. The default
92
+ * slot must bind `@slotchange=${this.slotText.handleSlotChange}` for changes
93
+ * after the first render to be tracked.
94
+ *
95
+ * @internal
96
+ */
97
+ protected slotText: SlotTextController;
90
98
  /**
91
99
  * @internal
92
100
  *
93
101
  * Used for rendering gap when the badge has an icon.
94
102
  */
95
103
  protected get hasIcon(): boolean;
104
+ /**
105
+ * Whether the default slot has meaningful text content. Consumed by the
106
+ * concrete element's `render()` to toggle icon-only presentation (the
107
+ * `swc-Badge--no-label` class and its icon-only padding tokens).
108
+ *
109
+ * @internal
110
+ */
111
+ protected get slotHasContent(): boolean;
96
112
  protected update(changedProperties: PropertyValues): void;
97
113
  }
98
114
  export {};
@@ -3,18 +3,21 @@ import { BADGE_VALID_SIZES as t, BADGE_VARIANTS_SEMANTIC as n, FIXED_VALUES as r
3
3
  import { property as i } from "lit/decorators.js";
4
4
  import { SpectrumElement as a } from "@adobe/spectrum-wc-core/element/index.js";
5
5
  import { SizedMixin as o } from "@adobe/spectrum-wc-core/mixins/index.js";
6
- import { ObserveSlotPresence as s } from "@adobe/spectrum-wc-core/mixins/observe-slot-presence.js";
7
- import { ObserveSlotText as c } from "@adobe/spectrum-wc-core/mixins/observe-slot-text.js";
6
+ import { SlotPresenceController as s } from "@adobe/spectrum-wc-core/controllers/slot-presence-controller/index.js";
7
+ import { SlotTextController as c } from "@adobe/spectrum-wc-core/controllers/slot-text-controller/index.js";
8
8
  //#region components/badge/Badge.base.ts
9
- var l, u = class extends o(c(s(a, "[slot=\"icon\"]"), ""), {
9
+ var l, u = class extends o(a, {
10
10
  validSizes: t,
11
11
  defaultSize: "s"
12
12
  }) {
13
13
  constructor(...e) {
14
- super(...e), this.variant = "neutral", this.subtle = !1, this.outline = !1;
14
+ super(...e), this.variant = "neutral", this.subtle = !1, this.outline = !1, this.slotPresence = new s(this, "[slot=\"icon\"]"), this.slotText = new c(this);
15
15
  }
16
16
  get hasIcon() {
17
- return this.slotContentIsPresent;
17
+ return this.slotPresence.isPresent;
18
+ }
19
+ get slotHasContent() {
20
+ return this.slotText.hasContent;
18
21
  }
19
22
  update(e) {
20
23
  var t;
@@ -1 +1 @@
1
- {"version":3,"file":"Badge.base.js","names":[],"sources":["../../../components/badge/Badge.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 { SizedMixin } from '@adobe/spectrum-wc-core/mixins/index.js';\nimport { ObserveSlotPresence } from '@adobe/spectrum-wc-core/mixins/observe-slot-presence.js';\nimport { ObserveSlotText } from '@adobe/spectrum-wc-core/mixins/observe-slot-text.js';\n\nimport {\n BADGE_VALID_SIZES,\n BADGE_VARIANTS_SEMANTIC,\n type BadgeSize,\n type BadgeVariant,\n FIXED_VALUES,\n type FixedValues,\n} from './Badge.types.js';\n\n/**\n * A badge component that displays short, descriptive information about an element.\n * Badges are typically used to indicate status, categories, or provide supplementary information.\n *\n * @attribute {ElementSize} size - The size of the badge.\n *\n * @slot - Text label of the badge.\n * @slot icon - Optional icon that appears to the left of the label\n *\n * @todo review the mixin composition here. We currently have 3 levels of mixins on this class, but the mixin composition guide recommends a maximum of 2.\n */\nexport abstract class BadgeBase extends SizedMixin(\n ObserveSlotText(ObserveSlotPresence(SpectrumElement, '[slot=\"icon\"]'), ''),\n {\n validSizes: BADGE_VALID_SIZES,\n defaultSize: 's',\n }\n) {\n /**\n * The size of the badge.\n *\n * @default s\n */\n declare public size: BadgeSize;\n\n // ─────────────────────────\n // API TO OVERRIDE\n // ─────────────────────────\n\n /**\n * @internal\n *\n * A readonly array of the valid color variants for the badge.\n *\n * This is an actual internal property, intended not for customer use\n * but for use in internal validation logic, stories, tests, etc.\n *\n * Because S1 and S2 support different color variants, the value of this\n * property must be set in each subclass.\n */\n static readonly VARIANTS_COLOR: readonly string[];\n\n /**\n * @internal\n *\n * A readonly array of all valid variants for the badge.\n *\n * This is an actual internal property, intended not for customer use\n * but for use in internal validation logic, stories, tests, etc.\n *\n * Because S1 and S2 support different variants, the value of this\n * property must be set in each subclass.\n */\n static readonly VARIANTS: readonly string[];\n\n /**\n * @internal\n *\n * The variant of the badge.\n *\n * This is a public property, but its valid values vary between S1 and S2,\n * so the property (and its docs) need to be redefined in each subclass.\n *\n * The type declared here is a union of the valid values for S1 and S2,\n * and should be narrowed in each subclass.\n */\n @property({ type: String, reflect: true })\n public variant: BadgeVariant = 'neutral';\n\n // ──────────────────\n // SHARED API\n // ──────────────────\n\n /**\n * @internal\n */\n static readonly FIXED_VALUES: readonly string[] = FIXED_VALUES;\n\n /**\n * @internal\n */\n static readonly VARIANTS_SEMANTIC: readonly string[] =\n BADGE_VARIANTS_SEMANTIC;\n\n /**\n * The fixed position of the badge.\n */\n @property({ type: String, reflect: true })\n public fixed?: FixedValues;\n\n /**\n * Whether the badge is subtle.\n */\n @property({ type: Boolean, reflect: true })\n public subtle: boolean = false;\n\n /**\n * Whether the badge is outlined.\n *\n * Can only be used with semantic variants.\n */\n @property({ type: Boolean, reflect: true })\n public outline: boolean = false;\n\n // ──────────────────────\n // IMPLEMENTATION\n // ──────────────────────\n\n /**\n * @internal\n *\n * Used for rendering gap when the badge has an icon.\n */\n protected get hasIcon(): boolean {\n return this.slotContentIsPresent;\n }\n\n protected override update(changedProperties: PropertyValues): void {\n if (window.__swc?.DEBUG) {\n const constructor = this.constructor as typeof BadgeBase;\n if (!constructor.VARIANTS.includes(this.variant)) {\n window.__swc.warn(\n this,\n `<${this.localName}> element expects the \"variant\" attribute to be one of the following:`,\n 'https://opensource.adobe.com/spectrum-web-components/components/badge/#variants',\n {\n issues: [...constructor.VARIANTS],\n }\n );\n }\n // Check outline property if it exists (S2 only)\n if (\n 'outline' in this &&\n (this as { outline: boolean }).outline === true &&\n !constructor.VARIANTS_SEMANTIC.includes(this.variant)\n ) {\n window.__swc.warn(\n this,\n `<${this.localName}> element only supports the outline styling if the variant is a semantic color variant.`,\n 'https://opensource.adobe.com/spectrum-web-components/components/badge/#variants',\n {\n issues: [...constructor.VARIANTS_SEMANTIC],\n }\n );\n }\n }\n super.update(changedProperties);\n }\n}\n"],"mappings":";;;;;;;;OAuCsB,IAAtB,cAAwC,EACtC,EAAgB,EAAoB,GAAiB,kBAAgB,EAAE,GAAG,EAC1E;CACE,YAAY;CACZ,aAAa;CACd,CACF,CAAC;;8BAkD+B,yBA2BN,mBAQC;;CAW1B,IAAc,UAAmB;AAC/B,SAAO,KAAK;;CAGd,OAA0B,GAAyC;;AACjE,OAAA,IAAI,OAAO,UAAA,QAAA,EAAO,OAAO;GACvB,IAAM,IAAc,KAAK;AAYzB,GAXK,EAAY,SAAS,SAAS,KAAK,QAAQ,IAC9C,OAAO,MAAM,KACX,MACA,IAAI,KAAK,UAAU,wEACnB,mFACA,EACE,QAAQ,CAAC,GAAG,EAAY,SAAS,EAClC,CACF,EAID,aAAa,QACZ,KAA8B,YAAY,MAC3C,CAAC,EAAY,kBAAkB,SAAS,KAAK,QAAQ,IAErD,OAAO,MAAM,KACX,MACA,IAAI,KAAK,UAAU,0FACnB,mFACA,EACE,QAAQ,CAAC,GAAG,EAAY,kBAAkB,EAC3C,CACF;;AAGL,QAAM,OAAO,EAAkB;;;wBAtEiB,yBAMhD,MAhBD,EAAS;CAAE,MAAM;CAAQ,SAAS;CAAM,CAAC,CAAA,EAAA,EAAA,WAAA,WAAA,KAAA,EAAA,KAqBzC,EAAS;CAAE,MAAM;CAAQ,SAAS;CAAM,CAAC,CAAA,EAAA,EAAA,WAAA,SAAA,KAAA,EAAA,KAMzC,EAAS;CAAE,MAAM;CAAS,SAAS;CAAM,CAAC,CAAA,EAAA,EAAA,WAAA,UAAA,KAAA,EAAA,KAQ1C,EAAS;CAAE,MAAM;CAAS,SAAS;CAAM,CAAC,CAAA,EAAA,EAAA,WAAA,WAAA,KAAA,EAAA"}
1
+ {"version":3,"file":"Badge.base.js","names":[],"sources":["../../../components/badge/Badge.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 { SlotPresenceController } from '@adobe/spectrum-wc-core/controllers/slot-presence-controller/index.js';\nimport { SlotTextController } from '@adobe/spectrum-wc-core/controllers/slot-text-controller/index.js';\nimport { SpectrumElement } from '@adobe/spectrum-wc-core/element/index.js';\nimport { SizedMixin } from '@adobe/spectrum-wc-core/mixins/index.js';\n\nimport {\n BADGE_VALID_SIZES,\n BADGE_VARIANTS_SEMANTIC,\n type BadgeSize,\n type BadgeVariant,\n FIXED_VALUES,\n type FixedValues,\n} from './Badge.types.js';\n\n/**\n * A badge component that displays short, descriptive information about an element.\n * Badges are typically used to indicate status, categories, or provide supplementary information.\n *\n * @attribute {ElementSize} size - The size of the badge.\n *\n * @slot - Text label of the badge.\n * @slot icon - Optional icon that appears to the left of the label\n */\nexport abstract class BadgeBase extends SizedMixin(SpectrumElement, {\n validSizes: BADGE_VALID_SIZES,\n defaultSize: 's',\n}) {\n /**\n * The size of the badge.\n *\n * @default s\n */\n declare public size: BadgeSize;\n\n // ─────────────────────────\n // API TO OVERRIDE\n // ─────────────────────────\n\n /**\n * @internal\n *\n * A readonly array of the valid color variants for the badge.\n *\n * This is an actual internal property, intended not for customer use\n * but for use in internal validation logic, stories, tests, etc.\n *\n * Because S1 and S2 support different color variants, the value of this\n * property must be set in each subclass.\n */\n static readonly VARIANTS_COLOR: readonly string[];\n\n /**\n * @internal\n *\n * A readonly array of all valid variants for the badge.\n *\n * This is an actual internal property, intended not for customer use\n * but for use in internal validation logic, stories, tests, etc.\n *\n * Because S1 and S2 support different variants, the value of this\n * property must be set in each subclass.\n */\n static readonly VARIANTS: readonly string[];\n\n /**\n * @internal\n *\n * The variant of the badge.\n *\n * This is a public property, but its valid values vary between S1 and S2,\n * so the property (and its docs) need to be redefined in each subclass.\n *\n * The type declared here is a union of the valid values for S1 and S2,\n * and should be narrowed in each subclass.\n */\n @property({ type: String, reflect: true })\n public variant: BadgeVariant = 'neutral';\n\n // ──────────────────\n // SHARED API\n // ──────────────────\n\n /**\n * @internal\n */\n static readonly FIXED_VALUES: readonly string[] = FIXED_VALUES;\n\n /**\n * @internal\n */\n static readonly VARIANTS_SEMANTIC: readonly string[] =\n BADGE_VARIANTS_SEMANTIC;\n\n /**\n * The fixed position of the badge.\n */\n @property({ type: String, reflect: true })\n public fixed?: FixedValues;\n\n /**\n * Whether the badge is subtle.\n */\n @property({ type: Boolean, reflect: true })\n public subtle: boolean = false;\n\n /**\n * Whether the badge is outlined.\n *\n * Can only be used with semantic variants.\n */\n @property({ type: Boolean, reflect: true })\n public outline: boolean = false;\n\n // ──────────────────────\n // CONTROLLERS\n // ──────────────────────\n\n /**\n * Observes whether an icon is slotted into `[slot=\"icon\"]`.\n *\n * @internal\n */\n protected slotPresence = new SlotPresenceController(this, '[slot=\"icon\"]');\n\n /**\n * Observes whether the default slot has meaningful text content. The default\n * slot must bind `@slotchange=${this.slotText.handleSlotChange}` for changes\n * after the first render to be tracked.\n *\n * @internal\n */\n protected slotText = new SlotTextController(this);\n\n // ──────────────────────\n // IMPLEMENTATION\n // ──────────────────────\n\n /**\n * @internal\n *\n * Used for rendering gap when the badge has an icon.\n */\n protected get hasIcon(): boolean {\n return this.slotPresence.isPresent;\n }\n\n /**\n * Whether the default slot has meaningful text content. Consumed by the\n * concrete element's `render()` to toggle icon-only presentation (the\n * `swc-Badge--no-label` class and its icon-only padding tokens).\n *\n * @internal\n */\n protected get slotHasContent(): boolean {\n return this.slotText.hasContent;\n }\n\n protected override update(changedProperties: PropertyValues): void {\n if (window.__swc?.DEBUG) {\n const constructor = this.constructor as typeof BadgeBase;\n if (!constructor.VARIANTS.includes(this.variant)) {\n window.__swc.warn(\n this,\n `<${this.localName}> element expects the \"variant\" attribute to be one of the following:`,\n 'https://opensource.adobe.com/spectrum-web-components/components/badge/#variants',\n {\n issues: [...constructor.VARIANTS],\n }\n );\n }\n // Check outline property if it exists (S2 only)\n if (\n 'outline' in this &&\n (this as { outline: boolean }).outline === true &&\n !constructor.VARIANTS_SEMANTIC.includes(this.variant)\n ) {\n window.__swc.warn(\n this,\n `<${this.localName}> element only supports the outline styling if the variant is a semantic color variant.`,\n 'https://opensource.adobe.com/spectrum-web-components/components/badge/#variants',\n {\n issues: [...constructor.VARIANTS_SEMANTIC],\n }\n );\n }\n }\n super.update(changedProperties);\n }\n}\n"],"mappings":";;;;;;;;OAqCsB,IAAtB,cAAwC,EAAW,GAAiB;CAClE,YAAY;CACZ,aAAa;CACd,CAAC,CAAC;;8BAkD8B,yBA2BN,mBAQC,wBAWD,IAAI,EAAuB,MAAM,kBAAgB,kBASrD,IAAI,EAAmB,KAAK;;CAWjD,IAAc,UAAmB;AAC/B,SAAO,KAAK,aAAa;;CAU3B,IAAc,iBAA0B;AACtC,SAAO,KAAK,SAAS;;CAGvB,OAA0B,GAAyC;;AACjE,OAAA,IAAI,OAAO,UAAA,QAAA,EAAO,OAAO;GACvB,IAAM,IAAc,KAAK;AAYzB,GAXK,EAAY,SAAS,SAAS,KAAK,QAAQ,IAC9C,OAAO,MAAM,KACX,MACA,IAAI,KAAK,UAAU,wEACnB,mFACA,EACE,QAAQ,CAAC,GAAG,EAAY,SAAS,EAClC,CACF,EAID,aAAa,QACZ,KAA8B,YAAY,MAC3C,CAAC,EAAY,kBAAkB,SAAS,KAAK,QAAQ,IAErD,OAAO,MAAM,KACX,MACA,IAAI,KAAK,UAAU,0FACnB,mFACA,EACE,QAAQ,CAAC,GAAG,EAAY,kBAAkB,EAC3C,CACF;;AAGL,QAAM,OAAO,EAAkB;;;wBArGiB,yBAMhD,MAhBD,EAAS;CAAE,MAAM;CAAQ,SAAS;CAAM,CAAC,CAAA,EAAA,EAAA,WAAA,WAAA,KAAA,EAAA,KAqBzC,EAAS;CAAE,MAAM;CAAQ,SAAS;CAAM,CAAC,CAAA,EAAA,EAAA,WAAA,SAAA,KAAA,EAAA,KAMzC,EAAS;CAAE,MAAM;CAAS,SAAS;CAAM,CAAC,CAAA,EAAA,EAAA,WAAA,UAAA,KAAA,EAAA,KAQ1C,EAAS;CAAE,MAAM;CAAS,SAAS;CAAM,CAAC,CAAA,EAAA,EAAA,WAAA,WAAA,KAAA,EAAA"}
@@ -1,13 +1,9 @@
1
1
  import { PropertyValues } from 'lit';
2
+ import { SlotPresenceController } from '../../controllers/slot-presence-controller/index.js';
3
+ import { SlotTextController } from '../../controllers/slot-text-controller/index.js';
2
4
  import { SpectrumElement } from '../../element/index.js';
3
5
  import { ButtonSize } from './Button.types.js';
4
6
  declare const ButtonBase_base: typeof SpectrumElement & {
5
- new (...args: any[]): import('../../mixins/index.js').SlotPresenceObservingInterface;
6
- prototype: import('../../mixins/index.js').SlotPresenceObservingInterface;
7
- } & {
8
- new (...args: any[]): import('../../mixins/index.js').SlotTextObservingInterface;
9
- prototype: import('../../mixins/index.js').SlotTextObservingInterface;
10
- } & {
11
7
  new (...args: any[]): import('../../mixins/index.js').SizedElementInterface;
12
8
  prototype: import('../../mixins/index.js').SizedElementInterface;
13
9
  } & import('../../mixins/index.js').SizedElementConstructor;
@@ -22,9 +18,6 @@ declare const ButtonBase_base: typeof SpectrumElement & {
22
18
  * the `swc-button` visual surface.
23
19
  *
24
20
  * @attribute {ElementSize} size - The size of the button.
25
- *
26
- * @todo We currently have 3 levels of mixins on this class, but the mixin
27
- * composition guide recommends a maximum of 2. Explore reducing after milestone 2.
28
21
  */
29
22
  export declare abstract class ButtonBase extends ButtonBase_base {
30
23
  static shadowRootOptions: ShadowRootInit;
@@ -44,6 +37,20 @@ export declare abstract class ButtonBase extends ButtonBase_base {
44
37
  * `aria-label`. Required for icon-only buttons, which have no visible text.
45
38
  */
46
39
  accessibleLabel?: string;
40
+ /**
41
+ * Observes whether an icon is slotted into `[slot="icon"]`.
42
+ *
43
+ * @internal
44
+ */
45
+ protected slotPresence: SlotPresenceController;
46
+ /**
47
+ * Observes whether the default slot has a meaningful text label. The default
48
+ * slot must bind `@slotchange=${this.slotText.handleSlotChange}` for changes
49
+ * after the first render to be tracked.
50
+ *
51
+ * @internal
52
+ */
53
+ protected slotText: SlotTextController;
47
54
  protected get hasIcon(): boolean;
48
55
  protected get hasLabel(): boolean;
49
56
  /**
@@ -2,19 +2,21 @@ import { __decorate as e } from "../../_virtual/_@oxc-project_runtime@0.124.0/he
2
2
  import { BUTTON_VALID_SIZES as t } from "./Button.types.js";
3
3
  import { property as n } from "lit/decorators.js";
4
4
  import { SpectrumElement as r } from "@adobe/spectrum-wc-core/element/index.js";
5
- import { ObserveSlotPresence as i, ObserveSlotText as a, SizedMixin as o } from "@adobe/spectrum-wc-core/mixins/index.js";
5
+ import { SizedMixin as i } from "@adobe/spectrum-wc-core/mixins/index.js";
6
+ import { SlotPresenceController as a } from "@adobe/spectrum-wc-core/controllers/slot-presence-controller/index.js";
7
+ import { SlotTextController as o } from "@adobe/spectrum-wc-core/controllers/slot-text-controller/index.js";
6
8
  //#region components/button/Button.base.ts
7
- var s, c = class extends o(a(i(r, "[slot=\"icon\"]"), ""), { validSizes: t }) {
9
+ var s, c = class extends i(r, { validSizes: t }) {
8
10
  constructor(...e) {
9
- super(...e), this.disabled = !1, this.handleClick = (e) => {
11
+ super(...e), this.disabled = !1, this.slotPresence = new a(this, "[slot=\"icon\"]"), this.slotText = new o(this), this.handleClick = (e) => {
10
12
  this.disabled && (e.preventDefault(), e.stopImmediatePropagation());
11
13
  };
12
14
  }
13
15
  get hasIcon() {
14
- return this.slotContentIsPresent;
16
+ return this.slotPresence.isPresent;
15
17
  }
16
18
  get hasLabel() {
17
- return this.slotHasContent;
19
+ return this.slotText.hasContent;
18
20
  }
19
21
  getForwardedButtonAttributes() {
20
22
  return { disabled: this.disabled };
@@ -1 +1 @@
1
- {"version":3,"file":"Button.base.js","names":[],"sources":["../../../components/button/Button.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 */\n\nimport { PropertyValues } from 'lit';\nimport { property } from 'lit/decorators.js';\n\nimport { SpectrumElement } from '@adobe/spectrum-wc-core/element/index.js';\nimport {\n ObserveSlotPresence,\n ObserveSlotText,\n SizedMixin,\n} from '@adobe/spectrum-wc-core/mixins/index.js';\n\nimport { BUTTON_VALID_SIZES, type ButtonSize } from './Button.types.js';\n\n/**\n * Abstract base class for all button-like components. Owns shared semantic\n * concerns: interaction state, sizing, slot-derived icon/label state,\n * accessible-name resolution, and host-to-internal-button attribute forwarding.\n *\n * Visual API specific to `sp-button` (`variant`, `fill-style`, `static-color`)\n * is intentionally absent so that ActionButton, ClearButton, CloseButton,\n * PickerButton, and InfieldButton can extend this base without inheriting\n * the `swc-button` visual surface.\n *\n * @attribute {ElementSize} size - The size of the button.\n *\n * @todo We currently have 3 levels of mixins on this class, but the mixin\n * composition guide recommends a maximum of 2. Explore reducing after milestone 2.\n */\nexport abstract class ButtonBase extends SizedMixin(\n ObserveSlotText(ObserveSlotPresence(SpectrumElement, '[slot=\"icon\"]'), ''),\n { validSizes: BUTTON_VALID_SIZES }\n) {\n static override shadowRootOptions: ShadowRootInit = {\n ...SpectrumElement.shadowRootOptions,\n delegatesFocus: true,\n };\n\n // ──────────────────\n // SHARED API\n // ──────────────────\n\n /**\n * The size of the button.\n *\n * @default m\n */\n declare public size: ButtonSize;\n\n /**\n * Whether the button is disabled. Removes focusability and prevents\n * interaction.\n */\n @property({ type: Boolean, reflect: true })\n public disabled: boolean = false;\n\n /**\n * Accessible label forwarded to the internal `<button>` element as\n * `aria-label`. Required for icon-only buttons, which have no visible text.\n */\n @property({ type: String, attribute: 'accessible-label' })\n public accessibleLabel?: string;\n\n // ──────────────────────\n // IMPLEMENTATION\n // ──────────────────────\n\n protected get hasIcon(): boolean {\n return this.slotContentIsPresent;\n }\n\n protected get hasLabel(): boolean {\n return this.slotHasContent;\n }\n\n /**\n * Returns the set of attributes that should be forwarded to the internal\n * semantic `<button>` element, if not otherwise directly managed.\n *\n * @internal\n */\n protected getForwardedButtonAttributes(): Record<\n string,\n string | boolean | undefined\n > {\n return {\n disabled: this.disabled,\n };\n }\n\n public override connectedCallback(): void {\n super.connectedCallback();\n // Capture phase so slotted light-DOM clicks are suppressed before host\n // listeners (e.g. Storybook actions) run.\n this.addEventListener('click', this.handleClick, true);\n }\n\n public override disconnectedCallback(): void {\n this.removeEventListener('click', this.handleClick, true);\n super.disconnectedCallback();\n }\n\n /**\n * Suppresses click activation while the button is `disabled`.\n *\n * Slotted icon content lives in the light DOM, so pointer clicks on icons\n * bypass the disabled inner `<button>` and bubble on the host. The host\n * listener (capture) and inner `@click` binding both call this handler.\n */\n protected readonly handleClick = (event: Event): void => {\n if (this.disabled) {\n event.preventDefault();\n event.stopImmediatePropagation();\n }\n };\n\n protected override update(changedProperties: PropertyValues): void {\n super.update(changedProperties);\n if (window.__swc?.DEBUG) {\n if (this.hasIcon && !this.hasLabel && !this.accessibleLabel) {\n window.__swc.warn(\n this,\n `<${this.localName}> with an icon and no label must have an \"accessible-label\" attribute to be accessible.`,\n 'https://opensource.adobe.com/spectrum-web-components/components/button/#icon-only',\n { issues: ['accessible-label'] }\n );\n }\n }\n }\n}\n"],"mappings":";;;;;;OAuCsB,IAAtB,cAAyC,EACvC,EAAgB,EAAoB,GAAiB,kBAAgB,EAAE,GAAG,EAC1E,EAAE,YAAY,GAAoB,CACnC,CAAC;;+BAsB2B,wBAuDO,MAAuB;AACvD,GAAI,KAAK,aACP,EAAM,gBAAgB,EACtB,EAAM,0BAA0B;;;CA7CpC,IAAc,UAAmB;AAC/B,SAAO,KAAK;;CAGd,IAAc,WAAoB;AAChC,SAAO,KAAK;;CASd,+BAGE;AACA,SAAO,EACL,UAAU,KAAK,UAChB;;CAGH,oBAA0C;AAIxC,EAHA,MAAM,mBAAmB,EAGzB,KAAK,iBAAiB,SAAS,KAAK,aAAa,GAAK;;CAGxD,uBAA6C;AAE3C,EADA,KAAK,oBAAoB,SAAS,KAAK,aAAa,GAAK,EACzD,MAAM,sBAAsB;;CAiB9B,OAA0B,GAAyC;;AAEjE,EADA,MAAM,OAAO,EAAkB,GAC/B,IAAI,OAAO,UAAA,QAAA,EAAO,SACZ,KAAK,WAAW,CAAC,KAAK,YAAY,CAAC,KAAK,mBAC1C,OAAO,MAAM,KACX,MACA,IAAI,KAAK,UAAU,0FACnB,qFACA,EAAE,QAAQ,CAAC,mBAAmB,EAAE,CACjC;;;6BA5F6C;CAClD,GAAG,EAAgB;CACnB,gBAAgB;CACjB,KAiBA,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"}
1
+ {"version":3,"file":"Button.base.js","names":[],"sources":["../../../components/button/Button.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 */\n\nimport { PropertyValues } from 'lit';\nimport { property } from 'lit/decorators.js';\n\nimport { SlotPresenceController } from '@adobe/spectrum-wc-core/controllers/slot-presence-controller/index.js';\nimport { SlotTextController } from '@adobe/spectrum-wc-core/controllers/slot-text-controller/index.js';\nimport { SpectrumElement } from '@adobe/spectrum-wc-core/element/index.js';\nimport { SizedMixin } from '@adobe/spectrum-wc-core/mixins/index.js';\n\nimport { BUTTON_VALID_SIZES, type ButtonSize } from './Button.types.js';\n\n/**\n * Abstract base class for all button-like components. Owns shared semantic\n * concerns: interaction state, sizing, slot-derived icon/label state,\n * accessible-name resolution, and host-to-internal-button attribute forwarding.\n *\n * Visual API specific to `sp-button` (`variant`, `fill-style`, `static-color`)\n * is intentionally absent so that ActionButton, ClearButton, CloseButton,\n * PickerButton, and InfieldButton can extend this base without inheriting\n * the `swc-button` visual surface.\n *\n * @attribute {ElementSize} size - The size of the button.\n */\nexport abstract class ButtonBase extends SizedMixin(SpectrumElement, {\n validSizes: BUTTON_VALID_SIZES,\n}) {\n static override shadowRootOptions: ShadowRootInit = {\n ...SpectrumElement.shadowRootOptions,\n delegatesFocus: true,\n };\n\n // ──────────────────\n // SHARED API\n // ──────────────────\n\n /**\n * The size of the button.\n *\n * @default m\n */\n declare public size: ButtonSize;\n\n /**\n * Whether the button is disabled. Removes focusability and prevents\n * interaction.\n */\n @property({ type: Boolean, reflect: true })\n public disabled: boolean = false;\n\n /**\n * Accessible label forwarded to the internal `<button>` element as\n * `aria-label`. Required for icon-only buttons, which have no visible text.\n */\n @property({ type: String, attribute: 'accessible-label' })\n public accessibleLabel?: string;\n\n // ──────────────────────\n // CONTROLLERS\n // ──────────────────────\n\n /**\n * Observes whether an icon is slotted into `[slot=\"icon\"]`.\n *\n * @internal\n */\n protected slotPresence = new SlotPresenceController(this, '[slot=\"icon\"]');\n\n /**\n * Observes whether the default slot has a meaningful text label. The default\n * slot must bind `@slotchange=${this.slotText.handleSlotChange}` for changes\n * after the first render to be tracked.\n *\n * @internal\n */\n protected slotText = new SlotTextController(this);\n\n // ──────────────────────\n // IMPLEMENTATION\n // ──────────────────────\n\n protected get hasIcon(): boolean {\n return this.slotPresence.isPresent;\n }\n\n protected get hasLabel(): boolean {\n return this.slotText.hasContent;\n }\n\n /**\n * Returns the set of attributes that should be forwarded to the internal\n * semantic `<button>` element, if not otherwise directly managed.\n *\n * @internal\n */\n protected getForwardedButtonAttributes(): Record<\n string,\n string | boolean | undefined\n > {\n return {\n disabled: this.disabled,\n };\n }\n\n public override connectedCallback(): void {\n super.connectedCallback();\n // Capture phase so slotted light-DOM clicks are suppressed before host\n // listeners (e.g. Storybook actions) run.\n this.addEventListener('click', this.handleClick, true);\n }\n\n public override disconnectedCallback(): void {\n this.removeEventListener('click', this.handleClick, true);\n super.disconnectedCallback();\n }\n\n /**\n * Suppresses click activation while the button is `disabled`.\n *\n * Slotted icon content lives in the light DOM, so pointer clicks on icons\n * bypass the disabled inner `<button>` and bubble on the host. The host\n * listener (capture) and inner `@click` binding both call this handler.\n */\n protected readonly handleClick = (event: Event): void => {\n if (this.disabled) {\n event.preventDefault();\n event.stopImmediatePropagation();\n }\n };\n\n protected override update(changedProperties: PropertyValues): void {\n super.update(changedProperties);\n if (window.__swc?.DEBUG) {\n if (this.hasIcon && !this.hasLabel && !this.accessibleLabel) {\n window.__swc.warn(\n this,\n `<${this.localName}> with an icon and no label must have an \"accessible-label\" attribute to be accessible.`,\n 'https://opensource.adobe.com/spectrum-web-components/components/button/#icon-only',\n { issues: ['accessible-label'] }\n );\n }\n }\n }\n}\n"],"mappings":";;;;;;;;OAkCsB,IAAtB,cAAyC,EAAW,GAAiB,EACnE,YAAY,GACb,CAAC,CAAC;;+BAsB0B,wBAkBF,IAAI,EAAuB,MAAM,kBAAgB,kBASrD,IAAI,EAAmB,KAAK,sBAgDf,MAAuB;AACvD,GAAI,KAAK,aACP,EAAM,gBAAgB,EACtB,EAAM,0BAA0B;;;CA7CpC,IAAc,UAAmB;AAC/B,SAAO,KAAK,aAAa;;CAG3B,IAAc,WAAoB;AAChC,SAAO,KAAK,SAAS;;CASvB,+BAGE;AACA,SAAO,EACL,UAAU,KAAK,UAChB;;CAGH,oBAA0C;AAIxC,EAHA,MAAM,mBAAmB,EAGzB,KAAK,iBAAiB,SAAS,KAAK,aAAa,GAAK;;CAGxD,uBAA6C;AAE3C,EADA,KAAK,oBAAoB,SAAS,KAAK,aAAa,GAAK,EACzD,MAAM,sBAAsB;;CAiB9B,OAA0B,GAAyC;;AAEjE,EADA,MAAM,OAAO,EAAkB,GAC/B,IAAI,OAAO,UAAA,QAAA,EAAO,SACZ,KAAK,WAAW,CAAC,KAAK,YAAY,CAAC,KAAK,mBAC1C,OAAO,MAAM,KACX,MACA,IAAI,KAAK,UAAU,0FACnB,qFACA,EAAE,QAAQ,CAAC,mBAAmB,EAAE,CACjC;;;6BAhH6C;CAClD,GAAG,EAAgB;CACnB,gBAAgB;CACjB,KAiBA,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"}
@@ -21,3 +21,5 @@ export { PageScrollLockController } from './page-scroll-lock.js';
21
21
  export { PendingController, type PendingControllerHost, type PendingControllerOptions, } from './pending-controller/index.js';
22
22
  export { ALL_PLACEMENTS, fromFloatingPlacement, PlacementController, toFloatingPlacement, type Placement, type PlacementOptions, type VirtualTrigger, } from './placement-controller/index.js';
23
23
  export { SlotAttributePropagationController, type SlotAttributePropagationControllerOptions, } from './slot-attribute-propagation-controller/index.js';
24
+ export { SlotPresenceController } from './slot-presence-controller/index.js';
25
+ export { SlotTextController, type SlotTextConfig, } from './slot-text-controller/index.js';
@@ -5,11 +5,13 @@ import { ALL_PLACEMENTS as i } from "./placement-controller/src/types.js";
5
5
  import { PlacementController as a } from "./placement-controller/src/placement-controller.js";
6
6
  import "./placement-controller/index.js";
7
7
  import { LanguageResolutionController as o, languageResolverUpdatedSymbol as s } from "./language-resolution.js";
8
- import { PendingController as c } from "./pending-controller/src/pending-controller.js";
8
+ import { SlotPresenceController as c } from "./slot-presence-controller/src/slot-presence-controller.js";
9
+ import { PendingController as l } from "./pending-controller/src/pending-controller.js";
9
10
  import "./pending-controller/index.js";
10
- import { ColorController as l } from "./color-controller/src/color-controller.js";
11
+ import { ColorController as u } from "./color-controller/src/color-controller.js";
11
12
  import "./color-controller/index.js";
12
- import { FocusgroupNavigationController as u, focusgroupNavigationActiveChange as d } from "./focusgroup-navigation-controller/src/focusgroup-navigation-controller.js";
13
- import { LiveSelectionController as f } from "./live-selection-controller/src/live-selection-controller.js";
14
- import { PageScrollLockController as p } from "./page-scroll-lock.js";
15
- export { i as ALL_PLACEMENTS, l as ColorController, u as FocusgroupNavigationController, t as HoverController, o as LanguageResolutionController, f as LiveSelectionController, p as PageScrollLockController, c as PendingController, a as PlacementController, e as SlotAttributePropagationController, d as focusgroupNavigationActiveChange, n as fromFloatingPlacement, s as languageResolverUpdatedSymbol, r as toFloatingPlacement };
13
+ import { FocusgroupNavigationController as d, focusgroupNavigationActiveChange as f } from "./focusgroup-navigation-controller/src/focusgroup-navigation-controller.js";
14
+ import { LiveSelectionController as p } from "./live-selection-controller/src/live-selection-controller.js";
15
+ import { PageScrollLockController as m } from "./page-scroll-lock.js";
16
+ import { SlotTextController as h } from "./slot-text-controller/src/slot-text-controller.js";
17
+ export { i as ALL_PLACEMENTS, u as ColorController, d as FocusgroupNavigationController, t as HoverController, o as LanguageResolutionController, p as LiveSelectionController, m as PageScrollLockController, l as PendingController, a as PlacementController, e as SlotAttributePropagationController, c as SlotPresenceController, h as SlotTextController, f as focusgroupNavigationActiveChange, n as fromFloatingPlacement, s as languageResolverUpdatedSymbol, r as toFloatingPlacement };
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Copyright 2026 Adobe. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+ export { SlotPresenceController } from './src/slot-presence-controller.js';
@@ -0,0 +1,2 @@
1
+ import { SlotPresenceController as e } from "./src/slot-presence-controller.js";
2
+ export { e as SlotPresenceController };
@@ -0,0 +1,57 @@
1
+ import { ReactiveController, ReactiveElement } from 'lit';
2
+ /**
3
+ * A reactive controller that observes whether slotted content matching
4
+ * given CSS selectors is present in the host's light DOM.
5
+ *
6
+ * Replaces the `ObserveSlotPresence` mixin with a composition-based approach.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * class MyComponent extends SpectrumElement {
11
+ * private slotPresence = new SlotPresenceController(this, '[slot="icon"]');
12
+ *
13
+ * get hasIcon(): boolean {
14
+ * return this.slotPresence.isPresent;
15
+ * }
16
+ * }
17
+ * ```
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * // Observing multiple selectors
22
+ * class MyComponent extends SpectrumElement {
23
+ * private slotPresence = new SlotPresenceController(this, [
24
+ * '[slot="icon"]',
25
+ * '[slot="description"]',
26
+ * ]);
27
+ *
28
+ * get hasIcon(): boolean {
29
+ * return this.slotPresence.getPresence('[slot="icon"]');
30
+ * }
31
+ *
32
+ * get hasDescription(): boolean {
33
+ * return this.slotPresence.getPresence('[slot="description"]');
34
+ * }
35
+ * }
36
+ * ```
37
+ */
38
+ export declare class SlotPresenceController implements ReactiveController {
39
+ private host;
40
+ private selectors;
41
+ private presenceMap;
42
+ private observer;
43
+ constructor(host: ReactiveElement, selectors: string | string[]);
44
+ /**
45
+ * Whether slotted content is present. Use this when observing a single
46
+ * selector. Throws if multiple selectors were provided — use
47
+ * `getPresence(selector)` instead.
48
+ */
49
+ get isPresent(): boolean;
50
+ /**
51
+ * Returns whether content matching the given selector is present.
52
+ */
53
+ getPresence(selector: string): boolean;
54
+ private checkPresence;
55
+ hostConnected(): void;
56
+ hostDisconnected(): void;
57
+ }
@@ -0,0 +1,44 @@
1
+ //#region controllers/slot-presence-controller/src/slot-presence-controller.ts
2
+ var e = class {
3
+ constructor(e, t) {
4
+ this.presenceMap = /* @__PURE__ */ new Map(), this.host = e, this.selectors = Array.isArray(t) ? t : [t], this.host.addController(this), this.observer = new MutationObserver(() => {
5
+ this.checkPresence();
6
+ }), this.checkPresence();
7
+ }
8
+ get isPresent() {
9
+ if (this.selectors.length === 1) {
10
+ var e;
11
+ return (e = this.presenceMap.get(this.selectors[0])) == null ? !1 : e;
12
+ }
13
+ throw Error("Multiple selectors provided to SlotPresenceController — use getPresence(selector) instead.");
14
+ }
15
+ getPresence(e) {
16
+ if (this.presenceMap.has(e)) {
17
+ var t;
18
+ return (t = this.presenceMap.get(e)) == null ? !1 : t;
19
+ }
20
+ throw Error(`The provided selector \`${e}\` is not being observed.`);
21
+ }
22
+ checkPresence() {
23
+ let e = !1;
24
+ for (let n of this.selectors) {
25
+ var t;
26
+ let r = !!this.host.querySelector(`:scope > ${n}`);
27
+ ((t = this.presenceMap.get(n)) == null ? !1 : t) !== r && (e = !0), this.presenceMap.set(n, r);
28
+ }
29
+ e && this.host.requestUpdate();
30
+ }
31
+ hostConnected() {
32
+ this.observer.observe(this.host, {
33
+ childList: !0,
34
+ subtree: !0
35
+ }), this.checkPresence();
36
+ }
37
+ hostDisconnected() {
38
+ this.observer.disconnect();
39
+ }
40
+ };
41
+ //#endregion
42
+ export { e as SlotPresenceController };
43
+
44
+ //# sourceMappingURL=slot-presence-controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slot-presence-controller.js","names":[],"sources":["../../../../controllers/slot-presence-controller/src/slot-presence-controller.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 */\n\nimport type { ReactiveController, ReactiveElement } from 'lit';\n\n/**\n * A reactive controller that observes whether slotted content matching\n * given CSS selectors is present in the host's light DOM.\n *\n * Replaces the `ObserveSlotPresence` mixin with a composition-based approach.\n *\n * @example\n * ```typescript\n * class MyComponent extends SpectrumElement {\n * private slotPresence = new SlotPresenceController(this, '[slot=\"icon\"]');\n *\n * get hasIcon(): boolean {\n * return this.slotPresence.isPresent;\n * }\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Observing multiple selectors\n * class MyComponent extends SpectrumElement {\n * private slotPresence = new SlotPresenceController(this, [\n * '[slot=\"icon\"]',\n * '[slot=\"description\"]',\n * ]);\n *\n * get hasIcon(): boolean {\n * return this.slotPresence.getPresence('[slot=\"icon\"]');\n * }\n *\n * get hasDescription(): boolean {\n * return this.slotPresence.getPresence('[slot=\"description\"]');\n * }\n * }\n * ```\n */\nexport class SlotPresenceController implements ReactiveController {\n private host: ReactiveElement;\n private selectors: string[];\n private presenceMap = new Map<string, boolean>();\n private observer: MutationObserver;\n\n constructor(host: ReactiveElement, selectors: string | string[]) {\n this.host = host;\n this.selectors = Array.isArray(selectors) ? selectors : [selectors];\n this.host.addController(this);\n\n this.observer = new MutationObserver(() => {\n this.checkPresence();\n });\n\n // Check initial state synchronously\n this.checkPresence();\n }\n\n /**\n * Whether slotted content is present. Use this when observing a single\n * selector. Throws if multiple selectors were provided — use\n * `getPresence(selector)` instead.\n */\n get isPresent(): boolean {\n if (this.selectors.length === 1) {\n return this.presenceMap.get(this.selectors[0]) ?? false;\n }\n throw new Error(\n 'Multiple selectors provided to SlotPresenceController — use getPresence(selector) instead.'\n );\n }\n\n /**\n * Returns whether content matching the given selector is present.\n */\n getPresence(selector: string): boolean {\n if (this.presenceMap.has(selector)) {\n return this.presenceMap.get(selector) ?? false;\n }\n throw new Error(\n `The provided selector \\`${selector}\\` is not being observed.`\n );\n }\n\n private checkPresence(): void {\n let changed = false;\n for (const selector of this.selectors) {\n const nextValue = !!this.host.querySelector(`:scope > ${selector}`);\n const previousValue = this.presenceMap.get(selector) ?? false;\n if (previousValue !== nextValue) {\n changed = true;\n }\n this.presenceMap.set(selector, nextValue);\n }\n if (changed) {\n // The `ObserveSlotPresence` mixin this controller replaces deferred the\n // re-render with `this.updateComplete.then(() => this.requestUpdate())`.\n // The controller requests the update synchronously instead. This is\n // safe because `checkPresence` only runs from the constructor, the\n // `MutationObserver` callback, and `hostConnected` — never from inside\n // the host's own `update()`/`render()` — so there is no re-entrant\n // update. Lit batches `requestUpdate()` calls into the next microtask,\n // so multiple presence changes in one tick still coalesce into a single\n // render. See the controller stories for the behavioral test.\n this.host.requestUpdate();\n }\n }\n\n hostConnected(): void {\n this.observer.observe(this.host, {\n childList: true,\n subtree: true,\n });\n this.checkPresence();\n }\n\n hostDisconnected(): void {\n this.observer.disconnect();\n }\n}\n"],"mappings":";AAkDA,IAAa,IAAb,MAAkE;CAMhE,YAAY,GAAuB,GAA8B;AAU/D,qCAboB,IAAI,KAAsB,EAI9C,KAAK,OAAO,GACZ,KAAK,YAAY,MAAM,QAAQ,EAAU,GAAG,IAAY,CAAC,EAAU,EACnE,KAAK,KAAK,cAAc,KAAK,EAE7B,KAAK,WAAW,IAAI,uBAAuB;AACzC,QAAK,eAAe;IACpB,EAGF,KAAK,eAAe;;CAQtB,IAAI,YAAqB;AACvB,MAAI,KAAK,UAAU,WAAW,GAAG;;AAC/B,WAAA,IAAO,KAAK,YAAY,IAAI,KAAK,UAAU,GAAG,KAAA,OAAI,KAAJ;;AAEhD,QAAU,MACR,6FACD;;CAMH,YAAY,GAA2B;AACrC,MAAI,KAAK,YAAY,IAAI,EAAS,EAAE;;AAClC,WAAA,IAAO,KAAK,YAAY,IAAI,EAAS,KAAA,OAAI,KAAJ;;AAEvC,QAAU,MACR,2BAA2B,EAAS,2BACrC;;CAGH,gBAA8B;EAC5B,IAAI,IAAU;AACd,OAAK,IAAM,KAAY,KAAK,WAAW;;GACrC,IAAM,IAAY,CAAC,CAAC,KAAK,KAAK,cAAc,YAAY,IAAW;AAKnE,KAHA,IADsB,KAAK,YAAY,IAAI,EAAS,KAAA,OAAI,KAAJ,OAC9B,MACpB,IAAU,KAEZ,KAAK,YAAY,IAAI,GAAU,EAAU;;AAE3C,EAAI,KAUF,KAAK,KAAK,eAAe;;CAI7B,gBAAsB;AAKpB,EAJA,KAAK,SAAS,QAAQ,KAAK,MAAM;GAC/B,WAAW;GACX,SAAS;GACV,CAAC,EACF,KAAK,eAAe;;CAGtB,mBAAyB;AACvB,OAAK,SAAS,YAAY"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Copyright 2026 Adobe. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+ export { SlotTextController, type SlotTextConfig, } from './src/slot-text-controller.js';
@@ -0,0 +1,2 @@
1
+ import { SlotTextController as e } from "./src/slot-text-controller.js";
2
+ export { e as SlotTextController };