feat: more components

This commit is contained in:
shahdlala66
2026-04-19 01:00:14 +02:00
parent 53459648c6
commit 5497997455
7 changed files with 170 additions and 173 deletions
@@ -1,6 +1,6 @@
.piece { .piece {
width: clamp(50px, 11cqh, 160px); width: clamp(50px, 11cqh, 160px);
height: clamp(50px, 11cqh, 160px); height: clamp(40px, 8cqh, 120px);
display: block; display: block;
object-fit: contain; object-fit: contain;
pointer-events: none; pointer-events: none;
@@ -0,0 +1,63 @@
.input-card {
background: var(--color-bg-card);
border: var(--border-width) solid var(--color-border);
border-radius: var(--border-radius-md);
padding: var(--size-lg-padding);
display: flex;
flex-direction: column;
gap: var(--size-lg-gap);
}
.move-card {
padding: var(--size-sm-padding);
gap: var(--size-sm-gap);
}
.move-card input {
min-height: 45px;
padding: var(--size-md-padding);
}
.input-card label {
color: var(--color-text-primary);
font-weight: 700;
margin-bottom: var(--size-xs);
}
.input-card textarea {
resize: vertical;
height: 40px;
background-color:lightcyan;
border: 3px solid var(--color-border);
border-radius: var(--border-radius-sm);
}
.input-card input {
min-width: unset;
background-color:lightcyan;
border: 3px solid var(--color-border);
border-radius: var(--border-radius-sm);
}
.input-card .btn {
border: var(--button-border);
border-radius: var(--border-radius-sm);
background: var(--color-bg-button);
color: var(--color-text-primary);
padding: var(--button-padding);
cursor: pointer;
font-weight: 600;
transition: background-color 0.2s, color 0.2s;
}
.input-card .btn:hover {
background: var(--color-bg-button-hover);
color: var(--color-text-button-hover);
}
.hint-text {
margin: 0;
color: var(--color-text-primary);
font-size: 0.9rem;
}
@@ -0,0 +1,31 @@
<section [ngClass]="['input-card', cardClass]">
<label>{{ label }}</label>
@if (inputType === 'textarea') {
<textarea
#textareaInput
[placeholder]="placeholder"
[value]="value"
[rows]="rows"
(input)="onValueChange(textareaInput.value)"
class="form-input"
></textarea>
} @else {
<input
#textInput
type="text"
[placeholder]="placeholder"
[value]="value"
(input)="onValueChange(textInput.value)"
class="form-input"
/>
}
<button type="button" class="btn w-100" (click)="onButtonClick()">
{{ buttonLabel }}
</button>
@if (hintText) {
<p class="hint-text">{{ hintText }}</p>
}
</section>
@@ -0,0 +1,33 @@
import { Component, Input, Output, EventEmitter } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-input-card',
standalone: true,
imports: [CommonModule, FormsModule],
templateUrl: './input-card.component.html',
styleUrl: './input-card.component.css',
})
export class InputCardComponent {
@Input() label: string = '';
@Input() placeholder: string = '';
@Input() buttonLabel: string = 'Send';
@Input() inputType: 'input' | 'textarea' = 'input';
@Input() value: string = '';
@Input() cardClass: string = '';
@Input() hintText: string = '';
@Input() rows: number = 4;
@Output() valueChange = new EventEmitter<string>();
@Output() buttonClick = new EventEmitter<void>();
onValueChange(newValue: string): void {
this.value = newValue;
this.valueChange.emit(newValue);
}
onButtonClick(): void {
this.buttonClick.emit();
}
}
+6 -138
View File
@@ -44,56 +44,17 @@ h2 {
.top-section { .top-section {
display: grid; display: grid;
gap: var(--size-md); gap: var(--size-md);
margin-bottom: var(--size-sm); margin-top: var(--size-sm);
flex: 0 0 auto; flex: 0 0 auto;
} }
.move-form { .move-card {
display: flex; padding: var(--size-lg-padding);
flex-wrap: wrap;
align-items: center;
gap: var(--size-lg);
} }
.move-form label { .move-card .btn {
color: var(--color-text-primary); align-self: flex-start;
font-weight: 600; width: auto;
}
.board-hint {
margin: 0;
color: var(--color-text-primary);
}
.move-form button {
border: var(--button-border);
border-radius: var(--border-radius-md);
background: var(--color-bg-button);
color: var(--color-text-primary);
padding: var(--button-padding);
cursor: pointer;
font-weight: 600;
}
.move-form button:hover {
background: var(--color-bg-button-hover);
color: var(--color-text-button-hover);
}
/* Unified form input class for all text inputs and textareas */
.form-input {
border: var(--form-input-border);
border-radius: var(--form-input-radius);
background: var(--color-bg-input);
padding: var(--form-input-padding);
font-family: inherit;
min-width: 180px;
}
.form-input:focus {
outline: none;
background: var(--color-bg-input-focus);
border-color: var(--color-primary);
} }
.center-column { .center-column {
@@ -109,47 +70,6 @@ h2 {
container-type: size; container-type: size;
} }
.import-card {
background: var(--color-bg-card);
border: var(--border-width) solid var(--color-border);
border-radius: var(--border-radius-md);
padding: var(--size-lg-padding);
display: flex;
flex-direction: column;
gap: var(--size-lg-gap);
}
.import-card label {
color: var(--color-text-primary);
font-weight: 700;
margin-bottom: var(--size-xs);
}
.import-card textarea {
resize: vertical;
min-height: 100px;
}
.import-card input {
min-width: unset;
}
.btn {
border: var(--button-border);
border-radius: var(--border-radius-sm);
background: var(--color-bg-button);
color: var(--color-text-primary);
padding: var(--button-padding);
cursor: pointer;
font-weight: 600;
transition: background-color 0.2s, color 0.2s;
}
.btn:hover {
background: var(--color-bg-button-hover);
color: var(--color-text-button-hover);
}
.alert { .alert {
border-radius: var(--border-radius-sm); border-radius: var(--border-radius-sm);
border: var(--border-width) solid var(--color-border); border: var(--border-width) solid var(--color-border);
@@ -197,49 +117,9 @@ h2 {
margin-bottom: var(--size-xs); margin-bottom: var(--size-xs);
} }
.move-form {
flex-direction: column;
gap: var(--size-md);
}
.move-form label {
align-self: flex-start;
}
.move-form input {
width: 100%;
min-width: unset;
}
.move-form button {
width: 100%;
padding: var(--size-sm-padding);
}
.board-section { .board-section {
min-height: 300px; min-height: 300px;
} }
.import-card {
padding: var(--size-md-padding);
gap: var(--size-md-gap);
}
.import-card label {
font-size: 0.9rem;
}
.import-card textarea,
.import-card input {
min-height: 70px;
font-size: 0.9rem;
padding: var(--size-sm-padding);
}
.btn {
padding: var(--size-sm-padding);
font-size: 0.9rem;
}
} }
@media (max-width: 480px) { @media (max-width: 480px) {
@@ -272,16 +152,4 @@ h2 {
.board-section { .board-section {
min-height: 250px; min-height: 250px;
} }
.import-card {
padding: var(--size-sm-padding);
gap: var(--size-sm-gap);
}
.import-card textarea,
.import-card input {
min-height: 50px;
font-size: 0.8rem;
padding: var(--size-xs-padding);
}
} }
+34 -33
View File
@@ -13,32 +13,21 @@
<div class="row g-3"> <div class="row g-3">
<!-- Left Sidebar - FEN Import --> <!-- Left Sidebar - FEN Import -->
<div class="col-lg-3 col-md-6 col-12 order-lg-1 order-2"> <div class="col-lg-3 col-md-6 col-12 order-lg-1 order-2">
<aside class="import-card fen-card h-100"> <app-input-card
<label for="fenImport">Import FEN</label> label="Import FEN"
<textarea placeholder="rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
id="fenImport" buttonLabel="Load FEN"
name="fenImport" inputType="textarea"
class="form-input" [value]="facade.fenInput"
[(ngModel)]="facade.fenInput" cardClass="fen-card"
rows="4" (valueChange)="facade.fenInput = $event"
placeholder="rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" (buttonClick)="facade.importFen()"
></textarea> />
<button type="button" class="btn w-100" (click)="facade.importFen()">Load FEN</button>
</aside>
</div> </div>
<!-- Center - Chess Board --> <!-- Center - Chess Board -->
<div class="col-lg-6 col-md-12 col-12 order-lg-2 order-1"> <div class="col-lg-6 col-md-12 col-12 order-lg-2 order-1">
<section class="center-column d-flex flex-column h-100"> <section class="center-column d-flex flex-column h-100">
<section class="top-section">
<form class="move-form" (ngSubmit)="facade.submitMove()">
<label for="uciMove">Play move (UCI)</label>
<input id="uciMove" name="uciMove" class="form-input" [(ngModel)]="facade.moveInput" placeholder="e2e4" />
<button type="submit">Send Move</button>
</form>
<p class="board-hint">Click your piece to highlight legal targets.</p>
</section>
<div class="board-section flex-grow-1 d-flex align-items-center justify-content-center"> <div class="board-section flex-grow-1 d-flex align-items-center justify-content-center">
<app-chess-board <app-chess-board
[fen]="facade.state.fen" [fen]="facade.state.fen"
@@ -47,23 +36,35 @@
(squareSelected)="facade.onBoardSquareSelected($event)" (squareSelected)="facade.onBoardSquareSelected($event)"
/> />
</div> </div>
<section class="top-section">
<app-input-card
label="Play move (UCI)"
placeholder="e2e4"
buttonLabel="Send Move"
inputType="input"
[value]="facade.moveInput"
cardClass="move-card"
hintText="Click your piece to highlight legal targets."
(valueChange)="facade.moveInput = $event"
(buttonClick)="facade.submitMove()"
/>
</section>
</section> </section>
</div> </div>
<!-- Right Sidebar - PGN Import --> <!-- Right Sidebar - PGN Import -->
<div class="col-lg-3 col-md-6 col-12 order-lg-3 order-3"> <div class="col-lg-3 col-md-6 col-12 order-lg-3 order-3">
<aside class="import-card pgn-card h-100"> <app-input-card
<label for="pgnImport">Import PGN</label> label="Import PGN"
<textarea placeholder="1. e4 e5 2. Nf3 Nc6 *"
id="pgnImport" buttonLabel="Load PGN"
name="pgnImport" inputType="textarea"
class="form-input" [value]="facade.pgnInput"
[(ngModel)]="facade.pgnInput" cardClass="pgn-card"
rows="4" (valueChange)="facade.pgnInput = $event"
placeholder="1. e4 e5 2. Nf3 Nc6 *" (buttonClick)="facade.importPgn()"
></textarea> />
<button type="button" class="btn w-100" (click)="facade.importPgn()">Load PGN</button>
</aside>
</div> </div>
</div> </div>
</div> </div>
+2 -1
View File
@@ -4,12 +4,13 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { ActivatedRoute, RouterLink } from '@angular/router'; import { ActivatedRoute, RouterLink } from '@angular/router';
import { ChessBoardComponent } from '../../components/chess-board/chess-board.component'; import { ChessBoardComponent } from '../../components/chess-board/chess-board.component';
import { InputCardComponent } from '../../components/input-card/input-card.component';
import { GameFacade } from './game.facade'; import { GameFacade } from './game.facade';
@Component({ @Component({
selector: 'app-game', selector: 'app-game',
standalone: true, standalone: true,
imports: [CommonModule, FormsModule, RouterLink, ChessBoardComponent], imports: [CommonModule, FormsModule, RouterLink, ChessBoardComponent, InputCardComponent],
providers: [GameFacade], providers: [GameFacade],
templateUrl: './game.component.html', templateUrl: './game.component.html',
styleUrl: './game.component.css' styleUrl: './game.component.css'