luxen-ui 0.22.0 → 0.22.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -78,6 +78,14 @@ export declare class Dropdown extends LuxenElement {
78
78
  private _hoverTimer;
79
79
  private _hoverItem;
80
80
  private _openSubmenu;
81
+ /**
82
+ * Drop the pending hover-open. A click or a key press is an explicit decision
83
+ * that supersedes it — left armed, the timer would fire afterwards and reopen
84
+ * a submenu the user just closed. By default `_hoverItem` is kept, so staying
85
+ * on the same row doesn't re-arm it; `forget` also clears it, for when the
86
+ * pointer leaves the row or the menu goes away.
87
+ */
88
+ private _cancelHoverIntent;
81
89
  private _closeAllSubmenus;
82
90
  /**
83
91
  * The item targeted by a pointer event, via the composed path. Returns null
@@ -1 +1 @@
1
- {"version":3,"file":"dropdown.d.ts","sourceRoot":"","sources":["../../../src/html/elements/dropdown/dropdown.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dropdown.d.ts","sourceRoot":"","sources":["../../../src/html/elements/dropdown/dropdown.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import{tagName as e}from"../../registry.js";import{i as t}from"../../chunks/lit.js";import{i as n,t as r}from"../../chunks/decorate.js";import{a as i}from"../../chunks/lit-html.js";import{LuxenElement as a}from"../../shared/luxen-element.js";import{ShowEvent as o}from"../../events/show.js";import{AfterShowEvent as s}from"../../events/after-show.js";import{HideEvent as c}from"../../events/hide.js";import{AfterHideEvent as l}from"../../events/after-hide.js";import u from"../../shared/styles/host.styles.js";import{PopoverController as d}from"../../shared/controllers/popover.js";var f=t(`:host{--padding:.25rem;--background:var(--l-color-surface-overlay);--border-radius:6px;--shadow:var(--l-shadow-md);--show-duration:150;--hide-duration:150;display:inline-block;position:relative}.trigger{display:contents}[popover]{inset:unset;box-sizing:border-box;width:max-content;padding:var(--padding);border:1px solid var(--l-color-border-overlay);border-radius:var(--border-radius);background:var(--background);color:var(--l-color-text-primary);box-shadow:var(--shadow);margin:0;font-size:.875rem;line-height:1.5;overflow:visible}::slotted(hr){height:1px;margin-block:var(--padding);margin-inline:calc(var(--padding) * -1);background:var(--l-color-divider);border:0}`),p=class extends Event{constructor(e){super(`select`,{bubbles:!0,composed:!1,cancelable:!1}),this.item=e}},m=class extends a{constructor(...e){super(...e),this._floating=new d(this,{getTriggerElement:()=>this._triggerEl,getFloatingElement:()=>this._panelEl,getArrowElement:()=>null}),this._typeaheadBuffer=``,this._typeaheadTimeout=0,this.#e=!1,this.#t=`bottom-start`,this.#n=4,this.#r=!1,this._hoverTimer=0,this._hoverItem=null,this._onTriggerSlotChange=()=>{let e=this._triggerEl;if(!e)return;e.setAttribute(`aria-haspopup`,`menu`),e.setAttribute(`aria-expanded`,String(this.open));let t=e.getAttribute(`aria-label`)??e.textContent?.trim()??``,n=this.shadowRoot?.querySelector(`[role="menu"]`);n&&t&&n.setAttribute(`aria-label`,t)},this._onTriggerClick=e=>{this.disabled||this.open&&!this._panelEl?.matches(`:popover-open`)||(this.toggle(),this.open&&e.detail===0&&requestAnimationFrame(()=>this._focusFirstItem()))},this._onTriggerKeyDown=e=>{if(!this.disabled)if(e.key===`Escape`){if(!this.open)return;e.preventDefault(),this.hide()}else e.key===`ArrowDown`?(e.preventDefault(),this.show(),requestAnimationFrame(()=>this._focusFirstItem())):e.key===`ArrowUp`&&(e.preventDefault(),this.show(),requestAnimationFrame(()=>this._focusLastItem()))},this._onPanelKeyDown=e=>{switch(e.key){case`ArrowDown`:e.preventDefault(),this._focusNextItem();break;case`ArrowUp`:e.preventDefault(),this._focusPreviousItem();break;case`Home`:e.preventDefault(),this._focusFirstItem(this._currentLevelItems());break;case`End`:e.preventDefault(),this._focusLastItem(this._currentLevelItems());break;case`ArrowRight`:{let t=this._getCurrentItem();t?.hasSubmenu&&(e.preventDefault(),this._openSubmenu(t,{focus:!0}));break}case`ArrowLeft`:{let t=this._parentItemOf(this._getCurrentItem());t&&(e.preventDefault(),t.closeSubmenu(),this._setActiveItem(t));break}case`Escape`:{e.preventDefault();let t=this._parentItemOf(this._getCurrentItem());t?(t.closeSubmenu(),this._setActiveItem(t)):(this.hide(),this._triggerEl?.focus());break}case`Tab`:this.hide();break;case`Enter`:case` `:e.preventDefault(),this._selectCurrentItem();break;default:e.key.length===1&&!e.ctrlKey&&!e.metaKey&&this._handleTypeahead(e.key)}},this._onItemClick=e=>{let t=this._itemFromEvent(e);if(!(!t||t.disabled)){if(t.hasSubmenu){t.submenuOpen?t.closeSubmenu():this._openSubmenu(t);return}this._selectItem(t)}},this._onPanelPointerOver=e=>{if(e.pointerType!==`mouse`)return;let t=this._itemFromEvent(e);t!==this._hoverItem&&(clearTimeout(this._hoverTimer),this._hoverItem=t,!(!t||t.disabled)&&(this._hoverTimer=window.setTimeout(()=>{this._openSubmenu(t)},100)))},this._onPanelPointerOut=()=>{clearTimeout(this._hoverTimer),this._hoverItem=null},this._onToggle=e=>{if(e.newState!==`closed`||!this.open)return;if(this.dispatchEvent(new c)){this.open=!1;return}let t=this._panelEl;t?.isConnected&&t.showPopover()}}static{this.styles=[u,f]}#e;get open(){return this.#e}set open(e){this.#e=e}#t;get placement(){return this.#t}set placement(e){this.#t=e}#n;get distance(){return this.#n}set distance(e){this.#n=e}#r;get disabled(){return this.#r}set disabled(e){this.#r=e}#i;get minWidth(){return this.#i}set minWidth(e){this.#i=e}get _triggerEl(){return this.shadowRoot.querySelector(`.trigger slot`)?.assignedElements()[0]??null}get _panelEl(){return this.shadowRoot.querySelector(`[popover]`)}_getItems(){let t=this.shadowRoot.querySelector(`slot:not([name])`);return t?t.assignedElements().filter(t=>t.tagName===e(`dropdown-item`).toUpperCase()&&!t.disabled):[]}_getAllItems(){let t=this.shadowRoot.querySelector(`slot:not([name])`);return t?t.assignedElements().filter(t=>t.tagName===e(`dropdown-item`).toUpperCase()):[]}_getAllItemsDeep(){return[...this.querySelectorAll(e(`dropdown-item`))]}_levelItemsOf(t){let n=t.parentElement;return n&&n.tagName===e(`dropdown-item`).toUpperCase()?n.getSubmenuItems():this._getItems()}_parentItemOf(t){let n=t?.parentElement;return n&&n.tagName===e(`dropdown-item`).toUpperCase()?n:null}_currentLevelItems(){let e=this._getCurrentItem();return e?this._levelItemsOf(e):this._getItems()}_getDuration(e){let t=parseFloat(getComputedStyle(this).getPropertyValue(e));return Number.isNaN(t)?150:t}show(){this.open||this.disabled||this.dispatchEvent(new o)&&(this.open=!0)}hide(){this.open&&this.dispatchEvent(new c)&&(this.open=!1)}toggle(){this.open?this.hide():this.show()}disconnectedCallback(){super.disconnectedCallback(),clearTimeout(this._hoverTimer),this._hoverItem=null}updated(e){e.has(`open`)&&this._handleOpenChange()}async _handleOpenChange(){let e=this._panelEl;if(!e)return;let t={placement:this.placement,distance:this.distance,minWidth:this.minWidth};if(this.open){if(e.showPopover(),await this._floating.updatePosition(t),!this.open)return;await this._floating.animateShow(e,this._getDuration(`--show-duration`)),this._floating.startPositioning(t),this._triggerEl?.setAttribute(`aria-expanded`,`true`),this.dispatchEvent(new s)}else this._floating.stopPositioning(),this._closeAllSubmenus(),this._triggerEl?.setAttribute(`aria-expanded`,`false`),await this._floating.animateHide(e,this._getDuration(`--hide-duration`)),e.matches(`:popover-open`)&&e.hidePopover(),this.dispatchEvent(new l)}async _openSubmenu(e,{focus:t=!1}={}){if(!this.open||!this.isConnected)return;let n=!1;for(let t of this._levelItemsOf(e))t!==e&&(t.contains(document.activeElement)&&(n=!0),t.closeSubmenu());if(n&&!t&&this._setActiveItem(e),e.openSubmenu(),t){await e.updateComplete;let t=e.getSubmenuItems()[0];t&&this._setActiveItem(t)}}_closeAllSubmenus(){clearTimeout(this._hoverTimer),this._hoverItem=null;for(let e of this._getAllItems())e.closeSubmenu()}_itemFromEvent(t){let n=t.composedPath(),r=e(`dropdown-item`).toUpperCase(),i=n.find(e=>e instanceof Element&&e.tagName===r)??null;if(!i)return null;let a=i.shadowRoot?.querySelector(`.item`);if(a&&!n.includes(a)){let e=i.shadowRoot?.querySelector(`.submenu`);if(e&&n.includes(e))return null}return i}_setActiveItem(e){let t=e.shadowRoot.querySelector(`.item`);if(t){for(let e of this._getAllItemsDeep())e.shadowRoot.querySelector(`.item`)?.setAttribute(`tabindex`,`-1`);t.setAttribute(`tabindex`,`0`),t.focus();for(let t of this._levelItemsOf(e))t!==e&&t.closeSubmenu()}}_focusFirstItem(e=this._getItems()){e.length&&this._setActiveItem(e[0])}_focusLastItem(e=this._getItems()){e.length&&this._setActiveItem(e[e.length-1])}_getCurrentItem(){return this._getAllItemsDeep().find(e=>{let t=e.shadowRoot.querySelector(`.item`);return t?.getAttribute(`tabindex`)===`0`&&e.shadowRoot.activeElement===t})??null}_focusNextItem(){let e=this._currentLevelItems(),t=this._getCurrentItem(),n=e[((t?e.indexOf(t):-1)+1)%e.length];n&&this._setActiveItem(n)}_focusPreviousItem(){let e=this._currentLevelItems(),t=this._getCurrentItem(),n=e[((t?e.indexOf(t):0)-1+e.length)%e.length];n&&this._setActiveItem(n)}_handleTypeahead(e){clearTimeout(this._typeaheadTimeout),this._typeaheadBuffer+=e.toLowerCase(),this._typeaheadTimeout=window.setTimeout(()=>{this._typeaheadBuffer=``},500);let t=this._currentLevelItems().find(e=>e.getTextLabel().toLowerCase().startsWith(this._typeaheadBuffer));t&&this._setActiveItem(t)}_selectCurrentItem(){let e=this._getCurrentItem();e&&(e.hasSubmenu?this._openSubmenu(e,{focus:!0}):this._selectItem(e))}_selectItem(e){e.type===`checkbox`&&(e.checked=!e.checked),this.dispatchEvent(new p(e)),e.type!==`checkbox`&&(this.hide(),this._triggerEl?.focus())}render(){return i`
1
+ import{tagName as e}from"../../registry.js";import{i as t}from"../../chunks/lit.js";import{i as n,t as r}from"../../chunks/decorate.js";import{a as i}from"../../chunks/lit-html.js";import{LuxenElement as a}from"../../shared/luxen-element.js";import{ShowEvent as o}from"../../events/show.js";import{AfterShowEvent as s}from"../../events/after-show.js";import{HideEvent as c}from"../../events/hide.js";import{AfterHideEvent as l}from"../../events/after-hide.js";import u from"../../shared/styles/host.styles.js";import{PopoverController as d}from"../../shared/controllers/popover.js";var f=t(`:host{--padding:.25rem;--background:var(--l-color-surface-overlay);--border-radius:6px;--shadow:var(--l-shadow-md);--show-duration:150;--hide-duration:150;display:inline-block;position:relative}.trigger{display:contents}[popover]{inset:unset;box-sizing:border-box;width:max-content;padding:var(--padding);border:1px solid var(--l-color-border-overlay);border-radius:var(--border-radius);background:var(--background);color:var(--l-color-text-primary);box-shadow:var(--shadow);margin:0;font-size:.875rem;line-height:1.5;overflow:visible}::slotted(hr){height:1px;margin-block:var(--padding);margin-inline:calc(var(--padding) * -1);background:var(--l-color-divider);border:0}`),p=new Set([`Shift`,`Control`,`Alt`,`AltGraph`,`Meta`]),m=class extends Event{constructor(e){super(`select`,{bubbles:!0,composed:!1,cancelable:!1}),this.item=e}},h=class extends a{constructor(...t){super(...t),this._floating=new d(this,{getTriggerElement:()=>this._triggerEl,getFloatingElement:()=>this._panelEl,getArrowElement:()=>null}),this._typeaheadBuffer=``,this._typeaheadTimeout=0,this.#e=!1,this.#t=`bottom-start`,this.#n=4,this.#r=!1,this._hoverTimer=0,this._hoverItem=null,this._onTriggerSlotChange=()=>{let e=this._triggerEl;if(!e)return;e.setAttribute(`aria-haspopup`,`menu`),e.setAttribute(`aria-expanded`,String(this.open));let t=e.getAttribute(`aria-label`)??e.textContent?.trim()??``,n=this.shadowRoot?.querySelector(`[role="menu"]`);n&&t&&n.setAttribute(`aria-label`,t)},this._onTriggerClick=e=>{this.disabled||this.open&&!this._panelEl?.matches(`:popover-open`)||(this.toggle(),this.open&&e.detail===0&&requestAnimationFrame(()=>this._focusFirstItem()))},this._onTriggerKeyDown=e=>{if(!this.disabled)if(e.key===`Escape`){if(!this.open)return;e.preventDefault(),this.hide()}else e.key===`ArrowDown`?(e.preventDefault(),this.show(),requestAnimationFrame(()=>this._focusFirstItem())):e.key===`ArrowUp`&&(e.preventDefault(),this.show(),requestAnimationFrame(()=>this._focusLastItem()))},this._onPanelKeyDown=e=>{switch(p.has(e.key)||this._cancelHoverIntent(),e.key){case`ArrowDown`:e.preventDefault(),this._focusNextItem();break;case`ArrowUp`:e.preventDefault(),this._focusPreviousItem();break;case`Home`:e.preventDefault(),this._focusFirstItem(this._currentLevelItems());break;case`End`:e.preventDefault(),this._focusLastItem(this._currentLevelItems());break;case`ArrowRight`:{let t=this._getCurrentItem();t?.hasSubmenu&&(e.preventDefault(),this._openSubmenu(t,{focus:!0}));break}case`ArrowLeft`:{let t=this._parentItemOf(this._getCurrentItem());t&&(e.preventDefault(),t.closeSubmenu(),this._setActiveItem(t));break}case`Escape`:{e.preventDefault();let t=this._parentItemOf(this._getCurrentItem());t?(t.closeSubmenu(),this._setActiveItem(t)):(this.hide(),this._triggerEl?.focus());break}case`Tab`:this.hide();break;case`Enter`:case` `:e.preventDefault(),this._selectCurrentItem();break;default:e.key.length===1&&!e.ctrlKey&&!e.metaKey&&this._handleTypeahead(e.key)}},this._onItemClick=e=>{let t=this._itemFromEvent(e);if(!(!t||t.disabled)){if(t.hasSubmenu){this._cancelHoverIntent(),t.submenuOpen?t.closeSubmenu():this._openSubmenu(t);return}this._selectItem(t)}},this._onPanelPointerOver=e=>{if(e.pointerType!==`mouse`)return;let t=this._itemFromEvent(e);t!==this._hoverItem&&(this._cancelHoverIntent(),this._hoverItem=t,!(!t||t.disabled)&&(this._hoverTimer=window.setTimeout(()=>{this._openSubmenu(t)},100)))},this._onPanelPointerOut=t=>{let n=t.relatedTarget;this._hoverItem&&n instanceof Element&&n.closest(e(`dropdown-item`))===this._hoverItem||this._cancelHoverIntent({forget:!0})},this._onToggle=e=>{if(e.newState!==`closed`||!this.open)return;if(this.dispatchEvent(new c)){this.open=!1;return}let t=this._panelEl;t?.isConnected&&t.showPopover()}}static{this.styles=[u,f]}#e;get open(){return this.#e}set open(e){this.#e=e}#t;get placement(){return this.#t}set placement(e){this.#t=e}#n;get distance(){return this.#n}set distance(e){this.#n=e}#r;get disabled(){return this.#r}set disabled(e){this.#r=e}#i;get minWidth(){return this.#i}set minWidth(e){this.#i=e}get _triggerEl(){return this.shadowRoot.querySelector(`.trigger slot`)?.assignedElements()[0]??null}get _panelEl(){return this.shadowRoot.querySelector(`[popover]`)}_getItems(){let t=this.shadowRoot.querySelector(`slot:not([name])`);return t?t.assignedElements().filter(t=>t.tagName===e(`dropdown-item`).toUpperCase()&&!t.disabled):[]}_getAllItems(){let t=this.shadowRoot.querySelector(`slot:not([name])`);return t?t.assignedElements().filter(t=>t.tagName===e(`dropdown-item`).toUpperCase()):[]}_getAllItemsDeep(){return[...this.querySelectorAll(e(`dropdown-item`))]}_levelItemsOf(t){let n=t.parentElement;return n&&n.tagName===e(`dropdown-item`).toUpperCase()?n.getSubmenuItems():this._getItems()}_parentItemOf(t){let n=t?.parentElement;return n&&n.tagName===e(`dropdown-item`).toUpperCase()?n:null}_currentLevelItems(){let e=this._getCurrentItem();return e?this._levelItemsOf(e):this._getItems()}_getDuration(e){let t=parseFloat(getComputedStyle(this).getPropertyValue(e));return Number.isNaN(t)?150:t}show(){this.open||this.disabled||this.dispatchEvent(new o)&&(this.open=!0)}hide(){this.open&&this.dispatchEvent(new c)&&(this.open=!1)}toggle(){this.open?this.hide():this.show()}disconnectedCallback(){super.disconnectedCallback(),this._cancelHoverIntent({forget:!0})}updated(e){e.has(`open`)&&this._handleOpenChange()}async _handleOpenChange(){let e=this._panelEl;if(!e)return;let t={placement:this.placement,distance:this.distance,minWidth:this.minWidth};if(this.open){if(e.showPopover(),await this._floating.updatePosition(t),!this.open)return;await this._floating.animateShow(e,this._getDuration(`--show-duration`)),this._floating.startPositioning(t),this._triggerEl?.setAttribute(`aria-expanded`,`true`),this.dispatchEvent(new s)}else this._floating.stopPositioning(),this._closeAllSubmenus(),this._triggerEl?.setAttribute(`aria-expanded`,`false`),await this._floating.animateHide(e,this._getDuration(`--hide-duration`)),e.matches(`:popover-open`)&&e.hidePopover(),this.dispatchEvent(new l)}async _openSubmenu(e,{focus:t=!1}={}){if(!this.open||!this.isConnected)return;let n=!1;for(let t of this._levelItemsOf(e))t!==e&&(t.contains(document.activeElement)&&(n=!0),t.closeSubmenu());if(n&&!t&&this._setActiveItem(e),e.openSubmenu(),t){await e.updateComplete;let t=e.getSubmenuItems()[0];t&&this._setActiveItem(t)}}_cancelHoverIntent({forget:e=!1}={}){clearTimeout(this._hoverTimer),e&&(this._hoverItem=null)}_closeAllSubmenus(){this._cancelHoverIntent({forget:!0});for(let e of this._getAllItems())e.closeSubmenu()}_itemFromEvent(t){let n=t.composedPath(),r=e(`dropdown-item`).toUpperCase(),i=n.find(e=>e instanceof Element&&e.tagName===r)??null;if(!i)return null;let a=i.shadowRoot?.querySelector(`.item`);if(a&&!n.includes(a)){let e=i.shadowRoot?.querySelector(`.submenu`);if(e&&n.includes(e))return null}return i}_setActiveItem(e){let t=e.shadowRoot.querySelector(`.item`);if(t){for(let e of this._getAllItemsDeep())e.shadowRoot.querySelector(`.item`)?.setAttribute(`tabindex`,`-1`);t.setAttribute(`tabindex`,`0`),t.focus();for(let t of this._levelItemsOf(e))t!==e&&t.closeSubmenu()}}_focusFirstItem(e=this._getItems()){e.length&&this._setActiveItem(e[0])}_focusLastItem(e=this._getItems()){e.length&&this._setActiveItem(e[e.length-1])}_getCurrentItem(){return this._getAllItemsDeep().find(e=>{let t=e.shadowRoot.querySelector(`.item`);return t?.getAttribute(`tabindex`)===`0`&&e.shadowRoot.activeElement===t})??null}_focusNextItem(){let e=this._currentLevelItems(),t=this._getCurrentItem(),n=e[((t?e.indexOf(t):-1)+1)%e.length];n&&this._setActiveItem(n)}_focusPreviousItem(){let e=this._currentLevelItems(),t=this._getCurrentItem(),n=e[((t?e.indexOf(t):0)-1+e.length)%e.length];n&&this._setActiveItem(n)}_handleTypeahead(e){clearTimeout(this._typeaheadTimeout),this._typeaheadBuffer+=e.toLowerCase(),this._typeaheadTimeout=window.setTimeout(()=>{this._typeaheadBuffer=``},500);let t=this._currentLevelItems().find(e=>e.getTextLabel().toLowerCase().startsWith(this._typeaheadBuffer));t&&this._setActiveItem(t)}_selectCurrentItem(){let e=this._getCurrentItem();e&&(e.hasSubmenu?this._openSubmenu(e,{focus:!0}):this._selectItem(e))}_selectItem(e){e.type===`checkbox`&&(e.checked=!e.checked),this.dispatchEvent(new m(e)),e.type!==`checkbox`&&(this.hide(),this._triggerEl?.focus())}render(){return i`
2
2
  <div
3
3
  class="trigger"
4
4
  @click=${this._onTriggerClick}
@@ -24,5 +24,5 @@ import{tagName as e}from"../../registry.js";import{i as t}from"../../chunks/lit.
24
24
  </div>
25
25
  <slot name="footer"></slot>
26
26
  </div>
27
- `}};r([n({type:Boolean,reflect:!0})],m.prototype,`open`,null),r([n()],m.prototype,`placement`,null),r([n({type:Number})],m.prototype,`distance`,null),r([n({type:Boolean,reflect:!0})],m.prototype,`disabled`,null),r([n({attribute:`min-width`})],m.prototype,`minWidth`,null);export{m as Dropdown,p as DropdownSelectEvent};
27
+ `}};r([n({type:Boolean,reflect:!0})],h.prototype,`open`,null),r([n()],h.prototype,`placement`,null),r([n({type:Number})],h.prototype,`distance`,null),r([n({type:Boolean,reflect:!0})],h.prototype,`disabled`,null),r([n({attribute:`min-width`})],h.prototype,`minWidth`,null);export{h as Dropdown,m as DropdownSelectEvent};
28
28
  //# sourceMappingURL=dropdown.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"dropdown.js","names":[],"sources":["../../../src/html/elements/dropdown/dropdown.css?inline","../../../src/html/elements/dropdown/dropdown.ts"],"sourcesContent":[":host {\n --padding: 0.25rem;\n\n --background: var(--l-color-surface-overlay);\n --border-radius: 6px;\n --shadow: var(--l-shadow-md);\n --show-duration: 150;\n --hide-duration: 150;\n\n display: inline-block;\n position: relative;\n}\n\n.trigger {\n display: contents;\n}\n\n[popover] {\n inset: unset;\n overflow: visible;\n box-sizing: border-box;\n width: max-content;\n padding: var(--padding);\n margin: 0;\n border: 1px solid var(--l-color-border-overlay);\n border-radius: var(--border-radius);\n background: var(--background);\n color: var(--l-color-text-primary);\n box-shadow: var(--shadow);\n font-size: 0.875rem;\n line-height: 1.5;\n}\n\n/* Lightweight separator alternative to <l-divider> — same dimensions, no extra import. */\n::slotted(hr) {\n height: 1px;\n margin-block: var(--padding);\n margin-inline: calc(var(--padding) * -1);\n border: 0;\n background: var(--l-color-divider);\n}\n","import { html, unsafeCSS, type PropertyValues } from 'lit';\nimport { LuxenElement } from '../../shared/luxen-element.js';\nimport { property } from 'lit/decorators.js';\nimport type { Placement } from '@floating-ui/dom';\nimport { PopoverController } from '../../shared/controllers/popover.js';\nimport { tagName } from '../../registry.js';\nimport type { DropdownItem } from '../dropdown-item/dropdown-item.js';\nimport { ShowEvent, AfterShowEvent, HideEvent, AfterHideEvent } from '../../events/index.js';\nimport hostStyles from '../../shared/styles/host.styles.js';\nimport rawStyles from './dropdown.css?inline';\n\nconst styles = unsafeCSS(rawStyles);\n\n/** Fired when a dropdown item is selected. Bubbles; not composed. */\nexport class DropdownSelectEvent extends Event {\n readonly item: DropdownItem;\n constructor(item: DropdownItem) {\n super('select', { bubbles: true, composed: false, cancelable: false });\n this.item = item;\n }\n}\n\ninterface DropdownEventMap {\n select: DropdownSelectEvent;\n}\n\n/**\n * A dropdown menu anchored to a trigger element.\n *\n * @slot trigger - The element that triggers the dropdown.\n * @slot header - Optional content rendered above the menu items (e.g. a user profile row). Use an `<l-divider>` (or `<hr>`) after it to separate from items.\n * @slot - Menu content (`l-dropdown-item` elements). Drop an `<l-divider>` (or `<hr>`) between items to render a section separator, or an `<l-dropdown-label>` to caption a group of items. Nest items with `slot=\"submenu\"` inside an item to create a submenu.\n * @slot footer - Optional content rendered below the menu items (e.g. a version label or shortcut row). Use an `<l-divider>` (or `<hr>`) before it to separate from items.\n *\n * @csspart panel - The floating menu container.\n *\n * @cssproperty --background - Panel background color.\n * @cssproperty --border-radius - Panel border radius. Default `8px`.\n * @cssproperty --padding - Panel inner padding. Default `0.25rem`. Slotted `<l-divider>` elements bleed by this amount on each side to span the panel edges.\n * @cssproperty --shadow - Panel box shadow.\n * @cssproperty --show-duration - Show animation duration in ms. Default `150`.\n * @cssproperty --hide-duration - Hide animation duration in ms. Default `150`.\n *\n * @event show - Fired before the dropdown opens. Cancelable.\n * @event after-show - Fired after the open animation completes.\n * @event hide - Fired before the dropdown closes. Cancelable.\n * @event after-hide - Fired after the close animation completes.\n * @event select - Fired when an item is selected, including items nested in submenus. Bubbles. Properties: `item: DropdownItem`.\n *\n * @customElement l-dropdown\n */\n// oxlint-disable-next-line typescript/no-unsafe-declaration-merging -- typed addEventListener overloads merged below; no uninitialized properties.\nexport class Dropdown extends LuxenElement {\n static override styles = [hostStyles, styles];\n\n private _floating = new PopoverController(this, {\n getTriggerElement: () => this._triggerEl,\n getFloatingElement: () => this._panelEl,\n getArrowElement: () => null,\n });\n\n private _typeaheadBuffer = '';\n private _typeaheadTimeout = 0;\n\n /** Whether the dropdown is open. */\n @property({ type: Boolean, reflect: true })\n accessor open = false;\n\n /** Preferred placement of the panel. */\n @property()\n accessor placement: Placement = 'bottom-start';\n\n /** Distance in pixels from the trigger. */\n @property({ type: Number })\n accessor distance = 4;\n\n /** Disables the dropdown trigger. */\n @property({ type: Boolean, reflect: true })\n accessor disabled = false;\n\n /**\n * Floor the panel's width at the trigger's width. Set to `trigger` so the\n * panel is never narrower than the trigger; it still grows with its content.\n * Useful for select-like triggers (a date-range or filter button) where the\n * panel should line up with the control. Re-applies if the trigger resizes\n * while open.\n */\n @property({ attribute: 'min-width' })\n accessor minWidth: 'trigger' | undefined;\n\n private get _triggerEl(): HTMLElement | null {\n const slot = this.shadowRoot!.querySelector<HTMLSlotElement>('.trigger slot');\n return (slot?.assignedElements()[0] as HTMLElement) ?? null;\n }\n\n private get _panelEl(): HTMLElement | null {\n return this.shadowRoot!.querySelector<HTMLElement>('[popover]');\n }\n\n private _getItems(): DropdownItem[] {\n const menuSlot = this.shadowRoot!.querySelector<HTMLSlotElement>('slot:not([name])');\n if (!menuSlot) return [];\n return (menuSlot.assignedElements() as DropdownItem[]).filter(\n (el) => el.tagName === tagName('dropdown-item').toUpperCase() && !el.disabled,\n );\n }\n\n private _getAllItems(): DropdownItem[] {\n const menuSlot = this.shadowRoot!.querySelector<HTMLSlotElement>('slot:not([name])');\n if (!menuSlot) return [];\n return (menuSlot.assignedElements() as DropdownItem[]).filter(\n (el) => el.tagName === tagName('dropdown-item').toUpperCase(),\n );\n }\n\n /** Every item in the menu tree, submenu items included (light-DOM descendants). */\n private _getAllItemsDeep(): DropdownItem[] {\n return [...this.querySelectorAll<DropdownItem>(tagName('dropdown-item'))];\n }\n\n /** The enabled items of the menu level `item` belongs to (its siblings, itself included). */\n private _levelItemsOf(item: DropdownItem): DropdownItem[] {\n const parent = item.parentElement;\n if (parent && parent.tagName === tagName('dropdown-item').toUpperCase()) {\n return (parent as DropdownItem).getSubmenuItems();\n }\n return this._getItems();\n }\n\n /** The parent item whose submenu contains `item`, or null at the root level. */\n private _parentItemOf(item: DropdownItem | null): DropdownItem | null {\n const parent = item?.parentElement;\n if (parent && parent.tagName === tagName('dropdown-item').toUpperCase()) {\n return parent as DropdownItem;\n }\n return null;\n }\n\n /** The enabled items of the level that currently holds focus (root level as fallback). */\n private _currentLevelItems(): DropdownItem[] {\n const current = this._getCurrentItem();\n return current ? this._levelItemsOf(current) : this._getItems();\n }\n\n private _getDuration(prop: '--show-duration' | '--hide-duration'): number {\n const parsed = parseFloat(getComputedStyle(this).getPropertyValue(prop));\n return Number.isNaN(parsed) ? 150 : parsed;\n }\n\n // --- Public API ---\n\n show() {\n if (this.open || this.disabled) return;\n if (this.dispatchEvent(new ShowEvent())) this.open = true;\n }\n\n hide() {\n if (!this.open) return;\n if (this.dispatchEvent(new HideEvent())) this.open = false;\n }\n\n toggle() {\n if (this.open) this.hide();\n else this.show();\n }\n\n // --- Lifecycle ---\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n clearTimeout(this._hoverTimer);\n this._hoverItem = null;\n }\n\n override updated(changed: PropertyValues<this>) {\n if (changed.has('open')) {\n void this._handleOpenChange();\n }\n }\n\n // --- Open/Close ---\n\n private async _handleOpenChange() {\n const panel = this._panelEl;\n if (!panel) return;\n\n const posOpts = {\n placement: this.placement,\n distance: this.distance,\n minWidth: this.minWidth,\n };\n\n if (this.open) {\n panel.showPopover();\n await this._floating.updatePosition(posOpts);\n if (!this.open) return;\n await this._floating.animateShow(panel, this._getDuration('--show-duration'));\n this._floating.startPositioning(posOpts);\n this._triggerEl?.setAttribute('aria-expanded', 'true');\n this.dispatchEvent(new AfterShowEvent());\n } else {\n this._floating.stopPositioning();\n this._closeAllSubmenus();\n this._triggerEl?.setAttribute('aria-expanded', 'false');\n await this._floating.animateHide(panel, this._getDuration('--hide-duration'));\n if (panel.matches(':popover-open')) panel.hidePopover();\n this.dispatchEvent(new AfterHideEvent());\n }\n }\n\n // --- Submenus ---\n\n private _hoverTimer = 0;\n private _hoverItem: DropdownItem | null = null;\n\n private async _openSubmenu(item: DropdownItem, { focus = false } = {}) {\n // A pending hover timer may fire after the menu closed or unmounted\n if (!this.open || !this.isConnected) return;\n // Only one submenu open per level. If a sibling we're about to close holds\n // the focus (keyboard focus inside it, mouse hovers another row), the panel\n // would vanish under the focus and drop it to <body> — move focus to `item`\n // first so the roving tabindex stays coherent.\n let stealsFocus = false;\n for (const sibling of this._levelItemsOf(item)) {\n if (sibling === item) continue;\n if (sibling.contains(document.activeElement)) stealsFocus = true;\n sibling.closeSubmenu();\n }\n if (stealsFocus && !focus) this._setActiveItem(item);\n item.openSubmenu();\n if (focus) {\n await item.updateComplete;\n const first = item.getSubmenuItems()[0];\n if (first) this._setActiveItem(first);\n }\n }\n\n private _closeAllSubmenus() {\n clearTimeout(this._hoverTimer);\n this._hoverItem = null;\n for (const item of this._getAllItems()) item.closeSubmenu();\n }\n\n /**\n * The item targeted by a pointer event, via the composed path. Returns null\n * for hits inside an item's submenu panel that miss every nested item row\n * (e.g. the panel padding) — those must not read as hits on the parent item.\n */\n private _itemFromEvent(e: Event): DropdownItem | null {\n const path = e.composedPath();\n const itemTag = tagName('dropdown-item').toUpperCase();\n const item =\n path.find((n): n is DropdownItem => n instanceof Element && n.tagName === itemTag) ?? null;\n if (!item) return null;\n const row = item.shadowRoot?.querySelector('.item');\n if (row && !path.includes(row)) {\n // The path reached the item host but not its row. Reject only when the\n // hit actually landed inside this item's own submenu panel (its padding),\n // which must not read as a selection of the parent item.\n //\n // Why not `return null` outright: a click dispatched on the host element\n // — `item.click()`, a synthetic MouseEvent, or a testing tool (Cypress,\n // Playwright) whose coordinate hit-test resolves to the host rather than\n // the inner row — produces a path with neither the row nor the submenu in\n // it. Such programmatic activation should still select the item. Checking\n // for the submenu explicitly (instead of the absent row) is also\n // retargeting-independent, so it holds regardless of which shadow tree the\n // listener sits in.\n const submenu = item.shadowRoot?.querySelector('.submenu');\n if (submenu && path.includes(submenu)) return null;\n }\n return item;\n }\n\n // --- Focus management ---\n\n private _setActiveItem(item: DropdownItem) {\n const itemEl = item.shadowRoot!.querySelector<HTMLElement>('.item');\n if (!itemEl) return;\n\n // Reset all items, submenu items included — one roving tabindex tree-wide\n for (const i of this._getAllItemsDeep()) {\n const el = i.shadowRoot!.querySelector<HTMLElement>('.item');\n el?.setAttribute('tabindex', '-1');\n }\n\n itemEl.setAttribute('tabindex', '0');\n itemEl.focus();\n\n // APG: moving focus within a level collapses any sibling's open submenu, so\n // an unfocused parent never keeps aria-expanded=\"true\". The active item's\n // own submenu (one level down) is untouched — `item` is not its own sibling.\n for (const sibling of this._levelItemsOf(item)) {\n if (sibling !== item) sibling.closeSubmenu();\n }\n }\n\n private _focusFirstItem(items = this._getItems()) {\n if (items.length) this._setActiveItem(items[0]);\n }\n\n private _focusLastItem(items = this._getItems()) {\n if (items.length) this._setActiveItem(items[items.length - 1]);\n }\n\n private _getCurrentItem(): DropdownItem | null {\n return (\n this._getAllItemsDeep().find((item) => {\n const el = item.shadowRoot!.querySelector<HTMLElement>('.item');\n return el?.getAttribute('tabindex') === '0' && item.shadowRoot!.activeElement === el;\n }) ?? null\n );\n }\n\n private _focusNextItem() {\n const items = this._currentLevelItems();\n const current = this._getCurrentItem();\n const index = current ? items.indexOf(current) : -1;\n const next = items[(index + 1) % items.length];\n if (next) this._setActiveItem(next);\n }\n\n private _focusPreviousItem() {\n const items = this._currentLevelItems();\n const current = this._getCurrentItem();\n const index = current ? items.indexOf(current) : 0;\n const prev = items[(index - 1 + items.length) % items.length];\n if (prev) this._setActiveItem(prev);\n }\n\n // --- Typeahead ---\n\n private _handleTypeahead(key: string) {\n clearTimeout(this._typeaheadTimeout);\n this._typeaheadBuffer += key.toLowerCase();\n this._typeaheadTimeout = window.setTimeout(() => {\n this._typeaheadBuffer = '';\n }, 500);\n\n const items = this._currentLevelItems();\n const match = items.find((item) =>\n item.getTextLabel().toLowerCase().startsWith(this._typeaheadBuffer),\n );\n if (match) this._setActiveItem(match);\n }\n\n // --- Event handlers ---\n\n /**\n * Expose menu-button semantics on whatever element is slotted as the trigger,\n * and give the root `role=\"menu\"` panel an accessible name derived from it.\n * Runs on initial assignment and on any runtime trigger swap.\n */\n private _onTriggerSlotChange = () => {\n const trigger = this._triggerEl;\n if (!trigger) return;\n trigger.setAttribute('aria-haspopup', 'menu');\n trigger.setAttribute('aria-expanded', String(this.open));\n\n // Cross-shadow aria-labelledby to the trigger is impossible, so copy its\n // accessible name onto the menu as a plain aria-label.\n const name = trigger.getAttribute('aria-label') ?? trigger.textContent?.trim() ?? '';\n const menu = this.shadowRoot?.querySelector<HTMLElement>('[role=\"menu\"]');\n if (menu && name) menu.setAttribute('aria-label', name);\n };\n\n private _onTriggerClick = (e: MouseEvent) => {\n if (this.disabled) return;\n // The pointerdown that produced this click already light-dismissed the panel:\n // `open` still reads true, but the panel has left the top layer and the\n // queued `toggle` owns the close (and its `hide` event). Closing here too\n // would emit a second one. A keyboard activation never gets here — no\n // light-dismiss, so the panel is still open.\n if (this.open && !this._panelEl?.matches(':popover-open')) return;\n this.toggle();\n // Space/Enter on a native button dispatches click with detail=0; focus the\n // first item so the menu is keyboard-navigable immediately on open.\n if (this.open && e.detail === 0) {\n requestAnimationFrame(() => this._focusFirstItem());\n }\n };\n\n private _onTriggerKeyDown = (e: KeyboardEvent) => {\n if (this.disabled) return;\n\n if (e.key === 'Escape') {\n // A pointer-opened menu leaves focus on the trigger, so `_onPanelKeyDown`\n // never sees the key. The platform's popover light-dismiss normally covers\n // that, but it runs as a close request the page can suppress — any\n // `preventDefault()` on the Escape keydown (a common app-level shortcut\n // handler) leaves the menu stuck open. Close it here, and preventDefault\n // so the close request doesn't race our animated hide.\n if (!this.open) return;\n e.preventDefault();\n this.hide();\n // Focus is already on the trigger — nothing to restore.\n } else if (e.key === 'ArrowDown') {\n e.preventDefault();\n this.show();\n requestAnimationFrame(() => this._focusFirstItem());\n } else if (e.key === 'ArrowUp') {\n e.preventDefault();\n this.show();\n requestAnimationFrame(() => this._focusLastItem());\n }\n };\n\n private _onPanelKeyDown = (e: KeyboardEvent) => {\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault();\n this._focusNextItem();\n break;\n case 'ArrowUp':\n e.preventDefault();\n this._focusPreviousItem();\n break;\n case 'Home':\n e.preventDefault();\n this._focusFirstItem(this._currentLevelItems());\n break;\n case 'End':\n e.preventDefault();\n this._focusLastItem(this._currentLevelItems());\n break;\n case 'ArrowRight': {\n const current = this._getCurrentItem();\n if (current?.hasSubmenu) {\n e.preventDefault();\n void this._openSubmenu(current, { focus: true });\n }\n break;\n }\n case 'ArrowLeft': {\n const parentItem = this._parentItemOf(this._getCurrentItem());\n if (parentItem) {\n e.preventDefault();\n parentItem.closeSubmenu();\n this._setActiveItem(parentItem);\n }\n break;\n }\n case 'Escape': {\n // Close one level at a time: a submenu first, the menu itself at the root\n e.preventDefault();\n const parentItem = this._parentItemOf(this._getCurrentItem());\n if (parentItem) {\n parentItem.closeSubmenu();\n this._setActiveItem(parentItem);\n } else {\n this.hide();\n this._triggerEl?.focus();\n }\n break;\n }\n case 'Tab':\n // APG: Tab closes the menu (and any open submenus, via the hide path)\n // and lets focus move to the next tab-sequence element naturally — so\n // no preventDefault and no focus return to the trigger.\n this.hide();\n break;\n case 'Enter':\n case ' ':\n e.preventDefault();\n this._selectCurrentItem();\n break;\n default:\n if (e.key.length === 1 && !e.ctrlKey && !e.metaKey) {\n this._handleTypeahead(e.key);\n }\n }\n };\n\n private _onItemClick = (e: Event) => {\n const item = this._itemFromEvent(e);\n if (!item || item.disabled) return;\n if (item.hasSubmenu) {\n // A parent item toggles its submenu instead of selecting\n if (item.submenuOpen) item.closeSubmenu();\n else void this._openSubmenu(item);\n return;\n }\n this._selectItem(item);\n };\n\n /** Open submenus on hover after a short intent delay; hovering a sibling closes them. */\n private _onPanelPointerOver = (e: PointerEvent) => {\n // Touch taps fire pointerover too — they must only toggle via click,\n // or the hover timer would reopen a submenu the tap just closed.\n if (e.pointerType !== 'mouse') return;\n const item = this._itemFromEvent(e);\n if (item === this._hoverItem) return;\n clearTimeout(this._hoverTimer);\n this._hoverItem = item;\n if (!item || item.disabled) return;\n this._hoverTimer = window.setTimeout(() => {\n void this._openSubmenu(item);\n }, 100);\n };\n\n private _onPanelPointerOut = () => {\n clearTimeout(this._hoverTimer);\n this._hoverItem = null;\n };\n\n private _selectCurrentItem() {\n const current = this._getCurrentItem();\n if (!current) return;\n if (current.hasSubmenu) {\n void this._openSubmenu(current, { focus: true });\n } else {\n this._selectItem(current);\n }\n }\n\n private _selectItem(item: DropdownItem) {\n if (item.type === 'checkbox') {\n item.checked = !item.checked;\n }\n this.dispatchEvent(new DropdownSelectEvent(item));\n if (item.type !== 'checkbox') {\n this.hide();\n this._triggerEl?.focus();\n }\n }\n\n /**\n * Route `popover=\"auto\"` light-dismiss through the same close path as every\n * other user-driven close (trigger click, Escape, item selection), so the\n * cancelable `hide` event fires for it too. Assigning `open` here directly\n * would skip it and desync anything a consumer syncs on `hide`.\n *\n * Driving `open` as a property or attribute still closes the menu with only\n * `after-hide` — `l-dialog` mediates that path from `updated()`, this element\n * does not. A gap worth closing on its own.\n *\n * The platform makes a popover's close non-cancelable (`beforetoggle` is only\n * cancelable when opening), so a vetoed `hide` is honored by putting the panel\n * back. `toggle` is the right hook for that: it is queued after the hide has\n * actually been applied, whereas during `beforetoggle` the panel is still\n * `:popover-open` and `showPopover()` would be a no-op the pending hide undoes.\n */\n private _onToggle = (e: Event) => {\n const toggleEvent = e as ToggleEvent;\n // Our own close path flips `open` before hiding the popover, so this guard\n // keeps it from emitting a second `hide` for the same close.\n if (toggleEvent.newState !== 'closed' || !this.open) return;\n\n if (this.dispatchEvent(new HideEvent())) {\n this.open = false;\n return;\n }\n const panel = this._panelEl;\n if (panel?.isConnected) panel.showPopover();\n };\n\n override render() {\n return html`\n <div\n class=\"trigger\"\n @click=${this._onTriggerClick}\n @keydown=${this._onTriggerKeyDown}\n >\n <slot\n name=\"trigger\"\n @slotchange=${this._onTriggerSlotChange}\n ></slot>\n </div>\n <div\n popover=\"auto\"\n part=\"panel\"\n @keydown=${this._onPanelKeyDown}\n @click=${this._onItemClick}\n @pointerover=${this._onPanelPointerOver}\n @pointerout=${this._onPanelPointerOut}\n @toggle=${this._onToggle}\n >\n <slot name=\"header\"></slot>\n <div role=\"menu\">\n <slot></slot>\n </div>\n <slot name=\"footer\"></slot>\n </div>\n `;\n }\n}\n\n// Types `addEventListener('select', …)` as `DropdownSelectEvent` on this element\n// (the global event map can't be augmented for the colliding name `select`).\n// See the Tabs interface in tabs.ts for the full rationale and the gotchas.\nexport interface Dropdown {\n addEventListener<K extends keyof DropdownEventMap>(\n type: K,\n listener: (this: Dropdown, ev: DropdownEventMap[K]) => void,\n options?: boolean | AddEventListenerOptions,\n ): void;\n addEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | AddEventListenerOptions,\n ): void;\n removeEventListener<K extends keyof DropdownEventMap>(\n type: K,\n listener: (this: Dropdown, ev: DropdownEventMap[K]) => void,\n options?: boolean | EventListenerOptions,\n ): void;\n removeEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | EventListenerOptions,\n ): void;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"dropdown.js","names":[],"sources":["../../../src/html/elements/dropdown/dropdown.css?inline","../../../src/html/elements/dropdown/dropdown.ts"],"sourcesContent":[":host {\n --padding: 0.25rem;\n\n --background: var(--l-color-surface-overlay);\n --border-radius: 6px;\n --shadow: var(--l-shadow-md);\n --show-duration: 150;\n --hide-duration: 150;\n\n display: inline-block;\n position: relative;\n}\n\n.trigger {\n display: contents;\n}\n\n[popover] {\n inset: unset;\n overflow: visible;\n box-sizing: border-box;\n width: max-content;\n padding: var(--padding);\n margin: 0;\n border: 1px solid var(--l-color-border-overlay);\n border-radius: var(--border-radius);\n background: var(--background);\n color: var(--l-color-text-primary);\n box-shadow: var(--shadow);\n font-size: 0.875rem;\n line-height: 1.5;\n}\n\n/* Lightweight separator alternative to <l-divider> — same dimensions, no extra import. */\n::slotted(hr) {\n height: 1px;\n margin-block: var(--padding);\n margin-inline: calc(var(--padding) * -1);\n border: 0;\n background: var(--l-color-divider);\n}\n","import { html, unsafeCSS, type PropertyValues } from 'lit';\nimport { LuxenElement } from '../../shared/luxen-element.js';\nimport { property } from 'lit/decorators.js';\nimport type { Placement } from '@floating-ui/dom';\nimport { PopoverController } from '../../shared/controllers/popover.js';\nimport { tagName } from '../../registry.js';\nimport type { DropdownItem } from '../dropdown-item/dropdown-item.js';\nimport { ShowEvent, AfterShowEvent, HideEvent, AfterHideEvent } from '../../events/index.js';\nimport hostStyles from '../../shared/styles/host.styles.js';\nimport rawStyles from './dropdown.css?inline';\n\nconst styles = unsafeCSS(rawStyles);\n\n/** Keys that decide nothing on their own — pressing one must not drop a pending hover. */\nconst MODIFIER_KEYS = new Set(['Shift', 'Control', 'Alt', 'AltGraph', 'Meta']);\n\n/** Fired when a dropdown item is selected. Bubbles; not composed. */\nexport class DropdownSelectEvent extends Event {\n readonly item: DropdownItem;\n constructor(item: DropdownItem) {\n super('select', { bubbles: true, composed: false, cancelable: false });\n this.item = item;\n }\n}\n\ninterface DropdownEventMap {\n select: DropdownSelectEvent;\n}\n\n/**\n * A dropdown menu anchored to a trigger element.\n *\n * @slot trigger - The element that triggers the dropdown.\n * @slot header - Optional content rendered above the menu items (e.g. a user profile row). Use an `<l-divider>` (or `<hr>`) after it to separate from items.\n * @slot - Menu content (`l-dropdown-item` elements). Drop an `<l-divider>` (or `<hr>`) between items to render a section separator, or an `<l-dropdown-label>` to caption a group of items. Nest items with `slot=\"submenu\"` inside an item to create a submenu.\n * @slot footer - Optional content rendered below the menu items (e.g. a version label or shortcut row). Use an `<l-divider>` (or `<hr>`) before it to separate from items.\n *\n * @csspart panel - The floating menu container.\n *\n * @cssproperty --background - Panel background color.\n * @cssproperty --border-radius - Panel border radius. Default `8px`.\n * @cssproperty --padding - Panel inner padding. Default `0.25rem`. Slotted `<l-divider>` elements bleed by this amount on each side to span the panel edges.\n * @cssproperty --shadow - Panel box shadow.\n * @cssproperty --show-duration - Show animation duration in ms. Default `150`.\n * @cssproperty --hide-duration - Hide animation duration in ms. Default `150`.\n *\n * @event show - Fired before the dropdown opens. Cancelable.\n * @event after-show - Fired after the open animation completes.\n * @event hide - Fired before the dropdown closes. Cancelable.\n * @event after-hide - Fired after the close animation completes.\n * @event select - Fired when an item is selected, including items nested in submenus. Bubbles. Properties: `item: DropdownItem`.\n *\n * @customElement l-dropdown\n */\n// oxlint-disable-next-line typescript/no-unsafe-declaration-merging -- typed addEventListener overloads merged below; no uninitialized properties.\nexport class Dropdown extends LuxenElement {\n static override styles = [hostStyles, styles];\n\n private _floating = new PopoverController(this, {\n getTriggerElement: () => this._triggerEl,\n getFloatingElement: () => this._panelEl,\n getArrowElement: () => null,\n });\n\n private _typeaheadBuffer = '';\n private _typeaheadTimeout = 0;\n\n /** Whether the dropdown is open. */\n @property({ type: Boolean, reflect: true })\n accessor open = false;\n\n /** Preferred placement of the panel. */\n @property()\n accessor placement: Placement = 'bottom-start';\n\n /** Distance in pixels from the trigger. */\n @property({ type: Number })\n accessor distance = 4;\n\n /** Disables the dropdown trigger. */\n @property({ type: Boolean, reflect: true })\n accessor disabled = false;\n\n /**\n * Floor the panel's width at the trigger's width. Set to `trigger` so the\n * panel is never narrower than the trigger; it still grows with its content.\n * Useful for select-like triggers (a date-range or filter button) where the\n * panel should line up with the control. Re-applies if the trigger resizes\n * while open.\n */\n @property({ attribute: 'min-width' })\n accessor minWidth: 'trigger' | undefined;\n\n private get _triggerEl(): HTMLElement | null {\n const slot = this.shadowRoot!.querySelector<HTMLSlotElement>('.trigger slot');\n return (slot?.assignedElements()[0] as HTMLElement) ?? null;\n }\n\n private get _panelEl(): HTMLElement | null {\n return this.shadowRoot!.querySelector<HTMLElement>('[popover]');\n }\n\n private _getItems(): DropdownItem[] {\n const menuSlot = this.shadowRoot!.querySelector<HTMLSlotElement>('slot:not([name])');\n if (!menuSlot) return [];\n return (menuSlot.assignedElements() as DropdownItem[]).filter(\n (el) => el.tagName === tagName('dropdown-item').toUpperCase() && !el.disabled,\n );\n }\n\n private _getAllItems(): DropdownItem[] {\n const menuSlot = this.shadowRoot!.querySelector<HTMLSlotElement>('slot:not([name])');\n if (!menuSlot) return [];\n return (menuSlot.assignedElements() as DropdownItem[]).filter(\n (el) => el.tagName === tagName('dropdown-item').toUpperCase(),\n );\n }\n\n /** Every item in the menu tree, submenu items included (light-DOM descendants). */\n private _getAllItemsDeep(): DropdownItem[] {\n return [...this.querySelectorAll<DropdownItem>(tagName('dropdown-item'))];\n }\n\n /** The enabled items of the menu level `item` belongs to (its siblings, itself included). */\n private _levelItemsOf(item: DropdownItem): DropdownItem[] {\n const parent = item.parentElement;\n if (parent && parent.tagName === tagName('dropdown-item').toUpperCase()) {\n return (parent as DropdownItem).getSubmenuItems();\n }\n return this._getItems();\n }\n\n /** The parent item whose submenu contains `item`, or null at the root level. */\n private _parentItemOf(item: DropdownItem | null): DropdownItem | null {\n const parent = item?.parentElement;\n if (parent && parent.tagName === tagName('dropdown-item').toUpperCase()) {\n return parent as DropdownItem;\n }\n return null;\n }\n\n /** The enabled items of the level that currently holds focus (root level as fallback). */\n private _currentLevelItems(): DropdownItem[] {\n const current = this._getCurrentItem();\n return current ? this._levelItemsOf(current) : this._getItems();\n }\n\n private _getDuration(prop: '--show-duration' | '--hide-duration'): number {\n const parsed = parseFloat(getComputedStyle(this).getPropertyValue(prop));\n return Number.isNaN(parsed) ? 150 : parsed;\n }\n\n // --- Public API ---\n\n show() {\n if (this.open || this.disabled) return;\n if (this.dispatchEvent(new ShowEvent())) this.open = true;\n }\n\n hide() {\n if (!this.open) return;\n if (this.dispatchEvent(new HideEvent())) this.open = false;\n }\n\n toggle() {\n if (this.open) this.hide();\n else this.show();\n }\n\n // --- Lifecycle ---\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n this._cancelHoverIntent({ forget: true });\n }\n\n override updated(changed: PropertyValues<this>) {\n if (changed.has('open')) {\n void this._handleOpenChange();\n }\n }\n\n // --- Open/Close ---\n\n private async _handleOpenChange() {\n const panel = this._panelEl;\n if (!panel) return;\n\n const posOpts = {\n placement: this.placement,\n distance: this.distance,\n minWidth: this.minWidth,\n };\n\n if (this.open) {\n panel.showPopover();\n await this._floating.updatePosition(posOpts);\n if (!this.open) return;\n await this._floating.animateShow(panel, this._getDuration('--show-duration'));\n this._floating.startPositioning(posOpts);\n this._triggerEl?.setAttribute('aria-expanded', 'true');\n this.dispatchEvent(new AfterShowEvent());\n } else {\n this._floating.stopPositioning();\n this._closeAllSubmenus();\n this._triggerEl?.setAttribute('aria-expanded', 'false');\n await this._floating.animateHide(panel, this._getDuration('--hide-duration'));\n if (panel.matches(':popover-open')) panel.hidePopover();\n this.dispatchEvent(new AfterHideEvent());\n }\n }\n\n // --- Submenus ---\n\n private _hoverTimer = 0;\n private _hoverItem: DropdownItem | null = null;\n\n private async _openSubmenu(item: DropdownItem, { focus = false } = {}) {\n // A pending hover timer may fire after the menu closed or unmounted\n if (!this.open || !this.isConnected) return;\n // Only one submenu open per level. If a sibling we're about to close holds\n // the focus (keyboard focus inside it, mouse hovers another row), the panel\n // would vanish under the focus and drop it to <body> — move focus to `item`\n // first so the roving tabindex stays coherent.\n let stealsFocus = false;\n for (const sibling of this._levelItemsOf(item)) {\n if (sibling === item) continue;\n if (sibling.contains(document.activeElement)) stealsFocus = true;\n sibling.closeSubmenu();\n }\n if (stealsFocus && !focus) this._setActiveItem(item);\n item.openSubmenu();\n if (focus) {\n await item.updateComplete;\n const first = item.getSubmenuItems()[0];\n if (first) this._setActiveItem(first);\n }\n }\n\n /**\n * Drop the pending hover-open. A click or a key press is an explicit decision\n * that supersedes it — left armed, the timer would fire afterwards and reopen\n * a submenu the user just closed. By default `_hoverItem` is kept, so staying\n * on the same row doesn't re-arm it; `forget` also clears it, for when the\n * pointer leaves the row or the menu goes away.\n */\n private _cancelHoverIntent({ forget = false } = {}) {\n clearTimeout(this._hoverTimer);\n if (forget) this._hoverItem = null;\n }\n\n private _closeAllSubmenus() {\n this._cancelHoverIntent({ forget: true });\n for (const item of this._getAllItems()) item.closeSubmenu();\n }\n\n /**\n * The item targeted by a pointer event, via the composed path. Returns null\n * for hits inside an item's submenu panel that miss every nested item row\n * (e.g. the panel padding) — those must not read as hits on the parent item.\n */\n private _itemFromEvent(e: Event): DropdownItem | null {\n const path = e.composedPath();\n const itemTag = tagName('dropdown-item').toUpperCase();\n const item =\n path.find((n): n is DropdownItem => n instanceof Element && n.tagName === itemTag) ?? null;\n if (!item) return null;\n const row = item.shadowRoot?.querySelector('.item');\n if (row && !path.includes(row)) {\n // The path reached the item host but not its row. Reject only when the\n // hit actually landed inside this item's own submenu panel (its padding),\n // which must not read as a selection of the parent item.\n //\n // Why not `return null` outright: a click dispatched on the host element\n // — `item.click()`, a synthetic MouseEvent, or a testing tool (Cypress,\n // Playwright) whose coordinate hit-test resolves to the host rather than\n // the inner row — produces a path with neither the row nor the submenu in\n // it. Such programmatic activation should still select the item. Checking\n // for the submenu explicitly (instead of the absent row) is also\n // retargeting-independent, so it holds regardless of which shadow tree the\n // listener sits in.\n const submenu = item.shadowRoot?.querySelector('.submenu');\n if (submenu && path.includes(submenu)) return null;\n }\n return item;\n }\n\n // --- Focus management ---\n\n private _setActiveItem(item: DropdownItem) {\n const itemEl = item.shadowRoot!.querySelector<HTMLElement>('.item');\n if (!itemEl) return;\n\n // Reset all items, submenu items included — one roving tabindex tree-wide\n for (const i of this._getAllItemsDeep()) {\n const el = i.shadowRoot!.querySelector<HTMLElement>('.item');\n el?.setAttribute('tabindex', '-1');\n }\n\n itemEl.setAttribute('tabindex', '0');\n itemEl.focus();\n\n // APG: moving focus within a level collapses any sibling's open submenu, so\n // an unfocused parent never keeps aria-expanded=\"true\". The active item's\n // own submenu (one level down) is untouched — `item` is not its own sibling.\n for (const sibling of this._levelItemsOf(item)) {\n if (sibling !== item) sibling.closeSubmenu();\n }\n }\n\n private _focusFirstItem(items = this._getItems()) {\n if (items.length) this._setActiveItem(items[0]);\n }\n\n private _focusLastItem(items = this._getItems()) {\n if (items.length) this._setActiveItem(items[items.length - 1]);\n }\n\n private _getCurrentItem(): DropdownItem | null {\n return (\n this._getAllItemsDeep().find((item) => {\n const el = item.shadowRoot!.querySelector<HTMLElement>('.item');\n return el?.getAttribute('tabindex') === '0' && item.shadowRoot!.activeElement === el;\n }) ?? null\n );\n }\n\n private _focusNextItem() {\n const items = this._currentLevelItems();\n const current = this._getCurrentItem();\n const index = current ? items.indexOf(current) : -1;\n const next = items[(index + 1) % items.length];\n if (next) this._setActiveItem(next);\n }\n\n private _focusPreviousItem() {\n const items = this._currentLevelItems();\n const current = this._getCurrentItem();\n const index = current ? items.indexOf(current) : 0;\n const prev = items[(index - 1 + items.length) % items.length];\n if (prev) this._setActiveItem(prev);\n }\n\n // --- Typeahead ---\n\n private _handleTypeahead(key: string) {\n clearTimeout(this._typeaheadTimeout);\n this._typeaheadBuffer += key.toLowerCase();\n this._typeaheadTimeout = window.setTimeout(() => {\n this._typeaheadBuffer = '';\n }, 500);\n\n const items = this._currentLevelItems();\n const match = items.find((item) =>\n item.getTextLabel().toLowerCase().startsWith(this._typeaheadBuffer),\n );\n if (match) this._setActiveItem(match);\n }\n\n // --- Event handlers ---\n\n /**\n * Expose menu-button semantics on whatever element is slotted as the trigger,\n * and give the root `role=\"menu\"` panel an accessible name derived from it.\n * Runs on initial assignment and on any runtime trigger swap.\n */\n private _onTriggerSlotChange = () => {\n const trigger = this._triggerEl;\n if (!trigger) return;\n trigger.setAttribute('aria-haspopup', 'menu');\n trigger.setAttribute('aria-expanded', String(this.open));\n\n // Cross-shadow aria-labelledby to the trigger is impossible, so copy its\n // accessible name onto the menu as a plain aria-label.\n const name = trigger.getAttribute('aria-label') ?? trigger.textContent?.trim() ?? '';\n const menu = this.shadowRoot?.querySelector<HTMLElement>('[role=\"menu\"]');\n if (menu && name) menu.setAttribute('aria-label', name);\n };\n\n private _onTriggerClick = (e: MouseEvent) => {\n if (this.disabled) return;\n // The pointerdown that produced this click already light-dismissed the panel:\n // `open` still reads true, but the panel has left the top layer and the\n // queued `toggle` owns the close (and its `hide` event). Closing here too\n // would emit a second one. A keyboard activation never gets here — no\n // light-dismiss, so the panel is still open.\n if (this.open && !this._panelEl?.matches(':popover-open')) return;\n this.toggle();\n // Space/Enter on a native button dispatches click with detail=0; focus the\n // first item so the menu is keyboard-navigable immediately on open.\n if (this.open && e.detail === 0) {\n requestAnimationFrame(() => this._focusFirstItem());\n }\n };\n\n private _onTriggerKeyDown = (e: KeyboardEvent) => {\n if (this.disabled) return;\n\n if (e.key === 'Escape') {\n // A pointer-opened menu leaves focus on the trigger, so `_onPanelKeyDown`\n // never sees the key. The platform's popover light-dismiss normally covers\n // that, but it runs as a close request the page can suppress — any\n // `preventDefault()` on the Escape keydown (a common app-level shortcut\n // handler) leaves the menu stuck open. Close it here, and preventDefault\n // so the close request doesn't race our animated hide.\n if (!this.open) return;\n e.preventDefault();\n this.hide();\n // Focus is already on the trigger — nothing to restore.\n } else if (e.key === 'ArrowDown') {\n e.preventDefault();\n this.show();\n requestAnimationFrame(() => this._focusFirstItem());\n } else if (e.key === 'ArrowUp') {\n e.preventDefault();\n this.show();\n requestAnimationFrame(() => this._focusLastItem());\n }\n };\n\n private _onPanelKeyDown = (e: KeyboardEvent) => {\n if (!MODIFIER_KEYS.has(e.key)) this._cancelHoverIntent();\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault();\n this._focusNextItem();\n break;\n case 'ArrowUp':\n e.preventDefault();\n this._focusPreviousItem();\n break;\n case 'Home':\n e.preventDefault();\n this._focusFirstItem(this._currentLevelItems());\n break;\n case 'End':\n e.preventDefault();\n this._focusLastItem(this._currentLevelItems());\n break;\n case 'ArrowRight': {\n const current = this._getCurrentItem();\n if (current?.hasSubmenu) {\n e.preventDefault();\n void this._openSubmenu(current, { focus: true });\n }\n break;\n }\n case 'ArrowLeft': {\n const parentItem = this._parentItemOf(this._getCurrentItem());\n if (parentItem) {\n e.preventDefault();\n parentItem.closeSubmenu();\n this._setActiveItem(parentItem);\n }\n break;\n }\n case 'Escape': {\n // Close one level at a time: a submenu first, the menu itself at the root\n e.preventDefault();\n const parentItem = this._parentItemOf(this._getCurrentItem());\n if (parentItem) {\n parentItem.closeSubmenu();\n this._setActiveItem(parentItem);\n } else {\n this.hide();\n this._triggerEl?.focus();\n }\n break;\n }\n case 'Tab':\n // APG: Tab closes the menu (and any open submenus, via the hide path)\n // and lets focus move to the next tab-sequence element naturally — so\n // no preventDefault and no focus return to the trigger.\n this.hide();\n break;\n case 'Enter':\n case ' ':\n e.preventDefault();\n this._selectCurrentItem();\n break;\n default:\n if (e.key.length === 1 && !e.ctrlKey && !e.metaKey) {\n this._handleTypeahead(e.key);\n }\n }\n };\n\n private _onItemClick = (e: Event) => {\n const item = this._itemFromEvent(e);\n if (!item || item.disabled) return;\n if (item.hasSubmenu) {\n // A parent item toggles its submenu instead of selecting\n this._cancelHoverIntent();\n if (item.submenuOpen) item.closeSubmenu();\n else void this._openSubmenu(item);\n return;\n }\n this._selectItem(item);\n };\n\n /** Open submenus on hover after a short intent delay; hovering a sibling closes them. */\n private _onPanelPointerOver = (e: PointerEvent) => {\n // Touch taps fire pointerover too — they must only toggle via click,\n // or the hover timer would reopen a submenu the tap just closed.\n if (e.pointerType !== 'mouse') return;\n const item = this._itemFromEvent(e);\n if (item === this._hoverItem) return;\n this._cancelHoverIntent();\n this._hoverItem = item;\n if (!item || item.disabled) return;\n this._hoverTimer = window.setTimeout(() => {\n void this._openSubmenu(item);\n }, 100);\n };\n\n private _onPanelPointerOut = (e: PointerEvent) => {\n // pointerout also fires between parts of the same row — a slotted icon and\n // the row padding (the related target arrives retargeted to the item host).\n // Forgetting the row there would let the next pointerover re-arm the timer\n // and reopen a submenu a click just closed. Leaving the row is pointerover's\n // call, or this one's once the pointer exits the panel.\n const to = e.relatedTarget;\n if (\n this._hoverItem &&\n to instanceof Element &&\n to.closest(tagName('dropdown-item')) === this._hoverItem\n ) {\n return;\n }\n this._cancelHoverIntent({ forget: true });\n };\n\n private _selectCurrentItem() {\n const current = this._getCurrentItem();\n if (!current) return;\n if (current.hasSubmenu) {\n void this._openSubmenu(current, { focus: true });\n } else {\n this._selectItem(current);\n }\n }\n\n private _selectItem(item: DropdownItem) {\n if (item.type === 'checkbox') {\n item.checked = !item.checked;\n }\n this.dispatchEvent(new DropdownSelectEvent(item));\n if (item.type !== 'checkbox') {\n this.hide();\n this._triggerEl?.focus();\n }\n }\n\n /**\n * Route `popover=\"auto\"` light-dismiss through the same close path as every\n * other user-driven close (trigger click, Escape, item selection), so the\n * cancelable `hide` event fires for it too. Assigning `open` here directly\n * would skip it and desync anything a consumer syncs on `hide`.\n *\n * Driving `open` as a property or attribute still closes the menu with only\n * `after-hide` — `l-dialog` mediates that path from `updated()`, this element\n * does not. A gap worth closing on its own.\n *\n * The platform makes a popover's close non-cancelable (`beforetoggle` is only\n * cancelable when opening), so a vetoed `hide` is honored by putting the panel\n * back. `toggle` is the right hook for that: it is queued after the hide has\n * actually been applied, whereas during `beforetoggle` the panel is still\n * `:popover-open` and `showPopover()` would be a no-op the pending hide undoes.\n */\n private _onToggle = (e: Event) => {\n const toggleEvent = e as ToggleEvent;\n // Our own close path flips `open` before hiding the popover, so this guard\n // keeps it from emitting a second `hide` for the same close.\n if (toggleEvent.newState !== 'closed' || !this.open) return;\n\n if (this.dispatchEvent(new HideEvent())) {\n this.open = false;\n return;\n }\n const panel = this._panelEl;\n if (panel?.isConnected) panel.showPopover();\n };\n\n override render() {\n return html`\n <div\n class=\"trigger\"\n @click=${this._onTriggerClick}\n @keydown=${this._onTriggerKeyDown}\n >\n <slot\n name=\"trigger\"\n @slotchange=${this._onTriggerSlotChange}\n ></slot>\n </div>\n <div\n popover=\"auto\"\n part=\"panel\"\n @keydown=${this._onPanelKeyDown}\n @click=${this._onItemClick}\n @pointerover=${this._onPanelPointerOver}\n @pointerout=${this._onPanelPointerOut}\n @toggle=${this._onToggle}\n >\n <slot name=\"header\"></slot>\n <div role=\"menu\">\n <slot></slot>\n </div>\n <slot name=\"footer\"></slot>\n </div>\n `;\n }\n}\n\n// Types `addEventListener('select', …)` as `DropdownSelectEvent` on this element\n// (the global event map can't be augmented for the colliding name `select`).\n// See the Tabs interface in tabs.ts for the full rationale and the gotchas.\nexport interface Dropdown {\n addEventListener<K extends keyof DropdownEventMap>(\n type: K,\n listener: (this: Dropdown, ev: DropdownEventMap[K]) => void,\n options?: boolean | AddEventListenerOptions,\n ): void;\n addEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | AddEventListenerOptions,\n ): void;\n removeEventListener<K extends keyof DropdownEventMap>(\n type: K,\n listener: (this: Dropdown, ev: DropdownEventMap[K]) => void,\n options?: boolean | EventListenerOptions,\n ): void;\n removeEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | EventListenerOptions,\n ): void;\n}\n"],"mappings":"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"}