55 Difficulty ,
66 GameMode ,
77 GameType ,
8+ RankedType ,
89 isDifficulty ,
910 isGameMode ,
1011 isGameType ,
@@ -17,11 +18,12 @@ import "./PlayerStatsTable";
1718@customElement ( "player-stats-tree-view" )
1819export class PlayerStatsTreeView extends LitElement {
1920 @property ( { type : Object } ) statsTree ?: PlayerStatsTree ;
20- @state ( ) selectedType : GameType = GameType . Public ;
21+ @state ( ) selectedType : GameType | "Ranked" = GameType . Public ;
2122 @state ( ) selectedMode : GameMode = GameMode . FFA ;
2223 @state ( ) selectedDifficulty : Difficulty = Difficulty . Medium ;
23-
24+ @ state ( ) selectedRankedType : RankedType = RankedType . OneVOne ;
2425 private get typeNode ( ) {
26+ if ( this . selectedType === "Ranked" ) return undefined ;
2527 return this . statsTree ?. [ this . selectedType ] ;
2628 }
2729
@@ -33,16 +35,34 @@ export class PlayerStatsTreeView extends LitElement {
3335 return this . selectedType === GameType . Public ;
3436 }
3537
36- private get availableTypes ( ) : GameType [ ] {
38+ private get availableTypes ( ) : ( GameType | "Ranked" ) [ ] {
3739 if ( ! this . statsTree ) return [ ] ;
38- return Object . keys ( this . statsTree ) . filter ( isGameType ) ;
40+ const types : ( GameType | "Ranked" ) [ ] = Object . keys ( this . statsTree ) . filter (
41+ ( k ) : k is GameType =>
42+ isGameType ( k ) &&
43+ Object . keys ( this . statsTree ! [ k as GameType ] ?? { } ) . length > 0 ,
44+ ) ;
45+ if (
46+ this . statsTree . Ranked &&
47+ Object . keys ( this . statsTree . Ranked ) . length > 0
48+ ) {
49+ types . push ( "Ranked" ) ;
50+ }
51+ return types ;
3952 }
4053
4154 private get availableModes ( ) : GameMode [ ] {
4255 if ( ! this . typeNode ) return [ ] ;
4356 return Object . keys ( this . typeNode ) . filter ( isGameMode ) ;
4457 }
4558
59+ private get availableRankedTypes ( ) : RankedType [ ] {
60+ if ( ! this . statsTree ?. Ranked ) return [ ] ;
61+ return Object . keys ( this . statsTree . Ranked ) . filter ( ( k ) : k is RankedType =>
62+ Object . values ( RankedType ) . includes ( k as RankedType ) ,
63+ ) ;
64+ }
65+
4666 private get availableDifficulties ( ) : Difficulty [ ] {
4767 if ( ! this . modeNode ) return [ ] ;
4868 return Object . keys ( this . modeNode ) . filter ( isDifficulty ) ;
@@ -54,11 +74,22 @@ export class PlayerStatsTreeView extends LitElement {
5474 : translateText ( "game_mode.teams" ) ;
5575 }
5676
77+ private labelForRankedType ( r : RankedType ) {
78+ switch ( r ) {
79+ case RankedType . OneVOne :
80+ return translateText ( "player_stats_tree.ranked_1v1" ) ;
81+ }
82+ }
83+
5784 createRenderRoot ( ) {
5885 return this ;
5986 }
6087
6188 private getSelectedLeaf ( ) : PlayerStatsLeaf | null {
89+ if ( this . selectedType === "Ranked" ) {
90+ return this . statsTree ?. Ranked ?. [ this . selectedRankedType ] ?? null ;
91+ }
92+
6293 const modeNode = this . modeNode ;
6394 if ( ! modeNode ) return null ;
6495
@@ -91,9 +122,19 @@ export class PlayerStatsTreeView extends LitElement {
91122
92123 private syncSelection ( ) : void {
93124 const types = this . availableTypes ;
94- if ( types . length && ! types . includes ( this . selectedType ) ) {
125+ if ( types . length && ! types . includes ( this . selectedType as GameType ) ) {
95126 this . selectedType = types [ 0 ] ;
96127 }
128+ if ( this . selectedType === "Ranked" ) {
129+ const rankedTypes = this . availableRankedTypes ;
130+ if (
131+ rankedTypes . length &&
132+ ! rankedTypes . includes ( this . selectedRankedType )
133+ ) {
134+ this . selectedRankedType = rankedTypes [ 0 ] ;
135+ }
136+ return ;
137+ }
97138 const modes = this . availableModes ;
98139 if ( modes . length && ! modes . includes ( this . selectedMode ) ) {
99140 this . selectedMode = modes [ 0 ] ;
@@ -113,13 +154,14 @@ export class PlayerStatsTreeView extends LitElement {
113154 changedProperties . has ( "statsTree" ) ||
114155 changedProperties . has ( "selectedType" ) ||
115156 changedProperties . has ( "selectedMode" ) ||
116- changedProperties . has ( "selectedDifficulty" )
157+ changedProperties . has ( "selectedDifficulty" ) ||
158+ changedProperties . has ( "selectedRankedType" )
117159 ) {
118160 this . syncSelection ( ) ;
119161 }
120162 }
121163
122- private setGameType ( t : GameType ) {
164+ private setGameType ( t : GameType | "Ranked" ) {
123165 if ( this . selectedType === t ) return ;
124166 this . selectedType = t ;
125167 this . requestUpdate ( ) ;
@@ -131,6 +173,12 @@ export class PlayerStatsTreeView extends LitElement {
131173 this . requestUpdate ( ) ;
132174 }
133175
176+ private setRankedType ( r : RankedType ) {
177+ if ( this . selectedRankedType === r ) return ;
178+ this . selectedRankedType = r ;
179+ this . requestUpdate ( ) ;
180+ }
181+
134182 private setDifficulty ( d : Difficulty ) {
135183 if ( this . selectedDifficulty === d ) return ;
136184 this . selectedDifficulty = d ;
@@ -215,6 +263,7 @@ export class PlayerStatsTreeView extends LitElement {
215263 const types = this . availableTypes ;
216264 const modes = this . availableModes ;
217265 const diffs = this . availableDifficulties ;
266+ const rankedTypes = this . availableRankedTypes ;
218267 const leaf = this . getSelectedLeaf ( ) ;
219268 const wlr = leaf
220269 ? leaf . losses === 0n
@@ -239,17 +288,40 @@ export class PlayerStatsTreeView extends LitElement {
239288 : "bg-white/5 border-white/10 text-gray-400 hover:bg-white/10 hover:text-white" } "
240289 @click =${ ( ) => this . setGameType ( t ) }
241290 >
242- ${ t === GameType . Public
243- ? translateText ( "player_stats_tree.public" )
244- : t === GameType . Private
245- ? translateText ( "player_stats_tree.private" )
246- : translateText ( "player_stats_tree.solo" ) }
291+ ${ t === "Ranked"
292+ ? translateText ( "player_stats_tree.ranked" )
293+ : t === GameType . Public
294+ ? translateText ( "player_stats_tree.public" )
295+ : t === GameType . Private
296+ ? translateText ( "player_stats_tree.private" )
297+ : translateText ( "player_stats_tree.solo" ) }
247298 </ button >
248299 ` ,
249300 ) }
250301 </ div >
251302
252303 < div class ="flex gap-2 ">
304+ <!-- Ranked type selector -->
305+ ${ this . selectedType === "Ranked" && rankedTypes . length
306+ ? html `< div
307+ class ="flex gap-1 bg-black/20 rounded-md p-1 border border-white/5 "
308+ >
309+ ${ rankedTypes . map (
310+ ( r ) => html `
311+ < button
312+ class ="text-xs px-3 py-1 rounded-sm transition-colors ${ this
313+ . selectedRankedType === r
314+ ? "bg-white/20 text-white font-bold"
315+ : "text-gray-400 hover:text-white" } "
316+ @click =${ ( ) => this . setRankedType ( r ) }
317+ >
318+ ${ this . labelForRankedType ( r ) }
319+ </ button >
320+ ` ,
321+ ) }
322+ </ div > `
323+ : html `` }
324+
253325 <!-- Mode selector -->
254326 ${ modes . length
255327 ? html `< div
0 commit comments