mirror of
https://github.com/adam-benyekkou/my_portfolio.git
synced 2026-01-15 20:20:09 +00:00
Refactoring personal info column and preparing other history components for refactoring
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
import { Component, signal } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-column-education-info',
|
||||
imports: [],
|
||||
templateUrl: './column-education-info.component.html',
|
||||
styleUrl: './column-education-info.component.css',
|
||||
})
|
||||
export class ColumnEducationInfoComponent {}
|
||||
@@ -0,0 +1,398 @@
|
||||
/* Personal Column Container */
|
||||
.personal-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--card-gap);
|
||||
min-height: 0;
|
||||
padding-right: 0.5rem;
|
||||
|
||||
/* Hide overflow initially */
|
||||
overflow: hidden;
|
||||
animation: enableColumnScrollAfterAnimation 0s ease-out 3s forwards;
|
||||
|
||||
/* Initial state for animation */
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
animation: fadeInUp var(--animation-duration) ease-out calc(var(--animation-delay-base) * 5) forwards, enableColumnScrollAfterAnimation 0s ease-out 3s forwards;
|
||||
}
|
||||
|
||||
/* Column Title */
|
||||
.column-title {
|
||||
font-family: var(--font-terminal);
|
||||
font-size: 1rem;
|
||||
color: var(--color-nier-accent);
|
||||
letter-spacing: 0.1em;
|
||||
text-align: center;
|
||||
margin-bottom: var(--card-gap);
|
||||
padding: 0.5rem;
|
||||
border-bottom: 1px solid var(--color-nier-border);
|
||||
flex-shrink: 0;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background: var(--color-nier-bg);
|
||||
z-index: 10;
|
||||
|
||||
/* Title animation */
|
||||
opacity: 0;
|
||||
transform: translateX(-10px);
|
||||
animation: slideInLeft 0.4s ease-out calc(var(--animation-delay-base) * 7) forwards;
|
||||
}
|
||||
|
||||
/* Info Card Styles */
|
||||
.info-card {
|
||||
border: 2px solid var(--color-nier-border);
|
||||
background: var(--color-nier-bg);
|
||||
position: relative;
|
||||
transition: var(--transition-smooth);
|
||||
margin-bottom: var(--card-gap);
|
||||
flex-shrink: 0;
|
||||
border-radius: var(--border-radius);
|
||||
|
||||
/* Card initial state */
|
||||
opacity: 0;
|
||||
transform: translateY(30px) scale(0.95);
|
||||
animation: cardFadeIn var(--animation-duration) ease-out calc(var(--animation-delay-base) * 10) forwards;
|
||||
}
|
||||
|
||||
.info-card:hover {
|
||||
transform: translateY(-1px) scale(1.01);
|
||||
box-shadow: var(--shadow-subtle);
|
||||
}
|
||||
|
||||
.info-header {
|
||||
background: linear-gradient(135deg, var(--color-nier-dark) 0%, var(--color-nier-highlight) 100%);
|
||||
color: var(--color-nier-text-light);
|
||||
padding: 0.5rem;
|
||||
font-family: var(--font-terminal);
|
||||
font-size: 0.8rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
border-bottom: 2px solid var(--color-nier-border);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.info-content {
|
||||
padding: var(--card-padding);
|
||||
background: var(--color-nier-bg);
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* Personal Info Grid */
|
||||
.trait-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.trait-item {
|
||||
background: linear-gradient(135deg, var(--color-nier-accent) 0%, var(--color-nier-highlight) 100%);
|
||||
color: var(--color-nier-text-light);
|
||||
padding: 0.375rem 0.25rem;
|
||||
font-size: 0.7rem;
|
||||
font-family: var(--font-terminal);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
border-radius: 3px;
|
||||
white-space: nowrap;
|
||||
transition: var(--transition-smooth);
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
text-align: center;
|
||||
|
||||
/* Initial state for staggered animation */
|
||||
opacity: 0;
|
||||
transform: scale(0.8) translateY(10px);
|
||||
animation: techItemFadeIn 0.4s ease-out forwards;
|
||||
animation-delay: calc(var(--animation-delay-base) * 19);
|
||||
}
|
||||
|
||||
.trait-item:hover {
|
||||
transform: translateY(-2px) scale(1.05);
|
||||
box-shadow: 0 6px 12px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.interest-item {
|
||||
margin-bottom: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.25rem;
|
||||
border-radius: 2px;
|
||||
transition: background-color 0.3s ease, transform 0.3s ease;
|
||||
|
||||
/* Interest item animation */
|
||||
opacity: 0;
|
||||
transform: translateX(-10px);
|
||||
animation: slideInLeft 0.3s ease-out calc(var(--animation-delay-base) * 20) forwards;
|
||||
}
|
||||
|
||||
.interest-item:hover {
|
||||
background-color: rgba(41, 41, 37, 0.05);
|
||||
transform: translateX(2px);
|
||||
}
|
||||
|
||||
.interest-label {
|
||||
font-family: var(--font-terminal);
|
||||
font-size: 0.7rem;
|
||||
color: var(--color-nier-text-dark);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
width: 60px;
|
||||
flex-shrink: 0;
|
||||
opacity: 0.8;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.interest-value {
|
||||
font-family: var(--font-noto-jp);
|
||||
font-size: 0.8rem;
|
||||
color: var(--color-nier-text-dark);
|
||||
}
|
||||
|
||||
/* Enhanced Philosophy Section */
|
||||
.philosophy-text {
|
||||
font-size: 0.8rem;
|
||||
line-height: 1.5;
|
||||
font-style: italic;
|
||||
margin-bottom: var(--card-gap);
|
||||
position: relative;
|
||||
padding-left: 1rem;
|
||||
|
||||
/* Philosophy text animation */
|
||||
opacity: 0;
|
||||
animation: fadeIn 0.6s ease-out calc(var(--animation-delay-base) * 22) forwards;
|
||||
}
|
||||
|
||||
.philosophy-text::before {
|
||||
content: '"';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: -0.25rem;
|
||||
font-size: 1.5rem;
|
||||
color: var(--color-nier-accent);
|
||||
opacity: 0;
|
||||
animation: fadeIn 0.4s ease-out calc(var(--animation-delay-base) * 23) forwards;
|
||||
}
|
||||
|
||||
.philosophy-note {
|
||||
font-family: var(--font-terminal);
|
||||
font-size: 0.75rem;
|
||||
color: var(--color-nier-text-dark);
|
||||
opacity: 0;
|
||||
margin-top: var(--card-gap);
|
||||
padding-top: var(--card-gap);
|
||||
border-top: 1px solid var(--color-nier-border);
|
||||
line-height: 1.4;
|
||||
animation: fadeIn 0.4s ease-out calc(var(--animation-delay-base) * 24) forwards;
|
||||
}
|
||||
|
||||
/* Keyframe animations (needed for this component) */
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes enableColumnScrollAfterAnimation {
|
||||
to {
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes cardFadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px) scale(0.95);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideInLeft {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(-10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes techItemFadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.8) translateY(10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1) translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Responsive Design - Personal Column Specific */
|
||||
@media (max-width: 480px) {
|
||||
.personal-column {
|
||||
gap: 0.5rem;
|
||||
padding-right: 0.25rem;
|
||||
|
||||
/* Ensure columns are visible on mobile */
|
||||
min-height: auto;
|
||||
|
||||
/* Simplified mobile column animations */
|
||||
animation-name: fadeInUp, enableColumnScrollAfterAnimation;
|
||||
animation-duration: 0.3s, 0s;
|
||||
animation-timing-function: ease-out, ease-out;
|
||||
animation-delay: 0.25s, 1.5s;
|
||||
animation-fill-mode: forwards, forwards;
|
||||
}
|
||||
|
||||
.column-title {
|
||||
font-size: 0.9rem;
|
||||
padding: 0.375rem;
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
/* Simplified title animation */
|
||||
animation-name: slideInLeft;
|
||||
animation-duration: 0.3s;
|
||||
animation-timing-function: ease-out;
|
||||
animation-delay: 0.3s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
.info-header {
|
||||
padding: 0.75rem 0.5rem 0.5rem 0.5rem;
|
||||
}
|
||||
|
||||
.info-content {
|
||||
padding: 0.75rem 0.5rem;
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.trait-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 0.375rem;
|
||||
}
|
||||
|
||||
.trait-item {
|
||||
padding: 0.5rem 0.375rem;
|
||||
font-size: 0.75rem;
|
||||
|
||||
/* Simplified trait animation */
|
||||
animation-name: techItemFadeIn;
|
||||
animation-duration: 0.2s;
|
||||
animation-timing-function: ease-out;
|
||||
animation-delay: 0.7s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
.interest-item {
|
||||
margin-bottom: 0.75rem;
|
||||
padding: 0.5rem 0.25rem;
|
||||
|
||||
/* Simplified interest animation */
|
||||
animation-name: slideInLeft;
|
||||
animation-duration: 0.2s;
|
||||
animation-timing-function: ease-out;
|
||||
animation-delay: 0.8s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
.interest-label {
|
||||
font-size: 0.75rem;
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
.interest-value {
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.philosophy-text {
|
||||
font-size: 0.8rem;
|
||||
padding-left: 0.75rem;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 1rem;
|
||||
|
||||
/* Simplified philosophy animation */
|
||||
animation-name: fadeIn;
|
||||
animation-duration: 0.3s;
|
||||
animation-timing-function: ease-out;
|
||||
animation-delay: 0.9s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
.philosophy-text::before {
|
||||
/* Simple fade for quote mark */
|
||||
animation-name: fadeIn;
|
||||
animation-duration: 0.2s;
|
||||
animation-timing-function: ease-out;
|
||||
animation-delay: 1s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
.philosophy-note {
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.4;
|
||||
padding-top: 1rem;
|
||||
|
||||
/* Simplified note animation */
|
||||
animation-name: fadeIn;
|
||||
animation-duration: 0.2s;
|
||||
animation-timing-function: ease-out;
|
||||
animation-delay: 1.1s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
/* Remove complex animations on very small screens */
|
||||
@media (max-width: 360px) {
|
||||
.trait-item,
|
||||
.interest-item,
|
||||
.philosophy-text,
|
||||
.philosophy-text::before,
|
||||
.philosophy-note {
|
||||
animation: none !important;
|
||||
opacity: 1 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) and (min-width: 769px) {
|
||||
.personal-column {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Reduced Motion Support - Personal Column */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.personal-column,
|
||||
.column-title,
|
||||
.info-card,
|
||||
.trait-item,
|
||||
.interest-item,
|
||||
.philosophy-text,
|
||||
.philosophy-text::before,
|
||||
.philosophy-note {
|
||||
animation: none !important;
|
||||
transition: none !important;
|
||||
opacity: 1 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<!-- Personal Info Column -->
|
||||
<div class="column personal-column">
|
||||
<h2 class="column-title">[PERSONAL_DATA]</h2>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-header">INTERESTS</div>
|
||||
<div class="info-content">
|
||||
<div class="interest-item">
|
||||
<span class="interest-label">READINGS:</span>
|
||||
<span class="interest-value"> Science Fiction, Philosophy, Fantasy</span>
|
||||
</div>
|
||||
<div class="interest-item">
|
||||
<span class="interest-label">INTERESTS:</span>
|
||||
<span class="interest-value">Digital & Tabletop Games, Hiking, Drawing</span>
|
||||
</div>
|
||||
<div class="interest-item">
|
||||
<span class="interest-label">PETS:</span>
|
||||
<span class="interest-value"> Proud Dad of 6 Guinea Pigs (You read right!)</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-header">CORE_TRAITS</div>
|
||||
<div class="info-content">
|
||||
<div class="trait-grid">
|
||||
<span class="trait-item">CURIOUS</span>
|
||||
<span class="trait-item">PERSISTENT</span>
|
||||
<span class="trait-item">ANALYTICAL</span>
|
||||
<span class="trait-item">CREATIVE</span>
|
||||
<span class="trait-item">ADAPTIVE</span>
|
||||
<span class="trait-item">SELF-LEARNER</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card philosophy-card">
|
||||
<div class="info-header">PHILOSOPHY</div>
|
||||
<div class="info-content">
|
||||
<p class="philosophy-text">
|
||||
"The most elegant code emerges from understanding both the technical substrate and the consciousness it serves."
|
||||
</p>
|
||||
<div class="philosophy-note">
|
||||
{{ personalNote() }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Component, signal } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-column-personal-info',
|
||||
imports: [],
|
||||
templateUrl: './column-personal-info.component.html',
|
||||
styleUrl: './column-personal-info.component.css',
|
||||
})
|
||||
export class ColumnPersonalInfoComponent {
|
||||
private readonly _personalNote = signal<string>(
|
||||
'In the spaces between keystrokes and compiler runs, I contemplate the philosophical implications of our digital convergence. When not debugging the matrix, I tend to six small organic beings who remind me that even in our increasingly automated world, care and attention to living creatures remains profoundly important.',
|
||||
);
|
||||
|
||||
private readonly _philosophyText = signal<readonly string[]>([
|
||||
'Four years navigating the intersection between human needs and machine logic has taught me that the most elegant code emerges from understanding both the technical substrate and the consciousness it serves. My journey through enterprise system matrices—from military-grade security protocols to corporate data synthesis—has prepared me for a world where the line between human intention and digital execution grows ever thinner.',
|
||||
'I approach development like a philosopher examining the nature of existence: with persistent curiosity and the understanding that every system, no matter how complex, serves a fundamentally human purpose. In a cyberpunk reality where APIs are neural pathways and databases are digital memories, I believe in crafting code that honors both computational efficiency and human dignity.',
|
||||
'The future belongs to those who can bridge the organic and the synthetic, who understand that behind every elegant algorithm lies a human story waiting to be told.',
|
||||
] as const);
|
||||
|
||||
readonly personalNote = this._personalNote.asReadonly();
|
||||
readonly philosophyText = this._philosophyText.asReadonly();
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<p>column-professional-info works!</p>
|
||||
@@ -0,0 +1,11 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-column-professional-info',
|
||||
imports: [],
|
||||
templateUrl: './column-professional-info.component.html',
|
||||
styleUrl: './column-professional-info.component.css'
|
||||
})
|
||||
export class ColumnProfessionalInfoComponent {
|
||||
|
||||
}
|
||||
@@ -94,52 +94,5 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Personal Info Column -->
|
||||
<div class="column personal-column">
|
||||
<h2 class="column-title">[PERSONAL_DATA]</h2>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-header">INTERESTS</div>
|
||||
<div class="info-content">
|
||||
<div class="interest-item">
|
||||
<span class="interest-label">READINGS:</span>
|
||||
<span class="interest-value"> Science Fiction, Philosophy, Fantasy</span>
|
||||
</div>
|
||||
<div class="interest-item">
|
||||
<span class="interest-label">INTERESTS:</span>
|
||||
<span class="interest-value">Digital & Tabletop Games, Hiking, Drawing</span>
|
||||
</div>
|
||||
<div class="interest-item">
|
||||
<span class="interest-label">PETS:</span>
|
||||
<span class="interest-value"> Proud Dad of 6 Guinea Pigs (You read right!)</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-header">CORE_TRAITS</div>
|
||||
<div class="info-content">
|
||||
<div class="trait-grid">
|
||||
<span class="trait-item">CURIOUS</span>
|
||||
<span class="trait-item">PERSISTENT</span>
|
||||
<span class="trait-item">ANALYTICAL</span>
|
||||
<span class="trait-item">CREATIVE</span>
|
||||
<span class="trait-item">ADAPTIVE</span>
|
||||
<span class="trait-item">SELF-LEARNER</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card philosophy-card">
|
||||
<div class="info-header">PHILOSOPHY</div>
|
||||
<div class="info-content">
|
||||
<p class="philosophy-text">
|
||||
"The most elegant code emerges from understanding both the technical substrate and the consciousness it serves."
|
||||
</p>
|
||||
<div class="philosophy-note">
|
||||
{{ personalNote() }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<app-column-personal-info />
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// operative-history.component.ts
|
||||
import { Component, signal, computed } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ColumnPersonalInfoComponent } from './column-personal-info/column-personal-info.component';
|
||||
|
||||
interface Experience {
|
||||
readonly id: string;
|
||||
@@ -17,7 +18,7 @@ interface Experience {
|
||||
@Component({
|
||||
selector: 'app-operative-history',
|
||||
standalone: true,
|
||||
imports: [CommonModule],
|
||||
imports: [CommonModule, ColumnPersonalInfoComponent],
|
||||
templateUrl: './operative-history.component.html',
|
||||
styleUrl: './operative-history.component.css',
|
||||
})
|
||||
@@ -88,21 +89,9 @@ export class OperativeHistoryComponent {
|
||||
isCurrent: true,
|
||||
} as const);
|
||||
|
||||
private readonly _philosophyText = signal<readonly string[]>([
|
||||
'Four years navigating the intersection between human needs and machine logic has taught me that the most elegant code emerges from understanding both the technical substrate and the consciousness it serves. My journey through enterprise system matrices—from military-grade security protocols to corporate data synthesis—has prepared me for a world where the line between human intention and digital execution grows ever thinner.',
|
||||
'I approach development like a philosopher examining the nature of existence: with persistent curiosity and the understanding that every system, no matter how complex, serves a fundamentally human purpose. In a cyberpunk reality where APIs are neural pathways and databases are digital memories, I believe in crafting code that honors both computational efficiency and human dignity.',
|
||||
'The future belongs to those who can bridge the organic and the synthetic, who understand that behind every elegant algorithm lies a human story waiting to be told.',
|
||||
] as const);
|
||||
|
||||
private readonly _personalNote = signal<string>(
|
||||
'In the spaces between keystrokes and compiler runs, I contemplate the philosophical implications of our digital convergence. When not debugging the matrix, I tend to six small organic beings who remind me that even in our increasingly automated world, care and attention to living creatures remains profoundly important.',
|
||||
);
|
||||
|
||||
// Public computed signals for template
|
||||
readonly experiences = this._experiences.asReadonly();
|
||||
readonly currentTraining = this._currentTraining.asReadonly();
|
||||
readonly philosophyText = this._philosophyText.asReadonly();
|
||||
readonly personalNote = this._personalNote.asReadonly();
|
||||
|
||||
// Computed signals for derived data
|
||||
readonly totalExperience = computed(() => this.experiences().length);
|
||||
|
||||
Reference in New Issue
Block a user