Skip to content
Open
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
5 changes: 5 additions & 0 deletions packages/configs/eslint-config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export default tseslint.config(
'Button',
'ButtonBase',
'Checkbox',
'CircularProgress',
'Dialog',
'DialogActions',
'DialogContent',
Expand Down Expand Up @@ -146,6 +147,10 @@ export default tseslint.config(
group: ['@mui/material/Checkbox'],
importNames: ['default'],
},
{
group: ['@mui/material/CircularProgress'],
importNames: ['default'],
},
{
group: ['@mui/material/Dialog'],
importNames: ['default'],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Meta } from '@storybook/addon-docs';
import LinkTo from '@storybook/addon-links/react';
import { TableInterface } from '~storybook/components/TableInterface';

<Meta title="Components API/CircularProgress" />

# CircularProgress API

```js
import { CircularProgress } from '@elonkit/react';
```

## Component name

The name `ESCircularProgress` can be used when providing default props or style overrides in the theme.

## Props

<TableInterface name="CircularProgressProps" variant="props" />

<br />

## CSS

<TableInterface name="CircularProgressClasses" variant="css" />

<br />

## Demos

<ul>
<li>
<LinkTo kind="components-CircularProgress" story="demo">
<code>CircularProgress</code>
</LinkTo>
</li>
</ul>
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { generateUtilityClass, generateUtilityClasses } from '@mui/material';

export interface CircularProgressClasses {
/** Styles applied to the root element. */
root: string;
/** Styles applied to the root element if `variant="determinate"`. */
determinate: string;
/** Styles applied to the root element if `variant="indeterminate"`. */
indeterminate: string;
/** Styles applied to the root element if `color="primary"`. */
colorPrimary: string;
/** Styles applied to the root element if `color="secondary"`. */
colorSecondary: string;
/** Styles applied to the svg element. */
svg: string;
/** Styles applied to the `circle` svg path. */
circle: string;
/** Styles applied to the `circle` svg path if `variant="determinate"`.
* @deprecated Combine the [.MuiCircularProgress-circle](/material-ui/api/circular-progress/#circular-progress-classes-circle) and [.MuiCircularProgress-determinate](/material-ui/api/circular-progress/#circular-progress-classes-determinate) classes instead. See [Migrating from deprecated APIs](/material-ui/migration/migrating-from-deprecated-apis/) for more details.
*/
circleDeterminate: string;
/** Styles applied to the `circle` svg path if `variant="indeterminate"`.
* @deprecated Combine the [.MuiCircularProgress-circle](/material-ui/api/circular-progress/#circular-progress-classes-circle) and [.MuiCircularProgress-indeterminate](/material-ui/api/circular-progress/#circular-progress-classes-indeterminate) classes instead. See [Migrating from deprecated APIs](/material-ui/migration/migrating-from-deprecated-apis/) for more details.
*/
circleIndeterminate: string;
/** Styles applied to the `circle` svg path if `disableShrink={true}`. */
circleDisableShrink: string;
/** Styles applied to the background `circle` svg path. */
background: string;
/** Styles applied to the content. */
content: string;
}

export type CircularProgressClassKey = keyof CircularProgressClasses;

export function getCircularProgressUtilityClass(slot: string): string {
return generateUtilityClass('MuiCircularProgress', slot);
}

export const circularProgressClasses: CircularProgressClasses = generateUtilityClasses('MuiCircularProgress', [
'root',
'determinate',
'indeterminate',
'colorPrimary',
'colorSecondary',
'svg',
'circle',
'circleDeterminate',
'circleIndeterminate',
'circleDisableShrink',
'background',
'content',
]);
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { Meta, StoryObj } from '@storybook/react';

import { Typography } from '@mui/material';

import { CircularProgress } from '.';

const meta: Meta<typeof CircularProgress> = {
tags: ['autodocs'],
component: CircularProgress,
parameters: {
references: ['CircularProgress'],
},
argTypes: {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

В демке контрол size как объект отображается, надо бы number сделать

variant: {
options: ['determinate', 'indeterminate'],
control: {
type: 'select',
},
},
color: {
options: ['primary', 'secondary', 'error', 'info', 'success', 'warning', 'inherit'],
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Падает демка

Suggested change
options: ['primary', 'secondary', 'error', 'info', 'success', 'warning', 'inherit'],
options: ['primary', 'secondary', 'error', 'info', 'success', 'warning'],

control: {
type: 'select',
},
},
},
args: {
variant: 'indeterminate',
color: 'primary',
value: 20,
},
};

export default meta;
type Story = StoryObj<typeof CircularProgress>;

export const Demo: Story = {
render: (args) => {
return (
<CircularProgress {...args}>
<Typography
color="monoA.A700"
component="div"
variant="caption"
>{`${Math.round(args.value ?? 0)}%`}</Typography>
</CircularProgress>
);
},
};
231 changes: 231 additions & 0 deletions packages/react/src/components/CircularProgress/CircularProgress.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
import { forwardRef } from 'react';

import { CircularProgressProps } from './CircularProgress.types';

import clsx from 'clsx';
import { getCircularProgressUtilityClass } from './CircularProgress.classes';

import { unstable_composeClasses as composeClasses } from '@mui/base';

import { styled, useThemeProps } from '@mui/material/styles';
import { css, keyframes } from '@mui/system';
import { capitalize } from '@mui/material';

const SIZE = 44;

const circularRotateKeyframe = keyframes`
0% {
transform: rotate(0deg);
}

100% {
transform: rotate(360deg);
}
`;

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

50% {
stroke-dasharray: 100px, 200px;
stroke-dashoffset: -15px;
}

100% {
stroke-dasharray: 100px, 200px;
stroke-dashoffset: -125px;
}
`;

type CircularProgressOwnerState = {
classes?: CircularProgressProps['classes'];
variant: NonNullable<CircularProgressProps['variant']>;
color: NonNullable<CircularProgressProps['color']>;
disableShrink: NonNullable<CircularProgressProps['disableShrink']>;
thickness: NonNullable<CircularProgressProps['thickness']>;
value: NonNullable<CircularProgressProps['value']>;
};

const useUtilityClasses = (ownerState: CircularProgressOwnerState) => {
const { classes, variant, color, disableShrink } = ownerState;

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

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

const CircularProgressRoot = styled('span', {
name: 'ESCircularProgress',
slot: 'Root',
overridesResolver: (props, styles) => {
const { ownerState } = props;

return [styles.root, styles[ownerState.variant], styles[`color${capitalize(ownerState.color)}`]];
},
})<{ ownerState: CircularProgressOwnerState }>(({ ownerState: { color, variant }, theme }) => ({
display: 'inline-block',
color: theme.vars.palette[color][300],
position: 'relative',

...(variant === 'determinate' && {
transition: theme.transitions.create('transform'),
}),
}));

const CircularProgressSVG = styled('svg', {
name: 'ESCircularProgress',
slot: 'Svg',
overridesResolver: (props, styles) => {
return styles.svg;
},
})<{ ownerState: CircularProgressOwnerState }>(({ ownerState: { variant } }) => ({
transform: 'rotate(-90deg)',
display: 'block',
...(variant === 'indeterminate' && {
animation: `${circularRotateKeyframe} 1.4s linear infinite`,
}),
}));

const CircularProgressCircle = styled('circle', {
name: 'ESCircularProgress',
slot: 'Circle',
overridesResolver: (props, styles) => {
const { ownerState } = props;

return [
styles.circle,
styles[`circle${capitalize(ownerState.variant)}`],
ownerState.disableShrink && styles.circleDisableShrink,
];
},
})<{ ownerState: CircularProgressOwnerState }>(
({ ownerState: { variant, thickness, value }, theme }) => ({
stroke: 'currentColor',
...(variant === 'determinate' && {
transition: theme.transitions.create('stroke-dashoffset'),
strokeDasharray: (2 * Math.PI * ((SIZE - thickness) / 2)).toFixed(3),
strokeDashoffset: `${(((100 - value) / 100) * (2 * Math.PI * ((SIZE - thickness) / 2))).toFixed(3)}px`,
}),
...(variant === 'indeterminate' && {
strokeDasharray: '80px, 200px',
strokeDashoffset: 0,
animation: `${circularDashKeyframe} 1.4s ease-in-out infinite`,
}),
}),
({ ownerState }) =>
ownerState.variant === 'indeterminate' &&
css`
animation: ${circularDashKeyframe} 1.4s ease-in-out infinite;
`
);

const CircularProgressBackground = styled('circle', {
name: 'ESCircularProgress',
slot: 'Background',
overridesResolver: (props, styles) => styles.background,
})<{ ownerState: CircularProgressOwnerState }>(({ ownerState: { color }, theme }) => ({
stroke: theme.vars.palette[color].A400,
transition: theme.transitions.create('stroke-dashoffset'),
}));

const CircularProgressContent = styled('div', {
name: 'ESCircularProgress',
slot: 'Content',
overridesResolver: (props, styles) => styles.content,
})(() => ({
top: 0,
left: 0,
bottom: 0,
right: 0,
position: 'absolute',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}));

export const CircularProgress = forwardRef<HTMLButtonElement, CircularProgressProps>(
function CircularProgress(inProps, ref) {
const props = useThemeProps({ props: inProps, name: 'ESCircularProgress' });
const {
children,
className,
color = 'primary',
disableShrink = false,
size = 40,
thickness = 3.6,
value = 0,
variant = 'indeterminate',
...other
} = props;

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

const classes = useUtilityClasses(ownerState);

// const circleStyle = {};
// const rootStyle = {};
// const rootProps = {};

// if (variant === 'determinate') {
// const circumference = 2 * Math.PI * ((SIZE - thickness) / 2);
// circleStyle.strokeDasharray = circumference.toFixed(3);
// circleStyle.strokeDashoffset = `${(((100 - value) / 100) * circumference).toFixed(3)}px`;
// rootStyle.transform = 'rotate(-90deg)';
// }

return (
<CircularProgressRoot
ref={ref}
className={clsx(classes.root, className)}
ownerState={ownerState}
role="progressbar"
style={{ width: size, height: size }}
{...other}
>
<CircularProgressSVG
className={classes.svg}
ownerState={ownerState}
viewBox={`${SIZE / 2} ${SIZE / 2} ${SIZE} ${SIZE}`}
>
<CircularProgressCircle
className={classes.circle}
cx={SIZE}
cy={SIZE}
fill="none"
ownerState={ownerState}
r={(SIZE - thickness) / 2}
strokeWidth={thickness}
/>
<CircularProgressBackground
className={classes.background}
cx={SIZE}
cy={SIZE}
fill="none"
ownerState={ownerState}
r={(SIZE - thickness) / 2}
strokeWidth={thickness}
/>
</CircularProgressSVG>

{!!children && <CircularProgressContent className={classes.content}>{children}</CircularProgressContent>}
</CircularProgressRoot>
);
}
);
Loading