mirror of
https://github.com/adam-benyekkou/my_portfolio.git
synced 2026-01-15 20:20:09 +00:00
Added animation to tree node
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
<app-section-title title="NEURAL PROFILE" />
|
||||
|
||||
<div class="flex-1 flex items-start justify-between gap-6 mt-6">
|
||||
<!-- Left side - Neural Profile Tree (always shown) -->
|
||||
<!-- Left side - Neural Profile Tree with NieR animations -->
|
||||
<div class="flex-shrink-0">
|
||||
<app-neural-profile-tree />
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/* NieR-style hover effects for tree nodes */
|
||||
.tree-node-item {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: color 0.3s ease;
|
||||
border-bottom: 1px solid transparent;
|
||||
}
|
||||
|
||||
/* Underline effect */
|
||||
.tree-node-item::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 0;
|
||||
height: 1px;
|
||||
background-color: var(--color-nier-text-dark);
|
||||
transition: width 0.3s ease;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
/* Text color change on hover */
|
||||
.tree-node-item:hover {
|
||||
color: var(--color-nier-text-dark);
|
||||
border-bottom-color: var(--color-nier-text-dark);
|
||||
}
|
||||
|
||||
/* Underline animation on hover */
|
||||
.tree-node-item:hover::before {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Glitch effect on hover - target the text span directly */
|
||||
.tree-node-item:hover span.text-nier-dark {
|
||||
animation: nier-text-glitch 0.6s ease;
|
||||
}
|
||||
|
||||
/* Alternative - target any span with text */
|
||||
.tree-node-item:hover span:last-of-type {
|
||||
animation: nier-text-glitch 0.6s ease;
|
||||
}
|
||||
|
||||
/* Scan line for tree nodes */
|
||||
.tree-node-item .scan-line {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background-color: var(--color-nier-text-dark);
|
||||
opacity: 0;
|
||||
z-index: 3;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.tree-node-item:hover .scan-line {
|
||||
animation: nier-button-scan 0.3s ease forwards;
|
||||
}
|
||||
|
||||
/* Text glitch animation for tree nodes */
|
||||
@keyframes nier-text-glitch {
|
||||
0%, 100% {
|
||||
transform: translateX(0);
|
||||
clip-path: inset(0 0 0 0);
|
||||
}
|
||||
10% {
|
||||
transform: translateX(-2px);
|
||||
clip-path: inset(0 0 40% 0);
|
||||
}
|
||||
15% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
20% {
|
||||
transform: translateX(1px);
|
||||
clip-path: inset(40% 0 0 0);
|
||||
}
|
||||
25% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
30% {
|
||||
transform: translateX(-1px);
|
||||
clip-path: inset(20% 0 20% 0);
|
||||
}
|
||||
35% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* The scan line animation */
|
||||
@keyframes nier-button-scan {
|
||||
0% {
|
||||
opacity: 0.5;
|
||||
left: -100%;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
left: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* Active/click state for tree nodes */
|
||||
.tree-node-item:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
/* Touch device optimization */
|
||||
@media (hover: none) {
|
||||
.tree-node-item:active::before {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tree-node-item:active .scan-line {
|
||||
animation: nier-button-scan 0.3s ease forwards;
|
||||
}
|
||||
|
||||
.tree-node-item:active {
|
||||
color: var(--color-nier-text-dark);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div class="bg-nier-bg border-2 border-nier-border text-nier-dark w-96 font-terminal text-sm leading-relaxed p-4">
|
||||
<div class="bg-nier-bg border-2 border-nier-border text-nier-dark w-96 md:w-[28rem] lg:w-[32rem] font-terminal text-sm md:text-base lg:text-lg leading-relaxed p-4 md:p-6">
|
||||
@for (node of treeData(); track node.id) {
|
||||
<div
|
||||
class="border-l-2 border-transparent"
|
||||
@@ -6,8 +6,10 @@
|
||||
[class.hidden]="!node.visible"
|
||||
>
|
||||
<div
|
||||
class="flex items-center py-1 px-2 cursor-pointer transition-all duration-150 ease-in-out hover:bg-nier-mid/20 hover:translate-x-0.5 relative"
|
||||
class="tree-node-item flex items-center py-1 md:py-2 px-2 md:px-3 cursor-pointer transition-all duration-150 ease-in-out hover:bg-nier-mid/20 hover:translate-x-0.5 relative overflow-hidden"
|
||||
[class.font-bold]="node.isSelected"
|
||||
[style.padding-left]="getIndentPadding(node.level)"
|
||||
[attr.data-label]="node.title"
|
||||
(click)="selectNode(node)"
|
||||
>
|
||||
<!-- Selection indicator -->
|
||||
@@ -17,7 +19,7 @@
|
||||
|
||||
<!-- Expand/Collapse Icon -->
|
||||
<span
|
||||
class="w-4 h-4 flex items-center justify-center text-nier-accent mr-2 cursor-pointer select-none"
|
||||
class="w-4 h-4 md:w-5 md:h-5 lg:w-6 lg:h-6 flex items-center justify-center text-nier-accent mr-2 md:mr-3 cursor-pointer select-none text-sm md:text-base lg:text-lg"
|
||||
[class.text-nier-mid]="!node.hasChildren"
|
||||
(click)="$event.stopPropagation(); toggleExpand(node)"
|
||||
>
|
||||
@@ -34,13 +36,16 @@
|
||||
}
|
||||
</span>
|
||||
|
||||
<!-- Node Title -->
|
||||
<!-- Node Title with NieR effects -->
|
||||
<span
|
||||
class="flex-1 text-nier-dark select-none"
|
||||
class="flex-1 text-nier-dark select-none relative z-10"
|
||||
[class.font-bold]="node.isSelected"
|
||||
>
|
||||
{{ node.title }}
|
||||
</span>
|
||||
|
||||
<!-- Scan line effect -->
|
||||
<span class="scan-line"></span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ export interface NeuralProfileNode {
|
||||
standalone: true,
|
||||
imports: [CommonModule],
|
||||
templateUrl: './neural-profile-tree.component.html',
|
||||
styles: [],
|
||||
styleUrl: './neural-profile-tree.component.css',
|
||||
})
|
||||
export class NeuralProfileTreeComponent {
|
||||
// Input for external data (optional)
|
||||
@@ -32,8 +32,8 @@ export class NeuralProfileTreeComponent {
|
||||
private getInitialData(): NeuralProfileNode[] {
|
||||
return [
|
||||
{
|
||||
id: 'neuralProfile',
|
||||
title: ':TECH_CORE ',
|
||||
id: 'workstation',
|
||||
title: 'Workstation/',
|
||||
isExpanded: true,
|
||||
isSelected: false,
|
||||
level: 0,
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { HeaderContactLinksComponent } from './header-contact-links.component';
|
||||
|
||||
describe('HeaderContactLinksComponent', () => {
|
||||
let component: HeaderContactLinksComponent;
|
||||
let fixture: ComponentFixture<HeaderContactLinksComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [HeaderContactLinksComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(HeaderContactLinksComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user