chowa
Version:
UI component library based on React
358 lines (357 loc) • 16 kB
JavaScript
/**
* @license chowa v1.1.3
*
* Copyright (c) Chowa Techonlogies Co.,Ltd.(http://www.chowa.cn).
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const React = require("react");
const PropTypes = require("prop-types");
const classnames_1 = require("classnames");
const receiver_1 = require("../i18n/receiver");
const utils_1 = require("../utils/");
const tool_1 = require("./tool");
const cascader_card_1 = require("./cascader-card");
const cascader_list_1 = require("./cascader-list");
const cascader_not_found_1 = require("./cascader-not-found");
const cascader_filter_options_1 = require("./cascader-filter-options");
const dropdown_1 = require("../dropdown");
const icon_1 = require("../icon");
class Cascader extends React.PureComponent {
constructor(props) {
super(props);
const { options, defaultValue, value } = props;
this.state = {
renderOptions: [].concat(props.options),
searchValue: '',
searching: false,
selectorVisible: props.visible || props.defaultVisible,
selectedOptions: tool_1.compileSelectedOptions(value || defaultValue, options),
showClear: false,
optionsTier: tool_1.computedOptionsTier(options),
activeOption: undefined,
activeFilterOptions: undefined
};
[
'onVisibleChange',
'onSelectHandler',
'onSearchChangeHandler',
'onSearchBlurHandler',
'onSearchFocusHandler',
'onTriggerMouseEnterHandler',
'onTriggerMouseLeaveHandler',
'clearSelectedValues',
'onKeyboardOperation',
'onOptionMouseEnter',
'onOptionMouseLeave',
'onFilterOptionMouseEnter',
'onFilterOptionMouseLeave',
'onFilterSelectHandler'
].forEach((fn) => {
this[fn] = this[fn].bind(this);
});
}
componentDidUpdate(preProps, preState) {
if (!utils_1.isEqual(preProps.value, this.props.value)) {
this.setState({
selectedOptions: tool_1.compileSelectedOptions(this.props.value, this.state.renderOptions)
});
}
if (!utils_1.isEqual(preProps.options, this.props.options)) {
this.setState({
optionsTier: tool_1.computedOptionsTier(this.props.options),
renderOptions: tool_1.filterOptions(this.props.options, this.state.searchValue, this.props.onFilter)
});
}
if (this.props.searchable
&& this.state.selectorVisible
&& (preState.selectorVisible !== this.state.selectorVisible
|| !utils_1.isEqual(preState.selectedOptions, this.state.selectedOptions))) {
this.inputElement.focus();
}
if (preProps.visible !== this.props.visible && this.props.visible !== this.state.selectorVisible) {
this.setState({
selectorVisible: this.props.visible
});
}
}
clearSelectedValues(e) {
this.setState({
selectedOptions: []
}, () => {
if (this.props.onChange) {
this.props.onChange([]);
}
});
utils_1.stopReactPropagation(e);
}
onOptionMouseEnter(option) {
this.setState({ activeOption: option });
}
onOptionMouseLeave() {
this.setState({ activeOption: undefined });
}
onFilterOptionMouseEnter(spread) {
this.setState({ activeFilterOptions: spread });
}
onFilterOptionMouseLeave() {
this.setState({ activeFilterOptions: undefined });
}
onTriggerMouseEnterHandler() {
this.setState({ showClear: true });
}
onTriggerMouseLeaveHandler() {
this.setState({ showClear: false });
}
onKeyboardOperation(e) {
const { selectorVisible, activeOption, selectedOptions, searchValue, optionsTier, activeFilterOptions, renderOptions } = this.state;
if (e.keyCode === 9) {
return this.setState({ selectorVisible: false });
}
if (!selectorVisible) {
if (e.keyCode === 13 || e.keyCode === 40) {
this.setState({ selectorVisible: true });
e.preventDefault();
}
}
else {
switch (e.keyCode) {
case 38:
if (!searchValue) {
this.setState({
activeOption: tool_1.getPreOption(selectedOptions, renderOptions, optionsTier, activeOption)
});
}
else {
this.setState({
activeFilterOptions: tool_1.getPreFilterOption(renderOptions, activeFilterOptions)
});
}
e.preventDefault();
break;
case 40:
if (!searchValue) {
this.setState({
activeOption: tool_1.getNextOption(selectedOptions, renderOptions, optionsTier, activeOption)
});
}
else {
this.setState({
activeFilterOptions: tool_1.getNextFilterOption(renderOptions, activeFilterOptions)
});
}
e.preventDefault();
break;
case 13:
case 39:
if (!searchValue) {
if (utils_1.isExist(activeOption)) {
const updateTier = optionsTier < selectedOptions.length + 1
? optionsTier
: selectedOptions.length + 1;
this.setState({ activeOption: undefined });
this.onSelectHandler(activeOption, updateTier);
}
}
else {
if (utils_1.isExist(activeFilterOptions)) {
this.onFilterSelectHandler(activeFilterOptions);
}
}
e.preventDefault();
break;
case 37:
if (!searchValue && selectedOptions.length > 0) {
this.setState({ activeOption: undefined });
this.onSelectHandler(undefined, selectedOptions.length);
}
e.preventDefault();
break;
case 27:
this.setState({ selectorVisible: false });
e.preventDefault();
break;
}
}
utils_1.stopReactPropagation(e);
}
onSearchChangeHandler(e) {
if (this.props.onSearch) {
this.props.onSearch(e.target.value);
}
this.setState({
searchValue: e.target.value,
renderOptions: tool_1.filterOptions(this.props.options, e.target.value, this.props.onFilter)
});
utils_1.stopReactPropagation(e);
}
onSearchFocusHandler(e) {
this.setState({ searching: true });
utils_1.stopReactPropagation(e);
}
onSearchBlurHandler(e) {
this.setState({ searching: false });
utils_1.stopReactPropagation(e);
}
onVisibleChange(v) {
this.setState({
selectorVisible: v,
searchValue: '',
renderOptions: this.props.options
});
}
onSelectHandler(option, tier) {
const { changeOnSelect, onChange } = this.props;
const { optionsTier } = this.state;
const selectedOptions = [].concat(this.state.selectedOptions);
if (tier <= selectedOptions.length) {
selectedOptions.splice(tier - 1, selectedOptions.length + 1 - tier);
}
if (utils_1.isExist(option)) {
selectedOptions.push(option);
}
this.setState({
selectedOptions
}, () => {
if (onChange) {
const values = selectedOptions.map((item) => item.value);
if (changeOnSelect) {
onChange(values);
}
else if (selectedOptions.length === this.state.optionsTier) {
onChange(values);
}
}
});
if (optionsTier === selectedOptions.length) {
this.setState({ selectorVisible: false });
}
}
onFilterSelectHandler(spread) {
this.setState({
selectedOptions: spread
}, () => {
if (this.props.onChange) {
const values = spread.map((item) => item.value);
this.props.onChange(values);
}
});
this.setState({ selectorVisible: false });
}
renderDrop(i18nNoDataDescription) {
const { mode, noDataDescription, noDataImg, noDataImgStyle, optionTitles, separator } = this.props;
const { searchValue, selectedOptions, optionsTier, activeOption, renderOptions, activeFilterOptions } = this.state;
const selectorBaseProps = {
options: renderOptions,
selectedOptions,
activeOption,
onSelect: this.onSelectHandler,
onKeyDown: this.onKeyboardOperation,
onOptionMouseEnter: this.onOptionMouseEnter,
onOptionMouseLeave: this.onOptionMouseLeave
};
return (React.createElement("div", { className: utils_1.preClass('cascader-selector-wrapper') },
renderOptions.length === 0 && searchValue &&
React.createElement(cascader_not_found_1.default, { noDataImgStyle: noDataImgStyle, noDataImg: noDataImg, noDataDescription: utils_1.isExist(noDataDescription) ? noDataDescription : i18nNoDataDescription }),
renderOptions.length > 0 && searchValue &&
React.createElement(cascader_filter_options_1.default, { options: renderOptions, activeOptions: activeFilterOptions, selectedOptions: selectedOptions, onOptionMouseEnter: this.onFilterOptionMouseEnter, onOptionMouseLeave: this.onFilterOptionMouseLeave, onSelect: this.onFilterSelectHandler, onKeyDown: this.onKeyboardOperation, separator: separator }),
mode === 'list' && !searchValue &&
React.createElement(cascader_list_1.default, Object.assign({}, selectorBaseProps)),
mode === 'card' && !searchValue &&
React.createElement(cascader_card_1.default, Object.assign({ optionsTier: optionsTier }, selectorBaseProps, { optionTitles: optionTitles }))));
}
render() {
const { className, style, showArrow, disabled, externalWheelHide, clearable, tabIndex, changeOnSelect, formatter, separator, searchable, placeholder } = this.props;
const { selectorVisible, selectedOptions, showClear, optionsTier, searching, searchValue } = this.state;
const amount = selectedOptions.length;
const componentClass = classnames_1.default({
[utils_1.preClass('cascader')]: true,
[utils_1.preClass('cascader-disabled')]: disabled,
[utils_1.preClass('cascader-focused')]: selectorVisible,
[className]: utils_1.isExist(className)
});
const arrowClass = classnames_1.default({
[utils_1.preClass('cascader-arrow')]: true,
[utils_1.preClass('cascader-arrow-active')]: selectorVisible
});
const innerClass = classnames_1.default({
[utils_1.preClass('cascader-selection')]: true,
[utils_1.preClass('cascader-searching')]: searching
});
const searchInputStyle = {
display: (selectorVisible ? 'block' : 'none')
};
return (React.createElement(dropdown_1.default, { trigger: 'click', content: (React.createElement(receiver_1.default, { module: 'Cascader' }, (i18n) => this.renderDrop(i18n.noDataDescription))), disabled: disabled, visible: selectorVisible, externalWheelHide: externalWheelHide, onVisibleChange: this.onVisibleChange },
React.createElement("div", { onMouseEnter: clearable ? this.onTriggerMouseEnterHandler : null, onMouseLeave: clearable ? this.onTriggerMouseLeaveHandler : null, onKeyDown: disabled ? null : this.onKeyboardOperation, className: componentClass, style: style, tabIndex: disabled ? -1 : tabIndex },
React.createElement("div", { className: innerClass },
(!searchValue &&
((amount === 0 && changeOnSelect) || (amount !== optionsTier && !changeOnSelect))) &&
React.createElement("span", { key: 'placeholder', className: utils_1.preClass('cascader-placeholder') },
utils_1.isExist(placeholder) && placeholder,
!utils_1.isExist(placeholder) &&
React.createElement(receiver_1.default, { module: 'Cascader' }, (i18n) => i18n.placeholder)),
!searchValue &&
React.createElement("span", null,
((amount === optionsTier && !changeOnSelect) ||
(amount > 0 && changeOnSelect)) && !utils_1.isExist(formatter) &&
selectedOptions.map((option) => option.value).join(` ${separator} `),
((amount === optionsTier && !changeOnSelect) ||
(amount > 0 && changeOnSelect)) && utils_1.isExist(formatter) &&
formatter(selectedOptions)),
searchable &&
React.createElement("input", { placeholder: '', type: 'text', value: searchValue, style: searchInputStyle, className: utils_1.preClass('cascader-search-input'), onFocus: this.onSearchFocusHandler, onChange: this.onSearchChangeHandler, onBlur: this.onSearchBlurHandler, onKeyDown: this.onKeyboardOperation, ref: (ele) => {
this.inputElement = ele;
} })),
showArrow &&
React.createElement("span", { className: arrowClass },
React.createElement(icon_1.default, { type: 'arrow-down' })),
clearable &&
React.createElement(utils_1.ClearButton, { visible: showClear && (changeOnSelect
? amount > 0
: amount === optionsTier), onClick: this.clearSelectedValues }))));
}
}
Cascader.propTypes = {
className: PropTypes.string,
style: PropTypes.object,
visible: PropTypes.bool,
defaultVisible: PropTypes.bool,
externalWheelHide: PropTypes.bool,
tabIndex: PropTypes.number,
options: PropTypes.array.isRequired,
optionTitles: PropTypes.array,
onChange: PropTypes.func,
changeOnSelect: PropTypes.bool,
defaultValue: PropTypes.array,
value: PropTypes.array,
noDataDescription: PropTypes.node,
noDataImg: PropTypes.string,
noDataImgStyle: PropTypes.object,
mode: PropTypes.oneOf(['list', 'card']),
placeholder: PropTypes.string,
showArrow: PropTypes.bool,
disabled: PropTypes.bool,
searchable: PropTypes.bool,
formatter: PropTypes.func,
separator: PropTypes.node,
onSearch: PropTypes.func,
onFilter: PropTypes.func,
clearable: PropTypes.bool
};
Cascader.defaultProps = {
visible: false,
defaultVisible: false,
externalWheelHide: true,
tabIndex: 0,
mode: 'list',
changeOnSelect: false,
searchable: false,
showArrow: true,
disabled: false,
separator: '/',
clearable: false
};
exports.default = Cascader;