mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-10 05:06:15 -05:00
86 lines
2.1 KiB
TypeScript
86 lines
2.1 KiB
TypeScript
import type * as React from "react";
|
|
import type { SearchLoaderData } from "~/features/search/routes/search";
|
|
import { searchSearchParams } from "~/features/search/search-search-params";
|
|
import {
|
|
SearchSelect,
|
|
type SearchSelectFieldProps,
|
|
SearchSelectItem,
|
|
SearchSelectItemLogo,
|
|
} from "./SearchSelect";
|
|
import teamSearchStyles from "./TeamSearch.module.css";
|
|
import { useEntitySearch } from "./useEntitySearch";
|
|
|
|
export type TeamSearchResult = Extract<
|
|
NonNullable<SearchLoaderData>["results"][number],
|
|
{ type: "team" }
|
|
>;
|
|
|
|
interface TeamSearchProps extends SearchSelectFieldProps {
|
|
/** preselected on mount (e.g. when editing a linked team) */
|
|
initialTeam?: { id: number; name: string; avatarUrl?: string | null };
|
|
onChange?: (team: TeamSearchResult | null) => void;
|
|
ref?: React.Ref<HTMLButtonElement>;
|
|
}
|
|
|
|
export function TeamSearch({
|
|
initialTeam,
|
|
onChange,
|
|
ref,
|
|
...rest
|
|
}: TeamSearchProps) {
|
|
const search = useEntitySearch<TeamSearchResult>({
|
|
buildUrl: (query) =>
|
|
searchSearchParams.href("/search", {
|
|
q: query,
|
|
type: "teams",
|
|
limit: 6,
|
|
}),
|
|
parseResults: parseTeamResults,
|
|
initialItem: initialTeam as TeamSearchResult | undefined,
|
|
initialSelectedId: initialTeam?.id,
|
|
onChange,
|
|
});
|
|
|
|
return (
|
|
<SearchSelect
|
|
{...rest}
|
|
ariaLabel="Team search"
|
|
inputTestId="team-search-input"
|
|
i18nKey="teamSearch"
|
|
search={search}
|
|
buttonRef={ref}
|
|
renderItem={(item) => <TeamItem item={item} />}
|
|
/>
|
|
);
|
|
}
|
|
|
|
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 (
|
|
<SearchSelectItem
|
|
id={item.id}
|
|
textValue={item.name}
|
|
testId="team-search-item"
|
|
leading={
|
|
item.avatarUrl ? (
|
|
<SearchSelectItemLogo src={item.avatarUrl} />
|
|
) : (
|
|
<div className={teamSearchStyles.logoPlaceholder} />
|
|
)
|
|
}
|
|
>
|
|
<span>{item.name}</span>
|
|
</SearchSelectItem>
|
|
);
|
|
}
|