Skip to content

Commit

Permalink
[CircularProgress] Migrate to emotion (#24622)
Browse files Browse the repository at this point in the history
  • Loading branch information
natac13 authored Jan 27, 2021
1 parent 0645524 commit 93b6eb9
Show file tree
Hide file tree
Showing 10 changed files with 223 additions and 101 deletions.
3 changes: 2 additions & 1 deletion docs/pages/api-docs/circular-progress.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"type": { "name": "union", "description": "number<br>&#124;&nbsp;string" },
"default": "40"
},
"sx": { "type": { "name": "object" } },
"thickness": { "type": { "name": "number" }, "default": "3.6" },
"value": { "type": { "name": "number" }, "default": "0" },
"variant": {
Expand Down Expand Up @@ -42,6 +43,6 @@
"filename": "/packages/material-ui/src/CircularProgress/CircularProgress.js",
"inheritance": null,
"demos": "<ul><li><a href=\"/components/progress/\">Progress</a></li></ul>",
"styledComponent": false,
"styledComponent": true,
"cssComponent": false
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"color": "The color of the component. It supports those theme colors that make sense for this component.",
"disableShrink": "If <code>true</code>, the shrink animation is disabled. This only works if variant is <code>indeterminate</code>.",
"size": "The size of the component. If using a number, the pixel unit is assumed. If using a string, you need to provide the CSS unit, e.g &#39;3rem&#39;.",
"sx": "The system prop that allows defining system overrides as well as additional CSS styles. See the <a href=\"/system/basics/#the-sx-prop\">`sx` page</a> for more details.",
"thickness": "The thickness of the circle.",
"value": "The value of the progress indicator for the determinate variant. Value between 0 and 100.",
"variant": "The variant to use. Use indeterminate when there is no progress value."
Expand Down
2 changes: 1 addition & 1 deletion framer/scripts/framerConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ export const componentSettings = {
template: 'chip.txt',
},
CircularProgress: {
ignoredProps: ['disableShrink', 'size'],
ignoredProps: ['disableShrink', 'size', 'sx'],
propValues: {
width: 44,
height: 44,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { SxProps } from '@material-ui/system';
import * as React from 'react';
import { InternalStandardProps as StandardProps } from '..';
import { InternalStandardProps as StandardProps, Theme } from '..';

export interface CircularProgressProps
extends StandardProps<React.HTMLAttributes<HTMLSpanElement>, 'children'> {
Expand Down Expand Up @@ -46,6 +47,10 @@ export interface CircularProgressProps
* @default 40
*/
size?: number | string;
/**
* The system prop that allows defining system overrides as well as additional CSS styles.
*/
sx?: SxProps<Theme>;
/**
* The thickness of the circle.
* @default 3.6
Expand Down
250 changes: 161 additions & 89 deletions packages/material-ui/src/CircularProgress/CircularProgress.js
Original file line number Diff line number Diff line change
@@ -1,92 +1,154 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';
import { chainPropTypes } from '@material-ui/utils';
import withStyles from '../styles/withStyles';
import { chainPropTypes, deepmerge } from '@material-ui/utils';
import { unstable_composeClasses as composeClasses } from '@material-ui/unstyled';
import { keyframes } from '@material-ui/styled-engine';
import capitalize from '../utils/capitalize';

import useThemeProps from '../styles/useThemeProps';
import experimentalStyled from '../styles/experimentalStyled';
import circularProgressClasses, {
getCircularProgressUtilityClass,
} from './circularProgressClasses';

const SIZE = 44;

export const styles = (theme) => ({
/* Styles applied to the root element. */
root: {
display: 'inline-block',
},
/* Styles applied to the root element if `variant="determinate"`. */
determinate: {
transition: theme.transitions.create('transform'),
},
/* Styles applied to the root element if `variant="indeterminate"`. */
indeterminate: {
animation: '$circular-rotate 1.4s linear infinite',
},
/* Styles applied to the root element if `color="primary"`. */
colorPrimary: {
color: theme.palette.primary.main,
},
/* Styles applied to the root element if `color="secondary"`. */
colorSecondary: {
color: theme.palette.secondary.main,
},
/* Styles applied to the svg element. */
svg: {
display: 'block', // Keeps the progress centered
},
/* Styles applied to the `circle` svg path. */
circle: {
stroke: 'currentColor',
// Use butt to follow the specification, by chance, it's already the default CSS value.
// strokeLinecap: 'butt',
},
/* Styles applied to the `circle` svg path if `variant="determinate"`. */
circleDeterminate: {
transition: theme.transitions.create('stroke-dashoffset'),
},
/* Styles applied to the `circle` svg path if `variant="indeterminate"`. */
circleIndeterminate: {
animation: '$circular-dash 1.4s ease-in-out infinite',
// Some default value that looks fine waiting for the animation to kicks in.
strokeDasharray: '80px, 200px',
strokeDashoffset: '0px', // Add the unit to fix a Edge 16 and below bug.
},
'@keyframes circular-rotate': {
'0%': {
transform: 'rotate(0deg)',
},
'100%': {
transform: 'rotate(360deg)',
},
},
'@keyframes circular-dash': {
'0%': {
strokeDasharray: '1px, 200px',
strokeDashoffset: '0px',
},
'50%': {
strokeDasharray: '100px, 200px',
strokeDashoffset: '-15px',
},
'100%': {
strokeDasharray: '100px, 200px',
strokeDashoffset: '-125px',
const circularRotateKeyframe = keyframes`
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
`;

const circularDashKeyframe = keyframes`
0% {
stroke-dasharray: 1px, 200px;
stroke-dashoffset: 0px;
}
50% {
stroke-dasharray: 100px, 200px;
stroke-dashoffset: -15px;
}
100% {
stroke-dasharray: 100px, 200px;
stroke-dashoffset: -125px;
}
`;

const overridesResolver = (props, styles) => {
const { styleProps } = props;

return deepmerge(styles.root || {}, {
...styles[styleProps.variant],
...styles[`color${capitalize(styleProps.color)}`],
[`& .${circularProgressClasses.svg}`]: styles.svg,
[`& .${circularProgressClasses.circle}`]: {
...styles.circle,
...styles[`circle${capitalize(styleProps.variant)}`],
...(styleProps.disableShrink && styles.circleDisableShrink),
},
});
};

const useUtilityClasses = (styleProps) => {
const { classes, variant, color, disableShrink } = styleProps;

const slots = {
root: ['root', variant, `color${capitalize(color)}`],
svg: ['svg'],
circle: ['circle', `circle${capitalize(variant)}`, disableShrink && 'circleDisableShrink'],
};

return composeClasses(slots, getCircularProgressUtilityClass, classes);
};

// This `styled()` function invokes keyframes. `styled-components` only supports keyframes
// in string templates. Do not convert these styles in JS object as it will break.
const CircularProgressRoot = experimentalStyled(
'span',
{},
{
name: 'MuiCircularProgress',
slot: 'Root',
overridesResolver,
},
/* Styles applied to the `circle` svg path if `disableShrink={true}`. */
circleDisableShrink: {
animation: 'none',
)`
display: inline-block;
transition: ${({ styleProps, theme }) =>
styleProps.variant === 'determinate' ? theme.transitions.create('transform') : undefined};
animation-name: ${({ styleProps }) =>
styleProps.variant === 'indeterminate' && circularRotateKeyframe};
animation-duration: ${({ styleProps }) => styleProps.variant === 'indeterminate' && '1.4s'};
animation-timing-function: ${({ styleProps }) =>
styleProps.variant === 'indeterminate' && 'linear'};
animation-iteration-count: ${({ styleProps }) =>
styleProps.variant === 'indeterminate' && 'infinite'};
color: ${({ styleProps, theme }) =>
styleProps.color === 'primary' || styleProps.color === 'secondary'
? theme.palette[styleProps.color].main
: undefined};
`;

const CircularProgressSVG = experimentalStyled(
'svg',
{},
{
name: 'MuiCircularProgress',
slot: 'Svg',
},
)({
/* Styles applied to the svg element. */
display: 'block', // Keeps the progress centered
});

// This `styled()` function invokes keyframes. `styled-components` only supports keyframes
// in string templates. Do not convert these styles in JS object as it will break.
const CircularProgressCircle = experimentalStyled(
'circle',
{},
{
name: 'MuiCircularProgress',
slot: 'Circle',
},
)`
stroke: currentColor;
transition: ${({ styleProps, theme }) =>
styleProps.variant === 'determinate'
? theme.transitions.create('stroke-dashoffset')
: undefined};
animation-name: ${({ styleProps }) => {
if (styleProps.disableShrink) {
return 'none';
}
return styleProps.variant === 'indeterminate' ? circularDashKeyframe : undefined;
}};
animation-duration: ${({ styleProps }) => styleProps.variant === 'indeterminate' && '1.4s'};
animation-timing-function: ${({ styleProps }) =>
styleProps.variant === 'indeterminate' && 'ease-in-out'};
animation-iteration-count: ${({ styleProps }) =>
styleProps.variant === 'indeterminate' && 'infinite'};
stroke-dasharray: ${({ styleProps }) => styleProps.variant === 'indeterminate' && '80px, 200px'};
stroke-dashoffset: ${({ styleProps }) => styleProps.variant === 'indeterminate' && '0px'};
`;

/**
* ## ARIA
*
* If the progress bar is describing the loading progress of a particular region of a page,
* you should use `aria-describedby` to point to the progress bar, and set the `aria-busy`
* attribute to `true` on that region until it has finished loading.
*/
const CircularProgress = React.forwardRef(function CircularProgress(props, ref) {
const CircularProgress = React.forwardRef(function CircularProgress(inProps, ref) {
const props = useThemeProps({ props: inProps, name: 'MuiCircularProgress' });
const {
classes,
className,
color = 'primary',
disableShrink = false,
Expand All @@ -98,6 +160,18 @@ const CircularProgress = React.forwardRef(function CircularProgress(props, ref)
...other
} = props;

const styleProps = {
...props,
color,
disableShrink,
size,
thickness,
value,
variant,
};

const classes = useUtilityClasses(styleProps);

const circleStyle = {};
const rootStyle = {};
const rootProps = {};
Expand All @@ -111,38 +185,32 @@ const CircularProgress = React.forwardRef(function CircularProgress(props, ref)
}

return (
<span
className={clsx(
classes.root,
{
[classes[`color${capitalize(color)}`]]: color !== 'inherit',
[classes.determinate]: variant === 'determinate',
[classes.indeterminate]: variant === 'indeterminate',
},
className,
)}
<CircularProgressRoot
className={clsx(classes.root, className)}
style={{ width: size, height: size, ...rootStyle, ...style }}
styleProps={styleProps}
ref={ref}
role="progressbar"
{...rootProps}
{...other}
>
<svg className={classes.svg} viewBox={`${SIZE / 2} ${SIZE / 2} ${SIZE} ${SIZE}`}>
<circle
className={clsx(classes.circle, {
[classes.circleDeterminate]: variant === 'determinate',
[classes.circleIndeterminate]: variant === 'indeterminate',
[classes.circleDisableShrink]: disableShrink,
})}
<CircularProgressSVG
className={classes.svg}
styleProps={styleProps}
viewBox={`${SIZE / 2} ${SIZE / 2} ${SIZE} ${SIZE}`}
>
<CircularProgressCircle
className={classes.circle}
style={circleStyle}
styleProps={styleProps}
cx={SIZE}
cy={SIZE}
r={(SIZE - thickness) / 2}
fill="none"
strokeWidth={thickness}
/>
</svg>
</span>
</CircularProgressSVG>
</CircularProgressRoot>
);
});

Expand Down Expand Up @@ -190,6 +258,10 @@ CircularProgress.propTypes = {
* @ignore
*/
style: PropTypes.object,
/**
* The system prop that allows defining system overrides as well as additional CSS styles.
*/
sx: PropTypes.object,
/**
* The thickness of the circle.
* @default 3.6
Expand All @@ -209,4 +281,4 @@ CircularProgress.propTypes = {
variant: PropTypes.oneOf(['determinate', 'indeterminate']),
};

export default withStyles(styles, { name: 'MuiCircularProgress', flip: false })(CircularProgress);
export default CircularProgress;
Loading

0 comments on commit 93b6eb9

Please sign in to comment.