"use client";
import {
  usePortal
} from "./VJH3F6TN.js";
import {
  HeadingLevel
} from "./5M6RIVE2.js";
import {
  useFocusableContainer
} from "./3HM4TGWW.js";
import {
  prependHiddenDismiss
} from "./6GXEOXGT.js";
import {
  useHideOnInteractOutside
} from "./KKITJVHA.js";
import {
  useNestedDialogs
} from "./JSI7U3EA.js";
import {
  usePreventBodyScroll
} from "./B6W4QFKS.js";
import {
  disableTree,
  disableTreeOutside
} from "./Z5GCVBAY.js";
import {
  supportsInert
} from "./677M2CI3.js";
import {
  DialogBackdrop
} from "./P45QRJLH.js";
import {
  isElementMarked,
  markTreeOutside
} from "./3NDVDEB4.js";
import {
  createWalkTreeSnapshot
} from "./AOUGVQZ3.js";
import {
  isHidden,
  useDisclosureContent
} from "./6B3RXHKP.js";
import {
  useDialogStore
} from "./4NYSH4UO.js";
import {
  DialogDescriptionContext,
  DialogHeadingContext,
  DialogScopedContextProvider,
  useDialogProviderContext
} from "./A62MDFCW.js";
import {
  useFocusable
} from "./U6HHPQDW.js";
import {
  useStoreState
} from "./Q5W46E73.js";
import {
  createElement,
  createHook,
  forwardRef
} from "./GWSL6KNJ.js";
import {
  useBooleanEvent,
  useEvent,
  useId,
  useMergeRefs,
  usePortalRef,
  useSafeLayoutEffect,
  useWrapElement
} from "./KPHZR4MB.js";

