chore: updates angular to v20

This commit is contained in:
2025-10-31 18:31:34 -03:00
parent 401879275c
commit 3fd87ba2c8
35 changed files with 4636 additions and 9694 deletions

View File

@@ -9,6 +9,7 @@ import {CookieConsertService} from './shared/cookie-consent/cookie-consert.servi
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
standalone: false
})
export class AppComponent implements OnInit {
title = 'frontend-hideyoshi.com';

View File

@@ -5,6 +5,7 @@ import {faGithub, faLinkedinIn, faTwitter,} from '@fortawesome/free-brands-svg-i
selector: 'app-footer',
templateUrl: './footer.component.html',
styleUrls: ['./footer.component.css'],
standalone: false
})
export class FooterComponent {
_githubIcon = faGithub;

View File

@@ -1,36 +1,44 @@
<div
class="dropdown"
appClickedOutside
(clickOutside)="onClickedOutside()"
[includeClickedOutside]="[management]"
[ignoreElementList]="ignoreClickOutside"
[@dropdownState]="dropDownState"
(@dropdownState.start)="$event.element.style.display = 'block'"
class="dropdown"
appClickedOutside
(clickOutside)="onClickedOutside()"
[includeClickedOutside]="[management]"
[ignoreElementList]="ignoreClickOutside"
[@dropdownState]="dropDownState"
(@dropdownState.start)="$event.element.style.display = 'block'"
(@dropdownState.done)="
$event.element.style.display = state ? 'block' : 'none'
"
>
<div class="info">
<h3>{{ this.user ? this.user.username : "User Account" }}</h3>
</div>
<div #management>
<ul class="user-management" *ngIf="!this.user">
<li *ngFor="let option of mainOptions"
class="dropdown-item" (click)=option.callback()>
<div class="icon-box">
<fa-icon [icon]="option.icon"></fa-icon>
</div>
<p>{{option.text}}</p>
</li>
</ul>
<ul class="user-management" *ngIf="this.user">
<li *ngFor="let option of userOptions"
class="dropdown-item" (click)=option.callback()>
<div class="icon-box">
<fa-icon [icon]="option.icon"></fa-icon>
</div>
<p>{{option.text}}</p>
</li>
</ul>
</div>
>
<div class="info">
<h3>{{ this.user ? this.user.username : "User Account" }}</h3>
</div>
<div #management>
@if (!this.user) {
<ul class="user-management">
@for (option of mainOptions; track option) {
<li
class="dropdown-item" (click)=option.callback()>
<div class="icon-box">
<fa-icon [icon]="option.icon"></fa-icon>
</div>
<p>{{option.text}}</p>
</li>
}
</ul>
}
@if (this.user) {
<ul class="user-management">
@for (option of userOptions; track option) {
<li
class="dropdown-item" (click)=option.callback()>
<div class="icon-box">
<fa-icon [icon]="option.icon"></fa-icon>
</div>
<p>{{option.text}}</p>
</li>
}
</ul>
}
</div>
</div>

View File

@@ -13,22 +13,17 @@ import {Value} from "@sinclair/typebox/value";
styleUrls: ['./header-dropdown.component.css'],
animations: [
trigger('dropdownState', [
state(
'hide',
style({
opacity: '0',
}),
),
state(
'show',
style({
opacity: '1',
}),
),
state('hide', style({
opacity: '0',
})),
state('show', style({
opacity: '1',
})),
transition('hide => show', animate('20ms ease-in')),
transition('show => hide', animate('5ms ease-out')),
]),
],
standalone: false
})
export class HeaderDropdownComponent implements OnInit, OnDestroy {
mainOptions: { text: string, icon: IconDefinition, callback: () => void }[] = [

View File

@@ -6,6 +6,7 @@ import {AuthService} from 'src/app/shared/service/auth.service';
selector: 'app-callback',
templateUrl: './callback.component.html',
styleUrls: ['./callback.component.css'],
standalone: false
})
export class CallbackComponent implements OnInit {
constructor(

View File

@@ -1,3 +1,5 @@
<div class="error-box" *ngIf="errorMessage">
@if (errorMessage) {
<div class="error-box">
{{ errorMessage }}
</div>
</div>
}

View File

@@ -4,6 +4,7 @@ import {Component, Input} from '@angular/core';
selector: 'app-error-box',
templateUrl: './error-box.component.html',
styleUrls: ['./error-box.component.css'],
standalone: false
})
export class ErrorBoxComponent {
@Input()

View File

@@ -4,6 +4,7 @@ import {Component, EventEmitter, Input, Output} from '@angular/core';
selector: 'app-help',
templateUrl: './help.component.html',
styleUrls: ['./help.component.css'],
standalone: false
})
export class HelpComponent {
@Input()

View File

@@ -1,96 +1,98 @@
<app-popup
[state]="state"
(stateChange)="onStateChange($event)"
[ignoreClickOutside]="ignoreClickOutside"
>
<div
class="container m-0 overflow-hidden"
[@resizeContainerForErrorMessage]="hideErrorMessage()"
[state]="state"
(stateChange)="onStateChange($event)"
[ignoreClickOutside]="ignoreClickOutside"
>
<div
class="container m-0 overflow-hidden"
[@resizeContainerForErrorMessage]="hideErrorMessage()"
>
<app-error-box
[errorMessage]="errorMessage"
[@showErrorMessage]="showErrorMessage()"
>
</app-error-box>
<app-error-box
[errorMessage]="errorMessage"
[@showErrorMessage]="showErrorMessage()"
>
</app-error-box>
<div
class="container authentication-container"
[@hideAuthContainer]="hideErrorMessage()"
(@hideAuthContainer.done)="hideAuthContainer($event)"
>
<div class="row">
<div class="col-lg-6 authentication-body">
<form [formGroup]="loginForm" (ngSubmit)="onLogin()">
<div class="input-div">
<fa-icon class="input-div-icon" [icon]="_userIcon">
</fa-icon>
<input
type="text"
id="username"
formControlName="username"
class="form-control"
placeholder="Username"
/>
</div>
<div class="input-div">
<fa-icon
class="input-div-icon"
[icon]="_passwordIcon"
>
</fa-icon>
<input
type="password"
id="password"
formControlName="password"
class="form-control"
placeholder="Password"
/>
</div>
<button
class="btn"
[disabled]="loginForm.invalid"
type="submit"
>
Login
</button>
</form>
</div>
<div class="separator-line">
<div class="line"></div>
</div>
<div class="col-lg-6 authentication-body">
<button
mat-button
class="oauth-button d-flex justify-content-center align-items-center"
[disabled]="isCookieBlocked"
(click)="onGoogleLogin()"
>
<mat-icon
*ngIf="!isCookieBlocked"
style="width: 50px; height: 30px"
svgIcon="google-logo"
></mat-icon>
<mat-icon
*ngIf="isCookieBlocked"
style="width: 50px; height: 30px"
svgIcon="google-disabled-logo"
></mat-icon>
Login With Google
</button>
<button
mat-button
class="oauth-button d-flex justify-content-center align-items-center"
[disabled]="isCookieBlocked"
(click)="onGithubLogin()"
>
<mat-icon
style="width: 50px; height: 30px"
svgIcon="github-logo"
></mat-icon>
Login With Github
</button>
</div>
<div
class="container authentication-container"
[@hideAuthContainer]="hideErrorMessage()"
(@hideAuthContainer.done)="hideAuthContainer($event)"
>
<div class="row">
<div class="col-lg-6 authentication-body">
<form [formGroup]="loginForm" (ngSubmit)="onLogin()">
<div class="input-div">
<fa-icon class="input-div-icon" [icon]="_userIcon">
</fa-icon>
<input
type="text"
id="username"
formControlName="username"
class="form-control"
placeholder="Username"
/>
</div>
<div class="input-div">
<fa-icon
class="input-div-icon"
[icon]="_passwordIcon"
>
</fa-icon>
<input
type="password"
id="password"
formControlName="password"
class="form-control"
placeholder="Password"
/>
</div>
<button
class="btn"
[disabled]="loginForm.invalid"
type="submit"
>
Login
</button>
</form>
</div>
<div class="separator-line">
<div class="line"></div>
</div>
<div class="col-lg-6 authentication-body">
<button
mat-button
class="oauth-button d-flex justify-content-center align-items-center"
[disabled]="isCookieBlocked"
(click)="onGoogleLogin()"
>
@if (!isCookieBlocked) {
<mat-icon
style="width: 50px; height: 30px"
svgIcon="google-logo"
></mat-icon>
}
@if (isCookieBlocked) {
<mat-icon
style="width: 50px; height: 30px"
svgIcon="google-disabled-logo"
></mat-icon>
}
Login With Google
</button>
<button
mat-button
class="oauth-button d-flex justify-content-center align-items-center"
[disabled]="isCookieBlocked"
(click)="onGithubLogin()"
>
<mat-icon
style="width: 50px; height: 30px"
svgIcon="github-logo"
></mat-icon>
Login With Github
</button>
</div>
</div>
</div>
</div>
</app-popup>

View File

@@ -33,56 +33,39 @@ const GITHUB_LOGO_SVG = 'assets/img/providers/github.svg';
styleUrls: ['./login.component.css'],
animations: [
trigger('resizeContainerForErrorMessage', [
state(
'hide',
style({
height: '100px',
width: '320px',
}),
),
transition(
'show => hide',
group([
query('@*', animateChild(), { optional: true }),
animate('1s ease'),
]),
),
state('hide', style({
height: '100px',
width: '320px',
})),
transition('show => hide', group([
query('@*', animateChild(), { optional: true }),
animate('1s ease'),
])),
]),
trigger('showErrorMessage', [
state(
'show',
style({
opacity: 1,
height: '100px',
width: '320px',
}),
),
state(
'hide',
style({
opacity: 0,
height: '0px',
width: '0px',
}),
),
state('show', style({
opacity: 1,
height: '100px',
width: '320px',
})),
state('hide', style({
opacity: 0,
height: '0px',
width: '0px',
})),
transition('* => show', animate('500ms ease-in')),
]),
trigger('hideAuthContainer', [
state(
'hide',
style({
opacity: 0,
}),
),
transition(
'show => hide',
group([
query('@*', animateChild(), { optional: true }),
animate('250ms ease-out'),
]),
),
state('hide', style({
opacity: 0,
})),
transition('show => hide', group([
query('@*', animateChild(), { optional: true }),
animate('250ms ease-out'),
])),
]),
],
standalone: false
})
export class LoginComponent implements OnInit, AfterViewInit, OnDestroy {
@Input()

View File

@@ -13,56 +13,39 @@ import {faFileUpload} from '@fortawesome/free-solid-svg-icons';
styleUrls: ['./my-profile.component.css'],
animations: [
trigger('resizeContainerForErrorMessage', [
state(
'hide',
style({
height: '100px',
width: '320px',
}),
),
transition(
'show => hide',
group([
query('@*', animateChild(), { optional: true }),
animate('1s ease'),
]),
),
state('hide', style({
height: '100px',
width: '320px',
})),
transition('show => hide', group([
query('@*', animateChild(), { optional: true }),
animate('1s ease'),
])),
]),
trigger('showErrorMessage', [
state(
'show',
style({
opacity: 1,
height: '100px',
width: '320px',
}),
),
state(
'hide',
style({
opacity: 0,
height: '0px',
width: '0px',
}),
),
state('show', style({
opacity: 1,
height: '100px',
width: '320px',
})),
state('hide', style({
opacity: 0,
height: '0px',
width: '0px',
})),
transition('* => show', animate('500ms ease-in')),
]),
trigger('hideAuthContainer', [
state(
'hide',
style({
opacity: 0,
}),
),
transition(
'show => hide',
group([
query('@*', animateChild(), { optional: true }),
animate('250ms ease-out'),
]),
),
state('hide', style({
opacity: 0,
})),
transition('show => hide', group([
query('@*', animateChild(), { optional: true }),
animate('250ms ease-out'),
])),
]),
],
standalone: false
})
export class MyProfileComponent implements OnInit {
@Input()

View File

@@ -5,6 +5,7 @@ import {AuthService} from '../../../../shared/service/auth.service';
selector: 'app-profile-picture-picker',
templateUrl: './profile-picture-picker.component.html',
styleUrls: ['./profile-picture-picker.component.css'],
standalone: false
})
export class ProfilePicturePickerComponent {
@Output()

View File

@@ -22,56 +22,39 @@ const GITHUB_LOGO_SVG = 'assets/img/providers/github.svg';
styleUrls: ['./signup.component.css'],
animations: [
trigger('resizeContainerForErrorMessage', [
state(
'hide',
style({
height: '100px',
width: '320px',
}),
),
transition(
'show => hide',
group([
query('@*', animateChild(), { optional: true }),
animate('1s ease'),
]),
),
state('hide', style({
height: '100px',
width: '320px',
})),
transition('show => hide', group([
query('@*', animateChild(), { optional: true }),
animate('1s ease'),
])),
]),
trigger('showErrorMessage', [
state(
'show',
style({
opacity: 1,
height: '100px',
width: '320px',
}),
),
state(
'hide',
style({
opacity: 0,
height: '0px',
width: '0px',
}),
),
state('show', style({
opacity: 1,
height: '100px',
width: '320px',
})),
state('hide', style({
opacity: 0,
height: '0px',
width: '0px',
})),
transition('* => show', animate('500ms ease-in')),
]),
trigger('hideAuthContainer', [
state(
'hide',
style({
opacity: 0,
}),
),
transition(
'show => hide',
group([
query('@*', animateChild(), { optional: true }),
animate('250ms ease-out'),
]),
),
state('hide', style({
opacity: 0,
})),
transition('show => hide', group([
query('@*', animateChild(), { optional: true }),
animate('250ms ease-out'),
])),
]),
],
standalone: false
})
export class SignupComponent implements OnInit {
@Input()

View File

@@ -7,18 +7,12 @@ import {Component, EventEmitter, Input, Output} from '@angular/core';
styleUrls: ['./header-slider.component.css'],
animations: [
trigger('slideState', [
state(
'hide',
style({
transform: 'translateX(100%)',
}),
),
state(
'show',
style({
transform: 'translateX(0%)',
}),
),
state('hide', style({
transform: 'translateX(100%)',
})),
state('show', style({
transform: 'translateX(0%)',
})),
transition('hide => show', [
group([
query('@*', animateChild(), { optional: true }),
@@ -33,6 +27,7 @@ import {Component, EventEmitter, Input, Output} from '@angular/core';
]),
]),
],
standalone: false
})
export class HeaderSliderComponent {
@Input()

View File

@@ -1,8 +1,8 @@
<div class="links-container">
<div class="nav-links">
<ul>
<li
*ngFor="let page of pages; let i = index"
<div class="nav-links">
<ul>
@for (page of pages; track page; let i = $index) {
<li
[@animateSliderItem]="{
value: itemStatus,
params: {
@@ -10,18 +10,19 @@
fadeOutTime: 0.6 - i / 10
}
}"
>
<a [routerLink]="page.route">
{{ page.name }}
</a>
</li>
</ul>
</div>
>
<a [routerLink]="page.route">
{{ page.name }}
</a>
</li>
}
</ul>
</div>
</div>
<div class="profile-container">
<div
class="profile"
<div
class="profile"
[@animateSliderItem]="{
value: itemStatus,
params: {
@@ -29,23 +30,25 @@
fadeOutTime: 0.6 - (pages.length + 1) / 10
}
}"
#profile
>
<div class="profile-btn" (click)="onProfileButtonClicked()">
<fa-icon
*ngIf="!loggedUser || !loggedUser.profilePictureUrl"
class="fas fa-user"
[icon]="userIcon"
></fa-icon>
<img
*ngIf="!!loggedUser && !!loggedUser.profilePictureUrl"
class="profile-picture"
[ngSrc]="loggedUser.profilePictureUrl"
width="50"
height="50"
alt="Profile Picture"
priority
/>
</div>
</div>
#profile
>
<div class="profile-btn" (click)="onProfileButtonClicked()">
@if (!loggedUser || !loggedUser.profilePictureUrl) {
<fa-icon
class="fas fa-user"
[icon]="userIcon"
></fa-icon>
}
@if (!!loggedUser && !!loggedUser.profilePictureUrl) {
<img
class="profile-picture"
[ngSrc]="loggedUser.profilePictureUrl"
width="50"
height="50"
alt="Profile Picture"
priority
/>
}
</div>
</div>
</div>

