diff --git a/src/components.tsx b/src/components.tsx index 1692235..01e663c 100644 --- a/src/components.tsx +++ b/src/components.tsx @@ -1261,9 +1261,33 @@ function PrintingsSection({ ); } +/** Below this many printings the picker skips its filter box. */ +const PRINTING_FILTER_MIN = 7; + +/** Whether a printing matches a filter query: every word must start one of + * the things the picker shows for it — set code, collector number, year, + * rarity, language, finish. Prefixes rather than substrings, so "foil" + * doesn't also match every nonfoil printing. */ +function printingMatches(p: PrintingRef, query: string): boolean { + const fields = [ + p.set_code, + p.collector_number, + `#${p.collector_number}`, + p.released_at, + p.rarity, + p.lang, + ...p.finishes.map((f) => FINISH_LABELS[f] ?? f), + ].map(fold); + return fold(query) + .split(/\s+/) + .filter(Boolean) + .every((word) => fields.some((f) => f.startsWith(word))); +} + /** * A grid of a card's printings to pick from. With `chooseFinish`, each * printing also offers its finishes (clicking the art takes the first). + * Long lists (basic lands run to hundreds) get a filter box. * * Opens above the click preview, which closes on any Escape it hears on * `window` — so this listens in the capture phase and stops the event there, @@ -1294,6 +1318,8 @@ function PrintingPicker({ return () => window.removeEventListener("keydown", onKey, true); }, [onCancel]); useBackButton(onCancel); + const [query, setQuery] = useState(""); + const shown = printings.filter((p) => printingMatches(p, query)); return createPortal(
No printings match.
}