import * as React from "react"; import { useTranslation } from "react-i18next"; import styles from "./SearchSelect.module.css"; import { SendouSelect, SendouSelectItem } from "./Select"; import type { EntitySearch } from "./useEntitySearch"; const PLACEHOLDER_TEXTS = { organizationSearch: { placeholder: "common:forms.organizationSearch.placeholder", noResults: "common:forms.organizationSearch.noResults", }, teamSearch: { placeholder: "common:forms.teamSearch.placeholder", noResults: "common:forms.teamSearch.noResults", }, tournamentSearch: { placeholder: "common:forms.tournamentSearch.placeholder", noResults: "common:forms.tournamentSearch.noResults", }, userSearch: { placeholder: "common:forms.userSearch.placeholder", noResults: "common:forms.userSearch.noResults", }, } as const; /** Field props the entity search components (`UserSearch`, `TeamSearch`, ...) pass straight through. */ export interface SearchSelectFieldProps { name?: string; label?: string; bottomText?: string; errorText?: string; isRequired?: boolean; isDisabled?: boolean; className?: string; onBlur?: () => void; "data-testid"?: string; } interface SearchSelectProps extends SearchSelectFieldProps { ariaLabel: string; inputTestId: string; inputClassName?: string; i18nKey: keyof typeof PLACEHOLDER_TEXTS; search: EntitySearch; buttonRef?: React.Ref; renderItem: (item: TItem) => React.ReactElement; } /** Presentational autocomplete select for the entity searches (`UserSearch` etc.); `search` comes from `useEntitySearch`. */ export function SearchSelect({ name, label, bottomText, errorText, ariaLabel, inputTestId, inputClassName, i18nKey, search, buttonRef, renderItem, isRequired, isDisabled, className, onBlur, "data-testid": testId, }: SearchSelectProps) { return ( { if (key != null) { search.onSelectionChange(Number(key)); } }} aria-label={ariaLabel} searchInputValue={search.filterText} onSearchInputChange={search.setFilterText} search={{ testId: inputTestId, inputClassName: inputClassName ?? "" }} bottomText={bottomText} errorText={errorText} triggerRef={buttonRef} popoverClassName={styles.popover} > {search.items.map((item) => typeof item.id === "string" ? ( ) : ( React.cloneElement(renderItem(item as TItem), { key: item.id }) ), )} ); } function PlaceholderItem({ id, i18nKey, }: { id: "PLACEHOLDER" | "NO_RESULTS"; i18nKey: keyof typeof PLACEHOLDER_TEXTS; }) { const { t } = useTranslation(["common"]); return ( {id === "PLACEHOLDER" ? t(PLACEHOLDER_TEXTS[i18nKey].placeholder) : t(PLACEHOLDER_TEXTS[i18nKey].noResults)} ); } /** One `SearchSelect` result; `SearchSelectItemAdditionalText` is the muted second line, hidden in the trigger. */ export function SearchSelectItem({ id, textValue, testId, leading, children, }: { id: number; textValue: string; testId: string; leading?: React.ReactNode; children: React.ReactNode; }) { return ( {leading}
{children}
); } export function SearchSelectItemLogo({ src }: { src: string }) { return ; } export function SearchSelectItemAdditionalText({ children, }: { children: React.ReactNode; }) { return
{children}
; }