Fix: Update all components to match Synthwave design showcase
Co-authored-by: Junie <junie@jetbrains.com>
This commit is contained in:
@@ -1,49 +1,93 @@
|
||||
import { Component, inject, signal } from '@angular/core';
|
||||
import { Component, inject, signal, effect } from '@angular/core';
|
||||
import { RouterOutlet, RouterLink, RouterLinkActive } from '@angular/router';
|
||||
import { AuthService } from '../../services/auth.service';
|
||||
import { MailService } from '../../services/mail.service';
|
||||
import { Folder } from '../../models/mail.models';
|
||||
|
||||
const THEMES = [
|
||||
{ value: 'theme-cyber-magenta', label: '💖 Cyber Magenta' },
|
||||
{ value: 'theme-neon-cyan', label: '🩵 Neon Cyan' },
|
||||
{ value: 'theme-sunset-orange', label: '🧡 Sunset Orange' },
|
||||
{ value: 'theme-matrix-green', label: '💚 Matrix Green' },
|
||||
];
|
||||
|
||||
@Component({
|
||||
selector: 'app-layout',
|
||||
standalone: true,
|
||||
imports: [RouterOutlet, RouterLink, RouterLinkActive],
|
||||
template: `
|
||||
<div class="min-h-screen relative" [class.light-theme]="isLightTheme()">
|
||||
<!-- Synthwave фон -->
|
||||
<div class="synth-bg" aria-hidden="true">
|
||||
<div #root class="min-h-screen relative theme-cyber-magenta" [class.light-theme]="isLightTheme()" [class.high-contrast]="isHighContrast()">
|
||||
<!-- Synthwave анимированный фон -->
|
||||
<div class="synth-stage" aria-hidden="true">
|
||||
<div class="synth-sun"></div>
|
||||
<div class="synth-grid">
|
||||
<div class="synth-grid-plane"></div>
|
||||
<div class="synth-mountains"></div>
|
||||
<div class="grid-container">
|
||||
<div class="grid-plane"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Контент -->
|
||||
<div class="relative z-10 flex flex-col min-h-screen">
|
||||
<!-- Шапка -->
|
||||
<header class="glass-panel rounded-none border-t-0 border-x-0 p-4 sticky top-0 z-40 backdrop-blur-xl">
|
||||
<div class="max-w-7xl mx-auto flex items-center justify-between">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-10 h-10 rounded-lg bg-gradient-to-br from-synth-pink to-synth-cyan flex items-center justify-center text-synth-dark font-black text-xl font-orbitron shadow-[0_0_15px_rgba(255,0,127,0.8)]">
|
||||
<header class="p-4 lg:p-6 border-b border-cyan-500/20 glass-panel rounded-none border-t-0 border-x-0 sticky top-0 z-40 backdrop-blur-xl">
|
||||
<div class="max-w-7xl mx-auto flex flex-col md:flex-row items-center justify-between gap-4">
|
||||
|
||||
<!-- Логотип -->
|
||||
<div class="flex items-center space-x-3">
|
||||
<div class="w-10 h-10 rounded-lg bg-gradient-to-br from-pink-500 to-cyan-400 flex items-center justify-center text-slate-950 font-black text-xl shadow-[0_0_15px_rgba(255,0,127,0.8)] font-orbitron">
|
||||
M
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="text-2xl font-orbitron font-extrabold tracking-wider neon-text-primary">
|
||||
MAIL<span class="text-synth-cyan font-normal">SERVER</span>
|
||||
<h1 class="text-2xl font-extrabold font-orbitron tracking-wider neon-text-primary">
|
||||
MAIL<span class="text-cyan-400 font-normal">SERVER</span>
|
||||
</h1>
|
||||
<p class="text-xs text-synth-cyan/70 font-mono tracking-widest uppercase">Synthwave Edition</p>
|
||||
<p class="text-xs text-cyan-300/70 font-mono tracking-widest uppercase">Synthwave Edition</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-4">
|
||||
<!-- Переключатель темы -->
|
||||
<button (click)="toggleTheme()" class="cyber-btn px-3 py-2 rounded-lg text-xs font-bold" [title]="isLightTheme() ? 'Переключить на тёмную тему' : 'Переключить на светлую тему'">
|
||||
<!-- Статистика -->
|
||||
<div class="flex items-center space-x-4 bg-slate-950/60 px-4 py-2 rounded-full border border-pink-500/30 text-xs font-mono">
|
||||
<div class="flex items-center space-x-2">
|
||||
<span class="w-2 h-2 rounded-full bg-emerald-400 animate-ping"></span>
|
||||
<span class="text-slate-300">SMTP: <span class="text-emerald-400 font-bold">ONLINE</span></span>
|
||||
</div>
|
||||
<span class="text-slate-600">|</span>
|
||||
<div class="text-slate-300">User: <span class="text-cyan-400 font-bold">{{ email }}</span></div>
|
||||
<span class="text-slate-600">|</span>
|
||||
<div class="text-slate-300">Folders: <span class="text-pink-400 font-bold">{{ folders().length }}</span></div>
|
||||
</div>
|
||||
|
||||
<!-- Контроли -->
|
||||
<div class="flex items-center space-x-3">
|
||||
<!-- Выбор цветовой темы -->
|
||||
<div class="relative">
|
||||
<select #themeSelect (change)="onThemeChange(themeSelect.value)"
|
||||
class="bg-slate-900/90 text-cyan-300 border border-cyan-500/40 rounded-lg px-3 py-1.5 text-sm font-mono focus:outline-none focus:ring-2 focus:ring-pink-500 cursor-pointer">
|
||||
@for (theme of themes; track theme.value) {
|
||||
<option [value]="theme.value" [selected]="currentTheme() === theme.value">
|
||||
{{ theme.label }}
|
||||
</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- High Contrast -->
|
||||
<button (click)="toggleHighContrast()"
|
||||
[attr.aria-pressed]="isHighContrast()"
|
||||
title="Toggle High Contrast Mode"
|
||||
class="px-3 py-1.5 rounded-lg border border-cyan-500/40 bg-slate-900/80 text-xs font-mono hover:bg-cyan-500/20 focus:outline-none focus:ring-2 focus:ring-pink-500 transition-all flex items-center gap-1.5">
|
||||
<span class="text-yellow-400">◐</span>
|
||||
<span>Contrast</span>
|
||||
</button>
|
||||
|
||||
<!-- Light/Dark -->
|
||||
<button (click)="toggleTheme()" class="cyber-btn px-3 py-1.5 rounded-lg text-xs font-bold">
|
||||
{{ isLightTheme() ? '🌙 Dark' : '☀️ Light' }}
|
||||
</button>
|
||||
|
||||
<span class="text-sm font-mono text-slate-400">{{ email }}</span>
|
||||
<button (click)="onLogout()" class="cyber-btn px-4 py-2 rounded-lg text-xs font-bold">
|
||||
<i class="mr-1">⏻</i> Logout
|
||||
<!-- Logout -->
|
||||
<button (click)="onLogout()" class="cyber-btn px-4 py-1.5 rounded-lg text-xs font-bold">
|
||||
⏻ Logout
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -51,21 +95,21 @@ import { Folder } from '../../models/mail.models';
|
||||
|
||||
<!-- Основная область -->
|
||||
<div class="flex flex-1 max-w-7xl mx-auto w-full p-4 gap-4">
|
||||
<!-- Боковая панель с папками -->
|
||||
<!-- Боковая панель -->
|
||||
<aside class="w-64 flex-shrink-0">
|
||||
<div class="glass-panel p-4">
|
||||
<h2 class="text-sm font-orbitron font-bold text-synth-cyan mb-3 flex items-center gap-2">
|
||||
<span class="text-synth-pink">📁</span> FOLDERS
|
||||
<h2 class="text-sm font-orbitron font-bold text-cyan-300 mb-3 flex items-center gap-2">
|
||||
<span class="text-pink-500">📁</span> FOLDERS
|
||||
</h2>
|
||||
<nav class="space-y-1">
|
||||
@for (folder of folders(); track folder.id) {
|
||||
<a routerLink="/inbox" [queryParams]="{folderId: folder.id}"
|
||||
routerLinkActive="bg-synth-pink/20 text-synth-pink border-synth-pink"
|
||||
[queryParamsHandling]=" 'merge'"
|
||||
class="block px-3 py-2 rounded-lg text-sm font-rajdhani text-slate-300 hover:bg-synth-cyan/10 hover:text-synth-cyan border border-transparent transition-all">
|
||||
routerLinkActive="bg-pink-500/20 text-pink-400 border-pink-500"
|
||||
[queryParamsHandling]="'merge'"
|
||||
class="block px-3 py-2 rounded-lg text-sm font-rajdhani text-slate-300 hover:bg-cyan-500/10 hover:text-cyan-300 border border-transparent transition-all">
|
||||
{{ folder.name }}
|
||||
@if (folder.unreadCount > 0) {
|
||||
<span class="ml-2 px-2 py-0.5 text-xs rounded-full bg-synth-pink/30 text-synth-pink font-mono">
|
||||
<span class="ml-2 px-2 py-0.5 text-xs rounded-full bg-pink-500/30 text-pink-400 font-mono">
|
||||
{{ folder.unreadCount }}
|
||||
</span>
|
||||
}
|
||||
@@ -77,8 +121,8 @@ import { Folder } from '../../models/mail.models';
|
||||
<!-- Контакты -->
|
||||
<div class="glass-panel p-4 mt-4">
|
||||
<a routerLink="/contacts"
|
||||
routerLinkActive="bg-synth-cyan/20 text-synth-cyan border-synth-cyan"
|
||||
class="block px-3 py-2 rounded-lg text-sm font-rajdhani text-slate-300 hover:bg-synth-cyan/10 hover:text-synth-cyan border border-transparent transition-all">
|
||||
routerLinkActive="bg-cyan-500/20 text-cyan-300 border-cyan-500"
|
||||
class="block px-3 py-2 rounded-lg text-sm font-rajdhani text-slate-300 hover:bg-cyan-500/10 hover:text-cyan-300 border border-transparent transition-all">
|
||||
👥 Contacts
|
||||
</a>
|
||||
</div>
|
||||
@@ -96,6 +140,17 @@ import { Folder } from '../../models/mail.models';
|
||||
<router-outlet />
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<!-- Футер -->
|
||||
<footer class="p-4 border-t border-cyan-500/20 glass-panel rounded-none border-b-0 border-x-0 mt-6 text-center text-xs font-mono text-slate-400">
|
||||
<div class="max-w-7xl mx-auto flex flex-col sm:flex-row items-center justify-between gap-2">
|
||||
<div>MAILSERVER SYNTHWAVE © 2026</div>
|
||||
<div class="flex items-center space-x-4">
|
||||
<span class="text-cyan-400">◉ A11Y READY</span>
|
||||
<span class="text-pink-400">⚡ HIGH PERF CSS3D</span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
@@ -108,15 +163,38 @@ export default class LayoutComponent {
|
||||
email = this.authService.getEmail() || '';
|
||||
folders = signal<Folder[]>([]);
|
||||
isLightTheme = signal(false);
|
||||
isHighContrast = signal(false);
|
||||
currentTheme = signal('theme-cyber-magenta');
|
||||
themes = THEMES;
|
||||
|
||||
constructor() {
|
||||
this.loadFolders();
|
||||
// Загружаем сохранённую тему
|
||||
const saved = localStorage.getItem('theme');
|
||||
if (saved === 'light') {
|
||||
// Загружаем сохранённые настройки
|
||||
const savedTheme = localStorage.getItem('theme');
|
||||
if (savedTheme === 'light') {
|
||||
this.isLightTheme.set(true);
|
||||
document.documentElement.classList.add('light-theme');
|
||||
}
|
||||
const savedPalette = localStorage.getItem('palette');
|
||||
if (savedPalette) {
|
||||
this.currentTheme.set(savedPalette);
|
||||
}
|
||||
const savedContrast = localStorage.getItem('highContrast');
|
||||
if (savedContrast === 'true') {
|
||||
this.isHighContrast.set(true);
|
||||
}
|
||||
|
||||
// Применяем тему при изменении
|
||||
effect(() => {
|
||||
const root = document.documentElement;
|
||||
// Удаляем все theme классы
|
||||
THEMES.forEach(t => root.classList.remove(t.value));
|
||||
root.classList.add(this.currentTheme());
|
||||
});
|
||||
}
|
||||
|
||||
onThemeChange(theme: string): void {
|
||||
this.currentTheme.set(theme);
|
||||
localStorage.setItem('palette', theme);
|
||||
}
|
||||
|
||||
toggleTheme(): void {
|
||||
@@ -131,6 +209,18 @@ export default class LayoutComponent {
|
||||
}
|
||||
}
|
||||
|
||||
toggleHighContrast(): void {
|
||||
const hc = !this.isHighContrast();
|
||||
this.isHighContrast.set(hc);
|
||||
if (hc) {
|
||||
document.documentElement.classList.add('high-contrast');
|
||||
localStorage.setItem('highContrast', 'true');
|
||||
} else {
|
||||
document.documentElement.classList.remove('high-contrast');
|
||||
localStorage.setItem('highContrast', 'false');
|
||||
}
|
||||
}
|
||||
|
||||
loadFolders(): void {
|
||||
this.mailService.getFolders().subscribe({
|
||||
next: (folders) => this.folders.set(folders),
|
||||
|
||||
Reference in New Issue
Block a user