UNPKG

@6thquake/react-material

Version:

React components that implement Google's Material Design.

316 lines (283 loc) 6.82 kB
import React from 'react'; import PropTypes from 'prop-types'; import Popover from '../Popover'; import withStyles from '../styles/withStyles'; import TextField from '../TextField'; import CascadeOption from './CascadeOption'; import ArrowDropDown from '@material-ui/icons/ArrowDropDown'; import InputAdornment from '../InputAdornment'; export const styles = theme => ({ root: { display: 'flex', flexWrap: 'wrap', flexDirection: 'column' }, textField: { marginLeft: theme.spacing(1), marginRight: theme.spacing(1), minWidth: 200 }, arrowDown: { color: 'rgba(0, 0, 0, 0.54)' }, menuBox: { // width: 200, display: 'flex', flexDirection: 'row' }, noData: { padding: theme.spacing(1), minWidth: 200 } }); class CascadeSelect extends React.Component { constructor(props) { super(props); this.state = { open: false, opens: [true, false, false, false, false], data: [this.props.options], textFieldValue: '', checkes: [-1, -1, -1, -1, -1] }; this.handleChange = name => event => { this.setState({ [name]: event.target.value }); }; this.handleInputClick = e => { this.setState({ open: true, anchorEl: e.currentTarget }); }; this.handlePopOverClose = () => { this.setState({ open: false }); }; this.handelMenuChange = e => { const { level, next } = e; this.updateSeries(e); this.updateMenuData(next, level); this.updateNextMenu(e); this.setTextFieldValue(); this.checkChange(e); // 将信息传给父组件 this.props.onChange && this.props.onChange(this.series); }; this.updateSeries = e => { // 清空当前级之后的数据 并更新当前级数据 const { item, level, index } = e; this.series.splice(level); this.series[level] = item[index]; }; this.updateMenuData = (next, level) => { const opens = this.state.opens; for (let i = level; i < opens.length - 1; i++) { opens[i + 1] = false; } this.setState({ opens }); }; this.updateNextMenu = e => { const { level, next } = e; const data = next; if (this.state.opens[level + 1] === false && data.length > 0) { const opens = this.state.opens; opens[level + 1] = true; const items = this.state.data; items[level + 1] = data; this.setState({ opens, data: items }); } }; this.setTextFieldValue = () => { const { mapper } = this.props; const selections = this.series.map((item, index) => { let { label, value } = item; if (typeof mapper.label === 'function') { label = mapper.label(item, index); } return { value, label }; }); const node = this.renderSelections(selections); this.setState({ textFieldValue: node }); }; this.renderSelections = list => { const { separator, renderValue } = this.props; if (renderValue) { return renderValue(list); } return list.map(item => { return item.label; }).join(` ${separator} `); }; this.series = []; } checkChange(e) { const { level, index } = e; const checkes = this.state.checkes; for (let i = level; i < checkes.length - 1; i++) { checkes[i + 1] = -1; } checkes[level] = index; this.setState({ checkes }); } renderMenuItems() { const levels = [0, 1, 2, 3, 4]; const { children, mapper } = this.props; const list = levels.map((item, index) => { return React.createElement(CascadeOption, { children: children, key: item, level: item, open: this.state.opens[item], options: this.state.data[item], onChange: this.handelMenuChange, checkedIndex: this.state.checkes[item], mapper: mapper }); }); return list; } render() { const { classes, label, options, notFound, width } = this.props; const { open, anchorEl } = this.state; const hasData = options && options.length > 0; const t = React.createElement("div", { className: classes.noData }, notFound); return React.createElement("div", { className: classes.root }, React.createElement("div", { className: classes.inputBox }, React.createElement(TextField, { InputProps: { endAdornment: React.createElement(InputAdornment, { position: "end" }, React.createElement(ArrowDropDown, { className: classes.arrowDown })) }, style: { width }, onClick: this.handleInputClick, id: "select", label: label, className: classes.textField, value: this.state.textFieldValue, onChange: this.handleChange(), margin: "normal" })), React.createElement(Popover, { anchorEl: anchorEl, anchorOrigin: { vertical: 'bottom', horizontal: 'left' }, transformOrigin: { vertical: 'top', horizontal: 'left' }, open: open, onClose: this.handlePopOverClose }, React.createElement("div", { className: classes.menuBox }, hasData ? this.renderMenuItems() : t))); } } process.env.NODE_ENV !== "production" ? CascadeSelect.propTypes = { /** * Override or extend the styles applied to the component. * See [CSS API](#css-api) below for more details. */ classes: PropTypes.object.isRequired, /** * The label content. */ label: PropTypes.node, /** * render maps, */ mapper: PropTypes.shape({ label: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), value: PropTypes.oneOfType([PropTypes.string, PropTypes.func]) }), /** * Callback when finishing cascader select */ onChange: PropTypes.func, /** * data options of cascade */ options: PropTypes.array.isRequired, /** * Render the selected item. * * @param {array} list The selected items `array`. * @returns {String} */ renderValue: PropTypes.func, /** * The separator between different levels */ separator: PropTypes.string, /** * The width of cascader */ width: PropTypes.number } : void 0; CascadeSelect.defaultProps = { mapper: { label: 'label', value: 'value' }, width: 150, separator: '/' }; export default withStyles(styles, { name: 'RMCascadeSelect' })(CascadeSelect);