@6thquake/react-material
Version:
React components that implement Google's Material Design.
369 lines (321 loc) • 9.61 kB
JavaScript
import _extends from "@babel/runtime/helpers/extends";
/**
* @ignore - do not document.
*/
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import withStyles from '../styles/withStyles';
import TextField from '../TextField';
import Paper from '../Paper';
import Pagination from '../Pagination';
import Divider from '../Divider';
import MenuItem from '../MenuItem';
import { debounce } from '../utils/throttle';
const styles = theme => ({
root: {
flexGrow: 1
},
container: {
flexGrow: 1,
position: 'relative'
},
textarea: {
width: '100%'
},
modal: {
position: 'fixed',
top: 0,
right: 0,
bottom: 0,
left: 0,
overflow: 'hidden',
outline: 0,
backgroundColor: 'rgb(0, 0, 0)',
opacity: 0,
zIndex: 1
},
paper: {
position: 'absolute',
zIndex: 200,
marginTop: theme.spacing(1),
left: 0,
right: 0,
padding: '10px'
},
chip: {
margin: `${theme.spacing(0.5)}px ${theme.spacing(0.25)}px`
},
inputRoot: {
flexGrow: 1,
flexWrap: 'wrap'
},
inputHold: {
padding: '10px 0 7px'
},
notFound: {
padding: '10px',
color: 'rgba(0,0,0,0.5)'
}
});
var _ref = React.createElement(Divider, null);
class Mention extends Component {
constructor(props) {
super();
this.handleChange = e => {
let trigger = null;
if (this.state.open === false) {
// 可选框未打开时打开可选框
if (e.target.value.indexOf(' ') === -1 && this.props.prefix.indexOf(e.target.value.charAt(0)) > -1) {
// 标志符在开头
trigger = e.target.value[0];
this.setState({
open: true,
triggerOption: trigger
});
this.props.onSearchChange(e.target.value.slice(1, e.target.value.length), trigger); // 让外部传入可选的项目
} else if (e.target.value.indexOf(' ') > -1) {
// 标志符在中间但是前面有空格
const stringForComplete = e.target.value.split(' ').pop();
if (this.props.prefix.indexOf(stringForComplete[0]) > -1) {
trigger = stringForComplete[0];
}
if (trigger) {
this.setState({
open: true,
triggerOption: trigger
});
this.props.onSearchChange(stringForComplete.slice(1, stringForComplete.length), trigger); // 让外部传入可选的项目
}
}
} else if (e.target.value) {
// 可选框打开时是过滤字符串的功能
const targetOptionIndex = e.target.value.lastIndexOf(this.state.triggerOption);
const searchLength = e.target.value.length;
const filterOption = e.target.value.slice(targetOptionIndex + 1, searchLength);
this.props.onSearchChange(filterOption, this.state.triggerOption);
}
this.setState({
inputValue: e.target.value
});
this.props.onChange(e.target.value); // 用来回显
};
this.handleItemClick = item => e => {
const position = this.state.inputValue.lastIndexOf(this.state.triggerOption); // 找到触发符号的位置
const newValue = this.state.inputValue.slice(0, position + 1) + item; // 把选中的item接到后面
const newSelectedItem = [...this.state.selectedItem, item]; // 所有的选中值
this.setState({
open: false,
inputValue: newValue,
selectedItem: newSelectedItem
});
this.props.onSelect(newSelectedItem); // 回传选中值
this.props.onChange(newValue); // 回传输入值
};
this.state = {
open: false,
inputValue: props.defaultValue,
selectedItem: props.selected,
triggerOption: '',
pageConfig: {
page: props.pageConfig.page || 0,
rowsPerPage: props.pageConfig.rowsPerPage || 5,
count: props.pageConfig.count || 5
}
};
}
handleBlur(e) {
this.setState({
open: false
});
}
pageCallbackFn(i) {
// 切换页面的函数
this.setState({
pageConfig: _extends({}, this.state.pageConfig, {
page: i
})
});
}
static getDerivedStateFromProps(newProps, prevState) {
// 父组件改変分页参数时state要跟着变
if (newProps !== prevState.preProps) {
return {
pageConfig: _extends({}, prevState.pageConfig, {
count: newProps.pageConfig.count
}),
prevProps: newProps
};
}
return null;
}
render() {
const {
classes,
placeholder,
children,
dataSource,
disabled,
showError,
pageConfig,
debounceProps
} = this.props;
const {
count,
page,
rowsPerPage
} = this.state.pageConfig;
const {
open,
inputValue,
selectedItem
} = this.state;
let items;
let showPagination = false;
let noItemPatterned = false;
noItemPatterned = dataSource && dataSource.length === 0 || !dataSource && !React.children;
if (pageConfig) {
showPagination = true;
}
if (dataSource) {
// 通过dataSource传参的情况
items = dataSource ? dataSource.map(item => {
let selected = false;
if (typeof item === 'string') {
if (!Array.isArray(selectedItem)) {
throw new Error('React-Material: the `value` property must be an array ' + 'when using the `AutoComplete` component with `multiple`.');
}
selected = selectedItem.indexOf(item) !== -1;
return React.createElement(MenuItem, {
key: item,
value: item,
selected: selected,
onClick: this.handleItemClick(item)
}, item);
}
throw new Error('AutoComplete[dataSource] only supports type `string[] | Object[]`.');
}) : [];
} else if (React.Children) {
// 通过child传参的情况
items = React.Children ? React.Children.map(children, child => {
if (!React.isValidElement(child)) {
return null;
}
let selected = false;
if (!Array.isArray(selectedItem)) {
throw new Error('React-Material: the `value` property must be an array ' + 'when using the `AutoComplete` component with `multiple`.');
}
selected = selectedItem.indexOf(child.props.value) !== -1;
return React.cloneElement(child, {
onClick: this.handleItemClick(child.props.value),
role: 'option',
selected,
value: undefined,
// The value is most likely not a valid HTML attribute.
'data-value': child.props.value // Instead, we provide it as a data attribute.
});
}) : [];
}
return React.createElement("div", {
className: classes.root
}, open ? React.createElement("div", {
onClick: this.handleBlur.bind(this),
className: classes.modal
}) : null, React.createElement("div", {
className: classes.container
}, React.createElement(TextField, {
disabled: disabled,
className: classes.textarea,
onChange: debounce(this.handleChange, debounceProps.wait).bind(this),
value: inputValue,
placeholder: placeholder,
error: showError
}), open && !noItemPatterned && React.createElement(Paper, {
className: classes.paper,
square: true
}, items.slice(count === 0 ? count : page * rowsPerPage, (page + 1) * rowsPerPage > count ? count : (page + 1) * rowsPerPage), _ref, showPagination && React.createElement(Pagination, _extends({}, this.state.pageConfig, {
onChangePage: this.pageCallbackFn.bind(this)
}))), open && noItemPatterned && React.createElement(Paper, {
className: classes.notFound
}, 'no patterned result, press space to end')));
}
}
process.env.NODE_ENV !== "production" ? Mention.propTypes = {
/**
* set the option to be selected
*/
dataSource: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
/**
* debounce props
*/
debounceProps: PropTypes.shape({
wait: PropTypes.number
}),
/**
* default value
*/
defaultValue: PropTypes.string,
/**
* should component disabled
*/
disabled: PropTypes.bool,
// inputChangeCb: PropTypes.func.isRequired,
/**
* Callback when input @ content changes, parameters are (value,trigger)
*/
onChange: PropTypes.func.isRequired,
/**
* Callback when input content changes
*/
onSearchChange: PropTypes.func.isRequired,
/**
* callback when select value change
*/
onSelect: PropTypes.func.isRequired,
/**
* pagination config
*/
pageConfig: PropTypes.shape({
count: PropTypes.number,
page: PropTypes.number,
rowsPerPage: PropTypes.number
}),
/**
* text palcehold
*/
placeholder: PropTypes.string,
/**
* set char of trigger
*/
prefix: PropTypes.oneOfType([PropTypes.string, PropTypes.array]),
/**
* set if the text can only be read
*/
readOnly: PropTypes.bool,
/**
* select value,
*/
selected: PropTypes.array,
/**
* show error
*/
showError: PropTypes.bool // triggerOptions: PropTypes.oneOfType([PropTypes.string, PropTypes.array]),
} : void 0;
Mention.defaultProps = {
onSearchChange() {},
onChange() {},
onSelect() {},
disabled: false,
prefix: ['@'],
// 默认的触发符号
placeholder: 'please input something',
readOnly: false,
defaultValue: '',
selected: [],
// 初始化传入的item
debounceProps: {
wait: 1000
}
};
export default withStyles(styles, {
name: 'RMMention'
})(Mention);