← back to Epstein Card
components/ui/enhanced-dropdown.tsx
151 lines
"use client";
import { useState, useRef, useEffect } from "react";
interface EnhancedDropdownProps {
options: string[];
placeholder?: string;
onSelect: (value: string) => void;
className?: string;
redactedText?: string;
}
export function EnhancedDropdown({
options,
placeholder = "████████",
onSelect,
className = "",
redactedText = placeholder
}: EnhancedDropdownProps) {
const [isOpen, setIsOpen] = useState(false);
const [selectedValue, setSelectedValue] = useState("");
const [customValue, setCustomValue] = useState("");
const [showCustomInput, setShowCustomInput] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);
const customInputRef = useRef<HTMLInputElement>(null);
// Take only first 3 options for presets
const presetOptions = options.slice(0, 3);
useEffect(() => {
function handleClickOutside(event: MouseEvent) {
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
setIsOpen(false);
setShowCustomInput(false);
}
}
document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
}, []);
useEffect(() => {
if (showCustomInput && customInputRef.current) {
customInputRef.current.focus();
}
}, [showCustomInput]);
const handleSelect = (value: string) => {
if (value === "custom") {
setShowCustomInput(true);
setIsOpen(false);
} else {
setSelectedValue(value);
onSelect(value);
setIsOpen(false);
setShowCustomInput(false);
}
};
const handleCustomSubmit = () => {
if (customValue.trim()) {
setSelectedValue(customValue);
onSelect(customValue);
setShowCustomInput(false);
setCustomValue("");
}
};
const displayValue = selectedValue || redactedText;
return (
<div className={`relative inline-block ${className}`} ref={dropdownRef}>
<button
type="button"
onClick={() => setIsOpen(!isOpen)}
className={`
border-b-2 border-purple-500 bg-transparent px-2 py-1
font-mono text-xl mx-1 min-w-[120px] text-left
hover:border-purple-400 transition-colors cursor-pointer
${selectedValue ? 'text-purple-400' : 'text-gray-500'}
`}
>
{displayValue}
</button>
{isOpen && (
<div className="absolute z-50 mt-2 w-64 rounded-lg border-2 border-purple-500 bg-black shadow-2xl">
<div className="p-1">
{presetOptions.map((option, index) => (
<button
key={option}
type="button"
onClick={() => handleSelect(option)}
className="w-full text-left px-4 py-3 hover:bg-purple-900/50 text-purple-300 font-mono text-lg transition-colors rounded"
>
{index + 1}. {option}
</button>
))}
<div className="border-t border-purple-700 my-1"></div>
<button
type="button"
onClick={() => handleSelect("custom")}
className="w-full text-left px-4 py-3 hover:bg-purple-900/50 text-yellow-400 font-mono text-lg transition-colors rounded"
>
✏️ Fill in your own...
</button>
</div>
</div>
)}
{showCustomInput && (
<div className="absolute z-50 mt-2 w-64 rounded-lg border-2 border-yellow-500 bg-black shadow-2xl p-4">
<div className="text-yellow-400 font-mono text-sm mb-2">Enter custom value:</div>
<input
ref={customInputRef}
type="text"
value={customValue}
onChange={(e) => setCustomValue(e.target.value)}
onKeyDown={(e) => {
if (e.key === 'Enter') handleCustomSubmit();
if (e.key === 'Escape') {
setShowCustomInput(false);
setCustomValue("");
}
}}
className="w-full bg-gray-900 border border-yellow-500 text-white px-3 py-2 rounded font-mono text-lg focus:outline-none focus:border-yellow-400"
placeholder="Type here..."
/>
<div className="flex gap-2 mt-3">
<button
type="button"
onClick={handleCustomSubmit}
className="flex-1 bg-yellow-600 hover:bg-yellow-500 text-black font-bold py-2 rounded transition-colors"
>
Submit
</button>
<button
type="button"
onClick={() => {
setShowCustomInput(false);
setCustomValue("");
}}
className="flex-1 bg-gray-700 hover:bg-gray-600 text-white font-bold py-2 rounded transition-colors"
>
Cancel
</button>
</div>
</div>
)}
</div>
);
}