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}
}