Skip to content

Commit d4052c4

Browse files
committed
feat(ui): add escape key handling and smooth overlay animations
1 parent a217f8d commit d4052c4

2 files changed

Lines changed: 48 additions & 10 deletions

File tree

‎src/app/app.component.ts‎

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, signal } from '@angular/core';
1+
import { Component, signal, HostListener } from '@angular/core';
22
import { CommonModule } from '@angular/common';
33
import { ConfigStoreService } from '../services/config-store.service';
44
import { CategoryNavigationComponent } from '../components/category-navigation/category-navigation.component';
@@ -26,29 +26,30 @@ import { AboutModalComponent } from '../components/about-modal/about-modal.compo
2626
}
2727
2828
<div class="flex flex-1 overflow-hidden relative">
29-
<!-- Mobile overlay backdrop -->
29+
<!-- Mobile overlay backdrop with fade animation -->
3030
@if (configStore.sidebarOpen()) {
3131
<div
32-
class="fixed inset-0 bg-black/50 z-30 lg:hidden"
32+
class="fixed inset-0 bg-black/50 z-30 lg:hidden animate-fade-in"
3333
(click)="configStore.setSidebarOpen(false)"
3434
></div>
3535
}
3636
37-
<!-- Sidebar navigation -->
37+
<!-- Sidebar navigation with enhanced animation -->
3838
<app-category-navigation
39-
class="absolute lg:relative z-40 h-full w-72 flex-shrink-0 transform transition-transform duration-300 ease-out lg:translate-x-0"
39+
class="absolute lg:relative z-40 h-full w-72 flex-shrink-0 transform transition-all duration-300 ease-out lg:translate-x-0 shadow-2xl lg:shadow-none"
4040
[class.-translate-x-full]="!configStore.sidebarOpen()"
41+
[class.opacity-0]="!configStore.sidebarOpen()"
4142
/>
4243
4344
<!-- Main content area -->
4445
<main class="flex-1 overflow-hidden flex flex-col lg:flex-row min-w-0">
4546
<!-- Config editor -->
4647
<app-config-editor class="flex-1 overflow-y-auto min-w-0" />
4748
48-
<!-- Preview panel - full screen on mobile when visible, side panel on desktop -->
49+
<!-- Preview panel with slide-in animation on mobile -->
4950
@if (configStore.previewVisible()) {
5051
<app-live-preview
51-
class="fixed inset-0 z-50 lg:static lg:z-auto lg:w-2/5 lg:flex-shrink-0 lg:border-l lg:border-kitty-border bg-kitty-darker"
52+
class="fixed inset-0 z-50 lg:static lg:z-auto lg:w-2/5 lg:flex-shrink-0 lg:border-l lg:border-kitty-border bg-kitty-darker animate-slide-in-right lg:animate-none"
5253
/>
5354
}
5455
</main>
@@ -79,13 +80,40 @@ import { AboutModalComponent } from '../components/about-modal/about-modal.compo
7980
:host {
8081
display: block;
8182
}
83+
@keyframes fade-in {
84+
from { opacity: 0; }
85+
to { opacity: 1; }
86+
}
87+
@keyframes slide-in-right {
88+
from { transform: translateX(100%); opacity: 0; }
89+
to { transform: translateX(0); opacity: 1; }
90+
}
91+
.animate-fade-in {
92+
animation: fade-in 0.2s ease-out;
93+
}
94+
.animate-slide-in-right {
95+
animation: slide-in-right 0.25s ease-out;
96+
}
8297
`]
8398
})
8499
export class AppComponent {
85100
readonly showAbout = signal(false);
86101

87102
constructor(public configStore: ConfigStoreService) {}
88103

104+
@HostListener('window:keydown.escape')
105+
onEscape(): void {
106+
if (this.configStore.sidebarOpen()) {
107+
this.configStore.setSidebarOpen(false);
108+
}
109+
if (this.configStore.previewVisible()) {
110+
this.configStore.setPreviewVisible(false);
111+
}
112+
if (this.showAbout()) {
113+
this.showAbout.set(false);
114+
}
115+
}
116+
89117
openKofi(): void {
90118
globalThis.open('https://ko-fi.com/xcutiboo', '_blank', 'noopener,noreferrer');
91119
}

‎src/components/header/header.component.ts‎

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import { Component, EventEmitter, Output, signal } from '@angular/core';
1+
import { Component, EventEmitter, Output, signal, effect, inject } from '@angular/core';
22
import { CommonModule } from '@angular/common';
33
import { ConfigStoreService } from '../../services/config-store.service';
44
import { KittyGeneratorService } from '../../services/kitty-generator.service';
55
import { KittyParserService } from '../../services/kitty-parser.service';
66
import { ThemeService } from '../../services/theme.service';
7+
import { SearchService } from '../../services/search.service';
78
import { SearchBarComponent } from '../search-bar/search-bar.component';
89

910
@Component({
@@ -162,8 +163,17 @@ export class HeaderComponent {
162163
public readonly configStore: ConfigStoreService,
163164
public readonly themeService: ThemeService,
164165
private readonly generator: KittyGeneratorService,
165-
private readonly parser: KittyParserService
166-
) {}
166+
private readonly parser: KittyParserService,
167+
private readonly searchService: SearchService
168+
) {
169+
effect(() => {
170+
const hasResults = this.searchService.results().length > 0;
171+
const wasOpen = this.mobileSearchOpen();
172+
if (wasOpen && !hasResults) {
173+
this.mobileSearchOpen.set(false);
174+
}
175+
});
176+
}
167177

168178
handleImport(): void {
169179
const input = document.createElement('input');

0 commit comments

Comments
 (0)