diff --git a/src/common/components/organisms/SearchAutocompleteInput.tsx b/src/common/components/organisms/SearchAutocompleteInput.tsx new file mode 100644 index 00000000..7676dcd1 --- /dev/null +++ b/src/common/components/organisms/SearchAutocompleteInput.tsx @@ -0,0 +1,110 @@ +import React, { useState, useCallback } from "react"; +import { useRouter } from "next/router"; +import useSearchUsers from "@/common/lib/hooks/useSearchUsers"; +import { User } from "@neynar/nodejs-sdk/build/neynar-api/v2"; +import { Avatar, AvatarImage } from "@/common/components/atoms/avatar"; +import { + Command, + CommandEmpty, + CommandList, + CommandGroup, + CommandItem, + CommandInput, +} from "@/common/components/atoms/command"; +import { MagnifyingGlassIcon } from "@radix-ui/react-icons"; + +type SearchAutocompleteInputProps = { + onSelect: () => void; +}; + +const SearchAutocompleteInput: React.FC = ({ + onSelect, +}) => { + const router = useRouter(); + const [isFocused, setIsFocused] = useState(false); + const [query, setQuery] = useState(null); + const { users, loading } = useSearchUsers(query); + + const handleFocus = useCallback(() => { + setIsFocused(true); + }, []); + + const handleBlur = useCallback(() => { + setIsFocused(false); + }, []); + + const handlePreventBlur = useCallback((event: React.PointerEvent) => { + event?.preventDefault(); + }, []); + + const onSelectQuery = useCallback(() => { + router.push(`/search?q=${query}`); + onSelect && onSelect(); + }, []); + + const onSelectUser = useCallback((user: User) => { + router.push(`/s/${user.username}`); + onSelect && onSelect(); + }, []); + + return ( + +
+ +
+ {isFocused && ( + + {false && ( + +
+ +
{`Search for "${query}"`}
+
+
+ )} + {users?.length > 0 && ( + + {users.map((user: any, i: number) => ( + onSelectUser(user)} + value={user.username} + className="gap-x-2 cursor-pointer" + > + + + +
+

+ {user.display_name} +

+

+ @{user.username} +

+
+
+ ))} +
+ )} + {query && !loading && No results found} +
+ )} +
+ ); +}; + +export default SearchAutocompleteInput; diff --git a/src/common/components/organisms/SearchModal.tsx b/src/common/components/organisms/SearchModal.tsx new file mode 100644 index 00000000..ffbf974c --- /dev/null +++ b/src/common/components/organisms/SearchModal.tsx @@ -0,0 +1,51 @@ +import React, { + useState, + useEffect, + useCallback, + useRef, + useImperativeHandle, +} from "react"; +import { Dialog, DialogContent } from "@/common/components/atoms/dialog"; + +import SearchAutocompleteInput from "@/common/components/organisms/SearchAutocompleteInput"; + +const SearchModal = React.forwardRef((props, ref) => { + const [isFocused, setIsFocused] = useState(false); + + const handleFocus = useCallback(() => { + setIsFocused(true); + }, []); + + const handleBlur = useCallback(() => { + setIsFocused(false); + }, []); + + useImperativeHandle(ref, () => ({ + focus: handleFocus, + })); + + useEffect(() => { + const down = (e: KeyboardEvent) => { + if (e.key === "k" && (e.metaKey || e.ctrlKey)) { + setIsFocused((open) => !open); + } + if (e.key === "Escape") { + setIsFocused(false); + } + }; + document.addEventListener("keydown", down); + return () => document.removeEventListener("keydown", down); + }, []); + + return ( + + + + + + ); +}); + +SearchModal.displayName = "SearchModal"; + +export default SearchModal;