Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions libs/react/ui-core/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './lib/progress/progress';
export * from './lib/switch/switch';
export * from './lib/modal/modal';
export * from './lib/no-data/no-data';
Expand Down
16 changes: 16 additions & 0 deletions libs/react/ui-core/src/lib/progress/ProgressProps.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { DefaultParams } from '../../default-types/defaultParams';

export interface ProgressProps extends DefaultParams {
/** Current step of the progress */
currentStep: number;
/** Total amount of steps */
steps: number;
/** Type of the progress */
type?: 'linear' | 'radial';
/** Size of the progress */
size?: 'small' | 'standart';
/** State of the process shown by progress */
state?: 'progress' | 'error' | 'success';
/** Formatter to show progress. By default x/y */
format?: (currentStep: number, steps: number) => string;
}
1 change: 1 addition & 0 deletions libs/react/ui-core/src/lib/progress/progress.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@use '../../../../../../styles/components/progress' as *;
10 changes: 10 additions & 0 deletions libs/react/ui-core/src/lib/progress/progress.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { render } from '@testing-library/react';

import { Progress } from './progress';

describe('Progress', () => {
it('should render successfully', () => {
const { baseElement } = render(<Progress currentStep={2} steps={4} />);
expect(baseElement).toBeTruthy();
});
});
14 changes: 14 additions & 0 deletions libs/react/ui-core/src/lib/progress/progress.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { ComponentStory, ComponentMeta } from '@storybook/react';
import { Progress } from './progress';

export default {
component: Progress,
title: 'Progress',
} as ComponentMeta<typeof Progress>;

const Template: ComponentStory<typeof Progress> = (args) => (
<Progress {...args} />
);

export const Primary = Template.bind({});
Primary.args = {currentStep: 0, steps: 10};
121 changes: 121 additions & 0 deletions libs/react/ui-core/src/lib/progress/progress.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import styles from './progress.module.scss';
import { forwardRef, memo, useMemo } from 'react';
import { ProgressProps } from './ProgressProps';
import { useProgress } from './useProgress';
import Icon from '../icon/icon';

const LinearBar = memo(({ fillPercentage }: { fillPercentage: string }) => {
return (
<svg
className={styles['figure']}
viewBox="0 0 650 11"
xmlns="http://www.w3.org/2000/svg"
preserveAspectRatio="none"
>
<rect className={styles['track']} y="0" width="650" height="11" rx="5" />
<rect
className={styles['indicator']}
style={{ width: fillPercentage }}
y="0"
width="650"
height="11"
rx="5"
/>
</svg>
);
});

const RadialBar = memo(
({ dashArray, dashOffset }: { dashArray: number; dashOffset: number }) => {
return (
<svg
viewBox="0 0 164 164"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className={styles['figure']}
>
<circle
className={styles['track']}
cx="82"
cy="82"
r="72"
strokeWidth="20"
/>
<circle
strokeDasharray={dashArray}
strokeDashoffset={dashOffset}
className={styles['indicator']}
strokeLinecap="round"
cx="82"
cy="82"
r="72"
strokeWidth="20"
/>
</svg>
);
}
);

const Label = memo(
({
labelText,
state,
type,
}: {
labelText?: string;
state?: 'progress' | 'error' | 'success';
type?: 'linear' | 'radial';
}) => {
const content = useMemo(() => {
switch (state) {
case 'progress':
return <div className={styles['label']}>{labelText}</div>;
break;
case 'error':
return (
<Icon
name={type === 'linear' ? 'close-circle' : 'close'}
type="fill"
className={styles['label-icon']}
/>
);
break;
case 'success':
return (
<Icon
name={type === 'linear' ? 'checkbox-circle' : 'check'}
type="fill"
className={styles['label-icon']}
/>
);
break;
default:
return <div className={styles['label']}>{labelText}</div>;
break;
}
}, [labelText, state, type]);
return content;
}
);

export const Progress = forwardRef((props: ProgressProps, ref: any) => {
const { classes, fillPercentage, labelText, dashArray, dashOffset } =
useProgress(props);
return (
<div className={classes} ref={ref} style={props.style}>
{props.type === 'linear' ? (
<LinearBar fillPercentage={fillPercentage} />
) : (
<RadialBar dashArray={dashArray} dashOffset={dashOffset} />
)}
<Label labelText={labelText} state={props.state} type={props.type} />
</div>
);
});

