diff --git a/src/lib/components/pokedex/PokedexViewBoxes.svelte b/src/lib/components/pokedex/PokedexViewBoxes.svelte index c5d204d..f7d6837 100644 --- a/src/lib/components/pokedex/PokedexViewBoxes.svelte +++ b/src/lib/components/pokedex/PokedexViewBoxes.svelte @@ -20,6 +20,87 @@ export let createCatchRecords = () => {}; export let onPokemonClick: (pokemon: CombinedData) => void = () => {}; + let filterNotCaught = false; + let filterNeedsToEvolve = false; + let filterInHome = false; + let filterNotInHome = false; + + let filteredCombinedData: CombinedData[] = []; + let filteredTotal = 0; + let overallTotal = 0; + let overallCaughtCount = 0; + let overallNotCaughtCount = 0; + let overallNeedsToEvolveCount = 0; + let overallInHomeCount = 0; + let overallNotInHomeCount = 0; + let filtersActive = false; + let filtersKey = ''; + + function normalizedStatus(catchRecord: CatchRecord | null) { + return { + caught: !!catchRecord?.caught, + needsToEvolve: !!catchRecord?.haveToEvolve, + inHome: !!catchRecord?.inHome + }; + } + + function matchesFilters(catchRecord: CatchRecord | null) { + const status = normalizedStatus(catchRecord); + if (!filtersActive) return true; + + const isCaught = status.caught || status.needsToEvolve; + const isNotCaught = !isCaught; + const isNeedsToEvolve = status.needsToEvolve; + const isInHome = status.inHome; + const isNotInHome = !status.inHome; + + // OR within each group, AND between groups. + const progressGroupActive = filterNotCaught || filterNeedsToEvolve; + const homeGroupActive = filterInHome || filterNotInHome; + + const progressMatch = + !progressGroupActive || + (filterNotCaught && isNotCaught) || + (filterNeedsToEvolve && isNeedsToEvolve); + + const homeMatch = + !homeGroupActive || (filterInHome && isInHome) || (filterNotInHome && isNotInHome); + + return progressMatch && homeMatch; + } + + $: filtersActive = filterNotCaught || filterNeedsToEvolve || filterInHome || filterNotInHome; + $: filtersKey = `${filterNotCaught}-${filterNeedsToEvolve}-${filterInHome}-${filterNotInHome}`; + + $: { + // Ensure this recalculates when any filter changes (Svelte doesn't track function internals). + filtersKey; + filteredCombinedData = combinedData + ? combinedData.filter(({ catchRecord }) => matchesFilters(catchRecord)) + : []; + } + + $: filteredTotal = filteredCombinedData.length; + $: overallTotal = combinedData?.length ?? 0; + + $: overallCaughtCount = (combinedData ?? []).reduce((acc, { catchRecord }) => { + const status = normalizedStatus(catchRecord); + // Treat "needs to evolve" as a subset of "caught" (it is caught, just not finished). + return acc + (status.caught || status.needsToEvolve ? 1 : 0); + }, 0); + + $: overallNotCaughtCount = overallTotal - overallCaughtCount; + + $: overallNeedsToEvolveCount = (combinedData ?? []).reduce((acc, { catchRecord }) => { + return acc + (normalizedStatus(catchRecord).needsToEvolve ? 1 : 0); + }, 0); + + $: overallInHomeCount = (combinedData ?? []).reduce((acc, { catchRecord }) => { + return acc + (normalizedStatus(catchRecord).inHome ? 1 : 0); + }, 0); + + $: overallNotInHomeCount = overallTotal - overallInHomeCount; + const BOX_VIEW_LAYOUT_STORAGE_KEY = 'livingdex:boxViewLayout:v1'; type BoxViewLayout = 'comfortable' | 'compact' | 'ultra'; let boxViewLayout: BoxViewLayout = 'comfortable'; @@ -89,86 +170,162 @@ } } } + + function onInHomeFilterChange(event: Event) { + const checked = (event.currentTarget as HTMLInputElement).checked; + if (checked) filterNotInHome = false; + } + + function onNotInHomeFilterChange(event: Event) { + const checked = (event.currentTarget as HTMLInputElement).checked; + if (checked) filterInHome = false; + }
{#if combinedData && combinedData.length > 0}
-
-
- - +
+
+
+ + -
- Legend: - -
+ +
+
+ Filters: + + + + +
+ +
+
+ Caught + {overallCaughtCount} + + Not caught + {overallNotCaughtCount} + + Needs to evolve + {overallNeedsToEvolveCount} + + In HOME + {overallInHomeCount} + + Not in HOME + {overallNotInHomeCount} +
+
+ Showing {filteredTotal} of {overallTotal} +
+
@@ -198,15 +355,20 @@
{#each combinedData as { pokedexEntry, catchRecord }, index} {@const placement = calculateBoxPlacement(index)} + {@const isFilteredOut = + filtersActive && !!filtersKey && !matchesFilters(catchRecord)} {#if placement.box === boxNumber}