Closed sonigeez closed 4 weeks ago
This can be easily done with tailwind animate-spin
;
// Icon.tsx
import {Loader2} from 'lucide-react';
export const Icons = {
spinner: Loader2,
};
// Usage
<Icons.spinner className="h-4 w-4 animate-spin" />
I think they meant something like this, where you can have a specific progress % rather than a general spinner:
@sonigeez , @AnandChowdhary provided more context. I think everyone meant the above.
@sonigeez @AnandChowdhary any chance this will get picked up again?
+1
+1 was also looking for this
+1
looking for circular progress
+1
+1
+1
+1
This would be a great addition
+1
+1
+1
+1
+1
+1
+1
+1
+1 for Radial progress
+1
+1
+1
+1
+1
+1
Paste this code into progress.tsx and utilize CircleProgress.
export const CircleProgress = React.forwardRef<
React.ElementRef<typeof ProgressPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>
>(({ className, value, ...props }, ref) => (
<ProgressPrimitive.Root
ref={ref}
className={cn(
`relative h-20 w-20 overflow-hidden rounded-full bg-primary/20 flex justify-center items-center`,
className
)}
{...props}
style={{ background: `radial-gradient(closest-side, white 79%, transparent 80% 100%), conic-gradient(green ${(value || 0)}%, pink 0)` }}
>
<div className="">{`${(value || 0)}%`}</div>
</ProgressPrimitive.Root>
))
this is my solution
import { motion } from 'framer-motion';
type Props = {
value: number;
};
export default function CircleProgress({ value }: Props) {
const percentage = Math.min(Math.max(value, 0), 100);
const width = 216;
const radius = 98;
const circumference = 2 * Math.PI * radius;
const offset = circumference - (percentage / 100) * circumference;
return (
<svg width={width} height={width} xmlns="http://www.w3.org/2000/svg">
<defs>
<radialGradient
id="circle-progress"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(53.1659 -18.1884) rotate(51.1683) scale(267.012 282.957)"
>
<stop stopColor="#F05F84" />
<stop offset="1" stopColor="#FD312E" />
</radialGradient>
</defs>
<circle
cx={width / 2}
cy={width / 2}
r={radius}
strokeLinecap="round"
className="fill-none stroke-neutral-300 stroke-[20px]"
style={{
strokeDasharray: circumference,
strokeDashoffset: circumference,
}}
/>
<motion.circle
cx={'108'}
cy={'108'}
r="98"
strokeLinecap="round"
className="fill-none stroke-[url('#circle-progress')] stroke-[20px]"
initial={{
strokeDashoffset: circumference,
strokeDasharray: circumference,
}}
animate={{ strokeDashoffset: offset }}
transition={{
stiffness: 260,
damping: 20,
delay: 0.5,
duration: 1,
ease: 'easeOut',
}}
/>
</svg>
);
}
+1
It should be good to have same behaviour as MUI react-circular-progress
It would be great if shadcn implement it to avoid doing tricks to cover this common usecase.
Radial Charts ?
Radial Charts ?
Indeed, this does seem solved now: https://ui.shadcn.com/charts#radial-chart
This issue has been automatically closed because it received no activity for a while. If you think it was closed by accident, please leave a comment. Thank you.
Adjusted nambui98 solution a bit, couldn't find a sweet spot to calculate strokeWidth so it's set manually, the rest works good enough for me as generic circular progress.
import React, { useRef, useEffect, useState } from 'react';
import { motion } from 'framer-motion';
interface CircularProgressProps extends React.HTMLAttributes<HTMLDivElement> {
value: number; // Something between 1 and 100
strokeWidth: number;
}
const CircularProgress: React.FC<CircularProgressProps> = ({ value, strokeWidth, ...divProps }) => {
const containerRef = useRef<HTMLDivElement | null>(null);
const [size, setSize] = useState(0);
useEffect(() => {
if (containerRef.current && "getBoundingClientRect" in containerRef.current) {
const {width, height} = containerRef.current.getBoundingClientRect();
setSize(Math.min(width, height));
}
}, []);
const percentage = Math.min(Math.max(value, 0), 100);
const radius = (size - strokeWidth) / 2;
const circumference = 2 * Math.PI * radius;
const offset = circumference - (percentage / 100) * circumference;
return (
<div ref={containerRef} {...divProps}>
{size > 0 && (
<svg width={size} height={size} xmlns="http://www.w3.org/2000/svg">
<defs>
<radialGradient
id="circle-progress"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(53.1659 -18.1884) rotate(51.1683) scale(267.012 282.957)"
>
<stop stopColor="currentColor" />
<stop offset="1" stopColor="currentColor" />
</radialGradient>
</defs>
<circle
cx={size / 2}
cy={size / 2}
r={radius}
strokeLinecap="round"
className="fill-none stroke-neutral-300"
style={{
strokeWidth,
strokeDasharray: circumference,
strokeDashoffset: circumference,
}}
/>
<motion.circle
cx={size / 2}
cy={size / 2}
r={radius}
strokeLinecap="round"
className="fill-none"
style={{ stroke: "url(#circle-progress)", strokeWidth }}
initial={{
strokeDashoffset: circumference,
strokeDasharray: circumference,
}}
animate={{ strokeDashoffset: offset }}
transition={{
ease: 'easeOut',
}}
/>
</svg>
)}
</div>
);
};
export default CircularProgress;
Usage example in button
const ProgressButton = React.forwardRef<HTMLButtonElement, ProgressButtonProps>(
({ value = 0, children, disabled, className, ...props }, ref) => {
return (
<Button
ref={ref}
disabled={value < 100 || disabled}
className={cn("transition-all duration-300 delay-300", value < 100 ? "pl-4" : "pl-0", className)}
{...props}
>
<CircularProgress value={value} strokeWidth={3} className={cn("transition-all duration-300 delay-300 h-4", value < 100 ? "w-4 mr-2 scale-1" : "w-2 scale-0 mr-1 ml-1")}/>
{children}
</Button>
);
}
);
why we don't have any circular progress indicator in library?