diff --git a/docs/pages/api/avatar-group.js b/docs/pages/api/avatar-group.js new file mode 100644 index 00000000000000..d2069e3cb6ddf4 --- /dev/null +++ b/docs/pages/api/avatar-group.js @@ -0,0 +1,7 @@ +import React from 'react'; +import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs'; +import markdown from './avatar-group.md'; + +export default function Page() { + return ; +} diff --git a/docs/pages/api/avatar-group.md b/docs/pages/api/avatar-group.md new file mode 100644 index 00000000000000..1d6e752f384e45 --- /dev/null +++ b/docs/pages/api/avatar-group.md @@ -0,0 +1,55 @@ +--- +filename: /packages/material-ui-lab/src/AvatarGroup/AvatarGroup.js +--- + + + +# AvatarGroup API + +

The API documentation of the AvatarGroup React component. Learn more about the props and the CSS customization points.

+ +## Import + +```js +import AvatarGroup from '@material-ui/lab/AvatarGroup'; +// or +import { AvatarGroup } from '@material-ui/lab'; +``` + +You can learn more about the difference by [reading this guide](/guides/minimizing-bundle-size/). + + + +## Props + +| Name | Type | Default | Description | +|:-----|:-----|:--------|:------------| +| children | node | | The avatars to stack. | +| classes | object | | Override or extend the styles applied to the component. See [CSS API](#css) below for more details. | + +The `ref` is forwarded to the root element. + +Any other props supplied will be provided to the root element (native element). + +## CSS + +- Style sheet name: `MuiAvatarGroup`. +- Style sheet details: + +| Rule name | Global class | Description | +|:-----|:-------------|:------------| +| root | .MuiAvatarGroup-root | Styles applied to the root element. +| avatar | .MuiAvatarGroup-avatar | Styles applied to the avatar elements. + +You can override the style of the component thanks to one of these customization points: + +- With a rule name of the [`classes` object prop](/customization/components/#overriding-styles-with-classes). +- With a [global class name](/customization/components/#overriding-styles-with-global-class-names). +- With a theme and an [`overrides` property](/customization/globals/#css). + +If that's not sufficient, you can check the [implementation of the component](https://github.com/mui-org/material-ui/blob/master/packages/material-ui-lab/src/AvatarGroup/AvatarGroup.js) for more detail. + +## Demos + +- [Avatars](/components/avatars/) + diff --git a/docs/pages/api/badge.md b/docs/pages/api/badge.md index 2d0cf4337cd398..10d6f68700a2ee 100644 --- a/docs/pages/api/badge.md +++ b/docs/pages/api/badge.md @@ -73,5 +73,6 @@ If that's not sufficient, you can check the [implementation of the component](ht ## Demos +- [Avatars](/components/avatars/) - [Badges](/components/badges/) diff --git a/docs/src/pages/components/avatars/BadgeAvatars.js b/docs/src/pages/components/avatars/BadgeAvatars.js new file mode 100644 index 00000000000000..51deb2a9cbddb9 --- /dev/null +++ b/docs/src/pages/components/avatars/BadgeAvatars.js @@ -0,0 +1,79 @@ +import React from 'react'; +import Badge from '@material-ui/core/Badge'; +import Avatar from '@material-ui/core/Avatar'; +import { makeStyles, withStyles } from '@material-ui/core/styles'; + +const StyledBadge = withStyles(theme => ({ + badge: { + backgroundColor: '#44b700', + color: '#44b700', + boxShadow: `0 0 0 2px ${theme.palette.background.paper}`, + '&::after': { + position: 'absolute', + top: 0, + left: 0, + width: '100%', + height: '100%', + borderRadius: '50%', + animation: '$ripple 1.2s infinite ease-in-out', + border: '1px solid currentColor', + content: '""', + }, + }, + '@keyframes ripple': { + '0%': { + transform: 'scale(.8)', + opacity: 1, + }, + '100%': { + transform: 'scale(2.4)', + opacity: 0, + }, + }, +}))(Badge); + +const SmallAvatar = withStyles(theme => ({ + root: { + width: 22, + height: 22, + border: `2px solid ${theme.palette.background.paper}`, + }, +}))(Avatar); + +const useStyles = makeStyles(theme => ({ + root: { + display: 'flex', + '& > *': { + margin: theme.spacing(1), + }, + }, +})); + +export default function BadgeAvatars() { + const classes = useStyles(); + + return ( +
+ + + + } + > + + +
+ ); +} diff --git a/docs/src/pages/components/avatars/BadgeAvatars.tsx b/docs/src/pages/components/avatars/BadgeAvatars.tsx new file mode 100644 index 00000000000000..ae4354d24785e8 --- /dev/null +++ b/docs/src/pages/components/avatars/BadgeAvatars.tsx @@ -0,0 +1,85 @@ +import React from 'react'; +import Badge from '@material-ui/core/Badge'; +import Avatar from '@material-ui/core/Avatar'; +import { Theme, makeStyles, withStyles, createStyles } from '@material-ui/core/styles'; + +const StyledBadge = withStyles((theme: Theme) => + createStyles({ + badge: { + backgroundColor: '#44b700', + color: '#44b700', + boxShadow: `0 0 0 2px ${theme.palette.background.paper}`, + '&::after': { + position: 'absolute', + top: 0, + left: 0, + width: '100%', + height: '100%', + borderRadius: '50%', + animation: '$ripple 1.2s infinite ease-in-out', + border: '1px solid currentColor', + content: '""', + }, + }, + '@keyframes ripple': { + '0%': { + transform: 'scale(.8)', + opacity: 1, + }, + '100%': { + transform: 'scale(2.4)', + opacity: 0, + }, + }, + }), +)(Badge); + +const SmallAvatar = withStyles((theme: Theme) => + createStyles({ + root: { + width: 22, + height: 22, + border: `2px solid ${theme.palette.background.paper}`, + }, + }), +)(Avatar); + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + '& > *': { + margin: theme.spacing(1), + }, + }, + }), +); + +export default function BadgeAvatars() { + const classes = useStyles(); + + return ( +
+ + + + } + > + + +
+ ); +} diff --git a/docs/src/pages/components/avatars/GroupAvatars.js b/docs/src/pages/components/avatars/GroupAvatars.js new file mode 100644 index 00000000000000..1af0c027edd5d6 --- /dev/null +++ b/docs/src/pages/components/avatars/GroupAvatars.js @@ -0,0 +1,17 @@ +import React from 'react'; +import Avatar from '@material-ui/core/Avatar'; +import AvatarGroup from '@material-ui/lab/AvatarGroup'; +import Tooltip from '@material-ui/core/Tooltip'; + +export default function GroupAvatars() { + return ( + + + + + + +3 + + + ); +} diff --git a/docs/src/pages/components/avatars/GroupAvatars.tsx b/docs/src/pages/components/avatars/GroupAvatars.tsx new file mode 100644 index 00000000000000..1af0c027edd5d6 --- /dev/null +++ b/docs/src/pages/components/avatars/GroupAvatars.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import Avatar from '@material-ui/core/Avatar'; +import AvatarGroup from '@material-ui/lab/AvatarGroup'; +import Tooltip from '@material-ui/core/Tooltip'; + +export default function GroupAvatars() { + return ( + + + + + + +3 + + + ); +} diff --git a/docs/src/pages/components/avatars/IconAvatars.js b/docs/src/pages/components/avatars/IconAvatars.js index e18afa154c505e..4af6bc088fa6a3 100644 --- a/docs/src/pages/components/avatars/IconAvatars.js +++ b/docs/src/pages/components/avatars/IconAvatars.js @@ -14,7 +14,7 @@ const useStyles = makeStyles(theme => ({ }, }, pink: { - color: '#fff', + color: theme.palette.getContrastText(pink[500]), backgroundColor: pink[500], }, green: { diff --git a/docs/src/pages/components/avatars/IconAvatars.tsx b/docs/src/pages/components/avatars/IconAvatars.tsx index e4611b50f73ff0..48f7e1923d937d 100644 --- a/docs/src/pages/components/avatars/IconAvatars.tsx +++ b/docs/src/pages/components/avatars/IconAvatars.tsx @@ -15,7 +15,7 @@ const useStyles = makeStyles((theme: Theme) => }, }, pink: { - color: '#fff', + color: theme.palette.getContrastText(pink[500]), backgroundColor: pink[500], }, green: { diff --git a/docs/src/pages/components/avatars/ImageAvatars.js b/docs/src/pages/components/avatars/ImageAvatars.js index 33bec663f1f6db..468fab2ea8493e 100644 --- a/docs/src/pages/components/avatars/ImageAvatars.js +++ b/docs/src/pages/components/avatars/ImageAvatars.js @@ -9,10 +9,6 @@ const useStyles = makeStyles(theme => ({ margin: theme.spacing(1), }, }, - bigAvatar: { - width: 60, - height: 60, - }, })); export default function ImageAvatars() { @@ -21,7 +17,8 @@ export default function ImageAvatars() { return (
- + +
); } diff --git a/docs/src/pages/components/avatars/ImageAvatars.tsx b/docs/src/pages/components/avatars/ImageAvatars.tsx index 9739765e4541a9..7725d7640332d0 100644 --- a/docs/src/pages/components/avatars/ImageAvatars.tsx +++ b/docs/src/pages/components/avatars/ImageAvatars.tsx @@ -10,10 +10,6 @@ const useStyles = makeStyles((theme: Theme) => margin: theme.spacing(1), }, }, - bigAvatar: { - width: 60, - height: 60, - }, }), ); @@ -23,7 +19,8 @@ export default function ImageAvatars() { return (
- + +
); } diff --git a/docs/src/pages/components/avatars/LetterAvatars.js b/docs/src/pages/components/avatars/LetterAvatars.js index a272dfd400059e..fb126df2c7dc61 100644 --- a/docs/src/pages/components/avatars/LetterAvatars.js +++ b/docs/src/pages/components/avatars/LetterAvatars.js @@ -11,11 +11,11 @@ const useStyles = makeStyles(theme => ({ }, }, orange: { - color: '#fff', + color: theme.palette.getContrastText(deepOrange[500]), backgroundColor: deepOrange[500], }, purple: { - color: '#fff', + color: theme.palette.getContrastText(deepPurple[500]), backgroundColor: deepPurple[500], }, })); diff --git a/docs/src/pages/components/avatars/LetterAvatars.tsx b/docs/src/pages/components/avatars/LetterAvatars.tsx index 1f7157f24f12c1..cd2fe74c6af267 100644 --- a/docs/src/pages/components/avatars/LetterAvatars.tsx +++ b/docs/src/pages/components/avatars/LetterAvatars.tsx @@ -12,11 +12,11 @@ const useStyles = makeStyles((theme: Theme) => }, }, orange: { - color: '#fff', + color: theme.palette.getContrastText(deepOrange[500]), backgroundColor: deepOrange[500], }, purple: { - color: '#fff', + color: theme.palette.getContrastText(deepPurple[500]), backgroundColor: deepPurple[500], }, }), diff --git a/docs/src/pages/components/avatars/SizeAvatars.js b/docs/src/pages/components/avatars/SizeAvatars.js new file mode 100644 index 00000000000000..e9d0028594137f --- /dev/null +++ b/docs/src/pages/components/avatars/SizeAvatars.js @@ -0,0 +1,32 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import Avatar from '@material-ui/core/Avatar'; + +const useStyles = makeStyles(theme => ({ + root: { + display: 'flex', + '& > *': { + margin: theme.spacing(1), + }, + }, + small: { + width: theme.spacing(3), + height: theme.spacing(3), + }, + large: { + width: theme.spacing(7), + height: theme.spacing(7), + }, +})); + +export default function ImageAvatars() { + const classes = useStyles(); + + return ( +
+ + + +
+ ); +} diff --git a/docs/src/pages/components/avatars/SizeAvatars.tsx b/docs/src/pages/components/avatars/SizeAvatars.tsx new file mode 100644 index 00000000000000..43c152d6ad5be4 --- /dev/null +++ b/docs/src/pages/components/avatars/SizeAvatars.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import Avatar from '@material-ui/core/Avatar'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + '& > *': { + margin: theme.spacing(1), + }, + }, + small: { + width: theme.spacing(3), + height: theme.spacing(3), + }, + large: { + width: theme.spacing(7), + height: theme.spacing(7), + }, + }), +); + +export default function ImageAvatars() { + const classes = useStyles(); + + return ( +
+ + + +
+ ); +} diff --git a/docs/src/pages/components/avatars/VariantAvatars.js b/docs/src/pages/components/avatars/VariantAvatars.js index dbe5e004335d59..497cf2a91fe2cb 100644 --- a/docs/src/pages/components/avatars/VariantAvatars.js +++ b/docs/src/pages/components/avatars/VariantAvatars.js @@ -12,7 +12,7 @@ const useStyles = makeStyles(theme => ({ }, }, square: { - color: '#fff', + color: theme.palette.getContrastText(deepOrange[500]), backgroundColor: deepOrange[500], }, rounded: { diff --git a/docs/src/pages/components/avatars/VariantAvatars.tsx b/docs/src/pages/components/avatars/VariantAvatars.tsx index 27255d4b6e35c8..4f10e6d2ccfe54 100644 --- a/docs/src/pages/components/avatars/VariantAvatars.tsx +++ b/docs/src/pages/components/avatars/VariantAvatars.tsx @@ -13,7 +13,7 @@ const useStyles = makeStyles((theme: Theme) => }, }, square: { - color: '#fff', + color: theme.palette.getContrastText(deepOrange[500]), backgroundColor: deepOrange[500], }, rounded: { diff --git a/docs/src/pages/components/avatars/avatars.md b/docs/src/pages/components/avatars/avatars.md index a3cfa8fc434772..c3577afe020552 100644 --- a/docs/src/pages/components/avatars/avatars.md +++ b/docs/src/pages/components/avatars/avatars.md @@ -1,6 +1,6 @@ --- title: Avatar React component -components: Avatar +components: Avatar, AvatarGroup, Badge --- # Avatar @@ -19,6 +19,12 @@ Avatars containing simple characters can be created by passing your string as `c {{"demo": "pages/components/avatars/LetterAvatars.js"}} +## Sizes + +You can change the size of the avatar with the `height` and `width` CSS properties. + +{{"demo": "pages/components/avatars/SizeAvatars.js"}} + ## Icon avatars Icon avatars are created by passing an icon as `children`. @@ -40,3 +46,13 @@ The component fallbacks if there is an error loading the avatar image, in this o - a generic avatar icon {{"demo": "pages/components/avatars/FallbackAvatars.js"}} + +## Grouped + +`AvatarGroup` renders its children as a stack. + +{{"demo": "pages/components/avatars/GroupAvatars.js"}} + +## With badge + +{{"demo": "pages/components/avatars/BadgeAvatars.js"}} diff --git a/docs/src/pages/components/badges/BadgeAlignment.js b/docs/src/pages/components/badges/BadgeAlignment.js index ee660970f43db4..27574669afb161 100644 --- a/docs/src/pages/components/badges/BadgeAlignment.js +++ b/docs/src/pages/components/badges/BadgeAlignment.js @@ -7,11 +7,11 @@ import Radio from '@material-ui/core/Radio'; import RadioGroup from '@material-ui/core/RadioGroup'; import { makeStyles } from '@material-ui/core/styles'; import MailIcon from '@material-ui/icons/Mail'; +import MarkdownElement from 'docs/src/modules/components/MarkdownElement'; const useStyles = makeStyles(theme => ({ root: { - display: 'flex', - flexDirection: 'column', + width: '100%', }, formControl: { margin: theme.spacing(3), @@ -38,19 +38,30 @@ export default function BadgeAlignment() { setVertical(event.target.value); }; + const code = ` +\`\`\`jsx + +\`\`\` +`; + return (
Vertical - + } label="Top" /> } label="Bottom" /> Horizontal - + } label="Right" /> } label="Left" /> @@ -115,6 +126,7 @@ export default function BadgeAlignment() {
+
); } diff --git a/docs/src/pages/components/badges/BadgeAlignment.tsx b/docs/src/pages/components/badges/BadgeAlignment.tsx deleted file mode 100644 index 8792389d0d65b4..00000000000000 --- a/docs/src/pages/components/badges/BadgeAlignment.tsx +++ /dev/null @@ -1,122 +0,0 @@ -import React from 'react'; -import Badge from '@material-ui/core/Badge'; -import FormControl from '@material-ui/core/FormControl'; -import FormControlLabel from '@material-ui/core/FormControlLabel'; -import FormLabel from '@material-ui/core/FormLabel'; -import Radio from '@material-ui/core/Radio'; -import RadioGroup from '@material-ui/core/RadioGroup'; -import { Theme, createStyles, makeStyles } from '@material-ui/core/styles'; -import MailIcon from '@material-ui/icons/Mail'; - -const useStyles = makeStyles((theme: Theme) => - createStyles({ - root: { - display: 'flex', - flexDirection: 'column', - }, - formControl: { - margin: theme.spacing(3), - }, - row: { - display: 'flex', - justifyContent: 'center', - }, - margin: { - margin: theme.spacing(2), - }, - }), -); - -export default function BadgeAlignment() { - const classes = useStyles(); - const [horizontal, setHorizontal] = React.useState<'left' | 'right'>('right'); - const [vertical, setVertical] = React.useState<'top' | 'bottom'>('top'); - - const handleHorizontalChange = (event: React.ChangeEvent) => { - setHorizontal((event.target as HTMLInputElement).value as 'left' | 'right'); - }; - - const handleVerticalChange = (event: React.ChangeEvent) => { - setVertical((event.target as HTMLInputElement).value as 'top' | 'bottom'); - }; - - return ( -
-
- - Vertical - - } label="Top" /> - } label="Bottom" /> - - - - Horizontal - - } label="Right" /> - } label="Left" /> - - -
-
- - - - - - - - - - - - - - - -
-
- ); -} diff --git a/docs/src/pages/components/badges/BadgeMax.js b/docs/src/pages/components/badges/BadgeMax.js index ff317f609897f8..7b756a155f6089 100644 --- a/docs/src/pages/components/badges/BadgeMax.js +++ b/docs/src/pages/components/badges/BadgeMax.js @@ -1,26 +1,30 @@ import React from 'react'; -import Box from '@material-ui/core/Box'; +import { makeStyles } from '@material-ui/core/styles'; import Badge from '@material-ui/core/Badge'; import MailIcon from '@material-ui/icons/Mail'; +const useStyles = makeStyles(theme => ({ + root: { + '& > *': { + margin: theme.spacing(2), + }, + }, +})); + export default function BadgeMax() { + const classes = useStyles(); + return ( - - - - - - - - - - - - - - - - - +
+ + + + + + + + + +
); } diff --git a/docs/src/pages/components/badges/BadgeMax.tsx b/docs/src/pages/components/badges/BadgeMax.tsx index ff317f609897f8..ae688b01589177 100644 --- a/docs/src/pages/components/badges/BadgeMax.tsx +++ b/docs/src/pages/components/badges/BadgeMax.tsx @@ -1,26 +1,32 @@ import React from 'react'; -import Box from '@material-ui/core/Box'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; import Badge from '@material-ui/core/Badge'; import MailIcon from '@material-ui/icons/Mail'; +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + '& > *': { + margin: theme.spacing(2), + }, + }, + }), +); + export default function BadgeMax() { + const classes = useStyles(); + return ( - - - - - - - - - - - - - - - - - +
+ + + + + + + + + +
); } diff --git a/docs/src/pages/components/badges/BadgeOverlap.js b/docs/src/pages/components/badges/BadgeOverlap.js index d578ec6b30c6e1..278f60f05484a5 100644 --- a/docs/src/pages/components/badges/BadgeOverlap.js +++ b/docs/src/pages/components/badges/BadgeOverlap.js @@ -1,23 +1,13 @@ import React from 'react'; import clsx from 'clsx'; -import Badge from '@material-ui/core/Badge'; -import FormControl from '@material-ui/core/FormControl'; -import FormControlLabel from '@material-ui/core/FormControlLabel'; -import FormLabel from '@material-ui/core/FormLabel'; -import Radio from '@material-ui/core/Radio'; -import RadioGroup from '@material-ui/core/RadioGroup'; import { makeStyles } from '@material-ui/core/styles'; +import Badge from '@material-ui/core/Badge'; const useStyles = makeStyles(theme => ({ - formControl: { - marginBottom: theme.spacing(3), - }, - row: { - display: 'flex', - justifyContent: 'center', - }, - shapeContainer: { - margin: theme.spacing(2), + root: { + '& > *': { + margin: theme.spacing(1), + }, }, shape: { backgroundColor: theme.palette.primary.main, @@ -31,35 +21,24 @@ const useStyles = makeStyles(theme => ({ export default function BadgeOverlap() { const classes = useStyles(); - const [overlap, setOverlap] = React.useState('rectangle'); - const handleChange = event => { - setOverlap(event.target.value); - }; + const rectangle =
; + const circle =
; return ( -
-
- - Overlap - - } label="Rectangle" /> - } label="Circle" /> - - -
-
-
- -
- -
-
- -
- -
-
+
+ + {rectangle} + + + {rectangle} + + + {circle} + + + {circle} +
); } diff --git a/docs/src/pages/components/badges/BadgeOverlap.tsx b/docs/src/pages/components/badges/BadgeOverlap.tsx index 85bc7634509afd..29669b01930e9c 100644 --- a/docs/src/pages/components/badges/BadgeOverlap.tsx +++ b/docs/src/pages/components/badges/BadgeOverlap.tsx @@ -1,24 +1,14 @@ import React from 'react'; import clsx from 'clsx'; -import Badge from '@material-ui/core/Badge'; -import FormControl from '@material-ui/core/FormControl'; -import FormControlLabel from '@material-ui/core/FormControlLabel'; -import FormLabel from '@material-ui/core/FormLabel'; -import Radio from '@material-ui/core/Radio'; -import RadioGroup from '@material-ui/core/RadioGroup'; import { Theme, createStyles, makeStyles } from '@material-ui/core/styles'; +import Badge from '@material-ui/core/Badge'; const useStyles = makeStyles((theme: Theme) => createStyles({ - formControl: { - marginBottom: theme.spacing(3), - }, - row: { - display: 'flex', - justifyContent: 'center', - }, - shapeContainer: { - margin: theme.spacing(2), + root: { + '& > *': { + margin: theme.spacing(1), + }, }, shape: { backgroundColor: theme.palette.primary.main, @@ -33,35 +23,24 @@ const useStyles = makeStyles((theme: Theme) => export default function BadgeOverlap() { const classes = useStyles(); - const [overlap, setOverlap] = React.useState<'circle' | 'rectangle'>('rectangle'); - const handleChange = (event: React.ChangeEvent) => { - setOverlap((event.target as HTMLInputElement).value as 'circle' | 'rectangle'); - }; + const rectangle =
; + const circle =
; return ( -
-
- - Overlap - - } label="Rectangle" /> - } label="Circle" /> - - -
-
-
- -
- -
-
- -
- -
-
+
+ + {rectangle} + + + {rectangle} + + + {circle} + + + {circle} +
); } diff --git a/docs/src/pages/components/badges/CustomizedBadges.js b/docs/src/pages/components/badges/CustomizedBadges.js index f781d09a53152a..6071cca81b3589 100644 --- a/docs/src/pages/components/badges/CustomizedBadges.js +++ b/docs/src/pages/components/badges/CustomizedBadges.js @@ -1,89 +1,24 @@ import React from 'react'; import Badge from '@material-ui/core/Badge'; -import Box from '@material-ui/core/Box'; -import Avatar from '@material-ui/core/Avatar'; import { withStyles } from '@material-ui/core/styles'; import IconButton from '@material-ui/core/IconButton'; import ShoppingCartIcon from '@material-ui/icons/ShoppingCart'; -const StyledBadge1 = withStyles(theme => ({ +const StyledBadge = withStyles(theme => ({ badge: { right: -3, + top: 13, border: `2px solid ${theme.palette.background.paper}`, padding: '0 4px', }, }))(Badge); -const StyledBadge2 = withStyles(theme => ({ - badge: { - backgroundColor: '#44b700', - boxShadow: `0 0 0 2px ${theme.palette.background.paper}`, - '&::after': { - position: 'absolute', - top: 0, - left: 0, - width: '100%', - height: '100%', - borderRadius: '50%', - animation: '$ripple 1.2s infinite ease-in-out', - border: '1px solid #44b700', - content: '""', - }, - }, - '@keyframes ripple': { - '0%': { - transform: 'scale(.8)', - opacity: 1, - }, - '100%': { - transform: 'scale(2.4)', - opacity: 0, - }, - }, -}))(Badge); - -const SmallAvatar = withStyles(theme => ({ - root: { - width: 22, - height: 22, - border: `2px solid ${theme.palette.background.paper}`, - }, -}))(Avatar); - export default function CustomizedBadges() { return ( - - - - - - - - - - - - - - - } - > - - - - + + + + + ); } diff --git a/docs/src/pages/components/badges/CustomizedBadges.tsx b/docs/src/pages/components/badges/CustomizedBadges.tsx index c28abe5d17e8ae..7da7ffe40155e5 100644 --- a/docs/src/pages/components/badges/CustomizedBadges.tsx +++ b/docs/src/pages/components/badges/CustomizedBadges.tsx @@ -1,95 +1,26 @@ import React from 'react'; import Badge from '@material-ui/core/Badge'; -import Box from '@material-ui/core/Box'; -import Avatar from '@material-ui/core/Avatar'; import { Theme, withStyles, createStyles } from '@material-ui/core/styles'; import IconButton from '@material-ui/core/IconButton'; import ShoppingCartIcon from '@material-ui/icons/ShoppingCart'; -const StyledBadge1 = withStyles((theme: Theme) => +const StyledBadge = withStyles((theme: Theme) => createStyles({ badge: { right: -3, + top: 13, border: `2px solid ${theme.palette.background.paper}`, padding: '0 4px', }, }), )(Badge); -const StyledBadge2 = withStyles((theme: Theme) => - createStyles({ - badge: { - backgroundColor: '#44b700', - boxShadow: `0 0 0 2px ${theme.palette.background.paper}`, - '&::after': { - position: 'absolute', - top: 0, - left: 0, - width: '100%', - height: '100%', - borderRadius: '50%', - animation: '$ripple 1.2s infinite ease-in-out', - border: '1px solid #44b700', - content: '""', - }, - }, - '@keyframes ripple': { - '0%': { - transform: 'scale(.8)', - opacity: 1, - }, - '100%': { - transform: 'scale(2.4)', - opacity: 0, - }, - }, - }), -)(Badge); - -const SmallAvatar = withStyles((theme: Theme) => - createStyles({ - root: { - width: 22, - height: 22, - border: `2px solid ${theme.palette.background.paper}`, - }, - }), -)(Avatar); - export default function CustomizedBadges() { return ( - - - - - - - - - - - - - - - } - > - - - - + + + + + ); } diff --git a/docs/src/pages/components/badges/DotBadge.js b/docs/src/pages/components/badges/DotBadge.js index 16558e3b2eec00..bcc6604b2ed4a5 100644 --- a/docs/src/pages/components/badges/DotBadge.js +++ b/docs/src/pages/components/badges/DotBadge.js @@ -1,27 +1,31 @@ import React from 'react'; -import Box from '@material-ui/core/Box'; +import { makeStyles } from '@material-ui/core/styles'; import Badge from '@material-ui/core/Badge'; import MailIcon from '@material-ui/icons/Mail'; import Typography from '@material-ui/core/Typography'; +const useStyles = makeStyles(theme => ({ + root: { + '& > *': { + margin: theme.spacing(1), + }, + }, +})); + export default function DotBadge() { + const classes = useStyles(); + return ( - - - - - - - - - - - - - - Typography - - - +
+ + + + + + + + Typography + +
); } diff --git a/docs/src/pages/components/badges/DotBadge.tsx b/docs/src/pages/components/badges/DotBadge.tsx index 16558e3b2eec00..dcc4d242435622 100644 --- a/docs/src/pages/components/badges/DotBadge.tsx +++ b/docs/src/pages/components/badges/DotBadge.tsx @@ -1,27 +1,33 @@ import React from 'react'; -import Box from '@material-ui/core/Box'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; import Badge from '@material-ui/core/Badge'; import MailIcon from '@material-ui/icons/Mail'; import Typography from '@material-ui/core/Typography'; +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + '& > *': { + margin: theme.spacing(1), + }, + }, + }), +); + export default function DotBadge() { + const classes = useStyles(); + return ( - - - - - - - - - - - - - - Typography - - - +
+ + + + + + + + Typography + +
); } diff --git a/docs/src/pages/components/badges/SimpleBadge.js b/docs/src/pages/components/badges/SimpleBadge.js index 58476cb4012075..d0dfad7cd61b9c 100644 --- a/docs/src/pages/components/badges/SimpleBadge.js +++ b/docs/src/pages/components/badges/SimpleBadge.js @@ -3,18 +3,12 @@ import { makeStyles } from '@material-ui/core/styles'; import Badge from '@material-ui/core/Badge'; import IconButton from '@material-ui/core/IconButton'; import MailIcon from '@material-ui/icons/Mail'; -import AppBar from '@material-ui/core/AppBar'; -import Tabs from '@material-ui/core/Tabs'; -import Tab from '@material-ui/core/Tab'; -import Typography from '@material-ui/core/Typography'; -import Button from '@material-ui/core/Button'; const useStyles = makeStyles(theme => ({ - margin: { - margin: theme.spacing(2), - }, - padding: { - padding: theme.spacing(0, 2), + root: { + '& > *': { + margin: theme.spacing(1), + }, }, })); @@ -22,39 +16,18 @@ export default function SimpleBadge() { const classes = useStyles(); return ( -
-
- - - - - - - - - - - -
- - - - Item One - - } - /> - - - - - - Typography +
+ + - - + + + + + + +
); } diff --git a/docs/src/pages/components/badges/SimpleBadge.tsx b/docs/src/pages/components/badges/SimpleBadge.tsx index 9a47471350eea1..3ed7f2aed2596f 100644 --- a/docs/src/pages/components/badges/SimpleBadge.tsx +++ b/docs/src/pages/components/badges/SimpleBadge.tsx @@ -3,19 +3,13 @@ import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; import Badge from '@material-ui/core/Badge'; import IconButton from '@material-ui/core/IconButton'; import MailIcon from '@material-ui/icons/Mail'; -import AppBar from '@material-ui/core/AppBar'; -import Tabs from '@material-ui/core/Tabs'; -import Tab from '@material-ui/core/Tab'; -import Typography from '@material-ui/core/Typography'; -import Button from '@material-ui/core/Button'; const useStyles = makeStyles((theme: Theme) => createStyles({ - margin: { - margin: theme.spacing(2), - }, - padding: { - padding: theme.spacing(0, 2), + root: { + '& > *': { + margin: theme.spacing(1), + }, }, }), ); @@ -24,39 +18,18 @@ export default function SimpleBadge() { const classes = useStyles(); return ( -
-
- - - - - - - - - - - -
- - - - Item One - - } - /> - - - - - - Typography +
+ + - - + + + + + + +
); } diff --git a/docs/src/pages/components/badges/badges.md b/docs/src/pages/components/badges/badges.md index fa68eec06546c7..69a1c6e50712e6 100644 --- a/docs/src/pages/components/badges/badges.md +++ b/docs/src/pages/components/badges/badges.md @@ -15,7 +15,7 @@ Examples of badges containing text, using primary and secondary colors. The badg ## Customized badges -Here are some examples of customizing the component. You can learn more about this in the [overrides documentation page](/customization/components/). +Here is an example of customizing the component. You can learn more about this in the [overrides documentation page](/customization/components/). {{"demo": "pages/components/badges/CustomizedBadges.js"}} @@ -49,4 +49,4 @@ You can use the `overlap` property to place the badge relative to the corner of You can use the `horizontalAlignment` and `verticalAlignment` properties to move the badge to any corner of the wrapped element. -{{"demo": "pages/components/badges/BadgeAlignment.js"}} +{{"demo": "pages/components/badges/BadgeAlignment.js", "hideHeader": true}} diff --git a/packages/material-ui-lab/src/AvatarGroup/AvatarGroup.d.ts b/packages/material-ui-lab/src/AvatarGroup/AvatarGroup.d.ts new file mode 100644 index 00000000000000..2d2e7065d971e1 --- /dev/null +++ b/packages/material-ui-lab/src/AvatarGroup/AvatarGroup.d.ts @@ -0,0 +1,14 @@ +import * as React from 'react'; +import { StandardProps } from '@material-ui/core'; + +export interface AvatarGroupProps + extends StandardProps, AvatarGroupClassKey> { + /** + * The avatars to stack. + */ + children: React.ReactNode; +} + +export type AvatarGroupClassKey = 'root' | 'avatar'; + +export default function AvatarGroup(props: AvatarGroupProps): JSX.Element | null; diff --git a/packages/material-ui-lab/src/AvatarGroup/AvatarGroup.js b/packages/material-ui-lab/src/AvatarGroup/AvatarGroup.js new file mode 100644 index 00000000000000..016441e475b9ed --- /dev/null +++ b/packages/material-ui-lab/src/AvatarGroup/AvatarGroup.js @@ -0,0 +1,72 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { isFragment } from 'react-is'; +import clsx from 'clsx'; +import { withStyles } from '@material-ui/core/styles'; + +export const styles = theme => ({ + /* Styles applied to the root element. */ + root: { + display: 'flex', + }, + /* Styles applied to the avatar elements. */ + avatar: { + border: `2px solid ${theme.palette.background.default}`, + marginLeft: -8, + }, +}); + +const AvatarGroup = React.forwardRef(function AvatarGroup(props, ref) { + const { children: childrenProp, classes, className, ...other } = props; + + const children = React.Children.toArray(childrenProp).filter(child => { + if (process.env.NODE_ENV !== 'production') { + if (isFragment(child)) { + console.error( + [ + "Material-UI: the AvatarGroup component doesn't accept a Fragment as a child.", + 'Consider providing an array instead.', + ].join('\n'), + ); + } + } + + return React.isValidElement(child); + }); + + return ( +
+ {children.map((child, index) => { + return React.cloneElement(child, { + className: clsx(child.props.className, classes.avatar), + style: { + zIndex: children.length - index, + ...child.props.style, + }, + }); + })} +
+ ); +}); + +AvatarGroup.propTypes = { + // ----------------------------- Warning -------------------------------- + // | These PropTypes are generated from the TypeScript type definitions | + // | To update them edit the d.ts file and run "yarn proptypes" | + // ---------------------------------------------------------------------- + /** + * The avatars to stack. + */ + children: PropTypes.node, + /** + * Override or extend the styles applied to the component. + * See [CSS API](#css) below for more details. + */ + classes: PropTypes.object, + /** + * @ignore + */ + className: PropTypes.string, +}; + +export default withStyles(styles, { name: 'MuiAvatarGroup' })(AvatarGroup); diff --git a/packages/material-ui-lab/src/AvatarGroup/AvatarGroup.test.js b/packages/material-ui-lab/src/AvatarGroup/AvatarGroup.test.js new file mode 100644 index 00000000000000..135ed703ad6962 --- /dev/null +++ b/packages/material-ui-lab/src/AvatarGroup/AvatarGroup.test.js @@ -0,0 +1,26 @@ +import React from 'react'; +import { createMount, getClasses } from '@material-ui/core/test-utils'; +import describeConformance from '@material-ui/core/test-utils/describeConformance'; +import AvatarGroup from './AvatarGroup'; + +describe('', () => { + let mount; + let classes; + + before(() => { + mount = createMount({ strict: true }); + classes = getClasses(); + }); + + after(() => { + mount.cleanUp(); + }); + + describeConformance(, () => ({ + classes, + inheritComponent: 'div', + mount, + refInstanceof: window.HTMLDivElement, + skip: ['componentProp'], + })); +}); diff --git a/packages/material-ui-lab/src/AvatarGroup/index.d.ts b/packages/material-ui-lab/src/AvatarGroup/index.d.ts new file mode 100644 index 00000000000000..a691e2cb2eff92 --- /dev/null +++ b/packages/material-ui-lab/src/AvatarGroup/index.d.ts @@ -0,0 +1,2 @@ +export { default } from './AvatarGroup'; +export * from './AvatarGroup'; diff --git a/packages/material-ui-lab/src/AvatarGroup/index.js b/packages/material-ui-lab/src/AvatarGroup/index.js new file mode 100644 index 00000000000000..20c5443b297112 --- /dev/null +++ b/packages/material-ui-lab/src/AvatarGroup/index.js @@ -0,0 +1 @@ +export { default } from './AvatarGroup'; diff --git a/packages/material-ui-lab/src/index.d.ts b/packages/material-ui-lab/src/index.d.ts index 8016e0cc3c094f..c39dc2b6a1545b 100644 --- a/packages/material-ui-lab/src/index.d.ts +++ b/packages/material-ui-lab/src/index.d.ts @@ -1,6 +1,9 @@ export { default as Autocomplete } from './Autocomplete'; export * from './Autocomplete'; +export { default as AvatarGroup } from './AvatarGroup'; +export * from './AvatarGroup'; + export { default as Rating } from './Rating'; export * from './Rating'; diff --git a/packages/material-ui-lab/src/index.js b/packages/material-ui-lab/src/index.js index 42ffe2284d6874..4014dcac7ca134 100644 --- a/packages/material-ui-lab/src/index.js +++ b/packages/material-ui-lab/src/index.js @@ -2,6 +2,9 @@ export { default as Autocomplete } from './Autocomplete'; export * from './Autocomplete'; +export { default as AvatarGroup } from './AvatarGroup'; +export * from './AvatarGroup'; + export { default as Rating } from './Rating'; export * from './Rating'; diff --git a/packages/material-ui/src/Badge/Badge.js b/packages/material-ui/src/Badge/Badge.js index 0027b6602c29a4..cf1fcef48e5a8e 100644 --- a/packages/material-ui/src/Badge/Badge.js +++ b/packages/material-ui/src/Badge/Badge.js @@ -5,7 +5,7 @@ import withStyles from '../styles/withStyles'; import capitalize from '../utils/capitalize'; const RADIUS_STANDARD = 10; -const RADIUS_DOT = 3; +const RADIUS_DOT = 4; export const styles = theme => ({ /* Styles applied to the root element. */ @@ -34,8 +34,6 @@ export const styles = theme => ({ padding: '0 6px', height: RADIUS_STANDARD * 2, borderRadius: RADIUS_STANDARD, - backgroundColor: theme.palette.color, - color: theme.palette.textColor, zIndex: 1, // Render the badge on top of potential ripples. transition: theme.transitions.create('transform', { easing: theme.transitions.easing.easeInOut, @@ -59,6 +57,7 @@ export const styles = theme => ({ }, /* Styles applied to the root element if `variant="dot"`. */ dot: { + borderRadius: RADIUS_DOT, height: RADIUS_DOT * 2, minWidth: RADIUS_DOT * 2, padding: 0, diff --git a/test/regressions/index.js b/test/regressions/index.js index cb5cdc818bac05..9b5559e544b5d4 100644 --- a/test/regressions/index.js +++ b/test/regressions/index.js @@ -82,6 +82,7 @@ const blacklistFilename = [ 'docs-components-tree-view/CustomizedTreeView.png', // Redux isolation + 'docs-components-badges/BadgeAlignment.png', 'docs-components-chips/ChipsPlayground.png', 'docs-components-popover/AnchorPlayground.png', 'docs-components-popper/ScrollPlayground.png',