import clsx from "clsx"; import * as React from "react"; import { ListBoxItem, type SelectProps } from "react-aria-components"; import type { SearchLoaderData } from "~/features/search/routes/search"; import { SearchSelect } from "./SearchSelect"; import searchSelectStyles from "./SearchSelect.module.css"; import selectStyles from "./Select.module.css"; import teamSearchStyles from "./TeamSearch.module.css"; import { useEntitySearch } from "./useEntitySearch"; export type TeamSearchResult = Extract< NonNullable["results"][number], { type: "team" } >; interface TeamSearchProps extends Omit, "children" | "onChange"> { name?: string; label?: string; bottomText?: string; errorText?: string; /** Team to preselect and display on mount (e.g. when editing a linked team). */ initialTeam?: { id: number; name: string; avatarUrl?: string | null }; onChange?: (team: TeamSearchResult | null) => void; } export const TeamSearch = React.forwardRef(function TeamSearch< T extends object, >( { name, label, bottomText, errorText, initialTeam, onChange, ...rest }: TeamSearchProps, ref?: React.Ref, ) { const search = useEntitySearch({ buildUrl: (query) => `/search?q=${query}&type=teams&limit=6`, parseResults: parseTeamResults, initialItem: initialTeam as TeamSearchResult | undefined, initialSelectedId: initialTeam?.id, onChange, }); return ( } /> ); }); function parseTeamResults( data: unknown, query: string, ): TeamSearchResult[] | null { const searchData = data as SearchLoaderData; if (!searchData || searchData.query !== query) return null; return searchData.results.filter( (result): result is TeamSearchResult => result.type === "team", ); } function TeamItem({ item }: { item: TeamSearchResult }) { return ( clsx(searchSelectStyles.item, { [selectStyles.itemFocused]: isFocused, [selectStyles.itemSelected]: isSelected, }) } data-testid="team-search-item" > {item.avatarUrl ? ( ) : (
)}
{item.name}
); }