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

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();
}
}