2019-09-12 20:15:17 +08:00
// TODO: 4.0 - codemod should help to change `filterOption` to support node props.
2023-09-11 17:28:04 +08:00
import * as React from 'react' ;
2019-09-12 20:15:17 +08:00
import classNames from 'classnames' ;
2023-02-17 22:11:06 +08:00
import type { BaseSelectRef , SelectProps as RcSelectProps } from 'rc-select' ;
2023-01-12 11:04:35 +08:00
import RcSelect , { OptGroup , Option } from 'rc-select' ;
2022-11-09 12:28:04 +08:00
import type { OptionProps } from 'rc-select/lib/Option' ;
2022-06-06 23:39:00 +08:00
import type { BaseOptionType , DefaultOptionType } from 'rc-select/lib/Select' ;
import omit from 'rc-util/lib/omit' ;
2023-09-11 17:28:04 +08:00
2023-10-19 15:03:20 +08:00
import { useZIndex } from '../_util/hooks/useZIndex' ;
2023-03-29 20:30:50 +08:00
import type { SelectCommonPlacement } from '../_util/motion' ;
2023-07-25 10:54:58 +08:00
import { getTransitionName } from '../_util/motion' ;
2023-09-11 17:28:04 +08:00
import genPurePanel from '../_util/PurePanel' ;
2023-03-29 20:30:50 +08:00
import type { InputStatus } from '../_util/statusUtils' ;
import { getMergedStatus , getStatusClassNames } from '../_util/statusUtils' ;
2023-09-11 17:28:04 +08:00
import { devUseWarning } from '../_util/warning' ;
2020-10-24 20:33:18 +08:00
import { ConfigContext } from '../config-provider' ;
2023-03-29 20:30:50 +08:00
import DefaultRenderEmpty from '../config-provider/defaultRenderEmpty' ;
2023-09-11 17:28:04 +08:00
import DisabledContext from '../config-provider/DisabledContext' ;
2023-05-12 14:53:47 +08:00
import useSize from '../config-provider/hooks/useSize' ;
2023-09-11 17:28:04 +08:00
import type { SizeType } from '../config-provider/SizeContext' ;
2022-03-24 21:54:20 +08:00
import { FormItemInputContext } from '../form/context' ;
2023-03-29 20:30:50 +08:00
import { useCompactItemContext } from '../space/Compact' ;
2023-02-17 22:11:06 +08:00
import useStyle from './style' ;
2023-04-04 13:43:53 +08:00
import useBuiltinPlacements from './useBuiltinPlacements' ;
2023-09-11 17:28:04 +08:00
import useIcons from './useIcons' ;
2023-10-19 15:03:20 +08:00
import useShowArrow from './useShowArrow' ;
2023-11-09 09:35:45 +08:00
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls' ;
2023-12-11 14:55:58 +08:00
import useVariants from '../_util/hooks/useVariants' ;
2022-03-08 10:29:00 +08:00
2019-09-12 20:15:17 +08:00
type RawValue = string | number ;
2015-06-09 21:16:59 +08:00
2023-06-07 21:59:21 +08:00
export type { BaseOptionType , DefaultOptionType , OptionProps , BaseSelectRef as RefSelectProps } ;
2017-01-12 09:23:56 +08:00
2017-03-28 15:10:07 +08:00
export interface LabeledValue {
2019-09-12 20:15:17 +08:00
key? : string ;
value : RawValue ;
2017-03-28 15:10:07 +08:00
label : React.ReactNode ;
}
2021-04-10 13:35:08 +08:00
export type SelectValue = RawValue | RawValue [ ] | LabeledValue | LabeledValue [ ] | undefined ;
2017-03-28 15:10:07 +08:00
2023-12-21 13:39:04 +08:00
export const SelectVariants = [ 'outlined' , 'borderless' , 'filled' ] as const ;
2023-12-11 14:55:58 +08:00
export type SelectVariant = ( typeof SelectVariants ) [ number ] ;
2021-12-21 21:38:11 +08:00
export interface InternalSelectProps <
ValueType = any ,
OptionType extends BaseOptionType | DefaultOptionType = DefaultOptionType ,
> extends Omit < RcSelectProps < ValueType , OptionType > , 'mode' > {
2023-08-08 10:50:34 +08:00
rootClassName? : string ;
2018-09-16 19:43:53 +08:00
suffixIcon? : React.ReactNode ;
2020-01-03 13:38:16 +08:00
size? : SizeType ;
2022-04-29 20:48:10 +08:00
disabled? : boolean ;
2023-07-07 15:00:15 +08:00
mode ? : 'multiple' | 'tags' | 'SECRET_COMBOBOX_MODE_DO_NOT_USE' | 'combobox' ;
2023-12-11 14:55:58 +08:00
/** @deprecated Use `variant` instead. */
2020-02-06 10:02:02 +08:00
bordered? : boolean ;
2023-07-19 20:27:09 +08:00
/ * *
* @deprecated ` showArrow ` is deprecated which will be removed in next major version . It will be a
* default behavior , you can hide it by setting ` suffixIcon ` to null .
* /
showArrow? : boolean ;
2023-12-18 17:53:31 +08:00
/ * *
* @since 5.13 . 0
* @default "outlined"
* /
2023-12-11 14:55:58 +08:00
variant? : SelectVariant ;
2016-09-28 16:58:14 +08:00
}
2021-12-21 21:38:11 +08:00
export interface SelectProps <
ValueType = any ,
OptionType extends BaseOptionType | DefaultOptionType = DefaultOptionType ,
> extends Omit <
InternalSelectProps < ValueType , OptionType > ,
2023-07-19 20:27:09 +08:00
'mode' | 'getInputElement' | 'getRawInputElement' | 'backfill' | 'placement'
2021-08-02 12:07:26 +08:00
> {
2022-01-20 16:54:47 +08:00
placement? : SelectCommonPlacement ;
2019-09-12 20:15:17 +08:00
mode ? : 'multiple' | 'tags' ;
2022-02-16 21:39:48 +08:00
status? : InputStatus ;
2022-08-05 11:21:07 +08:00
popupClassName? : string ;
2022-09-08 14:33:11 +08:00
/** @deprecated Please use `popupClassName` instead */
dropdownClassName? : string ;
2023-04-04 17:17:36 +08:00
/** @deprecated Please use `popupMatchSelectWidth` instead */
dropdownMatchSelectWidth? : boolean | number ;
popupMatchSelectWidth? : boolean | number ;
2017-11-20 17:03:08 +08:00
}
2020-10-24 20:33:18 +08:00
const SECRET_COMBOBOX_MODE_DO_NOT_USE = 'SECRET_COMBOBOX_MODE_DO_NOT_USE' ;
2023-06-09 16:29:52 +08:00
const InternalSelect = <
ValueType = any ,
OptionType extends BaseOptionType | DefaultOptionType = DefaultOptionType ,
> (
2023-12-11 14:55:58 +08:00
props : SelectProps < ValueType , OptionType > ,
ref : React.Ref < BaseSelectRef > ,
) = > {
const {
2020-10-24 20:33:18 +08:00
prefixCls : customizePrefixCls ,
bordered = true ,
className ,
2023-01-20 11:03:50 +08:00
rootClassName ,
2020-10-24 20:33:18 +08:00
getPopupContainer ,
2022-08-05 11:21:07 +08:00
popupClassName ,
2022-09-08 14:33:11 +08:00
dropdownClassName ,
2020-10-24 20:33:18 +08:00
listHeight = 256 ,
2022-01-20 16:54:47 +08:00
placement ,
2020-10-24 20:33:18 +08:00
listItemHeight = 24 ,
size : customizeSize ,
2022-04-29 20:48:10 +08:00
disabled : customDisabled ,
2020-10-24 20:33:18 +08:00
notFoundContent ,
2022-02-16 21:39:48 +08:00
status : customStatus ,
2023-04-04 13:43:53 +08:00
builtinPlacements ,
2023-04-04 17:17:36 +08:00
dropdownMatchSelectWidth ,
popupMatchSelectWidth ,
2023-05-21 02:59:50 +08:00
direction : propDirection ,
2023-06-27 17:12:36 +08:00
style ,
2023-08-02 14:21:11 +08:00
allowClear ,
2023-12-11 14:55:58 +08:00
variant : customizeVariant ,
. . . rest
} = props ;
2020-10-24 20:33:18 +08:00
const {
getPopupContainer : getContextPopupContainer ,
getPrefixCls ,
renderEmpty ,
2023-05-21 02:59:50 +08:00
direction : contextDirection ,
2020-10-24 20:33:18 +08:00
virtual ,
2023-04-04 17:17:36 +08:00
popupMatchSelectWidth : contextPopupMatchSelectWidth ,
2023-04-07 10:17:00 +08:00
popupOverflow ,
2022-12-14 10:22:17 +08:00
select ,
2020-10-24 20:33:18 +08:00
} = React . useContext ( ConfigContext ) ;
2018-06-19 14:37:35 +08:00
2020-10-24 20:33:18 +08:00
const prefixCls = getPrefixCls ( 'select' , customizePrefixCls ) ;
2021-02-08 17:09:13 +08:00
const rootPrefixCls = getPrefixCls ( ) ;
2023-05-21 02:59:50 +08:00
const direction = propDirection ? ? contextDirection ;
2022-10-18 16:23:10 +08:00
const { compactSize , compactItemClassnames } = useCompactItemContext ( prefixCls , direction ) ;
2018-12-26 16:01:00 +08:00
2023-12-21 13:39:04 +08:00
const [ variant , enableVariantCls ] = useVariants ( customizeVariant , bordered , SelectVariants ) ;
2023-12-11 14:55:58 +08:00
2023-11-10 10:02:46 +08:00
const rootCls = useCSSVarCls ( prefixCls ) ;
2023-12-14 14:58:53 +08:00
const [ wrapCSSVar , hashId , cssVarCls ] = useStyle ( prefixCls , rootCls ) ;
2022-03-08 10:29:00 +08:00
2020-10-24 20:33:18 +08:00
const mode = React . useMemo ( ( ) = > {
2021-12-21 21:38:11 +08:00
const { mode : m } = props as InternalSelectProps < OptionType > ;
2019-08-05 18:38:10 +08:00
2023-07-07 15:00:15 +08:00
if ( m === 'combobox' ) {
2019-09-12 20:15:17 +08:00
return undefined ;
2018-11-27 12:01:30 +08:00
}
2019-09-12 20:15:17 +08:00
2020-10-24 20:33:18 +08:00
if ( m === SECRET_COMBOBOX_MODE_DO_NOT_USE ) {
2019-09-12 20:15:17 +08:00
return 'combobox' ;
2018-11-27 12:01:30 +08:00
}
2020-10-24 20:33:18 +08:00
return m ;
} , [ props . mode ] ) ;
2019-09-12 20:15:17 +08:00
2020-10-24 20:33:18 +08:00
const isMultiple = mode === 'multiple' || mode === 'tags' ;
2023-07-19 20:27:09 +08:00
const showSuffixIcon = useShowArrow ( props . suffixIcon , props . showArrow ) ;
2022-02-16 21:39:48 +08:00
2023-04-04 17:17:36 +08:00
const mergedPopupMatchSelectWidth =
popupMatchSelectWidth ? ? dropdownMatchSelectWidth ? ? contextPopupMatchSelectWidth ;
2022-03-24 21:54:20 +08:00
// ===================== Form Status =====================
2022-03-25 17:48:12 +08:00
const {
status : contextStatus ,
hasFeedback ,
isFormItemInput ,
feedbackIcon ,
2022-12-06 21:09:59 +08:00
} = React . useContext ( FormItemInputContext ) ;
2022-02-16 21:39:48 +08:00
const mergedStatus = getMergedStatus ( contextStatus , customStatus ) ;
2018-12-26 16:01:00 +08:00
2020-10-24 20:33:18 +08:00
// ===================== Empty =====================
let mergedNotFound : React.ReactNode ;
if ( notFoundContent !== undefined ) {
mergedNotFound = notFoundContent ;
} else if ( mode === 'combobox' ) {
mergedNotFound = null ;
} else {
2023-01-09 10:04:35 +08:00
mergedNotFound = renderEmpty ? . ( 'Select' ) || < DefaultRenderEmpty componentName = "Select" / > ;
2017-10-20 14:54:38 +08:00
}
2020-10-24 20:33:18 +08:00
// ===================== Icons =====================
2023-09-11 17:28:04 +08:00
const { suffixIcon , itemIcon , removeIcon , clearIcon } = useIcons ( {
2023-12-11 14:55:58 +08:00
. . . rest ,
2020-10-24 20:33:18 +08:00
multiple : isMultiple ,
2022-02-16 21:39:48 +08:00
hasFeedback ,
2022-03-25 17:48:12 +08:00
feedbackIcon ,
2023-07-19 20:27:09 +08:00
showSuffixIcon ,
2020-10-24 20:33:18 +08:00
prefixCls ,
2023-08-02 14:21:11 +08:00
componentName : 'Select' ,
2020-10-24 20:33:18 +08:00
} ) ;
2023-08-02 14:21:11 +08:00
const mergedAllowClear = allowClear === true ? { clearIcon } : allowClear ;
2023-12-11 14:55:58 +08:00
const selectProps = omit ( rest as typeof rest & { itemIcon : React.ReactNode } , [
2023-07-14 13:25:15 +08:00
'suffixIcon' ,
'itemIcon' ,
] ) ;
2020-10-24 20:33:18 +08:00
2023-11-09 09:35:45 +08:00
const mergedPopupClassName = classNames (
2022-09-08 14:33:11 +08:00
popupClassName || dropdownClassName ,
2022-03-08 10:29:00 +08:00
{
[ ` ${ prefixCls } -dropdown- ${ direction } ` ] : direction === 'rtl' ,
} ,
2023-01-20 11:03:50 +08:00
rootClassName ,
2023-12-14 14:58:53 +08:00
cssVarCls ,
2023-11-09 09:35:45 +08:00
rootCls ,
2022-03-08 10:29:00 +08:00
hashId ,
) ;
2020-10-24 20:33:18 +08:00
2023-06-19 14:26:48 +08:00
const mergedSize = useSize ( ( ctx ) = > customizeSize ? ? compactSize ? ? ctx ) ;
2022-04-29 20:48:10 +08:00
// ===================== Disabled =====================
const disabled = React . useContext ( DisabledContext ) ;
2022-09-20 16:48:59 +08:00
const mergedDisabled = customDisabled ? ? disabled ;
2022-04-29 20:48:10 +08:00
2020-10-24 20:33:18 +08:00
const mergedClassName = classNames (
{
[ ` ${ prefixCls } -lg ` ] : mergedSize === 'large' ,
[ ` ${ prefixCls } -sm ` ] : mergedSize === 'small' ,
[ ` ${ prefixCls } -rtl ` ] : direction === 'rtl' ,
2023-12-11 14:55:58 +08:00
[ ` ${ prefixCls } - ${ variant } ` ] : enableVariantCls ,
2022-03-24 21:54:20 +08:00
[ ` ${ prefixCls } -in-form-item ` ] : isFormItemInput ,
2020-10-24 20:33:18 +08:00
} ,
2022-02-16 21:39:48 +08:00
getStatusClassNames ( prefixCls , mergedStatus , hasFeedback ) ,
2022-10-18 16:23:10 +08:00
compactItemClassnames ,
2023-06-27 17:12:36 +08:00
select ? . className ,
2020-10-24 20:33:18 +08:00
className ,
2023-01-20 11:03:50 +08:00
rootClassName ,
2023-12-14 14:58:53 +08:00
cssVarCls ,
2023-11-09 09:35:45 +08:00
rootCls ,
2022-03-08 10:29:00 +08:00
hashId ,
2020-10-24 20:33:18 +08:00
) ;
2022-01-20 16:54:47 +08:00
// ===================== Placement =====================
2023-03-29 20:30:50 +08:00
const memoPlacement = React . useMemo < SelectCommonPlacement > ( ( ) = > {
2022-01-20 16:54:47 +08:00
if ( placement !== undefined ) {
return placement ;
}
2023-02-17 22:11:06 +08:00
return direction === 'rtl' ? 'bottomRight' : 'bottomLeft' ;
2023-03-29 20:30:50 +08:00
} , [ placement , direction ] ) ;
2022-01-20 16:54:47 +08:00
2023-04-07 10:17:00 +08:00
const mergedBuiltinPlacements = useBuiltinPlacements ( builtinPlacements , popupOverflow ) ;
2023-04-04 13:43:53 +08:00
2022-09-08 14:33:11 +08:00
// ====================== Warning ======================
if ( process . env . NODE_ENV !== 'production' ) {
2023-09-13 22:07:33 +08:00
const warning = devUseWarning ( 'Select' ) ;
2023-09-11 17:28:04 +08:00
2023-09-13 22:07:33 +08:00
warning . deprecated ( ! dropdownClassName , 'dropdownClassName' , 'popupClassName' ) ;
2023-04-04 17:17:36 +08:00
2023-09-13 22:07:33 +08:00
warning . deprecated (
2023-04-04 17:17:36 +08:00
dropdownMatchSelectWidth === undefined ,
2023-09-13 22:07:33 +08:00
'dropdownMatchSelectWidth' ,
'popupMatchSelectWidth' ,
2023-04-04 17:17:36 +08:00
) ;
2023-07-19 20:27:09 +08:00
warning (
! ( 'showArrow' in props ) ,
2023-09-11 17:28:04 +08:00
'deprecated' ,
2023-07-19 20:27:09 +08:00
'`showArrow` is deprecated which will be removed in next major version. It will be a default behavior, you can hide it by setting `suffixIcon` to null.' ,
) ;
2023-12-11 14:55:58 +08:00
warning . deprecated ( ! ( 'bordered' in props ) , 'bordered' , 'variant' ) ;
2022-09-08 14:33:11 +08:00
}
2023-10-19 15:03:20 +08:00
// ====================== zIndex =========================
2023-10-23 17:16:41 +08:00
const [ zIndex ] = useZIndex ( 'SelectLike' , props . dropdownStyle ? . zIndex as number ) ;
2023-10-19 15:03:20 +08:00
2022-09-08 14:33:11 +08:00
// ====================== Render =======================
2023-11-09 09:35:45 +08:00
return wrapCSSVar (
2023-06-09 16:29:52 +08:00
< RcSelect < ValueType , OptionType >
2023-03-30 14:55:34 +08:00
ref = { ref }
2020-10-24 20:33:18 +08:00
virtual = { virtual }
2022-12-14 10:22:17 +08:00
showSearch = { select ? . showSearch }
2020-10-24 20:33:18 +08:00
{ . . . selectProps }
2023-06-27 17:12:36 +08:00
style = { { . . . select ? . style , . . . style } }
2023-04-04 17:17:36 +08:00
dropdownMatchSelectWidth = { mergedPopupMatchSelectWidth }
2023-04-04 13:43:53 +08:00
builtinPlacements = { mergedBuiltinPlacements }
2023-07-25 10:54:58 +08:00
transitionName = { getTransitionName ( rootPrefixCls , 'slide-up' , props . transitionName ) }
2020-10-24 20:33:18 +08:00
listHeight = { listHeight }
listItemHeight = { listItemHeight }
2023-03-29 20:30:50 +08:00
mode = { mode }
2020-10-24 20:33:18 +08:00
prefixCls = { prefixCls }
2023-03-29 20:30:50 +08:00
placement = { memoPlacement }
2020-10-24 20:33:18 +08:00
direction = { direction }
2023-07-19 20:27:09 +08:00
suffixIcon = { suffixIcon }
2020-10-24 20:33:18 +08:00
menuItemSelectedIcon = { itemIcon }
removeIcon = { removeIcon }
2023-08-02 14:21:11 +08:00
allowClear = { mergedAllowClear }
2020-10-24 20:33:18 +08:00
notFoundContent = { mergedNotFound }
className = { mergedClassName }
getPopupContainer = { getPopupContainer || getContextPopupContainer }
2023-11-09 09:35:45 +08:00
dropdownClassName = { mergedPopupClassName }
2022-04-29 20:48:10 +08:00
disabled = { mergedDisabled }
2023-10-19 15:03:20 +08:00
dropdownStyle = { {
. . . props ? . dropdownStyle ,
2023-10-24 11:49:30 +08:00
zIndex ,
2023-10-19 15:03:20 +08:00
} }
2022-03-08 10:29:00 +08:00
/ > ,
2020-10-24 20:33:18 +08:00
) ;
} ;
2023-01-08 21:30:41 +08:00
if ( process . env . NODE_ENV !== 'production' ) {
InternalSelect . displayName = 'Select' ;
}
2021-12-21 21:38:11 +08:00
const Select = React . forwardRef ( InternalSelect ) as unknown as ( <
ValueType = any ,
OptionType extends BaseOptionType | DefaultOptionType = DefaultOptionType ,
> (
props : React.PropsWithChildren < SelectProps < ValueType , OptionType > > & {
ref? : React.Ref < BaseSelectRef > ;
} ,
) = > React . ReactElement ) & {
2023-07-17 23:43:32 +08:00
displayName? : string ;
2020-10-24 20:33:18 +08:00
SECRET_COMBOBOX_MODE_DO_NOT_USE : string ;
Option : typeof Option ;
OptGroup : typeof OptGroup ;
2022-07-07 20:05:19 +08:00
_InternalPanelDoNotUseOrYouWillBeFired : typeof PurePanel ;
2021-12-21 21:38:11 +08:00
} ;
2020-10-24 20:33:18 +08:00
2022-07-07 20:05:19 +08:00
// We don't care debug panel
2023-06-07 21:59:21 +08:00
/* istanbul ignore next */
2022-07-07 20:05:19 +08:00
const PurePanel = genPurePanel ( Select ) ;
2020-10-24 20:33:18 +08:00
Select . SECRET_COMBOBOX_MODE_DO_NOT_USE = SECRET_COMBOBOX_MODE_DO_NOT_USE ;
Select . Option = Option ;
Select . OptGroup = OptGroup ;
2022-07-07 20:05:19 +08:00
Select . _InternalPanelDoNotUseOrYouWillBeFired = PurePanel ;
2020-10-24 20:33:18 +08:00
2023-07-17 23:43:32 +08:00
if ( process . env . NODE_ENV !== 'production' ) {
Select . displayName = 'Select' ;
}
2019-09-12 20:15:17 +08:00
export default Select ;