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',
imports: [ButtonModule, StyleClassModule, AppConfigurator],
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" />
<div class="relative">
<p-button
@@ -17,7 +17,7 @@ import { LayoutService } from '@/src/service/layout/layout.service';
enterFromClass="hidden"
enterActiveClass="animate-scalein"
leaveToClass="hidden"
leaveActiveClass="animate-scalein"
leaveActiveClass="animate-fadeout"
[hideOnOutsideClick]="true"
type="button"
rounded

View File

@@ -4,7 +4,6 @@ import { FormsModule } from '@angular/forms';
import { $t, updatePreset, updateSurfacePalette } from '@primeng/themes';
import Aura from '@primeng/themes/aura';
import Lara from '@primeng/themes/lara';
import { ButtonModule } from 'primeng/button';
import { PrimeNG } from 'primeng/config';
import { SelectButtonModule } from 'primeng/selectbutton';
import { LayoutService } from '@/src/service/layout/layout.service';
@@ -17,55 +16,55 @@ const presets = {
@Component({
selector: 'app-configurator',
standalone: true,
imports: [CommonModule, FormsModule, ButtonModule, SelectButtonModule],
imports: [CommonModule, FormsModule, SelectButtonModule],
template: `
<div class="flex flex-col gap-4">
<div>
<span class="text-sm text-muted-color font-semibold">Primary</span>
<div class="pt-2 flex gap-2 flex-wrap justify-between">
@for (primaryColor of primaryColors(); track primaryColor.name) {
<button
type="button"
[title]="primaryColor.name"
(click)="updateColors($event, 'primary', primaryColor)"
[ngClass]="{ 'outline-primary': primaryColor.name === selectedPrimaryColor() }"
class="border-none w-5 h-5 rounded-full p-0 cursor-pointer outline-none outline-offset-1"
[style]="{
'background-color': primaryColor.name === 'noir' ? 'var(--text-color)' : primaryColor?.palette['500']
}"
></button>
}
</div>
</div>
<div>
<span class="text-sm text-muted-color font-semibold">Surface</span>
<div class="pt-2 flex gap-2 flex-wrap justify-between">
@for (surface of surfaces; track surface.name) {
<button
type="button"
[title]="surface.name"
(click)="updateColors($event, 'surface', surface)"
[ngClass]="{ 'outline-primary': selectedSurfaceColor() ? selectedSurfaceColor() === surface.name : layoutService.layoutConfig().darkTheme ? surface.name === 'zinc' : surface.name === 'slate' }"
class="border-none w-5 h-5 rounded-full p-0 cursor-pointer outline-none outline-offset-1"
[style]="{
'background-color': surface.name === 'noir' ? 'var(--text-color)' : surface?.palette['500']
}"
></button>
}
</div>
</div>
<div class="flex flex-col gap-2">
<span class="text-sm text-muted-color font-semibold">Presets</span>
<p-selectbutton [options]="presets" [ngModel]="selectedPreset()" (ngModelChange)="onPresetChange($event)" [allowEmpty]="false" />
</div>
<div class="flex flex-col gap-2">
<span class="text-sm text-muted-color font-semibold">Menu Mode</span>
<p-selectbutton [ngModel]="menuMode()" (ngModelChange)="onMenuModeChange($event)" [options]="menuModeOptions" [allowEmpty]="false" />
<div class="flex flex-col gap-4">
<div>
<span class="text-sm text-muted-color font-semibold">Primary</span>
<div class="pt-2 flex gap-2 flex-wrap justify-between">
@for (primaryColor of primaryColors(); track primaryColor.name) {
<button
type="button"
[title]="primaryColor.name"
(click)="updateColors($event, 'primary', primaryColor)"
[ngClass]="{ 'outline-primary': primaryColor.name === selectedPrimaryColor() }"
class="border-none w-5 h-5 rounded-full p-0 cursor-pointer outline-none outline-offset-1"
[style]="{
'background-color': primaryColor.name === 'noir' ? 'var(--text-color)' : primaryColor?.palette['500']
}"
></button>
}
</div>
</div>
<div>
<span class="text-sm text-muted-color font-semibold">Surface</span>
<div class="pt-2 flex gap-2 flex-wrap justify-between">
@for (surface of surfaces; track surface.name) {
<button
type="button"
[title]="surface.name"
(click)="updateColors($event, 'surface', surface)"
[ngClass]="{ 'outline-primary': selectedSurfaceColor() ? selectedSurfaceColor() === surface.name : layoutService.layoutConfig().darkTheme ? surface.name === 'zinc' : surface.name === 'slate' }"
class="border-none w-5 h-5 rounded-full p-0 cursor-pointer outline-none outline-offset-1"
[style]="{
'background-color': surface.name === 'noir' ? 'var(--text-color)' : surface?.palette['500']
}"
></button>
}
</div>
</div>
<div class="flex flex-col gap-2">
<span class="text-sm text-muted-color font-semibold">Presets</span>
<p-selectbutton [options]="presets" [ngModel]="selectedPreset()" (ngModelChange)="onPresetChange($event)" [allowEmpty]="false" size="small" />
</div>
<div class="flex flex-col gap-2">
<span class="text-sm text-muted-color font-semibold">Menu Mode</span>
<p-selectbutton [ngModel]="menuMode()" (ngModelChange)="onMenuModeChange($event)" [options]="menuModeOptions" [allowEmpty]="false" size="small" />
</div>
</div>
`,
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 {

View File

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

View File

@@ -2,7 +2,7 @@ import { Injectable, effect, signal, computed } from '@angular/core';
import { Subject } from 'rxjs';
export interface layoutConfig {
preset?: string,
preset?: string;
primary?: string;
surface?: string;
darkTheme?: boolean;
@@ -23,7 +23,7 @@ interface MenuChangeEvent {
}
@Injectable({
providedIn: 'root',
providedIn: 'root'
})
export class LayoutService {
_config: layoutConfig = {
@@ -39,12 +39,12 @@ export class LayoutService {
overlayMenuActive: false,
configSidebarVisible: false,
staticMenuMobileActive: false,
menuHoverActive: false,
menuHoverActive: false
};
layoutConfig = signal<layoutConfig>(this._config);
layoutState = signal<LayoutState>(this._state)
layoutState = signal<LayoutState>(this._state);
private configUpdate = new Subject<layoutConfig>();
@@ -62,7 +62,7 @@ export class LayoutService {
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);
@@ -72,7 +72,7 @@ export class LayoutService {
getSurface = computed(() => this.layoutConfig().surface);
isOverlay = computed(() => this.layoutConfig().menuMode === 'overlay')
isOverlay = computed(() => this.layoutConfig().menuMode === 'overlay');
transitionComplete = signal<boolean>(false);
@@ -81,7 +81,7 @@ export class LayoutService {
constructor() {
effect(() => {
const config = this.layoutConfig();
if(config) {
if (config) {
this.onConfigUpdate();
}
});
@@ -95,7 +95,7 @@ export class LayoutService {
}
this.handleDarkModeTransition(config);
})
});
}
private handleDarkModeTransition(config: layoutConfig): void {
@@ -112,11 +112,15 @@ export class LayoutService {
this.toggleDarkMode(config);
});
transition.ready.then(() => this.onTransitionEnd());
transition.ready
.then(() => {
this.onTransitionEnd();
})
.catch(() => {});
}
toggleDarkMode(config?: layoutConfig): void {
const _config = config || this.layoutConfig()
const _config = config || this.layoutConfig();
if (_config.darkTheme) {
document.documentElement.classList.add('app-dark');
} else {
@@ -131,10 +135,9 @@ export class LayoutService {
});
}
onMenuToggle() {
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) {
this.overlayOpen.next(null);
@@ -142,9 +145,9 @@ export class LayoutService {
}
if (this.isDesktop()) {
this.layoutState.update((prev) => ({...prev, staticMenuDesktopInactive: !this.layoutState().staticMenuDesktopInactive}));
this.layoutState.update((prev) => ({ ...prev, staticMenuDesktopInactive: !this.layoutState().staticMenuDesktopInactive }));
} else {
this.layoutState.update((prev) => ({...prev, staticMenuMobileActive: !this.layoutState().staticMenuMobileActive}));
this.layoutState.update((prev) => ({ ...prev, staticMenuMobileActive: !this.layoutState().staticMenuMobileActive }));
if (this.layoutState().staticMenuMobileActive) {
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.
Eget egestas purus viverra accumsan in nisl nisi. Suscipit adipiscing bibendum est ultricies integer. Mattis aliquam faucibus purus in massa tempor nec.
</p>
<p-scrolltop target="parent" [threshold]="100" icon="pi pi-arrow-up"></p-scrolltop>
</p-scrollpanel>
</div>
</div>