import { useRef, useEffect, useLayoutEffect } from "preact/compat";
import { shimDialogElement } from "./shim";
import { jsx as _jsx } from "preact/compat/jsx-runtime";
/**
 * This is an internal component and may change without warning. Use at your own risk!
 */
export const NativeDialog = _ref => {
  let {
    children,
    exit,
    isOpen,
    showModal,
    backdropClickExits,
    boundingBoxRef,
    ...dialogProps
  } = _ref;
  const dialogRef = useRef(null);
  if (isOpen === undefined) {
    const missingPropMessage = "The 'isOpen' prop is now used to control the state of Dialogs and Drawers. Please do not conditionally render these components to control their state. All Dialogs and Drawers will become invisible without this prop in the next major release. Using this prop will fix a focus-management issue that affects accessibility.";
    if (process.env.NODE_ENV !== 'production') {
      throw new Error(missingPropMessage);
    } else {
      console.error(missingPropMessage);
      isOpen = true;
    }
  }

  // Register dialog with the polyfill if necessary
  useLayoutEffect(() => {
    shimDialogElement(dialogRef.current);
  });

  // Call imperative show and close functions on mount/unmount
  useEffect(() => {
    const dialogNode = dialogRef.current;

    // Show or hide the dialog based on `isOpen` value. The `dialogNode.open` property is
    // a read-only value that will tell us if our dialog DOM element is actually in the
    // open state.
    let closingBecauseOfProp = false;
    if (isOpen) {
      if (!dialogNode.open) {
        showModal ? dialogNode.showModal() : dialogNode.show();
        // For a11y reasons, focus needs to be specified:
        // 1. `<dialog>` receives focus first
        // 2. `<dialog>` close button receives focus next
        // 3. If `<dialog>` has some sorta sticky positioning requiring keyboard navigation, that wrapping element should receive focus next.
        dialogNode.focus();
      }
    } else {
      if (dialogNode.open) {
        dialogNode.close();
        closingBecauseOfProp = true;
      }
    }

    // Bind close event listener for ESC press
    const handleClose = event => {
      event.preventDefault();
      // Only call the exit handler if the parent didn't close it by setting isOpen to false
      if (!closingBecauseOfProp) {
        exit(event);
      }
    };
    dialogNode.addEventListener('close', handleClose);
    return () => {
      dialogNode.removeEventListener('close', handleClose);
    };
  }, [isOpen, showModal, exit]);

  // Bind and unbind backdrop click event listeners on mount and unmount
  useEffect(() => {
    if (!backdropClickExits) {
      return;
    }
    const dialogNode = dialogRef.current;
    const handleClick = event => {
      // In Chrome and Firefox Pointer Events triggered by a key press receive a clientX & clientY value of 0 each.
      // This puts the pointer outside of our dialog element, and so we trigger the exit() event.
      // This causes the exit event to fire twice on keyboard presses if the button you are focusing has an event
      // handler/listener attached to it.
      // Note Safari is smart and actually assigns a value to the clientX and clientY values for button presses, so we
      // don't see this error in Safari.
      // event.detail counts the number of clicks. There are no clicks on a keyboard press, so if this function is called
      // and the event.detail value is zero, we exit without firing the exit() event.
      if (event.detail === 0) {
        return;
      }
      const boundingNode = boundingBoxRef?.current ?? dialogRef.current;
      const rect = boundingNode.getBoundingClientRect();
      const isInDialog = rect.top <= event.clientY && event.clientY <= rect.top + rect.height && rect.left <= event.clientX && event.clientX <= rect.left + rect.width;
      if (!isInDialog) {
        exit(event);
      }
    };
    dialogNode.addEventListener('click', handleClick);
    return () => {
      dialogNode.removeEventListener('click', handleClick);
    };
  }, [exit, backdropClickExits]);
  return /*#__PURE__*/_jsx("dialog", {
    ref: dialogRef,
    ...dialogProps,
    tabIndex: -1,
    children: children
  });
};
export default NativeDialog;