75 lines
2.3 KiB
TypeScript
75 lines
2.3 KiB
TypeScript
import { useId, type ComponentPropsWithoutRef } from 'react';
|
|
import { cx } from '../utils';
|
|
|
|
export const Spinner = ({
|
|
size,
|
|
className,
|
|
...props
|
|
}: ComponentPropsWithoutRef<'span'> & { size?: 'sm' | 'lg' }) => {
|
|
const uid = useId();
|
|
const grooveId = `modern-sk-groove-${uid}`;
|
|
const glowId = `modern-sk-glow-${uid}`;
|
|
return (
|
|
<span
|
|
role="status"
|
|
aria-label="Loading"
|
|
className={cx('modern-sk-spinner', size && `modern-sk-spinner--${size}`, className)}
|
|
{...props}
|
|
>
|
|
<svg viewBox="0 0 36 36" fill="none">
|
|
<defs>
|
|
{/* Carved channel: flat ring sunk by a top inner shadow — like the switch well. */}
|
|
<filter id={grooveId} x="-30%" y="-30%" width="160%" height="160%">
|
|
<feComponentTransfer in="SourceAlpha" result="inv">
|
|
<feFuncA type="table" tableValues="1 0" />
|
|
</feComponentTransfer>
|
|
<feGaussianBlur in="inv" stdDeviation="1" result="blur" />
|
|
<feOffset in="blur" dy="1" result="off" />
|
|
<feFlood floodColor="#000" floodOpacity="0.7" />
|
|
<feComposite in2="off" operator="in" />
|
|
<feComposite in2="SourceAlpha" operator="in" result="shadow" />
|
|
<feMerge>
|
|
<feMergeNode in="SourceGraphic" />
|
|
<feMergeNode in="shadow" />
|
|
</feMerge>
|
|
</filter>
|
|
{/* Soft round glow — generous region so it never clips to a square. */}
|
|
<filter id={glowId} x="-100%" y="-100%" width="300%" height="300%">
|
|
<feGaussianBlur stdDeviation="1.6" />
|
|
</filter>
|
|
</defs>
|
|
<circle
|
|
cx="18"
|
|
cy="18"
|
|
r="14"
|
|
stroke="var(--spin-track)"
|
|
strokeWidth="5"
|
|
filter={`url(#${grooveId})`}
|
|
/>
|
|
<g className="modern-sk-spinner__arc">
|
|
<circle
|
|
cx="18"
|
|
cy="18"
|
|
r="14"
|
|
stroke="var(--lime)"
|
|
strokeWidth="4"
|
|
strokeLinecap="round"
|
|
strokeDasharray="22 88"
|
|
opacity="0.8"
|
|
filter={`url(#${glowId})`}
|
|
/>
|
|
<circle
|
|
cx="18"
|
|
cy="18"
|
|
r="14"
|
|
stroke="var(--lime)"
|
|
strokeWidth="3"
|
|
strokeLinecap="round"
|
|
strokeDasharray="22 88"
|
|
/>
|
|
</g>
|
|
</svg>
|
|
</span>
|
|
);
|
|
};
|