簡單介紹一下 AntD Select 的用法
mode: undefined, tag, multiple
- undefine: 默認值,就是dropdown,
- tag: 跟multple 唯一的區別就是,輸入的值不在list 內,敲回車,自動插入.
- multiple, 就是多值選擇。
一些約定
- showSearch: 看這段代碼
const mergedShowSearch = showSearch !== undefined ? showSearch : isMultiple || mode === 'combobox';
結論: 如果顯示設置,優先級最高,隱含的其次,也就是 tags, multiple, combobox 也是可以showSearch.- 類似的開關還有很多,例如: showArrow 在其他的組件中,這個約定也是類似的。當然這些開關只是第一層,開關,默認有這個開關就可以打開,但是也有一些相關的屬性進一步控制這個開關,例如 filterOption=false 就可以關閉search的功能,這個在SearchBox 或者Email 這種組件中使用。例如以下代碼,盡管,我們打開了showSearch 開關,但是我們通過filterOption={false}關閉了該功能,我們通過onSearchevent來刷新options。
const options = this.state.data.map(d => <Option key={d.value}>{d.text}</Option>);
<Select
showSearch
value={this.state.value}
placeholder={this.props.placeholder}
showArrow={false}
filterOption={false}
onSearch={this.handleSearch}
onChange={this.handleChange}
notFoundContent={null}
>
{options}
</Select>
談談 Select 里面的animation.
首先,我們可以通過transitionName 參數來提供一個transtionName, 看這段代碼:transitionName={getTransitionName(rootPrefixCls, 'slide-up', props.transitionName)}
, 如果不提供,默認就是slide-up 這個transitionName,
第一個問題,這個transtionName 有神馬用途,或者,用它會發生什么,首先它會傳遞個CSSMotion ,如果有同學對CSSMotion 不理解,參考CSSMotion.
簡單說一下,下來菜單在展開或者關閉的過程,className 會如此變化。
- 展開
- .ant-slide-up-enter(Dom 已經存在) 或者 .ant-slide-up-appear(Dom 剛剛創建)
- .ant-slide-up-enter-(prepare/start/active) 依次發生變化, 一般我們只關心start 與active
- 最后動畫結束,與動畫相關的類名全部移除。
- 關閉
- .ant-slide-leave
- .ant-slide-up-leave-(parepare/start/active) 依次發生,同樣,我們只關心start 跟active
- 最后動畫結束,與動畫相關的類名全部移除,附加上ant-select-dropdown-hidden 類.
下面是slide-up 相關的css ,變化的就是opacity 跟 transform 兩個屬性
.motion-common(@duration: @animation-duration-base) {
animation-duration: @duration;
animation-fill-mode: both;
}
.motion-common-leave(@duration: @animation-duration-base) {
animation-duration: @duration;
animation-fill-mode: both;
}
.make-motion(@className, @keyframeName, @duration: @animation-duration-base) {
.@{className}-enter,
.@{className}-appear {
.motion-common(@duration);
animation-play-state: paused;
}
.@{className}-leave {
.motion-common-leave(@duration);
animation-play-state: paused;
}
.@{className}-enter.@{className}-enter-active,
.@{className}-appear.@{className}-appear-active {
animation-name: ~'@{keyframeName}In';
animation-play-state: running;
}
.@{className}-leave.@{className}-leave-active {
animation-name: ~'@{keyframeName}Out';
animation-play-state: running;
pointer-events: none;
}
}
.slide-motion(@className, @keyframeName) {
@name: ~'@{ant-prefix}-@{className}';
.make-motion(@name, @keyframeName);
.@{name}-enter,
.@{name}-appear {
opacity: 0;
animation-timing-function: @ease-out-quint;
}
.@{name}-leave {
animation-timing-function: @ease-in-quint;
}
}
.slide-motion(slide-up, antSlideUp);
@keyframes antSlideUpIn {
0% {
transform: scaleY(0.8);
transform-origin: 0% 0%;
opacity: 0;
}
100% {
transform: scaleY(1);
transform-origin: 0% 0%;
opacity: 1;
}
}
談談Select 里面的Option OptionGroup
首先,Rc-Select 里面的Option 類型是 export type OptionsType = (OptionData | OptionGroupData)[];
我們來看一下代碼片段。
export interface OptionCoreData {
key?: Key;
disabled?: boolean;
value: Key;
title?: string;
className?: string;
style?: React.CSSProperties;
label?: React.ReactNode;
/** @deprecated Only works when use `children` as option data */
children?: React.ReactNode;
}
export interface OptionData extends OptionCoreData {
/** Save for customize data */
[prop: string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any
}
export interface OptionGroupData {
key?: Key;
label?: React.ReactNode;
options: OptionData[];
className?: string;
style?: React.CSSProperties;
/** Save for customize data */
[prop: string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any
}
export type OptionsType = (OptionData | OptionGroupData)[];
同時Select 暴露出了Option, OptGroup
來用於提供Options, 常用於AntD Select
. 看一下OptGroup類型定義. 它沒有Options 屬性。Options 直接來源於children.
export interface OptGroupProps extends Omit<OptionGroupData, 'options'> {
children?: React.ReactNode;
}
export interface OptionGroupFC extends React.FC<OptGroupProps> {
/** Legacy for check if is a Option Group */
isSelectOptGroup: boolean;
}
const OptGroup: OptionGroupFC = () => null;
OptGroup.isSelectOptGroup = true;
export default OptGroup;
這個是Option的定義,它沒有label這個屬性,label 直接來源於children.
export interface OptionProps extends Omit<OptionCoreData, 'label'> {
children: React.ReactNode;
/** Save for customize data */
[prop: string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any
}
export interface OptionFC extends React.FC<OptionProps> {
/** Legacy for check if is a Option Group */
isSelectOption: boolean;
}
/** This is a placeholder, not real render in dom */
const Option: OptionFC = () => null;
Option.isSelectOption = true;
export default Option;
對於AutoComplete 這個組件,它也是基於Select 來做的,它的Options 是通過Options這個屬性來提供的,它的代碼片段如下
export interface DataSourceItemObject {
value: string;
text: string;
}
export type DataSourceItemType = DataSourceItemObject | React.ReactNode;
export interface AutoCompleteProps
extends Omit<
InternalSelectProps<string>,
'inputIcon' | 'loading' | 'mode' | 'optionLabelProp' | 'labelInValue'
> {
dataSource?: DataSourceItemType[];
}
那么對於分組這種情況,它的Options 應該張這樣
[
{label:xxx,options:[xxx,xxx,...]},
{label:xxx,options:[xxx,xxx,...]},
{label:xxx,options:[xxx,xxx,...]},
{label:xxx,options:[xxx,xxx,...]},
]
對於未分組的情況,它的Options 應該長這樣
[
{label:xx,value:xxx,key:xxx},
{label:xx,value:xxx,key:xxx},
{label:xx,value:xxx,key:xxx},
{label:xx,value:xxx,key:xxx},
...
]
在Rc-Select/utils/valueUtil.tsx 里面有這個函數flattenOptions 來解析以上這些數據結構.
export function flattenOptions(options: SelectOptionsType): FlattenOptionData[] {
const flattenList: FlattenOptionData[] = [];
function dig(list: SelectOptionsType, isGroupOption: boolean) {
list.forEach((data) => {
if (isGroupOption || !('options' in data)) {
// Option
flattenList.push({
key: getKey(data, flattenList.length),
groupOption: isGroupOption,
data,
});
} else {
// Option Group
flattenList.push({
key: getKey(data, flattenList.length),
group: true,
data,
});
dig(data.options, true);
}
});
}
dig(options, false);
return flattenList;
}