UNPKG

phx-react

Version:

PHX REACT

125 lines 5.66 kB
import * as React from 'react'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { createPortal } from 'react-dom'; const DropDownContext = React.createContext(null); const dropDownPadding = 4; export function DropDownItem({ children, className, onClick, title, }) { const ref = useRef(null); const dropDownContext = React.useContext(DropDownContext); if (dropDownContext === null) { throw new Error('DropDownItem must be used within a DropDown'); } const { registerItem } = dropDownContext; useEffect(() => { if (ref && ref.current) { registerItem(ref); } }, [ref, registerItem]); return (React.createElement("button", { ref: ref, className: className, onClick: onClick, title: title, type: 'button' }, children)); } function DropDownItems({ children, dropDownRef, onClose, }) { const [items, setItems] = useState(); const [highlightedItem, setHighlightedItem] = useState(); const registerItem = useCallback((itemRef) => { setItems((prev) => (prev ? [...prev, itemRef] : [itemRef])); }, [setItems]); const handleKeyDown = (event) => { if (!items) return; const key = event.key; if (['Escape', 'ArrowUp', 'ArrowDown', 'Tab'].includes(key)) { event.preventDefault(); } if (key === 'Escape' || key === 'Tab') { onClose(); } else if (key === 'ArrowUp') { setHighlightedItem((prev) => { if (!prev) return items[0]; const index = items.indexOf(prev) - 1; return items[index === -1 ? items.length - 1 : index]; }); } else if (key === 'ArrowDown') { setHighlightedItem((prev) => { if (!prev) return items[0]; return items[items.indexOf(prev) + 1]; }); } }; const contextValue = useMemo(() => ({ registerItem, }), [registerItem]); useEffect(() => { if (items && !highlightedItem) { setHighlightedItem(items[0]); } if (highlightedItem && highlightedItem.current) { highlightedItem.current.focus(); } }, [items, highlightedItem]); return (React.createElement(DropDownContext.Provider, { value: contextValue }, React.createElement("div", { ref: dropDownRef, className: 'dropdown', onKeyDown: handleKeyDown }, children))); } export default function DropDown({ buttonAriaLabel, buttonClassName, buttonIconClassName, buttonLabel, children, disabled = false, stopCloseOnClickSelf, }) { console.log(stopCloseOnClickSelf, 'stopCloseOnClickSelf'); const dropDownRef = useRef(null); const buttonRef = useRef(null); const [showDropDown, setShowDropDown] = useState(false); const handleClose = () => { setShowDropDown(false); if (buttonRef && buttonRef.current) { buttonRef.current.focus(); } }; useEffect(() => { const button = buttonRef.current; const dropDown = dropDownRef.current; if (showDropDown && button !== null && dropDown !== null) { const { left, top } = button.getBoundingClientRect(); dropDown.style.top = `${top + button.offsetHeight + dropDownPadding}px`; dropDown.style.left = `${Math.min(left, window.innerWidth - dropDown.offsetWidth - 20)}px`; } }, [dropDownRef, buttonRef, showDropDown]); useEffect(() => { const handleButtonPositionUpdate = () => { if (showDropDown) { const button = buttonRef.current; const dropDown = dropDownRef.current; if (button !== null && dropDown !== null) { const { top } = button.getBoundingClientRect(); const newPosition = top + button.offsetHeight + dropDownPadding; if (newPosition !== dropDown.getBoundingClientRect().top) { dropDown.style.top = `${newPosition}px`; } } } }; document.addEventListener('scroll', handleButtonPositionUpdate); return () => { document.removeEventListener('scroll', handleButtonPositionUpdate); }; }, [buttonRef, dropDownRef, showDropDown]); useEffect(() => { const handleClickOutside = (event) => { var _a; if (dropDownRef.current && !((_a = dropDownRef.current) === null || _a === void 0 ? void 0 : _a.contains(event.target))) { setShowDropDown(false); } }; document.addEventListener('mousedown', handleClickOutside); return () => { document.removeEventListener('mousedown', handleClickOutside); }; }, [dropDownRef]); return (React.createElement("div", { className: 'z-10' }, React.createElement("button", { ref: buttonRef, "aria-label": buttonAriaLabel || buttonLabel, className: buttonClassName, disabled: disabled, onClick: () => setShowDropDown(!showDropDown), type: 'button' }, buttonIconClassName && React.createElement("span", { className: buttonIconClassName }), buttonLabel && React.createElement("span", { className: 'text dropdown-button-text' }, buttonLabel), React.createElement("i", { className: 'chevron-down' })), showDropDown && createPortal(React.createElement(DropDownItems, { dropDownRef: dropDownRef, onClose: handleClose }, children), document.body))); } //# sourceMappingURL=DropDown.js.map