diff --git a/packages/webawesome/docs/docs/components/dropdown-item.md b/packages/webawesome/docs/docs/components/dropdown-item.md
new file mode 100644
index 000000000..861b87f0a
--- /dev/null
+++ b/packages/webawesome/docs/docs/components/dropdown-item.md
@@ -0,0 +1,7 @@
+---
+title: Dropdown Item
+description: Description of component.
+layout: component
+---
+
+This component must be used as a child of ``. Please see the [Dropdown docs](/docs/components/dropdown) to see examples of this component in action.
diff --git a/packages/webawesome/docs/docs/components/dropdown.md b/packages/webawesome/docs/docs/components/dropdown.md
new file mode 100644
index 000000000..312074fa0
--- /dev/null
+++ b/packages/webawesome/docs/docs/components/dropdown.md
@@ -0,0 +1,74 @@
+---
+title: Dropdown
+description: Description of component.
+layout: component
+---
+
+```html {.example}
+
+
+
+ Message
+
+
+
Actions
+
+
+
+ Reply
+ ⌘ R
+
+
+
+
+ Forward
+ ⌘ F
+
+
+
+
+ Archive
+
+
+
+
+ Delete
+
+
+
+
+ Show images
+
+ Word wrap
+
+
+
+
+
+ Labels
+
+
+ Add label
+
+
+
+ Manage labels
+
+
+
+
+
+ Preferences
+
+
+```
+
+## Examples
+
+### First Example
+
+TODO
+
+### Second Example
+
+TODO
diff --git a/packages/webawesome/src/components/dropdown-item/dropdown-item.css b/packages/webawesome/src/components/dropdown-item/dropdown-item.css
new file mode 100644
index 000000000..3cfac4e0d
--- /dev/null
+++ b/packages/webawesome/src/components/dropdown-item/dropdown-item.css
@@ -0,0 +1,239 @@
+:host {
+ display: flex;
+ position: relative;
+ align-items: center;
+ padding: 0.33em 1em;
+ border-radius: var(--wa-border-radius-s);
+ isolation: isolate;
+ color: var(--wa-color-neutral-on-quiet);
+ font-size: 0.9375em;
+ line-height: var(--wa-line-height-normal);
+ cursor: pointer;
+ transition:
+ 100ms background-color ease,
+ 100ms color ease;
+}
+
+@media (hover: hover) {
+ :host(:hover:not(:state(disabled))) {
+ background-color: var(--wa-color-neutral-fill-quiet);
+ color: var(--wa-color-neutral-on-quiet);
+ }
+}
+
+:host(:focus-visible) {
+ z-index: 1;
+ outline: var(--wa-color-brand-border-loud);
+ outline-offset: var(--wa-focus-ring-offset);
+ background-color: var(--wa-color-neutral-fill-quiet);
+ color: var(--wa-color-neutral-on-quiet);
+}
+
+:host(:state(disabled)) {
+ cursor: not-allowed;
+}
+
+:host([variant='danger']:focus-visible) {
+ background-color: var(--wa-color-danger-fill-quiet);
+ color: var(--wa-color-danger-on-quiet);
+}
+
+:host(:state(disabled)) {
+ opacity: 0.5;
+}
+
+/* danger variant */
+:host([variant='danger']),
+:host([variant='danger']) #details {
+ color: var(--wa-color-danger-on-quiet);
+}
+
+@media (hover: hover) {
+ :host([variant='danger']:hover) {
+ background-color: var(--wa-color-danger-fill-quiet);
+ color: var(--wa-color-danger-on-quiet);
+ }
+}
+
+:host([variant='danger']:focus-visible) {
+ background-color: var(--wa-color-danger-fill-quiet);
+ color: var(--wa-color-danger-on-quiet);
+}
+
+:host([checkbox-adjacent]) {
+ padding-inline-start: 2em;
+}
+
+/* Only add padding when item actually has a submenu */
+:host([submenu-adjacent]:not(:state(has-submenu))) #details {
+ padding-inline-end: 0;
+}
+
+:host(:state(has-submenu)[submenu-adjacent]) #details {
+ padding-inline-end: 1.75em;
+}
+
+#check {
+ visibility: hidden;
+ margin-inline-start: -1.25em;
+ margin-inline-end: 0.25em;
+ font-size: 1.25em;
+}
+
+:host(:state(checked)) #check {
+ visibility: visible;
+}
+
+#icon ::slotted(*) {
+ display: flex;
+ flex: 0 0 auto;
+ align-items: center;
+ margin-inline-end: 0.5em !important;
+ font-size: 1.25em;
+}
+
+#label {
+ flex: 1 1 auto;
+ min-width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+#details {
+ display: flex;
+ flex: 0 0 auto;
+ align-items: center;
+ justify-content: end;
+ color: var(--wa-color-neutral-border-normal);
+ font-size: 0.933334em !important;
+}
+
+#details ::slotted(*) {
+ margin-inline-start: 2em !important;
+}
+
+/* Submenu indicator icon */
+#submenu-indicator {
+ position: absolute;
+ inset-inline-end: 0.25em;
+ color: var(--wa-color-neutral-border-normal);
+ font-size: 1.25em;
+}
+
+/* Flip chevron icon when RTL */
+:host(:dir(rtl)) #submenu-indicator {
+ transform: scaleX(-1);
+}
+
+/* Submenu styles */
+#submenu {
+ display: flex;
+ z-index: 10;
+ position: absolute;
+ top: 0;
+ left: 0;
+ flex-direction: column;
+ width: max-content;
+ margin: 0;
+ padding: 0.25em;
+ border: var(--wa-border-style) var(--wa-border-width-s) var(--wa-color-neutral-border-quiet);
+ border-radius: var(--wa-border-radius-m);
+ background-color: var(--wa-color-surface-default);
+ box-shadow: var(--wa-shadow-l);
+ color: var(--wa-color-neutral-on-quiet);
+ text-align: start;
+ user-select: none;
+
+ /* Override default popover styles */
+ &[popover] {
+ margin: 0;
+ inset: auto;
+ padding: 0.25em;
+ overflow: visible;
+ border-radius: var(--wa-border-radius-m);
+ }
+
+ &.show {
+ animation: submenu-show var(--show-duration, 50ms) ease;
+ }
+
+ &.hide {
+ animation: submenu-show var(--show-duration, 50ms) ease reverse;
+ }
+
+ /* Submenu placement transform origins */
+ &[data-placement^='top'] {
+ transform-origin: bottom;
+ }
+
+ &[data-placement^='bottom'] {
+ transform-origin: top;
+ }
+
+ &[data-placement^='left'] {
+ transform-origin: right;
+ }
+
+ &[data-placement^='right'] {
+ transform-origin: left;
+ }
+
+ &[data-placement='left-start'] {
+ transform-origin: right top;
+ }
+
+ &[data-placement='left-end'] {
+ transform-origin: right bottom;
+ }
+
+ &[data-placement='right-start'] {
+ transform-origin: left top;
+ }
+
+ &[data-placement='right-end'] {
+ transform-origin: left bottom;
+ }
+
+ /* Safe triangle styling */
+ &::before {
+ display: none;
+ z-index: 9;
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ background-color: transparent;
+ content: '';
+ clip-path: polygon(
+ var(--safe-triangle-cursor-x, 0) var(--safe-triangle-cursor-y, 0),
+ var(--safe-triangle-submenu-start-x, 0) var(--safe-triangle-submenu-start-y, 0),
+ var(--safe-triangle-submenu-end-x, 0) var(--safe-triangle-submenu-end-y, 0)
+ );
+ pointer-events: auto; /* Enable mouse events on the triangle */
+ }
+
+ &[data-visible]::before {
+ display: block;
+ }
+}
+
+::slotted(wa-dropdown-item) {
+ font-size: inherit;
+}
+
+::slotted(wa-divider) {
+ --spacing: 0.25em;
+}
+
+@keyframes submenu-show {
+ from {
+ scale: 0.9;
+ opacity: 0;
+ }
+ to {
+ scale: 1;
+ opacity: 1;
+ }
+}
diff --git a/packages/webawesome/src/components/dropdown-item/dropdown-item.test.ts b/packages/webawesome/src/components/dropdown-item/dropdown-item.test.ts
new file mode 100644
index 000000000..3fa2c28c1
--- /dev/null
+++ b/packages/webawesome/src/components/dropdown-item/dropdown-item.test.ts
@@ -0,0 +1,9 @@
+import { expect, fixture, html } from '@open-wc/testing';
+
+describe('', () => {
+ it('should render a component', async () => {
+ const el = await fixture(html` `);
+
+ expect(el).to.exist;
+ });
+});
diff --git a/packages/webawesome/src/components/dropdown-item/dropdown-item.ts b/packages/webawesome/src/components/dropdown-item/dropdown-item.ts
new file mode 100644
index 000000000..cf9491e68
--- /dev/null
+++ b/packages/webawesome/src/components/dropdown-item/dropdown-item.ts
@@ -0,0 +1,285 @@
+import type { PropertyValues } from 'lit';
+import { html } from 'lit';
+import { customElement, property, query, state } from 'lit/decorators.js';
+import { animateWithClass } from '../../internal/animate.js';
+import { HasSlotController } from '../../internal/slot.js';
+import WebAwesomeElement from '../../internal/webawesome-element.js';
+import styles from './dropdown-item.css';
+
+/**
+ * @summary Short summary of the component's intended use.
+ * @documentation https://backers.webawesome.com/docs/components/dropdown-item
+ * @status experimental
+ * @since 3.0
+ *
+ * @dependency wa-example
+ *
+ * @slot - TODO - description here
+ *
+ */
+@customElement('wa-dropdown-item')
+export default class WaDropdownItem extends WebAwesomeElement {
+ static css = styles;
+
+ private readonly hasSlotController = new HasSlotController(this, '[default]', 'prefix', 'suffix');
+
+ @query('#submenu') submenuElement: HTMLDivElement;
+
+ /** @internal The controller will set this property to true when the item is active. */
+ @property({ type: Boolean }) active = false;
+
+ /** The type of menu item to render. */
+ @property({ reflect: true }) variant: 'danger' | 'default' = 'default';
+
+ /**
+ * @internal The controller will set this property to true when at least one checkbox exists in the dropdown. This
+ * allows non-checkbox items to draw additional space to align properly with checkbox items.
+ */
+ @property({ attribute: 'checkbox-adjacent', type: Boolean, reflect: true }) checkboxAdjacent = false;
+
+ /**
+ * @internal The controller will set this property to true when at least one item with a submenu exists in the
+ * dropdown. This allows non-submenu items to draw additional space to align properly with items that have submenus.
+ */
+ @property({ attribute: 'submenu-adjacent', type: Boolean, reflect: true }) submenuAdjacent = false;
+
+ /**
+ * An optional value for the menu item. This is useful for determining which item was selected when listening to the
+ * dropdown's `wa-select` event.
+ */
+ @property() value: string;
+
+ /** Set to `checkbox` to make the item a checkbox. */
+ @property({ reflect: true }) type: 'normal' | 'checkbox' = 'normal';
+
+ /** Set to true to check the dropdown item. Only valid when `type` is `checkbox`. */
+ @property({ type: Boolean }) checked = false;
+
+ /** Disables the dropdown item. */
+ @property({ type: Boolean, reflect: true }) disabled = false;
+
+ /** Whether the submenu is currently open. */
+ @property({ type: Boolean, reflect: true }) submenuOpen = false;
+
+ /** @internal Store whether this item has a submenu */
+ @state() hasSubmenu = false;
+
+ connectedCallback() {
+ super.connectedCallback();
+ this.addEventListener('mouseenter', this.handleMouseEnter.bind(this));
+ this.shadowRoot!.addEventListener('slotchange', this.handleSlotChange);
+ }
+
+ disconnectedCallback() {
+ super.disconnectedCallback();
+ this.closeSubmenu();
+ this.removeEventListener('mouseenter', this.handleMouseEnter);
+ this.shadowRoot!.removeEventListener('slotchange', this.handleSlotChange);
+ }
+
+ firstUpdated() {
+ this.setAttribute('tabindex', '-1');
+ this.hasSubmenu = this.hasSlotController.test('submenu');
+ this.updateHasSubmenuState();
+ }
+
+ updated(changedProperties: PropertyValues) {
+ if (changedProperties.has('active')) {
+ this.setAttribute('tabindex', this.active ? '0' : '-1');
+ this.customStates.set('active', this.active);
+ }
+
+ if (changedProperties.has('checked')) {
+ this.setAttribute('aria-checked', this.checked ? 'true' : 'false');
+ this.customStates.set('checked', this.checked);
+ }
+
+ if (changedProperties.has('disabled')) {
+ this.setAttribute('aria-disabled', this.disabled ? 'true' : 'false');
+ this.customStates.set('disabled', this.disabled);
+ }
+
+ if (changedProperties.has('type')) {
+ if (this.type === 'checkbox') {
+ this.setAttribute('role', 'menuitemcheckbox');
+ } else {
+ this.setAttribute('role', 'menuitem');
+ }
+ }
+
+ if (changedProperties.has('submenuOpen')) {
+ this.customStates.set('submenu-open', this.submenuOpen);
+ if (this.submenuOpen) {
+ this.openSubmenu();
+ } else {
+ this.closeSubmenu();
+ }
+ }
+ }
+
+ private handleSlotChange = () => {
+ this.hasSubmenu = this.hasSlotController.test('submenu');
+ this.updateHasSubmenuState();
+
+ if (this.hasSubmenu) {
+ this.setAttribute('aria-haspopup', 'menu');
+ this.setAttribute('aria-expanded', this.submenuOpen ? 'true' : 'false');
+ } else {
+ this.removeAttribute('aria-haspopup');
+ this.removeAttribute('aria-expanded');
+ }
+ };
+
+ /** Update the has-submenu custom state */
+ private updateHasSubmenuState() {
+ this.customStates.set('has-submenu', this.hasSubmenu);
+ }
+
+ /** Opens the submenu. */
+ async openSubmenu() {
+ if (!this.hasSubmenu || !this.submenuElement) return;
+
+ // Notify parent dropdown to handle positioning
+ this.notifyParentOfOpening();
+
+ // Use Popover API to show the submenu
+ this.submenuElement.showPopover();
+ this.submenuElement.hidden = false;
+ this.submenuElement.setAttribute('data-visible', '');
+ this.submenuOpen = true;
+ this.setAttribute('aria-expanded', 'true');
+
+ // Animate the submenu
+ await animateWithClass(this.submenuElement, 'show');
+
+ // Set focus to the first submenu item
+ setTimeout(() => {
+ const items = this.getSubmenuItems();
+ if (items.length > 0) {
+ items.forEach((item, index) => (item.active = index === 0));
+ items[0].focus();
+ }
+ }, 0);
+ }
+
+ /** Notifies the parent dropdown that this item is opening its submenu */
+ private notifyParentOfOpening() {
+ // First notify the parent that we're about to open
+ const event = new CustomEvent('submenu-opening', {
+ bubbles: true,
+ composed: true,
+ detail: { item: this },
+ });
+ this.dispatchEvent(event);
+
+ // Find sibling items that have open submenus and close them
+ const parent = this.parentElement;
+ if (parent) {
+ const siblings = [...parent.children].filter(
+ el =>
+ el !== this &&
+ el.localName === 'wa-dropdown-item' &&
+ el.getAttribute('slot') === this.getAttribute('slot') &&
+ (el as WaDropdownItem).submenuOpen,
+ ) as WaDropdownItem[];
+
+ // Close each sibling submenu with animation
+ siblings.forEach(sibling => {
+ sibling.submenuOpen = false;
+ });
+ }
+ }
+
+ /** Closes the submenu. */
+ async closeSubmenu() {
+ if (!this.hasSubmenu || !this.submenuElement) return;
+
+ this.submenuOpen = false;
+ this.setAttribute('aria-expanded', 'false');
+
+ if (!this.submenuElement.hidden) {
+ await animateWithClass(this.submenuElement, 'hide');
+ this.submenuElement.hidden = true;
+ this.submenuElement.removeAttribute('data-visible');
+ this.submenuElement.hidePopover();
+ }
+ }
+
+ /** Gets all dropdown items in the submenu. */
+ private getSubmenuItems(): WaDropdownItem[] {
+ // Only get direct children with slot="submenu", not nested ones
+ return [...this.children].filter(
+ el =>
+ el.localName === 'wa-dropdown-item' && el.getAttribute('slot') === 'submenu' && !el.hasAttribute('disabled'),
+ ) as WaDropdownItem[];
+ }
+
+ /** Handles mouse enter to open the submenu */
+ private handleMouseEnter() {
+ if (this.hasSubmenu && !this.disabled) {
+ this.notifyParentOfOpening();
+ this.submenuOpen = true;
+ }
+ }
+
+ render() {
+ return html`
+ ${this.type === 'checkbox'
+ ? html`
+
+ `
+ : ''}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${this.hasSubmenu
+ ? html`
+
+ `
+ : ''}
+ ${this.hasSubmenu
+ ? html`
+
+
+
+ `
+ : ''}
+ `;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'wa-dropdown-item': WaDropdownItem;
+ }
+}
diff --git a/packages/webawesome/src/components/dropdown/dropdown.css b/packages/webawesome/src/components/dropdown/dropdown.css
new file mode 100644
index 000000000..100ef6433
--- /dev/null
+++ b/packages/webawesome/src/components/dropdown/dropdown.css
@@ -0,0 +1,91 @@
+:host {
+ --show-duration: 50ms;
+ display: contents;
+}
+
+#menu {
+ display: flex;
+ position: absolute;
+ top: 0;
+ left: 0;
+ flex-direction: column;
+ width: max-content;
+ margin: 0;
+ padding: 0.25em;
+ border: var(--wa-border-style) var(--wa-border-width-s) var(--wa-color-neutral-border-quiet);
+ border-radius: var(--wa-border-radius-m);
+ background-color: var(--wa-color-surface-default);
+ box-shadow: var(--wa-shadow-m);
+ color: var(--wa-color-neutral-on-quiet);
+ text-align: start;
+ user-select: none;
+
+ &.show {
+ animation: show var(--show-duration) ease;
+ }
+
+ &.hide {
+ animation: show var(--show-duration) ease reverse;
+ }
+
+ ::slotted(h1),
+ ::slotted(h2),
+ ::slotted(h3),
+ ::slotted(h4),
+ ::slotted(h5),
+ ::slotted(h6) {
+ display: block !important;
+ margin: 0.25em 0 !important;
+ padding: 0.25em 1em !important;
+ color: var(--wa-color-text-quiet) !important;
+ font-weight: var(--wa-font-weight-semibold) !important;
+ font-size: 0.75em !important;
+ }
+
+ ::slotted(wa-divider) {
+ --spacing: 0.25em; /* Component-specific, left as-is */
+ }
+}
+
+:host([data-placement^='top']) #menu {
+ transform-origin: bottom;
+}
+
+:host([data-placement^='bottom']) #menu {
+ transform-origin: top;
+}
+
+:host([data-placement^='left']) #menu {
+ transform-origin: right;
+}
+
+:host([data-placement^='right']) #menu {
+ transform-origin: left;
+}
+
+:host([data-placement='left-start']) #menu {
+ transform-origin: right top;
+}
+
+:host([data-placement='left-end']) #menu {
+ transform-origin: right bottom;
+}
+
+:host([data-placement='right-start']) #menu {
+ transform-origin: left top;
+}
+
+:host([data-placement='right-end']) #menu {
+ transform-origin: left bottom;
+}
+
+@keyframes show {
+ from {
+ scale: 0.9;
+ opacity: 0;
+ }
+ to {
+ scale: 1;
+ opacity: 1;
+ }
+}
diff --git a/packages/webawesome/src/components/dropdown/dropdown.test.ts b/packages/webawesome/src/components/dropdown/dropdown.test.ts
new file mode 100644
index 000000000..0ec43f66a
--- /dev/null
+++ b/packages/webawesome/src/components/dropdown/dropdown.test.ts
@@ -0,0 +1,9 @@
+import { expect, fixture, html } from '@open-wc/testing';
+
+describe('', () => {
+ it('should render a component', async () => {
+ const el = await fixture(html` `);
+
+ expect(el).to.exist;
+ });
+});
diff --git a/packages/webawesome/src/components/dropdown/dropdown.ts b/packages/webawesome/src/components/dropdown/dropdown.ts
new file mode 100644
index 000000000..d617ad785
--- /dev/null
+++ b/packages/webawesome/src/components/dropdown/dropdown.ts
@@ -0,0 +1,816 @@
+import { autoUpdate, computePosition, flip, offset, shift } from '@floating-ui/dom';
+import type { PropertyValues } from 'lit';
+import { html } from 'lit';
+import { customElement, property, query } from 'lit/decorators.js';
+import { WaAfterHideEvent } from '../../events/after-hide.js';
+import { WaAfterShowEvent } from '../../events/after-show.js';
+import { WaHideEvent } from '../../events/hide.js';
+import { WaSelectEvent } from '../../events/select.js';
+import { WaShowEvent } from '../../events/show.js';
+import { animateWithClass } from '../../internal/animate.js';
+import { uniqueId } from '../../internal/math.js';
+import WebAwesomeElement from '../../internal/webawesome-element.js';
+import { LocalizeController } from '../../utilities/localize.js';
+import type WaButton from '../button/button.js';
+import '../dropdown-item/dropdown-item.js';
+import type WaDropdownItem from '../dropdown-item/dropdown-item.js';
+import styles from './dropdown.css';
+
+const openDropdowns = new Set();
+
+/**
+ * @summary TODO - short summary of the component's intended use.
+ * @documentation https://backers.webawesome.com/docs/components/dropdown
+ * @status stable
+ * @since 2.0
+ *
+ * @dependency wa-dropdown-item
+ *
+ * @slot - TODO - description here
+ *
+ * @csspart base - The component's base wrapper.
+ */
+@customElement('wa-dropdown')
+export default class WaDropdown extends WebAwesomeElement {
+ static css = styles;
+
+ private cleanup: ReturnType | undefined;
+ private submenuCleanups: Map> = new Map();
+ private readonly localize = new LocalizeController(this);
+ private userTypedQuery = '';
+ private userTypedTimeout: ReturnType;
+ private openSubmenuStack: WaDropdownItem[] = [];
+
+ @query('#menu') private menu: HTMLDivElement;
+
+ /** Opens or closes the dropdown. */
+ @property({ type: Boolean, reflect: true }) open = false;
+
+ /**
+ * The placement of the dropdown menu in reference to the trigger. The menu will shift to a more optimal location if
+ * the preferred placement doesn't have enough room.
+ */
+ @property({ reflect: true }) placement:
+ | 'top'
+ | 'top-start'
+ | 'top-end'
+ | 'bottom'
+ | 'bottom-start'
+ | 'bottom-end'
+ | 'right'
+ | 'right-start'
+ | 'right-end'
+ | 'left'
+ | 'left-start'
+ | 'left-end' = 'bottom-start';
+
+ /** The distance of the dropdown menu from its trigger. */
+ @property({ type: Number }) distance = 0;
+
+ /** The offset of the dropdown menu along its trigger. */
+ @property({ type: Number }) offset = 0;
+
+ disconnectedCallback() {
+ super.disconnectedCallback();
+ clearInterval(this.userTypedTimeout);
+ this.closeAllSubmenus();
+
+ // Clean up all submenu positioning
+ this.submenuCleanups.forEach(cleanup => cleanup());
+ this.submenuCleanups.clear();
+
+ document.removeEventListener('mousemove', this.handleGlobalMouseMove);
+ }
+
+ firstUpdated() {
+ this.syncAriaAttributes();
+ }
+
+ updated(changedProperties: PropertyValues) {
+ if (changedProperties.has('open')) {
+ this.customStates.set('open', this.open);
+
+ if (this.open) {
+ this.showMenu();
+ } else {
+ this.closeAllSubmenus();
+ this.hideMenu();
+ }
+ }
+ }
+
+ /** Gets all elements slotted in the menu that aren't disabled. */
+ private getItems(includeDisabled = false): WaDropdownItem[] {
+ // Only select direct children of the dropdown, not deep descendants
+ const items = [...this.children].filter(
+ el => el.localName === 'wa-dropdown-item' && !el.hasAttribute('slot'),
+ ) as WaDropdownItem[];
+ return includeDisabled ? items : items.filter(item => !item.disabled);
+ }
+
+ /** Gets all dropdown items in a specific submenu. */
+ private getSubmenuItems(parentItem: WaDropdownItem, includeDisabled = false): WaDropdownItem[] {
+ // Only get direct children with slot="submenu", not nested ones
+ const items = [...parentItem.children].filter(
+ el => el.localName === 'wa-dropdown-item' && el.getAttribute('slot') === 'submenu',
+ ) as WaDropdownItem[];
+ return includeDisabled ? items : items.filter(item => !item.disabled);
+ }
+
+ /** Handles the submenu navigation stack */
+ private addToSubmenuStack(item: WaDropdownItem) {
+ // Remove any items that might be after this one in the stack
+ // This happens if the user navigates back and then to a different submenu
+ const index = this.openSubmenuStack.indexOf(item);
+ if (index !== -1) {
+ this.openSubmenuStack = this.openSubmenuStack.slice(0, index + 1);
+ } else {
+ this.openSubmenuStack.push(item);
+ }
+ }
+
+ /** Removes the last item from the submenu stack */
+ private removeFromSubmenuStack() {
+ return this.openSubmenuStack.pop();
+ }
+
+ /** Gets the current active submenu item */
+ private getCurrentSubmenuItem(): WaDropdownItem | undefined {
+ return this.openSubmenuStack.length > 0 ? this.openSubmenuStack[this.openSubmenuStack.length - 1] : undefined;
+ }
+
+ /** Closes all submenus in the dropdown. */
+ private closeAllSubmenus() {
+ const items = this.getItems(true);
+ items.forEach(item => {
+ item.submenuOpen = false;
+ });
+ this.openSubmenuStack = [];
+ }
+
+ /** Closes sibling submenus at the same level as the specified item. */
+ private closeSiblingSubmenus(item: WaDropdownItem) {
+ // Find direct parent (either another dropdown item or the main dropdown)
+ const parentDropdownItem = item.closest('wa-dropdown-item:not([slot="submenu"])');
+
+ let siblingItems: WaDropdownItem[];
+
+ if (parentDropdownItem) {
+ // Item is in a submenu, so get sibling items from the parent
+ siblingItems = this.getSubmenuItems(parentDropdownItem, true);
+ } else {
+ // Item is in the top level menu
+ siblingItems = this.getItems(true);
+ }
+
+ // Close only sibling submenus, not the item itself or its ancestors
+ siblingItems.forEach(siblingItem => {
+ if (siblingItem !== item && siblingItem.submenuOpen) {
+ siblingItem.submenuOpen = false;
+ }
+ });
+
+ // Don't reset the submenu stack - just add this item if it's not already there
+ if (!this.openSubmenuStack.includes(item)) {
+ this.openSubmenuStack.push(item);
+ }
+ }
+
+ /** Get the slotted trigger button, a or