113 lines
3.3 KiB
TypeScript
113 lines
3.3 KiB
TypeScript
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
|
|
import { CommonModule } from '@angular/common';
|
|
import { BoardCard } from './challenges.pure';
|
|
import { CategoryIconComponent } from '../../shared/components/category-icon/category-icon.component';
|
|
|
|
@Component({
|
|
selector: 'app-challenge-card',
|
|
standalone: true,
|
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
imports: [CommonModule, CategoryIconComponent],
|
|
styles: [`
|
|
:host { display: block; }
|
|
.card {
|
|
display: flex; flex-direction: column; gap: 8px;
|
|
padding: 12px 14px; border: 2px solid #1a1a24;
|
|
background: #000000;
|
|
cursor: pointer; transition: all 0.15s ease-in-out;
|
|
min-height: 90px;
|
|
text-align: left;
|
|
color: inherit;
|
|
font: inherit;
|
|
width: 100%;
|
|
}
|
|
.card:hover {
|
|
border-color: var(--neon-purple);
|
|
box-shadow: 0 0 12px var(--neon-purple-glow);
|
|
transform: translateY(-1px);
|
|
}
|
|
.card.solved {
|
|
border-color: var(--neon-green);
|
|
}
|
|
.card-head { display: flex; align-items: center; gap: 10px; }
|
|
.name {
|
|
font-family: var(--font-mono);
|
|
font-weight: 800;
|
|
font-size: 0.95rem;
|
|
flex: 1;
|
|
min-width: 0;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
color: #ffffff;
|
|
text-transform: uppercase;
|
|
}
|
|
.check {
|
|
color: var(--neon-green);
|
|
font-size: 16px;
|
|
font-weight: bold;
|
|
}
|
|
.meta {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
font-size: 11px;
|
|
font-family: var(--font-mono);
|
|
padding-top: 4px;
|
|
border-top: 1px solid #111116;
|
|
}
|
|
.points {
|
|
font-weight: 800;
|
|
color: #ffffff;
|
|
}
|
|
.solves-count {
|
|
color: #777788;
|
|
}
|
|
.diff {
|
|
font-weight: 800;
|
|
text-transform: uppercase;
|
|
font-size: 10px;
|
|
letter-spacing: 0.5px;
|
|
}
|
|
.diff-LOW { color: var(--neon-green); }
|
|
.diff-MEDIUM { color: var(--neon-yellow); }
|
|
.diff-HIGH { color: var(--neon-red); }
|
|
`],
|
|
template: `
|
|
<button
|
|
type="button"
|
|
class="card"
|
|
[class.solved]="card.solvedByMe"
|
|
[attr.data-testid]="'challenge-card-' + card.id"
|
|
[attr.data-solved]="card.solvedByMe ? 'true' : 'false'"
|
|
[attr.aria-label]="'Challenge ' + card.name + (card.solvedByMe ? ', solved' : ', not solved')"
|
|
[attr.aria-pressed]="card.solvedByMe"
|
|
(click)="open.emit(card.id)"
|
|
>
|
|
<div class="card-head">
|
|
<app-category-icon
|
|
[category]="card.categoryAbbreviation"
|
|
[size]="20"
|
|
[color]="card.solvedByMe ? 'var(--neon-green)' : 'var(--neon-purple)'"
|
|
/>
|
|
<span class="name">{{ card.name }}</span>
|
|
<span
|
|
*ngIf="card.solvedByMe"
|
|
class="check"
|
|
[attr.data-testid]="'challenge-check-' + card.id"
|
|
aria-hidden="true"
|
|
>✓</span>
|
|
</div>
|
|
<div class="meta">
|
|
<span class="diff" [class]="'diff-' + card.difficulty">{{ card.difficulty }}</span>
|
|
<span class="points" [attr.data-testid]="'points-' + card.id">{{ card.livePoints }} PTS</span>
|
|
<span class="solves-count">{{ card.solveCount }} 👥</span>
|
|
</div>
|
|
</button>
|
|
`,
|
|
})
|
|
export class ChallengeCardComponent {
|
|
@Input({ required: true }) card!: BoardCard;
|
|
@Output() open = new EventEmitter<string>();
|
|
}
|