Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CircularProgress] Migrate to emotion #24622

Merged
merged 10 commits into from
Jan 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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