Progress.defaultProps = {
size: 'standart',
type: 'linear',
state: 'progress',
format: (currentStep: number, steps: number) => `${currentStep}/${steps}`,
};
52 changes: 52 additions & 0 deletions libs/react/ui-core/src/lib/progress/useProgress.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import styles from './progress.module.scss';
import { useMemo } from 'react';
import { getClasses } from '../../utils/getClasses';
import { ProgressProps } from './ProgressProps';

export function useProgress(props: ProgressProps) {
const classes = useMemo(() => {
const conditions = {
'progress': true,
'progress-success': props.state === 'success',
'progress-error': props.state === 'error',
'progress-regular': props.state === 'progress',
'progress-radial': props.type === 'radial',
'progress-linear': props.type === 'linear',
'progress-small': props.size === 'small',
'progress-standart': props.size === 'standart',
};
return getClasses(conditions, styles, props.className);
}, [props.state, props.size, props.className, props.type]);

const fillPortion = useMemo(() => {
// Rounds to 2 digits.
const portion = Math.round((props.currentStep / props.steps) * 100) / 100;
return portion <= 1 ? (portion >= 0 ? portion : 0) : 1;
}, [props.currentStep, props.steps]);

const dashArray = useMemo(() => {
const radius = 72;
return 2 * Math.PI * radius;
}, [props.size]);

const dashOffset = useMemo(() => {
return dashArray * (1 - fillPortion);
}, [dashArray, fillPortion]);

const fillPercentage = useMemo(() => {
return `${fillPortion * 100}%`;
}, [fillPortion]);

const labelText = useMemo(() => {
return props.format?.(props.currentStep, props.steps);
}, [props.currentStep, props.steps, props.format]);

return {
classes,
fillPortion,
fillPercentage,
labelText,
dashArray,
dashOffset,
};
}
164 changes: 164 additions & 0 deletions styles/components/progress/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
@use '../../../styles/design-tokens' as dt;

.progress {
position: relative;
box-sizing: border-box;

.label {
font-family: dt.$typo-font-p-regular-family;
font-style: dt.$typo-font-p-regular-style;
color: dt.$general-100;
}

.indicator {
transition: width 250ms, stroke-dashoffset 250ms;
}

&-linear {
display: flex;
align-items: center;
gap: 15px;
width: 100%;

.figure {
width: 100%;
}

.track {
fill: dt.$general-40;
}

.track,
.indicator {
height: 100%;
}

.label {
font-weight: dt.$typo-font-p-regular-weight;
line-height: 24px;
}
}

&-radial {
display: inline-block;
.label,
.label-icon {
position: absolute;
transform: translate(-50%, -50%);
top: 50%;
left: 50%;
}

.label {
font-weight: dt.$typo-font-p-medium-weight;
}

.figure {
transform: rotate(-90deg);
}

.track {
stroke: dt.$general-40;
}
}

// Colors
&-success {
.label-icon {
color: dt.$green-80;
}
}

&-error {
.label-icon {
color: dt.$red-100;
}
}

&-linear.progress-success {
.indicator {
fill: dt.$green-80;
}
}
&-linear.progress-error {
.indicator {
fill: dt.$red-100;
}
}
&-linear.progress-regular {
.indicator {
fill: dt.$primary-100;
}
}

&-radial.progress-success {
.indicator {
stroke: dt.$green-80;
}
}
&-radial.progress-error {
.indicator {
stroke: dt.$red-100;
}
}
&-radial.progress-regular {
.indicator {
stroke: dt.$primary-100;
}
}

// Sizes
&-linear.progress-standart {
height: 20px;
.figure {
height: 11px;
}
.label {
font-size: dt.$typo-font-p-regular-size;
}
.label-icon {
font-size: 20px;
}
}

&-linear.progress-small {
height: 15px;
.figure {
height: 7px;
}
.label {
font-size: 12px;
}
.label-icon {
font-size: 15px;
}
}

&-radial.progress-standart {
height: 164px;
.figure {
height: 164px;
}
.label {
font-size: 36px;
line-height: 44px;
}
.label-icon {
font-size: 55px;
}
}

&-radial.progress-small {
height: 82px;
.figure {
height: 82px;
}
.label {
font-size: 18px;
line-height: 22px;
}
.label-icon {
font-size: 20px;
}
}
}