2021-06-23 04:09:46 -04:00
|
|
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
|
|
|
import { findDOMNode } from 'react-dom'
|
|
|
|
|
|
|
|
export default function useDropdown(defaultOpen = false) {
|
|
|
|
const [open, setOpen] = useState(defaultOpen)
|
|
|
|
|
|
|
|
// store the dropdown node for use in the "click outside" event listener
|
2023-01-09 09:33:43 -05:00
|
|
|
const ref = useRef<ReturnType<typeof findDOMNode>>(null)
|
2021-06-23 04:09:46 -04:00
|
|
|
|
|
|
|
// react-bootstrap v0.x passes `component` instead of `node` to the ref callback
|
|
|
|
const handleRef = useCallback(
|
|
|
|
component => {
|
|
|
|
if (component) {
|
|
|
|
// eslint-disable-next-line react/no-find-dom-node
|
|
|
|
ref.current = findDOMNode(component)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
[ref]
|
|
|
|
)
|
|
|
|
|
|
|
|
// prevent a click on the dropdown toggle propagating to the original handler
|
|
|
|
const handleClick = useCallback(event => {
|
|
|
|
event.stopPropagation()
|
|
|
|
}, [])
|
|
|
|
|
|
|
|
// handle dropdown toggle
|
|
|
|
const handleToggle = useCallback(value => {
|
|
|
|
setOpen(value)
|
|
|
|
}, [])
|
|
|
|
|
|
|
|
// close the dropdown on click outside the dropdown
|
|
|
|
const handleDocumentClick = useCallback(
|
|
|
|
event => {
|
|
|
|
if (ref.current && !ref.current.contains(event.target)) {
|
|
|
|
setOpen(false)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
[ref]
|
|
|
|
)
|
|
|
|
|
|
|
|
// add/remove listener for click anywhere in document
|
|
|
|
useEffect(() => {
|
|
|
|
if (open) {
|
|
|
|
document.addEventListener('mousedown', handleDocumentClick)
|
|
|
|
}
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
document.removeEventListener('mousedown', handleDocumentClick)
|
|
|
|
}
|
|
|
|
}, [open, handleDocumentClick])
|
|
|
|
|
|
|
|
// return props for the Dropdown component
|
|
|
|
return { ref: handleRef, onClick: handleClick, onToggle: handleToggle, open }
|
|
|
|
}
|