@sdcorejs/angular 20.1.3 → 20.1.5
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.
- package/README.md +19 -14
- package/components/audit-diff/index.d.ts +74 -0
- package/components/badge/index.d.ts +1 -1
- package/components/breadcrumb/index.d.ts +53 -0
- package/components/data-state/index.d.ts +44 -0
- package/components/document-builder/index.d.ts +5 -5
- package/components/form-generic/index.d.ts +67 -104
- package/components/import-excel/index.d.ts +4 -14
- package/components/index.d.ts +4 -0
- package/components/job-progress/index.d.ts +38 -0
- package/components/mini-editor/index.d.ts +9 -9
- package/components/modal/index.d.ts +7 -1
- package/components/preview/index.d.ts +181 -28
- package/components/side-drawer/index.d.ts +7 -1
- package/components/tab/index.d.ts +9 -2
- package/components/table/index.d.ts +8 -7
- package/components/tree/index.d.ts +24 -2
- package/components/upload-file/index.d.ts +5 -4
- package/directives/index.d.ts +10 -8
- package/fesm2022/sdcorejs-angular-components-anchor.mjs +14 -14
- package/fesm2022/sdcorejs-angular-components-anchor.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-audit-diff.mjs +332 -0
- package/fesm2022/sdcorejs-angular-components-audit-diff.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-badge.mjs +1 -0
- package/fesm2022/sdcorejs-angular-components-badge.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-breadcrumb.mjs +121 -0
- package/fesm2022/sdcorejs-angular-components-breadcrumb.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-data-state.mjs +68 -0
- package/fesm2022/sdcorejs-angular-components-data-state.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-document-builder.mjs +18 -23
- package/fesm2022/sdcorejs-angular-components-document-builder.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-editor.mjs +6 -4
- package/fesm2022/sdcorejs-angular-components-editor.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-form-generic.mjs +495 -838
- package/fesm2022/sdcorejs-angular-components-form-generic.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-import-excel.mjs +24 -34
- package/fesm2022/sdcorejs-angular-components-import-excel.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-job-progress.mjs +97 -0
- package/fesm2022/sdcorejs-angular-components-job-progress.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-mini-editor.mjs +10 -20
- package/fesm2022/sdcorejs-angular-components-mini-editor.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-modal.mjs +61 -4
- package/fesm2022/sdcorejs-angular-components-modal.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-preview.mjs +1438 -218
- package/fesm2022/sdcorejs-angular-components-preview.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-query-bar.mjs +4 -4
- package/fesm2022/sdcorejs-angular-components-query-bar.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-query-builder.mjs +1 -1
- package/fesm2022/sdcorejs-angular-components-query-builder.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-side-drawer.mjs +39 -2
- package/fesm2022/sdcorejs-angular-components-side-drawer.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-tab-router.mjs +16 -22
- package/fesm2022/sdcorejs-angular-components-tab-router.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-tab.mjs +48 -4
- package/fesm2022/sdcorejs-angular-components-tab.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-table.mjs +96 -63
- package/fesm2022/sdcorejs-angular-components-table.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-tree.mjs +186 -17
- package/fesm2022/sdcorejs-angular-components-tree.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-upload-file.mjs +15 -22
- package/fesm2022/sdcorejs-angular-components-upload-file.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components.mjs +4 -0
- package/fesm2022/sdcorejs-angular-components.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-directives.mjs +29 -41
- package/fesm2022/sdcorejs-angular-directives.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs +7 -7
- package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-checkbox.mjs +20 -83
- package/fesm2022/sdcorejs-angular-forms-checkbox.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs +6 -3
- package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-chip.mjs +6 -3
- package/fesm2022/sdcorejs-angular-forms-chip.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-date-range.mjs +16 -13
- package/fesm2022/sdcorejs-angular-forms-date-range.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-date.mjs +42 -65
- package/fesm2022/sdcorejs-angular-forms-date.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-datetime.mjs +51 -66
- package/fesm2022/sdcorejs-angular-forms-datetime.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-entity-picker.mjs +384 -0
- package/fesm2022/sdcorejs-angular-forms-entity-picker.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-forms-input-color.mjs +1 -1
- package/fesm2022/sdcorejs-angular-forms-input-color.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-input-number.mjs +7 -5
- package/fesm2022/sdcorejs-angular-forms-input-number.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-input.mjs +290 -56
- package/fesm2022/sdcorejs-angular-forms-input.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-models.mjs +155 -2
- package/fesm2022/sdcorejs-angular-forms-models.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-radio.mjs +44 -78
- package/fesm2022/sdcorejs-angular-forms-radio.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-select.mjs +6 -20
- package/fesm2022/sdcorejs-angular-forms-select.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-switch.mjs +19 -76
- package/fesm2022/sdcorejs-angular-forms-switch.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-textarea.mjs +6 -5
- package/fesm2022/sdcorejs-angular-forms-textarea.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-time-range.mjs +280 -0
- package/fesm2022/sdcorejs-angular-forms-time-range.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-forms-time.mjs +310 -0
- package/fesm2022/sdcorejs-angular-forms-time.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-forms-tree-select.mjs +210 -0
- package/fesm2022/sdcorejs-angular-forms-tree-select.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-forms.mjs +73 -4
- package/fesm2022/sdcorejs-angular-forms.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-i18n.mjs +375 -10
- package/fesm2022/sdcorejs-angular-i18n.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-interceptors.mjs +12 -17
- package/fesm2022/sdcorejs-angular-interceptors.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-modules-layout.mjs +994 -168
- package/fesm2022/sdcorejs-angular-modules-layout.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-pipes.mjs +5 -7
- package/fesm2022/sdcorejs-angular-pipes.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-api.mjs +407 -137
- package/fesm2022/sdcorejs-angular-services-api.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-cache.mjs +515 -105
- package/fesm2022/sdcorejs-angular-services-cache.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-confirm.mjs +5 -7
- package/fesm2022/sdcorejs-angular-services-confirm.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-docx.mjs +7 -10
- package/fesm2022/sdcorejs-angular-services-docx.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-loading.mjs +339 -74
- package/fesm2022/sdcorejs-angular-services-loading.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-notify.mjs +7 -10
- package/fesm2022/sdcorejs-angular-services-notify.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-persistence.mjs +899 -0
- package/fesm2022/sdcorejs-angular-services-persistence.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-services-storage.mjs +324 -122
- package/fesm2022/sdcorejs-angular-services-storage.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-task.mjs +389 -0
- package/fesm2022/sdcorejs-angular-services-task.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-services-unsaved-changes.mjs +255 -0
- package/fesm2022/sdcorejs-angular-services-unsaved-changes.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-services-viewport.mjs +76 -0
- package/fesm2022/sdcorejs-angular-services-viewport.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-services.mjs +4 -0
- package/fesm2022/sdcorejs-angular-services.mjs.map +1 -1
- package/forms/autocomplete/index.d.ts +3 -4
- package/forms/checkbox/index.d.ts +4 -8
- package/forms/date/index.d.ts +2 -2
- package/forms/date-range/index.d.ts +6 -7
- package/forms/datetime/index.d.ts +2 -2
- package/forms/entity-picker/index.d.ts +128 -0
- package/forms/index.d.ts +15 -5
- package/forms/input/index.d.ts +74 -11
- package/forms/input-number/index.d.ts +1 -1
- package/forms/models/index.d.ts +86 -3
- package/forms/radio/index.d.ts +12 -8
- package/forms/switch/index.d.ts +4 -7
- package/forms/time/index.d.ts +117 -0
- package/forms/time-range/index.d.ts +98 -0
- package/forms/tree-select/index.d.ts +76 -0
- package/i18n/index.d.ts +72 -2
- package/interceptors/index.d.ts +5 -6
- package/modules/layout/index.d.ts +354 -17
- package/package.json +49 -1
- package/pipes/index.d.ts +2 -2
- package/services/api/index.d.ts +68 -24
- package/services/cache/index.d.ts +52 -16
- package/services/confirm/index.d.ts +3 -2
- package/services/docx/index.d.ts +2 -3
- package/services/index.d.ts +4 -0
- package/services/loading/index.d.ts +16 -8
- package/services/persistence/index.d.ts +159 -0
- package/services/storage/index.d.ts +26 -8
- package/services/task/index.d.ts +104 -0
- package/services/unsaved-changes/index.d.ts +83 -0
- package/services/viewport/index.d.ts +42 -0
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, ElementRef, input, booleanAttribute, effect, Component, Injectable, InjectionToken, signal, Pipe, output,
|
|
3
|
-
import * as i1
|
|
4
|
-
import { CommonModule, NgTemplateOutlet } from '@angular/common';
|
|
5
|
-
import * as i1$
|
|
2
|
+
import { inject, ElementRef, input, booleanAttribute, effect, Component, Injectable, InjectionToken, signal, computed, Pipe, DestroyRef, viewChild, output, isSignal, ChangeDetectionStrategy, untracked, NgModule } from '@angular/core';
|
|
3
|
+
import * as i1 from '@angular/common';
|
|
4
|
+
import { DOCUMENT, CommonModule, NgTemplateOutlet } from '@angular/common';
|
|
5
|
+
import * as i1$3 from '@angular/router';
|
|
6
6
|
import { Router, NavigationEnd, RouterModule, ActivatedRoute } from '@angular/router';
|
|
7
7
|
import { __decorate } from 'tslib';
|
|
8
8
|
import { SdAvatar, SdTabComponent, SdButton } from '@sdcorejs/angular/components';
|
|
9
9
|
import { I18nService, TranslatePipe, I18N_STORAGE_KEY, I18N_MESSAGES } from '@sdcorejs/angular/i18n';
|
|
10
|
-
import
|
|
10
|
+
import { SdStorageService } from '@sdcorejs/angular/services';
|
|
11
|
+
import { StringUtilities, Utilities } from '@sdcorejs/utils/fns';
|
|
12
|
+
import { SD_VIEWPORT, SdViewportService } from '@sdcorejs/angular/services/viewport';
|
|
11
13
|
import { resolveMaybeAsync } from '@sdcorejs/utils/models';
|
|
12
|
-
import {
|
|
13
|
-
import * as i1$1 from '@
|
|
14
|
-
import * as i1$4 from '@angular/material/sidenav';
|
|
14
|
+
import { SdPermissionService } from '@sdcorejs/angular/modules/permission';
|
|
15
|
+
import * as i1$1 from '@angular/material/sidenav';
|
|
15
16
|
import { MatSidenavModule } from '@angular/material/sidenav';
|
|
16
17
|
import { NestedTreeControl } from '@angular/cdk/tree';
|
|
17
18
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
@@ -19,15 +20,14 @@ import { FormsModule } from '@angular/forms';
|
|
|
19
20
|
import { MatInputModule } from '@angular/material/input';
|
|
20
21
|
import * as i3 from '@angular/material/tooltip';
|
|
21
22
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
22
|
-
import * as i2
|
|
23
|
+
import * as i2 from '@angular/material/tree';
|
|
23
24
|
import { MatTreeNestedDataSource, MatTreeModule } from '@angular/material/tree';
|
|
24
25
|
import { SdInput, SdSuffixDefDirective } from '@sdcorejs/angular/forms';
|
|
25
26
|
import { SdSafeHtmlPipe } from '@sdcorejs/angular/pipes';
|
|
26
|
-
import * as i2 from '@angular/material/button';
|
|
27
|
-
import { MatButtonModule } from '@angular/material/button';
|
|
28
|
-
import * as i1$2 from '@angular/material/menu';
|
|
29
|
-
import { MatMenuModule } from '@angular/material/menu';
|
|
30
27
|
import { SdIcon } from '@sdcorejs/angular/modules/icon';
|
|
28
|
+
import { isObservable } from 'rxjs';
|
|
29
|
+
import * as i1$2 from '@angular/cdk/a11y';
|
|
30
|
+
import { A11yModule } from '@angular/cdk/a11y';
|
|
31
31
|
|
|
32
32
|
class SdPageComponent {
|
|
33
33
|
el = inject(ElementRef);
|
|
@@ -49,24 +49,136 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
49
49
|
args: [{ selector: 'sd-page', template: "@if (!noHeader()) {\n <div class=\"c-page-header bg-white\">\n <div class=\"p-16 d-flex align-items-center w-full justify-content-between\">\n <div class=\"d-flex\">\n <ng-content select=\"[headerLeft]\">\n <div class=\"d-flex flex-column\">\n <div class=\"T20M\">{{ title() }}</div>\n @if (description()) {\n <div class=\"T14R text-secondary mt-4\">{{ description() }}</div>\n }\n </div>\n </ng-content>\n </div>\n <ng-content select=\"[headerRight]\"></ng-content>\n </div>\n </div>\n}\n\n<div class=\"c-page-wrapper px-0\">\n <div class=\"c-page-content\">\n <ng-content></ng-content>\n </div>\n</div>\n", styles: [":host{height:100%;display:flex;flex-direction:column;background-color:#f9f9f9}.c-page-header{box-shadow:0 1px 2px #2f31361a;z-index:1;position:relative}.c-page-wrapper{flex:1;display:flex;flex-direction:row;width:100%;overflow-y:auto;max-height:100vh}.c-page-wrapper .c-page-content{flex:1;height:100%;width:100%}.c-page-wrapper .c-page-right{position:-webkit-sticky;position:sticky;top:0;height:100%;overflow-y:auto}\n"] }]
|
|
50
50
|
}], ctorParameters: () => [], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], noHeader: [{ type: i0.Input, args: [{ isSignal: true, alias: "noHeader", required: false }] }] } });
|
|
51
51
|
|
|
52
|
+
function getMenuStableKey(menu, ancestors = []) {
|
|
53
|
+
if (!menu || typeof menu !== 'object')
|
|
54
|
+
return '';
|
|
55
|
+
if (typeof menu.id === 'string' && menu.id.trim())
|
|
56
|
+
return `id:${menu.id.trim()}`;
|
|
57
|
+
if ('path' in menu && typeof menu.path === 'string' && menu.path.trim())
|
|
58
|
+
return `path:${menu.path.trim()}`;
|
|
59
|
+
const title = typeof menu.title === 'string' ? menu.title.trim() : '';
|
|
60
|
+
const segments = [...ancestors, title]
|
|
61
|
+
.map(segment => StringUtilities.changeAliasLowerCase(segment).trim().replace(/\s+/g, '-'))
|
|
62
|
+
.filter(Boolean);
|
|
63
|
+
return segments.length ? `group:${segments.join('/')}` : '';
|
|
64
|
+
}
|
|
65
|
+
function searchMenuLeaves(menus, query) {
|
|
66
|
+
const leaves = flattenMenuLeaves(menus);
|
|
67
|
+
const normalizedQuery = query?.trim();
|
|
68
|
+
if (!normalizedQuery)
|
|
69
|
+
return leaves;
|
|
70
|
+
return leaves.filter(menu => [menu.title, menu.tooltipTitle].some(value => value && StringUtilities.aliasIncludes(value, normalizedQuery)));
|
|
71
|
+
}
|
|
72
|
+
function selectPrimaryMenuGroups(menus, requestedIds, maximum = 3) {
|
|
73
|
+
const limit = Number.isFinite(maximum) && maximum > 0 ? Math.min(3, Math.floor(maximum)) : 3;
|
|
74
|
+
const byId = new Map(menus.filter(menu => typeof menu.id === 'string').map(menu => [menu.id, menu]));
|
|
75
|
+
const selected = [];
|
|
76
|
+
const selectedKeys = new Set();
|
|
77
|
+
for (const id of requestedIds ?? []) {
|
|
78
|
+
const menu = byId.get(id);
|
|
79
|
+
const key = getMenuStableKey(menu);
|
|
80
|
+
if (!menu || !key || selectedKeys.has(key))
|
|
81
|
+
continue;
|
|
82
|
+
selected.push(menu);
|
|
83
|
+
selectedKeys.add(key);
|
|
84
|
+
if (selected.length === limit)
|
|
85
|
+
return selected;
|
|
86
|
+
}
|
|
87
|
+
for (const menu of menus) {
|
|
88
|
+
const key = getMenuStableKey(menu);
|
|
89
|
+
if (!key || selectedKeys.has(key))
|
|
90
|
+
continue;
|
|
91
|
+
selected.push(menu);
|
|
92
|
+
selectedKeys.add(key);
|
|
93
|
+
if (selected.length === limit)
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
return selected;
|
|
97
|
+
}
|
|
98
|
+
function resolveMenuKeys(menus, keys) {
|
|
99
|
+
const menuByKey = new Map();
|
|
100
|
+
visitMenus(menus, [], (menu, ancestors) => {
|
|
101
|
+
const key = getMenuStableKey(menu, ancestors);
|
|
102
|
+
if (key && !menuByKey.has(key))
|
|
103
|
+
menuByKey.set(key, menu);
|
|
104
|
+
});
|
|
105
|
+
return [...new Set(keys)].map(key => menuByKey.get(key)).filter((menu) => !!menu);
|
|
106
|
+
}
|
|
107
|
+
function flattenMenuLeaves(menus) {
|
|
108
|
+
const leaves = [];
|
|
109
|
+
visitMenus(menus, [], menu => {
|
|
110
|
+
if ('path' in menu && typeof menu.path === 'string' && menu.path)
|
|
111
|
+
leaves.push(menu);
|
|
112
|
+
});
|
|
113
|
+
return leaves;
|
|
114
|
+
}
|
|
115
|
+
function visitMenus(menus, ancestors, visitor) {
|
|
116
|
+
for (const menu of menus ?? []) {
|
|
117
|
+
if (!menu || typeof menu !== 'object')
|
|
118
|
+
continue;
|
|
119
|
+
visitor(menu, ancestors);
|
|
120
|
+
if ('children' in menu && Array.isArray(menu.children)) {
|
|
121
|
+
const title = typeof menu.title === 'string' && menu.title.trim() ? menu.title.trim() : getMenuStableKey(menu, ancestors);
|
|
122
|
+
visitMenus(menu.children, title ? [...ancestors, title] : ancestors, visitor);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
52
127
|
class SdLayoutStorageService {
|
|
53
|
-
sdStorageService;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
this.
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
|
|
128
|
+
#sdStorageService = inject(SdStorageService);
|
|
129
|
+
isShowSidebar = this.#sdStorageService.create('cb07c316-ed6d-4620-9e92-53dbef6aa983');
|
|
130
|
+
menuLockStatus = this.#sdStorageService.create('2c2f4816-18b3-4ec3-8177-7b90bff036c3');
|
|
131
|
+
lastActiveMenuGroupId = this.#sdStorageService.create('2e6961d0-3380-4a94-a6a1-38837560cd96');
|
|
132
|
+
pinnedMenuGroup = this.#sdStorageService.create('e81da122-5dab-4250-b309-64197fb19d44');
|
|
133
|
+
pinnedMenuKeys = this.#sdStorageService.create('4bdc7f80-9ff4-4fe0-908f-6c9f7fe4100a');
|
|
134
|
+
recentMenuKeys = this.#sdStorageService.create('fb989dc1-f6e7-4b62-a611-861a8f214a51');
|
|
135
|
+
versionStates = this.#sdStorageService.create('ca8e4261-fbd4-4209-af88-2dbe278c1a1b');
|
|
136
|
+
readPinnedMenuKeys(menus) {
|
|
137
|
+
const storedKeys = this.pinnedMenuKeys.has() ? this.pinnedMenuKeys.get() : this.#getLegacyPinnedKeys();
|
|
138
|
+
const validKeys = this.#resolveValidKeys(menus, storedKeys ?? []);
|
|
139
|
+
if (!this.pinnedMenuKeys.has() || !this.#sameKeys(storedKeys ?? [], validKeys)) {
|
|
140
|
+
this.pinnedMenuKeys.set(validKeys);
|
|
141
|
+
}
|
|
142
|
+
return validKeys;
|
|
143
|
+
}
|
|
144
|
+
writePinnedMenuKeys(keys) {
|
|
145
|
+
this.pinnedMenuKeys.set([...new Set(keys.filter(Boolean))]);
|
|
146
|
+
}
|
|
147
|
+
readRecentMenuKeys(menus, maximum = 5) {
|
|
148
|
+
const storedKeys = this.recentMenuKeys.get() ?? [];
|
|
149
|
+
const limit = Number.isFinite(maximum) && maximum > 0 ? Math.floor(maximum) : 5;
|
|
150
|
+
const validKeys = this.#resolveValidKeys(menus, storedKeys).slice(0, limit);
|
|
151
|
+
if (!this.#sameKeys(storedKeys, validKeys))
|
|
152
|
+
this.recentMenuKeys.set(validKeys);
|
|
153
|
+
return validKeys;
|
|
154
|
+
}
|
|
155
|
+
writeRecentMenuKeys(keys) {
|
|
156
|
+
this.recentMenuKeys.set([...new Set(keys.filter(Boolean))]);
|
|
157
|
+
}
|
|
158
|
+
readVersionState(version) {
|
|
159
|
+
return this.versionStates.get()?.[String(version)] ?? {};
|
|
160
|
+
}
|
|
161
|
+
patchVersionState(version, patch) {
|
|
162
|
+
const states = this.versionStates.get() ?? {};
|
|
163
|
+
const nextState = { ...(states[String(version)] ?? {}), ...patch };
|
|
164
|
+
this.versionStates.set({ ...states, [String(version)]: nextState });
|
|
165
|
+
return nextState;
|
|
166
|
+
}
|
|
167
|
+
#getLegacyPinnedKeys() {
|
|
168
|
+
const legacyGroup = this.pinnedMenuGroup.get();
|
|
169
|
+
if (!legacyGroup || !Array.isArray(legacyGroup.children))
|
|
170
|
+
return [];
|
|
171
|
+
return legacyGroup.children.map(menu => getMenuStableKey(menu)).filter(Boolean);
|
|
172
|
+
}
|
|
173
|
+
#resolveValidKeys(menus, keys) {
|
|
174
|
+
return resolveMenuKeys(menus, keys)
|
|
175
|
+
.map(menu => getMenuStableKey(menu))
|
|
176
|
+
.filter(Boolean);
|
|
177
|
+
}
|
|
178
|
+
#sameKeys(left, right) {
|
|
179
|
+
return left.length === right.length && left.every((key, index) => key === right[index]);
|
|
180
|
+
}
|
|
181
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdLayoutStorageService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
70
182
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdLayoutStorageService, providedIn: 'root' });
|
|
71
183
|
}
|
|
72
184
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdLayoutStorageService, decorators: [{
|
|
@@ -74,10 +186,108 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
74
186
|
args: [{
|
|
75
187
|
providedIn: 'root',
|
|
76
188
|
}]
|
|
77
|
-
}]
|
|
189
|
+
}] });
|
|
78
190
|
|
|
191
|
+
const DEFAULT_LAYOUT_MOBILE_BREAKPOINT = 1024;
|
|
192
|
+
function normalizeLayoutMobileBreakpoint(value) {
|
|
193
|
+
if (!Number.isFinite(value) || Number(value) <= 0) {
|
|
194
|
+
return DEFAULT_LAYOUT_MOBILE_BREAKPOINT;
|
|
195
|
+
}
|
|
196
|
+
return Math.floor(Number(value));
|
|
197
|
+
}
|
|
198
|
+
function resolveSidebarV2Interaction(sidebar) {
|
|
199
|
+
return sidebar.interaction === 'hover-lock' ? 'hover-lock' : 'click';
|
|
200
|
+
}
|
|
201
|
+
function resolveSidebarV3Recent(sidebar) {
|
|
202
|
+
const configuredLimit = sidebar.recent?.maxItems;
|
|
203
|
+
const maxItems = Number.isFinite(configuredLimit) && Number(configuredLimit) > 0 ? Math.floor(Number(configuredLimit)) : 5;
|
|
204
|
+
return {
|
|
205
|
+
enabled: sidebar.recent?.enabled ?? true,
|
|
206
|
+
maxItems,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
function normalizeSidebarConfiguration(sidebar) {
|
|
210
|
+
if (sidebar.version === 2) {
|
|
211
|
+
return {
|
|
212
|
+
...sidebar,
|
|
213
|
+
interaction: resolveSidebarV2Interaction(sidebar),
|
|
214
|
+
primaryMenuIds: [...new Set((sidebar.primaryMenuIds ?? []).filter(id => typeof id === 'string' && id.trim()))].slice(0, 3),
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
if (sidebar.version === 3) {
|
|
218
|
+
return {
|
|
219
|
+
...sidebar,
|
|
220
|
+
defaultCollapsed: sidebar.defaultCollapsed ?? false,
|
|
221
|
+
recent: resolveSidebarV3Recent(sidebar),
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
return sidebar;
|
|
225
|
+
}
|
|
79
226
|
const SD_LAYOUT_CONFIGURATION = new InjectionToken('sd.layout.configuration');
|
|
80
227
|
|
|
228
|
+
const SD_LAYOUT_VIEWPORT = SD_VIEWPORT;
|
|
229
|
+
/** @deprecated Prefer `SdViewportService` from `@sdcorejs/angular/services/viewport`. */
|
|
230
|
+
class SdLayoutResponsiveService {
|
|
231
|
+
#viewport = inject(SdViewportService);
|
|
232
|
+
viewportWidth = this.#viewport.width;
|
|
233
|
+
isMobile(breakpoint) {
|
|
234
|
+
return this.viewportWidth() < normalizeLayoutMobileBreakpoint(breakpoint);
|
|
235
|
+
}
|
|
236
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdLayoutResponsiveService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
237
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdLayoutResponsiveService, providedIn: 'root' });
|
|
238
|
+
}
|
|
239
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdLayoutResponsiveService, decorators: [{
|
|
240
|
+
type: Injectable,
|
|
241
|
+
args: [{ providedIn: 'root' }]
|
|
242
|
+
}] });
|
|
243
|
+
|
|
244
|
+
class SdLayoutNavigationStateService {
|
|
245
|
+
#storage = inject(SdLayoutStorageService);
|
|
246
|
+
#menus = signal([], ...(ngDevMode ? [{ debugName: "#menus" }] : []));
|
|
247
|
+
pinnedKeys = signal([], ...(ngDevMode ? [{ debugName: "pinnedKeys" }] : []));
|
|
248
|
+
recentKeys = signal([], ...(ngDevMode ? [{ debugName: "recentKeys" }] : []));
|
|
249
|
+
pinnedMenus = computed(() => resolveMenuKeys(this.#menus(), this.pinnedKeys()), ...(ngDevMode ? [{ debugName: "pinnedMenus" }] : []));
|
|
250
|
+
recentMenus = computed(() => resolveMenuKeys(this.#menus(), this.recentKeys()), ...(ngDevMode ? [{ debugName: "recentMenus" }] : []));
|
|
251
|
+
hydrate(menus, recentMaximum = 5) {
|
|
252
|
+
this.#menus.set(menus);
|
|
253
|
+
this.pinnedKeys.set(this.#storage.readPinnedMenuKeys(menus));
|
|
254
|
+
this.recentKeys.set(this.#storage.readRecentMenuKeys(menus, recentMaximum));
|
|
255
|
+
}
|
|
256
|
+
togglePinned(menu) {
|
|
257
|
+
const key = getMenuStableKey(menu);
|
|
258
|
+
if (!key)
|
|
259
|
+
return;
|
|
260
|
+
const nextKeys = this.pinnedKeys().includes(key)
|
|
261
|
+
? this.pinnedKeys().filter(currentKey => currentKey !== key)
|
|
262
|
+
: [...this.pinnedKeys(), key];
|
|
263
|
+
this.pinnedKeys.set(nextKeys);
|
|
264
|
+
this.#storage.writePinnedMenuKeys(nextKeys);
|
|
265
|
+
}
|
|
266
|
+
recordRecent(menu, configuration) {
|
|
267
|
+
if (!configuration.enabled)
|
|
268
|
+
return;
|
|
269
|
+
const key = getMenuStableKey(menu);
|
|
270
|
+
if (!key)
|
|
271
|
+
return;
|
|
272
|
+
const maximum = Number.isFinite(configuration.maxItems) && configuration.maxItems > 0 ? Math.floor(configuration.maxItems) : 5;
|
|
273
|
+
const nextKeys = [key, ...this.recentKeys().filter(currentKey => currentKey !== key)].slice(0, maximum);
|
|
274
|
+
this.recentKeys.set(nextKeys);
|
|
275
|
+
this.#storage.writeRecentMenuKeys(nextKeys);
|
|
276
|
+
}
|
|
277
|
+
patchVersionState(version, patch) {
|
|
278
|
+
this.#storage.patchVersionState(version, patch);
|
|
279
|
+
}
|
|
280
|
+
versionState(version) {
|
|
281
|
+
return this.#storage.readVersionState(version);
|
|
282
|
+
}
|
|
283
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdLayoutNavigationStateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
284
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdLayoutNavigationStateService, providedIn: 'root' });
|
|
285
|
+
}
|
|
286
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdLayoutNavigationStateService, decorators: [{
|
|
287
|
+
type: Injectable,
|
|
288
|
+
args: [{ providedIn: 'root' }]
|
|
289
|
+
}] });
|
|
290
|
+
|
|
81
291
|
class SdLayoutService {
|
|
82
292
|
// ==========================================
|
|
83
293
|
// INJECT CONFIGURATIONS
|
|
@@ -154,10 +364,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
154
364
|
|
|
155
365
|
// End
|
|
156
366
|
class MenuPipe {
|
|
157
|
-
permissionService;
|
|
158
|
-
constructor(
|
|
159
|
-
this.permissionService = permissionService;
|
|
160
|
-
}
|
|
367
|
+
permissionService = inject(SdPermissionService);
|
|
368
|
+
constructor() { }
|
|
161
369
|
transform(menus) {
|
|
162
370
|
const results = [];
|
|
163
371
|
const check = (menu) => {
|
|
@@ -221,6 +429,9 @@ class MenuPipe {
|
|
|
221
429
|
return results;
|
|
222
430
|
}
|
|
223
431
|
#getHashIdMenu = (menu) => {
|
|
432
|
+
const explicitId = typeof menu.id === 'string' ? menu.id.trim() : '';
|
|
433
|
+
if (explicitId)
|
|
434
|
+
return explicitId;
|
|
224
435
|
const extendedMenu = menu;
|
|
225
436
|
const hashKeys = [
|
|
226
437
|
'title',
|
|
@@ -241,7 +452,7 @@ class MenuPipe {
|
|
|
241
452
|
}, {});
|
|
242
453
|
return Object.keys(hashData).length > 0 ? Utilities.hash(hashData) : Utilities.generateUuid();
|
|
243
454
|
};
|
|
244
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: MenuPipe, deps: [
|
|
455
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: MenuPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
245
456
|
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.25", ngImport: i0, type: MenuPipe, isStandalone: true, name: "menu" });
|
|
246
457
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: MenuPipe, providedIn: 'root' });
|
|
247
458
|
}
|
|
@@ -255,7 +466,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
255
466
|
args: [{
|
|
256
467
|
providedIn: 'root',
|
|
257
468
|
}]
|
|
258
|
-
}], ctorParameters: () => [
|
|
469
|
+
}], ctorParameters: () => [] });
|
|
259
470
|
|
|
260
471
|
// End
|
|
261
472
|
class MenuFocusPipe {
|
|
@@ -335,53 +546,181 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
335
546
|
}]
|
|
336
547
|
}] });
|
|
337
548
|
|
|
549
|
+
class SdLayoutUserMenuComponent {
|
|
550
|
+
#configuration = inject(SD_LAYOUT_CONFIGURATION, { optional: true });
|
|
551
|
+
#destroyRef = inject(DestroyRef);
|
|
552
|
+
#document = inject(DOCUMENT);
|
|
553
|
+
#host = inject(ElementRef);
|
|
554
|
+
trigger = viewChild('trigger', ...(ngDevMode ? [{ debugName: "trigger" }] : []));
|
|
555
|
+
menu = viewChild('menu', ...(ngDevMode ? [{ debugName: "menu" }] : []));
|
|
556
|
+
userInfo = input.required(...(ngDevMode ? [{ debugName: "userInfo" }] : []));
|
|
557
|
+
signout = input(...(ngDevMode ? [undefined, { debugName: "signout" }] : []));
|
|
558
|
+
changePassword = input(...(ngDevMode ? [undefined, { debugName: "changePassword" }] : []));
|
|
559
|
+
updateProfile = input(...(ngDevMode ? [undefined, { debugName: "updateProfile" }] : []));
|
|
560
|
+
setting = input(...(ngDevMode ? [undefined, { debugName: "setting" }] : []));
|
|
561
|
+
notification = input(...(ngDevMode ? [undefined, { debugName: "notification" }] : []));
|
|
562
|
+
compact = input(false, ...(ngDevMode ? [{ debugName: "compact" }] : []));
|
|
563
|
+
presentation = input('disclosure', ...(ngDevMode ? [{ debugName: "presentation" }] : []));
|
|
564
|
+
opened = output();
|
|
565
|
+
closed = output();
|
|
566
|
+
isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
|
|
567
|
+
#observableNotificationCount = signal(undefined, ...(ngDevMode ? [{ debugName: "#observableNotificationCount" }] : []));
|
|
568
|
+
avatar = computed(() => {
|
|
569
|
+
const user = this.userInfo();
|
|
570
|
+
return user.avatar || user.fullName || user.username || user.email;
|
|
571
|
+
}, ...(ngDevMode ? [{ debugName: "avatar" }] : []));
|
|
572
|
+
displayName = computed(() => this.userInfo().fullName || this.userInfo().username || this.userInfo().email || 'User', ...(ngDevMode ? [{ debugName: "displayName" }] : []));
|
|
573
|
+
role = computed(() => {
|
|
574
|
+
const role = this.userInfo().role;
|
|
575
|
+
const text = role?.text?.trim();
|
|
576
|
+
return text ? { ...role, text } : undefined;
|
|
577
|
+
}, ...(ngDevMode ? [{ debugName: "role" }] : []));
|
|
578
|
+
signoutAction = computed(() => this.signout() ?? this.#configuration?.signout, ...(ngDevMode ? [{ debugName: "signoutAction" }] : []));
|
|
579
|
+
changePasswordAction = computed(() => this.changePassword() ?? this.#configuration?.changePassword, ...(ngDevMode ? [{ debugName: "changePasswordAction" }] : []));
|
|
580
|
+
updateProfileAction = computed(() => this.updateProfile() ?? this.#configuration?.updateProfile, ...(ngDevMode ? [{ debugName: "updateProfileAction" }] : []));
|
|
581
|
+
settingAction = computed(() => this.setting() ?? this.#configuration?.setting, ...(ngDevMode ? [{ debugName: "settingAction" }] : []));
|
|
582
|
+
notificationConfiguration = computed(() => this.notification() ?? this.#configuration?.notification, ...(ngDevMode ? [{ debugName: "notificationConfiguration" }] : []));
|
|
583
|
+
notificationAction = computed(() => this.notificationConfiguration()?.action, ...(ngDevMode ? [{ debugName: "notificationAction" }] : []));
|
|
584
|
+
notificationCount = computed(() => {
|
|
585
|
+
const source = this.notificationConfiguration()?.count;
|
|
586
|
+
const value = isSignal(source) ? source() : isObservable(source) ? this.#observableNotificationCount() : source;
|
|
587
|
+
return this.#normalizeNotificationCount(value);
|
|
588
|
+
}, ...(ngDevMode ? [{ debugName: "notificationCount" }] : []));
|
|
589
|
+
notificationBadge = computed(() => {
|
|
590
|
+
const count = this.notificationCount();
|
|
591
|
+
return count > 99 ? '99+' : count > 0 ? String(count) : undefined;
|
|
592
|
+
}, ...(ngDevMode ? [{ debugName: "notificationBadge" }] : []));
|
|
593
|
+
constructor() {
|
|
594
|
+
// Observable sources may change with component inputs; effect cleanup guarantees one active subscription.
|
|
595
|
+
effect(onCleanup => {
|
|
596
|
+
const source = this.notificationConfiguration()?.count;
|
|
597
|
+
this.#observableNotificationCount.set(undefined);
|
|
598
|
+
if (!isObservable(source))
|
|
599
|
+
return;
|
|
600
|
+
const subscription = source.subscribe(value => this.#observableNotificationCount.set(value));
|
|
601
|
+
onCleanup(() => subscription.unsubscribe());
|
|
602
|
+
});
|
|
603
|
+
effect(() => {
|
|
604
|
+
if (!this.isOpen())
|
|
605
|
+
return;
|
|
606
|
+
this.menu()?.nativeElement.querySelector('[role="menuitem"]')?.focus();
|
|
607
|
+
});
|
|
608
|
+
const onDocumentKeydown = (event) => {
|
|
609
|
+
if (!this.isOpen() || event.key !== 'Escape')
|
|
610
|
+
return;
|
|
611
|
+
event.preventDefault();
|
|
612
|
+
this.close();
|
|
613
|
+
};
|
|
614
|
+
const onDocumentPointerDown = (event) => {
|
|
615
|
+
if (!this.isOpen())
|
|
616
|
+
return;
|
|
617
|
+
const nodeConstructor = this.#document.defaultView?.Node;
|
|
618
|
+
if (!nodeConstructor || !(event.target instanceof nodeConstructor) || this.#host.nativeElement.contains(event.target))
|
|
619
|
+
return;
|
|
620
|
+
this.close(false);
|
|
621
|
+
};
|
|
622
|
+
this.#document.addEventListener('keydown', onDocumentKeydown);
|
|
623
|
+
this.#document.addEventListener('pointerdown', onDocumentPointerDown);
|
|
624
|
+
this.#destroyRef.onDestroy(() => {
|
|
625
|
+
this.#document.removeEventListener('keydown', onDocumentKeydown);
|
|
626
|
+
this.#document.removeEventListener('pointerdown', onDocumentPointerDown);
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
open() {
|
|
630
|
+
if (this.isOpen())
|
|
631
|
+
return;
|
|
632
|
+
this.isOpen.set(true);
|
|
633
|
+
this.opened.emit();
|
|
634
|
+
}
|
|
635
|
+
toggle() {
|
|
636
|
+
if (this.isOpen())
|
|
637
|
+
this.close(false);
|
|
638
|
+
else
|
|
639
|
+
this.open();
|
|
640
|
+
}
|
|
641
|
+
close(restoreFocus = true) {
|
|
642
|
+
const wasOpen = this.isOpen();
|
|
643
|
+
this.isOpen.set(false);
|
|
644
|
+
if (wasOpen)
|
|
645
|
+
this.closed.emit();
|
|
646
|
+
if (restoreFocus)
|
|
647
|
+
this.trigger()?.nativeElement.focus();
|
|
648
|
+
}
|
|
649
|
+
onMenuKeydown(event) {
|
|
650
|
+
if (event.key === 'Escape') {
|
|
651
|
+
event.preventDefault();
|
|
652
|
+
this.close();
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
if (!['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(event.key) || !(event.currentTarget instanceof HTMLElement))
|
|
656
|
+
return;
|
|
657
|
+
const actions = [...event.currentTarget.querySelectorAll('[role="menuitem"]')];
|
|
658
|
+
if (!actions.length)
|
|
659
|
+
return;
|
|
660
|
+
event.preventDefault();
|
|
661
|
+
const currentIndex = actions.findIndex(action => action === this.#document.activeElement);
|
|
662
|
+
const nextIndex = event.key === 'Home'
|
|
663
|
+
? 0
|
|
664
|
+
: event.key === 'End'
|
|
665
|
+
? actions.length - 1
|
|
666
|
+
: event.key === 'ArrowDown'
|
|
667
|
+
? (currentIndex + 1) % actions.length
|
|
668
|
+
: (currentIndex - 1 + actions.length) % actions.length;
|
|
669
|
+
actions[nextIndex].focus();
|
|
670
|
+
}
|
|
671
|
+
runChangePassword() {
|
|
672
|
+
this.#runAction(this.changePasswordAction());
|
|
673
|
+
}
|
|
674
|
+
runSignout() {
|
|
675
|
+
this.#runAction(this.signoutAction());
|
|
676
|
+
}
|
|
677
|
+
runUpdateProfile() {
|
|
678
|
+
this.#runAction(this.updateProfileAction());
|
|
679
|
+
}
|
|
680
|
+
runSetting() {
|
|
681
|
+
this.#runAction(this.settingAction());
|
|
682
|
+
}
|
|
683
|
+
runNotification() {
|
|
684
|
+
this.#runAction(this.notificationAction());
|
|
685
|
+
}
|
|
686
|
+
#runAction(action) {
|
|
687
|
+
action?.();
|
|
688
|
+
this.close();
|
|
689
|
+
}
|
|
690
|
+
#normalizeNotificationCount(value) {
|
|
691
|
+
return Number.isFinite(value) && Number(value) > 0 ? Math.floor(Number(value)) : 0;
|
|
692
|
+
}
|
|
693
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdLayoutUserMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
694
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SdLayoutUserMenuComponent, isStandalone: true, selector: "sd-layout-user-menu", inputs: { userInfo: { classPropertyName: "userInfo", publicName: "userInfo", isSignal: true, isRequired: true, transformFunction: null }, signout: { classPropertyName: "signout", publicName: "signout", isSignal: true, isRequired: false, transformFunction: null }, changePassword: { classPropertyName: "changePassword", publicName: "changePassword", isSignal: true, isRequired: false, transformFunction: null }, updateProfile: { classPropertyName: "updateProfile", publicName: "updateProfile", isSignal: true, isRequired: false, transformFunction: null }, setting: { classPropertyName: "setting", publicName: "setting", isSignal: true, isRequired: false, transformFunction: null }, notification: { classPropertyName: "notification", publicName: "notification", isSignal: true, isRequired: false, transformFunction: null }, compact: { classPropertyName: "compact", publicName: "compact", isSignal: true, isRequired: false, transformFunction: null }, presentation: { classPropertyName: "presentation", publicName: "presentation", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { opened: "opened", closed: "closed" }, viewQueries: [{ propertyName: "trigger", first: true, predicate: ["trigger"], descendants: true, isSignal: true }, { propertyName: "menu", first: true, predicate: ["menu"], descendants: true, isSignal: true }], ngImport: i0, template: "@let _presentation = presentation();\n@let _role = role();\n@let _notificationBadge = notificationBadge();\n<div class=\"sd-layout-user-menu position-relative\">\n @if (_presentation !== 'disclosure') {\n <div class=\"sd-layout-user-menu__mobile d-flex flex-column gap-8\">\n <div\n data-mobile-account-row\n class=\"sd-layout-user-menu__mobile-row sd-layout-user-menu__mobile--inline d-flex align-items-center gap-8\">\n <div data-user-summary class=\"sd-layout-user-menu__summary d-flex align-items-center gap-8 flex-1\">\n <sd-avatar [src]=\"avatar()\" [size]=\"40\"></sd-avatar>\n <span class=\"d-flex flex-column flex-1 overflow-hidden\">\n <span class=\"T14M text-ellipsis\">{{ displayName() }}</span>\n @if (userInfo().email) {\n <span class=\"T12R text-black400 text-ellipsis\">{{ userInfo().email }}</span>\n }\n @if (_role) {\n <span\n data-user-role\n class=\"sd-layout-user-menu__role d-flex align-items-center gap-4 T12R text-ellipsis\"\n [style.color]=\"_role.color\">\n @if (_role.icon) {\n <sd-icon [name]=\"_role.icon\"></sd-icon>\n }\n {{ _role.text }}\n </span>\n }\n </span>\n </div>\n @if (signoutAction()) {\n <button\n type=\"button\"\n data-user-action=\"signout\"\n class=\"sd-layout-user-menu__action sd-layout-user-menu__signout d-flex align-items-center gap-8 px-8 py-8 T14R text-left text-error\"\n (click)=\"runSignout()\">\n <sd-icon name=\"logout\"></sd-icon>\n {{ 'core.module.layout.user.logout' | translate }}\n </button>\n }\n </div>\n\n @if (updateProfileAction() || settingAction() || notificationAction() || changePasswordAction()) {\n <div data-mobile-account-actions class=\"sd-layout-user-menu__mobile-actions d-flex flex-column gap-4\">\n @if (updateProfileAction()) {\n <button\n type=\"button\"\n data-user-action=\"update-profile\"\n class=\"sd-layout-user-menu__action d-flex align-items-center gap-8 px-8 py-8 T14R text-left\"\n (click)=\"runUpdateProfile()\">\n <sd-icon name=\"edit\"></sd-icon>\n {{ 'core.module.layout.user.update-profile' | translate }}\n </button>\n }\n @if (settingAction()) {\n <button\n type=\"button\"\n data-user-action=\"setting\"\n class=\"sd-layout-user-menu__action d-flex align-items-center gap-8 px-8 py-8 T14R text-left\"\n (click)=\"runSetting()\">\n <sd-icon name=\"settings\"></sd-icon>\n {{ 'core.module.layout.user.setting' | translate }}\n </button>\n }\n @if (notificationAction()) {\n <button\n type=\"button\"\n data-user-action=\"notification\"\n class=\"sd-layout-user-menu__action d-flex align-items-center gap-8 px-8 py-8 T14R text-left\"\n (click)=\"runNotification()\">\n <sd-icon name=\"notifications\"></sd-icon>\n <span class=\"flex-1\">{{ 'core.module.layout.user.notification' | translate }}</span>\n @if (_notificationBadge) {\n <span data-notification-badge class=\"sd-layout-user-menu__badge\">{{ _notificationBadge }}</span>\n }\n </button>\n }\n @if (changePasswordAction()) {\n <button\n type=\"button\"\n data-user-action=\"change-password\"\n class=\"sd-layout-user-menu__action d-flex align-items-center gap-8 px-8 py-8 T14R text-left\"\n (click)=\"runChangePassword()\">\n <sd-icon name=\"lock\"></sd-icon>\n {{ 'core.module.layout.user.change-password' | translate }}\n </button>\n }\n </div>\n }\n </div>\n } @else {\n <button\n #trigger\n type=\"button\"\n data-user-trigger\n class=\"sd-layout-user-menu__trigger d-flex align-items-center text-left\"\n [class.sd-layout-user-menu__trigger--compact]=\"compact()\"\n [class.w-full]=\"!compact()\"\n [class.gap-8]=\"!compact()\"\n [class.w-56]=\"compact()\"\n [class.gap-0]=\"compact()\"\n aria-haspopup=\"menu\"\n [attr.aria-label]=\"compact() ? displayName() : null\"\n [attr.aria-expanded]=\"isOpen()\"\n (click)=\"toggle()\">\n <sd-avatar [src]=\"avatar()\" [size]=\"compact() ? 32 : 40\"></sd-avatar>\n @if (!compact()) {\n <span class=\"d-flex flex-column flex-1 overflow-hidden\">\n <span class=\"T14M text-ellipsis\">{{ displayName() }}</span>\n @if (userInfo().email) {\n <span class=\"T12R text-black400 text-ellipsis\">{{ userInfo().email }}</span>\n }\n </span>\n <sd-icon [name]=\"isOpen() ? 'expand_less' : 'expand_more'\"></sd-icon>\n }\n </button>\n\n @if (isOpen()) {\n <div\n #menu\n data-user-menu\n class=\"sd-layout-user-menu__popup d-flex flex-column gap-4 p-8 bg-white border border-black200 rounded-8 mat-elevation-z4\"\n role=\"menu\"\n tabindex=\"-1\"\n (keydown)=\"onMenuKeydown($event)\">\n <div data-user-identity class=\"sd-layout-user-menu__identity d-flex align-items-center gap-8 px-8 py-4\">\n <sd-avatar [src]=\"avatar()\" [size]=\"40\"></sd-avatar>\n <span class=\"d-flex flex-column flex-1 overflow-hidden\">\n <span class=\"T14M text-ellipsis\">{{ displayName() }}</span>\n @if (userInfo().email) {\n <span class=\"T12R text-black400 text-ellipsis\">{{ userInfo().email }}</span>\n }\n @if (_role) {\n <span\n data-user-role\n class=\"sd-layout-user-menu__role d-flex align-items-center gap-4 T12R text-ellipsis\"\n [style.color]=\"_role.color\">\n @if (_role.icon) {\n <sd-icon [name]=\"_role.icon\"></sd-icon>\n }\n {{ _role.text }}\n </span>\n }\n </span>\n </div>\n @if (updateProfileAction()) {\n <button\n type=\"button\"\n role=\"menuitem\"\n data-user-action=\"update-profile\"\n class=\"sd-layout-user-menu__action d-flex align-items-center gap-8 px-8 py-8 T14R text-left\"\n (click)=\"runUpdateProfile()\">\n <sd-icon name=\"edit\"></sd-icon>\n {{ 'core.module.layout.user.update-profile' | translate }}\n </button>\n }\n @if (settingAction()) {\n <button\n type=\"button\"\n role=\"menuitem\"\n data-user-action=\"setting\"\n class=\"sd-layout-user-menu__action d-flex align-items-center gap-8 px-8 py-8 T14R text-left\"\n (click)=\"runSetting()\">\n <sd-icon name=\"settings\"></sd-icon>\n {{ 'core.module.layout.user.setting' | translate }}\n </button>\n }\n @if (notificationAction()) {\n <button\n type=\"button\"\n role=\"menuitem\"\n data-user-action=\"notification\"\n class=\"sd-layout-user-menu__action d-flex align-items-center gap-8 px-8 py-8 T14R text-left\"\n (click)=\"runNotification()\">\n <sd-icon name=\"notifications\"></sd-icon>\n <span class=\"flex-1\">{{ 'core.module.layout.user.notification' | translate }}</span>\n @if (_notificationBadge) {\n <span data-notification-badge class=\"sd-layout-user-menu__badge\">{{ _notificationBadge }}</span>\n }\n </button>\n }\n @if (changePasswordAction()) {\n <button\n type=\"button\"\n role=\"menuitem\"\n data-user-action=\"change-password\"\n class=\"sd-layout-user-menu__action d-flex align-items-center gap-8 px-8 py-8 T14R text-left\"\n (click)=\"runChangePassword()\">\n <sd-icon name=\"lock\"></sd-icon>\n {{ 'core.module.layout.user.change-password' | translate }}\n </button>\n }\n @if (signoutAction()) {\n <button\n type=\"button\"\n role=\"menuitem\"\n data-user-action=\"signout\"\n class=\"sd-layout-user-menu__action d-flex align-items-center gap-8 px-8 py-8 T14R text-left text-error\"\n (click)=\"runSignout()\">\n <sd-icon name=\"logout\"></sd-icon>\n {{ 'core.module.layout.user.logout' | translate }}\n </button>\n }\n </div>\n }\n }\n</div>\n", styles: [".sd-layout-user-menu__trigger,.sd-layout-user-menu__action{border:0;background:transparent;color:var(--sd-black500, var(--sd-text, #1a1b1f));cursor:pointer;border-radius:8px;transition:background-color .12s ease,color .12s ease}.sd-layout-user-menu__trigger:hover,.sd-layout-user-menu__action:hover{background:var(--sd-black100, var(--sd-surface-muted, #f3f3f3))}.sd-layout-user-menu__trigger:focus-visible,.sd-layout-user-menu__action:focus-visible{outline:2px solid var(--sd-primary);outline-offset:2px}.sd-layout-user-menu__trigger--compact{width:56px;min-height:52px;justify-content:center;gap:0;padding:0}.sd-layout-user-menu__popup{position:absolute;z-index:40;left:0;bottom:calc(100% + 8px);width:272px}.sd-layout-user-menu__popup .sd-layout-user-menu__action,.sd-layout-user-menu__mobile-actions .sd-layout-user-menu__action{width:100%}.sd-layout-user-menu__summary{min-width:0;padding:0 8px}.sd-layout-user-menu__identity{min-width:0}.sd-layout-user-menu__signout{width:auto;flex:0 0 auto;min-height:40px;white-space:nowrap}.sd-layout-user-menu__role sd-icon{width:14px;height:14px;font-size:14px;flex:0 0 auto}.sd-layout-user-menu__badge{min-width:20px;padding:1px 6px;border-radius:999px;background:var(--sd-error, #ba1a1a);color:var(--sd-white, #ffffff);font-size:11px;line-height:18px;text-align:center}.sd-layout-user-menu__signout:hover{background:var(--sd-error-light, #ffdad6);color:var(--sd-error-dark, var(--sd-error, #ba1a1a))}@media(prefers-reduced-motion:reduce){.sd-layout-user-menu__trigger,.sd-layout-user-menu__action{transition:none}}\n"], dependencies: [{ kind: "component", type: SdAvatar, selector: "sd-avatar", inputs: ["src", "size"] }, { kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
695
|
+
}
|
|
696
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdLayoutUserMenuComponent, decorators: [{
|
|
697
|
+
type: Component,
|
|
698
|
+
args: [{ selector: 'sd-layout-user-menu', standalone: true, imports: [SdAvatar, SdIcon, TranslatePipe], changeDetection: ChangeDetectionStrategy.OnPush, template: "@let _presentation = presentation();\n@let _role = role();\n@let _notificationBadge = notificationBadge();\n<div class=\"sd-layout-user-menu position-relative\">\n @if (_presentation !== 'disclosure') {\n <div class=\"sd-layout-user-menu__mobile d-flex flex-column gap-8\">\n <div\n data-mobile-account-row\n class=\"sd-layout-user-menu__mobile-row sd-layout-user-menu__mobile--inline d-flex align-items-center gap-8\">\n <div data-user-summary class=\"sd-layout-user-menu__summary d-flex align-items-center gap-8 flex-1\">\n <sd-avatar [src]=\"avatar()\" [size]=\"40\"></sd-avatar>\n <span class=\"d-flex flex-column flex-1 overflow-hidden\">\n <span class=\"T14M text-ellipsis\">{{ displayName() }}</span>\n @if (userInfo().email) {\n <span class=\"T12R text-black400 text-ellipsis\">{{ userInfo().email }}</span>\n }\n @if (_role) {\n <span\n data-user-role\n class=\"sd-layout-user-menu__role d-flex align-items-center gap-4 T12R text-ellipsis\"\n [style.color]=\"_role.color\">\n @if (_role.icon) {\n <sd-icon [name]=\"_role.icon\"></sd-icon>\n }\n {{ _role.text }}\n </span>\n }\n </span>\n </div>\n @if (signoutAction()) {\n <button\n type=\"button\"\n data-user-action=\"signout\"\n class=\"sd-layout-user-menu__action sd-layout-user-menu__signout d-flex align-items-center gap-8 px-8 py-8 T14R text-left text-error\"\n (click)=\"runSignout()\">\n <sd-icon name=\"logout\"></sd-icon>\n {{ 'core.module.layout.user.logout' | translate }}\n </button>\n }\n </div>\n\n @if (updateProfileAction() || settingAction() || notificationAction() || changePasswordAction()) {\n <div data-mobile-account-actions class=\"sd-layout-user-menu__mobile-actions d-flex flex-column gap-4\">\n @if (updateProfileAction()) {\n <button\n type=\"button\"\n data-user-action=\"update-profile\"\n class=\"sd-layout-user-menu__action d-flex align-items-center gap-8 px-8 py-8 T14R text-left\"\n (click)=\"runUpdateProfile()\">\n <sd-icon name=\"edit\"></sd-icon>\n {{ 'core.module.layout.user.update-profile' | translate }}\n </button>\n }\n @if (settingAction()) {\n <button\n type=\"button\"\n data-user-action=\"setting\"\n class=\"sd-layout-user-menu__action d-flex align-items-center gap-8 px-8 py-8 T14R text-left\"\n (click)=\"runSetting()\">\n <sd-icon name=\"settings\"></sd-icon>\n {{ 'core.module.layout.user.setting' | translate }}\n </button>\n }\n @if (notificationAction()) {\n <button\n type=\"button\"\n data-user-action=\"notification\"\n class=\"sd-layout-user-menu__action d-flex align-items-center gap-8 px-8 py-8 T14R text-left\"\n (click)=\"runNotification()\">\n <sd-icon name=\"notifications\"></sd-icon>\n <span class=\"flex-1\">{{ 'core.module.layout.user.notification' | translate }}</span>\n @if (_notificationBadge) {\n <span data-notification-badge class=\"sd-layout-user-menu__badge\">{{ _notificationBadge }}</span>\n }\n </button>\n }\n @if (changePasswordAction()) {\n <button\n type=\"button\"\n data-user-action=\"change-password\"\n class=\"sd-layout-user-menu__action d-flex align-items-center gap-8 px-8 py-8 T14R text-left\"\n (click)=\"runChangePassword()\">\n <sd-icon name=\"lock\"></sd-icon>\n {{ 'core.module.layout.user.change-password' | translate }}\n </button>\n }\n </div>\n }\n </div>\n } @else {\n <button\n #trigger\n type=\"button\"\n data-user-trigger\n class=\"sd-layout-user-menu__trigger d-flex align-items-center text-left\"\n [class.sd-layout-user-menu__trigger--compact]=\"compact()\"\n [class.w-full]=\"!compact()\"\n [class.gap-8]=\"!compact()\"\n [class.w-56]=\"compact()\"\n [class.gap-0]=\"compact()\"\n aria-haspopup=\"menu\"\n [attr.aria-label]=\"compact() ? displayName() : null\"\n [attr.aria-expanded]=\"isOpen()\"\n (click)=\"toggle()\">\n <sd-avatar [src]=\"avatar()\" [size]=\"compact() ? 32 : 40\"></sd-avatar>\n @if (!compact()) {\n <span class=\"d-flex flex-column flex-1 overflow-hidden\">\n <span class=\"T14M text-ellipsis\">{{ displayName() }}</span>\n @if (userInfo().email) {\n <span class=\"T12R text-black400 text-ellipsis\">{{ userInfo().email }}</span>\n }\n </span>\n <sd-icon [name]=\"isOpen() ? 'expand_less' : 'expand_more'\"></sd-icon>\n }\n </button>\n\n @if (isOpen()) {\n <div\n #menu\n data-user-menu\n class=\"sd-layout-user-menu__popup d-flex flex-column gap-4 p-8 bg-white border border-black200 rounded-8 mat-elevation-z4\"\n role=\"menu\"\n tabindex=\"-1\"\n (keydown)=\"onMenuKeydown($event)\">\n <div data-user-identity class=\"sd-layout-user-menu__identity d-flex align-items-center gap-8 px-8 py-4\">\n <sd-avatar [src]=\"avatar()\" [size]=\"40\"></sd-avatar>\n <span class=\"d-flex flex-column flex-1 overflow-hidden\">\n <span class=\"T14M text-ellipsis\">{{ displayName() }}</span>\n @if (userInfo().email) {\n <span class=\"T12R text-black400 text-ellipsis\">{{ userInfo().email }}</span>\n }\n @if (_role) {\n <span\n data-user-role\n class=\"sd-layout-user-menu__role d-flex align-items-center gap-4 T12R text-ellipsis\"\n [style.color]=\"_role.color\">\n @if (_role.icon) {\n <sd-icon [name]=\"_role.icon\"></sd-icon>\n }\n {{ _role.text }}\n </span>\n }\n </span>\n </div>\n @if (updateProfileAction()) {\n <button\n type=\"button\"\n role=\"menuitem\"\n data-user-action=\"update-profile\"\n class=\"sd-layout-user-menu__action d-flex align-items-center gap-8 px-8 py-8 T14R text-left\"\n (click)=\"runUpdateProfile()\">\n <sd-icon name=\"edit\"></sd-icon>\n {{ 'core.module.layout.user.update-profile' | translate }}\n </button>\n }\n @if (settingAction()) {\n <button\n type=\"button\"\n role=\"menuitem\"\n data-user-action=\"setting\"\n class=\"sd-layout-user-menu__action d-flex align-items-center gap-8 px-8 py-8 T14R text-left\"\n (click)=\"runSetting()\">\n <sd-icon name=\"settings\"></sd-icon>\n {{ 'core.module.layout.user.setting' | translate }}\n </button>\n }\n @if (notificationAction()) {\n <button\n type=\"button\"\n role=\"menuitem\"\n data-user-action=\"notification\"\n class=\"sd-layout-user-menu__action d-flex align-items-center gap-8 px-8 py-8 T14R text-left\"\n (click)=\"runNotification()\">\n <sd-icon name=\"notifications\"></sd-icon>\n <span class=\"flex-1\">{{ 'core.module.layout.user.notification' | translate }}</span>\n @if (_notificationBadge) {\n <span data-notification-badge class=\"sd-layout-user-menu__badge\">{{ _notificationBadge }}</span>\n }\n </button>\n }\n @if (changePasswordAction()) {\n <button\n type=\"button\"\n role=\"menuitem\"\n data-user-action=\"change-password\"\n class=\"sd-layout-user-menu__action d-flex align-items-center gap-8 px-8 py-8 T14R text-left\"\n (click)=\"runChangePassword()\">\n <sd-icon name=\"lock\"></sd-icon>\n {{ 'core.module.layout.user.change-password' | translate }}\n </button>\n }\n @if (signoutAction()) {\n <button\n type=\"button\"\n role=\"menuitem\"\n data-user-action=\"signout\"\n class=\"sd-layout-user-menu__action d-flex align-items-center gap-8 px-8 py-8 T14R text-left text-error\"\n (click)=\"runSignout()\">\n <sd-icon name=\"logout\"></sd-icon>\n {{ 'core.module.layout.user.logout' | translate }}\n </button>\n }\n </div>\n }\n }\n</div>\n", styles: [".sd-layout-user-menu__trigger,.sd-layout-user-menu__action{border:0;background:transparent;color:var(--sd-black500, var(--sd-text, #1a1b1f));cursor:pointer;border-radius:8px;transition:background-color .12s ease,color .12s ease}.sd-layout-user-menu__trigger:hover,.sd-layout-user-menu__action:hover{background:var(--sd-black100, var(--sd-surface-muted, #f3f3f3))}.sd-layout-user-menu__trigger:focus-visible,.sd-layout-user-menu__action:focus-visible{outline:2px solid var(--sd-primary);outline-offset:2px}.sd-layout-user-menu__trigger--compact{width:56px;min-height:52px;justify-content:center;gap:0;padding:0}.sd-layout-user-menu__popup{position:absolute;z-index:40;left:0;bottom:calc(100% + 8px);width:272px}.sd-layout-user-menu__popup .sd-layout-user-menu__action,.sd-layout-user-menu__mobile-actions .sd-layout-user-menu__action{width:100%}.sd-layout-user-menu__summary{min-width:0;padding:0 8px}.sd-layout-user-menu__identity{min-width:0}.sd-layout-user-menu__signout{width:auto;flex:0 0 auto;min-height:40px;white-space:nowrap}.sd-layout-user-menu__role sd-icon{width:14px;height:14px;font-size:14px;flex:0 0 auto}.sd-layout-user-menu__badge{min-width:20px;padding:1px 6px;border-radius:999px;background:var(--sd-error, #ba1a1a);color:var(--sd-white, #ffffff);font-size:11px;line-height:18px;text-align:center}.sd-layout-user-menu__signout:hover{background:var(--sd-error-light, #ffdad6);color:var(--sd-error-dark, var(--sd-error, #ba1a1a))}@media(prefers-reduced-motion:reduce){.sd-layout-user-menu__trigger,.sd-layout-user-menu__action{transition:none}}\n"] }]
|
|
699
|
+
}], ctorParameters: () => [], propDecorators: { trigger: [{ type: i0.ViewChild, args: ['trigger', { isSignal: true }] }], menu: [{ type: i0.ViewChild, args: ['menu', { isSignal: true }] }], userInfo: [{ type: i0.Input, args: [{ isSignal: true, alias: "userInfo", required: true }] }], signout: [{ type: i0.Input, args: [{ isSignal: true, alias: "signout", required: false }] }], changePassword: [{ type: i0.Input, args: [{ isSignal: true, alias: "changePassword", required: false }] }], updateProfile: [{ type: i0.Input, args: [{ isSignal: true, alias: "updateProfile", required: false }] }], setting: [{ type: i0.Input, args: [{ isSignal: true, alias: "setting", required: false }] }], notification: [{ type: i0.Input, args: [{ isSignal: true, alias: "notification", required: false }] }], compact: [{ type: i0.Input, args: [{ isSignal: true, alias: "compact", required: false }] }], presentation: [{ type: i0.Input, args: [{ isSignal: true, alias: "presentation", required: false }] }], opened: [{ type: i0.Output, args: ["opened"] }], closed: [{ type: i0.Output, args: ["closed"] }] } });
|
|
700
|
+
|
|
338
701
|
let LayoutUserComponent$1 = class LayoutUserComponent {
|
|
339
|
-
// ==========================================
|
|
340
|
-
// INJECT SERVICES
|
|
341
|
-
// ==========================================
|
|
342
|
-
#layoutConfiguration = inject(SD_LAYOUT_CONFIGURATION);
|
|
343
|
-
// ==========================================
|
|
344
|
-
// SIGNAL INPUTS & OUTPUTS
|
|
345
|
-
// ==========================================
|
|
346
702
|
isMobileOrTablet = input(false, ...(ngDevMode ? [{ debugName: "isMobileOrTablet" }] : []));
|
|
347
703
|
isMenuLock = input(false, ...(ngDevMode ? [{ debugName: "isMenuLock" }] : []));
|
|
348
704
|
isShowSidebar = input(false, ...(ngDevMode ? [{ debugName: "isShowSidebar" }] : []));
|
|
349
705
|
userInfo = input.required(...(ngDevMode ? [{ debugName: "userInfo" }] : []));
|
|
350
|
-
// Chuyển thành void vì các hàm emit hiện tại không truyền data
|
|
351
706
|
menuClosed = output();
|
|
352
707
|
menuOpened = output();
|
|
353
708
|
toggleMenuLock = output();
|
|
354
|
-
|
|
355
|
-
singoutLayoutConfig = this.#layoutConfiguration.signout;
|
|
356
|
-
changePasswordLayoutConfig = this.#layoutConfiguration?.changePassword;
|
|
357
|
-
// ==========================================
|
|
358
|
-
// EVENT HANDLERS
|
|
359
|
-
// ==========================================
|
|
360
|
-
onMenuOpened = () => {
|
|
709
|
+
onMenuOpened() {
|
|
361
710
|
this.menuOpened.emit();
|
|
362
|
-
}
|
|
363
|
-
onMenuClosed
|
|
711
|
+
}
|
|
712
|
+
onMenuClosed() {
|
|
364
713
|
this.menuClosed.emit();
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
event.stopPropagation();
|
|
368
|
-
};
|
|
369
|
-
logout = () => {
|
|
370
|
-
this.singoutLayoutConfig();
|
|
371
|
-
};
|
|
372
|
-
changePassword = () => {
|
|
373
|
-
// Dùng Optional Chaining gọi hàm cực gọn
|
|
374
|
-
this.changePasswordLayoutConfig?.();
|
|
375
|
-
};
|
|
376
|
-
onToggleMenuLock = (event) => {
|
|
714
|
+
}
|
|
715
|
+
onToggleMenuLock(event) {
|
|
377
716
|
this.toggleMenuLock.emit(event);
|
|
378
|
-
}
|
|
717
|
+
}
|
|
379
718
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: LayoutUserComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
380
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: LayoutUserComponent, isStandalone: true, selector: "lib-layout-user", inputs: { isMobileOrTablet: { classPropertyName: "isMobileOrTablet", publicName: "isMobileOrTablet", isSignal: true, isRequired: false, transformFunction: null }, isMenuLock: { classPropertyName: "isMenuLock", publicName: "isMenuLock", isSignal: true, isRequired: false, transformFunction: null }, isShowSidebar: { classPropertyName: "isShowSidebar", publicName: "isShowSidebar", isSignal: true, isRequired: false, transformFunction: null }, userInfo: { classPropertyName: "userInfo", publicName: "userInfo", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { menuClosed: "menuClosed", menuOpened: "menuOpened", toggleMenuLock: "toggleMenuLock" }, ngImport: i0, template: "
|
|
719
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: LayoutUserComponent, isStandalone: true, selector: "lib-layout-user", inputs: { isMobileOrTablet: { classPropertyName: "isMobileOrTablet", publicName: "isMobileOrTablet", isSignal: true, isRequired: false, transformFunction: null }, isMenuLock: { classPropertyName: "isMenuLock", publicName: "isMenuLock", isSignal: true, isRequired: false, transformFunction: null }, isShowSidebar: { classPropertyName: "isShowSidebar", publicName: "isShowSidebar", isSignal: true, isRequired: false, transformFunction: null }, userInfo: { classPropertyName: "userInfo", publicName: "userInfo", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { menuClosed: "menuClosed", menuOpened: "menuOpened", toggleMenuLock: "toggleMenuLock" }, ngImport: i0, template: "<div class=\"c-layout-user-wrapper\">\n <sd-layout-user-menu\n class=\"c-layout-user-avatar\"\n [userInfo]=\"userInfo()\"\n [compact]=\"true\"\n (opened)=\"onMenuOpened()\"\n (closed)=\"onMenuClosed()\"></sd-layout-user-menu>\n\n @if (!isMobileOrTablet()) {\n <button\n type=\"button\"\n class=\"c-layout-icon-toggle\"\n [attr.aria-label]=\"'core.module.layout.sidebar.toggle' | translate\"\n (click)=\"onToggleMenuLock($event)\">\n <sd-icon [name]=\"isShowSidebar() ? 'keyboard_arrow_right' : 'keyboard_arrow_left'\"></sd-icon>\n </button>\n }\n</div>\n", styles: [".c-layout-user-wrapper{display:flex;flex-direction:column;align-items:center;width:60px;height:80px}.c-layout-user-avatar{display:block;width:56px;height:52px}.c-layout-icon-toggle{display:flex;align-items:center;justify-content:center;width:32px;height:28px;margin:0;padding:0;border:0;border-top:1px solid var(--sd-black200, #f0f0f0);background:transparent;color:var(--sd-black400, #8c8c8c);cursor:pointer}.c-layout-icon-toggle:focus-visible{outline:2px solid var(--sd-primary);outline-offset:2px}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "component", type: SdLayoutUserMenuComponent, selector: "sd-layout-user-menu", inputs: ["userInfo", "signout", "changePassword", "updateProfile", "setting", "notification", "compact", "presentation"], outputs: ["opened", "closed"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
381
720
|
};
|
|
382
721
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: LayoutUserComponent$1, decorators: [{
|
|
383
722
|
type: Component,
|
|
384
|
-
args: [{ selector: 'lib-layout-user', imports: [SdIcon,
|
|
723
|
+
args: [{ selector: 'lib-layout-user', imports: [SdIcon, SdLayoutUserMenuComponent, TranslatePipe], standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"c-layout-user-wrapper\">\n <sd-layout-user-menu\n class=\"c-layout-user-avatar\"\n [userInfo]=\"userInfo()\"\n [compact]=\"true\"\n (opened)=\"onMenuOpened()\"\n (closed)=\"onMenuClosed()\"></sd-layout-user-menu>\n\n @if (!isMobileOrTablet()) {\n <button\n type=\"button\"\n class=\"c-layout-icon-toggle\"\n [attr.aria-label]=\"'core.module.layout.sidebar.toggle' | translate\"\n (click)=\"onToggleMenuLock($event)\">\n <sd-icon [name]=\"isShowSidebar() ? 'keyboard_arrow_right' : 'keyboard_arrow_left'\"></sd-icon>\n </button>\n }\n</div>\n", styles: [".c-layout-user-wrapper{display:flex;flex-direction:column;align-items:center;width:60px;height:80px}.c-layout-user-avatar{display:block;width:56px;height:52px}.c-layout-icon-toggle{display:flex;align-items:center;justify-content:center;width:32px;height:28px;margin:0;padding:0;border:0;border-top:1px solid var(--sd-black200, #f0f0f0);background:transparent;color:var(--sd-black400, #8c8c8c);cursor:pointer}.c-layout-icon-toggle:focus-visible{outline:2px solid var(--sd-primary);outline-offset:2px}\n"] }]
|
|
385
724
|
}], propDecorators: { isMobileOrTablet: [{ type: i0.Input, args: [{ isSignal: true, alias: "isMobileOrTablet", required: false }] }], isMenuLock: [{ type: i0.Input, args: [{ isSignal: true, alias: "isMenuLock", required: false }] }], isShowSidebar: [{ type: i0.Input, args: [{ isSignal: true, alias: "isShowSidebar", required: false }] }], userInfo: [{ type: i0.Input, args: [{ isSignal: true, alias: "userInfo", required: true }] }], menuClosed: [{ type: i0.Output, args: ["menuClosed"] }], menuOpened: [{ type: i0.Output, args: ["menuOpened"] }], toggleMenuLock: [{ type: i0.Output, args: ["toggleMenuLock"] }] } });
|
|
386
725
|
|
|
387
726
|
class SidebarComponent {
|
|
@@ -390,7 +729,9 @@ class SidebarComponent {
|
|
|
390
729
|
// ==========================================
|
|
391
730
|
#router = inject(Router);
|
|
392
731
|
#layoutStorageService = inject(SdLayoutStorageService);
|
|
732
|
+
#navigationState = inject(SdLayoutNavigationStateService);
|
|
393
733
|
#menuFocusPipe = inject(MenuFocusPipe);
|
|
734
|
+
#window = inject(DOCUMENT).defaultView;
|
|
394
735
|
#destroyRef = inject(DestroyRef); // Dùng để unsubscribe RxJS tự động
|
|
395
736
|
// ==========================================
|
|
396
737
|
// SIGNAL INPUTS & OUTPUTS
|
|
@@ -399,6 +740,7 @@ class SidebarComponent {
|
|
|
399
740
|
menus = input.required(...(ngDevMode ? [{ debugName: "menus" }] : []));
|
|
400
741
|
userInfo = input.required(...(ngDevMode ? [{ debugName: "userInfo" }] : []));
|
|
401
742
|
sidebar = input.required(...(ngDevMode ? [{ debugName: "sidebar" }] : []));
|
|
743
|
+
isMobile = input(false, ...(ngDevMode ? [{ debugName: "isMobile" }] : []));
|
|
402
744
|
expandSideBar = output();
|
|
403
745
|
popupUserMenuClosed = output();
|
|
404
746
|
popupUserMenuOpened = output();
|
|
@@ -406,25 +748,24 @@ class SidebarComponent {
|
|
|
406
748
|
// ==========================================
|
|
407
749
|
// STATE SIGNALS
|
|
408
750
|
// ==========================================
|
|
409
|
-
screenHeight = window.innerHeight;
|
|
410
|
-
isMobileOrTablet = BrowserUtilities.isMobile();
|
|
411
751
|
isMenuLock = signal(this.#layoutStorageService.menuLockStatus?.get() ?? true, ...(ngDevMode ? [{ debugName: "isMenuLock" }] : []));
|
|
412
|
-
currentPath = signal(window
|
|
752
|
+
currentPath = signal(this.#window?.location.pathname ?? this.#router.url.split(/[?#]/, 1)[0] ?? '', ...(ngDevMode ? [{ debugName: "currentPath" }] : []));
|
|
413
753
|
searchText = signal('', ...(ngDevMode ? [{ debugName: "searchText" }] : []));
|
|
414
754
|
titleMenuGroup = signal('', ...(ngDevMode ? [{ debugName: "titleMenuGroup" }] : []));
|
|
415
755
|
idMenuGroupActive = signal('', ...(ngDevMode ? [{ debugName: "idMenuGroupActive" }] : []));
|
|
416
756
|
menusByGroup = signal([], ...(ngDevMode ? [{ debugName: "menusByGroup" }] : []));
|
|
417
757
|
#hoveredMenuNodeKey = signal(null, ...(ngDevMode ? [{ debugName: "#hoveredMenuNodeKey" }] : []));
|
|
418
758
|
#pinIconHoverTimerId = signal(null, ...(ngDevMode ? [{ debugName: "#pinIconHoverTimerId" }] : []));
|
|
419
|
-
pinnedMenuGroup =
|
|
759
|
+
pinnedMenuGroup = computed(() => ({
|
|
420
760
|
id: 'pinned-menu-group',
|
|
421
761
|
title: 'Đã ghim',
|
|
422
|
-
children:
|
|
423
|
-
}, ...(ngDevMode ? [{ debugName: "pinnedMenuGroup" }] : []));
|
|
762
|
+
children: this.#navigationState.pinnedMenus(),
|
|
763
|
+
}), ...(ngDevMode ? [{ debugName: "pinnedMenuGroup" }] : []));
|
|
424
764
|
// ==========================================
|
|
425
765
|
// COMPUTED STATE
|
|
426
766
|
// ==========================================
|
|
427
767
|
totalMenuInMenusByGroup = computed(() => this.#getTotalMenus(this.menusByGroup()), ...(ngDevMode ? [{ debugName: "totalMenuInMenusByGroup" }] : []));
|
|
768
|
+
logoUrl = computed(() => this.sidebar().logoUrl?.trim() || undefined, ...(ngDevMode ? [{ debugName: "logoUrl" }] : []));
|
|
428
769
|
pinnedNodeKeys = computed(() => new Set(this.pinnedMenuGroup().children?.map(m => this.#getMenuNodeKey(m)) ?? []), ...(ngDevMode ? [{ debugName: "pinnedNodeKeys" }] : []));
|
|
429
770
|
isPinnedMenuGroupActive = computed(() => this.idMenuGroupActive() === this.pinnedMenuGroup().id, ...(ngDevMode ? [{ debugName: "isPinnedMenuGroupActive" }] : []));
|
|
430
771
|
// ==========================================
|
|
@@ -442,6 +783,7 @@ class SidebarComponent {
|
|
|
442
783
|
effect(() => {
|
|
443
784
|
const currentMenus = this.menus();
|
|
444
785
|
untracked(() => {
|
|
786
|
+
this.#navigationState.hydrate(currentMenus);
|
|
445
787
|
const lastActiveMenuGroupId = this.#layoutStorageService.lastActiveMenuGroupId.get();
|
|
446
788
|
const pinnedGroup = this.pinnedMenuGroup();
|
|
447
789
|
const isPinEnabled = this.sidebar()?.pin?.enabled;
|
|
@@ -459,8 +801,9 @@ class SidebarComponent {
|
|
|
459
801
|
#setupSubscriptions() {
|
|
460
802
|
this.#router.events.pipe(takeUntilDestroyed(this.#destroyRef)).subscribe(event => {
|
|
461
803
|
if (event instanceof NavigationEnd) {
|
|
462
|
-
|
|
463
|
-
|
|
804
|
+
const currentPath = event.urlAfterRedirects.split(/[?#]/, 1)[0] ?? '';
|
|
805
|
+
if (this.currentPath() !== currentPath) {
|
|
806
|
+
this.currentPath.set(currentPath);
|
|
464
807
|
this.#bindingMenuGroupByCurrentPath(this.menus());
|
|
465
808
|
}
|
|
466
809
|
}
|
|
@@ -486,15 +829,7 @@ class SidebarComponent {
|
|
|
486
829
|
};
|
|
487
830
|
onTogglePin = (event, node) => {
|
|
488
831
|
event.stopPropagation();
|
|
489
|
-
|
|
490
|
-
this.pinnedMenuGroup.update(group => {
|
|
491
|
-
const children = group.children ?? [];
|
|
492
|
-
const exists = children.some(m => this.#getMenuNodeKey(m) === key);
|
|
493
|
-
const updatedChildren = exists ? children.filter(m => this.#getMenuNodeKey(m) !== key) : [...children, node];
|
|
494
|
-
const updatedGroup = { id: 'pinned-menu-group', title: 'Đã ghim', children: updatedChildren };
|
|
495
|
-
this.#layoutStorageService.pinnedMenuGroup.set(updatedGroup);
|
|
496
|
-
return updatedGroup;
|
|
497
|
-
});
|
|
832
|
+
this.#navigationState.togglePinned(node);
|
|
498
833
|
};
|
|
499
834
|
#onExpandAllMenuNodes = (menus) => {
|
|
500
835
|
menus?.forEach(menu => {
|
|
@@ -519,12 +854,7 @@ class SidebarComponent {
|
|
|
519
854
|
this.showSideBar.emit(this.isMenuLock());
|
|
520
855
|
}
|
|
521
856
|
openHomePage = () => {
|
|
522
|
-
|
|
523
|
-
this.#router.navigateByUrl('/layout/home');
|
|
524
|
-
}
|
|
525
|
-
else {
|
|
526
|
-
window.location.href = '/layout/home';
|
|
527
|
-
}
|
|
857
|
+
this.#router.navigateByUrl('/layout/home');
|
|
528
858
|
this.searchText.set('');
|
|
529
859
|
this.#layoutStorageService.lastActiveMenuGroupId.set('');
|
|
530
860
|
};
|
|
@@ -533,14 +863,14 @@ class SidebarComponent {
|
|
|
533
863
|
navigate = (args) => {
|
|
534
864
|
const { path, queryParams } = args;
|
|
535
865
|
if (path.includes('http')) {
|
|
536
|
-
window
|
|
866
|
+
this.#window?.open(path, '_blank', 'noopener');
|
|
537
867
|
return;
|
|
538
868
|
}
|
|
539
869
|
this.#router.navigate([path.split('?')[0]], {
|
|
540
870
|
queryParams,
|
|
541
871
|
state: { switchTab: true },
|
|
542
872
|
});
|
|
543
|
-
if (this.
|
|
873
|
+
if (this.isMobile()) {
|
|
544
874
|
this.#closeMenu();
|
|
545
875
|
}
|
|
546
876
|
};
|
|
@@ -571,7 +901,7 @@ class SidebarComponent {
|
|
|
571
901
|
this.idMenuGroupActive.set(menuGroupNode?.id);
|
|
572
902
|
this.#layoutStorageService.lastActiveMenuGroupId.set(menuGroupNode?.id || '');
|
|
573
903
|
if (!this.currentPath()) {
|
|
574
|
-
this.currentPath.set(window
|
|
904
|
+
this.currentPath.set(this.#window?.location.pathname ?? this.#router.url.split(/[?#]/, 1)[0] ?? '');
|
|
575
905
|
}
|
|
576
906
|
};
|
|
577
907
|
// ==========================================
|
|
@@ -887,20 +1217,20 @@ class SidebarComponent {
|
|
|
887
1217
|
return str.trim();
|
|
888
1218
|
};
|
|
889
1219
|
#getTotalMenus = (menus) => {
|
|
890
|
-
return menus.reduce((total, menu) => total + this.#
|
|
1220
|
+
return menus.reduce((total, menu) => total + this.#countMenuNode(menu), 0);
|
|
891
1221
|
};
|
|
892
|
-
#
|
|
1222
|
+
#countMenuNode = (menu) => {
|
|
893
1223
|
if (!('children' in menu) || !menu.children?.length) {
|
|
894
|
-
return
|
|
1224
|
+
return 1;
|
|
895
1225
|
}
|
|
896
|
-
let count =
|
|
1226
|
+
let count = 1;
|
|
897
1227
|
for (const child of menu.children) {
|
|
898
|
-
count += this.#
|
|
1228
|
+
count += this.#countMenuNode(child);
|
|
899
1229
|
}
|
|
900
1230
|
return count;
|
|
901
1231
|
};
|
|
902
1232
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SidebarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
903
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SidebarComponent, isStandalone: true, selector: "sidebar", inputs: { isShowSidebar: { classPropertyName: "isShowSidebar", publicName: "isShowSidebar", isSignal: true, isRequired: true, transformFunction: null }, menus: { classPropertyName: "menus", publicName: "menus", isSignal: true, isRequired: true, transformFunction: null }, userInfo: { classPropertyName: "userInfo", publicName: "userInfo", isSignal: true, isRequired: true, transformFunction: null }, sidebar: { classPropertyName: "sidebar", publicName: "sidebar", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { expandSideBar: "expandSideBar", popupUserMenuClosed: "popupUserMenuClosed", popupUserMenuOpened: "popupUserMenuOpened", showSideBar: "showSideBar" }, ngImport: i0, template: "@let _userInfo = userInfo();\n@let _sidebar = sidebar();\n\n@if (_userInfo && _sidebar) {\n <div class=\"wrapper\" [style.height]=\"isMobileOrTablet ? screenHeight + 'px' : '100%'\">\n <div class=\"c-header\">\n @if (_sidebar.logoUrl) {\n <div class=\"c-logo\">\n <a href=\"javascript:;\" (click)=\"openHomePage()\">\n <img alt=\"logo\" [src]=\"_sidebar.logoUrl\" />\n </a>\n </div>\n }\n <div class=\"c-title-menu-group\">\n <span>{{ titleMenuGroup() || _sidebar.defaultTitle || 'Back Office' }}</span>\n </div>\n </div>\n <div class=\"c-body\">\n <div class=\"c-menu\">\n <div class=\"c-menu-group\">\n @if (_sidebar?.pin?.enabled && pinnedMenuGroup().children?.length) {\n <button\n (mouseenter)=\"onMouseOverMenuGroupNode($event, pinnedMenuGroup())\"\n (mouseleave)=\"onMouseLeaveMenuGroupNode($event, pinnedMenuGroup())\"\n (click)=\"expandPinnedGroup()\"\n [matTooltip]=\"'\u0110\u00E3 ghim'\"\n [matTooltipClass]=\"'c-tooltip-menu-group-7a22ab15-0083-4d4c-9c0c-00a30bc8c140'\"\n [matTooltipPosition]=\"'right'\"\n style=\"padding: 0; margin: 0; border: none; background-color: transparent\">\n <sd-icon class=\"c-menu-group-icon\"\n [ngStyle]=\"{\n color: isPinnedMenuGroupActive() ? _sidebar.brandColor || 'var(--sd-primary, #005cbb)' : 'var(--sd-text-secondary, #44474f)',\n backgroundColor: isPinnedMenuGroupActive() ? _sidebar.brandLightColor || 'var(--sd-primary-light, #d7e3ff)' : 'transparent',\n }\" name=\"push_pin\"></sd-icon>\n </button>\n }\n @for (nodeMenuGroup of menus(); track nodeMenuGroup.id) {\n <button\n (mouseenter)=\"onMouseOverMenuGroupNode($event, nodeMenuGroup)\"\n (mouseleave)=\"onMouseLeaveMenuGroupNode($event, nodeMenuGroup)\"\n (click)=\"expandMenuGroup(nodeMenuGroup)\"\n [matTooltip]=\"nodeMenuGroup.tooltipTitle || nodeMenuGroup.title\"\n [matTooltipClass]=\"'c-tooltip-menu-group-7a22ab15-0083-4d4c-9c0c-00a30bc8c140'\"\n [matTooltipPosition]=\"'right'\"\n style=\"padding: 0; margin: 0; border: none; background-color: transparent\">\n @if (nodeMenuGroup.iconUrl) {\n <span\n class=\"c-menu-group-icon\"\n [ngStyle]=\"{\n backgroundColor: idMenuGroupActive() === nodeMenuGroup?.id ? _sidebar.brandLightColor || 'var(--sd-primary-light, #d7e3ff)' : 'transparent',\n }\">\n <img width=\"24px\" height=\"24px\" [src]=\"nodeMenuGroup.iconUrl\" [alt]=\"nodeMenuGroup.title\" />\n </span>\n } @else {\n <sd-icon class=\"c-menu-group-icon\"\n [ngStyle]=\"{\n color: idMenuGroupActive() === nodeMenuGroup?.id ? _sidebar.brandColor || 'var(--sd-primary, #005cbb)' : 'var(--sd-text-secondary, #44474f)',\n backgroundColor: idMenuGroupActive() === nodeMenuGroup?.id ? _sidebar.brandLightColor || 'var(--sd-primary-light, #d7e3ff)' : 'transparent',\n }\" [name]=\"nodeMenuGroup.icon || 'widgets'\"></sd-icon>\n }\n </button>\n }\n </div>\n <div class=\"c-menu-tree\">\n @if (totalMenuInMenusByGroup() >= 10) {\n <div style=\"padding: 0px 4px\" class=\"c-menu-tree-search\">\n <sd-input size=\"sm\" [placeholder]=\"'core.module.layout.sidebar.search' | translate\" [model]=\"searchText()\" (sdChange)=\"onFilterSearchText($event)\">\n <ng-template sdSuffixDef>\n @if (searchText()) {\n <sd-icon class=\"c-search-prefix-icon cancel\" (click)=\"onClearSearchText()\" name=\"cancel\"></sd-icon>\n } @else {\n <sd-icon class=\"c-search-prefix-icon search\" name=\"search\"></sd-icon>\n }\n </ng-template>\n </sd-input>\n </div>\n }\n <div class=\"c-menu-tree-container\">\n <mat-tree [dataSource]=\"dataSource\" [treeControl]=\"treeControl\" [style.backgroundColor]=\"'transparent'\">\n <mat-nested-tree-node *matTreeNodeDef=\"let node; when: !hasChild\">\n <li\n class=\"c-menu-node\"\n (mouseenter)=\"onMouseOverMenuNode($event, node)\"\n (mouseleave)=\"onMouseLeaveMenuNode($event, node)\"\n [ngStyle]=\"{\n backgroundColor: (currentPath() | menuFocus: node) ? _sidebar.brandLightColor || 'var(--sd-primary-light, #d7e3ff)' : 'transparent',\n }\">\n <div aria-hidden=\"true\" class=\"c-menu-node-description\" (click)=\"navigate(node)\" [class.d-none]=\"!isShowSidebar()\">\n <div\n class=\"c-menu-node-description-content\"\n [ngStyle]=\"{\n color: (currentPath() | menuFocus: node) ? _sidebar.brandColor || 'var(--sd-primary, #005cbb)' : 'var(--sd-text, #1a1b1f)',\n }\"\n [innerHTML]=\"node.title | highLightSearch: searchText() | sdSafeHtml\"></div>\n\n @if (_sidebar?.pin?.enabled) {\n <sd-icon class=\"c-menu-node-description-icon-pin\"\n [style.opacity]=\"isPinnedNode(node) || isHoveredNode(node) ? '1' : null\"\n [style.color]=\"isPinnedNode(node) || isHoveredNode(node) ? sidebar().brandColor || 'var(--sd-primary, #005cbb)' : null\"\n (click)=\"onTogglePin($event, node)\" name=\"push_pin\"></sd-icon>\n }\n </div>\n </li>\n </mat-nested-tree-node>\n\n <mat-nested-tree-node\n *matTreeNodeDef=\"let node; when: hasChild\"\n aria-hidden=\"true\"\n [class]=\"{ expanded: treeControl.isExpanded(node), isfocus: currentPath() | menuFocus: node }\">\n <li>\n <div\n class=\"c-menu-node\"\n (mouseenter)=\"onMouseOverMenuNode($event, node)\"\n (mouseleave)=\"onMouseLeaveMenuNode($event, node)\">\n <div class=\"d-flex align-items-center\" style=\"gap: 10px; width: 100%\">\n <div [class.d-none]=\"!isShowSidebar()\" class=\"c-menu-node-description\" (click)=\"onToggleMenuNode(node)\">\n <div\n class=\"c-menu-node-description-content\"\n [innerHTML]=\"node.title | highLightSearch: searchText() | sdSafeHtml\"></div>\n <sd-icon class=\"c-menu-node-description-icon-expand\" [name]=\"treeControl.isExpanded(node) ? 'keyboard_arrow_up' : 'keyboard_arrow_down'\"></sd-icon>\n </div>\n </div>\n </div>\n <ul class=\"p-0\" [class.d-none]=\"!treeControl.isExpanded(node)\">\n <ng-container matTreeNodeOutlet></ng-container>\n </ul>\n </li>\n </mat-nested-tree-node>\n </mat-tree>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"c-footer\">\n <lib-layout-user\n [userInfo]=\"_userInfo\"\n [isMobileOrTablet]=\"isMobileOrTablet\"\n (menuOpened)=\"onUserMenuOpened()\"\n (menuClosed)=\"onUserMenuClosed()\"\n [isShowSidebar]=\"isShowSidebar()\"\n (toggleMenuLock)=\"toggleMenuLock($event)\">\n </lib-layout-user>\n </div>\n\n <div class=\"c-vertical\"></div>\n </div>\n}\n", styles: ["::ng-deep .mat-mdc-tooltip-panel:has(.c-tooltip-menu-group-7a22ab15-0083-4d4c-9c0c-00a30bc8c140){pointer-events:none}:host ::ng-deep .mat-nested-tree-node ul .c-menu-node-description-content{margin-left:16px!important}:host ::ng-deep .mat-nested-tree-node ul .mat-nested-tree-node ul .c-menu-node-description-content{margin-left:32px!important}:host ::ng-deep .mat-nested-tree-node ul .mat-nested-tree-node ul .mat-nested-tree-node ul .c-menu-node-description-content{margin-left:48px!important}ul,li{margin-top:0;margin-bottom:0;list-style-type:none}.wrapper{display:flex;flex-direction:column;width:290px;background-color:#fff}.wrapper .c-header{display:flex;align-items:center;height:52px;padding:3px;gap:16px}.wrapper .c-header .c-logo{display:flex;justify-content:center;align-items:center;width:52px;height:52px}.wrapper .c-header .c-logo img{width:32px;height:32px;object-fit:cover}.wrapper .c-header .c-title-menu-group{display:flex;align-items:center;font-size:18px;font-weight:500;flex:1}.wrapper .c-body{flex:1;overflow-y:hidden}.wrapper .c-body .c-menu{height:100%;display:flex}.wrapper .c-body .c-menu .c-menu-group{display:flex;flex-direction:column;align-items:center;width:60px;overflow-y:scroll;scrollbar-width:none}.wrapper .c-body .c-menu .c-menu-group .c-menu-group-icon{display:flex;justify-content:center;align-items:center;min-height:50px;width:52px;border-radius:8px;transition:all .15s}.wrapper .c-body .c-menu .c-menu-group .c-menu-group-icon img{height:24px;width:24px;object-fit:contain}.wrapper .c-body .c-menu .c-menu-tree{flex:1;display:flex;flex-direction:column;padding:3px 4px 3px 3px}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-search .c-search-prefix-icon{cursor:pointer;color:#757575;padding:0}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-search .c-search-prefix-icon.search{width:20px;height:20px;font-size:18px}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-search .c-search-prefix-icon.cancel{width:16px;height:16px;font-size:16px}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-container{flex:1;overflow-y:scroll;scrollbar-width:none}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-container .c-menu-node{width:100%;display:flex;cursor:pointer;min-height:44px;padding:8px;border-radius:8px}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-container .c-menu-node .c-menu-node-icon{display:flex;justify-content:center;align-items:center;height:100%}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-container .c-menu-node .c-menu-node-description{flex:1;display:flex;align-items:center}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-container .c-menu-node .c-menu-node-description .c-menu-node-description-content{flex:1;display:flex;align-items:center;flex-wrap:wrap;white-space:pre-wrap;font-size:15px}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-container .c-menu-node .c-menu-node-description .c-menu-node-description-icon-expand{display:flex;align-items:center;justify-content:start;background-color:transparent;border:none;font-size:20px}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-container .c-menu-node .c-menu-node-description .c-menu-node-description-icon-pin{display:flex;align-items:center;justify-content:start;background-color:transparent;border:none;font-size:18px;opacity:0}.wrapper .c-footer{height:80px}.wrapper .c-vertical{position:fixed;height:100vh;left:58px;width:2px;background-color:#f8f9fa;pointer-events:none}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "component", type: SdInput, selector: "sd-input", inputs: ["autoId", "name", "appearance", "floatLabel", "size", "form", "label", "helperText", "placeholder", "type", "hideInlineError", "blurOnEnter", "clearable", "required", "readonly", "disabled", "viewed", "minlength", "maxlength", "pattern", "patternErrorMessage", "validator", "inlineError", "hyperlink", "model"], outputs: ["modelChange", "sdChange", "sdFocus", "sdBlur", "keyupEnter", "cleared", "sdFocusForceBlur"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: RouterModule }, { kind: "ngmodule", type: MatTreeModule }, { kind: "directive", type: i2$1.MatNestedTreeNode, selector: "mat-nested-tree-node", inputs: ["matNestedTreeNode", "disabled", "tabIndex"], outputs: ["activation", "expandedChange"], exportAs: ["matNestedTreeNode"] }, { kind: "directive", type: i2$1.MatTreeNodeDef, selector: "[matTreeNodeDef]", inputs: ["matTreeNodeDefWhen", "matTreeNode"] }, { kind: "component", type: i2$1.MatTree, selector: "mat-tree", exportAs: ["matTree"] }, { kind: "directive", type: i2$1.MatTreeNodeOutlet, selector: "[matTreeNodeOutlet]" }, { kind: "ngmodule", type: MatInputModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: SdSuffixDefDirective, selector: "[sdSuffixDef]" }, { kind: "component", type: LayoutUserComponent$1, selector: "lib-layout-user", inputs: ["isMobileOrTablet", "isMenuLock", "isShowSidebar", "userInfo"], outputs: ["menuClosed", "menuOpened", "toggleMenuLock"] }, { kind: "pipe", type: SdSafeHtmlPipe, name: "sdSafeHtml" }, { kind: "pipe", type: MenuFocusPipe, name: "menuFocus" }, { kind: "pipe", type: HighlightSearchPipe, name: "highLightSearch" }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
|
|
1233
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SidebarComponent, isStandalone: true, selector: "sidebar", inputs: { isShowSidebar: { classPropertyName: "isShowSidebar", publicName: "isShowSidebar", isSignal: true, isRequired: true, transformFunction: null }, menus: { classPropertyName: "menus", publicName: "menus", isSignal: true, isRequired: true, transformFunction: null }, userInfo: { classPropertyName: "userInfo", publicName: "userInfo", isSignal: true, isRequired: true, transformFunction: null }, sidebar: { classPropertyName: "sidebar", publicName: "sidebar", isSignal: true, isRequired: true, transformFunction: null }, isMobile: { classPropertyName: "isMobile", publicName: "isMobile", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { expandSideBar: "expandSideBar", popupUserMenuClosed: "popupUserMenuClosed", popupUserMenuOpened: "popupUserMenuOpened", showSideBar: "showSideBar" }, ngImport: i0, template: "@let _userInfo = userInfo();\n@let _sidebar = sidebar();\n\n@if (_userInfo && _sidebar) {\n <div class=\"wrapper\" [style.height]=\"isMobile() ? '100dvh' : '100%'\">\n <div class=\"c-header\">\n <div class=\"c-logo\">\n <a href=\"javascript:;\" aria-label=\"Trang ch\u1EE7\" (click)=\"openHomePage()\">\n @if (logoUrl(); as _logoUrl) {\n <img alt=\"\" [src]=\"_logoUrl\" />\n } @else {\n <sd-icon name=\"apps\"></sd-icon>\n }\n </a>\n </div>\n <div class=\"c-title-menu-group\" [class.d-none]=\"!isShowSidebar()\">\n <span>{{ titleMenuGroup() || _sidebar.defaultTitle || 'Back Office' }}</span>\n </div>\n </div>\n <div class=\"c-body\">\n <div class=\"c-menu\">\n <div class=\"c-menu-group\">\n @if (_sidebar?.pin?.enabled && pinnedMenuGroup().children?.length) {\n <button\n (mouseenter)=\"onMouseOverMenuGroupNode($event, pinnedMenuGroup())\"\n (mouseleave)=\"onMouseLeaveMenuGroupNode($event, pinnedMenuGroup())\"\n (click)=\"expandPinnedGroup()\"\n [matTooltip]=\"'\u0110\u00E3 ghim'\"\n [matTooltipClass]=\"'c-tooltip-menu-group-7a22ab15-0083-4d4c-9c0c-00a30bc8c140'\"\n [matTooltipPosition]=\"'right'\"\n style=\"padding: 0; margin: 0; border: none; background-color: transparent\">\n <sd-icon\n class=\"c-menu-group-icon\"\n [ngStyle]=\"{\n color: isPinnedMenuGroupActive()\n ? _sidebar.brandColor || 'var(--sd-primary, #005cbb)'\n : 'var(--sd-text-secondary, #44474f)',\n backgroundColor: isPinnedMenuGroupActive()\n ? _sidebar.brandLightColor || 'var(--sd-primary-light, #d7e3ff)'\n : 'transparent',\n }\"\n name=\"push_pin\"></sd-icon>\n </button>\n }\n @for (nodeMenuGroup of menus(); track nodeMenuGroup.id) {\n <button\n (mouseenter)=\"onMouseOverMenuGroupNode($event, nodeMenuGroup)\"\n (mouseleave)=\"onMouseLeaveMenuGroupNode($event, nodeMenuGroup)\"\n (click)=\"expandMenuGroup(nodeMenuGroup)\"\n [matTooltip]=\"nodeMenuGroup.tooltipTitle || nodeMenuGroup.title\"\n [matTooltipClass]=\"'c-tooltip-menu-group-7a22ab15-0083-4d4c-9c0c-00a30bc8c140'\"\n [matTooltipPosition]=\"'right'\"\n style=\"padding: 0; margin: 0; border: none; background-color: transparent\">\n @if (nodeMenuGroup.iconUrl) {\n <span\n class=\"c-menu-group-icon\"\n [ngStyle]=\"{\n backgroundColor:\n idMenuGroupActive() === nodeMenuGroup?.id\n ? _sidebar.brandLightColor || 'var(--sd-primary-light, #d7e3ff)'\n : 'transparent',\n }\">\n <img width=\"24px\" height=\"24px\" [src]=\"nodeMenuGroup.iconUrl\" [alt]=\"nodeMenuGroup.title\" />\n </span>\n } @else {\n <sd-icon\n class=\"c-menu-group-icon\"\n [ngStyle]=\"{\n color:\n idMenuGroupActive() === nodeMenuGroup?.id\n ? _sidebar.brandColor || 'var(--sd-primary, #005cbb)'\n : 'var(--sd-text-secondary, #44474f)',\n backgroundColor:\n idMenuGroupActive() === nodeMenuGroup?.id\n ? _sidebar.brandLightColor || 'var(--sd-primary-light, #d7e3ff)'\n : 'transparent',\n }\"\n [name]=\"nodeMenuGroup.icon || 'widgets'\"></sd-icon>\n }\n </button>\n }\n </div>\n <div class=\"c-menu-tree\" [class.d-none]=\"!isShowSidebar()\" [attr.inert]=\"!isShowSidebar() ? '' : null\">\n @if (totalMenuInMenusByGroup() > 10) {\n <div style=\"padding: 0px 4px\" class=\"c-menu-tree-search\">\n <sd-input\n size=\"sm\"\n [placeholder]=\"'core.module.layout.sidebar.search' | translate\"\n [autoId]=\"'layout-v1-menu-search'\"\n [model]=\"searchText()\"\n (sdChange)=\"onFilterSearchText($event)\">\n <ng-template sdSuffixDef>\n @if (searchText()) {\n <sd-icon class=\"c-search-prefix-icon cancel\" (click)=\"onClearSearchText()\" name=\"cancel\"></sd-icon>\n } @else {\n <sd-icon class=\"c-search-prefix-icon search\" name=\"search\"></sd-icon>\n }\n </ng-template>\n </sd-input>\n </div>\n }\n <div class=\"c-menu-tree-container\">\n <mat-tree [dataSource]=\"dataSource\" [treeControl]=\"treeControl\" [style.backgroundColor]=\"'transparent'\">\n <mat-nested-tree-node *matTreeNodeDef=\"let node; when: !hasChild\">\n <li\n class=\"c-menu-node\"\n (mouseenter)=\"onMouseOverMenuNode($event, node)\"\n (mouseleave)=\"onMouseLeaveMenuNode($event, node)\"\n [ngStyle]=\"{\n backgroundColor:\n (currentPath() | menuFocus: node) ? _sidebar.brandLightColor || 'var(--sd-primary-light, #d7e3ff)' : 'transparent',\n }\">\n <div aria-hidden=\"true\" class=\"c-menu-node-description\" (click)=\"navigate(node)\" [class.d-none]=\"!isShowSidebar()\">\n <div\n class=\"c-menu-node-description-content\"\n [ngStyle]=\"{\n color:\n (currentPath() | menuFocus: node)\n ? _sidebar.brandColor || 'var(--sd-primary, #005cbb)'\n : 'var(--sd-text, #1a1b1f)',\n }\"\n [innerHTML]=\"node.title | highLightSearch: searchText() | sdSafeHtml\"></div>\n\n @if (_sidebar?.pin?.enabled) {\n <sd-icon\n class=\"c-menu-node-description-icon-pin\"\n [style.opacity]=\"isPinnedNode(node) || isHoveredNode(node) ? '1' : null\"\n [style.color]=\"\n isPinnedNode(node) || isHoveredNode(node) ? sidebar().brandColor || 'var(--sd-primary, #005cbb)' : null\n \"\n (click)=\"onTogglePin($event, node)\"\n name=\"push_pin\"></sd-icon>\n }\n </div>\n </li>\n </mat-nested-tree-node>\n\n <mat-nested-tree-node\n *matTreeNodeDef=\"let node; when: hasChild\"\n aria-hidden=\"true\"\n [class]=\"{ expanded: treeControl.isExpanded(node), isfocus: currentPath() | menuFocus: node }\">\n <li>\n <div\n class=\"c-menu-node\"\n (mouseenter)=\"onMouseOverMenuNode($event, node)\"\n (mouseleave)=\"onMouseLeaveMenuNode($event, node)\">\n <div class=\"d-flex align-items-center\" style=\"gap: 10px; width: 100%\">\n <div [class.d-none]=\"!isShowSidebar()\" class=\"c-menu-node-description\" (click)=\"onToggleMenuNode(node)\">\n <div\n class=\"c-menu-node-description-content\"\n [innerHTML]=\"node.title | highLightSearch: searchText() | sdSafeHtml\"></div>\n <sd-icon\n class=\"c-menu-node-description-icon-expand\"\n [name]=\"treeControl.isExpanded(node) ? 'keyboard_arrow_up' : 'keyboard_arrow_down'\"></sd-icon>\n </div>\n </div>\n </div>\n <ul class=\"p-0\" [class.d-none]=\"!treeControl.isExpanded(node)\">\n <ng-container matTreeNodeOutlet></ng-container>\n </ul>\n </li>\n </mat-nested-tree-node>\n </mat-tree>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"c-footer\">\n <lib-layout-user\n [userInfo]=\"_userInfo\"\n [isMobileOrTablet]=\"isMobile()\"\n (menuOpened)=\"onUserMenuOpened()\"\n (menuClosed)=\"onUserMenuClosed()\"\n [isShowSidebar]=\"isShowSidebar()\"\n (toggleMenuLock)=\"toggleMenuLock($event)\">\n </lib-layout-user>\n </div>\n\n <div class=\"c-vertical\"></div>\n </div>\n}\n", styles: ["::ng-deep .mat-mdc-tooltip-panel:has(.c-tooltip-menu-group-7a22ab15-0083-4d4c-9c0c-00a30bc8c140){pointer-events:none}:host ::ng-deep .mat-nested-tree-node ul .c-menu-node-description-content{margin-left:16px!important}:host ::ng-deep .mat-nested-tree-node ul .mat-nested-tree-node ul .c-menu-node-description-content{margin-left:32px!important}:host ::ng-deep .mat-nested-tree-node ul .mat-nested-tree-node ul .mat-nested-tree-node ul .c-menu-node-description-content{margin-left:48px!important}ul,li{margin-top:0;margin-bottom:0;list-style-type:none}.wrapper{display:flex;flex-direction:column;width:290px;background-color:#fff}.wrapper .c-header{display:flex;align-items:center;height:52px;padding:3px;gap:16px}.wrapper .c-header .c-logo{display:flex;justify-content:center;align-items:center;width:52px;height:52px}.wrapper .c-header .c-logo a{display:flex;align-items:center;justify-content:center;width:100%;height:100%;color:var(--sd-text-secondary, #44474f)}.wrapper .c-header .c-logo img{width:32px;height:32px;object-fit:contain}.wrapper .c-header .c-title-menu-group{display:flex;align-items:center;font-size:18px;font-weight:500;flex:1}.wrapper .c-body{flex:1;overflow-y:hidden}.wrapper .c-body .c-menu{height:100%;display:flex}.wrapper .c-body .c-menu .c-menu-group{display:flex;flex-direction:column;align-items:center;flex:0 0 60px;min-width:60px;width:60px;overflow-y:scroll;scrollbar-width:none}.wrapper .c-body .c-menu .c-menu-group .c-menu-group-icon{display:flex;justify-content:center;align-items:center;min-height:50px;width:52px;border-radius:8px;transition:all .15s}.wrapper .c-body .c-menu .c-menu-group .c-menu-group-icon img{height:24px;width:24px;object-fit:contain}.wrapper .c-body .c-menu .c-menu-tree{flex:1;display:flex;flex-direction:column;padding:3px 4px 3px 3px}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-search .c-search-prefix-icon{cursor:pointer;color:#757575;padding:0}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-search .c-search-prefix-icon.search{width:20px;height:20px;font-size:18px}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-search .c-search-prefix-icon.cancel{width:16px;height:16px;font-size:16px}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-container{flex:1;overflow-y:scroll;scrollbar-width:none}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-container .c-menu-node{width:100%;display:flex;cursor:pointer;min-height:44px;padding:8px;border-radius:8px}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-container .c-menu-node .c-menu-node-icon{display:flex;justify-content:center;align-items:center;height:100%}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-container .c-menu-node .c-menu-node-description{flex:1;display:flex;align-items:center}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-container .c-menu-node .c-menu-node-description .c-menu-node-description-content{flex:1;display:flex;align-items:center;flex-wrap:wrap;white-space:pre-wrap;font-size:15px}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-container .c-menu-node .c-menu-node-description .c-menu-node-description-icon-expand{display:flex;align-items:center;justify-content:start;background-color:transparent;border:none;font-size:20px}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-container .c-menu-node .c-menu-node-description .c-menu-node-description-icon-pin{display:flex;align-items:center;justify-content:start;background-color:transparent;border:none;font-size:18px;opacity:0}.wrapper .c-footer{height:80px}.wrapper .c-vertical{position:fixed;height:100vh;left:58px;width:2px;background-color:#f8f9fa;pointer-events:none}:host ::ng-deep .c-menu-tree-search input:focus-visible{outline:1px solid var(--sd-primary, #005cbb);outline-offset:1px}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "component", type: SdInput, selector: "sd-input", inputs: ["autoId", "name", "appearance", "floatLabel", "size", "form", "label", "helperText", "placeholder", "type", "mask", "hideInlineError", "blurOnEnter", "clearable", "required", "readonly", "disabled", "viewed", "minlength", "maxlength", "pattern", "patternErrorMessage", "validator", "inlineError", "hyperlink", "model"], outputs: ["modelChange", "sdChange", "sdFocus", "sdBlur", "keyupEnter", "cleared", "sdFocusForceBlur"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: RouterModule }, { kind: "ngmodule", type: MatTreeModule }, { kind: "directive", type: i2.MatNestedTreeNode, selector: "mat-nested-tree-node", inputs: ["matNestedTreeNode", "disabled", "tabIndex"], outputs: ["activation", "expandedChange"], exportAs: ["matNestedTreeNode"] }, { kind: "directive", type: i2.MatTreeNodeDef, selector: "[matTreeNodeDef]", inputs: ["matTreeNodeDefWhen", "matTreeNode"] }, { kind: "component", type: i2.MatTree, selector: "mat-tree", exportAs: ["matTree"] }, { kind: "directive", type: i2.MatTreeNodeOutlet, selector: "[matTreeNodeOutlet]" }, { kind: "ngmodule", type: MatInputModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: SdSuffixDefDirective, selector: "[sdSuffixDef]" }, { kind: "component", type: LayoutUserComponent$1, selector: "lib-layout-user", inputs: ["isMobileOrTablet", "isMenuLock", "isShowSidebar", "userInfo"], outputs: ["menuClosed", "menuOpened", "toggleMenuLock"] }, { kind: "pipe", type: SdSafeHtmlPipe, name: "sdSafeHtml" }, { kind: "pipe", type: MenuFocusPipe, name: "menuFocus" }, { kind: "pipe", type: HighlightSearchPipe, name: "highLightSearch" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
904
1234
|
}
|
|
905
1235
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SidebarComponent, decorators: [{
|
|
906
1236
|
type: Component,
|
|
@@ -919,8 +1249,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
919
1249
|
SdSuffixDefDirective,
|
|
920
1250
|
LayoutUserComponent$1,
|
|
921
1251
|
TranslatePipe,
|
|
922
|
-
], template: "@let _userInfo = userInfo();\n@let _sidebar = sidebar();\n\n@if (_userInfo && _sidebar) {\n <div class=\"wrapper\" [style.height]=\"isMobileOrTablet ? screenHeight + 'px' : '100%'\">\n <div class=\"c-header\">\n @if (_sidebar.logoUrl) {\n <div class=\"c-logo\">\n <a href=\"javascript:;\" (click)=\"openHomePage()\">\n <img alt=\"logo\" [src]=\"_sidebar.logoUrl\" />\n </a>\n </div>\n }\n <div class=\"c-title-menu-group\">\n <span>{{ titleMenuGroup() || _sidebar.defaultTitle || 'Back Office' }}</span>\n </div>\n </div>\n <div class=\"c-body\">\n <div class=\"c-menu\">\n <div class=\"c-menu-group\">\n @if (_sidebar?.pin?.enabled && pinnedMenuGroup().children?.length) {\n <button\n (mouseenter)=\"onMouseOverMenuGroupNode($event, pinnedMenuGroup())\"\n (mouseleave)=\"onMouseLeaveMenuGroupNode($event, pinnedMenuGroup())\"\n (click)=\"expandPinnedGroup()\"\n [matTooltip]=\"'\u0110\u00E3 ghim'\"\n [matTooltipClass]=\"'c-tooltip-menu-group-7a22ab15-0083-4d4c-9c0c-00a30bc8c140'\"\n [matTooltipPosition]=\"'right'\"\n style=\"padding: 0; margin: 0; border: none; background-color: transparent\">\n <sd-icon class=\"c-menu-group-icon\"\n [ngStyle]=\"{\n color: isPinnedMenuGroupActive() ? _sidebar.brandColor || 'var(--sd-primary, #005cbb)' : 'var(--sd-text-secondary, #44474f)',\n backgroundColor: isPinnedMenuGroupActive() ? _sidebar.brandLightColor || 'var(--sd-primary-light, #d7e3ff)' : 'transparent',\n }\" name=\"push_pin\"></sd-icon>\n </button>\n }\n @for (nodeMenuGroup of menus(); track nodeMenuGroup.id) {\n <button\n (mouseenter)=\"onMouseOverMenuGroupNode($event, nodeMenuGroup)\"\n (mouseleave)=\"onMouseLeaveMenuGroupNode($event, nodeMenuGroup)\"\n (click)=\"expandMenuGroup(nodeMenuGroup)\"\n [matTooltip]=\"nodeMenuGroup.tooltipTitle || nodeMenuGroup.title\"\n [matTooltipClass]=\"'c-tooltip-menu-group-7a22ab15-0083-4d4c-9c0c-00a30bc8c140'\"\n [matTooltipPosition]=\"'right'\"\n style=\"padding: 0; margin: 0; border: none; background-color: transparent\">\n @if (nodeMenuGroup.iconUrl) {\n <span\n class=\"c-menu-group-icon\"\n [ngStyle]=\"{\n backgroundColor: idMenuGroupActive() === nodeMenuGroup?.id ? _sidebar.brandLightColor || 'var(--sd-primary-light, #d7e3ff)' : 'transparent',\n }\">\n <img width=\"24px\" height=\"24px\" [src]=\"nodeMenuGroup.iconUrl\" [alt]=\"nodeMenuGroup.title\" />\n </span>\n } @else {\n <sd-icon class=\"c-menu-group-icon\"\n [ngStyle]=\"{\n color: idMenuGroupActive() === nodeMenuGroup?.id ? _sidebar.brandColor || 'var(--sd-primary, #005cbb)' : 'var(--sd-text-secondary, #44474f)',\n backgroundColor: idMenuGroupActive() === nodeMenuGroup?.id ? _sidebar.brandLightColor || 'var(--sd-primary-light, #d7e3ff)' : 'transparent',\n }\" [name]=\"nodeMenuGroup.icon || 'widgets'\"></sd-icon>\n }\n </button>\n }\n </div>\n <div class=\"c-menu-tree\">\n @if (totalMenuInMenusByGroup() >= 10) {\n <div style=\"padding: 0px 4px\" class=\"c-menu-tree-search\">\n <sd-input size=\"sm\" [placeholder]=\"'core.module.layout.sidebar.search' | translate\" [model]=\"searchText()\" (sdChange)=\"onFilterSearchText($event)\">\n <ng-template sdSuffixDef>\n @if (searchText()) {\n <sd-icon class=\"c-search-prefix-icon cancel\" (click)=\"onClearSearchText()\" name=\"cancel\"></sd-icon>\n } @else {\n <sd-icon class=\"c-search-prefix-icon search\" name=\"search\"></sd-icon>\n }\n </ng-template>\n </sd-input>\n </div>\n }\n <div class=\"c-menu-tree-container\">\n <mat-tree [dataSource]=\"dataSource\" [treeControl]=\"treeControl\" [style.backgroundColor]=\"'transparent'\">\n <mat-nested-tree-node *matTreeNodeDef=\"let node; when: !hasChild\">\n <li\n class=\"c-menu-node\"\n (mouseenter)=\"onMouseOverMenuNode($event, node)\"\n (mouseleave)=\"onMouseLeaveMenuNode($event, node)\"\n [ngStyle]=\"{\n backgroundColor: (currentPath() | menuFocus: node) ? _sidebar.brandLightColor || 'var(--sd-primary-light, #d7e3ff)' : 'transparent',\n }\">\n <div aria-hidden=\"true\" class=\"c-menu-node-description\" (click)=\"navigate(node)\" [class.d-none]=\"!isShowSidebar()\">\n <div\n class=\"c-menu-node-description-content\"\n [ngStyle]=\"{\n color: (currentPath() | menuFocus: node) ? _sidebar.brandColor || 'var(--sd-primary, #005cbb)' : 'var(--sd-text, #1a1b1f)',\n }\"\n [innerHTML]=\"node.title | highLightSearch: searchText() | sdSafeHtml\"></div>\n\n @if (_sidebar?.pin?.enabled) {\n <sd-icon class=\"c-menu-node-description-icon-pin\"\n [style.opacity]=\"isPinnedNode(node) || isHoveredNode(node) ? '1' : null\"\n [style.color]=\"isPinnedNode(node) || isHoveredNode(node) ? sidebar().brandColor || 'var(--sd-primary, #005cbb)' : null\"\n (click)=\"onTogglePin($event, node)\" name=\"push_pin\"></sd-icon>\n }\n </div>\n </li>\n </mat-nested-tree-node>\n\n <mat-nested-tree-node\n *matTreeNodeDef=\"let node; when: hasChild\"\n aria-hidden=\"true\"\n [class]=\"{ expanded: treeControl.isExpanded(node), isfocus: currentPath() | menuFocus: node }\">\n <li>\n <div\n class=\"c-menu-node\"\n (mouseenter)=\"onMouseOverMenuNode($event, node)\"\n (mouseleave)=\"onMouseLeaveMenuNode($event, node)\">\n <div class=\"d-flex align-items-center\" style=\"gap: 10px; width: 100%\">\n <div [class.d-none]=\"!isShowSidebar()\" class=\"c-menu-node-description\" (click)=\"onToggleMenuNode(node)\">\n <div\n class=\"c-menu-node-description-content\"\n [innerHTML]=\"node.title | highLightSearch: searchText() | sdSafeHtml\"></div>\n <sd-icon class=\"c-menu-node-description-icon-expand\" [name]=\"treeControl.isExpanded(node) ? 'keyboard_arrow_up' : 'keyboard_arrow_down'\"></sd-icon>\n </div>\n </div>\n </div>\n <ul class=\"p-0\" [class.d-none]=\"!treeControl.isExpanded(node)\">\n <ng-container matTreeNodeOutlet></ng-container>\n </ul>\n </li>\n </mat-nested-tree-node>\n </mat-tree>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"c-footer\">\n <lib-layout-user\n [userInfo]=\"_userInfo\"\n [isMobileOrTablet]=\"isMobileOrTablet\"\n (menuOpened)=\"onUserMenuOpened()\"\n (menuClosed)=\"onUserMenuClosed()\"\n [isShowSidebar]=\"isShowSidebar()\"\n (toggleMenuLock)=\"toggleMenuLock($event)\">\n </lib-layout-user>\n </div>\n\n <div class=\"c-vertical\"></div>\n </div>\n}\n", styles: ["::ng-deep .mat-mdc-tooltip-panel:has(.c-tooltip-menu-group-7a22ab15-0083-4d4c-9c0c-00a30bc8c140){pointer-events:none}:host ::ng-deep .mat-nested-tree-node ul .c-menu-node-description-content{margin-left:16px!important}:host ::ng-deep .mat-nested-tree-node ul .mat-nested-tree-node ul .c-menu-node-description-content{margin-left:32px!important}:host ::ng-deep .mat-nested-tree-node ul .mat-nested-tree-node ul .mat-nested-tree-node ul .c-menu-node-description-content{margin-left:48px!important}ul,li{margin-top:0;margin-bottom:0;list-style-type:none}.wrapper{display:flex;flex-direction:column;width:290px;background-color:#fff}.wrapper .c-header{display:flex;align-items:center;height:52px;padding:3px;gap:16px}.wrapper .c-header .c-logo{display:flex;justify-content:center;align-items:center;width:52px;height:52px}.wrapper .c-header .c-logo img{width:32px;height:32px;object-fit:cover}.wrapper .c-header .c-title-menu-group{display:flex;align-items:center;font-size:18px;font-weight:500;flex:1}.wrapper .c-body{flex:1;overflow-y:hidden}.wrapper .c-body .c-menu{height:100%;display:flex}.wrapper .c-body .c-menu .c-menu-group{display:flex;flex-direction:column;align-items:center;width:60px;overflow-y:scroll;scrollbar-width:none}.wrapper .c-body .c-menu .c-menu-group .c-menu-group-icon{display:flex;justify-content:center;align-items:center;min-height:50px;width:52px;border-radius:8px;transition:all .15s}.wrapper .c-body .c-menu .c-menu-group .c-menu-group-icon img{height:24px;width:24px;object-fit:contain}.wrapper .c-body .c-menu .c-menu-tree{flex:1;display:flex;flex-direction:column;padding:3px 4px 3px 3px}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-search .c-search-prefix-icon{cursor:pointer;color:#757575;padding:0}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-search .c-search-prefix-icon.search{width:20px;height:20px;font-size:18px}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-search .c-search-prefix-icon.cancel{width:16px;height:16px;font-size:16px}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-container{flex:1;overflow-y:scroll;scrollbar-width:none}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-container .c-menu-node{width:100%;display:flex;cursor:pointer;min-height:44px;padding:8px;border-radius:8px}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-container .c-menu-node .c-menu-node-icon{display:flex;justify-content:center;align-items:center;height:100%}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-container .c-menu-node .c-menu-node-description{flex:1;display:flex;align-items:center}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-container .c-menu-node .c-menu-node-description .c-menu-node-description-content{flex:1;display:flex;align-items:center;flex-wrap:wrap;white-space:pre-wrap;font-size:15px}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-container .c-menu-node .c-menu-node-description .c-menu-node-description-icon-expand{display:flex;align-items:center;justify-content:start;background-color:transparent;border:none;font-size:20px}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-container .c-menu-node .c-menu-node-description .c-menu-node-description-icon-pin{display:flex;align-items:center;justify-content:start;background-color:transparent;border:none;font-size:18px;opacity:0}.wrapper .c-footer{height:80px}.wrapper .c-vertical{position:fixed;height:100vh;left:58px;width:2px;background-color:#f8f9fa;pointer-events:none}\n"] }]
|
|
923
|
-
}], ctorParameters: () => [], propDecorators: { isShowSidebar: [{ type: i0.Input, args: [{ isSignal: true, alias: "isShowSidebar", required: true }] }], menus: [{ type: i0.Input, args: [{ isSignal: true, alias: "menus", required: true }] }], userInfo: [{ type: i0.Input, args: [{ isSignal: true, alias: "userInfo", required: true }] }], sidebar: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebar", required: true }] }], expandSideBar: [{ type: i0.Output, args: ["expandSideBar"] }], popupUserMenuClosed: [{ type: i0.Output, args: ["popupUserMenuClosed"] }], popupUserMenuOpened: [{ type: i0.Output, args: ["popupUserMenuOpened"] }], showSideBar: [{ type: i0.Output, args: ["showSideBar"] }] } });
|
|
1252
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "@let _userInfo = userInfo();\n@let _sidebar = sidebar();\n\n@if (_userInfo && _sidebar) {\n <div class=\"wrapper\" [style.height]=\"isMobile() ? '100dvh' : '100%'\">\n <div class=\"c-header\">\n <div class=\"c-logo\">\n <a href=\"javascript:;\" aria-label=\"Trang ch\u1EE7\" (click)=\"openHomePage()\">\n @if (logoUrl(); as _logoUrl) {\n <img alt=\"\" [src]=\"_logoUrl\" />\n } @else {\n <sd-icon name=\"apps\"></sd-icon>\n }\n </a>\n </div>\n <div class=\"c-title-menu-group\" [class.d-none]=\"!isShowSidebar()\">\n <span>{{ titleMenuGroup() || _sidebar.defaultTitle || 'Back Office' }}</span>\n </div>\n </div>\n <div class=\"c-body\">\n <div class=\"c-menu\">\n <div class=\"c-menu-group\">\n @if (_sidebar?.pin?.enabled && pinnedMenuGroup().children?.length) {\n <button\n (mouseenter)=\"onMouseOverMenuGroupNode($event, pinnedMenuGroup())\"\n (mouseleave)=\"onMouseLeaveMenuGroupNode($event, pinnedMenuGroup())\"\n (click)=\"expandPinnedGroup()\"\n [matTooltip]=\"'\u0110\u00E3 ghim'\"\n [matTooltipClass]=\"'c-tooltip-menu-group-7a22ab15-0083-4d4c-9c0c-00a30bc8c140'\"\n [matTooltipPosition]=\"'right'\"\n style=\"padding: 0; margin: 0; border: none; background-color: transparent\">\n <sd-icon\n class=\"c-menu-group-icon\"\n [ngStyle]=\"{\n color: isPinnedMenuGroupActive()\n ? _sidebar.brandColor || 'var(--sd-primary, #005cbb)'\n : 'var(--sd-text-secondary, #44474f)',\n backgroundColor: isPinnedMenuGroupActive()\n ? _sidebar.brandLightColor || 'var(--sd-primary-light, #d7e3ff)'\n : 'transparent',\n }\"\n name=\"push_pin\"></sd-icon>\n </button>\n }\n @for (nodeMenuGroup of menus(); track nodeMenuGroup.id) {\n <button\n (mouseenter)=\"onMouseOverMenuGroupNode($event, nodeMenuGroup)\"\n (mouseleave)=\"onMouseLeaveMenuGroupNode($event, nodeMenuGroup)\"\n (click)=\"expandMenuGroup(nodeMenuGroup)\"\n [matTooltip]=\"nodeMenuGroup.tooltipTitle || nodeMenuGroup.title\"\n [matTooltipClass]=\"'c-tooltip-menu-group-7a22ab15-0083-4d4c-9c0c-00a30bc8c140'\"\n [matTooltipPosition]=\"'right'\"\n style=\"padding: 0; margin: 0; border: none; background-color: transparent\">\n @if (nodeMenuGroup.iconUrl) {\n <span\n class=\"c-menu-group-icon\"\n [ngStyle]=\"{\n backgroundColor:\n idMenuGroupActive() === nodeMenuGroup?.id\n ? _sidebar.brandLightColor || 'var(--sd-primary-light, #d7e3ff)'\n : 'transparent',\n }\">\n <img width=\"24px\" height=\"24px\" [src]=\"nodeMenuGroup.iconUrl\" [alt]=\"nodeMenuGroup.title\" />\n </span>\n } @else {\n <sd-icon\n class=\"c-menu-group-icon\"\n [ngStyle]=\"{\n color:\n idMenuGroupActive() === nodeMenuGroup?.id\n ? _sidebar.brandColor || 'var(--sd-primary, #005cbb)'\n : 'var(--sd-text-secondary, #44474f)',\n backgroundColor:\n idMenuGroupActive() === nodeMenuGroup?.id\n ? _sidebar.brandLightColor || 'var(--sd-primary-light, #d7e3ff)'\n : 'transparent',\n }\"\n [name]=\"nodeMenuGroup.icon || 'widgets'\"></sd-icon>\n }\n </button>\n }\n </div>\n <div class=\"c-menu-tree\" [class.d-none]=\"!isShowSidebar()\" [attr.inert]=\"!isShowSidebar() ? '' : null\">\n @if (totalMenuInMenusByGroup() > 10) {\n <div style=\"padding: 0px 4px\" class=\"c-menu-tree-search\">\n <sd-input\n size=\"sm\"\n [placeholder]=\"'core.module.layout.sidebar.search' | translate\"\n [autoId]=\"'layout-v1-menu-search'\"\n [model]=\"searchText()\"\n (sdChange)=\"onFilterSearchText($event)\">\n <ng-template sdSuffixDef>\n @if (searchText()) {\n <sd-icon class=\"c-search-prefix-icon cancel\" (click)=\"onClearSearchText()\" name=\"cancel\"></sd-icon>\n } @else {\n <sd-icon class=\"c-search-prefix-icon search\" name=\"search\"></sd-icon>\n }\n </ng-template>\n </sd-input>\n </div>\n }\n <div class=\"c-menu-tree-container\">\n <mat-tree [dataSource]=\"dataSource\" [treeControl]=\"treeControl\" [style.backgroundColor]=\"'transparent'\">\n <mat-nested-tree-node *matTreeNodeDef=\"let node; when: !hasChild\">\n <li\n class=\"c-menu-node\"\n (mouseenter)=\"onMouseOverMenuNode($event, node)\"\n (mouseleave)=\"onMouseLeaveMenuNode($event, node)\"\n [ngStyle]=\"{\n backgroundColor:\n (currentPath() | menuFocus: node) ? _sidebar.brandLightColor || 'var(--sd-primary-light, #d7e3ff)' : 'transparent',\n }\">\n <div aria-hidden=\"true\" class=\"c-menu-node-description\" (click)=\"navigate(node)\" [class.d-none]=\"!isShowSidebar()\">\n <div\n class=\"c-menu-node-description-content\"\n [ngStyle]=\"{\n color:\n (currentPath() | menuFocus: node)\n ? _sidebar.brandColor || 'var(--sd-primary, #005cbb)'\n : 'var(--sd-text, #1a1b1f)',\n }\"\n [innerHTML]=\"node.title | highLightSearch: searchText() | sdSafeHtml\"></div>\n\n @if (_sidebar?.pin?.enabled) {\n <sd-icon\n class=\"c-menu-node-description-icon-pin\"\n [style.opacity]=\"isPinnedNode(node) || isHoveredNode(node) ? '1' : null\"\n [style.color]=\"\n isPinnedNode(node) || isHoveredNode(node) ? sidebar().brandColor || 'var(--sd-primary, #005cbb)' : null\n \"\n (click)=\"onTogglePin($event, node)\"\n name=\"push_pin\"></sd-icon>\n }\n </div>\n </li>\n </mat-nested-tree-node>\n\n <mat-nested-tree-node\n *matTreeNodeDef=\"let node; when: hasChild\"\n aria-hidden=\"true\"\n [class]=\"{ expanded: treeControl.isExpanded(node), isfocus: currentPath() | menuFocus: node }\">\n <li>\n <div\n class=\"c-menu-node\"\n (mouseenter)=\"onMouseOverMenuNode($event, node)\"\n (mouseleave)=\"onMouseLeaveMenuNode($event, node)\">\n <div class=\"d-flex align-items-center\" style=\"gap: 10px; width: 100%\">\n <div [class.d-none]=\"!isShowSidebar()\" class=\"c-menu-node-description\" (click)=\"onToggleMenuNode(node)\">\n <div\n class=\"c-menu-node-description-content\"\n [innerHTML]=\"node.title | highLightSearch: searchText() | sdSafeHtml\"></div>\n <sd-icon\n class=\"c-menu-node-description-icon-expand\"\n [name]=\"treeControl.isExpanded(node) ? 'keyboard_arrow_up' : 'keyboard_arrow_down'\"></sd-icon>\n </div>\n </div>\n </div>\n <ul class=\"p-0\" [class.d-none]=\"!treeControl.isExpanded(node)\">\n <ng-container matTreeNodeOutlet></ng-container>\n </ul>\n </li>\n </mat-nested-tree-node>\n </mat-tree>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"c-footer\">\n <lib-layout-user\n [userInfo]=\"_userInfo\"\n [isMobileOrTablet]=\"isMobile()\"\n (menuOpened)=\"onUserMenuOpened()\"\n (menuClosed)=\"onUserMenuClosed()\"\n [isShowSidebar]=\"isShowSidebar()\"\n (toggleMenuLock)=\"toggleMenuLock($event)\">\n </lib-layout-user>\n </div>\n\n <div class=\"c-vertical\"></div>\n </div>\n}\n", styles: ["::ng-deep .mat-mdc-tooltip-panel:has(.c-tooltip-menu-group-7a22ab15-0083-4d4c-9c0c-00a30bc8c140){pointer-events:none}:host ::ng-deep .mat-nested-tree-node ul .c-menu-node-description-content{margin-left:16px!important}:host ::ng-deep .mat-nested-tree-node ul .mat-nested-tree-node ul .c-menu-node-description-content{margin-left:32px!important}:host ::ng-deep .mat-nested-tree-node ul .mat-nested-tree-node ul .mat-nested-tree-node ul .c-menu-node-description-content{margin-left:48px!important}ul,li{margin-top:0;margin-bottom:0;list-style-type:none}.wrapper{display:flex;flex-direction:column;width:290px;background-color:#fff}.wrapper .c-header{display:flex;align-items:center;height:52px;padding:3px;gap:16px}.wrapper .c-header .c-logo{display:flex;justify-content:center;align-items:center;width:52px;height:52px}.wrapper .c-header .c-logo a{display:flex;align-items:center;justify-content:center;width:100%;height:100%;color:var(--sd-text-secondary, #44474f)}.wrapper .c-header .c-logo img{width:32px;height:32px;object-fit:contain}.wrapper .c-header .c-title-menu-group{display:flex;align-items:center;font-size:18px;font-weight:500;flex:1}.wrapper .c-body{flex:1;overflow-y:hidden}.wrapper .c-body .c-menu{height:100%;display:flex}.wrapper .c-body .c-menu .c-menu-group{display:flex;flex-direction:column;align-items:center;flex:0 0 60px;min-width:60px;width:60px;overflow-y:scroll;scrollbar-width:none}.wrapper .c-body .c-menu .c-menu-group .c-menu-group-icon{display:flex;justify-content:center;align-items:center;min-height:50px;width:52px;border-radius:8px;transition:all .15s}.wrapper .c-body .c-menu .c-menu-group .c-menu-group-icon img{height:24px;width:24px;object-fit:contain}.wrapper .c-body .c-menu .c-menu-tree{flex:1;display:flex;flex-direction:column;padding:3px 4px 3px 3px}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-search .c-search-prefix-icon{cursor:pointer;color:#757575;padding:0}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-search .c-search-prefix-icon.search{width:20px;height:20px;font-size:18px}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-search .c-search-prefix-icon.cancel{width:16px;height:16px;font-size:16px}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-container{flex:1;overflow-y:scroll;scrollbar-width:none}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-container .c-menu-node{width:100%;display:flex;cursor:pointer;min-height:44px;padding:8px;border-radius:8px}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-container .c-menu-node .c-menu-node-icon{display:flex;justify-content:center;align-items:center;height:100%}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-container .c-menu-node .c-menu-node-description{flex:1;display:flex;align-items:center}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-container .c-menu-node .c-menu-node-description .c-menu-node-description-content{flex:1;display:flex;align-items:center;flex-wrap:wrap;white-space:pre-wrap;font-size:15px}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-container .c-menu-node .c-menu-node-description .c-menu-node-description-icon-expand{display:flex;align-items:center;justify-content:start;background-color:transparent;border:none;font-size:20px}.wrapper .c-body .c-menu .c-menu-tree .c-menu-tree-container .c-menu-node .c-menu-node-description .c-menu-node-description-icon-pin{display:flex;align-items:center;justify-content:start;background-color:transparent;border:none;font-size:18px;opacity:0}.wrapper .c-footer{height:80px}.wrapper .c-vertical{position:fixed;height:100vh;left:58px;width:2px;background-color:#f8f9fa;pointer-events:none}:host ::ng-deep .c-menu-tree-search input:focus-visible{outline:1px solid var(--sd-primary, #005cbb);outline-offset:1px}\n"] }]
|
|
1253
|
+
}], ctorParameters: () => [], propDecorators: { isShowSidebar: [{ type: i0.Input, args: [{ isSignal: true, alias: "isShowSidebar", required: true }] }], menus: [{ type: i0.Input, args: [{ isSignal: true, alias: "menus", required: true }] }], userInfo: [{ type: i0.Input, args: [{ isSignal: true, alias: "userInfo", required: true }] }], sidebar: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebar", required: true }] }], isMobile: [{ type: i0.Input, args: [{ isSignal: true, alias: "isMobile", required: false }] }], expandSideBar: [{ type: i0.Output, args: ["expandSideBar"] }], popupUserMenuClosed: [{ type: i0.Output, args: ["popupUserMenuClosed"] }], popupUserMenuOpened: [{ type: i0.Output, args: ["popupUserMenuOpened"] }], showSideBar: [{ type: i0.Output, args: ["showSideBar"] }] } });
|
|
924
1254
|
|
|
925
1255
|
class SidebarV1Component {
|
|
926
1256
|
// ==========================================
|
|
@@ -934,14 +1264,14 @@ class SidebarV1Component {
|
|
|
934
1264
|
menus = input([], ...(ngDevMode ? [{ debugName: "menus" }] : []));
|
|
935
1265
|
userInfo = input.required(...(ngDevMode ? [{ debugName: "userInfo" }] : []));
|
|
936
1266
|
sidebar = input.required(...(ngDevMode ? [{ debugName: "sidebar" }] : []));
|
|
1267
|
+
isMobile = input(false, ...(ngDevMode ? [{ debugName: "isMobile" }] : []));
|
|
937
1268
|
// ==========================================
|
|
938
1269
|
// STATE SIGNALS
|
|
939
1270
|
// ==========================================
|
|
940
|
-
isMobileOrTablet = signal(BrowserUtilities.isMobile(), ...(ngDevMode ? [{ debugName: "isMobileOrTablet" }] : []));
|
|
941
1271
|
// Trạng thái khóa menu (luôn mở rộng hay không)
|
|
942
1272
|
isMenuLock = signal(this.#layoutStorageService.menuLockStatus?.get() ?? true, ...(ngDevMode ? [{ debugName: "isMenuLock" }] : []));
|
|
943
1273
|
// Trạng thái hiển thị (mở/đóng) của Sidenav
|
|
944
|
-
isShowSidebar = signal(
|
|
1274
|
+
isShowSidebar = signal(this.isMobile() ? false : (this.#layoutStorageService.menuLockStatus?.get() ?? true), ...(ngDevMode ? [{ debugName: "isShowSidebar" }] : []));
|
|
945
1275
|
onhover = signal(false, ...(ngDevMode ? [{ debugName: "onhover" }] : []));
|
|
946
1276
|
// ==========================================
|
|
947
1277
|
// PRIVATE TIMERS
|
|
@@ -983,7 +1313,7 @@ class SidebarV1Component {
|
|
|
983
1313
|
}
|
|
984
1314
|
};
|
|
985
1315
|
openSidebar = () => {
|
|
986
|
-
if (!this.
|
|
1316
|
+
if (!this.isMobile()) {
|
|
987
1317
|
return;
|
|
988
1318
|
}
|
|
989
1319
|
if (!this.isShowSidebar()) {
|
|
@@ -1005,7 +1335,7 @@ class SidebarV1Component {
|
|
|
1005
1335
|
}
|
|
1006
1336
|
};
|
|
1007
1337
|
onExpandSideBar = () => {
|
|
1008
|
-
if (this.
|
|
1338
|
+
if (this.isMobile()) {
|
|
1009
1339
|
return;
|
|
1010
1340
|
}
|
|
1011
1341
|
if (!this.isShowSidebar()) {
|
|
@@ -1014,11 +1344,16 @@ class SidebarV1Component {
|
|
|
1014
1344
|
}
|
|
1015
1345
|
};
|
|
1016
1346
|
onMouseleaveSideBar = () => {
|
|
1017
|
-
if (this.
|
|
1347
|
+
if (this.isMobile()) {
|
|
1018
1348
|
return;
|
|
1019
1349
|
}
|
|
1020
1350
|
this.#mouseLeave();
|
|
1021
1351
|
};
|
|
1352
|
+
onSidenavOpenedChange = (opened) => {
|
|
1353
|
+
if (this.isMobile()) {
|
|
1354
|
+
this.isShowSidebar.set(opened);
|
|
1355
|
+
}
|
|
1356
|
+
};
|
|
1022
1357
|
onToggle = (data) => {
|
|
1023
1358
|
if (data === null) {
|
|
1024
1359
|
this.#handleMouseLeaveTransition();
|
|
@@ -1027,34 +1362,29 @@ class SidebarV1Component {
|
|
|
1027
1362
|
this.#mouseLeave();
|
|
1028
1363
|
this.isShowSidebar.set(data);
|
|
1029
1364
|
this.isMenuLock.set(data);
|
|
1030
|
-
//
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
if (
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1365
|
+
// Desktop keeps the Material drawer mounted as a 60px rail; mobile still opens and closes the overlay.
|
|
1366
|
+
if (this.isMobile()) {
|
|
1367
|
+
const sidenavComp = this.sidenav();
|
|
1368
|
+
if (sidenavComp) {
|
|
1369
|
+
if (data) {
|
|
1370
|
+
sidenavComp.open();
|
|
1371
|
+
}
|
|
1372
|
+
else {
|
|
1373
|
+
sidenavComp.close();
|
|
1374
|
+
}
|
|
1038
1375
|
}
|
|
1039
1376
|
}
|
|
1040
1377
|
}
|
|
1041
1378
|
};
|
|
1042
1379
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SidebarV1Component, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1043
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.3.25", type: SidebarV1Component, isStandalone: true, selector: "sidebar-v1", inputs: { menus: { classPropertyName: "menus", publicName: "menus", isSignal: true, isRequired: false, transformFunction: null }, userInfo: { classPropertyName: "userInfo", publicName: "userInfo", isSignal: true, isRequired: true, transformFunction: null }, sidebar: { classPropertyName: "sidebar", publicName: "sidebar", isSignal: true, isRequired: true, transformFunction: null } }, viewQueries: [{ propertyName: "sidenav", first: true, predicate: ["sidenav"], descendants: true, isSignal: true }], ngImport: i0, template: "<mat-sidenav-container class=\"c-layout-wrapper\">\n <mat-sidenav\n #sidenav\n [opened]=\"isShowSidebar()\"\n (openedChange)=\"
|
|
1380
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.3.25", type: SidebarV1Component, isStandalone: true, selector: "sidebar-v1", inputs: { menus: { classPropertyName: "menus", publicName: "menus", isSignal: true, isRequired: false, transformFunction: null }, userInfo: { classPropertyName: "userInfo", publicName: "userInfo", isSignal: true, isRequired: true, transformFunction: null }, sidebar: { classPropertyName: "sidebar", publicName: "sidebar", isSignal: true, isRequired: true, transformFunction: null }, isMobile: { classPropertyName: "isMobile", publicName: "isMobile", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "sidenav", first: true, predicate: ["sidenav"], descendants: true, isSignal: true }], ngImport: i0, template: "<mat-sidenav-container class=\"c-layout-wrapper\">\n <mat-sidenav\n #sidenav\n [opened]=\"!isMobile() || isShowSidebar()\"\n (openedChange)=\"onSidenavOpenedChange($event)\"\n class=\"c-layout-sidebar\"\n [class.is-expanded]=\"isShowSidebar()\"\n (click)=\"openSidebar()\"\n (mouseleave)=\"onMouseleaveSideBar()\"\n [mode]=\"isMobile() ? 'over' : 'side'\"\n [fixedInViewport]=\"isMobile()\">\n <sidebar\n [menus]=\"menus()\"\n [userInfo]=\"userInfo()\"\n [sidebar]=\"sidebar()\"\n [isMobile]=\"isMobile()\"\n [isShowSidebar]=\"isShowSidebar()\"\n (showSideBar)=\"onToggle($event)\"\n (expandSideBar)=\"onExpandSideBar()\"\n (popupUserMenuOpened)=\"onPopupOfSideBarOpened()\"\n (popupUserMenuClosed)=\"onPopupOfSideBarClosed()\">\n </sidebar>\n </mat-sidenav>\n\n <mat-sidenav-content class=\"c-layout-content\" [class.isShowSidebar]=\"isMenuLock()\">\n <mat-drawer-container class=\"c-mat-drawer-container\">\n <mat-drawer-content>\n <ng-content></ng-content>\n </mat-drawer-content>\n </mat-drawer-container>\n </mat-sidenav-content>\n</mat-sidenav-container>\n", styles: [":host ::ng-deep .c-layout-wrapper{width:100%}:host ::ng-deep .c-layout-wrapper .c-layout-sidebar{margin-top:0;width:60px;height:100vh!important;transform:none!important;overflow:clip!important;visibility:visible!important;transition:width .15s!important;box-shadow:0 0 6px #0003!important}:host ::ng-deep .c-layout-wrapper .c-layout-sidebar .mat-drawer-inner-container{position:relative;overflow:visible;display:block!important}:host ::ng-deep .c-layout-wrapper .c-layout-sidebar.is-expanded{width:290px;transition:width .15s}:host ::ng-deep .c-layout-wrapper .c-layout-content{background-color:#fff;box-sizing:border-box;height:100vh!important;margin-left:60px!important}:host ::ng-deep .c-layout-wrapper .c-layout-content.isShowSidebar{margin-left:290px!important}:host ::ng-deep .c-layout-wrapper .c-layout-content .c-mat-drawer-container{height:100%}\n"], dependencies: [{ kind: "ngmodule", type: MatSidenavModule }, { kind: "component", type: i1$1.MatDrawerContainer, selector: "mat-drawer-container", inputs: ["autosize", "hasBackdrop"], outputs: ["backdropClick"], exportAs: ["matDrawerContainer"] }, { kind: "component", type: i1$1.MatDrawerContent, selector: "mat-drawer-content" }, { kind: "component", type: i1$1.MatSidenav, selector: "mat-sidenav", inputs: ["fixedInViewport", "fixedTopGap", "fixedBottomGap"], exportAs: ["matSidenav"] }, { kind: "component", type: i1$1.MatSidenavContainer, selector: "mat-sidenav-container", exportAs: ["matSidenavContainer"] }, { kind: "component", type: i1$1.MatSidenavContent, selector: "mat-sidenav-content" }, { kind: "ngmodule", type: CommonModule }, { kind: "component", type: SidebarComponent, selector: "sidebar", inputs: ["isShowSidebar", "menus", "userInfo", "sidebar", "isMobile"], outputs: ["expandSideBar", "popupUserMenuClosed", "popupUserMenuOpened", "showSideBar"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1044
1381
|
}
|
|
1045
1382
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SidebarV1Component, decorators: [{
|
|
1046
1383
|
type: Component,
|
|
1047
|
-
args: [{ selector: 'sidebar-v1', imports: [MatSidenavModule, CommonModule, SidebarComponent], standalone: true, template: "<mat-sidenav-container class=\"c-layout-wrapper\">\n <mat-sidenav\n #sidenav\n [opened]=\"isShowSidebar()\"\n (openedChange)=\"
|
|
1048
|
-
}], ctorParameters: () => [], propDecorators: { sidenav: [{ type: i0.ViewChild, args: ['sidenav', { isSignal: true }] }], menus: [{ type: i0.Input, args: [{ isSignal: true, alias: "menus", required: false }] }], userInfo: [{ type: i0.Input, args: [{ isSignal: true, alias: "userInfo", required: true }] }], sidebar: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebar", required: true }] }] } });
|
|
1384
|
+
args: [{ selector: 'sidebar-v1', imports: [MatSidenavModule, CommonModule, SidebarComponent], standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<mat-sidenav-container class=\"c-layout-wrapper\">\n <mat-sidenav\n #sidenav\n [opened]=\"!isMobile() || isShowSidebar()\"\n (openedChange)=\"onSidenavOpenedChange($event)\"\n class=\"c-layout-sidebar\"\n [class.is-expanded]=\"isShowSidebar()\"\n (click)=\"openSidebar()\"\n (mouseleave)=\"onMouseleaveSideBar()\"\n [mode]=\"isMobile() ? 'over' : 'side'\"\n [fixedInViewport]=\"isMobile()\">\n <sidebar\n [menus]=\"menus()\"\n [userInfo]=\"userInfo()\"\n [sidebar]=\"sidebar()\"\n [isMobile]=\"isMobile()\"\n [isShowSidebar]=\"isShowSidebar()\"\n (showSideBar)=\"onToggle($event)\"\n (expandSideBar)=\"onExpandSideBar()\"\n (popupUserMenuOpened)=\"onPopupOfSideBarOpened()\"\n (popupUserMenuClosed)=\"onPopupOfSideBarClosed()\">\n </sidebar>\n </mat-sidenav>\n\n <mat-sidenav-content class=\"c-layout-content\" [class.isShowSidebar]=\"isMenuLock()\">\n <mat-drawer-container class=\"c-mat-drawer-container\">\n <mat-drawer-content>\n <ng-content></ng-content>\n </mat-drawer-content>\n </mat-drawer-container>\n </mat-sidenav-content>\n</mat-sidenav-container>\n", styles: [":host ::ng-deep .c-layout-wrapper{width:100%}:host ::ng-deep .c-layout-wrapper .c-layout-sidebar{margin-top:0;width:60px;height:100vh!important;transform:none!important;overflow:clip!important;visibility:visible!important;transition:width .15s!important;box-shadow:0 0 6px #0003!important}:host ::ng-deep .c-layout-wrapper .c-layout-sidebar .mat-drawer-inner-container{position:relative;overflow:visible;display:block!important}:host ::ng-deep .c-layout-wrapper .c-layout-sidebar.is-expanded{width:290px;transition:width .15s}:host ::ng-deep .c-layout-wrapper .c-layout-content{background-color:#fff;box-sizing:border-box;height:100vh!important;margin-left:60px!important}:host ::ng-deep .c-layout-wrapper .c-layout-content.isShowSidebar{margin-left:290px!important}:host ::ng-deep .c-layout-wrapper .c-layout-content .c-mat-drawer-container{height:100%}\n"] }]
|
|
1385
|
+
}], ctorParameters: () => [], propDecorators: { sidenav: [{ type: i0.ViewChild, args: ['sidenav', { isSignal: true }] }], menus: [{ type: i0.Input, args: [{ isSignal: true, alias: "menus", required: false }] }], userInfo: [{ type: i0.Input, args: [{ isSignal: true, alias: "userInfo", required: true }] }], sidebar: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebar", required: true }] }], isMobile: [{ type: i0.Input, args: [{ isSignal: true, alias: "isMobile", required: false }] }] } });
|
|
1049
1386
|
|
|
1050
1387
|
class LayoutUserComponent {
|
|
1051
|
-
// ==========================================
|
|
1052
|
-
// INJECT SERVICES
|
|
1053
|
-
// ==========================================
|
|
1054
|
-
#layoutConfiguration = inject(SD_LAYOUT_CONFIGURATION);
|
|
1055
|
-
// ==========================================
|
|
1056
|
-
// SIGNAL INPUTS & OUTPUTS
|
|
1057
|
-
// ==========================================
|
|
1058
1388
|
isMobileOrTablet = input(false, ...(ngDevMode ? [{ debugName: "isMobileOrTablet" }] : []));
|
|
1059
1389
|
isMenuLock = input(false, ...(ngDevMode ? [{ debugName: "isMenuLock" }] : []));
|
|
1060
1390
|
isShowSidebar = input(false, ...(ngDevMode ? [{ debugName: "isShowSidebar" }] : []));
|
|
@@ -1062,47 +1392,21 @@ class LayoutUserComponent {
|
|
|
1062
1392
|
menuClosed = output();
|
|
1063
1393
|
menuOpened = output();
|
|
1064
1394
|
toggleMenuLock = output();
|
|
1065
|
-
|
|
1066
|
-
singoutLayoutConfig = this.#layoutConfiguration.signout;
|
|
1067
|
-
changePasswordLayoutConfig = this.#layoutConfiguration?.changePassword;
|
|
1068
|
-
// Accordion state for mobile inline expand
|
|
1069
|
-
isExpanded = signal(false, ...(ngDevMode ? [{ debugName: "isExpanded" }] : []));
|
|
1070
|
-
// ==========================================
|
|
1071
|
-
// EVENT HANDLERS
|
|
1072
|
-
// ==========================================
|
|
1073
|
-
onMenuOpened = () => {
|
|
1395
|
+
onMenuOpened() {
|
|
1074
1396
|
this.menuOpened.emit();
|
|
1075
|
-
}
|
|
1076
|
-
onMenuClosed
|
|
1397
|
+
}
|
|
1398
|
+
onMenuClosed() {
|
|
1077
1399
|
this.menuClosed.emit();
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
|
-
event.stopPropagation();
|
|
1081
|
-
};
|
|
1082
|
-
toggleExpanded = () => {
|
|
1083
|
-
this.isExpanded.update(v => !v);
|
|
1084
|
-
if (this.isExpanded()) {
|
|
1085
|
-
this.menuOpened.emit();
|
|
1086
|
-
}
|
|
1087
|
-
else {
|
|
1088
|
-
this.menuClosed.emit();
|
|
1089
|
-
}
|
|
1090
|
-
};
|
|
1091
|
-
logout = () => {
|
|
1092
|
-
this.singoutLayoutConfig();
|
|
1093
|
-
};
|
|
1094
|
-
changePassword = () => {
|
|
1095
|
-
this.changePasswordLayoutConfig?.();
|
|
1096
|
-
};
|
|
1097
|
-
onToggleMenuLock = (event) => {
|
|
1400
|
+
}
|
|
1401
|
+
onToggleMenuLock(event) {
|
|
1098
1402
|
this.toggleMenuLock.emit(event);
|
|
1099
|
-
}
|
|
1403
|
+
}
|
|
1100
1404
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: LayoutUserComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1101
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: LayoutUserComponent, isStandalone: true, selector: "lib-layout-user", inputs: { isMobileOrTablet: { classPropertyName: "isMobileOrTablet", publicName: "isMobileOrTablet", isSignal: true, isRequired: false, transformFunction: null }, isMenuLock: { classPropertyName: "isMenuLock", publicName: "isMenuLock", isSignal: true, isRequired: false, transformFunction: null }, isShowSidebar: { classPropertyName: "isShowSidebar", publicName: "isShowSidebar", isSignal: true, isRequired: false, transformFunction: null }, userInfo: { classPropertyName: "userInfo", publicName: "userInfo", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { menuClosed: "menuClosed", menuOpened: "menuOpened", toggleMenuLock: "toggleMenuLock" }, ngImport: i0, template: "@
|
|
1405
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: LayoutUserComponent, isStandalone: true, selector: "lib-layout-user", inputs: { isMobileOrTablet: { classPropertyName: "isMobileOrTablet", publicName: "isMobileOrTablet", isSignal: true, isRequired: false, transformFunction: null }, isMenuLock: { classPropertyName: "isMenuLock", publicName: "isMenuLock", isSignal: true, isRequired: false, transformFunction: null }, isShowSidebar: { classPropertyName: "isShowSidebar", publicName: "isShowSidebar", isSignal: true, isRequired: false, transformFunction: null }, userInfo: { classPropertyName: "userInfo", publicName: "userInfo", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { menuClosed: "menuClosed", menuOpened: "menuOpened", toggleMenuLock: "toggleMenuLock" }, ngImport: i0, template: "@if (isMobileOrTablet()) {\n <sd-layout-user-menu [userInfo]=\"userInfo()\" presentation=\"mobile\"></sd-layout-user-menu>\n} @else {\n <div class=\"c-layout-user-wrapper\">\n <sd-layout-user-menu\n class=\"c-layout-user-avatar\"\n [userInfo]=\"userInfo()\"\n [compact]=\"true\"\n (opened)=\"onMenuOpened()\"\n (closed)=\"onMenuClosed()\"></sd-layout-user-menu>\n\n <button\n type=\"button\"\n class=\"c-layout-icon-toggle\"\n [attr.aria-label]=\"'core.module.layout.sidebar.toggle' | translate\"\n (click)=\"onToggleMenuLock($event)\">\n <sd-icon [name]=\"isShowSidebar() ? 'keyboard_arrow_right' : 'keyboard_arrow_left'\"></sd-icon>\n </button>\n </div>\n}\n", styles: [":host{display:block;width:100%}.c-layout-user-wrapper{display:flex;flex-direction:column;align-items:center;width:60px;height:80px}.c-layout-user-avatar{display:block;width:56px;height:52px}.c-layout-icon-toggle{display:flex;align-items:center;justify-content:center;width:32px;height:28px;margin:0;padding:0;border:0;border-top:1px solid var(--sd-black200, #f0f0f0);background:transparent;color:var(--sd-black400, #8c8c8c);cursor:pointer}.c-layout-icon-toggle:focus-visible{outline:2px solid var(--sd-primary);outline-offset:2px}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "component", type: SdLayoutUserMenuComponent, selector: "sd-layout-user-menu", inputs: ["userInfo", "signout", "changePassword", "updateProfile", "setting", "notification", "compact", "presentation"], outputs: ["opened", "closed"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1102
1406
|
}
|
|
1103
1407
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: LayoutUserComponent, decorators: [{
|
|
1104
1408
|
type: Component,
|
|
1105
|
-
args: [{ selector: 'lib-layout-user', imports: [SdIcon,
|
|
1409
|
+
args: [{ selector: 'lib-layout-user', imports: [SdIcon, SdLayoutUserMenuComponent, TranslatePipe], standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (isMobileOrTablet()) {\n <sd-layout-user-menu [userInfo]=\"userInfo()\" presentation=\"mobile\"></sd-layout-user-menu>\n} @else {\n <div class=\"c-layout-user-wrapper\">\n <sd-layout-user-menu\n class=\"c-layout-user-avatar\"\n [userInfo]=\"userInfo()\"\n [compact]=\"true\"\n (opened)=\"onMenuOpened()\"\n (closed)=\"onMenuClosed()\"></sd-layout-user-menu>\n\n <button\n type=\"button\"\n class=\"c-layout-icon-toggle\"\n [attr.aria-label]=\"'core.module.layout.sidebar.toggle' | translate\"\n (click)=\"onToggleMenuLock($event)\">\n <sd-icon [name]=\"isShowSidebar() ? 'keyboard_arrow_right' : 'keyboard_arrow_left'\"></sd-icon>\n </button>\n </div>\n}\n", styles: [":host{display:block;width:100%}.c-layout-user-wrapper{display:flex;flex-direction:column;align-items:center;width:60px;height:80px}.c-layout-user-avatar{display:block;width:56px;height:52px}.c-layout-icon-toggle{display:flex;align-items:center;justify-content:center;width:32px;height:28px;margin:0;padding:0;border:0;border-top:1px solid var(--sd-black200, #f0f0f0);background:transparent;color:var(--sd-black400, #8c8c8c);cursor:pointer}.c-layout-icon-toggle:focus-visible{outline:2px solid var(--sd-primary);outline-offset:2px}\n"] }]
|
|
1106
1410
|
}], propDecorators: { isMobileOrTablet: [{ type: i0.Input, args: [{ isSignal: true, alias: "isMobileOrTablet", required: false }] }], isMenuLock: [{ type: i0.Input, args: [{ isSignal: true, alias: "isMenuLock", required: false }] }], isShowSidebar: [{ type: i0.Input, args: [{ isSignal: true, alias: "isShowSidebar", required: false }] }], userInfo: [{ type: i0.Input, args: [{ isSignal: true, alias: "userInfo", required: true }] }], menuClosed: [{ type: i0.Output, args: ["menuClosed"] }], menuOpened: [{ type: i0.Output, args: ["menuOpened"] }], toggleMenuLock: [{ type: i0.Output, args: ["toggleMenuLock"] }] } });
|
|
1107
1411
|
|
|
1108
1412
|
class SidebarMobileOverlayComponent {
|
|
@@ -1296,12 +1600,513 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
1296
1600
|
args: [{ selector: 'sidebar-mobile-v1', imports: [SdIcon, CommonModule, SidebarMobileOverlayComponent], standalone: true, template: "<div class=\"c-mobile-layout\">\n <div class=\"c-mobile-topbar\">\n <div class=\"c-topbar-brand\">\n @if (sidebar().logoUrl) {\n <img class=\"c-topbar-logo\" [src]=\"sidebar().logoUrl\" alt=\"logo\" />\n }\n <span class=\"c-topbar-title\">{{ titleMenuGroup() || sidebar().defaultTitle || 'Back Office' }}</span>\n </div>\n <button class=\"c-hamburger-btn\" (click)=\"openSidebar()\">\n <sd-icon name=\"menu\"></sd-icon>\n </button>\n </div>\n\n <div class=\"c-mobile-content\">\n <ng-content></ng-content>\n </div>\n\n <sd-sidebar-mobile-overlay\n [menus]=\"menus()\"\n [userInfo]=\"userInfo()\"\n [sidebar]=\"sidebar()\"\n [isShowSidebar]=\"isShowSidebar()\"\n (showSideBar)=\"onToggle($event)\"\n (expandSideBar)=\"onExpandSideBar()\"\n (popupUserMenuOpened)=\"onPopupOfSideBarOpened()\"\n (popupUserMenuClosed)=\"onPopupOfSideBarClosed()\"\n (titleMenuGroupChanged)=\"onTitleMenuGroupChanged($event)\">\n </sd-sidebar-mobile-overlay>\n</div>\n", styles: [":host{display:block;height:100vh;position:relative;overflow:hidden}.c-mobile-layout{display:flex;flex-direction:column;height:100%;position:relative}.c-mobile-topbar{display:flex;align-items:center;justify-content:space-between;padding:0 16px;height:56px;background-color:#fff;border-bottom:1px solid #e0e0e0;flex-shrink:0}.c-mobile-topbar .c-topbar-brand{display:flex;align-items:center;gap:10px;overflow:hidden}.c-mobile-topbar .c-topbar-brand .c-topbar-logo{width:28px;height:28px;object-fit:contain;flex-shrink:0}.c-mobile-topbar .c-topbar-brand .c-topbar-title{font-size:16px;font-weight:600;color:#1f1f1f;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.c-mobile-topbar .c-hamburger-btn{display:flex;align-items:center;justify-content:center;background:none;border:none;cursor:pointer;padding:6px;border-radius:50%;color:#333;flex-shrink:0}.c-mobile-topbar .c-hamburger-btn:hover{background-color:#0000000f}.c-mobile-content{flex:1;overflow-y:auto}\n"] }]
|
|
1297
1601
|
}], ctorParameters: () => [], propDecorators: { menus: [{ type: i0.Input, args: [{ isSignal: true, alias: "menus", required: false }] }], userInfo: [{ type: i0.Input, args: [{ isSignal: true, alias: "userInfo", required: true }] }], sidebar: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebar", required: true }] }] } });
|
|
1298
1602
|
|
|
1603
|
+
class SdLayoutMenuTreeComponent {
|
|
1604
|
+
#destroyRef = inject(DestroyRef);
|
|
1605
|
+
#hoveredPinKey = signal(null, ...(ngDevMode ? [{ debugName: "#hoveredPinKey" }] : []));
|
|
1606
|
+
#pinHoverTimerId;
|
|
1607
|
+
menus = input([], ...(ngDevMode ? [{ debugName: "menus" }] : []));
|
|
1608
|
+
query = input('', ...(ngDevMode ? [{ debugName: "query" }] : []));
|
|
1609
|
+
activePath = input('', ...(ngDevMode ? [{ debugName: "activePath" }] : []));
|
|
1610
|
+
pinnedKeys = input([], ...(ngDevMode ? [{ debugName: "pinnedKeys" }] : []));
|
|
1611
|
+
showPin = input(true, ...(ngDevMode ? [{ debugName: "showPin" }] : []));
|
|
1612
|
+
pinVisibility = input('hover', ...(ngDevMode ? [{ debugName: "pinVisibility" }] : []));
|
|
1613
|
+
navigate = output();
|
|
1614
|
+
togglePinned = output();
|
|
1615
|
+
nodes = computed(() => {
|
|
1616
|
+
const pinnedKeys = new Set(this.pinnedKeys());
|
|
1617
|
+
const hoveredPinKey = this.#hoveredPinKey();
|
|
1618
|
+
const alwaysShowPin = this.pinVisibility() === 'always';
|
|
1619
|
+
const query = this.query().trim();
|
|
1620
|
+
const menus = query ? searchMenuLeaves(this.menus(), query) : this.menus();
|
|
1621
|
+
const nodes = [];
|
|
1622
|
+
const append = (items, depth, ancestors) => {
|
|
1623
|
+
for (const menu of items) {
|
|
1624
|
+
const key = getMenuStableKey(menu, ancestors);
|
|
1625
|
+
if (!key)
|
|
1626
|
+
continue;
|
|
1627
|
+
const isGroup = 'children' in menu && !!menu.children?.length;
|
|
1628
|
+
const path = 'path' in menu ? menu.path : '';
|
|
1629
|
+
nodes.push({
|
|
1630
|
+
menu,
|
|
1631
|
+
key,
|
|
1632
|
+
title: menu.title ?? menu.tooltipTitle ?? '',
|
|
1633
|
+
depth,
|
|
1634
|
+
paddingLeft: 12 + depth * 16,
|
|
1635
|
+
isGroup,
|
|
1636
|
+
isActive: !!path && this.#pathMatches(this.activePath(), path),
|
|
1637
|
+
isPinned: pinnedKeys.has(key),
|
|
1638
|
+
isPinVisible: alwaysShowPin || pinnedKeys.has(key) || hoveredPinKey === key,
|
|
1639
|
+
});
|
|
1640
|
+
if (isGroup && 'children' in menu)
|
|
1641
|
+
append(menu.children ?? [], depth + 1, [...ancestors, menu.title ?? key]);
|
|
1642
|
+
}
|
|
1643
|
+
};
|
|
1644
|
+
append(menus, 0, []);
|
|
1645
|
+
return nodes;
|
|
1646
|
+
}, ...(ngDevMode ? [{ debugName: "nodes" }] : []));
|
|
1647
|
+
constructor() {
|
|
1648
|
+
this.#destroyRef.onDestroy(() => this.#clearPinHoverTimer());
|
|
1649
|
+
}
|
|
1650
|
+
onNavigate(menu) {
|
|
1651
|
+
if ('path' in menu)
|
|
1652
|
+
this.navigate.emit(menu);
|
|
1653
|
+
}
|
|
1654
|
+
onTogglePinned(event, menu) {
|
|
1655
|
+
event.stopPropagation();
|
|
1656
|
+
this.togglePinned.emit(menu);
|
|
1657
|
+
}
|
|
1658
|
+
onPinHoverStart(key) {
|
|
1659
|
+
if (this.pinVisibility() === 'always')
|
|
1660
|
+
return;
|
|
1661
|
+
this.#clearPinHoverTimer();
|
|
1662
|
+
this.#pinHoverTimerId = setTimeout(() => {
|
|
1663
|
+
this.#hoveredPinKey.set(key);
|
|
1664
|
+
this.#pinHoverTimerId = undefined;
|
|
1665
|
+
}, 300);
|
|
1666
|
+
}
|
|
1667
|
+
onPinHoverEnd(key) {
|
|
1668
|
+
this.#clearPinHoverTimer();
|
|
1669
|
+
if (this.#hoveredPinKey() === key)
|
|
1670
|
+
this.#hoveredPinKey.set(null);
|
|
1671
|
+
}
|
|
1672
|
+
#clearPinHoverTimer() {
|
|
1673
|
+
if (this.#pinHoverTimerId === undefined)
|
|
1674
|
+
return;
|
|
1675
|
+
clearTimeout(this.#pinHoverTimerId);
|
|
1676
|
+
this.#pinHoverTimerId = undefined;
|
|
1677
|
+
}
|
|
1678
|
+
#pathMatches(currentPath, menuPath) {
|
|
1679
|
+
const normalize = (path) => (path.endsWith('/') ? path : `${path}/`);
|
|
1680
|
+
return currentPath === menuPath || normalize(currentPath).startsWith(normalize(menuPath));
|
|
1681
|
+
}
|
|
1682
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdLayoutMenuTreeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1683
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SdLayoutMenuTreeComponent, isStandalone: true, selector: "sd-layout-menu-tree", inputs: { menus: { classPropertyName: "menus", publicName: "menus", isSignal: true, isRequired: false, transformFunction: null }, query: { classPropertyName: "query", publicName: "query", isSignal: true, isRequired: false, transformFunction: null }, activePath: { classPropertyName: "activePath", publicName: "activePath", isSignal: true, isRequired: false, transformFunction: null }, pinnedKeys: { classPropertyName: "pinnedKeys", publicName: "pinnedKeys", isSignal: true, isRequired: false, transformFunction: null }, showPin: { classPropertyName: "showPin", publicName: "showPin", isSignal: true, isRequired: false, transformFunction: null }, pinVisibility: { classPropertyName: "pinVisibility", publicName: "pinVisibility", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { navigate: "navigate", togglePinned: "togglePinned" }, ngImport: i0, template: "<nav class=\"d-flex flex-column gap-4\" aria-label=\"Navigation menu\">\n @for (node of nodes(); track node.key) {\n @if (node.isGroup) {\n <div class=\"sd-layout-menu-tree__group d-flex align-items-center gap-8 T12M text-black400\" [style.padding-left.px]=\"node.paddingLeft\">\n <sd-icon [name]=\"node.menu.icon || 'folder'\"></sd-icon>\n <span class=\"text-ellipsis\">{{ node.title }}</span>\n </div>\n } @else {\n <div\n class=\"sd-layout-menu-tree__route d-flex align-items-center gap-4\"\n (mouseenter)=\"onPinHoverStart(node.key)\"\n (mouseleave)=\"onPinHoverEnd(node.key)\">\n <button\n type=\"button\"\n class=\"sd-layout-menu-tree__route-button d-flex align-items-center gap-8 flex-1 text-left T14R\"\n data-menu-route\n [attr.data-menu-key]=\"node.key\"\n [attr.aria-current]=\"node.isActive ? 'page' : null\"\n [class.sd-layout-menu-tree__route-button--active]=\"node.isActive\"\n [style.padding-left.px]=\"node.paddingLeft\"\n (click)=\"onNavigate(node.menu)\">\n @if (node.menu.iconUrl) {\n <img class=\"sd-layout-menu-tree__icon-image\" [src]=\"node.menu.iconUrl\" alt=\"\" />\n } @else {\n <sd-icon [name]=\"node.menu.icon || 'chevron_right'\"></sd-icon>\n }\n <span class=\"text-ellipsis\">{{ node.title }}</span>\n </button>\n @if (showPin()) {\n <button\n type=\"button\"\n class=\"sd-layout-menu-tree__pin d-flex align-items-center justify-content-center\"\n [class.sd-layout-menu-tree__pin--visible]=\"node.isPinVisible\"\n [attr.data-pin-key]=\"node.key\"\n [attr.aria-label]=\"node.isPinned ? 'Unpin ' + node.title : 'Pin ' + node.title\"\n [attr.aria-pressed]=\"node.isPinned\"\n (click)=\"onTogglePinned($event, node.menu)\">\n <sd-icon name=\"push_pin\"></sd-icon>\n </button>\n }\n </div>\n }\n } @empty {\n <div class=\"d-flex flex-column align-items-center justify-content-center gap-12 p-24 text-black400 T14R\">\n Kh\u00F4ng t\u00ECm th\u1EA5y menu ph\u00F9 h\u1EE3p\n </div>\n }\n</nav>\n", styles: [".sd-layout-menu-tree__route-button,.sd-layout-menu-tree__pin{min-height:40px;border:0;background:transparent;color:var(--sd-black500, var(--sd-text, #1a1b1f));cursor:pointer;border-radius:8px;transition:background-color .12s ease,color .12s ease}.sd-layout-menu-tree__route-button--active{color:var(--sd-primary);background:var(--sd-primary-light);font-weight:500}.sd-layout-menu-tree__route-button:hover,.sd-layout-menu-tree__pin:hover{background:var(--sd-black100, var(--sd-surface-muted, #f3f3f3));color:var(--sd-primary, #005cbb)}.sd-layout-menu-tree__route-button--active:hover{background:var(--sd-primary-light, #d7e3ff)}.sd-layout-menu-tree__route-button:focus-visible,.sd-layout-menu-tree__pin:focus-visible{outline:2px solid var(--sd-primary);outline-offset:2px}.sd-layout-menu-tree__pin{width:36px;flex:0 0 36px;opacity:0;pointer-events:none;transition:opacity .12s ease,background-color .12s ease,color .12s ease}.sd-layout-menu-tree__pin--visible,.sd-layout-menu-tree__pin:focus-visible{opacity:1;pointer-events:auto}.sd-layout-menu-tree__group{min-height:32px}.sd-layout-menu-tree__icon-image{width:20px;height:20px;object-fit:contain}@media(prefers-reduced-motion:reduce){.sd-layout-menu-tree__route-button,.sd-layout-menu-tree__pin{transition:none}}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1684
|
+
}
|
|
1685
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdLayoutMenuTreeComponent, decorators: [{
|
|
1686
|
+
type: Component,
|
|
1687
|
+
args: [{ selector: 'sd-layout-menu-tree', standalone: true, imports: [SdIcon], changeDetection: ChangeDetectionStrategy.OnPush, template: "<nav class=\"d-flex flex-column gap-4\" aria-label=\"Navigation menu\">\n @for (node of nodes(); track node.key) {\n @if (node.isGroup) {\n <div class=\"sd-layout-menu-tree__group d-flex align-items-center gap-8 T12M text-black400\" [style.padding-left.px]=\"node.paddingLeft\">\n <sd-icon [name]=\"node.menu.icon || 'folder'\"></sd-icon>\n <span class=\"text-ellipsis\">{{ node.title }}</span>\n </div>\n } @else {\n <div\n class=\"sd-layout-menu-tree__route d-flex align-items-center gap-4\"\n (mouseenter)=\"onPinHoverStart(node.key)\"\n (mouseleave)=\"onPinHoverEnd(node.key)\">\n <button\n type=\"button\"\n class=\"sd-layout-menu-tree__route-button d-flex align-items-center gap-8 flex-1 text-left T14R\"\n data-menu-route\n [attr.data-menu-key]=\"node.key\"\n [attr.aria-current]=\"node.isActive ? 'page' : null\"\n [class.sd-layout-menu-tree__route-button--active]=\"node.isActive\"\n [style.padding-left.px]=\"node.paddingLeft\"\n (click)=\"onNavigate(node.menu)\">\n @if (node.menu.iconUrl) {\n <img class=\"sd-layout-menu-tree__icon-image\" [src]=\"node.menu.iconUrl\" alt=\"\" />\n } @else {\n <sd-icon [name]=\"node.menu.icon || 'chevron_right'\"></sd-icon>\n }\n <span class=\"text-ellipsis\">{{ node.title }}</span>\n </button>\n @if (showPin()) {\n <button\n type=\"button\"\n class=\"sd-layout-menu-tree__pin d-flex align-items-center justify-content-center\"\n [class.sd-layout-menu-tree__pin--visible]=\"node.isPinVisible\"\n [attr.data-pin-key]=\"node.key\"\n [attr.aria-label]=\"node.isPinned ? 'Unpin ' + node.title : 'Pin ' + node.title\"\n [attr.aria-pressed]=\"node.isPinned\"\n (click)=\"onTogglePinned($event, node.menu)\">\n <sd-icon name=\"push_pin\"></sd-icon>\n </button>\n }\n </div>\n }\n } @empty {\n <div class=\"d-flex flex-column align-items-center justify-content-center gap-12 p-24 text-black400 T14R\">\n Kh\u00F4ng t\u00ECm th\u1EA5y menu ph\u00F9 h\u1EE3p\n </div>\n }\n</nav>\n", styles: [".sd-layout-menu-tree__route-button,.sd-layout-menu-tree__pin{min-height:40px;border:0;background:transparent;color:var(--sd-black500, var(--sd-text, #1a1b1f));cursor:pointer;border-radius:8px;transition:background-color .12s ease,color .12s ease}.sd-layout-menu-tree__route-button--active{color:var(--sd-primary);background:var(--sd-primary-light);font-weight:500}.sd-layout-menu-tree__route-button:hover,.sd-layout-menu-tree__pin:hover{background:var(--sd-black100, var(--sd-surface-muted, #f3f3f3));color:var(--sd-primary, #005cbb)}.sd-layout-menu-tree__route-button--active:hover{background:var(--sd-primary-light, #d7e3ff)}.sd-layout-menu-tree__route-button:focus-visible,.sd-layout-menu-tree__pin:focus-visible{outline:2px solid var(--sd-primary);outline-offset:2px}.sd-layout-menu-tree__pin{width:36px;flex:0 0 36px;opacity:0;pointer-events:none;transition:opacity .12s ease,background-color .12s ease,color .12s ease}.sd-layout-menu-tree__pin--visible,.sd-layout-menu-tree__pin:focus-visible{opacity:1;pointer-events:auto}.sd-layout-menu-tree__group{min-height:32px}.sd-layout-menu-tree__icon-image{width:20px;height:20px;object-fit:contain}@media(prefers-reduced-motion:reduce){.sd-layout-menu-tree__route-button,.sd-layout-menu-tree__pin{transition:none}}\n"] }]
|
|
1688
|
+
}], ctorParameters: () => [], propDecorators: { menus: [{ type: i0.Input, args: [{ isSignal: true, alias: "menus", required: false }] }], query: [{ type: i0.Input, args: [{ isSignal: true, alias: "query", required: false }] }], activePath: [{ type: i0.Input, args: [{ isSignal: true, alias: "activePath", required: false }] }], pinnedKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "pinnedKeys", required: false }] }], showPin: [{ type: i0.Input, args: [{ isSignal: true, alias: "showPin", required: false }] }], pinVisibility: [{ type: i0.Input, args: [{ isSignal: true, alias: "pinVisibility", required: false }] }], navigate: [{ type: i0.Output, args: ["navigate"] }], togglePinned: [{ type: i0.Output, args: ["togglePinned"] }] } });
|
|
1689
|
+
|
|
1690
|
+
class SdLayoutSearchFieldComponent {
|
|
1691
|
+
model = input('', ...(ngDevMode ? [{ debugName: "model" }] : []));
|
|
1692
|
+
placeholder = input.required(...(ngDevMode ? [{ debugName: "placeholder" }] : []));
|
|
1693
|
+
autoId = input.required(...(ngDevMode ? [{ debugName: "autoId" }] : []));
|
|
1694
|
+
sdChange = output();
|
|
1695
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdLayoutSearchFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1696
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.25", type: SdLayoutSearchFieldComponent, isStandalone: true, selector: "sd-layout-search-field", inputs: { model: { classPropertyName: "model", publicName: "model", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: true, transformFunction: null }, autoId: { classPropertyName: "autoId", publicName: "autoId", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { sdChange: "sdChange" }, ngImport: i0, template: "<div\n data-layout-search\n class=\"sd-layout-search-field sd-layout-search-field--soft-pill d-flex align-items-center gap-8\">\n <sd-icon class=\"sd-layout-search-field__icon\" name=\"search\"></sd-icon>\n <sd-input\n class=\"sd-layout-search-field__control\"\n size=\"sm\"\n hideInlineError\n [placeholder]=\"placeholder()\"\n [autoId]=\"autoId()\"\n [model]=\"model()\"\n (sdChange)=\"sdChange.emit($event)\">\n </sd-input>\n</div>\n", styles: [":host{display:block;min-width:0}.sd-layout-search-field{min-height:38px;padding:0 12px;border:0;border-radius:9999px;outline:1px solid transparent;outline-offset:1px;background:var(--sd-black100, var(--sd-surface-muted));color:var(--sd-black400, var(--sd-text-muted))}.sd-layout-search-field:focus-within{outline-color:var(--sd-primary)}.sd-layout-search-field__icon{flex:0 0 auto;color:var(--sd-black400, var(--sd-text-muted))}.sd-layout-search-field__control{flex:1;min-width:0;--sd-border: transparent;--mdc-outlined-text-field-outline-color: transparent;--mdc-outlined-text-field-hover-outline-color: transparent;--mdc-outlined-text-field-focus-outline-color: transparent;--mdc-outlined-text-field-input-text-placeholder-color: var(--sd-black400, var(--sd-text-muted));--mdc-outlined-text-field-container-shape: 0px}:host ::ng-deep .sd-layout-search-field__control input:focus-visible{outline:0}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "component", type: SdInput, selector: "sd-input", inputs: ["autoId", "name", "appearance", "floatLabel", "size", "form", "label", "helperText", "placeholder", "type", "mask", "hideInlineError", "blurOnEnter", "clearable", "required", "readonly", "disabled", "viewed", "minlength", "maxlength", "pattern", "patternErrorMessage", "validator", "inlineError", "hyperlink", "model"], outputs: ["modelChange", "sdChange", "sdFocus", "sdBlur", "keyupEnter", "cleared", "sdFocusForceBlur"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1697
|
+
}
|
|
1698
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdLayoutSearchFieldComponent, decorators: [{
|
|
1699
|
+
type: Component,
|
|
1700
|
+
args: [{ selector: 'sd-layout-search-field', standalone: true, imports: [SdIcon, SdInput], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n data-layout-search\n class=\"sd-layout-search-field sd-layout-search-field--soft-pill d-flex align-items-center gap-8\">\n <sd-icon class=\"sd-layout-search-field__icon\" name=\"search\"></sd-icon>\n <sd-input\n class=\"sd-layout-search-field__control\"\n size=\"sm\"\n hideInlineError\n [placeholder]=\"placeholder()\"\n [autoId]=\"autoId()\"\n [model]=\"model()\"\n (sdChange)=\"sdChange.emit($event)\">\n </sd-input>\n</div>\n", styles: [":host{display:block;min-width:0}.sd-layout-search-field{min-height:38px;padding:0 12px;border:0;border-radius:9999px;outline:1px solid transparent;outline-offset:1px;background:var(--sd-black100, var(--sd-surface-muted));color:var(--sd-black400, var(--sd-text-muted))}.sd-layout-search-field:focus-within{outline-color:var(--sd-primary)}.sd-layout-search-field__icon{flex:0 0 auto;color:var(--sd-black400, var(--sd-text-muted))}.sd-layout-search-field__control{flex:1;min-width:0;--sd-border: transparent;--mdc-outlined-text-field-outline-color: transparent;--mdc-outlined-text-field-hover-outline-color: transparent;--mdc-outlined-text-field-focus-outline-color: transparent;--mdc-outlined-text-field-input-text-placeholder-color: var(--sd-black400, var(--sd-text-muted));--mdc-outlined-text-field-container-shape: 0px}:host ::ng-deep .sd-layout-search-field__control input:focus-visible{outline:0}\n"] }]
|
|
1701
|
+
}], propDecorators: { model: [{ type: i0.Input, args: [{ isSignal: true, alias: "model", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: true }] }], autoId: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoId", required: true }] }], sdChange: [{ type: i0.Output, args: ["sdChange"] }] } });
|
|
1702
|
+
|
|
1703
|
+
class SidebarV2Component {
|
|
1704
|
+
#router = inject(Router);
|
|
1705
|
+
#document = inject(DOCUMENT);
|
|
1706
|
+
#destroyRef = inject(DestroyRef);
|
|
1707
|
+
#navigationState = inject(SdLayoutNavigationStateService);
|
|
1708
|
+
menus = input([], ...(ngDevMode ? [{ debugName: "menus" }] : []));
|
|
1709
|
+
userInfo = input.required(...(ngDevMode ? [{ debugName: "userInfo" }] : []));
|
|
1710
|
+
sidebar = input.required(...(ngDevMode ? [{ debugName: "sidebar" }] : []));
|
|
1711
|
+
activeGroupKey = signal('', ...(ngDevMode ? [{ debugName: "activeGroupKey" }] : []));
|
|
1712
|
+
isFlyoutOpen = signal(false, ...(ngDevMode ? [{ debugName: "isFlyoutOpen" }] : []));
|
|
1713
|
+
isFlyoutLocked = signal(false, ...(ngDevMode ? [{ debugName: "isFlyoutLocked" }] : []));
|
|
1714
|
+
searchText = signal('', ...(ngDevMode ? [{ debugName: "searchText" }] : []));
|
|
1715
|
+
activePath = signal(this.#router.url.split(/[?#]/, 1)[0] ?? '', ...(ngDevMode ? [{ debugName: "activePath" }] : []));
|
|
1716
|
+
interaction = computed(() => resolveSidebarV2Interaction(this.sidebar()), ...(ngDevMode ? [{ debugName: "interaction" }] : []));
|
|
1717
|
+
railItems = computed(() => this.menus()
|
|
1718
|
+
.map(menu => ({ menu, key: getMenuStableKey(menu), title: menu.tooltipTitle || menu.title || 'Menu' }))
|
|
1719
|
+
.filter(item => !!item.key), ...(ngDevMode ? [{ debugName: "railItems" }] : []));
|
|
1720
|
+
activeGroup = computed(() => this.railItems().find(item => item.key === this.activeGroupKey())?.menu, ...(ngDevMode ? [{ debugName: "activeGroup" }] : []));
|
|
1721
|
+
contextMenus = computed(() => {
|
|
1722
|
+
const activeGroup = this.activeGroup();
|
|
1723
|
+
if (!activeGroup)
|
|
1724
|
+
return [];
|
|
1725
|
+
return 'children' in activeGroup && activeGroup.children?.length ? activeGroup.children : [activeGroup];
|
|
1726
|
+
}, ...(ngDevMode ? [{ debugName: "contextMenus" }] : []));
|
|
1727
|
+
pinnedKeys = this.#navigationState.pinnedKeys;
|
|
1728
|
+
constructor() {
|
|
1729
|
+
effect(() => {
|
|
1730
|
+
const menus = this.menus();
|
|
1731
|
+
untracked(() => {
|
|
1732
|
+
this.#navigationState.hydrate(menus);
|
|
1733
|
+
const persistedState = this.#navigationState.versionState(2);
|
|
1734
|
+
if (persistedState.activeGroupKey && !this.activeGroupKey())
|
|
1735
|
+
this.activeGroupKey.set(persistedState.activeGroupKey);
|
|
1736
|
+
});
|
|
1737
|
+
});
|
|
1738
|
+
this.#router.events.pipe(takeUntilDestroyed(this.#destroyRef)).subscribe(event => {
|
|
1739
|
+
if (!(event instanceof NavigationEnd))
|
|
1740
|
+
return;
|
|
1741
|
+
this.activePath.set(event.urlAfterRedirects.split(/[?#]/, 1)[0] ?? '');
|
|
1742
|
+
this.closeFromNavigation();
|
|
1743
|
+
});
|
|
1744
|
+
}
|
|
1745
|
+
selectGroup(menu) {
|
|
1746
|
+
const key = getMenuStableKey(menu);
|
|
1747
|
+
if (!key)
|
|
1748
|
+
return;
|
|
1749
|
+
if (this.interaction() === 'click') {
|
|
1750
|
+
if (this.activeGroupKey() === key && this.isFlyoutOpen()) {
|
|
1751
|
+
this.#closeFlyout();
|
|
1752
|
+
return;
|
|
1753
|
+
}
|
|
1754
|
+
this.#openGroup(key, false);
|
|
1755
|
+
return;
|
|
1756
|
+
}
|
|
1757
|
+
if (this.activeGroupKey() === key && this.isFlyoutLocked()) {
|
|
1758
|
+
this.#closeFlyout();
|
|
1759
|
+
return;
|
|
1760
|
+
}
|
|
1761
|
+
this.#openGroup(key, true);
|
|
1762
|
+
}
|
|
1763
|
+
previewGroup(menu) {
|
|
1764
|
+
if (this.interaction() !== 'hover-lock' || this.isFlyoutLocked())
|
|
1765
|
+
return;
|
|
1766
|
+
const key = getMenuStableKey(menu);
|
|
1767
|
+
if (key)
|
|
1768
|
+
this.#openGroup(key, false, false);
|
|
1769
|
+
}
|
|
1770
|
+
leaveFlyout() {
|
|
1771
|
+
if (this.interaction() === 'hover-lock' && !this.isFlyoutLocked())
|
|
1772
|
+
this.#closeFlyout(false);
|
|
1773
|
+
}
|
|
1774
|
+
closeFromBackdrop() {
|
|
1775
|
+
this.#closeFlyout();
|
|
1776
|
+
}
|
|
1777
|
+
closeFromEscape() {
|
|
1778
|
+
this.#closeFlyout();
|
|
1779
|
+
}
|
|
1780
|
+
closeFromNavigation() {
|
|
1781
|
+
this.#closeFlyout();
|
|
1782
|
+
}
|
|
1783
|
+
onNavigate(menu) {
|
|
1784
|
+
if (!('path' in menu))
|
|
1785
|
+
return;
|
|
1786
|
+
this.#navigationState.recordRecent(menu, { enabled: true, maxItems: 5 });
|
|
1787
|
+
if (menu.path.includes('http')) {
|
|
1788
|
+
this.#document.defaultView?.open(menu.path, '_blank', 'noopener');
|
|
1789
|
+
this.closeFromNavigation();
|
|
1790
|
+
return;
|
|
1791
|
+
}
|
|
1792
|
+
void this.#router.navigate([menu.path.split('?')[0]], {
|
|
1793
|
+
queryParams: menu.queryParams ?? {},
|
|
1794
|
+
state: { switchTab: true },
|
|
1795
|
+
});
|
|
1796
|
+
}
|
|
1797
|
+
onTogglePinned(menu) {
|
|
1798
|
+
this.#navigationState.togglePinned(menu);
|
|
1799
|
+
}
|
|
1800
|
+
#openGroup(key, locked, persist = true) {
|
|
1801
|
+
this.activeGroupKey.set(key);
|
|
1802
|
+
this.isFlyoutOpen.set(true);
|
|
1803
|
+
this.isFlyoutLocked.set(locked);
|
|
1804
|
+
this.searchText.set('');
|
|
1805
|
+
if (persist)
|
|
1806
|
+
this.#navigationState.patchVersionState(2, { activeGroupKey: key, locked });
|
|
1807
|
+
}
|
|
1808
|
+
#closeFlyout(persist = true) {
|
|
1809
|
+
this.isFlyoutOpen.set(false);
|
|
1810
|
+
this.isFlyoutLocked.set(false);
|
|
1811
|
+
this.searchText.set('');
|
|
1812
|
+
if (persist)
|
|
1813
|
+
this.#navigationState.patchVersionState(2, { locked: false });
|
|
1814
|
+
}
|
|
1815
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SidebarV2Component, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1816
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SidebarV2Component, isStandalone: true, selector: "sidebar-v2", inputs: { menus: { classPropertyName: "menus", publicName: "menus", isSignal: true, isRequired: false, transformFunction: null }, userInfo: { classPropertyName: "userInfo", publicName: "userInfo", isSignal: true, isRequired: true, transformFunction: null }, sidebar: { classPropertyName: "sidebar", publicName: "sidebar", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"sd-sidebar-v2\" (keydown.escape)=\"closeFromEscape()\">\n <aside class=\"sd-sidebar-v2__rail d-flex flex-column align-items-center gap-8 py-12 bg-white border-right border-black200\">\n <button type=\"button\" class=\"sd-sidebar-v2__brand d-flex align-items-center justify-content-center\" aria-label=\"Trang ch\u1EE7\">\n @if (sidebar().logoUrl) {\n <img class=\"sd-sidebar-v2__logo\" [src]=\"sidebar().logoUrl\" alt=\"\" />\n } @else {\n <sd-icon name=\"apps\"></sd-icon>\n }\n </button>\n\n <nav class=\"d-flex flex-column align-items-center gap-4 flex-1\" aria-label=\"Nh\u00F3m menu\">\n @for (item of railItems(); track item.key) {\n <button\n type=\"button\"\n class=\"sd-sidebar-v2__rail-action d-flex flex-column align-items-center justify-content-center gap-4\"\n [attr.data-v2-group-key]=\"item.key\"\n [attr.aria-label]=\"item.title\"\n [attr.aria-expanded]=\"activeGroupKey() === item.key && isFlyoutOpen()\"\n [class.sd-sidebar-v2__rail-action--active]=\"activeGroupKey() === item.key\"\n (click)=\"selectGroup(item.menu)\"\n (mouseenter)=\"previewGroup(item.menu)\"\n (focus)=\"previewGroup(item.menu)\">\n @if (item.menu.iconUrl) {\n <img class=\"sd-sidebar-v2__rail-icon\" [src]=\"item.menu.iconUrl\" alt=\"\" />\n } @else {\n <sd-icon [name]=\"item.menu.icon || 'widgets'\"></sd-icon>\n }\n <span class=\"sd-sidebar-v2__rail-label text-ellipsis\">{{ item.title }}</span>\n </button>\n }\n </nav>\n\n <sd-layout-user-menu [userInfo]=\"userInfo()\" [compact]=\"true\"></sd-layout-user-menu>\n </aside>\n\n @if (isFlyoutOpen()) {\n <button\n type=\"button\"\n class=\"sd-sidebar-v2__backdrop\"\n aria-label=\"\u0110\u00F3ng menu\"\n (click)=\"closeFromBackdrop()\"\n (keydown.escape)=\"closeFromEscape()\"></button>\n <section\n class=\"sd-sidebar-v2__flyout d-flex flex-column bg-white border-right border-black200 mat-elevation-z4\"\n [attr.aria-label]=\"activeGroup()?.title || 'Menu'\"\n (mouseleave)=\"leaveFlyout()\"\n (keydown.escape)=\"closeFromEscape()\">\n <header class=\"d-flex align-items-center justify-content-between gap-8 px-16 py-12 border-bottom border-black200\">\n <span class=\"T16M text-ellipsis\">{{ activeGroup()?.title || sidebar().defaultTitle || 'Menu' }}</span>\n @if (isFlyoutLocked()) {\n <sd-icon name=\"lock\"></sd-icon>\n }\n </header>\n <div class=\"p-12\">\n <sd-layout-search-field\n placeholder=\"T\u00ECm trong nh\u00F3m\"\n [autoId]=\"'layout-v2-context-search'\"\n [model]=\"searchText()\"\n (sdChange)=\"searchText.set($event)\">\n </sd-layout-search-field>\n </div>\n <div class=\"flex-1 overflow-auto px-8 pb-12\">\n <sd-layout-menu-tree\n [menus]=\"contextMenus()\"\n [query]=\"searchText()\"\n [activePath]=\"activePath()\"\n [pinnedKeys]=\"pinnedKeys()\"\n (navigate)=\"onNavigate($event)\"\n (togglePinned)=\"onTogglePinned($event)\">\n </sd-layout-menu-tree>\n </div>\n </section>\n }\n\n <main class=\"sd-sidebar-v2__content\" data-v2-content data-content-offset=\"rail\">\n <ng-content></ng-content>\n </main>\n</div>\n", styles: [".sd-sidebar-v2{min-height:100vh;background:var(--sd-light)}.sd-sidebar-v2__rail{position:fixed;z-index:30;inset:0 auto 0 0;width:72px}.sd-sidebar-v2__brand,.sd-sidebar-v2__rail-action{width:56px;min-height:52px;border:0;border-radius:10px;background:transparent;color:var(--sd-black400);cursor:pointer}.sd-sidebar-v2__rail-action:hover,.sd-sidebar-v2__rail-action--active{color:var(--sd-primary);background:var(--sd-primary-light)}.sd-sidebar-v2__brand:focus-visible,.sd-sidebar-v2__rail-action:focus-visible{outline:2px solid var(--sd-primary);outline-offset:2px}.sd-sidebar-v2__rail-label{width:52px;font-size:10px;line-height:12px;text-align:center}.sd-sidebar-v2__logo,.sd-sidebar-v2__rail-icon{width:24px;height:24px;object-fit:contain}.sd-sidebar-v2__backdrop{position:fixed;z-index:10;inset:0;border:0;background:transparent}.sd-sidebar-v2__flyout{position:fixed;z-index:20;inset:0 auto 0 72px;width:320px}.sd-sidebar-v2__content{min-height:100vh;margin-left:72px}@media(prefers-reduced-motion:reduce){.sd-sidebar-v2__flyout{transition:none}}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "component", type: SdLayoutSearchFieldComponent, selector: "sd-layout-search-field", inputs: ["model", "placeholder", "autoId"], outputs: ["sdChange"] }, { kind: "component", type: SdLayoutMenuTreeComponent, selector: "sd-layout-menu-tree", inputs: ["menus", "query", "activePath", "pinnedKeys", "showPin", "pinVisibility"], outputs: ["navigate", "togglePinned"] }, { kind: "component", type: SdLayoutUserMenuComponent, selector: "sd-layout-user-menu", inputs: ["userInfo", "signout", "changePassword", "updateProfile", "setting", "notification", "compact", "presentation"], outputs: ["opened", "closed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1817
|
+
}
|
|
1818
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SidebarV2Component, decorators: [{
|
|
1819
|
+
type: Component,
|
|
1820
|
+
args: [{ selector: 'sidebar-v2', standalone: true, imports: [SdIcon, SdLayoutSearchFieldComponent, SdLayoutMenuTreeComponent, SdLayoutUserMenuComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"sd-sidebar-v2\" (keydown.escape)=\"closeFromEscape()\">\n <aside class=\"sd-sidebar-v2__rail d-flex flex-column align-items-center gap-8 py-12 bg-white border-right border-black200\">\n <button type=\"button\" class=\"sd-sidebar-v2__brand d-flex align-items-center justify-content-center\" aria-label=\"Trang ch\u1EE7\">\n @if (sidebar().logoUrl) {\n <img class=\"sd-sidebar-v2__logo\" [src]=\"sidebar().logoUrl\" alt=\"\" />\n } @else {\n <sd-icon name=\"apps\"></sd-icon>\n }\n </button>\n\n <nav class=\"d-flex flex-column align-items-center gap-4 flex-1\" aria-label=\"Nh\u00F3m menu\">\n @for (item of railItems(); track item.key) {\n <button\n type=\"button\"\n class=\"sd-sidebar-v2__rail-action d-flex flex-column align-items-center justify-content-center gap-4\"\n [attr.data-v2-group-key]=\"item.key\"\n [attr.aria-label]=\"item.title\"\n [attr.aria-expanded]=\"activeGroupKey() === item.key && isFlyoutOpen()\"\n [class.sd-sidebar-v2__rail-action--active]=\"activeGroupKey() === item.key\"\n (click)=\"selectGroup(item.menu)\"\n (mouseenter)=\"previewGroup(item.menu)\"\n (focus)=\"previewGroup(item.menu)\">\n @if (item.menu.iconUrl) {\n <img class=\"sd-sidebar-v2__rail-icon\" [src]=\"item.menu.iconUrl\" alt=\"\" />\n } @else {\n <sd-icon [name]=\"item.menu.icon || 'widgets'\"></sd-icon>\n }\n <span class=\"sd-sidebar-v2__rail-label text-ellipsis\">{{ item.title }}</span>\n </button>\n }\n </nav>\n\n <sd-layout-user-menu [userInfo]=\"userInfo()\" [compact]=\"true\"></sd-layout-user-menu>\n </aside>\n\n @if (isFlyoutOpen()) {\n <button\n type=\"button\"\n class=\"sd-sidebar-v2__backdrop\"\n aria-label=\"\u0110\u00F3ng menu\"\n (click)=\"closeFromBackdrop()\"\n (keydown.escape)=\"closeFromEscape()\"></button>\n <section\n class=\"sd-sidebar-v2__flyout d-flex flex-column bg-white border-right border-black200 mat-elevation-z4\"\n [attr.aria-label]=\"activeGroup()?.title || 'Menu'\"\n (mouseleave)=\"leaveFlyout()\"\n (keydown.escape)=\"closeFromEscape()\">\n <header class=\"d-flex align-items-center justify-content-between gap-8 px-16 py-12 border-bottom border-black200\">\n <span class=\"T16M text-ellipsis\">{{ activeGroup()?.title || sidebar().defaultTitle || 'Menu' }}</span>\n @if (isFlyoutLocked()) {\n <sd-icon name=\"lock\"></sd-icon>\n }\n </header>\n <div class=\"p-12\">\n <sd-layout-search-field\n placeholder=\"T\u00ECm trong nh\u00F3m\"\n [autoId]=\"'layout-v2-context-search'\"\n [model]=\"searchText()\"\n (sdChange)=\"searchText.set($event)\">\n </sd-layout-search-field>\n </div>\n <div class=\"flex-1 overflow-auto px-8 pb-12\">\n <sd-layout-menu-tree\n [menus]=\"contextMenus()\"\n [query]=\"searchText()\"\n [activePath]=\"activePath()\"\n [pinnedKeys]=\"pinnedKeys()\"\n (navigate)=\"onNavigate($event)\"\n (togglePinned)=\"onTogglePinned($event)\">\n </sd-layout-menu-tree>\n </div>\n </section>\n }\n\n <main class=\"sd-sidebar-v2__content\" data-v2-content data-content-offset=\"rail\">\n <ng-content></ng-content>\n </main>\n</div>\n", styles: [".sd-sidebar-v2{min-height:100vh;background:var(--sd-light)}.sd-sidebar-v2__rail{position:fixed;z-index:30;inset:0 auto 0 0;width:72px}.sd-sidebar-v2__brand,.sd-sidebar-v2__rail-action{width:56px;min-height:52px;border:0;border-radius:10px;background:transparent;color:var(--sd-black400);cursor:pointer}.sd-sidebar-v2__rail-action:hover,.sd-sidebar-v2__rail-action--active{color:var(--sd-primary);background:var(--sd-primary-light)}.sd-sidebar-v2__brand:focus-visible,.sd-sidebar-v2__rail-action:focus-visible{outline:2px solid var(--sd-primary);outline-offset:2px}.sd-sidebar-v2__rail-label{width:52px;font-size:10px;line-height:12px;text-align:center}.sd-sidebar-v2__logo,.sd-sidebar-v2__rail-icon{width:24px;height:24px;object-fit:contain}.sd-sidebar-v2__backdrop{position:fixed;z-index:10;inset:0;border:0;background:transparent}.sd-sidebar-v2__flyout{position:fixed;z-index:20;inset:0 auto 0 72px;width:320px}.sd-sidebar-v2__content{min-height:100vh;margin-left:72px}@media(prefers-reduced-motion:reduce){.sd-sidebar-v2__flyout{transition:none}}\n"] }]
|
|
1821
|
+
}], ctorParameters: () => [], propDecorators: { menus: [{ type: i0.Input, args: [{ isSignal: true, alias: "menus", required: false }] }], userInfo: [{ type: i0.Input, args: [{ isSignal: true, alias: "userInfo", required: true }] }], sidebar: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebar", required: true }] }] } });
|
|
1822
|
+
|
|
1823
|
+
class SidebarMobileV2Component {
|
|
1824
|
+
#router = inject(Router);
|
|
1825
|
+
#document = inject(DOCUMENT);
|
|
1826
|
+
#destroyRef = inject(DestroyRef);
|
|
1827
|
+
#navigationState = inject(SdLayoutNavigationStateService);
|
|
1828
|
+
#restoreFocusElement = null;
|
|
1829
|
+
#previousBodyOverflow = '';
|
|
1830
|
+
menus = input([], ...(ngDevMode ? [{ debugName: "menus" }] : []));
|
|
1831
|
+
userInfo = input.required(...(ngDevMode ? [{ debugName: "userInfo" }] : []));
|
|
1832
|
+
sidebar = input.required(...(ngDevMode ? [{ debugName: "sidebar" }] : []));
|
|
1833
|
+
primaryMenus = computed(() => selectPrimaryMenuGroups(this.menus(), this.sidebar().primaryMenuIds), ...(ngDevMode ? [{ debugName: "primaryMenus" }] : []));
|
|
1834
|
+
overflowMenus = computed(() => {
|
|
1835
|
+
const primaryKeys = new Set(this.primaryMenus().map(menu => getMenuStableKey(menu)));
|
|
1836
|
+
return this.menus().filter(menu => !primaryKeys.has(getMenuStableKey(menu)));
|
|
1837
|
+
}, ...(ngDevMode ? [{ debugName: "overflowMenus" }] : []));
|
|
1838
|
+
isSheetOpen = signal(false, ...(ngDevMode ? [{ debugName: "isSheetOpen" }] : []));
|
|
1839
|
+
sheetGroupKey = signal('', ...(ngDevMode ? [{ debugName: "sheetGroupKey" }] : []));
|
|
1840
|
+
sheetTitle = signal('', ...(ngDevMode ? [{ debugName: "sheetTitle" }] : []));
|
|
1841
|
+
sheetMenus = signal([], ...(ngDevMode ? [{ debugName: "sheetMenus" }] : []));
|
|
1842
|
+
searchText = signal('', ...(ngDevMode ? [{ debugName: "searchText" }] : []));
|
|
1843
|
+
activePath = signal(this.#router.url.split(/[?#]/, 1)[0] ?? '', ...(ngDevMode ? [{ debugName: "activePath" }] : []));
|
|
1844
|
+
pinnedKeys = this.#navigationState.pinnedKeys;
|
|
1845
|
+
constructor() {
|
|
1846
|
+
effect(() => {
|
|
1847
|
+
const menus = this.menus();
|
|
1848
|
+
untracked(() => this.#navigationState.hydrate(menus));
|
|
1849
|
+
});
|
|
1850
|
+
this.#router.events.pipe(takeUntilDestroyed(this.#destroyRef)).subscribe(event => {
|
|
1851
|
+
if (!(event instanceof NavigationEnd))
|
|
1852
|
+
return;
|
|
1853
|
+
this.activePath.set(event.urlAfterRedirects.split(/[?#]/, 1)[0] ?? '');
|
|
1854
|
+
this.closeSheet(false);
|
|
1855
|
+
});
|
|
1856
|
+
this.#destroyRef.onDestroy(() => this.#releaseBodyScroll());
|
|
1857
|
+
}
|
|
1858
|
+
openMore(trigger) {
|
|
1859
|
+
this.#openSheet('more', 'Thêm', this.overflowMenus(), trigger);
|
|
1860
|
+
}
|
|
1861
|
+
activateMenu(menu, trigger) {
|
|
1862
|
+
if ('path' in menu) {
|
|
1863
|
+
this.#navigate(menu);
|
|
1864
|
+
return;
|
|
1865
|
+
}
|
|
1866
|
+
this.#openSheet(getMenuStableKey(menu), menu.title || 'Menu', menu.children ?? [], trigger);
|
|
1867
|
+
}
|
|
1868
|
+
closeSheet(restoreFocus = true) {
|
|
1869
|
+
if (!this.isSheetOpen())
|
|
1870
|
+
return;
|
|
1871
|
+
this.isSheetOpen.set(false);
|
|
1872
|
+
this.searchText.set('');
|
|
1873
|
+
this.#releaseBodyScroll();
|
|
1874
|
+
if (restoreFocus)
|
|
1875
|
+
this.#restoreFocusElement?.focus();
|
|
1876
|
+
this.#restoreFocusElement = null;
|
|
1877
|
+
}
|
|
1878
|
+
onSheetKeydown(event) {
|
|
1879
|
+
if (event.key !== 'Escape')
|
|
1880
|
+
return;
|
|
1881
|
+
event.preventDefault();
|
|
1882
|
+
this.closeSheet();
|
|
1883
|
+
}
|
|
1884
|
+
onNavigate(menu) {
|
|
1885
|
+
if ('path' in menu)
|
|
1886
|
+
this.#navigate(menu);
|
|
1887
|
+
}
|
|
1888
|
+
onTogglePinned(menu) {
|
|
1889
|
+
this.#navigationState.togglePinned(menu);
|
|
1890
|
+
}
|
|
1891
|
+
#openSheet(key, title, menus, trigger) {
|
|
1892
|
+
this.sheetGroupKey.set(key);
|
|
1893
|
+
this.sheetTitle.set(title);
|
|
1894
|
+
this.sheetMenus.set(menus);
|
|
1895
|
+
this.searchText.set('');
|
|
1896
|
+
this.#restoreFocusElement =
|
|
1897
|
+
trigger instanceof HTMLElement ? trigger : this.#document.activeElement instanceof HTMLElement ? this.#document.activeElement : null;
|
|
1898
|
+
if (!this.isSheetOpen()) {
|
|
1899
|
+
this.#previousBodyOverflow = this.#document.body.style.overflow;
|
|
1900
|
+
this.#document.body.style.overflow = 'hidden';
|
|
1901
|
+
}
|
|
1902
|
+
this.isSheetOpen.set(true);
|
|
1903
|
+
}
|
|
1904
|
+
#navigate(menu) {
|
|
1905
|
+
this.#navigationState.recordRecent(menu, { enabled: true, maxItems: 5 });
|
|
1906
|
+
if (menu.path.includes('http')) {
|
|
1907
|
+
this.#document.defaultView?.open(menu.path, '_blank', 'noopener');
|
|
1908
|
+
this.closeSheet();
|
|
1909
|
+
return;
|
|
1910
|
+
}
|
|
1911
|
+
void this.#router
|
|
1912
|
+
.navigate([menu.path.split('?')[0]], { queryParams: menu.queryParams ?? {}, state: { switchTab: true } })
|
|
1913
|
+
.then(navigated => {
|
|
1914
|
+
if (navigated)
|
|
1915
|
+
this.closeSheet();
|
|
1916
|
+
});
|
|
1917
|
+
}
|
|
1918
|
+
#releaseBodyScroll() {
|
|
1919
|
+
this.#document.body.style.overflow = this.#previousBodyOverflow;
|
|
1920
|
+
}
|
|
1921
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SidebarMobileV2Component, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1922
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SidebarMobileV2Component, isStandalone: true, selector: "sidebar-mobile-v2", inputs: { menus: { classPropertyName: "menus", publicName: "menus", isSignal: true, isRequired: false, transformFunction: null }, userInfo: { classPropertyName: "userInfo", publicName: "userInfo", isSignal: true, isRequired: true, transformFunction: null }, sidebar: { classPropertyName: "sidebar", publicName: "sidebar", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"sd-sidebar-mobile-v2 d-flex flex-column\">\n <main class=\"sd-sidebar-mobile-v2__content flex-1 overflow-auto\">\n <ng-content></ng-content>\n </main>\n\n <nav class=\"sd-sidebar-mobile-v2__bar d-flex align-items-stretch bg-white border-top border-black200\" aria-label=\"\u0110i\u1EC1u h\u01B0\u1EDBng ch\u00EDnh\">\n @for (menu of primaryMenus(); track menu.id || menu.title) {\n <button\n type=\"button\"\n class=\"sd-sidebar-mobile-v2__bar-action d-flex flex-column align-items-center justify-content-center gap-4 flex-1\"\n [attr.data-mobile-menu-key]=\"menu.id ? 'id:' + menu.id : null\"\n [attr.aria-label]=\"menu.tooltipTitle || menu.title || 'Menu'\"\n [attr.aria-expanded]=\"sheetGroupKey() === (menu.id ? 'id:' + menu.id : '') && isSheetOpen()\"\n (click)=\"activateMenu(menu, $event.currentTarget)\">\n @if (menu.iconUrl) {\n <img class=\"sd-sidebar-mobile-v2__icon\" [src]=\"menu.iconUrl\" alt=\"\" />\n } @else {\n <sd-icon [name]=\"menu.icon || 'widgets'\"></sd-icon>\n }\n <span class=\"sd-sidebar-mobile-v2__label text-ellipsis\">{{ menu.tooltipTitle || menu.title }}</span>\n </button>\n }\n @if (overflowMenus().length) {\n <button\n type=\"button\"\n class=\"sd-sidebar-mobile-v2__bar-action d-flex flex-column align-items-center justify-content-center gap-4 flex-1\"\n aria-label=\"Th\u00EAm menu\"\n [attr.aria-expanded]=\"sheetGroupKey() === 'more' && isSheetOpen()\"\n (click)=\"openMore($event.currentTarget)\">\n <sd-icon name=\"more_horiz\"></sd-icon>\n <span class=\"sd-sidebar-mobile-v2__label\">Th\u00EAm</span>\n </button>\n }\n </nav>\n\n @if (isSheetOpen()) {\n <button type=\"button\" class=\"sd-sidebar-mobile-v2__backdrop\" aria-label=\"\u0110\u00F3ng menu\" (click)=\"closeSheet()\"></button>\n <section\n data-v2-sheet\n class=\"sd-sidebar-mobile-v2__sheet d-flex flex-column bg-white rounded-top-16 mat-elevation-z8\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"sheetTitle()\"\n cdkTrapFocus\n [cdkTrapFocusAutoCapture]=\"true\"\n (keydown)=\"onSheetKeydown($event)\">\n <header class=\"d-flex align-items-center justify-content-between gap-8 px-16 py-12 border-bottom border-black200\">\n <span class=\"T16M text-ellipsis\">{{ sheetTitle() }}</span>\n <button\n type=\"button\"\n class=\"sd-sidebar-mobile-v2__close d-flex align-items-center justify-content-center\"\n aria-label=\"\u0110\u00F3ng\"\n (click)=\"closeSheet()\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n </header>\n <div class=\"d-flex flex-column gap-12 p-12 overflow-auto\">\n <sd-layout-user-menu presentation=\"mobile-inline\" [userInfo]=\"userInfo()\"></sd-layout-user-menu>\n <sd-layout-search-field\n placeholder=\"T\u00ECm trong menu\"\n [autoId]=\"'layout-v2-mobile-search'\"\n [model]=\"searchText()\"\n (sdChange)=\"searchText.set($event)\">\n </sd-layout-search-field>\n <sd-layout-menu-tree\n [menus]=\"sheetMenus()\"\n [query]=\"searchText()\"\n [activePath]=\"activePath()\"\n [pinnedKeys]=\"pinnedKeys()\"\n pinVisibility=\"always\"\n (navigate)=\"onNavigate($event)\"\n (togglePinned)=\"onTogglePinned($event)\">\n </sd-layout-menu-tree>\n </div>\n </section>\n }\n</div>\n", styles: [":host{display:block}.sd-sidebar-mobile-v2{min-height:100dvh;padding-bottom:calc(64px + env(safe-area-inset-bottom));background:var(--sd-light)}.sd-sidebar-mobile-v2__bar{position:fixed;z-index:30;inset:auto 0 0;min-height:64px;padding-bottom:env(safe-area-inset-bottom)}.sd-sidebar-mobile-v2__bar-action,.sd-sidebar-mobile-v2__close{min-width:0;min-height:48px;border:0;background:transparent;color:var(--sd-black400);cursor:pointer}.sd-sidebar-mobile-v2__bar-action:hover,.sd-sidebar-mobile-v2__close:hover{color:var(--sd-primary);background:var(--sd-primary-light)}.sd-sidebar-mobile-v2__bar-action:focus-visible,.sd-sidebar-mobile-v2__close:focus-visible{outline:2px solid var(--sd-primary);outline-offset:-2px}.sd-sidebar-mobile-v2__label{max-width:72px;font-size:11px;line-height:14px}.sd-sidebar-mobile-v2__icon{width:22px;height:22px;object-fit:contain}.sd-sidebar-mobile-v2__backdrop{position:fixed;z-index:40;inset:0;border:0;background:color-mix(in srgb,var(--sd-black) 40%,transparent)}.sd-sidebar-mobile-v2__sheet{position:fixed;z-index:50;inset:auto 0 0;max-height:min(78dvh,720px);padding-bottom:env(safe-area-inset-bottom);overflow:hidden}@media(prefers-reduced-motion:reduce){.sd-sidebar-mobile-v2__sheet{transition:none}}\n"], dependencies: [{ kind: "ngmodule", type: A11yModule }, { kind: "directive", type: i1$2.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "component", type: SdLayoutSearchFieldComponent, selector: "sd-layout-search-field", inputs: ["model", "placeholder", "autoId"], outputs: ["sdChange"] }, { kind: "component", type: SdLayoutMenuTreeComponent, selector: "sd-layout-menu-tree", inputs: ["menus", "query", "activePath", "pinnedKeys", "showPin", "pinVisibility"], outputs: ["navigate", "togglePinned"] }, { kind: "component", type: SdLayoutUserMenuComponent, selector: "sd-layout-user-menu", inputs: ["userInfo", "signout", "changePassword", "updateProfile", "setting", "notification", "compact", "presentation"], outputs: ["opened", "closed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1923
|
+
}
|
|
1924
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SidebarMobileV2Component, decorators: [{
|
|
1925
|
+
type: Component,
|
|
1926
|
+
args: [{ selector: 'sidebar-mobile-v2', standalone: true, imports: [A11yModule, SdIcon, SdLayoutSearchFieldComponent, SdLayoutMenuTreeComponent, SdLayoutUserMenuComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"sd-sidebar-mobile-v2 d-flex flex-column\">\n <main class=\"sd-sidebar-mobile-v2__content flex-1 overflow-auto\">\n <ng-content></ng-content>\n </main>\n\n <nav class=\"sd-sidebar-mobile-v2__bar d-flex align-items-stretch bg-white border-top border-black200\" aria-label=\"\u0110i\u1EC1u h\u01B0\u1EDBng ch\u00EDnh\">\n @for (menu of primaryMenus(); track menu.id || menu.title) {\n <button\n type=\"button\"\n class=\"sd-sidebar-mobile-v2__bar-action d-flex flex-column align-items-center justify-content-center gap-4 flex-1\"\n [attr.data-mobile-menu-key]=\"menu.id ? 'id:' + menu.id : null\"\n [attr.aria-label]=\"menu.tooltipTitle || menu.title || 'Menu'\"\n [attr.aria-expanded]=\"sheetGroupKey() === (menu.id ? 'id:' + menu.id : '') && isSheetOpen()\"\n (click)=\"activateMenu(menu, $event.currentTarget)\">\n @if (menu.iconUrl) {\n <img class=\"sd-sidebar-mobile-v2__icon\" [src]=\"menu.iconUrl\" alt=\"\" />\n } @else {\n <sd-icon [name]=\"menu.icon || 'widgets'\"></sd-icon>\n }\n <span class=\"sd-sidebar-mobile-v2__label text-ellipsis\">{{ menu.tooltipTitle || menu.title }}</span>\n </button>\n }\n @if (overflowMenus().length) {\n <button\n type=\"button\"\n class=\"sd-sidebar-mobile-v2__bar-action d-flex flex-column align-items-center justify-content-center gap-4 flex-1\"\n aria-label=\"Th\u00EAm menu\"\n [attr.aria-expanded]=\"sheetGroupKey() === 'more' && isSheetOpen()\"\n (click)=\"openMore($event.currentTarget)\">\n <sd-icon name=\"more_horiz\"></sd-icon>\n <span class=\"sd-sidebar-mobile-v2__label\">Th\u00EAm</span>\n </button>\n }\n </nav>\n\n @if (isSheetOpen()) {\n <button type=\"button\" class=\"sd-sidebar-mobile-v2__backdrop\" aria-label=\"\u0110\u00F3ng menu\" (click)=\"closeSheet()\"></button>\n <section\n data-v2-sheet\n class=\"sd-sidebar-mobile-v2__sheet d-flex flex-column bg-white rounded-top-16 mat-elevation-z8\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"sheetTitle()\"\n cdkTrapFocus\n [cdkTrapFocusAutoCapture]=\"true\"\n (keydown)=\"onSheetKeydown($event)\">\n <header class=\"d-flex align-items-center justify-content-between gap-8 px-16 py-12 border-bottom border-black200\">\n <span class=\"T16M text-ellipsis\">{{ sheetTitle() }}</span>\n <button\n type=\"button\"\n class=\"sd-sidebar-mobile-v2__close d-flex align-items-center justify-content-center\"\n aria-label=\"\u0110\u00F3ng\"\n (click)=\"closeSheet()\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n </header>\n <div class=\"d-flex flex-column gap-12 p-12 overflow-auto\">\n <sd-layout-user-menu presentation=\"mobile-inline\" [userInfo]=\"userInfo()\"></sd-layout-user-menu>\n <sd-layout-search-field\n placeholder=\"T\u00ECm trong menu\"\n [autoId]=\"'layout-v2-mobile-search'\"\n [model]=\"searchText()\"\n (sdChange)=\"searchText.set($event)\">\n </sd-layout-search-field>\n <sd-layout-menu-tree\n [menus]=\"sheetMenus()\"\n [query]=\"searchText()\"\n [activePath]=\"activePath()\"\n [pinnedKeys]=\"pinnedKeys()\"\n pinVisibility=\"always\"\n (navigate)=\"onNavigate($event)\"\n (togglePinned)=\"onTogglePinned($event)\">\n </sd-layout-menu-tree>\n </div>\n </section>\n }\n</div>\n", styles: [":host{display:block}.sd-sidebar-mobile-v2{min-height:100dvh;padding-bottom:calc(64px + env(safe-area-inset-bottom));background:var(--sd-light)}.sd-sidebar-mobile-v2__bar{position:fixed;z-index:30;inset:auto 0 0;min-height:64px;padding-bottom:env(safe-area-inset-bottom)}.sd-sidebar-mobile-v2__bar-action,.sd-sidebar-mobile-v2__close{min-width:0;min-height:48px;border:0;background:transparent;color:var(--sd-black400);cursor:pointer}.sd-sidebar-mobile-v2__bar-action:hover,.sd-sidebar-mobile-v2__close:hover{color:var(--sd-primary);background:var(--sd-primary-light)}.sd-sidebar-mobile-v2__bar-action:focus-visible,.sd-sidebar-mobile-v2__close:focus-visible{outline:2px solid var(--sd-primary);outline-offset:-2px}.sd-sidebar-mobile-v2__label{max-width:72px;font-size:11px;line-height:14px}.sd-sidebar-mobile-v2__icon{width:22px;height:22px;object-fit:contain}.sd-sidebar-mobile-v2__backdrop{position:fixed;z-index:40;inset:0;border:0;background:color-mix(in srgb,var(--sd-black) 40%,transparent)}.sd-sidebar-mobile-v2__sheet{position:fixed;z-index:50;inset:auto 0 0;max-height:min(78dvh,720px);padding-bottom:env(safe-area-inset-bottom);overflow:hidden}@media(prefers-reduced-motion:reduce){.sd-sidebar-mobile-v2__sheet{transition:none}}\n"] }]
|
|
1927
|
+
}], ctorParameters: () => [], propDecorators: { menus: [{ type: i0.Input, args: [{ isSignal: true, alias: "menus", required: false }] }], userInfo: [{ type: i0.Input, args: [{ isSignal: true, alias: "userInfo", required: true }] }], sidebar: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebar", required: true }] }] } });
|
|
1928
|
+
|
|
1929
|
+
class SidebarV3Component {
|
|
1930
|
+
#router = inject(Router);
|
|
1931
|
+
#document = inject(DOCUMENT);
|
|
1932
|
+
#destroyRef = inject(DestroyRef);
|
|
1933
|
+
#navigationState = inject(SdLayoutNavigationStateService);
|
|
1934
|
+
#initialized = false;
|
|
1935
|
+
menus = input([], ...(ngDevMode ? [{ debugName: "menus" }] : []));
|
|
1936
|
+
userInfo = input.required(...(ngDevMode ? [{ debugName: "userInfo" }] : []));
|
|
1937
|
+
sidebar = input.required(...(ngDevMode ? [{ debugName: "sidebar" }] : []));
|
|
1938
|
+
isCollapsed = signal(false, ...(ngDevMode ? [{ debugName: "isCollapsed" }] : []));
|
|
1939
|
+
searchText = signal('', ...(ngDevMode ? [{ debugName: "searchText" }] : []));
|
|
1940
|
+
activePath = signal(this.#router.url.split(/[?#]/, 1)[0] ?? '', ...(ngDevMode ? [{ debugName: "activePath" }] : []));
|
|
1941
|
+
searchResults = computed(() => searchMenuLeaves(this.menus(), this.searchText()), ...(ngDevMode ? [{ debugName: "searchResults" }] : []));
|
|
1942
|
+
recentConfiguration = computed(() => resolveSidebarV3Recent(this.sidebar()), ...(ngDevMode ? [{ debugName: "recentConfiguration" }] : []));
|
|
1943
|
+
pinnedKeys = this.#navigationState.pinnedKeys;
|
|
1944
|
+
pinnedMenus = this.#navigationState.pinnedMenus;
|
|
1945
|
+
recentMenus = this.#navigationState.recentMenus;
|
|
1946
|
+
collapsedItems = computed(() => this.menus().map(menu => ({ menu, key: getMenuStableKey(menu), title: menu.tooltipTitle || menu.title || 'Menu' })), ...(ngDevMode ? [{ debugName: "collapsedItems" }] : []));
|
|
1947
|
+
constructor() {
|
|
1948
|
+
effect(() => {
|
|
1949
|
+
const menus = this.menus();
|
|
1950
|
+
const sidebar = this.sidebar();
|
|
1951
|
+
const recentConfiguration = resolveSidebarV3Recent(sidebar);
|
|
1952
|
+
untracked(() => {
|
|
1953
|
+
this.#navigationState.hydrate(menus, recentConfiguration.maxItems);
|
|
1954
|
+
if (this.#initialized)
|
|
1955
|
+
return;
|
|
1956
|
+
const persistedCollapsed = this.#navigationState.versionState(3).collapsed;
|
|
1957
|
+
this.isCollapsed.set(typeof persistedCollapsed === 'boolean' ? persistedCollapsed : (sidebar.defaultCollapsed ?? false));
|
|
1958
|
+
this.#initialized = true;
|
|
1959
|
+
});
|
|
1960
|
+
});
|
|
1961
|
+
this.#router.events.pipe(takeUntilDestroyed(this.#destroyRef)).subscribe(event => {
|
|
1962
|
+
if (event instanceof NavigationEnd)
|
|
1963
|
+
this.activePath.set(event.urlAfterRedirects.split(/[?#]/, 1)[0] ?? '');
|
|
1964
|
+
});
|
|
1965
|
+
}
|
|
1966
|
+
toggleCollapsed() {
|
|
1967
|
+
this.isCollapsed.update(collapsed => !collapsed);
|
|
1968
|
+
this.#navigationState.patchVersionState(3, { collapsed: this.isCollapsed() });
|
|
1969
|
+
}
|
|
1970
|
+
activateCollapsedMenu(menu) {
|
|
1971
|
+
if ('path' in menu)
|
|
1972
|
+
this.navigateMenu(menu);
|
|
1973
|
+
else {
|
|
1974
|
+
this.isCollapsed.set(false);
|
|
1975
|
+
this.#navigationState.patchVersionState(3, { collapsed: false });
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
navigateMenu(menu) {
|
|
1979
|
+
if (!('path' in menu))
|
|
1980
|
+
return;
|
|
1981
|
+
this.#navigationState.recordRecent(menu, this.recentConfiguration());
|
|
1982
|
+
if (menu.path.includes('http')) {
|
|
1983
|
+
this.#document.defaultView?.open(menu.path, '_blank', 'noopener');
|
|
1984
|
+
return;
|
|
1985
|
+
}
|
|
1986
|
+
void this.#router.navigate([menu.path.split('?')[0]], {
|
|
1987
|
+
queryParams: menu.queryParams ?? {},
|
|
1988
|
+
state: { switchTab: true },
|
|
1989
|
+
});
|
|
1990
|
+
}
|
|
1991
|
+
onTogglePinned(menu) {
|
|
1992
|
+
this.#navigationState.togglePinned(menu);
|
|
1993
|
+
}
|
|
1994
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SidebarV3Component, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1995
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SidebarV3Component, isStandalone: true, selector: "sidebar-v3", inputs: { menus: { classPropertyName: "menus", publicName: "menus", isSignal: true, isRequired: false, transformFunction: null }, userInfo: { classPropertyName: "userInfo", publicName: "userInfo", isSignal: true, isRequired: true, transformFunction: null }, sidebar: { classPropertyName: "sidebar", publicName: "sidebar", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"sd-sidebar-v3\" [class.sd-sidebar-v3--collapsed]=\"isCollapsed()\">\n <aside\n class=\"sd-sidebar-v3__drawer d-flex flex-column bg-white border-right border-black200\"\n data-v3-sidebar\n [attr.data-collapsed]=\"isCollapsed()\">\n <header\n data-v3-header\n class=\"sd-sidebar-v3__header d-flex align-items-center gap-8 p-12 border-bottom border-black200\"\n [class.sd-sidebar-v3__header--collapsed]=\"isCollapsed()\"\n [class.justify-content-between]=\"!isCollapsed()\"\n [class.justify-content-center]=\"isCollapsed()\">\n @if (!isCollapsed()) {\n <div data-v3-brand class=\"d-flex align-items-center gap-8 overflow-hidden\">\n @if (sidebar().logoUrl) {\n <img class=\"sd-sidebar-v3__logo\" [src]=\"sidebar().logoUrl\" alt=\"\" />\n } @else {\n <sd-icon name=\"apps\"></sd-icon>\n }\n <span class=\"T16M text-ellipsis\">{{ sidebar().defaultTitle || 'Back Office' }}</span>\n </div>\n }\n <button\n type=\"button\"\n class=\"sd-sidebar-v3__icon-action d-flex align-items-center justify-content-center\"\n [attr.aria-label]=\"isCollapsed() ? 'M\u1EDF r\u1ED9ng sidebar' : 'Thu g\u1ECDn sidebar'\"\n [attr.aria-expanded]=\"!isCollapsed()\"\n (click)=\"toggleCollapsed()\">\n <sd-icon [name]=\"isCollapsed() ? 'chevron_right' : 'chevron_left'\"></sd-icon>\n </button>\n </header>\n\n @if (isCollapsed()) {\n <nav class=\"d-flex flex-column align-items-center gap-4 p-8 flex-1\" aria-label=\"Menu thu g\u1ECDn\">\n @for (item of collapsedItems(); track item.key) {\n <button\n type=\"button\"\n class=\"sd-sidebar-v3__collapsed-action d-flex align-items-center justify-content-center\"\n [attr.aria-label]=\"item.title\"\n (click)=\"activateCollapsedMenu(item.menu)\">\n @if (item.menu.iconUrl) {\n <img class=\"sd-sidebar-v3__logo\" [src]=\"item.menu.iconUrl\" alt=\"\" />\n } @else {\n <sd-icon [name]=\"item.menu.icon || 'widgets'\"></sd-icon>\n }\n </button>\n }\n </nav>\n } @else {\n <div class=\"p-12\">\n <sd-layout-search-field\n placeholder=\"T\u00ECm trong t\u1EA5t c\u1EA3 menu\"\n [autoId]=\"'layout-v3-global-search'\"\n [model]=\"searchText()\"\n (sdChange)=\"searchText.set($event)\">\n </sd-layout-search-field>\n </div>\n <div class=\"sd-sidebar-v3__navigation d-flex flex-column gap-16 flex-1 overflow-auto px-8 pb-12\">\n @if (pinnedMenus().length) {\n <section data-v3-pinned class=\"d-flex flex-column gap-4\">\n <h2 class=\"T12M text-black400 px-12 m-0\">\u0110\u00E3 ghim</h2>\n <sd-layout-menu-tree\n [menus]=\"pinnedMenus()\"\n [activePath]=\"activePath()\"\n [pinnedKeys]=\"pinnedKeys()\"\n (navigate)=\"navigateMenu($event)\"\n (togglePinned)=\"onTogglePinned($event)\">\n </sd-layout-menu-tree>\n </section>\n }\n @if (recentConfiguration().enabled && recentMenus().length) {\n <section data-v3-recent class=\"d-flex flex-column gap-4\">\n <h2 class=\"T12M text-black400 px-12 m-0\">G\u1EA7n \u0111\u00E2y</h2>\n <sd-layout-menu-tree [menus]=\"recentMenus()\" [activePath]=\"activePath()\" [showPin]=\"false\" (navigate)=\"navigateMenu($event)\">\n </sd-layout-menu-tree>\n </section>\n }\n <section class=\"d-flex flex-column gap-4\">\n <h2 class=\"T12M text-black400 px-12 m-0\">T\u1EA5t c\u1EA3 menu</h2>\n <sd-layout-menu-tree\n [menus]=\"menus()\"\n [query]=\"searchText()\"\n [activePath]=\"activePath()\"\n [pinnedKeys]=\"pinnedKeys()\"\n (navigate)=\"navigateMenu($event)\"\n (togglePinned)=\"onTogglePinned($event)\">\n </sd-layout-menu-tree>\n </section>\n </div>\n }\n\n <div class=\"p-8 border-top border-black200\">\n <sd-layout-user-menu [userInfo]=\"userInfo()\" [compact]=\"isCollapsed()\"></sd-layout-user-menu>\n </div>\n </aside>\n\n <main class=\"sd-sidebar-v3__content\">\n <ng-content></ng-content>\n </main>\n</div>\n", styles: [".sd-sidebar-v3{min-height:100vh;background:var(--sd-light)}.sd-sidebar-v3__drawer{position:fixed;z-index:20;inset:0 auto 0 0;width:304px;transition:width .16s ease}.sd-sidebar-v3--collapsed .sd-sidebar-v3__drawer{width:72px}.sd-sidebar-v3__content{min-height:100vh;margin-left:304px;transition:margin-left .16s ease}.sd-sidebar-v3--collapsed .sd-sidebar-v3__content{margin-left:72px}.sd-sidebar-v3__icon-action,.sd-sidebar-v3__collapsed-action{width:40px;height:40px;border:0;border-radius:8px;background:transparent;color:var(--sd-black400);cursor:pointer}.sd-sidebar-v3__icon-action:hover,.sd-sidebar-v3__collapsed-action:hover{color:var(--sd-primary);background:var(--sd-primary-light)}.sd-sidebar-v3__icon-action:focus-visible,.sd-sidebar-v3__collapsed-action:focus-visible{outline:2px solid var(--sd-primary);outline-offset:2px}.sd-sidebar-v3__logo{width:28px;height:28px;object-fit:contain}@media(prefers-reduced-motion:reduce){.sd-sidebar-v3__drawer,.sd-sidebar-v3__content{transition:none}}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "component", type: SdLayoutSearchFieldComponent, selector: "sd-layout-search-field", inputs: ["model", "placeholder", "autoId"], outputs: ["sdChange"] }, { kind: "component", type: SdLayoutMenuTreeComponent, selector: "sd-layout-menu-tree", inputs: ["menus", "query", "activePath", "pinnedKeys", "showPin", "pinVisibility"], outputs: ["navigate", "togglePinned"] }, { kind: "component", type: SdLayoutUserMenuComponent, selector: "sd-layout-user-menu", inputs: ["userInfo", "signout", "changePassword", "updateProfile", "setting", "notification", "compact", "presentation"], outputs: ["opened", "closed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1996
|
+
}
|
|
1997
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SidebarV3Component, decorators: [{
|
|
1998
|
+
type: Component,
|
|
1999
|
+
args: [{ selector: 'sidebar-v3', standalone: true, imports: [SdIcon, SdLayoutSearchFieldComponent, SdLayoutMenuTreeComponent, SdLayoutUserMenuComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"sd-sidebar-v3\" [class.sd-sidebar-v3--collapsed]=\"isCollapsed()\">\n <aside\n class=\"sd-sidebar-v3__drawer d-flex flex-column bg-white border-right border-black200\"\n data-v3-sidebar\n [attr.data-collapsed]=\"isCollapsed()\">\n <header\n data-v3-header\n class=\"sd-sidebar-v3__header d-flex align-items-center gap-8 p-12 border-bottom border-black200\"\n [class.sd-sidebar-v3__header--collapsed]=\"isCollapsed()\"\n [class.justify-content-between]=\"!isCollapsed()\"\n [class.justify-content-center]=\"isCollapsed()\">\n @if (!isCollapsed()) {\n <div data-v3-brand class=\"d-flex align-items-center gap-8 overflow-hidden\">\n @if (sidebar().logoUrl) {\n <img class=\"sd-sidebar-v3__logo\" [src]=\"sidebar().logoUrl\" alt=\"\" />\n } @else {\n <sd-icon name=\"apps\"></sd-icon>\n }\n <span class=\"T16M text-ellipsis\">{{ sidebar().defaultTitle || 'Back Office' }}</span>\n </div>\n }\n <button\n type=\"button\"\n class=\"sd-sidebar-v3__icon-action d-flex align-items-center justify-content-center\"\n [attr.aria-label]=\"isCollapsed() ? 'M\u1EDF r\u1ED9ng sidebar' : 'Thu g\u1ECDn sidebar'\"\n [attr.aria-expanded]=\"!isCollapsed()\"\n (click)=\"toggleCollapsed()\">\n <sd-icon [name]=\"isCollapsed() ? 'chevron_right' : 'chevron_left'\"></sd-icon>\n </button>\n </header>\n\n @if (isCollapsed()) {\n <nav class=\"d-flex flex-column align-items-center gap-4 p-8 flex-1\" aria-label=\"Menu thu g\u1ECDn\">\n @for (item of collapsedItems(); track item.key) {\n <button\n type=\"button\"\n class=\"sd-sidebar-v3__collapsed-action d-flex align-items-center justify-content-center\"\n [attr.aria-label]=\"item.title\"\n (click)=\"activateCollapsedMenu(item.menu)\">\n @if (item.menu.iconUrl) {\n <img class=\"sd-sidebar-v3__logo\" [src]=\"item.menu.iconUrl\" alt=\"\" />\n } @else {\n <sd-icon [name]=\"item.menu.icon || 'widgets'\"></sd-icon>\n }\n </button>\n }\n </nav>\n } @else {\n <div class=\"p-12\">\n <sd-layout-search-field\n placeholder=\"T\u00ECm trong t\u1EA5t c\u1EA3 menu\"\n [autoId]=\"'layout-v3-global-search'\"\n [model]=\"searchText()\"\n (sdChange)=\"searchText.set($event)\">\n </sd-layout-search-field>\n </div>\n <div class=\"sd-sidebar-v3__navigation d-flex flex-column gap-16 flex-1 overflow-auto px-8 pb-12\">\n @if (pinnedMenus().length) {\n <section data-v3-pinned class=\"d-flex flex-column gap-4\">\n <h2 class=\"T12M text-black400 px-12 m-0\">\u0110\u00E3 ghim</h2>\n <sd-layout-menu-tree\n [menus]=\"pinnedMenus()\"\n [activePath]=\"activePath()\"\n [pinnedKeys]=\"pinnedKeys()\"\n (navigate)=\"navigateMenu($event)\"\n (togglePinned)=\"onTogglePinned($event)\">\n </sd-layout-menu-tree>\n </section>\n }\n @if (recentConfiguration().enabled && recentMenus().length) {\n <section data-v3-recent class=\"d-flex flex-column gap-4\">\n <h2 class=\"T12M text-black400 px-12 m-0\">G\u1EA7n \u0111\u00E2y</h2>\n <sd-layout-menu-tree [menus]=\"recentMenus()\" [activePath]=\"activePath()\" [showPin]=\"false\" (navigate)=\"navigateMenu($event)\">\n </sd-layout-menu-tree>\n </section>\n }\n <section class=\"d-flex flex-column gap-4\">\n <h2 class=\"T12M text-black400 px-12 m-0\">T\u1EA5t c\u1EA3 menu</h2>\n <sd-layout-menu-tree\n [menus]=\"menus()\"\n [query]=\"searchText()\"\n [activePath]=\"activePath()\"\n [pinnedKeys]=\"pinnedKeys()\"\n (navigate)=\"navigateMenu($event)\"\n (togglePinned)=\"onTogglePinned($event)\">\n </sd-layout-menu-tree>\n </section>\n </div>\n }\n\n <div class=\"p-8 border-top border-black200\">\n <sd-layout-user-menu [userInfo]=\"userInfo()\" [compact]=\"isCollapsed()\"></sd-layout-user-menu>\n </div>\n </aside>\n\n <main class=\"sd-sidebar-v3__content\">\n <ng-content></ng-content>\n </main>\n</div>\n", styles: [".sd-sidebar-v3{min-height:100vh;background:var(--sd-light)}.sd-sidebar-v3__drawer{position:fixed;z-index:20;inset:0 auto 0 0;width:304px;transition:width .16s ease}.sd-sidebar-v3--collapsed .sd-sidebar-v3__drawer{width:72px}.sd-sidebar-v3__content{min-height:100vh;margin-left:304px;transition:margin-left .16s ease}.sd-sidebar-v3--collapsed .sd-sidebar-v3__content{margin-left:72px}.sd-sidebar-v3__icon-action,.sd-sidebar-v3__collapsed-action{width:40px;height:40px;border:0;border-radius:8px;background:transparent;color:var(--sd-black400);cursor:pointer}.sd-sidebar-v3__icon-action:hover,.sd-sidebar-v3__collapsed-action:hover{color:var(--sd-primary);background:var(--sd-primary-light)}.sd-sidebar-v3__icon-action:focus-visible,.sd-sidebar-v3__collapsed-action:focus-visible{outline:2px solid var(--sd-primary);outline-offset:2px}.sd-sidebar-v3__logo{width:28px;height:28px;object-fit:contain}@media(prefers-reduced-motion:reduce){.sd-sidebar-v3__drawer,.sd-sidebar-v3__content{transition:none}}\n"] }]
|
|
2000
|
+
}], ctorParameters: () => [], propDecorators: { menus: [{ type: i0.Input, args: [{ isSignal: true, alias: "menus", required: false }] }], userInfo: [{ type: i0.Input, args: [{ isSignal: true, alias: "userInfo", required: true }] }], sidebar: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebar", required: true }] }] } });
|
|
2001
|
+
|
|
2002
|
+
class SidebarMobileV3Component {
|
|
2003
|
+
#router = inject(Router);
|
|
2004
|
+
#document = inject(DOCUMENT);
|
|
2005
|
+
#destroyRef = inject(DestroyRef);
|
|
2006
|
+
#navigationState = inject(SdLayoutNavigationStateService);
|
|
2007
|
+
trigger = viewChild('trigger', ...(ngDevMode ? [{ debugName: "trigger" }] : []));
|
|
2008
|
+
#restoreFocusElement = null;
|
|
2009
|
+
#previousBodyOverflow = '';
|
|
2010
|
+
menus = input([], ...(ngDevMode ? [{ debugName: "menus" }] : []));
|
|
2011
|
+
userInfo = input.required(...(ngDevMode ? [{ debugName: "userInfo" }] : []));
|
|
2012
|
+
sidebar = input.required(...(ngDevMode ? [{ debugName: "sidebar" }] : []));
|
|
2013
|
+
isDrawerOpen = signal(false, ...(ngDevMode ? [{ debugName: "isDrawerOpen" }] : []));
|
|
2014
|
+
searchText = signal('', ...(ngDevMode ? [{ debugName: "searchText" }] : []));
|
|
2015
|
+
activePath = signal(this.#router.url.split(/[?#]/, 1)[0] ?? '', ...(ngDevMode ? [{ debugName: "activePath" }] : []));
|
|
2016
|
+
searchResults = computed(() => searchMenuLeaves(this.menus(), this.searchText()), ...(ngDevMode ? [{ debugName: "searchResults" }] : []));
|
|
2017
|
+
recentConfiguration = computed(() => resolveSidebarV3Recent(this.sidebar()), ...(ngDevMode ? [{ debugName: "recentConfiguration" }] : []));
|
|
2018
|
+
pinnedKeys = this.#navigationState.pinnedKeys;
|
|
2019
|
+
pinnedMenus = this.#navigationState.pinnedMenus;
|
|
2020
|
+
recentMenus = this.#navigationState.recentMenus;
|
|
2021
|
+
constructor() {
|
|
2022
|
+
effect(() => {
|
|
2023
|
+
const menus = this.menus();
|
|
2024
|
+
const maximum = resolveSidebarV3Recent(this.sidebar()).maxItems;
|
|
2025
|
+
untracked(() => this.#navigationState.hydrate(menus, maximum));
|
|
2026
|
+
});
|
|
2027
|
+
this.#router.events.pipe(takeUntilDestroyed(this.#destroyRef)).subscribe(event => {
|
|
2028
|
+
if (!(event instanceof NavigationEnd))
|
|
2029
|
+
return;
|
|
2030
|
+
this.activePath.set(event.urlAfterRedirects.split(/[?#]/, 1)[0] ?? '');
|
|
2031
|
+
this.closeFromNavigation();
|
|
2032
|
+
});
|
|
2033
|
+
this.#destroyRef.onDestroy(() => this.#releaseBodyScroll());
|
|
2034
|
+
}
|
|
2035
|
+
openDrawer(trigger) {
|
|
2036
|
+
this.#restoreFocusElement =
|
|
2037
|
+
trigger instanceof HTMLElement
|
|
2038
|
+
? trigger
|
|
2039
|
+
: this.#document.activeElement instanceof HTMLElement
|
|
2040
|
+
? this.#document.activeElement
|
|
2041
|
+
: (this.trigger()?.nativeElement ?? null);
|
|
2042
|
+
if (!this.isDrawerOpen()) {
|
|
2043
|
+
this.#previousBodyOverflow = this.#document.body.style.overflow;
|
|
2044
|
+
this.#document.body.style.overflow = 'hidden';
|
|
2045
|
+
}
|
|
2046
|
+
this.isDrawerOpen.set(true);
|
|
2047
|
+
}
|
|
2048
|
+
closeFromBackdrop() {
|
|
2049
|
+
this.#closeDrawer();
|
|
2050
|
+
}
|
|
2051
|
+
closeFromEscape() {
|
|
2052
|
+
this.#closeDrawer();
|
|
2053
|
+
}
|
|
2054
|
+
closeFromNavigation() {
|
|
2055
|
+
this.#closeDrawer();
|
|
2056
|
+
}
|
|
2057
|
+
onDrawerKeydown(event) {
|
|
2058
|
+
if (event.key !== 'Escape')
|
|
2059
|
+
return;
|
|
2060
|
+
event.preventDefault();
|
|
2061
|
+
this.closeFromEscape();
|
|
2062
|
+
}
|
|
2063
|
+
navigateMenu(menu) {
|
|
2064
|
+
if (!('path' in menu))
|
|
2065
|
+
return;
|
|
2066
|
+
this.#navigationState.recordRecent(menu, this.recentConfiguration());
|
|
2067
|
+
if (menu.path.includes('http')) {
|
|
2068
|
+
this.#document.defaultView?.open(menu.path, '_blank', 'noopener');
|
|
2069
|
+
this.closeFromNavigation();
|
|
2070
|
+
return;
|
|
2071
|
+
}
|
|
2072
|
+
void this.#router
|
|
2073
|
+
.navigate([menu.path.split('?')[0]], { queryParams: menu.queryParams ?? {}, state: { switchTab: true } })
|
|
2074
|
+
.then(navigated => {
|
|
2075
|
+
if (navigated)
|
|
2076
|
+
this.closeFromNavigation();
|
|
2077
|
+
});
|
|
2078
|
+
}
|
|
2079
|
+
onTogglePinned(menu) {
|
|
2080
|
+
this.#navigationState.togglePinned(menu);
|
|
2081
|
+
}
|
|
2082
|
+
#closeDrawer() {
|
|
2083
|
+
if (!this.isDrawerOpen())
|
|
2084
|
+
return;
|
|
2085
|
+
this.isDrawerOpen.set(false);
|
|
2086
|
+
this.searchText.set('');
|
|
2087
|
+
this.#releaseBodyScroll();
|
|
2088
|
+
(this.#restoreFocusElement ?? this.trigger()?.nativeElement)?.focus();
|
|
2089
|
+
this.#restoreFocusElement = null;
|
|
2090
|
+
}
|
|
2091
|
+
#releaseBodyScroll() {
|
|
2092
|
+
this.#document.body.style.overflow = this.#previousBodyOverflow;
|
|
2093
|
+
}
|
|
2094
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SidebarMobileV3Component, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2095
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SidebarMobileV3Component, isStandalone: true, selector: "sidebar-mobile-v3", inputs: { menus: { classPropertyName: "menus", publicName: "menus", isSignal: true, isRequired: false, transformFunction: null }, userInfo: { classPropertyName: "userInfo", publicName: "userInfo", isSignal: true, isRequired: true, transformFunction: null }, sidebar: { classPropertyName: "sidebar", publicName: "sidebar", isSignal: true, isRequired: true, transformFunction: null } }, viewQueries: [{ propertyName: "trigger", first: true, predicate: ["trigger"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"sd-sidebar-mobile-v3 d-flex flex-column\">\n <header\n class=\"sd-sidebar-mobile-v3__topbar d-flex align-items-center justify-content-between gap-8 px-16 bg-white border-bottom border-black200\">\n <div class=\"d-flex align-items-center gap-8 overflow-hidden\">\n @if (sidebar().logoUrl) {\n <img class=\"sd-sidebar-mobile-v3__logo\" [src]=\"sidebar().logoUrl\" alt=\"\" />\n }\n <span class=\"T16M text-ellipsis\">{{ sidebar().defaultTitle || 'Back Office' }}</span>\n </div>\n <button\n #trigger\n type=\"button\"\n data-v3-mobile-trigger\n class=\"sd-sidebar-mobile-v3__icon-action d-flex align-items-center justify-content-center\"\n aria-label=\"M\u1EDF menu\"\n [attr.aria-expanded]=\"isDrawerOpen()\"\n (click)=\"openDrawer($event.currentTarget)\">\n <sd-icon name=\"menu\"></sd-icon>\n </button>\n </header>\n\n <main class=\"flex-1 overflow-auto\">\n <ng-content></ng-content>\n </main>\n\n @if (isDrawerOpen()) {\n <button type=\"button\" class=\"sd-sidebar-mobile-v3__backdrop\" aria-label=\"\u0110\u00F3ng menu\" (click)=\"closeFromBackdrop()\"></button>\n <aside\n class=\"sd-sidebar-mobile-v3__drawer d-flex flex-column bg-white mat-elevation-z8\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Menu ch\u00EDnh\"\n cdkTrapFocus\n [cdkTrapFocusAutoCapture]=\"true\"\n (keydown)=\"onDrawerKeydown($event)\">\n <header class=\"d-flex align-items-center justify-content-between gap-8 px-16 py-12 border-bottom border-black200\">\n <span class=\"T18M text-ellipsis\">{{ sidebar().defaultTitle || 'Back Office' }}</span>\n <button\n type=\"button\"\n class=\"sd-sidebar-mobile-v3__icon-action d-flex align-items-center justify-content-center\"\n aria-label=\"\u0110\u00F3ng menu\"\n (click)=\"closeFromBackdrop()\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n </header>\n <div class=\"sd-sidebar-mobile-v3__navigation d-flex flex-column gap-16 flex-1 overflow-auto p-12\">\n <sd-layout-user-menu presentation=\"mobile-inline\" [userInfo]=\"userInfo()\"></sd-layout-user-menu>\n <div class=\"sd-sidebar-mobile-v3__search bg-white\">\n <sd-layout-search-field\n placeholder=\"T\u00ECm trong t\u1EA5t c\u1EA3 menu\"\n [autoId]=\"'layout-v3-mobile-global-search'\"\n [model]=\"searchText()\"\n (sdChange)=\"searchText.set($event)\">\n </sd-layout-search-field>\n </div>\n @if (pinnedMenus().length) {\n <section data-v3-mobile-pinned class=\"d-flex flex-column gap-4\">\n <h2 class=\"T12M text-black400 px-12 m-0\">\u0110\u00E3 ghim</h2>\n <sd-layout-menu-tree\n [menus]=\"pinnedMenus()\"\n [activePath]=\"activePath()\"\n [pinnedKeys]=\"pinnedKeys()\"\n pinVisibility=\"always\"\n (navigate)=\"navigateMenu($event)\"\n (togglePinned)=\"onTogglePinned($event)\">\n </sd-layout-menu-tree>\n </section>\n }\n @if (recentConfiguration().enabled && recentMenus().length) {\n <section data-v3-mobile-recent class=\"d-flex flex-column gap-4\">\n <h2 class=\"T12M text-black400 px-12 m-0\">G\u1EA7n \u0111\u00E2y</h2>\n <sd-layout-menu-tree [menus]=\"recentMenus()\" [activePath]=\"activePath()\" [showPin]=\"false\" (navigate)=\"navigateMenu($event)\">\n </sd-layout-menu-tree>\n </section>\n }\n <section class=\"d-flex flex-column gap-4\">\n <h2 class=\"T12M text-black400 px-12 m-0\">T\u1EA5t c\u1EA3 menu</h2>\n <sd-layout-menu-tree\n [menus]=\"menus()\"\n [query]=\"searchText()\"\n [activePath]=\"activePath()\"\n [pinnedKeys]=\"pinnedKeys()\"\n pinVisibility=\"always\"\n (navigate)=\"navigateMenu($event)\"\n (togglePinned)=\"onTogglePinned($event)\">\n </sd-layout-menu-tree>\n </section>\n </div>\n </aside>\n }\n</div>\n", styles: [":host{display:block}.sd-sidebar-mobile-v3{min-height:100dvh;background:var(--sd-light)}.sd-sidebar-mobile-v3__topbar{min-height:56px}.sd-sidebar-mobile-v3__icon-action{width:40px;height:40px;border:0;border-radius:8px;background:transparent;color:var(--sd-black500);cursor:pointer}.sd-sidebar-mobile-v3__icon-action:hover{color:var(--sd-primary);background:var(--sd-primary-light)}.sd-sidebar-mobile-v3__icon-action:focus-visible{outline:2px solid var(--sd-primary);outline-offset:2px}.sd-sidebar-mobile-v3__logo{width:28px;height:28px;object-fit:contain}.sd-sidebar-mobile-v3__backdrop{position:fixed;z-index:40;inset:0;border:0;background:color-mix(in srgb,var(--sd-black) 40%,transparent)}.sd-sidebar-mobile-v3__drawer{position:fixed;z-index:50;inset:0 auto 0 0;width:min(88vw,360px);max-width:100%;padding-bottom:env(safe-area-inset-bottom)}.sd-sidebar-mobile-v3__search{position:sticky;z-index:2;top:-12px;margin:-12px;padding:12px;background:var(--sd-white, #fff)}@media(prefers-reduced-motion:reduce){.sd-sidebar-mobile-v3__drawer{transition:none}}\n"], dependencies: [{ kind: "ngmodule", type: A11yModule }, { kind: "directive", type: i1$2.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "component", type: SdLayoutSearchFieldComponent, selector: "sd-layout-search-field", inputs: ["model", "placeholder", "autoId"], outputs: ["sdChange"] }, { kind: "component", type: SdLayoutMenuTreeComponent, selector: "sd-layout-menu-tree", inputs: ["menus", "query", "activePath", "pinnedKeys", "showPin", "pinVisibility"], outputs: ["navigate", "togglePinned"] }, { kind: "component", type: SdLayoutUserMenuComponent, selector: "sd-layout-user-menu", inputs: ["userInfo", "signout", "changePassword", "updateProfile", "setting", "notification", "compact", "presentation"], outputs: ["opened", "closed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2096
|
+
}
|
|
2097
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SidebarMobileV3Component, decorators: [{
|
|
2098
|
+
type: Component,
|
|
2099
|
+
args: [{ selector: 'sidebar-mobile-v3', standalone: true, imports: [A11yModule, SdIcon, SdLayoutSearchFieldComponent, SdLayoutMenuTreeComponent, SdLayoutUserMenuComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"sd-sidebar-mobile-v3 d-flex flex-column\">\n <header\n class=\"sd-sidebar-mobile-v3__topbar d-flex align-items-center justify-content-between gap-8 px-16 bg-white border-bottom border-black200\">\n <div class=\"d-flex align-items-center gap-8 overflow-hidden\">\n @if (sidebar().logoUrl) {\n <img class=\"sd-sidebar-mobile-v3__logo\" [src]=\"sidebar().logoUrl\" alt=\"\" />\n }\n <span class=\"T16M text-ellipsis\">{{ sidebar().defaultTitle || 'Back Office' }}</span>\n </div>\n <button\n #trigger\n type=\"button\"\n data-v3-mobile-trigger\n class=\"sd-sidebar-mobile-v3__icon-action d-flex align-items-center justify-content-center\"\n aria-label=\"M\u1EDF menu\"\n [attr.aria-expanded]=\"isDrawerOpen()\"\n (click)=\"openDrawer($event.currentTarget)\">\n <sd-icon name=\"menu\"></sd-icon>\n </button>\n </header>\n\n <main class=\"flex-1 overflow-auto\">\n <ng-content></ng-content>\n </main>\n\n @if (isDrawerOpen()) {\n <button type=\"button\" class=\"sd-sidebar-mobile-v3__backdrop\" aria-label=\"\u0110\u00F3ng menu\" (click)=\"closeFromBackdrop()\"></button>\n <aside\n class=\"sd-sidebar-mobile-v3__drawer d-flex flex-column bg-white mat-elevation-z8\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Menu ch\u00EDnh\"\n cdkTrapFocus\n [cdkTrapFocusAutoCapture]=\"true\"\n (keydown)=\"onDrawerKeydown($event)\">\n <header class=\"d-flex align-items-center justify-content-between gap-8 px-16 py-12 border-bottom border-black200\">\n <span class=\"T18M text-ellipsis\">{{ sidebar().defaultTitle || 'Back Office' }}</span>\n <button\n type=\"button\"\n class=\"sd-sidebar-mobile-v3__icon-action d-flex align-items-center justify-content-center\"\n aria-label=\"\u0110\u00F3ng menu\"\n (click)=\"closeFromBackdrop()\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n </header>\n <div class=\"sd-sidebar-mobile-v3__navigation d-flex flex-column gap-16 flex-1 overflow-auto p-12\">\n <sd-layout-user-menu presentation=\"mobile-inline\" [userInfo]=\"userInfo()\"></sd-layout-user-menu>\n <div class=\"sd-sidebar-mobile-v3__search bg-white\">\n <sd-layout-search-field\n placeholder=\"T\u00ECm trong t\u1EA5t c\u1EA3 menu\"\n [autoId]=\"'layout-v3-mobile-global-search'\"\n [model]=\"searchText()\"\n (sdChange)=\"searchText.set($event)\">\n </sd-layout-search-field>\n </div>\n @if (pinnedMenus().length) {\n <section data-v3-mobile-pinned class=\"d-flex flex-column gap-4\">\n <h2 class=\"T12M text-black400 px-12 m-0\">\u0110\u00E3 ghim</h2>\n <sd-layout-menu-tree\n [menus]=\"pinnedMenus()\"\n [activePath]=\"activePath()\"\n [pinnedKeys]=\"pinnedKeys()\"\n pinVisibility=\"always\"\n (navigate)=\"navigateMenu($event)\"\n (togglePinned)=\"onTogglePinned($event)\">\n </sd-layout-menu-tree>\n </section>\n }\n @if (recentConfiguration().enabled && recentMenus().length) {\n <section data-v3-mobile-recent class=\"d-flex flex-column gap-4\">\n <h2 class=\"T12M text-black400 px-12 m-0\">G\u1EA7n \u0111\u00E2y</h2>\n <sd-layout-menu-tree [menus]=\"recentMenus()\" [activePath]=\"activePath()\" [showPin]=\"false\" (navigate)=\"navigateMenu($event)\">\n </sd-layout-menu-tree>\n </section>\n }\n <section class=\"d-flex flex-column gap-4\">\n <h2 class=\"T12M text-black400 px-12 m-0\">T\u1EA5t c\u1EA3 menu</h2>\n <sd-layout-menu-tree\n [menus]=\"menus()\"\n [query]=\"searchText()\"\n [activePath]=\"activePath()\"\n [pinnedKeys]=\"pinnedKeys()\"\n pinVisibility=\"always\"\n (navigate)=\"navigateMenu($event)\"\n (togglePinned)=\"onTogglePinned($event)\">\n </sd-layout-menu-tree>\n </section>\n </div>\n </aside>\n }\n</div>\n", styles: [":host{display:block}.sd-sidebar-mobile-v3{min-height:100dvh;background:var(--sd-light)}.sd-sidebar-mobile-v3__topbar{min-height:56px}.sd-sidebar-mobile-v3__icon-action{width:40px;height:40px;border:0;border-radius:8px;background:transparent;color:var(--sd-black500);cursor:pointer}.sd-sidebar-mobile-v3__icon-action:hover{color:var(--sd-primary);background:var(--sd-primary-light)}.sd-sidebar-mobile-v3__icon-action:focus-visible{outline:2px solid var(--sd-primary);outline-offset:2px}.sd-sidebar-mobile-v3__logo{width:28px;height:28px;object-fit:contain}.sd-sidebar-mobile-v3__backdrop{position:fixed;z-index:40;inset:0;border:0;background:color-mix(in srgb,var(--sd-black) 40%,transparent)}.sd-sidebar-mobile-v3__drawer{position:fixed;z-index:50;inset:0 auto 0 0;width:min(88vw,360px);max-width:100%;padding-bottom:env(safe-area-inset-bottom)}.sd-sidebar-mobile-v3__search{position:sticky;z-index:2;top:-12px;margin:-12px;padding:12px;background:var(--sd-white, #fff)}@media(prefers-reduced-motion:reduce){.sd-sidebar-mobile-v3__drawer{transition:none}}\n"] }]
|
|
2100
|
+
}], ctorParameters: () => [], propDecorators: { trigger: [{ type: i0.ViewChild, args: ['trigger', { isSignal: true }] }], menus: [{ type: i0.Input, args: [{ isSignal: true, alias: "menus", required: false }] }], userInfo: [{ type: i0.Input, args: [{ isSignal: true, alias: "userInfo", required: true }] }], sidebar: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebar", required: true }] }] } });
|
|
2101
|
+
|
|
1299
2102
|
class SdLayoutComponent {
|
|
1300
2103
|
// ==========================================
|
|
1301
2104
|
// INJECT SERVICES
|
|
1302
2105
|
// ==========================================
|
|
1303
2106
|
#menuPipe = inject(MenuPipe);
|
|
1304
2107
|
#layoutService = inject(SdLayoutService);
|
|
2108
|
+
#responsiveService = inject(SdLayoutResponsiveService);
|
|
2109
|
+
#layoutConfiguration = inject(SD_LAYOUT_CONFIGURATION, { optional: true });
|
|
1305
2110
|
// ==========================================
|
|
1306
2111
|
// SIGNAL INPUTS
|
|
1307
2112
|
// ==========================================
|
|
@@ -1312,13 +2117,34 @@ class SdLayoutComponent {
|
|
|
1312
2117
|
// ==========================================
|
|
1313
2118
|
userInfo = this.#layoutService.userInfo;
|
|
1314
2119
|
sidebar = this.#layoutService.sidebar;
|
|
1315
|
-
|
|
2120
|
+
sidebarV1 = computed(() => {
|
|
2121
|
+
const sidebar = this.sidebar();
|
|
2122
|
+
return sidebar?.version === 1 ? sidebar : undefined;
|
|
2123
|
+
}, ...(ngDevMode ? [{ debugName: "sidebarV1" }] : []));
|
|
2124
|
+
sidebarV2 = computed(() => {
|
|
2125
|
+
const sidebar = this.sidebar();
|
|
2126
|
+
return sidebar?.version === 2 ? sidebar : undefined;
|
|
2127
|
+
}, ...(ngDevMode ? [{ debugName: "sidebarV2" }] : []));
|
|
2128
|
+
sidebarV3 = computed(() => {
|
|
2129
|
+
const sidebar = this.sidebar();
|
|
2130
|
+
return sidebar?.version === 3 ? sidebar : undefined;
|
|
2131
|
+
}, ...(ngDevMode ? [{ debugName: "sidebarV3" }] : []));
|
|
2132
|
+
mobileBreakpoint = normalizeLayoutMobileBreakpoint(this.#layoutConfiguration?.mobileBreakpoint);
|
|
2133
|
+
isMobile = computed(() => this.#responsiveService.isMobile(this.mobileBreakpoint), ...(ngDevMode ? [{ debugName: "isMobile" }] : []));
|
|
1316
2134
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdLayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1317
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SdLayoutComponent, isStandalone: true, selector: "sd-layout", inputs: { menusInput: { classPropertyName: "menusInput", publicName: "menus", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<ng-template #projectedContent>\n <ng-content></ng-content>\n</ng-template>\n\n@let _userInfo = userInfo();\n@let
|
|
2135
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SdLayoutComponent, isStandalone: true, selector: "sd-layout", inputs: { menusInput: { classPropertyName: "menusInput", publicName: "menus", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<ng-template #projectedContent>\n <ng-content></ng-content>\n</ng-template>\n\n@let _userInfo = userInfo();\n@let _sidebarV1 = sidebarV1();\n@let _sidebarV2 = sidebarV2();\n@let _sidebarV3 = sidebarV3();\n\n@if (_userInfo && _sidebarV1) {\n @if (!isMobile()) {\n <sidebar-v1 [menus]=\"menus()\" [userInfo]=\"_userInfo\" [sidebar]=\"_sidebarV1\" [isMobile]=\"false\">\n <ng-container [ngTemplateOutlet]=\"projectedContent\"></ng-container>\n </sidebar-v1>\n } @else {\n <sidebar-mobile-v1 [menus]=\"menus()\" [userInfo]=\"_userInfo\" [sidebar]=\"_sidebarV1\">\n <ng-container [ngTemplateOutlet]=\"projectedContent\"></ng-container>\n </sidebar-mobile-v1>\n }\n}\n\n@if (_userInfo && _sidebarV3) {\n @if (!isMobile()) {\n <sidebar-v3 [menus]=\"menus()\" [userInfo]=\"_userInfo\" [sidebar]=\"_sidebarV3\">\n <ng-container [ngTemplateOutlet]=\"projectedContent\"></ng-container>\n </sidebar-v3>\n } @else {\n <sidebar-mobile-v3 [menus]=\"menus()\" [userInfo]=\"_userInfo\" [sidebar]=\"_sidebarV3\">\n <ng-container [ngTemplateOutlet]=\"projectedContent\"></ng-container>\n </sidebar-mobile-v3>\n }\n}\n\n@if (_userInfo && _sidebarV2) {\n @if (!isMobile()) {\n <sidebar-v2 [menus]=\"menus()\" [userInfo]=\"_userInfo\" [sidebar]=\"_sidebarV2\">\n <ng-container [ngTemplateOutlet]=\"projectedContent\"></ng-container>\n </sidebar-v2>\n } @else {\n <sidebar-mobile-v2 [menus]=\"menus()\" [userInfo]=\"_userInfo\" [sidebar]=\"_sidebarV2\">\n <ng-container [ngTemplateOutlet]=\"projectedContent\"></ng-container>\n </sidebar-mobile-v2>\n }\n}\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "component", type: SidebarV1Component, selector: "sidebar-v1", inputs: ["menus", "userInfo", "sidebar", "isMobile"] }, { kind: "component", type: SidebarMobileV1Component, selector: "sidebar-mobile-v1", inputs: ["menus", "userInfo", "sidebar"] }, { kind: "component", type: SidebarV2Component, selector: "sidebar-v2", inputs: ["menus", "userInfo", "sidebar"] }, { kind: "component", type: SidebarMobileV2Component, selector: "sidebar-mobile-v2", inputs: ["menus", "userInfo", "sidebar"] }, { kind: "component", type: SidebarV3Component, selector: "sidebar-v3", inputs: ["menus", "userInfo", "sidebar"] }, { kind: "component", type: SidebarMobileV3Component, selector: "sidebar-mobile-v3", inputs: ["menus", "userInfo", "sidebar"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1318
2136
|
}
|
|
1319
2137
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdLayoutComponent, decorators: [{
|
|
1320
2138
|
type: Component,
|
|
1321
|
-
args: [{ selector: 'sd-layout', imports: [
|
|
2139
|
+
args: [{ selector: 'sd-layout', imports: [
|
|
2140
|
+
SidebarV1Component,
|
|
2141
|
+
SidebarMobileV1Component,
|
|
2142
|
+
SidebarV2Component,
|
|
2143
|
+
SidebarMobileV2Component,
|
|
2144
|
+
SidebarV3Component,
|
|
2145
|
+
SidebarMobileV3Component,
|
|
2146
|
+
NgTemplateOutlet,
|
|
2147
|
+
], standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-template #projectedContent>\n <ng-content></ng-content>\n</ng-template>\n\n@let _userInfo = userInfo();\n@let _sidebarV1 = sidebarV1();\n@let _sidebarV2 = sidebarV2();\n@let _sidebarV3 = sidebarV3();\n\n@if (_userInfo && _sidebarV1) {\n @if (!isMobile()) {\n <sidebar-v1 [menus]=\"menus()\" [userInfo]=\"_userInfo\" [sidebar]=\"_sidebarV1\" [isMobile]=\"false\">\n <ng-container [ngTemplateOutlet]=\"projectedContent\"></ng-container>\n </sidebar-v1>\n } @else {\n <sidebar-mobile-v1 [menus]=\"menus()\" [userInfo]=\"_userInfo\" [sidebar]=\"_sidebarV1\">\n <ng-container [ngTemplateOutlet]=\"projectedContent\"></ng-container>\n </sidebar-mobile-v1>\n }\n}\n\n@if (_userInfo && _sidebarV3) {\n @if (!isMobile()) {\n <sidebar-v3 [menus]=\"menus()\" [userInfo]=\"_userInfo\" [sidebar]=\"_sidebarV3\">\n <ng-container [ngTemplateOutlet]=\"projectedContent\"></ng-container>\n </sidebar-v3>\n } @else {\n <sidebar-mobile-v3 [menus]=\"menus()\" [userInfo]=\"_userInfo\" [sidebar]=\"_sidebarV3\">\n <ng-container [ngTemplateOutlet]=\"projectedContent\"></ng-container>\n </sidebar-mobile-v3>\n }\n}\n\n@if (_userInfo && _sidebarV2) {\n @if (!isMobile()) {\n <sidebar-v2 [menus]=\"menus()\" [userInfo]=\"_userInfo\" [sidebar]=\"_sidebarV2\">\n <ng-container [ngTemplateOutlet]=\"projectedContent\"></ng-container>\n </sidebar-v2>\n } @else {\n <sidebar-mobile-v2 [menus]=\"menus()\" [userInfo]=\"_userInfo\" [sidebar]=\"_sidebarV2\">\n <ng-container [ngTemplateOutlet]=\"projectedContent\"></ng-container>\n </sidebar-mobile-v2>\n }\n}\n", styles: [":host{display:block}\n"] }]
|
|
1322
2148
|
}], propDecorators: { menusInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "menus", required: false }] }] } });
|
|
1323
2149
|
|
|
1324
2150
|
// End
|
|
@@ -1440,7 +2266,7 @@ const routes = [
|
|
|
1440
2266
|
];
|
|
1441
2267
|
class HomeRoutingModule {
|
|
1442
2268
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: HomeRoutingModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
1443
|
-
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.25", ngImport: i0, type: HomeRoutingModule, imports: [i1$
|
|
2269
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.25", ngImport: i0, type: HomeRoutingModule, imports: [i1$3.RouterModule], exports: [RouterModule] });
|
|
1444
2270
|
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: HomeRoutingModule, imports: [RouterModule.forChild(routes), RouterModule] });
|
|
1445
2271
|
}
|
|
1446
2272
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: HomeRoutingModule, decorators: [{
|
|
@@ -1515,7 +2341,7 @@ const Routes$2 = [{ path: '', component: RootComponent$1 }];
|
|
|
1515
2341
|
|
|
1516
2342
|
class NotFoundModule {
|
|
1517
2343
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: NotFoundModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
1518
|
-
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.25", ngImport: i0, type: NotFoundModule, imports: [CommonModule, i1$
|
|
2344
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.25", ngImport: i0, type: NotFoundModule, imports: [CommonModule, i1$3.RouterModule] });
|
|
1519
2345
|
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: NotFoundModule, imports: [CommonModule, RouterModule.forChild(Routes$2)] });
|
|
1520
2346
|
}
|
|
1521
2347
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: NotFoundModule, decorators: [{
|
|
@@ -1577,7 +2403,7 @@ const Routes$1 = [{ path: '', component: RootComponent }];
|
|
|
1577
2403
|
|
|
1578
2404
|
class ForbiddenModule {
|
|
1579
2405
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ForbiddenModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
1580
|
-
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.25", ngImport: i0, type: ForbiddenModule, imports: [CommonModule, i1$
|
|
2406
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.25", ngImport: i0, type: ForbiddenModule, imports: [CommonModule, i1$3.RouterModule] });
|
|
1581
2407
|
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ForbiddenModule, imports: [CommonModule, RouterModule.forChild(Routes$1)] });
|
|
1582
2408
|
}
|
|
1583
2409
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ForbiddenModule, decorators: [{
|
|
@@ -1615,7 +2441,7 @@ const Routes = [
|
|
|
1615
2441
|
|
|
1616
2442
|
class SdLayoutModule {
|
|
1617
2443
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdLayoutModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
1618
|
-
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.25", ngImport: i0, type: SdLayoutModule, imports: [i1$
|
|
2444
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.25", ngImport: i0, type: SdLayoutModule, imports: [i1$3.RouterModule] });
|
|
1619
2445
|
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdLayoutModule, imports: [RouterModule.forChild(Routes)] });
|
|
1620
2446
|
}
|
|
1621
2447
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdLayoutModule, decorators: [{
|
|
@@ -1631,5 +2457,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
1631
2457
|
* Generated bundle index. Do not edit.
|
|
1632
2458
|
*/
|
|
1633
2459
|
|
|
1634
|
-
export { ForbiddenModule, HomeModule, NotFoundModule, SD_LAYOUT_CONFIGURATION, SdLayoutComponent, SdLayoutModule, SdLayoutService, SdLayoutStorageService, SdPageComponent, SidebarMobileOverlayComponent, SidebarMobileV1Component };
|
|
2460
|
+
export { DEFAULT_LAYOUT_MOBILE_BREAKPOINT, ForbiddenModule, HighlightSearchPipe, HomeModule, MenuFocusPipe, MenuPipe, NotFoundModule, SD_LAYOUT_CONFIGURATION, SD_LAYOUT_VIEWPORT, SdLayoutComponent, SdLayoutMenuTreeComponent, SdLayoutModule, SdLayoutNavigationStateService, SdLayoutResponsiveService, SdLayoutService, SdLayoutStorageService, SdLayoutUserMenuComponent, SdPageComponent, SidebarMobileOverlayComponent, SidebarMobileV1Component, SidebarMobileV2Component, SidebarMobileV3Component, SidebarV2Component, SidebarV3Component, flattenMenuLeaves, getMenuStableKey, normalizeLayoutMobileBreakpoint, normalizeSidebarConfiguration, resolveMenuKeys, resolveSidebarV2Interaction, resolveSidebarV3Recent, searchMenuLeaves, selectPrimaryMenuGroups };
|
|
1635
2461
|
//# sourceMappingURL=sdcorejs-angular-modules-layout.mjs.map
|