From 77dfe203d446f69cc0012e89271fbb52e54140e2 Mon Sep 17 00:00:00 2001 From: Peter Lord Date: Thu, 30 Jul 2026 17:39:25 -0700 Subject: [PATCH] Use the game's energy and star icons in the cost filter --- frontend/app/cards/CardsClient.tsx | 34 +++-- frontend/app/components/IconSelect.tsx | 151 +++++++++++++++++++++++ frontend/app/components/SearchFilter.tsx | 13 ++ frontend/lib/image-url.ts | 2 +- 4 files changed, 187 insertions(+), 13 deletions(-) create mode 100644 frontend/app/components/IconSelect.tsx diff --git a/frontend/app/cards/CardsClient.tsx b/frontend/app/cards/CardsClient.tsx index 1dfb75f21..668415395 100644 --- a/frontend/app/cards/CardsClient.tsx +++ b/frontend/app/cards/CardsClient.tsx @@ -8,6 +8,8 @@ import CardGrid from "../components/CardGrid"; import FullCardGrid from "../components/FullCardGrid"; import SearchFilter from "../components/SearchFilter"; import { useLanguage } from "../contexts/LanguageContext"; +import { t } from "@/lib/ui-translations"; +import { CDN_BASE } from "@/lib/image-url"; import { useChannel, useLangPrefix } from "@/lib/use-lang-prefix"; import { useEntityScores } from "@/lib/use-entity-scores"; import { useBetaAdditions } from "@/lib/use-beta-additions"; @@ -52,18 +54,23 @@ const rarityOptions = [ { label: "Quest", value: "Quest" }, ]; +// The game's blank energy orb marks energy costs and the star icon marks +// Ancient star costs (served from the CDN), so the two groups can't be +// confused; the group names are translated at render time. +const ENERGY_ICON = `${CDN_BASE}/icons/colorless_energy_icon.webp`; +const STAR_ICON = `${CDN_BASE}/icons/star_icon.webp`; const costOptions = [ - { label: "0", value: "0", group: "Energy" }, - { label: "1", value: "1", group: "Energy" }, - { label: "2", value: "2", group: "Energy" }, - { label: "3", value: "3", group: "Energy" }, - { label: "4+", value: "4plus", group: "Energy" }, - { label: "X", value: "x", group: "Energy" }, - { label: "1", value: "star1", group: "Star" }, - { label: "2", value: "star2", group: "Star" }, - { label: "3", value: "star3", group: "Star" }, - { label: "4+", value: "star4plus", group: "Star" }, - { label: "X", value: "starx", group: "Star" }, + { label: "0", value: "0", group: "Energy", icon: ENERGY_ICON }, + { label: "1", value: "1", group: "Energy", icon: ENERGY_ICON }, + { label: "2", value: "2", group: "Energy", icon: ENERGY_ICON }, + { label: "3", value: "3", group: "Energy", icon: ENERGY_ICON }, + { label: "4+", value: "4plus", group: "Energy", icon: ENERGY_ICON }, + { label: "X", value: "x", group: "Energy", icon: ENERGY_ICON }, + { label: "1", value: "star1", group: "Star", icon: STAR_ICON }, + { label: "2", value: "star2", group: "Star", icon: STAR_ICON }, + { label: "3", value: "star3", group: "Star", icon: STAR_ICON }, + { label: "4+", value: "star4plus", group: "Star", icon: STAR_ICON }, + { label: "X", value: "starx", group: "Star", icon: STAR_ICON }, ]; function matchesCost(c: Card, want: string): boolean { @@ -264,7 +271,10 @@ function CardsClientInner({ initialCards }: { initialCards: Card[] }) { label: "Any Cost", name: "Cost", value: cost, - options: costOptions, + options: costOptions.map((o) => ({ + ...o, + group: o.group === "Energy" ? t("Energy", lang) : t("Star Cost", lang), + })), onChange: (v) => setFilterAndUrl("cost", v, setCost), }, { diff --git a/frontend/app/components/IconSelect.tsx b/frontend/app/components/IconSelect.tsx new file mode 100644 index 000000000..9ecb15692 --- /dev/null +++ b/frontend/app/components/IconSelect.tsx @@ -0,0 +1,151 @@ +"use client"; + +import { useEffect, useId, useRef, useState } from "react"; +import { imageUrl } from "@/lib/image-url"; + +export interface IconOption { + label: string; + value: string; + group?: string; + icon?: string; +} + +interface IconSelectProps { + label: string; + value: string; + options: IconOption[]; + onChange: (value: string) => void; +} + +/** + * Drop-in replacement for the native filter filter.onChange(e.target.value)} @@ -133,6 +145,7 @@ export default function SearchFilter({ ), )} + )} ))} {sortOptions && onSortChange && ( diff --git a/frontend/lib/image-url.ts b/frontend/lib/image-url.ts index 839946433..749e256ae 100644 --- a/frontend/lib/image-url.ts +++ b/frontend/lib/image-url.ts @@ -10,7 +10,7 @@ export function imageUrl(path: string | null | undefined): string { return `${API}${path}`; } -const CDN_BASE = CDN_URL || "https://cdn.spire-codex.com"; +export const CDN_BASE = CDN_URL || "https://cdn.spire-codex.com"; // The current beta version for render paths ("0.107.0" shape, no leading v). // The literal here is only a first-paint fallback: SiteSwitcher refreshes it