11import { useDraftStore } from "@/lib/store/draftStore" ;
2- import { searchChampions , championsMap , type Champion } from "@/datasets/championPreprocessed" ;
3- import { useEffect , useEffectEvent } from "react" ;
2+ import { championsMap , searchChampions , type Champion } from "@/datasets/championPreprocessed" ;
3+ import { useEffect , useEffectEvent , useMemo } from "react" ;
44import { clsx } from "clsx" ;
5+ import { useShallow } from "zustand/react/shallow" ;
56
67interface ChampionListProps {
78 searchQuery : string ;
8- roleFilters : string [ ] ;
9+ roleFilters : Array < string > ;
910}
1011
1112export function ChampionList ( { searchQuery, roleFilters } : ChampionListProps ) {
12- const selectChampion = useDraftStore ( ( state ) => state . selectChampion ) ;
13- const selectedChampion = useDraftStore ( ( state ) => state . selectedChampion ) ;
14- const isDraftComplete = useDraftStore ( ( state ) => state . isDraftComplete ) ;
15- const isOverridingAny = useDraftStore ( ( state ) => state . isOverridingAny ( ) ) ;
16- const cancelAnyOverride = useDraftStore ( ( state ) => state . cancelAnyOverride ) ;
13+ const { cancelAnyOverride, isDraftComplete, isOverridingAny, selectChampion, selectedChampion, unavailableChampions } =
14+ useDraftStore (
15+ useShallow (
16+ ( state ) => ( {
17+ cancelAnyOverride : state . cancelAnyOverride ,
18+ isDraftComplete : state . isDraftComplete ,
19+ isOverridingAny : state . isOverridingAny ( ) ,
20+ selectChampion : state . selectChampion ,
21+ selectedChampion : state . selectedChampion ,
22+ unavailableChampions : state . getUnavailableChampions ( ) ,
23+ } )
24+ )
25+ ) ;
1726
18- // Subscribe to the actual state that affects champion availability
19- const unavailableChampions = useDraftStore ( ( state ) => state . getUnavailableChampions ( ) ) ;
27+ const roleFilterSet = useMemo ( ( ) => new Set ( roleFilters ) , [ roleFilters ] ) ;
2028
21- let displayChampions = championsMap ;
22-
23- if ( searchQuery . trim ( ) ) {
24- displayChampions = searchChampions ( searchQuery ) ;
25- }
29+ const displayChampions = useMemo ( ( ) => {
30+ const baseChampions = searchQuery . trim ( ) ? searchChampions ( searchQuery ) : championsMap ;
31+ if ( roleFilterSet . size === 0 ) {
32+ return baseChampions ;
33+ }
2634
27- if ( roleFilters . length > 0 ) {
28- displayChampions = displayChampions . filter ( ( champion ) => champion . roles . some ( ( role ) => roleFilters . includes ( role ) ) ) ;
29- }
35+ return baseChampions . filter ( ( champion ) => champion . roles . some ( ( role ) => roleFilterSet . has ( role ) ) ) ;
36+ } , [ roleFilterSet , searchQuery ] ) ;
3037
3138 const handleChampionClick = ( e : React . MouseEvent < HTMLButtonElement > ) => {
3239 if ( isDraftComplete && ! isOverridingAny ) return ;
@@ -63,7 +70,7 @@ export function ChampionList({ searchQuery, roleFilters }: ChampionListProps) {
6370
6471 return (
6572 < >
66- { displayChampions . map ( ( champ : Champion ) => {
73+ { displayChampions . map ( ( champ : Champion , index : number ) => {
6774 const isAvailable = isDraftComplete && ! isOverridingAny ? false : ! unavailableChampions . has ( champ . key ) ;
6875 const isSelected = selectedChampion === champ . key ;
6976
@@ -77,16 +84,12 @@ export function ChampionList({ searchQuery, roleFilters }: ChampionListProps) {
7784 className = { clsx ( isSelected && "selected" , isOverridingAny && "override-mode" ) }
7885 aria-label = { `${ isSelected ? "Selected" : "Select" } ${ champ . name } ${
7986 isOverridingAny ? " (Override Mode)" : ""
80- } `}
81- role = "option"
82- aria-selected = { isSelected }
83- tabIndex = { 8 } >
87+ } `} >
8488 < img
8589 src = { `/assets/champions/${ champ . key } .png` }
8690 alt = ""
87- aria-hidden = "true"
8891 decoding = "async"
89- loading = "eager"
92+ loading = { index < 24 ? "eager" : "lazy" }
9093 onError = { handleImageError }
9194 />
9295 < span > { champ . name } </ span >
0 commit comments