Fix sidebar styling (#313)

This commit is contained in:
Flaminel
2025-09-25 12:06:46 +03:00
committed by GitHub
parent 911849c6dd
commit 98ccee866d
7 changed files with 471 additions and 913 deletions

View File

@@ -6,13 +6,11 @@
<p-confirmDialog></p-confirmDialog>
<!-- Main layout with sidebar and content -->
<div class="layout-main">
<!-- Desktop Sidebar -->
<div class="sidebar">
<div class="sidebar-top-line"></div>
<!-- Shared Sidebar Content -->
<app-sidebar-content
[isMobile]="false">
#sidebarContent
[isMobile]="false"
[enableMobileDrawer]="true">
</app-sidebar-content>
</div>
@@ -24,7 +22,7 @@
<div class="topbar-section left">
<button pButton class="p-button-text sidebar-toggle-mobile"
icon="pi pi-bars"
(click)="mobileSidebarVisible.set(true)"></button>
(click)="sidebarContent.toggleMobileDrawer()"></button>
</div>
<!-- <div class="topbar-section center">
<div class="version-bar">
@@ -63,25 +61,4 @@
</div>
</div>
</div>
<!-- Mobile Sidebar using PrimeNG sidebar component -->
<p-drawer
styleClass="mobile-sidebar"
[visible]="mobileSidebarVisible()"
(visibleChange)="mobileSidebarVisible.set($event)"
[dismissible]="true"
[showCloseIcon]="false"
position="left">
<ng-template #headless>
<div class="mobile-sidebar-content">
<div class="sidebar-top-line"></div>
<!-- Shared Sidebar Content for Mobile -->
<app-sidebar-content
[isMobile]="true"
(navItemClicked)="mobileSidebarVisible.set(false)">
</app-sidebar-content>
</div>
</ng-template>
</p-drawer>
</div>

View File

@@ -15,208 +15,7 @@
}
}
// Main Sidebar Styling
.sidebar {
display: flex;
flex-direction: column;
width: 260px;
min-width: 260px;
height: 100vh;
background-color: var(--surface-overlay);
border-right: 1px solid var(--surface-border);
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
overflow-y: auto;
z-index: 10;
// Top color line
.sidebar-top-line {
height: 3px;
background: var(--primary-color);
margin-bottom: 10px;
}
// Hide on mobile screens
@media (max-width: 768px) {
display: none;
}
}
// Top color line
.sidebar-top-line {
height: 3px;
background: linear-gradient(to right, #673ab7, #9b59b6);
margin-bottom: 10px;
}
// Logo container
.logo-container {
display: flex;
align-items: center;
padding: 15px 20px;
margin-bottom: 5px;
.logo {
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
border-radius: 50%;
margin-right: 15px;
background-color: rgba(142, 68, 173, 0.1);
border: 1px solid rgba(142, 68, 173, 0.3);
animation: logo-glow 10s infinite ease-in-out;
i {
font-size: 20px;
color: #a569bd;
}
}
.logo-small {
display: none;
}
h1 {
font-size: 20px;
font-weight: 700;
margin: 0;
background: linear-gradient(to right, #fff, #bbb);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
}
// Navigation menu
.nav-menu {
display: flex;
flex-direction: column;
padding: 5px 0 20px 0;
flex: 1;
// Sponsor link
.sponsor-link {
margin-bottom: 15px;
border-bottom: 1px solid rgba(142, 68, 173, 0.15);
padding-bottom: 15px !important;
.heart-icon i {
color: #ff4d6d !important;
animation: heart-pulse 7.2s infinite ease-in-out;
}
}
// Nav groups
.nav-group {
margin-bottom: 15px;
.nav-group-title {
font-size: 12px;
font-weight: 700;
color: rgba(142, 68, 173, 0.8);
text-transform: uppercase;
letter-spacing: 2px;
padding: 0 20px 8px;
margin-top: 10px;
margin-bottom: 5px;
border-bottom: 1px solid rgba(142, 68, 173, 0.2);
text-shadow: 0 0 10px rgba(142, 68, 173, 0.3);
}
}
// Navigation items
.nav-item {
display: flex;
align-items: center;
padding: 10px 20px;
color: #e0e0e0;
text-decoration: none;
border-radius: 0 6px 6px 0;
margin: 2px 0 2px 0;
position: relative;
overflow: hidden;
transition: all 0.2s ease;
.nav-icon-wrapper {
width: 32px;
height: 32px;
display: flex;
justify-content: center;
align-items: center;
margin-right: 15px;
border-radius: 8px;
background: rgba(46, 39, 56, 0.3);
border: 1px solid rgba(255, 255, 255, 0.05);
transition: all 0.2s ease;
i {
font-size: 14px;
color: #e0e0e0;
}
}
span {
white-space: nowrap;
font-size: 14px;
}
&::before {
content: '';
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 3px;
background: transparent;
transition: all 0.2s ease;
}
&:hover {
background: rgba(142, 68, 173, 0.15);
transform: translateX(2px);
.nav-icon-wrapper {
background: rgba(142, 68, 173, 0.2);
transform: scale(1.05);
}
&::before {
background: linear-gradient(to bottom, rgba(142, 68, 173, 0.4), rgba(103, 58, 183, 0.4));
}
}
&.active {
background: linear-gradient(to right, rgba(142, 68, 173, 0.9), rgba(103, 58, 183, 0.7));
box-shadow: 0 2px 10px rgba(142, 68, 173, 0.3);
.nav-icon-wrapper {
background: rgba(255, 255, 255, 0.15);
border-color: rgba(255, 255, 255, 0.2);
}
&::before {
background: #fff;
}
}
}
}
// Animation keyframes
@keyframes logo-glow {
0% { box-shadow: 0 0 15px #8e44ad, 0 0 5px rgba(142, 68, 173, 0.4); } // Purple
20% { box-shadow: 0 0 15px #9b59b6, 0 0 5px rgba(155, 89, 182, 0.4); } // Lighter purple
40% { box-shadow: 0 0 15px #673ab7, 0 0 5px rgba(103, 58, 183, 0.4); } // Deep purple
60% { box-shadow: 0 0 15px #5e35b1, 0 0 5px rgba(94, 53, 177, 0.4); } // Dark deep purple
80% { box-shadow: 0 0 15px #7e57c2, 0 0 5px rgba(126, 87, 194, 0.4); } // Medium purple
100% { box-shadow: 0 0 15px #8e44ad, 0 0 5px rgba(142, 68, 173, 0.4); } // Back to purple
}
@keyframes heart-pulse {
0% { color: #ff4d6d; text-shadow: 0 0 10px #ff4d6d, 0 0 20px #ff4d6d; } // Pink
50% { color: #ff0a33; text-shadow: 0 0 15px #ff0a33, 0 0 25px #ff0a33; } // Red
100% { color: #ff4d6d; text-shadow: 0 0 10px #ff4d6d, 0 0 20px #ff4d6d; } // Pink
}
// Animation keyframes (logo-glow and heart-pulse moved to sidebar-content component)
@keyframes pulse-heart {
0% {
@@ -375,22 +174,6 @@
// Deep styling overrides for PrimeNG components
:host ::ng-deep {
// Mobile sidebar styling
.p-sidebar.p-component.mobile-sidebar {
background-color: var(--surface-overlay) !important;
border: none !important;
color: var(--text-color) !important;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2) !important;
.p-sidebar-header {
display: none !important;
}
.p-sidebar-content {
padding: 0 !important;
}
}
// Override Prime NG button styles
.p-button {
&.p-button-text {
@@ -419,453 +202,15 @@
}
}
// Mobile sidebar content
.mobile-sidebar-content {
display: flex;
flex-direction: column;
background-color: var(--surface-overlay);
color: var(--text-color);
height: 100%;
.sidebar-top-line {
height: 3px;
background: var(--primary-color);
margin-bottom: 10px;
}
.sidebar-header {
padding: 1rem;
display: flex;
align-items: center;
margin-bottom: 5px;
.sidebar-logo {
display: flex;
align-items: center;
.logo {
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
border-radius: 50%;
margin-right: 15px;
background-color: rgba(142, 68, 173, 0.1);
border: 1px solid rgba(142, 68, 173, 0.3);
animation: logo-glow 10s infinite ease-in-out;
i {
font-size: 20px;
color: #a569bd;
}
}
.app-name {
font-size: 20px;
font-weight: 700;
margin-left: 0.5rem;
background: linear-gradient(to right, #fff, #bbb);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
white-space: nowrap;
}
}
}
.sidebar-nav {
padding: 5px 0 20px 0;
overflow-y: auto;
flex: 1;
.sidebar-section-title {
font-size: 12px;
font-weight: 700;
color: rgba(142, 68, 173, 0.8);
text-transform: uppercase;
letter-spacing: 2px;
padding: 0 20px 8px;
margin-top: 10px;
margin-bottom: 5px;
border-bottom: 1px solid rgba(142, 68, 173, 0.2);
text-shadow: 0 0 10px rgba(142, 68, 173, 0.3);
}
ul {
list-style: none;
}
.sidebar-item {
display: flex;
align-items: center;
padding: 10px 20px;
color: #e0e0e0;
text-decoration: none;
border-radius: 0 6px 6px 0;
margin: 2px 0 2px 0;
position: relative;
overflow: hidden;
transition: all 0.2s ease;
.item-icon-wrapper {
width: 32px;
height: 32px;
display: flex;
justify-content: center;
align-items: center;
margin-right: 15px;
border-radius: 8px;
background: rgba(46, 39, 56, 0.3);
border: 1px solid rgba(255, 255, 255, 0.05);
transition: all 0.2s ease;
}
.item-icon {
font-size: 14px;
color: #e0e0e0;
}
.item-label {
font-size: 14px;
white-space: nowrap;
}
&::before {
content: '';
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 3px;
background: transparent;
transition: all 0.2s ease;
}
&:hover {
background: rgba(142, 68, 173, 0.15);
transform: translateX(2px);
.item-icon-wrapper {
background: rgba(142, 68, 173, 0.2);
transform: scale(1.05);
}
&::before {
background: linear-gradient(to bottom, rgba(142, 68, 173, 0.4), rgba(103, 58, 183, 0.4));
background-color: rgba(255, 255, 255, 0.1);
color: #ffffff;
font-weight: 600;
border-left: 3px solid #4299e1;
padding-left: calc(1rem - 3px);
i {
color: #4299e1;
}
}
}
}
}
.sponsor-section {
padding: 0.5rem 1rem 1rem;
margin-top: auto;
.sponsor-link {
display: flex;
align-items: center;
padding: 0.75rem 1rem;
background-color: rgba(255, 255, 255, 0.05);
border-radius: 4px;
color: #ffffff;
text-decoration: none;
transition: background-color 0.2s;
&:hover {
background-color: rgba(255, 255, 255, 0.1);
}
i {
color: #ff5a5f;
margin-right: 0.75rem;
}
span {
white-space: nowrap;
}
}
}
}
// Main layout
.layout-main {
display: flex;
flex: 1;
// Sidebar
.sidebar {
width: 250px;
height: 100vh;
background: linear-gradient(135deg, #2c1a47 0%, #1a0e29 50%, #1e1230 75%, rgba(30, 18, 48, 0.95) 100%);
background-size: 100% 100%;
border-right: 1px solid rgba(142, 68, 173, 0.15);
box-shadow: 2px 0 15px rgba(0, 0, 0, 0.3);
display: flex;
flex-direction: column;
z-index: 1000;
flex-shrink: 0;
overflow-y: auto;
position: sticky;
top: 0;
transition: all 0.3s ease;
backdrop-filter: blur(5px);
// Colored edge accents
&::before,
&::after {
content: '';
position: absolute;
top: 0;
bottom: 0;
width: 1px;
background: linear-gradient(to bottom, rgba(142, 68, 173, 0.6), rgba(103, 58, 183, 0.6)); // Purple gradient
z-index: 2;
}
&::before {
left: 0;
}
&::after {
right: 0;
}
.sidebar-top-line {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 1px;
background: linear-gradient(90deg, rgba(142, 68, 173, 0.6), rgba(103, 58, 183, 0.6)); // Purple gradient
z-index: 2;
box-shadow: 0 0 8px rgba(142, 68, 173, 0.4); // Add subtle glow
}
// Sidebar container - all styling moved to sidebar-content component
// The sidebar-content component now handles all visual styling via :host selector
// Transitions for text elements in expanded state
.app-name,
.sidebar-section-title,
.sidebar-nav span,
.sponsor-link span {
opacity: 1;
visibility: visible;
width: auto;
max-width: 180px;
white-space: nowrap;
transition: all 0.3s ease;
}
@media (max-width: 991px) {
display: none;
}
&.collapsed {
width: 70px;
.app-name,
.sidebar-section-title,
.sidebar-nav span,
.sponsor-link span {
opacity: 0;
visibility: hidden;
width: 0;
max-width: 0;
overflow: hidden;
transition: all 0.3s ease;
}
.sidebar-nav li a {
padding: 0.75rem 0;
justify-content: center;
i {
margin: 0 auto;
width: 100%;
display: flex;
justify-content: center;
}
}
.sponsor-section .sponsor-link {
padding: 0.75rem 0;
justify-content: center;
i {
margin: 0 auto;
width: 100%;
display: flex;
justify-content: center;
}
}
.sidebar-footer {
justify-content: center;
}
}
// Logo container
.logo-container {
display: flex;
align-items: center;
padding: 15px 20px 0px;
border-bottom: none;
margin-bottom: -15px;
position: relative;
.logo {
display: flex;
align-items: center;
justify-content: center;
width: 60px;
height: 60px;
margin-right: 15px;
border-radius: 50%;
background-color: rgba(142, 68, 173, 0.1);
border: 1px solid rgba(142, 68, 173, 0.3);
animation: logo-glow 10s infinite ease-in-out;
transition: all 0.3s ease;
i {
font-size: 30px;
color: #ffffff;
}
}
.logo-small {
display: none;
}
h1 {
font-size: 22px;
font-weight: 700;
margin: 0;
color: #fff;
text-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
letter-spacing: 0.5px;
background: linear-gradient(to right, #fff, #bbb);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
}
// Sponsor section
.sponsor-section {
padding: 0.5rem 1rem 1rem;
.sponsor-link {
display: flex;
align-items: center;
padding: 0.75rem 1rem;
background-color: rgba(255, 255, 255, 0.05);
border-radius: 4px;
color: #ffffff;
text-decoration: none;
transition: background-color 0.2s;
&:hover {
background-color: rgba(255, 255, 255, 0.1);
}
i {
color: #ff5a5f;
margin-right: 0.75rem;
}
}
}
// Sidebar footer with toggle button
.sidebar-footer {
display: flex;
justify-content: flex-end;
padding: 1rem;
margin-top: auto;
.sidebar-toggle {
color: #ffffff;
background-color: rgba(255, 255, 255, 0.1);
&:hover {
background-color: rgba(255, 255, 255, 0.2);
}
}
}
}
// Sidebar navigation
.sidebar-nav {
padding: 1rem 0;
overflow-y: auto;
flex: 1;
.sidebar-section {
margin-bottom: 1.5rem;
.sidebar-section-title {
padding: 0 1rem;
margin: 0 0 0.5rem 0;
font-size: 0.75rem;
font-weight: 600;
color: rgba(255, 255, 255, 0.5);
letter-spacing: 0.5px;
}
ul {
list-style: none;
padding: 0;
margin: 0;
li {
a {
display: flex;
align-items: center;
padding: 0.75rem 1rem;
color: rgba(255, 255, 255, 0.8);
text-decoration: none;
transition: background-color 0.2s;
&:hover {
background-color: rgba(255, 255, 255, 0.05);
}
i {
font-size: 1.25rem;
width: 1.5rem;
margin-right: 0.75rem;
text-align: center;
}
span {
font-size: 0.95rem;
transition: opacity 0.2s ease, width 0.2s ease;
white-space: nowrap;
}
}
&.active a {
background-color: rgba(255, 255, 255, 0.1);
color: #ffffff;
font-weight: 600;
border-left: 3px solid #4299e1;
padding-left: calc(1rem - 3px);
i {
color: #4299e1;
}
}
}
}
}
}
// Sidebar navigation styles moved to sidebar-content component
// Content area
.layout-content {
@@ -1196,39 +541,12 @@
transition: all 0.3s ease;
}
}
@keyframes logo-glow {
0% { box-shadow: 0 0 15px #8e44ad, 0 0 5px rgba(142, 68, 173, 0.4); } // Purple
20% { box-shadow: 0 0 15px #9b59b6, 0 0 5px rgba(155, 89, 182, 0.4); } // Lighter purple
40% { box-shadow: 0 0 15px #673ab7, 0 0 5px rgba(103, 58, 183, 0.4); } // Deep purple
60% { box-shadow: 0 0 15px #5e35b1, 0 0 5px rgba(94, 53, 177, 0.4); } // Dark deep purple
80% { box-shadow: 0 0 15px #7e57c2, 0 0 5px rgba(126, 87, 194, 0.4); } // Medium purple
100% { box-shadow: 0 0 15px #8e44ad, 0 0 5px rgba(142, 68, 173, 0.4); } // Back to purple
}
}
}
}
}
// Mobile sidebar styling
.mobile-sidebar-content {
display: flex;
flex-direction: column;
height: 100%;
background: linear-gradient(135deg, #2c1a47 0%, #1a0e29 50%, #1e1230 75%, rgba(30, 18, 48, 0.95) 100%);
overflow-y: auto;
.sidebar-top-line {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
background: linear-gradient(90deg, rgba(142, 68, 173, 0.6), rgba(103, 58, 183, 0.6));
z-index: 2;
box-shadow: 0 0 8px rgba(142, 68, 173, 0.4);
}
}
// Mobile sidebar styling moved to sidebar-content component
/* Media queries for responsiveness */
@media screen and (max-width: 768px) {
@@ -1252,16 +570,5 @@
}
}
// Style PrimeNG mobile sidebar
:host ::ng-deep {
.p-sidebar.mobile-sidebar {
.p-sidebar-header {
display: none; // Hide the header since we're not using it
}
.p-sidebar-content {
padding: 0;
}
}
}
// Drawer/Sidebar mobile styling is centralized in sidebar-content component
}

View File

@@ -1,4 +1,4 @@
import { Component, inject, signal } from '@angular/core';
import { Component, HostListener, inject, signal } from '@angular/core';
import { Router, RouterOutlet } from '@angular/router';
import { CommonModule } from '@angular/common';
import { Title } from '@angular/platform-browser';
@@ -9,7 +9,7 @@ import { ToolbarModule } from 'primeng/toolbar';
import { FormsModule } from '@angular/forms';
import { MenuModule } from 'primeng/menu';
import { SidebarModule } from 'primeng/sidebar';
import { DrawerModule } from 'primeng/drawer';
import { DividerModule } from 'primeng/divider';
import { RippleModule } from 'primeng/ripple';
import { ConfirmDialogModule } from 'primeng/confirmdialog';
@@ -29,7 +29,6 @@ import { ToastContainerComponent } from '../../shared/components/toast-container
FormsModule,
MenuModule,
SidebarModule,
DrawerModule,
DividerModule,
RippleModule,
ConfirmDialogModule,
@@ -40,25 +39,15 @@ import { ToastContainerComponent } from '../../shared/components/toast-container
styleUrl: './main-layout.component.scss'
})
export class MainLayoutComponent {
// Mobile menu state
mobileSidebarVisible = signal<boolean>(false);
// Inject router
public router = inject(Router);
constructor() {}
/**
* Handles mobile navigation click events by closing the sidebar
*/
onMobileNavClick(): void {
this.mobileSidebarVisible.set(false);
}
/**
* Toggle mobile sidebar visibility
* Toggle mobile sidebar visibility via sidebar component
*/
toggleMobileSidebar(): void {
this.mobileSidebarVisible.update(value => !value);
// This will be called via template reference
}
}

View File

@@ -1,10 +1,7 @@
<!-- Logo Container -->
<div class="logo-container">
<div class="logo logo-large logo-glow">
<img src="icons/128.png" width="40" height="40" alt="Logo">
</div>
<div class="logo logo-small logo-glow">
<img src="icons/128.png" width="40" height="40" alt="Logo">
<img src="/icons/128.png" width="40" height="40" alt="Logo">
</div>
<h2>Cleanuparr</h2>
</div>
@@ -26,7 +23,7 @@
[@staggerItems]>
<!-- Project Sponsors Link (always visible) -->
<a href="https://cleanuparr.github.io/Cleanuparr/support"
class="nav-item sponsor-link"
class="nav-item support-link"
target="_blank"
rel="noopener noreferrer">
<div class="nav-icon-wrapper heart-icon">
@@ -142,3 +139,21 @@
</ng-container>
</div>
</nav>
<!-- Mobile Drawer (only rendered when enableMobileDrawer is true) -->
<p-drawer
*ngIf="enableMobileDrawer"
styleClass="mobile-sidebar"
[visible]="mobileSidebarVisible()"
(visibleChange)="onMobileDrawerVisibilityChange($event)"
[dismissible]="true"
[showCloseIcon]="false"
position="left">
<ng-template #headless>
<app-sidebar-content
[isMobile]="true"
[enableMobileDrawer]="false"
(navItemClicked)="onNavItemClick()">
</app-sidebar-content>
</ng-template>
</p-drawer>

View File

@@ -1,57 +1,158 @@
// Main container stability
:host {
width: var(--sidebar-width);
height: var(--sidebar-height);
background: var(--sidebar-background);
background-size: 100% 100%;
border-right: var(--sidebar-border);
box-shadow: var(--sidebar-shadow);
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden; // Prevent scrolling on the host
position: relative;
z-index: var(--sidebar-z-index);
flex-shrink: 0;
overflow-y: auto;
position: sticky;
top: 0;
transition: all var(--sidebar-transition-duration) ease;
backdrop-filter: var(--sidebar-backdrop-filter);
// Colored edge accents
&::before,
&::after {
content: '';
position: absolute;
top: 0;
bottom: 0;
width: var(--sidebar-top-line-height);
background: var(--sidebar-top-line-background);
z-index: 2;
}
&::before {
left: 0;
}
&::after {
right: 0;
}
// Transitions for text elements in expanded state
.app-name,
.sidebar-section-title,
.sidebar-nav span,
.support-link span {
opacity: 1;
visibility: visible;
width: auto;
max-width: 180px;
white-space: nowrap;
transition: all var(--sidebar-transition-duration) ease;
}
@media (max-width: 991px) {
display: none;
}
// When isMobile input is true, ensure component is always visible regardless of screen size
&.mobile-variant {
display: flex !important;
position: static;
height: 100% !important;
width: 100% !important;
}
&.collapsed {
width: var(--sidebar-width-collapsed);
.app-name,
.sidebar-section-title,
.sidebar-nav span,
.support-link span {
opacity: 0;
visibility: hidden;
width: 0;
max-width: 0;
overflow: hidden;
transition: all var(--sidebar-transition-duration) ease;
}
.sidebar-nav li a {
padding: 0.75rem 0;
justify-content: center;
i {
margin: 0 auto;
width: 100%;
display: flex;
justify-content: center;
}
}
.sponsor-section .support-link {
padding: 0.75rem 0;
justify-content: center;
i {
margin: 0 auto;
width: 100%;
display: flex;
justify-content: center;
}
}
.sidebar-footer {
justify-content: center;
}
}
}
// Logo container
/* ========================================================================
LOGO SECTION
======================================================================== */
.logo-container {
display: flex;
align-items: center;
padding: 20px;
margin-top: 20px;
margin-bottom: 20px;
position: relative;
flex: 0 0 auto; // Prevent logo container from growing/shrinking
.logo {
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
border-radius: 50%;
width: var(--sidebar-logo-size);
height: var(--sidebar-logo-size);
margin-right: 15px;
background-color: var(--primary-100);
border: 1px solid var(--primary-300);
box-shadow: 0 0 10px rgba(var(--primary-500-rgb), 0.2);
transition: all 0.3s ease;
&:hover {
box-shadow: 0 0 15px rgba(var(--primary-500-rgb), 0.3);
}
i {
font-size: 20px;
color: var(--primary-color);
}
}
border-radius: 50%;
background-color: var(--sidebar-logo-background);
border: var(--sidebar-logo-border);
animation: logo-glow 10s infinite ease-in-out;
transition: all var(--sidebar-transition-duration) ease;
.logo-glow {
box-shadow: 0 0 10px 6px rgba(89, 16, 185, 0.5);
animation: logo-glow 2s infinite ease-in-out;
}
.logo-small {
display: none;
img {
width: var(--sidebar-logo-size);
height: var(--sidebar-logo-size);
}
}
h2 {
font-size: 22px;
font-weight: 700;
margin: 0;
color: var(--text-color);
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
color: var(--sidebar-text-white);
text-shadow: var(--sidebar-text-shadow);
letter-spacing: 0.5px;
background: var(--sidebar-text-gradient);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
.logo-glow {
box-shadow: var(--sidebar-logo-glow);
animation: logo-glow 2s infinite ease-in-out;
}
}
@@ -60,6 +161,7 @@
display: block;
gap: 0; // Remove gap to prevent layout shifts
transition: opacity 0.2s ease;
margin-top: 15px; // Add spacing from logo container
// Keep horizontal overflow hidden to avoid horizontal scrollbar
overflow-x: hidden;
@@ -136,14 +238,14 @@
}
// Sponsor link
.sponsor-link {
.support-link {
border-bottom: none;
margin-bottom: 15px;
.heart-icon i {
color: rgb(147 0 255) !important;
color: var(--sidebar-heart-color) !important;
transition: all 0.3s ease;
text-shadow: 0 0 6px rgba(239, 68, 68, 0.7), 0 0 12px rgba(239, 68, 68, 0.5);
text-shadow: var(--sidebar-heart-shadow);
&:hover {
transform: scale(1.2);
@@ -224,9 +326,14 @@
font-weight: 600;
}
}
// Navigation items
.nav-item {
}
/* ========================================================================
NAVIGATION SECTION
======================================================================== */
// Main navigation items
.nav-item {
display: flex;
align-items: center;
padding: 10px 20px;
@@ -350,7 +457,7 @@
}
&::before {
background-color: #ffffff;
background-color: var(--sidebar-text-white);
}
}
}
@@ -376,9 +483,65 @@
transform: translateX(3px) scale(1.1);
}
}
/* ========================================================================
SPONSOR SECTION
======================================================================== */
.sponsor-section {
padding: 0.5rem 1rem 1rem;
.support-link {
display: flex;
align-items: center;
padding: 0.75rem 1rem;
background-color: rgba(255, 255, 255, 0.05);
border-radius: 4px;
color: var(--sidebar-text-white);
text-decoration: none;
transition: background-color 0.2s;
&:hover {
background-color: rgba(255, 255, 255, 0.1);
}
i {
color: #ff5a5f;
margin-right: 0.75rem;
}
}
}
// Sidebar footer with toggle button
.sidebar-footer {
display: flex;
justify-content: flex-end;
padding: 1rem;
margin-top: auto;
.sidebar-toggle {
color: var(--sidebar-text-white);
background-color: rgba(255, 255, 255, 0.1);
&:hover {
background-color: rgba(255, 255, 255, 0.2);
}
}
}
/* ========================================================================
ANIMATIONS
======================================================================== */
@keyframes logo-glow {
0% { box-shadow: 0 0 15px #8e44ad, 0 0 5px rgba(142, 68, 173, 0.4); }
20% { box-shadow: 0 0 15px #9b59b6, 0 0 5px rgba(155, 89, 182, 0.4); }
40% { box-shadow: 0 0 15px #673ab7, 0 0 5px rgba(103, 58, 183, 0.4); }
60% { box-shadow: 0 0 15px #5e35b1, 0 0 5px rgba(94, 53, 177, 0.4); }
80% { box-shadow: 0 0 15px #7e57c2, 0 0 5px rgba(126, 87, 194, 0.4); }
100% { box-shadow: 0 0 15px #8e44ad, 0 0 5px rgba(142, 68, 173, 0.4); }
}
// Loading skeleton animation
@keyframes skeleton-loading {
0% {
background-position: 200% 0;
@@ -388,7 +551,6 @@
}
}
// Animation keyframes
@keyframes heart-pulse {
0% {
transform: scale(1);
@@ -403,3 +565,124 @@
opacity: 0.7;
}
}
::ng-deep {
.p-drawer.mobile-sidebar {
background: var(--sidebar-background) !important;
border: none !important;
.p-drawer-content {
padding: 0 !important;
background: var(--sidebar-background) !important;
color: var(--sidebar-text-white) !important;
.sidebar-header {
padding: 1.5rem 1rem;
display: flex;
align-items: center;
justify-content: center;
.sidebar-logo {
display: flex;
align-items: center;
.sidebar-icon {
font-size: 1.5rem;
color: #ffcc00;
}
.app-name {
font-size: 1.25rem;
font-weight: 700;
margin-left: 0.5rem;
color: var(--sidebar-text-white);
}
}
}
.sidebar-nav {
padding: var(--sidebar-nav-padding);
overflow-y: auto;
flex: 1;
.sidebar-section-title {
padding: 0 1rem;
margin: 0 0 0.5rem 0;
font-size: 0.75rem;
font-weight: 600;
color: rgba(255, 255, 255, 0.5);
letter-spacing: 0.5px;
}
ul {
list-style: none;
padding: 0;
margin: 0;
li {
a {
display: flex;
align-items: center;
padding: 0.75rem 1rem;
color: rgba(255, 255, 255, 0.8);
text-decoration: none;
transition: background-color 0.2s;
&:hover {
background-color: rgba(255, 255, 255, 0.05);
}
i {
font-size: 1.25rem;
width: 1.5rem;
margin-right: 0.75rem;
text-align: center;
}
span {
font-size: 0.95rem;
}
}
&.active a {
background-color: rgba(255, 255, 255, 0.1);
color: var(--sidebar-text-white);
font-weight: 600;
border-left: 3px solid #4299e1;
padding-left: calc(1rem - 3px);
i {
color: #4299e1;
}
}
}
}
}
.sponsor-section {
padding: 0.5rem 1rem 1rem;
margin-top: auto;
.support-link {
display: flex;
align-items: center;
padding: 0.75rem 1rem;
background-color: rgba(255, 255, 255, 0.05);
border-radius: 4px;
color: var(--sidebar-text-white);
text-decoration: none;
transition: background-color 0.2s;
&:hover {
background-color: rgba(255, 255, 255, 0.1);
}
i {
color: #ff5a5f;
margin-right: 0.75rem;
}
}
}
}
}
}

View File

@@ -1,9 +1,10 @@
import { Component, Input, inject, Output, EventEmitter, OnInit, OnDestroy } from '@angular/core';
import { Component, Input, inject, Output, EventEmitter, OnInit, OnDestroy, signal } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Router, RouterLink, NavigationEnd } from '@angular/router';
import { ButtonModule } from 'primeng/button';
import { filter } from 'rxjs/operators';
import { Subscription } from 'rxjs';
import { DrawerModule } from 'primeng/drawer';
import { filter, debounceTime } from 'rxjs/operators';
import { Subscription, fromEvent } from 'rxjs';
import { trigger, state, style, transition, animate, query, stagger } from '@angular/animations';
interface NavigationItem {
@@ -32,10 +33,14 @@ interface RouteMapping {
imports: [
CommonModule,
RouterLink,
ButtonModule
ButtonModule,
DrawerModule
],
templateUrl: './sidebar-content.component.html',
styleUrl: './sidebar-content.component.scss',
host: {
'[class.mobile-variant]': 'isMobile'
},
animations: [
trigger('staggerItems', [
transition(':enter', [
@@ -67,7 +72,12 @@ interface RouteMapping {
})
export class SidebarContentComponent implements OnInit, OnDestroy {
@Input() isMobile = false;
@Input() enableMobileDrawer = false;
@Output() navItemClicked = new EventEmitter<void>();
@Output() mobileDrawerVisibilityChange = new EventEmitter<boolean>();
// Mobile drawer state
mobileSidebarVisible = signal<boolean>(false);
// Inject router for active route styling
public router = inject(Router);
@@ -90,6 +100,7 @@ export class SidebarContentComponent implements OnInit, OnDestroy {
// Route synchronization properties
private routerSubscription?: Subscription;
private resizeSubscription?: Subscription;
private routeMappings: RouteMapping[] = [
// Dashboard
{ route: '/dashboard', navigationPath: ['dashboard'] },
@@ -123,10 +134,31 @@ export class SidebarContentComponent implements OnInit, OnDestroy {
setTimeout(() => {
this.initializeNavigation();
}, 100);
// Listen for window resize events to auto-hide mobile drawer
this.setupWindowResizeListener();
}
ngOnDestroy(): void {
this.routerSubscription?.unsubscribe();
this.resizeSubscription?.unsubscribe();
}
/**
* Setup window resize listener to auto-hide mobile drawer on larger screens
*/
private setupWindowResizeListener(): void {
// Define the mobile breakpoint (should match CSS media query)
const MOBILE_BREAKPOINT = 991;
this.resizeSubscription = fromEvent(window, 'resize')
.pipe(
debounceTime(150), // Debounce resize events for better performance
filter(() => window.innerWidth > MOBILE_BREAKPOINT && this.mobileSidebarVisible())
)
.subscribe(() => {
this.mobileSidebarVisible.set(false);
});
}
/**
@@ -481,5 +513,38 @@ export class SidebarContentComponent implements OnInit, OnDestroy {
if (this.isMobile) {
this.navItemClicked.emit();
}
// Close mobile drawer when nav item is clicked
if (this.mobileSidebarVisible()) {
this.mobileSidebarVisible.set(false);
}
}
/**
* Show mobile drawer
*/
showMobileDrawer(): void {
this.mobileSidebarVisible.set(true);
}
/**
* Hide mobile drawer
*/
hideMobileDrawer(): void {
this.mobileSidebarVisible.set(false);
}
/**
* Toggle mobile drawer visibility
*/
toggleMobileDrawer(): void {
this.mobileSidebarVisible.update(visible => !visible);
}
/**
* Handle mobile drawer visibility change
*/
onMobileDrawerVisibilityChange(visible: boolean): void {
this.mobileSidebarVisible.set(visible);
this.mobileDrawerVisibilityChange.emit(visible);
}
}

View File

@@ -43,6 +43,50 @@
--accent-purple-800: #4527A0;
--accent-purple-900: #311B92;
/* Sidebar Layout Variables */
--sidebar-width: 270px; /* Single source for both desktop/mobile */
--sidebar-width-collapsed: 70px;
--sidebar-height: 100vh;
--sidebar-z-index: 1000;
/* Sidebar Visual Variables */
--sidebar-background: linear-gradient(135deg, #2c1a47 0%, #1a0e29 50%, #1e1230 75%, rgba(30, 18, 48, 0.95) 100%);
--sidebar-border: 1px solid rgba(142, 68, 173, 0.15);
--sidebar-shadow: 2px 0 15px rgba(0, 0, 0, 0.3);
--sidebar-backdrop-filter: blur(5px);
/* Sidebar Top Line */
--sidebar-top-line-height: 1px;
--sidebar-top-line-background: linear-gradient(90deg, rgba(142, 68, 173, 0.6), rgba(103, 58, 183, 0.6));
--sidebar-top-line-shadow: 0 0 8px rgba(142, 68, 173, 0.4);
/* Logo Variables */
--sidebar-logo-size: 50px;
--sidebar-logo-background: rgba(142, 68, 173, 0.1);
--sidebar-logo-border: 1px solid rgba(142, 68, 173, 0.3);
--sidebar-logo-icon-size: 30px;
/* Navigation Variables */
--sidebar-nav-padding: 1rem 0;
--sidebar-nav-item-padding: 10px 20px;
--sidebar-nav-icon-wrapper-size: 48px;
--sidebar-nav-icon-size: 22px;
--sidebar-nav-border-radius: 0 6px 6px 0;
--sidebar-nav-hover-transform: translateX(4px);
/* Text & Color Variables */
--sidebar-text-white: #ffffff;
--sidebar-text-gradient: linear-gradient(to right, #fff, #bbb);
--sidebar-text-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
--sidebar-logo-glow: 0 0 10px 6px rgba(89, 16, 185, 0.5);
--sidebar-heart-color: rgb(147, 0, 255);
--sidebar-heart-shadow: 0 0 6px rgba(239, 68, 68, 0.7), 0 0 12px rgba(239, 68, 68, 0.5);
/* Animation Variables */
--sidebar-transition-duration: 0.3s;
--sidebar-transition-easing: cubic-bezier(0.4, 0.0, 0.2, 1);
--sidebar-animation-easing: ease-in-out;
/* Standard color palette for logs and events */
--yellow-50: #fffde7;
--yellow-100: #fff9c4;
@@ -258,129 +302,7 @@ a {
}
}
/* PrimeNG Sidebar Override */
.p-sidebar.mobile-sidebar {
background-color: #1a1e27 !important;
border: none !important;
.p-sidebar-header {
display: none !important;
}
.p-sidebar-content {
padding: 0 !important;
background-color: #1a1e27 !important;
color: #ffffff !important;
.sidebar-header {
padding: 1.5rem 1rem;
display: flex;
align-items: center;
justify-content: center;
.sidebar-logo {
display: flex;
align-items: center;
.sidebar-icon {
font-size: 1.5rem;
color: #ffcc00;
}
.app-name {
font-size: 1.25rem;
font-weight: 700;
margin-left: 0.5rem;
color: #ffffff;
}
}
}
.sidebar-nav {
padding: 1rem 0;
overflow-y: auto;
flex: 1;
.sidebar-section-title {
padding: 0 1rem;
margin: 0 0 0.5rem 0;
font-size: 0.75rem;
font-weight: 600;
color: rgba(255, 255, 255, 0.5);
letter-spacing: 0.5px;
}
ul {
list-style: none;
padding: 0;
margin: 0;
li {
a {
display: flex;
align-items: center;
padding: 0.75rem 1rem;
color: rgba(255, 255, 255, 0.8);
text-decoration: none;
transition: background-color 0.2s;
&:hover {
background-color: rgba(255, 255, 255, 0.05);
}
i {
font-size: 1.25rem;
width: 1.5rem;
margin-right: 0.75rem;
text-align: center;
}
span {
font-size: 0.95rem;
}
}
&.active a {
background-color: rgba(255, 255, 255, 0.1);
color: #ffffff;
font-weight: 600;
border-left: 3px solid #4299e1;
padding-left: calc(1rem - 3px);
i {
color: #4299e1;
}
}
}
}
}
.sponsor-section {
padding: 0.5rem 1rem 1rem;
margin-top: auto;
.sponsor-link {
display: flex;
align-items: center;
padding: 0.75rem 1rem;
background-color: rgba(255, 255, 255, 0.05);
border-radius: 4px;
color: #ffffff;
text-decoration: none;
transition: background-color 0.2s;
&:hover {
background-color: rgba(255, 255, 255, 0.1);
}
i {
color: #ff5a5f;
margin-right: 0.75rem;
}
}
}
}
}
/* PrimeNG Sidebar Override - moved to sidebar-content component */
/* Dark purple theme overrides */
.dark-mode {