Skip to content
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
32 changes: 32 additions & 0 deletions docs/data/material/components/progress/CircularEnableTrack.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import * as React from 'react';
import Stack from '@mui/material/Stack';
import CircularProgress from '@mui/material/CircularProgress';

export default function CircularEnableTrack() {
const [progress, setProgress] = React.useState(0);

React.useEffect(() => {
const timer = setInterval(() => {
setProgress((prevProgress) => (prevProgress >= 100 ? 0 : prevProgress + 10));
}, 800);

return () => {
clearInterval(timer);
};
}, []);

return (
<Stack spacing={2} direction="row">
<CircularProgress enableTrackSlot size="30px" />
<CircularProgress enableTrackSlot size={40} />
<CircularProgress enableTrackSlot size="3rem" />
<CircularProgress enableTrackSlot variant="determinate" value={70} />
<CircularProgress
enableTrackSlot
variant="determinate"
color="secondary"
value={progress}
/>
</Stack>
);
}
32 changes: 32 additions & 0 deletions docs/data/material/components/progress/CircularEnableTrack.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import * as React from 'react';
import Stack from '@mui/material/Stack';
import CircularProgress from '@mui/material/CircularProgress';

export default function CircularEnableTrack() {
const [progress, setProgress] = React.useState(0);

React.useEffect(() => {
const timer = setInterval(() => {
setProgress((prevProgress) => (prevProgress >= 100 ? 0 : prevProgress + 10));
}, 800);

return () => {
clearInterval(timer);
};
}, []);

return (
<Stack spacing={2} direction="row">
<CircularProgress enableTrackSlot size="30px" />
<CircularProgress enableTrackSlot size={40} />
<CircularProgress enableTrackSlot size="3rem" />
<CircularProgress enableTrackSlot variant="determinate" value={70} />
<CircularProgress
enableTrackSlot
variant="determinate"
color="secondary"
value={progress}
/>
</Stack>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<CircularProgress enableTrackSlot size="30px" />
<CircularProgress enableTrackSlot size={40} />
<CircularProgress enableTrackSlot size="3rem" />
<CircularProgress enableTrackSlot variant="determinate" value={70} />
<CircularProgress
enableTrackSlot
variant="determinate"
color="secondary"
value={progress}
/>
56 changes: 23 additions & 33 deletions docs/data/material/components/progress/CustomizedProgressBars.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as React from 'react';
import { styled } from '@mui/material/styles';
import Box from '@mui/material/Box';
import Stack from '@mui/material/Stack';
import CircularProgress, {
circularProgressClasses,
Expand Down Expand Up @@ -28,40 +27,31 @@ const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({
// Inspired by the former Facebook spinners.
function FacebookCircularProgress(props) {
return (
<Box sx={{ position: 'relative' }}>
<CircularProgress
variant="determinate"
sx={(theme) => ({
color: theme.palette.grey[200],
<CircularProgress
variant="indeterminate"
disableShrink
enableTrackSlot
sx={(theme) => ({
color: '#1a90ff',
animationDuration: '550ms',
[`& .${circularProgressClasses.circle}`]: {
strokeLinecap: 'round',
},
[`& .${circularProgressClasses.track}`]: {
opacity: 1,
stroke: (theme.vars || theme).palette.grey[200],
...theme.applyStyles('dark', {
color: theme.palette.grey[800],
stroke: (theme.vars || theme).palette.grey[800],
}),
})}
size={40}
thickness={4}
{...props}
value={100}
/>
<CircularProgress
variant="indeterminate"
disableShrink
sx={(theme) => ({
color: '#1a90ff',
animationDuration: '550ms',
position: 'absolute',
left: 0,
[`& .${circularProgressClasses.circle}`]: {
strokeLinecap: 'round',
},
...theme.applyStyles('dark', {
color: '#308fe8',
}),
})}
size={40}
thickness={4}
{...props}
/>
</Box>
},
...theme.applyStyles('dark', {
color: '#308fe8',
}),
})}
size={40}
thickness={4}
{...props}
/>
);
}

Expand Down
56 changes: 23 additions & 33 deletions docs/data/material/components/progress/CustomizedProgressBars.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as React from 'react';
import { styled } from '@mui/material/styles';
import Box from '@mui/material/Box';
import Stack from '@mui/material/Stack';
import CircularProgress, {
circularProgressClasses,
Expand Down Expand Up @@ -29,40 +28,31 @@ const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({
// Inspired by the former Facebook spinners.
function FacebookCircularProgress(props: CircularProgressProps) {
return (
<Box sx={{ position: 'relative' }}>
<CircularProgress
variant="determinate"
sx={(theme) => ({
color: theme.palette.grey[200],
<CircularProgress
variant="indeterminate"
disableShrink
enableTrackSlot
sx={(theme) => ({
color: '#1a90ff',
animationDuration: '550ms',
[`& .${circularProgressClasses.circle}`]: {
strokeLinecap: 'round',
},
[`& .${circularProgressClasses.track}`]: {
opacity: 1,
stroke: (theme.vars || theme).palette.grey[200],
...theme.applyStyles('dark', {
color: theme.palette.grey[800],
stroke: (theme.vars || theme).palette.grey[800],
}),
})}
size={40}
thickness={4}
{...props}
value={100}
/>
<CircularProgress
variant="indeterminate"
disableShrink
sx={(theme) => ({
color: '#1a90ff',
animationDuration: '550ms',
position: 'absolute',
left: 0,
[`& .${circularProgressClasses.circle}`]: {
strokeLinecap: 'round',
},
...theme.applyStyles('dark', {
color: '#308fe8',
}),
})}
size={40}
thickness={4}
{...props}
/>
</Box>
},
...theme.applyStyles('dark', {
color: '#308fe8',
}),
})}
size={40}
thickness={4}
{...props}
/>
);
}

Expand Down
4 changes: 4 additions & 0 deletions docs/data/material/components/progress/progress.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ The animations of the components rely on CSS as much as possible to work even be

{{"demo": "CircularDeterminate.js"}}

### Circular track

{{"demo": "CircularEnableTrack.js"}}

### Interactive integration

{{"demo": "CircularIntegration.js"}}
Expand Down
7 changes: 7 additions & 0 deletions docs/pages/material-ui/api/circular-progress.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"default": "'primary'"
},
"disableShrink": { "type": { "name": "custom", "description": "bool" }, "default": "false" },
"enableTrackSlot": { "type": { "name": "bool" }, "default": "false" },
"size": {
"type": { "name": "union", "description": "number<br>&#124;&nbsp;string" },
"default": "40"
Expand Down Expand Up @@ -94,6 +95,12 @@
"className": "MuiCircularProgress-svg",
"description": "Styles applied to the svg element.",
"isGlobal": false
},
{
"key": "track",
"className": "MuiCircularProgress-track",
"description": "Styles applied to the track slot if `enableTrackSlot={true}`.",
"isGlobal": false
}
],
"spread": true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
"disableShrink": {
"description": "If <code>true</code>, the shrink animation is disabled. This only works if variant is <code>indeterminate</code>."
},
"enableTrackSlot": {
"description": "If <code>true</code>, a track circle slot is mounted to show a subtle background for the progress. The <code>size</code> and <code>thickness</code> apply to the track slot to be consistent with the progress circle."
},
"size": {
"description": "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, for example &#39;3rem&#39;."
},
Expand Down Expand Up @@ -65,6 +68,11 @@
"conditions": "<code>variant=\"indeterminate\"</code>"
},
"root": { "description": "Styles applied to the root element." },
"svg": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the svg element" }
"svg": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the svg element" },
"track": {
"description": "Styles applied to {{nodeName}} if {{conditions}}.",
"nodeName": "the track slot",
"conditions": "<code>enableTrackSlot={true}</code>"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ export interface CircularProgressProps
* @default false
*/
disableShrink?: boolean;
/**
* If `true`, a track circle slot is mounted to show a subtle background for the progress.
* The `size` and `thickness` apply to the track slot to be consistent with the progress circle.
* @default false
*/
enableTrackSlot?: boolean;
/**
* The size of the component.
* If using a number, the pixel unit is assumed.
Expand Down
31 changes: 31 additions & 0 deletions packages/mui-material/src/CircularProgress/CircularProgress.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ const useUtilityClasses = (ownerState) => {
const slots = {
root: ['root', variant, `color${capitalize(color)}`],
svg: ['svg'],
track: ['track'],
circle: ['circle', `circle${capitalize(variant)}`, disableShrink && 'circleDisableShrink'],
};

Expand Down Expand Up @@ -166,6 +167,16 @@ const CircularProgressCircle = styled('circle', {
})),
);

const CircularProgressTrack = styled('circle', {
name: 'MuiCircularProgress',
slot: 'Track',
})(
memoTheme(({ theme }) => ({
stroke: 'currentColor',
opacity: (theme.vars || theme).palette.action.activatedOpacity,
})),
);

/**
* ## ARIA
*
Expand All @@ -179,6 +190,7 @@ const CircularProgress = React.forwardRef(function CircularProgress(inProps, ref
className,
color = 'primary',
disableShrink = false,
enableTrackSlot = false,
size = 40,
style,
thickness = 3.6,
Expand All @@ -195,6 +207,7 @@ const CircularProgress = React.forwardRef(function CircularProgress(inProps, ref
thickness,
value,
variant,
enableTrackSlot,
};

const classes = useUtilityClasses(ownerState);
Expand Down Expand Up @@ -226,6 +239,18 @@ const CircularProgress = React.forwardRef(function CircularProgress(inProps, ref
ownerState={ownerState}
viewBox={`${SIZE / 2} ${SIZE / 2} ${SIZE} ${SIZE}`}
>
{enableTrackSlot ? (
<CircularProgressTrack
className={classes.track}
ownerState={ownerState}
cx={SIZE}
cy={SIZE}
r={(SIZE - thickness) / 2}
fill="none"
strokeWidth={thickness}
aria-hidden="true"
/>
) : null}
<CircularProgressCircle
className={classes.circle}
style={circleStyle}
Expand Down Expand Up @@ -279,6 +304,12 @@ CircularProgress.propTypes /* remove-proptypes */ = {

return null;
}),
/**
* If `true`, a track circle slot is mounted to show a subtle background for the progress.
* The `size` and `thickness` apply to the track slot to be consistent with the progress circle.
* @default false
*/
enableTrackSlot: PropTypes.bool,
/**
* The size of the component.
* If using a number, the pixel unit is assumed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,37 @@ describe('<CircularProgress />', () => {
expect(circle).to.have.class(classes.circleDisableShrink);
});
});

describe('prop: enableTrackSlot', () => {
it('does not render track by default', () => {
const { container } = render(<CircularProgress />);
const circles = container.querySelectorAll('svg circle');
expect(circles.length).to.equal(1);
});

it('renders track when enableTrackSlot is true', () => {
const { container } = render(<CircularProgress enableTrackSlot />);
const circles = container.querySelectorAll('svg circle');
expect(circles.length).to.equal(2);
expect(circles[0]).to.have.class(classes.track);
expect(circles[0]).to.have.attribute('aria-hidden', 'true');
});

it('track and circle share geometry (r, strokeWidth)', () => {
const thickness = 5;
const { container } = render(<CircularProgress enableTrackSlot thickness={thickness} />);
const [trackEl, circleEl] = container.querySelectorAll('svg circle');
expect(trackEl.getAttribute('r')).to.equal(circleEl.getAttribute('r'));
expect(trackEl.getAttribute('stroke-width')).to.equal(String(thickness));
});

it('track has no dash styles in determinate', () => {
const { container } = render(
<CircularProgress enableTrackSlot variant="determinate" value={70} />,
);
const [trackEl] = container.querySelectorAll('svg circle');
expect(trackEl.style.strokeDasharray).to.equal('');
expect(trackEl.style.strokeDashoffset).to.equal('');
});
});
});
Loading
Loading