@ojiepermana/angular-navigation 22.0.43 → 22.0.45
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 +9 -0
- package/fesm2022/ojiepermana-angular-navigation-navigation-dockbar-menu.component-BQgpBy1I.mjs +432 -0
- package/fesm2022/ojiepermana-angular-navigation-navigation-entry-grid.component-BY-DXx81.mjs +569 -0
- package/fesm2022/ojiepermana-angular-navigation-navigation-flyout-menu.component-CACN0O3T.mjs +637 -0
- package/fesm2022/ojiepermana-angular-navigation-navigation-horizontal.component-SK1lXswT.mjs +423 -0
- package/fesm2022/ojiepermana-angular-navigation-ojiepermana-angular-navigation-SlMGlTuA.mjs +2225 -0
- package/fesm2022/ojiepermana-angular-navigation-service.mjs +3 -3
- package/fesm2022/ojiepermana-angular-navigation.mjs +1 -4022
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -227,3 +227,12 @@ Gunakan template `NavigationIcon` hanya bila consumer ingin override renderer ba
|
|
|
227
227
|
## Boundaries
|
|
228
228
|
|
|
229
229
|
Entry point ini tetap extensible di layer consumer. Type defaults, instance registration, overlay state, dan affordance icon bawaan dikelola di library, sementara consumer masih bisa mengganti renderer icon dan komposisi layout sesuai kebutuhan.
|
|
230
|
+
|
|
231
|
+
## Bundle / performance
|
|
232
|
+
|
|
233
|
+
`NavigationContent` me-render renderer sesuai type aktif. Renderer yang lebih jarang
|
|
234
|
+
dipakai — **flyout**, **horizontal/navbar**, dan **dockbar** — dibungkus `@defer (on immediate)`
|
|
235
|
+
sehingga bundler aplikasi memecahnya menjadi lazy chunk: aplikasi yang hanya memakai
|
|
236
|
+
**sidebar** (vertical list, renderer default yang tetap eager) tidak ikut membundel ketiga
|
|
237
|
+
renderer tersebut. Tidak ada perubahan API — komposisi `<Navigation><NavigationSidebar>…`
|
|
238
|
+
tetap sama; hanya distribusi chunk di sisi consumer yang lebih ringan.
|
package/fesm2022/ojiepermana-angular-navigation-navigation-dockbar-menu.component-BQgpBy1I.mjs
ADDED
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { inject, ElementRef, Injector, input, output, computed, signal, afterNextRender, Component } from '@angular/core';
|
|
3
|
+
import { IconComponent } from '@ojiepermana/angular-component/icon';
|
|
4
|
+
import { cn } from '@ojiepermana/angular-component/utils';
|
|
5
|
+
import { N as NavigationItemComponent, a as NavigationItemContentComponent, b as NavigationListComponent } from './ojiepermana-angular-navigation-ojiepermana-angular-navigation-SlMGlTuA.mjs';
|
|
6
|
+
import { NavigationService } from '@ojiepermana/angular-navigation/service';
|
|
7
|
+
|
|
8
|
+
class NavigationDockbarMenuComponent {
|
|
9
|
+
nav = inject(NavigationService);
|
|
10
|
+
host = inject(ElementRef);
|
|
11
|
+
injector = inject(Injector);
|
|
12
|
+
navId = input('default', /* @ts-ignore */
|
|
13
|
+
...(ngDevMode ? [{ debugName: "navId" }] : /* istanbul ignore next */ []));
|
|
14
|
+
items = input([], /* @ts-ignore */
|
|
15
|
+
...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
|
|
16
|
+
mode = input('sticky', /* @ts-ignore */
|
|
17
|
+
...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
|
|
18
|
+
position = input('left', /* @ts-ignore */
|
|
19
|
+
...(ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []));
|
|
20
|
+
itemClass = input('', /* @ts-ignore */
|
|
21
|
+
...(ngDevMode ? [{ debugName: "itemClass" }] : /* istanbul ignore next */ []));
|
|
22
|
+
activeIds = input(new Set(), /* @ts-ignore */
|
|
23
|
+
...(ngDevMode ? [{ debugName: "activeIds" }] : /* istanbul ignore next */ []));
|
|
24
|
+
activeUrl = input(null, /* @ts-ignore */
|
|
25
|
+
...(ngDevMode ? [{ debugName: "activeUrl" }] : /* istanbul ignore next */ []));
|
|
26
|
+
iconTemplate = input(undefined, /* @ts-ignore */
|
|
27
|
+
...(ngDevMode ? [{ debugName: "iconTemplate" }] : /* istanbul ignore next */ []));
|
|
28
|
+
collapseTree = input('stairs', /* @ts-ignore */
|
|
29
|
+
...(ngDevMode ? [{ debugName: "collapseTree" }] : /* istanbul ignore next */ []));
|
|
30
|
+
openedIds = input([], /* @ts-ignore */
|
|
31
|
+
...(ngDevMode ? [{ debugName: "openedIds" }] : /* istanbul ignore next */ []));
|
|
32
|
+
openedIdsChange = output();
|
|
33
|
+
itemSelected = output();
|
|
34
|
+
isDrawerMode = computed(() => this.mode() === 'drawer', /* @ts-ignore */
|
|
35
|
+
...(ngDevMode ? [{ debugName: "isDrawerMode" }] : /* istanbul ignore next */ []));
|
|
36
|
+
asideId = computed(() => `nav-dockbar-${this.safeId(this.navId())}`, /* @ts-ignore */
|
|
37
|
+
...(ngDevMode ? [{ debugName: "asideId" }] : /* istanbul ignore next */ []));
|
|
38
|
+
asideHeadingId = computed(() => `${this.asideId()}-heading`, /* @ts-ignore */
|
|
39
|
+
...(ngDevMode ? [{ debugName: "asideHeadingId" }] : /* istanbul ignore next */ []));
|
|
40
|
+
openGroup = computed(() => this.nav.resolveDockbarGroup(this.navId(), this.items(), this.mode(), this.activeIds(), this.activeUrl()), /* @ts-ignore */
|
|
41
|
+
...(ngDevMode ? [{ debugName: "openGroup" }] : /* istanbul ignore next */ []));
|
|
42
|
+
hostClasses = computed(() => cn('block w-16'), /* @ts-ignore */
|
|
43
|
+
...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
|
|
44
|
+
railClasses = computed(() => cn('flex w-full list-none flex-col items-center gap-0.5 p-2',
|
|
45
|
+
// Mode drawer: rail tetap di atas backdrop (z-40) agar user bisa langsung
|
|
46
|
+
// berpindah group lain tanpa menutup drawer lebih dulu.
|
|
47
|
+
this.isDrawerMode() && this.openGroup() !== null && 'relative z-50'), /* @ts-ignore */
|
|
48
|
+
...(ngDevMode ? [{ debugName: "railClasses" }] : /* istanbul ignore next */ []));
|
|
49
|
+
asideClasses = computed(() => cn('absolute inset-y-0 flex w-60 flex-col bg-background',
|
|
50
|
+
// Border only on the CONTENT-facing edge. The dockbar rail already draws the
|
|
51
|
+
// rail-facing separator, so a rail-facing border here would double up
|
|
52
|
+
// (right of the mini-dock + left of the aside).
|
|
53
|
+
this.position() === 'right'
|
|
54
|
+
? 'right-16 border-l border-[hsl(var(--border)/var(--opacity-70))]'
|
|
55
|
+
: 'left-16 border-r border-[hsl(var(--border)/var(--opacity-70))]', this.isDrawerMode() && 'z-50 shadow-xl'), /* @ts-ignore */
|
|
56
|
+
...(ngDevMode ? [{ debugName: "asideClasses" }] : /* istanbul ignore next */ []));
|
|
57
|
+
/**
|
|
58
|
+
* Offset backdrop agar kolom navigasi (rail + header/footer) benar-benar bebas
|
|
59
|
+
* overlay — dockbar dan aside drawer berada di level yang sama di atas backdrop.
|
|
60
|
+
* `null` berarti pengukuran tidak tersedia (mis. jsdom) dan backdrop jatuh ke
|
|
61
|
+
* `inset-0` penuh seperti sebelumnya.
|
|
62
|
+
*/
|
|
63
|
+
backdropInset = signal({
|
|
64
|
+
left: null,
|
|
65
|
+
right: null,
|
|
66
|
+
}, /* @ts-ignore */
|
|
67
|
+
...(ngDevMode ? [{ debugName: "backdropInset" }] : /* istanbul ignore next */ []));
|
|
68
|
+
onWindowResize() {
|
|
69
|
+
if (this.isDrawerMode() && this.openGroup() !== null) {
|
|
70
|
+
this.measureBackdropInset();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
measureBackdropInset() {
|
|
74
|
+
const navEl = this.host.nativeElement.closest('[data-navigation-id]') ??
|
|
75
|
+
this.host.nativeElement;
|
|
76
|
+
const rect = navEl.getBoundingClientRect();
|
|
77
|
+
const viewportWidth = this.host.nativeElement.ownerDocument?.defaultView?.innerWidth ?? 0;
|
|
78
|
+
if (rect.width <= 0 || viewportWidth <= 0) {
|
|
79
|
+
this.backdropInset.set({ left: null, right: null });
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
// Containing block backdrop `fixed` bisa bergeser dari viewport (ancestor dengan
|
|
83
|
+
// filter/contain). Koreksi origin dihitung dari posisi aktual backdrop terhadap
|
|
84
|
+
// style left/right yang sedang berlaku — konvergen dalam satu langkah re-measure.
|
|
85
|
+
const backdrop = this.host.nativeElement.querySelector('[data-navigation-dockbar-backdrop="true"]');
|
|
86
|
+
const backdropRect = backdrop?.getBoundingClientRect() ?? null;
|
|
87
|
+
let originLeft = 0;
|
|
88
|
+
let originRight = viewportWidth;
|
|
89
|
+
if (backdrop && backdropRect && backdropRect.width > 0) {
|
|
90
|
+
originLeft = backdropRect.left - this.parsePx(backdrop.style.left);
|
|
91
|
+
originRight = backdropRect.right + this.parsePx(backdrop.style.right);
|
|
92
|
+
}
|
|
93
|
+
if (this.position() === 'right') {
|
|
94
|
+
this.backdropInset.set({ left: null, right: Math.max(originRight - rect.left, 0) });
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
this.backdropInset.set({ left: Math.max(rect.right - originLeft, 0), right: null });
|
|
98
|
+
}
|
|
99
|
+
parsePx(value) {
|
|
100
|
+
const parsed = Number.parseFloat(value);
|
|
101
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
102
|
+
}
|
|
103
|
+
isGroupOpen(entry) {
|
|
104
|
+
return this.openGroup()?.key === entry.key;
|
|
105
|
+
}
|
|
106
|
+
isGroupActive(entry) {
|
|
107
|
+
return this.nav.isItemActive(entry, this.activeIds(), this.activeUrl());
|
|
108
|
+
}
|
|
109
|
+
triggerClasses(entry) {
|
|
110
|
+
return cn('nav-text group/nav inline-flex h-10 w-10 items-center justify-center rounded-md font-medium transition-colors', 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50', this.isGroupOpen(entry)
|
|
111
|
+
? 'bg-accent text-accent-foreground'
|
|
112
|
+
: this.isGroupActive(entry)
|
|
113
|
+
? 'font-semibold text-foreground'
|
|
114
|
+
: 'text-foreground/80 hover:bg-accent hover:text-accent-foreground', entry.classes?.wrapper, this.itemClass());
|
|
115
|
+
}
|
|
116
|
+
toggleGroup(entry) {
|
|
117
|
+
const wasOpen = this.isGroupOpen(entry);
|
|
118
|
+
if (!wasOpen && this.isDrawerMode()) {
|
|
119
|
+
this.measureBackdropInset();
|
|
120
|
+
}
|
|
121
|
+
this.nav.togglePanel(this.navId(), entry);
|
|
122
|
+
if (!this.isDrawerMode()) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (wasOpen) {
|
|
126
|
+
this.afterRender(() => this.focusTrigger(entry.key));
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
this.afterRender(() => {
|
|
130
|
+
// Ukur ulang setelah render: geometri kolom bisa bergeser saat panel terbuka.
|
|
131
|
+
this.measureBackdropInset();
|
|
132
|
+
this.focusFirstAsideItem();
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
closeAside() {
|
|
136
|
+
const openKey = this.openGroup()?.key ?? null;
|
|
137
|
+
this.nav.closePanel(this.navId());
|
|
138
|
+
if (openKey !== null) {
|
|
139
|
+
this.afterRender(() => this.focusTrigger(openKey));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
onEscape(event) {
|
|
143
|
+
if (!this.isDrawerMode() || this.openGroup() === null) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
event.preventDefault();
|
|
147
|
+
this.closeAside();
|
|
148
|
+
}
|
|
149
|
+
afterRender(callback) {
|
|
150
|
+
afterNextRender({ read: callback }, { injector: this.injector });
|
|
151
|
+
}
|
|
152
|
+
focusFirstAsideItem() {
|
|
153
|
+
const aside = this.host.nativeElement.querySelector(`#${this.asideId()}`);
|
|
154
|
+
const focusable = aside?.querySelector('a[href], button:not([disabled]), [tabindex]:not([tabindex="-1"])');
|
|
155
|
+
focusable?.focus();
|
|
156
|
+
}
|
|
157
|
+
focusTrigger(key) {
|
|
158
|
+
const trigger = this.host.nativeElement.querySelector(`[data-navigation-dockbar-trigger="true"][data-navigation-item-key="${key}"]`);
|
|
159
|
+
trigger?.focus();
|
|
160
|
+
}
|
|
161
|
+
safeId(value) {
|
|
162
|
+
const normalized = value
|
|
163
|
+
.trim()
|
|
164
|
+
.toLowerCase()
|
|
165
|
+
.replace(/[^a-z0-9_-]+/g, '-');
|
|
166
|
+
return normalized || 'default';
|
|
167
|
+
}
|
|
168
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: NavigationDockbarMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
169
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.4", type: NavigationDockbarMenuComponent, isStandalone: true, selector: "NavigationDockbarMenu", inputs: { navId: { classPropertyName: "navId", publicName: "navId", isSignal: true, isRequired: false, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, itemClass: { classPropertyName: "itemClass", publicName: "itemClass", isSignal: true, isRequired: false, transformFunction: null }, activeIds: { classPropertyName: "activeIds", publicName: "activeIds", isSignal: true, isRequired: false, transformFunction: null }, activeUrl: { classPropertyName: "activeUrl", publicName: "activeUrl", isSignal: true, isRequired: false, transformFunction: null }, iconTemplate: { classPropertyName: "iconTemplate", publicName: "iconTemplate", isSignal: true, isRequired: false, transformFunction: null }, collapseTree: { classPropertyName: "collapseTree", publicName: "collapseTree", isSignal: true, isRequired: false, transformFunction: null }, openedIds: { classPropertyName: "openedIds", publicName: "openedIds", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { openedIdsChange: "openedIdsChange", itemSelected: "itemSelected" }, host: { listeners: { "keydown.escape": "onEscape($event)", "window:resize": "onWindowResize()" }, properties: { "class": "hostClasses()", "attr.data-mode": "mode()", "attr.data-position": "position()" } }, ngImport: i0, template: `
|
|
170
|
+
<ul [class]="railClasses()" role="list">
|
|
171
|
+
@for (entry of items(); track entry.key) {
|
|
172
|
+
@if (entry.children.length > 0) {
|
|
173
|
+
<li class="flex w-full justify-center">
|
|
174
|
+
<button
|
|
175
|
+
type="button"
|
|
176
|
+
data-navigation-dockbar-trigger="true"
|
|
177
|
+
[class]="triggerClasses(entry)"
|
|
178
|
+
[attr.aria-expanded]="isGroupOpen(entry)"
|
|
179
|
+
[attr.aria-controls]="asideId()"
|
|
180
|
+
[attr.aria-label]="entry.tooltip ?? entry.title ?? null"
|
|
181
|
+
[attr.title]="entry.tooltip ?? entry.title ?? null"
|
|
182
|
+
[attr.data-navigation-item-key]="entry.key"
|
|
183
|
+
[disabled]="entry.disabled || null"
|
|
184
|
+
(click)="toggleGroup(entry)"
|
|
185
|
+
>
|
|
186
|
+
<NavigationItemContent
|
|
187
|
+
[item]="entry"
|
|
188
|
+
[active]="isGroupActive(entry)"
|
|
189
|
+
[compact]="true"
|
|
190
|
+
orientation="vertical"
|
|
191
|
+
[level]="0"
|
|
192
|
+
[collapseTree]="collapseTree()"
|
|
193
|
+
[iconTemplate]="iconTemplate()"
|
|
194
|
+
/>
|
|
195
|
+
</button>
|
|
196
|
+
</li>
|
|
197
|
+
} @else {
|
|
198
|
+
<li
|
|
199
|
+
NavigationItem
|
|
200
|
+
[navId]="navId()"
|
|
201
|
+
[item]="entry"
|
|
202
|
+
[level]="0"
|
|
203
|
+
orientation="vertical"
|
|
204
|
+
[compact]="true"
|
|
205
|
+
[itemClass]="itemClass()"
|
|
206
|
+
[activeIds]="activeIds()"
|
|
207
|
+
[activeUrl]="activeUrl()"
|
|
208
|
+
[iconTemplate]="iconTemplate()"
|
|
209
|
+
[collapseTree]="collapseTree()"
|
|
210
|
+
[openedIds]="openedIds()"
|
|
211
|
+
(openedIdsChange)="openedIdsChange.emit($event)"
|
|
212
|
+
(itemSelected)="itemSelected.emit($event)"
|
|
213
|
+
></li>
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
</ul>
|
|
217
|
+
|
|
218
|
+
@if (openGroup(); as group) {
|
|
219
|
+
@if (isDrawerMode()) {
|
|
220
|
+
<div
|
|
221
|
+
class="fixed inset-0 z-40 bg-[hsl(var(--overlay-backdrop-strong))]"
|
|
222
|
+
data-navigation-dockbar-backdrop="true"
|
|
223
|
+
aria-hidden="true"
|
|
224
|
+
[style.left.px]="backdropInset().left"
|
|
225
|
+
[style.right.px]="backdropInset().right"
|
|
226
|
+
(click)="closeAside()"
|
|
227
|
+
></div>
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
<!-- Drawer non-modal: rail tetap interaktif di atas backdrop, jadi tanpa aria-modal. -->
|
|
231
|
+
<aside
|
|
232
|
+
[id]="asideId()"
|
|
233
|
+
[class]="asideClasses()"
|
|
234
|
+
data-navigation-dockbar-aside="true"
|
|
235
|
+
[attr.role]="isDrawerMode() ? 'dialog' : null"
|
|
236
|
+
[attr.aria-labelledby]="asideHeadingId()"
|
|
237
|
+
>
|
|
238
|
+
<header
|
|
239
|
+
class="flex h-12 shrink-0 items-center gap-2 border-b border-[hsl(var(--border)/var(--opacity-70))] px-4"
|
|
240
|
+
>
|
|
241
|
+
<div class="min-w-0 flex-1">
|
|
242
|
+
<h2
|
|
243
|
+
[id]="asideHeadingId()"
|
|
244
|
+
class="truncate text-sm font-semibold leading-tight text-foreground"
|
|
245
|
+
>
|
|
246
|
+
{{ group.title }}
|
|
247
|
+
</h2>
|
|
248
|
+
@if (group.subtitle) {
|
|
249
|
+
<p class="truncate text-xs leading-tight text-muted-foreground">
|
|
250
|
+
{{ group.subtitle }}
|
|
251
|
+
</p>
|
|
252
|
+
}
|
|
253
|
+
</div>
|
|
254
|
+
|
|
255
|
+
@if (isDrawerMode()) {
|
|
256
|
+
<button
|
|
257
|
+
type="button"
|
|
258
|
+
class="inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-md text-foreground/80 transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
259
|
+
data-navigation-dockbar-close="true"
|
|
260
|
+
aria-label="Close navigation panel"
|
|
261
|
+
(click)="closeAside()"
|
|
262
|
+
>
|
|
263
|
+
<Icon name="close" [size]="16" />
|
|
264
|
+
</button>
|
|
265
|
+
}
|
|
266
|
+
</header>
|
|
267
|
+
|
|
268
|
+
<div
|
|
269
|
+
class="min-h-0 flex-1 overflow-y-auto px-2 py-3"
|
|
270
|
+
data-navigation-dockbar-aside-scroll="true"
|
|
271
|
+
>
|
|
272
|
+
<NavigationList
|
|
273
|
+
[navId]="navId()"
|
|
274
|
+
[items]="group.children"
|
|
275
|
+
[collapsed]="false"
|
|
276
|
+
position="left"
|
|
277
|
+
[itemClass]="itemClass()"
|
|
278
|
+
[activeIds]="activeIds()"
|
|
279
|
+
[activeUrl]="activeUrl()"
|
|
280
|
+
[iconTemplate]="iconTemplate()"
|
|
281
|
+
[collapseTree]="collapseTree()"
|
|
282
|
+
[openedIds]="openedIds()"
|
|
283
|
+
(openedIdsChange)="openedIdsChange.emit($event)"
|
|
284
|
+
(itemSelected)="itemSelected.emit($event)"
|
|
285
|
+
/>
|
|
286
|
+
</div>
|
|
287
|
+
</aside>
|
|
288
|
+
}
|
|
289
|
+
`, isInline: true, dependencies: [{ kind: "component", type: IconComponent, selector: "Icon", inputs: ["name", "class", "size", "fill", "weight", "grade", "opticalSize"] }, { kind: "component", type: NavigationItemComponent, selector: "li[NavigationItem]", inputs: ["navId", "item", "level", "orientation", "compact", "itemClass", "activeIds", "activeUrl", "iconTemplate", "collapseTree", "straightRail", "straightRailActive", "firstInBranch", "lastInBranch", "openedIds"], outputs: ["openedIdsChange", "itemSelected"] }, { kind: "component", type: NavigationItemContentComponent, selector: "NavigationItemContent", inputs: ["item", "active", "compact", "orientation", "level", "collapseTree", "iconTemplate"] }, { kind: "component", type: NavigationListComponent, selector: "NavigationList", inputs: ["navId", "items", "collapsed", "compact", "position", "itemClass", "activeIds", "activeUrl", "iconTemplate", "collapseTree", "openedIds"], outputs: ["openedIdsChange", "itemSelected"] }] });
|
|
290
|
+
}
|
|
291
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: NavigationDockbarMenuComponent, decorators: [{
|
|
292
|
+
type: Component,
|
|
293
|
+
args: [{
|
|
294
|
+
selector: 'NavigationDockbarMenu',
|
|
295
|
+
imports: [
|
|
296
|
+
IconComponent,
|
|
297
|
+
NavigationItemComponent,
|
|
298
|
+
NavigationItemContentComponent,
|
|
299
|
+
NavigationListComponent,
|
|
300
|
+
],
|
|
301
|
+
host: {
|
|
302
|
+
'[class]': 'hostClasses()',
|
|
303
|
+
'[attr.data-mode]': 'mode()',
|
|
304
|
+
'[attr.data-position]': 'position()',
|
|
305
|
+
'(keydown.escape)': 'onEscape($event)',
|
|
306
|
+
'(window:resize)': 'onWindowResize()',
|
|
307
|
+
},
|
|
308
|
+
template: `
|
|
309
|
+
<ul [class]="railClasses()" role="list">
|
|
310
|
+
@for (entry of items(); track entry.key) {
|
|
311
|
+
@if (entry.children.length > 0) {
|
|
312
|
+
<li class="flex w-full justify-center">
|
|
313
|
+
<button
|
|
314
|
+
type="button"
|
|
315
|
+
data-navigation-dockbar-trigger="true"
|
|
316
|
+
[class]="triggerClasses(entry)"
|
|
317
|
+
[attr.aria-expanded]="isGroupOpen(entry)"
|
|
318
|
+
[attr.aria-controls]="asideId()"
|
|
319
|
+
[attr.aria-label]="entry.tooltip ?? entry.title ?? null"
|
|
320
|
+
[attr.title]="entry.tooltip ?? entry.title ?? null"
|
|
321
|
+
[attr.data-navigation-item-key]="entry.key"
|
|
322
|
+
[disabled]="entry.disabled || null"
|
|
323
|
+
(click)="toggleGroup(entry)"
|
|
324
|
+
>
|
|
325
|
+
<NavigationItemContent
|
|
326
|
+
[item]="entry"
|
|
327
|
+
[active]="isGroupActive(entry)"
|
|
328
|
+
[compact]="true"
|
|
329
|
+
orientation="vertical"
|
|
330
|
+
[level]="0"
|
|
331
|
+
[collapseTree]="collapseTree()"
|
|
332
|
+
[iconTemplate]="iconTemplate()"
|
|
333
|
+
/>
|
|
334
|
+
</button>
|
|
335
|
+
</li>
|
|
336
|
+
} @else {
|
|
337
|
+
<li
|
|
338
|
+
NavigationItem
|
|
339
|
+
[navId]="navId()"
|
|
340
|
+
[item]="entry"
|
|
341
|
+
[level]="0"
|
|
342
|
+
orientation="vertical"
|
|
343
|
+
[compact]="true"
|
|
344
|
+
[itemClass]="itemClass()"
|
|
345
|
+
[activeIds]="activeIds()"
|
|
346
|
+
[activeUrl]="activeUrl()"
|
|
347
|
+
[iconTemplate]="iconTemplate()"
|
|
348
|
+
[collapseTree]="collapseTree()"
|
|
349
|
+
[openedIds]="openedIds()"
|
|
350
|
+
(openedIdsChange)="openedIdsChange.emit($event)"
|
|
351
|
+
(itemSelected)="itemSelected.emit($event)"
|
|
352
|
+
></li>
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
</ul>
|
|
356
|
+
|
|
357
|
+
@if (openGroup(); as group) {
|
|
358
|
+
@if (isDrawerMode()) {
|
|
359
|
+
<div
|
|
360
|
+
class="fixed inset-0 z-40 bg-[hsl(var(--overlay-backdrop-strong))]"
|
|
361
|
+
data-navigation-dockbar-backdrop="true"
|
|
362
|
+
aria-hidden="true"
|
|
363
|
+
[style.left.px]="backdropInset().left"
|
|
364
|
+
[style.right.px]="backdropInset().right"
|
|
365
|
+
(click)="closeAside()"
|
|
366
|
+
></div>
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
<!-- Drawer non-modal: rail tetap interaktif di atas backdrop, jadi tanpa aria-modal. -->
|
|
370
|
+
<aside
|
|
371
|
+
[id]="asideId()"
|
|
372
|
+
[class]="asideClasses()"
|
|
373
|
+
data-navigation-dockbar-aside="true"
|
|
374
|
+
[attr.role]="isDrawerMode() ? 'dialog' : null"
|
|
375
|
+
[attr.aria-labelledby]="asideHeadingId()"
|
|
376
|
+
>
|
|
377
|
+
<header
|
|
378
|
+
class="flex h-12 shrink-0 items-center gap-2 border-b border-[hsl(var(--border)/var(--opacity-70))] px-4"
|
|
379
|
+
>
|
|
380
|
+
<div class="min-w-0 flex-1">
|
|
381
|
+
<h2
|
|
382
|
+
[id]="asideHeadingId()"
|
|
383
|
+
class="truncate text-sm font-semibold leading-tight text-foreground"
|
|
384
|
+
>
|
|
385
|
+
{{ group.title }}
|
|
386
|
+
</h2>
|
|
387
|
+
@if (group.subtitle) {
|
|
388
|
+
<p class="truncate text-xs leading-tight text-muted-foreground">
|
|
389
|
+
{{ group.subtitle }}
|
|
390
|
+
</p>
|
|
391
|
+
}
|
|
392
|
+
</div>
|
|
393
|
+
|
|
394
|
+
@if (isDrawerMode()) {
|
|
395
|
+
<button
|
|
396
|
+
type="button"
|
|
397
|
+
class="inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-md text-foreground/80 transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
398
|
+
data-navigation-dockbar-close="true"
|
|
399
|
+
aria-label="Close navigation panel"
|
|
400
|
+
(click)="closeAside()"
|
|
401
|
+
>
|
|
402
|
+
<Icon name="close" [size]="16" />
|
|
403
|
+
</button>
|
|
404
|
+
}
|
|
405
|
+
</header>
|
|
406
|
+
|
|
407
|
+
<div
|
|
408
|
+
class="min-h-0 flex-1 overflow-y-auto px-2 py-3"
|
|
409
|
+
data-navigation-dockbar-aside-scroll="true"
|
|
410
|
+
>
|
|
411
|
+
<NavigationList
|
|
412
|
+
[navId]="navId()"
|
|
413
|
+
[items]="group.children"
|
|
414
|
+
[collapsed]="false"
|
|
415
|
+
position="left"
|
|
416
|
+
[itemClass]="itemClass()"
|
|
417
|
+
[activeIds]="activeIds()"
|
|
418
|
+
[activeUrl]="activeUrl()"
|
|
419
|
+
[iconTemplate]="iconTemplate()"
|
|
420
|
+
[collapseTree]="collapseTree()"
|
|
421
|
+
[openedIds]="openedIds()"
|
|
422
|
+
(openedIdsChange)="openedIdsChange.emit($event)"
|
|
423
|
+
(itemSelected)="itemSelected.emit($event)"
|
|
424
|
+
/>
|
|
425
|
+
</div>
|
|
426
|
+
</aside>
|
|
427
|
+
}
|
|
428
|
+
`,
|
|
429
|
+
}]
|
|
430
|
+
}], propDecorators: { navId: [{ type: i0.Input, args: [{ isSignal: true, alias: "navId", required: false }] }], items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], itemClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemClass", required: false }] }], activeIds: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeIds", required: false }] }], activeUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeUrl", required: false }] }], iconTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconTemplate", required: false }] }], collapseTree: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapseTree", required: false }] }], openedIds: [{ type: i0.Input, args: [{ isSignal: true, alias: "openedIds", required: false }] }], openedIdsChange: [{ type: i0.Output, args: ["openedIdsChange"] }], itemSelected: [{ type: i0.Output, args: ["itemSelected"] }] } });
|
|
431
|
+
|
|
432
|
+
export { NavigationDockbarMenuComponent };
|