import React, { useRef, useCallback, useState } from 'react'; import useClickOutside from 'utils/useClickOutside'; import useRotate from 'styles/animation/useRotate'; import { animated, useTransition } from 'react-spring'; import { Wrapper, DownIcon, Menu, MenuItem } from './style'; import { IDropdown, IValue } from './types'; const Down = animated(DownIcon); const AnimatedMenu = animated(Menu); const isSelected = (selected: IValue, current: number | string) => { if (Array.isArray(selected)) { return selected.includes(current); } else { return selected === current; } }; export const Dropdown = function({ content, trigger, options, overlay, onChange, position, children, visible, onVisibleChange, value, defaultValue, toggle = false, multiple = false, indicator = false, width = 168, hideArrow = false, ...rest }: IDropdown) { const controlledValue = 'value' in arguments[0]; const controlledVisible = 'visible' in arguments[0]; if (!Array.isArray(value) && controlledValue && multiple) { throw new Error('多选模式的Dropdown指定的value必须是数组'); } const [isOpen, setIsOpen] = useState(false); const [selected, setSelected] = useState(defaultValue || (multiple ? [] : undefined)); if (controlledValue) { if (!multiple && selected !== value) { setSelected(value); } else if (multiple && (selected as any).join() != (value as any).join()) { setSelected(value); } } const hasSelected = multiple ? !!(selected as any).length : selected !== undefined; const ref = useRef() as any; const isTextContent = typeof content === 'string'; if (controlledVisible && visible !== isOpen) { setIsOpen(!isOpen); } if (isTextContent) { content = {content}; } const rotate = useRotate(isOpen); useClickOutside( (e: any) => { if (ref.current && !ref.current.contains(e.target)) { if (!controlledVisible) { setIsOpen(false); } onVisibleChange && isOpen && onVisibleChange(false); } }, [isOpen] ); const onClick = useCallback(() => { if (trigger === 'click') { !controlledVisible && setIsOpen(o => !o); controlledVisible && onVisibleChange && onVisibleChange(!isOpen); } }, [trigger, onVisibleChange, isOpen]); const onMouseOver = useCallback(() => { if (trigger === 'hover' && !isOpen) { setIsOpen(true); onVisibleChange && onVisibleChange(true); } }, [trigger, onVisibleChange, isOpen]); const onMouseLeave = useCallback(() => { if (trigger === 'hover' && isOpen) { setIsOpen(false); onVisibleChange && onVisibleChange(false); } }, [trigger, onVisibleChange, isOpen]); const height = Array.isArray(options) && options.length ? options.length * 36 + 12 : 'auto'; const collapse = useTransition(isOpen, null, { from: { height: 0, scale: 0 }, enter: { display: 'flex', scale: 1, height, }, leave: { height: 0, overflow: 'hidden', scale: 0 }, unique: false, config: { tension: 300, clamp: true, }, }); return ( {content || children} {!hideArrow && ( )} {collapse.map(({ item, key, props: { scale, ...rest } }) => { return ( item && ( `scale(${s}) ${position === 'middle' ? 'translate(-50%,0)' : ''}` ), transformOrigin: position === 'end' ? 'top right' : 'top left', }, }} key={key} position={position} > {options.length ? options.map((o, index) => ( { // 防止触发父节点的onClick e.stopPropagation(); if (multiple) { const selected1 = [...(selected as any)]; const mIndex = selected1.indexOf(o.value); if (~mIndex) { selected1.splice(mIndex, 1); } else { selected1.push(o.value); } !controlledValue && setSelected(selected1); onChange && onChange(selected1); // multiple 不关闭弹窗 return; } let newValue; if (selected !== o.value) { newValue = o.value; } else if (toggle) { newValue = toggle ? undefined : o.value; } else { newValue = selected; } !controlledValue && setSelected(newValue); onChange && onChange(newValue); !controlledVisible && setIsOpen(!isOpen); controlledVisible && onVisibleChange && onVisibleChange(false); }} > {o.label} )) : overlay} ) ); })} ); }; Dropdown.defaultProps = { trigger: 'hover', options: [], position: 'start', }; export default Dropdown;