Printing picker: quick-filter box for cards with many printings
Filters by set code, collector number, year, rarity, language and finish, matching each word against the start of a field. Shown when a card has 7+ printings. Closes #31 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -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(
|
||||
<div
|
||||
@@ -1308,8 +1334,23 @@ function PrintingPicker({
|
||||
<h3 className="dialog-title">{chooseFinish ? `Add a printing of ${name}` : `Show ${name} as`}</h3>
|
||||
<IconButton icon={Icons.close} label="Close" onClick={onCancel} />
|
||||
</div>
|
||||
{printings.length >= PRINTING_FILTER_MIN && (
|
||||
<div className="printing-filter">
|
||||
<input
|
||||
type="search"
|
||||
value={query}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
placeholder="Filter by set, number, year, rarity, finish…"
|
||||
autoFocus
|
||||
/>
|
||||
<span className="muted">
|
||||
{shown.length === printings.length ? printings.length : `${shown.length} of ${printings.length}`}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{shown.length === 0 && <p className="hint">No printings match.</p>}
|
||||
<div className="printing-grid">
|
||||
{printings.map((p) => {
|
||||
{shown.map((p) => {
|
||||
const finishes = p.finishes.length > 0 ? p.finishes : ["nonfoil"];
|
||||
const meta = [
|
||||
p.released_at.slice(0, 4),
|
||||
|
||||
@@ -3949,6 +3949,16 @@ button.pull-action {
|
||||
.printing-picker-backdrop {
|
||||
z-index: 500;
|
||||
}
|
||||
.printing-filter {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-3);
|
||||
margin-bottom: var(--space-3);
|
||||
}
|
||||
.printing-filter input {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
.printing-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(min(130px, 40vw), 1fr));
|
||||
|
||||
Reference in New Issue
Block a user