import './$$dropdown.less'; import ReactDom from 'react-dom'; import React, { useContext, useEffect, useMemo, useRef, useState } from 'react'; import { useCallbackRef } from '../../hook/useCallbackRef'; interface DropdownOption { reference: HTMLElement | { x: number; y: number } | MouseEvent; render: () => JSX.Element | JSX.Element[] | React.ReactFragment; } const DropdownContext = React.createContext<{ onClick: () => void } | undefined>(undefined); const Dropdown: React.FC<{ option: DropdownOption; onRef: (ins: { show: (opt: DropdownOption) => void }) => void; }> = (props) => { const elRef = useRef({} as HTMLDivElement); const [option, setOption] = useState(props.option); const [showFlag, setShowFlag] = useState(false); const styles = useMemo(() => { let top: number, left: number; const { reference } = option; if ('addEventListener' in reference) { const { top: y, left: x } = reference.getBoundingClientRect(); top = y; left = x; } else if ('target' in reference) { const { clientY: y, clientX: x } = reference; top = y; left = x; } else { top = reference.y; left = reference.x; } return { top: `${top}px`, left: `${left}px`, display: showFlag ? 'inline-block' : 'none', }; }, [option.reference, showFlag]); const methods = { close: () => { setShowFlag(false); }, show: (opt: DropdownOption) => { setOption(opt); setShowFlag(true); }, }; const handler = { onClickBody: useCallbackRef((e: MouseEvent) => { if (elRef.current.contains(e.target as Node)) { /*点击了dropdown*/ } else { methods.close(); } }), onClickDropdownItem: useCallbackRef(() => { methods.close(); }), }; props.onRef(methods); useEffect(() => { document.body.addEventListener('click', handler.onClickBody); return () => { document.body.removeEventListener('click', handler.onClickBody); }; }, []); return (
{option.render()}
); }; export const DropdownItem: React.FC<{ onClick?: (e: React.MouseEvent) => void; icon?: string; }> = (props) => { const dropdown = useContext(DropdownContext); const handler = { onClick: (e: React.MouseEvent) => { !!dropdown && dropdown.onClick(); !!props.onClick && props.onClick(e); }, }; return (
{!!props.icon && } {props.children}
); }; export const $$dropdown = (() => { let ins: any; return (option: DropdownOption) => { if (!ins) { const el = document.createElement('div'); document.body.appendChild(el); ReactDom.render( (ins = i)} />, el); } ins.show(option); }; })();