diff --git a/src/components/presentational/ProgressRing/ProgressRing.css b/src/components/presentational/ProgressRing/ProgressRing.css index 8dbe0b080..276af928f 100644 --- a/src/components/presentational/ProgressRing/ProgressRing.css +++ b/src/components/presentational/ProgressRing/ProgressRing.css @@ -10,27 +10,22 @@ .mdhui-progress-ring { position: relative; margin: 0 auto 0 auto; - width: 220px; - height: 220px; - --mdhui-progress-ring-animation-start: 630; --mdhui-progress-ring-animation-end: 0; } -.mdhui-progress-ring svg { - width: 220px; - height: 220px; +.mdhui-progress-ring > svg { + position: relative; transform: rotate(-90deg); + z-index: 1; } -.mdhui-progress-ring circle { +.mdhui-progress-ring > svg circle { margin: 0 auto; fill: none; - stroke-width: 20; - stroke-dasharray: 630; stroke-linecap: round; } -.mdhui-progress-ring circle:nth-child(2) { +.mdhui-progress-ring > svg circle:nth-child(n+2) { stroke: var(--mdhui-color-primary); animation-fill-mode: forwards; } diff --git a/src/components/presentational/ProgressRing/ProgressRing.stories.css b/src/components/presentational/ProgressRing/ProgressRing.stories.css deleted file mode 100644 index ea0b38998..000000000 --- a/src/components/presentational/ProgressRing/ProgressRing.stories.css +++ /dev/null @@ -1,4 +0,0 @@ -.progress-ring-story { - font-weight: bold; - font-size: 28px; -} \ No newline at end of file diff --git a/src/components/presentational/ProgressRing/ProgressRing.stories.tsx b/src/components/presentational/ProgressRing/ProgressRing.stories.tsx index fa2b61991..5cf28bcbe 100644 --- a/src/components/presentational/ProgressRing/ProgressRing.stories.tsx +++ b/src/components/presentational/ProgressRing/ProgressRing.stories.tsx @@ -1,97 +1,81 @@ import React from 'react'; import { Layout } from '../../presentational'; -import ProgressRing, { ProgressRingProps } from './ProgressRing'; -import './ProgressRing.stories.css'; +import ProgressRing from './ProgressRing'; +import { StoryObj } from '@storybook/react'; +import { argTypesToHide } from '../../../../.storybook/helpers'; + +type ProgressRingStoryArgs = React.ComponentProps & { + colorScheme: 'auto' | 'light' | 'dark'; +}; export default { title: 'Presentational/ProgressRing', component: ProgressRing, - parameters: {layout: 'fullscreen'} -}; - -const render = (args: ProgressRingProps) => - -const children =
Great Job!
; - -export const Default = { - args: { - children: children - }, - render: render -}; - -export const OneThirdDone = { - args: { - children: children, - percentCompleted: 33 - }, - render: render -}; - -export const TwoThirdsDone = { - args: { - children: children, - percentCompleted: 66 - }, - render: render -}; - -export const Done = { - args: { - children: children, - percentCompleted: 100 - }, - render: render -}; - -export const DifferentColor = { - args: { - children: children, - color: '#71b345' - }, - render: render -}; - -export const Default_Animated = { - args: { - children: children, - animate: true - }, - render: render -}; - -export const OneThirdDone_Animated = { - args: { - children: children, - percentCompleted: 33, - animate: true - }, - render: render -}; - -export const TwoThirdsDone_Animated = { - args: { - children: children, - percentCompleted: 66, - animate: true - }, - render: render + parameters: { layout: 'fullscreen' }, + render: (args: ProgressRingStoryArgs) => { + return + +
Great Job!
+
+
; + } }; -export const Done_Animated = { +export const Default: StoryObj = { args: { - children: children, + colorScheme: 'auto', + diameter: 220, + strokeWidth: 20, + color: undefined, + incompleteColor: undefined, percentCompleted: 100, animate: true }, - render: render -}; - -export const DifferentColor_Animated = { - args: { - children: children, - color: '#71b345', - animate: true - }, - render: render + argTypes: { + colorScheme: { + name: 'color scheme', + control: 'radio', + options: ['auto', 'light', 'dark'] + }, + diameter: { + name: 'diameter', + control: { + type: 'range', + min: 40, + max: 400, + step: 1 + } + }, + strokeWidth: { + name: 'strokeWidth', + control: { + type: 'range', + min: 1, + max: 40, + step: 1 + } + }, + color: { + name: 'color', + control: 'color' + }, + incompleteColor: { + name: 'incomplete color', + control: 'color' + }, + percentCompleted: { + name: 'percent completed', + control: { + type: 'range', + min: 0, + max: 100, + step: 1 + } + }, + animate: { + name: 'animate', + control: 'boolean' + }, + ...argTypesToHide(['style', 'innerRef']) + } }; \ No newline at end of file diff --git a/src/components/presentational/ProgressRing/ProgressRing.tsx b/src/components/presentational/ProgressRing/ProgressRing.tsx index dfbd843af..f5fa4cac9 100644 --- a/src/components/presentational/ProgressRing/ProgressRing.tsx +++ b/src/components/presentational/ProgressRing/ProgressRing.tsx @@ -1,25 +1,67 @@ -import React, { CSSProperties, useContext } from 'react' -import './ProgressRing.css' -import { ColorDefinition, resolveColor } from "../../../helpers/colors"; -import { LayoutContext } from "../Layout"; +import React, { CSSProperties, ReactNode, Ref, useContext } from 'react'; +import './ProgressRing.css'; +import { ColorDefinition, resolveColor } from '../../../helpers'; +import { LayoutContext } from '../Layout'; +import { v4 as uuid } from 'uuid'; export interface ProgressRingProps { - children: React.ReactNode; - style?: CSSProperties; + diameter?: number; + strokeWidth?: number; color?: ColorDefinition; + incompleteColor?: ColorDefinition; percentCompleted?: number; animate?: boolean; + style?: CSSProperties; + children: ReactNode; + innerRef?: Ref; } -export default function (props: ProgressRingProps) { +export default function ProgressRing(props: ProgressRingProps) { const context = useContext(LayoutContext); + const diameter = props.diameter ?? 220; + const strokeWidth = props.strokeWidth ?? 20; + + const circleDiameter = diameter - strokeWidth; + const circleCircumference = circleDiameter * Math.PI; + let offset = 0; - if (props.percentCompleted) { - offset = 630 - ((props.percentCompleted / 100.0) * 630); + if (props.percentCompleted !== undefined) { + offset = circleCircumference - ((props.percentCompleted / 100.0) * circleCircumference); } - const circleStyle = { + const ringStyle = { + width: `${diameter}px`, + height: `${diameter}px`, + '--mdhui-progress-ring-animation-start': circleCircumference + } as CSSProperties; + + const svgStyle = { + width: `${diameter}px`, + height: `${diameter}px`, + overflow: 'visible' + } as CSSProperties; + + const backgroundCircleStyle = { + strokeWidth: strokeWidth, + strokeDasharray: circleCircumference, + stroke: resolveColor(context.colorScheme, props.incompleteColor) ?? 'transparent' + } as CSSProperties; + + const foregroundCircleMaskId = uuid(); + const foregroundCircleMaskStyle = { + strokeWidth: strokeWidth + 2, + strokeDasharray: circleCircumference, + stroke: 'black', + strokeDashoffset: offset, + strokeLinecap: 'butt', + animation: props.animate ? 'mdhui-progress-ring 750ms ease-in-out' : undefined, + '--mdhui-progress-ring-animation-end': offset + } as CSSProperties; + + const foregroundCircleStyle = { + strokeWidth: strokeWidth, + strokeDasharray: circleCircumference, stroke: resolveColor(context.colorScheme, props.color), strokeDashoffset: offset, animation: props.animate ? 'mdhui-progress-ring 750ms ease-in-out' : undefined, @@ -33,15 +75,19 @@ export default function (props: ProgressRingProps) { animationFillMode: 'forwards' } as CSSProperties; - return ( -
- - - - -
- {props.children} -
+ return
+ + {offset != circleCircumference && + + + + + } + + + +
+ {props.children}
- ); +
; } \ No newline at end of file