@design-factory/angular 21.0.0-next.0
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/LICENSE +26 -0
- package/README.md +3 -0
- package/fesm2022/design-factory-angular-accordion.mjs +55 -0
- package/fesm2022/design-factory-angular-accordion.mjs.map +1 -0
- package/fesm2022/design-factory-angular-config.mjs +22 -0
- package/fesm2022/design-factory-angular-config.mjs.map +1 -0
- package/fesm2022/design-factory-angular-drawer.mjs +297 -0
- package/fesm2022/design-factory-angular-drawer.mjs.map +1 -0
- package/fesm2022/design-factory-angular-internals.mjs +111 -0
- package/fesm2022/design-factory-angular-internals.mjs.map +1 -0
- package/fesm2022/design-factory-angular-sidenav.mjs +1354 -0
- package/fesm2022/design-factory-angular-sidenav.mjs.map +1 -0
- package/fesm2022/design-factory-angular.mjs +12 -0
- package/fesm2022/design-factory-angular.mjs.map +1 -0
- package/package.json +59 -0
- package/schematics/collection.json +10 -0
- package/schematics/ng-add/index.d.ts +3 -0
- package/schematics/ng-add/index.js +65 -0
- package/schematics/ng-add/schema.d.ts +6 -0
- package/schematics/ng-add/schema.js +2 -0
- package/schematics/ng-add/schema.json +15 -0
- package/types/design-factory-angular-accordion.d.ts +47 -0
- package/types/design-factory-angular-config.d.ts +24 -0
- package/types/design-factory-angular-drawer.d.ts +136 -0
- package/types/design-factory-angular-internals.d.ts +83 -0
- package/types/design-factory-angular-sidenav.d.ts +367 -0
- package/types/design-factory-angular.d.ts +6 -0
|
@@ -0,0 +1,1354 @@
|
|
|
1
|
+
import { createNavManager, useDirectiveForHost } from '@agnos-ui/angular-headless';
|
|
2
|
+
import { NgTemplateOutlet, isPlatformBrowser } from '@angular/common';
|
|
3
|
+
import * as i0 from '@angular/core';
|
|
4
|
+
import { input, model, inject, contentChild, computed, Directive, effect, ChangeDetectionStrategy, Component, signal, viewChild, contentChildren, booleanAttribute, linkedSignal, ElementRef, PLATFORM_ID, Injector, afterRenderEffect, afterNextRender } from '@angular/core';
|
|
5
|
+
import { toSignal } from '@angular/core/rxjs-interop';
|
|
6
|
+
import * as i1$1 from '@angular/forms';
|
|
7
|
+
import { FormsModule } from '@angular/forms';
|
|
8
|
+
import { DfDrawerComponent } from '@design-factory/angular/drawer';
|
|
9
|
+
import { DfMedia } from '@design-factory/design-factory';
|
|
10
|
+
import * as i1 from '@angular/router';
|
|
11
|
+
import { RouterModule } from '@angular/router';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Base item class that is extended by desktop and mobile item components
|
|
15
|
+
* The item represents a container which can hold other items or links
|
|
16
|
+
* Handles collapsed state
|
|
17
|
+
* @internal
|
|
18
|
+
*/
|
|
19
|
+
class DfSidenavItemBaseComponent {
|
|
20
|
+
constructor() {
|
|
21
|
+
/**
|
|
22
|
+
* The label of the item
|
|
23
|
+
*/
|
|
24
|
+
this.label = input.required(...(ngDevMode ? [{ debugName: "label" }] : []));
|
|
25
|
+
/**
|
|
26
|
+
* Collapsed state of the item
|
|
27
|
+
* Only used for the desktop version
|
|
28
|
+
* @default false
|
|
29
|
+
*/
|
|
30
|
+
this.collapsed = model(false, ...(ngDevMode ? [{ debugName: "collapsed" }] : []));
|
|
31
|
+
/**
|
|
32
|
+
* The icon class name or inline content (e.g., font awesome class)
|
|
33
|
+
* @default ''
|
|
34
|
+
*/
|
|
35
|
+
this.icon = input('', ...(ngDevMode ? [{ debugName: "icon" }] : []));
|
|
36
|
+
/**
|
|
37
|
+
* Whether the wrapper component has icon content (internally used)
|
|
38
|
+
* @default false
|
|
39
|
+
*/
|
|
40
|
+
this.wrapperHasIconContent = input(false, ...(ngDevMode ? [{ debugName: "wrapperHasIconContent" }] : []));
|
|
41
|
+
this.sidenav = inject(DfSidenavComponent);
|
|
42
|
+
this.wrapperInstance = inject(DfSidenavItemComponent);
|
|
43
|
+
// check for the parent item to determine nesting level
|
|
44
|
+
this.parentItem = inject(DfSidenavItemComponent, {
|
|
45
|
+
optional: true,
|
|
46
|
+
skipSelf: true
|
|
47
|
+
});
|
|
48
|
+
this.isFirstLevel = this.parentItem?.['parentWrapper'] === null;
|
|
49
|
+
this.iconContent = contentChild('dfSidenavIcon', ...(ngDevMode ? [{ debugName: "iconContent" }] : []));
|
|
50
|
+
this.hasActiveLinks = computed(() => this.childLinks().some((link) => link?.isActiveLink()), ...(ngDevMode ? [{ debugName: "hasActiveLinks" }] : []));
|
|
51
|
+
this.hasActiveItems = computed(() => this.childItems().some((item) => item?.hasActiveLinks()), ...(ngDevMode ? [{ debugName: "hasActiveItems" }] : []));
|
|
52
|
+
this.hasActiveChildren = computed(() => this.hasActiveLinks() || this.hasActiveItems(), ...(ngDevMode ? [{ debugName: "hasActiveChildren" }] : []));
|
|
53
|
+
/**
|
|
54
|
+
* Computed signal indicating if the item has visible children
|
|
55
|
+
*/
|
|
56
|
+
this.hasVisibleChildren = computed(() => this.childLinks().some((link) => link?.['isVisible']()), ...(ngDevMode ? [{ debugName: "hasVisibleChildren" }] : []));
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Method to toggle the collapsed state of the item
|
|
60
|
+
* If the sidenav is minimized, it will expand it and uncollapse the item
|
|
61
|
+
*/
|
|
62
|
+
toggleCollapse() {
|
|
63
|
+
if (this.sidenav.isMinimized()) {
|
|
64
|
+
this.sidenav.toggleMinimize(false);
|
|
65
|
+
this.collapsed.set(false);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
this.collapsed.set(!this.collapsed());
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DfSidenavItemBaseComponent, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
72
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "21.0.6", type: DfSidenavItemBaseComponent, isStandalone: true, inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, wrapperHasIconContent: { classPropertyName: "wrapperHasIconContent", publicName: "wrapperHasIconContent", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { collapsed: "collapsedChange" }, queries: [{ propertyName: "iconContent", first: true, predicate: ["dfSidenavIcon"], descendants: true, isSignal: true }], ngImport: i0 }); }
|
|
73
|
+
}
|
|
74
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DfSidenavItemBaseComponent, decorators: [{
|
|
75
|
+
type: Directive
|
|
76
|
+
}], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }, { type: i0.Output, args: ["collapsedChange"] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], wrapperHasIconContent: [{ type: i0.Input, args: [{ isSignal: true, alias: "wrapperHasIconContent", required: false }] }], iconContent: [{ type: i0.ContentChild, args: ['dfSidenavIcon', { isSignal: true }] }] } });
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Component representing an item in the sidenav for desktop devices
|
|
80
|
+
* The item represents a container which can hold other items or links
|
|
81
|
+
* @internal
|
|
82
|
+
*/
|
|
83
|
+
class DfSidenavItemDesktopComponent extends DfSidenavItemBaseComponent {
|
|
84
|
+
constructor() {
|
|
85
|
+
super();
|
|
86
|
+
this.childLinks = computed(() => this.wrapperInstance['childrenLinks']().map((link) => link['desktopInstance']()), ...(ngDevMode ? [{ debugName: "childLinks" }] : []));
|
|
87
|
+
this.childItems = computed(() => this.wrapperInstance['childrenItems']().map((item) => item['desktopInstance']()), ...(ngDevMode ? [{ debugName: "childItems" }] : []));
|
|
88
|
+
this.isVisible = computed(() => {
|
|
89
|
+
const search = this.sidenav['searchTerm']().toLowerCase().trim() || '';
|
|
90
|
+
if (!search) {
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
return this.childLinks().some((link) => link?.['isVisible']());
|
|
94
|
+
}, ...(ngDevMode ? [{ debugName: "isVisible" }] : []));
|
|
95
|
+
// Automatically collapse when minimized
|
|
96
|
+
effect(() => {
|
|
97
|
+
if (this.sidenav.isMinimized()) {
|
|
98
|
+
this.collapsed.set(true);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
// Auto-expand when searching and has visible children
|
|
102
|
+
effect(() => {
|
|
103
|
+
if (this.sidenav['searchTerm']().trim() && this.hasVisibleChildren()) {
|
|
104
|
+
this.collapsed.set(false);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
// Auto-expand when the child is active and sidenav is not minimized
|
|
108
|
+
effect(() => {
|
|
109
|
+
if (this.hasActiveChildren() && !this.sidenav.isMinimized()) {
|
|
110
|
+
this.collapsed.set(false);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DfSidenavItemDesktopComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
115
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: DfSidenavItemDesktopComponent, isStandalone: true, selector: "df-sidenav-item-desktop", host: { properties: { "class.d-none": "!isVisible() || (sidenav.isMinimized() && (!isFirstLevel || !wrapperHasIconContent()))" } }, usesInheritance: true, ngImport: i0, template: `
|
|
116
|
+
<button
|
|
117
|
+
class="btn df-sidenav-button d-flex gap-3"
|
|
118
|
+
[class.df-sidenav-active-level]="sidenav.isMinimized() && hasActiveChildren() && isFirstLevel"
|
|
119
|
+
[class.df-sidenav-active]="hasActiveChildren()"
|
|
120
|
+
[class.w-100]="!sidenav.isMinimized()"
|
|
121
|
+
(click)="toggleCollapse()"
|
|
122
|
+
[attr.aria-expanded]="!collapsed()"
|
|
123
|
+
[class.justify-content-start]="!sidenav.isMinimized()"
|
|
124
|
+
[attr.aria-label]="label()"
|
|
125
|
+
>
|
|
126
|
+
<ng-content select="[dfSidenavIcon]" />
|
|
127
|
+
@if (icon()) {
|
|
128
|
+
<span class="{{ icon() }}"></span>
|
|
129
|
+
}
|
|
130
|
+
@if (!sidenav.isMinimized()) {
|
|
131
|
+
<span class="sidenav-label" [title]="label()">{{ label() }}</span>
|
|
132
|
+
<span
|
|
133
|
+
[class.expanded]="!collapsed()"
|
|
134
|
+
class="fa-light df-sidenav-collapse-toggle fa-chevron-down ms-auto"
|
|
135
|
+
></span>
|
|
136
|
+
}
|
|
137
|
+
</button>
|
|
138
|
+
|
|
139
|
+
<div [class.d-none]="collapsed()" role="list">
|
|
140
|
+
<ng-container [ngTemplateOutlet]="content" />
|
|
141
|
+
</div>
|
|
142
|
+
<ng-template #content>
|
|
143
|
+
<ng-content />
|
|
144
|
+
</ng-template>
|
|
145
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
146
|
+
}
|
|
147
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DfSidenavItemDesktopComponent, decorators: [{
|
|
148
|
+
type: Component,
|
|
149
|
+
args: [{
|
|
150
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
151
|
+
selector: 'df-sidenav-item-desktop',
|
|
152
|
+
template: `
|
|
153
|
+
<button
|
|
154
|
+
class="btn df-sidenav-button d-flex gap-3"
|
|
155
|
+
[class.df-sidenav-active-level]="sidenav.isMinimized() && hasActiveChildren() && isFirstLevel"
|
|
156
|
+
[class.df-sidenav-active]="hasActiveChildren()"
|
|
157
|
+
[class.w-100]="!sidenav.isMinimized()"
|
|
158
|
+
(click)="toggleCollapse()"
|
|
159
|
+
[attr.aria-expanded]="!collapsed()"
|
|
160
|
+
[class.justify-content-start]="!sidenav.isMinimized()"
|
|
161
|
+
[attr.aria-label]="label()"
|
|
162
|
+
>
|
|
163
|
+
<ng-content select="[dfSidenavIcon]" />
|
|
164
|
+
@if (icon()) {
|
|
165
|
+
<span class="{{ icon() }}"></span>
|
|
166
|
+
}
|
|
167
|
+
@if (!sidenav.isMinimized()) {
|
|
168
|
+
<span class="sidenav-label" [title]="label()">{{ label() }}</span>
|
|
169
|
+
<span
|
|
170
|
+
[class.expanded]="!collapsed()"
|
|
171
|
+
class="fa-light df-sidenav-collapse-toggle fa-chevron-down ms-auto"
|
|
172
|
+
></span>
|
|
173
|
+
}
|
|
174
|
+
</button>
|
|
175
|
+
|
|
176
|
+
<div [class.d-none]="collapsed()" role="list">
|
|
177
|
+
<ng-container [ngTemplateOutlet]="content" />
|
|
178
|
+
</div>
|
|
179
|
+
<ng-template #content>
|
|
180
|
+
<ng-content />
|
|
181
|
+
</ng-template>
|
|
182
|
+
`,
|
|
183
|
+
host: {
|
|
184
|
+
'[class.d-none]': '!isVisible() || (sidenav.isMinimized() && (!isFirstLevel || !wrapperHasIconContent()))'
|
|
185
|
+
},
|
|
186
|
+
imports: [NgTemplateOutlet]
|
|
187
|
+
}]
|
|
188
|
+
}], ctorParameters: () => [] });
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Component representing an item in the sidenav for mobile devices
|
|
192
|
+
* The item represents a container which can hold other items or links
|
|
193
|
+
* @internal
|
|
194
|
+
*/
|
|
195
|
+
class DfSidenavItemMobileComponent extends DfSidenavItemBaseComponent {
|
|
196
|
+
constructor() {
|
|
197
|
+
super(...arguments);
|
|
198
|
+
this.childLinks = computed(() => this.wrapperInstance['childrenLinks']().map((link) => link['mobileInstance']()), ...(ngDevMode ? [{ debugName: "childLinks" }] : []));
|
|
199
|
+
this.childItems = computed(() => this.wrapperInstance['childrenItems']().map((item) => item['mobileInstance']()), ...(ngDevMode ? [{ debugName: "childItems" }] : []));
|
|
200
|
+
this.isSearching = computed(() => !!this.sidenav['searchTerm']().trim(), ...(ngDevMode ? [{ debugName: "isSearching" }] : []));
|
|
201
|
+
// Check if this item is in the navigation path (is an ancestor of current panel)
|
|
202
|
+
this.isInNavigationPath = computed(() => this.sidenav['mobileNavigationStack']().includes(this.wrapperInstance), ...(ngDevMode ? [{ debugName: "isInNavigationPath" }] : []));
|
|
203
|
+
// Check if this item should show as mobile header
|
|
204
|
+
this.shouldShowMobileHeader = computed(() => {
|
|
205
|
+
// Don't show header when searching
|
|
206
|
+
if (this.isSearching()) {
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
return this.sidenav['currentMobilePanel']() === this.wrapperInstance;
|
|
210
|
+
}, ...(ngDevMode ? [{ debugName: "shouldShowMobileHeader" }] : []));
|
|
211
|
+
// Determine if this item's children should be visible
|
|
212
|
+
this.shouldShowChildren = computed(() => {
|
|
213
|
+
// Mobile with search: show children to display matching links
|
|
214
|
+
if (this.isSearching()) {
|
|
215
|
+
return this.hasMatchingChildren();
|
|
216
|
+
}
|
|
217
|
+
// Mobile without search: show if this item is in the navigation path
|
|
218
|
+
return this.isInNavigationPath();
|
|
219
|
+
}, ...(ngDevMode ? [{ debugName: "shouldShowChildren" }] : []));
|
|
220
|
+
// Determine if this item should be visible in the list
|
|
221
|
+
this.isVisibleInList = computed(() => {
|
|
222
|
+
// Mobile with search: don't show items, only links (items just provide structure)
|
|
223
|
+
if (this.isSearching()) {
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
// Mobile without search: use navigation levels
|
|
227
|
+
const currentPanel = this.sidenav['currentMobilePanel']();
|
|
228
|
+
if (currentPanel === null) {
|
|
229
|
+
// Root level: show only first-level items
|
|
230
|
+
return this.isFirstLevel && this.isVisible();
|
|
231
|
+
}
|
|
232
|
+
else if (currentPanel === this.parentItem) {
|
|
233
|
+
// This is the active panel, don't show it in the list (it's in the header)
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
// Show items whose parent matches the current panel
|
|
238
|
+
const parentMobile = this.parentItem?.['parentWrapper'];
|
|
239
|
+
return parentMobile === currentPanel && this.isVisible();
|
|
240
|
+
}
|
|
241
|
+
}, ...(ngDevMode ? [{ debugName: "isVisibleInList" }] : []));
|
|
242
|
+
// Determine if the entire component should be hidden
|
|
243
|
+
this.shouldHideComponent = computed(() => {
|
|
244
|
+
// Don't hide if this is the current mobile panel showing its children
|
|
245
|
+
if (this.shouldShowMobileHeader()) {
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
// Otherwise, use the regular visibility logic
|
|
249
|
+
return !this.isVisibleInList() && !this.shouldShowChildren();
|
|
250
|
+
}, ...(ngDevMode ? [{ debugName: "shouldHideComponent" }] : []));
|
|
251
|
+
this.isVisible = computed(() => {
|
|
252
|
+
const search = this.sidenav['searchTerm']().toLowerCase().trim() || '';
|
|
253
|
+
if (!search) {
|
|
254
|
+
return true;
|
|
255
|
+
}
|
|
256
|
+
// Check if this item's label matches
|
|
257
|
+
return String(this.label()).toLowerCase().includes(search);
|
|
258
|
+
}, ...(ngDevMode ? [{ debugName: "isVisible" }] : []));
|
|
259
|
+
// Check if this item has any matching children (for showing the container)
|
|
260
|
+
this.hasMatchingChildren = computed(() => {
|
|
261
|
+
const search = this.sidenav['searchTerm']().toLowerCase().trim() || '';
|
|
262
|
+
if (!search) {
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
return this.childLinks().some((link) => link?.['isVisible']());
|
|
266
|
+
}, ...(ngDevMode ? [{ debugName: "hasMatchingChildren" }] : []));
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Handle click event on the item
|
|
270
|
+
*/
|
|
271
|
+
handleItemClick() {
|
|
272
|
+
this.sidenav.navigateToChildren(this.wrapperInstance);
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Handle click event on the header back button
|
|
276
|
+
*/
|
|
277
|
+
handleBackClick() {
|
|
278
|
+
this.sidenav.navigateBack();
|
|
279
|
+
}
|
|
280
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DfSidenavItemMobileComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
281
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: DfSidenavItemMobileComponent, isStandalone: true, selector: "df-sidenav-item-mobile", host: { properties: { "class.d-none": "shouldHideComponent() || (sidenav.isMinimized() && (!isFirstLevel || !wrapperHasIconContent()))" }, classAttribute: "mobile" }, usesInheritance: true, ngImport: i0, template: `
|
|
282
|
+
@if (shouldShowMobileHeader()) {
|
|
283
|
+
<div class="df-sidenav-mobile-header mobile mb-2" role="listitem">
|
|
284
|
+
<button
|
|
285
|
+
class="btn w-100 d-flex gap-0 align-items-center justify-content-start df-sidenav-button"
|
|
286
|
+
(click)="handleBackClick()"
|
|
287
|
+
[class.df-sidenav-active]="hasActiveChildren()"
|
|
288
|
+
>
|
|
289
|
+
<span class="fa-light df-sidenav-mobile-header-back-arrow fa-arrow-left me-4"></span>
|
|
290
|
+
<ng-content select="[dfSidenavIcon]" />
|
|
291
|
+
@if (icon()) {
|
|
292
|
+
<span class="{{ icon() }}"></span>
|
|
293
|
+
}
|
|
294
|
+
<span class="ms-3 sidenav-label">{{ label() }}</span>
|
|
295
|
+
</button>
|
|
296
|
+
</div>
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
<!-- Regular item button (hidden when it's the active mobile panel) -->
|
|
300
|
+
@if (isVisibleInList()) {
|
|
301
|
+
<!-- Mobile search: Show as breadcrumb/path (non-clickable) -->
|
|
302
|
+
@if (isSearching()) {
|
|
303
|
+
<div class="btn w-100 d-flex gap-3 justify-content-start text-muted disabled pe-none">
|
|
304
|
+
<ng-content select="[dfSidenavIcon]" />
|
|
305
|
+
@if (icon()) {
|
|
306
|
+
<span class="{{ icon() }}"></span>
|
|
307
|
+
}
|
|
308
|
+
<span class="sidenav-label" [title]="label()">{{ label() }}</span>
|
|
309
|
+
<span class="fa-light df-sidenav-collapse-toggle fa-chevron-right ms-auto"></span>
|
|
310
|
+
</div>
|
|
311
|
+
} @else {
|
|
312
|
+
<!-- Regular clickable button -->
|
|
313
|
+
<button
|
|
314
|
+
class="btn w-100 d-flex gap-3 justify-content-start df-sidenav-button"
|
|
315
|
+
(click)="handleItemClick()"
|
|
316
|
+
[class.df-sidenav-active]="hasActiveChildren()"
|
|
317
|
+
>
|
|
318
|
+
<ng-content select="[dfSidenavIcon]" />
|
|
319
|
+
@if (icon()) {
|
|
320
|
+
<span class="{{ icon() }}"></span>
|
|
321
|
+
}
|
|
322
|
+
<span class="sidenav-label" [title]="label()">{{ label() }}</span>
|
|
323
|
+
<!-- Mobile: Show forward arrow for items with children -->
|
|
324
|
+
<span class="fa-light df-sidenav-collapse-toggle fa-chevron-right ms-auto"></span>
|
|
325
|
+
</button>
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
<!-- Children container - rendered separately from the button -->
|
|
330
|
+
@if (shouldShowChildren()) {
|
|
331
|
+
<div role="list" class="mobile">
|
|
332
|
+
<ng-container [ngTemplateOutlet]="content" />
|
|
333
|
+
</div>
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
<ng-template #content>
|
|
337
|
+
<ng-content />
|
|
338
|
+
</ng-template>
|
|
339
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
340
|
+
}
|
|
341
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DfSidenavItemMobileComponent, decorators: [{
|
|
342
|
+
type: Component,
|
|
343
|
+
args: [{
|
|
344
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
345
|
+
selector: 'df-sidenav-item-mobile',
|
|
346
|
+
template: `
|
|
347
|
+
@if (shouldShowMobileHeader()) {
|
|
348
|
+
<div class="df-sidenav-mobile-header mobile mb-2" role="listitem">
|
|
349
|
+
<button
|
|
350
|
+
class="btn w-100 d-flex gap-0 align-items-center justify-content-start df-sidenav-button"
|
|
351
|
+
(click)="handleBackClick()"
|
|
352
|
+
[class.df-sidenav-active]="hasActiveChildren()"
|
|
353
|
+
>
|
|
354
|
+
<span class="fa-light df-sidenav-mobile-header-back-arrow fa-arrow-left me-4"></span>
|
|
355
|
+
<ng-content select="[dfSidenavIcon]" />
|
|
356
|
+
@if (icon()) {
|
|
357
|
+
<span class="{{ icon() }}"></span>
|
|
358
|
+
}
|
|
359
|
+
<span class="ms-3 sidenav-label">{{ label() }}</span>
|
|
360
|
+
</button>
|
|
361
|
+
</div>
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
<!-- Regular item button (hidden when it's the active mobile panel) -->
|
|
365
|
+
@if (isVisibleInList()) {
|
|
366
|
+
<!-- Mobile search: Show as breadcrumb/path (non-clickable) -->
|
|
367
|
+
@if (isSearching()) {
|
|
368
|
+
<div class="btn w-100 d-flex gap-3 justify-content-start text-muted disabled pe-none">
|
|
369
|
+
<ng-content select="[dfSidenavIcon]" />
|
|
370
|
+
@if (icon()) {
|
|
371
|
+
<span class="{{ icon() }}"></span>
|
|
372
|
+
}
|
|
373
|
+
<span class="sidenav-label" [title]="label()">{{ label() }}</span>
|
|
374
|
+
<span class="fa-light df-sidenav-collapse-toggle fa-chevron-right ms-auto"></span>
|
|
375
|
+
</div>
|
|
376
|
+
} @else {
|
|
377
|
+
<!-- Regular clickable button -->
|
|
378
|
+
<button
|
|
379
|
+
class="btn w-100 d-flex gap-3 justify-content-start df-sidenav-button"
|
|
380
|
+
(click)="handleItemClick()"
|
|
381
|
+
[class.df-sidenav-active]="hasActiveChildren()"
|
|
382
|
+
>
|
|
383
|
+
<ng-content select="[dfSidenavIcon]" />
|
|
384
|
+
@if (icon()) {
|
|
385
|
+
<span class="{{ icon() }}"></span>
|
|
386
|
+
}
|
|
387
|
+
<span class="sidenav-label" [title]="label()">{{ label() }}</span>
|
|
388
|
+
<!-- Mobile: Show forward arrow for items with children -->
|
|
389
|
+
<span class="fa-light df-sidenav-collapse-toggle fa-chevron-right ms-auto"></span>
|
|
390
|
+
</button>
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
<!-- Children container - rendered separately from the button -->
|
|
395
|
+
@if (shouldShowChildren()) {
|
|
396
|
+
<div role="list" class="mobile">
|
|
397
|
+
<ng-container [ngTemplateOutlet]="content" />
|
|
398
|
+
</div>
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
<ng-template #content>
|
|
402
|
+
<ng-content />
|
|
403
|
+
</ng-template>
|
|
404
|
+
`,
|
|
405
|
+
host: {
|
|
406
|
+
class: 'mobile',
|
|
407
|
+
'[class.d-none]': 'shouldHideComponent() || (sidenav.isMinimized() && (!isFirstLevel || !wrapperHasIconContent()))'
|
|
408
|
+
},
|
|
409
|
+
imports: [NgTemplateOutlet]
|
|
410
|
+
}]
|
|
411
|
+
}] });
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Base link class that is extended by desktop and mobile link components
|
|
415
|
+
* @internal
|
|
416
|
+
*/
|
|
417
|
+
class DfSidenavLinkBaseComponent {
|
|
418
|
+
constructor() {
|
|
419
|
+
/**
|
|
420
|
+
* The router link path for to the link
|
|
421
|
+
*/
|
|
422
|
+
this.linkPath = input.required(...(ngDevMode ? [{ debugName: "linkPath" }] : []));
|
|
423
|
+
/**
|
|
424
|
+
* The label of the link
|
|
425
|
+
*/
|
|
426
|
+
this.label = input.required(...(ngDevMode ? [{ debugName: "label" }] : []));
|
|
427
|
+
/**
|
|
428
|
+
* The icon class name or inline content (e.g., font awesome class)
|
|
429
|
+
*/
|
|
430
|
+
this.icon = input('', ...(ngDevMode ? [{ debugName: "icon" }] : []));
|
|
431
|
+
/**
|
|
432
|
+
* Whether the wrapper component has icon content (internally used)
|
|
433
|
+
*/
|
|
434
|
+
this.wrapperHasIconContent = input(false, ...(ngDevMode ? [{ debugName: "wrapperHasIconContent" }] : []));
|
|
435
|
+
/**
|
|
436
|
+
* Indicates if the link is currently active based on the Router state
|
|
437
|
+
*/
|
|
438
|
+
this.isActiveLink = signal(false, ...(ngDevMode ? [{ debugName: "isActiveLink" }] : []));
|
|
439
|
+
/**
|
|
440
|
+
* Options to determine when the router link is considered active
|
|
441
|
+
*/
|
|
442
|
+
this.routerLinkActiveOptions = input({ exact: false }, ...(ngDevMode ? [{ debugName: "routerLinkActiveOptions" }] : []));
|
|
443
|
+
this.isSmallScreen = toSignal(inject(DfMedia).getObservable(['sm', 'xs']), { initialValue: false });
|
|
444
|
+
this.sidenav = inject(DfSidenavComponent);
|
|
445
|
+
this.parentItem = inject(DfSidenavItemComponent, {
|
|
446
|
+
optional: true,
|
|
447
|
+
skipSelf: true
|
|
448
|
+
});
|
|
449
|
+
this.isFirstLevel = this.parentItem === null;
|
|
450
|
+
this.iconContent = contentChild('dfSidenavIcon', ...(ngDevMode ? [{ debugName: "iconContent" }] : []));
|
|
451
|
+
/**
|
|
452
|
+
* Computed signal indicating if the link is visible based on search term
|
|
453
|
+
*/
|
|
454
|
+
this.isVisible = computed(() => {
|
|
455
|
+
const search = this.sidenav['searchTerm']().toLowerCase().trim() || '';
|
|
456
|
+
if (!search) {
|
|
457
|
+
return true;
|
|
458
|
+
}
|
|
459
|
+
return this.label().toLowerCase().includes(search);
|
|
460
|
+
}, ...(ngDevMode ? [{ debugName: "isVisible" }] : []));
|
|
461
|
+
}
|
|
462
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DfSidenavLinkBaseComponent, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
463
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "21.0.6", type: DfSidenavLinkBaseComponent, isStandalone: true, inputs: { linkPath: { classPropertyName: "linkPath", publicName: "linkPath", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, wrapperHasIconContent: { classPropertyName: "wrapperHasIconContent", publicName: "wrapperHasIconContent", isSignal: true, isRequired: false, transformFunction: null }, routerLinkActiveOptions: { classPropertyName: "routerLinkActiveOptions", publicName: "routerLinkActiveOptions", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "iconContent", first: true, predicate: ["dfSidenavIcon"], descendants: true, isSignal: true }], ngImport: i0 }); }
|
|
464
|
+
}
|
|
465
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DfSidenavLinkBaseComponent, decorators: [{
|
|
466
|
+
type: Directive
|
|
467
|
+
}], propDecorators: { linkPath: [{ type: i0.Input, args: [{ isSignal: true, alias: "linkPath", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], wrapperHasIconContent: [{ type: i0.Input, args: [{ isSignal: true, alias: "wrapperHasIconContent", required: false }] }], routerLinkActiveOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "routerLinkActiveOptions", required: false }] }], iconContent: [{ type: i0.ContentChild, args: ['dfSidenavIcon', { isSignal: true }] }] } });
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Component representing a leaf in the sidenav for desktop devices
|
|
471
|
+
* @internal
|
|
472
|
+
*/
|
|
473
|
+
class DfSidenavLinkDesktopComponent extends DfSidenavLinkBaseComponent {
|
|
474
|
+
constructor() {
|
|
475
|
+
super(...arguments);
|
|
476
|
+
this.anchor = viewChild.required('anchor');
|
|
477
|
+
}
|
|
478
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DfSidenavLinkDesktopComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
479
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: DfSidenavLinkDesktopComponent, isStandalone: true, selector: "df-sidenav-link-desktop", host: { properties: { "class.d-none": "!isVisible() || (sidenav.isMinimized() && (!isFirstLevel || !wrapperHasIconContent()))" }, classAttribute: "d-flex" }, viewQueries: [{ propertyName: "anchor", first: true, predicate: ["anchor"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: `
|
|
480
|
+
<a
|
|
481
|
+
class="btn df-sidenav-button d-flex gap-3"
|
|
482
|
+
[class.w-100]="!sidenav.isMinimized()"
|
|
483
|
+
routerLinkActive="df-sidenav-active-level"
|
|
484
|
+
ariaCurrentWhenActive="page"
|
|
485
|
+
(isActiveChange)="isActiveLink.set($event)"
|
|
486
|
+
[routerLink]="linkPath()"
|
|
487
|
+
[class.justify-content-start]="!sidenav.isMinimized()"
|
|
488
|
+
[routerLinkActiveOptions]="routerLinkActiveOptions()"
|
|
489
|
+
[attr.aria-label]="label()"
|
|
490
|
+
(click)="isSmallScreen() && sidenav.toggleMinimize()"
|
|
491
|
+
#anchor
|
|
492
|
+
>
|
|
493
|
+
<ng-container [ngTemplateOutlet]="content" />
|
|
494
|
+
</a>
|
|
495
|
+
<ng-template #content>
|
|
496
|
+
<ng-content select="[dfSidenavIcon]" />
|
|
497
|
+
@if (icon()) {
|
|
498
|
+
<span class="{{ icon() }}"></span>
|
|
499
|
+
}
|
|
500
|
+
@if (!sidenav.isMinimized()) {
|
|
501
|
+
<span class="sidenav-label" [title]="label()">{{ label() }}</span>
|
|
502
|
+
}
|
|
503
|
+
</ng-template>
|
|
504
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i1.RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
505
|
+
}
|
|
506
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DfSidenavLinkDesktopComponent, decorators: [{
|
|
507
|
+
type: Component,
|
|
508
|
+
args: [{
|
|
509
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
510
|
+
selector: 'df-sidenav-link-desktop',
|
|
511
|
+
template: `
|
|
512
|
+
<a
|
|
513
|
+
class="btn df-sidenav-button d-flex gap-3"
|
|
514
|
+
[class.w-100]="!sidenav.isMinimized()"
|
|
515
|
+
routerLinkActive="df-sidenav-active-level"
|
|
516
|
+
ariaCurrentWhenActive="page"
|
|
517
|
+
(isActiveChange)="isActiveLink.set($event)"
|
|
518
|
+
[routerLink]="linkPath()"
|
|
519
|
+
[class.justify-content-start]="!sidenav.isMinimized()"
|
|
520
|
+
[routerLinkActiveOptions]="routerLinkActiveOptions()"
|
|
521
|
+
[attr.aria-label]="label()"
|
|
522
|
+
(click)="isSmallScreen() && sidenav.toggleMinimize()"
|
|
523
|
+
#anchor
|
|
524
|
+
>
|
|
525
|
+
<ng-container [ngTemplateOutlet]="content" />
|
|
526
|
+
</a>
|
|
527
|
+
<ng-template #content>
|
|
528
|
+
<ng-content select="[dfSidenavIcon]" />
|
|
529
|
+
@if (icon()) {
|
|
530
|
+
<span class="{{ icon() }}"></span>
|
|
531
|
+
}
|
|
532
|
+
@if (!sidenav.isMinimized()) {
|
|
533
|
+
<span class="sidenav-label" [title]="label()">{{ label() }}</span>
|
|
534
|
+
}
|
|
535
|
+
</ng-template>
|
|
536
|
+
`,
|
|
537
|
+
host: {
|
|
538
|
+
'[class.d-none]': '!isVisible() || (sidenav.isMinimized() && (!isFirstLevel || !wrapperHasIconContent()))',
|
|
539
|
+
class: 'd-flex'
|
|
540
|
+
},
|
|
541
|
+
imports: [RouterModule, NgTemplateOutlet]
|
|
542
|
+
}]
|
|
543
|
+
}], propDecorators: { anchor: [{ type: i0.ViewChild, args: ['anchor', { isSignal: true }] }] } });
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Component representing a leaf in the sidenav for mobile devices
|
|
547
|
+
* @internal
|
|
548
|
+
*/
|
|
549
|
+
class DfSidenavLinkMobileComponent extends DfSidenavLinkBaseComponent {
|
|
550
|
+
constructor() {
|
|
551
|
+
super(...arguments);
|
|
552
|
+
// Inject link wrapper to get parent wrapper reference
|
|
553
|
+
this.linkWrapper = inject(DfSidenavLinkComponent);
|
|
554
|
+
this.currentMobilePanel = computed(() => this.sidenav['currentMobilePanel']() ?? null, ...(ngDevMode ? [{ debugName: "currentMobilePanel" }] : []));
|
|
555
|
+
this.isSearching = computed(() => !!this.sidenav['searchTerm']().trim(), ...(ngDevMode ? [{ debugName: "isSearching" }] : []));
|
|
556
|
+
this.isVisibleInList = computed(() => {
|
|
557
|
+
const currentPanel = this.currentMobilePanel();
|
|
558
|
+
const parentWrapper = this.linkWrapper['parentWrapper'];
|
|
559
|
+
// Mobile with search: show all matching links regardless of level
|
|
560
|
+
if (this.isSearching()) {
|
|
561
|
+
return this.isVisible();
|
|
562
|
+
}
|
|
563
|
+
// Mobile without search: use navigation levels
|
|
564
|
+
if (currentPanel === null) {
|
|
565
|
+
// Root level: show only first-level links
|
|
566
|
+
return this.isFirstLevel && this.isVisible();
|
|
567
|
+
}
|
|
568
|
+
else {
|
|
569
|
+
// Show links whose parent item wrapper matches the current panel
|
|
570
|
+
return parentWrapper === currentPanel && this.isVisible();
|
|
571
|
+
}
|
|
572
|
+
}, ...(ngDevMode ? [{ debugName: "isVisibleInList" }] : []));
|
|
573
|
+
// Build breadcrumb path for mobile search view
|
|
574
|
+
this.breadcrumbPath = computed(() => {
|
|
575
|
+
const path = [];
|
|
576
|
+
let currentItem = this.parentItem;
|
|
577
|
+
// Walk up the parent chain
|
|
578
|
+
while (currentItem?.label()) {
|
|
579
|
+
path.unshift(String(currentItem.label()));
|
|
580
|
+
const grandParent = currentItem?.['parentWrapper'];
|
|
581
|
+
currentItem = grandParent || null;
|
|
582
|
+
}
|
|
583
|
+
return path;
|
|
584
|
+
}, ...(ngDevMode ? [{ debugName: "breadcrumbPath" }] : []));
|
|
585
|
+
}
|
|
586
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DfSidenavLinkMobileComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
587
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: DfSidenavLinkMobileComponent, isStandalone: true, selector: "df-sidenav-link-mobile", host: { properties: { "class.d-none": "!isVisibleInList() || (sidenav.isMinimized() && (!isFirstLevel || !wrapperHasIconContent()))" }, classAttribute: "d-flex" }, usesInheritance: true, ngImport: i0, template: `
|
|
588
|
+
<a
|
|
589
|
+
class="btn df-sidenav-button d-flex gap-3 justify-content-start w-100 df-sidenav-breadcrumb-button"
|
|
590
|
+
routerLinkActive="df-sidenav-active-level"
|
|
591
|
+
ariaCurrentWhenActive="page"
|
|
592
|
+
(isActiveChange)="isActiveLink.set($event)"
|
|
593
|
+
[routerLink]="linkPath()"
|
|
594
|
+
[routerLinkActiveOptions]="routerLinkActiveOptions()"
|
|
595
|
+
(click)="isSmallScreen() && sidenav.toggleMinimize()"
|
|
596
|
+
>
|
|
597
|
+
<ng-container [ngTemplateOutlet]="content" />
|
|
598
|
+
</a>
|
|
599
|
+
<ng-template #content>
|
|
600
|
+
<div class="d-flex gap-3 w-100">
|
|
601
|
+
<ng-content select="[dfSidenavIcon]" />
|
|
602
|
+
@if (icon()) {
|
|
603
|
+
<span class="{{ icon() }}"></span>
|
|
604
|
+
}
|
|
605
|
+
<div class="d-flex flex-column align-items-start">
|
|
606
|
+
<span class="sidenav-label" [title]="label()">{{ label() }}</span>
|
|
607
|
+
@if (isSearching() && breadcrumbPath().length > 0) {
|
|
608
|
+
<small class="d-flex align-items-center gap-1" [class.text-muted]="!isActiveLink()">
|
|
609
|
+
@for (item of breadcrumbPath(); track $index) {
|
|
610
|
+
<span>{{ item }}</span>
|
|
611
|
+
@if (!$last) {
|
|
612
|
+
<span class="fa-arrow-right"></span>
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
</small>
|
|
616
|
+
}
|
|
617
|
+
</div>
|
|
618
|
+
</div>
|
|
619
|
+
</ng-template>
|
|
620
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i1.RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
621
|
+
}
|
|
622
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DfSidenavLinkMobileComponent, decorators: [{
|
|
623
|
+
type: Component,
|
|
624
|
+
args: [{
|
|
625
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
626
|
+
selector: 'df-sidenav-link-mobile',
|
|
627
|
+
template: `
|
|
628
|
+
<a
|
|
629
|
+
class="btn df-sidenav-button d-flex gap-3 justify-content-start w-100 df-sidenav-breadcrumb-button"
|
|
630
|
+
routerLinkActive="df-sidenav-active-level"
|
|
631
|
+
ariaCurrentWhenActive="page"
|
|
632
|
+
(isActiveChange)="isActiveLink.set($event)"
|
|
633
|
+
[routerLink]="linkPath()"
|
|
634
|
+
[routerLinkActiveOptions]="routerLinkActiveOptions()"
|
|
635
|
+
(click)="isSmallScreen() && sidenav.toggleMinimize()"
|
|
636
|
+
>
|
|
637
|
+
<ng-container [ngTemplateOutlet]="content" />
|
|
638
|
+
</a>
|
|
639
|
+
<ng-template #content>
|
|
640
|
+
<div class="d-flex gap-3 w-100">
|
|
641
|
+
<ng-content select="[dfSidenavIcon]" />
|
|
642
|
+
@if (icon()) {
|
|
643
|
+
<span class="{{ icon() }}"></span>
|
|
644
|
+
}
|
|
645
|
+
<div class="d-flex flex-column align-items-start">
|
|
646
|
+
<span class="sidenav-label" [title]="label()">{{ label() }}</span>
|
|
647
|
+
@if (isSearching() && breadcrumbPath().length > 0) {
|
|
648
|
+
<small class="d-flex align-items-center gap-1" [class.text-muted]="!isActiveLink()">
|
|
649
|
+
@for (item of breadcrumbPath(); track $index) {
|
|
650
|
+
<span>{{ item }}</span>
|
|
651
|
+
@if (!$last) {
|
|
652
|
+
<span class="fa-arrow-right"></span>
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
</small>
|
|
656
|
+
}
|
|
657
|
+
</div>
|
|
658
|
+
</div>
|
|
659
|
+
</ng-template>
|
|
660
|
+
`,
|
|
661
|
+
host: {
|
|
662
|
+
'[class.d-none]': '!isVisibleInList() || (sidenav.isMinimized() && (!isFirstLevel || !wrapperHasIconContent()))',
|
|
663
|
+
class: 'd-flex'
|
|
664
|
+
},
|
|
665
|
+
imports: [RouterModule, NgTemplateOutlet]
|
|
666
|
+
}]
|
|
667
|
+
}] });
|
|
668
|
+
|
|
669
|
+
/**
|
|
670
|
+
* Utility directive for marking icon content in sidenav items and links.
|
|
671
|
+
* Needed for identification of the projected icon content.
|
|
672
|
+
* Use in case if there is no icon provided with font-awesome.
|
|
673
|
+
* @since 21.0.0
|
|
674
|
+
*/
|
|
675
|
+
class DfSidenavIconDirective {
|
|
676
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DfSidenavIconDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
677
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.6", type: DfSidenavIconDirective, isStandalone: true, selector: "[dfSidenavIcon]", ngImport: i0 }); }
|
|
678
|
+
}
|
|
679
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DfSidenavIconDirective, decorators: [{
|
|
680
|
+
type: Directive,
|
|
681
|
+
args: [{
|
|
682
|
+
selector: '[dfSidenavIcon]'
|
|
683
|
+
}]
|
|
684
|
+
}] });
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* Sidenav link that can be used inside <df-sidenav-item> or directly inside the <df-sidenav>
|
|
688
|
+
* @since 21.0.0
|
|
689
|
+
*/
|
|
690
|
+
class DfSidenavLinkComponent {
|
|
691
|
+
constructor() {
|
|
692
|
+
/**
|
|
693
|
+
* The router link path for to the link
|
|
694
|
+
*/
|
|
695
|
+
this.linkPath = input.required(...(ngDevMode ? [{ debugName: "linkPath" }] : []));
|
|
696
|
+
/**
|
|
697
|
+
* The label of the link
|
|
698
|
+
*/
|
|
699
|
+
this.label = input.required(...(ngDevMode ? [{ debugName: "label" }] : []));
|
|
700
|
+
/**
|
|
701
|
+
* The icon class name or inline content (e.g., font awesome class)
|
|
702
|
+
*/
|
|
703
|
+
this.icon = input('', ...(ngDevMode ? [{ debugName: "icon" }] : []));
|
|
704
|
+
/**
|
|
705
|
+
* Options to determine when the router link is considered active
|
|
706
|
+
*/
|
|
707
|
+
this.routerLinkActiveOptions = input({ exact: false }, ...(ngDevMode ? [{ debugName: "routerLinkActiveOptions" }] : []));
|
|
708
|
+
/**
|
|
709
|
+
* The parent wrapper item component (if any)
|
|
710
|
+
*/
|
|
711
|
+
this.parentWrapper = inject(DfSidenavItemComponent, {
|
|
712
|
+
optional: true,
|
|
713
|
+
skipSelf: true
|
|
714
|
+
});
|
|
715
|
+
/**
|
|
716
|
+
* Reference to the desktop link component instance
|
|
717
|
+
*/
|
|
718
|
+
this.desktopInstance = viewChild(DfSidenavLinkDesktopComponent, ...(ngDevMode ? [{ debugName: "desktopInstance" }] : []));
|
|
719
|
+
/**
|
|
720
|
+
* Reference to the mobile link component instance
|
|
721
|
+
*/
|
|
722
|
+
this.mobileInstance = viewChild(DfSidenavLinkMobileComponent, ...(ngDevMode ? [{ debugName: "mobileInstance" }] : []));
|
|
723
|
+
this.iconContentQuery = contentChild(DfSidenavIconDirective, ...(ngDevMode ? [{ debugName: "iconContentQuery" }] : []));
|
|
724
|
+
this.wrapperHasIconContent = computed(() => this.icon() !== '' || this.iconContentQuery() !== undefined, ...(ngDevMode ? [{ debugName: "wrapperHasIconContent" }] : []));
|
|
725
|
+
this.sidenav = inject(DfSidenavComponent);
|
|
726
|
+
}
|
|
727
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DfSidenavLinkComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
728
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: DfSidenavLinkComponent, isStandalone: true, selector: "df-sidenav-link", inputs: { linkPath: { classPropertyName: "linkPath", publicName: "linkPath", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, routerLinkActiveOptions: { classPropertyName: "routerLinkActiveOptions", publicName: "routerLinkActiveOptions", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "listitem" }, properties: { "class.mobile": "sidenav['isMobileDevice']" }, classAttribute: "d-block" }, queries: [{ propertyName: "iconContentQuery", first: true, predicate: DfSidenavIconDirective, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "desktopInstance", first: true, predicate: DfSidenavLinkDesktopComponent, descendants: true, isSignal: true }, { propertyName: "mobileInstance", first: true, predicate: DfSidenavLinkMobileComponent, descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
729
|
+
<ng-template #iconSlot>
|
|
730
|
+
<ng-content select="[dfSidenavIcon]" />
|
|
731
|
+
</ng-template>
|
|
732
|
+
<ng-template #defaultContent>
|
|
733
|
+
<ng-content />
|
|
734
|
+
</ng-template>
|
|
735
|
+
|
|
736
|
+
@if (sidenav['isMobileDevice']) {
|
|
737
|
+
<df-sidenav-link-mobile
|
|
738
|
+
[linkPath]="linkPath()"
|
|
739
|
+
[label]="label()"
|
|
740
|
+
[icon]="icon()"
|
|
741
|
+
[wrapperHasIconContent]="wrapperHasIconContent()"
|
|
742
|
+
[routerLinkActiveOptions]="routerLinkActiveOptions()"
|
|
743
|
+
>
|
|
744
|
+
<ng-container [ngTemplateOutlet]="iconSlot" ngProjectAs="[dfSidenavIcon]" />
|
|
745
|
+
<ng-container [ngTemplateOutlet]="defaultContent" />
|
|
746
|
+
</df-sidenav-link-mobile>
|
|
747
|
+
} @else {
|
|
748
|
+
<df-sidenav-link-desktop
|
|
749
|
+
[linkPath]="linkPath()"
|
|
750
|
+
[label]="label()"
|
|
751
|
+
[icon]="icon()"
|
|
752
|
+
[wrapperHasIconContent]="wrapperHasIconContent()"
|
|
753
|
+
[routerLinkActiveOptions]="routerLinkActiveOptions()"
|
|
754
|
+
>
|
|
755
|
+
<ng-container [ngTemplateOutlet]="iconSlot" ngProjectAs="[dfSidenavIcon]" />
|
|
756
|
+
<ng-container [ngTemplateOutlet]="defaultContent" />
|
|
757
|
+
</df-sidenav-link-desktop>
|
|
758
|
+
}
|
|
759
|
+
`, isInline: true, dependencies: [{ kind: "component", type: DfSidenavLinkDesktopComponent, selector: "df-sidenav-link-desktop" }, { kind: "component", type: DfSidenavLinkMobileComponent, selector: "df-sidenav-link-mobile" }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
760
|
+
}
|
|
761
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DfSidenavLinkComponent, decorators: [{
|
|
762
|
+
type: Component,
|
|
763
|
+
args: [{
|
|
764
|
+
selector: 'df-sidenav-link',
|
|
765
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
766
|
+
template: `
|
|
767
|
+
<ng-template #iconSlot>
|
|
768
|
+
<ng-content select="[dfSidenavIcon]" />
|
|
769
|
+
</ng-template>
|
|
770
|
+
<ng-template #defaultContent>
|
|
771
|
+
<ng-content />
|
|
772
|
+
</ng-template>
|
|
773
|
+
|
|
774
|
+
@if (sidenav['isMobileDevice']) {
|
|
775
|
+
<df-sidenav-link-mobile
|
|
776
|
+
[linkPath]="linkPath()"
|
|
777
|
+
[label]="label()"
|
|
778
|
+
[icon]="icon()"
|
|
779
|
+
[wrapperHasIconContent]="wrapperHasIconContent()"
|
|
780
|
+
[routerLinkActiveOptions]="routerLinkActiveOptions()"
|
|
781
|
+
>
|
|
782
|
+
<ng-container [ngTemplateOutlet]="iconSlot" ngProjectAs="[dfSidenavIcon]" />
|
|
783
|
+
<ng-container [ngTemplateOutlet]="defaultContent" />
|
|
784
|
+
</df-sidenav-link-mobile>
|
|
785
|
+
} @else {
|
|
786
|
+
<df-sidenav-link-desktop
|
|
787
|
+
[linkPath]="linkPath()"
|
|
788
|
+
[label]="label()"
|
|
789
|
+
[icon]="icon()"
|
|
790
|
+
[wrapperHasIconContent]="wrapperHasIconContent()"
|
|
791
|
+
[routerLinkActiveOptions]="routerLinkActiveOptions()"
|
|
792
|
+
>
|
|
793
|
+
<ng-container [ngTemplateOutlet]="iconSlot" ngProjectAs="[dfSidenavIcon]" />
|
|
794
|
+
<ng-container [ngTemplateOutlet]="defaultContent" />
|
|
795
|
+
</df-sidenav-link-desktop>
|
|
796
|
+
}
|
|
797
|
+
`,
|
|
798
|
+
imports: [DfSidenavLinkDesktopComponent, DfSidenavLinkMobileComponent, NgTemplateOutlet],
|
|
799
|
+
host: {
|
|
800
|
+
role: 'listitem',
|
|
801
|
+
class: 'd-block',
|
|
802
|
+
'[class.mobile]': "sidenav['isMobileDevice']"
|
|
803
|
+
}
|
|
804
|
+
}]
|
|
805
|
+
}], propDecorators: { linkPath: [{ type: i0.Input, args: [{ isSignal: true, alias: "linkPath", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], routerLinkActiveOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "routerLinkActiveOptions", required: false }] }], desktopInstance: [{ type: i0.ViewChild, args: [i0.forwardRef(() => DfSidenavLinkDesktopComponent), { isSignal: true }] }], mobileInstance: [{ type: i0.ViewChild, args: [i0.forwardRef(() => DfSidenavLinkMobileComponent), { isSignal: true }] }], iconContentQuery: [{ type: i0.ContentChild, args: [i0.forwardRef(() => DfSidenavIconDirective), { isSignal: true }] }] } });
|
|
806
|
+
|
|
807
|
+
/**
|
|
808
|
+
* Sidenav element that should be nested inside <df-sidenav> or <df-sidenav-item>
|
|
809
|
+
* @since 21.0.0
|
|
810
|
+
*/
|
|
811
|
+
class DfSidenavItemComponent {
|
|
812
|
+
constructor() {
|
|
813
|
+
/**
|
|
814
|
+
* Label of the sidenav item
|
|
815
|
+
*/
|
|
816
|
+
this.label = input.required(...(ngDevMode ? [{ debugName: "label" }] : []));
|
|
817
|
+
/**
|
|
818
|
+
* Flag whether the sidenav item is collapsed
|
|
819
|
+
* @default false
|
|
820
|
+
*/
|
|
821
|
+
this.collapsed = model(false, ...(ngDevMode ? [{ debugName: "collapsed" }] : []));
|
|
822
|
+
/**
|
|
823
|
+
* Optional icon class for the sidenav item
|
|
824
|
+
*/
|
|
825
|
+
this.icon = input('', ...(ngDevMode ? [{ debugName: "icon" }] : []));
|
|
826
|
+
/**
|
|
827
|
+
* Reference to the parent element containing this item
|
|
828
|
+
*/
|
|
829
|
+
this.parentWrapper = inject(DfSidenavItemComponent, {
|
|
830
|
+
optional: true,
|
|
831
|
+
skipSelf: true
|
|
832
|
+
});
|
|
833
|
+
/**
|
|
834
|
+
* Child sidenav items nested inside this item
|
|
835
|
+
*/
|
|
836
|
+
this.childrenItems = contentChildren(DfSidenavItemComponent, { ...(ngDevMode ? { debugName: "childrenItems" } : {}), descendants: true });
|
|
837
|
+
/**
|
|
838
|
+
* Child sidenav links nested inside this item
|
|
839
|
+
*/
|
|
840
|
+
this.childrenLinks = contentChildren(DfSidenavLinkComponent, { ...(ngDevMode ? { debugName: "childrenLinks" } : {}), descendants: true });
|
|
841
|
+
/**
|
|
842
|
+
* Reference to the desktop instance of the sidenav item
|
|
843
|
+
*/
|
|
844
|
+
this.desktopInstance = viewChild(DfSidenavItemDesktopComponent, ...(ngDevMode ? [{ debugName: "desktopInstance" }] : []));
|
|
845
|
+
/**
|
|
846
|
+
* Reference to the mobile instance of the sidenav item
|
|
847
|
+
*/
|
|
848
|
+
this.mobileInstance = viewChild(DfSidenavItemMobileComponent, ...(ngDevMode ? [{ debugName: "mobileInstance" }] : []));
|
|
849
|
+
this.iconContentQuery = contentChild(DfSidenavIconDirective, ...(ngDevMode ? [{ debugName: "iconContentQuery" }] : []));
|
|
850
|
+
this.wrapperHasIconContent = computed(() => this.icon() !== '' || this.iconContentQuery() !== undefined, ...(ngDevMode ? [{ debugName: "wrapperHasIconContent" }] : []));
|
|
851
|
+
this.sidenav = inject(DfSidenavComponent);
|
|
852
|
+
}
|
|
853
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DfSidenavItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
854
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: DfSidenavItemComponent, isStandalone: true, selector: "df-sidenav-item", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { collapsed: "collapsedChange" }, host: { attributes: { "role": "listitem" }, properties: { "class.mobile": "sidenav['isMobileDevice']" }, classAttribute: "d-block" }, queries: [{ propertyName: "childrenItems", predicate: DfSidenavItemComponent, descendants: true, isSignal: true }, { propertyName: "childrenLinks", predicate: DfSidenavLinkComponent, descendants: true, isSignal: true }, { propertyName: "iconContentQuery", first: true, predicate: DfSidenavIconDirective, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "desktopInstance", first: true, predicate: DfSidenavItemDesktopComponent, descendants: true, isSignal: true }, { propertyName: "mobileInstance", first: true, predicate: DfSidenavItemMobileComponent, descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
855
|
+
<ng-template #iconSlot>
|
|
856
|
+
<ng-content select="[dfSidenavIcon]" />
|
|
857
|
+
</ng-template>
|
|
858
|
+
<ng-template #content>
|
|
859
|
+
<ng-content />
|
|
860
|
+
</ng-template>
|
|
861
|
+
|
|
862
|
+
@if (sidenav['isMobileDevice']) {
|
|
863
|
+
<df-sidenav-item-mobile
|
|
864
|
+
[label]="label()"
|
|
865
|
+
[(collapsed)]="collapsed"
|
|
866
|
+
[icon]="icon()"
|
|
867
|
+
[wrapperHasIconContent]="wrapperHasIconContent()"
|
|
868
|
+
>
|
|
869
|
+
<ng-container [ngTemplateOutlet]="iconSlot" ngProjectAs="[dfSidenavIcon]" />
|
|
870
|
+
<ng-container [ngTemplateOutlet]="content" />
|
|
871
|
+
</df-sidenav-item-mobile>
|
|
872
|
+
} @else {
|
|
873
|
+
<df-sidenav-item-desktop
|
|
874
|
+
[label]="label()"
|
|
875
|
+
[(collapsed)]="collapsed"
|
|
876
|
+
[icon]="icon()"
|
|
877
|
+
[wrapperHasIconContent]="wrapperHasIconContent()"
|
|
878
|
+
>
|
|
879
|
+
<ng-container [ngTemplateOutlet]="iconSlot" ngProjectAs="[dfSidenavIcon]" />
|
|
880
|
+
<ng-container [ngTemplateOutlet]="content" />
|
|
881
|
+
</df-sidenav-item-desktop>
|
|
882
|
+
}
|
|
883
|
+
`, isInline: true, dependencies: [{ kind: "component", type: DfSidenavItemDesktopComponent, selector: "df-sidenav-item-desktop" }, { kind: "component", type: DfSidenavItemMobileComponent, selector: "df-sidenav-item-mobile" }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
884
|
+
}
|
|
885
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DfSidenavItemComponent, decorators: [{
|
|
886
|
+
type: Component,
|
|
887
|
+
args: [{
|
|
888
|
+
selector: 'df-sidenav-item',
|
|
889
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
890
|
+
template: `
|
|
891
|
+
<ng-template #iconSlot>
|
|
892
|
+
<ng-content select="[dfSidenavIcon]" />
|
|
893
|
+
</ng-template>
|
|
894
|
+
<ng-template #content>
|
|
895
|
+
<ng-content />
|
|
896
|
+
</ng-template>
|
|
897
|
+
|
|
898
|
+
@if (sidenav['isMobileDevice']) {
|
|
899
|
+
<df-sidenav-item-mobile
|
|
900
|
+
[label]="label()"
|
|
901
|
+
[(collapsed)]="collapsed"
|
|
902
|
+
[icon]="icon()"
|
|
903
|
+
[wrapperHasIconContent]="wrapperHasIconContent()"
|
|
904
|
+
>
|
|
905
|
+
<ng-container [ngTemplateOutlet]="iconSlot" ngProjectAs="[dfSidenavIcon]" />
|
|
906
|
+
<ng-container [ngTemplateOutlet]="content" />
|
|
907
|
+
</df-sidenav-item-mobile>
|
|
908
|
+
} @else {
|
|
909
|
+
<df-sidenav-item-desktop
|
|
910
|
+
[label]="label()"
|
|
911
|
+
[(collapsed)]="collapsed"
|
|
912
|
+
[icon]="icon()"
|
|
913
|
+
[wrapperHasIconContent]="wrapperHasIconContent()"
|
|
914
|
+
>
|
|
915
|
+
<ng-container [ngTemplateOutlet]="iconSlot" ngProjectAs="[dfSidenavIcon]" />
|
|
916
|
+
<ng-container [ngTemplateOutlet]="content" />
|
|
917
|
+
</df-sidenav-item-desktop>
|
|
918
|
+
}
|
|
919
|
+
`,
|
|
920
|
+
imports: [DfSidenavItemDesktopComponent, DfSidenavItemMobileComponent, NgTemplateOutlet],
|
|
921
|
+
host: {
|
|
922
|
+
role: 'listitem',
|
|
923
|
+
class: 'd-block',
|
|
924
|
+
'[class.mobile]': "sidenav['isMobileDevice']"
|
|
925
|
+
}
|
|
926
|
+
}]
|
|
927
|
+
}], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }, { type: i0.Output, args: ["collapsedChange"] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], childrenItems: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DfSidenavItemComponent), { ...{ descendants: true }, isSignal: true }] }], childrenLinks: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DfSidenavLinkComponent), { ...{ descendants: true }, isSignal: true }] }], desktopInstance: [{ type: i0.ViewChild, args: [i0.forwardRef(() => DfSidenavItemDesktopComponent), { isSignal: true }] }], mobileInstance: [{ type: i0.ViewChild, args: [i0.forwardRef(() => DfSidenavItemMobileComponent), { isSignal: true }] }], iconContentQuery: [{ type: i0.ContentChild, args: [i0.forwardRef(() => DfSidenavIconDirective), { isSignal: true }] }] } });
|
|
928
|
+
|
|
929
|
+
/**
|
|
930
|
+
* Utility method to check if the user agent is a mobile device
|
|
931
|
+
*
|
|
932
|
+
* @param userAgent - The user agent string to check
|
|
933
|
+
* @returns `true` if the user agent corresponds to a mobile device, `false` otherwise
|
|
934
|
+
*/
|
|
935
|
+
function isMobileUserAgent(userAgent) {
|
|
936
|
+
return /mobile|android|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(userAgent);
|
|
937
|
+
}
|
|
938
|
+
/**
|
|
939
|
+
* SideNav is a component to provide navigation feature with a panel on the side of your page
|
|
940
|
+
* @since 21.0.0
|
|
941
|
+
*/
|
|
942
|
+
class DfSidenavComponent {
|
|
943
|
+
constructor() {
|
|
944
|
+
/**
|
|
945
|
+
* Flag whether the sidenav includes a search input
|
|
946
|
+
* @default false
|
|
947
|
+
*/
|
|
948
|
+
this.searchable = input(false, { ...(ngDevMode ? { debugName: "searchable" } : {}), transform: booleanAttribute });
|
|
949
|
+
/**
|
|
950
|
+
* The container element for the sidenav (null for body)
|
|
951
|
+
* @default null
|
|
952
|
+
*/
|
|
953
|
+
this.container = input(null, ...(ngDevMode ? [{ debugName: "container" }] : []));
|
|
954
|
+
/**
|
|
955
|
+
* Flag whether the sidenav should be resizable
|
|
956
|
+
* @default false
|
|
957
|
+
*/
|
|
958
|
+
this.resizable = input(false, { ...(ngDevMode ? { debugName: "resizable" } : {}), transform: booleanAttribute });
|
|
959
|
+
/**
|
|
960
|
+
* Flag whether the sidenav should have the collapse button
|
|
961
|
+
* @default false
|
|
962
|
+
*/
|
|
963
|
+
this.collapsible = input(false, { ...(ngDevMode ? { debugName: "collapsible" } : {}), transform: booleanAttribute });
|
|
964
|
+
/**
|
|
965
|
+
* Flag to enable mobile device detection for responsive behavior
|
|
966
|
+
* @default true
|
|
967
|
+
*/
|
|
968
|
+
this.enableMobile = input(true, { ...(ngDevMode ? { debugName: "enableMobile" } : {}), transform: booleanAttribute });
|
|
969
|
+
this._isMinimized = linkedSignal(() => {
|
|
970
|
+
// in mobile devices, the default value is true (hiding the drawer) and does not track changes to the screen size
|
|
971
|
+
if (this.isMobileDevice) {
|
|
972
|
+
return true;
|
|
973
|
+
}
|
|
974
|
+
// in desktop, we track the large screen signal in order to reset the minimized state
|
|
975
|
+
// it allows resizing the window and have a more user-friendly behavior
|
|
976
|
+
return !this.isLargeUpScreen();
|
|
977
|
+
}, ...(ngDevMode ? [{ debugName: "_isMinimized" }] : []));
|
|
978
|
+
/**
|
|
979
|
+
* Flag to indicate if the sidenav is minimized
|
|
980
|
+
*/
|
|
981
|
+
this.isMinimized = computed(() => this._isMinimized(), ...(ngDevMode ? [{ debugName: "isMinimized" }] : []));
|
|
982
|
+
this.hostRef = inject((ElementRef));
|
|
983
|
+
this.scrollableContainer = viewChild.required('scrollableContainer');
|
|
984
|
+
this.isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
985
|
+
this.isMobileDevice = (() => {
|
|
986
|
+
if (this.isBrowser) {
|
|
987
|
+
return this.enableMobile() && isMobileUserAgent(navigator.userAgent);
|
|
988
|
+
}
|
|
989
|
+
else {
|
|
990
|
+
// SSR: try to get request if available
|
|
991
|
+
try {
|
|
992
|
+
const request = inject(Request, { optional: true });
|
|
993
|
+
if (request && 'headers' in request) {
|
|
994
|
+
const userAgent = request.headers.get('user-agent') || '';
|
|
995
|
+
return this.enableMobile() && isMobileUserAgent(userAgent);
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
catch {
|
|
999
|
+
// Request token not available or not in SSR context
|
|
1000
|
+
}
|
|
1001
|
+
return false;
|
|
1002
|
+
}
|
|
1003
|
+
})();
|
|
1004
|
+
this.searchInput = viewChild('textInput', ...(ngDevMode ? [{ debugName: "searchInput" }] : []));
|
|
1005
|
+
// Query all links to find active ones
|
|
1006
|
+
this.allLinks = contentChildren(DfSidenavLinkComponent, { ...(ngDevMode ? { debugName: "allLinks" } : {}), descendants: true });
|
|
1007
|
+
this.allItems = contentChildren(DfSidenavItemComponent, { ...(ngDevMode ? { debugName: "allItems" } : {}), descendants: true });
|
|
1008
|
+
this.width = signal(null, ...(ngDevMode ? [{ debugName: "width" }] : []));
|
|
1009
|
+
this.savedWidth = null;
|
|
1010
|
+
this.searchTerm = signal('', ...(ngDevMode ? [{ debugName: "searchTerm" }] : []));
|
|
1011
|
+
// Mobile navigation stack - tracks which item's children are currently displayed
|
|
1012
|
+
// Using component references instead of labels for uniqueness
|
|
1013
|
+
this.mobileNavigationStack = signal([], ...(ngDevMode ? [{ debugName: "mobileNavigationStack" }] : []));
|
|
1014
|
+
this.currentMobilePanel = computed(() => {
|
|
1015
|
+
const stack = this.mobileNavigationStack();
|
|
1016
|
+
return stack.length > 0 ? stack[stack.length - 1] : null;
|
|
1017
|
+
}, ...(ngDevMode ? [{ debugName: "currentMobilePanel" }] : []));
|
|
1018
|
+
this.isSmallScreen = toSignal(inject(DfMedia).getObservable(['sm', 'xs']), { initialValue: false });
|
|
1019
|
+
this.isMediumScreen = toSignal(inject(DfMedia).getObservable(['md']), { initialValue: false });
|
|
1020
|
+
this.isLargeUpScreen = toSignal(inject(DfMedia).getObservable(['lg', 'xl', 'xxl', 'xxxl']), {
|
|
1021
|
+
initialValue: false
|
|
1022
|
+
});
|
|
1023
|
+
this.isMobileOrSmallScreen = computed(() => this.isSmallScreen() || this.isMediumScreen(), ...(ngDevMode ? [{ debugName: "isMobileOrSmallScreen" }] : []));
|
|
1024
|
+
this.backdrop = computed(() => this.isMediumScreen(), ...(ngDevMode ? [{ debugName: "backdrop" }] : []));
|
|
1025
|
+
this.navManager = createNavManager();
|
|
1026
|
+
this.navManagerConfig = {
|
|
1027
|
+
selector: (node) => node.querySelectorAll('input, button, a'),
|
|
1028
|
+
keys: {
|
|
1029
|
+
Home: this.navManager.focusFirst,
|
|
1030
|
+
End: this.navManager.focusLast
|
|
1031
|
+
}
|
|
1032
|
+
};
|
|
1033
|
+
this.injector = inject(Injector);
|
|
1034
|
+
this.contentHasIcons = computed(() => this.allItems().some((item) => item['wrapperHasIconContent']()) ||
|
|
1035
|
+
this.allLinks().some((link) => link['wrapperHasIconContent']()), ...(ngDevMode ? [{ debugName: "contentHasIcons" }] : []));
|
|
1036
|
+
useDirectiveForHost(this.navManager.directive, this.navManagerConfig);
|
|
1037
|
+
afterRenderEffect(() => {
|
|
1038
|
+
if (this.isBrowser) {
|
|
1039
|
+
if (this.isMobileDevice && !this._isMinimized()) {
|
|
1040
|
+
// Delay to ensure DOM and contentChildren queries are ready
|
|
1041
|
+
this.navigateToActiveItem();
|
|
1042
|
+
}
|
|
1043
|
+
// Keep track of the screen size changes to reset width
|
|
1044
|
+
if (this.isMediumScreen() || this.isSmallScreen() || this.isLargeUpScreen()) {
|
|
1045
|
+
this.width.set(null);
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
});
|
|
1049
|
+
}
|
|
1050
|
+
ngAfterContentInit() {
|
|
1051
|
+
if (!this.isMobileDevice && this.isBrowser && this.isLargeUpScreen()) {
|
|
1052
|
+
void this.initializeDesktopNavigation();
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
/**
|
|
1056
|
+
* This method allows to center and show the active link, when the sidenav is used in desktop and a large screen.
|
|
1057
|
+
* It collapses all items and expands only the parents of the active link.
|
|
1058
|
+
* Also, it scrolls to the active link to make it visible.
|
|
1059
|
+
*/
|
|
1060
|
+
async initializeDesktopNavigation() {
|
|
1061
|
+
// RouterLinkActive queues two microtasks, so we wait for them to complete
|
|
1062
|
+
await Promise.resolve();
|
|
1063
|
+
await Promise.resolve();
|
|
1064
|
+
// Find the first active link
|
|
1065
|
+
const activeLink = this.allLinks()
|
|
1066
|
+
.find((link) => link['desktopInstance']()?.isActiveLink())?.['desktopInstance']?.();
|
|
1067
|
+
if (activeLink) {
|
|
1068
|
+
this.allItems().forEach((item) => {
|
|
1069
|
+
item.collapsed.set(true);
|
|
1070
|
+
});
|
|
1071
|
+
let currentParent = activeLink['parentItem'];
|
|
1072
|
+
while (currentParent) {
|
|
1073
|
+
currentParent.collapsed.set(false);
|
|
1074
|
+
currentParent = currentParent['parentWrapper'];
|
|
1075
|
+
}
|
|
1076
|
+
// We need a render to make sure the getBoundingClientRect calls will be correct AFTER the collapse of the items.
|
|
1077
|
+
afterNextRender({
|
|
1078
|
+
read: () => {
|
|
1079
|
+
const anchorElement = activeLink['anchor']().nativeElement;
|
|
1080
|
+
const containerElement = this.scrollableContainer().nativeElement;
|
|
1081
|
+
const heightPx = anchorElement.getBoundingClientRect().top +
|
|
1082
|
+
anchorElement.clientHeight / 2 -
|
|
1083
|
+
containerElement.getBoundingClientRect().top -
|
|
1084
|
+
containerElement.clientHeight / 3;
|
|
1085
|
+
containerElement.scrollTo({ top: heightPx, behavior: 'instant' });
|
|
1086
|
+
}
|
|
1087
|
+
}, { injector: this.injector });
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
/**
|
|
1091
|
+
* Toggles the minimized state of the sidenav
|
|
1092
|
+
*
|
|
1093
|
+
* @param minimized - Optional parameter to explicitly set the minimized state
|
|
1094
|
+
*
|
|
1095
|
+
*/
|
|
1096
|
+
toggleMinimize(minimized = !this._isMinimized()) {
|
|
1097
|
+
if (minimized) {
|
|
1098
|
+
this.savedWidth = this.width();
|
|
1099
|
+
this.width.set(0);
|
|
1100
|
+
}
|
|
1101
|
+
else {
|
|
1102
|
+
this.width.set(this.savedWidth);
|
|
1103
|
+
}
|
|
1104
|
+
this._isMinimized.set(minimized);
|
|
1105
|
+
}
|
|
1106
|
+
/**
|
|
1107
|
+
* Handles visibility changes of the sidenav component to handle the component state accordingly.
|
|
1108
|
+
*
|
|
1109
|
+
* @param isVisible - Boolean indicating whether the sidenav is currently visible
|
|
1110
|
+
*/
|
|
1111
|
+
visibleChangeHandler(isVisible) {
|
|
1112
|
+
if (!isVisible) {
|
|
1113
|
+
this.width.set(0);
|
|
1114
|
+
}
|
|
1115
|
+
this._isMinimized.set(!isVisible);
|
|
1116
|
+
}
|
|
1117
|
+
/**
|
|
1118
|
+
* Handles changes when the sidenav is resized, to handle the state accordingly.
|
|
1119
|
+
*
|
|
1120
|
+
* @param isResizing - Whether the sidenav is currently being resized
|
|
1121
|
+
*/
|
|
1122
|
+
resizingChangeHandler(isResizing) {
|
|
1123
|
+
if (isResizing) {
|
|
1124
|
+
this._isMinimized.set(false);
|
|
1125
|
+
}
|
|
1126
|
+
else {
|
|
1127
|
+
if (this._isMinimized()) {
|
|
1128
|
+
this.width.set(0);
|
|
1129
|
+
}
|
|
1130
|
+
else {
|
|
1131
|
+
this.savedWidth = this.width();
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
// utility to transfer focus from the button when minimized to the search input when uncollapsed
|
|
1136
|
+
focusSearch() {
|
|
1137
|
+
this.toggleMinimize(false);
|
|
1138
|
+
setTimeout(() => {
|
|
1139
|
+
this.searchInput()?.nativeElement.focus();
|
|
1140
|
+
});
|
|
1141
|
+
}
|
|
1142
|
+
/**
|
|
1143
|
+
* Method to add the given item to the mobile navigation stack
|
|
1144
|
+
* @param itemRef item to be added to the navigation stack
|
|
1145
|
+
*/
|
|
1146
|
+
navigateToChildren(itemRef) {
|
|
1147
|
+
const stack = this.mobileNavigationStack();
|
|
1148
|
+
this.mobileNavigationStack.set([...stack, itemRef]);
|
|
1149
|
+
}
|
|
1150
|
+
/**
|
|
1151
|
+
* Method to navigate back in the mobile navigation stack
|
|
1152
|
+
*/
|
|
1153
|
+
navigateBack() {
|
|
1154
|
+
const stack = this.mobileNavigationStack();
|
|
1155
|
+
if (stack.length > 0) {
|
|
1156
|
+
this.mobileNavigationStack.set(stack.slice(0, -1));
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
/**
|
|
1160
|
+
* Navigates to the active item in the sidenav
|
|
1161
|
+
* Expands parent items (desktop) and sets up mobile navigation stack
|
|
1162
|
+
*/
|
|
1163
|
+
navigateToActiveItem() {
|
|
1164
|
+
if (!this.isBrowser) {
|
|
1165
|
+
return;
|
|
1166
|
+
}
|
|
1167
|
+
// Find the first active link
|
|
1168
|
+
const activeLink = this.allLinks()
|
|
1169
|
+
.find((link) => link['mobileInstance']()?.isActiveLink())?.['mobileInstance']();
|
|
1170
|
+
if (activeLink && activeLink['parentItem']) {
|
|
1171
|
+
// Build the path from root to the active link's parent (inclusive)
|
|
1172
|
+
const navigationPath = [];
|
|
1173
|
+
let current = activeLink['parentItem'];
|
|
1174
|
+
while (current) {
|
|
1175
|
+
navigationPath.unshift(current); // Add to beginning to build path from root
|
|
1176
|
+
current = current['parentWrapper'];
|
|
1177
|
+
}
|
|
1178
|
+
this.mobileNavigationStack.set(navigationPath);
|
|
1179
|
+
}
|
|
1180
|
+
else if (activeLink && !activeLink['parentItem']) {
|
|
1181
|
+
// Clear navigation stack to show root level
|
|
1182
|
+
this.mobileNavigationStack.set([]);
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DfSidenavComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1186
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: DfSidenavComponent, isStandalone: true, selector: "df-sidenav", inputs: { searchable: { classPropertyName: "searchable", publicName: "searchable", isSignal: true, isRequired: false, transformFunction: null }, container: { classPropertyName: "container", publicName: "container", isSignal: true, isRequired: false, transformFunction: null }, resizable: { classPropertyName: "resizable", publicName: "resizable", isSignal: true, isRequired: false, transformFunction: null }, collapsible: { classPropertyName: "collapsible", publicName: "collapsible", isSignal: true, isRequired: false, transformFunction: null }, enableMobile: { classPropertyName: "enableMobile", publicName: "enableMobile", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.df-sidenav-lg": "isLargeUpScreen()", "class.df-sidenav-withicon": "contentHasIcons()", "class.d-none": "isSmallScreen() && _isMinimized()", "class.df-sidenav-drawer": "isMediumScreen() && !_isMinimized()" }, classAttribute: "df-sidenav" }, queries: [{ propertyName: "allLinks", predicate: DfSidenavLinkComponent, descendants: true, isSignal: true }, { propertyName: "allItems", predicate: DfSidenavItemComponent, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "scrollableContainer", first: true, predicate: ["scrollableContainer"], descendants: true, isSignal: true }, { propertyName: "searchInput", first: true, predicate: ["textInput"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
1187
|
+
<df-drawer
|
|
1188
|
+
#drawer
|
|
1189
|
+
className="inline-start {{ isSmallScreen() ? 'df-sidenav-mobile-drawer' : '' }}"
|
|
1190
|
+
[visible]="!_isMinimized()"
|
|
1191
|
+
(visibleChange)="visibleChangeHandler($event)"
|
|
1192
|
+
(minimizedChange)="_isMinimized.set($event)"
|
|
1193
|
+
(resizingChange)="resizingChangeHandler($event)"
|
|
1194
|
+
[bodyScroll]="true"
|
|
1195
|
+
[backdrop]="backdrop()"
|
|
1196
|
+
[(size)]="width"
|
|
1197
|
+
[container]="isSmallScreen() ? null : hostRef.nativeElement"
|
|
1198
|
+
[resizable]="resizable() && !isSmallScreen()"
|
|
1199
|
+
>
|
|
1200
|
+
<nav class="d-flex flex-column w-100 h-100 p-5 pb-3">
|
|
1201
|
+
<div class="df-sidenav-header flex-shrink-0" [class.df-sidenav-header-minimized]="_isMinimized()">
|
|
1202
|
+
@if (searchable()) {
|
|
1203
|
+
@if (_isMinimized()) {
|
|
1204
|
+
<button
|
|
1205
|
+
class="btn btn-outline-neutral df-btn-icononly mb-3 w-100 fa-search"
|
|
1206
|
+
[class.df-sidenav-search-minimized]="!contentHasIcons()"
|
|
1207
|
+
type="button"
|
|
1208
|
+
(click)="focusSearch()"
|
|
1209
|
+
i18n-aria-label="@@df.sidenav.search.ariaLabel"
|
|
1210
|
+
aria-label="Search"
|
|
1211
|
+
></button>
|
|
1212
|
+
} @else {
|
|
1213
|
+
<div class="input-group mb-3">
|
|
1214
|
+
<div class="input-group-prepend">
|
|
1215
|
+
<span class="input-group-text fa-light fa-search"></span>
|
|
1216
|
+
</div>
|
|
1217
|
+
<input
|
|
1218
|
+
type="search"
|
|
1219
|
+
class="form-control df-input-withicon"
|
|
1220
|
+
i18n-placeholder="@@df.sidenav.search.placeholder"
|
|
1221
|
+
placeholder="Search"
|
|
1222
|
+
i18n-aria-label="@@df.sidenav.search.ariaLabel"
|
|
1223
|
+
aria-label="Search"
|
|
1224
|
+
[(ngModel)]="searchTerm"
|
|
1225
|
+
#textInput
|
|
1226
|
+
/>
|
|
1227
|
+
<div class="input-group-append" [class.df-is-empty]="searchTerm() === ''">
|
|
1228
|
+
<button class="input-group-text fa-light fa-times" (click)="searchTerm.set(''); textInput.focus()">
|
|
1229
|
+
<span class="visually-hidden" i18n="@@df.sidenav.search.clearText">Clear text</span>
|
|
1230
|
+
</button>
|
|
1231
|
+
</div>
|
|
1232
|
+
</div>
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
</div>
|
|
1236
|
+
|
|
1237
|
+
<div role="list" class="sidenav-body flex-grow-1 overflow-y-auto" #scrollableContainer>
|
|
1238
|
+
<ng-content />
|
|
1239
|
+
</div>
|
|
1240
|
+
@if (collapsible() && !isSmallScreen()) {
|
|
1241
|
+
<div class="d-flex sidenav-footer flex-shrink-0 py-3">
|
|
1242
|
+
<button
|
|
1243
|
+
class="btn btn-outline-primary df-btn-icononly df-sidenav-toggle-minimized"
|
|
1244
|
+
(click)="toggleMinimize()"
|
|
1245
|
+
i18n-aria-label="@@df.sidenav.toggleMinimize.ariaLabel"
|
|
1246
|
+
aria-label="Toggle minimized sidenav"
|
|
1247
|
+
>
|
|
1248
|
+
@if (isMinimized()) {
|
|
1249
|
+
<span class="fa-arrow-right-to-line"></span>
|
|
1250
|
+
} @else {
|
|
1251
|
+
<span class="fa-arrow-left-to-line"></span>
|
|
1252
|
+
}
|
|
1253
|
+
</button>
|
|
1254
|
+
</div>
|
|
1255
|
+
}
|
|
1256
|
+
</nav>
|
|
1257
|
+
</df-drawer>
|
|
1258
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: DfDrawerComponent, selector: "df-drawer", inputs: ["className", "bodyScroll", "backdrop", "container", "size", "resizable", "visible", "enableMobile"], outputs: ["sizeChange", "visibleChange", "minimizedChange", "maximizedChange", "resizingChange", "minSize", "maxSize"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1259
|
+
}
|
|
1260
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DfSidenavComponent, decorators: [{
|
|
1261
|
+
type: Component,
|
|
1262
|
+
args: [{
|
|
1263
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
1264
|
+
selector: 'df-sidenav',
|
|
1265
|
+
template: `
|
|
1266
|
+
<df-drawer
|
|
1267
|
+
#drawer
|
|
1268
|
+
className="inline-start {{ isSmallScreen() ? 'df-sidenav-mobile-drawer' : '' }}"
|
|
1269
|
+
[visible]="!_isMinimized()"
|
|
1270
|
+
(visibleChange)="visibleChangeHandler($event)"
|
|
1271
|
+
(minimizedChange)="_isMinimized.set($event)"
|
|
1272
|
+
(resizingChange)="resizingChangeHandler($event)"
|
|
1273
|
+
[bodyScroll]="true"
|
|
1274
|
+
[backdrop]="backdrop()"
|
|
1275
|
+
[(size)]="width"
|
|
1276
|
+
[container]="isSmallScreen() ? null : hostRef.nativeElement"
|
|
1277
|
+
[resizable]="resizable() && !isSmallScreen()"
|
|
1278
|
+
>
|
|
1279
|
+
<nav class="d-flex flex-column w-100 h-100 p-5 pb-3">
|
|
1280
|
+
<div class="df-sidenav-header flex-shrink-0" [class.df-sidenav-header-minimized]="_isMinimized()">
|
|
1281
|
+
@if (searchable()) {
|
|
1282
|
+
@if (_isMinimized()) {
|
|
1283
|
+
<button
|
|
1284
|
+
class="btn btn-outline-neutral df-btn-icononly mb-3 w-100 fa-search"
|
|
1285
|
+
[class.df-sidenav-search-minimized]="!contentHasIcons()"
|
|
1286
|
+
type="button"
|
|
1287
|
+
(click)="focusSearch()"
|
|
1288
|
+
i18n-aria-label="@@df.sidenav.search.ariaLabel"
|
|
1289
|
+
aria-label="Search"
|
|
1290
|
+
></button>
|
|
1291
|
+
} @else {
|
|
1292
|
+
<div class="input-group mb-3">
|
|
1293
|
+
<div class="input-group-prepend">
|
|
1294
|
+
<span class="input-group-text fa-light fa-search"></span>
|
|
1295
|
+
</div>
|
|
1296
|
+
<input
|
|
1297
|
+
type="search"
|
|
1298
|
+
class="form-control df-input-withicon"
|
|
1299
|
+
i18n-placeholder="@@df.sidenav.search.placeholder"
|
|
1300
|
+
placeholder="Search"
|
|
1301
|
+
i18n-aria-label="@@df.sidenav.search.ariaLabel"
|
|
1302
|
+
aria-label="Search"
|
|
1303
|
+
[(ngModel)]="searchTerm"
|
|
1304
|
+
#textInput
|
|
1305
|
+
/>
|
|
1306
|
+
<div class="input-group-append" [class.df-is-empty]="searchTerm() === ''">
|
|
1307
|
+
<button class="input-group-text fa-light fa-times" (click)="searchTerm.set(''); textInput.focus()">
|
|
1308
|
+
<span class="visually-hidden" i18n="@@df.sidenav.search.clearText">Clear text</span>
|
|
1309
|
+
</button>
|
|
1310
|
+
</div>
|
|
1311
|
+
</div>
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
</div>
|
|
1315
|
+
|
|
1316
|
+
<div role="list" class="sidenav-body flex-grow-1 overflow-y-auto" #scrollableContainer>
|
|
1317
|
+
<ng-content />
|
|
1318
|
+
</div>
|
|
1319
|
+
@if (collapsible() && !isSmallScreen()) {
|
|
1320
|
+
<div class="d-flex sidenav-footer flex-shrink-0 py-3">
|
|
1321
|
+
<button
|
|
1322
|
+
class="btn btn-outline-primary df-btn-icononly df-sidenav-toggle-minimized"
|
|
1323
|
+
(click)="toggleMinimize()"
|
|
1324
|
+
i18n-aria-label="@@df.sidenav.toggleMinimize.ariaLabel"
|
|
1325
|
+
aria-label="Toggle minimized sidenav"
|
|
1326
|
+
>
|
|
1327
|
+
@if (isMinimized()) {
|
|
1328
|
+
<span class="fa-arrow-right-to-line"></span>
|
|
1329
|
+
} @else {
|
|
1330
|
+
<span class="fa-arrow-left-to-line"></span>
|
|
1331
|
+
}
|
|
1332
|
+
</button>
|
|
1333
|
+
</div>
|
|
1334
|
+
}
|
|
1335
|
+
</nav>
|
|
1336
|
+
</df-drawer>
|
|
1337
|
+
`,
|
|
1338
|
+
imports: [FormsModule, DfDrawerComponent],
|
|
1339
|
+
host: {
|
|
1340
|
+
class: 'df-sidenav',
|
|
1341
|
+
'[class.df-sidenav-lg]': 'isLargeUpScreen()',
|
|
1342
|
+
'[class.df-sidenav-withicon]': 'contentHasIcons()',
|
|
1343
|
+
'[class.d-none]': 'isSmallScreen() && _isMinimized()',
|
|
1344
|
+
'[class.df-sidenav-drawer]': 'isMediumScreen() && !_isMinimized()'
|
|
1345
|
+
}
|
|
1346
|
+
}]
|
|
1347
|
+
}], ctorParameters: () => [], propDecorators: { searchable: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchable", required: false }] }], container: [{ type: i0.Input, args: [{ isSignal: true, alias: "container", required: false }] }], resizable: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizable", required: false }] }], collapsible: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsible", required: false }] }], enableMobile: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableMobile", required: false }] }], scrollableContainer: [{ type: i0.ViewChild, args: ['scrollableContainer', { isSignal: true }] }], searchInput: [{ type: i0.ViewChild, args: ['textInput', { isSignal: true }] }], allLinks: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DfSidenavLinkComponent), { ...{ descendants: true }, isSignal: true }] }], allItems: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DfSidenavItemComponent), { ...{ descendants: true }, isSignal: true }] }] } });
|
|
1348
|
+
|
|
1349
|
+
/**
|
|
1350
|
+
* Generated bundle index. Do not edit.
|
|
1351
|
+
*/
|
|
1352
|
+
|
|
1353
|
+
export { DfSidenavComponent, DfSidenavIconDirective, DfSidenavItemComponent, DfSidenavLinkComponent };
|
|
1354
|
+
//# sourceMappingURL=design-factory-angular-sidenav.mjs.map
|