Merge remote-tracking branch 'origin/v19' into v19

# Conflicts:
#	src/layout/appconfigurator.ts
#	src/layout/applayout.ts
This commit is contained in:
Çetin
2025-01-07 10:53:30 +03:00
6 changed files with 97 additions and 136 deletions

View File

@@ -158,45 +158,3 @@
} }
} }
} }
.config-panel {
.config-panel-label {
font-size: 0.875rem;
color: var(--text-secondary-color);
font-weight: 600;
line-height: 1;
}
.config-panel-colors {
> div {
padding-top: 0.5rem;
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
justify-content: space-between;
button {
border: none;
width: 1.25rem;
height: 1.25rem;
border-radius: 50%;
padding: 0;
cursor: pointer;
outline-color: transparent;
outline-width: 2px;
outline-style: solid;
outline-offset: 1px;
&.active-color {
outline-color: var(--primary-color);
}
}
}
}
.config-panel-settings {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
}

View File

@@ -8,7 +8,7 @@ import { LayoutService } from '@/src/service/layout/layout.service';
selector: 'floating-configurator', selector: 'floating-configurator',
imports: [ButtonModule, StyleClassModule, AppConfigurator], imports: [ButtonModule, StyleClassModule, AppConfigurator],
template: ` template: `
<div class="fixed flex gap-6 top-8 right-8"> <div class="fixed flex gap-4 top-8 right-8">
<p-button type="button" (onClick)="toggleDarkMode()" [rounded]="true" [icon]="isDarkTheme() ? 'pi pi-moon' : 'pi pi-sun'" severity="secondary" /> <p-button type="button" (onClick)="toggleDarkMode()" [rounded]="true" [icon]="isDarkTheme() ? 'pi pi-moon' : 'pi pi-sun'" severity="secondary" />
<div class="relative"> <div class="relative">
<p-button <p-button
@@ -17,7 +17,7 @@ import { LayoutService } from '@/src/service/layout/layout.service';
enterFromClass="hidden" enterFromClass="hidden"
enterActiveClass="animate-scalein" enterActiveClass="animate-scalein"
leaveToClass="hidden" leaveToClass="hidden"
leaveActiveClass="animate-scalein" leaveActiveClass="animate-fadeout"
[hideOnOutsideClick]="true" [hideOnOutsideClick]="true"
type="button" type="button"
rounded rounded

View File

@@ -4,7 +4,6 @@ import { FormsModule } from '@angular/forms';
import { $t, updatePreset, updateSurfacePalette } from '@primeng/themes'; import { $t, updatePreset, updateSurfacePalette } from '@primeng/themes';
import Aura from '@primeng/themes/aura'; import Aura from '@primeng/themes/aura';
import Lara from '@primeng/themes/lara'; import Lara from '@primeng/themes/lara';
import { ButtonModule } from 'primeng/button';
import { PrimeNG } from 'primeng/config'; import { PrimeNG } from 'primeng/config';
import { SelectButtonModule } from 'primeng/selectbutton'; import { SelectButtonModule } from 'primeng/selectbutton';
import { LayoutService } from '@/src/service/layout/layout.service'; import { LayoutService } from '@/src/service/layout/layout.service';
@@ -17,7 +16,7 @@ const presets = {
@Component({ @Component({
selector: 'app-configurator', selector: 'app-configurator',
standalone: true, standalone: true,
imports: [CommonModule, FormsModule, ButtonModule, SelectButtonModule], imports: [CommonModule, FormsModule, SelectButtonModule],
template: ` template: `
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<div> <div>
@@ -56,16 +55,16 @@ const presets = {
</div> </div>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<span class="text-sm text-muted-color font-semibold">Presets</span> <span class="text-sm text-muted-color font-semibold">Presets</span>
<p-selectbutton [options]="presets" [ngModel]="selectedPreset()" (ngModelChange)="onPresetChange($event)" [allowEmpty]="false" /> <p-selectbutton [options]="presets" [ngModel]="selectedPreset()" (ngModelChange)="onPresetChange($event)" [allowEmpty]="false" size="small" />
</div> </div>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<span class="text-sm text-muted-color font-semibold">Menu Mode</span> <span class="text-sm text-muted-color font-semibold">Menu Mode</span>
<p-selectbutton [ngModel]="menuMode()" (ngModelChange)="onMenuModeChange($event)" [options]="menuModeOptions" [allowEmpty]="false" /> <p-selectbutton [ngModel]="menuMode()" (ngModelChange)="onMenuModeChange($event)" [options]="menuModeOptions" [allowEmpty]="false" size="small" />
</div> </div>
</div> </div>
`, `,
host: { host: {
class: 'hidden absolute top-[3.25rem] right-0 w-64 p-4 bg-surface-0 dark:bg-surface-900 border border-surface rounded-border origin-top shadow-[0px_3px_5px_rgba(0,0,0,0.02),0px_0px_2px_rgba(0,0,0,0.05),0px_1px_4px_rgba(0,0,0,0.08)]', class: 'hidden absolute top-[3.25rem] right-0 w-64 p-4 bg-surface-0 dark:bg-surface-900 border border-surface rounded-border origin-top shadow-[0px_3px_5px_rgba(0,0,0,0.02),0px_0px_2px_rgba(0,0,0,0.05),0px_1px_4px_rgba(0,0,0,0.08)]'
} }
}) })
export class AppConfigurator { export class AppConfigurator {

View File

@@ -4,21 +4,15 @@ import { CommonModule } from '@angular/common';
import { AppTopBar } from '@/src/layout/apptopbar'; import { AppTopBar } from '@/src/layout/apptopbar';
import { AppSidebar } from '@/src/layout/appsidebar'; import { AppSidebar } from '@/src/layout/appsidebar';
import { NavigationEnd, Router, RouterModule } from '@angular/router'; import { NavigationEnd, Router, RouterModule } from '@angular/router';
import { AppConfigurator } from '@/src/layout/appconfigurator';
import { AppFooter } from '@/src/layout/appfooter'; import { AppFooter } from '@/src/layout/appfooter';
import { filter, Subscription } from 'rxjs'; import { filter, Subscription } from 'rxjs';
import { LayoutService } from '@/src/service/layout/layout.service'; import { LayoutService } from '@/src/service/layout/layout.service';
@Component({ @Component({
selector: 'app-layout', selector: 'app-layout',
standalone:true, standalone: true,
imports: [ imports: [CommonModule, ToastModule, AppTopBar, AppSidebar, RouterModule, AppFooter, AppConfigurator],
CommonModule,
ToastModule,
AppTopBar,
AppSidebar,
RouterModule,
AppFooter,
],
template: `<div class="layout-wrapper" [ngClass]="containerClass"> template: `<div class="layout-wrapper" [ngClass]="containerClass">
<app-topbar></app-topbar> <app-topbar></app-topbar>
<div class="layout-sidebar"> <div class="layout-sidebar">
@@ -30,9 +24,9 @@ import { LayoutService } from '@/src/service/layout/layout.service';
</div> </div>
<app-footer></app-footer> <app-footer></app-footer>
</div> </div>
<app-configurator></app-configurator>
<div class="layout-mask animate-fadein"></div> <div class="layout-mask animate-fadein"></div>
</div> </div> `
`,
}) })
export class AppLayout { export class AppLayout {
overlayMenuOpenSubscription: Subscription; overlayMenuOpenSubscription: Subscription;
@@ -45,23 +39,28 @@ export class AppLayout {
@ViewChild(AppTopBar) appTopBar!: AppTopBar; @ViewChild(AppTopBar) appTopBar!: AppTopBar;
constructor(public layoutService: LayoutService, public renderer: Renderer2, public router: Router) { constructor(
public layoutService: LayoutService,
public renderer: Renderer2,
public router: Router
) {
this.overlayMenuOpenSubscription = this.layoutService.overlayOpen$.subscribe(() => { this.overlayMenuOpenSubscription = this.layoutService.overlayOpen$.subscribe(() => {
if (!this.menuOutsideClickListener) { if (!this.menuOutsideClickListener) {
this.menuOutsideClickListener = this.renderer.listen('document', 'click', event => { this.menuOutsideClickListener = this.renderer.listen('document', 'click', (event) => {
const isOutsideClicked = !(this.appSidebar.el.nativeElement.isSameNode(event.target) || this.appSidebar.el.nativeElement.contains(event.target) if (this.isOutsideClicked(event)) {
|| this.appTopBar.menuButton.nativeElement.isSameNode(event.target) || this.appTopBar.menuButton.nativeElement.contains(event.target));
if (isOutsideClicked) {
this.hideMenu(); this.hideMenu();
} }
}); });
} }
if (!this.profileMenuOutsideClickListener) { if (!this.profileMenuOutsideClickListener) {
this.profileMenuOutsideClickListener = this.renderer.listen('document', 'click', event => { this.profileMenuOutsideClickListener = this.renderer.listen('document', 'click', (event) => {
const isOutsideClicked = !(this.appTopBar.menu.nativeElement.isSameNode(event.target) || this.appTopBar.menu.nativeElement.contains(event.target) const isOutsideClicked = !(
|| this.appTopBar.topbarMenuButton.nativeElement.isSameNode(event.target) || this.appTopBar.topbarMenuButton.nativeElement.contains(event.target)); this.appTopBar.menu.nativeElement.isSameNode(event.target) ||
this.appTopBar.menu.nativeElement.contains(event.target) ||
this.appTopBar.topbarMenuButton.nativeElement.isSameNode(event.target) ||
this.appTopBar.topbarMenuButton.nativeElement.contains(event.target)
);
if (isOutsideClicked) { if (isOutsideClicked) {
} }
@@ -73,14 +72,20 @@ export class AppLayout {
} }
}); });
this.router.events.pipe(filter(event => event instanceof NavigationEnd)) this.router.events.pipe(filter((event) => event instanceof NavigationEnd)).subscribe(() => {
.subscribe(() => {
this.hideMenu(); this.hideMenu();
}); });
} }
isOutsideClicked(event) {
const sidebarEl = document.querySelector('.layout-sidebar');
const topbarEl = document.querySelector('.layout-menu-button');
return !(sidebarEl.isSameNode(event.target) || sidebarEl.contains(event.target) || topbarEl.isSameNode(event.target) || topbarEl.contains(event.target));
}
hideMenu() { hideMenu() {
this.layoutService.layoutState.update((prev) => ({...prev, overlayMenuActive:false, staticMenuMobileActive: false, menuHoverActive: false})) this.layoutService.layoutState.update((prev) => ({ ...prev, overlayMenuActive: false, staticMenuMobileActive: false, menuHoverActive: false }));
if (this.menuOutsideClickListener) { if (this.menuOutsideClickListener) {
this.menuOutsideClickListener(); this.menuOutsideClickListener();
this.menuOutsideClickListener = null; this.menuOutsideClickListener = null;
@@ -91,8 +96,7 @@ export class AppLayout {
blockBodyScroll(): void { blockBodyScroll(): void {
if (document.body.classList) { if (document.body.classList) {
document.body.classList.add('blocked-scroll'); document.body.classList.add('blocked-scroll');
} } else {
else {
document.body.className += ' blocked-scroll'; document.body.className += ' blocked-scroll';
} }
} }
@@ -100,10 +104,8 @@ export class AppLayout {
unblockBodyScroll(): void { unblockBodyScroll(): void {
if (document.body.classList) { if (document.body.classList) {
document.body.classList.remove('blocked-scroll'); document.body.classList.remove('blocked-scroll');
} } else {
else { document.body.className = document.body.className.replace(new RegExp('(^|\\b)' + 'blocked-scroll'.split(' ').join('|') + '(\\b|$)', 'gi'), ' ');
document.body.className = document.body.className.replace(new RegExp('(^|\\b)' +
'blocked-scroll'.split(' ').join('|') + '(\\b|$)', 'gi'), ' ');
} }
} }
@@ -114,7 +116,7 @@ export class AppLayout {
'layout-static-inactive': this.layoutService.layoutState().staticMenuDesktopInactive && this.layoutService.layoutConfig().menuMode === 'static', 'layout-static-inactive': this.layoutService.layoutState().staticMenuDesktopInactive && this.layoutService.layoutConfig().menuMode === 'static',
'layout-overlay-active': this.layoutService.layoutState().overlayMenuActive, 'layout-overlay-active': this.layoutService.layoutState().overlayMenuActive,
'layout-mobile-active': this.layoutService.layoutState().staticMenuMobileActive 'layout-mobile-active': this.layoutService.layoutState().staticMenuMobileActive
} };
} }
ngOnDestroy() { ngOnDestroy() {

View File

@@ -2,7 +2,7 @@ import { Injectable, effect, signal, computed } from '@angular/core';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
export interface layoutConfig { export interface layoutConfig {
preset?: string, preset?: string;
primary?: string; primary?: string;
surface?: string; surface?: string;
darkTheme?: boolean; darkTheme?: boolean;
@@ -23,7 +23,7 @@ interface MenuChangeEvent {
} }
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root'
}) })
export class LayoutService { export class LayoutService {
_config: layoutConfig = { _config: layoutConfig = {
@@ -39,12 +39,12 @@ export class LayoutService {
overlayMenuActive: false, overlayMenuActive: false,
configSidebarVisible: false, configSidebarVisible: false,
staticMenuMobileActive: false, staticMenuMobileActive: false,
menuHoverActive: false, menuHoverActive: false
}; };
layoutConfig = signal<layoutConfig>(this._config); layoutConfig = signal<layoutConfig>(this._config);
layoutState = signal<LayoutState>(this._state) layoutState = signal<LayoutState>(this._state);
private configUpdate = new Subject<layoutConfig>(); private configUpdate = new Subject<layoutConfig>();
@@ -62,7 +62,7 @@ export class LayoutService {
overlayOpen$ = this.overlayOpen.asObservable(); overlayOpen$ = this.overlayOpen.asObservable();
theme = computed(() => this.layoutConfig()?.darkTheme ? 'light' : 'dark'); theme = computed(() => (this.layoutConfig()?.darkTheme ? 'light' : 'dark'));
isSidebarActive = computed(() => this.layoutState().overlayMenuActive || this.layoutState().staticMenuMobileActive); isSidebarActive = computed(() => this.layoutState().overlayMenuActive || this.layoutState().staticMenuMobileActive);
@@ -72,7 +72,7 @@ export class LayoutService {
getSurface = computed(() => this.layoutConfig().surface); getSurface = computed(() => this.layoutConfig().surface);
isOverlay = computed(() => this.layoutConfig().menuMode === 'overlay') isOverlay = computed(() => this.layoutConfig().menuMode === 'overlay');
transitionComplete = signal<boolean>(false); transitionComplete = signal<boolean>(false);
@@ -81,7 +81,7 @@ export class LayoutService {
constructor() { constructor() {
effect(() => { effect(() => {
const config = this.layoutConfig(); const config = this.layoutConfig();
if(config) { if (config) {
this.onConfigUpdate(); this.onConfigUpdate();
} }
}); });
@@ -95,7 +95,7 @@ export class LayoutService {
} }
this.handleDarkModeTransition(config); this.handleDarkModeTransition(config);
}) });
} }
private handleDarkModeTransition(config: layoutConfig): void { private handleDarkModeTransition(config: layoutConfig): void {
@@ -112,11 +112,15 @@ export class LayoutService {
this.toggleDarkMode(config); this.toggleDarkMode(config);
}); });
transition.ready.then(() => this.onTransitionEnd()); transition.ready
.then(() => {
this.onTransitionEnd();
})
.catch(() => {});
} }
toggleDarkMode(config?: layoutConfig): void { toggleDarkMode(config?: layoutConfig): void {
const _config = config || this.layoutConfig() const _config = config || this.layoutConfig();
if (_config.darkTheme) { if (_config.darkTheme) {
document.documentElement.classList.add('app-dark'); document.documentElement.classList.add('app-dark');
} else { } else {
@@ -131,10 +135,9 @@ export class LayoutService {
}); });
} }
onMenuToggle() { onMenuToggle() {
if (this.isOverlay()) { if (this.isOverlay()) {
this.layoutState.update((prev) => ({...prev, overlayMenuActive: !this.layoutState().overlayMenuActive})); this.layoutState.update((prev) => ({ ...prev, overlayMenuActive: !this.layoutState().overlayMenuActive }));
if (this.layoutState().overlayMenuActive) { if (this.layoutState().overlayMenuActive) {
this.overlayOpen.next(null); this.overlayOpen.next(null);
@@ -142,9 +145,9 @@ export class LayoutService {
} }
if (this.isDesktop()) { if (this.isDesktop()) {
this.layoutState.update((prev) => ({...prev, staticMenuDesktopInactive: !this.layoutState().staticMenuDesktopInactive})); this.layoutState.update((prev) => ({ ...prev, staticMenuDesktopInactive: !this.layoutState().staticMenuDesktopInactive }));
} else { } else {
this.layoutState.update((prev) => ({...prev, staticMenuMobileActive: !this.layoutState().staticMenuMobileActive})); this.layoutState.update((prev) => ({ ...prev, staticMenuMobileActive: !this.layoutState().staticMenuMobileActive }));
if (this.layoutState().staticMenuMobileActive) { if (this.layoutState().staticMenuMobileActive) {
this.overlayOpen.next(null); this.overlayOpen.next(null);

View File

@@ -100,7 +100,6 @@ import { OverlayBadgeModule } from 'primeng/overlaybadge';
etiam sit amet nisl purus. Cursus sit amet dictum sit amet. Tristique senectus et netus et malesuada fames ac turpis egestas. Et tortor consequat id porta nibh venenatis cras sed. Diam maecenas ultricies mi eget mauris. etiam sit amet nisl purus. Cursus sit amet dictum sit amet. Tristique senectus et netus et malesuada fames ac turpis egestas. Et tortor consequat id porta nibh venenatis cras sed. Diam maecenas ultricies mi eget mauris.
Eget egestas purus viverra accumsan in nisl nisi. Suscipit adipiscing bibendum est ultricies integer. Mattis aliquam faucibus purus in massa tempor nec. Eget egestas purus viverra accumsan in nisl nisi. Suscipit adipiscing bibendum est ultricies integer. Mattis aliquam faucibus purus in massa tempor nec.
</p> </p>
<p-scrolltop target="parent" [threshold]="100" icon="pi pi-arrow-up"></p-scrolltop>
</p-scrollpanel> </p-scrollpanel>
</div> </div>
</div> </div>