UNPKG

lightswind

Version:

A collection of beautifully crafted React Components, Blocks & Templates for Modern Developers. Create stunning web applications effortlessly by using our 160+ professional and animated react components.

84 lines (83 loc) 5.69 kB
// @ts-nocheck "use client"; import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { useState, useRef } from "react"; import { motion, useAnimation, useMotionValue, useTransform } from "framer-motion"; import { ArrowRight, Check } from "lucide-react"; import { cn } from "../../lib/utils"; export function SlideToConfirm({ text = "Slide to confirm", successText = "Confirmed", onConfirm, width = 320, height = 56, className, }) { const [state, setState] = useState("idle"); const containerRef = useRef(null); const trackWidth = width - height; // Total drag distance const thumbSize = height - 8; // Margin inside const x = useMotionValue(0); const controls = useAnimation(); // Opacity of the text fades out as you drag const textOpacity = useTransform(x, [0, trackWidth * 0.5], [1, 0]); // Background gradient progresses as you drag const bgWidth = useTransform(x, [0, trackWidth], [height, width]); const handleDragEnd = async () => { if (state !== "idle") return; if (x.get() >= trackWidth * 0.9) { // Completed drag controls.start({ x: trackWidth, transition: { type: "spring", stiffness: 400, damping: 30 } }); setState("loading"); try { await onConfirm(); setState("success"); } catch (error) { // If error, reset setState("idle"); controls.start({ x: 0, transition: { type: "spring", stiffness: 400, damping: 30 } }); } } else { // Reset if not fully dragged controls.start({ x: 0, transition: { type: "spring", stiffness: 400, damping: 30 } }); } }; const handleReset = () => { if (state === "success") { setState("idle"); x.set(0); controls.start({ x: 0 }); } }; return (_jsxs("div", { ref: containerRef, className: cn("relative flex items-center justify-center overflow-hidden rounded-full border bg-muted select-none", state === "success" ? "cursor-pointer border-green-500/50" : "", className), style: { width, height, }, onClick: handleReset, children: [_jsx(motion.div, { className: "absolute left-0 top-0 h-full rounded-full", style: { width: state === "success" ? width : bgWidth, backgroundColor: state === "success" ? "#22c55e" : "var(--primary)", opacity: state === "success" ? 0.1 : 0.05, }, animate: { width: state === "success" ? width : undefined }, transition: { duration: 0.3 } }), _jsx(motion.span, { className: cn("absolute font-medium text-sm z-0", state === "success" ? "text-green-600 dark:text-green-400" : "text-muted-foreground"), style: { opacity: state === "idle" ? textOpacity : 0 }, children: text }), _jsx(motion.span, { className: "absolute font-medium text-sm z-0 text-green-600 dark:text-green-400", initial: { opacity: 0, y: 10 }, animate: { opacity: state === "success" ? 1 : 0, y: state === "success" ? 0 : 10 }, transition: { duration: 0.3, delay: 0.1 }, children: successText }), _jsxs(motion.div, { drag: state === "idle" ? "x" : false, dragConstraints: { left: 0, right: trackWidth }, dragElastic: 0.05, dragMomentum: false, onDragEnd: handleDragEnd, className: cn("absolute left-1 z-10 flex cursor-grab items-center justify-center rounded-full bg-background shadow-md active:cursor-grabbing", state !== "idle" && "cursor-default"), initial: false, whileTap: { scale: state === "idle" ? 0.95 : 1 }, animate: state === "success" ? { x: trackWidth, backgroundColor: "#22c55e", color: "white" } : controls, style: { width: thumbSize, height: thumbSize, x, }, children: [_jsx(motion.div, { animate: { rotate: state === "loading" ? 360 : 0, scale: state === "idle" ? 1 : 0, opacity: state === "idle" ? 1 : 0, }, transition: { duration: 0.2 }, className: "absolute", children: _jsx(ArrowRight, { className: "h-5 w-5 opacity-70" }) }), _jsx(motion.div, { animate: { scale: state === "loading" ? 1 : 0, opacity: state === "loading" ? 1 : 0, }, transition: { duration: 0.2 }, className: "absolute flex h-full w-full items-center justify-center", children: _jsx("div", { className: "relative h-[20px] w-[20px]", children: [...Array(12)].map((_, i) => (_jsx(motion.span, { className: "absolute left-[9px] top-0 h-[5.5px] w-[1.8px] rounded-full bg-foreground", style: { rotate: i * 30, transformOrigin: "center 10px", }, animate: { opacity: [0.15, 1, 0.15], }, transition: { duration: 1.1, repeat: Infinity, delay: i * 0.091, ease: "linear", } }, i))) }) }), _jsx(motion.div, { animate: { scale: state === "success" ? 1 : 0, opacity: state === "success" ? 1 : 0, }, transition: { type: "spring", stiffness: 300, damping: 20 }, className: "absolute text-white", children: _jsx(Check, { className: "h-5 w-5" }) })] })] })); }