import React, { FocusEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react' import TextInput, { Props as TextInputProps } from './TextInput' import { SelectOptionItems, SelectFetchFn } from '../Select' import { useActive } from '../Select/common' import OptionsOverlay, { OptionsOverlayProvider, OptionsOverlayCtxValue, hasAvailableOption } from '../Select/Overlay' import Dropdown from '../Dropdown' import { useInputSize } from './input' import { useValue } from '../utils/hooks' export interface Props extends Omit { /** 提示项获取方法 */ fetch: SelectFetchFn } export default function AutoComplete({ value: valueFromProps, defaultValue = '', onChange, disabled = false, size, inputProps, fetch, ...otherProps }: Props) { const { value, setValue } = useValue({ valueFromProps, defaultValue, onChange }) const { active, setActive, selectorRef, overlayRef } = useActive(disabled) const { items, loading, visible } = useOptionItems(active, value, fetch) const handleChange = useCallback((newValue: string) => { setValue(newValue) }, [setValue]) const handleFocus = useCallback((e: FocusEvent) => { inputProps?.onFocus?.(e) setActive(true) }, [setActive, inputProps]) const handleSelect = useCallback((v: string[]) => { const newValue = v.length > 0 ? v[0] : null if (newValue != null) { setValue(newValue) } setActive(false) }, [setValue, setActive]) const inputSize = useInputSize(size) const overlayCtxValue: OptionsOverlayCtxValue = useMemo(() => ({ selected: value != null ? [value] : [], onSelect: handleSelect, multiple: false, size: inputSize }), [value, handleSelect, inputSize]) const overlay = ( ) return ( ) } /** 文本输入框内容变更时会触发重新获取数据,这里给这个行为添加一个 debounce,单位 ms */ const fetchDelay = 200 export function useOptionItems(active: boolean, keyword: string, fetch: SelectFetchFn) { const [items, setItems] = useState>([]) const [loading, setLoading] = useState(false) const fetchRef = useRef(fetch) fetchRef.current = fetch useEffect(() => { // 自动补全功能关闭(约等于 TextInput 失焦)时清空数据 if (!active) { setItems([]) return } // keyword 发生变化 | 自动补全功能打开(TextInput 变为编辑状态)时获取数据 let cancelled = false async function doFetch() { setLoading(true) const fetchedItems = await fetchRef.current(keyword) if (!cancelled) { setItems(fetchedItems) setLoading(false) } } const timer = setTimeout(doFetch, fetchDelay) return () => { clearTimeout(timer) cancelled = true } }, [active, keyword]) return { items, loading, visible: active && (loading || hasAvailableOption(items)) } }