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

@@ -11,7 +11,7 @@
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:application",
"builder": "@angular/build:application",
"options": {
"outputPath": {
"base": "dist/frontend-hideyoshi.com"
@@ -78,7 +78,7 @@
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"builder": "@angular/build:dev-server",
"configurations": {
"production": {
"buildTarget": "frontend-hideyoshi.com:build:production"
@@ -90,13 +90,13 @@
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"builder": "@angular/build:extract-i18n",
"options": {
"buildTarget": "frontend-hideyoshi.com:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"builder": "@angular/build:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
@@ -130,6 +130,30 @@
},
"@angular-eslint/schematics:library": {
"setParserOptionsProject": true
},
"@schematics/angular:component": {
"type": "component"
},
"@schematics/angular:directive": {
"type": "directive"
},
"@schematics/angular:service": {
"type": "service"
},
"@schematics/angular:guard": {
"typeSeparator": "."
},
"@schematics/angular:interceptor": {
"typeSeparator": "."
},
"@schematics/angular:module": {
"typeSeparator": "."
},
"@schematics/angular:pipe": {
"typeSeparator": "."
},
"@schematics/angular:resolver": {
"typeSeparator": "."
}
}
}

13085
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -16,17 +16,17 @@
},
"private": true,
"dependencies": {
"@angular/animations": "^18.2.14",
"@angular/cdk": "^16.2.1",
"@angular/common": "^18.2.14",
"@angular/compiler": "^18.2.14",
"@angular/core": "^18.2.14",
"@angular/forms": "^18.2.14",
"@angular/material": "^16.2.1",
"@angular/platform-browser": "^18.2.14",
"@angular/platform-browser-dynamic": "^18.2.14",
"@angular/router": "^18.2.14",
"@angular/service-worker": "^18.2.14",
"@angular/animations": "^20.3.9",
"@angular/cdk": "^19.2.19",
"@angular/common": "^20.3.9",
"@angular/compiler": "^20.3.9",
"@angular/core": "^20.3.9",
"@angular/forms": "^20.3.9",
"@angular/material": "^19.2.19",
"@angular/platform-browser": "^20.3.9",
"@angular/platform-browser-dynamic": "^20.3.9",
"@angular/router": "^20.3.9",
"@angular/service-worker": "^20.3.9",
"@fortawesome/angular-fontawesome": "^0.13.0",
"@fortawesome/fontawesome-svg-core": "^6.1.1",
"@fortawesome/free-brands-svg-icons": "^6.1.1",
@@ -38,6 +38,7 @@
"bootstrap": "^4.6.2",
"cookieconsent": "^3.1.1",
"cors": "^2.8.5",
"dotenv": "^17.2.3",
"envsub": "^4.1.0",
"express": "^4.18.1",
"jquery": "^3.6.0",
@@ -49,19 +50,19 @@
"rxjs": "~7.5.0",
"ts-interface-checker": "^1.0.2",
"tslib": "^2.3.0",
"zone.js": "~0.14.10"
"zone.js": "~0.15.1"
},
"devDependencies": {
"@angular-devkit/build-angular": "^18.2.21",
"@angular-eslint/builder": "^16.1.1",
"@angular-eslint/eslint-plugin": "16.1.1",
"@angular-eslint/eslint-plugin-template": "16.1.1",
"@angular-eslint/builder": "^19.8.1",
"@angular-eslint/eslint-plugin": "^19.8.1",
"@angular-eslint/eslint-plugin-template": "^19.8.1",
"@angular-eslint/schematics": "^20.5.0",
"@angular-eslint/template-parser": "16.1.1",
"@angular/cli": "^18.2.21",
"@angular/compiler-cli": "^18.2.14",
"@angular-eslint/template-parser": "^19.8.1",
"@angular/build": "^20.3.8",
"@angular/cli": "^20.3.8",
"@angular/compiler-cli": "^20.3.9",
"@types/jasmine": "~4.0.0",
"@types/node": "^18.11.19",
"@types/node": "^22.18.13",
"@typescript-eslint/eslint-plugin": "^5.59.2",
"@typescript-eslint/parser": "^5.59.2",
"eslint": "^8.39.0",
@@ -76,6 +77,6 @@
"prettier": "^3.0.3",
"prettier-eslint": "^16.1.1",
"ts-interface-builder": "^0.3.3",
"typescript": "~5.4.5"
"typescript": "~5.8.3"
}
}

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

