{"version":3,"sources":["../src/use-modal.ts"],"sourcesContent":["import { callAllHandlers } from \"@chakra-ui/shared-utils\"\nimport { PropGetter } from \"@chakra-ui/react-types\"\nimport { mergeRefs } from \"@chakra-ui/react-use-merge-refs\"\nimport { hideOthers } from \"aria-hidden\"\n\nimport { useCallback, useEffect, useId, useMemo, useRef, useState } from \"react\"\nimport { modalManager, useModalManager } from \"./modal-manager\"\n\nexport interface UseModalProps {\n  /**\n   * If `true`, the modal will be open.\n   */\n  isOpen: boolean\n  /**\n   * The `id` of the modal\n   */\n  id?: string\n  /**\n   * Callback invoked to close the modal.\n   */\n  onClose(): void\n  /**\n   * If `true`, the modal will close when the overlay is clicked\n   * @default true\n   */\n  closeOnOverlayClick?: boolean\n  /**\n   * If `true`, the modal will close when the `Esc` key is pressed\n   * @default true\n   */\n  closeOnEsc?: boolean\n  /**\n   * Callback fired when the overlay is clicked.\n   */\n  onOverlayClick?(): void\n  /**\n   * Callback fired when the escape key is pressed and focus is within modal\n   */\n  onEsc?(): void\n  /**\n   * A11y: If `true`, the siblings of the `modal` will have `aria-hidden`\n   * set to `true` so that screen readers can only see the `modal`.\n   *\n   * This is commonly known as making the other elements **inert**\n   *\n   * @default true\n   */\n  useInert?: boolean\n}\n\n/**\n * Modal hook that manages all the logic for the modal dialog widget\n * and returns prop getters, state and actions.\n *\n * @param props\n */\nexport function useModal(props: UseModalProps) {\n  const {\n    isOpen,\n    onClose,\n    id,\n    closeOnOverlayClick = true,\n    closeOnEsc = true,\n    useInert = true,\n    onOverlayClick: onOverlayClickProp,\n    onEsc,\n  } = props\n\n  const dialogRef = useRef<HTMLElement>(null)\n  const overlayRef = useRef<HTMLElement>(null)\n\n  const [dialogId, headerId, bodyId] = useIds(\n    id,\n    `chakra-modal`,\n    `chakra-modal--header`,\n    `chakra-modal--body`,\n  )\n\n  /**\n   * Hook used to polyfill `aria-modal` for older browsers.\n   * It uses `aria-hidden` to all other nodes.\n   *\n   * @see https://developer.paciellogroup.com/blog/2018/06/the-current-state-of-modal-dialog-accessibility/\n   */\n  useAriaHidden(dialogRef, isOpen && useInert)\n  /**\n   * Hook used to manage multiple or nested modals\n   */\n  const index = useModalManager(dialogRef, isOpen)\n\n  const mouseDownTarget = useRef<EventTarget | null>(null)\n\n  const onMouseDown = useCallback((event: React.MouseEvent) => {\n    mouseDownTarget.current = event.target\n  }, [])\n\n  const onKeyDown = useCallback(\n    (event: React.KeyboardEvent) => {\n      if (event.key === \"Escape\") {\n        event.stopPropagation()\n\n        if (closeOnEsc) {\n          onClose?.()\n        }\n\n        onEsc?.()\n      }\n    },\n    [closeOnEsc, onClose, onEsc],\n  )\n\n  const [headerMounted, setHeaderMounted] = useState(false)\n  const [bodyMounted, setBodyMounted] = useState(false)\n\n  const getDialogProps: PropGetter = useCallback(\n    (props = {}, ref = null) => ({\n      role: \"dialog\",\n      ...props,\n      ref: mergeRefs(ref, dialogRef),\n      id: dialogId,\n      tabIndex: -1,\n      \"aria-modal\": true,\n      \"aria-labelledby\": headerMounted ? headerId : undefined,\n      \"aria-describedby\": bodyMounted ? bodyId : undefined,\n      onClick: callAllHandlers(props.onClick, (event: React.MouseEvent) =>\n        event.stopPropagation(),\n      ),\n    }),\n    [bodyId, bodyMounted, dialogId, headerId, headerMounted],\n  )\n\n  const onOverlayClick = useCallback(\n    (event: React.MouseEvent) => {\n      event.stopPropagation()\n      /**\n       * Make sure the event starts and ends on the same DOM element.\n       *\n       * This is used to prevent the modal from closing when you\n       * start dragging from the content, and release drag outside the content.\n       *\n       * We prevent this because it is technically not a considered \"click outside\"\n       */\n      if (mouseDownTarget.current !== event.target) return\n\n      /**\n       * When you click on the overlay, we want to remove only the topmost modal\n       */\n      if (!modalManager.isTopModal(dialogRef.current)) return\n\n      if (closeOnOverlayClick) {\n        onClose?.()\n      }\n\n      onOverlayClickProp?.()\n    },\n    [onClose, closeOnOverlayClick, onOverlayClickProp],\n  )\n\n  const getDialogContainerProps: PropGetter = useCallback(\n    (props = {}, ref = null) => ({\n      ...props,\n      ref: mergeRefs(ref, overlayRef),\n      onClick: callAllHandlers(props.onClick, onOverlayClick),\n      onKeyDown: callAllHandlers(props.onKeyDown, onKeyDown),\n      onMouseDown: callAllHandlers(props.onMouseDown, onMouseDown),\n    }),\n    [onKeyDown, onMouseDown, onOverlayClick],\n  )\n\n  return {\n    isOpen,\n    onClose,\n    headerId,\n    bodyId,\n    setBodyMounted,\n    setHeaderMounted,\n    dialogRef,\n    overlayRef,\n    getDialogProps,\n    getDialogContainerProps,\n    index,\n  }\n}\n\nexport type UseModalReturn = ReturnType<typeof useModal>\n\n/**\n * Modal hook to polyfill `aria-modal`.\n *\n * It applies `aria-hidden` to elements behind the modal\n * to indicate that they're `inert`.\n *\n * @param ref React ref of the node\n * @param shouldHide whether `aria-hidden` should be applied\n */\nexport function useAriaHidden(\n  ref: React.RefObject<HTMLElement>,\n  shouldHide: boolean,\n) {\n  // save current ref in a local var to trigger the effect on identity change\n  const currentElement = ref.current\n\n  useEffect(() => {\n    // keep using `ref.current` inside the effect\n    // it may have changed during render and the execution of the effect\n    if (!ref.current || !shouldHide) return undefined\n\n    return hideOthers(ref.current)\n  }, [shouldHide, ref, currentElement])\n}\n\nfunction useIds(idProp?: string, ...prefixes: string[]) {\n  const reactId = useId()\n  const id = idProp || reactId\n  return useMemo(() => {\n    return prefixes.map((prefix) => `${prefix}-${id}`)\n  }, [id, prefixes])\n}\n"],"mappings":"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","names":["props"]}