UNPKG

@6thquake/react-material

Version:

React components that implement Google's Material Design.

418 lines (382 loc) 10.8 kB
/** * @ignore - do not document. */ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import withStyles from '../styles/withStyles'; import Chip from '../Chip'; import isFunction from 'lodash/isFunction'; import classNames from 'classnames'; import { emphasize } from '../styles/colorManipulator'; import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import ExpandLessIcon from '@material-ui/icons/ExpandLess'; import IconButton from '../IconButton'; import Collapse from '../Collapse'; import { withLocale } from '../LocaleProvider'; // 颜色需要替换 const style = theme => { const transition = { duration: theme.transitions.duration.shortest }; return { root: { display: 'flex', flexWrap: 'nowrap', // padding: theme.spacing(1), padding: theme.spacing(1), display: 'flex' }, spacer: { flex: 1 }, scroll: { display: 'flex', overflowX: 'auto' }, right: {}, label: { fontFamily: theme.typography.fontFamily, fontSize: theme.typography.pxToRem(15), color: theme.palette.text.primary, textAlign: 'left', lineHeight: '48px' }, labelDefault: { color: theme.palette.common.white }, item: { color: theme.palette.text.primary, backgroundColor: 'transparent' }, colorDefault: { color: theme.palette.common.white, backgroundColor: theme.palette.primary.dark }, clickableColorDefault: { backgroundColor: theme.palette.primary.dark, color: theme.palette.getContrastText(theme.palette.primary.dark), '&:hover, &:focus': { backgroundColor: `${emphasize(theme.palette.primary.dark, 0.08)} !important` }, '&:active': { backgroundColor: `${emphasize(theme.palette.primary.dark, 0.12)} !important` } }, /* Styles applied to the root element if `color="primary"`. */ colorPrimary: { backgroundColor: theme.palette.primary.dark, color: theme.palette.primary.contrastText }, /* Styles applied to the root element if `color="secondary"`. */ colorSecondary: { backgroundColor: theme.palette.secondary.dark, color: theme.palette.primary.contrastText }, clickable: { WebkitTapHighlightColor: 'transparent', // Remove grey highlight cursor: 'pointer', '&:hover, &:focus': {// backgroundColor: emphasize(theme.palette.common.white, 0.08), }, '&:active': { boxShadow: theme.shadows[1], backgroundColor: emphasize(theme.palette.common.white, 0.12) } }, /** * Styles applied to the root element if * `onClick` and `color="primary"` is defined or `clickable={true}`. */ clickableColorPrimary: { '&:hover, &:focus': { backgroundColor: emphasize(theme.palette.primary.dark, 0.08) }, '&:active': { // backgroundColor: emphasize(theme.palette.primary.main, 0.12), backgroundColor: emphasize(theme.palette.primary.main, 0.08) } }, /** * Styles applied to the root element if * `onClick` and `color="secondary"` is defined or `clickable={true}`. */ clickableColorSecondary: { '&:hover, &:focus': { backgroundColor: emphasize(theme.palette.secondary.dark, 0.08) }, '&:active': { backgroundColor: emphasize(theme.palette.secondary.main, 0.12) } }, /* Styles applied to the `IconButton` component when `expandIcon` is supplied. */ expandIcon: { fontFamily: theme.typography.fontFamily, fontSize: theme.typography.pxToRem(15), color: theme.palette.text.primary, transform: 'rotate(0deg)', transition: theme.transitions.create('transform', transition), '&:hover': { // Disable the hover effect for the IconButton, // because a hover effect should apply to the entire Expand button and // not only to the IconButton. backgroundColor: 'transparent' }, '&$expanded': { transform: 'rotate(180deg)' } }, more: { display: 'flex' }, expandIconLabel: { fontFamily: theme.typography.fontFamily, fontSize: theme.typography.pxToRem(13), color: theme.palette.text.primary, whiteSpace: 'nowrap' // textAlign: 'right', // lineHeight: '48px', }, /* Styles applied to the root element if `expanded={true}`. */ expanded: {} }; }; class Filter extends Component { constructor(...args) { super(...args); this.state = { expanded: false }; this.onClick = selectItem => () => { const { multiple, onChange, value, mapper, options } = this.props; const selectVal = isFunction(mapper.value) ? mapper.value(selectItem, options) : selectItem[mapper.value]; let newVal; if (!multiple) { if (this.isSelected(selectVal)) { newVal = []; } else { newVal = [selectVal]; } } else if (this.isSelected(selectVal)) { newVal = value.filter(v => v !== selectVal); } else { newVal = [...value, selectVal]; } onChange(newVal); }; this.handleClear = () => { const { onChange } = this.props; onChange([]); }; this.handleToggle = () => { const { expanded } = this.state; this.setState({ expanded: !expanded }); }; this.isSelected = value => this.props.value.includes(value); } render() { const { classes, options, label, mapper, spacing, labelWidth, color, expandable, row, selectedColor, theme, more, less, scrollX } = this.props; const { expanded } = this.state; const collapsedHeight = `${48 * row}px`; const labelCls = classNames(classes.label); const textColor = theme.palette[color] ? theme.palette[color].contrastText : theme.palette.text.primary; const backgroundColor = theme.palette[color] ? theme.palette[color].main : 'transparent'; const IconComponent = expanded ? ExpandLessIcon : ExpandMoreIcon; return React.createElement("div", { className: classes.root, style: { backgroundColor } }, React.createElement("div", { item: true, className: labelCls }, React.createElement("div", { style: { color: textColor, cursor: 'pointer', width: labelWidth }, onClick: this.handleClear }, label)), React.createElement(Collapse, { style: { overflowX: 'hidden' }, in: !expandable || expanded, collapsedHeight: collapsedHeight }, React.createElement("div", { className: classNames({ [classes.scroll]: scrollX }) }, options.map(option => { const chipLabel = isFunction(mapper.label) ? mapper.label(option, options) : option[mapper.label]; const value = isFunction(mapper.value) ? mapper.value(option, options) : option[mapper.value]; const isSelected = this.isSelected(value); const item = classNames(classes.item, { [classes.colorDefault]: selectedColor === 'default' && isSelected }); const chipTextColor = isSelected ? theme.palette.common.white : textColor; return React.createElement(Chip, { key: value, color: isSelected ? selectedColor : 'default', label: chipLabel, clickable: true, style: { margin: `8px ${spacing}px`, color: chipTextColor }, onClick: this.onClick(option), classes: { root: item, colorPrimary: classes.colorPrimary, colorSecondary: classes.colorSecondary, clickableColorPrimary: classes.clickableColorPrimary, clickableColorSecondary: classes.clickableColorSecondary } }); }))), React.createElement("div", { className: classes.spacer }), React.createElement("div", { className: classes.right }, React.createElement("div", { className: classes.more }, !scrollX && expandable && options.length > 0 && React.createElement(IconButton, { className: classNames(classes.expandIcon, {// [classes.expanded]: expanded, }), component: "div", onClick: this.handleToggle }, React.createElement(IconComponent, { style: { color: textColor } }), React.createElement("span", { className: classes.expandIconLabel, style: { color: textColor } }, expanded ? less : more))))); } } process.env.NODE_ENV !== "production" ? Filter.propTypes = { /** * Override or extend the styles applied to the component. * See [CSS API](#css-api) below for more details. */ classes: PropTypes.object.isRequired, /** * background color. */ color: PropTypes.oneOf(['default', 'primary', 'secondary']), /** * if true , Filter is expandable */ expandable: PropTypes.bool, /** * label name of the Filter */ label: PropTypes.string, /** * label width */ labelWidth: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), /** * @ignore */ less: PropTypes.string, /** * 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]) }), /** * @ignore */ more: PropTypes.string, /** * a multiple select filter or single select filter */ multiple: PropTypes.bool, /** * callback to parent component when select option */ onChange: PropTypes.func, /** * data options. */ options: PropTypes.array, /** * when expandable is true, row is the default lines the Filter will display */ row: PropTypes.number, /** * when scrollX is true, there is a x scroll bar for Filter */ scrollX: PropTypes.bool, /** * the color of active items */ selectedColor: PropTypes.oneOf(['default', 'primary', 'secondary']), /** * spacing between items */ spacing: PropTypes.number, /** * @ignore */ theme: PropTypes.object, /** * The value of the Filter. */ value: PropTypes.array } : void 0; Filter.defaultProps = { color: 'default', selectedColor: 'primary', options: [], value: [], multiple: false, label: '', mapper: { label: 'label', value: 'value' }, onChange() {}, spacing: 8, labelWidth: 'auto', expandable: false, row: 1, scrollX: false }; export default withStyles(style, { name: 'RMFilter', withTheme: true })(withLocale({ name: 'Filter' })(Filter));