UNPKG

@6thquake/react-material

Version:

React components that implement Google's Material Design.

369 lines (321 loc) 9.61 kB
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);