function useDialog()

in src/hooks/useDialog.ts [24:119]


function useDialog({
  modalRef,
  overlayRef,
  isOpen,
  onDismiss = noop,
  initialFocusRef,
  closeButtonRef
}: UseDialogParameters) {
  const onClickOutside = useCallback(
    e => {
      if (
        modalRef.current &&
        overlayRef.current &&
        !modalRef.current.contains(e.target) &&
        overlayRef.current.contains(e.target)
      ) {
        onDismiss()
      }
    },
    [onDismiss, modalRef, overlayRef]
  )

  useEffect(() => {
    if (isOpen) {
      document.addEventListener('click', onClickOutside)
      return () => {
        document.removeEventListener('click', onClickOutside)
      }
    }
  }, [isOpen, onClickOutside])

  useEffect(() => {
    if (isOpen) {
      if (initialFocusRef && initialFocusRef.current) {
        initialFocusRef.current.focus()
      } else if (closeButtonRef && closeButtonRef.current) {
        closeButtonRef.current.focus()
      }
    }
  }, [isOpen, initialFocusRef, closeButtonRef])

  const getFocusableItem = useCallback(
    (e: Event, movement: number) => {
      if (modalRef.current) {
        const items = Array.from(modalRef.current.querySelectorAll('*')).filter(focusable)
        if (items.length === 0) return
        e.preventDefault()
        const focusedElement = document.activeElement
        if (!focusedElement) {
          return
        }

        const index = items.indexOf(focusedElement)
        const offsetIndex = index + movement
        const fallbackIndex = movement === 1 ? 0 : items.length - 1
        const focusableItem = items[offsetIndex] || items[fallbackIndex]
        return focusableItem as HTMLElement
      }
    },
    [modalRef]
  )

  const handleTab = useCallback(
    e => {
      const movement = e.shiftKey ? -1 : 1
      const focusableItem = getFocusableItem(e, movement)
      if (!focusableItem) {
        return
      }

      focusableItem.focus()
    },
    [getFocusableItem]
  )

  const onKeyDown = useCallback(
    event => {
      switch (event.key) {
        case 'Tab':
          handleTab(event)
          break
        case 'Escape':
          onDismiss()
          event.stopPropagation()
          break
      }
    },
    [handleTab, onDismiss]
  )

  const getDialogProps = () => {
    return {onKeyDown}
  }

  return {getDialogProps}
}