import React, { forwardRef, useEffect, useImperativeHandle, useMemo, useState, } from 'react'; import KeyboardManager from 'react-native-keyboard-manager'; import { FieldValues } from 'react-hook-form'; import { FormItemProps } from '../Item'; import Select from '../../Select'; import type { Option, Group, SelectProps, SelectInputProps, } from '../../Select/interface'; import { PlatformOS } from '../../../utils'; import { useLocale } from '../../Locale/locale'; import { useFormFieldContext } from '../hooks/FormFieldContext'; import withFormItem from '../withFormItem'; // Add helper functions to handle grouped options const isGroup = (option: Option | Group): option is Group => { return (option as Group).children !== undefined; }; const flattenOptions = (options: (Option | Group)[]): BizOption[] => { return options.reduce((acc, curr) => { if (isGroup(curr)) { return [...acc, ...curr.children]; } return [...acc, curr as Option]; }, []); }; export interface BizOption extends Option { /** * Option本身带有label,textLabel的作用?与label的区别?为什么需要textLabel? * 作用:纯文本,用于表单展示; * 与label的区别:textLabel纯文本,label可以是个复杂节点; * 为什么需要textLabel:当你的label为react元素的时候需要textLabel去做展示;如果你的label只有文案,不需要textLabel */ textLabel?: string; } export interface BizGroup extends Group { children: BizOption[]; } export interface SelectBindingRef { /** * 打开选择弹窗 */ open: () => void; /** * 关闭选择弹窗 */ close: () => void; /** * 切换选择弹窗显示状态 */ toggle: () => void; /** * 获取当前弹窗显示状态 */ isVisible: () => boolean; } export type PureSelectProps = Omit< SelectProps, 'defaultValue' | 'visible' | 'onChange' | 'options' | 'type' | 'onConfirm' > & { options: (BizOption | BizGroup)[]; /** options为空时的点击处理事件 */ emptyHandler?: () => void; disable?: boolean; selectInputProps?: SelectInputProps; placeholder?: string; type?: SelectProps['type']; accessibilityLabel?: string; /** 点击确认时,选中的值和搜索框内的值,可返回 false 取消关闭当前Select */ onConfirm?: (value: string | string[], searchStr?: string) => void | boolean; onChange?: ( value: string | string[], selectOptions?: BizOption[] | undefined ) => void; }; function PureSelectBase( props: PureSelectProps, ref: React.Ref ) { const langValues = useLocale().Selector; const { onChange, onPressOverlay, onConfirm, onCancel, options, type = Select.Type.radio, showFooter = false, placeholder = langValues.title, emptyHandler, disable, onRepeatClick, onRequestClose, selectInputProps, accessibilityLabel, ...resetProps } = props; // 从FormFieldContext获取表单相关数据 const { field, hasError } = useFormFieldContext(); const value = useMemo(() => { // props.value 优先级高于 field.value if (props.value !== undefined) { return props.value; } return field.value; }, [props.value, field.value]); // 展示options const [showSelectPop, setShowSelectPop] = useState(false); // 暴露ref useImperativeHandle(ref, () => ({ open: () => setShowSelectPop(true), close: () => setShowSelectPop(false), toggle: () => setShowSelectPop((prev) => !prev), isVisible: () => showSelectPop, })); const combinedOnPressOverlay = () => { onPressOverlay?.(); setShowSelectPop(false); }; const combinedOnRequestClose = () => { const res = onRequestClose?.() ?? true; res && setShowSelectPop(false); return res; }; const combinedOnConfirm = (value: string | string[]) => { const res = onConfirm?.(value) ?? true; res && setShowSelectPop(false); }; const combinedOnCancel = () => { onCancel?.(); setShowSelectPop(false); }; const combinedOnRepeatClick = () => { onRepeatClick?.(); setShowSelectPop(false); }; // @ts-ignore const isMulti = type === Select.Type.checkbox; // 是否是多选 // Flatten options for processing const flattenedOptions = useMemo(() => flattenOptions(options), [options]); const currentSelected = useMemo( () => flattenedOptions.filter((option) => isMulti ? value?.includes(option.value) : option.value === value ), [value, flattenedOptions, isMulti] ); const combinedOnChange = (val: string | string[]) => { // 当前点击的option const currentClickOption = flattenedOptions.filter((option) => Array.isArray(val) ? val.includes(option.value) : option.value === val ); if (!isMulti && currentSelected.some((option) => option.value === val)) { return setShowSelectPop(false); } onChange?.(val ?? null, currentClickOption); field.onChange(val ?? null); field.onBlur(); if (!isMulti) { setShowSelectPop(false); } }; const handleTagClose = (clearKey: string) => { const resetValues = currentSelected ?.filter((select) => select?.value !== clearKey) ?.map((resetOption) => resetOption?.value); onChange?.( resetValues || [], currentSelected.filter((option) => option.value !== clearKey) ); field.onChange(resetValues || []); field.onBlur(); }; const showContent = useMemo(() => { if (!currentSelected || currentSelected?.length === 0) return undefined; if (currentSelected.some((select) => select.textLabel !== undefined)) { return currentSelected.map((select) => select.textLabel).join(','); } return currentSelected.map((select) => select.label); }, [currentSelected]); const handlePress = () => { if (disable) return; if (!options?.length && emptyHandler) return emptyHandler(); setShowSelectPop(true); }; const renderSingleSelect = () => { return ( ); }; const renderMultiSelect = () => { return ( handleTagClose(item?.value)} allowClear={false} accessibilityLabel={accessibilityLabel} {...selectInputProps} /> ); }; useEffect(() => { if (!PlatformOS.isIos) return; KeyboardManager.setEnable(!showSelectPop); return () => { if (PlatformOS.isIos) { KeyboardManager.setEnable(true); } }; }, [showSelectPop]); return ( <> {isMulti ? renderMultiSelect() : renderSingleSelect()} {/* 某些机型下 options 后端获取的导致滑动不了 */}