import isNil from 'lodash/isNil'; import isFunction from 'lodash/isFunction'; import React, { useCallback, useMemo, useEffect, memo, isValidElement, useImperativeHandle, forwardRef, ReactNode, } from 'react'; import type { Ref } from 'react'; import { Text, Keyboard } from 'react-native'; import { getDefaultValue } from '../helpers'; import { usePersistFn } from '../hooks'; import useState from '../hooks/useStateUpdate'; import Portal from '../portal'; import Space from '../space'; import Theme from '../theme'; import Tree from '../tree'; import type { TreeOption, TreeValue } from '../tree/interface'; import { DropdownItemConfig } from './context'; import DropdownBadge from './dropdown-badge'; import DropdownPopup from './dropdown-popup'; import type { DropdownItemOption, DropdownSelectorMethodProps, DropdownItemContext, ItemRef, } from './interface'; import { varCreator, styleCreator } from './style'; import { Deferred } from './deferred'; const DropdownSelectorMethod = ( { targetHeight, targetPageY, onConfirm, onCancel, defaultValue, options, duration, zIndex, closeOnPressOutside, onClosed, activeColor, customPanelContent, setActive, overlay, search, onSearch, cancellable, children, useNative, }: DropdownSelectorMethodProps, ref: Ref ) => { const TOKENS = Theme.useThemeTokens(); const CV = Theme.createVar(TOKENS, varCreator); const CV_TREE = Theme.createVar(TOKENS, Tree.varCreator); // @ts-ignore const STYLES = Theme.createStyle(CV, styleCreator); const _activeColor = getDefaultValue(activeColor, CV.dropdown_active_color); const [visible, setVisible] = useState(false); const treeOptions = useMemo(() => { const convertOption = (ops: DropdownItemOption[]) => { const nodes: TreeOption[] = []; ops.forEach((item) => { const _opt: TreeOption = { label: item.label, value: item.value as unknown as number | string, children: item.children?.length ? convertOption(item.children) : [], render: isNil(item.badge) ? undefined : (p) => { return ( {p.label} ); }, }; nodes.push(_opt); }); return nodes; }; return convertOption(options); }, [ CV_TREE.tree_item_text_color, CV_TREE.tree_item_text_font_size, STYLES.item_tree_item, options, ]); useEffect(() => { setVisible(true); }, []); const onPressShade = useCallback(() => { setVisible(false); Keyboard.dismiss(); onCancel?.(); }, [onCancel]); const onRequestClose = usePersistFn(() => { onPressShade(); return true; }); const onChangePersistFn = usePersistFn((v: TreeValue) => { setVisible(false); Keyboard.dismiss(); const findNodeByValue = ( tree: DropdownItemOption[], value: T // @ts-ignore ): DropdownItemOption => { for (const item of tree) { if (item.value === value) { return item; } if (item.children) { const _v = findNodeByValue(item.children, value); if (_v) { return _v; } } } }; const _v = v as unknown as T; onConfirm?.(_v as unknown as T, findNodeByValue(options, _v)); }); // DropdownItem value const dropdownItemValue: DropdownItemContext = { setActive, setVisible, close: () => { setActive?.(false); setVisible(false); }, }; useImperativeHandle(ref, () => ({ close: () => { setActive?.(false); setVisible(false); }, })); return ( {/* 渲染面板内容 */} {children} {/* 如果有自定义的面板内容,则渲染 */} {isValidElement(customPanelContent) && customPanelContent} {treeOptions.length !== 0 && ( )} ); }; const DropdownSelectorMethodMemo = memo(forwardRef(DropdownSelectorMethod)) as < T >( p: DropdownSelectorMethodProps, ref: Ref ) => React.ReactElement; const DropdownSelector = ( opt: DropdownSelectorMethodProps, ref: Ref ) => { const deferred = new Deferred<{ value: T; data: DropdownItemOption }>(); const getDropdown = (props: any, ref: any) => ( {...props} onCancel={() => { opt.onCancel?.(); deferred.reject(new Error()); }} onConfirm={(v, d) => { opt.onConfirm?.(v, d); deferred.resolve({ value: v, data: d, }); }} onClosed={() => { // opt里的onClosed会被覆盖掉,所以这里手动触发一下 if (isFunction(opt.onClosed)) { opt.onClosed(); } Portal.remove(key); }} ref={ref} /> ); const key = Portal.add(getDropdown(opt, ref)); const update = (children: ReactNode) => { Portal.update(key, getDropdown({ ...opt, children }, ref)); }; return { promise: deferred.promise, update, }; }; export default DropdownSelector;