mirror of
https://github.com/willchen96/mike.git
synced 2026-07-24 23:41:04 +02:00
94 lines
3.4 KiB
TypeScript
94 lines
3.4 KiB
TypeScript
import { Input } from "@/components/ui/input";
|
|
import { ArrowUp, ArrowDown, X } from "lucide-react";
|
|
import { useRef } from "react";
|
|
|
|
interface TextSearchWidgetProps {
|
|
isOpen: boolean;
|
|
onClose: () => void;
|
|
searchQuery: string;
|
|
onSearchChange: (query: string) => void;
|
|
currentMatchIdx: number;
|
|
matchCount: number;
|
|
setCurrentMatchIdx: (idx: number | ((prev: number) => number)) => void;
|
|
className?: string;
|
|
}
|
|
|
|
export function TextSearchWidget({
|
|
isOpen,
|
|
onClose,
|
|
searchQuery,
|
|
onSearchChange,
|
|
currentMatchIdx,
|
|
matchCount,
|
|
setCurrentMatchIdx,
|
|
className = "",
|
|
}: TextSearchWidgetProps) {
|
|
const searchInputRef = useRef<HTMLInputElement>(null);
|
|
|
|
const handleNext = () => {
|
|
if (matchCount === 0) return;
|
|
setCurrentMatchIdx((prev) => (prev + 1) % matchCount);
|
|
};
|
|
|
|
const handlePrev = () => {
|
|
if (matchCount === 0) return;
|
|
setCurrentMatchIdx((prev) => (prev - 1 + matchCount) % matchCount);
|
|
};
|
|
|
|
if (!isOpen) return null;
|
|
|
|
return (
|
|
<div
|
|
className={`flex flex-col bg-white shadow-lg border border-gray-200 rounded-md overflow-hidden min-w-[300px] ${className}`}
|
|
>
|
|
<div className="flex items-center gap-1 p-1">
|
|
<div className="flex-1 relative">
|
|
<Input
|
|
ref={searchInputRef}
|
|
autoFocus
|
|
placeholder="Find"
|
|
value={searchQuery}
|
|
onChange={(e) => onSearchChange(e.target.value)}
|
|
className="h-8 text-sm w-full pr-[80px] rounded-sm border-gray-200 bg-gray-100/50 focus-visible:ring-0 focus-visible:border-blue-600 placeholder:text-gray-500"
|
|
onKeyDown={(e) => {
|
|
if (e.key === "Escape") {
|
|
onClose();
|
|
onSearchChange("");
|
|
} else if (e.key === "Enter") {
|
|
if (e.shiftKey) handlePrev();
|
|
else handleNext();
|
|
}
|
|
}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Results count and navigation */}
|
|
{searchQuery && (
|
|
<div className="flex items-center justify-between px-2 pb-1 pt-0.5 text-xs text-gray-500">
|
|
<span>
|
|
{matchCount > 0
|
|
? `${currentMatchIdx + 1} of ${matchCount}`
|
|
: "No results"}
|
|
</span>
|
|
<div className="flex items-center gap-1">
|
|
<button
|
|
onClick={handlePrev}
|
|
disabled={matchCount === 0}
|
|
className="p-1 hover:bg-gray-100 rounded disabled:opacity-50"
|
|
>
|
|
<ArrowUp className="h-4 w-4" />
|
|
</button>
|
|
<button
|
|
onClick={handleNext}
|
|
disabled={matchCount === 0}
|
|
className="p-1 hover:bg-gray-100 rounded disabled:opacity-50"
|
|
>
|
|
<ArrowDown className="h-4 w-4" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|