58 lines
1.3 KiB
TypeScript
58 lines
1.3 KiB
TypeScript
import { forwardRef } from "react";
|
|
|
|
import { cn } from "../lib/cn";
|
|
import { cva, type VariantProps } from "../lib/cva";
|
|
import { createDataAttributes, createSlot } from "../lib/contracts";
|
|
|
|
const spinnerVariants = cva(
|
|
[
|
|
"inline-block rounded-full border-current border-r-transparent align-middle",
|
|
"animate-spin motion-reduce:animate-none"
|
|
],
|
|
{
|
|
variants: {
|
|
size: {
|
|
sm: "size-3 border-[1.5px]",
|
|
md: "size-4 border-2",
|
|
lg: "size-5 border-2"
|
|
},
|
|
tone: {
|
|
default: "text-[var(--color-muted-foreground)]",
|
|
current: "text-current",
|
|
primary: "text-[var(--color-primary)]"
|
|
}
|
|
},
|
|
defaultVariants: {
|
|
size: "md",
|
|
tone: "current"
|
|
}
|
|
}
|
|
);
|
|
|
|
export type SpinnerProps = React.ComponentPropsWithoutRef<"span"> &
|
|
VariantProps<typeof spinnerVariants>;
|
|
|
|
export const Spinner = forwardRef<HTMLSpanElement, SpinnerProps>(function Spinner(
|
|
{
|
|
className,
|
|
size = "md",
|
|
tone = "current",
|
|
...props
|
|
},
|
|
ref
|
|
) {
|
|
return (
|
|
<span
|
|
{...props}
|
|
{...createSlot("icon")}
|
|
{...createDataAttributes({
|
|
size,
|
|
tone
|
|
})}
|
|
aria-hidden={props["aria-label"] ? undefined : true}
|
|
className={cn(spinnerVariants({ size, tone }), className)}
|
|
ref={ref}
|
|
/>
|
|
);
|
|
});
|