/**
* @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 }
}