chore: update to nextgen

This commit is contained in:
Çetin
2025-12-25 01:06:08 +03:00
parent 1f9af0e63b
commit 219fe4a1e9
91 changed files with 4839 additions and 3558 deletions

View File

@@ -1,6 +1,6 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2018-2022 PrimeTek Copyright (c) 2018-2026 PrimeTek
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,6 +1,6 @@
# Sakai19 # Sakai19
This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 20.0.5. This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 21.
## Development server ## Development server

View File

@@ -20,9 +20,6 @@
"outputPath": "dist/sakai-ng", "outputPath": "dist/sakai-ng",
"index": "src/index.html", "index": "src/index.html",
"browser": "src/main.ts", "browser": "src/main.ts",
"polyfills": [
"zone.js"
],
"tsConfig": "tsconfig.app.json", "tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "scss", "inlineStyleLanguage": "scss",
"assets": [ "assets": [
@@ -32,12 +29,25 @@
} }
], ],
"styles": [ "styles": [
"src/assets/styles.scss" "src/assets/styles.scss",
"src/assets/tailwind.css"
], ],
"scripts": [] "scripts": []
}, },
"configurations": { "configurations": {
"production": { "production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "1mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"outputHashing": "all" "outputHashing": "all"
}, },
"development": { "development": {
@@ -66,17 +76,11 @@
"test": { "test": {
"builder": "@angular-devkit/build-angular:karma", "builder": "@angular-devkit/build-angular:karma",
"options": { "options": {
"polyfills": [
"zone.js",
"zone.js/testing"
],
"tsConfig": "tsconfig.spec.json", "tsConfig": "tsconfig.spec.json",
"inlineStyleLanguage": "scss", "inlineStyleLanguage": "scss",
"assets": [ "assets": [
{ "src/favicon.ico",
"glob": "**/*", "src/assets"
"input": "public"
}
], ],
"styles": [ "styles": [
"src/assets/styles.scss" "src/assets/styles.scss"

7302
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "sakai-ng", "name": "sakai-ng",
"version": "20.0.0", "version": "21.0.0",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",
"start": "ng serve", "start": "ng serve",
@@ -11,45 +11,44 @@
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular/animations": "^20", "@angular/common": "^21",
"@angular/common": "^20", "@angular/compiler": "^21",
"@angular/compiler": "^20", "@angular/core": "^21",
"@angular/core": "^20", "@angular/forms": "^21",
"@angular/forms": "^20", "@angular/platform-browser": "^21",
"@angular/platform-browser": "^20", "@angular/platform-browser-dynamic": "^21",
"@angular/platform-browser-dynamic": "^20", "@angular/router": "^21",
"@angular/router": "^20", "@primeuix/themes": "^2.0.0",
"@primeuix/themes": "^1.2.1",
"@tailwindcss/postcss": "^4.1.11", "@tailwindcss/postcss": "^4.1.11",
"chart.js": "4.4.2", "chart.js": "4.4.2",
"primeclt": "^0.1.5", "primeclt": "^0.1.5",
"primeicons": "^7.0.0", "primeicons": "^7.0.0",
"primeng": "^20", "primeng": "^21.0.2",
"rxjs": "~7.8.2", "quill": "^2.0.3",
"rxjs": "~7.8.0",
"tailwindcss-primeui": "^0.6.1", "tailwindcss-primeui": "^0.6.1",
"tslib": "^2.8.1", "tslib": "^2.8.1"
"zone.js": "~0.15.1"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "^20", "@angular-devkit/build-angular": "^21",
"@angular/cli": "^20", "@angular/cli": "^21",
"@angular/compiler-cli": "^20", "@angular/compiler-cli": "^21",
"@types/jasmine": "~5.1.0", "@types/jasmine": "~5.1.0",
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.20",
"eslint": "^9.30.1", "eslint": "^9.14.0",
"eslint-config-prettier": "^10.1.5", "eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.32.0", "eslint-plugin-import": "^2.31.0",
"eslint-plugin-prefer-arrow": "^1.2.3", "eslint-plugin-prefer-arrow": "^1.2.3",
"eslint-plugin-prettier": "^5.5.1", "eslint-plugin-prettier": "^4.2.1",
"jasmine-core": "~5.8.0", "jasmine-core": "~5.4.0",
"karma": "~6.4.4", "karma": "~6.4.0",
"karma-chrome-launcher": "~3.2.0", "karma-chrome-launcher": "~3.2.0",
"karma-coverage": "~2.2.1", "karma-coverage": "~2.2.0",
"karma-jasmine": "~5.1.0", "karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.1.0", "karma-jasmine-html-reporter": "~2.1.0",
"postcss": "^8.5.6", "postcss": "^8.5.6",
"prettier": "^3.6.2", "prettier": "^3.0.0",
"tailwindcss": "^4.1.11", "tailwindcss": "^4.1.11",
"typescript": "~5.8.3" "typescript": "~5.9.3"
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1009 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="300px" height="200px" viewBox="0 0 300 200" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Artboard</title>
<g id="Artboard" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect id="Rectangle" fill="#F8F9FA" x="0" y="0" width="300" height="200"></rect>
<g id="image" transform="translate(110.000000, 70.000000)" fill="#BABABC" fill-rule="nonzero">
<path d="M75,0 L5,0 C2.23857625,0 0,2.23857625 0,5 L0,55 C0,57.7614237 2.23857625,60 5,60 L75,60 C77.7614237,60 80,57.7614237 80,55 L80,5 C80,2.23857625 77.7614237,0 75,0 Z M20,10 C25.5228475,10 30,14.4771525 30,20 C30,25.5228475 25.5228475,30 20,30 C14.4771525,30 10,25.5228475 10,20 C10,14.4771525 14.4771525,10 20,10 Z M70,40 L70,50 L10,50 L10,40 L18.55,35.7 C19.4648753,35.2524957 20.5351247,35.2524957 21.45,35.7 L30,40 L53.65,21.1 C54.4866298,20.4991452 55.6133702,20.4991452 56.45,21.1 L70,30 L70,40 Z" id="Shape"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@@ -1,6 +1,5 @@
import { provideHttpClient, withFetch } from '@angular/common/http'; import { provideHttpClient, withFetch } from '@angular/common/http';
import { ApplicationConfig } from '@angular/core'; import { ApplicationConfig, provideZonelessChangeDetection } from '@angular/core';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
import { provideRouter, withEnabledBlockingInitialNavigation, withInMemoryScrolling } from '@angular/router'; import { provideRouter, withEnabledBlockingInitialNavigation, withInMemoryScrolling } from '@angular/router';
import Aura from '@primeuix/themes/aura'; import Aura from '@primeuix/themes/aura';
import { providePrimeNG } from 'primeng/config'; import { providePrimeNG } from 'primeng/config';
@@ -10,7 +9,7 @@ export const appConfig: ApplicationConfig = {
providers: [ providers: [
provideRouter(appRoutes, withInMemoryScrolling({ anchorScrolling: 'enabled', scrollPositionRestoration: 'enabled' }), withEnabledBlockingInitialNavigation()), provideRouter(appRoutes, withInMemoryScrolling({ anchorScrolling: 'enabled', scrollPositionRestoration: 'enabled' }), withEnabledBlockingInitialNavigation()),
provideHttpClient(withFetch()), provideHttpClient(withFetch()),
provideAnimationsAsync(), provideZonelessChangeDetection(),
providePrimeNG({ theme: { preset: Aura, options: { darkModeSelector: '.app-dark' } } }) providePrimeNG({ theme: { preset: Aura, options: { darkModeSelector: '.app-dark' } } })
] ]
}; };

View File

@@ -8,7 +8,7 @@ import Lara from '@primeuix/themes/lara';
import Nora from '@primeuix/themes/nora'; import Nora from '@primeuix/themes/nora';
import { PrimeNG } from 'primeng/config'; import { PrimeNG } from 'primeng/config';
import { SelectButtonModule } from 'primeng/selectbutton'; import { SelectButtonModule } from 'primeng/selectbutton';
import { LayoutService } from '../service/layout.service'; import { LayoutService } from '@/app/layout/service/layout.service';
const presets = { const presets = {
Aura, Aura,

View File

@@ -2,7 +2,7 @@ import {Component, computed, inject, input} from '@angular/core';
import { ButtonModule } from 'primeng/button'; import { ButtonModule } from 'primeng/button';
import { StyleClassModule } from 'primeng/styleclass'; import { StyleClassModule } from 'primeng/styleclass';
import { AppConfigurator } from './app.configurator'; import { AppConfigurator } from './app.configurator';
import { LayoutService } from '../service/layout.service'; import { LayoutService } from '@/app/layout/service/layout.service';
import {CommonModule} from "@angular/common"; import {CommonModule} from "@angular/common";
@Component({ @Component({

View File

@@ -1,17 +1,16 @@
import { Component, Renderer2, ViewChild } from '@angular/core'; import { Component, computed, effect, inject } from '@angular/core';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { NavigationEnd, Router, RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { filter, Subscription } from 'rxjs';
import { AppTopbar } from './app.topbar'; import { AppTopbar } from './app.topbar';
import { AppSidebar } from './app.sidebar'; import { AppSidebar } from './app.sidebar';
import { AppFooter } from './app.footer'; import { AppFooter } from './app.footer';
import { LayoutService } from '../service/layout.service'; import { LayoutService } from '@/app/layout/service/layout.service';
@Component({ @Component({
selector: 'app-layout', selector: 'app-layout',
standalone: true, standalone: true,
imports: [CommonModule, AppTopbar, AppSidebar, RouterModule, AppFooter], imports: [CommonModule, 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>
<app-sidebar></app-sidebar> <app-sidebar></app-sidebar>
<div class="layout-main-container"> <div class="layout-main-container">
@@ -20,92 +19,32 @@ import { LayoutService } from '../service/layout.service';
</div> </div>
<app-footer></app-footer> <app-footer></app-footer>
</div> </div>
<div class="layout-mask animate-fadein"></div> <div class="layout-mask"></div>
</div> ` </div> `
}) })
export class AppLayout { export class AppLayout {
overlayMenuOpenSubscription: Subscription; layoutService = inject(LayoutService);
menuOutsideClickListener: any; constructor() {
effect(() => {
@ViewChild(AppSidebar) appSidebar!: AppSidebar; const state = this.layoutService.layoutState();
if (state.mobileMenuActive) {
@ViewChild(AppTopbar) appTopBar!: AppTopbar;
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) => {
if (this.isOutsideClicked(event)) {
this.hideMenu();
}
});
}
if (this.layoutService.layoutState().staticMenuMobileActive) {
this.blockBodyScroll();
}
});
this.router.events.pipe(filter((event) => event instanceof NavigationEnd)).subscribe(() => {
this.hideMenu();
});
}
isOutsideClicked(event: MouseEvent) {
const sidebarEl = document.querySelector('.layout-sidebar');
const topbarEl = document.querySelector('.layout-menu-button');
const eventTarget = event.target as Node;
return !(sidebarEl?.isSameNode(eventTarget) || sidebarEl?.contains(eventTarget) || topbarEl?.isSameNode(eventTarget) || topbarEl?.contains(eventTarget));
}
hideMenu() {
this.layoutService.layoutState.update((prev) => ({ ...prev, overlayMenuActive: false, staticMenuMobileActive: false, menuHoverActive: false }));
if (this.menuOutsideClickListener) {
this.menuOutsideClickListener();
this.menuOutsideClickListener = null;
}
this.unblockBodyScroll();
}
blockBodyScroll(): void {
if (document.body.classList) {
document.body.classList.add('blocked-scroll'); document.body.classList.add('blocked-scroll');
} else { } else {
document.body.className += ' blocked-scroll';
}
}
unblockBodyScroll(): void {
if (document.body.classList) {
document.body.classList.remove('blocked-scroll'); document.body.classList.remove('blocked-scroll');
} else {
document.body.className = document.body.className.replace(new RegExp('(^|\\b)' + 'blocked-scroll'.split(' ').join('|') + '(\\b|$)', 'gi'), ' ');
} }
});
} }
get containerClass() { containerClass = computed(() => {
const config = this.layoutService.layoutConfig();
const state = this.layoutService.layoutState();
return { return {
'layout-overlay': this.layoutService.layoutConfig().menuMode === 'overlay', 'layout-overlay': config.menuMode === 'overlay',
'layout-static': this.layoutService.layoutConfig().menuMode === 'static', 'layout-static': config.menuMode === 'static',
'layout-static-inactive': this.layoutService.layoutState().staticMenuDesktopInactive && this.layoutService.layoutConfig().menuMode === 'static', 'layout-static-inactive': state.staticMenuDesktopInactive && config.menuMode === 'static',
'layout-overlay-active': this.layoutService.layoutState().overlayMenuActive, 'layout-overlay-active': state.overlayMenuActive,
'layout-mobile-active': this.layoutService.layoutState().staticMenuMobileActive 'layout-mobile-active': state.mobileMenuActive
}; };
} })
ngOnDestroy() {
if (this.overlayMenuOpenSubscription) {
this.overlayMenuOpenSubscription.unsubscribe();
}
if (this.menuOutsideClickListener) {
this.menuOutsideClickListener();
}
}
} }

View File

@@ -9,11 +9,14 @@ import { AppMenuitem } from './app.menuitem';
standalone: true, standalone: true,
imports: [CommonModule, AppMenuitem, RouterModule], imports: [CommonModule, AppMenuitem, RouterModule],
template: `<ul class="layout-menu"> template: `<ul class="layout-menu">
<ng-container *ngFor="let item of model; let i = index"> @for (item of model; track item.label) {
<li app-menuitem *ngIf="!item.separator" [item]="item" [index]="i" [root]="true"></li> @if (!item.separator) {
<li *ngIf="item.separator" class="menu-separator"></li> <li app-menuitem [item]="item" [root]="true"></li>
</ng-container> } @else {
</ul> ` <li class="menu-separator"></li>
}
}
</ul> `,
}) })
export class AppMenu { export class AppMenu {
model: MenuItem[] = []; model: MenuItem[] = [];
@@ -47,7 +50,7 @@ export class AppMenu {
{ {
label: 'Pages', label: 'Pages',
icon: 'pi pi-fw pi-briefcase', icon: 'pi pi-fw pi-briefcase',
routerLink: ['/pages'], path: '/pages',
items: [ items: [
{ {
label: 'Landing', label: 'Landing',
@@ -57,6 +60,7 @@ export class AppMenu {
{ {
label: 'Auth', label: 'Auth',
icon: 'pi pi-fw pi-user', icon: 'pi pi-fw pi-user',
path: '/auth',
items: [ items: [
{ {
label: 'Login', label: 'Login',
@@ -94,14 +98,17 @@ export class AppMenu {
}, },
{ {
label: 'Hierarchy', label: 'Hierarchy',
path: '/hierarchy',
items: [ items: [
{ {
label: 'Submenu 1', label: 'Submenu 1',
icon: 'pi pi-fw pi-bookmark', icon: 'pi pi-fw pi-bookmark',
path: '/hierarchy/submenu_1',
items: [ items: [
{ {
label: 'Submenu 1.1', label: 'Submenu 1.1',
icon: 'pi pi-fw pi-bookmark', icon: 'pi pi-fw pi-bookmark',
path: '/hierarchy/submenu_1/submenu_1_1',
items: [ items: [
{ label: 'Submenu 1.1.1', icon: 'pi pi-fw pi-bookmark' }, { label: 'Submenu 1.1.1', icon: 'pi pi-fw pi-bookmark' },
{ label: 'Submenu 1.1.2', icon: 'pi pi-fw pi-bookmark' }, { label: 'Submenu 1.1.2', icon: 'pi pi-fw pi-bookmark' },
@@ -111,6 +118,7 @@ export class AppMenu {
{ {
label: 'Submenu 1.2', label: 'Submenu 1.2',
icon: 'pi pi-fw pi-bookmark', icon: 'pi pi-fw pi-bookmark',
path: '/hierarchy/submenu_1/submenu_1_2',
items: [{ label: 'Submenu 1.2.1', icon: 'pi pi-fw pi-bookmark' }] items: [{ label: 'Submenu 1.2.1', icon: 'pi pi-fw pi-bookmark' }]
} }
] ]
@@ -118,10 +126,12 @@ export class AppMenu {
{ {
label: 'Submenu 2', label: 'Submenu 2',
icon: 'pi pi-fw pi-bookmark', icon: 'pi pi-fw pi-bookmark',
path: '/hierarchy/submenu_2',
items: [ items: [
{ {
label: 'Submenu 2.1', label: 'Submenu 2.1',
icon: 'pi pi-fw pi-bookmark', icon: 'pi pi-fw pi-bookmark',
path: '/hierarchy/submenu_2/submenu_2_1',
items: [ items: [
{ label: 'Submenu 2.1.1', icon: 'pi pi-fw pi-bookmark' }, { label: 'Submenu 2.1.1', icon: 'pi pi-fw pi-bookmark' },
{ label: 'Submenu 2.1.2', icon: 'pi pi-fw pi-bookmark' } { label: 'Submenu 2.1.2', icon: 'pi pi-fw pi-bookmark' }
@@ -130,6 +140,7 @@ export class AppMenu {
{ {
label: 'Submenu 2.2', label: 'Submenu 2.2',
icon: 'pi pi-fw pi-bookmark', icon: 'pi pi-fw pi-bookmark',
path: '/hierarchy/submenu_2/submenu_2_2',
items: [{ label: 'Submenu 2.2.1', icon: 'pi pi-fw pi-bookmark' }] items: [{ label: 'Submenu 2.2.1', icon: 'pi pi-fw pi-bookmark' }]
} }
] ]

View File

@@ -1,170 +1,209 @@
import { Component, HostBinding, Input } from '@angular/core'; import { Component, computed, inject, input, signal } from '@angular/core';
import { NavigationEnd, Router, RouterModule } from '@angular/router'; import { NavigationEnd, Router, RouterModule } from '@angular/router';
import { animate, state, style, transition, trigger } from '@angular/animations';
import { Subscription } from 'rxjs';
import { filter } from 'rxjs/operators';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { RippleModule } from 'primeng/ripple'; import { RippleModule } from 'primeng/ripple';
import { MenuItem } from 'primeng/api'; import { LayoutService } from '@/app/layout/service/layout.service';
import { LayoutService } from '../service/layout.service'; import { filter } from 'rxjs/operators';
@Component({ @Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: '[app-menuitem]', selector: '[app-menuitem]',
imports: [CommonModule, RouterModule, RippleModule], imports: [CommonModule, RouterModule, RippleModule],
template: ` template: `
<ng-container> @if (root() && isVisible()) {
<div *ngIf="root && item.visible !== false" class="layout-menuitem-root-text">{{ item.label }}</div> <div class="layout-menuitem-root-text">{{ item().label }}</div>
<a *ngIf="(!item.routerLink || item.items) && item.visible !== false" [attr.href]="item.url" (click)="itemClick($event)" [ngClass]="item.styleClass" [attr.target]="item.target" tabindex="0" pRipple> }
<i [ngClass]="item.icon" class="layout-menuitem-icon"></i> @if ((!hasRouterLink() || hasChildren()) && isVisible()) {
<span class="layout-menuitem-text">{{ item.label }}</span> <a [attr.href]="item().url" (click)="itemClick($event)" [ngClass]="item().class" [attr.target]="item().target" tabindex="0" pRipple>
<i class="pi pi-fw pi-angle-down layout-submenu-toggler" *ngIf="item.items"></i> <i [ngClass]="item().icon" class="layout-menuitem-icon"></i>
<span class="layout-menuitem-text">{{ item().label }}</span>
@if (hasChildren()) {
<i class="pi pi-fw pi-angle-down layout-submenu-toggler"></i>
}
</a> </a>
}
@if (hasRouterLink() && !hasChildren() && isVisible()) {
<a <a
*ngIf="item.routerLink && !item.items && item.visible !== false"
(click)="itemClick($event)" (click)="itemClick($event)"
[ngClass]="item.styleClass" [ngClass]="item().class"
[routerLink]="item.routerLink" [routerLink]="item().routerLink"
routerLinkActive="active-route" routerLinkActive="active-route"
[routerLinkActiveOptions]="item.routerLinkActiveOptions || { paths: 'exact', queryParams: 'ignored', matrixParams: 'ignored', fragment: 'ignored' }" [routerLinkActiveOptions]="item().routerLinkActiveOptions || { paths: 'exact', queryParams: 'ignored', matrixParams: 'ignored', fragment: 'ignored' }"
[fragment]="item.fragment" [fragment]="item().fragment"
[queryParamsHandling]="item.queryParamsHandling" [queryParamsHandling]="item().queryParamsHandling"
[preserveFragment]="item.preserveFragment" [preserveFragment]="item().preserveFragment"
[skipLocationChange]="item.skipLocationChange" [skipLocationChange]="item().skipLocationChange"
[replaceUrl]="item.replaceUrl" [replaceUrl]="item().replaceUrl"
[state]="item.state" [state]="item().state"
[queryParams]="item.queryParams" [queryParams]="item().queryParams"
[attr.target]="item.target" [attr.target]="item().target"
tabindex="0" tabindex="0"
pRipple pRipple
> >
<i [ngClass]="item.icon" class="layout-menuitem-icon"></i> <i [ngClass]="item().icon" class="layout-menuitem-icon"></i>
<span class="layout-menuitem-text">{{ item.label }}</span> <span class="layout-menuitem-text">{{ item().label }}</span>
<i class="pi pi-fw pi-angle-down layout-submenu-toggler" *ngIf="item.items"></i> @if (hasChildren()) {
<i class="pi pi-fw pi-angle-down layout-submenu-toggler"></i>
}
</a> </a>
}
<ul *ngIf="item.items && item.visible !== false" [@children]="submenuAnimation"> @if (hasChildren() && isVisible() && (root() || isActive())) {
<ng-template ngFor let-child let-i="index" [ngForOf]="item.items"> <ul [animate.enter]="initialized() ? 'p-submenu-enter' : null" [animate.leave]="'p-submenu-leave'" [class.layout-root-submenulist]="root()">
<li app-menuitem [item]="child" [index]="i" [parentKey]="key" [class]="child['badgeClass']"></li> @for (child of item().items; track child?.label) {
</ng-template> <li app-menuitem [item]="child" [parentPath]="fullPath()" [root]="false" [class]="child['badgeClass']"></li>
}
</ul> </ul>
</ng-container> }
`, `,
animations: [ host: {
trigger('children', [ '[class.active-menuitem]': 'isActive()',
state( '[class.layout-root-menuitem]': 'root()'
'collapsed', },
style({ styles: [
height: '0' `
}) .p-submenu-enter {
), animation: p-animate-submenu-expand 450ms cubic-bezier(0.86, 0, 0.07, 1) forwards;
state( }
'expanded',
style({ .p-submenu-leave {
height: '*' animation: p-animate-submenu-collapse 450ms cubic-bezier(0.86, 0, 0.07, 1) forwards;
}) }
),
transition('collapsed <=> expanded', animate('400ms cubic-bezier(0.86, 0, 0.07, 1)')) @keyframes p-animate-submenu-expand {
]) from {
], max-height: 0;
providers: [LayoutService] overflow: hidden;
}
to {
max-height: 1000px;
overflow: visible;
}
}
@keyframes p-animate-submenu-collapse {
from {
max-height: 1000px;
overflow: hidden;
}
to {
max-height: 0;
overflow: hidden;
}
}
`
]
}) })
export class AppMenuitem { export class AppMenuitem {
@Input() item!: MenuItem; layoutService = inject(LayoutService);
@Input() index!: number; router = inject(Router);
@Input() @HostBinding('class.layout-root-menuitem') root!: boolean; item = input<any>(null);
@Input() parentKey!: string; root = input<boolean>(false);
active = false; parentPath = input<string | null>(null);
menuSourceSubscription: Subscription; isVisible = computed(() => this.item()?.visible !== false);
menuResetSubscription: Subscription; hasChildren = computed(() => this.item()?.items && this.item()?.items.length > 0);
key: string = ''; hasRouterLink = computed(() => !!this.item()?.routerLink);
constructor( fullPath = computed(() => {
public router: Router, const itemPath = this.item()?.path;
private layoutService: LayoutService if (!itemPath) return this.parentPath();
) { const parent = this.parentPath();
this.menuSourceSubscription = this.layoutService.menuSource$.subscribe((value) => { if (parent && !itemPath.startsWith(parent)) {
Promise.resolve(null).then(() => { return parent + itemPath;
if (value.routeEvent) {
this.active = value.key === this.key || value.key.startsWith(this.key + '-') ? true : false;
} else {
if (value.key !== this.key && !value.key.startsWith(this.key + '-')) {
this.active = false;
} }
return itemPath;
});
isActive = computed(() => {
const activePath = this.layoutService.layoutState().activePath;
if (this.item()?.path) {
return activePath?.startsWith(this.fullPath() ?? '') ?? false;
} }
}); return false;
}); });
this.menuResetSubscription = this.layoutService.resetSource$.subscribe(() => { initialized = signal<boolean>(false);
this.active = false;
});
this.router.events.pipe(filter((event) => event instanceof NavigationEnd)).subscribe((params) => { constructor() {
if (this.item.routerLink) { this.router.events.pipe(filter((event) => event instanceof NavigationEnd)).subscribe(() => {
if (this.item()?.routerLink) {
this.updateActiveStateFromRoute(); this.updateActiveStateFromRoute();
} }
}); });
} }
ngOnInit() { ngOnInit() {
this.key = this.parentKey ? this.parentKey + '-' + this.index : String(this.index); if (this.item()?.routerLink) {
if (this.item.routerLink) {
this.updateActiveStateFromRoute(); this.updateActiveStateFromRoute();
} }
} }
updateActiveStateFromRoute() { ngAfterViewInit() {
let activeRoute = this.router.isActive(this.item.routerLink[0], { paths: 'exact', queryParams: 'ignored', matrixParams: 'ignored', fragment: 'ignored' }); setTimeout(() => {
this.initialized.set(true);
});
}
if (activeRoute) { updateActiveStateFromRoute() {
this.layoutService.onMenuStateChange({ key: this.key, routeEvent: true }); const item = this.item();
if (!item?.routerLink) return;
const isRouteActive = this.router.isActive(item.routerLink[0], {
paths: 'exact',
queryParams: 'ignored',
matrixParams: 'ignored',
fragment: 'ignored'
});
if (isRouteActive) {
const parentPath = this.parentPath();
if (parentPath) {
this.layoutService.layoutState.update((val) => ({
...val,
activePath: parentPath
}));
}
} }
} }
itemClick(event: Event) { itemClick(event: Event) {
// avoid processing disabled items const item = this.item();
if (this.item.disabled) {
if (item?.disabled) {
event.preventDefault(); event.preventDefault();
return; return;
} }
// execute command if (item?.command) {
if (this.item.command) { item.command({ originalEvent: event, item: item });
this.item.command({ originalEvent: event, item: this.item });
} }
// toggle active state if (this.hasChildren()) {
if (this.item.items) { if (this.isActive()) {
this.active = !this.active; this.layoutService.layoutState.update((val) => ({
...val,
activePath: this.parentPath()
}));
} else {
this.layoutService.layoutState.update((val) => ({
...val,
activePath: this.fullPath(),
menuHoverActive: true
}));
} }
} else {
this.layoutService.onMenuStateChange({ key: this.key }); this.layoutService.layoutState.update((val) => ({
} ...val,
overlayMenuActive: false,
get submenuAnimation() { staticMenuMobileActive: false,
return this.root ? 'expanded' : this.active ? 'expanded' : 'collapsed'; mobileMenuActive: false,
} menuHoverActive: false
}));
@HostBinding('class.active-menuitem')
get activeClass() {
return this.active && !this.root;
}
ngOnDestroy() {
if (this.menuSourceSubscription) {
this.menuSourceSubscription.unsubscribe();
}
if (this.menuResetSubscription) {
this.menuResetSubscription.unsubscribe();
} }
} }
} }

View File

@@ -1,14 +1,115 @@
import { Component, ElementRef } from '@angular/core'; import { Component, computed, effect, ElementRef, inject, OnDestroy, OnInit } from '@angular/core';
import { NavigationEnd, Router, RouterModule } from '@angular/router';
import { filter, Subject, takeUntil } from 'rxjs';
import { AppMenu } from './app.menu'; import { AppMenu } from './app.menu';
import { LayoutService } from '@/app/layout/service/layout.service';
@Component({ @Component({
selector: 'app-sidebar', selector: 'app-sidebar',
standalone: true, standalone: true,
imports: [AppMenu], imports: [AppMenu, RouterModule],
template: ` <div class="layout-sidebar"> template: `
<div class="layout-sidebar">
<app-menu></app-menu> <app-menu></app-menu>
</div>` </div>
`
}) })
export class AppSidebar { export class AppSidebar implements OnInit, OnDestroy {
constructor(public el: ElementRef) {} layoutService = inject(LayoutService);
router = inject(Router);
el = inject(ElementRef);
private outsideClickListener: ((event: MouseEvent) => void) | null = null;
private destroy$ = new Subject<void>();
constructor() {
effect(() => {
const state = this.layoutService.layoutState();
if (this.layoutService.isDesktop()) {
if (state.overlayMenuActive) {
this.bindOutsideClickListener();
} else {
this.unbindOutsideClickListener();
}
} else {
if (state.mobileMenuActive) {
this.bindOutsideClickListener();
} else {
this.unbindOutsideClickListener();
}
}
});
}
ngOnInit() {
this.router.events
.pipe(
filter((event) => event instanceof NavigationEnd),
takeUntil(this.destroy$)
)
.subscribe((event) => {
const navEvent = event as NavigationEnd;
this.onRouteChange(navEvent.urlAfterRedirects);
});
this.onRouteChange(this.router.url);
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
this.unbindOutsideClickListener();
}
private onRouteChange(path: string) {
this.layoutService.layoutState.update((val) => ({
...val,
activePath: path,
overlayMenuActive: false,
staticMenuMobileActive: false,
mobileMenuActive: false,
menuHoverActive: false
}));
}
private bindOutsideClickListener() {
if (!this.outsideClickListener) {
this.outsideClickListener = (event: MouseEvent) => {
if (this.isOutsideClicked(event)) {
this.layoutService.layoutState.update((val) => ({
...val,
overlayMenuActive: false,
staticMenuMobileActive: false,
mobileMenuActive: false,
menuHoverActive: false
}));
}
};
document.addEventListener('click', this.outsideClickListener);
}
}
private unbindOutsideClickListener() {
if (this.outsideClickListener) {
document.removeEventListener('click', this.outsideClickListener);
this.outsideClickListener = null;
}
}
private isOutsideClicked(event: MouseEvent): boolean {
const topbarButtonEl = document.querySelector('.topbar-start > button');
const sidebarEl = this.el.nativeElement;
return !(
sidebarEl?.isSameNode(event.target as Node) ||
sidebarEl?.contains(event.target as Node) ||
topbarButtonEl?.isSameNode(event.target as Node) ||
topbarButtonEl?.contains(event.target as Node)
);
}
} }

View File

@@ -1,10 +1,10 @@
import { Component } from '@angular/core'; import { Component, inject } from '@angular/core';
import { MenuItem } from 'primeng/api'; import { MenuItem } from 'primeng/api';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { StyleClassModule } from 'primeng/styleclass'; import { StyleClassModule } from 'primeng/styleclass';
import { AppConfigurator } from './app.configurator'; import { AppConfigurator } from './app.configurator';
import { LayoutService } from '../service/layout.service'; import { LayoutService } from '@/app/layout/service/layout.service';
@Component({ @Component({
selector: 'app-topbar', selector: 'app-topbar',
@@ -84,9 +84,12 @@ import { LayoutService } from '../service/layout.service';
export class AppTopbar { export class AppTopbar {
items!: MenuItem[]; items!: MenuItem[];
constructor(public layoutService: LayoutService) {} layoutService = inject(LayoutService);
toggleDarkMode() { toggleDarkMode() {
this.layoutService.layoutConfig.update((state) => ({ ...state, darkTheme: !state.darkTheme })); this.layoutService.layoutConfig.update((state) => ({
...state,
darkTheme: !state.darkTheme
}));
} }
} }

View File

@@ -1,70 +1,46 @@
import { Injectable, effect, signal, computed } from '@angular/core'; import { Injectable, effect, signal, computed } from '@angular/core';
import { Subject } from 'rxjs';
export interface layoutConfig { export interface LayoutConfig {
preset?: string; preset: string;
primary?: string; primary: string;
surface?: string | undefined | null; surface: string | undefined | null;
darkTheme?: boolean; darkTheme: boolean;
menuMode?: string; menuMode: string;
} }
interface LayoutState { interface LayoutState {
staticMenuDesktopInactive?: boolean; staticMenuDesktopInactive: boolean;
overlayMenuActive?: boolean; overlayMenuActive: boolean;
configSidebarVisible?: boolean; configSidebarVisible: boolean;
staticMenuMobileActive?: boolean; mobileMenuActive: boolean;
menuHoverActive?: boolean; menuHoverActive: boolean;
} activePath: string | null;
interface MenuChangeEvent {
key: string;
routeEvent?: boolean;
} }
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class LayoutService { export class LayoutService {
_config: layoutConfig = { layoutConfig = signal<LayoutConfig>({
preset: 'Aura', preset: 'Aura',
primary: 'emerald', primary: 'emerald',
surface: null, surface: null,
darkTheme: false, darkTheme: false,
menuMode: 'static' menuMode: 'static'
}; });
_state: LayoutState = { layoutState = signal<LayoutState>({
staticMenuDesktopInactive: false, staticMenuDesktopInactive: false,
overlayMenuActive: false, overlayMenuActive: false,
configSidebarVisible: false, configSidebarVisible: false,
staticMenuMobileActive: false, mobileMenuActive: false,
menuHoverActive: false menuHoverActive: false,
}; activePath: null
});
layoutConfig = signal<layoutConfig>(this._config); theme = computed(() => (this.layoutConfig().darkTheme ? 'light' : 'dark'));
layoutState = signal<LayoutState>(this._state); isSidebarActive = computed(() => this.layoutState().overlayMenuActive || this.layoutState().mobileMenuActive);
private configUpdate = new Subject<layoutConfig>();
private overlayOpen = new Subject<any>();
private menuSource = new Subject<MenuChangeEvent>();
private resetSource = new Subject();
menuSource$ = this.menuSource.asObservable();
resetSource$ = this.resetSource.asObservable();
configUpdate$ = this.configUpdate.asObservable();
overlayOpen$ = this.overlayOpen.asObservable();
theme = computed(() => (this.layoutConfig()?.darkTheme ? 'light' : 'dark'));
isSidebarActive = computed(() => this.layoutState().overlayMenuActive || this.layoutState().staticMenuMobileActive);
isDarkTheme = computed(() => this.layoutConfig().darkTheme); isDarkTheme = computed(() => this.layoutConfig().darkTheme);
@@ -79,13 +55,6 @@ export class LayoutService {
private initialized = false; private initialized = false;
constructor() { constructor() {
effect(() => {
const config = this.layoutConfig();
if (config) {
this.onConfigUpdate();
}
});
effect(() => { effect(() => {
const config = this.layoutConfig(); const config = this.layoutConfig();
@@ -98,28 +67,23 @@ export class LayoutService {
}); });
} }
private handleDarkModeTransition(config: layoutConfig): void { private handleDarkModeTransition(config: LayoutConfig): void {
if ((document as any).startViewTransition) { const supportsViewTransition = 'startViewTransition' in document;
if (supportsViewTransition) {
this.startViewTransition(config); this.startViewTransition(config);
} else { } else {
this.toggleDarkMode(config); this.toggleDarkMode(config);
this.onTransitionEnd();
} }
} }
private startViewTransition(config: layoutConfig): void { private startViewTransition(config: LayoutConfig): void {
const transition = (document as any).startViewTransition(() => { document.startViewTransition(() => {
this.toggleDarkMode(config); this.toggleDarkMode(config);
}); });
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');
@@ -128,31 +92,24 @@ export class LayoutService {
} }
} }
private onTransitionEnd() {
this.transitionComplete.set(true);
setTimeout(() => {
this.transitionComplete.set(false);
});
}
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) {
this.overlayOpen.next(null);
}
} }
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, mobileMenuActive: !this.layoutState().mobileMenuActive }));
}
}
if (this.layoutState().staticMenuMobileActive) { showConfigSidebar() {
this.overlayOpen.next(null); this.layoutState.update((prev) => ({ ...prev, configSidebarVisible: true }));
}
} }
hideConfigSidebar() {
this.layoutState.update((prev) => ({ ...prev, configSidebarVisible: false }));
} }
isDesktop() { isDesktop() {
@@ -162,17 +119,4 @@ export class LayoutService {
isMobile() { isMobile() {
return !this.isDesktop(); return !this.isDesktop();
} }
onConfigUpdate() {
this._config = { ...this.layoutConfig() };
this.configUpdate.next(this.layoutConfig());
}
onMenuStateChange(event: MenuChangeEvent) {
this.menuSource.next(event);
}
reset() {
this.resetSource.next(true);
}
} }

View File

@@ -18,7 +18,7 @@ import { TagModule } from 'primeng/tag';
import { InputIconModule } from 'primeng/inputicon'; import { InputIconModule } from 'primeng/inputicon';
import { IconFieldModule } from 'primeng/iconfield'; import { IconFieldModule } from 'primeng/iconfield';
import { ConfirmDialogModule } from 'primeng/confirmdialog'; import { ConfirmDialogModule } from 'primeng/confirmdialog';
import { Product, ProductService } from '../service/product.service'; import { Product, ProductService } from '@/app/pages/service/product.service';
interface Column { interface Column {
field: string; field: string;

View File

@@ -1,9 +1,9 @@
import { Component } from '@angular/core'; import { Component, inject, signal } from '@angular/core';
import { RippleModule } from 'primeng/ripple'; import { RippleModule } from 'primeng/ripple';
import { TableModule } from 'primeng/table'; import { TableModule } from 'primeng/table';
import { ButtonModule } from 'primeng/button'; import { ButtonModule } from 'primeng/button';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { Product, ProductService } from '../../service/product.service'; import { Product, ProductService } from '@/app/pages/service/product.service';
@Component({ @Component({
standalone: true, standalone: true,
@@ -11,7 +11,7 @@ import { Product, ProductService } from '../../service/product.service';
imports: [CommonModule, TableModule, ButtonModule, RippleModule], imports: [CommonModule, TableModule, ButtonModule, RippleModule],
template: `<div class="card mb-8!"> template: `<div class="card mb-8!">
<div class="font-semibold text-xl mb-4">Recent Sales</div> <div class="font-semibold text-xl mb-4">Recent Sales</div>
<p-table [value]="products" [paginator]="true" [rows]="5" responsiveLayout="scroll"> <p-table [value]="products()" [paginator]="true" [rows]="5" responsiveLayout="scroll">
<ng-template #header> <ng-template #header>
<tr> <tr>
<th>Image</th> <th>Image</th>
@@ -37,11 +37,11 @@ import { Product, ProductService } from '../../service/product.service';
providers: [ProductService] providers: [ProductService]
}) })
export class RecentSalesWidget { export class RecentSalesWidget {
products!: Product[]; products = signal<Product[]>([]);
constructor(private productService: ProductService) {} productService = inject(ProductService);
ngOnInit() { ngOnInit() {
this.productService.getProductsSmall().then((data) => (this.products = data)); this.productService.getProductsSmall().then((data) => (this.products.set(data)));
} }
} }

View File

@@ -1,7 +1,6 @@
import { Component } from '@angular/core'; import { afterNextRender, Component, effect, inject, signal } from '@angular/core';
import { ChartModule } from 'primeng/chart'; import { ChartModule } from 'primeng/chart';
import { debounceTime, Subscription } from 'rxjs'; import { LayoutService } from '@/app/layout/service/layout.service';
import { LayoutService } from '../../../layout/service/layout.service';
@Component({ @Component({
standalone: true, standalone: true,
@@ -9,24 +8,29 @@ import { LayoutService } from '../../../layout/service/layout.service';
imports: [ChartModule], imports: [ChartModule],
template: `<div class="card mb-8!"> template: `<div class="card mb-8!">
<div class="font-semibold text-xl mb-4">Revenue Stream</div> <div class="font-semibold text-xl mb-4">Revenue Stream</div>
<p-chart type="bar" [data]="chartData" [options]="chartOptions" class="h-100" /> <p-chart type="bar" [data]="chartData()" [options]="chartOptions()" class="h-100" />
</div>` </div>`
}) })
export class RevenueStreamWidget { export class RevenueStreamWidget {
chartData: any; layoutService = inject(LayoutService);
chartOptions: any; chartData = signal<any>(null);
subscription!: Subscription; chartOptions = signal<any>(null);
constructor(public layoutService: LayoutService) { constructor() {
this.subscription = this.layoutService.configUpdate$.pipe(debounceTime(25)).subscribe(() => { afterNextRender(() => {
setTimeout(() => {
this.initChart(); this.initChart();
}, 150);
}); });
}
ngOnInit() { effect(() => {
this.layoutService.layoutConfig().darkTheme;
setTimeout(() => {
this.initChart(); this.initChart();
}, 150);
});
} }
initChart() { initChart() {
@@ -35,7 +39,7 @@ export class RevenueStreamWidget {
const borderColor = documentStyle.getPropertyValue('--surface-border'); const borderColor = documentStyle.getPropertyValue('--surface-border');
const textMutedColor = documentStyle.getPropertyValue('--text-color-secondary'); const textMutedColor = documentStyle.getPropertyValue('--text-color-secondary');
this.chartData = { this.chartData.set({
labels: ['Q1', 'Q2', 'Q3', 'Q4'], labels: ['Q1', 'Q2', 'Q3', 'Q4'],
datasets: [ datasets: [
{ {
@@ -67,9 +71,9 @@ export class RevenueStreamWidget {
barThickness: 32 barThickness: 32
} }
] ]
}; });
this.chartOptions = { this.chartOptions.set({
maintainAspectRatio: false, maintainAspectRatio: false,
aspectRatio: 0.8, aspectRatio: 0.8,
plugins: { plugins: {
@@ -102,12 +106,6 @@ export class RevenueStreamWidget {
} }
} }
} }
}; });
}
ngOnDestroy() {
if (this.subscription) {
this.subscription.unsubscribe();
}
} }
} }

View File

@@ -9,7 +9,7 @@ import { Component } from '@angular/core';
<div class="card"> <div class="card">
<div class="font-semibold text-2xl mb-4">Documentation</div> <div class="font-semibold text-2xl mb-4">Documentation</div>
<div class="font-semibold text-xl mb-4">Get Started</div> <div class="font-semibold text-xl mb-4">Get Started</div>
<p class="text-lg mb-4">Sakai is an application template for Angular and is distributed as a CLI project. Current versions is Angular v20 with PrimeNG v20. In case CLI is not installed already, use the command below to set it up.</p> <p class="text-lg mb-4">Sakai is an application template for Angular and is distributed as a CLI project. Current versions is Angular v21 with PrimeNG v21. In case CLI is not installed already, use the command below to set it up.</p>
<pre class="app-code"> <pre class="app-code">
<code>npm install -g &#64;angular/cli</code></pre> <code>npm install -g &#64;angular/cli</code></pre>
<p class="text-lg mb-4"> <p class="text-lg mb-4">

View File

@@ -3,7 +3,7 @@ import { StyleClassModule } from 'primeng/styleclass';
import { Router, RouterModule } from '@angular/router'; import { Router, RouterModule } from '@angular/router';
import { RippleModule } from 'primeng/ripple'; import { RippleModule } from 'primeng/ripple';
import { ButtonModule } from 'primeng/button'; import { ButtonModule } from 'primeng/button';
import {AppFloatingConfigurator} from "@/layout/component/app.floatingconfigurator"; import {AppFloatingConfigurator} from "@/app/layout/component/app.floatingconfigurator";
@Component({ @Component({
selector: 'topbar-widget', selector: 'topbar-widget',

View File

@@ -1,96 +1,96 @@
import { Injectable } from '@angular/core'; import {Injectable} from '@angular/core';
@Injectable() @Injectable()
export class PhotoService { export class PhotoService {
getData() { getData() {
return [ return [
{ {
itemImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria1.jpg', itemImageSrc: '/demo/images/galleria/galleria1.jpg',
thumbnailImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria1s.jpg', thumbnailImageSrc: '/demo/images/galleria/galleria1s.jpg',
alt: 'Description for Image 1', alt: 'Description for Image 1',
title: 'Title 1' title: 'Title 1'
}, },
{ {
itemImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria2.jpg', itemImageSrc: '/demo/images/galleria/galleria2.jpg',
thumbnailImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria2s.jpg', thumbnailImageSrc: '/demo/images/galleria/galleria2s.jpg',
alt: 'Description for Image 2', alt: 'Description for Image 2',
title: 'Title 2' title: 'Title 2'
}, },
{ {
itemImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria3.jpg', itemImageSrc: '/demo/images/galleria/galleria3.jpg',
thumbnailImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria3s.jpg', thumbnailImageSrc: '/demo/images/galleria/galleria3s.jpg',
alt: 'Description for Image 3', alt: 'Description for Image 3',
title: 'Title 3' title: 'Title 3'
}, },
{ {
itemImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria4.jpg', itemImageSrc: '/demo/images/galleria/galleria4.jpg',
thumbnailImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria4s.jpg', thumbnailImageSrc: '/demo/images/galleria/galleria4s.jpg',
alt: 'Description for Image 4', alt: 'Description for Image 4',
title: 'Title 4' title: 'Title 4'
}, },
{ {
itemImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria5.jpg', itemImageSrc: '/demo/images/galleria/galleria5.jpg',
thumbnailImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria5s.jpg', thumbnailImageSrc: '/demo/images/galleria/galleria5s.jpg',
alt: 'Description for Image 5', alt: 'Description for Image 5',
title: 'Title 5' title: 'Title 5'
}, },
{ {
itemImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria6.jpg', itemImageSrc: '/demo/images/galleria/galleria6.jpg',
thumbnailImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria6s.jpg', thumbnailImageSrc: '/demo/images/galleria/galleria6s.jpg',
alt: 'Description for Image 6', alt: 'Description for Image 6',
title: 'Title 6' title: 'Title 6'
}, },
{ {
itemImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria7.jpg', itemImageSrc: '/demo/images/galleria/galleria7.jpg',
thumbnailImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria7s.jpg', thumbnailImageSrc: '/demo/images/galleria/galleria7s.jpg',
alt: 'Description for Image 7', alt: 'Description for Image 7',
title: 'Title 7' title: 'Title 7'
}, },
{ {
itemImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria8.jpg', itemImageSrc: '/demo/images/galleria/galleria8.jpg',
thumbnailImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria8s.jpg', thumbnailImageSrc: '/demo/images/galleria/galleria8s.jpg',
alt: 'Description for Image 8', alt: 'Description for Image 8',
title: 'Title 8' title: 'Title 8'
}, },
{ {
itemImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria9.jpg', itemImageSrc: '/demo/images/galleria/galleria9.jpg',
thumbnailImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria9s.jpg', thumbnailImageSrc: '/demo/images/galleria/galleria9s.jpg',
alt: 'Description for Image 9', alt: 'Description for Image 9',
title: 'Title 9' title: 'Title 9'
}, },
{ {
itemImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria10.jpg', itemImageSrc: '/demo/images/galleria/galleria10.jpg',
thumbnailImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria10s.jpg', thumbnailImageSrc: '/demo/images/galleria/galleria10s.jpg',
alt: 'Description for Image 10', alt: 'Description for Image 10',
title: 'Title 10' title: 'Title 10'
}, },
{ {
itemImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria11.jpg', itemImageSrc: '/demo/images/galleria/galleria11.jpg',
thumbnailImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria11s.jpg', thumbnailImageSrc: '/demo/images/galleria/galleria11s.jpg',
alt: 'Description for Image 11', alt: 'Description for Image 11',
title: 'Title 11' title: 'Title 11'
}, },
{ {
itemImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria12.jpg', itemImageSrc: '/demo/images/galleria/galleria12.jpg',
thumbnailImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria12s.jpg', thumbnailImageSrc: '/demo/images/galleria/galleria12s.jpg',
alt: 'Description for Image 12', alt: 'Description for Image 12',
title: 'Title 12' title: 'Title 12'
}, },
{ {
itemImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria13.jpg', itemImageSrc: '/demo/images/galleria/galleria13.jpg',
thumbnailImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria13s.jpg', thumbnailImageSrc: '/demo/images/galleria/galleria13s.jpg',
alt: 'Description for Image 13', alt: 'Description for Image 13',
title: 'Title 13' title: 'Title 13'
}, },
{ {
itemImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria14.jpg', itemImageSrc: '/demo/images/galleria/galleria14.jpg',
thumbnailImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria14s.jpg', thumbnailImageSrc: '/demo/images/galleria/galleria14s.jpg',
alt: 'Description for Image 14', alt: 'Description for Image 14',
title: 'Title 14' title: 'Title 14'
}, },
{ {
itemImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria15.jpg', itemImageSrc: '/demo/images/galleria/galleria15.jpg',
thumbnailImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria15s.jpg', thumbnailImageSrc: '/demo/images/galleria/galleria15s.jpg',
alt: 'Description for Image 15', alt: 'Description for Image 15',
title: 'Title 15' title: 'Title 15'
} }

View File

@@ -1,86 +1,80 @@
import { CommonModule } from '@angular/common'; import {Component, effect, inject, signal} from '@angular/core';
import { Component } from '@angular/core'; import {ChartModule} from 'primeng/chart';
import { ChartModule } from 'primeng/chart'; import {FluidModule} from 'primeng/fluid';
import { FluidModule } from 'primeng/fluid'; import {LayoutService} from '@/app/layout/service/layout.service';
import { debounceTime, Subscription } from 'rxjs';
import { LayoutService } from '../../layout/service/layout.service';
@Component({ @Component({
selector: 'app-chart-demo', selector: 'app-chart-demo',
standalone: true, standalone: true,
imports: [CommonModule, ChartModule, FluidModule], imports: [ChartModule, FluidModule],
template: ` template: `
<p-fluid class="grid grid-cols-12 gap-8"> <p-fluid class="grid grid-cols-12 gap-8">
<div class="col-span-12 xl:col-span-6"> <div class="col-span-12 xl:col-span-6">
<div class="card"> <div class="card">
<div class="font-semibold text-xl mb-4">Linear</div> <div class="font-semibold text-xl mb-6">Linear</div>
<p-chart type="line" [data]="lineData" [options]="lineOptions"></p-chart> <p-chart type="line" [data]="lineData()" [options]="lineOptions()"></p-chart>
</div> </div>
</div> </div>
<div class="col-span-12 xl:col-span-6"> <div class="col-span-12 xl:col-span-6">
<div class="card"> <div class="card">
<div class="font-semibold text-xl mb-4">Bar</div> <div class="font-semibold text-xl mb-6">Bar</div>
<p-chart type="bar" [data]="barData" [options]="barOptions"></p-chart> <p-chart type="bar" [data]="barData()" [options]="barOptions()"></p-chart>
</div> </div>
</div> </div>
<div class="col-span-12 xl:col-span-6"> <div class="col-span-12 xl:col-span-6">
<div class="card flex flex-col items-center"> <div class="card flex flex-col items-center">
<div class="font-semibold text-xl mb-4">Pie</div> <div class="font-semibold text-xl mb-6">Pie</div>
<p-chart type="pie" [data]="pieData" [options]="pieOptions"></p-chart> <p-chart type="pie" [data]="pieData()" [options]="pieOptions()"></p-chart>
</div> </div>
</div> </div>
<div class="col-span-12 xl:col-span-6"> <div class="col-span-12 xl:col-span-6">
<div class="card flex flex-col items-center"> <div class="card flex flex-col items-center">
<div class="font-semibold text-xl mb-4">Doughnut</div> <div class="font-semibold text-xl mb-6">Doughnut</div>
<p-chart type="doughnut" [data]="pieData" [options]="pieOptions"></p-chart> <p-chart type="doughnut" [data]="pieData()" [options]="pieOptions()"></p-chart>
</div> </div>
</div> </div>
<div class="col-span-12 xl:col-span-6"> <div class="col-span-12 xl:col-span-6">
<div class="card flex flex-col items-center"> <div class="card flex flex-col items-center">
<div class="font-semibold text-xl mb-4">Polar Area</div> <div class="font-semibold text-xl mb-6">Polar Area</div>
<p-chart type="polarArea" [data]="polarData" [options]="polarOptions"></p-chart> <p-chart type="polarArea" [data]="polarData()" [options]="polarOptions()"></p-chart>
</div> </div>
</div> </div>
<div class="col-span-12 xl:col-span-6"> <div class="col-span-12 xl:col-span-6">
<div class="card flex flex-col items-center"> <div class="card flex flex-col items-center">
<div class="font-semibold text-xl mb-4">Radar</div> <div class="font-semibold text-xl mb-6">Radar</div>
<p-chart type="radar" [data]="radarData" [options]="radarOptions"></p-chart> <p-chart type="radar" [data]="radarData()" [options]="radarOptions()"></p-chart>
</div> </div>
</div> </div>
</p-fluid> </p-fluid>
` `
}) })
export class ChartDemo { export class ChartDemo {
lineData: any; layoutService = inject(LayoutService);
barData: any; lineData = signal<any>(null);
pieData: any; barData = signal<any>(null);
polarData: any; pieData = signal<any>(null);
radarData: any; polarData = signal<any>(null);
lineOptions: any; radarData = signal<any>(null);
barOptions: any; lineOptions = signal<any>(null);
pieOptions: any; barOptions = signal<any>(null);
polarOptions: any; pieOptions = signal<any>(null);
radarOptions: any; polarOptions = signal<any>(null);
subscription: Subscription; radarOptions = signal<any>(null);
constructor(private layoutService: LayoutService) {
this.subscription = this.layoutService.configUpdate$.pipe(debounceTime(25)).subscribe(() => {
this.initCharts();
});
}
ngOnInit() { chartEffect = effect(() => {
this.initCharts(); this.layoutService.layoutConfig().darkTheme;
} setTimeout(() => this.initCharts(), 150);
})
initCharts() { initCharts() {
const documentStyle = getComputedStyle(document.documentElement); const documentStyle = getComputedStyle(document.documentElement);
@@ -88,7 +82,7 @@ export class ChartDemo {
const textColorSecondary = documentStyle.getPropertyValue('--text-color-secondary'); const textColorSecondary = documentStyle.getPropertyValue('--text-color-secondary');
const surfaceBorder = documentStyle.getPropertyValue('--surface-border'); const surfaceBorder = documentStyle.getPropertyValue('--surface-border');
this.barData = { this.barData.set({
labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'], labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
datasets: [ datasets: [
{ {
@@ -104,9 +98,9 @@ export class ChartDemo {
data: [28, 48, 40, 19, 86, 27, 90] data: [28, 48, 40, 19, 86, 27, 90]
} }
] ]
}; });
this.barOptions = { this.barOptions.set({
maintainAspectRatio: false, maintainAspectRatio: false,
aspectRatio: 0.8, aspectRatio: 0.8,
plugins: { plugins: {
@@ -139,9 +133,9 @@ export class ChartDemo {
} }
} }
} }
}; });
this.pieData = { this.pieData.set({
labels: ['A', 'B', 'C'], labels: ['A', 'B', 'C'],
datasets: [ datasets: [
{ {
@@ -150,9 +144,9 @@ export class ChartDemo {
hoverBackgroundColor: [documentStyle.getPropertyValue('--p-indigo-400'), documentStyle.getPropertyValue('--p-purple-400'), documentStyle.getPropertyValue('--p-teal-400')] hoverBackgroundColor: [documentStyle.getPropertyValue('--p-indigo-400'), documentStyle.getPropertyValue('--p-purple-400'), documentStyle.getPropertyValue('--p-teal-400')]
} }
] ]
}; });
this.pieOptions = { this.pieOptions.set({
plugins: { plugins: {
legend: { legend: {
labels: { labels: {
@@ -161,9 +155,9 @@ export class ChartDemo {
} }
} }
} }
}; });
this.lineData = { this.lineData.set({
labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'], labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
datasets: [ datasets: [
{ {
@@ -183,9 +177,9 @@ export class ChartDemo {
tension: 0.4 tension: 0.4
} }
] ]
}; });
this.lineOptions = { this.lineOptions.set({
maintainAspectRatio: false, maintainAspectRatio: false,
aspectRatio: 0.8, aspectRatio: 0.8,
plugins: { plugins: {
@@ -215,9 +209,9 @@ export class ChartDemo {
} }
} }
} }
}; });
this.polarData = { this.polarData.set({
datasets: [ datasets: [
{ {
data: [11, 16, 7, 3], data: [11, 16, 7, 3],
@@ -226,9 +220,9 @@ export class ChartDemo {
} }
], ],
labels: ['Indigo', 'Purple', 'Teal', 'Orange'] labels: ['Indigo', 'Purple', 'Teal', 'Orange']
}; });
this.polarOptions = { this.polarOptions.set({
plugins: { plugins: {
legend: { legend: {
labels: { labels: {
@@ -239,17 +233,17 @@ export class ChartDemo {
scales: { scales: {
r: { r: {
grid: { grid: {
color: surfaceBorder, color: surfaceBorder
}, },
ticks: { ticks: {
display: false, display: false,
color: textColorSecondary color: textColorSecondary
}, }
}, }
}, }
}; });
this.radarData = { this.radarData.set({
labels: ['Eating', 'Drinking', 'Sleeping', 'Designing', 'Coding', 'Cycling', 'Running'], labels: ['Eating', 'Drinking', 'Sleeping', 'Designing', 'Coding', 'Cycling', 'Running'],
datasets: [ datasets: [
{ {
@@ -271,9 +265,9 @@ export class ChartDemo {
data: [28, 48, 40, 19, 96, 27, 100] data: [28, 48, 40, 19, 96, 27, 100]
} }
] ]
}; });
this.radarOptions = { this.radarOptions.set({
plugins: { plugins: {
legend: { legend: {
labels: { labels: {
@@ -291,12 +285,6 @@ export class ChartDemo {
} }
} }
} }
}; });
}
ngOnDestroy() {
if (this.subscription) {
this.subscription.unsubscribe();
}
} }
} }

View File

@@ -26,10 +26,10 @@ import { ListboxModule } from 'primeng/listbox';
import { InputGroupAddonModule } from 'primeng/inputgroupaddon'; import { InputGroupAddonModule } from 'primeng/inputgroupaddon';
import { TextareaModule } from 'primeng/textarea'; import { TextareaModule } from 'primeng/textarea';
import { ToggleButtonModule } from 'primeng/togglebutton'; import { ToggleButtonModule } from 'primeng/togglebutton';
import { CountryService } from '../service/country.service'; import { CountryService } from '@/app/pages/service/country.service';
import { NodeService } from '../service/node.service'; import { NodeService } from '@/app/pages/service/node.service';
import { TreeNode } from 'primeng/api'; import { TreeNode } from 'primeng/api';
import { Country } from '../service/customer.service'; import { Country } from '@/app/pages/service/customer.service';
@Component({ @Component({
selector: 'app-input-demo', selector: 'app-input-demo',

View File

@@ -7,7 +7,7 @@ import { OrderListModule } from 'primeng/orderlist';
import { PickListModule } from 'primeng/picklist'; import { PickListModule } from 'primeng/picklist';
import { SelectButtonModule } from 'primeng/selectbutton'; import { SelectButtonModule } from 'primeng/selectbutton';
import { TagModule } from 'primeng/tag'; import { TagModule } from 'primeng/tag';
import { Product, ProductService } from '../service/product.service'; import { Product, ProductService } from '@/app/pages/service/product.service';
@Component({ @Component({
selector: 'app-list-demo', selector: 'app-list-demo',

View File

@@ -1,12 +1,12 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { Component, OnInit } from '@angular/core'; import { Component, inject, OnInit, signal } from '@angular/core';
import { ButtonModule } from 'primeng/button'; import { ButtonModule } from 'primeng/button';
import { CarouselModule } from 'primeng/carousel'; import { CarouselModule } from 'primeng/carousel';
import { GalleriaModule } from 'primeng/galleria'; import { GalleriaModule } from 'primeng/galleria';
import { ImageModule } from 'primeng/image'; import { ImageModule } from 'primeng/image';
import { TagModule } from 'primeng/tag'; import { TagModule } from 'primeng/tag';
import { PhotoService } from '../service/photo.service'; import { PhotoService } from '@/app/pages/service/photo.service';
import { Product, ProductService } from '../service/product.service'; import { Product, ProductService } from '@/app/pages/service/product.service';
@Component({ @Component({
selector: 'app-media-demo', selector: 'app-media-demo',
@@ -14,7 +14,7 @@ import { Product, ProductService } from '../service/product.service';
imports: [CommonModule, CarouselModule, ButtonModule, GalleriaModule, ImageModule, TagModule], imports: [CommonModule, CarouselModule, ButtonModule, GalleriaModule, ImageModule, TagModule],
template: `<div class="card"> template: `<div class="card">
<div class="font-semibold text-xl mb-4">Carousel</div> <div class="font-semibold text-xl mb-4">Carousel</div>
<p-carousel [value]="products" [numVisible]="3" [numScroll]="3" [circular]="false" [responsiveOptions]="carouselResponsiveOptions"> <p-carousel [value]="products()" [numVisible]="3" [numScroll]="3" [circular]="false" [responsiveOptions]="carouselResponsiveOptions">
<ng-template let-product #item> <ng-template let-product #item>
<div class="border border-surface rounded-border m-2 p-4"> <div class="border border-surface rounded-border m-2 p-4">
<div class="mb-4"> <div class="mb-4">
@@ -45,7 +45,7 @@ import { Product, ProductService } from '../service/product.service';
<div class="card"> <div class="card">
<div class="font-semibold text-xl mb-4">Galleria</div> <div class="font-semibold text-xl mb-4">Galleria</div>
<p-galleria [value]="images" [responsiveOptions]="galleriaResponsiveOptions" [containerStyle]="{ 'max-width': '640px' }" [numVisible]="5"> <p-galleria [value]="images()" [responsiveOptions]="galleriaResponsiveOptions" [containerStyle]="{ 'max-width': '640px' }" [numVisible]="5">
<ng-template #item let-item> <ng-template #item let-item>
<img [src]="item.itemImageSrc" style="width:100%" /> <img [src]="item.itemImageSrc" style="width:100%" />
</ng-template> </ng-template>
@@ -57,9 +57,13 @@ import { Product, ProductService } from '../service/product.service';
providers: [ProductService, PhotoService] providers: [ProductService, PhotoService]
}) })
export class MediaDemo implements OnInit { export class MediaDemo implements OnInit {
products!: Product[]; productService = inject(ProductService);
images!: any[]; photoService = inject(PhotoService);
products = signal<Product[]>([]);
images = signal<any[]>([]);
galleriaResponsiveOptions: any[] = [ galleriaResponsiveOptions: any[] = [
{ {
@@ -98,19 +102,9 @@ export class MediaDemo implements OnInit {
} }
]; ];
constructor(
private productService: ProductService,
private photoService: PhotoService
) {}
ngOnInit() { ngOnInit() {
this.productService.getProductsSmall().then((products) => { this.productService.getProductsSmall().then((products) => this.products.set(products));
this.products = products; this.photoService.getImages().then((images) => this.images.set(images));
});
this.photoService.getImages().then((images) => {
this.images = images;
});
} }
getSeverity(status: string) { getSeverity(status: string) {

View File

@@ -10,7 +10,7 @@ import { InputTextModule } from 'primeng/inputtext';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { TooltipModule } from 'primeng/tooltip'; import { TooltipModule } from 'primeng/tooltip';
import { TableModule } from 'primeng/table'; import { TableModule } from 'primeng/table';
import { Product, ProductService } from '../service/product.service'; import { Product, ProductService } from '@/app/pages/service/product.service';
@Component({ @Component({
selector: 'app-overlay-demo', selector: 'app-overlay-demo',

View File

@@ -16,8 +16,8 @@ import { RippleModule } from 'primeng/ripple';
import { InputIconModule } from 'primeng/inputicon'; import { InputIconModule } from 'primeng/inputicon';
import { IconFieldModule } from 'primeng/iconfield'; import { IconFieldModule } from 'primeng/iconfield';
import { TagModule } from 'primeng/tag'; import { TagModule } from 'primeng/tag';
import { Customer, CustomerService, Representative } from '../service/customer.service'; import { Customer, CustomerService, Representative } from '@/app/pages/service/customer.service';
import { Product, ProductService } from '../service/product.service'; import { Product, ProductService } from '@/app/pages/service/product.service';
import {ObjectUtils} from "primeng/utils"; import {ObjectUtils} from "primeng/utils";
interface expandedRows { interface expandedRows {

View File

@@ -63,7 +63,7 @@ import {ButtonModule} from 'primeng/button';
</ng-template> </ng-template>
<ng-template #content let-event> <ng-template #content let-event>
<p-card [header]="event.status" [subheader]="event.date"> <p-card [header]="event.status" [subheader]="event.date">
<img *ngIf="event.image" [src]="'/images/product/' + event.image" [alt]="event.name" width="200" class="shadow" /> <img *ngIf="event.image" [src]="'/demo/images/product/' + event.image" [alt]="event.name" width="200" class="shadow" />
<p> <p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Inventore sed consequuntur error repudiandae numquam deserunt quisquam repellat libero asperiores earum nam nobis, culpa ratione quam perferendis esse, Lorem ipsum dolor sit amet, consectetur adipisicing elit. Inventore sed consequuntur error repudiandae numquam deserunt quisquam repellat libero asperiores earum nam nobis, culpa ratione quam perferendis esse,
cupiditate neque quas! cupiditate neque quas!

View File

@@ -1,10 +1,10 @@
import { Component, inject, OnInit } from '@angular/core'; import { Component, inject, OnInit, signal } from '@angular/core';
import { TreeNode } from 'primeng/api'; import { TreeNode } from 'primeng/api';
import { TreeModule } from 'primeng/tree'; import { TreeModule } from 'primeng/tree';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { TreeTableModule } from 'primeng/treetable'; import { TreeTableModule } from 'primeng/treetable';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { NodeService } from '../service/node.service'; import { NodeService } from '@/app/pages/service/node.service';
@Component({ @Component({
selector: 'app-tree-demo', selector: 'app-tree-demo',
@@ -13,12 +13,12 @@ import { NodeService } from '../service/node.service';
template: ` template: `
<div class="card"> <div class="card">
<div class="font-semibold text-xl">Tree</div> <div class="font-semibold text-xl">Tree</div>
<p-tree [value]="treeValue" selectionMode="checkbox" [(selection)]="selectedTreeValue"></p-tree> <p-tree [value]="treeValue()" selectionMode="checkbox" [(selection)]="selectedTreeValue"></p-tree>
</div> </div>
<div class="card"> <div class="card">
<div class="font-semibold text-xl mb-4">TreeTable</div> <div class="font-semibold text-xl mb-4">TreeTable</div>
<p-treetable [value]="treeTableValue" [columns]="cols" selectionMode="checkbox" [(selectionKeys)]="selectedTreeTableValue" dataKey="key" [scrollable]="true" [tableStyle]="{ 'min-width': '50rem' }"> <p-treetable [value]="treeTableValue()" [columns]="cols" selectionMode="checkbox" [(selectionKeys)]="selectedTreeTableValue" dataKey="key" [scrollable]="true" [tableStyle]="{ 'min-width': '50rem' }">
<ng-template #header let-columns> <ng-template #header let-columns>
<tr> <tr>
<th *ngFor="let col of columns"> <th *ngFor="let col of columns">
@@ -43,9 +43,9 @@ import { NodeService } from '../service/node.service';
providers: [NodeService] providers: [NodeService]
}) })
export class TreeDemo implements OnInit { export class TreeDemo implements OnInit {
treeValue: TreeNode[] = []; treeValue = signal<TreeNode[]>([]);
treeTableValue: TreeNode[] = []; treeTableValue = signal<TreeNode[]>([]);
selectedTreeValue: TreeNode[] = []; selectedTreeValue: TreeNode[] = [];
@@ -56,8 +56,8 @@ export class TreeDemo implements OnInit {
nodeService = inject(NodeService); nodeService = inject(NodeService);
ngOnInit() { ngOnInit() {
this.nodeService.getFiles().then((files) => (this.treeValue = files)); this.nodeService.getFiles().then((files) => this.treeValue.set(files));
this.nodeService.getTreeTableNodes().then((files: any) => (this.treeTableValue = files)); this.nodeService.getTreeTableNodes().then((files: any) => this.treeTableValue.set(files));
this.cols = [ this.cols = [
{ field: 'name', header: 'Name' }, { field: 'name', header: 'Name' },

View File

@@ -20,7 +20,7 @@
"baseUrl": "./", "baseUrl": "./",
"paths": { "paths": {
"@/*": [ "@/*": [
"src/app/*" "src/*"
] ]
} }
}, },