try sidebar #2

This commit is contained in:
Flaminel
2025-05-25 19:23:00 +03:00
parent 2143c74767
commit 8b7e39fa86
6 changed files with 365 additions and 196 deletions

View File

@@ -5,76 +5,11 @@
<div class="sidebar" [class.collapsed]="isSidebarCollapsed()">
<div class="sidebar-top-line"></div>
<!-- Logo Container -->
<div class="logo-container">
<div class="logo logo-large">
<i class="pi pi-check-circle"></i>
</div>
<div class="logo logo-small">
<i class="pi pi-check"></i>
</div>
<h1>Cleanuparr</h1>
</div>
<!-- Sidebar Navigation -->
<nav class="nav-menu">
<!-- Project Sponsors Link -->
<a href="https://github.com/sponsors/username" class="nav-item sponsor-link" target="_blank" rel="noopener noreferrer">
<div class="nav-icon-wrapper heart-icon">
<i class="pi pi-heart"></i>
</div>
<span>Become A Sponsor</span>
</a>
<!-- Core Navigation Group -->
<div class="nav-group" style="margin-top: -10px;">
<div class="nav-group-title">Core</div>
<a [routerLink]="['/dashboard']" class="nav-item" [class.active]="router.url.includes('/dashboard')">
<div class="nav-icon-wrapper">
<i class="pi pi-home"></i>
</div>
<span>Dashboard</span>
</a>
<a [routerLink]="['/logs']" class="nav-item" [class.active]="router.url.includes('/logs')">
<div class="nav-icon-wrapper">
<i class="pi pi-list"></i>
</div>
<span>Logs</span>
</a>
<a [routerLink]="['/settings']" class="nav-item" [class.active]="router.url.includes('/settings')">
<div class="nav-icon-wrapper">
<i class="pi pi-cog"></i>
</div>
<span>Settings</span>
</a>
</div>
<!-- Features Group -->
<div class="nav-group">
<div class="nav-group-title">Features</div>
<ng-container *ngFor="let item of menuItems">
<ng-container *ngIf="!['Dashboard', 'Logs', 'Settings'].includes(item.label)">
<a [routerLink]="item.route" class="nav-item" [class.active]="router.url.includes(item.route)">
<div class="nav-icon-wrapper">
<i [class]="item.icon"></i>
</div>
<span>{{ item.label }}</span>
</a>
</ng-container>
</ng-container>
</div>
<!-- Resources Group -->
<div class="nav-group">
<div class="nav-group-title">Resources</div>
<a href="https://github.com/username/cleanuparr" class="nav-item" target="_blank" rel="noopener noreferrer">
<div class="nav-icon-wrapper">
<i class="pi pi-github"></i>
</div>
<span>GitHub Repository</span>
</a>
</div>
</nav>
<!-- Shared Sidebar Content -->
<app-sidebar-content
[menuItems]="menuItems"
[isMobile]="false">
</app-sidebar-content>
<!-- Sidebar Footer (Toggle button) -->
<div class="sidebar-footer">
@@ -133,83 +68,23 @@
</div>
</div>
<!-- Mobile Sidebar -->
<p-sidebar styleClass="mobile-sidebar"
[visible]="mobileSidebarVisible()"
(visibleChange)="mobileSidebarVisible.set($event)"
[dismissible]="true"
[showCloseIcon]="false"
position="left">
<!-- Mobile Sidebar using PrimeNG sidebar component -->
<p-sidebar
styleClass="mobile-sidebar"
[visible]="mobileSidebarVisible()"
(visibleChange)="mobileSidebarVisible.set($event)"
[dismissible]="true"
[showCloseIcon]="false"
position="left">
<div class="mobile-sidebar-content">
<div class="sidebar-top-line"></div>
<!-- Logo Container -->
<div class="logo-container">
<div class="logo logo-small">
<i class="pi pi-check"></i>
</div>
<h1>Cleanuparr</h1>
</div>
<!-- Mobile Sidebar Navigation -->
<nav class="nav-menu">
<!-- Project Sponsors Link -->
<a href="https://github.com/sponsors/username" class="nav-item sponsor-link" target="_blank" rel="noopener noreferrer">
<div class="nav-icon-wrapper heart-icon">
<i class="pi pi-heart"></i>
</div>
<span>Become A Sponsor</span>
</a>
<!-- Core Navigation Group -->
<div class="nav-group" style="margin-top: -10px;">
<div class="nav-group-title">Core</div>
<a [routerLink]="['/dashboard']" class="nav-item" [class.active]="router.url.includes('/dashboard')" (click)="mobileSidebarVisible.set(false)">
<div class="nav-icon-wrapper">
<i class="pi pi-home"></i>
</div>
<span>Dashboard</span>
</a>
<a [routerLink]="['/logs']" class="nav-item" [class.active]="router.url.includes('/logs')" (click)="mobileSidebarVisible.set(false)">
<div class="nav-icon-wrapper">
<i class="pi pi-list"></i>
</div>
<span>Logs</span>
</a>
<a [routerLink]="['/settings']" class="nav-item" [class.active]="router.url.includes('/settings')" (click)="mobileSidebarVisible.set(false)">
<div class="nav-icon-wrapper">
<i class="pi pi-cog"></i>
</div>
<span>Settings</span>
</a>
</div>
<!-- Features Group -->
<div class="nav-group">
<div class="nav-group-title">Features</div>
<ng-container *ngFor="let item of menuItems">
<ng-container *ngIf="!['Dashboard', 'Logs', 'Settings'].includes(item.label)">
<a [routerLink]="item.route" class="nav-item" [class.active]="router.url.includes(item.route)" (click)="mobileSidebarVisible.set(false)">
<div class="nav-icon-wrapper">
<i [class]="item.icon"></i>
</div>
<span>{{ item.label }}</span>
</a>
</ng-container>
</ng-container>
</div>
<!-- Resources Group -->
<div class="nav-group">
<div class="nav-group-title">Resources</div>
<a href="https://github.com/username/cleanuparr" class="nav-item" target="_blank" rel="noopener noreferrer">
<div class="nav-icon-wrapper">
<i class="pi pi-github"></i>
</div>
<span>GitHub Repository</span>
</a>
</div>
</nav>
<!-- Shared Sidebar Content for Mobile -->
<app-sidebar-content
[menuItems]="menuItems"
[isMobile]="true"
(navItemClicked)="mobileSidebarVisible.set(false)">
</app-sidebar-content>
</div>
</p-sidebar>
</div>

