@coreui/react-pro
Version:
UI Components Library for React.js
124 lines (103 loc) • 2.85 kB
text/typescript
import { useEffect, useRef, useState } from 'react'
import { type Options, type Placement } from '@popperjs/core'
import { usePopper } from './usePopper'
import { isRTL } from '../utils'
export const useDropdownWithPopper = <T extends HTMLElement = never>(
popperConfig?: Partial<Options>,
) => {
const dropdownRefElement = useRef<T>(null)
const dropdownMenuElement = useRef<HTMLDivElement | null>(null)
const [isOpen, setIsOpen] = useState<boolean>(false)
const { popper, initPopper, destroyPopper } = usePopper()
const _popperConfig = {
placement: (isRTL(dropdownRefElement.current) ? 'bottom-end' : 'bottom-start') as Placement,
modifiers: [
{
name: 'preventOverflow',
options: {
boundary: 'clippingParents',
},
},
{
name: 'offset',
options: {
offset: [0, 2],
},
},
],
...popperConfig,
}
const closeDropdown = () => {
setIsOpen(false)
}
const openDropdown = () => {
setIsOpen(true)
}
const toggleDropdown = () => {
setIsOpen((prev) => !prev)
}
const updatePopper = () => {
if (popper) {
popper.update()
}
}
const handleKeyUp = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
setIsOpen(false)
return
}
if (event.key === 'Tab') {
if (
dropdownRefElement.current &&
dropdownRefElement.current.contains(document.activeElement as HTMLElement)
) {
return
}
if (
dropdownMenuElement.current &&
dropdownMenuElement.current.contains(document.activeElement as HTMLElement)
) {
return
}
setIsOpen(false)
}
}
const handleMouseUp = (event: Event) => {
if (
(dropdownMenuElement.current &&
dropdownMenuElement.current.contains(event.target as HTMLElement)) ||
(dropdownRefElement.current &&
dropdownRefElement.current.contains(event.target as HTMLElement))
) {
return
}
setIsOpen(false)
}
useEffect(() => {
if (isOpen) {
window.addEventListener('mouseup', handleMouseUp)
window.addEventListener('keyup', handleKeyUp)
if (dropdownRefElement.current && dropdownMenuElement.current) {
initPopper(dropdownRefElement.current, dropdownMenuElement.current, _popperConfig)
}
} else {
window.removeEventListener('mouseup', handleMouseUp)
window.removeEventListener('keyup', handleKeyUp)
destroyPopper()
}
return () => {
window.removeEventListener('mouseup', handleMouseUp)
window.removeEventListener('keyup', handleKeyUp)
destroyPopper()
}
}, [isOpen])
return {
dropdownMenuElement,
dropdownRefElement,
isOpen,
closeDropdown,
openDropdown,
toggleDropdown,
updatePopper,
}
}