View File

@@ -10,6 +10,7 @@ import {Value} from "@sinclair/typebox/value";
selector: 'app-nav-slider',
templateUrl: './nav-slider.component.html',
styleUrls: ['./nav-slider.component.css'],
standalone: false
})
export class NavSliderComponent
extends SliderItemComponent

View File

@@ -1,11 +1,10 @@
<div class="user-container">
<div class="user-options">
<ul>
<li
*ngFor="
let options of user ? userOptions : userlessOptions;
let i = index
"
<div class="user-options">
<ul>
@for (
options of user ? userOptions : userlessOptions; track
options; let i = $index) {
<li
[@animateSliderItem]="{
value: itemStatus,
params: {
@@ -13,11 +12,12 @@
fadeOutTime: 0.6 - i / 10
}
}"
>
<a (click)="options.onClick()">
{{ options.name }}
</a>
</li>
</ul>
</div>
>
<a (click)="options.onClick()">
{{ options.name }}
</a>
</li>
}
</ul>
</div>
</div>

View File

@@ -9,6 +9,7 @@ import {Value} from "@sinclair/typebox/value";
selector: 'app-user-slider',
templateUrl: './user-slider.component.html',
styleUrls: ['./user-slider.component.css'],
standalone: false
})
export class UserSliderComponent extends SliderItemComponent implements OnInit {
userlessOptions = [

View File

@@ -1,90 +1,94 @@
<div class="header">
<div class="main" #header>
<div class="logo">
<a routerLink="">
<img src="assets/img/logohideyoshi-white.png" alt="" />
</a>
</div>
<div class="nav-links">
<ul class="link-container">
<li *ngFor="let page of pages">
<a [routerLink]="page.route">{{ page.name }}</a>
</li>
</ul>
</div>
<div class="profile" #profileDropdown>
<div
class="profile-btn"
(click)="toogleProfileDropdown()"
#profileBtn
>
<fa-icon
*ngIf="!loggedUser || !loggedUser.profilePictureUrl"
class="fas fa-user"
[icon]="userIcon"
></fa-icon>
<img
*ngIf="!!loggedUser && !!loggedUser.profilePictureUrl"
class="profile-picture"
[ngSrc]="loggedUser.profilePictureUrl"
width="50"
height="50"
alt="Profile Picture"
priority
/>
</div>
<app-header-dropdown
class="dropdown"
(clickOutside)="closeDropdown()"
[ignoreClickOutside]="[profileBtn]"
[state]="profileDropdownState"
(loginPopupState)="loginPopupStateChange($event)"
(signupPopupState)="signupPopupStateChange($event)"
(myProfilePopupState)="myProfilePopupStateChange($event)"
(helpPopupState)="helpPopupStateChange($event)"
>
</app-header-dropdown>
</div>
<div class="burger-container" (click)="toogleNavSlider()">
<div
class="burger-menu"
[ngClass]="{ open: navSliderStatus }"
></div>
</div>
<div class="main" #header>
<div class="logo">
<a routerLink="">
<img src="assets/img/logohideyoshi-white.png" alt="" />
</a>
</div>
<div class="nav-links">
<ul class="link-container">
@for (page of pages; track page) {
<li>
<a [routerLink]="page.route">{{ page.name }}</a>
</li>
}
</ul>
</div>
<div class="profile" #profileDropdown>
<div
class="profile-btn"
(click)="toogleProfileDropdown()"
#profileBtn
>
@if (!loggedUser || !loggedUser.profilePictureUrl) {
<fa-icon
class="fas fa-user"
[icon]="userIcon"
></fa-icon>
}
@if (!!loggedUser && !!loggedUser.profilePictureUrl) {
<img
class="profile-picture"
[ngSrc]="loggedUser.profilePictureUrl"
width="50"
height="50"
alt="Profile Picture"
priority
/>
}
</div>
<app-header-dropdown
class="dropdown"
(clickOutside)="closeDropdown()"
[ignoreClickOutside]="[profileBtn]"
[state]="profileDropdownState"
(loginPopupState)="loginPopupStateChange($event)"
(signupPopupState)="signupPopupStateChange($event)"
(myProfilePopupState)="myProfilePopupStateChange($event)"
(helpPopupState)="helpPopupStateChange($event)"
>
</app-header-dropdown>
</div>
<div class="burger-container" (click)="toogleNavSlider()">
<div
class="burger-menu"
[ngClass]="{ open: navSliderStatus }"
></div>
</div>
</div>
</div>
<div class="slider-container" #nav>
<app-header-slider
[(state)]="navSliderStatus"
[clickOutsideStopWatching]="userSliderStatus"
[ignoreClickOutside]="[header, user]"
<app-header-slider
[(state)]="navSliderStatus"
[clickOutsideStopWatching]="userSliderStatus"
[ignoreClickOutside]="[header, user]"
>
<app-nav-slider
[state]="navSliderStatus"
(profileButtonClicked)="profileButtonClicked()"
[pages]="pages"
>
</app-nav-slider>
</app-header-slider>
<app-nav-slider
[state]="navSliderStatus"
(profileButtonClicked)="profileButtonClicked()"
[pages]="pages"
>
</app-nav-slider>
</app-header-slider>
</div>
<div class="slider-container" #user>
<app-header-slider
[(state)]="userSliderStatus"
[ignoreClickOutside]="[header, nav]"
<app-header-slider
[(state)]="userSliderStatus"
[ignoreClickOutside]="[header, nav]"
>
<app-user-slider
[state]="userSliderStatus"
(loginPopupState)="loginPopupStateChange($event)"
(signupPopupState)="signupPopupStateChange($event)"
(myProfilePopupState)="myProfilePopupStateChange($event)"
(helpPopupState)="helpPopupStateChange($event)"
>
</app-user-slider>
</app-header-slider>
<app-user-slider
[state]="userSliderStatus"
(loginPopupState)="loginPopupStateChange($event)"
(signupPopupState)="signupPopupStateChange($event)"
(myProfilePopupState)="myProfilePopupStateChange($event)"
(helpPopupState)="helpPopupStateChange($event)"
>
</app-user-slider>
</app-header-slider>
</div>
<div class="header-spacer"></div>

View File

@@ -13,6 +13,7 @@ import {Value} from "@sinclair/typebox/value";
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.css'],
standalone: false
})
export class HeaderComponent implements OnInit, OnDestroy {
pages: { name: string; route: string }[] = [

View File

@@ -4,6 +4,7 @@ import {Component} from '@angular/core';
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css'],
standalone: false
})
export class HomeComponent {
constructor() {}

View File

@@ -23,6 +23,7 @@ import {animate, state, style, transition, trigger} from "@angular/animations";
]),
])
],
standalone: false
})
export class StackCardComponent {
@Input()

View File

@@ -1,5 +1,9 @@
<ngx-glide #ngxGlide class="container stack-slider" *ngIf="stacks && stacks.length > 0">
<app-stack-card *ngFor="let stack of stacks" class="slider-card" [stack]="stack"
@if (stacks && stacks.length > 0) {
<ngx-glide #ngxGlide class="container stack-slider">
@for (stack of stacks; track stack) {
<app-stack-card class="slider-card" [stack]="stack"
[inFocus]="isInFocus(stack)">
</app-stack-card>
</ngx-glide>
</app-stack-card>
}
</ngx-glide>
}

View File

@@ -6,7 +6,8 @@ import {Stack} from "../../shared/model/stack/stack.model";
@Component({
selector: 'app-stack-slider',
templateUrl: './stack-slider.component.html',
styleUrls: ['./stack-slider.component.css']
styleUrls: ['./stack-slider.component.css'],
standalone: false
})
export class StackSliderComponent implements AfterViewInit {
@ViewChild('ngxGlide')

View File

@@ -1,49 +1,55 @@
<div class="card container">
<div class="card-content">
<div class="card-content-h">
<h2 class="card-title">
<a [href]="project.link">{{project.name}}</a>
</h2>
<p class="card-text">{{project.description}}</p>
</div>
<div class="card-content-f row">
<div class="card-languages col-md-9" *ngIf="hasLanguage" id="language-chart">
<apx-chart *ngIf="chartOptions !== undefined"
[series]="chartOptions.series"
[colors]="chartOptions.colors"
[chart]="chartOptions.chart"
[labels]="chartOptions.labels"
[responsive]="chartOptions.responsive"
[plotOptions]="chartOptions.plotOptions"
[dataLabels]="chartOptions.dataLabels">
</apx-chart>
</div>
<div class="card-stats" [ngClass]="hasLanguage ? 'col-md-3' : 'stats-inline'">
<div class="stat-item" *ngIf="hasLicense">
<div class="stat-icon">
<fa-icon [icon]="faLicense"></fa-icon>
</div>
<span>{{project.license}}</span>
</div>
<div class="stat-item">
<div class="stat-icon">
<fa-icon [icon]="faStars"></fa-icon>
</div>
<span>{{project.stars}}</span>
</div>
<div class="stat-item">
<div class="stat-icon">
<fa-icon [icon]="faCodeFork"></fa-icon>
</div>
<span>{{project.forks}}</span>
</div>
<div class="stat-item">
<div class="stat-icon">
<fa-icon [icon]="faEye"></fa-icon>
</div>
<span>{{project.watchers}}</span>
</div>
</div>
</div>
<div class="card container">
<div class="card-content">
<div class="card-content-h">
<h2 class="card-title">
<a [href]="project.link">{{project.name}}</a>
</h2>
<p class="card-text">{{project.description}}</p>
</div>
<div class="card-content-f row">
@if (hasLanguage) {
<div class="card-languages col-md-9" id="language-chart">
@if (chartOptions !== undefined) {
<apx-chart
[series]="chartOptions.series"
[colors]="chartOptions.colors"
[chart]="chartOptions.chart"
[labels]="chartOptions.labels"
[responsive]="chartOptions.responsive"
[plotOptions]="chartOptions.plotOptions"
[dataLabels]="chartOptions.dataLabels">
</apx-chart>
}
</div>
}
<div class="card-stats" [ngClass]="hasLanguage ? 'col-md-3' : 'stats-inline'">
@if (hasLicense) {
<div class="stat-item">
<div class="stat-icon">
<fa-icon [icon]="faLicense"></fa-icon>
</div>
<span>{{project.license}}</span>
</div>
}
<div class="stat-item">
<div class="stat-icon">
<fa-icon [icon]="faStars"></fa-icon>
</div>
<span>{{project.stars}}</span>
</div>
<div class="stat-item">
<div class="stat-icon">
<fa-icon [icon]="faCodeFork"></fa-icon>
</div>
<span>{{project.forks}}</span>
</div>
<div class="stat-item">
<div class="stat-icon">
<fa-icon [icon]="faEye"></fa-icon>
</div>
<span>{{project.watchers}}</span>
</div>
</div>
</div>
</div>
</div>

View File

@@ -24,7 +24,8 @@ export type ChartOptions = {
@Component({
selector: 'app-project-card',
templateUrl: './project-card.component.html',
styleUrls: ['./project-card.component.css']
styleUrls: ['./project-card.component.css'],
standalone: false
})
export class ProjectCardComponent implements OnInit {
@Input() inverted: boolean = false;

View File

@@ -1,6 +1,8 @@
<div class="container">
<div *ngFor="let p of projects; index as i;trackBy: identifyProject">
<app-project-card [project]="p" [inverted]="i % 2 !== 0">
</app-project-card>
@for (p of projects; track identifyProject(i, p); let i = $index) {
<div>
<app-project-card [project]="p" [inverted]="i % 2 !== 0">
</app-project-card>
</div>
}
</div>

View File

@@ -5,7 +5,8 @@ import {Project} from "../shared/model/project/project.model";
@Component({
selector: 'app-projects',
templateUrl: './projects.component.html',
styleUrls: ['./projects.component.css']
styleUrls: ['./projects.component.css'],
standalone: false
})
export class ProjectsComponent implements OnInit {
projects!: Project[];

View File

@@ -7,36 +7,25 @@ import {Component, EventEmitter, Input, Output,} from '@angular/core';
styleUrls: ['./popup.component.css'],
animations: [
trigger('popupState', [
state(
'hide',
style({
opacity: '0',
zIndex: 2
}),
),
state(
'show',
style({
opacity: '1',
zIndex: 2
}),
),
transition(
'* => show',
group([
query('@*', animateChild(), { optional: true }),
animate('250ms ease-in'),
]),
),
transition(
'show => hide',
group([
query('@*', animateChild(), { optional: true }),
animate('250ms ease-out'),
]),
),
state('hide', style({
opacity: '0',
zIndex: 2
})),
state('show', style({
opacity: '1',
zIndex: 2
})),
transition('* => show', group([
query('@*', animateChild(), { optional: true }),
animate('250ms ease-in'),
])),
transition('show => hide', group([
query('@*', animateChild(), { optional: true }),
animate('250ms ease-out'),
])),
]),
],
standalone: false
})
export class PopupComponent {
@Input()

View File

@@ -7,36 +7,29 @@ import {Component, Input} from '@angular/core';
styleUrls: ['./slider-item.component.css'],
animations: [
trigger('animateSliderItem', [
state(
'hide',
style({
opacity: '0',
transform: 'translateX(150px)',
}),
{
params: {
fadeInTime: 600,
fadeOutTime: 600,
},
state('hide', style({
opacity: '0',
transform: 'translateX(150px)',
}), {
params: {
fadeInTime: 600,
fadeOutTime: 600,
},
),
state(
'show',
style({
opacity: '1',
transform: 'translateX(0px)',
}),
{
params: {
fadeOutTime: 600,
fadeInTime: 600,
},
}),
state('show', style({
opacity: '1',
transform: 'translateX(0px)',
}), {
params: {
fadeOutTime: 600,
fadeInTime: 600,
},
),
}),
transition('hide => show', animate(`{{ fadeInTime }}s ease-in`)),
transition('show => hide', animate(`{{ fadeOutTime }}s ease-out`)),
]),
],
standalone: false
})
export class SliderItemComponent {
@Input()

View File

@@ -1,9 +1,10 @@
import {DOCUMENT} from '@angular/common';
import {AfterViewInit, Directive, ElementRef, EventEmitter, Inject, Input, OnDestroy, Output,} from '@angular/core';
import {AfterViewInit, Directive, ElementRef, EventEmitter, Inject, Input, OnDestroy, Output, DOCUMENT} from '@angular/core';
import {filter, fromEvent, Subscription,} from 'rxjs';
@Directive({
selector: '[appClickedOutside]',
standalone: false
})
export class ClickedOutsideDirective implements AfterViewInit, OnDestroy {
@Input()