@es.framework/ng.ui.theme 2.0.60 → 2.0.61
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -4,12 +4,12 @@ import { Subject, Observable, lastValueFrom, BehaviorSubject, map, filter } from
|
|
|
4
4
|
import * as i3$3 from '@es.framework/ng.core/services';
|
|
5
5
|
import { ConfigStateService, SessionStateService, LocalizationService, LocalStorageService, NavItemsService, UserMenuService, RoutesService, SubscriptionService, ReplaceableComponentsService } from '@es.framework/ng.core/services';
|
|
6
6
|
import { eLayoutType } from '@es.framework/ng.core/models';
|
|
7
|
-
import * as i2$
|
|
7
|
+
import * as i2$2 from '@angular/router';
|
|
8
8
|
import { Router, RouterModule, RouterOutlet, NavigationEnd } from '@angular/router';
|
|
9
9
|
import * as i1$1 from '@angular/common';
|
|
10
10
|
import { isPlatformBrowser, CommonModule } from '@angular/common';
|
|
11
11
|
import { StyleClassModule } from 'primeng/styleclass';
|
|
12
|
-
import * as i3$
|
|
12
|
+
import * as i3$1 from 'primeng/avatar';
|
|
13
13
|
import { AvatarModule } from 'primeng/avatar';
|
|
14
14
|
import * as i5 from 'primeng/menu';
|
|
15
15
|
import { MenuModule } from 'primeng/menu';
|
|
@@ -18,8 +18,8 @@ import { ButtonModule } from 'primeng/button';
|
|
|
18
18
|
import { OverlayModule } from 'primeng/overlay';
|
|
19
19
|
import { VisibleDirective, HasPermissionDirective, ReplaceableTemplateDirective } from '@es.framework/ng.core/directives';
|
|
20
20
|
import { TranslatePipe, ToInjectorPipe } from '@es.framework/ng.core/pipes';
|
|
21
|
-
import { toSignal } from '@angular/core/rxjs-interop';
|
|
22
|
-
import * as
|
|
21
|
+
import { toSignal, takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
22
|
+
import * as i2$1 from '@angular/forms';
|
|
23
23
|
import { FormsModule } from '@angular/forms';
|
|
24
24
|
import { SelectModule } from 'primeng/select';
|
|
25
25
|
import * as i1 from 'primeng/popover';
|
|
@@ -30,11 +30,11 @@ import Aura from '@primeng/themes/aura';
|
|
|
30
30
|
import Lara from '@primeng/themes/lara';
|
|
31
31
|
import Nora from '@primeng/themes/nora';
|
|
32
32
|
import { PrimeNG } from 'primeng/config';
|
|
33
|
-
import * as i3
|
|
33
|
+
import * as i3 from 'primeng/selectbutton';
|
|
34
34
|
import { SelectButtonModule } from 'primeng/selectbutton';
|
|
35
35
|
import { AuthService } from '@es.framework/ng.core/abstracts';
|
|
36
36
|
import * as i4 from 'primeng/api';
|
|
37
|
-
import * as
|
|
37
|
+
import * as i3$2 from 'primeng/tooltip';
|
|
38
38
|
import { TooltipModule } from 'primeng/tooltip';
|
|
39
39
|
import Swal from 'sweetalert2';
|
|
40
40
|
import { NAVIGATE_TO_MANAGE_PROFILE } from '@es.framework/ng.core/tokens';
|
|
@@ -721,7 +721,7 @@ class AppConfigurator {
|
|
|
721
721
|
</div>
|
|
722
722
|
}
|
|
723
723
|
</div>
|
|
724
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type:
|
|
724
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: SelectButtonModule }, { kind: "component", type: i3.SelectButton, selector: "p-selectButton, p-selectbutton, p-select-button", inputs: ["options", "optionLabel", "optionValue", "optionDisabled", "unselectable", "tabindex", "multiple", "allowEmpty", "styleClass", "ariaLabelledBy", "dataKey", "autofocus", "size", "fluid"], outputs: ["onOptionClick", "onChange"] }] });
|
|
725
725
|
}
|
|
726
726
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AppConfigurator, decorators: [{
|
|
727
727
|
type: Component,
|
|
@@ -1067,7 +1067,7 @@ class NavItemsComponent {
|
|
|
1067
1067
|
</button> -->
|
|
1068
1068
|
</div>
|
|
1069
1069
|
</header>
|
|
1070
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i2$
|
|
1070
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i2$2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: StyleClassModule }, { kind: "component", type: AppConfigurator, selector: "app-configurator" }, { kind: "component", type: LanguagesComponent, selector: "abp-languages" }, { kind: "ngmodule", type: AvatarModule }, { kind: "component", type: i3$1.Avatar, selector: "p-avatar", inputs: ["label", "icon", "image", "size", "shape", "styleClass", "ariaLabel", "ariaLabelledBy"], outputs: ["onImageError"] }, { kind: "directive", type: i4.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: MenuModule }, { kind: "component", type: i5.Menu, selector: "p-menu", inputs: ["model", "popup", "style", "styleClass", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "ariaLabel", "ariaLabelledBy", "id", "tabindex", "appendTo"], outputs: ["onShow", "onHide", "onBlur", "onFocus"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i2.ButtonDirective, selector: "[pButton]", inputs: ["ptButtonDirective", "hostName", "text", "plain", "raised", "size", "outlined", "rounded", "iconPos", "loadingIcon", "fluid", "label", "icon", "loading", "buttonProps", "severity"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: VisibleDirective, selector: "[frameworkVisible]", inputs: ["frameworkVisible"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "component", type: i1.Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions"], outputs: ["onShow", "onHide"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "pipe", type: ToInjectorPipe, name: "toInjector" }] });
|
|
1071
1071
|
}
|
|
1072
1072
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: NavItemsComponent, decorators: [{
|
|
1073
1073
|
type: Component,
|
|
@@ -1255,91 +1255,74 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
1255
1255
|
}]
|
|
1256
1256
|
}], ctorParameters: () => [] });
|
|
1257
1257
|
|
|
1258
|
-
class
|
|
1259
|
-
subSidebarVisible = false;
|
|
1260
|
-
sidebarVisible = true;
|
|
1261
|
-
isMobile = false;
|
|
1262
|
-
searchText = ''; // ✅ البحث
|
|
1258
|
+
class RoutesMenuService {
|
|
1263
1259
|
router = inject(Router);
|
|
1264
|
-
localization = inject(LocalizationService);
|
|
1265
|
-
layoutService = inject(LayoutService);
|
|
1266
1260
|
routesService = inject(RoutesService);
|
|
1267
|
-
|
|
1268
|
-
|
|
1261
|
+
layoutService = inject(LayoutService);
|
|
1262
|
+
sidebarVisible = true;
|
|
1263
|
+
subSidebarVisible = false;
|
|
1264
|
+
isMobile = false;
|
|
1265
|
+
showTreeSearch = false;
|
|
1266
|
+
searchText = '';
|
|
1269
1267
|
_menuItemsSubject = new BehaviorSubject([]);
|
|
1268
|
+
expandedStateBeforeSearch = null;
|
|
1270
1269
|
menuItems$ = this._menuItemsSubject.asObservable();
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
const items = this.buildItems(routes);
|
|
1274
|
-
// ✅ فتح الشجرة كاملة عند الدخول للنظام
|
|
1275
|
-
// this.setExpandedRecursive(items, true);
|
|
1276
|
-
return items;
|
|
1277
|
-
})).subscribe(items => this._menuItemsSubject.next(items));
|
|
1278
|
-
}
|
|
1279
|
-
ngOnInit() {
|
|
1280
|
-
this.checkViewport();
|
|
1281
|
-
this.secondToLastLevel$ = this.menuItems$.pipe(map(items => {
|
|
1282
|
-
const result = [];
|
|
1283
|
-
for (const item of items) {
|
|
1284
|
-
if (item.items && item.items.length > 0) {
|
|
1285
|
-
result.push(...item.items);
|
|
1286
|
-
}
|
|
1287
|
-
}
|
|
1288
|
-
return result;
|
|
1289
|
-
}));
|
|
1290
|
-
// ✅ إظهار الشجرة الفرعية مفتوحة في الديسكتوب عند الدخول
|
|
1291
|
-
if (!this.isMobile) {
|
|
1292
|
-
this.sidebarVisible = true;
|
|
1293
|
-
this.subSidebarVisible = true;
|
|
1294
|
-
}
|
|
1295
|
-
}
|
|
1296
|
-
setExpandedRecursive(items, expanded) {
|
|
1270
|
+
secondToLastLevel$ = this.menuItems$.pipe(map((items) => {
|
|
1271
|
+
const result = [];
|
|
1297
1272
|
for (const item of items) {
|
|
1298
|
-
item.expanded = expanded;
|
|
1299
1273
|
if (item.items?.length) {
|
|
1300
|
-
|
|
1274
|
+
result.push(...item.items);
|
|
1301
1275
|
}
|
|
1302
1276
|
}
|
|
1277
|
+
return result;
|
|
1278
|
+
}));
|
|
1279
|
+
constructor() {
|
|
1280
|
+
this.routesService.visible$
|
|
1281
|
+
.pipe(map((routes) => this.buildItems(routes)), takeUntilDestroyed())
|
|
1282
|
+
.subscribe((items) => this.setItems(items));
|
|
1283
|
+
}
|
|
1284
|
+
get items() {
|
|
1285
|
+
return this._menuItemsSubject.value;
|
|
1286
|
+
}
|
|
1287
|
+
setItems(items) {
|
|
1288
|
+
this.setExpandedByLevel(items);
|
|
1289
|
+
this._menuItemsSubject.next(items);
|
|
1290
|
+
}
|
|
1291
|
+
refresh() {
|
|
1292
|
+
this._menuItemsSubject.next([...this.items]);
|
|
1303
1293
|
}
|
|
1304
1294
|
expandAll() {
|
|
1305
|
-
|
|
1306
|
-
this.setExpandedRecursive(items, true);
|
|
1295
|
+
this.setExpandedRecursive(this.items, true);
|
|
1307
1296
|
this.subSidebarVisible = true;
|
|
1308
|
-
this.
|
|
1297
|
+
this.refresh();
|
|
1309
1298
|
}
|
|
1310
1299
|
collapseAll() {
|
|
1311
|
-
|
|
1312
|
-
this.
|
|
1313
|
-
this._menuItemsSubject.next([...items]);
|
|
1300
|
+
this.setExpandedRecursive(this.items, false);
|
|
1301
|
+
this.refresh();
|
|
1314
1302
|
}
|
|
1315
|
-
|
|
1316
|
-
|
|
1303
|
+
search(keyword) {
|
|
1304
|
+
if (!this.expandedStateBeforeSearch) {
|
|
1305
|
+
this.expandedStateBeforeSearch = this.captureExpandedState(this.items);
|
|
1306
|
+
}
|
|
1307
|
+
this.searchText = keyword;
|
|
1317
1308
|
if (!this.searchText.trim()) {
|
|
1318
|
-
|
|
1319
|
-
this.
|
|
1320
|
-
this._menuItemsSubject.next([...items]);
|
|
1309
|
+
this.restoreExpandedStateBeforeSearch();
|
|
1310
|
+
this.refresh();
|
|
1321
1311
|
return;
|
|
1322
1312
|
}
|
|
1323
|
-
|
|
1324
|
-
this.
|
|
1325
|
-
this._menuItemsSubject.next([...items]);
|
|
1313
|
+
this.expandMatchedParents(this.items, this.searchText);
|
|
1314
|
+
this.refresh();
|
|
1326
1315
|
}
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
: false;
|
|
1337
|
-
item.expanded = !!childMatch || !!selfMatch;
|
|
1338
|
-
if (selfMatch || childMatch) {
|
|
1339
|
-
hasMatch = true;
|
|
1340
|
-
}
|
|
1316
|
+
clearSearch() {
|
|
1317
|
+
this.searchText = '';
|
|
1318
|
+
this.restoreExpandedStateBeforeSearch();
|
|
1319
|
+
this.refresh();
|
|
1320
|
+
}
|
|
1321
|
+
toggleSearch() {
|
|
1322
|
+
this.showTreeSearch = !this.showTreeSearch;
|
|
1323
|
+
if (!this.showTreeSearch) {
|
|
1324
|
+
this.clearSearch();
|
|
1341
1325
|
}
|
|
1342
|
-
return hasMatch;
|
|
1343
1326
|
}
|
|
1344
1327
|
isSearchMatch(item) {
|
|
1345
1328
|
const term = this.searchText.trim().toLowerCase();
|
|
@@ -1349,86 +1332,39 @@ class RoutesComponent {
|
|
|
1349
1332
|
const title = String(item.title ?? '').toLowerCase();
|
|
1350
1333
|
return label.includes(term) || title.includes(term);
|
|
1351
1334
|
}
|
|
1352
|
-
|
|
1353
|
-
this.
|
|
1354
|
-
|
|
1355
|
-
// ✅ لا تفتح الكل عند مسح البحث
|
|
1356
|
-
this.setExpandedRecursive(items, false);
|
|
1357
|
-
this._menuItemsSubject.next([...items]);
|
|
1358
|
-
}
|
|
1359
|
-
getSecondToLastLevel(items) {
|
|
1360
|
-
const result = [];
|
|
1361
|
-
const traverse = (nodes) => {
|
|
1362
|
-
for (const node of nodes) {
|
|
1363
|
-
if (node.items && node.items.length > 0) {
|
|
1364
|
-
const hasGrandChildren = node.items.some(c => c.items && c.items.length > 0);
|
|
1365
|
-
if (!hasGrandChildren) {
|
|
1366
|
-
result.push(node);
|
|
1367
|
-
}
|
|
1368
|
-
else {
|
|
1369
|
-
traverse(node.items);
|
|
1370
|
-
}
|
|
1371
|
-
}
|
|
1372
|
-
}
|
|
1373
|
-
};
|
|
1374
|
-
traverse(items);
|
|
1375
|
-
return result;
|
|
1376
|
-
}
|
|
1377
|
-
onParentClick(item) {
|
|
1378
|
-
item.expanded = !item.expanded;
|
|
1335
|
+
closeMenu() {
|
|
1336
|
+
this.subSidebarVisible = false;
|
|
1337
|
+
this.sidebarVisible = false;
|
|
1379
1338
|
}
|
|
1380
|
-
|
|
1381
|
-
this.
|
|
1339
|
+
openMenu() {
|
|
1340
|
+
this.sidebarVisible = true;
|
|
1341
|
+
this.subSidebarVisible = true;
|
|
1382
1342
|
}
|
|
1383
|
-
|
|
1343
|
+
syncViewport() {
|
|
1384
1344
|
this.isMobile = window.innerWidth < 1024;
|
|
1345
|
+
this.layoutService.smallScreen = this.isMobile;
|
|
1385
1346
|
if (this.isMobile) {
|
|
1386
1347
|
this.sidebarVisible = false;
|
|
1387
1348
|
this.subSidebarVisible = false;
|
|
1349
|
+
return;
|
|
1388
1350
|
}
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
// ✅ خلي الشجرة ظاهرة في الديسكتوب
|
|
1392
|
-
this.subSidebarVisible = true;
|
|
1393
|
-
}
|
|
1351
|
+
this.sidebarVisible = true;
|
|
1352
|
+
this.subSidebarVisible = true;
|
|
1394
1353
|
}
|
|
1395
|
-
|
|
1354
|
+
toggleSidebar() {
|
|
1396
1355
|
if (this.isMobile) {
|
|
1397
1356
|
this.sidebarVisible = !this.sidebarVisible;
|
|
1398
1357
|
this.subSidebarVisible = this.sidebarVisible;
|
|
1358
|
+
return;
|
|
1399
1359
|
}
|
|
1400
|
-
|
|
1401
|
-
this.subSidebarVisible = !this.subSidebarVisible;
|
|
1402
|
-
}
|
|
1403
|
-
}
|
|
1404
|
-
openMenu() {
|
|
1405
|
-
this.sidebarVisible = true;
|
|
1406
|
-
this.subSidebarVisible = true;
|
|
1407
|
-
}
|
|
1408
|
-
expandPath(items, target) {
|
|
1409
|
-
for (const item of items) {
|
|
1410
|
-
if (item === target)
|
|
1411
|
-
return true;
|
|
1412
|
-
if (item.items && item.items.length > 0) {
|
|
1413
|
-
const found = this.expandPath(item.items, target);
|
|
1414
|
-
if (found) {
|
|
1415
|
-
item.expanded = true;
|
|
1416
|
-
return true;
|
|
1417
|
-
}
|
|
1418
|
-
}
|
|
1419
|
-
}
|
|
1420
|
-
return false;
|
|
1360
|
+
this.subSidebarVisible = !this.subSidebarVisible;
|
|
1421
1361
|
}
|
|
1422
|
-
|
|
1423
|
-
const items = this._menuItemsSubject.value;
|
|
1362
|
+
activateItem(target) {
|
|
1424
1363
|
const isExternal = target.isExternal ?? false;
|
|
1425
1364
|
if (!target.items?.length) {
|
|
1426
1365
|
if (isExternal && target.url) {
|
|
1427
1366
|
window.open(target.url, '_blank', 'noopener,noreferrer');
|
|
1428
|
-
|
|
1429
|
-
this.closeMenu();
|
|
1430
|
-
this.expandPath(items, target);
|
|
1431
|
-
this._menuItemsSubject.next([...items]);
|
|
1367
|
+
this.afterNavigate(target);
|
|
1432
1368
|
return;
|
|
1433
1369
|
}
|
|
1434
1370
|
if (target.routerLink) {
|
|
@@ -1437,42 +1373,93 @@ class RoutesComponent {
|
|
|
1437
1373
|
? target.routerLink[0]
|
|
1438
1374
|
: target.routerLink;
|
|
1439
1375
|
if (currentUrl !== targetUrl) {
|
|
1440
|
-
this.router.navigate(Array.isArray(target.routerLink)
|
|
1376
|
+
this.router.navigate(Array.isArray(target.routerLink)
|
|
1377
|
+
? target.routerLink
|
|
1378
|
+
: [target.routerLink]);
|
|
1441
1379
|
}
|
|
1442
|
-
|
|
1443
|
-
this.closeMenu();
|
|
1444
|
-
this.expandPath(items, target);
|
|
1445
|
-
this._menuItemsSubject.next([...items]);
|
|
1380
|
+
this.afterNavigate(target);
|
|
1446
1381
|
return;
|
|
1447
1382
|
}
|
|
1448
1383
|
}
|
|
1449
1384
|
target.expanded = !target.expanded;
|
|
1450
|
-
this.subSidebarVisible =
|
|
1451
|
-
this.
|
|
1385
|
+
this.subSidebarVisible = true;
|
|
1386
|
+
this.refresh();
|
|
1387
|
+
}
|
|
1388
|
+
closeAfterNavigate() {
|
|
1389
|
+
if (this.isMobile) {
|
|
1390
|
+
this.closeMenu();
|
|
1391
|
+
}
|
|
1452
1392
|
}
|
|
1453
|
-
|
|
1393
|
+
getLinkProps(item) {
|
|
1394
|
+
const isExternal = item.isExternal ?? false;
|
|
1395
|
+
return {
|
|
1396
|
+
isExternal,
|
|
1397
|
+
href: isExternal ? (item.url ?? null) : null,
|
|
1398
|
+
routerLink: !isExternal ? (item.routerLink ?? null) : null,
|
|
1399
|
+
addRoute: item.addRoute ?? '',
|
|
1400
|
+
};
|
|
1401
|
+
}
|
|
1402
|
+
expandPath(target) {
|
|
1403
|
+
const found = this.expandPathRecursive(this.items, target);
|
|
1404
|
+
this.refresh();
|
|
1405
|
+
return found;
|
|
1406
|
+
}
|
|
1407
|
+
toggleItem(item) {
|
|
1454
1408
|
item.expanded = !item.expanded;
|
|
1409
|
+
this.refresh();
|
|
1455
1410
|
}
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1411
|
+
setExpandedByLevel(items, level = 1) {
|
|
1412
|
+
for (const item of items) {
|
|
1413
|
+
item.expanded = level === 1;
|
|
1414
|
+
if (item.items?.length) {
|
|
1415
|
+
this.setExpandedByLevel(item.items, level + 1);
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1459
1418
|
}
|
|
1460
|
-
|
|
1419
|
+
captureExpandedState(items) {
|
|
1420
|
+
const state = new WeakMap();
|
|
1421
|
+
const traverse = (nodes) => {
|
|
1422
|
+
for (const node of nodes) {
|
|
1423
|
+
state.set(node, node.expanded === true);
|
|
1424
|
+
if (node.items?.length) {
|
|
1425
|
+
traverse(node.items);
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
};
|
|
1429
|
+
traverse(items);
|
|
1430
|
+
return state;
|
|
1431
|
+
}
|
|
1432
|
+
restoreExpandedStateBeforeSearch() {
|
|
1433
|
+
if (!this.expandedStateBeforeSearch)
|
|
1434
|
+
return;
|
|
1435
|
+
const traverse = (nodes) => {
|
|
1436
|
+
for (const node of nodes) {
|
|
1437
|
+
node.expanded = this.expandedStateBeforeSearch?.get(node) ?? false;
|
|
1438
|
+
if (node.items?.length) {
|
|
1439
|
+
traverse(node.items);
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
};
|
|
1443
|
+
traverse(this.items);
|
|
1444
|
+
this.expandedStateBeforeSearch = null;
|
|
1445
|
+
}
|
|
1446
|
+
afterNavigate(target) {
|
|
1461
1447
|
if (this.isMobile) {
|
|
1462
1448
|
this.closeMenu();
|
|
1463
1449
|
}
|
|
1450
|
+
this.expandPath(target);
|
|
1464
1451
|
}
|
|
1465
1452
|
buildItems(routes) {
|
|
1466
1453
|
return routes
|
|
1467
|
-
.filter(
|
|
1468
|
-
.map(
|
|
1454
|
+
.filter((route) => !route.requiredPolicy || this.hasPermission(route.requiredPolicy))
|
|
1455
|
+
.map((route) => this.createItem(route));
|
|
1469
1456
|
}
|
|
1470
1457
|
createItem(route) {
|
|
1471
1458
|
const item = {
|
|
1472
1459
|
label: route.name,
|
|
1473
1460
|
icon: route.iconClass,
|
|
1474
1461
|
title: route.name,
|
|
1475
|
-
expanded: false
|
|
1462
|
+
expanded: false,
|
|
1476
1463
|
};
|
|
1477
1464
|
if (route.isExternal) {
|
|
1478
1465
|
item.url = route.path;
|
|
@@ -1491,38 +1478,104 @@ class RoutesComponent {
|
|
|
1491
1478
|
hasPermission(policy) {
|
|
1492
1479
|
return true;
|
|
1493
1480
|
}
|
|
1494
|
-
|
|
1495
|
-
const
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
};
|
|
1481
|
+
setExpandedRecursive(items, expanded) {
|
|
1482
|
+
for (const item of items) {
|
|
1483
|
+
item.expanded = expanded;
|
|
1484
|
+
if (item.items?.length) {
|
|
1485
|
+
this.setExpandedRecursive(item.items, expanded);
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1502
1488
|
}
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1489
|
+
expandMatchedParents(items, keyword) {
|
|
1490
|
+
const term = keyword.trim().toLowerCase();
|
|
1491
|
+
let hasMatch = false;
|
|
1492
|
+
for (const item of items) {
|
|
1493
|
+
const label = String(item.label ?? '').toLowerCase();
|
|
1494
|
+
const title = String(item.title ?? '').toLowerCase();
|
|
1495
|
+
const selfMatch = label.includes(term) || title.includes(term);
|
|
1496
|
+
const childMatch = item.items?.length
|
|
1497
|
+
? this.expandMatchedParents(item.items, keyword)
|
|
1498
|
+
: false;
|
|
1499
|
+
item.expanded = !!childMatch || !!selfMatch;
|
|
1500
|
+
if (selfMatch || childMatch) {
|
|
1501
|
+
hasMatch = true;
|
|
1502
|
+
}
|
|
1508
1503
|
}
|
|
1504
|
+
return hasMatch;
|
|
1505
|
+
}
|
|
1506
|
+
expandPathRecursive(items, target) {
|
|
1507
|
+
for (const item of items) {
|
|
1508
|
+
if (item === target)
|
|
1509
|
+
return true;
|
|
1510
|
+
if (item.items?.length) {
|
|
1511
|
+
const found = this.expandPathRecursive(item.items, target);
|
|
1512
|
+
if (found) {
|
|
1513
|
+
item.expanded = true;
|
|
1514
|
+
return true;
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
return false;
|
|
1519
|
+
}
|
|
1520
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: RoutesMenuService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1521
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: RoutesMenuService });
|
|
1522
|
+
}
|
|
1523
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: RoutesMenuService, decorators: [{
|
|
1524
|
+
type: Injectable
|
|
1525
|
+
}], ctorParameters: () => [] });
|
|
1526
|
+
|
|
1527
|
+
class RoutesSearchComponent {
|
|
1528
|
+
menu = inject(RoutesMenuService);
|
|
1529
|
+
closeMenu() {
|
|
1530
|
+
this.menu.closeMenu();
|
|
1531
|
+
}
|
|
1532
|
+
expandAll() {
|
|
1533
|
+
this.menu.expandAll();
|
|
1534
|
+
}
|
|
1535
|
+
collapseAll() {
|
|
1536
|
+
this.menu.collapseAll();
|
|
1537
|
+
}
|
|
1538
|
+
toggleSearch() {
|
|
1539
|
+
this.menu.toggleSearch();
|
|
1540
|
+
}
|
|
1541
|
+
onSearchTextChange(value) {
|
|
1542
|
+
this.menu.search(value);
|
|
1543
|
+
}
|
|
1544
|
+
clearSearch() {
|
|
1545
|
+
this.menu.clearSearch();
|
|
1546
|
+
}
|
|
1547
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: RoutesSearchComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1548
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: RoutesSearchComponent, isStandalone: true, selector: "app-routes-search", ngImport: i0, template: "<div class=\"mb-4 space-y-3\">\r\n <div class=\"flex items-center justify-between gap-3\">\r\n <button\r\n type=\"button\"\r\n (click)=\"closeMenu()\"\r\n class=\"inline-flex h-9 items-center gap-1.5 rounded-full px-2 ps-1.5 pe-3 text-sm font-bold text-slate-500 transition hover:bg-primary-50 hover:text-primary-600 dark:text-slate-300 dark:hover:bg-primary-500/10 dark:hover:text-primary-300\"\r\n >\r\n <i class=\"pi pi-angle-right text-sm\"></i>\r\n <span>{{ 'back' | translate }}</span>\r\n </button>\r\n\r\n <div class=\"inline-flex items-center gap-1\">\r\n <button\r\n type=\"button\"\r\n class=\"inline-flex h-8 w-8 items-center justify-center rounded-xl border border-slate-300/25 bg-slate-50/85 text-slate-500 transition hover:-translate-y-px hover:border-primary-500/25 hover:bg-primary-50 hover:text-primary-600 dark:border-slate-400/15 dark:bg-gray-800/75 dark:text-slate-300 dark:hover:bg-primary-500/10 dark:hover:text-primary-300\"\r\n (click)=\"expandAll()\"\r\n pTooltip=\"\u0641\u062A\u062D \u0627\u0644\u0634\u062C\u0631\u0629 \u0643\u0627\u0645\u0644\u0629\"\r\n tooltipPosition=\"top\"\r\n >\r\n <i class=\"pi pi-plus text-xs\"></i>\r\n </button>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"inline-flex h-8 w-8 items-center justify-center rounded-xl border border-slate-300/25 bg-slate-50/85 text-slate-500 transition hover:-translate-y-px hover:border-primary-500/25 hover:bg-primary-50 hover:text-primary-600 dark:border-slate-400/15 dark:bg-gray-800/75 dark:text-slate-300 dark:hover:bg-primary-500/10 dark:hover:text-primary-300\"\r\n (click)=\"collapseAll()\"\r\n pTooltip=\"\u0625\u063A\u0644\u0627\u0642 \u0627\u0644\u0634\u062C\u0631\u0629 \u0643\u0627\u0645\u0644\u0629\"\r\n tooltipPosition=\"top\"\r\n >\r\n <i class=\"pi pi-minus text-xs\"></i>\r\n </button>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"inline-flex h-8 w-8 items-center justify-center rounded-xl border transition hover:-translate-y-px\"\r\n [ngClass]=\"\r\n menu.showTreeSearch\r\n ? 'border-transparent bg-primary-600 text-white shadow-lg shadow-primary-500/20'\r\n : 'border-primary-500/20 bg-primary-500/10 text-primary-600 hover:bg-primary-50 dark:text-primary-300 dark:hover:bg-primary-500/10'\r\n \"\r\n (click)=\"toggleSearch()\"\r\n pTooltip=\"\u0628\u062D\u062B \u062F\u0627\u062E\u0644 \u0627\u0644\u0634\u0627\u0634\u0627\u062A\"\r\n tooltipPosition=\"top\"\r\n >\r\n <i class=\"pi pi-search text-xs\"></i>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n @if (menu.showTreeSearch) {\r\n <div\r\n class=\"animate-slideIn rounded-2xl border border-white/45 bg-white/60 p-2 shadow-lg shadow-slate-900/10 backdrop-blur-xl dark:border-white/10 dark:bg-gray-900/70\"\r\n >\r\n <div class=\"relative flex items-center\">\r\n <i\r\n class=\"pi pi-search pointer-events-none absolute right-3 text-sm text-primary-500 dark:text-primary-300\"\r\n ></i>\r\n\r\n <input\r\n type=\"text\"\r\n [ngModel]=\"menu.searchText\"\r\n (ngModelChange)=\"onSearchTextChange($event)\"\r\n placeholder=\"\u0628\u062D\u062B \u062F\u0627\u062E\u0644 \u0627\u0644\u0634\u0627\u0634\u0627\u062A...\"\r\n class=\"h-10 w-full rounded-2xl border border-slate-300/30 bg-slate-50/85 py-0 pl-9 pr-10 text-sm text-slate-700 outline-none transition placeholder:text-slate-400 focus:border-primary-500/50 focus:bg-white focus:ring-4 focus:ring-primary-500/10 dark:border-slate-400/20 dark:bg-gray-800/80 dark:text-gray-100 dark:placeholder:text-slate-500 dark:focus:bg-gray-900\"\r\n />\r\n\r\n @if (menu.searchText) {\r\n <button\r\n type=\"button\"\r\n class=\"absolute left-2 inline-flex h-7 w-7 items-center justify-center rounded-full text-slate-400 transition hover:bg-primary-50 hover:text-primary-600 dark:text-slate-300 dark:hover:bg-primary-500/10 dark:hover:text-primary-300\"\r\n (click)=\"clearSearch()\"\r\n >\r\n <i class=\"pi pi-times text-xs\"></i>\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i3$2.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
|
|
1549
|
+
}
|
|
1550
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: RoutesSearchComponent, decorators: [{
|
|
1551
|
+
type: Component,
|
|
1552
|
+
args: [{ selector: 'app-routes-search', standalone: true, imports: [CommonModule, FormsModule, TooltipModule, TranslatePipe], template: "<div class=\"mb-4 space-y-3\">\r\n <div class=\"flex items-center justify-between gap-3\">\r\n <button\r\n type=\"button\"\r\n (click)=\"closeMenu()\"\r\n class=\"inline-flex h-9 items-center gap-1.5 rounded-full px-2 ps-1.5 pe-3 text-sm font-bold text-slate-500 transition hover:bg-primary-50 hover:text-primary-600 dark:text-slate-300 dark:hover:bg-primary-500/10 dark:hover:text-primary-300\"\r\n >\r\n <i class=\"pi pi-angle-right text-sm\"></i>\r\n <span>{{ 'back' | translate }}</span>\r\n </button>\r\n\r\n <div class=\"inline-flex items-center gap-1\">\r\n <button\r\n type=\"button\"\r\n class=\"inline-flex h-8 w-8 items-center justify-center rounded-xl border border-slate-300/25 bg-slate-50/85 text-slate-500 transition hover:-translate-y-px hover:border-primary-500/25 hover:bg-primary-50 hover:text-primary-600 dark:border-slate-400/15 dark:bg-gray-800/75 dark:text-slate-300 dark:hover:bg-primary-500/10 dark:hover:text-primary-300\"\r\n (click)=\"expandAll()\"\r\n pTooltip=\"\u0641\u062A\u062D \u0627\u0644\u0634\u062C\u0631\u0629 \u0643\u0627\u0645\u0644\u0629\"\r\n tooltipPosition=\"top\"\r\n >\r\n <i class=\"pi pi-plus text-xs\"></i>\r\n </button>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"inline-flex h-8 w-8 items-center justify-center rounded-xl border border-slate-300/25 bg-slate-50/85 text-slate-500 transition hover:-translate-y-px hover:border-primary-500/25 hover:bg-primary-50 hover:text-primary-600 dark:border-slate-400/15 dark:bg-gray-800/75 dark:text-slate-300 dark:hover:bg-primary-500/10 dark:hover:text-primary-300\"\r\n (click)=\"collapseAll()\"\r\n pTooltip=\"\u0625\u063A\u0644\u0627\u0642 \u0627\u0644\u0634\u062C\u0631\u0629 \u0643\u0627\u0645\u0644\u0629\"\r\n tooltipPosition=\"top\"\r\n >\r\n <i class=\"pi pi-minus text-xs\"></i>\r\n </button>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"inline-flex h-8 w-8 items-center justify-center rounded-xl border transition hover:-translate-y-px\"\r\n [ngClass]=\"\r\n menu.showTreeSearch\r\n ? 'border-transparent bg-primary-600 text-white shadow-lg shadow-primary-500/20'\r\n : 'border-primary-500/20 bg-primary-500/10 text-primary-600 hover:bg-primary-50 dark:text-primary-300 dark:hover:bg-primary-500/10'\r\n \"\r\n (click)=\"toggleSearch()\"\r\n pTooltip=\"\u0628\u062D\u062B \u062F\u0627\u062E\u0644 \u0627\u0644\u0634\u0627\u0634\u0627\u062A\"\r\n tooltipPosition=\"top\"\r\n >\r\n <i class=\"pi pi-search text-xs\"></i>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n @if (menu.showTreeSearch) {\r\n <div\r\n class=\"animate-slideIn rounded-2xl border border-white/45 bg-white/60 p-2 shadow-lg shadow-slate-900/10 backdrop-blur-xl dark:border-white/10 dark:bg-gray-900/70\"\r\n >\r\n <div class=\"relative flex items-center\">\r\n <i\r\n class=\"pi pi-search pointer-events-none absolute right-3 text-sm text-primary-500 dark:text-primary-300\"\r\n ></i>\r\n\r\n <input\r\n type=\"text\"\r\n [ngModel]=\"menu.searchText\"\r\n (ngModelChange)=\"onSearchTextChange($event)\"\r\n placeholder=\"\u0628\u062D\u062B \u062F\u0627\u062E\u0644 \u0627\u0644\u0634\u0627\u0634\u0627\u062A...\"\r\n class=\"h-10 w-full rounded-2xl border border-slate-300/30 bg-slate-50/85 py-0 pl-9 pr-10 text-sm text-slate-700 outline-none transition placeholder:text-slate-400 focus:border-primary-500/50 focus:bg-white focus:ring-4 focus:ring-primary-500/10 dark:border-slate-400/20 dark:bg-gray-800/80 dark:text-gray-100 dark:placeholder:text-slate-500 dark:focus:bg-gray-900\"\r\n />\r\n\r\n @if (menu.searchText) {\r\n <button\r\n type=\"button\"\r\n class=\"absolute left-2 inline-flex h-7 w-7 items-center justify-center rounded-full text-slate-400 transition hover:bg-primary-50 hover:text-primary-600 dark:text-slate-300 dark:hover:bg-primary-500/10 dark:hover:text-primary-300\"\r\n (click)=\"clearSearch()\"\r\n >\r\n <i class=\"pi pi-times text-xs\"></i>\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n" }]
|
|
1553
|
+
}] });
|
|
1554
|
+
|
|
1555
|
+
class RoutesComponent {
|
|
1556
|
+
menu = inject(RoutesMenuService);
|
|
1557
|
+
ngOnInit() {
|
|
1558
|
+
this.checkViewport();
|
|
1559
|
+
}
|
|
1560
|
+
onResize() {
|
|
1561
|
+
this.checkViewport();
|
|
1562
|
+
}
|
|
1563
|
+
checkViewport() {
|
|
1564
|
+
this.menu.syncViewport();
|
|
1509
1565
|
}
|
|
1510
1566
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: RoutesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1511
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: RoutesComponent, isStandalone: true, selector: "app-routes", host: { listeners: { "window:resize": "onResize()" } }, ngImport: i0, template: "<aside class=\"flex h-screen bg-white dark:bg-gray-900 transition-all duration-300\"\r\n [ngClass]=\"{ 'w-0 overflow-hidden': !sidebarVisible && isMobile }\">\r\n\r\n <!-- \uD83C\uDFA8 \u0627\u0644\u0634\u0631\u064A\u0637 \u0627\u0644\u062C\u0627\u0646\u0628\u064A \u0627\u0644\u0631\u0626\u064A\u0633\u064A -->\r\n <div\r\n class=\"flex flex-col justify-between pt-4 pb-2 w-16 border-l shadow-xl\r\n bg-gradient-to-b from-primary-500 via-primary-600 to-primary-700\r\n text-white rounded-r-3xl\">\r\n\r\n <!-- \uD83D\uDD1D \u0627\u0644\u0623\u0639\u0644\u0649 -->\r\n <div class=\"flex flex-col items-center gap-4\">\r\n <img src=\"/assets/logo.png\" alt=\"Logo\"\r\n class=\"w-9 h-9 mt-1 rounded-lg shadow-sm\"/>\r\n\r\n <!-- \u0632\u0631 \u0627\u0644\u0642\u0627\u0626\u0645\u0629 -->\r\n <!-- [pTooltip]=\"'TOGGLE_MENU' | translate\" -->\r\n <button (click)=\"toggleAll()\"\r\n tooltipPosition=\"right\"\r\n class=\"text-white hover:scale-110 transition-transform duration-200\">\r\n <i class=\"pi pi-bars text-lg\"></i>\r\n </button>\r\n\r\n <!-- \u0623\u064A\u0642\u0648\u0646\u0627\u062A -->\r\n @for (item of secondToLastLevel$ | async; track item) {\r\n <div class=\"relative flex flex-col items-center\">\r\n <!-- \u0627\u0644\u0623\u064A\u0642\u0648\u0646\u0629 -->\r\n <div (click)=\"onMainClick(item)\"\r\n [pTooltip]=\"item.label | translate\"\r\n tooltipPosition=\"right\"\r\n class=\"relative flex items-center justify-center w-10 h-10 rounded-xl cursor-pointer\r\n hover:bg-white/20 transition-all duration-200\"\r\n [ngClass]=\"{ 'bg-white/30 scale-105 shadow-inner': item.expanded }\">\r\n <i [class]=\"item.icon + ' text-xl'\"></i>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- \u2699\uFE0F \u0627\u0644\u0623\u0633\u0641\u0644 -->\r\n <div class=\"flex flex-col items-center pb-3 text-white/80\">\r\n <i class=\"pi pi-cog text-lg hover:text-white cursor-pointer transition\"></i>\r\n </div>\r\n </div>\r\n\r\n <!-- \uD83D\uDCCB \u0627\u0644\u0642\u0627\u0626\u0645\u0629 \u0627\u0644\u0641\u0631\u0639\u064A\u0629 -->\r\n @if (subSidebarVisible) {\r\n <div\r\n class=\"w-[290px] max-w-[290px] min-w-[260px] border-l border-gray-200 dark:border-gray-700 glass-bg animate-slideIn\r\n shadow-2xl rounded-l-3xl p-4 rtl:text-right overflow-y-auto overflow-x-hidden transition-all duration-300\"\r\n [ngClass]=\"{ 'absolute top-0 right-16 h-full z-40': isMobile }\">\r\n <!-- \u0632\u0631 \u0631\u062C\u0648\u0639 -->\r\n <!-- \uD83D\uDD1D \u0631\u0623\u0633 \u0627\u0644\u0634\u062C\u0631\u0629 -->\r\n<!-- \uD83D\uDD1D \u0631\u0623\u0633 \u0627\u0644\u0634\u062C\u0631\u0629 -->\r\n<div class=\"tree-header mb-1\">\r\n\r\n <button\r\n type=\"button\"\r\n (click)=\"closeMenu()\"\r\n class=\"tree-back-btn\">\r\n <i class=\"pi pi-angle-right\"></i>\r\n <span>{{ 'back' | translate }}</span>\r\n </button>\r\n\r\n <div class=\"tree-header-actions\">\r\n\r\n <button\r\n type=\"button\"\r\n class=\"tree-mini-btn\"\r\n (click)=\"expandAll()\"\r\n pTooltip=\"\u0641\u062A\u062D \u0627\u0644\u0634\u062C\u0631\u0629 \u0643\u0627\u0645\u0644\u0629\"\r\n tooltipPosition=\"top\">\r\n <i class=\"pi pi-plus\"></i>\r\n </button>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"tree-mini-btn\"\r\n (click)=\"collapseAll()\"\r\n pTooltip=\"\u0625\u063A\u0644\u0627\u0642 \u0627\u0644\u0634\u062C\u0631\u0629 \u0643\u0627\u0645\u0644\u0629\"\r\n tooltipPosition=\"top\">\r\n <i class=\"pi pi-minus\"></i>\r\n </button>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"tree-search-toggle\"\r\n [ngClass]=\"{ 'active': showTreeSearch }\"\r\n (click)=\"toggleTreeSearch()\"\r\n pTooltip=\"\u0628\u062D\u062B \u062F\u0627\u062E\u0644 \u0627\u0644\u0634\u0627\u0634\u0627\u062A\"\r\n tooltipPosition=\"top\">\r\n <i class=\"pi pi-search\"></i>\r\n </button>\r\n\r\n </div>\r\n\r\n</div>\r\n\r\n@if (showTreeSearch) {\r\n <div class=\"tree-search-panel mb-4 animate-slideIn\">\r\n <div class=\"tree-search\">\r\n <i class=\"pi pi-search\"></i>\r\n\r\n <input\r\n type=\"text\"\r\n [(ngModel)]=\"searchText\"\r\n (input)=\"onSearchChange()\"\r\n placeholder=\"\u0628\u062D\u062B \u062F\u0627\u062E\u0644 \u0627\u0644\u0634\u0627\u0634\u0627\u062A...\"\r\n class=\"tree-search-input\" />\r\n\r\n @if (searchText) {\r\n <button\r\n type=\"button\"\r\n class=\"tree-search-clear\"\r\n (click)=\"clearSearch()\">\r\n <i class=\"pi pi-times\"></i>\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n}\r\n\r\n\r\n <!-- Recursive rendering -->\r\n @if (menuItems$ | async; as menuItems) {\r\n <ng-container *ngTemplateOutlet=\"renderMenu; context:{ $implicit: menuItems, level: 1 }\"></ng-container>\r\n}\r\n\r\n<ng-template #renderMenu let-items let-level=\"level\">\r\n @for (item of items; track item) {\r\n\r\n @if (!item.items?.length) {\r\n @let link = getLinkProps(item);\r\n <div class=\"mb-1\">\r\n @if (link.isExternal) {\r\n <a [href]=\"link.href\"\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\"\r\n (click)=\"$event.stopPropagation()\"\r\n class=\"flex items-center gap-2 px-3 py-2 rounded-lg transition-all hover:bg-primary-50 dark:hover:bg-gray-800 text-gray-700 dark:text-gray-200\">\r\n <i [class]=\"item.icon + ' text-base'\"></i>\r\n <span class=\"min-w-0 break-words whitespace-normal\">{{ item.label | translate }}</span>\r\n <i class=\"pi pi-external-link text-[10px] opacity-50\"></i>\r\n </a>\r\n } @else {\r\n <div class=\"flex items-center gap-2\">\r\n <a [routerLink]=\"link.routerLink\"\r\n (click)=\"onNavigate()\"\r\n routerLinkActive=\"active-link\"\r\n [ngClass]=\"{ 'search-match': isSearchMatch(item) }\"\r\n class=\"flex-1 min-w-0 flex items-center gap-2 px-2 py-1 rounded-lg transition-all hover:bg-primary-50 dark:hover:bg-gray-800 text-gray-700 dark:text-gray-200\">\r\n <i [class]=\"item.icon + ' text-base'\"></i>\r\n <span class=\"min-w-0 break-words whitespace-normal\">\r\n {{ item.label | translate }}</span>\r\n </a>\r\n\r\n@if (link.addRoute) {\r\n <a\r\n [routerLink]=\"[link.addRoute]\"\r\n (click)=\"$event.stopPropagation(); onNavigate()\"\r\n class=\"shrink-0 inline-flex items-center justify-center w-7 h-7 rounded-full text-gray-400 hover:text-primary-500 hover:bg-primary-50 dark:text-gray-500 dark:hover:text-primary-400 dark:hover:bg-primary-900/20 transition-all duration-200\"\r\n [pTooltip]=\"'ADD' | translate\"\r\n tooltipPosition=\"top\">\r\n <i class=\"pi pi-plus text-xs\"></i>\r\n </a>\r\n}\r\n </div>\r\n }\r\n</div>\r\n}\r\n\r\n @if (item.items?.length) {\r\n <div class=\"mb-1\">\r\n\r\n <div\r\n (click)=\"onParentClick(item)\"\r\n [style.font-size.rem]=\"1.1 - (level * 0.05)\"\r\n[ngClass]=\"{\r\n 'font-bold': level === 1,\r\n 'font-semibold': level === 2,\r\n 'font-medium': level >= 3,\r\n 'bg-primary-50 text-primary-700 border-r-4 border-primary-500': item.expanded,\r\n 'search-match': isSearchMatch(item)\r\n}\"\r\n class=\"flex justify-between items-center py-2 px-3 rounded-lg cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-800 transition text-gray-800 dark:text-gray-200\">\r\n\r\n <div class=\"flex items-center gap-2\">\r\n\r\n <i [class]=\"item.icon + ' text-base'\"></i>\r\n\r\n <span>\r\n {{ item.label | translate }}\r\n </span>\r\n\r\n </div>\r\n\r\n <i\r\n class=\"pi\"\r\n [ngClass]=\"item.expanded ? 'pi-chevron-down' : 'pi-chevron-left'\"\r\n style=\"font-size:0.6rem;\">\r\n </i>\r\n\r\n </div>\r\n\r\n @if (item.expanded) {\r\n <div class=\"mr-4 border-r border-gray-200 dark:border-gray-700 pr-2 mt-1\">\r\n\r\n <ng-container\r\n *ngTemplateOutlet=\"renderMenu; context:{ $implicit: item.items, level: level + 1 }\">\r\n </ng-container>\r\n\r\n </div>\r\n }\r\n\r\n </div>\r\n }\r\n\r\n }\r\n</ng-template>\r\n\r\n </div>\r\n}\r\n\r\n</aside>\r\n\r\n<!-- \uD83C\uDF1F \u0627\u0644\u0632\u0631 \u0627\u0644\u0639\u0627\u0626\u0645 \u0627\u0644\u062D\u062F\u064A\u062B -->\r\n@if (isMobile && !sidebarVisible) {\r\n <button\r\n class=\"floating-btn\"\r\n (click)=\"openMenu()\"\r\n pTooltip=\"{{ 'MENU.OPEN' | translate }}\"\r\n tooltipPosition=\"top\">\r\n <img src=\"/assets/logo.png\" alt=\"App Icon\" />\r\n </button>\r\n}\r\n", styles: ["@charset \"UTF-8\";@keyframes slideIn{0%{transform:translate(60px);opacity:0}to{transform:translate(0);opacity:1}}.animate-slideIn{animation:slideIn .35s cubic-bezier(.25,1,.5,1)}.glass-bg{background:#ffffffbf;backdrop-filter:blur(16px) saturate(180%);-webkit-backdrop-filter:blur(16px) saturate(180%);border:1px solid rgba(255,255,255,.25)}@media(max-width:1024px){aside{position:fixed;top:0;right:0;height:100vh;z-index:50}}.floating-btn{position:fixed;bottom:1.25rem;right:1.25rem;z-index:60;width:60px;height:60px;border-radius:50%;background:linear-gradient(135deg,var(--p-primary-500),var(--p-primary-700));box-shadow:0 4px 18px #00000040;display:flex;align-items:center;justify-content:center;transition:all .3s ease}.floating-btn:hover{transform:scale(1.1) rotate(5deg)}.floating-btn img{width:28px;height:28px;border-radius:10px}.active-link{background-color:var(--p-primary-500)!important;color:#fff!important;font-weight:600;box-shadow:0 4px 6px -1px #0000001a;transform:translate(-3px)}.active-link i{color:#fff!important}.active-item-link{background:linear-gradient(to left,var(--p-primary-50),transparent);color:var(--p-primary-600)!important;position:relative;border-right:3px solid var(--p-primary-500);border-radius:0 12px 12px 0}.active-item-link i{color:var(--p-primary-500);filter:drop-shadow(0 0 5px rgba(var(--p-primary-500-rgb),.4))}.active-item-link span{font-weight:700}.animate-slideIn{animation:slideIn .3s ease-out}@keyframes slideIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}.glass-bg::-webkit-scrollbar{width:4px}.glass-bg::-webkit-scrollbar-thumb{background:#0000001a;border-radius:10px}.active-link{background-color:var(--p-primary-50)!important;color:var(--p-primary-700)!important;border-right:4px solid var(--p-primary-500)!important;border-radius:4px 12px 12px 4px!important}.active-link span{font-weight:800!important}.active-link i{color:var(--p-primary-600)!important;transform:scale(1.1)}aside{-webkit-font-smoothing:antialiased;letter-spacing:-.01em}.dark .active-link{background-color:rgba(var(--p-primary-500-rgb),.15)!important;color:var(--p-primary-300)!important}.submenu{animation:menuOpen .25s ease}@keyframes menuOpen{0%{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:translateY(0)}}aside{box-shadow:0 10px 40px #00000026}.tree-back-btn{height:2.25rem;padding:0 .35rem 0 .75rem;display:inline-flex;align-items:center;gap:.35rem;color:#64748b;font-weight:700;border-radius:999px;transition:all .2s ease}.tree-header{display:flex;align-items:center;justify-content:space-between;gap:.75rem}.tree-back-btn{height:2.35rem;padding:0 .45rem 0 .85rem;display:inline-flex;align-items:center;gap:.4rem;color:#64748b;font-size:.86rem;font-weight:700;border-radius:999px;transition:all .22s ease}.tree-back-btn:hover{color:var(--p-primary-600);background:var(--p-primary-50)}.tree-back-btn i{font-size:.85rem}.tree-header-actions{display:inline-flex;align-items:center;gap:.3rem}.tree-mini-btn,.tree-search-toggle{width:2rem;height:2rem;border-radius:.8rem;display:inline-flex;align-items:center;justify-content:center;color:#64748b;background:#f8fafcd9;border:1px solid rgba(148,163,184,.24);transition:all .2s ease}.tree-mini-btn i,.tree-search-toggle i{font-size:.72rem}.tree-mini-btn:hover,.tree-search-toggle:hover{color:var(--p-primary-600);background:var(--p-primary-50);border-color:rgba(var(--p-primary-500-rgb),.25);transform:translateY(-1px)}.tree-search-toggle{color:var(--p-primary-600);background:rgba(var(--p-primary-500-rgb),.08);border-color:rgba(var(--p-primary-500-rgb),.18)}.tree-search-toggle.active{color:#fff;background:linear-gradient(135deg,var(--p-primary-500),var(--p-primary-600));border-color:transparent;box-shadow:0 8px 18px rgba(var(--p-primary-500-rgb),.22)}.tree-search-panel{padding:.65rem;border-radius:1.15rem;background:#ffffff94;border:1px solid rgba(255,255,255,.45);box-shadow:0 10px 30px #0f172a14;backdrop-filter:blur(14px) saturate(160%);-webkit-backdrop-filter:blur(14px) saturate(160%)}.tree-search{position:relative;display:flex;align-items:center}.tree-search i.pi-search{position:absolute;right:.85rem;color:var(--p-primary-500);font-size:.85rem;z-index:1}.tree-search-input{width:100%;height:2.45rem;padding:0 2.35rem 0 2.2rem;border-radius:999px;border:1px solid rgba(148,163,184,.35);background:#ffffffd1;color:#334155;font-size:.86rem;outline:none;transition:all .25s ease}.tree-search-input:focus{border-color:var(--p-primary-400);box-shadow:0 0 0 4px rgba(var(--p-primary-500-rgb),.12);background:#fff}.tree-search-clear{position:absolute;left:.55rem;width:1.75rem;height:1.75rem;border-radius:999px;display:inline-flex;align-items:center;justify-content:center;color:#64748b;background:#f1f5f9e6;transition:all .2s ease}.tree-search-clear:hover{color:var(--p-primary-600);background:var(--p-primary-50);transform:scale(1.05)}.tree-search-icon{position:absolute;right:.85rem;color:#94a3b8;font-size:.85rem;pointer-events:none}.tree-search-input{width:100%;height:2.45rem;padding:0 2.35rem 0 2.2rem;border-radius:.95rem;border:1px solid rgba(148,163,184,.28);background:#f8fafcd1;color:#334155;font-size:.86rem;outline:none;transition:all .22s ease}.tree-search-input:focus{border-color:rgba(var(--p-primary-500-rgb),.45);background:#fff;box-shadow:0 0 0 3px rgba(var(--p-primary-500-rgb),.1)}.tree-search-input::placeholder{color:#94a3b8}.tree-search-clear{position:absolute;left:.55rem;width:1.65rem;height:1.65rem;border-radius:999px;display:inline-flex;align-items:center;justify-content:center;color:#94a3b8;background:transparent;transition:all .2s ease}.tree-search-clear:hover{color:var(--p-primary-600);background:var(--p-primary-50)}.tree-search-clear i{font-size:.7rem}.dark .tree-back-btn{color:#cbd5e1}.dark .tree-back-btn:hover{color:var(--p-primary-300);background:rgba(var(--p-primary-500-rgb),.12)}.dark .tree-header-actions{background:#1f2937b8;border-color:#94a3b824}.dark .tree-icon-btn{color:#cbd5e1}.dark .tree-icon-btn:hover{color:#fff;background:var(--p-primary-600)}.dark .tree-search-icon{color:#64748b}.dark .tree-search-input{background:#1f2937bd;border-color:#94a3b829;color:#e5e7eb}.dark .tree-search-input:focus{background:#111827eb;border-color:rgba(var(--p-primary-500-rgb),.48)}.dark .tree-search-input::placeholder{color:#64748b}.dark .tree-search-clear{color:#94a3b8}.dark .tree-search-clear:hover{color:var(--p-primary-300);background:rgba(var(--p-primary-500-rgb),.12)}.search-match{background:linear-gradient(to left,rgba(var(--p-primary-500-rgb),.16),transparent)!important;color:var(--p-primary-700)!important;border-right:3px solid var(--p-primary-500)!important;box-shadow:inset 0 0 0 1px rgba(var(--p-primary-500-rgb),.08)}.search-match span{font-weight:800!important}.search-match i{color:var(--p-primary-600)!important}.dark .tree-toolbar{background:#111827b8;border-color:#ffffff14}.dark .tree-search-input{background:#1f2937cc;border-color:#94a3b82e;color:#e5e7eb}.dark .tree-search-input:focus{background:#111827f2}.dark .tree-search-clear{background:#374151e6;color:#cbd5e1}.dark .tree-action-btn{background:rgba(var(--p-primary-500-rgb),.12);color:var(--p-primary-300);border-color:rgba(var(--p-primary-500-rgb),.22)}.dark .tree-action-btn:hover{background:var(--p-primary-600);color:#fff}.dark .search-match{background:rgba(var(--p-primary-500-rgb),.18)!important;color:var(--p-primary-300)!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i2$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i2$1.RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: // ✅ أضف هذا
|
|
1512
|
-
TooltipModule }, { kind: "directive", type: i4$1.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip"] }, { kind: "ngmodule", type: StyleClassModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
|
|
1567
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: RoutesComponent, isStandalone: true, selector: "app-routes", host: { listeners: { "window:resize": "onResize()" } }, providers: [RoutesMenuService], ngImport: i0, template: "<aside\r\n class=\"flex h-screen bg-white dark:bg-gray-900 transition-all duration-300\"\r\n [ngClass]=\"{ 'w-0 overflow-hidden': !menu.sidebarVisible && menu.isMobile }\"\r\n>\r\n <div\r\n class=\"flex flex-col justify-between pt-4 pb-2 w-16 border-l shadow-xl bg-gradient-to-b from-primary-500 via-primary-600 to-primary-700 text-white rounded-r-3xl\"\r\n >\r\n <div class=\"flex flex-col items-center gap-4\">\r\n <img\r\n src=\"/assets/logo.png\"\r\n alt=\"Logo\"\r\n class=\"w-9 h-9 mt-1 rounded-lg shadow-sm\"\r\n />\r\n\r\n <button\r\n (click)=\"menu.toggleSidebar()\"\r\n tooltipPosition=\"right\"\r\n class=\"text-white hover:scale-110 transition-transform duration-200\"\r\n >\r\n <i class=\"pi pi-bars text-lg\"></i>\r\n </button>\r\n @for (item of menu.secondToLastLevel$ | async; track item) {\r\n <div class=\"relative flex flex-col items-center\">\r\n <div\r\n (click)=\"menu.activateItem(item)\"\r\n [pTooltip]=\"item.label | translate\"\r\n tooltipPosition=\"right\"\r\n class=\"relative flex items-center justify-center w-10 h-10 rounded-xl cursor-pointer hover:bg-white/20 transition-all duration-200\"\r\n [ngClass]=\"{ 'bg-white/30 scale-105 shadow-inner': item.expanded }\"\r\n >\r\n <i [class]=\"item.icon + ' text-xl'\"></i>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n <div class=\"flex flex-col items-center pb-3 text-white/80\">\r\n <i\r\n class=\"pi pi-cog text-lg hover:text-white cursor-pointer transition\"\r\n ></i>\r\n </div>\r\n </div>\r\n @if (menu.subSidebarVisible) {\r\n <div\r\n class=\"w-[290px] max-w-[290px] min-w-[260px] border-l border-gray-200 dark:border-gray-700 glass-bg animate-slideIn shadow-2xl rounded-l-3xl p-4 rtl:text-right overflow-y-auto overflow-x-hidden transition-all duration-300\"\r\n [ngClass]=\"{ 'absolute top-0 right-16 h-full z-40': menu.isMobile }\"\r\n >\r\n <app-routes-search></app-routes-search>\r\n\r\n <!-- Recursive rendering -->\r\n @if (menu.menuItems$ | async; as menuItems) {\r\n <ng-container\r\n *ngTemplateOutlet=\"\r\n renderMenu;\r\n context: { $implicit: menuItems, level: 1 }\r\n \"\r\n ></ng-container>\r\n }\r\n\r\n <ng-template #renderMenu let-items let-level=\"level\">\r\n @for (item of items; track item) {\r\n @if (!item.items?.length) {\r\n @let link = menu.getLinkProps(item);\r\n <div class=\"mb-1\">\r\n @if (link.isExternal) {\r\n <a\r\n [href]=\"link.href\"\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\"\r\n (click)=\"$event.stopPropagation()\"\r\n class=\"flex items-center gap-2 px-3 py-2 rounded-lg transition-all hover:bg-primary-50 dark:hover:bg-gray-800 text-gray-700 dark:text-gray-200\"\r\n >\r\n <i [class]=\"item.icon + ' text-base'\"></i>\r\n <span class=\"min-w-0 break-words whitespace-normal\">{{\r\n item.label | translate\r\n }}</span>\r\n <i class=\"pi pi-external-link text-[10px] opacity-50\"></i>\r\n </a>\r\n } @else {\r\n <div class=\"flex items-center gap-2\">\r\n <a\r\n [routerLink]=\"link.routerLink\"\r\n (click)=\"menu.closeAfterNavigate()\"\r\n routerLinkActive=\"active-link\"\r\n [ngClass]=\"{ 'search-match': menu.isSearchMatch(item) }\"\r\n class=\"flex-1 min-w-0 flex items-center gap-2 px-2 py-1 rounded-lg transition-all hover:bg-primary-50 dark:hover:bg-gray-800 text-gray-700 dark:text-gray-200\"\r\n >\r\n <i [class]=\"item.icon + ' text-base'\"></i>\r\n <span class=\"min-w-0 break-words whitespace-normal\">\r\n {{ item.label | translate }}</span\r\n >\r\n </a>\r\n\r\n @if (link.addRoute) {\r\n <a\r\n [routerLink]=\"[link.addRoute]\"\r\n (click)=\"\r\n $event.stopPropagation(); menu.closeAfterNavigate()\r\n \"\r\n class=\"shrink-0 inline-flex items-center justify-center w-7 h-7 rounded-full text-gray-400 hover:text-primary-500 hover:bg-primary-50 dark:text-gray-500 dark:hover:text-primary-400 dark:hover:bg-primary-900/20 transition-all duration-200\"\r\n [pTooltip]=\"'ADD' | translate\"\r\n tooltipPosition=\"top\"\r\n >\r\n <i class=\"pi pi-plus text-xs\"></i>\r\n </a>\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n @if (item.items?.length) {\r\n <div class=\"mb-1\">\r\n <div\r\n (click)=\"menu.toggleItem(item)\"\r\n [style.font-size.rem]=\"1.1 - level * 0.05\"\r\n [ngClass]=\"{\r\n 'font-bold': level === 1,\r\n 'font-semibold': level === 2,\r\n 'font-medium': level >= 3,\r\n 'bg-primary-50 text-primary-700 border-r-4 border-primary-500':\r\n item.expanded,\r\n 'search-match': menu.isSearchMatch(item),\r\n }\"\r\n class=\"flex justify-between items-center py-2 px-3 rounded-lg cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-800 transition text-gray-800 dark:text-gray-200\"\r\n >\r\n <div class=\"flex items-center gap-2\">\r\n <i [class]=\"item.icon + ' text-base'\"></i>\r\n\r\n <span>\r\n {{ item.label | translate }}\r\n </span>\r\n </div>\r\n\r\n <i\r\n class=\"pi\"\r\n [ngClass]=\"\r\n item.expanded ? 'pi-chevron-down' : 'pi-chevron-left'\r\n \"\r\n style=\"font-size: 0.6rem\"\r\n >\r\n </i>\r\n </div>\r\n\r\n @if (item.expanded) {\r\n <div\r\n class=\"mr-4 border-r border-gray-200 dark:border-gray-700 pr-2 mt-1\"\r\n >\r\n <ng-container\r\n *ngTemplateOutlet=\"\r\n renderMenu;\r\n context: { $implicit: item.items, level: level + 1 }\r\n \"\r\n >\r\n </ng-container>\r\n </div>\r\n }\r\n </div>\r\n }\r\n }\r\n </ng-template>\r\n </div>\r\n }\r\n</aside>\r\n@if (menu.isMobile && !menu.sidebarVisible) {\r\n <button\r\n class=\"floating-btn\"\r\n (click)=\"menu.openMenu()\"\r\n pTooltip=\"{{ 'MENU.OPEN' | translate }}\"\r\n tooltipPosition=\"top\"\r\n >\r\n <img src=\"/assets/logo.png\" alt=\"App Icon\" />\r\n </button>\r\n}\r\n", styles: ["@keyframes slideIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}.animate-slideIn{animation:slideIn .3s ease-out}.glass-bg{background-color:#ffffffbf;--tw-backdrop-blur: blur(24px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.glass-bg:is(.dark *){background-color:#111827cc}.glass-bg{-webkit-backdrop-filter:blur(16px) saturate(180%);backdrop-filter:blur(16px) saturate(180%)}.glass-bg::-webkit-scrollbar{width:4px}.glass-bg::-webkit-scrollbar-thumb{border-radius:9999px;background-color:#94a3b84d}@media(max-width:1024px){aside{position:fixed;right:0;top:0;z-index:50;height:100vh}}.floating-btn{position:fixed;bottom:1.25rem;right:1.25rem;z-index:60;display:flex;height:60px;width:60px;align-items:center;justify-content:center;border-radius:9999px;--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.3s;background:linear-gradient(135deg,var(--p-primary-500),var(--p-primary-700))}.floating-btn:hover{transform:scale(1.1) rotate(5deg)}.floating-btn img{height:1.75rem;width:1.75rem;border-radius:.5rem}.active-link{border-radius:.5rem;--tw-bg-opacity: 1;background-color:color-mix(in srgb,var(--p-primary-50) calc(100% * var(--tw-bg-opacity, 1)),transparent);font-weight:600;--tw-text-opacity: 1;color:color-mix(in srgb,var(--p-primary-700) calc(100% * var(--tw-text-opacity, 1)),transparent);--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.active-link:is(.dark *){background-color:color-mix(in srgb,var(--p-primary-500) 15%,transparent);--tw-text-opacity: 1;color:color-mix(in srgb,var(--p-primary-300) calc(100% * var(--tw-text-opacity, 1)),transparent)}.active-link{border-right:4px solid var(--p-primary-500)}.active-link span{font-weight:800}.active-link i{--tw-scale-x: 1.1;--tw-scale-y: 1.1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));--tw-text-opacity: 1;color:color-mix(in srgb,var(--p-primary-600) calc(100% * var(--tw-text-opacity, 1)),transparent)}.active-link i:is(.dark *){--tw-text-opacity: 1;color:color-mix(in srgb,var(--p-primary-300) calc(100% * var(--tw-text-opacity, 1)),transparent)}.search-match{color:var(--p-primary-700)!important;background:linear-gradient(to left,rgba(var(--p-primary-500-rgb),.16),transparent)!important;border-right:3px solid var(--p-primary-500)!important;box-shadow:inset 0 0 0 1px rgba(var(--p-primary-500-rgb),.08)}.search-match span{font-weight:800}.search-match i{color:var(--p-primary-600)!important}.dark .search-match{color:var(--p-primary-300)!important;background:rgba(var(--p-primary-500-rgb),.18)!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i2$2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i2$2.RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i3$2.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip"] }, { kind: "component", type: RoutesSearchComponent, selector: "app-routes-search" }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
|
|
1513
1568
|
}
|
|
1514
1569
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: RoutesComponent, decorators: [{
|
|
1515
1570
|
type: Component,
|
|
1516
1571
|
args: [{ selector: 'app-routes', standalone: true, imports: [
|
|
1517
1572
|
CommonModule,
|
|
1518
1573
|
RouterModule,
|
|
1519
|
-
FormsModule, // ✅ أضف هذا
|
|
1520
1574
|
TooltipModule,
|
|
1521
1575
|
TranslatePipe,
|
|
1522
|
-
|
|
1523
|
-
ButtonModule
|
|
1524
|
-
], template: "<aside class=\"flex h-screen bg-white dark:bg-gray-900 transition-all duration-300\"\r\n [ngClass]=\"{ 'w-0 overflow-hidden': !sidebarVisible && isMobile }\">\r\n\r\n <!-- \uD83C\uDFA8 \u0627\u0644\u0634\u0631\u064A\u0637 \u0627\u0644\u062C\u0627\u0646\u0628\u064A \u0627\u0644\u0631\u0626\u064A\u0633\u064A -->\r\n <div\r\n class=\"flex flex-col justify-between pt-4 pb-2 w-16 border-l shadow-xl\r\n bg-gradient-to-b from-primary-500 via-primary-600 to-primary-700\r\n text-white rounded-r-3xl\">\r\n\r\n <!-- \uD83D\uDD1D \u0627\u0644\u0623\u0639\u0644\u0649 -->\r\n <div class=\"flex flex-col items-center gap-4\">\r\n <img src=\"/assets/logo.png\" alt=\"Logo\"\r\n class=\"w-9 h-9 mt-1 rounded-lg shadow-sm\"/>\r\n\r\n <!-- \u0632\u0631 \u0627\u0644\u0642\u0627\u0626\u0645\u0629 -->\r\n <!-- [pTooltip]=\"'TOGGLE_MENU' | translate\" -->\r\n <button (click)=\"toggleAll()\"\r\n tooltipPosition=\"right\"\r\n class=\"text-white hover:scale-110 transition-transform duration-200\">\r\n <i class=\"pi pi-bars text-lg\"></i>\r\n </button>\r\n\r\n <!-- \u0623\u064A\u0642\u0648\u0646\u0627\u062A -->\r\n @for (item of secondToLastLevel$ | async; track item) {\r\n <div class=\"relative flex flex-col items-center\">\r\n <!-- \u0627\u0644\u0623\u064A\u0642\u0648\u0646\u0629 -->\r\n <div (click)=\"onMainClick(item)\"\r\n [pTooltip]=\"item.label | translate\"\r\n tooltipPosition=\"right\"\r\n class=\"relative flex items-center justify-center w-10 h-10 rounded-xl cursor-pointer\r\n hover:bg-white/20 transition-all duration-200\"\r\n [ngClass]=\"{ 'bg-white/30 scale-105 shadow-inner': item.expanded }\">\r\n <i [class]=\"item.icon + ' text-xl'\"></i>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- \u2699\uFE0F \u0627\u0644\u0623\u0633\u0641\u0644 -->\r\n <div class=\"flex flex-col items-center pb-3 text-white/80\">\r\n <i class=\"pi pi-cog text-lg hover:text-white cursor-pointer transition\"></i>\r\n </div>\r\n </div>\r\n\r\n <!-- \uD83D\uDCCB \u0627\u0644\u0642\u0627\u0626\u0645\u0629 \u0627\u0644\u0641\u0631\u0639\u064A\u0629 -->\r\n @if (subSidebarVisible) {\r\n <div\r\n class=\"w-[290px] max-w-[290px] min-w-[260px] border-l border-gray-200 dark:border-gray-700 glass-bg animate-slideIn\r\n shadow-2xl rounded-l-3xl p-4 rtl:text-right overflow-y-auto overflow-x-hidden transition-all duration-300\"\r\n [ngClass]=\"{ 'absolute top-0 right-16 h-full z-40': isMobile }\">\r\n <!-- \u0632\u0631 \u0631\u062C\u0648\u0639 -->\r\n <!-- \uD83D\uDD1D \u0631\u0623\u0633 \u0627\u0644\u0634\u062C\u0631\u0629 -->\r\n<!-- \uD83D\uDD1D \u0631\u0623\u0633 \u0627\u0644\u0634\u062C\u0631\u0629 -->\r\n<div class=\"tree-header mb-1\">\r\n\r\n <button\r\n type=\"button\"\r\n (click)=\"closeMenu()\"\r\n class=\"tree-back-btn\">\r\n <i class=\"pi pi-angle-right\"></i>\r\n <span>{{ 'back' | translate }}</span>\r\n </button>\r\n\r\n <div class=\"tree-header-actions\">\r\n\r\n <button\r\n type=\"button\"\r\n class=\"tree-mini-btn\"\r\n (click)=\"expandAll()\"\r\n pTooltip=\"\u0641\u062A\u062D \u0627\u0644\u0634\u062C\u0631\u0629 \u0643\u0627\u0645\u0644\u0629\"\r\n tooltipPosition=\"top\">\r\n <i class=\"pi pi-plus\"></i>\r\n </button>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"tree-mini-btn\"\r\n (click)=\"collapseAll()\"\r\n pTooltip=\"\u0625\u063A\u0644\u0627\u0642 \u0627\u0644\u0634\u062C\u0631\u0629 \u0643\u0627\u0645\u0644\u0629\"\r\n tooltipPosition=\"top\">\r\n <i class=\"pi pi-minus\"></i>\r\n </button>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"tree-search-toggle\"\r\n [ngClass]=\"{ 'active': showTreeSearch }\"\r\n (click)=\"toggleTreeSearch()\"\r\n pTooltip=\"\u0628\u062D\u062B \u062F\u0627\u062E\u0644 \u0627\u0644\u0634\u0627\u0634\u0627\u062A\"\r\n tooltipPosition=\"top\">\r\n <i class=\"pi pi-search\"></i>\r\n </button>\r\n\r\n </div>\r\n\r\n</div>\r\n\r\n@if (showTreeSearch) {\r\n <div class=\"tree-search-panel mb-4 animate-slideIn\">\r\n <div class=\"tree-search\">\r\n <i class=\"pi pi-search\"></i>\r\n\r\n <input\r\n type=\"text\"\r\n [(ngModel)]=\"searchText\"\r\n (input)=\"onSearchChange()\"\r\n placeholder=\"\u0628\u062D\u062B \u062F\u0627\u062E\u0644 \u0627\u0644\u0634\u0627\u0634\u0627\u062A...\"\r\n class=\"tree-search-input\" />\r\n\r\n @if (searchText) {\r\n <button\r\n type=\"button\"\r\n class=\"tree-search-clear\"\r\n (click)=\"clearSearch()\">\r\n <i class=\"pi pi-times\"></i>\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n}\r\n\r\n\r\n <!-- Recursive rendering -->\r\n @if (menuItems$ | async; as menuItems) {\r\n <ng-container *ngTemplateOutlet=\"renderMenu; context:{ $implicit: menuItems, level: 1 }\"></ng-container>\r\n}\r\n\r\n<ng-template #renderMenu let-items let-level=\"level\">\r\n @for (item of items; track item) {\r\n\r\n @if (!item.items?.length) {\r\n @let link = getLinkProps(item);\r\n <div class=\"mb-1\">\r\n @if (link.isExternal) {\r\n <a [href]=\"link.href\"\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\"\r\n (click)=\"$event.stopPropagation()\"\r\n class=\"flex items-center gap-2 px-3 py-2 rounded-lg transition-all hover:bg-primary-50 dark:hover:bg-gray-800 text-gray-700 dark:text-gray-200\">\r\n <i [class]=\"item.icon + ' text-base'\"></i>\r\n <span class=\"min-w-0 break-words whitespace-normal\">{{ item.label | translate }}</span>\r\n <i class=\"pi pi-external-link text-[10px] opacity-50\"></i>\r\n </a>\r\n } @else {\r\n <div class=\"flex items-center gap-2\">\r\n <a [routerLink]=\"link.routerLink\"\r\n (click)=\"onNavigate()\"\r\n routerLinkActive=\"active-link\"\r\n [ngClass]=\"{ 'search-match': isSearchMatch(item) }\"\r\n class=\"flex-1 min-w-0 flex items-center gap-2 px-2 py-1 rounded-lg transition-all hover:bg-primary-50 dark:hover:bg-gray-800 text-gray-700 dark:text-gray-200\">\r\n <i [class]=\"item.icon + ' text-base'\"></i>\r\n <span class=\"min-w-0 break-words whitespace-normal\">\r\n {{ item.label | translate }}</span>\r\n </a>\r\n\r\n@if (link.addRoute) {\r\n <a\r\n [routerLink]=\"[link.addRoute]\"\r\n (click)=\"$event.stopPropagation(); onNavigate()\"\r\n class=\"shrink-0 inline-flex items-center justify-center w-7 h-7 rounded-full text-gray-400 hover:text-primary-500 hover:bg-primary-50 dark:text-gray-500 dark:hover:text-primary-400 dark:hover:bg-primary-900/20 transition-all duration-200\"\r\n [pTooltip]=\"'ADD' | translate\"\r\n tooltipPosition=\"top\">\r\n <i class=\"pi pi-plus text-xs\"></i>\r\n </a>\r\n}\r\n </div>\r\n }\r\n</div>\r\n}\r\n\r\n @if (item.items?.length) {\r\n <div class=\"mb-1\">\r\n\r\n <div\r\n (click)=\"onParentClick(item)\"\r\n [style.font-size.rem]=\"1.1 - (level * 0.05)\"\r\n[ngClass]=\"{\r\n 'font-bold': level === 1,\r\n 'font-semibold': level === 2,\r\n 'font-medium': level >= 3,\r\n 'bg-primary-50 text-primary-700 border-r-4 border-primary-500': item.expanded,\r\n 'search-match': isSearchMatch(item)\r\n}\"\r\n class=\"flex justify-between items-center py-2 px-3 rounded-lg cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-800 transition text-gray-800 dark:text-gray-200\">\r\n\r\n <div class=\"flex items-center gap-2\">\r\n\r\n <i [class]=\"item.icon + ' text-base'\"></i>\r\n\r\n <span>\r\n {{ item.label | translate }}\r\n </span>\r\n\r\n </div>\r\n\r\n <i\r\n class=\"pi\"\r\n [ngClass]=\"item.expanded ? 'pi-chevron-down' : 'pi-chevron-left'\"\r\n style=\"font-size:0.6rem;\">\r\n </i>\r\n\r\n </div>\r\n\r\n @if (item.expanded) {\r\n <div class=\"mr-4 border-r border-gray-200 dark:border-gray-700 pr-2 mt-1\">\r\n\r\n <ng-container\r\n *ngTemplateOutlet=\"renderMenu; context:{ $implicit: item.items, level: level + 1 }\">\r\n </ng-container>\r\n\r\n </div>\r\n }\r\n\r\n </div>\r\n }\r\n\r\n }\r\n</ng-template>\r\n\r\n </div>\r\n}\r\n\r\n</aside>\r\n\r\n<!-- \uD83C\uDF1F \u0627\u0644\u0632\u0631 \u0627\u0644\u0639\u0627\u0626\u0645 \u0627\u0644\u062D\u062F\u064A\u062B -->\r\n@if (isMobile && !sidebarVisible) {\r\n <button\r\n class=\"floating-btn\"\r\n (click)=\"openMenu()\"\r\n pTooltip=\"{{ 'MENU.OPEN' | translate }}\"\r\n tooltipPosition=\"top\">\r\n <img src=\"/assets/logo.png\" alt=\"App Icon\" />\r\n </button>\r\n}\r\n", styles: ["@charset \"UTF-8\";@keyframes slideIn{0%{transform:translate(60px);opacity:0}to{transform:translate(0);opacity:1}}.animate-slideIn{animation:slideIn .35s cubic-bezier(.25,1,.5,1)}.glass-bg{background:#ffffffbf;backdrop-filter:blur(16px) saturate(180%);-webkit-backdrop-filter:blur(16px) saturate(180%);border:1px solid rgba(255,255,255,.25)}@media(max-width:1024px){aside{position:fixed;top:0;right:0;height:100vh;z-index:50}}.floating-btn{position:fixed;bottom:1.25rem;right:1.25rem;z-index:60;width:60px;height:60px;border-radius:50%;background:linear-gradient(135deg,var(--p-primary-500),var(--p-primary-700));box-shadow:0 4px 18px #00000040;display:flex;align-items:center;justify-content:center;transition:all .3s ease}.floating-btn:hover{transform:scale(1.1) rotate(5deg)}.floating-btn img{width:28px;height:28px;border-radius:10px}.active-link{background-color:var(--p-primary-500)!important;color:#fff!important;font-weight:600;box-shadow:0 4px 6px -1px #0000001a;transform:translate(-3px)}.active-link i{color:#fff!important}.active-item-link{background:linear-gradient(to left,var(--p-primary-50),transparent);color:var(--p-primary-600)!important;position:relative;border-right:3px solid var(--p-primary-500);border-radius:0 12px 12px 0}.active-item-link i{color:var(--p-primary-500);filter:drop-shadow(0 0 5px rgba(var(--p-primary-500-rgb),.4))}.active-item-link span{font-weight:700}.animate-slideIn{animation:slideIn .3s ease-out}@keyframes slideIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}.glass-bg::-webkit-scrollbar{width:4px}.glass-bg::-webkit-scrollbar-thumb{background:#0000001a;border-radius:10px}.active-link{background-color:var(--p-primary-50)!important;color:var(--p-primary-700)!important;border-right:4px solid var(--p-primary-500)!important;border-radius:4px 12px 12px 4px!important}.active-link span{font-weight:800!important}.active-link i{color:var(--p-primary-600)!important;transform:scale(1.1)}aside{-webkit-font-smoothing:antialiased;letter-spacing:-.01em}.dark .active-link{background-color:rgba(var(--p-primary-500-rgb),.15)!important;color:var(--p-primary-300)!important}.submenu{animation:menuOpen .25s ease}@keyframes menuOpen{0%{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:translateY(0)}}aside{box-shadow:0 10px 40px #00000026}.tree-back-btn{height:2.25rem;padding:0 .35rem 0 .75rem;display:inline-flex;align-items:center;gap:.35rem;color:#64748b;font-weight:700;border-radius:999px;transition:all .2s ease}.tree-header{display:flex;align-items:center;justify-content:space-between;gap:.75rem}.tree-back-btn{height:2.35rem;padding:0 .45rem 0 .85rem;display:inline-flex;align-items:center;gap:.4rem;color:#64748b;font-size:.86rem;font-weight:700;border-radius:999px;transition:all .22s ease}.tree-back-btn:hover{color:var(--p-primary-600);background:var(--p-primary-50)}.tree-back-btn i{font-size:.85rem}.tree-header-actions{display:inline-flex;align-items:center;gap:.3rem}.tree-mini-btn,.tree-search-toggle{width:2rem;height:2rem;border-radius:.8rem;display:inline-flex;align-items:center;justify-content:center;color:#64748b;background:#f8fafcd9;border:1px solid rgba(148,163,184,.24);transition:all .2s ease}.tree-mini-btn i,.tree-search-toggle i{font-size:.72rem}.tree-mini-btn:hover,.tree-search-toggle:hover{color:var(--p-primary-600);background:var(--p-primary-50);border-color:rgba(var(--p-primary-500-rgb),.25);transform:translateY(-1px)}.tree-search-toggle{color:var(--p-primary-600);background:rgba(var(--p-primary-500-rgb),.08);border-color:rgba(var(--p-primary-500-rgb),.18)}.tree-search-toggle.active{color:#fff;background:linear-gradient(135deg,var(--p-primary-500),var(--p-primary-600));border-color:transparent;box-shadow:0 8px 18px rgba(var(--p-primary-500-rgb),.22)}.tree-search-panel{padding:.65rem;border-radius:1.15rem;background:#ffffff94;border:1px solid rgba(255,255,255,.45);box-shadow:0 10px 30px #0f172a14;backdrop-filter:blur(14px) saturate(160%);-webkit-backdrop-filter:blur(14px) saturate(160%)}.tree-search{position:relative;display:flex;align-items:center}.tree-search i.pi-search{position:absolute;right:.85rem;color:var(--p-primary-500);font-size:.85rem;z-index:1}.tree-search-input{width:100%;height:2.45rem;padding:0 2.35rem 0 2.2rem;border-radius:999px;border:1px solid rgba(148,163,184,.35);background:#ffffffd1;color:#334155;font-size:.86rem;outline:none;transition:all .25s ease}.tree-search-input:focus{border-color:var(--p-primary-400);box-shadow:0 0 0 4px rgba(var(--p-primary-500-rgb),.12);background:#fff}.tree-search-clear{position:absolute;left:.55rem;width:1.75rem;height:1.75rem;border-radius:999px;display:inline-flex;align-items:center;justify-content:center;color:#64748b;background:#f1f5f9e6;transition:all .2s ease}.tree-search-clear:hover{color:var(--p-primary-600);background:var(--p-primary-50);transform:scale(1.05)}.tree-search-icon{position:absolute;right:.85rem;color:#94a3b8;font-size:.85rem;pointer-events:none}.tree-search-input{width:100%;height:2.45rem;padding:0 2.35rem 0 2.2rem;border-radius:.95rem;border:1px solid rgba(148,163,184,.28);background:#f8fafcd1;color:#334155;font-size:.86rem;outline:none;transition:all .22s ease}.tree-search-input:focus{border-color:rgba(var(--p-primary-500-rgb),.45);background:#fff;box-shadow:0 0 0 3px rgba(var(--p-primary-500-rgb),.1)}.tree-search-input::placeholder{color:#94a3b8}.tree-search-clear{position:absolute;left:.55rem;width:1.65rem;height:1.65rem;border-radius:999px;display:inline-flex;align-items:center;justify-content:center;color:#94a3b8;background:transparent;transition:all .2s ease}.tree-search-clear:hover{color:var(--p-primary-600);background:var(--p-primary-50)}.tree-search-clear i{font-size:.7rem}.dark .tree-back-btn{color:#cbd5e1}.dark .tree-back-btn:hover{color:var(--p-primary-300);background:rgba(var(--p-primary-500-rgb),.12)}.dark .tree-header-actions{background:#1f2937b8;border-color:#94a3b824}.dark .tree-icon-btn{color:#cbd5e1}.dark .tree-icon-btn:hover{color:#fff;background:var(--p-primary-600)}.dark .tree-search-icon{color:#64748b}.dark .tree-search-input{background:#1f2937bd;border-color:#94a3b829;color:#e5e7eb}.dark .tree-search-input:focus{background:#111827eb;border-color:rgba(var(--p-primary-500-rgb),.48)}.dark .tree-search-input::placeholder{color:#64748b}.dark .tree-search-clear{color:#94a3b8}.dark .tree-search-clear:hover{color:var(--p-primary-300);background:rgba(var(--p-primary-500-rgb),.12)}.search-match{background:linear-gradient(to left,rgba(var(--p-primary-500-rgb),.16),transparent)!important;color:var(--p-primary-700)!important;border-right:3px solid var(--p-primary-500)!important;box-shadow:inset 0 0 0 1px rgba(var(--p-primary-500-rgb),.08)}.search-match span{font-weight:800!important}.search-match i{color:var(--p-primary-600)!important}.dark .tree-toolbar{background:#111827b8;border-color:#ffffff14}.dark .tree-search-input{background:#1f2937cc;border-color:#94a3b82e;color:#e5e7eb}.dark .tree-search-input:focus{background:#111827f2}.dark .tree-search-clear{background:#374151e6;color:#cbd5e1}.dark .tree-action-btn{background:rgba(var(--p-primary-500-rgb),.12);color:var(--p-primary-300);border-color:rgba(var(--p-primary-500-rgb),.22)}.dark .tree-action-btn:hover{background:var(--p-primary-600);color:#fff}.dark .search-match{background:rgba(var(--p-primary-500-rgb),.18)!important;color:var(--p-primary-300)!important}\n"] }]
|
|
1525
|
-
}], ctorParameters: () => [], propDecorators: { onResize: [{
|
|
1576
|
+
RoutesSearchComponent,
|
|
1577
|
+
], providers: [RoutesMenuService], template: "<aside\r\n class=\"flex h-screen bg-white dark:bg-gray-900 transition-all duration-300\"\r\n [ngClass]=\"{ 'w-0 overflow-hidden': !menu.sidebarVisible && menu.isMobile }\"\r\n>\r\n <div\r\n class=\"flex flex-col justify-between pt-4 pb-2 w-16 border-l shadow-xl bg-gradient-to-b from-primary-500 via-primary-600 to-primary-700 text-white rounded-r-3xl\"\r\n >\r\n <div class=\"flex flex-col items-center gap-4\">\r\n <img\r\n src=\"/assets/logo.png\"\r\n alt=\"Logo\"\r\n class=\"w-9 h-9 mt-1 rounded-lg shadow-sm\"\r\n />\r\n\r\n <button\r\n (click)=\"menu.toggleSidebar()\"\r\n tooltipPosition=\"right\"\r\n class=\"text-white hover:scale-110 transition-transform duration-200\"\r\n >\r\n <i class=\"pi pi-bars text-lg\"></i>\r\n </button>\r\n @for (item of menu.secondToLastLevel$ | async; track item) {\r\n <div class=\"relative flex flex-col items-center\">\r\n <div\r\n (click)=\"menu.activateItem(item)\"\r\n [pTooltip]=\"item.label | translate\"\r\n tooltipPosition=\"right\"\r\n class=\"relative flex items-center justify-center w-10 h-10 rounded-xl cursor-pointer hover:bg-white/20 transition-all duration-200\"\r\n [ngClass]=\"{ 'bg-white/30 scale-105 shadow-inner': item.expanded }\"\r\n >\r\n <i [class]=\"item.icon + ' text-xl'\"></i>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n <div class=\"flex flex-col items-center pb-3 text-white/80\">\r\n <i\r\n class=\"pi pi-cog text-lg hover:text-white cursor-pointer transition\"\r\n ></i>\r\n </div>\r\n </div>\r\n @if (menu.subSidebarVisible) {\r\n <div\r\n class=\"w-[290px] max-w-[290px] min-w-[260px] border-l border-gray-200 dark:border-gray-700 glass-bg animate-slideIn shadow-2xl rounded-l-3xl p-4 rtl:text-right overflow-y-auto overflow-x-hidden transition-all duration-300\"\r\n [ngClass]=\"{ 'absolute top-0 right-16 h-full z-40': menu.isMobile }\"\r\n >\r\n <app-routes-search></app-routes-search>\r\n\r\n <!-- Recursive rendering -->\r\n @if (menu.menuItems$ | async; as menuItems) {\r\n <ng-container\r\n *ngTemplateOutlet=\"\r\n renderMenu;\r\n context: { $implicit: menuItems, level: 1 }\r\n \"\r\n ></ng-container>\r\n }\r\n\r\n <ng-template #renderMenu let-items let-level=\"level\">\r\n @for (item of items; track item) {\r\n @if (!item.items?.length) {\r\n @let link = menu.getLinkProps(item);\r\n <div class=\"mb-1\">\r\n @if (link.isExternal) {\r\n <a\r\n [href]=\"link.href\"\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\"\r\n (click)=\"$event.stopPropagation()\"\r\n class=\"flex items-center gap-2 px-3 py-2 rounded-lg transition-all hover:bg-primary-50 dark:hover:bg-gray-800 text-gray-700 dark:text-gray-200\"\r\n >\r\n <i [class]=\"item.icon + ' text-base'\"></i>\r\n <span class=\"min-w-0 break-words whitespace-normal\">{{\r\n item.label | translate\r\n }}</span>\r\n <i class=\"pi pi-external-link text-[10px] opacity-50\"></i>\r\n </a>\r\n } @else {\r\n <div class=\"flex items-center gap-2\">\r\n <a\r\n [routerLink]=\"link.routerLink\"\r\n (click)=\"menu.closeAfterNavigate()\"\r\n routerLinkActive=\"active-link\"\r\n [ngClass]=\"{ 'search-match': menu.isSearchMatch(item) }\"\r\n class=\"flex-1 min-w-0 flex items-center gap-2 px-2 py-1 rounded-lg transition-all hover:bg-primary-50 dark:hover:bg-gray-800 text-gray-700 dark:text-gray-200\"\r\n >\r\n <i [class]=\"item.icon + ' text-base'\"></i>\r\n <span class=\"min-w-0 break-words whitespace-normal\">\r\n {{ item.label | translate }}</span\r\n >\r\n </a>\r\n\r\n @if (link.addRoute) {\r\n <a\r\n [routerLink]=\"[link.addRoute]\"\r\n (click)=\"\r\n $event.stopPropagation(); menu.closeAfterNavigate()\r\n \"\r\n class=\"shrink-0 inline-flex items-center justify-center w-7 h-7 rounded-full text-gray-400 hover:text-primary-500 hover:bg-primary-50 dark:text-gray-500 dark:hover:text-primary-400 dark:hover:bg-primary-900/20 transition-all duration-200\"\r\n [pTooltip]=\"'ADD' | translate\"\r\n tooltipPosition=\"top\"\r\n >\r\n <i class=\"pi pi-plus text-xs\"></i>\r\n </a>\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n @if (item.items?.length) {\r\n <div class=\"mb-1\">\r\n <div\r\n (click)=\"menu.toggleItem(item)\"\r\n [style.font-size.rem]=\"1.1 - level * 0.05\"\r\n [ngClass]=\"{\r\n 'font-bold': level === 1,\r\n 'font-semibold': level === 2,\r\n 'font-medium': level >= 3,\r\n 'bg-primary-50 text-primary-700 border-r-4 border-primary-500':\r\n item.expanded,\r\n 'search-match': menu.isSearchMatch(item),\r\n }\"\r\n class=\"flex justify-between items-center py-2 px-3 rounded-lg cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-800 transition text-gray-800 dark:text-gray-200\"\r\n >\r\n <div class=\"flex items-center gap-2\">\r\n <i [class]=\"item.icon + ' text-base'\"></i>\r\n\r\n <span>\r\n {{ item.label | translate }}\r\n </span>\r\n </div>\r\n\r\n <i\r\n class=\"pi\"\r\n [ngClass]=\"\r\n item.expanded ? 'pi-chevron-down' : 'pi-chevron-left'\r\n \"\r\n style=\"font-size: 0.6rem\"\r\n >\r\n </i>\r\n </div>\r\n\r\n @if (item.expanded) {\r\n <div\r\n class=\"mr-4 border-r border-gray-200 dark:border-gray-700 pr-2 mt-1\"\r\n >\r\n <ng-container\r\n *ngTemplateOutlet=\"\r\n renderMenu;\r\n context: { $implicit: item.items, level: level + 1 }\r\n \"\r\n >\r\n </ng-container>\r\n </div>\r\n }\r\n </div>\r\n }\r\n }\r\n </ng-template>\r\n </div>\r\n }\r\n</aside>\r\n@if (menu.isMobile && !menu.sidebarVisible) {\r\n <button\r\n class=\"floating-btn\"\r\n (click)=\"menu.openMenu()\"\r\n pTooltip=\"{{ 'MENU.OPEN' | translate }}\"\r\n tooltipPosition=\"top\"\r\n >\r\n <img src=\"/assets/logo.png\" alt=\"App Icon\" />\r\n </button>\r\n}\r\n", styles: ["@keyframes slideIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}.animate-slideIn{animation:slideIn .3s ease-out}.glass-bg{background-color:#ffffffbf;--tw-backdrop-blur: blur(24px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.glass-bg:is(.dark *){background-color:#111827cc}.glass-bg{-webkit-backdrop-filter:blur(16px) saturate(180%);backdrop-filter:blur(16px) saturate(180%)}.glass-bg::-webkit-scrollbar{width:4px}.glass-bg::-webkit-scrollbar-thumb{border-radius:9999px;background-color:#94a3b84d}@media(max-width:1024px){aside{position:fixed;right:0;top:0;z-index:50;height:100vh}}.floating-btn{position:fixed;bottom:1.25rem;right:1.25rem;z-index:60;display:flex;height:60px;width:60px;align-items:center;justify-content:center;border-radius:9999px;--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.3s;background:linear-gradient(135deg,var(--p-primary-500),var(--p-primary-700))}.floating-btn:hover{transform:scale(1.1) rotate(5deg)}.floating-btn img{height:1.75rem;width:1.75rem;border-radius:.5rem}.active-link{border-radius:.5rem;--tw-bg-opacity: 1;background-color:color-mix(in srgb,var(--p-primary-50) calc(100% * var(--tw-bg-opacity, 1)),transparent);font-weight:600;--tw-text-opacity: 1;color:color-mix(in srgb,var(--p-primary-700) calc(100% * var(--tw-text-opacity, 1)),transparent);--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.active-link:is(.dark *){background-color:color-mix(in srgb,var(--p-primary-500) 15%,transparent);--tw-text-opacity: 1;color:color-mix(in srgb,var(--p-primary-300) calc(100% * var(--tw-text-opacity, 1)),transparent)}.active-link{border-right:4px solid var(--p-primary-500)}.active-link span{font-weight:800}.active-link i{--tw-scale-x: 1.1;--tw-scale-y: 1.1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));--tw-text-opacity: 1;color:color-mix(in srgb,var(--p-primary-600) calc(100% * var(--tw-text-opacity, 1)),transparent)}.active-link i:is(.dark *){--tw-text-opacity: 1;color:color-mix(in srgb,var(--p-primary-300) calc(100% * var(--tw-text-opacity, 1)),transparent)}.search-match{color:var(--p-primary-700)!important;background:linear-gradient(to left,rgba(var(--p-primary-500-rgb),.16),transparent)!important;border-right:3px solid var(--p-primary-500)!important;box-shadow:inset 0 0 0 1px rgba(var(--p-primary-500-rgb),.08)}.search-match span{font-weight:800}.search-match i{color:var(--p-primary-600)!important}.dark .search-match{color:var(--p-primary-300)!important;background:rgba(var(--p-primary-500-rgb),.18)!important}\n"] }]
|
|
1578
|
+
}], propDecorators: { onResize: [{
|
|
1526
1579
|
type: HostListener,
|
|
1527
1580
|
args: ['window:resize']
|
|
1528
1581
|
}] } });
|
|
@@ -1728,18 +1781,18 @@ class ApplicationLayoutComponent {
|
|
|
1728
1781
|
catch (error) {
|
|
1729
1782
|
}
|
|
1730
1783
|
}
|
|
1731
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ApplicationLayoutComponent, deps: [{ token: LayoutService }, { token: i0.Renderer2 }, { token: i2$
|
|
1784
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ApplicationLayoutComponent, deps: [{ token: LayoutService }, { token: i0.Renderer2 }, { token: i2$2.Router }, { token: i0.NgZone }, { token: i3$3.LocalizationService }], target: i0.ɵɵFactoryTarget.Component });
|
|
1732
1785
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.3", type: ApplicationLayoutComponent, isStandalone: true, selector: "abp-layout-application", providers: [LayoutService, SubscriptionService], ngImport: i0, template: "\r\n\r\n<div class=\"flex h-screen overflow-hidden\">\r\n\r\n <!-- Sidebar stays fixed -->\r\n\r\n <app-routes\r\n *abpReplaceableTemplate=\"{\r\n componentKey: service.routesComponentKey,\r\n inputs: { smallScreen: { value: service.smallScreen } }\r\n }\"\r\n class=\"mb-2 md:mb-0\"\r\n ></app-routes>\r\n\r\n <!-- Main content scrolls -->\r\n <main class=\"flex-1 flex flex-col overflow-hidden\">\r\n\r\n <!-- Optional Topbar -->\r\n <!-- @if (showTopbar) { -->\r\n\r\n <app-nav-items\r\n *abpReplaceableTemplate=\"{\r\n componentKey: service.navItemsComponentKey\r\n }\"\r\n ></app-nav-items>\r\n\r\n <!-- p-6 space-y-6 -->\r\n <div class=\"flex-1 overflow-y-auto\">\r\n <router-outlet #outlet=\"outlet\"></router-outlet>\r\n </div>\r\n\r\n <!-- <app-footer></app-footer> -->\r\n\r\n </main>\r\n\r\n </div>\r\n\r\n", dependencies: [{ kind: "component", type: RoutesComponent, selector: "app-routes" }, { kind: "component", type: NavItemsComponent, selector: "app-nav-items" }, { kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "directive", type: ReplaceableTemplateDirective, selector: "[abpReplaceableTemplate]", inputs: ["abpReplaceableTemplate"] }], animations: [ /* slideFromBottom, */ /* appModuleAnimation() */] });
|
|
1733
1786
|
}
|
|
1734
1787
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ApplicationLayoutComponent, decorators: [{
|
|
1735
1788
|
type: Component,
|
|
1736
1789
|
args: [{ standalone: true, imports: [RoutesComponent, NavItemsComponent, RouterOutlet, ReplaceableTemplateDirective], selector: 'abp-layout-application', animations: [ /* slideFromBottom, */ /* appModuleAnimation() */], providers: [LayoutService, SubscriptionService], template: "\r\n\r\n<div class=\"flex h-screen overflow-hidden\">\r\n\r\n <!-- Sidebar stays fixed -->\r\n\r\n <app-routes\r\n *abpReplaceableTemplate=\"{\r\n componentKey: service.routesComponentKey,\r\n inputs: { smallScreen: { value: service.smallScreen } }\r\n }\"\r\n class=\"mb-2 md:mb-0\"\r\n ></app-routes>\r\n\r\n <!-- Main content scrolls -->\r\n <main class=\"flex-1 flex flex-col overflow-hidden\">\r\n\r\n <!-- Optional Topbar -->\r\n <!-- @if (showTopbar) { -->\r\n\r\n <app-nav-items\r\n *abpReplaceableTemplate=\"{\r\n componentKey: service.navItemsComponentKey\r\n }\"\r\n ></app-nav-items>\r\n\r\n <!-- p-6 space-y-6 -->\r\n <div class=\"flex-1 overflow-y-auto\">\r\n <router-outlet #outlet=\"outlet\"></router-outlet>\r\n </div>\r\n\r\n <!-- <app-footer></app-footer> -->\r\n\r\n </main>\r\n\r\n </div>\r\n\r\n" }]
|
|
1737
|
-
}], ctorParameters: () => [{ type: LayoutService }, { type: i0.Renderer2 }, { type: i2$
|
|
1790
|
+
}], ctorParameters: () => [{ type: LayoutService }, { type: i0.Renderer2 }, { type: i2$2.Router }, { type: i0.NgZone }, { type: i3$3.LocalizationService }] });
|
|
1738
1791
|
|
|
1739
1792
|
class EmptyLayoutComponent {
|
|
1740
1793
|
static type = eLayoutType.empty;
|
|
1741
1794
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: EmptyLayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1742
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.3", type: EmptyLayoutComponent, isStandalone: true, selector: "abp-layout-empty", ngImport: i0, template: ` <router-outlet></router-outlet> `, isInline: true, dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i2$
|
|
1795
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.3", type: EmptyLayoutComponent, isStandalone: true, selector: "abp-layout-empty", ngImport: i0, template: ` <router-outlet></router-outlet> `, isInline: true, dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i2$2.RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }] });
|
|
1743
1796
|
}
|
|
1744
1797
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: EmptyLayoutComponent, decorators: [{
|
|
1745
1798
|
type: Component,
|