diff --git a/src/app/scripts/_components/ScriptPage.tsx b/src/app/scripts/_components/ScriptPage.tsx index e9aa17e..bfdb2c9 100644 --- a/src/app/scripts/_components/ScriptPage.tsx +++ b/src/app/scripts/_components/ScriptPage.tsx @@ -2,6 +2,7 @@ import ScriptBrowser from "@/app/scripts/_components/ScriptBrowser"; import ScriptItem from "@/app/scripts/_components/ScriptItem"; +import { Spinner } from "@/components/ui/spinner"; import { Category } from "@/lib/types"; import { Loader2 } from "lucide-react"; import { useEffect, useState } from "react"; @@ -56,7 +57,7 @@ export default function ScriptPage() { return (
- +
); diff --git a/src/components/ui/spinner.tsx b/src/components/ui/spinner.tsx new file mode 100644 index 0000000..184ba3d --- /dev/null +++ b/src/components/ui/spinner.tsx @@ -0,0 +1,67 @@ +import { cn } from "@/lib/utils"; +import { Slot } from "@radix-ui/react-slot"; +import { cva, type VariantProps } from "class-variance-authority"; +import * as React from "react"; + +const spinnerVariants = cva("relative block opacity-[0.65]", { + variants: { + size: { + sm: "w-4 h-4", + md: "w-6 h-6", + lg: "w-8 h-8", + }, + }, + defaultVariants: { + size: "sm", + }, +}); + +export interface SpinnerProps + extends React.HTMLAttributes, + VariantProps { + loading?: boolean; + asChild?: boolean; +} + +const Spinner = React.forwardRef( + ({ className, size, loading = true, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : "span"; + + const [bgColorClass, filteredClassName] = React.useMemo(() => { + const bgClass = className?.match(/(?:dark:bg-|bg-)S+/g) || []; + const filteredClasses = className + ?.replace(/(?:dark:bg-|bg-)S+/g, "") + .trim(); + return [bgClass, filteredClasses]; + }, [className]); + + if (!loading) return null; + + return ( + + {Array.from({ length: 8 }).map((_, i) => ( + + + + ))} + + ); + }, +); + +Spinner.displayName = "Spinner"; + +export { Spinner }; diff --git a/tailwind.config.ts b/tailwind.config.ts index 0a9bddc..b89e59e 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -126,12 +126,17 @@ const config = { transform: "translateY(-50%)", }, }, + "spinner-leaf-fade": { + "0%, 100%": { opacity: "0" }, + "50%": { opacity: "1" }, + }, }, animation: { "accordion-down": "accordion-down 0.2s ease-out", "accordion-up": "accordion-up 0.2s ease-out", shine: "shine 8s ease-in-out infinite", gradient: "gradient 8s linear infinite", + "spinner-leaf-fade": "spinner-leaf-fade 800ms linear infinite", }, }, },