"use client"; import { motion, useInView, useReducedMotion } from "motion/react"; import { type ComponentProps, type ReactNode, useId, useRef } from "react"; import { cn } from "@/lib/utils"; export type DripLoaderVariant = "spin" | "drip" | "split"; export interface DripLoaderProps extends Omit, "children"> { variant?: DripLoaderVariant; size?: number; speed?: number; label?: string; } const forever = Number.POSITIVE_INFINITY; export function DripLoader({ variant = "spin", size = 48, speed, label = "Loading", className, style, ...props }: DripLoaderProps) { const filterId = `drip-loader-${useId().replace(/:/g, "")}`; const ref = useRef(null); const onScreen = useInView(ref); const prefersReduced = !!useReducedMotion(); const reduce = prefersReduced || !onScreen; const blur = Math.max(1.5, size / 22); let body: ReactNode; if (variant === "spin") body = ; else if (variant === "drip") body = ; else body = ; return ( {label} {body} ); } interface PartProps { size: number; duration: number; reduce: boolean; } function Dot({ d, className, style, ...props }: { d: number } & ComponentProps) { return ( ); } const TRAIL = 8; function Spin({ size, duration, reduce }: PartProps) { const reach = size * 0.3; return ( {Array.from({ length: TRAIL }, (_, i) => ( ))} ); } function Drip({ size, duration, reduce }: PartProps) { const ledgeTop = size * 0.1; const ledgeH = size * 0.16; const poolH = size * 0.14; const poolTop = size - size * 0.1 - poolH; const drop = size * 0.24; const times = [0, 0.45, 0.62, 0.82, 1]; return ( ); } function Split({ size, duration, reduce }: PartProps) { const r = size * 0.32; const d = size * 0.26; const t = { duration, repeat: forever, ease: "easeInOut" as const, times: [0, 0.2, 0.3, 0.5, 0.7, 0.8, 1] }; const parts: { key: string; x?: number[]; y?: number[] }[] = [ { key: "left", x: [0, -r, -r, 0, 0, 0, 0] }, { key: "right", x: [0, r, r, 0, 0, 0, 0] }, { key: "up", y: [0, 0, 0, 0, -r, -r, 0] }, { key: "down", y: [0, 0, 0, 0, r, r, 0] }, ]; return ( {parts.map(({ key, ...path }) => ( ))} ); }