"use client"; import { useRef } from "react"; import { isValidHandle } from "@atproto/syntax"; import { Input } from "components/Input"; import { Combobox, ComboboxResult } from "components/Combobox"; import { useActorTypeahead, type ActorSuggestion, } from "src/hooks/useActorTypeahead"; import { DotLoader } from "components/utils/DotLoader"; import { Avatar } from "components/Avatar"; import { theme } from "tailwind.config"; import { AtmosphereAccount } from "components/Icons/AtmosphereAccount"; import { INPUT_HIGHLIGHT_CLASS } from "./Subscribe/inputHighlight"; export const HandleSearchInput = (props: { autoFocus?: boolean; action?: React.ReactNode; renderAction?: (submit: () => void, value: string) => React.ReactNode; leading?: React.ReactNode | null; placeholder?: string; className?: string; triggerClassName?: string; large?: boolean; compact?: boolean; loading?: boolean; onSubmit?: ( handle: string, actor?: ActorSuggestion, ) => void | boolean | Promise; onChange?: (value: string) => void; highlight?: boolean; onFocus?: () => void; validateHandle?: boolean; }) => { let { handleValue, setHandleValue, suggestions, setSuggestions, dropdownOpen, setDropdownOpen, highlighted, setHighlighted, } = useActorTypeahead(); let triggerRef = useRef(null); const handleSelect = async (handle?: string) => { let selected = handle ?? handleValue; if (!selected) return; const actor = suggestions.find((s) => s.handle === selected); if (props.validateHandle && !actor) { selected = selected.trim().replace(/^@/, ""); if (!isValidHandle(selected)) { let input = triggerRef.current?.querySelector("input"); input?.setCustomValidity( "Please enter a full handle, like name.bsky.social", ); input?.reportValidity(); return; } } setHandleValue(selected); setDropdownOpen(false); setSuggestions([]); setHighlighted(undefined); props.onChange?.(selected); let submitted = await props.onSubmit?.(selected, actor); if (submitted) setHandleValue(""); }; const handles = suggestions.map((s) => s.handle); const leading = props.leading === undefined ? : props.leading; return ( { if (!open) { setDropdownOpen(false); setHighlighted(undefined); } }} results={handles} highlighted={highlighted} setHighlighted={setHighlighted} onSelect={() => handleSelect(highlighted)} zIndex={60} sideOffset={4} triggerClassName={props.triggerClassName} className="w-(--radix-popover-trigger-width)!" trigger={
{leading && (
{leading}
)} { e.currentTarget.setCustomValidity(""); setHandleValue(e.target.value); props.onChange?.(e.target.value); }} onFocus={props.onFocus} onClick={(e) => e.stopPropagation()} onPointerDown={(e) => e.stopPropagation()} onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault(); e.stopPropagation(); handleSelect(highlighted); } }} autoComplete="off" disabled={props.loading} /> {props.renderAction ? ( props.renderAction(() => handleSelect(), handleValue) ) : props.loading ? ( ) : props.onSubmit && props.action ? ( ) : ( props.action )}
} > {suggestions.map((actor) => ( handleSelect(actor.handle)} className=" flex-row! gap-2! leading-snug text-sm" >
{actor.displayName || actor.handle}
{actor.displayName && (
@{actor.handle}
)}
))}
); };