UNPKG

@6thquake/react-material

Version:

React components that implement Google's Material Design.

448 lines (389 loc) 11 kB
import _extends from "@babel/runtime/helpers/extends"; /** * @ignore - do not document. */ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import TextField from '../TextField'; import Paper from '../Paper'; import Chip from '../Chip'; import Pagination from '../Pagination'; import Divider from '../Divider'; import MenuItem from '../MenuItem'; import { debounce } from '../utils/throttle'; import withStyles from '../styles/withStyles'; 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' } }); var _ref = React.createElement(Divider, null); class AsyncAutoComplete extends Component { constructor(props) { super(props); this.handleDelete = item => event => { if (this.props.disabled) { return; } const value = [...this.props.value]; value.splice(value.indexOf(item), 1); let target; if (event.target) { target = event.target; } event.persist(); event.target = _extends({}, target, { value }); this.props.onChange(event); }; this.handleItemClick = child => event => { this.setState({ inputValue: '' }); if (!this.props.multiple) { this.setState({ open: false }); } const { onChange } = this.props; if (onChange) { let value; let target; if (event.target) { target = event.target; } const selecttext = child ? child.props.value : event.target.textContent; if (this.props.multiple) { value = Array.isArray(this.props.value) ? [...this.props.value] : []; const itemIndex = value.indexOf(selecttext); if (itemIndex === -1) { value.push(selecttext); } else { value.splice(itemIndex, 1); } } else { value = selecttext; this.setState({ inputValue: value }); } event.persist(); event.target = _extends({}, target, { value }); onChange(event, child); } }; this.handleBlur = event => { if (this.props.onBlur) { this.props.onBlur(event); } else { const { onChange } = this.props; if (onChange) { let value; let target; if (event.target) { target = event.target; } const selecttext = this.state.inputValue; if (this.props.multiple) { value = Array.isArray(this.props.value) ? [...this.props.value] : []; const itemIndex = value.indexOf(selecttext); if (itemIndex === -1) { if (selecttext) { value.push(selecttext); } } this.setState({ open: false, inputValue: '' }); } else { value = selecttext; this.setState({ open: false, inputValue: value }); } event.persist(); event.target = _extends({}, target, { value }); onChange(event); } } }; this.throttlingtem = debounce(props.onChangeInput, props.debounceProps.wait); this.state = { open: false, inputValue: '', selectedItem: [], page: 0 }; } componentDidMount() { if (!this.props.multiple) { this.setState({ inputValue: this.props.value }); } } onFocus(e) { if (this.props.select) { this.setState({ open: true }); } } handleChange(event) { this.setState({ open: true, inputValue: event.target.value }); if (this.props.debounceAble) { event.persist(); this.throttlingtem(event); } else { this.props.onChangeInput(event); } } render() { const { PaginationProps, onChangePage, classes, placeholder, children, multiple, value, options, mapper, disabled } = this.props; const { open, inputValue } = this.state; let items; if (options) { items = options ? options.map((item, index) => { let selected = false; switch (typeof item) { case 'string': if (multiple) { if (!Array.isArray(value)) { throw new Error('React-Material: the `value` property must be an array ' + 'when using the `AsyncAutoComplete` component with `multiple`.'); } selected = value.indexOf(item) !== -1; } else { selected = value === item; } return React.createElement(MenuItem, { key: index, value: item, selected: selected, onClick: this.handleItemClick(null) }, item); case 'object': if (multiple) { if (!Array.isArray(value)) { throw new Error('React-Material: the `value` property must be an array ' + 'when using the `AsyncAutoComplete` component with `multiple`.'); } selected = value.indexOf(typeof mapper.value === 'function' ? mapper.value(item, index) : item[mapper.value]) !== -1; } else { selected = value === (typeof mapper.value === 'function' ? mapper.value(item, index) : item[mapper.value]); } return React.createElement(MenuItem, { key: index, value: item[mapper.value], selected: selected, onClick: this.handleItemClick(null) }, typeof mapper.label === 'function' ? mapper.label(item, index) : item[mapper.label]); default: throw new Error('AsyncAutoComplete[options] only supports type `string[] | Object[]`.'); } }) : []; } else { items = React.Children.map(children, child => { if (!React.isValidElement(child)) { return null; } let selected = false; if (multiple) { if (!Array.isArray(value)) { throw new Error('React-Material: the `value` property must be an array ' + 'when using the `AsyncAutoComplete` component with `multiple`.'); } selected = value.indexOf(child.props.value) !== -1; } else { selected = value === child.props.value; } return React.cloneElement(child, { onClick: this.handleItemClick(child), 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, className: classes.modal }) : null, React.createElement("div", { className: classes.container }, multiple ? React.createElement(TextField, { disabled: disabled, className: classes.textarea, onChange: this.handleChange.bind(this), value: inputValue, multiline: true, onFocus: this.onFocus.bind(this), rows: "1", InputProps: { classes: { root: classes.inputRoot, input: classes.inputHold }, startAdornment: value.map(item => React.createElement(Chip, { key: item, label: item, className: classes.chip, onDelete: this.handleDelete(item) })), placeholder: value.length > 0 ? '' : placeholder } }) : React.createElement(TextField, { disabled: disabled, onFocus: this.onFocus.bind(this), className: classes.textarea, onChange: this.handleChange.bind(this), value: inputValue, placeholder: placeholder }), open ? React.createElement(Paper, { className: classes.paper, square: true }, items, _ref, React.createElement(Pagination, _extends({}, PaginationProps, { onChangePage: onChangePage }))) : null)); } } process.env.NODE_ENV !== "production" ? AsyncAutoComplete.propTypes = { /** * If true, autocomplete will add debounce when filter options by input value. */ debounceAble: PropTypes.bool, /** * If debounceAble true, config debounce wait and max continue time, the unit is milliseconds. */ debounceProps: PropTypes.shape({ wait: PropTypes.number }), /** * Decided autocomplete is disabled */ disabled: PropTypes.bool, /** * option item label and value, when assignment option by options */ mapper: PropTypes.shape({ label: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), value: PropTypes.oneOfType([PropTypes.string, PropTypes.func]) }), /** * Decided multiple select. If true, value must be an array and the menu will support multiple selections. */ multiple: PropTypes.bool, /** * Callback function fired when a menu item is selected. */ onChange: PropTypes.func.isRequired, /** * Callback fired when the input value is changed. */ onChangeInput: PropTypes.func, /** * Callback fired when the current page of pagination is changed. */ onChangePage: PropTypes.func, /** * autocomplete options, */ options: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.object, PropTypes.number])), /** * Pagination component config */ PaginationProps: PropTypes.object, /** * placeholder */ placeholder: PropTypes.string, /** * If true, autocomplete performance is like a select, when focus, option open. */ select: PropTypes.bool, /** * The value of the Input element, required for a controlled component. */ value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number]))]) } : void 0; AsyncAutoComplete.defaultProps = { PaginationProps: { page: 0, rowsPerPage: 5, count: 0 }, multiple: false, disabled: false, select: false, debounceProps: { wait: 1000 } }; export default withStyles(styles, { name: 'RMAsyncAutoComplete' })(AsyncAutoComplete);