import { html } from 'lit';
import { query, property } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { ifDefined } from 'lit/directives/if-defined.js';
import { stopAnimations, animateTo } from '../../internal/animate.mjs';
import { boolean } from '../../internal/boolean.mjs';
import { waitForEvent } from '../../internal/event.mjs';
import FlowRdfElement from '../../internal/flow-ui-rdf-element.mjs';
import { getTabbableBoundary } from '../../internal/tabbable.mjs';
import { watch } from '../../internal/watch.mjs';
import componentStyles from '../../styles/component.styles.mjs';
import { getAnimation, setDefaultAnimation } from '../../utilities/animation-registry.mjs';
import { LocalizeController } from '../../utilities/localize.mjs';
import FlowUiPopup from '../popup/popup.component.mjs';
import styles from './dropdown.styles.mjs';

var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __decorateClass = (decorators, target, key, kind) => {
  var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
  for (var i = decorators.length - 1, decorator; i >= 0; i--)
    if (decorator = decorators[i])
      result = (kind ? decorator(target, key, result) : decorator(result)) || result;
  if (kind && result)
    __defProp(target, key, result);
  return result;
};
class FlowUiDropdown extends FlowRdfElement {
  constructor() {
    super(...arguments);
    this.localize = new LocalizeController(this);
    this.open = false;
    this.placement = "bottom-start";
    this.disabled = false;
    this.stayOpenOnSelect = false;
    this.distance = 0;
    this.skidding = 0;
    this.hoist = false;
    this.arrow = false;
    this.arrowPlacement = "anchor";
    this.arrowPadding = 10;
    this.sync = void 0;
    this.handleKeyDown = (event) => {
      if (this.open && event.key === "Escape") {
        event.stopPropagation();
        this.hide();
        this.focusOnTrigger();
      }
    };
    this.handleDocumentKeyDown = (event) => {
      if (event.key === "Escape" && this.open && !this.closeWatcher) {
        event.stopPropagation();
        this.focusOnTrigger();
        this.hide();
        return;
      }
      if (event.key === "Tab") {
        if (this.open && document.activeElement?.tagName.toLowerCase() === "flow-ui-menu-item") {
          event.preventDefault();
          this.hide();
          this.focusOnTrigger();
          return;
        }
        setTimeout(() => {
          const activeElement = this.containingElement?.getRootNode() instanceof ShadowRoot ? document.activeElement?.shadowRoot?.activeElement : document.activeElement;
          if (!this.containingElement || activeElement?.closest(this.containingElement.tagName.toLowerCase()) !== this.containingElement) {
            this.hide();
          }
        });
      }
    };
    this.handleDocumentMouseDown = (event) => {
      const path = event.composedPath();
      if (this.containingElement && !path.includes(this.containingElement)) {
        this.hide();
      }
    };
    this.handlePanelSelect = (event) => {
      const target = event.target;
      if (!this.stayOpenOnSelect && target.tagName.toLowerCase() === "flow-ui-menu") {
        this.hide();
        this.focusOnTrigger();
      }
    };
  }
  connectedCallback() {
    super.connectedCallback();
    this.open = boolean(this.open, true);
    this.disabled = boolean(this.disabled, true);
    this.stayOpenOnSelect = boolean(this.stayOpenOnSelect, true);
    this.arrow = boolean(this.arrow, true);
    this.hoist = boolean(this.hoist, true);
    if (!this.containingElement) {
      this.containingElement = this;
    }
  }
  firstUpdated() {
    this.panel.hidden = !this.open;
    if (this.open) {
      this.addOpenListeners();
      this.popup.active = true;
    }
  }
  disconnectedCallback() {
    super.disconnectedCallback();
    this.removeOpenListeners();
    this.hide();
  }
  /** @internal */
  focusOnTrigger() {
    const trigger = this.trigger.assignedElements({ flatten: true })[0];
    if (typeof trigger?.focus === "function") {
      trigger.focus();
    }
  }
  /** @internal */
  getMenu() {
    return this.panel.assignedElements({ flatten: true }).find((el) => el.tagName.toLowerCase() === "flow-ui-menu");
  }
  /** @internal */
  handleTriggerClick() {
    if (this.open) {
      this.hide();
    } else {
      this.show();
      this.focusOnTrigger();
    }
  }
  /** @internal */
  async handleTriggerKeyDown(event) {
    if ([" ", "Enter"].includes(event.key)) {
      event.preventDefault();
      this.handleTriggerClick();
      return;
    }
    const menu = this.getMenu();
    if (menu) {
      const menuItems = menu.getAllItems();
      const firstMenuItem = menuItems[0];
      const lastMenuItem = menuItems[menuItems.length - 1];
      if (["ArrowDown", "ArrowUp", "Home", "End"].includes(event.key)) {
        event.preventDefault();
        if (!this.open) {
          this.show();
          await this.updateComplete;
        }
        if (menuItems.length > 0) {
          this.updateComplete.then(() => {
            if (event.key === "ArrowDown" || event.key === "Home") {
              menu.setCurrentItem(firstMenuItem);
              firstMenuItem.focus();
            }
            if (event.key === "ArrowUp" || event.key === "End") {
              menu.setCurrentItem(lastMenuItem);
              lastMenuItem.focus();
            }
          });
        }
      }
    }
  }
  /** @internal */
  handleTriggerKeyUp(event) {
    if (event.key === " ") {
      event.preventDefault();
    }
  }
  /** @internal */
  handleTriggerSlotChange() {
    this.updateAccessibleTrigger();
  }
  //
  // Slotted triggers can be arbitrary content, but we need to link them to the dropdown panel with `aria-haspopup` and
  // `aria-expanded`. These must be applied to the "accessible trigger" (the tabbable portion of the trigger element
  // that gets slotted in) so screen readers will understand them. The accessible trigger could be the slotted element,
  // a child of the slotted element, or an element in the slotted element's shadow root.
  //
  // For example, the accessible trigger of an <flow-ui-button> is a <button> located inside its shadow root.
  //
  // To determine this, we assume the first tabbable element in the trigger slot is the "accessible trigger."
  //
  /** @internal */
  updateAccessibleTrigger() {
    const assignedElements = this.trigger.assignedElements({ flatten: true });
    const accessibleTrigger = assignedElements.find((el) => getTabbableBoundary(el).start);
    let target;
    if (accessibleTrigger) {
      switch (accessibleTrigger.tagName.toLowerCase()) {
        case "flow-ui-button":
        case "flow-ui-icon-button":
          target = accessibleTrigger.button;
          break;
        default:
          target = accessibleTrigger;
      }
      target.setAttribute("aria-haspopup", "true");
      target.setAttribute("aria-expanded", this.open ? "true" : "false");
    }
  }
  /** Shows the dropdown panel. */
  async show() {
    if (this.open) {
      return void 0;
    }
    this.open = true;
    return waitForEvent(this, "flow-ui-after-show");
  }
  /** Hides the dropdown panel */
  async hide() {
    if (!this.open) {
      return void 0;
    }
    this.open = false;
    return waitForEvent(this, "flow-ui-after-hide");
  }
  /**
   * Instructs the dropdown menu to reposition. Useful when the position or size of the trigger changes when the menu
   * is activated.
   */
  reposition() {
    this.popup.reposition();
  }
  /** @internal */
  addOpenListeners() {
    this.panel.addEventListener("flow-ui-select", this.handlePanelSelect);
    if ("CloseWatcher" in window) {
      this.closeWatcher?.destroy();
      this.closeWatcher = new CloseWatcher();
      this.closeWatcher.onclose = () => {
        this.hide();
        this.focusOnTrigger();
      };
    } else {
      this.panel.addEventListener("keydown", this.handleKeyDown);
    }
    document.addEventListener("keydown", this.handleDocumentKeyDown);
    document.addEventListener("mousedown", this.handleDocumentMouseDown);
  }
  /** @internal */
  removeOpenListeners() {
    if (this.panel) {
      this.panel.removeEventListener("flow-ui-select", this.handlePanelSelect);
      this.panel.removeEventListener("keydown", this.handleKeyDown);
    }
    document.removeEventListener("keydown", this.handleDocumentKeyDown);
    document.removeEventListener("mousedown", this.handleDocumentMouseDown);
    this.closeWatcher?.destroy();
  }
  async handleOpenChange() {
    if (this.disabled) {
      this.open = false;
      return;
    }
    this.updateAccessibleTrigger();
    if (this.open) {
      this.emit("flow-ui-show");
      this.addOpenListeners();
      await stopAnimations(this);
      this.panel.hidden = false;
      this.popup.active = true;
      const { keyframes, options } = getAnimation(this, "dropdown.show", { dir: this.localize.dir() });
      await animateTo(this.popup.popup, keyframes, options);
      this.emit("flow-ui-after-show");
    } else {
      this.emit("flow-ui-hide");
      this.removeOpenListeners();
      await stopAnimations(this);
      const { keyframes, options } = getAnimation(this, "dropdown.hide", { dir: this.localize.dir() });
      await animateTo(this.popup.popup, keyframes, options);
      this.panel.hidden = true;
      this.popup.active = false;
      this.emit("flow-ui-after-hide");
    }
  }
  render() {
    return html`
      <flow-ui-popup
        part="base"
        exportparts="popup:base__popup"
        id="dropdown"
        placement=${this.placement}
        distance=${this.distance}
        skidding=${this.skidding}
        strategy=${this.hoist ? "fixed" : "absolute"}
        ?arrow=${this.arrow}
        arrow-placement=${ifDefined(this.arrowPlacement)}
        arrow-padding=${ifDefined(this.arrowPadding)}
        flip
        shift
        auto-size="vertical"
        auto-size-padding="10"
        sync=${ifDefined(this.sync ? this.sync : void 0)}
        class=${classMap({
      dropdown: true,
      "dropdown--open": this.open
    })}
      >
        <slot
          name="trigger"
          slot="anchor"
          part="trigger"
          class="dropdown__trigger"
          @click=${this.handleTriggerClick}
          @keydown=${this.handleTriggerKeyDown}
          @keyup=${this.handleTriggerKeyUp}
          @slotchange=${this.handleTriggerSlotChange}
        ></slot>

        <div aria-hidden=${this.open ? "false" : "true"} aria-labelledby="dropdown">
          <slot part="panel" class="dropdown__panel"></slot>
        </div>
      </flow-ui-popup>
    `;
  }
}
FlowUiDropdown.styles = [componentStyles, styles];
FlowUiDropdown.dependencies = { "flow-ui-popup": FlowUiPopup };
__decorateClass([
  query(".dropdown")
], FlowUiDropdown.prototype, "popup", 2);
__decorateClass([
  query(".dropdown__trigger")
], FlowUiDropdown.prototype, "trigger", 2);
__decorateClass([
  query(".dropdown__panel")
], FlowUiDropdown.prototype, "panel", 2);
__decorateClass([
  property({ type: Boolean, reflect: true })
], FlowUiDropdown.prototype, "open", 2);
__decorateClass([
  property({ reflect: true })
], FlowUiDropdown.prototype, "placement", 2);
__decorateClass([
  property({ type: Boolean, reflect: true })
], FlowUiDropdown.prototype, "disabled", 2);
__decorateClass([
  property({ attribute: "stay-open-on-select", type: Boolean, reflect: true })
], FlowUiDropdown.prototype, "stayOpenOnSelect", 2);
__decorateClass([
  property({ attribute: false })
], FlowUiDropdown.prototype, "containingElement", 2);
__decorateClass([
  property({ type: Number })
], FlowUiDropdown.prototype, "distance", 2);
__decorateClass([
  property({ type: Number })
], FlowUiDropdown.prototype, "skidding", 2);
__decorateClass([
  property({ type: Boolean })
], FlowUiDropdown.prototype, "hoist", 2);
__decorateClass([
  property({ type: Boolean })
], FlowUiDropdown.prototype, "arrow", 2);
__decorateClass([
  property({ attribute: "arrow-placement" })
], FlowUiDropdown.prototype, "arrowPlacement", 2);
__decorateClass([
  property({ attribute: "arrow-padding", type: Number })
], FlowUiDropdown.prototype, "arrowPadding", 2);
__decorateClass([
  property({ reflect: true })
], FlowUiDropdown.prototype, "sync", 2);
__decorateClass([
  watch("open", { waitUntilFirstUpdate: true })
], FlowUiDropdown.prototype, "handleOpenChange", 1);
setDefaultAnimation("dropdown.show", {
  keyframes: [
    { opacity: 0, scale: 0.9 },
    { opacity: 1, scale: 1 }
  ],
  options: { duration: 100, easing: "ease" }
});
setDefaultAnimation("dropdown.hide", {
  keyframes: [
    { opacity: 1, scale: 1 },
    { opacity: 0, scale: 0.9 }
  ],
  options: { duration: 100, easing: "ease" }
});

export { FlowUiDropdown as default };
