ant-design/components/cascader/index.tsx

183 lines
5.4 KiB
TypeScript
Raw Normal View History

import * as React from 'react';
import RcCascader from 'rc-cascader';
2015-12-29 11:46:13 +08:00
import Input from '../input';
2015-12-29 21:18:27 +08:00
import Icon from '../icon';
2015-12-29 11:46:13 +08:00
import arrayTreeFilter from 'array-tree-filter';
2015-12-29 18:31:48 +08:00
import classNames from 'classnames';
2016-06-22 13:18:43 +08:00
import splitObject from '../_util/splitObject';
import omit from 'object.omit';
2015-12-29 11:46:13 +08:00
export interface CascaderOptionType {
2016-07-13 11:14:24 +08:00
value: string;
label: string;
disabled?: boolean;
children?: Array<CascaderOptionType>;
}
2016-07-07 20:25:03 +08:00
export type CascaderExpandTrigger = 'click' | 'hover'
export interface CascaderProps {
2016-07-13 11:14:24 +08:00
/** 可选项数据源 */
options: Array<CascaderOptionType>;
/** 默认的选中项 */
defaultValue?: Array<CascaderOptionType>;
/** 指定选中项 */
value?: Array<CascaderOptionType>;
/** 选择完成后的回调 */
2016-07-13 17:22:23 +08:00
onChange?: (value: string, selectedOptions?: Array<CascaderOptionType>) => void;
2016-07-13 11:14:24 +08:00
/** 选择后展示的渲染函数 */
2016-07-13 17:22:23 +08:00
displayRender?: (label: Array<string>, selectedOptions?: Array<CascaderOptionType>) => React.ReactNode;
2016-07-13 11:14:24 +08:00
/** 自定义样式 */
style?: React.CSSProperties;
/** 自定义类名 */
className?: string;
/** 自定义浮层类名 */
popupClassName?: string;
/** 浮层预设位置:`bottomLeft` `bottomRight` `topLeft` `topRight` */
2016-07-13 11:14:24 +08:00
popupPlacement?: string;
/** 输入框占位文本*/
2016-07-13 11:14:24 +08:00
placeholder?: string;
/** 输入框大小,可选 `large` `default` `small` */
2016-07-13 11:14:24 +08:00
size?: string;
/** 禁用*/
2016-07-13 11:14:24 +08:00
disabled?: boolean;
/** 是否支持清除*/
2016-07-13 11:14:24 +08:00
allowClear?: boolean;
/** 次级菜单的展开方式,可选 'click' 和 'hover' */
2016-07-13 11:14:24 +08:00
expandTrigger?: CascaderExpandTrigger;
/** 当此项为 true 时,点选每级菜单选项值都会发生变化 */
2016-07-13 11:14:24 +08:00
changeOnSelect?: boolean;
/** 浮层可见变化时回调 */
2016-07-13 11:14:24 +08:00
onPopupVisibleChange?: (popupVisible: boolean) => void;
}
export default class Cascader extends React.Component<CascaderProps, any> {
static defaultProps = {
prefixCls: 'ant-cascader',
2016-04-01 13:51:26 +08:00
placeholder: 'Please select',
transitionName: 'slide-up',
popupPlacement: 'bottomLeft',
onChange() {},
options: [],
2016-03-30 19:36:29 +08:00
displayRender: label => label.join(' / '),
disabled: false,
allowClear: true,
onPopupVisibleChange() {},
2016-07-13 11:14:24 +08:00
};
2015-12-29 11:46:13 +08:00
constructor(props) {
super(props);
2016-05-09 11:59:30 +08:00
let value;
if ('value' in props) {
value = props.value;
} else if ('defaultValue' in props) {
value = props.defaultValue;
}
2015-12-29 11:46:13 +08:00
this.state = {
2016-05-09 11:59:30 +08:00
value: value || [],
2015-12-29 21:18:27 +08:00
popupVisible: false,
2015-12-29 11:46:13 +08:00
};
}
2015-12-29 21:18:27 +08:00
componentWillReceiveProps(nextProps) {
if ('value' in nextProps) {
2016-01-06 11:45:47 +08:00
this.setState({ value: nextProps.value || [] });
2015-12-29 21:18:27 +08:00
}
}
handleChange = (value, selectedOptions) => {
2015-12-29 21:18:27 +08:00
this.setValue(value, selectedOptions);
}
handlePopupVisibleChange = (popupVisible) => {
2015-12-29 21:18:27 +08:00
this.setState({ popupVisible });
this.props.onPopupVisibleChange(popupVisible);
}
setValue = (value, selectedOptions = []) => {
2015-12-29 21:18:27 +08:00
if (!('value' in this.props)) {
this.setState({ value });
}
2015-12-29 11:46:13 +08:00
this.props.onChange(value, selectedOptions);
}
2015-12-29 11:46:13 +08:00
getLabel() {
const { options, displayRender } = this.props;
const selectedOptions = arrayTreeFilter(options, (o, level) => o.value === this.state.value[level]);
const label = selectedOptions.map(o => o.label);
return displayRender(label, selectedOptions);
2015-12-29 11:46:13 +08:00
}
clearSelection = (e) => {
2015-12-29 21:18:27 +08:00
e.preventDefault();
2015-12-29 22:34:23 +08:00
e.stopPropagation();
2015-12-29 21:18:27 +08:00
this.setValue([]);
this.setState({ popupVisible: false });
}
2015-12-29 11:46:13 +08:00
render() {
2016-04-14 11:22:53 +08:00
const props = this.props;
2016-07-13 11:14:24 +08:00
const [{ prefixCls, children, placeholder, size, disabled,
className, style, allowClear }, otherProps] = splitObject(props,
['prefixCls', 'children', 'placeholder', 'size', 'disabled', 'className', 'style', 'allowClear']);
2016-07-07 15:13:01 +08:00
2015-12-29 18:31:48 +08:00
const sizeCls = classNames({
'ant-input-lg': size === 'large',
'ant-input-sm': size === 'small',
});
const clearIcon = (allowClear && !disabled && this.state.value.length > 0) ?
2015-12-29 21:18:27 +08:00
<Icon type="cross-circle"
className={`${prefixCls}-picker-clear`}
onClick={this.clearSelection}
/> : null;
2015-12-29 22:34:23 +08:00
const arrowCls = classNames({
[`${prefixCls}-picker-arrow`]: true,
[`${prefixCls}-picker-arrow-expand`]: this.state.popupVisible,
});
2016-01-05 11:31:22 +08:00
const pickerCls = classNames({
[className]: !!className,
2016-01-05 11:31:22 +08:00
[`${prefixCls}-picker`]: true,
[`${prefixCls}-picker-disabled`]: disabled,
});
2016-03-03 14:57:26 +08:00
// Fix bug of https://github.com/facebook/react/pull/5004
// and https://fb.me/react-unknown-prop
const inputProps = omit(otherProps, [
'onChange',
'options',
'popupPlacement',
'transitionName',
'displayRender',
'onPopupVisibleChange',
'changeOnSelect',
'expandTrigger',
]);
2016-03-03 14:57:26 +08:00
2015-12-29 11:46:13 +08:00
return (
2016-04-14 11:22:53 +08:00
<RcCascader {...props}
2015-12-29 21:18:27 +08:00
value={this.state.value}
popupVisible={this.state.popupVisible}
onPopupVisibleChange={this.handlePopupVisibleChange}
onChange={this.handleChange}
>
2015-12-29 11:46:13 +08:00
{children ||
2016-01-04 19:23:19 +08:00
<span
2016-03-02 21:44:38 +08:00
style={style}
className={pickerCls}
>
<Input {...inputProps}
placeholder={this.state.value && this.state.value.length > 0 ? null : placeholder}
2016-07-15 12:46:49 +08:00
className={`${prefixCls}-input ${sizeCls}`}
value=""
2016-01-05 11:31:22 +08:00
disabled={disabled}
readOnly
/>
<span className={`${prefixCls}-picker-label`}>{this.getLabel()}</span>
2015-12-29 21:18:27 +08:00
{clearIcon}
2015-12-29 22:34:23 +08:00
<Icon type="down" className={arrowCls} />
2015-12-29 21:18:27 +08:00
</span>
}
</RcCascader>
2015-12-29 11:46:13 +08:00
);
}
}