/** * @file Selector * @desc 选择器相关的公共组件,Select & MultiSelect 共用之 */ import cns from 'classnames' import React, { ReactNode, useCallback, MouseEvent as ReactMouseEvent, PropsWithChildren, ChangeEvent, HTMLAttributes, useEffect, useRef, useState } from 'react' import { DownIcon, LoadingThinIcon, CloseCircleFilledIcon, SearchThinIcon } from '../icons' import { concat, getText } from '../utils' import { useSelectPrefix } from './common' import { useSelectCtx } from './context' export interface StatusIconProps { active: boolean loading: boolean searchable: boolean } export function StatusIcon({ active, loading, searchable }: StatusIconProps) { const selectClassName = useSelectPrefix() const iconClassName = concat(selectClassName, 'icon') const icon = (() => { if (active && loading) return if (active && searchable) return return })() return (
{icon}
) } /** 清除按钮 */ export function ClearIcon({ searchActive, searchValue, setSearchValue, focusSearchInput }: { searchActive: boolean searchValue: string setSearchValue: (v: string) => void focusSearchInput: () => void }) { const selectClassName = useSelectPrefix() const { selected, onSelect } = useSelectCtx() // 注意在不同状态下清除按钮清除不同的内容: // * search active 状态下:清除当前搜索框的输入 // * 其他状态下:清除当前 select 选中项 const handleClearClick = useCallback((e: ReactMouseEvent) => { e.stopPropagation() // 防止点击造成 select active 变更 if (searchActive) { setSearchValue('') focusSearchInput() } else { onSelect([]) } }, [searchActive, setSearchValue, focusSearchInput, onSelect]) const clearVisible = searchActive ? !!searchValue : selected.length > 0 const clearClassName = concat(selectClassName, 'clear') const className = cns( clearClassName, !clearVisible && concat(clearClassName, 'invisible') ) return } export interface OptionContentProps { className?: string children: ReactNode } /** 选择器中对选中项进行展示 */ export function OptionContent({ children, className }: OptionContentProps) { const selectClassName = useSelectPrefix() const optionContentClassName = concat(selectClassName, 'option-content') const title = getText(children) return (
{children}
) } /** 选择器中在没有选中项时对 placeholder 进行展示 */ export function OptionPlaceholder({ children }: PropsWithChildren<{}>) { const selectClassName = useSelectPrefix() const className = concat(selectClassName, 'placeholder') return {children} } export function useSearchInput(active: boolean) { const selectClassName = useSelectPrefix() const [searchValue, setSearchValue] = useState('') const handleSearchInputChange = useCallback((e: ChangeEvent) => { setSearchValue(e.target.value) }, []) const searchInputRef = useRef(null) const focusSearchInput = useCallback(() => { searchInputRef.current?.focus() }, []) useEffect(() => { if (active) { // active 变更会导致 input visibility 的变更 // 延迟一点做 focus,否则会没法正确地获得焦点 const timer = setTimeout(focusSearchInput, 100) return () => clearTimeout(timer) } setSearchValue('') }, [active, focusSearchInput]) const renderSearchInput = (extraProps?: HTMLAttributes) => { const handleClick = (e: ReactMouseEvent) => { e.stopPropagation() // 防止点击造成 select active 变更 extraProps?.onClick?.(e) } return ( ) } return { searchValue, setSearchValue, renderSearchInput, focusSearchInput } }