简单介绍一下 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;
}