MIF_E31221222/sigap-website/app/_components/floating-action-search-bar.tsx

113 lines
3.1 KiB
TypeScript

"use client";
import { useState, useEffect, useRef, forwardRef } from "react";
import { motion, AnimatePresence } from "framer-motion";
import ActionSearchBar from "@/app/_components/action-search-bar";
export interface Action {
id: string
label: string
icon: React.ReactNode
description?: string
shortcut?: string
category?: string
onClick?: () => void
}
export interface ActionSearchBarProps {
actions?: Action[]
defaultActions?: boolean
autoFocus?: boolean
isFloating?: boolean
placeholder?: string
onSearch?: (query: string) => void
onActionSelect?: (action: Action) => void
className?: string
inputClassName?: string
dropdownClassName?: string
showShortcutHint?: boolean
commandKey?: string
}
export const FloatingActionSearchBar = forwardRef<HTMLInputElement, ActionSearchBarProps>(
(
{
actions,
defaultActions = true,
autoFocus = false,
isFloating = false,
placeholder = "What's up?",
onSearch,
onActionSelect,
className,
inputClassName,
dropdownClassName,
showShortcutHint = true,
commandKey = "⌘K",
},
ref,
) => {
const [isOpen, setIsOpen] = useState(false);
const searchBarRef = useRef<HTMLInputElement>(null);
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
if ((event.ctrlKey || event.metaKey) && event.key === "k") {
event.preventDefault();
setIsOpen((prev) => !prev);
} else if (event.key === "Escape") {
setIsOpen(false);
}
};
window.addEventListener("keydown", handleKeyDown);
return () => window.removeEventListener("keydown", handleKeyDown);
}, []);
useEffect(() => {
if (isOpen && searchBarRef.current) {
searchBarRef.current.focus();
}
}, [isOpen]);
return (
<AnimatePresence>
{isOpen && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.2 }}
className="fixed inset-0 z-50 flex items-start justify-center bg-background/80 backdrop-blur-sm p-4 sm:p-6 md:p-8 lg:p-40"
onClick={() => setIsOpen(false)}
>
<motion.div
initial={{ scale: 0.95, y: -20 }}
animate={{ scale: 1, y: 0 }}
exit={{ scale: 0.95, y: -20 }}
transition={{ duration: 0.2 }}
className="w-full max-w-lg sm:max-w-xl md:max-w-2xl"
onClick={(e) => e.stopPropagation()}
>
<ActionSearchBar
ref={searchBarRef}
autoFocus={true}
isFloating={true}
actions={actions}
defaultActions={defaultActions}
placeholder={placeholder}
onSearch={onSearch}
className={className}
inputClassName={inputClassName}
dropdownClassName={dropdownClassName}
showShortcutHint={showShortcutHint}
commandKey={commandKey}
/>
</motion.div>
</motion.div>
)}
</AnimatePresence>
);
});