You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
274 lines
7.5 KiB
274 lines
7.5 KiB
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
|
|
import { CommonModule } from '@angular/common';
|
|
import { MatCardModule } from '@angular/material/card';
|
|
import { MatIconModule } from '@angular/material/icon';
|
|
import { MatButtonModule } from '@angular/material/button';
|
|
import { MatAutocompleteModule } from '@angular/material/autocomplete';
|
|
import { MatInputModule } from '@angular/material/input';
|
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
|
import { PokemonService } from '../../../core/services/pokemon.service';
|
|
import { Pokemon } from '../../../core/models/pokemon.model';
|
|
import { PokemonCellComponent } from '../pokemon-cell/pokemon-cell.component';
|
|
import { PokemonDetailsComponent } from '../pokemon-details/pokemon-details.component';
|
|
import { map, startWith } from 'rxjs/operators';
|
|
import { Observable } from 'rxjs';
|
|
|
|
interface PokemonSearchResult {
|
|
pokemon: Pokemon;
|
|
boxNumber: number;
|
|
}
|
|
|
|
@Component({
|
|
selector: 'app-pokemon-carousel',
|
|
standalone: true,
|
|
imports: [
|
|
CommonModule,
|
|
MatCardModule,
|
|
MatIconModule,
|
|
MatButtonModule,
|
|
MatAutocompleteModule,
|
|
MatInputModule,
|
|
MatFormFieldModule,
|
|
ReactiveFormsModule,
|
|
PokemonCellComponent,
|
|
PokemonDetailsComponent
|
|
],
|
|
template: `
|
|
<div class="search-container">
|
|
<mat-form-field appearance="fill" class="search-field">
|
|
<mat-label>Search Pokémon</mat-label>
|
|
<input type="text"
|
|
matInput
|
|
[formControl]="searchControl"
|
|
[matAutocomplete]="auto">
|
|
<mat-autocomplete #auto="matAutocomplete"
|
|
(optionSelected)="onSearchSelect($event)"
|
|
[displayWith]="displayFn">
|
|
<mat-option *ngFor="let result of filteredOptions | async" [value]="result">
|
|
{{ result.pokemon.Name }}
|
|
<span *ngIf="result.pokemon.Form">
|
|
({{ result.pokemon.Form }})
|
|
</span>
|
|
<span *ngIf="result.boxNumber !== undefined">
|
|
(Box {{ result.boxNumber + 1 }})
|
|
</span>
|
|
</mat-option>
|
|
</mat-autocomplete>
|
|
</mat-form-field>
|
|
</div>
|
|
|
|
<div class="carousel-container">
|
|
<button mat-icon-button class="nav-button prev"
|
|
[disabled]="currentBoxIndex === 0"
|
|
(click)="previousBox()">
|
|
<mat-icon>chevron_left</mat-icon>
|
|
</button>
|
|
|
|
<div class="pokemon-box-container">
|
|
<mat-card class="pokemon-box">
|
|
<div class="box-title">Box {{ (currentBoxIndex + 1).toString().padStart(3, '0') }}</div>
|
|
<div class="pokemon-grid">
|
|
<app-pokemon-cell
|
|
*ngFor="let pokemon of currentGroup"
|
|
[pokemon]="pokemon"
|
|
(caught)="onPokemonCaught($event)"
|
|
(selected)="onPokemonSelected($event)"
|
|
></app-pokemon-cell>
|
|
</div>
|
|
</mat-card>
|
|
</div>
|
|
|
|
<button mat-icon-button class="nav-button next"
|
|
[disabled]="currentBoxIndex === pokemonGroups.length - 1"
|
|
(click)="nextBox()">
|
|
<mat-icon>chevron_right</mat-icon>
|
|
</button>
|
|
</div>
|
|
|
|
<div class="box-navigation">
|
|
<span>Box {{ currentBoxIndex + 1 }} of {{ pokemonGroups.length }}</span>
|
|
</div>
|
|
|
|
<app-pokemon-details
|
|
*ngIf="selectedPokemon"
|
|
[pokemon]="selectedPokemon"
|
|
[isOpen]="!!selectedPokemon"
|
|
(closed)="selectedPokemon = null"
|
|
></app-pokemon-details>
|
|
`,
|
|
styles: [`
|
|
.search-container {
|
|
display: flex;
|
|
justify-content: center;
|
|
}
|
|
|
|
.search-field {
|
|
width: 100%;
|
|
max-width: 400px;
|
|
}
|
|
.carousel-container {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
position: relative;
|
|
//height: calc(100vh - 200px);
|
|
padding-top: 2em;
|
|
padding-bottom: 2em;
|
|
}
|
|
|
|
.pokemon-box-container {
|
|
flex: 1;
|
|
max-width: 800px;
|
|
position: relative;
|
|
}
|
|
|
|
.pokemon-box {
|
|
background-color: white;
|
|
border: 2px solid #ccc;
|
|
border-radius: 10px;
|
|
padding: 20px;
|
|
position: relative;
|
|
width: 100%;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.box-title {
|
|
background-color: #4CAF50;
|
|
color: white;
|
|
padding: 5px 15px;
|
|
border-radius: 15px 15px 0 0;
|
|
position: absolute;
|
|
top: -30px;
|
|
left: 20px;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.pokemon-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(6, 1fr);
|
|
gap: 10px;
|
|
}
|
|
|
|
.nav-button {
|
|
margin: 0 20px;
|
|
&.prev { left: 0; }
|
|
&.next { right: 0; }
|
|
&:disabled {
|
|
opacity: 0.5;
|
|
}
|
|
}
|
|
|
|
.box-navigation {
|
|
text-align: center;
|
|
padding: 10px;
|
|
font-size: 1.1em;
|
|
color: #666;
|
|
}
|
|
`],
|
|
changeDetection: ChangeDetectionStrategy.OnPush
|
|
})
|
|
export class PokemonCarouselComponent implements OnInit {
|
|
pokemonGroups: (Pokemon | null)[][] = [];
|
|
currentBoxIndex = 0;
|
|
selectedPokemon: Pokemon | null = null;
|
|
caughtPokemon = new Set<string>();
|
|
searchControl = new FormControl('');
|
|
filteredOptions: Observable<PokemonSearchResult[]>;
|
|
|
|
get currentGroup(): (Pokemon | null)[] {
|
|
return this.pokemonGroups[this.currentBoxIndex] || [];
|
|
}
|
|
|
|
constructor(
|
|
private pokemonService: PokemonService,
|
|
private cdr: ChangeDetectorRef
|
|
) {
|
|
this.filteredOptions = this.searchControl.valueChanges.pipe(
|
|
startWith(''),
|
|
map(value => this.filterPokemon(value))
|
|
);
|
|
}
|
|
|
|
ngOnInit() {
|
|
this.loadPokemon();
|
|
}
|
|
|
|
private loadPokemon() {
|
|
this.pokemonService.getPokemonList().subscribe({
|
|
next: (groups) => {
|
|
this.pokemonGroups = groups;
|
|
this.cdr.markForCheck();
|
|
},
|
|
error: (error) => {
|
|
console.error('Error loading Pokemon:', error);
|
|
this.cdr.markForCheck();
|
|
}
|
|
});
|
|
}
|
|
|
|
nextBox() {
|
|
if (this.currentBoxIndex < this.pokemonGroups.length - 1) {
|
|
this.currentBoxIndex++;
|
|
this.cdr.markForCheck();
|
|
}
|
|
}
|
|
|
|
previousBox() {
|
|
if (this.currentBoxIndex > 0) {
|
|
this.currentBoxIndex--;
|
|
this.cdr.markForCheck();
|
|
}
|
|
}
|
|
|
|
onPokemonCaught(pfic: string) {
|
|
this.pokemonService.toggleCatch(pfic).subscribe(
|
|
response => {
|
|
if (response.status === 'caught') {
|
|
this.caughtPokemon.add(pfic);
|
|
} else {
|
|
this.caughtPokemon.delete(pfic);
|
|
}
|
|
this.cdr.markForCheck();
|
|
}
|
|
);
|
|
}
|
|
|
|
onPokemonSelected(pokemon: Pokemon) {
|
|
this.selectedPokemon = pokemon;
|
|
this.cdr.markForCheck();
|
|
}
|
|
|
|
private filterPokemon(value: string | PokemonSearchResult | null): PokemonSearchResult[] {
|
|
if (!value) return [];
|
|
|
|
const searchTerm = typeof value === 'string' ? value.toLowerCase() : '';
|
|
if (searchTerm.length < 2) return []; // Only search with 2 or more characters
|
|
|
|
const results: PokemonSearchResult[] = [];
|
|
|
|
this.pokemonGroups.forEach((group, boxIndex) => {
|
|
group.forEach(pokemon => {
|
|
if (pokemon && pokemon.Name.toLowerCase().includes(searchTerm)) {
|
|
results.push({
|
|
pokemon,
|
|
boxNumber: boxIndex
|
|
});
|
|
}
|
|
});
|
|
});
|
|
|
|
return results.slice(0, 10); // Limit to 10 results
|
|
}
|
|
|
|
displayFn(result: PokemonSearchResult): string {
|
|
return result?.pokemon?.Name || '';
|
|
}
|
|
|
|
onSearchSelect(event: any) {
|
|
const result: PokemonSearchResult = event.option.value;
|
|
this.currentBoxIndex = result.boxNumber;
|
|
this.searchControl.setValue('');
|
|
this.cdr.markForCheck();
|
|
}
|
|
}
|