View File

@@ -28,6 +28,18 @@
overflow-y: auto;
z-index: 10;
// Top color line
.sidebar-top-line {
height: 3px;
background: linear-gradient(to right, #673ab7, #9b59b6);
margin-bottom: 10px;
}
// Hide on mobile screens
@media (max-width: 768px) {
display: none;
}
// Collapsed state
&.collapsed {
width: 70px;
@@ -1286,75 +1298,51 @@
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: 1px;
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);
}
.logo-container {
display: flex;
align-items: center;
padding: 15px 20px 10px;
border-bottom: none;
position: relative;
.logo {
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
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;
i {
font-size: 20px;
color: #ffffff;
}
}
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;
}
}
}
/* Media queries for responsiveness */
@media screen and (max-width: 768px) {
.layout-main {
flex-direction: column;
}
.layout-content {
width: 100%;
.layout-content-inner {
padding: 1rem;
}
.top-bar {
.topbar-section {
&.center {
display: none; // Hide version bar on mobile to save space
}
}
}
}
// Optimize the PrimeNG sidebar for mobile
// Style PrimeNG mobile sidebar
:host ::ng-deep {
.p-sidebar.p-component.mobile-sidebar {
background: linear-gradient(135deg, #2c1a47 0%, #1a0e29 50%, #1e1230 75%, rgba(30, 18, 48, 0.95) 100%) !important;
border: none !important;
box-shadow: -5px 0 20px rgba(0, 0, 0, 0.5) !important;
.p-sidebar.mobile-sidebar {
.p-sidebar-header {
display: none; // Hide the header since we're not using it
}
.p-sidebar-content {
padding: 0 !important;
padding: 0;
}
}
}

View File

@@ -1,5 +1,5 @@
import { Component, inject, signal } from '@angular/core';
import { Router, RouterOutlet, RouterLink } from '@angular/router';
import { Router, RouterOutlet } from '@angular/router';
import { CommonModule } from '@angular/common';
import { Title } from '@angular/platform-browser';
@@ -12,6 +12,9 @@ import { SidebarModule } from 'primeng/sidebar';
import { DividerModule } from 'primeng/divider';
import { RippleModule } from 'primeng/ripple';
// Custom Components
import { SidebarContentComponent } from '../sidebar-content/sidebar-content.component';
interface MenuItem {
label: string;
icon: string;
@@ -25,14 +28,14 @@ interface MenuItem {
imports: [
CommonModule,
RouterOutlet,
RouterLink,
ButtonModule,
ToolbarModule,
FormsModule,
MenuModule,
SidebarModule,
DividerModule,
RippleModule
RippleModule,
SidebarContentComponent
],
templateUrl: './main-layout.component.html',
styleUrl: './main-layout.component.scss'
@@ -56,6 +59,16 @@ export class MainLayoutComponent {
constructor() {}
/**
* Handles mobile navigation click events by closing the sidebar
*/
onMobileNavClick(): void {
// Only close sidebar on mobile view
if (window.innerWidth <= 768) {
this.mobileSidebarVisible.set(false);
}
}
/**
* Get the current page title based on the active route
*/
@@ -73,10 +86,16 @@ export class MainLayoutComponent {
}
}
/**
* Toggle mobile sidebar visibility
*/
toggleMobileSidebar(): void {
this.mobileSidebarVisible.update(value => !value);
}
/**
* Toggle desktop sidebar collapsed state
*/
toggleSidebar(): void {
this.isSidebarCollapsed.update(value => !value);
}

View File

@@ -0,0 +1,70 @@
<!-- Logo Container -->
<div class="logo-container">
<div class="logo logo-large">
<i class="pi pi-check-circle"></i>
</div>
<div class="logo logo-small">
<i class="pi pi-check"></i>
</div>
<h1>Cleanuparr</h1>
</div>
<!-- Sidebar Navigation -->
<nav class="nav-menu">
<!-- Project Sponsors Link -->
<a href="https://github.com/sponsors/username" class="nav-item sponsor-link" target="_blank" rel="noopener noreferrer">
<div class="nav-icon-wrapper heart-icon">
<i class="pi pi-heart"></i>
</div>
<span>Become A Sponsor</span>
</a>
<!-- Core Navigation Group -->
<div class="nav-group" style="margin-top: -10px;">
<div class="nav-group-title">Core</div>
<a [routerLink]="['/dashboard']" class="nav-item" [class.active]="router.url.includes('/dashboard')" (click)="onNavItemClick()">
<div class="nav-icon-wrapper">
<i class="pi pi-home"></i>
</div>
<span>Dashboard</span>
</a>
<a [routerLink]="['/logs']" class="nav-item" [class.active]="router.url.includes('/logs')" (click)="onNavItemClick()">
<div class="nav-icon-wrapper">
<i class="pi pi-list"></i>
</div>
<span>Logs</span>
</a>
<a [routerLink]="['/settings']" class="nav-item" [class.active]="router.url.includes('/settings')" (click)="onNavItemClick()">
<div class="nav-icon-wrapper">
<i class="pi pi-cog"></i>
</div>
<span>Settings</span>
</a>
</div>
<!-- Features Group -->
<div class="nav-group">
<div class="nav-group-title">Features</div>
<ng-container *ngFor="let item of menuItems">
<ng-container *ngIf="!['Dashboard', 'Logs', 'Settings'].includes(item.label)">
<a [routerLink]="item.route" class="nav-item" [class.active]="router.url.includes(item.route)" (click)="onNavItemClick()">
<div class="nav-icon-wrapper">
<i [class]="item.icon"></i>
</div>
<span>{{ item.label }}</span>
</a>
</ng-container>
</ng-container>
</div>
<!-- Resources Group -->
<div class="nav-group">
<div class="nav-group-title">Resources</div>
<a href="https://github.com/username/cleanuparr" class="nav-item" target="_blank" rel="noopener noreferrer">
<div class="nav-icon-wrapper">
<i class="pi pi-github"></i>
</div>
<span>GitHub Repository</span>
</a>
</div>
</nav>

View File

@@ -0,0 +1,177 @@
// 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.05);
.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% {
transform: scale(1);
opacity: 0.7;
}
50% {
transform: scale(1.2);
opacity: 1;
}
100% {
transform: scale(1);
opacity: 0.7;
}
}

View File

@@ -0,0 +1,40 @@
import { Component, Input, inject, Output, EventEmitter } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Router, RouterLink } from '@angular/router';
import { ButtonModule } from 'primeng/button';
interface MenuItem {
label: string;
icon: string;
route: string;
badge?: string;
}
@Component({
selector: 'app-sidebar-content',
standalone: true,
imports: [
CommonModule,
RouterLink,
ButtonModule
],
templateUrl: './sidebar-content.component.html',
styleUrl: './sidebar-content.component.scss'
})
export class SidebarContentComponent {
@Input() menuItems: MenuItem[] = [];
@Input() isMobile = false;
@Output() navItemClicked = new EventEmitter<void>();
// Inject router for active route styling
public router = inject(Router);
/**
* Handle navigation item click
*/
onNavItemClick(): void {
if (this.isMobile) {
this.navItemClicked.emit();
}
}
}