--- title: Fluid Action Panel description: A professional, highly interactive floating utility widget that morphs between a collapsed progress trigger and an expanded glassmorphic settings dashboard. Features page scroll progress tracking, custom theme togglers, and clean copy-link and social sharing shortcuts with responsive touch controls. --- ## Installation CLI Manual ```bash npx lightswind@latest add fluid-action-panel ``` Copy and paste the following code into your project. ```tsx "use client"; import React, { useState, useEffect, useRef } from "react"; import { motion, AnimatePresence } from "framer-motion"; import { Settings2, X, Sun, Moon, Share2, Link2, Twitter, Compass, ArrowUp, Check, Monitor } from "lucide-react"; import { cn } from "@/components/lib/utils"; export interface FluidActionPanelProps { position?: "bottom-right" | "bottom-left" | "top-right" | "top-left"; className?: string; onThemeToggle?: () => void; isDark?: boolean; } export const FluidActionPanel = ({ position = "bottom-right", className, onThemeToggle, isDark = false }: FluidActionPanelProps) => { const [isOpen, setIsOpen] = useState(false); const [scrollProgress, setScrollProgress] = useState(0); const [copied, setCopied] = useState(false); const containerRef = useRef(null); // Track scroll position for reading progress useEffect(() => { const handleScroll = () => { const totalScroll = document.documentElement.scrollHeight - window.innerHeight; if (totalScroll > 0) { const progress = (window.scrollY / totalScroll) * 100; setScrollProgress(Math.min(progress, 100)); } }; window.addEventListener("scroll", handleScroll); handleScroll(); return () => window.removeEventListener("scroll", handleScroll); }, []); // Close on click outside useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (containerRef.current && !containerRef.current.contains(event.target as Node)) { setIsOpen(false); } }; if (isOpen) { document.addEventListener("mousedown", handleClickOutside); } return () => document.removeEventListener("mousedown", handleClickOutside); }, [isOpen]); const handleCopyLink = () => { navigator.clipboard.writeText(window.location.href); setCopied(true); setTimeout(() => setCopied(false), 2000); }; const handleScrollToTop = () => { window.scrollTo({ top: 0, behavior: "smooth" }); }; const positionClasses = { "bottom-right": "bottom-6 right-6", "bottom-left": "bottom-6 left-6", "top-right": "top-6 right-6", "top-left": "top-6 left-6", }; // SVG circular path metrics const radius = 26; const stroke = 2.5; const normalizedRadius = radius - stroke * 2; const circumference = normalizedRadius * 2 * Math.PI; const strokeDashoffset = circumference - (scrollProgress / 100) * circumference; // Stagger configurations for child elements const containerVariants = { hidden: { opacity: 0 }, visible: { opacity: 1, transition: { staggerChildren: 0.05, delayChildren: 0.05 } } }; const itemVariants = { hidden: { opacity: 0, y: 10 }, visible: { opacity: 1, y: 0, transition: { type: "spring" as const, stiffness: 300, damping: 25 } } }; return (
{/* Toggle Button wrapper (Always rendered at the top-left coordinate space of the layout container) */}
setIsOpen(!isOpen)} className="w-14 h-14 flex items-center justify-center relative focus:outline-none z-25 group" whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }} > {/* SVG Progress Ring */} {/* Morphing Icon */} {!isOpen ? ( ) : ( )}
{/* Action Panel Content (Animate in only when open) */} {isOpen && ( {/* Header Info */} System Hub Page Navigator {/* Progress Indicator Card */}
Progress {Math.round(scrollProgress)}% Scrolled
{/* Toggle controls grid */} {/* Theme Controller */} {/* Link Copy Widget */} {/* Share hub row */} Quick Share
Twitter
{/* Footer specs */} Lightswind UI v1.2.6
)}
); }; ```
## Usage ```tsx import { FluidActionPanel } from "@/components/lightswind/fluid-action-panel"; ``` ```tsx import { FluidActionPanel } from "@/components/lightswind/fluid-action-panel"; export default function App() { const [isDark, setIsDark] = useState(false); return (
{/* Content here */} setIsDark(!isDark)} />
); } ```