@6thquake/react-material
Version:
React components that implement Google's Material Design.
292 lines (263 loc) • 7.28 kB
JavaScript
import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/objectWithoutPropertiesLoose";
import React from 'react';
import PropTypes from 'prop-types';
import warning from 'warning';
import classNames from 'classnames';
import { createChainedFunction, find } from '../utils/helpers';
import withStyles from '../styles/withStyles';
import Check from '@material-ui/icons/Check';
const styles = theme => ({
root: {
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-start'
},
wrapper: {
position: 'relative',
overflow: 'visible',
cursor: 'pointer'
},
disabled: {
cursor: 'default'
},
foldUp: {},
checked: {},
smallAvatar: {
width: 24,
height: 24,
fontSize: theme.typography.pxToRem(12)
},
mediumAvatar: {
width: 40,
height: 40,
fontSize: theme.typography.pxToRem(20)
},
largeAvatar: {
width: 56,
height: 56,
fontSize: theme.typography.pxToRem(28)
},
check: {
position: 'absolute',
zIndex: 1,
borderRadius: '50%',
border: '2px solid #fff',
color: '#fff',
background: 'green'
},
small: {
fontSize: theme.typography.pxToRem(12),
bottom: -2,
right: -2
},
medium: {
fontSize: theme.typography.pxToRem(16),
bottom: -4,
right: -4
},
large: {
fontSize: theme.typography.pxToRem(24),
bottom: -8,
right: -8
},
rowContent: {
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'flex-start'
},
columnContent: {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center'
},
rowReverse: {
flexDirection: 'row-reverse'
},
columnReverse: {
flexDirection: 'column-reverse'
}
});
class AvatarGroup extends React.Component {
constructor(props) {
super(props);
this.avatars = [];
this.handleClick = (val, event) => {
const {
value: valueProp,
multiple,
disabled
} = this.props;
if (disabled) {
return;
}
let value = this.isControlled ? valueProp : this.state.value;
value = multiple ? value.slice() : [];
const index = value.indexOf(val);
if (index === -1) {
value.push(val);
} else {
value.splice(index, 1);
}
if (!this.isControlled) {
this.setState({
value
});
}
if (this.props.onChange) {
this.props.onChange(event, value);
}
};
this.isControlled = props.value != null;
if (!this.isControlled) {
this.state = {
value: props.defaultValue
};
}
}
render() {
const _this$props = this.props,
{
classes,
component: Component,
children,
value: valueProp,
size,
foldUp,
spacing,
placeholder,
max,
disabled
} = _this$props,
other = _objectWithoutPropertiesLoose(_this$props, ["classes", "className", "component", "children", "name", "value", "onChange", "size", "foldUp", "spacing", "direction", "placeholder", "max", "disabled"]);
const value = this.isControlled ? valueProp : this.state.value;
this.avatars = [];
return React.createElement(Component, {
role: "radiogroup",
className: classes.root
}, React.Children.map(children, (child, index) => {
if (index >= max) {
return null;
}
if (!React.isValidElement(child)) {
return null;
}
process.env.NODE_ENV !== "production" ? warning(child.type !== React.Fragment, ["React-Material: the AvatarGroup component doesn't accept a Fragment as a child.", 'Consider providing an array instead.'].join('\n')) : void 0;
const {
direction
} = child.props;
const val = typeof child.props.value !== 'undefined' ? child.props.value : index;
const checked = value && value.indexOf(val) !== -1;
return React.createElement("div", {
className: classNames({
[classes.wrapper]: true,
[classes.foldUp]: foldUp,
[classes.rowContent]: direction === 'left' || direction === 'right',
[classes.columnContent]: direction === 'top' || direction === 'bottom',
[classes.rowReverse]: direction === 'left',
[classes.columnReverse]: direction === 'top',
[classes.disabled]: disabled,
[classes.checked]: checked
}),
style: foldUp ? {
transform: `translateX(-${index * 20}%)`
} : {
margin: `0px ${spacing}px`
},
onClick: createChainedFunction(child.props.onClick, this.handleClick.bind(this, val))
}, React.cloneElement(child, {
className: classNames({
[classes[`${size}Avatar`]]: true
}),
inputRef: node => {
if (node) {
this.avatars.push(node);
}
}
}), child.props.content, checked && React.createElement(Check, {
className: classNames({
[classes.check]: true,
[classes[`${size}`]]: true
})
}));
}), React.Children.count(children) > max && '...', React.Children.count(children) <= 0 && placeholder);
}
}
process.env.NODE_ENV !== "production" ? AvatarGroup.propTypes = {
/**
* Used to render icon or text elements inside the AvatarGroup.
* This can be an element, or just a string.
*/
children: PropTypes.node,
/**
* Override or extend the styles applied to the component.
* See [CSS API](#css-api) below for more details.
*/
classes: PropTypes.object.isRequired,
/**
* 'div' The component used for the root node. Either a string to use a DOM element or a component.
*/
component: PropTypes.element,
/**
* The default selected avatar, useful when not controlling the component.
*/
defaultValue: PropTypes.arrayOf([PropTypes.string, PropTypes.number, PropTypes.bool]),
/**
* If true, do not fire click event.
*/
disabled: PropTypes.bool,
/**
* If true, the avatars will be fold up.
*/
foldUp: PropTypes.bool,
/**
* max item length.
*/
max: PropTypes.number,
/**
* a multiple select filter or single select filter.
*/
multiple: PropTypes.bool,
/**
* The name used to reference the value of the control.
*/
name: PropTypes.string,
/**
* Callback fired when a avatar is selected.
*
* @param {object} event The event source of the callback.
* You can pull out the new value by accessing `event.target.value`.
* @param {string} value The `value` of the selected avatar
*/
onChange: PropTypes.func,
/**
* The short hint displayed without any avatars.
*/
placeholder: PropTypes.string,
/**
* the size of each item.
*/
size: PropTypes.oneOf(['small', 'medium', 'large']),
/**
* spacing between items.
*/
spacing: PropTypes.number,
/**
* Value of the selected avatar.
*/
value: PropTypes.arrayOf([PropTypes.string, PropTypes.number, PropTypes.bool])
} : void 0;
AvatarGroup.defaultProps = {
component: 'div',
size: 'medium',
foldUp: false,
spacing: 8,
multiple: false,
max: 20,
placeholder: '暂无',
disabled: false
};
export default withStyles(styles, {
name: 'RMAvatarGroup'
})(AvatarGroup);