Refactor appConfig component, update layoutService and implement Angular signals

This commit is contained in:
Mehmet Çetin
2023-12-22 10:16:50 +03:00
parent 1787a3d6a5
commit 890d69af02
12 changed files with 107 additions and 61 deletions

View File

@@ -1,6 +1,6 @@
{ {
"name": "sakai-ng", "name": "sakai-ng",
"version": "17.0.0", "version": "17.2.0",
"license": "PrimeNG Commercial", "license": "PrimeNG Commercial",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",

View File

@@ -1,6 +1,6 @@
<div class="surface-ground flex align-items-center justify-content-center min-h-screen min-w-screen overflow-hidden"> <div class="surface-ground flex align-items-center justify-content-center min-h-screen min-w-screen overflow-hidden">
<div class="flex flex-column align-items-center justify-content-center"> <div class="flex flex-column align-items-center justify-content-center">
<img src="assets/layout/images/{{layoutService.config.colorScheme === 'light' ? 'logo-dark' : 'logo-white'}}.svg" alt="Sakai logo" class="mb-5 w-6rem flex-shrink-0"> <img src="assets/layout/images/{{layoutService.config().colorScheme === 'light' ? 'logo-dark' : 'logo-white'}}.svg" alt="Sakai logo" class="mb-5 w-6rem flex-shrink-0">
<div style="border-radius:56px; padding:0.3rem; background: linear-gradient(180deg, var(--primary-color) 10%, rgba(33, 150, 243, 0) 30%);"> <div style="border-radius:56px; padding:0.3rem; background: linear-gradient(180deg, var(--primary-color) 10%, rgba(33, 150, 243, 0) 30%);">
<div class="w-full surface-card py-8 px-5 sm:px-8" style="border-radius:53px"> <div class="w-full surface-card py-8 px-5 sm:px-8" style="border-radius:53px">
<div class="text-center mb-5"> <div class="text-center mb-5">

View File

@@ -2,7 +2,7 @@ import { Component, OnInit, OnDestroy } from '@angular/core';
import { MenuItem } from 'primeng/api'; import { MenuItem } from 'primeng/api';
import { Product } from '../../api/product'; import { Product } from '../../api/product';
import { ProductService } from '../../service/product.service'; import { ProductService } from '../../service/product.service';
import { Subscription } from 'rxjs'; import { Subscription, debounceTime } from 'rxjs';
import { LayoutService } from 'src/app/layout/service/app.layout.service'; import { LayoutService } from 'src/app/layout/service/app.layout.service';
@Component({ @Component({
@@ -21,7 +21,9 @@ export class DashboardComponent implements OnInit, OnDestroy {
subscription!: Subscription; subscription!: Subscription;
constructor(private productService: ProductService, public layoutService: LayoutService) { constructor(private productService: ProductService, public layoutService: LayoutService) {
this.subscription = this.layoutService.configUpdate$.subscribe(() => { this.subscription = this.layoutService.configUpdate$
.pipe(debounceTime(25))
.subscribe((config) => {
this.initChart(); this.initChart();
}); });
} }

View File

@@ -43,7 +43,7 @@ Run 'ng help' for more options.</code></pre>
<pre class="app-code"><code>import &#123; Component, OnInit &#125; from '&#64;angular/core'; <pre class="app-code"><code>import &#123; Component, OnInit &#125; from '&#64;angular/core';
import &#123; PrimeNGConfig &#125; from 'primeng/api'; import &#123; PrimeNGConfig &#125; from 'primeng/api';
import &#123; LayoutService &#125; from './layout/service/app.layout.service'; import &#123; LayoutService, AppConfig &#125; from './layout/service/app.layout.service';
&#64;Component(&#123; &#64;Component(&#123;
selector: 'app-root', selector: 'app-root',
@@ -57,7 +57,7 @@ export class AppComponent implements OnInit &#123;
this.primengConfig.ripple = true; //enables core ripple functionality this.primengConfig.ripple = true; //enables core ripple functionality
//optional configuration with the default configuration //optional configuration with the default configuration
this.layoutService.config = &#123; const config: AppConfig = &#123;
ripple: false, //toggles ripple on and off ripple: false, //toggles ripple on and off
inputStyle: 'outlined', //default style for input elements inputStyle: 'outlined', //default style for input elements
menuMode: 'static', //layout mode of the menu, valid values are "static" and "overlay" menuMode: 'static', //layout mode of the menu, valid values are "static" and "overlay"
@@ -65,6 +65,7 @@ export class AppComponent implements OnInit &#123;
theme: 'lara-light-indigo', //default component theme for PrimeNG theme: 'lara-light-indigo', //default component theme for PrimeNG
scale: 14 //size of the body font size to scale the whole application scale: 14 //size of the body font size to scale the whole application
&#125;; &#125;;
this.layoutService.config.set(config);
&#125; &#125;
&#125;</code></pre> &#125;</code></pre>

View File

@@ -2,7 +2,7 @@
<div id="home" class="landing-wrapper overflow-hidden"> <div id="home" class="landing-wrapper overflow-hidden">
<div class="py-4 px-4 mx-0 md:mx-6 lg:mx-8 lg:px-8 flex align-items-center justify-content-between relative lg:static mb-3"> <div class="py-4 px-4 mx-0 md:mx-6 lg:mx-8 lg:px-8 flex align-items-center justify-content-between relative lg:static mb-3">
<a class="flex align-items-center" href="#"> <a class="flex align-items-center" href="#">
<img src="assets/layout/images/{{layoutService.config.colorScheme === 'light' ? 'logo-dark' : 'logo-white'}}.svg" alt="Sakai Logo" height="50" class="mr-0 lg:mr-2"><span class="text-900 font-medium text-2xl line-height-3 mr-8">SAKAI</span> <img src="assets/layout/images/{{layoutService.config().colorScheme === 'light' ? 'logo-dark' : 'logo-white'}}.svg" alt="Sakai Logo" height="50" class="mr-0 lg:mr-2"><span class="text-900 font-medium text-2xl line-height-3 mr-8">SAKAI</span>
</a> </a>
<a pRipple class="cursor-pointer block lg:hidden text-700" pStyleClass="@next" enterClass="hidden" leaveToClass="hidden" [hideOnOutsideClick]="true"> <a pRipple class="cursor-pointer block lg:hidden text-700" pStyleClass="@next" enterClass="hidden" leaveToClass="hidden" [hideOnOutsideClick]="true">
<i class="pi pi-bars text-4xl"></i> <i class="pi pi-bars text-4xl"></i>
@@ -317,7 +317,7 @@
<div class="grid justify-content-between"> <div class="grid justify-content-between">
<div class="col-12 md:col-2" style="margin-top:-1.5rem;"> <div class="col-12 md:col-2" style="margin-top:-1.5rem;">
<a (click)="router.navigate(['/pages/landing'], {fragment: 'home'})" class="flex flex-wrap align-items-center justify-content-center md:justify-content-start md:mb-0 mb-3 cursor-pointer"> <a (click)="router.navigate(['/pages/landing'], {fragment: 'home'})" class="flex flex-wrap align-items-center justify-content-center md:justify-content-start md:mb-0 mb-3 cursor-pointer">
<img src="assets/layout/images/{{layoutService.config.colorScheme === 'light' ? 'logo-dark' : 'logo-white'}}.svg" alt="footer sections" width="50" height="50" class="mr-2"> <img src="assets/layout/images/{{layoutService.config().colorScheme === 'light' ? 'logo-dark' : 'logo-white'}}.svg" alt="footer sections" width="50" height="50" class="mr-2">
<h4 class="font-medium text-3xl text-900">SAKAI</h4> <h4 class="font-medium text-3xl text-900">SAKAI</h4>
</a> </a>
</div> </div>

View File

@@ -1,5 +1,5 @@
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { Subscription } from 'rxjs'; import { Subscription, debounceTime } from 'rxjs';
import { LayoutService } from 'src/app/layout/service/app.layout.service'; import { LayoutService } from 'src/app/layout/service/app.layout.service';
@Component({ @Component({
@@ -28,11 +28,12 @@ export class ChartsDemoComponent implements OnInit, OnDestroy {
radarOptions: any; radarOptions: any;
subscription: Subscription; subscription: Subscription;
constructor(private layoutService: LayoutService) {
constructor(public layoutService: LayoutService) { this.subscription = this.layoutService.configUpdate$
this.subscription = this.layoutService.configUpdate$.subscribe(config => { .pipe(debounceTime(25))
this.initCharts(); .subscribe((config) => {
}); this.initCharts();
});
} }
ngOnInit() { ngOnInit() {

View File

@@ -1,5 +1,5 @@
<div class="layout-footer"> <div class="layout-footer">
<img src="assets/layout/images/{{layoutService.config.colorScheme === 'light' ? 'logo-dark' : 'logo-white'}}.svg" alt="Logo" height="20" class="mr-2"/> <img src="assets/layout/images/{{layoutService.config().colorScheme === 'light' ? 'logo-dark' : 'logo-white'}}.svg" alt="Logo" height="20" class="mr-2"/>
by by
<span class="font-medium ml-2">PrimeNG</span> <span class="font-medium ml-2">PrimeNG</span>
</div> </div>

View File

@@ -97,15 +97,15 @@ export class AppLayoutComponent implements OnDestroy {
get containerClass() { get containerClass() {
return { return {
'layout-theme-light': this.layoutService.config.colorScheme === 'light', 'layout-theme-light': this.layoutService.config().colorScheme === 'light',
'layout-theme-dark': this.layoutService.config.colorScheme === 'dark', 'layout-theme-dark': this.layoutService.config().colorScheme === 'dark',
'layout-overlay': this.layoutService.config.menuMode === 'overlay', 'layout-overlay': this.layoutService.config().menuMode === 'overlay',
'layout-static': this.layoutService.config.menuMode === 'static', 'layout-static': this.layoutService.config().menuMode === 'static',
'layout-static-inactive': this.layoutService.state.staticMenuDesktopInactive && this.layoutService.config.menuMode === 'static', 'layout-static-inactive': this.layoutService.state.staticMenuDesktopInactive && this.layoutService.config().menuMode === 'static',
'layout-overlay-active': this.layoutService.state.overlayMenuActive, 'layout-overlay-active': this.layoutService.state.overlayMenuActive,
'layout-mobile-active': this.layoutService.state.staticMenuMobileActive, 'layout-mobile-active': this.layoutService.state.staticMenuMobileActive,
'p-input-filled': this.layoutService.config.inputStyle === 'filled', 'p-input-filled': this.layoutService.config().inputStyle === 'filled',
'p-ripple-disabled': !this.layoutService.config.ripple 'p-ripple-disabled': !this.layoutService.config().ripple
} }
} }

View File

@@ -5,7 +5,7 @@
</ng-container> </ng-container>
<li> <li>
<a href="https://www.primefaces.org/primeblocks-ng/#/"> <a href="https://www.primefaces.org/primeblocks-ng/#/">
<img src="assets/layout/images/{{layoutService.config.colorScheme === 'light' ? 'banner-primeblocks' : 'banner-primeblocks-dark'}}.png" alt="Prime Blocks" class="w-full mt-3"/> <img src="assets/layout/images/{{layoutService.config().colorScheme === 'light' ? 'banner-primeblocks' : 'banner-primeblocks-dark'}}.png" alt="Prime Blocks" class="w-full mt-3"/>
</a> </a>
</li> </li>
</ul> </ul>

View File

@@ -1,6 +1,6 @@
<div class="layout-topbar"> <div class="layout-topbar">
<a class="layout-topbar-logo" routerLink=""> <a class="layout-topbar-logo" routerLink="">
<img src="assets/layout/images/{{layoutService.config.colorScheme === 'light' ? 'logo-dark' : 'logo-white'}}.svg" alt="logo"> <img src="assets/layout/images/{{layoutService.config().colorScheme === 'light' ? 'logo-dark' : 'logo-white'}}.svg" alt="logo">
<span>SAKAI</span> <span>SAKAI</span>
</a> </a>

View File

@@ -23,35 +23,44 @@ export class AppConfigComponent {
} }
get scale(): number { get scale(): number {
return this.layoutService.config.scale; return this.layoutService.config().scale;
} }
set scale(_val: number) { set scale(_val: number) {
this.layoutService.config.scale = _val; this.layoutService.config.update((config) => ({
...config,
scale: _val,
}));
} }
get menuMode(): string { get menuMode(): string {
return this.layoutService.config.menuMode; return this.layoutService.config().menuMode;
} }
set menuMode(_val: string) { set menuMode(_val: string) {
this.layoutService.config.menuMode = _val; this.layoutService.config.update((config) => ({
...config,
menuMode: _val,
}));
} }
get inputStyle(): string { get inputStyle(): string {
return this.layoutService.config.inputStyle; return this.layoutService.config().inputStyle;
} }
set inputStyle(_val: string) { set inputStyle(_val: string) {
this.layoutService.config.inputStyle = _val; this.layoutService.config().inputStyle = _val;
} }
get ripple(): boolean { get ripple(): boolean {
return this.layoutService.config.ripple; return this.layoutService.config().ripple;
} }
set ripple(_val: boolean) { set ripple(_val: boolean) {
this.layoutService.config.ripple = _val; this.layoutService.config.update((config) => ({
...config,
ripple: _val,
}));
} }
onConfigButtonClick() { onConfigButtonClick() {
@@ -59,32 +68,9 @@ export class AppConfigComponent {
} }
changeTheme(theme: string, colorScheme: string) { changeTheme(theme: string, colorScheme: string) {
const themeLink = <HTMLLinkElement>document.getElementById('theme-css'); this.layoutService.config.update((config) => ({ ...config, theme:theme,colorScheme:colorScheme }));
const newHref = themeLink.getAttribute('href')!.replace(this.layoutService.config.theme, theme);
this.layoutService.config.colorScheme
this.replaceThemeLink(newHref, () => {
this.layoutService.config.theme = theme;
this.layoutService.config.colorScheme = colorScheme;
this.layoutService.onConfigUpdate();
});
} }
replaceThemeLink(href: string, onComplete: Function) {
const id = 'theme-css';
const themeLink = <HTMLLinkElement>document.getElementById('theme-css');
const cloneLinkElement = <HTMLLinkElement>themeLink.cloneNode(true);
cloneLinkElement.setAttribute('href', href);
cloneLinkElement.setAttribute('id', id + '-clone');
themeLink.parentNode!.insertBefore(cloneLinkElement, themeLink.nextSibling);
cloneLinkElement.addEventListener('load', () => {
themeLink.remove();
cloneLinkElement.setAttribute('id', id);
onComplete();
});
}
decrementScale() { decrementScale() {
this.scale--; this.scale--;
@@ -97,6 +83,9 @@ export class AppConfigComponent {
} }
applyScale() { applyScale() {
document.documentElement.style.fontSize = this.scale + 'px'; this.layoutService.config.update((config) => ({
...config,
scale: this.scale,
}));
} }
} }

View File

@@ -1,4 +1,4 @@
import { Injectable } from '@angular/core'; import { Injectable, effect, signal } from '@angular/core';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
export interface AppConfig { export interface AppConfig {
@@ -24,7 +24,7 @@ interface LayoutState {
}) })
export class LayoutService { export class LayoutService {
config: AppConfig = { _config: AppConfig = {
ripple: false, ripple: false,
inputStyle: 'outlined', inputStyle: 'outlined',
menuMode: 'static', menuMode: 'static',
@@ -33,6 +33,8 @@ export class LayoutService {
scale: 14, scale: 14,
}; };
config = signal<AppConfig>(this._config);
state: LayoutState = { state: LayoutState = {
staticMenuDesktopInactive: false, staticMenuDesktopInactive: false,
overlayMenuActive: false, overlayMenuActive: false,
@@ -82,7 +84,7 @@ export class LayoutService {
} }
isOverlay() { isOverlay() {
return this.config.menuMode === 'overlay'; return this.config().menuMode === 'overlay';
} }
isDesktop() { isDesktop() {
@@ -94,7 +96,58 @@ export class LayoutService {
} }
onConfigUpdate() { onConfigUpdate() {
this.configUpdate.next(this.config); this._config = { ...this.config() };
this.configUpdate.next(this.config());
}
constructor() {
effect(() => {
const config = this.config();
this.changeTheme();
this.changeScale(config.scale);
this.onConfigUpdate();
});
}
changeTheme() {
const config = this.config();
const themeLink = <HTMLLinkElement>(
document.getElementById('theme-css')
);
const themeLinkHref = themeLink.getAttribute('href')!;
const newHref = themeLinkHref
.split('/')
.map((el) =>
el == this._config.theme
? (el = config.theme)
: el == `theme-${this._config.colorScheme}`
? (el = `theme-${config.colorScheme}`)
: el
)
.join('/');
this.replaceThemeLink(newHref);
}
replaceThemeLink(href: string) {
const id = 'theme-css';
let themeLink = <HTMLLinkElement>document.getElementById(id);
const cloneLinkElement = <HTMLLinkElement>themeLink.cloneNode(true);
cloneLinkElement.setAttribute('href', href);
cloneLinkElement.setAttribute('id', id + '-clone');
themeLink.parentNode!.insertBefore(
cloneLinkElement,
themeLink.nextSibling
);
cloneLinkElement.addEventListener('load', () => {
themeLink.remove();
cloneLinkElement.setAttribute('id', id);
});
}
changeScale(value: number) {
document.documentElement.style.fontSize = `${value}px`;
} }
} }