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
JavaScript
// @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" }) }))] })] }) }));
}