@@ -9,28 +9,36 @@
(@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"
@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>
<ul class="user-management" *ngIf="this.user">
<li *ngFor="let option of userOptions"
}
@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({
state('hide', style({
opacity: '0',
}),
),
state(
'show',
style({
})),
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

@@ -2,7 +2,7 @@
[state]="state"
(stateChange)="onStateChange($event)"
[ignoreClickOutside]="ignoreClickOutside"
>
>
<div
class="container m-0 overflow-hidden"
[@resizeContainerForErrorMessage]="hideErrorMessage()"
@@ -65,16 +65,18 @@
[disabled]="isCookieBlocked"
(click)="onGoogleLogin()"
>
@if (!isCookieBlocked) {
<mat-icon
*ngIf="!isCookieBlocked"
style="width: 50px; height: 30px"
svgIcon="google-logo"
></mat-icon>
}
@if (isCookieBlocked) {
<mat-icon
*ngIf="isCookieBlocked"
style="width: 50px; height: 30px"
svgIcon="google-disabled-logo"
></mat-icon>
}
Login With Google
</button>
<button

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({
state('hide', style({
height: '100px',
width: '320px',
}),
),
transition(
'show => hide',
group([
})),
transition('show => hide', group([
query('@*', animateChild(), { optional: true }),
animate('1s ease'),
]),
),
])),
]),
trigger('showErrorMessage', [
state(
'show',
style({
state('show', style({
opacity: 1,
height: '100px',
width: '320px',
}),
),
state(
'hide',
style({
})),
state('hide', style({
opacity: 0,
height: '0px',
width: '0px',
}),
),
})),
transition('* => show', animate('500ms ease-in')),
]),
trigger('hideAuthContainer', [
state(
'hide',
style({
state('hide', style({
opacity: 0,
}),
),
transition(
'show => hide',
group([
})),
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({
state('hide', style({
height: '100px',
width: '320px',
}),
),
transition(
'show => hide',
group([
})),
transition('show => hide', group([
query('@*', animateChild(), { optional: true }),
animate('1s ease'),
]),
),
])),
]),
trigger('showErrorMessage', [
state(
'show',
style({
state('show', style({
opacity: 1,
height: '100px',
width: '320px',
}),
),
state(
'hide',
style({
})),
state('hide', style({
opacity: 0,
height: '0px',
width: '0px',
}),
),
})),
transition('* => show', animate('500ms ease-in')),
]),
trigger('hideAuthContainer', [
state(
'hide',
style({
state('hide', style({
opacity: 0,
}),
),
transition(
'show => hide',
group([
})),
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({
state('hide', style({
height: '100px',
width: '320px',
}),
),
transition(
'show => hide',
group([
})),
transition('show => hide', group([
query('@*', animateChild(), { optional: true }),
animate('1s ease'),
]),
),
])),
]),
trigger('showErrorMessage', [
state(
'show',
style({
state('show', style({
opacity: 1,
height: '100px',
width: '320px',
}),
),
state(
'hide',
style({
})),
state('hide', style({
opacity: 0,
height: '0px',
width: '0px',
}),
),
})),
transition('* => show', animate('500ms ease-in')),
]),
trigger('hideAuthContainer', [
state(
'hide',
style({
state('hide', style({
opacity: 0,
}),
),
transition(
'show => hide',
group([
})),
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({
state('hide', style({
transform: 'translateX(100%)',
}),
),
state(
'show',
style({
})),
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>
@for (page of pages; track page; let i = $index) {
<li
*ngFor="let page of pages; let i = index"
[@animateSliderItem]="{
value: itemStatus,
params: {
@@ -15,8 +15,9 @@
{{ page.name }}
</a>
</li>
}
</ul>
</div>
</div>
</div>
<div class="profile-container">
@@ -32,13 +33,14 @@
#profile
>
<div class="profile-btn" (click)="onProfileButtonClicked()">
@if (!loggedUser || !loggedUser.profilePictureUrl) {
<fa-icon
*ngIf="!loggedUser || !loggedUser.profilePictureUrl"
class="fas fa-user"
[icon]="userIcon"
></fa-icon>
}
@if (!!loggedUser && !!loggedUser.profilePictureUrl) {
<img
*ngIf="!!loggedUser && !!loggedUser.profilePictureUrl"
class="profile-picture"
[ngSrc]="loggedUser.profilePictureUrl"
width="50"
@@ -46,6 +48,7 @@
alt="Profile Picture"
priority
/>
</div>
}
</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>
@for (
options of user ? userOptions : userlessOptions; track
options; let i = $index) {
<li
*ngFor="
let options of user ? userOptions : userlessOptions;
let i = index
"
[@animateSliderItem]="{
value: itemStatus,
params: {
@@ -18,6 +17,7 @@
{{ options.name }}
</a>
</li>
}
</ul>
</div>
</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

@@ -7,9 +7,11 @@
</div>
<div class="nav-links">
<ul class="link-container">
<li *ngFor="let page of pages">
@for (page of pages; track page) {
<li>
<a [routerLink]="page.route">{{ page.name }}</a>
</li>
}
</ul>
</div>
@@ -19,13 +21,14 @@
(click)="toogleProfileDropdown()"
#profileBtn
>
@if (!loggedUser || !loggedUser.profilePictureUrl) {
<fa-icon
*ngIf="!loggedUser || !loggedUser.profilePictureUrl"
class="fas fa-user"
[icon]="userIcon"
></fa-icon>
}
@if (!!loggedUser && !!loggedUser.profilePictureUrl) {
<img
*ngIf="!!loggedUser && !!loggedUser.profilePictureUrl"
class="profile-picture"
[ngSrc]="loggedUser.profilePictureUrl"
width="50"
@@ -33,6 +36,7 @@
alt="Profile Picture"
priority
/>
}
</div>
<app-header-dropdown

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>
}
</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,4 +1,4 @@
<div class="card container">
<div class="card container">
<div class="card-content">
<div class="card-content-h">
<h2 class="card-title">
@@ -7,8 +7,10 @@
<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"
@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"
@@ -17,14 +19,18 @@
[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">
@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>
@@ -46,4 +52,4 @@
</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">
@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({
state('hide', style({
opacity: '0',
zIndex: 2
}),
),
state(
'show',
style({
})),
state('show', style({
opacity: '1',
zIndex: 2
}),
),
transition(
'* => show',
group([
})),
transition('* => show', group([
query('@*', animateChild(), { optional: true }),
animate('250ms ease-in'),
]),
),
transition(
'show => hide',
group([
])),
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({
state('hide', style({
opacity: '0',
transform: 'translateX(150px)',
}),
{
}), {
params: {
fadeInTime: 600,
fadeOutTime: 600,
},
},
),
state(
'show',
style({
}),
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()

View File

@@ -14,11 +14,14 @@
"sourceMap": true,
"declaration": false,
"experimentalDecorators": true,
"moduleResolution": "node",
"moduleResolution": "bundler",
"importHelpers": true,
"target": "ES2022",
"module": "es2020",
"lib": ["es2020", "dom"],
"lib": [
"es2020",
"dom"
],
"useDefineForClassFields": false
},
"angularCompilerOptions": {