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

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

View File

@@ -2,7 +2,7 @@ import {Component, computed, inject, input} from '@angular/core';
import { ButtonModule } from 'primeng/button';
import { StyleClassModule } from 'primeng/styleclass';
import { AppConfigurator } from './app.configurator';
import { LayoutService } from '../service/layout.service';
import { LayoutService } from '@/app/layout/service/layout.service';
import {CommonModule} from "@angular/common";
@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 { NavigationEnd, Router, RouterModule } from '@angular/router';
import { filter, Subscription } from 'rxjs';
import { RouterModule } from '@angular/router';
import { AppTopbar } from './app.topbar';
import { AppSidebar } from './app.sidebar';
import { AppFooter } from './app.footer';
import { LayoutService } from '../service/layout.service';
import { LayoutService } from '@/app/layout/service/layout.service';
@Component({
selector: 'app-layout',
standalone: true,
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-sidebar></app-sidebar>
<div class="layout-main-container">
@@ -20,92 +19,32 @@ import { LayoutService } from '../service/layout.service';
</div>
<app-footer></app-footer>
</div>
<div class="layout-mask animate-fadein"></div>
<div class="layout-mask"></div>
</div> `
})
export class AppLayout {
overlayMenuOpenSubscription: Subscription;
layoutService = inject(LayoutService);
menuOutsideClickListener: any;
@ViewChild(AppSidebar) appSidebar!: AppSidebar;
@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();
constructor() {
effect(() => {
const state = this.layoutService.layoutState();
if (state.mobileMenuActive) {
document.body.classList.add('blocked-scroll');
} else {
document.body.classList.remove('blocked-scroll');
}
});
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');
} else {
document.body.className += ' blocked-scroll';
}
}
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'), ' ');
}
}
get containerClass() {
containerClass = computed(() => {
const config = this.layoutService.layoutConfig();
const state = this.layoutService.layoutState();
return {
'layout-overlay': this.layoutService.layoutConfig().menuMode === 'overlay',
'layout-static': this.layoutService.layoutConfig().menuMode === 'static',
'layout-static-inactive': this.layoutService.layoutState().staticMenuDesktopInactive && this.layoutService.layoutConfig().menuMode === 'static',
'layout-overlay-active': this.layoutService.layoutState().overlayMenuActive,
'layout-mobile-active': this.layoutService.layoutState().staticMenuMobileActive
'layout-overlay': config.menuMode === 'overlay',
'layout-static': config.menuMode === 'static',
'layout-static-inactive': state.staticMenuDesktopInactive && config.menuMode === 'static',
'layout-overlay-active': state.overlayMenuActive,
'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,
imports: [CommonModule, AppMenuitem, RouterModule],
template: `<ul class="layout-menu">
<ng-container *ngFor="let item of model; let i = index">
<li app-menuitem *ngIf="!item.separator" [item]="item" [index]="i" [root]="true"></li>
<li *ngIf="item.separator" class="menu-separator"></li>
</ng-container>
</ul> `
@for (item of model; track item.label) {
@if (!item.separator) {
<li app-menuitem [item]="item" [root]="true"></li>
} @else {
<li class="menu-separator"></li>
}
}
</ul> `,
})
export class AppMenu {
model: MenuItem[] = [];
@@ -47,7 +50,7 @@ export class AppMenu {
{
label: 'Pages',
icon: 'pi pi-fw pi-briefcase',
routerLink: ['/pages'],
path: '/pages',
items: [
{
label: 'Landing',
@@ -57,6 +60,7 @@ export class AppMenu {
{
label: 'Auth',
icon: 'pi pi-fw pi-user',
path: '/auth',
items: [
{
label: 'Login',
@@ -94,14 +98,17 @@ export class AppMenu {
},
{
label: 'Hierarchy',
path: '/hierarchy',
items: [
{
label: 'Submenu 1',
icon: 'pi pi-fw pi-bookmark',
path: '/hierarchy/submenu_1',
items: [
{
label: 'Submenu 1.1',
icon: 'pi pi-fw pi-bookmark',
path: '/hierarchy/submenu_1/submenu_1_1',
items: [
{ label: 'Submenu 1.1.1', 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',
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' }]
}
]
@@ -118,10 +126,12 @@ export class AppMenu {
{
label: 'Submenu 2',
icon: 'pi pi-fw pi-bookmark',
path: '/hierarchy/submenu_2',
items: [
{
label: 'Submenu 2.1',
icon: 'pi pi-fw pi-bookmark',
path: '/hierarchy/submenu_2/submenu_2_1',
items: [
{ label: 'Submenu 2.1.1', 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',
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' }]
}
]

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

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 { LayoutService } from '@/app/layout/service/layout.service';
@Component({
selector: 'app-sidebar',
standalone: true,
imports: [AppMenu],
template: ` <div class="layout-sidebar">
<app-menu></app-menu>
</div>`
imports: [AppMenu, RouterModule],
template: `
<div class="layout-sidebar">
<app-menu></app-menu>
</div>
`
})
export class AppSidebar {
constructor(public el: ElementRef) {}
export class AppSidebar implements OnInit, OnDestroy {
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 { RouterModule } from '@angular/router';
import { CommonModule } from '@angular/common';
import { StyleClassModule } from 'primeng/styleclass';
import { AppConfigurator } from './app.configurator';
import { LayoutService } from '../service/layout.service';
import { LayoutService } from '@/app/layout/service/layout.service';
@Component({
selector: 'app-topbar',
@@ -84,9 +84,12 @@ import { LayoutService } from '../service/layout.service';
export class AppTopbar {
items!: MenuItem[];
constructor(public layoutService: LayoutService) {}
layoutService = inject(LayoutService);
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 { Subject } from 'rxjs';
export interface layoutConfig {
preset?: string;
primary?: string;
surface?: string | undefined | null;
darkTheme?: boolean;
menuMode?: string;
export interface LayoutConfig {
preset: string;
primary: string;
surface: string | undefined | null;
darkTheme: boolean;
menuMode: string;
}
interface LayoutState {
staticMenuDesktopInactive?: boolean;
overlayMenuActive?: boolean;
configSidebarVisible?: boolean;
staticMenuMobileActive?: boolean;
menuHoverActive?: boolean;
}
interface MenuChangeEvent {
key: string;
routeEvent?: boolean;
staticMenuDesktopInactive: boolean;
overlayMenuActive: boolean;
configSidebarVisible: boolean;
mobileMenuActive: boolean;
menuHoverActive: boolean;
activePath: string | null;
}
@Injectable({
providedIn: 'root'
})
export class LayoutService {
_config: layoutConfig = {
layoutConfig = signal<LayoutConfig>({
preset: 'Aura',
primary: 'emerald',
surface: null,
darkTheme: false,
menuMode: 'static'
};
});
_state: LayoutState = {
layoutState = signal<LayoutState>({
staticMenuDesktopInactive: false,
overlayMenuActive: false,
configSidebarVisible: false,
staticMenuMobileActive: false,
menuHoverActive: false
};
mobileMenuActive: false,
menuHoverActive: false,
activePath: null
});
layoutConfig = signal<layoutConfig>(this._config);
theme = computed(() => (this.layoutConfig().darkTheme ? 'light' : 'dark'));
layoutState = signal<LayoutState>(this._state);
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);
isSidebarActive = computed(() => this.layoutState().overlayMenuActive || this.layoutState().mobileMenuActive);
isDarkTheme = computed(() => this.layoutConfig().darkTheme);
@@ -79,13 +55,6 @@ export class LayoutService {
private initialized = false;
constructor() {
effect(() => {
const config = this.layoutConfig();
if (config) {
this.onConfigUpdate();
}
});
effect(() => {
const config = this.layoutConfig();
@@ -98,28 +67,23 @@ export class LayoutService {
});
}
private handleDarkModeTransition(config: layoutConfig): void {
if ((document as any).startViewTransition) {
private handleDarkModeTransition(config: LayoutConfig): void {
const supportsViewTransition = 'startViewTransition' in document;
if (supportsViewTransition) {
this.startViewTransition(config);
} else {
this.toggleDarkMode(config);
this.onTransitionEnd();
}
}
private startViewTransition(config: layoutConfig): void {
const transition = (document as any).startViewTransition(() => {
private startViewTransition(config: LayoutConfig): void {
document.startViewTransition(() => {
this.toggleDarkMode(config);
});
transition.ready
.then(() => {
this.onTransitionEnd();
})
.catch(() => {});
}
toggleDarkMode(config?: layoutConfig): void {
toggleDarkMode(config?: LayoutConfig): void {
const _config = config || this.layoutConfig();
if (_config.darkTheme) {
document.documentElement.classList.add('app-dark');
@@ -128,33 +92,26 @@ export class LayoutService {
}
}
private onTransitionEnd() {
this.transitionComplete.set(true);
setTimeout(() => {
this.transitionComplete.set(false);
});
}
onMenuToggle() {
if (this.isOverlay()) {
this.layoutState.update((prev) => ({ ...prev, overlayMenuActive: !this.layoutState().overlayMenuActive }));
if (this.layoutState().overlayMenuActive) {
this.overlayOpen.next(null);
}
}
if (this.isDesktop()) {
this.layoutState.update((prev) => ({ ...prev, staticMenuDesktopInactive: !this.layoutState().staticMenuDesktopInactive }));
} else {
this.layoutState.update((prev) => ({ ...prev, staticMenuMobileActive: !this.layoutState().staticMenuMobileActive }));
if (this.layoutState().staticMenuMobileActive) {
this.overlayOpen.next(null);
}
this.layoutState.update((prev) => ({ ...prev, mobileMenuActive: !this.layoutState().mobileMenuActive }));
}
}
showConfigSidebar() {
this.layoutState.update((prev) => ({ ...prev, configSidebarVisible: true }));
}
hideConfigSidebar() {
this.layoutState.update((prev) => ({ ...prev, configSidebarVisible: false }));
}
isDesktop() {
return window.innerWidth > 991;
}
@@ -162,17 +119,4 @@ export class LayoutService {
isMobile() {
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 { IconFieldModule } from 'primeng/iconfield';
import { ConfirmDialogModule } from 'primeng/confirmdialog';
import { Product, ProductService } from '../service/product.service';
import { Product, ProductService } from '@/app/pages/service/product.service';
interface Column {
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 { TableModule } from 'primeng/table';
import { ButtonModule } from 'primeng/button';
import { CommonModule } from '@angular/common';
import { Product, ProductService } from '../../service/product.service';
import { Product, ProductService } from '@/app/pages/service/product.service';
@Component({
standalone: true,
@@ -11,7 +11,7 @@ import { Product, ProductService } from '../../service/product.service';
imports: [CommonModule, TableModule, ButtonModule, RippleModule],
template: `<div class="card mb-8!">
<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>
<tr>
<th>Image</th>
@@ -37,11 +37,11 @@ import { Product, ProductService } from '../../service/product.service';
providers: [ProductService]
})
export class RecentSalesWidget {
products!: Product[];
products = signal<Product[]>([]);
constructor(private productService: ProductService) {}
productService = inject(ProductService);
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 { debounceTime, Subscription } from 'rxjs';
import { LayoutService } from '../../../layout/service/layout.service';
import { LayoutService } from '@/app/layout/service/layout.service';
@Component({
standalone: true,
@@ -9,24 +8,29 @@ import { LayoutService } from '../../../layout/service/layout.service';
imports: [ChartModule],
template: `<div class="card mb-8!">
<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>`
})
export class RevenueStreamWidget {
chartData: any;
layoutService = inject(LayoutService);
chartOptions: any;
chartData = signal<any>(null);
subscription!: Subscription;
chartOptions = signal<any>(null);
constructor(public layoutService: LayoutService) {
this.subscription = this.layoutService.configUpdate$.pipe(debounceTime(25)).subscribe(() => {
this.initChart();
constructor() {
afterNextRender(() => {
setTimeout(() => {
this.initChart();
}, 150);
});
}
ngOnInit() {
this.initChart();
effect(() => {
this.layoutService.layoutConfig().darkTheme;
setTimeout(() => {
this.initChart();
}, 150);
});
}
initChart() {
@@ -35,7 +39,7 @@ export class RevenueStreamWidget {
const borderColor = documentStyle.getPropertyValue('--surface-border');
const textMutedColor = documentStyle.getPropertyValue('--text-color-secondary');
this.chartData = {
this.chartData.set({
labels: ['Q1', 'Q2', 'Q3', 'Q4'],
datasets: [
{
@@ -67,9 +71,9 @@ export class RevenueStreamWidget {
barThickness: 32
}
]
};
});
this.chartOptions = {
this.chartOptions.set({
maintainAspectRatio: false,
aspectRatio: 0.8,
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="font-semibold text-2xl mb-4">Documentation</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">
<code>npm install -g &#64;angular/cli</code></pre>
<p class="text-lg mb-4">

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,12 +1,12 @@
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 { CarouselModule } from 'primeng/carousel';
import { GalleriaModule } from 'primeng/galleria';
import { ImageModule } from 'primeng/image';
import { TagModule } from 'primeng/tag';
import { PhotoService } from '../service/photo.service';
import { Product, ProductService } from '../service/product.service';
import { PhotoService } from '@/app/pages/service/photo.service';
import { Product, ProductService } from '@/app/pages/service/product.service';
@Component({
selector: 'app-media-demo',
@@ -14,7 +14,7 @@ import { Product, ProductService } from '../service/product.service';
imports: [CommonModule, CarouselModule, ButtonModule, GalleriaModule, ImageModule, TagModule],
template: `<div class="card">
<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>
<div class="border border-surface rounded-border m-2 p-4">
<div class="mb-4">
@@ -45,7 +45,7 @@ import { Product, ProductService } from '../service/product.service';
<div class="card">
<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>
<img [src]="item.itemImageSrc" style="width:100%" />
</ng-template>
@@ -57,9 +57,13 @@ import { Product, ProductService } from '../service/product.service';
providers: [ProductService, PhotoService]
})
export class MediaDemo implements OnInit {
products!: Product[];
productService = inject(ProductService);
images!: any[];
photoService = inject(PhotoService);
products = signal<Product[]>([]);
images = signal<any[]>([]);
galleriaResponsiveOptions: any[] = [
{
@@ -98,19 +102,9 @@ export class MediaDemo implements OnInit {
}
];
constructor(
private productService: ProductService,
private photoService: PhotoService
) {}
ngOnInit() {
this.productService.getProductsSmall().then((products) => {
this.products = products;
});
this.photoService.getImages().then((images) => {
this.images = images;
});
this.productService.getProductsSmall().then((products) => this.products.set(products));
this.photoService.getImages().then((images) => this.images.set(images));
}
getSeverity(status: string) {

View File

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

View File

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

View File

@@ -63,7 +63,7 @@ import {ButtonModule} from 'primeng/button';
</ng-template>
<ng-template #content let-event>
<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>
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!

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 { TreeModule } from 'primeng/tree';
import { FormsModule } from '@angular/forms';
import { TreeTableModule } from 'primeng/treetable';
import { CommonModule } from '@angular/common';
import { NodeService } from '../service/node.service';
import { NodeService } from '@/app/pages/service/node.service';
@Component({
selector: 'app-tree-demo',
@@ -13,12 +13,12 @@ import { NodeService } from '../service/node.service';
template: `
<div class="card">
<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 class="card">
<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>
<tr>
<th *ngFor="let col of columns">
@@ -43,9 +43,9 @@ import { NodeService } from '../service/node.service';
providers: [NodeService]
})
export class TreeDemo implements OnInit {
treeValue: TreeNode[] = [];
treeValue = signal<TreeNode[]>([]);
treeTableValue: TreeNode[] = [];
treeTableValue = signal<TreeNode[]>([]);
selectedTreeValue: TreeNode[] = [];
@@ -56,8 +56,8 @@ export class TreeDemo implements OnInit {
nodeService = inject(NodeService);
ngOnInit() {
this.nodeService.getFiles().then((files) => (this.treeValue = files));
this.nodeService.getTreeTableNodes().then((files: any) => (this.treeTableValue = files));
this.nodeService.getFiles().then((files) => this.treeValue.set(files));
this.nodeService.getTreeTableNodes().then((files: any) => this.treeTableValue.set(files));
this.cols = [
{ field: 'name', header: 'Name' },