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.

69 lines (68 loc) 5.05 kB
// @ts-nocheck "use client"; import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { useState, useRef, useEffect } from "react"; import { motion, AnimatePresence } from "framer-motion"; import { Search, X, Command } from "lucide-react"; import { cn } from "../../lib/utils"; export function ExpandableSearchBar({ placeholder = "Search...", onChange, onSubmit, className, expandedWidth = "18rem", // 288px (w-72) }) { const [isExpanded, setIsExpanded] = useState(false); const [value, setValue] = useState(""); const inputRef = useRef(null); const containerRef = useRef(null); // Focus input when expanded useEffect(() => { if (isExpanded && inputRef.current) { inputRef.current.focus(); } }, [isExpanded]); // Handle outside click to collapse if empty useEffect(() => { const handleClickOutside = (e) => { if (containerRef.current && !containerRef.current.contains(e.target) && value === "") { setIsExpanded(false); } }; document.addEventListener("mousedown", handleClickOutside); return () => document.removeEventListener("mousedown", handleClickOutside); }, [value]); const handleSubmit = (e) => { e.preventDefault(); if (onSubmit) onSubmit(value); }; const handleClear = () => { setValue(""); if (onChange) onChange(""); inputRef.current?.focus(); }; return (_jsx("div", { ref: containerRef, className: cn("relative flex items-center justify-end", className), // Ensures the container takes up the max space when expanded to prevent layout shifts if needed style: { width: isExpanded ? expandedWidth : "2.5rem" }, children: _jsxs(motion.form, { initial: false, animate: { width: isExpanded ? expandedWidth : "2.5rem", backgroundColor: isExpanded ? "var(--background)" : "var(--muted)", }, transition: { type: "spring", stiffness: 400, damping: 30, }, onSubmit: handleSubmit, className: cn("relative flex h-10 items-center overflow-hidden rounded-full border shadow-sm transition-colors focus:outline-none focus-visible:outline-none focus:ring-0 focus-visible:ring-0", isExpanded ? "border-border/50 bg-background" : "border-transparent bg-muted hover:bg-muted/80 cursor-pointer"), onClick: () => !isExpanded && setIsExpanded(true), children: [_jsx("button", { type: "button", className: "absolute left-0 flex h-full w-10 items-center justify-center text-muted-foreground transition-colors hover:text-foreground z-10 focus:outline-none focus-visible:outline-none focus:ring-0 focus-visible:ring-0", onClick: () => { if (isExpanded && value === "") { setIsExpanded(false); } else if (!isExpanded) { setIsExpanded(true); } }, "aria-label": "Search", disabled: isExpanded && value !== "", children: _jsx(Search, { className: "h-4 w-4" }) }), _jsx("input", { ref: inputRef, type: "text", value: value, onChange: (e) => { setValue(e.target.value); if (onChange) onChange(e.target.value); }, placeholder: placeholder, className: "h-full w-full border-none bg-transparent pl-10 pr-10 text-sm outline-none placeholder:text-muted-foreground/60 focus:border-transparent focus:outline-none focus:ring-0 focus-visible:ring-0 focus-visible:outline-none focus:ring-offset-0 focus-visible:ring-offset-0", style: { pointerEvents: isExpanded ? "auto" : "none", opacity: isExpanded ? 1 : 0, boxShadow: "none", }, tabIndex: isExpanded ? 0 : -1 }), _jsxs(AnimatePresence, { children: [isExpanded && value === "" && (_jsx(motion.div, { initial: { opacity: 0, scale: 0.8 }, animate: { opacity: 1, scale: 1 }, exit: { opacity: 0, scale: 0.8 }, transition: { duration: 0.15 }, className: "absolute right-3 flex items-center justify-center pointer-events-none", children: _jsxs("div", { className: "flex h-5 items-center gap-1 rounded bg-muted px-1.5 text-[10px] font-medium text-muted-foreground", children: [_jsx(Command, { className: "h-3 w-3" }), _jsx("span", { children: "K" })] }) })), isExpanded && value !== "" && (_jsx(motion.button, { type: "button", initial: { opacity: 0, scale: 0.8 }, animate: { opacity: 1, scale: 1 }, exit: { opacity: 0, scale: 0.8 }, transition: { duration: 0.15 }, onClick: handleClear, className: "absolute right-0 flex h-full w-10 items-center justify-center text-muted-foreground hover:text-foreground focus:outline-none focus-visible:outline-none focus:ring-0 focus-visible:ring-0", "aria-label": "Clear search", children: _jsx(X, { className: "h-4 w-4" }) }))] })] }) })); }