// src/dialog/dialog.tsx
import {
  contains,
  getActiveElement,
  getDocument,
  getWindow,
  isButton
} from "@ariakit/core/utils/dom";
import {
  addGlobalEventListener,
  queueBeforeEvent
} from "@ariakit/core/utils/events";
import {
  focusIfNeeded,
  getFirstTabbableIn,
  isFocusable
} from "@ariakit/core/utils/focus";
import { chain } from "@ariakit/core/utils/misc";
import { isSafari } from "@ariakit/core/utils/platform";
import { useCallback, useEffect, useRef, useState } from "react";
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
var TagName = "div";
var isSafariBrowser = isSafari();
function isAlreadyFocusingAnotherElement(dialog) {
  const activeElement = getActiveElement();
  if (!activeElement) return false;
  if (dialog && contains(dialog, activeElement)) return false;
  if (isFocusable(activeElement)) return true;
  return false;
}
function getElementFromProp(prop, focusable = false) {
  if (!prop) return null;
  const element = "current" in prop ? prop.current : prop;
  if (!element) return null;
  if (focusable) return isFocusable(element) ? element : null;
  return element;
}
var useDialog = createHook(function useDialog2({
  store: storeProp,
  open: openProp,
  onClose,
  focusable = true,
  modal = true,
  portal = !!modal,
  backdrop = !!modal,
  hideOnEscape = true,
  hideOnInteractOutside = true,
  getPersistentElements,
  preventBodyScroll = !!modal,
  autoFocusOnShow = true,
  autoFocusOnHide = true,
  initialFocus,
  finalFocus,
  unmountOnHide,
  unstable_treeSnapshotKey,
  ...props
}) {
  const context = useDialogProviderContext();
  const ref = useRef(null);
  const store = useDialogStore({
    store: storeProp || context,
    open: openProp,
    setOpen(open2) {
      if (open2) return;
      const dialog = ref.current;
      if (!dialog) return;
      const event = new Event("close", { bubbles: false, cancelable: true });
      if (onClose) {
        dialog.addEventListener("close", onClose, { once: true });
      }
      dialog.dispatchEvent(event);
      if (!event.defaultPrevented) return;
      store.setOpen(true);
    }
  });
  const { portalRef, domReady } = usePortalRef(portal, props.portalRef);
  const preserveTabOrderProp = props.preserveTabOrder;
  const preserveTabOrder = useStoreState(
    store,
    (state) => preserveTabOrderProp && !modal && state.mounted
  );
  const id = useId(props.id);
  const open = useStoreState(store, "open");
  const mounted = useStoreState(store, "mounted");
  const contentElement = useStoreState(store, "contentElement");
  const hidden = isHidden(mounted, props.hidden, props.alwaysVisible);
  usePreventBodyScroll(contentElement, id, preventBodyScroll && !hidden);
  useHideOnInteractOutside(store, hideOnInteractOutside, domReady);
  const { wrapElement, nestedDialogs } = useNestedDialogs(store);
  props = useWrapElement(props, wrapElement, [wrapElement]);
  useSafeLayoutEffect(() => {
    if (!open) return;
    const dialog = ref.current;
    const activeElement = getActiveElement(dialog, true);
    if (!activeElement) return;
    if (activeElement.tagName === "BODY") return;
    if (dialog && contains(dialog, activeElement)) return;
    store.setDisclosureElement(activeElement);
  }, [store, open]);
  if (isSafariBrowser) {
    useEffect(() => {
      if (!mounted) return;
      const { disclosureElement } = store.getState();
      if (!disclosureElement) return;
      if (!isButton(disclosureElement)) return;
      const onMouseDown = () => {
        let receivedFocus = false;
        const onFocus = () => {
          receivedFocus = true;
        };
        const options = { capture: true, once: true };
        disclosureElement.addEventListener("focusin", onFocus, options);
        queueBeforeEvent(disclosureElement, "mouseup", () => {
          disclosureElement.removeEventListener("focusin", onFocus, true);
          if (receivedFocus) return;
          focusIfNeeded(disclosureElement);
        });
      };
      disclosureElement.addEventListener("mousedown", onMouseDown);
      return () => {
        disclosureElement.removeEventListener("mousedown", onMouseDown);
      };
    }, [store, mounted]);
  }
  useEffect(() => {
    if (!mounted) return;
    if (!domReady) return;
    const dialog = ref.current;
    if (!dialog) return;
    const win = getWindow(dialog);
    const viewport = win.visualViewport || win;
    const setViewportHeight = () => {
      var _a, _b;
      const height = (_b = (_a = win.visualViewport) == null ? void 0 : _a.height) != null ? _b : win.innerHeight;
      dialog.style.setProperty("--dialog-viewport-height", `${height}px`);
    };
    setViewportHeight();
    viewport.addEventListener("resize", setViewportHeight);
    return () => {
      viewport.removeEventListener("resize", setViewportHeight);
    };
  }, [mounted, domReady]);
  useEffect(() => {
    if (!modal) return;
    if (!mounted) return;
    if (!domReady) return;
    const dialog = ref.current;
    if (!dialog) return;
    const existingDismiss = dialog.querySelector("[data-dialog-dismiss]");
    if (existingDismiss) return;
    return prependHiddenDismiss(dialog, store.hide);
  }, [store, modal, mounted, domReady]);
  useSafeLayoutEffect(() => {
    if (!supportsInert()) return;
    if (open) return;
    if (!mounted) return;
    if (!domReady) return;
    const dialog = ref.current;
    if (!dialog) return;
    return disableTree(dialog);
  }, [open, mounted, domReady]);
  const canTakeTreeSnapshot = open && domReady;
  useSafeLayoutEffect(() => {
    if (!id) return;
    if (!canTakeTreeSnapshot) return;
    const dialog = ref.current;
    return createWalkTreeSnapshot(id, [dialog]);
  }, [id, canTakeTreeSnapshot, unstable_treeSnapshotKey]);
  const getPersistentElementsProp = useEvent(getPersistentElements);
  useSafeLayoutEffect(() => {
    if (!id) return;
    if (!canTakeTreeSnapshot) return;
    const { disclosureElement } = store.getState();
    const dialog = ref.current;
    const persistentElements = getPersistentElementsProp() || [];
    const allElements = [
      dialog,
      ...persistentElements,
      ...nestedDialogs.map((dialog2) => dialog2.getState().contentElement)
    ];
    if (modal) {
      return chain(
        markTreeOutside(id, allElements),
        disableTreeOutside(id, allElements)
      );
    }
    return markTreeOutside(id, [disclosureElement, ...allElements]);
  }, [
    id,
    store,
    canTakeTreeSnapshot,
    getPersistentElementsProp,
    nestedDialogs,
    modal,
    unstable_treeSnapshotKey
  ]);
  const mayAutoFocusOnShow = !!autoFocusOnShow;
  const autoFocusOnShowProp = useBooleanEvent(autoFocusOnShow);
  const [autoFocusEnabled, setAutoFocusEnabled] = useState(false);
  useEffect(() => {
    if (!open) return;
    if (!mayAutoFocusOnShow) return;
    if (!domReady) return;
    if (!(contentElement == null ? void 0 : contentElement.isConnected)) return;
    const element = getElementFromProp(initialFocus, true) || // If no initial focus is specified, we try to focus the first element
    // with the autofocus attribute. If it's an Ariakit component, the
    // Focusable component will consume the autoFocus prop and add the
    // data-autofocus attribute to the element instead.
    contentElement.querySelector(
      "[data-autofocus=true],[autofocus]"
    ) || // We have to fallback to the first focusable element otherwise portaled
    // dialogs with preserveTabOrder set to true will not receive focus
    // properly because the elements aren't tabbable until the dialog receives
    // focus.
    getFirstTabbableIn(contentElement, true, portal && preserveTabOrder) || // Finally, we fallback to the dialog element itself.
    contentElement;
    const isElementFocusable = isFocusable(element);
    if (!autoFocusOnShowProp(isElementFocusable ? element : null)) return;
    setAutoFocusEnabled(true);
    queueMicrotask(() => {
      element.focus();
      if (!isSafariBrowser) return;
      if (!isElementFocusable) return;
      element.scrollIntoView({ block: "nearest", inline: "nearest" });
    });
  }, [
    open,
    mayAutoFocusOnShow,
    domReady,
    contentElement,
    initialFocus,
    portal,
    preserveTabOrder,
    autoFocusOnShowProp
  ]);
  const mayAutoFocusOnHide = !!autoFocusOnHide;
  const autoFocusOnHideProp = useBooleanEvent(autoFocusOnHide);
  const [hasOpened, setHasOpened] = useState(false);
  useEffect(() => {
    if (!open) return;
    setHasOpened(true);
    return () => setHasOpened(false);
  }, [open]);
  const focusOnHide = useCallback(
    (dialog, retry = true) => {
      const { disclosureElement } = store.getState();
      if (isAlreadyFocusingAnotherElement(dialog)) return;
      let element = getElementFromProp(finalFocus) || disclosureElement;
      if (element == null ? void 0 : element.id) {
        const doc = getDocument(element);
        const selector = `[aria-activedescendant="${element.id}"]`;
        const composite = doc.querySelector(selector);
        if (composite) {
          element = composite;
        }
      }
      if (element && !isFocusable(element)) {
        const maybeParentDialog = element.closest("[data-dialog]");
        if (maybeParentDialog == null ? void 0 : maybeParentDialog.id) {
          const doc = getDocument(maybeParentDialog);
          const selector = `[aria-controls~="${maybeParentDialog.id}"]`;
          const control = doc.querySelector(selector);
          if (control) {
            element = control;
          }
        }
      }
      const isElementFocusable = element && isFocusable(element);
      if (!isElementFocusable && retry) {
        requestAnimationFrame(() => focusOnHide(dialog, false));
        return;
      }
      if (!autoFocusOnHideProp(isElementFocusable ? element : null)) return;
      if (!isElementFocusable) return;
      element == null ? void 0 : element.focus({ preventScroll: true });
    },
    [store, finalFocus, autoFocusOnHideProp]
  );
  const focusedOnHideRef = useRef(false);
  useSafeLayoutEffect(() => {
    if (open) return;
    if (!hasOpened) return;
    if (!mayAutoFocusOnHide) return;
    const dialog = ref.current;
    focusedOnHideRef.current = true;
    focusOnHide(dialog);
  }, [open, hasOpened, domReady, mayAutoFocusOnHide, focusOnHide]);
  useEffect(() => {
    if (!hasOpened) return;
    if (!mayAutoFocusOnHide) return;
    const dialog = ref.current;
    return () => {
      if (focusedOnHideRef.current) {
        focusedOnHideRef.current = false;
        return;
      }
      focusOnHide(dialog);
    };
  }, [hasOpened, mayAutoFocusOnHide, focusOnHide]);
  const hideOnEscapeProp = useBooleanEvent(hideOnEscape);
  useEffect(() => {
    if (!domReady) return;
    if (!mounted) return;
    const onKeyDown = (event) => {
      if (event.key !== "Escape") return;
      if (event.defaultPrevented) return;
      const dialog = ref.current;
      if (!dialog) return;
      if (isElementMarked(dialog)) return;
      const target = event.target;
      if (!target) return;
      const { disclosureElement } = store.getState();
      const isValidTarget = () => {
        if (target.tagName === "BODY") return true;
        if (contains(dialog, target)) return true;
        if (!disclosureElement) return true;
        if (contains(disclosureElement, target)) return true;
        return false;
      };
      if (!isValidTarget()) return;
      if (!hideOnEscapeProp(event)) return;
      store.hide();
    };
    return addGlobalEventListener("keydown", onKeyDown, true);
  }, [store, domReady, mounted, hideOnEscapeProp]);
  props = useWrapElement(
    props,
    (element) => /* @__PURE__ */ jsx(HeadingLevel, { level: modal ? 1 : void 0, children: element }),
    [modal]
  );
  const hiddenProp = props.hidden;
  const alwaysVisible = props.alwaysVisible;
  props = useWrapElement(
    props,
    (element) => {
      if (!backdrop) return element;
      return /* @__PURE__ */ jsxs(Fragment, { children: [
        /* @__PURE__ */ jsx(
          DialogBackdrop,
          {
            store,
            backdrop,
            hidden: hiddenProp,
            alwaysVisible
          }
        ),
        element
      ] });
    },
    [store, backdrop, hiddenProp, alwaysVisible]
  );
  const [headingId, setHeadingId] = useState();
  const [descriptionId, setDescriptionId] = useState();
  props = useWrapElement(
    props,
    (element) => /* @__PURE__ */ jsx(DialogScopedContextProvider, { value: store, children: /* @__PURE__ */ jsx(DialogHeadingContext.Provider, { value: setHeadingId, children: /* @__PURE__ */ jsx(DialogDescriptionContext.Provider, { value: setDescriptionId, children: element }) }) }),
    [store]
  );
  props = {
    id,
    "data-dialog": "",
    role: "dialog",
    tabIndex: focusable ? -1 : void 0,
    "aria-labelledby": headingId,
    "aria-describedby": descriptionId,
    ...props,
    ref: useMergeRefs(ref, props.ref)
  };
  props = useFocusableContainer({
    ...props,
    autoFocusOnShow: autoFocusEnabled
  });
  props = useDisclosureContent({ store, ...props });
  props = useFocusable({ ...props, focusable });
  props = usePortal({ portal, ...props, portalRef, preserveTabOrder });
  return props;
});
function createDialogComponent(Component, useProviderContext = useDialogProviderContext) {
  return forwardRef(function DialogComponent(props) {
    const context = useProviderContext();
    const store = props.store || context;
    const mounted = useStoreState(
      store,
      (state) => !props.unmountOnHide || (state == null ? void 0 : state.mounted) || !!props.open
    );
    if (!mounted) return null;
    return /* @__PURE__ */ jsx(Component, { ...props });
  });
}
var Dialog = createDialogComponent(
  forwardRef(function Dialog2(props) {
    const htmlProps = useDialog(props);
    return createElement(TagName, htmlProps);
  }),
  useDialogProviderContext
);

export {
  useDialog,
  createDialogComponent,
  Dialog
};
