mirror of
https://github.com/adam-benyekkou/my_portfolio.git
synced 2026-01-15 20:20:09 +00:00
Added routing for nav links and SPA mounting of components using Angular router
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
.route-container {
|
||||
min-height: 100vh;
|
||||
background: inherit;
|
||||
}
|
||||
|
||||
@@ -6,8 +6,14 @@
|
||||
<title>{{title}}</title>
|
||||
</head>
|
||||
<body>
|
||||
<app-main-layout/>
|
||||
<app-header />
|
||||
<main class="bg-nier-bg checkered-background">
|
||||
<div class="route-container">
|
||||
<router-outlet />
|
||||
</div>
|
||||
</main>
|
||||
<app-footer />
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<router-outlet />
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { RouterOutlet } from '@angular/router';
|
||||
import {MainLayoutComponent} from './layout/components/main-layout/main-layout.component';
|
||||
import { FooterComponent } from './layout/components/footer/footer.component';
|
||||
import { HeaderComponent } from './layout/components/header/header.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
imports: [RouterOutlet, MainLayoutComponent],
|
||||
imports: [RouterOutlet, FooterComponent, HeaderComponent],
|
||||
templateUrl: './app.component.html',
|
||||
styleUrl: './app.component.css'
|
||||
styleUrl: './app.component.css',
|
||||
})
|
||||
export class AppComponent {
|
||||
title = 'Adam Benyekkou Portfolio';
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
import { Routes } from '@angular/router';
|
||||
import { AboutComponent } from './layout/components/about/about.component';
|
||||
import { ContactComponent } from './layout/components/contact/contact.component';
|
||||
import { HeroComponent } from './layout/components/hero/hero.component';
|
||||
import { ProjectsListComponent } from './features/project-display/components/projects-list/projects-list.component';
|
||||
import { ExperienceComponent } from './layout/components/experience/experience.component';
|
||||
|
||||
export const routes: Routes = [];
|
||||
export const routes: Routes = [
|
||||
{ path: '', component: HeroComponent },
|
||||
{ path: 'about', component: AboutComponent },
|
||||
{ path: 'contact', component: ContactComponent },
|
||||
{ path: 'projects', component: ProjectsListComponent },
|
||||
{ path: 'experience', component: ExperienceComponent },
|
||||
];
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<section class="border-b-1 h-100 flex items-top justify-top checkered-bg p-6 relative">
|
||||
<section class="border-b-1 h-screen flex items-top justify-top checkered-bg p-6 relative">
|
||||
<app-section-title title="NEURAL PROFILE" />
|
||||
</section>
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<section class="border-b-1 h-100 flex items-top justify-top checkered-bg p-6 relative">
|
||||
<section class="border-b-1 h-screen flex items-top justify-top checkered-bg p-6 relative">
|
||||
<app-section-title title="TRANSMISSION LINKS" />
|
||||
</section>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<section class="border-b-1 h-screen flex items-top justify-top checkered-bg p-6 relative">
|
||||
<app-section-title title=" OPERATIVE HISTORY" />
|
||||
</section>
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Component } from '@angular/core';
|
||||
import {SectionTitleComponent} from '../../shared/ui/section-title/section-title.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-experience-display',
|
||||
imports: [SectionTitleComponent],
|
||||
templateUrl: './experience-display.component.html',
|
||||
styleUrl: './experience-display.component.css',
|
||||
})
|
||||
export class ExperienceDisplayComponent {}
|
||||
@@ -30,15 +30,15 @@
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<!-- Rest of your layout remains unchanged -->
|
||||
<article class="border-r-0 md:border-r-1 grid grid-rows-2 w-full h-full my-4 md:my-0">
|
||||
|
||||
<article class="border-r-0 md:border-r-1 grid grid-rows-2 w-full h-full my-4 md:my-0 mb-6 md:mb-0">
|
||||
<div class="border-b-1 w-full h-full">
|
||||
<p class="flex items-center justify-center h-full w-full text-sm sm:text-lg md:text-lg lg:text-lg xl:text-lg text-center px-2">
|
||||
Based in unknown french-hideout
|
||||
</p>
|
||||
</div>
|
||||
<div class="w-full h-full">
|
||||
<p class="flex items-center justify-center h-full w-full text-sm sm:text-lg md:text-lg lg:text-lg xl:text-lg text-center px-2 flex-wrap">
|
||||
<p class="flex items-center justify-center h-full w-full text-sm sm:text-lg md:text-lg lg:text-lg xl:text-lg text-center px-2 flex-wrap ">
|
||||
<span class="whitespace-nowrap mb-1 sm:mb-0">Tech operative ready /</span>
|
||||
<a href="https://www.linkedin.com/in/adambnk/" target="_blank" class="text-nier-mid ml-1 sm:ml-2 whitespace-nowrap" data-label="Deploy my skills">
|
||||
<span class="relative z-10">Deploy my skills</span>
|
||||
@@ -48,7 +48,7 @@
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="flex items-center justify-center w-full h-full">
|
||||
<article class="flex items-center justify-center w-full h-full mt-2">
|
||||
<a href="mailto:your.email@example.com?subject=Initialize%20Connection%20-&body=System%20message%3A%20Your%20connection%20request%20has%20been%20received.%20Please%20provide%20your%20message%20to%20establish%20communication.">
|
||||
<div class="icon-email"></div>
|
||||
</a>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<!-- header-switch-theme-button.component.html -->
|
||||
<!-- header-switch-theme-nav-button.html -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="w-10 h-10 svg-icon">
|
||||
<!-- Outer ring -->
|
||||
<circle cx="12" cy="12" r="11" fill="none" stroke="currentColor" stroke-width="0.8" />
|
||||
|
||||
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
@@ -1,4 +1,4 @@
|
||||
// header-switch-theme-button.component.ts
|
||||
// header-switch-theme-nav-button.component.ts
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<div class="nier-logo-container">
|
||||
<div class="nier-logo-container"><a [routerLink]="routerLink()">
|
||||
<p class="font-terminal-nier text-8xl nier-logo nier-dark" data-text="AB">AB</p>
|
||||
<div class="nier-scan-line nier-dark"></div>
|
||||
<div class="nier-blocks nier-dark"></div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { HeaderLogoComponent } from './header-logo.component';
|
||||
|
||||
describe('HeaderLogoComponent', () => {
|
||||
let component: HeaderLogoComponent;
|
||||
let fixture: ComponentFixture<HeaderLogoComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [HeaderLogoComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(HeaderLogoComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -1,9 +1,12 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, input } from '@angular/core';
|
||||
import { RouterLink } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-header-logo',
|
||||
imports: [],
|
||||
imports: [RouterLink],
|
||||
templateUrl: './header-logo.component.html',
|
||||
styleUrl: './header-logo.component.css',
|
||||
})
|
||||
export class HeaderLogoComponent {}
|
||||
export class HeaderLogoComponent {
|
||||
routerLink = input<string>('');
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<nav class="flex items-center justify-center">
|
||||
<section class="grid grid-cols-2 gap-x-4 gap-y-5 my-4">
|
||||
<app-button class="flex items-center h-10" label="Neural Profile" (click)="onClick()"/>
|
||||
<app-button class="flex items-center h-10" label="Execute//Directory" (click)="onClick()"/>
|
||||
<app-button class="flex items-center h-10" label="Operative History" (click)="onClick()"/>
|
||||
<app-button class="flex items-center h-10" label="Transmission Link" (click)="onClick()"/>
|
||||
<app-nav-button class="flex items-center h-10" label="Neural Profile" (click)="onClick()" routerLink="about"/>
|
||||
<app-nav-button class="flex items-center h-10" label="Execute//Directory" (click)="onClick()" routerLink="projects"/>
|
||||
<app-nav-button class="flex items-center h-10" label="Operative History" (click)="onClick()" routerLink="experience"/>
|
||||
<app-nav-button class="flex items-center h-10" label="Transmission Link" (click)="onClick()" routerLink="contact"/>
|
||||
</section>
|
||||
</nav>
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
import { ButtonComponent } from '../../../shared/ui/button/button.component';
|
||||
import { NavButtonComponent } from '../../../shared/ui/button/nav-button.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-header-nav-links',
|
||||
imports: [ButtonComponent],
|
||||
imports: [NavButtonComponent],
|
||||
templateUrl: './header-nav-links.component.html',
|
||||
styleUrl: './header-nav-links.component.css',
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<section class="border-b-1 h-100 flex items-top justify-top checkered-bg p-6 relative">
|
||||
<section class="border-b-1 h-screen flex items-top justify-top checkered-bg p-6 relative">
|
||||
<app-section-title title="EXECUTE // DIRECTORY" />
|
||||
</section>
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
|
||||
<p class="font-terminal-retro">SIDEBAR</p>
|
||||
@@ -1,11 +0,0 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-sidebar-display',
|
||||
imports: [],
|
||||
templateUrl: './sidebar-display.component.html',
|
||||
styleUrl: './sidebar-display.component.css'
|
||||
})
|
||||
export class SidebarDisplayComponent {
|
||||
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ContactComponent } from './contact.component';
|
||||
|
||||
describe('ContactComponent', () => {
|
||||
let component: ContactComponent;
|
||||
let fixture: ComponentFixture<ContactComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [ContactComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ContactComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
<app-experience-display />
|
||||
10
src/app/layout/components/experience/experience.component.ts
Normal file
10
src/app/layout/components/experience/experience.component.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { ExperienceDisplayComponent } from '../../../features/experience-display/experience-display.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-experience',
|
||||
imports: [ExperienceDisplayComponent],
|
||||
templateUrl: './experience.component.html',
|
||||
styleUrl: './experience.component.css',
|
||||
})
|
||||
export class ExperienceComponent {}
|
||||
@@ -0,0 +1,15 @@
|
||||
/* QR Code size control for mobile */
|
||||
@media (max-width: 767px) {
|
||||
/* Target QR code image inside contact links - assumes proper structure */
|
||||
app-header-contact-links img {
|
||||
max-width: 80px;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
/* Additional styles for better typography and spacing */
|
||||
@media (min-width: 1024px) {
|
||||
app-header-text-animate-section {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
class="md:hidden grid grid-cols-1 bg-nier-bg font-terminal text-nier-dark border border-nier-accent overflow-hidden"
|
||||
>
|
||||
<div class="flex justify-center py-3 border-b border-nier-accent/30">
|
||||
<app-header-logo />
|
||||
<app-header-logo routerLink="" />
|
||||
</div>
|
||||
<div class="py-3 border-b border-nier-accent/30 min-h-[100px]">
|
||||
<app-header-text-animate-section
|
||||
@@ -137,20 +137,3 @@
|
||||
</section>
|
||||
</header>
|
||||
|
||||
<style>
|
||||
/* QR Code size control for mobile */
|
||||
@media (max-width: 767px) {
|
||||
/* Target QR code image inside contact links - assumes proper structure */
|
||||
app-header-contact-links img {
|
||||
max-width: 80px;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
/* Additional styles for better typography and spacing */
|
||||
@media (min-width: 1024px) {
|
||||
app-header-text-animate-section {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
<app-header />
|
||||
<main class="bg-nier-bg checkered-background">
|
||||
<app-hero />
|
||||
<app-projects />
|
||||
<app-about />
|
||||
<app-contact />
|
||||
</main>
|
||||
<app-footer />
|
||||
<app-sidebar />
|
||||
@@ -1,24 +0,0 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { FooterComponent } from '../footer/footer.component';
|
||||
import { SidebarComponent } from '../sidebar/sidebar.component';
|
||||
import { HeaderComponent } from '../header/header.component';
|
||||
import { HeroComponent } from '../hero/hero.component';
|
||||
import { AboutComponent } from '../about/about.component';
|
||||
import { ProjectsComponent } from '../projects/projects.component';
|
||||
import { ContactComponent } from '../contact/contact.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-main-layout',
|
||||
imports: [
|
||||
FooterComponent,
|
||||
SidebarComponent,
|
||||
HeaderComponent,
|
||||
HeroComponent,
|
||||
AboutComponent,
|
||||
ProjectsComponent,
|
||||
ContactComponent,
|
||||
],
|
||||
templateUrl: './main-layout.component.html',
|
||||
styleUrl: './main-layout.component.css',
|
||||
})
|
||||
export class MainLayoutComponent {}
|
||||
@@ -1 +0,0 @@
|
||||
<app-sidebar-display />
|
||||
@@ -1,10 +0,0 @@
|
||||
import { Component } from '@angular/core';
|
||||
import {SidebarDisplayComponent} from '../../../features/sidebar-display/sidebar-display.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-sidebar',
|
||||
imports: [SidebarDisplayComponent],
|
||||
templateUrl: './sidebar.component.html',
|
||||
styleUrl: './sidebar.component.css',
|
||||
})
|
||||
export class SidebarComponent {}
|
||||
19
src/app/shared/ui/button/nav-button.component.ts
Normal file
19
src/app/shared/ui/button/nav-button.component.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { Component, input, output } from '@angular/core';
|
||||
import { RouterLink, RouterLinkActive } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-nav-button',
|
||||
standalone: true, // Move this to the top
|
||||
imports: [RouterLink, RouterLinkActive],
|
||||
templateUrl: './nav-button.html',
|
||||
styleUrl: './nav-button.css',
|
||||
})
|
||||
export class NavButtonComponent {
|
||||
label = input<string>('label');
|
||||
routerLink = input<string>('');
|
||||
onClick = output<void>();
|
||||
|
||||
handleClick() {
|
||||
this.onClick.emit();
|
||||
}
|
||||
}
|
||||
10
src/app/shared/ui/button/nav-button.html
Normal file
10
src/app/shared/ui/button/nav-button.html
Normal file
@@ -0,0 +1,10 @@
|
||||
<a
|
||||
[routerLink]="routerLink()"
|
||||
routerLinkActive="active"
|
||||
class="w-full sm:w-40 md:w-48 text-left font-noto-jp text-sm sm:text-sm md:text-base uppercase transition-all duration-300 rounded-none p-3 sm:p-1.5 button-custom"
|
||||
(click)="handleClick()"
|
||||
[attr.data-label]="label()"
|
||||
>
|
||||
<span class="relative z-10">{{ label() }}</span>
|
||||
<span class="scan-line"></span>
|
||||
</a>
|
||||
149
src/app/shared/ui/nav-button/button.component.css
Normal file
149
src/app/shared/ui/nav-button/button.component.css
Normal file
@@ -0,0 +1,149 @@
|
||||
.button-custom {
|
||||
cursor: pointer;
|
||||
background-color: var(--color-nier-mid);
|
||||
color: var(--color-nier-dark);
|
||||
position: relative;
|
||||
border: 1px solid transparent;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
overflow: hidden;
|
||||
transition: color 0.2s ease, background-color 0.2s ease, border-color 0.2s ease;
|
||||
}
|
||||
|
||||
.button-custom::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 3px; /* Space from top border */
|
||||
bottom: 3px; /* Space from bottom border */
|
||||
width: 0;
|
||||
height: auto; /* This makes it respect top and bottom values */
|
||||
background-color: var(--color-nier-dark);
|
||||
transition: width 0.2s ease;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
/* Normal hover/focus state - keep your original behavior */
|
||||
.button-custom:hover,
|
||||
.button-custom:focus {
|
||||
color: var(--color-nier-text-light);
|
||||
background-color: transparent;
|
||||
border-color: var(--color-nier-dark);
|
||||
}
|
||||
|
||||
.button-custom:hover::before,
|
||||
.button-custom:focus::before {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Add NieR-style glitch effects on hover */
|
||||
.button-custom::after {
|
||||
content: attr(data-label); /* Use the button's text from data-label attribute */
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--color-nier-text-light);
|
||||
opacity: 0;
|
||||
z-index: 2;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.button-custom:hover::after {
|
||||
animation: nier-button-glitch 0.6s ease forwards;
|
||||
}
|
||||
|
||||
/* Add subtle scan line on hover */
|
||||
.button-custom .scan-line {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background-color: var(--color-nier-text-light);
|
||||
opacity: 0;
|
||||
z-index: 3;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.button-custom:hover .scan-line {
|
||||
animation: nier-button-scan 0.3s ease forwards;
|
||||
}
|
||||
|
||||
/* The glitch effect animation */
|
||||
@keyframes nier-button-glitch {
|
||||
0%, 100% {
|
||||
opacity: 0;
|
||||
transform: translateX(0);
|
||||
clip-path: inset(0 0 0 0);
|
||||
}
|
||||
10% {
|
||||
opacity: 0.2;
|
||||
transform: translateX(-2px);
|
||||
clip-path: inset(10% 0 80% 0);
|
||||
}
|
||||
12% {
|
||||
opacity: 0;
|
||||
transform: translateX(0);
|
||||
}
|
||||
20% {
|
||||
opacity: 0.2;
|
||||
transform: translateX(2px);
|
||||
clip-path: inset(30% 0 50% 0);
|
||||
}
|
||||
22% {
|
||||
opacity: 0;
|
||||
transform: translateX(0);
|
||||
}
|
||||
30% {
|
||||
opacity: 0.1;
|
||||
transform: translateX(-1px);
|
||||
clip-path: inset(50% 0 20% 0);
|
||||
}
|
||||
32% {
|
||||
opacity: 0;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* The scan line animation */
|
||||
@keyframes nier-button-scan {
|
||||
0% {
|
||||
opacity: 0.5;
|
||||
left: -100%;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
left: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add a brief effect on button click */
|
||||
.button-custom:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.button-custom:active::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
opacity: 0;
|
||||
animation: nier-button-flash 0.2s ease;
|
||||
}
|
||||
|
||||
@keyframes nier-button-flash {
|
||||
0% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user