53 lines
1.8 KiB
TypeScript
53 lines
1.8 KiB
TypeScript
'use client';
|
|||
|
|
|
||
|
|
/**
|
||
|
|
* The autosuggest dropdown. Presentational only — it fetches nothing and owns
|
||
|
|
* no state, so the fetching rules and the ARIA rules can be read separately.
|
||
|
|
*/
|
||
|
|
|
||
|
|
import type { Suggestion } from '@/lib/suggest';
|
||
|
|
import styles from './SuggestList.module.css';
|
||
|
|
|
||
|
|
/** The id the input's aria-activedescendant points at. */
|
||
|
|
export function suggestOptionId(id: string, index: number): string {
|
||
|
|
return `${id}-option-${index}`;
|
||
|
|
}
|
||
|
|
|
||
|
|
interface Props {
|
||
|
|
/** Shared with the input's aria-controls. */
|
||
|
|
id: string;
|
||
|
|
suggestions: Suggestion[];
|
||
|
|
activeIndex: number;
|
||
|
|
onPick: (s: Suggestion) => void;
|
||
|
|
onHover: (index: number) => void;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function SuggestList({ id, suggestions, activeIndex, onPick, onHover }: Props) {
|
||
|
|
if (suggestions.length === 0) return null;
|
||
|
|
|
||
|
|
return (
|
||
|
|
<ul className={styles.list} id={id} role="listbox">
|
||
|
|
{suggestions.map((s, i) => (
|
||
|
|
<li
|
||
|
|
key={s.urn}
|
||
|
|
id={suggestOptionId(id, i)}
|
||
|
|
role="option"
|
||
|
|
aria-selected={i === activeIndex}
|
||
|
|
className={`${styles.option} ${i === activeIndex ? styles.active : ''}`}
|
||
|
|
/*
|
||
|
|
* onMouseDown, not onClick: the input's blur handler closes the list,
|
||
|
|
* and blur fires before click — so a click handler never runs. This
|
||
|
|
* is the classic autosuggest bug where the dropdown is unclickable
|
||
|
|
* with a mouse while working perfectly with a keyboard.
|
||
|
|
*/
|
||
|
|
onMouseDown={(e) => { e.preventDefault(); onPick(s); }}
|
||
|
|
onMouseEnter={() => onHover(i)}
|
||
|
|
>
|
||
|
|
<span className={styles.name}>{s.school_name}</span>
|
||
|
|
{/* Not decoration: there are many "St Mary's". */}
|
||
|
|
<span className={styles.meta}>{s.local_authority}</span>
|
||
|
|
</li>
|
||
|
|
))}
|
||
|
|
</ul>
|
||
|
|
);
|
||
|
|
}
|