feat: add logo image and RTL support styles

- Added logo image to assets.
- Implemented RTL support styles in rtlSupport.scss for various components including breadcrumb, datatable, and tree node toggle button.

chore: configure environment files for production, staging, and development

- Created environment.prod.ts for production settings.
- Created environment.staging.ts for staging settings.
- Created environment.ts for local development settings.

feat: define custom theme preset

- Added presets.ts to define a custom theme preset using PrimeUIX with specific component styles and semantic colors.

chore: set up proxy configuration for local development
This commit is contained in:
2025-12-04 21:07:18 +03:30
parent c58210cdbd
commit 07fec02ea1
164 changed files with 20175 additions and 735 deletions
+143 -132
View File
@@ -1,178 +1,189 @@
import { Injectable, effect, signal, computed } from '@angular/core';
import { Injectable, computed, effect, signal } from '@angular/core';
import { Subject } from 'rxjs';
export interface layoutConfig {
preset?: string;
primary?: string;
surface?: string | undefined | null;
darkTheme?: boolean;
menuMode?: string;
preset?: string;
primary?: string;
surface?: string | undefined | null;
darkTheme?: boolean;
menuMode?: string;
}
interface LayoutState {
staticMenuDesktopInactive?: boolean;
overlayMenuActive?: boolean;
configSidebarVisible?: boolean;
staticMenuMobileActive?: boolean;
menuHoverActive?: boolean;
staticMenuDesktopInactive?: boolean;
overlayMenuActive?: boolean;
configSidebarVisible?: boolean;
staticMenuMobileActive?: boolean;
menuHoverActive?: boolean;
}
interface MenuChangeEvent {
key: string;
routeEvent?: boolean;
key: string;
routeEvent?: boolean;
}
@Injectable({
providedIn: 'root'
providedIn: 'root',
})
export class LayoutService {
_config: layoutConfig = {
preset: 'Aura',
primary: 'emerald',
surface: null,
darkTheme: false,
menuMode: 'static'
};
_config: layoutConfig = {
preset: 'Aura',
primary: 'violet',
surface: null,
darkTheme: localStorage.getItem('isDarkTheme') === 'true',
menuMode: 'static',
};
_state: LayoutState = {
staticMenuDesktopInactive: false,
overlayMenuActive: false,
configSidebarVisible: false,
staticMenuMobileActive: false,
menuHoverActive: false
};
_state: LayoutState = {
staticMenuDesktopInactive: false,
overlayMenuActive: false,
configSidebarVisible: false,
staticMenuMobileActive: false,
menuHoverActive: false,
};
layoutConfig = signal<layoutConfig>(this._config);
layoutConfig = signal<layoutConfig>(this._config);
layoutState = signal<LayoutState>(this._state);
layoutState = signal<LayoutState>(this._state);
private configUpdate = new Subject<layoutConfig>();
private configUpdate = new Subject<layoutConfig>();
private overlayOpen = new Subject<any>();
private overlayOpen = new Subject<any>();
private menuSource = new Subject<MenuChangeEvent>();
private menuSource = new Subject<MenuChangeEvent>();
private resetSource = new Subject();
private resetSource = new Subject();
menuSource$ = this.menuSource.asObservable();
menuSource$ = this.menuSource.asObservable();
resetSource$ = this.resetSource.asObservable();
resetSource$ = this.resetSource.asObservable();
configUpdate$ = this.configUpdate.asObservable();
configUpdate$ = this.configUpdate.asObservable();
overlayOpen$ = this.overlayOpen.asObservable();
overlayOpen$ = this.overlayOpen.asObservable();
theme = computed(() => (this.layoutConfig()?.darkTheme ? 'light' : 'dark'));
theme = computed(() => (this.layoutConfig()?.darkTheme ? 'light' : 'dark'));
isSidebarActive = computed(() => this.layoutState().overlayMenuActive || this.layoutState().staticMenuMobileActive);
isSidebarActive = computed(
() => this.layoutState().overlayMenuActive || this.layoutState().staticMenuMobileActive,
);
isDarkTheme = computed(() => this.layoutConfig().darkTheme);
isDarkTheme = computed(() => this.layoutConfig().darkTheme);
getPrimary = computed(() => this.layoutConfig().primary);
getPrimary = computed(() => this.layoutConfig().primary);
getSurface = computed(() => this.layoutConfig().surface);
getSurface = computed(() => this.layoutConfig().surface);
isOverlay = computed(() => this.layoutConfig().menuMode === 'overlay');
isOverlay = computed(() => this.layoutConfig().menuMode === 'overlay');
transitionComplete = signal<boolean>(false);
transitionComplete = signal<boolean>(false);
private initialized = false;
private initialized = false;
constructor() {
effect(() => {
const config = this.layoutConfig();
if (config) {
this.onConfigUpdate();
}
});
constructor() {
effect(() => {
const config = this.layoutConfig();
if (config) {
this.onConfigUpdate();
}
});
effect(() => {
const config = this.layoutConfig();
effect(() => {
const config = this.layoutConfig();
if (!this.initialized || !config) {
this.initialized = true;
return;
}
if (!this.initialized || !config) {
this.initialized = true;
return;
}
this.handleDarkModeTransition(config);
});
this.handleDarkModeTransition(config);
});
}
private handleDarkModeTransition(config: layoutConfig): void {
if ((document as any).startViewTransition) {
this.startViewTransition(config);
} else {
this.toggleDarkMode(config);
this.onTransitionEnd();
}
}
private startViewTransition(config: layoutConfig): void {
const transition = (document as any).startViewTransition(() => {
this.toggleDarkMode(config);
});
transition.ready
.then(() => {
this.onTransitionEnd();
})
.catch(() => {});
}
toggleDarkMode(config?: layoutConfig): void {
const _config = config || this.layoutConfig();
if (_config.darkTheme) {
document.documentElement.classList.add('app-dark');
} else {
document.documentElement.classList.remove('app-dark');
}
}
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);
}
}
private handleDarkModeTransition(config: layoutConfig): void {
if ((document as any).startViewTransition) {
this.startViewTransition(config);
} else {
this.toggleDarkMode(config);
this.onTransitionEnd();
}
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);
}
}
}
private startViewTransition(config: layoutConfig): void {
const transition = (document as any).startViewTransition(() => {
this.toggleDarkMode(config);
});
isDesktop() {
return window.innerWidth > 991;
}
transition.ready
.then(() => {
this.onTransitionEnd();
})
.catch(() => {});
}
isMobile() {
return !this.isDesktop();
}
toggleDarkMode(config?: layoutConfig): void {
const _config = config || this.layoutConfig();
if (_config.darkTheme) {
document.documentElement.classList.add('app-dark');
} else {
document.documentElement.classList.remove('app-dark');
}
}
onConfigUpdate() {
this._config = { ...this.layoutConfig() };
this.configUpdate.next(this.layoutConfig());
}
private onTransitionEnd() {
this.transitionComplete.set(true);
setTimeout(() => {
this.transitionComplete.set(false);
});
}
onMenuStateChange(event: MenuChangeEvent) {
this.menuSource.next(event);
}
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);
}
}
}
isDesktop() {
return window.innerWidth > 991;
}
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);
}
reset() {
this.resetSource.next(true);
}
}