@ojiepermana/angular-navigation 22.0.27

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.
@@ -0,0 +1,3568 @@
1
+ import * as i0 from '@angular/core';
2
+ import { inject, TemplateRef, Directive, DestroyRef, signal, computed, effect, InjectionToken, input, model, output, contentChild, isDevMode, forwardRef, ChangeDetectionStrategy, Component, ElementRef, Injector, afterNextRender, booleanAttribute, ViewContainerRef } from '@angular/core';
3
+ import { cn } from '@ojiepermana/angular-component/utils';
4
+ import { NavigationService } from '@ojiepermana/angular-navigation/service';
5
+ import { IconComponent } from '@ojiepermana/angular-component/icon';
6
+ import { RouterLink, RouterLinkActive } from '@angular/router';
7
+ import { NgTemplateOutlet } from '@angular/common';
8
+
9
+ class NavigationIconDirective {
10
+ template = inject(TemplateRef);
11
+ static ngTemplateContextGuard(_directive, context) {
12
+ return true;
13
+ }
14
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: NavigationIconDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
15
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: NavigationIconDirective, isStandalone: true, selector: "ng-template[NavigationIcon]", ngImport: i0 });
16
+ }
17
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: NavigationIconDirective, decorators: [{
18
+ type: Directive,
19
+ args: [{
20
+ selector: 'ng-template[NavigationIcon]',
21
+ }]
22
+ }] });
23
+
24
+ function normalizeType(type, hasChildren) {
25
+ if (type === 'divider' || type === 'spacer' || type === 'group' || type === 'mega') {
26
+ return type;
27
+ }
28
+ if (type === 'collapsible' || type === 'collapsable') {
29
+ return 'collapsible';
30
+ }
31
+ if (type === 'aside' && hasChildren) {
32
+ return 'collapsible';
33
+ }
34
+ if (!type && hasChildren) {
35
+ return 'collapsible';
36
+ }
37
+ return 'item';
38
+ }
39
+ function toKeyPart(value) {
40
+ return value
41
+ .toLowerCase()
42
+ .replace(/[^a-z0-9_-]+/g, '-')
43
+ .replace(/^-+|-+$/g, '');
44
+ }
45
+ function itemFallbackKey(item, index) {
46
+ return toKeyPart(item.title ?? item.link?.toString() ?? item.href ?? `item-${index}`) || `item-${index}`;
47
+ }
48
+ function normalizeItem(item, index, path) {
49
+ if (item.isHidden?.(item)) {
50
+ return null;
51
+ }
52
+ const children = (item.children ?? [])
53
+ .map((child, childIndex) => normalizeItem(child, childIndex, `${path}-${childIndex}`))
54
+ .filter((child) => child !== null);
55
+ const hasChildren = children.length > 0;
56
+ const key = item.id ?? `${path}-${itemFallbackKey(item, index)}`;
57
+ return {
58
+ ...item,
59
+ key,
60
+ stateId: item.id ?? key,
61
+ panelId: `nav-panel-${toKeyPart(key) || index}`,
62
+ type: normalizeType(item.type, hasChildren),
63
+ source: item,
64
+ children,
65
+ };
66
+ }
67
+ function normalizeUiNavItems(items) {
68
+ return items
69
+ .map((item, index) => normalizeItem(item, index, `nav-${index}`))
70
+ .filter((item) => item !== null);
71
+ }
72
+
73
+ function injectNavigationController(inputs) {
74
+ const nav = inject(NavigationService);
75
+ const destroyRef = inject(DestroyRef);
76
+ // Token unik per instance <Navigation> untuk validasi keunikan id di NavigationService.
77
+ const owner = {};
78
+ const registeredState = signal(null, /* @ts-ignore */
79
+ ...(ngDevMode ? [{ debugName: "registeredState" }] : /* istanbul ignore next */ []));
80
+ let previousRegisteredId = null;
81
+ const normalizedNavId = computed(() => normalizeId(inputs.navId()), /* @ts-ignore */
82
+ ...(ngDevMode ? [{ debugName: "normalizedNavId" }] : /* istanbul ignore next */ []));
83
+ const collapsedPreference = computed(() => inputs.collapsed() ?? (inputs.compact() ? true : null), /* @ts-ignore */
84
+ ...(ngDevMode ? [{ debugName: "collapsedPreference" }] : /* istanbul ignore next */ []));
85
+ const normalizedItems = computed(() => {
86
+ const data = inputs.data();
87
+ return normalizeUiNavItems(data.length > 0 ? data : inputs.items());
88
+ }, /* @ts-ignore */
89
+ ...(ngDevMode ? [{ debugName: "normalizedItems" }] : /* istanbul ignore next */ []));
90
+ const fallbackState = computed(() => ({
91
+ id: normalizedNavId(),
92
+ orientation: inputs.orientation(),
93
+ type: fallbackType(inputs.orientation(), inputs.type()),
94
+ position: inputs.position() ?? (inputs.orientation() === 'horizontal' ? 'top' : 'left'),
95
+ collapsed: collapsedPreference() ?? false,
96
+ dockbarMode: inputs.dockbarMode() ?? 'sticky',
97
+ }), /* @ts-ignore */
98
+ ...(ngDevMode ? [{ debugName: "fallbackState" }] : /* istanbul ignore next */ []));
99
+ const resolvedState = computed(() => nav.currentState(normalizedNavId()) ?? registeredState() ?? fallbackState(), /* @ts-ignore */
100
+ ...(ngDevMode ? [{ debugName: "resolvedState" }] : /* istanbul ignore next */ []));
101
+ const activeIdSet = computed(() => {
102
+ const ids = inputs.activeIds();
103
+ if (!ids) {
104
+ return new Set();
105
+ }
106
+ return ids instanceof Set ? new Set(ids) : new Set(ids);
107
+ }, /* @ts-ignore */
108
+ ...(ngDevMode ? [{ debugName: "activeIdSet" }] : /* istanbul ignore next */ []));
109
+ effect(() => {
110
+ if (!inputs.enabled()) {
111
+ return;
112
+ }
113
+ const nextState = nav.register({
114
+ id: normalizedNavId(),
115
+ orientation: inputs.orientation(),
116
+ type: inputs.type(),
117
+ position: inputs.position(),
118
+ collapsed: collapsedPreference(),
119
+ dockbarMode: inputs.dockbarMode(),
120
+ }, owner);
121
+ if (previousRegisteredId && previousRegisteredId !== nextState.id) {
122
+ nav.unregister(previousRegisteredId, owner);
123
+ }
124
+ previousRegisteredId = nextState.id;
125
+ registeredState.set(nextState);
126
+ });
127
+ destroyRef.onDestroy(() => {
128
+ if (previousRegisteredId) {
129
+ nav.unregister(previousRegisteredId, owner);
130
+ }
131
+ });
132
+ return {
133
+ normalizedNavId,
134
+ normalizedItems,
135
+ resolvedState,
136
+ activeIdSet,
137
+ toggleCollapsed: () => nav.toggleCollapsed(normalizedNavId()),
138
+ };
139
+ }
140
+ function normalizeId(value) {
141
+ return value.trim() || 'default';
142
+ }
143
+ function fallbackType(orientation, type) {
144
+ if (orientation === 'vertical' && (type === 'sidebar' || type === 'dockbar')) {
145
+ return type;
146
+ }
147
+ if (orientation === 'horizontal' && (type === 'navbar' || type === 'flyout')) {
148
+ return type;
149
+ }
150
+ return orientation === 'horizontal' ? 'navbar' : 'sidebar';
151
+ }
152
+
153
+ const NAVIGATION_SHELL = new InjectionToken('NAVIGATION_SHELL');
154
+
155
+ /**
156
+ * Container navigasi deklaratif. Type ditentukan oleh komponen anak:
157
+ *
158
+ * ```html
159
+ * <Navigation id="main" [data]="items">
160
+ * <NavigationSidebar>
161
+ * <NavigationHeader>…</NavigationHeader>
162
+ * <NavigationContent />
163
+ * <NavigationFooter>…</NavigationFooter>
164
+ * </NavigationSidebar>
165
+ * </Navigation>
166
+ * ```
167
+ *
168
+ * Header dan footer opsional; `NavigationContent` selalu dirender oleh
169
+ * type (default otomatis bila tidak diproyeksikan).
170
+ */
171
+ class NavigationContainerComponent {
172
+ navId = input('default', { ...(ngDevMode ? { debugName: "navId" } : /* istanbul ignore next */ {}), alias: 'id' });
173
+ data = input([], /* @ts-ignore */
174
+ ...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
175
+ items = input([], /* @ts-ignore */
176
+ ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
177
+ ariaLabel = input('Primary navigation', /* @ts-ignore */
178
+ ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
179
+ compact = input(false, /* @ts-ignore */
180
+ ...(ngDevMode ? [{ debugName: "compact" }] : /* istanbul ignore next */ []));
181
+ collapseTree = input('stairs', { ...(ngDevMode ? { debugName: "collapseTree" } : /* istanbul ignore next */ {}), alias: 'collapse-tree' });
182
+ class = input('', /* @ts-ignore */
183
+ ...(ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []));
184
+ itemClass = input('', /* @ts-ignore */
185
+ ...(ngDevMode ? [{ debugName: "itemClass" }] : /* istanbul ignore next */ []));
186
+ /** Kelas Tailwind untuk container `<li>` group horizontal (flyout tab / navbar group). */
187
+ groupClass = input('', { ...(ngDevMode ? { debugName: "groupClass" } : /* istanbul ignore next */ {}), alias: 'nav-group-class' });
188
+ activeIds = input(null, /* @ts-ignore */
189
+ ...(ngDevMode ? [{ debugName: "activeIds" }] : /* istanbul ignore next */ []));
190
+ activeUrl = input(null, /* @ts-ignore */
191
+ ...(ngDevMode ? [{ debugName: "activeUrl" }] : /* istanbul ignore next */ []));
192
+ openedIds = model([], /* @ts-ignore */
193
+ ...(ngDevMode ? [{ debugName: "openedIds" }] : /* istanbul ignore next */ []));
194
+ itemSelected = output();
195
+ nav = inject(NavigationService);
196
+ hoverPreviewExpanded = signal(false, /* @ts-ignore */
197
+ ...(ngDevMode ? [{ debugName: "hoverPreviewExpanded" }] : /* istanbul ignore next */ []));
198
+ /** Konfigurasi type aktif yang didaftarkan wrapper anak (sidebar/dockbar/navbar/flyout). */
199
+ typeConfig = signal(null, /* @ts-ignore */
200
+ ...(ngDevMode ? [{ debugName: "typeConfig" }] : /* istanbul ignore next */ []));
201
+ orientationPreference = computed(() => this.typeConfig()?.orientation ?? 'vertical', /* @ts-ignore */
202
+ ...(ngDevMode ? [{ debugName: "orientationPreference" }] : /* istanbul ignore next */ []));
203
+ typePreference = computed(() => this.typeConfig()?.type ?? null, /* @ts-ignore */
204
+ ...(ngDevMode ? [{ debugName: "typePreference" }] : /* istanbul ignore next */ []));
205
+ positionPreference = computed(() => this.typeConfig()?.position?.() ?? null, /* @ts-ignore */
206
+ ...(ngDevMode ? [{ debugName: "positionPreference" }] : /* istanbul ignore next */ []));
207
+ collapsedPreference = computed(() => this.typeConfig()?.collapsed?.() ?? null, /* @ts-ignore */
208
+ ...(ngDevMode ? [{ debugName: "collapsedPreference" }] : /* istanbul ignore next */ []));
209
+ dockbarModePreference = computed(() => this.typeConfig()?.dockbarMode?.() ?? null, /* @ts-ignore */
210
+ ...(ngDevMode ? [{ debugName: "dockbarModePreference" }] : /* istanbul ignore next */ []));
211
+ sidebarCollapse = computed(() => this.typeConfig()?.sidebarCollapse?.() ?? false, /* @ts-ignore */
212
+ ...(ngDevMode ? [{ debugName: "sidebarCollapse" }] : /* istanbul ignore next */ []));
213
+ previewExpanded = computed(() => this.typeConfig()?.previewExpanded?.() ?? false, /* @ts-ignore */
214
+ ...(ngDevMode ? [{ debugName: "previewExpanded" }] : /* istanbul ignore next */ []));
215
+ typeStyle = computed(() => this.typeConfig()?.typeStyle?.() ?? 'default', /* @ts-ignore */
216
+ ...(ngDevMode ? [{ debugName: "typeStyle" }] : /* istanbul ignore next */ []));
217
+ flyoutLabel = computed(() => this.typeConfig()?.flyoutLabel?.() ?? 'Menu', /* @ts-ignore */
218
+ ...(ngDevMode ? [{ debugName: "flyoutLabel" }] : /* istanbul ignore next */ []));
219
+ flyoutIcon = computed(() => this.typeConfig()?.flyoutIcon?.() ?? null, /* @ts-ignore */
220
+ ...(ngDevMode ? [{ debugName: "flyoutIcon" }] : /* istanbul ignore next */ []));
221
+ flyoutIconOnly = computed(() => this.typeConfig()?.flyoutIconOnly?.() ?? false, /* @ts-ignore */
222
+ ...(ngDevMode ? [{ debugName: "flyoutIconOnly" }] : /* istanbul ignore next */ []));
223
+ flyoutIconPosition = computed(() => this.typeConfig()?.flyoutIconPosition?.() ?? 'start', /* @ts-ignore */
224
+ ...(ngDevMode ? [{ debugName: "flyoutIconPosition" }] : /* istanbul ignore next */ []));
225
+ controller = injectNavigationController({
226
+ navId: this.navId,
227
+ data: this.data,
228
+ items: this.items,
229
+ orientation: this.orientationPreference,
230
+ type: this.typePreference,
231
+ position: this.positionPreference,
232
+ collapsed: this.collapsedPreference,
233
+ dockbarMode: this.dockbarModePreference,
234
+ compact: this.compact,
235
+ activeIds: this.activeIds,
236
+ enabled: computed(() => this.typeConfig() !== null),
237
+ });
238
+ iconTemplate = contentChild(NavigationIconDirective, /* @ts-ignore */
239
+ ...(ngDevMode ? [{ debugName: "iconTemplate" }] : /* istanbul ignore next */ []));
240
+ normalizedItems = this.controller.normalizedItems;
241
+ resolvedState = this.controller.resolvedState;
242
+ activeIdSet = this.controller.activeIdSet;
243
+ previewRailExpanded = computed(() => {
244
+ const state = this.resolvedState();
245
+ if (state.orientation !== 'vertical' || state.type !== 'sidebar' || !state.collapsed) {
246
+ return false;
247
+ }
248
+ return this.previewExpanded() || (this.sidebarCollapse() && this.hoverPreviewExpanded());
249
+ }, /* @ts-ignore */
250
+ ...(ngDevMode ? [{ debugName: "previewRailExpanded" }] : /* istanbul ignore next */ []));
251
+ previewRailOffset = computed(() => (this.previewRailExpanded() ? '15rem' : '0px'), /* @ts-ignore */
252
+ ...(ngDevMode ? [{ debugName: "previewRailOffset" }] : /* istanbul ignore next */ []));
253
+ displayState = computed(() => {
254
+ const state = this.resolvedState();
255
+ if (state.type === 'dockbar') {
256
+ return { ...state, collapsed: true };
257
+ }
258
+ return this.previewRailExpanded() ? { ...state, collapsed: false } : state;
259
+ }, /* @ts-ignore */
260
+ ...(ngDevMode ? [{ debugName: "displayState" }] : /* istanbul ignore next */ []));
261
+ dockbarAsideOpen = computed(() => {
262
+ const state = this.resolvedState();
263
+ if (state.type !== 'dockbar' || state.dockbarMode !== 'sticky') {
264
+ return false;
265
+ }
266
+ return (this.nav.resolveDockbarGroup(state.id, this.normalizedItems(), state.dockbarMode, this.activeIdSet(), this.activeUrl()) !== null);
267
+ }, /* @ts-ignore */
268
+ ...(ngDevMode ? [{ debugName: "dockbarAsideOpen" }] : /* istanbul ignore next */ []));
269
+ collapseEnabled = computed(() => {
270
+ const state = this.resolvedState();
271
+ return this.sidebarCollapse() && state.orientation === 'vertical' && state.type === 'sidebar';
272
+ }, /* @ts-ignore */
273
+ ...(ngDevMode ? [{ debugName: "collapseEnabled" }] : /* istanbul ignore next */ []));
274
+ /** Kelas shell yang dipakai host komponen type (eks `div` shell internal). */
275
+ shellClasses = computed(() => cn('flex h-full min-h-0 transition-[width,box-shadow,transform] duration-300 ease-[cubic-bezier(0.22,1,0.36,1)] motion-reduce:transition-none', this.resolvedState().orientation === 'horizontal'
276
+ ? 'flex-row items-stretch overflow-visible'
277
+ : this.resolvedState().type === 'dockbar'
278
+ ? 'flex-col overflow-visible'
279
+ : 'flex-col overflow-hidden', this.resolvedState().type === 'dockbar' && [
280
+ 'w-16 shrink-0',
281
+ this.resolvedState().position === 'right' && 'ml-auto',
282
+ ], this.resolvedState().position === 'right' ? 'origin-right' : 'origin-left', this.previewRailExpanded() && [
283
+ 'absolute inset-y-0 z-20 w-76 overflow-hidden bg-background shadow-xl',
284
+ this.resolvedState().position === 'right'
285
+ ? 'right-0 border-l border-[hsl(var(--border)/var(--opacity-70))]'
286
+ : 'left-0 border-r border-[hsl(var(--border)/var(--opacity-70))]',
287
+ ]), /* @ts-ignore */
288
+ ...(ngDevMode ? [{ debugName: "shellClasses" }] : /* istanbul ignore next */ []));
289
+ state = this.resolvedState;
290
+ hostClasses = computed(() => cn('relative block min-h-0 text-foreground transition-[width] duration-300 ease-[cubic-bezier(0.22,1,0.36,1)] motion-reduce:transition-none', this.resolvedState().orientation === 'horizontal'
291
+ ? 'w-full'
292
+ : this.resolvedState().type === 'dockbar'
293
+ ? this.dockbarAsideOpen()
294
+ ? 'w-76'
295
+ : 'w-16'
296
+ : this.resolvedState().collapsed
297
+ ? 'w-16'
298
+ : 'w-76', this.resolvedState().orientation === 'horizontal' ||
299
+ this.resolvedState().type === 'dockbar' ||
300
+ this.previewRailExpanded()
301
+ ? 'overflow-visible'
302
+ : 'overflow-hidden', this.class()), /* @ts-ignore */
303
+ ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
304
+ /**
305
+ * Dipanggil komponen type saat dibuat. Satu `<Navigation>` hanya boleh
306
+ * memuat satu type hidup — pendaftaran ganda dianggap salah konfigurasi.
307
+ */
308
+ registerType(config) {
309
+ if (this.typeConfig() !== null && isDevMode()) {
310
+ throw new Error('[Navigation] Hanya satu type (<NavigationSidebar>/<NavigationDockbar>/<NavigationNavbar>/<NavigationFlyout>) yang boleh hidup dalam satu <Navigation>.');
311
+ }
312
+ this.typeConfig.set(config);
313
+ }
314
+ unregisterType(config) {
315
+ if (this.typeConfig() === config) {
316
+ this.typeConfig.set(null);
317
+ }
318
+ }
319
+ toggleCollapsed() {
320
+ this.controller.toggleCollapsed();
321
+ }
322
+ setHoverPreview(value) {
323
+ this.hoverPreviewExpanded.set(this.sidebarCollapse() ? value : false);
324
+ }
325
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: NavigationContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
326
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.0.2", type: NavigationContainerComponent, isStandalone: true, selector: "Navigation", inputs: { navId: { classPropertyName: "navId", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, compact: { classPropertyName: "compact", publicName: "compact", isSignal: true, isRequired: false, transformFunction: null }, collapseTree: { classPropertyName: "collapseTree", publicName: "collapse-tree", isSignal: true, isRequired: false, transformFunction: null }, class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, itemClass: { classPropertyName: "itemClass", publicName: "itemClass", isSignal: true, isRequired: false, transformFunction: null }, groupClass: { classPropertyName: "groupClass", publicName: "nav-group-class", 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 }, openedIds: { classPropertyName: "openedIds", publicName: "openedIds", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { openedIds: "openedIdsChange", itemSelected: "itemSelected" }, host: { attributes: { "role": "navigation" }, listeners: { "mouseenter": "setHoverPreview(true)", "mouseleave": "setHoverPreview(false)" }, properties: { "class": "hostClasses()", "attr.aria-label": "ariaLabel() || null", "attr.data-navigation-id": "resolvedState().id", "attr.data-orientation": "resolvedState().orientation", "attr.data-type": "resolvedState().type", "attr.data-type-style": "typeStyle()", "attr.data-position": "resolvedState().position", "attr.data-collapse-tree": "collapseTree()", "attr.data-preview-expanded": "previewRailExpanded() ? \"true\" : null" } }, providers: [{ provide: NAVIGATION_SHELL, useExisting: forwardRef(() => NavigationContainerComponent) }], queries: [{ propertyName: "iconTemplate", first: true, predicate: NavigationIconDirective, descendants: true, isSignal: true }], ngImport: i0, template: ` <ng-content /> `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
327
+ }
328
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: NavigationContainerComponent, decorators: [{
329
+ type: Component,
330
+ args: [{
331
+ selector: 'Navigation',
332
+ changeDetection: ChangeDetectionStrategy.OnPush,
333
+ providers: [{ provide: NAVIGATION_SHELL, useExisting: forwardRef(() => NavigationContainerComponent) }],
334
+ host: {
335
+ '[class]': 'hostClasses()',
336
+ role: 'navigation',
337
+ '[attr.aria-label]': 'ariaLabel() || null',
338
+ '[attr.data-navigation-id]': 'resolvedState().id',
339
+ '[attr.data-orientation]': 'resolvedState().orientation',
340
+ '[attr.data-type]': 'resolvedState().type',
341
+ '[attr.data-type-style]': 'typeStyle()',
342
+ '[attr.data-position]': 'resolvedState().position',
343
+ '[attr.data-collapse-tree]': 'collapseTree()',
344
+ '[attr.data-preview-expanded]': 'previewRailExpanded() ? "true" : null',
345
+ '(mouseenter)': 'setHoverPreview(true)',
346
+ '(mouseleave)': 'setHoverPreview(false)',
347
+ },
348
+ template: ` <ng-content /> `,
349
+ }]
350
+ }], propDecorators: { navId: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], compact: [{ type: i0.Input, args: [{ isSignal: true, alias: "compact", required: false }] }], collapseTree: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapse-tree", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], itemClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemClass", required: false }] }], groupClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "nav-group-class", required: false }] }], activeIds: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeIds", required: false }] }], activeUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeUrl", required: false }] }], openedIds: [{ type: i0.Input, args: [{ isSignal: true, alias: "openedIds", required: false }] }, { type: i0.Output, args: ["openedIdsChange"] }], itemSelected: [{ type: i0.Output, args: ["itemSelected"] }], iconTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NavigationIconDirective), { isSignal: true }] }] } });
351
+
352
+ class NavigationItemContentComponent {
353
+ nav = inject(NavigationService);
354
+ item = input.required(/* @ts-ignore */
355
+ ...(ngDevMode ? [{ debugName: "item" }] : /* istanbul ignore next */ []));
356
+ active = input(false, /* @ts-ignore */
357
+ ...(ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []));
358
+ compact = input(false, /* @ts-ignore */
359
+ ...(ngDevMode ? [{ debugName: "compact" }] : /* istanbul ignore next */ []));
360
+ orientation = input('vertical', /* @ts-ignore */
361
+ ...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
362
+ level = input(0, /* @ts-ignore */
363
+ ...(ngDevMode ? [{ debugName: "level" }] : /* istanbul ignore next */ []));
364
+ collapseTree = input('stairs', /* @ts-ignore */
365
+ ...(ngDevMode ? [{ debugName: "collapseTree" }] : /* istanbul ignore next */ []));
366
+ iconTemplate = input(undefined, /* @ts-ignore */
367
+ ...(ngDevMode ? [{ debugName: "iconTemplate" }] : /* istanbul ignore next */ []));
368
+ iconSlotClasses = computed(() => cn('relative z-10 inline-flex shrink-0 items-center justify-center transition-[border-color,background-color,color,box-shadow] duration-200', this.orientation() === 'vertical'
369
+ ? this.compact()
370
+ ? 'h-8 w-8 rounded-full border'
371
+ : 'h-7 w-7 rounded-full border'
372
+ : 'h-5 w-5 rounded-full border border-transparent', this.usesStraightVerticalSurface() && 'overflow-hidden bg-background/85 backdrop-blur-sm border-transparent', this.active() && !this.usesStraightVerticalSurface() ? 'border-primary text-current' : 'border-transparent', this.active() && this.usesStraightVerticalSurface() && 'text-current', this.compact() && 'mx-auto'), /* @ts-ignore */
373
+ ...(ngDevMode ? [{ debugName: "iconSlotClasses" }] : /* istanbul ignore next */ []));
374
+ titleClasses = computed(() => cn('block truncate', this.active() && 'font-semibold text-foreground', this.item().classes?.title), /* @ts-ignore */
375
+ ...(ngDevMode ? [{ debugName: "titleClasses" }] : /* istanbul ignore next */ []));
376
+ showSubtitle = computed(() => !!this.item().subtitle && !(this.orientation() === 'horizontal' && this.level() === 0), /* @ts-ignore */
377
+ ...(ngDevMode ? [{ debugName: "showSubtitle" }] : /* istanbul ignore next */ []));
378
+ subtitleClasses = computed(() => cn('block truncate text-xs font-normal text-muted-foreground', this.item().classes?.subtitle), /* @ts-ignore */
379
+ ...(ngDevMode ? [{ debugName: "subtitleClasses" }] : /* istanbul ignore next */ []));
380
+ compactFallbackClasses = computed(() => cn('inline-flex h-5 min-w-5 items-center justify-center text-xs font-semibold uppercase'), /* @ts-ignore */
381
+ ...(ngDevMode ? [{ debugName: "compactFallbackClasses" }] : /* istanbul ignore next */ []));
382
+ defaultBadgeClasses = computed(() => cn('ml-auto inline-flex h-5 shrink-0 items-center rounded-full px-2 text-xs font-medium', this.active() ? 'bg-background text-foreground' : 'bg-muted text-muted-foreground'), /* @ts-ignore */
383
+ ...(ngDevMode ? [{ debugName: "defaultBadgeClasses" }] : /* istanbul ignore next */ []));
384
+ defaultIconSize = computed(() => (this.usesStraightVerticalSurface() ? 18 : 16), /* @ts-ignore */
385
+ ...(ngDevMode ? [{ debugName: "defaultIconSize" }] : /* istanbul ignore next */ []));
386
+ defaultIconClasses = computed(() => cn('text-current', this.item().classes?.icon), /* @ts-ignore */
387
+ ...(ngDevMode ? [{ debugName: "defaultIconClasses" }] : /* istanbul ignore next */ []));
388
+ iconContext = computed(() => this.nav.iconContext(this.item(), this.active(), this.level(), this.orientation()), /* @ts-ignore */
389
+ ...(ngDevMode ? [{ debugName: "iconContext" }] : /* istanbul ignore next */ []));
390
+ compactFallback() {
391
+ return this.nav.compactFallback(this.item());
392
+ }
393
+ usesStraightVerticalSurface() {
394
+ return this.orientation() === 'vertical' && !this.compact() && this.collapseTree() === 'straight';
395
+ }
396
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: NavigationItemContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
397
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: NavigationItemContentComponent, isStandalone: true, selector: "NavigationItemContent", inputs: { item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: true, transformFunction: null }, active: { classPropertyName: "active", publicName: "active", isSignal: true, isRequired: false, transformFunction: null }, compact: { classPropertyName: "compact", publicName: "compact", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, level: { classPropertyName: "level", publicName: "level", isSignal: true, isRequired: false, transformFunction: null }, collapseTree: { classPropertyName: "collapseTree", publicName: "collapseTree", isSignal: true, isRequired: false, transformFunction: null }, iconTemplate: { classPropertyName: "iconTemplate", publicName: "iconTemplate", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "contents" }, ngImport: i0, template: `
398
+ @if (item().icon; as iconName) {
399
+ <span data-navigation-icon-slot="true" [class]="iconSlotClasses()">
400
+ @if (iconTemplate(); as customIcon) {
401
+ <ng-container [ngTemplateOutlet]="customIcon.template" [ngTemplateOutletContext]="iconContext()" />
402
+ } @else {
403
+ <Icon [name]="iconName" [class]="defaultIconClasses()" [size]="defaultIconSize()" />
404
+ }
405
+ </span>
406
+ }
407
+
408
+ @if (!compact()) {
409
+ <span class="min-w-0 flex-1">
410
+ <span [class]="titleClasses()">{{ item().title }}</span>
411
+ @if (showSubtitle()) {
412
+ <span [class]="subtitleClasses()">{{ item().subtitle }}</span>
413
+ }
414
+ </span>
415
+
416
+ @if (item().badge; as badge) {
417
+ <span [class]="badge.classes ?? defaultBadgeClasses()">{{ badge.title }}</span>
418
+ }
419
+ }
420
+
421
+ @if (compact() && !item().icon) {
422
+ <span aria-hidden="true" [class]="compactFallbackClasses()">{{ compactFallback() }}</span>
423
+ }
424
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: IconComponent, selector: "Icon", inputs: ["name", "class", "size", "fill", "weight", "grade", "opticalSize"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
425
+ }
426
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: NavigationItemContentComponent, decorators: [{
427
+ type: Component,
428
+ args: [{
429
+ selector: 'NavigationItemContent',
430
+ changeDetection: ChangeDetectionStrategy.OnPush,
431
+ imports: [NgTemplateOutlet, IconComponent],
432
+ host: {
433
+ class: 'contents',
434
+ },
435
+ template: `
436
+ @if (item().icon; as iconName) {
437
+ <span data-navigation-icon-slot="true" [class]="iconSlotClasses()">
438
+ @if (iconTemplate(); as customIcon) {
439
+ <ng-container [ngTemplateOutlet]="customIcon.template" [ngTemplateOutletContext]="iconContext()" />
440
+ } @else {
441
+ <Icon [name]="iconName" [class]="defaultIconClasses()" [size]="defaultIconSize()" />
442
+ }
443
+ </span>
444
+ }
445
+
446
+ @if (!compact()) {
447
+ <span class="min-w-0 flex-1">
448
+ <span [class]="titleClasses()">{{ item().title }}</span>
449
+ @if (showSubtitle()) {
450
+ <span [class]="subtitleClasses()">{{ item().subtitle }}</span>
451
+ }
452
+ </span>
453
+
454
+ @if (item().badge; as badge) {
455
+ <span [class]="badge.classes ?? defaultBadgeClasses()">{{ badge.title }}</span>
456
+ }
457
+ }
458
+
459
+ @if (compact() && !item().icon) {
460
+ <span aria-hidden="true" [class]="compactFallbackClasses()">{{ compactFallback() }}</span>
461
+ }
462
+ `,
463
+ }]
464
+ }], propDecorators: { item: [{ type: i0.Input, args: [{ isSignal: true, alias: "item", required: true }] }], active: [{ type: i0.Input, args: [{ isSignal: true, alias: "active", required: false }] }], compact: [{ type: i0.Input, args: [{ isSignal: true, alias: "compact", required: false }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], level: [{ type: i0.Input, args: [{ isSignal: true, alias: "level", required: false }] }], collapseTree: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapseTree", required: false }] }], iconTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconTemplate", required: false }] }] } });
465
+
466
+ class NavigationItemComponent {
467
+ nav = inject(NavigationService);
468
+ navId = input('default', /* @ts-ignore */
469
+ ...(ngDevMode ? [{ debugName: "navId" }] : /* istanbul ignore next */ []));
470
+ item = input.required(/* @ts-ignore */
471
+ ...(ngDevMode ? [{ debugName: "item" }] : /* istanbul ignore next */ []));
472
+ level = input(0, /* @ts-ignore */
473
+ ...(ngDevMode ? [{ debugName: "level" }] : /* istanbul ignore next */ []));
474
+ orientation = input('vertical', /* @ts-ignore */
475
+ ...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
476
+ compact = input(false, /* @ts-ignore */
477
+ ...(ngDevMode ? [{ debugName: "compact" }] : /* istanbul ignore next */ []));
478
+ itemClass = input('', /* @ts-ignore */
479
+ ...(ngDevMode ? [{ debugName: "itemClass" }] : /* istanbul ignore next */ []));
480
+ activeIds = input(new Set(), /* @ts-ignore */
481
+ ...(ngDevMode ? [{ debugName: "activeIds" }] : /* istanbul ignore next */ []));
482
+ activeUrl = input(null, /* @ts-ignore */
483
+ ...(ngDevMode ? [{ debugName: "activeUrl" }] : /* istanbul ignore next */ []));
484
+ iconTemplate = input(undefined, /* @ts-ignore */
485
+ ...(ngDevMode ? [{ debugName: "iconTemplate" }] : /* istanbul ignore next */ []));
486
+ collapseTree = input('stairs', /* @ts-ignore */
487
+ ...(ngDevMode ? [{ debugName: "collapseTree" }] : /* istanbul ignore next */ []));
488
+ straightRail = input(false, /* @ts-ignore */
489
+ ...(ngDevMode ? [{ debugName: "straightRail" }] : /* istanbul ignore next */ []));
490
+ straightRailActive = input(false, /* @ts-ignore */
491
+ ...(ngDevMode ? [{ debugName: "straightRailActive" }] : /* istanbul ignore next */ []));
492
+ firstInBranch = input(false, /* @ts-ignore */
493
+ ...(ngDevMode ? [{ debugName: "firstInBranch" }] : /* istanbul ignore next */ []));
494
+ lastInBranch = input(false, /* @ts-ignore */
495
+ ...(ngDevMode ? [{ debugName: "lastInBranch" }] : /* istanbul ignore next */ []));
496
+ openedIds = input([], /* @ts-ignore */
497
+ ...(ngDevMode ? [{ debugName: "openedIds" }] : /* istanbul ignore next */ []));
498
+ openedIdsChange = output();
499
+ itemSelected = output();
500
+ openedIdSet = computed(() => new Set(this.openedIds()), /* @ts-ignore */
501
+ ...(ngDevMode ? [{ debugName: "openedIdSet" }] : /* istanbul ignore next */ []));
502
+ isHorizontalRoot = computed(() => this.orientation() === 'horizontal' && this.level() === 0, /* @ts-ignore */
503
+ ...(ngDevMode ? [{ debugName: "isHorizontalRoot" }] : /* istanbul ignore next */ []));
504
+ isActive = computed(() => this.nav.isItemActive(this.item(), this.activeIds(), this.activeUrl()), /* @ts-ignore */
505
+ ...(ngDevMode ? [{ debugName: "isActive" }] : /* istanbul ignore next */ []));
506
+ showChildren = computed(() => !this.compact() && this.isOpen(), /* @ts-ignore */
507
+ ...(ngDevMode ? [{ debugName: "showChildren" }] : /* istanbul ignore next */ []));
508
+ hostClasses = computed(() => {
509
+ if (this.item().type === 'divider') {
510
+ return this.dividerClasses();
511
+ }
512
+ if (this.item().type === 'spacer') {
513
+ return 'flex-1';
514
+ }
515
+ if (this.item().type === 'group' || this.item().type === 'mega') {
516
+ return this.groupContainerClasses();
517
+ }
518
+ return this.itemContainerClasses();
519
+ }, /* @ts-ignore */
520
+ ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
521
+ hostRole = computed(() => {
522
+ if (this.item().type === 'divider') {
523
+ return 'separator';
524
+ }
525
+ return this.orientation() === 'horizontal' ? 'none' : null;
526
+ }, /* @ts-ignore */
527
+ ...(ngDevMode ? [{ debugName: "hostRole" }] : /* istanbul ignore next */ []));
528
+ headingId = computed(() => `${this.item().panelId}-heading`, /* @ts-ignore */
529
+ ...(ngDevMode ? [{ debugName: "headingId" }] : /* istanbul ignore next */ []));
530
+ isRouterItem() {
531
+ return this.nav.isRouterItem(this.item());
532
+ }
533
+ hrefFor() {
534
+ return this.nav.hrefFor(this.item());
535
+ }
536
+ relFor() {
537
+ return this.nav.relFor(this.item());
538
+ }
539
+ routerLinkActiveOptions() {
540
+ return this.nav.routerLinkActiveOptions(this.item());
541
+ }
542
+ itemRole() {
543
+ return this.nav.itemRole(this.orientation(), this.level());
544
+ }
545
+ compactLabel() {
546
+ return this.nav.compactLabel(this.item(), this.compact());
547
+ }
548
+ titleFor() {
549
+ return this.nav.titleFor(this.item(), this.compact());
550
+ }
551
+ isOpen() {
552
+ return this.openedIdSet().has(this.item().stateId) || this.openedIdSet().has(this.item().key) || this.isActive();
553
+ }
554
+ toggleOpen() {
555
+ if (this.item().disabled) {
556
+ return;
557
+ }
558
+ const next = new Set(this.openedIds());
559
+ if (next.has(this.item().stateId)) {
560
+ next.delete(this.item().stateId);
561
+ }
562
+ else {
563
+ next.add(this.item().stateId);
564
+ }
565
+ this.openedIdsChange.emit([...next]);
566
+ }
567
+ selectItem(event) {
568
+ if (this.item().disabled) {
569
+ event.preventDefault();
570
+ event.stopPropagation();
571
+ return;
572
+ }
573
+ this.item().action?.(this.item().source);
574
+ this.itemSelected.emit({
575
+ item: this.item().source,
576
+ key: this.item().key,
577
+ type: this.item().type,
578
+ link: this.item().link,
579
+ external: !!this.hrefFor(),
580
+ });
581
+ this.nav.closePanel(this.navId());
582
+ this.nav.closeDrawer(this.navId());
583
+ }
584
+ leafClasses(active) {
585
+ return cn('NavigationItem group/nav inline-flex min-w-0 items-center rounded-md text-sm font-medium transition-colors', 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring', this.orientation() === 'horizontal' && this.level() === 0 ? 'h-9 px-3 py-2' : 'w-full px-3 py-1.5 text-left', this.orientation() === 'horizontal' && this.level() === 0 ? 'gap-3' : 'gap-1.5', this.compact() && 'h-10 w-10 justify-center gap-0 px-0 text-center', this.interactiveStateClasses(active), this.item().disabled && 'pointer-events-none opacity-50', this.item().classes?.wrapper, this.itemClass());
586
+ }
587
+ triggerClasses(active) {
588
+ return cn('nav-trigger group/nav inline-flex min-w-0 items-center rounded-md text-sm font-medium transition-colors', 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50', this.orientation() === 'horizontal' && this.level() === 0 ? 'h-9 px-3 py-2' : 'w-full px-3 py-1.5 text-left', this.orientation() === 'horizontal' && this.level() === 0 ? 'gap-3' : 'gap-1.5', this.compact() && 'h-10 w-10 justify-center gap-0 px-0 text-center', this.interactiveStateClasses(active), this.item().classes?.wrapper, this.itemClass());
589
+ }
590
+ interactiveStateClasses(active) {
591
+ const showsNestedActiveConnector = active &&
592
+ this.orientation() === 'vertical' &&
593
+ !this.compact() &&
594
+ this.level() > 1 &&
595
+ this.collapseTree() === 'stairs';
596
+ if (this.orientation() === 'vertical' && !this.compact()) {
597
+ return cn('relative isolate', "before:pointer-events-none before:absolute before:inset-y-0 before:left-2 before:right-2 before:-z-10 before:rounded-md before:border before:border-transparent before:bg-transparent before:transition-colors before:duration-200 before:content-['']", showsNestedActiveConnector &&
598
+ "after:pointer-events-none after:absolute after:-left-2 after:top-1/2 after:-z-10 after:h-px after:w-5 after:-translate-y-1/2 after:rounded-full after:bg-current/25 after:content-['']", active
599
+ ? 'font-semibold text-foreground hover:before:border-primary'
600
+ : 'text-foreground/80 hover:text-accent-foreground hover:before:border-primary hover:before:bg-accent');
601
+ }
602
+ return active ? 'font-semibold text-foreground' : 'text-foreground/80 hover:bg-accent hover:text-accent-foreground';
603
+ }
604
+ chevronClasses() {
605
+ return cn('ml-auto shrink-0 self-center transition-transform duration-200', this.isOpen() && 'rotate-90');
606
+ }
607
+ itemContainerClasses() {
608
+ return cn(this.orientation() === 'horizontal' && this.level() === 0 ? 'relative' : 'w-full', this.compact() && 'flex justify-center', this.straightRailClasses());
609
+ }
610
+ groupContainerClasses() {
611
+ return cn('w-full', this.level() > 0 && 'pt-2');
612
+ }
613
+ dividerClasses() {
614
+ return cn(this.orientation() === 'horizontal' && this.level() === 0 ? 'mx-1 h-5 w-px' : 'my-2 w-full px-2');
615
+ }
616
+ groupHeadingClasses() {
617
+ return cn('px-3 py-2 text-xs font-medium uppercase tracking-wide text-muted-foreground', this.item().classes?.title);
618
+ }
619
+ childListClasses() {
620
+ const isGroupedBranch = this.item().type === 'group' || this.item().type === 'mega';
621
+ const usesStairsTree = this.collapseTree() === 'stairs';
622
+ return cn('flex list-none flex-col gap-0.5 p-0', !this.compact() && 'mt-0.5', this.level() >= 0 &&
623
+ !this.compact() &&
624
+ !isGroupedBranch &&
625
+ usesStairsTree &&
626
+ cn("relative ml-[1.625rem] pl-2 before:absolute before:-top-2 before:bottom-0 before:left-0 before:w-px before:-translate-x-1/2 before:rounded-full before:content-['']", this.isActive() ? 'before:bg-current/25' : 'before:bg-border'));
627
+ }
628
+ showStraightRailForChildren() {
629
+ return (!this.compact() &&
630
+ this.collapseTree() === 'straight' &&
631
+ this.item().type !== 'group' &&
632
+ this.item().type !== 'mega');
633
+ }
634
+ straightRailClasses() {
635
+ if (!this.straightRail() || this.orientation() !== 'vertical' || this.compact()) {
636
+ return '';
637
+ }
638
+ return cn('relative', "before:pointer-events-none before:absolute before:left-[1.625rem] before:w-0 before:-z-10 before:border-l before:border-dotted before:content-['']", this.straightRailActive() ? 'before:border-primary/50' : 'before:border-border/50', this.firstInBranch() ? 'before:-top-9' : 'before:-top-0.5', this.lastInBranch() ? 'before:bottom-[calc(100%-1.25rem)]' : 'before:-bottom-0.5');
639
+ }
640
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: NavigationItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
641
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: NavigationItemComponent, isStandalone: true, selector: "li[NavigationItem]", inputs: { navId: { classPropertyName: "navId", publicName: "navId", isSignal: true, isRequired: false, transformFunction: null }, item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: true, transformFunction: null }, level: { classPropertyName: "level", publicName: "level", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, compact: { classPropertyName: "compact", publicName: "compact", 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 }, straightRail: { classPropertyName: "straightRail", publicName: "straightRail", isSignal: true, isRequired: false, transformFunction: null }, straightRailActive: { classPropertyName: "straightRailActive", publicName: "straightRailActive", isSignal: true, isRequired: false, transformFunction: null }, firstInBranch: { classPropertyName: "firstInBranch", publicName: "firstInBranch", isSignal: true, isRequired: false, transformFunction: null }, lastInBranch: { classPropertyName: "lastInBranch", publicName: "lastInBranch", isSignal: true, isRequired: false, transformFunction: null }, openedIds: { classPropertyName: "openedIds", publicName: "openedIds", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { openedIdsChange: "openedIdsChange", itemSelected: "itemSelected" }, host: { properties: { "class": "hostClasses()", "attr.role": "hostRole()", "attr.data-navigation-item-key": "item().key", "attr.aria-hidden": "item().type === \"spacer\" ? \"true\" : null" } }, ngImport: i0, template: `
642
+ @switch (item().type) {
643
+ @case ('divider') {
644
+ <span class="block h-px w-full bg-border"></span>
645
+ }
646
+
647
+ @case ('spacer') {}
648
+
649
+ @case ('group') {
650
+ @if (!compact() && item().title) {
651
+ <div [id]="headingId()" [class]="groupHeadingClasses()">{{ item().title }}</div>
652
+ }
653
+
654
+ <ul [class]="childListClasses()" role="group" [attr.aria-labelledby]="item().title ? headingId() : null">
655
+ @for (child of item().children; track child.key; let isFirst = $first; let isLast = $last) {
656
+ <li
657
+ NavigationItem
658
+ [navId]="navId()"
659
+ [item]="child"
660
+ [level]="level() + 1"
661
+ [orientation]="orientation()"
662
+ [compact]="compact()"
663
+ [itemClass]="itemClass()"
664
+ [activeIds]="activeIds()"
665
+ [activeUrl]="activeUrl()"
666
+ [iconTemplate]="iconTemplate()"
667
+ [collapseTree]="collapseTree()"
668
+ [straightRail]="showStraightRailForChildren()"
669
+ [straightRailActive]="isActive()"
670
+ [firstInBranch]="isFirst"
671
+ [lastInBranch]="isLast"
672
+ [openedIds]="openedIds()"
673
+ (openedIdsChange)="openedIdsChange.emit($event)"
674
+ (itemSelected)="itemSelected.emit($event)"></li>
675
+ }
676
+ </ul>
677
+ }
678
+
679
+ @case ('collapsible') {
680
+ <button
681
+ type="button"
682
+ [class]="triggerClasses(isActive())"
683
+ [attr.aria-expanded]="isOpen()"
684
+ [attr.aria-controls]="item().panelId"
685
+ [attr.aria-label]="compactLabel()"
686
+ [attr.title]="titleFor()"
687
+ [disabled]="item().disabled || null"
688
+ (click)="toggleOpen()">
689
+ <NavigationItemContent
690
+ [item]="item()"
691
+ [active]="isActive()"
692
+ [compact]="compact()"
693
+ [orientation]="orientation()"
694
+ [level]="level()"
695
+ [collapseTree]="collapseTree()"
696
+ [iconTemplate]="iconTemplate()" />
697
+
698
+ @if (!compact()) {
699
+ <Icon name="chevron_right" [size]="16" [class]="chevronClasses()" />
700
+ }
701
+ </button>
702
+
703
+ @if (showChildren()) {
704
+ <ul [id]="item().panelId" [class]="childListClasses()" role="group">
705
+ @for (child of item().children; track child.key; let isFirst = $first; let isLast = $last) {
706
+ <li
707
+ NavigationItem
708
+ [navId]="navId()"
709
+ [item]="child"
710
+ [level]="level() + 1"
711
+ [orientation]="orientation()"
712
+ [compact]="false"
713
+ [itemClass]="itemClass()"
714
+ [activeIds]="activeIds()"
715
+ [activeUrl]="activeUrl()"
716
+ [iconTemplate]="iconTemplate()"
717
+ [collapseTree]="collapseTree()"
718
+ [straightRail]="showStraightRailForChildren()"
719
+ [straightRailActive]="isActive()"
720
+ [firstInBranch]="isFirst"
721
+ [lastInBranch]="isLast"
722
+ [openedIds]="openedIds()"
723
+ (openedIdsChange)="openedIdsChange.emit($event)"
724
+ (itemSelected)="itemSelected.emit($event)"></li>
725
+ }
726
+ </ul>
727
+ }
728
+ }
729
+
730
+ @case ('mega') {
731
+ @if (!compact() && item().title) {
732
+ <div [id]="headingId()" [class]="groupHeadingClasses()">{{ item().title }}</div>
733
+ }
734
+
735
+ <ul [class]="childListClasses()" role="group" [attr.aria-labelledby]="item().title ? headingId() : null">
736
+ @for (child of item().children; track child.key; let isFirst = $first; let isLast = $last) {
737
+ <li
738
+ NavigationItem
739
+ [navId]="navId()"
740
+ [item]="child"
741
+ [level]="level() + 1"
742
+ [orientation]="orientation()"
743
+ [compact]="compact()"
744
+ [itemClass]="itemClass()"
745
+ [activeIds]="activeIds()"
746
+ [activeUrl]="activeUrl()"
747
+ [iconTemplate]="iconTemplate()"
748
+ [collapseTree]="collapseTree()"
749
+ [straightRail]="showStraightRailForChildren()"
750
+ [straightRailActive]="isActive()"
751
+ [firstInBranch]="isFirst"
752
+ [lastInBranch]="isLast"
753
+ [openedIds]="openedIds()"
754
+ (openedIdsChange)="openedIdsChange.emit($event)"
755
+ (itemSelected)="itemSelected.emit($event)"></li>
756
+ }
757
+ </ul>
758
+ }
759
+
760
+ @default {
761
+ @if (isRouterItem()) {
762
+ <a
763
+ [class]="leafClasses(routerActive.isActive || isActive())"
764
+ [routerLink]="item().link ?? null"
765
+ [queryParams]="item().queryParams ?? null"
766
+ [queryParamsHandling]="item().queryParamsHandling ?? null"
767
+ [fragment]="item().fragment ?? undefined"
768
+ [preserveFragment]="item().preserveFragment ?? false"
769
+ [target]="item().target ?? undefined"
770
+ routerLinkActive
771
+ #routerActive="routerLinkActive"
772
+ [routerLinkActiveOptions]="routerLinkActiveOptions()"
773
+ [attr.aria-current]="routerActive.isActive || isActive() ? 'page' : null"
774
+ [attr.aria-disabled]="item().disabled || null"
775
+ [attr.aria-label]="compactLabel()"
776
+ [attr.title]="titleFor()"
777
+ [attr.role]="itemRole()"
778
+ [attr.data-navigation-root-item]="isHorizontalRoot() ? 'true' : null"
779
+ (click)="selectItem($event)">
780
+ <NavigationItemContent
781
+ [item]="item()"
782
+ [active]="routerActive.isActive || isActive()"
783
+ [compact]="compact()"
784
+ [orientation]="orientation()"
785
+ [level]="level()"
786
+ [collapseTree]="collapseTree()"
787
+ [iconTemplate]="iconTemplate()" />
788
+ </a>
789
+ } @else if (hrefFor()) {
790
+ <a
791
+ [class]="leafClasses(isActive())"
792
+ [attr.href]="hrefFor()"
793
+ [attr.target]="item().target ?? (item().externalLink ? '_blank' : null)"
794
+ [attr.rel]="relFor()"
795
+ [attr.aria-current]="isActive() ? 'page' : null"
796
+ [attr.aria-disabled]="item().disabled || null"
797
+ [attr.aria-label]="compactLabel()"
798
+ [attr.title]="titleFor()"
799
+ [attr.role]="itemRole()"
800
+ [attr.data-navigation-root-item]="isHorizontalRoot() ? 'true' : null"
801
+ (click)="selectItem($event)">
802
+ <NavigationItemContent
803
+ [item]="item()"
804
+ [active]="isActive()"
805
+ [compact]="compact()"
806
+ [orientation]="orientation()"
807
+ [level]="level()"
808
+ [collapseTree]="collapseTree()"
809
+ [iconTemplate]="iconTemplate()" />
810
+ </a>
811
+ } @else {
812
+ <button
813
+ type="button"
814
+ [class]="leafClasses(isActive())"
815
+ [disabled]="item().disabled || null"
816
+ [attr.aria-current]="isActive() ? 'page' : null"
817
+ [attr.aria-label]="compactLabel()"
818
+ [attr.title]="titleFor()"
819
+ [attr.role]="itemRole()"
820
+ [attr.data-navigation-root-item]="isHorizontalRoot() ? 'true' : null"
821
+ (click)="selectItem($event)">
822
+ <NavigationItemContent
823
+ [item]="item()"
824
+ [active]="isActive()"
825
+ [compact]="compact()"
826
+ [orientation]="orientation()"
827
+ [level]="level()"
828
+ [collapseTree]="collapseTree()"
829
+ [iconTemplate]="iconTemplate()" />
830
+ </button>
831
+ }
832
+ }
833
+ }
834
+ `, isInline: true, dependencies: [{ 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: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "component", type: IconComponent, selector: "Icon", inputs: ["name", "class", "size", "fill", "weight", "grade", "opticalSize"] }, { kind: "component", type: NavigationItemContentComponent, selector: "NavigationItemContent", inputs: ["item", "active", "compact", "orientation", "level", "collapseTree", "iconTemplate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
835
+ }
836
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: NavigationItemComponent, decorators: [{
837
+ type: Component,
838
+ args: [{
839
+ selector: 'li[NavigationItem]',
840
+ changeDetection: ChangeDetectionStrategy.OnPush,
841
+ imports: [RouterLink, RouterLinkActive, IconComponent, NavigationItemContentComponent],
842
+ host: {
843
+ '[class]': 'hostClasses()',
844
+ '[attr.role]': 'hostRole()',
845
+ '[attr.data-navigation-item-key]': 'item().key',
846
+ '[attr.aria-hidden]': 'item().type === "spacer" ? "true" : null',
847
+ },
848
+ template: `
849
+ @switch (item().type) {
850
+ @case ('divider') {
851
+ <span class="block h-px w-full bg-border"></span>
852
+ }
853
+
854
+ @case ('spacer') {}
855
+
856
+ @case ('group') {
857
+ @if (!compact() && item().title) {
858
+ <div [id]="headingId()" [class]="groupHeadingClasses()">{{ item().title }}</div>
859
+ }
860
+
861
+ <ul [class]="childListClasses()" role="group" [attr.aria-labelledby]="item().title ? headingId() : null">
862
+ @for (child of item().children; track child.key; let isFirst = $first; let isLast = $last) {
863
+ <li
864
+ NavigationItem
865
+ [navId]="navId()"
866
+ [item]="child"
867
+ [level]="level() + 1"
868
+ [orientation]="orientation()"
869
+ [compact]="compact()"
870
+ [itemClass]="itemClass()"
871
+ [activeIds]="activeIds()"
872
+ [activeUrl]="activeUrl()"
873
+ [iconTemplate]="iconTemplate()"
874
+ [collapseTree]="collapseTree()"
875
+ [straightRail]="showStraightRailForChildren()"
876
+ [straightRailActive]="isActive()"
877
+ [firstInBranch]="isFirst"
878
+ [lastInBranch]="isLast"
879
+ [openedIds]="openedIds()"
880
+ (openedIdsChange)="openedIdsChange.emit($event)"
881
+ (itemSelected)="itemSelected.emit($event)"></li>
882
+ }
883
+ </ul>
884
+ }
885
+
886
+ @case ('collapsible') {
887
+ <button
888
+ type="button"
889
+ [class]="triggerClasses(isActive())"
890
+ [attr.aria-expanded]="isOpen()"
891
+ [attr.aria-controls]="item().panelId"
892
+ [attr.aria-label]="compactLabel()"
893
+ [attr.title]="titleFor()"
894
+ [disabled]="item().disabled || null"
895
+ (click)="toggleOpen()">
896
+ <NavigationItemContent
897
+ [item]="item()"
898
+ [active]="isActive()"
899
+ [compact]="compact()"
900
+ [orientation]="orientation()"
901
+ [level]="level()"
902
+ [collapseTree]="collapseTree()"
903
+ [iconTemplate]="iconTemplate()" />
904
+
905
+ @if (!compact()) {
906
+ <Icon name="chevron_right" [size]="16" [class]="chevronClasses()" />
907
+ }
908
+ </button>
909
+
910
+ @if (showChildren()) {
911
+ <ul [id]="item().panelId" [class]="childListClasses()" role="group">
912
+ @for (child of item().children; track child.key; let isFirst = $first; let isLast = $last) {
913
+ <li
914
+ NavigationItem
915
+ [navId]="navId()"
916
+ [item]="child"
917
+ [level]="level() + 1"
918
+ [orientation]="orientation()"
919
+ [compact]="false"
920
+ [itemClass]="itemClass()"
921
+ [activeIds]="activeIds()"
922
+ [activeUrl]="activeUrl()"
923
+ [iconTemplate]="iconTemplate()"
924
+ [collapseTree]="collapseTree()"
925
+ [straightRail]="showStraightRailForChildren()"
926
+ [straightRailActive]="isActive()"
927
+ [firstInBranch]="isFirst"
928
+ [lastInBranch]="isLast"
929
+ [openedIds]="openedIds()"
930
+ (openedIdsChange)="openedIdsChange.emit($event)"
931
+ (itemSelected)="itemSelected.emit($event)"></li>
932
+ }
933
+ </ul>
934
+ }
935
+ }
936
+
937
+ @case ('mega') {
938
+ @if (!compact() && item().title) {
939
+ <div [id]="headingId()" [class]="groupHeadingClasses()">{{ item().title }}</div>
940
+ }
941
+
942
+ <ul [class]="childListClasses()" role="group" [attr.aria-labelledby]="item().title ? headingId() : null">
943
+ @for (child of item().children; track child.key; let isFirst = $first; let isLast = $last) {
944
+ <li
945
+ NavigationItem
946
+ [navId]="navId()"
947
+ [item]="child"
948
+ [level]="level() + 1"
949
+ [orientation]="orientation()"
950
+ [compact]="compact()"
951
+ [itemClass]="itemClass()"
952
+ [activeIds]="activeIds()"
953
+ [activeUrl]="activeUrl()"
954
+ [iconTemplate]="iconTemplate()"
955
+ [collapseTree]="collapseTree()"
956
+ [straightRail]="showStraightRailForChildren()"
957
+ [straightRailActive]="isActive()"
958
+ [firstInBranch]="isFirst"
959
+ [lastInBranch]="isLast"
960
+ [openedIds]="openedIds()"
961
+ (openedIdsChange)="openedIdsChange.emit($event)"
962
+ (itemSelected)="itemSelected.emit($event)"></li>
963
+ }
964
+ </ul>
965
+ }
966
+
967
+ @default {
968
+ @if (isRouterItem()) {
969
+ <a
970
+ [class]="leafClasses(routerActive.isActive || isActive())"
971
+ [routerLink]="item().link ?? null"
972
+ [queryParams]="item().queryParams ?? null"
973
+ [queryParamsHandling]="item().queryParamsHandling ?? null"
974
+ [fragment]="item().fragment ?? undefined"
975
+ [preserveFragment]="item().preserveFragment ?? false"
976
+ [target]="item().target ?? undefined"
977
+ routerLinkActive
978
+ #routerActive="routerLinkActive"
979
+ [routerLinkActiveOptions]="routerLinkActiveOptions()"
980
+ [attr.aria-current]="routerActive.isActive || isActive() ? 'page' : null"
981
+ [attr.aria-disabled]="item().disabled || null"
982
+ [attr.aria-label]="compactLabel()"
983
+ [attr.title]="titleFor()"
984
+ [attr.role]="itemRole()"
985
+ [attr.data-navigation-root-item]="isHorizontalRoot() ? 'true' : null"
986
+ (click)="selectItem($event)">
987
+ <NavigationItemContent
988
+ [item]="item()"
989
+ [active]="routerActive.isActive || isActive()"
990
+ [compact]="compact()"
991
+ [orientation]="orientation()"
992
+ [level]="level()"
993
+ [collapseTree]="collapseTree()"
994
+ [iconTemplate]="iconTemplate()" />
995
+ </a>
996
+ } @else if (hrefFor()) {
997
+ <a
998
+ [class]="leafClasses(isActive())"
999
+ [attr.href]="hrefFor()"
1000
+ [attr.target]="item().target ?? (item().externalLink ? '_blank' : null)"
1001
+ [attr.rel]="relFor()"
1002
+ [attr.aria-current]="isActive() ? 'page' : null"
1003
+ [attr.aria-disabled]="item().disabled || null"
1004
+ [attr.aria-label]="compactLabel()"
1005
+ [attr.title]="titleFor()"
1006
+ [attr.role]="itemRole()"
1007
+ [attr.data-navigation-root-item]="isHorizontalRoot() ? 'true' : null"
1008
+ (click)="selectItem($event)">
1009
+ <NavigationItemContent
1010
+ [item]="item()"
1011
+ [active]="isActive()"
1012
+ [compact]="compact()"
1013
+ [orientation]="orientation()"
1014
+ [level]="level()"
1015
+ [collapseTree]="collapseTree()"
1016
+ [iconTemplate]="iconTemplate()" />
1017
+ </a>
1018
+ } @else {
1019
+ <button
1020
+ type="button"
1021
+ [class]="leafClasses(isActive())"
1022
+ [disabled]="item().disabled || null"
1023
+ [attr.aria-current]="isActive() ? 'page' : null"
1024
+ [attr.aria-label]="compactLabel()"
1025
+ [attr.title]="titleFor()"
1026
+ [attr.role]="itemRole()"
1027
+ [attr.data-navigation-root-item]="isHorizontalRoot() ? 'true' : null"
1028
+ (click)="selectItem($event)">
1029
+ <NavigationItemContent
1030
+ [item]="item()"
1031
+ [active]="isActive()"
1032
+ [compact]="compact()"
1033
+ [orientation]="orientation()"
1034
+ [level]="level()"
1035
+ [collapseTree]="collapseTree()"
1036
+ [iconTemplate]="iconTemplate()" />
1037
+ </button>
1038
+ }
1039
+ }
1040
+ }
1041
+ `,
1042
+ }]
1043
+ }], propDecorators: { navId: [{ type: i0.Input, args: [{ isSignal: true, alias: "navId", required: false }] }], item: [{ type: i0.Input, args: [{ isSignal: true, alias: "item", required: true }] }], level: [{ type: i0.Input, args: [{ isSignal: true, alias: "level", required: false }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], compact: [{ type: i0.Input, args: [{ isSignal: true, alias: "compact", 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 }] }], straightRail: [{ type: i0.Input, args: [{ isSignal: true, alias: "straightRail", required: false }] }], straightRailActive: [{ type: i0.Input, args: [{ isSignal: true, alias: "straightRailActive", required: false }] }], firstInBranch: [{ type: i0.Input, args: [{ isSignal: true, alias: "firstInBranch", required: false }] }], lastInBranch: [{ type: i0.Input, args: [{ isSignal: true, alias: "lastInBranch", 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"] }] } });
1044
+
1045
+ class NavigationListComponent {
1046
+ navId = input('default', /* @ts-ignore */
1047
+ ...(ngDevMode ? [{ debugName: "navId" }] : /* istanbul ignore next */ []));
1048
+ items = input([], /* @ts-ignore */
1049
+ ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
1050
+ collapsed = input(false, /* @ts-ignore */
1051
+ ...(ngDevMode ? [{ debugName: "collapsed" }] : /* istanbul ignore next */ []));
1052
+ compact = input(false, /* @ts-ignore */
1053
+ ...(ngDevMode ? [{ debugName: "compact" }] : /* istanbul ignore next */ []));
1054
+ position = input('left', /* @ts-ignore */
1055
+ ...(ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []));
1056
+ itemClass = input('', /* @ts-ignore */
1057
+ ...(ngDevMode ? [{ debugName: "itemClass" }] : /* istanbul ignore next */ []));
1058
+ activeIds = input(new Set(), /* @ts-ignore */
1059
+ ...(ngDevMode ? [{ debugName: "activeIds" }] : /* istanbul ignore next */ []));
1060
+ activeUrl = input(null, /* @ts-ignore */
1061
+ ...(ngDevMode ? [{ debugName: "activeUrl" }] : /* istanbul ignore next */ []));
1062
+ iconTemplate = input(undefined, /* @ts-ignore */
1063
+ ...(ngDevMode ? [{ debugName: "iconTemplate" }] : /* istanbul ignore next */ []));
1064
+ collapseTree = input('stairs', /* @ts-ignore */
1065
+ ...(ngDevMode ? [{ debugName: "collapseTree" }] : /* istanbul ignore next */ []));
1066
+ openedIds = input([], /* @ts-ignore */
1067
+ ...(ngDevMode ? [{ debugName: "openedIds" }] : /* istanbul ignore next */ []));
1068
+ openedIdsChange = output();
1069
+ itemSelected = output();
1070
+ isCollapsed = computed(() => this.collapsed() || this.compact(), /* @ts-ignore */
1071
+ ...(ngDevMode ? [{ debugName: "isCollapsed" }] : /* istanbul ignore next */ []));
1072
+ hostClasses = computed(() => cn('block', this.position() === 'right' && 'items-end text-right'), /* @ts-ignore */
1073
+ ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
1074
+ listClasses = computed(() => this.isCollapsed()
1075
+ ? cn('flex list-none flex-col items-center gap-0.5 p-0')
1076
+ : cn('flex list-none flex-col gap-0.5 p-0', this.position() === 'right' && 'items-end'), /* @ts-ignore */
1077
+ ...(ngDevMode ? [{ debugName: "listClasses" }] : /* istanbul ignore next */ []));
1078
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: NavigationListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1079
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: NavigationListComponent, isStandalone: true, selector: "NavigationList", inputs: { navId: { classPropertyName: "navId", publicName: "navId", isSignal: true, isRequired: false, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, compact: { classPropertyName: "compact", publicName: "compact", 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: { properties: { "class": "hostClasses()", "attr.data-position": "position()" } }, ngImport: i0, template: `
1080
+ <ul [class]="listClasses()" role="list">
1081
+ @for (item of items(); track item.key) {
1082
+ <li
1083
+ NavigationItem
1084
+ [navId]="navId()"
1085
+ [item]="item"
1086
+ [level]="0"
1087
+ [orientation]="'vertical'"
1088
+ [compact]="isCollapsed()"
1089
+ [itemClass]="itemClass()"
1090
+ [activeIds]="activeIds()"
1091
+ [activeUrl]="activeUrl()"
1092
+ [iconTemplate]="iconTemplate()"
1093
+ [collapseTree]="collapseTree()"
1094
+ [openedIds]="openedIds()"
1095
+ (openedIdsChange)="openedIdsChange.emit($event)"
1096
+ (itemSelected)="itemSelected.emit($event)"></li>
1097
+ }
1098
+ </ul>
1099
+ `, isInline: true, dependencies: [{ 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"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1100
+ }
1101
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: NavigationListComponent, decorators: [{
1102
+ type: Component,
1103
+ args: [{
1104
+ selector: 'NavigationList',
1105
+ changeDetection: ChangeDetectionStrategy.OnPush,
1106
+ imports: [NavigationItemComponent],
1107
+ host: {
1108
+ '[class]': 'hostClasses()',
1109
+ '[attr.data-position]': 'position()',
1110
+ },
1111
+ template: `
1112
+ <ul [class]="listClasses()" role="list">
1113
+ @for (item of items(); track item.key) {
1114
+ <li
1115
+ NavigationItem
1116
+ [navId]="navId()"
1117
+ [item]="item"
1118
+ [level]="0"
1119
+ [orientation]="'vertical'"
1120
+ [compact]="isCollapsed()"
1121
+ [itemClass]="itemClass()"
1122
+ [activeIds]="activeIds()"
1123
+ [activeUrl]="activeUrl()"
1124
+ [iconTemplate]="iconTemplate()"
1125
+ [collapseTree]="collapseTree()"
1126
+ [openedIds]="openedIds()"
1127
+ (openedIdsChange)="openedIdsChange.emit($event)"
1128
+ (itemSelected)="itemSelected.emit($event)"></li>
1129
+ }
1130
+ </ul>
1131
+ `,
1132
+ }]
1133
+ }], propDecorators: { navId: [{ type: i0.Input, args: [{ isSignal: true, alias: "navId", required: false }] }], items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }], compact: [{ type: i0.Input, args: [{ isSignal: true, alias: "compact", 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"] }] } });
1134
+
1135
+ class NavigationDockbarMenuComponent {
1136
+ nav = inject(NavigationService);
1137
+ host = inject(ElementRef);
1138
+ injector = inject(Injector);
1139
+ navId = input('default', /* @ts-ignore */
1140
+ ...(ngDevMode ? [{ debugName: "navId" }] : /* istanbul ignore next */ []));
1141
+ items = input([], /* @ts-ignore */
1142
+ ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
1143
+ mode = input('sticky', /* @ts-ignore */
1144
+ ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
1145
+ position = input('left', /* @ts-ignore */
1146
+ ...(ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []));
1147
+ itemClass = input('', /* @ts-ignore */
1148
+ ...(ngDevMode ? [{ debugName: "itemClass" }] : /* istanbul ignore next */ []));
1149
+ activeIds = input(new Set(), /* @ts-ignore */
1150
+ ...(ngDevMode ? [{ debugName: "activeIds" }] : /* istanbul ignore next */ []));
1151
+ activeUrl = input(null, /* @ts-ignore */
1152
+ ...(ngDevMode ? [{ debugName: "activeUrl" }] : /* istanbul ignore next */ []));
1153
+ iconTemplate = input(undefined, /* @ts-ignore */
1154
+ ...(ngDevMode ? [{ debugName: "iconTemplate" }] : /* istanbul ignore next */ []));
1155
+ collapseTree = input('stairs', /* @ts-ignore */
1156
+ ...(ngDevMode ? [{ debugName: "collapseTree" }] : /* istanbul ignore next */ []));
1157
+ openedIds = input([], /* @ts-ignore */
1158
+ ...(ngDevMode ? [{ debugName: "openedIds" }] : /* istanbul ignore next */ []));
1159
+ openedIdsChange = output();
1160
+ itemSelected = output();
1161
+ isDrawerMode = computed(() => this.mode() === 'drawer', /* @ts-ignore */
1162
+ ...(ngDevMode ? [{ debugName: "isDrawerMode" }] : /* istanbul ignore next */ []));
1163
+ asideId = computed(() => `nav-dockbar-${this.safeId(this.navId())}`, /* @ts-ignore */
1164
+ ...(ngDevMode ? [{ debugName: "asideId" }] : /* istanbul ignore next */ []));
1165
+ asideHeadingId = computed(() => `${this.asideId()}-heading`, /* @ts-ignore */
1166
+ ...(ngDevMode ? [{ debugName: "asideHeadingId" }] : /* istanbul ignore next */ []));
1167
+ openGroup = computed(() => this.nav.resolveDockbarGroup(this.navId(), this.items(), this.mode(), this.activeIds(), this.activeUrl()), /* @ts-ignore */
1168
+ ...(ngDevMode ? [{ debugName: "openGroup" }] : /* istanbul ignore next */ []));
1169
+ hostClasses = computed(() => cn('block w-16'), /* @ts-ignore */
1170
+ ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
1171
+ railClasses = computed(() => cn('flex w-full list-none flex-col items-center gap-0.5 p-2',
1172
+ // Mode drawer: rail tetap di atas backdrop (z-40) agar user bisa langsung
1173
+ // berpindah group lain tanpa menutup drawer lebih dulu.
1174
+ this.isDrawerMode() && this.openGroup() !== null && 'relative z-50'), /* @ts-ignore */
1175
+ ...(ngDevMode ? [{ debugName: "railClasses" }] : /* istanbul ignore next */ []));
1176
+ asideClasses = computed(() => cn('absolute inset-y-0 flex w-60 flex-col bg-background', this.position() === 'right'
1177
+ ? 'right-16 border-r border-[hsl(var(--border)/var(--opacity-70))]'
1178
+ : 'left-16 border-l border-[hsl(var(--border)/var(--opacity-70))]', this.isDrawerMode() &&
1179
+ cn('z-50 shadow-xl', this.position() === 'right'
1180
+ ? 'border-l border-l-[hsl(var(--border)/var(--opacity-70))]'
1181
+ : 'border-r border-r-[hsl(var(--border)/var(--opacity-70))]')), /* @ts-ignore */
1182
+ ...(ngDevMode ? [{ debugName: "asideClasses" }] : /* istanbul ignore next */ []));
1183
+ /**
1184
+ * Offset backdrop agar kolom navigasi (rail + header/footer) benar-benar bebas
1185
+ * overlay — dockbar dan aside drawer berada di level yang sama di atas backdrop.
1186
+ * `null` berarti pengukuran tidak tersedia (mis. jsdom) dan backdrop jatuh ke
1187
+ * `inset-0` penuh seperti sebelumnya.
1188
+ */
1189
+ backdropInset = signal({
1190
+ left: null,
1191
+ right: null,
1192
+ }, /* @ts-ignore */
1193
+ ...(ngDevMode ? [{ debugName: "backdropInset" }] : /* istanbul ignore next */ []));
1194
+ onWindowResize() {
1195
+ if (this.isDrawerMode() && this.openGroup() !== null) {
1196
+ this.measureBackdropInset();
1197
+ }
1198
+ }
1199
+ measureBackdropInset() {
1200
+ const navEl = this.host.nativeElement.closest('[data-navigation-id]') ?? this.host.nativeElement;
1201
+ const rect = navEl.getBoundingClientRect();
1202
+ const viewportWidth = this.host.nativeElement.ownerDocument?.defaultView?.innerWidth ?? 0;
1203
+ if (rect.width <= 0 || viewportWidth <= 0) {
1204
+ this.backdropInset.set({ left: null, right: null });
1205
+ return;
1206
+ }
1207
+ // Containing block backdrop `fixed` bisa bergeser dari viewport (ancestor dengan
1208
+ // filter/contain). Koreksi origin dihitung dari posisi aktual backdrop terhadap
1209
+ // style left/right yang sedang berlaku — konvergen dalam satu langkah re-measure.
1210
+ const backdrop = this.host.nativeElement.querySelector('[data-navigation-dockbar-backdrop="true"]');
1211
+ const backdropRect = backdrop?.getBoundingClientRect() ?? null;
1212
+ let originLeft = 0;
1213
+ let originRight = viewportWidth;
1214
+ if (backdrop && backdropRect && backdropRect.width > 0) {
1215
+ originLeft = backdropRect.left - this.parsePx(backdrop.style.left);
1216
+ originRight = backdropRect.right + this.parsePx(backdrop.style.right);
1217
+ }
1218
+ if (this.position() === 'right') {
1219
+ this.backdropInset.set({ left: null, right: Math.max(originRight - rect.left, 0) });
1220
+ return;
1221
+ }
1222
+ this.backdropInset.set({ left: Math.max(rect.right - originLeft, 0), right: null });
1223
+ }
1224
+ parsePx(value) {
1225
+ const parsed = Number.parseFloat(value);
1226
+ return Number.isFinite(parsed) ? parsed : 0;
1227
+ }
1228
+ isGroupOpen(entry) {
1229
+ return this.openGroup()?.key === entry.key;
1230
+ }
1231
+ isGroupActive(entry) {
1232
+ return this.nav.isItemActive(entry, this.activeIds(), this.activeUrl());
1233
+ }
1234
+ triggerClasses(entry) {
1235
+ return cn('group/nav inline-flex h-10 w-10 items-center justify-center rounded-md text-sm 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)
1236
+ ? 'bg-accent text-accent-foreground'
1237
+ : this.isGroupActive(entry)
1238
+ ? 'font-semibold text-foreground'
1239
+ : 'text-foreground/80 hover:bg-accent hover:text-accent-foreground', entry.classes?.wrapper, this.itemClass());
1240
+ }
1241
+ toggleGroup(entry) {
1242
+ const wasOpen = this.isGroupOpen(entry);
1243
+ if (!wasOpen && this.isDrawerMode()) {
1244
+ this.measureBackdropInset();
1245
+ }
1246
+ this.nav.togglePanel(this.navId(), entry);
1247
+ if (!this.isDrawerMode()) {
1248
+ return;
1249
+ }
1250
+ if (wasOpen) {
1251
+ this.afterRender(() => this.focusTrigger(entry.key));
1252
+ return;
1253
+ }
1254
+ this.afterRender(() => {
1255
+ // Ukur ulang setelah render: geometri kolom bisa bergeser saat panel terbuka.
1256
+ this.measureBackdropInset();
1257
+ this.focusFirstAsideItem();
1258
+ });
1259
+ }
1260
+ closeAside() {
1261
+ const openKey = this.openGroup()?.key ?? null;
1262
+ this.nav.closePanel(this.navId());
1263
+ if (openKey !== null) {
1264
+ this.afterRender(() => this.focusTrigger(openKey));
1265
+ }
1266
+ }
1267
+ onEscape(event) {
1268
+ if (!this.isDrawerMode() || this.openGroup() === null) {
1269
+ return;
1270
+ }
1271
+ event.preventDefault();
1272
+ this.closeAside();
1273
+ }
1274
+ afterRender(callback) {
1275
+ afterNextRender({ read: callback }, { injector: this.injector });
1276
+ }
1277
+ focusFirstAsideItem() {
1278
+ const aside = this.host.nativeElement.querySelector(`#${this.asideId()}`);
1279
+ const focusable = aside?.querySelector('a[href], button:not([disabled]), [tabindex]:not([tabindex="-1"])');
1280
+ focusable?.focus();
1281
+ }
1282
+ focusTrigger(key) {
1283
+ const trigger = this.host.nativeElement.querySelector(`[data-navigation-dockbar-trigger="true"][data-navigation-item-key="${key}"]`);
1284
+ trigger?.focus();
1285
+ }
1286
+ safeId(value) {
1287
+ const normalized = value
1288
+ .trim()
1289
+ .toLowerCase()
1290
+ .replace(/[^a-z0-9_-]+/g, '-');
1291
+ return normalized || 'default';
1292
+ }
1293
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: NavigationDockbarMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1294
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", 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: `
1295
+ <ul [class]="railClasses()" role="list">
1296
+ @for (entry of items(); track entry.key) {
1297
+ @if (entry.children.length > 0) {
1298
+ <li class="flex w-full justify-center">
1299
+ <button
1300
+ type="button"
1301
+ data-navigation-dockbar-trigger="true"
1302
+ [class]="triggerClasses(entry)"
1303
+ [attr.aria-expanded]="isGroupOpen(entry)"
1304
+ [attr.aria-controls]="asideId()"
1305
+ [attr.aria-label]="entry.tooltip ?? entry.title ?? null"
1306
+ [attr.title]="entry.tooltip ?? entry.title ?? null"
1307
+ [attr.data-navigation-item-key]="entry.key"
1308
+ [disabled]="entry.disabled || null"
1309
+ (click)="toggleGroup(entry)">
1310
+ <NavigationItemContent
1311
+ [item]="entry"
1312
+ [active]="isGroupActive(entry)"
1313
+ [compact]="true"
1314
+ orientation="vertical"
1315
+ [level]="0"
1316
+ [collapseTree]="collapseTree()"
1317
+ [iconTemplate]="iconTemplate()" />
1318
+ </button>
1319
+ </li>
1320
+ } @else {
1321
+ <li
1322
+ NavigationItem
1323
+ [navId]="navId()"
1324
+ [item]="entry"
1325
+ [level]="0"
1326
+ orientation="vertical"
1327
+ [compact]="true"
1328
+ [itemClass]="itemClass()"
1329
+ [activeIds]="activeIds()"
1330
+ [activeUrl]="activeUrl()"
1331
+ [iconTemplate]="iconTemplate()"
1332
+ [collapseTree]="collapseTree()"
1333
+ [openedIds]="openedIds()"
1334
+ (openedIdsChange)="openedIdsChange.emit($event)"
1335
+ (itemSelected)="itemSelected.emit($event)"></li>
1336
+ }
1337
+ }
1338
+ </ul>
1339
+
1340
+ @if (openGroup(); as group) {
1341
+ @if (isDrawerMode()) {
1342
+ <div
1343
+ class="fixed inset-0 z-40 bg-[hsl(var(--overlay-backdrop-strong))]"
1344
+ data-navigation-dockbar-backdrop="true"
1345
+ aria-hidden="true"
1346
+ [style.left.px]="backdropInset().left"
1347
+ [style.right.px]="backdropInset().right"
1348
+ (click)="closeAside()"></div>
1349
+ }
1350
+
1351
+ <!-- Drawer non-modal: rail tetap interaktif di atas backdrop, jadi tanpa aria-modal. -->
1352
+ <aside
1353
+ [id]="asideId()"
1354
+ [class]="asideClasses()"
1355
+ data-navigation-dockbar-aside="true"
1356
+ [attr.role]="isDrawerMode() ? 'dialog' : null"
1357
+ [attr.aria-labelledby]="asideHeadingId()">
1358
+ <header
1359
+ class="flex h-12 shrink-0 items-center gap-2 border-b border-[hsl(var(--border)/var(--opacity-70))] px-4">
1360
+ <div class="min-w-0 flex-1">
1361
+ <h2 [id]="asideHeadingId()" class="truncate text-sm font-semibold leading-tight text-foreground">
1362
+ {{ group.title }}
1363
+ </h2>
1364
+ @if (group.subtitle) {
1365
+ <p class="truncate text-xs leading-tight text-muted-foreground">{{ group.subtitle }}</p>
1366
+ }
1367
+ </div>
1368
+
1369
+ @if (isDrawerMode()) {
1370
+ <button
1371
+ type="button"
1372
+ 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"
1373
+ data-navigation-dockbar-close="true"
1374
+ aria-label="Close navigation panel"
1375
+ (click)="closeAside()">
1376
+ <Icon name="close" [size]="16" />
1377
+ </button>
1378
+ }
1379
+ </header>
1380
+
1381
+ <div class="min-h-0 flex-1 overflow-y-auto px-2 py-3" data-navigation-dockbar-aside-scroll="true">
1382
+ <NavigationList
1383
+ [navId]="navId()"
1384
+ [items]="group.children"
1385
+ [collapsed]="false"
1386
+ position="left"
1387
+ [itemClass]="itemClass()"
1388
+ [activeIds]="activeIds()"
1389
+ [activeUrl]="activeUrl()"
1390
+ [iconTemplate]="iconTemplate()"
1391
+ [collapseTree]="collapseTree()"
1392
+ [openedIds]="openedIds()"
1393
+ (openedIdsChange)="openedIdsChange.emit($event)"
1394
+ (itemSelected)="itemSelected.emit($event)" />
1395
+ </div>
1396
+ </aside>
1397
+ }
1398
+ `, 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"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1399
+ }
1400
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: NavigationDockbarMenuComponent, decorators: [{
1401
+ type: Component,
1402
+ args: [{
1403
+ selector: 'NavigationDockbarMenu',
1404
+ changeDetection: ChangeDetectionStrategy.OnPush,
1405
+ imports: [IconComponent, NavigationItemComponent, NavigationItemContentComponent, NavigationListComponent],
1406
+ host: {
1407
+ '[class]': 'hostClasses()',
1408
+ '[attr.data-mode]': 'mode()',
1409
+ '[attr.data-position]': 'position()',
1410
+ '(keydown.escape)': 'onEscape($event)',
1411
+ '(window:resize)': 'onWindowResize()',
1412
+ },
1413
+ template: `
1414
+ <ul [class]="railClasses()" role="list">
1415
+ @for (entry of items(); track entry.key) {
1416
+ @if (entry.children.length > 0) {
1417
+ <li class="flex w-full justify-center">
1418
+ <button
1419
+ type="button"
1420
+ data-navigation-dockbar-trigger="true"
1421
+ [class]="triggerClasses(entry)"
1422
+ [attr.aria-expanded]="isGroupOpen(entry)"
1423
+ [attr.aria-controls]="asideId()"
1424
+ [attr.aria-label]="entry.tooltip ?? entry.title ?? null"
1425
+ [attr.title]="entry.tooltip ?? entry.title ?? null"
1426
+ [attr.data-navigation-item-key]="entry.key"
1427
+ [disabled]="entry.disabled || null"
1428
+ (click)="toggleGroup(entry)">
1429
+ <NavigationItemContent
1430
+ [item]="entry"
1431
+ [active]="isGroupActive(entry)"
1432
+ [compact]="true"
1433
+ orientation="vertical"
1434
+ [level]="0"
1435
+ [collapseTree]="collapseTree()"
1436
+ [iconTemplate]="iconTemplate()" />
1437
+ </button>
1438
+ </li>
1439
+ } @else {
1440
+ <li
1441
+ NavigationItem
1442
+ [navId]="navId()"
1443
+ [item]="entry"
1444
+ [level]="0"
1445
+ orientation="vertical"
1446
+ [compact]="true"
1447
+ [itemClass]="itemClass()"
1448
+ [activeIds]="activeIds()"
1449
+ [activeUrl]="activeUrl()"
1450
+ [iconTemplate]="iconTemplate()"
1451
+ [collapseTree]="collapseTree()"
1452
+ [openedIds]="openedIds()"
1453
+ (openedIdsChange)="openedIdsChange.emit($event)"
1454
+ (itemSelected)="itemSelected.emit($event)"></li>
1455
+ }
1456
+ }
1457
+ </ul>
1458
+
1459
+ @if (openGroup(); as group) {
1460
+ @if (isDrawerMode()) {
1461
+ <div
1462
+ class="fixed inset-0 z-40 bg-[hsl(var(--overlay-backdrop-strong))]"
1463
+ data-navigation-dockbar-backdrop="true"
1464
+ aria-hidden="true"
1465
+ [style.left.px]="backdropInset().left"
1466
+ [style.right.px]="backdropInset().right"
1467
+ (click)="closeAside()"></div>
1468
+ }
1469
+
1470
+ <!-- Drawer non-modal: rail tetap interaktif di atas backdrop, jadi tanpa aria-modal. -->
1471
+ <aside
1472
+ [id]="asideId()"
1473
+ [class]="asideClasses()"
1474
+ data-navigation-dockbar-aside="true"
1475
+ [attr.role]="isDrawerMode() ? 'dialog' : null"
1476
+ [attr.aria-labelledby]="asideHeadingId()">
1477
+ <header
1478
+ class="flex h-12 shrink-0 items-center gap-2 border-b border-[hsl(var(--border)/var(--opacity-70))] px-4">
1479
+ <div class="min-w-0 flex-1">
1480
+ <h2 [id]="asideHeadingId()" class="truncate text-sm font-semibold leading-tight text-foreground">
1481
+ {{ group.title }}
1482
+ </h2>
1483
+ @if (group.subtitle) {
1484
+ <p class="truncate text-xs leading-tight text-muted-foreground">{{ group.subtitle }}</p>
1485
+ }
1486
+ </div>
1487
+
1488
+ @if (isDrawerMode()) {
1489
+ <button
1490
+ type="button"
1491
+ 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"
1492
+ data-navigation-dockbar-close="true"
1493
+ aria-label="Close navigation panel"
1494
+ (click)="closeAside()">
1495
+ <Icon name="close" [size]="16" />
1496
+ </button>
1497
+ }
1498
+ </header>
1499
+
1500
+ <div class="min-h-0 flex-1 overflow-y-auto px-2 py-3" data-navigation-dockbar-aside-scroll="true">
1501
+ <NavigationList
1502
+ [navId]="navId()"
1503
+ [items]="group.children"
1504
+ [collapsed]="false"
1505
+ position="left"
1506
+ [itemClass]="itemClass()"
1507
+ [activeIds]="activeIds()"
1508
+ [activeUrl]="activeUrl()"
1509
+ [iconTemplate]="iconTemplate()"
1510
+ [collapseTree]="collapseTree()"
1511
+ [openedIds]="openedIds()"
1512
+ (openedIdsChange)="openedIdsChange.emit($event)"
1513
+ (itemSelected)="itemSelected.emit($event)" />
1514
+ </div>
1515
+ </aside>
1516
+ }
1517
+ `,
1518
+ }]
1519
+ }], 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"] }] } });
1520
+
1521
+ /**
1522
+ * Grid entry ala flyout: children sebuah branch dirender sebagai entry
1523
+ * icon-box + title/subtitle dalam grid responsif (1/sm:2/md:3/lg:4, dibatasi
1524
+ * `columns`); entry ber-children tampil terbuka secara default dengan collapse
1525
+ * vertical yang icon-nya sejajar satu garis dengan icon induk. Dipakai oleh
1526
+ * flyout dan panel mega navbar.
1527
+ */
1528
+ class NavigationEntryGridComponent {
1529
+ nav = inject(NavigationService);
1530
+ navId = input('default', /* @ts-ignore */
1531
+ ...(ngDevMode ? [{ debugName: "navId" }] : /* istanbul ignore next */ []));
1532
+ branch = input.required(/* @ts-ignore */
1533
+ ...(ngDevMode ? [{ debugName: "branch" }] : /* istanbul ignore next */ []));
1534
+ typeStyle = input('default', /* @ts-ignore */
1535
+ ...(ngDevMode ? [{ debugName: "typeStyle" }] : /* istanbul ignore next */ []));
1536
+ itemClass = input('', /* @ts-ignore */
1537
+ ...(ngDevMode ? [{ debugName: "itemClass" }] : /* istanbul ignore next */ []));
1538
+ activeIds = input(new Set(), /* @ts-ignore */
1539
+ ...(ngDevMode ? [{ debugName: "activeIds" }] : /* istanbul ignore next */ []));
1540
+ activeUrl = input(null, /* @ts-ignore */
1541
+ ...(ngDevMode ? [{ debugName: "activeUrl" }] : /* istanbul ignore next */ []));
1542
+ iconTemplate = input(undefined, /* @ts-ignore */
1543
+ ...(ngDevMode ? [{ debugName: "iconTemplate" }] : /* istanbul ignore next */ []));
1544
+ collapseTree = input('stairs', /* @ts-ignore */
1545
+ ...(ngDevMode ? [{ debugName: "collapseTree" }] : /* istanbul ignore next */ []));
1546
+ openedIds = input([], /* @ts-ignore */
1547
+ ...(ngDevMode ? [{ debugName: "openedIds" }] : /* istanbul ignore next */ []));
1548
+ openedIdsChange = output();
1549
+ itemSelected = output();
1550
+ isBorderRail = computed(() => this.typeStyle() === 'border-rail', /* @ts-ignore */
1551
+ ...(ngDevMode ? [{ debugName: "isBorderRail" }] : /* istanbul ignore next */ []));
1552
+ /** Kelas tambahan untuk NavigationItem bersarang saat style border-rail aktif. */
1553
+ navItemClass = computed(() => cn(this.itemClass(), this.isBorderRail() && 'rounded-none hover:bg-transparent hover:text-primary'), /* @ts-ignore */
1554
+ ...(ngDevMode ? [{ debugName: "navItemClass" }] : /* istanbul ignore next */ []));
1555
+ /**
1556
+ * Entry ber-children tampil TERBUKA secara default; set ini hanya menyimpan
1557
+ * entry yang sengaja di-collapse pengguna. Instance grid dibuat ulang setiap
1558
+ * panel dibuka sehingga state kembali terbuka semua.
1559
+ */
1560
+ collapsedEntryKeys = signal(new Set(), /* @ts-ignore */
1561
+ ...(ngDevMode ? [{ debugName: "collapsedEntryKeys" }] : /* istanbul ignore next */ []));
1562
+ isItemActive(item) {
1563
+ return this.nav.isItemActive(item, this.activeIds(), this.activeUrl());
1564
+ }
1565
+ isEntryOpen(item) {
1566
+ return !this.collapsedEntryKeys().has(item.stateId);
1567
+ }
1568
+ toggleEntry(item) {
1569
+ if (item.disabled) {
1570
+ return;
1571
+ }
1572
+ const next = new Set(this.collapsedEntryKeys());
1573
+ if (next.has(item.stateId)) {
1574
+ next.delete(item.stateId);
1575
+ }
1576
+ else {
1577
+ next.add(item.stateId);
1578
+ }
1579
+ this.collapsedEntryKeys.set(next);
1580
+ }
1581
+ selectEntry(item, event) {
1582
+ if (item.disabled) {
1583
+ event.preventDefault();
1584
+ event.stopPropagation();
1585
+ return;
1586
+ }
1587
+ item.action?.(item.source);
1588
+ this.itemSelected.emit({
1589
+ item: item.source,
1590
+ key: item.key,
1591
+ type: item.type,
1592
+ link: item.link,
1593
+ external: !!this.nav.hrefFor(item),
1594
+ });
1595
+ this.nav.closePanel(this.navId());
1596
+ this.nav.closeDrawer(this.navId());
1597
+ }
1598
+ isRouterItem(item) {
1599
+ return this.nav.isRouterItem(item);
1600
+ }
1601
+ hrefFor(item) {
1602
+ return this.nav.hrefFor(item);
1603
+ }
1604
+ relFor(item) {
1605
+ return this.nav.relFor(item);
1606
+ }
1607
+ routerLinkActiveOptions(item) {
1608
+ return this.nav.routerLinkActiveOptions(item);
1609
+ }
1610
+ iconContext(item, active) {
1611
+ return this.nav.iconContext(item, active, 1, 'horizontal');
1612
+ }
1613
+ fallbackInitials(item) {
1614
+ return this.nav.compactFallback(item);
1615
+ }
1616
+ contentGridClasses(branch) {
1617
+ // Skala responsif: mobile 1, sm 2, md 3, lg ke atas 4; `columns` membatasi maksimumnya.
1618
+ const columns = Math.min(Math.max(branch.columns ?? 4, 1), 4);
1619
+ return cn('m-0 grid list-none grid-cols-1 gap-1 p-0', columns >= 2 && 'sm:grid-cols-2', columns >= 3 && 'md:grid-cols-3', columns >= 4 && 'lg:grid-cols-4');
1620
+ }
1621
+ entryClasses(item, active) {
1622
+ return cn('group/nav flex w-full items-center gap-3 px-3 py-2.5 text-left text-sm transition-colors', 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring', this.isBorderRail()
1623
+ ? cn('rounded-none', active ? 'text-primary' : 'text-foreground hover:text-primary')
1624
+ : cn('rounded-lg', active
1625
+ ? 'bg-accent text-accent-foreground'
1626
+ : 'text-foreground hover:bg-accent hover:text-accent-foreground'), item.disabled && 'pointer-events-none opacity-50', item.classes?.wrapper, this.itemClass());
1627
+ }
1628
+ entryIconClasses(active) {
1629
+ if (this.isBorderRail()) {
1630
+ // Kotak icon polos; bingkai digambar oleh rail line yang menjorok melewati sudut.
1631
+ return cn('relative flex h-10 w-10 shrink-0 items-center justify-center self-center text-foreground transition-colors group-hover/nav:text-primary', active && 'text-primary');
1632
+ }
1633
+ return cn('relative flex h-10 w-10 shrink-0 items-center justify-center self-center rounded-lg border bg-background text-foreground shadow-sm transition-colors', active ? 'border-primary text-primary' : 'border-[hsl(var(--border)/var(--opacity-70))]');
1634
+ }
1635
+ entryIconGlyphClasses(item) {
1636
+ return cn('text-current', item.classes?.icon);
1637
+ }
1638
+ entryTitleClasses(item, active) {
1639
+ return cn('block truncate text-sm font-medium', this.isBorderRail() ? 'text-current' : 'text-foreground', active && 'font-semibold', item.classes?.title);
1640
+ }
1641
+ entrySubtitleClasses(item) {
1642
+ return cn('mt-0.5 block truncate text-xs text-muted-foreground', item.classes?.subtitle);
1643
+ }
1644
+ entryChevronClasses(item) {
1645
+ return cn('shrink-0 self-center text-muted-foreground transition-transform duration-200', this.isEntryOpen(item) && 'rotate-90');
1646
+ }
1647
+ nestedListClasses() {
1648
+ // Children sejajar satu garis vertical dengan icon induk: rail digambar lewat pseudo-element
1649
+ // di x = 32px (px-3 entry + setengah icon box w-10); pl-2.5 membuat icon child (px-3 + slot
1650
+ // w-5) ber-center tepat di garis yang sama.
1651
+ return cn("relative mt-1 flex list-none flex-col gap-0.5 p-0 pl-2.5 before:pointer-events-none before:absolute before:inset-y-0 before:left-8 before:w-0 before:border-l before:content-['']", this.isBorderRail()
1652
+ ? 'before:border-dashed before:border-border/40'
1653
+ : 'before:border-[hsl(var(--border)/var(--opacity-60))]');
1654
+ }
1655
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: NavigationEntryGridComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1656
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: NavigationEntryGridComponent, isStandalone: true, selector: "NavigationEntryGrid", inputs: { navId: { classPropertyName: "navId", publicName: "navId", isSignal: true, isRequired: false, transformFunction: null }, branch: { classPropertyName: "branch", publicName: "branch", isSignal: true, isRequired: true, transformFunction: null }, typeStyle: { classPropertyName: "typeStyle", publicName: "typeStyle", 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: { classAttribute: "contents" }, ngImport: i0, template: `
1657
+ @let branchItem = branch();
1658
+
1659
+ <div
1660
+ role="group"
1661
+ [id]="branchItem.panelId"
1662
+ [attr.aria-label]="branchItem.title || null"
1663
+ [attr.data-navigation-flyout-content]="branchItem.key"
1664
+ class="p-2 md:p-3">
1665
+ <ul role="none" [class]="contentGridClasses(branchItem)">
1666
+ @for (child of branchItem.children; track child.key) {
1667
+ @switch (child.type) {
1668
+ @case ('divider') {
1669
+ <li role="separator" class="col-span-full my-1 h-px bg-border"></li>
1670
+ }
1671
+ @case ('spacer') {}
1672
+ @default {
1673
+ @if (child.children.length > 0) {
1674
+ <li role="none" class="min-w-0" [attr.data-navigation-item-key]="child.key">
1675
+ <button
1676
+ type="button"
1677
+ role="menuitem"
1678
+ data-navigation-flyout-entry="true"
1679
+ [class]="entryClasses(child, isItemActive(child))"
1680
+ [attr.aria-expanded]="isEntryOpen(child)"
1681
+ [attr.aria-controls]="child.panelId"
1682
+ [disabled]="child.disabled || null"
1683
+ (click)="toggleEntry(child)">
1684
+ <ng-container
1685
+ [ngTemplateOutlet]="entryContent"
1686
+ [ngTemplateOutletContext]="{ $implicit: child, active: isItemActive(child) }" />
1687
+ <Icon name="chevron_right" [size]="16" [class]="entryChevronClasses(child)" />
1688
+ </button>
1689
+
1690
+ @if (isEntryOpen(child)) {
1691
+ <ul [id]="child.panelId" role="none" [class]="nestedListClasses()">
1692
+ @for (nested of child.children; track nested.key) {
1693
+ <li
1694
+ NavigationItem
1695
+ [navId]="navId()"
1696
+ [item]="nested"
1697
+ [level]="2"
1698
+ [orientation]="'horizontal'"
1699
+ [compact]="false"
1700
+ [itemClass]="navItemClass()"
1701
+ [activeIds]="activeIds()"
1702
+ [activeUrl]="activeUrl()"
1703
+ [iconTemplate]="iconTemplate()"
1704
+ [collapseTree]="collapseTree()"
1705
+ [openedIds]="openedIds()"
1706
+ (openedIdsChange)="openedIdsChange.emit($event)"
1707
+ (itemSelected)="itemSelected.emit($event)"></li>
1708
+ }
1709
+ </ul>
1710
+ }
1711
+ </li>
1712
+ } @else {
1713
+ <li role="none" class="min-w-0" [attr.data-navigation-item-key]="child.key">
1714
+ @if (isRouterItem(child)) {
1715
+ <a
1716
+ role="menuitem"
1717
+ data-navigation-flyout-entry="true"
1718
+ [class]="entryClasses(child, routerActive.isActive || isItemActive(child))"
1719
+ [routerLink]="child.link ?? null"
1720
+ [queryParams]="child.queryParams ?? null"
1721
+ [queryParamsHandling]="child.queryParamsHandling ?? null"
1722
+ [fragment]="child.fragment ?? undefined"
1723
+ [preserveFragment]="child.preserveFragment ?? false"
1724
+ [target]="child.target ?? undefined"
1725
+ routerLinkActive
1726
+ #routerActive="routerLinkActive"
1727
+ [routerLinkActiveOptions]="routerLinkActiveOptions(child)"
1728
+ [attr.aria-current]="routerActive.isActive || isItemActive(child) ? 'page' : null"
1729
+ [attr.aria-disabled]="child.disabled || null"
1730
+ (click)="selectEntry(child, $event)">
1731
+ <ng-container
1732
+ [ngTemplateOutlet]="entryContent"
1733
+ [ngTemplateOutletContext]="{
1734
+ $implicit: child,
1735
+ active: routerActive.isActive || isItemActive(child),
1736
+ }" />
1737
+ </a>
1738
+ } @else if (hrefFor(child)) {
1739
+ <a
1740
+ role="menuitem"
1741
+ data-navigation-flyout-entry="true"
1742
+ [class]="entryClasses(child, isItemActive(child))"
1743
+ [attr.href]="hrefFor(child)"
1744
+ [attr.target]="child.target ?? (child.externalLink ? '_blank' : null)"
1745
+ [attr.rel]="relFor(child)"
1746
+ [attr.aria-current]="isItemActive(child) ? 'page' : null"
1747
+ [attr.aria-disabled]="child.disabled || null"
1748
+ (click)="selectEntry(child, $event)">
1749
+ <ng-container
1750
+ [ngTemplateOutlet]="entryContent"
1751
+ [ngTemplateOutletContext]="{ $implicit: child, active: isItemActive(child) }" />
1752
+ </a>
1753
+ } @else {
1754
+ <button
1755
+ type="button"
1756
+ role="menuitem"
1757
+ data-navigation-flyout-entry="true"
1758
+ [class]="entryClasses(child, isItemActive(child))"
1759
+ [disabled]="child.disabled || null"
1760
+ [attr.aria-current]="isItemActive(child) ? 'page' : null"
1761
+ (click)="selectEntry(child, $event)">
1762
+ <ng-container
1763
+ [ngTemplateOutlet]="entryContent"
1764
+ [ngTemplateOutletContext]="{ $implicit: child, active: isItemActive(child) }" />
1765
+ </button>
1766
+ }
1767
+ </li>
1768
+ }
1769
+ }
1770
+ }
1771
+ }
1772
+ </ul>
1773
+ </div>
1774
+
1775
+ <ng-template #entryContent let-child let-active="active">
1776
+ <span [class]="entryIconClasses(active)">
1777
+ @if (isBorderRail()) {
1778
+ <span
1779
+ aria-hidden="true"
1780
+ data-navigation-icon-rail="top"
1781
+ class="pointer-events-none absolute -inset-x-1.5 top-0 h-px bg-border/50"></span>
1782
+ <span
1783
+ aria-hidden="true"
1784
+ data-navigation-icon-rail="bottom"
1785
+ class="pointer-events-none absolute -inset-x-1.5 bottom-0 h-px bg-border/50"></span>
1786
+ <span
1787
+ aria-hidden="true"
1788
+ data-navigation-icon-rail="left"
1789
+ class="pointer-events-none absolute -inset-y-1.5 left-0 w-px bg-border/50"></span>
1790
+ <span
1791
+ aria-hidden="true"
1792
+ data-navigation-icon-rail="right"
1793
+ class="pointer-events-none absolute -inset-y-1.5 right-0 w-px bg-border/50"></span>
1794
+ <span
1795
+ aria-hidden="true"
1796
+ data-navigation-icon-rail="center-horizontal"
1797
+ class="pointer-events-none absolute -inset-x-1.5 top-1/2 border-t border-dashed border-border/40"></span>
1798
+ <span
1799
+ aria-hidden="true"
1800
+ data-navigation-icon-rail="center-vertical"
1801
+ class="pointer-events-none absolute -inset-y-1.5 left-1/2 border-l border-dashed border-border/40"></span>
1802
+ }
1803
+
1804
+ @if (child.icon) {
1805
+ @if (iconTemplate(); as customIcon) {
1806
+ <ng-container
1807
+ [ngTemplateOutlet]="customIcon.template"
1808
+ [ngTemplateOutletContext]="iconContext(child, active)" />
1809
+ } @else {
1810
+ <Icon [name]="child.icon" [size]="18" [class]="entryIconGlyphClasses(child)" />
1811
+ }
1812
+ } @else {
1813
+ <span aria-hidden="true" class="text-xs font-semibold uppercase">{{ fallbackInitials(child) }}</span>
1814
+ }
1815
+ </span>
1816
+
1817
+ <span class="min-w-0 flex-1">
1818
+ <span [class]="entryTitleClasses(child, active)">{{ child.title }}</span>
1819
+ @if (child.subtitle) {
1820
+ <span [class]="entrySubtitleClasses(child)">{{ child.subtitle }}</span>
1821
+ }
1822
+ </span>
1823
+
1824
+ @if (child.badge; as badge) {
1825
+ <span
1826
+ [class]="
1827
+ badge.classes ??
1828
+ 'inline-flex h-5 shrink-0 items-center self-center rounded-full bg-muted px-2 text-xs font-medium text-muted-foreground'
1829
+ "
1830
+ >{{ badge.title }}</span
1831
+ >
1832
+ }
1833
+ </ng-template>
1834
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { 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"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1835
+ }
1836
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: NavigationEntryGridComponent, decorators: [{
1837
+ type: Component,
1838
+ args: [{
1839
+ selector: 'NavigationEntryGrid',
1840
+ changeDetection: ChangeDetectionStrategy.OnPush,
1841
+ imports: [NgTemplateOutlet, RouterLink, RouterLinkActive, IconComponent, NavigationItemComponent],
1842
+ host: {
1843
+ class: 'contents',
1844
+ },
1845
+ template: `
1846
+ @let branchItem = branch();
1847
+
1848
+ <div
1849
+ role="group"
1850
+ [id]="branchItem.panelId"
1851
+ [attr.aria-label]="branchItem.title || null"
1852
+ [attr.data-navigation-flyout-content]="branchItem.key"
1853
+ class="p-2 md:p-3">
1854
+ <ul role="none" [class]="contentGridClasses(branchItem)">
1855
+ @for (child of branchItem.children; track child.key) {
1856
+ @switch (child.type) {
1857
+ @case ('divider') {
1858
+ <li role="separator" class="col-span-full my-1 h-px bg-border"></li>
1859
+ }
1860
+ @case ('spacer') {}
1861
+ @default {
1862
+ @if (child.children.length > 0) {
1863
+ <li role="none" class="min-w-0" [attr.data-navigation-item-key]="child.key">
1864
+ <button
1865
+ type="button"
1866
+ role="menuitem"
1867
+ data-navigation-flyout-entry="true"
1868
+ [class]="entryClasses(child, isItemActive(child))"
1869
+ [attr.aria-expanded]="isEntryOpen(child)"
1870
+ [attr.aria-controls]="child.panelId"
1871
+ [disabled]="child.disabled || null"
1872
+ (click)="toggleEntry(child)">
1873
+ <ng-container
1874
+ [ngTemplateOutlet]="entryContent"
1875
+ [ngTemplateOutletContext]="{ $implicit: child, active: isItemActive(child) }" />
1876
+ <Icon name="chevron_right" [size]="16" [class]="entryChevronClasses(child)" />
1877
+ </button>
1878
+
1879
+ @if (isEntryOpen(child)) {
1880
+ <ul [id]="child.panelId" role="none" [class]="nestedListClasses()">
1881
+ @for (nested of child.children; track nested.key) {
1882
+ <li
1883
+ NavigationItem
1884
+ [navId]="navId()"
1885
+ [item]="nested"
1886
+ [level]="2"
1887
+ [orientation]="'horizontal'"
1888
+ [compact]="false"
1889
+ [itemClass]="navItemClass()"
1890
+ [activeIds]="activeIds()"
1891
+ [activeUrl]="activeUrl()"
1892
+ [iconTemplate]="iconTemplate()"
1893
+ [collapseTree]="collapseTree()"
1894
+ [openedIds]="openedIds()"
1895
+ (openedIdsChange)="openedIdsChange.emit($event)"
1896
+ (itemSelected)="itemSelected.emit($event)"></li>
1897
+ }
1898
+ </ul>
1899
+ }
1900
+ </li>
1901
+ } @else {
1902
+ <li role="none" class="min-w-0" [attr.data-navigation-item-key]="child.key">
1903
+ @if (isRouterItem(child)) {
1904
+ <a
1905
+ role="menuitem"
1906
+ data-navigation-flyout-entry="true"
1907
+ [class]="entryClasses(child, routerActive.isActive || isItemActive(child))"
1908
+ [routerLink]="child.link ?? null"
1909
+ [queryParams]="child.queryParams ?? null"
1910
+ [queryParamsHandling]="child.queryParamsHandling ?? null"
1911
+ [fragment]="child.fragment ?? undefined"
1912
+ [preserveFragment]="child.preserveFragment ?? false"
1913
+ [target]="child.target ?? undefined"
1914
+ routerLinkActive
1915
+ #routerActive="routerLinkActive"
1916
+ [routerLinkActiveOptions]="routerLinkActiveOptions(child)"
1917
+ [attr.aria-current]="routerActive.isActive || isItemActive(child) ? 'page' : null"
1918
+ [attr.aria-disabled]="child.disabled || null"
1919
+ (click)="selectEntry(child, $event)">
1920
+ <ng-container
1921
+ [ngTemplateOutlet]="entryContent"
1922
+ [ngTemplateOutletContext]="{
1923
+ $implicit: child,
1924
+ active: routerActive.isActive || isItemActive(child),
1925
+ }" />
1926
+ </a>
1927
+ } @else if (hrefFor(child)) {
1928
+ <a
1929
+ role="menuitem"
1930
+ data-navigation-flyout-entry="true"
1931
+ [class]="entryClasses(child, isItemActive(child))"
1932
+ [attr.href]="hrefFor(child)"
1933
+ [attr.target]="child.target ?? (child.externalLink ? '_blank' : null)"
1934
+ [attr.rel]="relFor(child)"
1935
+ [attr.aria-current]="isItemActive(child) ? 'page' : null"
1936
+ [attr.aria-disabled]="child.disabled || null"
1937
+ (click)="selectEntry(child, $event)">
1938
+ <ng-container
1939
+ [ngTemplateOutlet]="entryContent"
1940
+ [ngTemplateOutletContext]="{ $implicit: child, active: isItemActive(child) }" />
1941
+ </a>
1942
+ } @else {
1943
+ <button
1944
+ type="button"
1945
+ role="menuitem"
1946
+ data-navigation-flyout-entry="true"
1947
+ [class]="entryClasses(child, isItemActive(child))"
1948
+ [disabled]="child.disabled || null"
1949
+ [attr.aria-current]="isItemActive(child) ? 'page' : null"
1950
+ (click)="selectEntry(child, $event)">
1951
+ <ng-container
1952
+ [ngTemplateOutlet]="entryContent"
1953
+ [ngTemplateOutletContext]="{ $implicit: child, active: isItemActive(child) }" />
1954
+ </button>
1955
+ }
1956
+ </li>
1957
+ }
1958
+ }
1959
+ }
1960
+ }
1961
+ </ul>
1962
+ </div>
1963
+
1964
+ <ng-template #entryContent let-child let-active="active">
1965
+ <span [class]="entryIconClasses(active)">
1966
+ @if (isBorderRail()) {
1967
+ <span
1968
+ aria-hidden="true"
1969
+ data-navigation-icon-rail="top"
1970
+ class="pointer-events-none absolute -inset-x-1.5 top-0 h-px bg-border/50"></span>
1971
+ <span
1972
+ aria-hidden="true"
1973
+ data-navigation-icon-rail="bottom"
1974
+ class="pointer-events-none absolute -inset-x-1.5 bottom-0 h-px bg-border/50"></span>
1975
+ <span
1976
+ aria-hidden="true"
1977
+ data-navigation-icon-rail="left"
1978
+ class="pointer-events-none absolute -inset-y-1.5 left-0 w-px bg-border/50"></span>
1979
+ <span
1980
+ aria-hidden="true"
1981
+ data-navigation-icon-rail="right"
1982
+ class="pointer-events-none absolute -inset-y-1.5 right-0 w-px bg-border/50"></span>
1983
+ <span
1984
+ aria-hidden="true"
1985
+ data-navigation-icon-rail="center-horizontal"
1986
+ class="pointer-events-none absolute -inset-x-1.5 top-1/2 border-t border-dashed border-border/40"></span>
1987
+ <span
1988
+ aria-hidden="true"
1989
+ data-navigation-icon-rail="center-vertical"
1990
+ class="pointer-events-none absolute -inset-y-1.5 left-1/2 border-l border-dashed border-border/40"></span>
1991
+ }
1992
+
1993
+ @if (child.icon) {
1994
+ @if (iconTemplate(); as customIcon) {
1995
+ <ng-container
1996
+ [ngTemplateOutlet]="customIcon.template"
1997
+ [ngTemplateOutletContext]="iconContext(child, active)" />
1998
+ } @else {
1999
+ <Icon [name]="child.icon" [size]="18" [class]="entryIconGlyphClasses(child)" />
2000
+ }
2001
+ } @else {
2002
+ <span aria-hidden="true" class="text-xs font-semibold uppercase">{{ fallbackInitials(child) }}</span>
2003
+ }
2004
+ </span>
2005
+
2006
+ <span class="min-w-0 flex-1">
2007
+ <span [class]="entryTitleClasses(child, active)">{{ child.title }}</span>
2008
+ @if (child.subtitle) {
2009
+ <span [class]="entrySubtitleClasses(child)">{{ child.subtitle }}</span>
2010
+ }
2011
+ </span>
2012
+
2013
+ @if (child.badge; as badge) {
2014
+ <span
2015
+ [class]="
2016
+ badge.classes ??
2017
+ 'inline-flex h-5 shrink-0 items-center self-center rounded-full bg-muted px-2 text-xs font-medium text-muted-foreground'
2018
+ "
2019
+ >{{ badge.title }}</span
2020
+ >
2021
+ }
2022
+ </ng-template>
2023
+ `,
2024
+ }]
2025
+ }], propDecorators: { navId: [{ type: i0.Input, args: [{ isSignal: true, alias: "navId", required: false }] }], branch: [{ type: i0.Input, args: [{ isSignal: true, alias: "branch", required: true }] }], typeStyle: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeStyle", 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"] }] } });
2026
+
2027
+ class NavigationFlyoutMenuComponent {
2028
+ nav = inject(NavigationService);
2029
+ host = inject(ElementRef);
2030
+ navId = input('default', /* @ts-ignore */
2031
+ ...(ngDevMode ? [{ debugName: "navId" }] : /* istanbul ignore next */ []));
2032
+ items = input([], /* @ts-ignore */
2033
+ ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
2034
+ label = input('Menu', /* @ts-ignore */
2035
+ ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
2036
+ /** Nama Material Symbols untuk trigger; `null` = label saja (mode normal). */
2037
+ icon = input(null, /* @ts-ignore */
2038
+ ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
2039
+ /** Trigger hanya ikon — label tetap diumumkan via aria-label/title. */
2040
+ iconOnly = input(false, /* @ts-ignore */
2041
+ ...(ngDevMode ? [{ debugName: "iconOnly" }] : /* istanbul ignore next */ []));
2042
+ /** Penempatan ikon relatif label: `start` (default) atau `end`. */
2043
+ iconPosition = input('start', /* @ts-ignore */
2044
+ ...(ngDevMode ? [{ debugName: "iconPosition" }] : /* istanbul ignore next */ []));
2045
+ /** `bottom` = bar berada di bawah konten; panel menempel sisi bawah container dan tumbuh ke atas. */
2046
+ position = input('top', /* @ts-ignore */
2047
+ ...(ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []));
2048
+ typeStyle = input('default', /* @ts-ignore */
2049
+ ...(ngDevMode ? [{ debugName: "typeStyle" }] : /* istanbul ignore next */ []));
2050
+ compact = input(false, /* @ts-ignore */
2051
+ ...(ngDevMode ? [{ debugName: "compact" }] : /* istanbul ignore next */ []));
2052
+ itemClass = input('', /* @ts-ignore */
2053
+ ...(ngDevMode ? [{ debugName: "itemClass" }] : /* istanbul ignore next */ []));
2054
+ /** Kelas Tailwind untuk container `<li>` tiap tab group (mengatur tinggi/border/padding container). */
2055
+ groupClass = input('', /* @ts-ignore */
2056
+ ...(ngDevMode ? [{ debugName: "groupClass" }] : /* istanbul ignore next */ []));
2057
+ activeIds = input(new Set(), /* @ts-ignore */
2058
+ ...(ngDevMode ? [{ debugName: "activeIds" }] : /* istanbul ignore next */ []));
2059
+ activeUrl = input(null, /* @ts-ignore */
2060
+ ...(ngDevMode ? [{ debugName: "activeUrl" }] : /* istanbul ignore next */ []));
2061
+ iconTemplate = input(undefined, /* @ts-ignore */
2062
+ ...(ngDevMode ? [{ debugName: "iconTemplate" }] : /* istanbul ignore next */ []));
2063
+ collapseTree = input('stairs', /* @ts-ignore */
2064
+ ...(ngDevMode ? [{ debugName: "collapseTree" }] : /* istanbul ignore next */ []));
2065
+ openedIds = input([], /* @ts-ignore */
2066
+ ...(ngDevMode ? [{ debugName: "openedIds" }] : /* istanbul ignore next */ []));
2067
+ openedIdsChange = output();
2068
+ itemSelected = output();
2069
+ panelId = computed(() => `nav-flyout-${this.navId()}`, /* @ts-ignore */
2070
+ ...(ngDevMode ? [{ debugName: "panelId" }] : /* istanbul ignore next */ []));
2071
+ isBorderRail = computed(() => this.typeStyle() === 'border-rail', /* @ts-ignore */
2072
+ ...(ngDevMode ? [{ debugName: "isBorderRail" }] : /* istanbul ignore next */ []));
2073
+ isBottom = computed(() => this.position() === 'bottom', /* @ts-ignore */
2074
+ ...(ngDevMode ? [{ debugName: "isBottom" }] : /* istanbul ignore next */ []));
2075
+ /** Compact (mobile) selalu icon-only; selain itu mengikuti input `iconOnly`. */
2076
+ showIconOnly = computed(() => this.compact() || this.iconOnly(), /* @ts-ignore */
2077
+ ...(ngDevMode ? [{ debugName: "showIconOnly" }] : /* istanbul ignore next */ []));
2078
+ triggerIcon = computed(() => this.icon() ?? (this.showIconOnly() ? 'menu' : null), /* @ts-ignore */
2079
+ ...(ngDevMode ? [{ debugName: "triggerIcon" }] : /* istanbul ignore next */ []));
2080
+ /** Kelas tambahan untuk NavigationItem di dalam flyout saat style border-rail aktif. */
2081
+ navItemClass = computed(() =>
2082
+ // h-8 menjaga leaf di row tetap muat dalam tinggi 48px (py-2 + 32px) agar
2083
+ // border bawah row tidak terdorong melewati posisi rail layout.
2084
+ cn(this.itemClass(), this.isBorderRail() && 'h-8 rounded-none hover:bg-transparent hover:text-primary'), /* @ts-ignore */
2085
+ ...(ngDevMode ? [{ debugName: "navItemClass" }] : /* istanbul ignore next */ []));
2086
+ branches = computed(() => this.items().filter((item) => item.children.length > 0), /* @ts-ignore */
2087
+ ...(ngDevMode ? [{ debugName: "branches" }] : /* istanbul ignore next */ []));
2088
+ activeBranch = computed(() => {
2089
+ const key = this.nav.currentPanelKey(this.navId());
2090
+ return this.branches().find((branch) => branch.key === key) ?? null;
2091
+ }, /* @ts-ignore */
2092
+ ...(ngDevMode ? [{ debugName: "activeBranch" }] : /* istanbul ignore next */ []));
2093
+ /**
2094
+ * Offset panel terhadap host agar panel menimpa penuh container parent dari
2095
+ * `Navigation` (lebar sama persis; posisi atas di mode top, posisi bawah di
2096
+ * mode bottom). `null` berarti pengukuran tidak tersedia (mis. SSR/jsdom)
2097
+ * dan panel jatuh ke lebar host sendiri.
2098
+ */
2099
+ panelPlacement = signal(null, /* @ts-ignore */
2100
+ ...(ngDevMode ? [{ debugName: "panelPlacement" }] : /* istanbul ignore next */ []));
2101
+ panelWrapperClasses = computed(() => cn('absolute z-50', !this.panelPlacement() && cn('inset-x-0', this.isBottom() ? 'bottom-0' : 'top-0')), /* @ts-ignore */
2102
+ ...(ngDevMode ? [{ debugName: "panelWrapperClasses" }] : /* istanbul ignore next */ []));
2103
+ /** Mode bottom membalik urutan visual: grid entri di atas, row tab di bawah (dekat bar). */
2104
+ menuClasses = computed(() => cn('flex', this.isBottom() ? 'flex-col-reverse' : 'flex-col'), /* @ts-ignore */
2105
+ ...(ngDevMode ? [{ debugName: "menuClasses" }] : /* istanbul ignore next */ []));
2106
+ closeButtonClasses() {
2107
+ return cn('absolute right-2.5 z-10 inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring', this.isBottom() ? 'bottom-2' : 'top-2');
2108
+ }
2109
+ isOpen() {
2110
+ return this.nav.isDrawerOpen(this.navId());
2111
+ }
2112
+ onWindowResize() {
2113
+ if (this.isOpen()) {
2114
+ this.measurePanelPlacement();
2115
+ }
2116
+ }
2117
+ measurePanelPlacement() {
2118
+ const hostEl = this.host.nativeElement;
2119
+ const navEl = hostEl.closest('[data-navigation-id]');
2120
+ let container = navEl?.parentElement ?? hostEl.parentElement;
2121
+ // Wrapper display:contents punya rect kosong — naik terus sampai container yang punya box visual.
2122
+ while (container && container.getBoundingClientRect().width <= 0 && container.parentElement) {
2123
+ container = container.parentElement;
2124
+ }
2125
+ const containerRect = (container ?? hostEl).getBoundingClientRect();
2126
+ const hostRect = hostEl.getBoundingClientRect();
2127
+ if (containerRect.width <= 0) {
2128
+ this.panelPlacement.set(null);
2129
+ return;
2130
+ }
2131
+ this.panelPlacement.set({
2132
+ left: containerRect.left - hostRect.left,
2133
+ top: containerRect.top - hostRect.top,
2134
+ bottom: hostRect.bottom - containerRect.bottom,
2135
+ width: containerRect.width,
2136
+ });
2137
+ }
2138
+ toggleFlyout() {
2139
+ if (this.isOpen()) {
2140
+ this.closeFlyout(false);
2141
+ return;
2142
+ }
2143
+ this.measurePanelPlacement();
2144
+ this.nav.openDrawer(this.navId());
2145
+ const current = this.nav.currentPanelKey(this.navId());
2146
+ const branches = this.branches();
2147
+ if (!branches.some((branch) => branch.key === current)) {
2148
+ const first = branches.find((branch) => !branch.disabled) ?? branches[0];
2149
+ if (first) {
2150
+ this.nav.openPanel(this.navId(), first);
2151
+ }
2152
+ }
2153
+ queueMicrotask(() => this.focusFirstPanelItem());
2154
+ }
2155
+ closeFlyout(restoreFocus) {
2156
+ this.nav.closeDrawer(this.navId());
2157
+ if (restoreFocus) {
2158
+ queueMicrotask(() => this.focusTrigger());
2159
+ }
2160
+ }
2161
+ onEscape(event) {
2162
+ if (!this.isOpen()) {
2163
+ return;
2164
+ }
2165
+ event.preventDefault();
2166
+ this.closeFlyout(true);
2167
+ }
2168
+ onDocumentClick(event) {
2169
+ if (!this.isOpen()) {
2170
+ return;
2171
+ }
2172
+ const target = event.target;
2173
+ if (target && !this.host.nativeElement.contains(target)) {
2174
+ this.closeFlyout(false);
2175
+ }
2176
+ }
2177
+ isItemActive(item) {
2178
+ return this.nav.isItemActive(item, this.activeIds(), this.activeUrl());
2179
+ }
2180
+ isBranchOpen(item) {
2181
+ return this.nav.isPanelOpen(this.navId(), item);
2182
+ }
2183
+ openBranch(item) {
2184
+ this.nav.openPanel(this.navId(), item);
2185
+ }
2186
+ triggerClasses() {
2187
+ return cn('nav-trigger group/nav inline-flex h-10 min-w-0 items-center gap-2 rounded-full border border-[hsl(var(--border)/var(--opacity-60))] bg-background px-4 py-2 text-sm font-medium shadow-sm transition-colors', 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring', this.showIconOnly() && 'w-10 justify-center gap-0 px-0', this.isBorderRail()
2188
+ ? this.isOpen()
2189
+ ? 'text-primary'
2190
+ : 'text-foreground/80 hover:text-primary'
2191
+ : this.isOpen()
2192
+ ? 'bg-accent text-accent-foreground'
2193
+ : 'text-foreground/80 hover:bg-accent hover:text-accent-foreground', this.itemClass());
2194
+ }
2195
+ panelCardClasses() {
2196
+ // Border-rail: panel menutup tepat content-box frame layout, jadi garis
2197
+ // sisi bar memakai border frame (rail) itu sendiri — panel cukup menggambar
2198
+ // border 1.5px senada rail pada sisi yang menjauh dari bar agar sambungan
2199
+ // tetap lurus (bawah di mode top, atas di mode bottom).
2200
+ return cn('relative overflow-hidden bg-popover text-popover-foreground shadow-2xl', this.isBorderRail()
2201
+ ? cn('rounded-none border-border', this.isBottom() ? 'border-t-[1.5px]' : 'border-b-[1.5px]')
2202
+ : 'rounded-xl border border-[hsl(var(--border)/var(--opacity-70))]');
2203
+ }
2204
+ rowClasses() {
2205
+ // `min-h` menjaga tinggi row stabil meski hanya berisi tab group yang memakai
2206
+ // `-my-2`. Border pemisah row↔grid selalu di sisi yang menjauh dari bar:
2207
+ // border-b di mode top, border-t di mode bottom (row berada di bawah grid).
2208
+ // Border-rail: total tinggi row 48px (min-h-12) + border 1.5px sehingga garis
2209
+ // row jatuh persis di posisi rail horizontal layout dan menyambung lurus.
2210
+ return cn('m-0 flex min-w-0 list-none flex-wrap items-center gap-1 px-2 py-2 pr-12 md:px-3 md:pr-14', this.isBorderRail()
2211
+ ? cn('min-h-12 border-border', this.isBottom() ? 'border-t-[1.5px]' : 'border-b-[1.5px]')
2212
+ : cn('min-h-[3.25rem] border-[hsl(var(--border)/var(--opacity-60))]', this.isBottom() ? 'border-t' : 'border-b'));
2213
+ }
2214
+ /**
2215
+ * Container `<li>` tab group. Basis `flex -my-2 self-stretch` membuat tab setinggi
2216
+ * penuh row (lihat tabClasses); consumer dapat menimpa/menambah lewat `nav-group-class`
2217
+ * (global) atau `item.classes.container` (per-group) untuk fleksibilitas.
2218
+ */
2219
+ groupContainerClasses(item) {
2220
+ return cn('flex -my-2 self-stretch', this.groupClass(), item.classes?.container);
2221
+ }
2222
+ tabClasses(item) {
2223
+ return cn(
2224
+ // Tinggi tab group mengikuti tinggi penuh row (parent). Wrapper <li> memakai
2225
+ // `-my-2 self-stretch` untuk membatalkan `py-2` row; tombol `h-full` mengisi penuh
2226
+ // sehingga sisinya rata dengan border row — sudut pada sisi border dibuat siku
2227
+ // agar menyambung (bawah di mode top, atas di mode bottom).
2228
+ 'nav-trigger group/nav inline-flex h-full min-h-9 min-w-0 items-center gap-2 rounded-md px-3 text-sm font-medium transition-colors', this.isBottom() ? 'rounded-t-none' : 'rounded-b-none', 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50', this.isBorderRail()
2229
+ ? this.isBranchOpen(item) || this.isItemActive(item)
2230
+ ? 'rounded-none text-primary'
2231
+ : 'rounded-none text-foreground/80 hover:text-primary'
2232
+ : this.isBranchOpen(item)
2233
+ ? 'bg-accent text-accent-foreground'
2234
+ : this.isItemActive(item)
2235
+ ? 'font-semibold text-foreground hover:bg-accent hover:text-accent-foreground'
2236
+ : 'text-foreground/80 hover:bg-accent hover:text-accent-foreground', item.classes?.wrapper, this.itemClass());
2237
+ }
2238
+ tabChevronClasses(item) {
2239
+ return cn('shrink-0 self-center text-current transition-transform duration-200', this.isBranchOpen(item) && 'rotate-90');
2240
+ }
2241
+ onRowKeydown(event) {
2242
+ if (event.key !== 'ArrowRight' && event.key !== 'ArrowLeft' && event.key !== 'ArrowDown') {
2243
+ return;
2244
+ }
2245
+ const rowItems = Array.from(this.host.nativeElement.querySelectorAll('[data-navigation-flyout-row="true"] [role="menuitem"]'));
2246
+ const currentIndex = rowItems.indexOf(document.activeElement);
2247
+ if (currentIndex === -1) {
2248
+ return;
2249
+ }
2250
+ event.preventDefault();
2251
+ if (event.key === 'ArrowDown') {
2252
+ const entry = this.host.nativeElement.querySelector('[data-navigation-flyout-content] [role="menuitem"]');
2253
+ entry?.focus();
2254
+ return;
2255
+ }
2256
+ const direction = event.key === 'ArrowRight' ? 1 : -1;
2257
+ rowItems[(currentIndex + direction + rowItems.length) % rowItems.length]?.focus();
2258
+ }
2259
+ focusFirstPanelItem() {
2260
+ const panel = this.host.nativeElement.querySelector('[data-navigation-flyout-panel="true"]');
2261
+ const target = panel?.querySelector('[role="menuitem"], a[href], button:not([disabled])');
2262
+ target?.focus();
2263
+ }
2264
+ focusTrigger() {
2265
+ this.host.nativeElement.querySelector('[data-navigation-flyout-trigger="true"]')?.focus();
2266
+ }
2267
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: NavigationFlyoutMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2268
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: NavigationFlyoutMenuComponent, isStandalone: true, selector: "NavigationFlyoutMenu", inputs: { navId: { classPropertyName: "navId", publicName: "navId", isSignal: true, isRequired: false, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, iconOnly: { classPropertyName: "iconOnly", publicName: "iconOnly", isSignal: true, isRequired: false, transformFunction: null }, iconPosition: { classPropertyName: "iconPosition", publicName: "iconPosition", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, typeStyle: { classPropertyName: "typeStyle", publicName: "typeStyle", isSignal: true, isRequired: false, transformFunction: null }, compact: { classPropertyName: "compact", publicName: "compact", isSignal: true, isRequired: false, transformFunction: null }, itemClass: { classPropertyName: "itemClass", publicName: "itemClass", isSignal: true, isRequired: false, transformFunction: null }, groupClass: { classPropertyName: "groupClass", publicName: "groupClass", 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)", "document:click": "onDocumentClick($event)", "window:resize": "onWindowResize()" }, classAttribute: "relative block w-full" }, ngImport: i0, template: `
2269
+ <button
2270
+ type="button"
2271
+ data-navigation-flyout-trigger="true"
2272
+ [class]="triggerClasses()"
2273
+ aria-haspopup="menu"
2274
+ [attr.aria-expanded]="isOpen()"
2275
+ [attr.aria-controls]="isOpen() ? panelId() : null"
2276
+ [attr.aria-label]="showIconOnly() ? label() : null"
2277
+ [attr.title]="showIconOnly() ? label() : null"
2278
+ (click)="toggleFlyout()">
2279
+ @if (showIconOnly()) {
2280
+ <Icon [name]="triggerIcon() ?? 'menu'" [size]="18" class="text-current" />
2281
+ } @else {
2282
+ @if (iconPosition() === 'start' && triggerIcon(); as iconName) {
2283
+ <Icon [name]="iconName" [size]="18" class="shrink-0 text-current" />
2284
+ }
2285
+
2286
+ <span class="truncate">{{ label() }}</span>
2287
+
2288
+ @if (iconPosition() === 'end' && triggerIcon(); as iconName) {
2289
+ <Icon [name]="iconName" [size]="18" class="shrink-0 text-current" />
2290
+ }
2291
+ }
2292
+ </button>
2293
+
2294
+ @if (isOpen()) {
2295
+ <div
2296
+ [id]="panelId()"
2297
+ data-navigation-flyout-panel="true"
2298
+ [class]="panelWrapperClasses()"
2299
+ [style.left.px]="panelPlacement()?.left"
2300
+ [style.top.px]="isBottom() ? null : panelPlacement()?.top"
2301
+ [style.bottom.px]="isBottom() ? panelPlacement()?.bottom : null"
2302
+ [style.width.px]="panelPlacement()?.width">
2303
+ <div [class]="panelCardClasses()">
2304
+ <button
2305
+ type="button"
2306
+ data-navigation-flyout-close="true"
2307
+ [class]="closeButtonClasses()"
2308
+ aria-label="Close menu"
2309
+ (click)="closeFlyout(true)">
2310
+ <span aria-hidden="true" class="text-base leading-none">✕</span>
2311
+ </button>
2312
+
2313
+ <div role="menu" [class]="menuClasses()" [attr.aria-label]="label()">
2314
+ <ul role="none" data-navigation-flyout-row="true" [class]="rowClasses()" (keydown)="onRowKeydown($event)">
2315
+ @for (item of items(); track item.key) {
2316
+ @switch (item.type) {
2317
+ @case ('divider') {
2318
+ <li role="separator" class="mx-1 h-5 w-px bg-border"></li>
2319
+ }
2320
+ @case ('spacer') {
2321
+ <li role="none" aria-hidden="true" class="flex-1"></li>
2322
+ }
2323
+ @default {
2324
+ @if (item.children.length > 0) {
2325
+ <li role="none" [class]="groupContainerClasses(item)" [attr.data-navigation-item-key]="item.key">
2326
+ <button
2327
+ type="button"
2328
+ role="menuitem"
2329
+ data-navigation-flyout-tab="true"
2330
+ [class]="tabClasses(item)"
2331
+ [attr.aria-expanded]="isBranchOpen(item)"
2332
+ [attr.aria-controls]="item.panelId"
2333
+ [disabled]="item.disabled || null"
2334
+ (click)="openBranch(item)">
2335
+ <NavigationItemContent
2336
+ [item]="item"
2337
+ [active]="isItemActive(item)"
2338
+ [compact]="false"
2339
+ [orientation]="'horizontal'"
2340
+ [level]="0"
2341
+ [iconTemplate]="iconTemplate()" />
2342
+ <Icon name="chevron_right" [size]="16" [class]="tabChevronClasses(item)" />
2343
+ </button>
2344
+ </li>
2345
+ } @else {
2346
+ <li
2347
+ NavigationItem
2348
+ [navId]="navId()"
2349
+ [item]="item"
2350
+ [level]="0"
2351
+ [orientation]="'horizontal'"
2352
+ [compact]="false"
2353
+ [itemClass]="navItemClass()"
2354
+ [activeIds]="activeIds()"
2355
+ [activeUrl]="activeUrl()"
2356
+ [iconTemplate]="iconTemplate()"
2357
+ [collapseTree]="collapseTree()"
2358
+ [openedIds]="openedIds()"
2359
+ (openedIdsChange)="openedIdsChange.emit($event)"
2360
+ (itemSelected)="itemSelected.emit($event)"></li>
2361
+ }
2362
+ }
2363
+ }
2364
+ }
2365
+ </ul>
2366
+
2367
+ @if (activeBranch(); as branch) {
2368
+ <NavigationEntryGrid
2369
+ [navId]="navId()"
2370
+ [branch]="branch"
2371
+ [typeStyle]="typeStyle()"
2372
+ [itemClass]="itemClass()"
2373
+ [activeIds]="activeIds()"
2374
+ [activeUrl]="activeUrl()"
2375
+ [iconTemplate]="iconTemplate()"
2376
+ [collapseTree]="collapseTree()"
2377
+ [openedIds]="openedIds()"
2378
+ (openedIdsChange)="openedIdsChange.emit($event)"
2379
+ (itemSelected)="itemSelected.emit($event)" />
2380
+ }
2381
+ </div>
2382
+ </div>
2383
+ </div>
2384
+ }
2385
+ `, isInline: true, dependencies: [{ kind: "component", type: IconComponent, selector: "Icon", inputs: ["name", "class", "size", "fill", "weight", "grade", "opticalSize"] }, { kind: "component", type: NavigationEntryGridComponent, selector: "NavigationEntryGrid", inputs: ["navId", "branch", "typeStyle", "itemClass", "activeIds", "activeUrl", "iconTemplate", "collapseTree", "openedIds"], outputs: ["openedIdsChange", "itemSelected"] }, { 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"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2386
+ }
2387
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: NavigationFlyoutMenuComponent, decorators: [{
2388
+ type: Component,
2389
+ args: [{
2390
+ selector: 'NavigationFlyoutMenu',
2391
+ changeDetection: ChangeDetectionStrategy.OnPush,
2392
+ imports: [IconComponent, NavigationEntryGridComponent, NavigationItemComponent, NavigationItemContentComponent],
2393
+ host: {
2394
+ class: 'relative block w-full',
2395
+ '(keydown.escape)': 'onEscape($event)',
2396
+ '(document:click)': 'onDocumentClick($event)',
2397
+ '(window:resize)': 'onWindowResize()',
2398
+ },
2399
+ template: `
2400
+ <button
2401
+ type="button"
2402
+ data-navigation-flyout-trigger="true"
2403
+ [class]="triggerClasses()"
2404
+ aria-haspopup="menu"
2405
+ [attr.aria-expanded]="isOpen()"
2406
+ [attr.aria-controls]="isOpen() ? panelId() : null"
2407
+ [attr.aria-label]="showIconOnly() ? label() : null"
2408
+ [attr.title]="showIconOnly() ? label() : null"
2409
+ (click)="toggleFlyout()">
2410
+ @if (showIconOnly()) {
2411
+ <Icon [name]="triggerIcon() ?? 'menu'" [size]="18" class="text-current" />
2412
+ } @else {
2413
+ @if (iconPosition() === 'start' && triggerIcon(); as iconName) {
2414
+ <Icon [name]="iconName" [size]="18" class="shrink-0 text-current" />
2415
+ }
2416
+
2417
+ <span class="truncate">{{ label() }}</span>
2418
+
2419
+ @if (iconPosition() === 'end' && triggerIcon(); as iconName) {
2420
+ <Icon [name]="iconName" [size]="18" class="shrink-0 text-current" />
2421
+ }
2422
+ }
2423
+ </button>
2424
+
2425
+ @if (isOpen()) {
2426
+ <div
2427
+ [id]="panelId()"
2428
+ data-navigation-flyout-panel="true"
2429
+ [class]="panelWrapperClasses()"
2430
+ [style.left.px]="panelPlacement()?.left"
2431
+ [style.top.px]="isBottom() ? null : panelPlacement()?.top"
2432
+ [style.bottom.px]="isBottom() ? panelPlacement()?.bottom : null"
2433
+ [style.width.px]="panelPlacement()?.width">
2434
+ <div [class]="panelCardClasses()">
2435
+ <button
2436
+ type="button"
2437
+ data-navigation-flyout-close="true"
2438
+ [class]="closeButtonClasses()"
2439
+ aria-label="Close menu"
2440
+ (click)="closeFlyout(true)">
2441
+ <span aria-hidden="true" class="text-base leading-none">✕</span>
2442
+ </button>
2443
+
2444
+ <div role="menu" [class]="menuClasses()" [attr.aria-label]="label()">
2445
+ <ul role="none" data-navigation-flyout-row="true" [class]="rowClasses()" (keydown)="onRowKeydown($event)">
2446
+ @for (item of items(); track item.key) {
2447
+ @switch (item.type) {
2448
+ @case ('divider') {
2449
+ <li role="separator" class="mx-1 h-5 w-px bg-border"></li>
2450
+ }
2451
+ @case ('spacer') {
2452
+ <li role="none" aria-hidden="true" class="flex-1"></li>
2453
+ }
2454
+ @default {
2455
+ @if (item.children.length > 0) {
2456
+ <li role="none" [class]="groupContainerClasses(item)" [attr.data-navigation-item-key]="item.key">
2457
+ <button
2458
+ type="button"
2459
+ role="menuitem"
2460
+ data-navigation-flyout-tab="true"
2461
+ [class]="tabClasses(item)"
2462
+ [attr.aria-expanded]="isBranchOpen(item)"
2463
+ [attr.aria-controls]="item.panelId"
2464
+ [disabled]="item.disabled || null"
2465
+ (click)="openBranch(item)">
2466
+ <NavigationItemContent
2467
+ [item]="item"
2468
+ [active]="isItemActive(item)"
2469
+ [compact]="false"
2470
+ [orientation]="'horizontal'"
2471
+ [level]="0"
2472
+ [iconTemplate]="iconTemplate()" />
2473
+ <Icon name="chevron_right" [size]="16" [class]="tabChevronClasses(item)" />
2474
+ </button>
2475
+ </li>
2476
+ } @else {
2477
+ <li
2478
+ NavigationItem
2479
+ [navId]="navId()"
2480
+ [item]="item"
2481
+ [level]="0"
2482
+ [orientation]="'horizontal'"
2483
+ [compact]="false"
2484
+ [itemClass]="navItemClass()"
2485
+ [activeIds]="activeIds()"
2486
+ [activeUrl]="activeUrl()"
2487
+ [iconTemplate]="iconTemplate()"
2488
+ [collapseTree]="collapseTree()"
2489
+ [openedIds]="openedIds()"
2490
+ (openedIdsChange)="openedIdsChange.emit($event)"
2491
+ (itemSelected)="itemSelected.emit($event)"></li>
2492
+ }
2493
+ }
2494
+ }
2495
+ }
2496
+ </ul>
2497
+
2498
+ @if (activeBranch(); as branch) {
2499
+ <NavigationEntryGrid
2500
+ [navId]="navId()"
2501
+ [branch]="branch"
2502
+ [typeStyle]="typeStyle()"
2503
+ [itemClass]="itemClass()"
2504
+ [activeIds]="activeIds()"
2505
+ [activeUrl]="activeUrl()"
2506
+ [iconTemplate]="iconTemplate()"
2507
+ [collapseTree]="collapseTree()"
2508
+ [openedIds]="openedIds()"
2509
+ (openedIdsChange)="openedIdsChange.emit($event)"
2510
+ (itemSelected)="itemSelected.emit($event)" />
2511
+ }
2512
+ </div>
2513
+ </div>
2514
+ </div>
2515
+ }
2516
+ `,
2517
+ }]
2518
+ }], propDecorators: { navId: [{ type: i0.Input, args: [{ isSignal: true, alias: "navId", required: false }] }], items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], iconOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconOnly", required: false }] }], iconPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconPosition", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], typeStyle: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeStyle", required: false }] }], compact: [{ type: i0.Input, args: [{ isSignal: true, alias: "compact", required: false }] }], itemClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemClass", required: false }] }], groupClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupClass", 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"] }] } });
2519
+
2520
+ class NavigationHorizontalComponent {
2521
+ nav = inject(NavigationService);
2522
+ host = inject(ElementRef);
2523
+ navId = input('default', /* @ts-ignore */
2524
+ ...(ngDevMode ? [{ debugName: "navId" }] : /* istanbul ignore next */ []));
2525
+ items = input([], /* @ts-ignore */
2526
+ ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
2527
+ /** Posisi bar terhadap konten: `bottom` membuka panel ke atas; selain itu ke bawah. */
2528
+ position = input('top', /* @ts-ignore */
2529
+ ...(ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []));
2530
+ typeStyle = input('default', /* @ts-ignore */
2531
+ ...(ngDevMode ? [{ debugName: "typeStyle" }] : /* istanbul ignore next */ []));
2532
+ compact = input(false, /* @ts-ignore */
2533
+ ...(ngDevMode ? [{ debugName: "compact" }] : /* istanbul ignore next */ []));
2534
+ itemClass = input('', /* @ts-ignore */
2535
+ ...(ngDevMode ? [{ debugName: "itemClass" }] : /* istanbul ignore next */ []));
2536
+ /** Kelas Tailwind untuk container `<li>` group navbar. */
2537
+ groupClass = input('', /* @ts-ignore */
2538
+ ...(ngDevMode ? [{ debugName: "groupClass" }] : /* istanbul ignore next */ []));
2539
+ activeIds = input(new Set(), /* @ts-ignore */
2540
+ ...(ngDevMode ? [{ debugName: "activeIds" }] : /* istanbul ignore next */ []));
2541
+ activeUrl = input(null, /* @ts-ignore */
2542
+ ...(ngDevMode ? [{ debugName: "activeUrl" }] : /* istanbul ignore next */ []));
2543
+ iconTemplate = input(undefined, /* @ts-ignore */
2544
+ ...(ngDevMode ? [{ debugName: "iconTemplate" }] : /* istanbul ignore next */ []));
2545
+ collapseTree = input('stairs', /* @ts-ignore */
2546
+ ...(ngDevMode ? [{ debugName: "collapseTree" }] : /* istanbul ignore next */ []));
2547
+ openedIds = input([], /* @ts-ignore */
2548
+ ...(ngDevMode ? [{ debugName: "openedIds" }] : /* istanbul ignore next */ []));
2549
+ openedIdsChange = output();
2550
+ itemSelected = output();
2551
+ isBorderRail = computed(() => this.typeStyle() === 'border-rail', /* @ts-ignore */
2552
+ ...(ngDevMode ? [{ debugName: "isBorderRail" }] : /* istanbul ignore next */ []));
2553
+ isBottom = computed(() => this.position() === 'bottom', /* @ts-ignore */
2554
+ ...(ngDevMode ? [{ debugName: "isBottom" }] : /* istanbul ignore next */ []));
2555
+ /** Kelas tambahan untuk NavigationItem leaf di row saat style border-rail aktif. */
2556
+ navItemClass = computed(() => cn(this.itemClass(), this.isBorderRail() && 'rounded-none hover:bg-transparent hover:text-primary'), /* @ts-ignore */
2557
+ ...(ngDevMode ? [{ debugName: "navItemClass" }] : /* istanbul ignore next */ []));
2558
+ listClasses = computed(() => cn('flex min-w-0 list-none p-0', this.compact() ? 'justify-center gap-1' : 'items-center justify-center gap-1'), /* @ts-ignore */
2559
+ ...(ngDevMode ? [{ debugName: "listClasses" }] : /* istanbul ignore next */ []));
2560
+ isItemActive(item) {
2561
+ return this.nav.isItemActive(item, this.activeIds(), this.activeUrl());
2562
+ }
2563
+ isPanelOpen(item) {
2564
+ return this.nav.isPanelOpen(this.navId(), item);
2565
+ }
2566
+ togglePanel(item) {
2567
+ if (!this.isPanelOpen(item)) {
2568
+ this.measurePanelPlacement();
2569
+ }
2570
+ this.nav.togglePanel(this.navId(), item);
2571
+ }
2572
+ onWindowResize() {
2573
+ if (this.nav.currentPanelKey(this.navId())) {
2574
+ this.measurePanelPlacement();
2575
+ }
2576
+ }
2577
+ /** Klik di luar menubar/panel menutup panel mega yang sedang terbuka. */
2578
+ onDocumentClick(event) {
2579
+ if (!this.nav.currentPanelKey(this.navId())) {
2580
+ return;
2581
+ }
2582
+ const target = event.target;
2583
+ if (target && !this.host.nativeElement.contains(target)) {
2584
+ this.nav.closePanel(this.navId());
2585
+ }
2586
+ }
2587
+ /**
2588
+ * Lebar & posisi kiri panel mengikuti container parent dari `Navigation`
2589
+ * (wrapper display:contents dilewati). `null` = pengukuran tak tersedia dan
2590
+ * panel jatuh ke lebar host (inset-x-0).
2591
+ */
2592
+ panelPlacement = signal(null, /* @ts-ignore */
2593
+ ...(ngDevMode ? [{ debugName: "panelPlacement" }] : /* istanbul ignore next */ []));
2594
+ measurePanelPlacement() {
2595
+ const hostEl = this.host.nativeElement;
2596
+ const navEl = hostEl.closest('[data-navigation-id]');
2597
+ let container = navEl?.parentElement ?? hostEl.parentElement;
2598
+ while (container && container.getBoundingClientRect().width <= 0 && container.parentElement) {
2599
+ container = container.parentElement;
2600
+ }
2601
+ const containerRect = (container ?? hostEl).getBoundingClientRect();
2602
+ const hostRect = hostEl.getBoundingClientRect();
2603
+ if (containerRect.width <= 0) {
2604
+ this.panelPlacement.set(null);
2605
+ return;
2606
+ }
2607
+ this.panelPlacement.set({
2608
+ left: containerRect.left - hostRect.left,
2609
+ width: containerRect.width,
2610
+ });
2611
+ }
2612
+ compactLabel(item) {
2613
+ return this.nav.compactLabel(item, this.compact());
2614
+ }
2615
+ titleFor(item) {
2616
+ return this.nav.titleFor(item, this.compact());
2617
+ }
2618
+ /** Container `<li>` group navbar; consumer atur lewat `nav-group-class` atau `item.classes.container`. */
2619
+ groupContainerClasses(item) {
2620
+ return cn(this.groupClass(), item.classes?.container);
2621
+ }
2622
+ triggerClasses(item) {
2623
+ return cn('nav-trigger group/nav inline-flex min-w-0 items-center gap-3 rounded-md text-sm font-medium transition-colors', 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50', 'h-9 px-3 py-2', this.compact() && 'w-10 justify-center px-0', this.isBorderRail()
2624
+ ? this.isPanelOpen(item) || this.isItemActive(item)
2625
+ ? 'rounded-none text-primary'
2626
+ : 'rounded-none text-foreground/80 hover:text-primary'
2627
+ : this.isItemActive(item)
2628
+ ? 'bg-accent text-accent-foreground'
2629
+ : 'text-foreground/80 hover:bg-accent hover:text-accent-foreground', item.classes?.wrapper, this.itemClass());
2630
+ }
2631
+ chevronClasses(item) {
2632
+ return cn('ml-auto shrink-0 self-center transition-transform duration-200', this.isPanelOpen(item) && 'rotate-90');
2633
+ }
2634
+ panelClasses() {
2635
+ // Panel mega selebar container parent, menempel rapat tanpa gap pada sisi bar:
2636
+ // nav-position top membuka ke bawah (sisi atas siku), bottom membuka ke atas
2637
+ // (sisi bawah siku) sehingga sambungan border selalu menyatu dengan bar.
2638
+ // Border-rail: rail horizontal layout menjadi garis sisi-bar panel — panel
2639
+ // cukup menggambar border 1.5px senada rail pada sisi yang menjauh dari bar.
2640
+ return cn('absolute z-50 overflow-hidden bg-popover text-popover-foreground shadow-lg', this.isBottom() ? 'bottom-full' : 'top-full', !this.panelPlacement() && 'inset-x-0', this.isBorderRail()
2641
+ ? cn('rounded-none border-border', this.isBottom() ? 'border-t-[1.5px]' : 'border-b-[1.5px]')
2642
+ : cn('rounded-xl border border-[hsl(var(--border)/var(--opacity-70))]', this.isBottom() ? 'rounded-b-none' : 'rounded-t-none'));
2643
+ }
2644
+ onKeydown(event) {
2645
+ if (event.key === 'Escape') {
2646
+ const panelKey = this.nav.currentPanelKey(this.navId());
2647
+ this.nav.closePanel(this.navId());
2648
+ this.focusOpenPanelTrigger(panelKey);
2649
+ return;
2650
+ }
2651
+ const rootItems = this.rootFocusItems();
2652
+ if (rootItems.length === 0) {
2653
+ return;
2654
+ }
2655
+ const currentIndex = rootItems.indexOf(document.activeElement);
2656
+ if (currentIndex === -1) {
2657
+ return;
2658
+ }
2659
+ // ArrowDown membuka panel di posisi top; ArrowUp di posisi bottom (panel di atas bar).
2660
+ if (event.key === (this.isBottom() ? 'ArrowUp' : 'ArrowDown')) {
2661
+ const currentItem = rootItems[currentIndex];
2662
+ if (currentItem.getAttribute('aria-haspopup') === 'menu') {
2663
+ event.preventDefault();
2664
+ currentItem.click();
2665
+ queueMicrotask(() => this.focusFirstPanelItem(this.nav.currentPanelKey(this.navId())));
2666
+ }
2667
+ return;
2668
+ }
2669
+ if (event.key === 'ArrowRight' || event.key === 'ArrowLeft') {
2670
+ event.preventDefault();
2671
+ const direction = event.key === 'ArrowRight' ? 1 : -1;
2672
+ const nextIndex = (currentIndex + direction + rootItems.length) % rootItems.length;
2673
+ rootItems[nextIndex]?.focus();
2674
+ }
2675
+ }
2676
+ onPanelKeydown(event) {
2677
+ if (event.key !== 'Escape') {
2678
+ return;
2679
+ }
2680
+ event.preventDefault();
2681
+ const panelKey = this.nav.currentPanelKey(this.navId());
2682
+ this.nav.closePanel(this.navId());
2683
+ this.focusOpenPanelTrigger(panelKey);
2684
+ }
2685
+ rootFocusItems() {
2686
+ return Array.from(this.host.nativeElement.querySelectorAll('[data-navigation-root-item="true"]'));
2687
+ }
2688
+ focusOpenPanelTrigger(panelKey) {
2689
+ if (!panelKey) {
2690
+ return;
2691
+ }
2692
+ const trigger = this.host.nativeElement.querySelector(`[data-navigation-item-key="${panelKey}"] [data-navigation-root-item="true"]`);
2693
+ trigger?.focus();
2694
+ }
2695
+ focusFirstPanelItem(panelKey) {
2696
+ if (!panelKey) {
2697
+ return;
2698
+ }
2699
+ const panel = this.host.nativeElement.querySelector(`[data-navigation-panel="${panelKey}"]`);
2700
+ const item = panel?.querySelector('a[href], button:not([disabled]), [tabindex]:not([tabindex="-1"])');
2701
+ item?.focus();
2702
+ }
2703
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: NavigationHorizontalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2704
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: NavigationHorizontalComponent, isStandalone: true, selector: "NavigationHorizontal", inputs: { navId: { classPropertyName: "navId", publicName: "navId", isSignal: true, isRequired: false, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, typeStyle: { classPropertyName: "typeStyle", publicName: "typeStyle", isSignal: true, isRequired: false, transformFunction: null }, compact: { classPropertyName: "compact", publicName: "compact", isSignal: true, isRequired: false, transformFunction: null }, itemClass: { classPropertyName: "itemClass", publicName: "itemClass", isSignal: true, isRequired: false, transformFunction: null }, groupClass: { classPropertyName: "groupClass", publicName: "groupClass", 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: { "window:resize": "onWindowResize()", "document:click": "onDocumentClick($event)" }, classAttribute: "relative block w-full" }, ngImport: i0, template: `
2705
+ <ul [class]="listClasses()" role="menubar" (keydown)="onKeydown($event)">
2706
+ @for (item of items(); track item.key) {
2707
+ @switch (item.type) {
2708
+ @case ('group') {
2709
+ <ng-container [ngTemplateOutlet]="branchTemplate" [ngTemplateOutletContext]="{ $implicit: item }" />
2710
+ }
2711
+ @case ('collapsible') {
2712
+ <ng-container [ngTemplateOutlet]="branchTemplate" [ngTemplateOutletContext]="{ $implicit: item }" />
2713
+ }
2714
+ @case ('mega') {
2715
+ <ng-container [ngTemplateOutlet]="branchTemplate" [ngTemplateOutletContext]="{ $implicit: item }" />
2716
+ }
2717
+ @default {
2718
+ <li
2719
+ NavigationItem
2720
+ [navId]="navId()"
2721
+ [item]="item"
2722
+ [level]="0"
2723
+ [orientation]="'horizontal'"
2724
+ [compact]="compact()"
2725
+ [itemClass]="navItemClass()"
2726
+ [activeIds]="activeIds()"
2727
+ [activeUrl]="activeUrl()"
2728
+ [iconTemplate]="iconTemplate()"
2729
+ [collapseTree]="collapseTree()"
2730
+ [openedIds]="openedIds()"
2731
+ (openedIdsChange)="openedIdsChange.emit($event)"
2732
+ (itemSelected)="itemSelected.emit($event)"></li>
2733
+ }
2734
+ }
2735
+ }
2736
+ </ul>
2737
+
2738
+ <ng-template #branchTemplate let-item>
2739
+ <li role="none" [class]="groupContainerClasses(item)" [attr.data-navigation-item-key]="item.key">
2740
+ <button
2741
+ type="button"
2742
+ role="menuitem"
2743
+ data-navigation-root-item="true"
2744
+ [class]="triggerClasses(item)"
2745
+ [attr.aria-expanded]="isPanelOpen(item)"
2746
+ [attr.aria-controls]="item.panelId"
2747
+ [attr.aria-haspopup]="'menu'"
2748
+ [attr.aria-label]="compactLabel(item)"
2749
+ [attr.title]="titleFor(item)"
2750
+ [disabled]="item.disabled || null"
2751
+ (click)="togglePanel(item)">
2752
+ <NavigationItemContent
2753
+ [item]="item"
2754
+ [active]="isItemActive(item)"
2755
+ [compact]="compact()"
2756
+ [orientation]="'horizontal'"
2757
+ [level]="0"
2758
+ [iconTemplate]="iconTemplate()" />
2759
+ <Icon name="chevron_right" [size]="16" [class]="chevronClasses(item)" />
2760
+ </button>
2761
+
2762
+ @if (isPanelOpen(item)) {
2763
+ <div
2764
+ [id]="item.panelId"
2765
+ role="menu"
2766
+ [class]="panelClasses()"
2767
+ [style.left.px]="panelPlacement()?.left"
2768
+ [style.width.px]="panelPlacement()?.width"
2769
+ [attr.data-navigation-panel]="item.key"
2770
+ (keydown)="onPanelKeydown($event)">
2771
+ <NavigationEntryGrid
2772
+ [navId]="navId()"
2773
+ [branch]="item"
2774
+ [typeStyle]="typeStyle()"
2775
+ [itemClass]="itemClass()"
2776
+ [activeIds]="activeIds()"
2777
+ [activeUrl]="activeUrl()"
2778
+ [iconTemplate]="iconTemplate()"
2779
+ [collapseTree]="collapseTree()"
2780
+ [openedIds]="openedIds()"
2781
+ (openedIdsChange)="openedIdsChange.emit($event)"
2782
+ (itemSelected)="itemSelected.emit($event)" />
2783
+ </div>
2784
+ }
2785
+ </li>
2786
+ </ng-template>
2787
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: IconComponent, selector: "Icon", inputs: ["name", "class", "size", "fill", "weight", "grade", "opticalSize"] }, { kind: "component", type: NavigationEntryGridComponent, selector: "NavigationEntryGrid", inputs: ["navId", "branch", "typeStyle", "itemClass", "activeIds", "activeUrl", "iconTemplate", "collapseTree", "openedIds"], outputs: ["openedIdsChange", "itemSelected"] }, { 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"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2788
+ }
2789
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: NavigationHorizontalComponent, decorators: [{
2790
+ type: Component,
2791
+ args: [{
2792
+ selector: 'NavigationHorizontal',
2793
+ changeDetection: ChangeDetectionStrategy.OnPush,
2794
+ imports: [
2795
+ NgTemplateOutlet,
2796
+ IconComponent,
2797
+ NavigationEntryGridComponent,
2798
+ NavigationItemComponent,
2799
+ NavigationItemContentComponent,
2800
+ ],
2801
+ host: {
2802
+ class: 'relative block w-full',
2803
+ '(window:resize)': 'onWindowResize()',
2804
+ '(document:click)': 'onDocumentClick($event)',
2805
+ },
2806
+ template: `
2807
+ <ul [class]="listClasses()" role="menubar" (keydown)="onKeydown($event)">
2808
+ @for (item of items(); track item.key) {
2809
+ @switch (item.type) {
2810
+ @case ('group') {
2811
+ <ng-container [ngTemplateOutlet]="branchTemplate" [ngTemplateOutletContext]="{ $implicit: item }" />
2812
+ }
2813
+ @case ('collapsible') {
2814
+ <ng-container [ngTemplateOutlet]="branchTemplate" [ngTemplateOutletContext]="{ $implicit: item }" />
2815
+ }
2816
+ @case ('mega') {
2817
+ <ng-container [ngTemplateOutlet]="branchTemplate" [ngTemplateOutletContext]="{ $implicit: item }" />
2818
+ }
2819
+ @default {
2820
+ <li
2821
+ NavigationItem
2822
+ [navId]="navId()"
2823
+ [item]="item"
2824
+ [level]="0"
2825
+ [orientation]="'horizontal'"
2826
+ [compact]="compact()"
2827
+ [itemClass]="navItemClass()"
2828
+ [activeIds]="activeIds()"
2829
+ [activeUrl]="activeUrl()"
2830
+ [iconTemplate]="iconTemplate()"
2831
+ [collapseTree]="collapseTree()"
2832
+ [openedIds]="openedIds()"
2833
+ (openedIdsChange)="openedIdsChange.emit($event)"
2834
+ (itemSelected)="itemSelected.emit($event)"></li>
2835
+ }
2836
+ }
2837
+ }
2838
+ </ul>
2839
+
2840
+ <ng-template #branchTemplate let-item>
2841
+ <li role="none" [class]="groupContainerClasses(item)" [attr.data-navigation-item-key]="item.key">
2842
+ <button
2843
+ type="button"
2844
+ role="menuitem"
2845
+ data-navigation-root-item="true"
2846
+ [class]="triggerClasses(item)"
2847
+ [attr.aria-expanded]="isPanelOpen(item)"
2848
+ [attr.aria-controls]="item.panelId"
2849
+ [attr.aria-haspopup]="'menu'"
2850
+ [attr.aria-label]="compactLabel(item)"
2851
+ [attr.title]="titleFor(item)"
2852
+ [disabled]="item.disabled || null"
2853
+ (click)="togglePanel(item)">
2854
+ <NavigationItemContent
2855
+ [item]="item"
2856
+ [active]="isItemActive(item)"
2857
+ [compact]="compact()"
2858
+ [orientation]="'horizontal'"
2859
+ [level]="0"
2860
+ [iconTemplate]="iconTemplate()" />
2861
+ <Icon name="chevron_right" [size]="16" [class]="chevronClasses(item)" />
2862
+ </button>
2863
+
2864
+ @if (isPanelOpen(item)) {
2865
+ <div
2866
+ [id]="item.panelId"
2867
+ role="menu"
2868
+ [class]="panelClasses()"
2869
+ [style.left.px]="panelPlacement()?.left"
2870
+ [style.width.px]="panelPlacement()?.width"
2871
+ [attr.data-navigation-panel]="item.key"
2872
+ (keydown)="onPanelKeydown($event)">
2873
+ <NavigationEntryGrid
2874
+ [navId]="navId()"
2875
+ [branch]="item"
2876
+ [typeStyle]="typeStyle()"
2877
+ [itemClass]="itemClass()"
2878
+ [activeIds]="activeIds()"
2879
+ [activeUrl]="activeUrl()"
2880
+ [iconTemplate]="iconTemplate()"
2881
+ [collapseTree]="collapseTree()"
2882
+ [openedIds]="openedIds()"
2883
+ (openedIdsChange)="openedIdsChange.emit($event)"
2884
+ (itemSelected)="itemSelected.emit($event)" />
2885
+ </div>
2886
+ }
2887
+ </li>
2888
+ </ng-template>
2889
+ `,
2890
+ }]
2891
+ }], propDecorators: { navId: [{ type: i0.Input, args: [{ isSignal: true, alias: "navId", required: false }] }], items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], typeStyle: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeStyle", required: false }] }], compact: [{ type: i0.Input, args: [{ isSignal: true, alias: "compact", required: false }] }], itemClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemClass", required: false }] }], groupClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupClass", 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"] }] } });
2892
+
2893
+ /**
2894
+ * Area konten navigasi: merender daftar item sesuai type aktif dari
2895
+ * `<Navigation>` induk. Selalu hadir di setiap type — komponen type
2896
+ * merender default-nya bila consumer tidak memproyeksikan `<NavigationContent />`.
2897
+ */
2898
+ class NavigationContentComponent {
2899
+ container = inject(NavigationContainerComponent);
2900
+ class = input('', { ...(ngDevMode ? { debugName: "class" } : /* istanbul ignore next */ {}), alias: 'class' });
2901
+ hostClasses = computed(() => cn('min-h-0 flex-1', this.container.displayState().orientation === 'horizontal'
2902
+ ? 'flex h-full min-w-0 items-stretch justify-center overflow-visible'
2903
+ : 'block overflow-y-auto overflow-x-hidden', this.class()), /* @ts-ignore */
2904
+ ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
2905
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: NavigationContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2906
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: NavigationContentComponent, isStandalone: true, selector: "NavigationContent", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "hostClasses()" } }, ngImport: i0, template: `
2907
+ @let navState = container.displayState();
2908
+
2909
+ @if (navState.orientation === 'horizontal' && navState.type === 'flyout') {
2910
+ <NavigationFlyoutMenu
2911
+ [navId]="navState.id"
2912
+ [items]="container.normalizedItems()"
2913
+ [label]="container.flyoutLabel()"
2914
+ [icon]="container.flyoutIcon()"
2915
+ [iconOnly]="container.flyoutIconOnly()"
2916
+ [iconPosition]="container.flyoutIconPosition()"
2917
+ [position]="navState.position"
2918
+ [typeStyle]="container.typeStyle()"
2919
+ [compact]="container.compact()"
2920
+ [itemClass]="container.itemClass()"
2921
+ [groupClass]="container.groupClass()"
2922
+ [activeIds]="container.activeIdSet()"
2923
+ [activeUrl]="container.activeUrl()"
2924
+ [iconTemplate]="container.iconTemplate()"
2925
+ [collapseTree]="container.collapseTree()"
2926
+ [openedIds]="container.openedIds()"
2927
+ (openedIdsChange)="container.openedIds.set($event)"
2928
+ (itemSelected)="container.itemSelected.emit($event)" />
2929
+ } @else if (navState.orientation === 'horizontal') {
2930
+ <NavigationHorizontal
2931
+ [navId]="navState.id"
2932
+ [items]="container.normalizedItems()"
2933
+ [position]="navState.position"
2934
+ [typeStyle]="container.typeStyle()"
2935
+ [compact]="container.compact()"
2936
+ [itemClass]="container.itemClass()"
2937
+ [groupClass]="container.groupClass()"
2938
+ [activeIds]="container.activeIdSet()"
2939
+ [activeUrl]="container.activeUrl()"
2940
+ [iconTemplate]="container.iconTemplate()"
2941
+ [collapseTree]="container.collapseTree()"
2942
+ [openedIds]="container.openedIds()"
2943
+ (openedIdsChange)="container.openedIds.set($event)"
2944
+ (itemSelected)="container.itemSelected.emit($event)" />
2945
+ } @else if (navState.type === 'dockbar') {
2946
+ <NavigationDockbarMenu
2947
+ [navId]="navState.id"
2948
+ [items]="container.normalizedItems()"
2949
+ [mode]="navState.dockbarMode"
2950
+ [position]="navState.position"
2951
+ [itemClass]="container.itemClass()"
2952
+ [activeIds]="container.activeIdSet()"
2953
+ [activeUrl]="container.activeUrl()"
2954
+ [iconTemplate]="container.iconTemplate()"
2955
+ [collapseTree]="container.collapseTree()"
2956
+ [openedIds]="container.openedIds()"
2957
+ (openedIdsChange)="container.openedIds.set($event)"
2958
+ (itemSelected)="container.itemSelected.emit($event)" />
2959
+ } @else {
2960
+ <NavigationList
2961
+ [navId]="navState.id"
2962
+ [items]="container.normalizedItems()"
2963
+ [collapsed]="navState.collapsed"
2964
+ [position]="navState.position"
2965
+ [itemClass]="container.itemClass()"
2966
+ [activeIds]="container.activeIdSet()"
2967
+ [activeUrl]="container.activeUrl()"
2968
+ [iconTemplate]="container.iconTemplate()"
2969
+ [collapseTree]="container.collapseTree()"
2970
+ [openedIds]="container.openedIds()"
2971
+ (openedIdsChange)="container.openedIds.set($event)"
2972
+ (itemSelected)="container.itemSelected.emit($event)" />
2973
+ }
2974
+ `, isInline: true, dependencies: [{ kind: "component", type: NavigationDockbarMenuComponent, selector: "NavigationDockbarMenu", inputs: ["navId", "items", "mode", "position", "itemClass", "activeIds", "activeUrl", "iconTemplate", "collapseTree", "openedIds"], outputs: ["openedIdsChange", "itemSelected"] }, { kind: "component", type: NavigationFlyoutMenuComponent, selector: "NavigationFlyoutMenu", inputs: ["navId", "items", "label", "icon", "iconOnly", "iconPosition", "position", "typeStyle", "compact", "itemClass", "groupClass", "activeIds", "activeUrl", "iconTemplate", "collapseTree", "openedIds"], outputs: ["openedIdsChange", "itemSelected"] }, { kind: "component", type: NavigationHorizontalComponent, selector: "NavigationHorizontal", inputs: ["navId", "items", "position", "typeStyle", "compact", "itemClass", "groupClass", "activeIds", "activeUrl", "iconTemplate", "collapseTree", "openedIds"], outputs: ["openedIdsChange", "itemSelected"] }, { kind: "component", type: NavigationListComponent, selector: "NavigationList", inputs: ["navId", "items", "collapsed", "compact", "position", "itemClass", "activeIds", "activeUrl", "iconTemplate", "collapseTree", "openedIds"], outputs: ["openedIdsChange", "itemSelected"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2975
+ }
2976
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: NavigationContentComponent, decorators: [{
2977
+ type: Component,
2978
+ args: [{
2979
+ selector: 'NavigationContent',
2980
+ changeDetection: ChangeDetectionStrategy.OnPush,
2981
+ imports: [
2982
+ NavigationDockbarMenuComponent,
2983
+ NavigationFlyoutMenuComponent,
2984
+ NavigationHorizontalComponent,
2985
+ NavigationListComponent,
2986
+ ],
2987
+ host: {
2988
+ '[class]': 'hostClasses()',
2989
+ },
2990
+ template: `
2991
+ @let navState = container.displayState();
2992
+
2993
+ @if (navState.orientation === 'horizontal' && navState.type === 'flyout') {
2994
+ <NavigationFlyoutMenu
2995
+ [navId]="navState.id"
2996
+ [items]="container.normalizedItems()"
2997
+ [label]="container.flyoutLabel()"
2998
+ [icon]="container.flyoutIcon()"
2999
+ [iconOnly]="container.flyoutIconOnly()"
3000
+ [iconPosition]="container.flyoutIconPosition()"
3001
+ [position]="navState.position"
3002
+ [typeStyle]="container.typeStyle()"
3003
+ [compact]="container.compact()"
3004
+ [itemClass]="container.itemClass()"
3005
+ [groupClass]="container.groupClass()"
3006
+ [activeIds]="container.activeIdSet()"
3007
+ [activeUrl]="container.activeUrl()"
3008
+ [iconTemplate]="container.iconTemplate()"
3009
+ [collapseTree]="container.collapseTree()"
3010
+ [openedIds]="container.openedIds()"
3011
+ (openedIdsChange)="container.openedIds.set($event)"
3012
+ (itemSelected)="container.itemSelected.emit($event)" />
3013
+ } @else if (navState.orientation === 'horizontal') {
3014
+ <NavigationHorizontal
3015
+ [navId]="navState.id"
3016
+ [items]="container.normalizedItems()"
3017
+ [position]="navState.position"
3018
+ [typeStyle]="container.typeStyle()"
3019
+ [compact]="container.compact()"
3020
+ [itemClass]="container.itemClass()"
3021
+ [groupClass]="container.groupClass()"
3022
+ [activeIds]="container.activeIdSet()"
3023
+ [activeUrl]="container.activeUrl()"
3024
+ [iconTemplate]="container.iconTemplate()"
3025
+ [collapseTree]="container.collapseTree()"
3026
+ [openedIds]="container.openedIds()"
3027
+ (openedIdsChange)="container.openedIds.set($event)"
3028
+ (itemSelected)="container.itemSelected.emit($event)" />
3029
+ } @else if (navState.type === 'dockbar') {
3030
+ <NavigationDockbarMenu
3031
+ [navId]="navState.id"
3032
+ [items]="container.normalizedItems()"
3033
+ [mode]="navState.dockbarMode"
3034
+ [position]="navState.position"
3035
+ [itemClass]="container.itemClass()"
3036
+ [activeIds]="container.activeIdSet()"
3037
+ [activeUrl]="container.activeUrl()"
3038
+ [iconTemplate]="container.iconTemplate()"
3039
+ [collapseTree]="container.collapseTree()"
3040
+ [openedIds]="container.openedIds()"
3041
+ (openedIdsChange)="container.openedIds.set($event)"
3042
+ (itemSelected)="container.itemSelected.emit($event)" />
3043
+ } @else {
3044
+ <NavigationList
3045
+ [navId]="navState.id"
3046
+ [items]="container.normalizedItems()"
3047
+ [collapsed]="navState.collapsed"
3048
+ [position]="navState.position"
3049
+ [itemClass]="container.itemClass()"
3050
+ [activeIds]="container.activeIdSet()"
3051
+ [activeUrl]="container.activeUrl()"
3052
+ [iconTemplate]="container.iconTemplate()"
3053
+ [collapseTree]="container.collapseTree()"
3054
+ [openedIds]="container.openedIds()"
3055
+ (openedIdsChange)="container.openedIds.set($event)"
3056
+ (itemSelected)="container.itemSelected.emit($event)" />
3057
+ }
3058
+ `,
3059
+ }]
3060
+ }], propDecorators: { class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
3061
+
3062
+ class NavigationFooterComponent {
3063
+ shell = inject(NAVIGATION_SHELL, { optional: true });
3064
+ class = input('', { ...(ngDevMode ? { debugName: "class" } : /* istanbul ignore next */ {}), alias: 'class' });
3065
+ isHorizontal = computed(() => this.shell?.displayState().orientation === 'horizontal', /* @ts-ignore */
3066
+ ...(ngDevMode ? [{ debugName: "isHorizontal" }] : /* istanbul ignore next */ []));
3067
+ hostClasses = computed(() => cn(this.isHorizontal()
3068
+ ? 'relative z-10 block h-full w-auto shrink-0'
3069
+ : 'sticky bottom-0 z-10 block h-12 shrink-0 border-t border-[hsl(var(--border)/var(--opacity-70))]', this.class()), /* @ts-ignore */
3070
+ ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
3071
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: NavigationFooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3072
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.2", type: NavigationFooterComponent, isStandalone: true, selector: "NavigationFooter", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "hostClasses()" } }, ngImport: i0, template: `
3073
+ <div class="flex h-full items-center">
3074
+ <ng-content />
3075
+ </div>
3076
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
3077
+ }
3078
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: NavigationFooterComponent, decorators: [{
3079
+ type: Component,
3080
+ args: [{
3081
+ selector: 'NavigationFooter',
3082
+ changeDetection: ChangeDetectionStrategy.OnPush,
3083
+ host: {
3084
+ '[class]': 'hostClasses()',
3085
+ },
3086
+ template: `
3087
+ <div class="flex h-full items-center">
3088
+ <ng-content />
3089
+ </div>
3090
+ `,
3091
+ }]
3092
+ }], propDecorators: { class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
3093
+
3094
+ class NavigationHeaderComponent {
3095
+ shell = inject(NAVIGATION_SHELL);
3096
+ toggle = input(false, /* @ts-ignore */
3097
+ ...(ngDevMode ? [{ debugName: "toggle" }] : /* istanbul ignore next */ []));
3098
+ class = input('', { ...(ngDevMode ? { debugName: "class" } : /* istanbul ignore next */ {}), alias: 'class' });
3099
+ collapsed = computed(() => this.shell.state().collapsed, /* @ts-ignore */
3100
+ ...(ngDevMode ? [{ debugName: "collapsed" }] : /* istanbul ignore next */ []));
3101
+ displayCollapsed = computed(() => this.shell.displayState().collapsed, /* @ts-ignore */
3102
+ ...(ngDevMode ? [{ debugName: "displayCollapsed" }] : /* istanbul ignore next */ []));
3103
+ isHorizontal = computed(() => this.shell.displayState().orientation === 'horizontal', /* @ts-ignore */
3104
+ ...(ngDevMode ? [{ debugName: "isHorizontal" }] : /* istanbul ignore next */ []));
3105
+ showToggle = computed(() => {
3106
+ if (this.isHorizontal()) {
3107
+ return false;
3108
+ }
3109
+ return this.toggle() || (this.shell.collapseEnabled() && !this.displayCollapsed());
3110
+ }, /* @ts-ignore */
3111
+ ...(ngDevMode ? [{ debugName: "showToggle" }] : /* istanbul ignore next */ []));
3112
+ toggleAriaLabel = computed(() => (this.collapsed() ? 'Expand navigation' : 'Collapse navigation'), /* @ts-ignore */
3113
+ ...(ngDevMode ? [{ debugName: "toggleAriaLabel" }] : /* istanbul ignore next */ []));
3114
+ toggleIconName = computed(() => (this.collapsed() ? 'left_panel_open' : 'left_panel_close'), /* @ts-ignore */
3115
+ ...(ngDevMode ? [{ debugName: "toggleIconName" }] : /* istanbul ignore next */ []));
3116
+ contentClasses = computed(() => (this.isHorizontal() ? 'min-w-0' : 'min-w-0 flex-1'), /* @ts-ignore */
3117
+ ...(ngDevMode ? [{ debugName: "contentClasses" }] : /* istanbul ignore next */ []));
3118
+ hostClasses = computed(() => cn(this.isHorizontal()
3119
+ ? 'relative z-10 block h-full w-auto shrink-0'
3120
+ : 'sticky top-0 z-10 block h-12 shrink-0 border-b border-[hsl(var(--border)/var(--opacity-70))]', this.class()), /* @ts-ignore */
3121
+ ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
3122
+ toggleCollapsed() {
3123
+ this.shell.toggleCollapsed();
3124
+ }
3125
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: NavigationHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3126
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: NavigationHeaderComponent, isStandalone: true, selector: "NavigationHeader", inputs: { toggle: { classPropertyName: "toggle", publicName: "toggle", isSignal: true, isRequired: false, transformFunction: null }, class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "hostClasses()", "attr.data-collapsed": "displayCollapsed()" } }, ngImport: i0, template: `
3127
+ <div class="flex h-full items-center gap-3 px-3">
3128
+ <div [class]="contentClasses()">
3129
+ <ng-content />
3130
+ </div>
3131
+
3132
+ @if (showToggle()) {
3133
+ <button
3134
+ type="button"
3135
+ class="inline-flex h-8 w-8 shrink-0 items-center justify-center bg-transparent text-foreground/80 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
3136
+ [attr.aria-label]="toggleAriaLabel()"
3137
+ [attr.title]="toggleAriaLabel()"
3138
+ data-navigation-header-toggle="true"
3139
+ (click)="toggleCollapsed()">
3140
+ <Icon data-navigation-header-toggle-icon="true" [name]="toggleIconName()" [size]="18" />
3141
+ </button>
3142
+ }
3143
+ </div>
3144
+ `, isInline: true, dependencies: [{ kind: "component", type: IconComponent, selector: "Icon", inputs: ["name", "class", "size", "fill", "weight", "grade", "opticalSize"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3145
+ }
3146
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: NavigationHeaderComponent, decorators: [{
3147
+ type: Component,
3148
+ args: [{
3149
+ selector: 'NavigationHeader',
3150
+ changeDetection: ChangeDetectionStrategy.OnPush,
3151
+ imports: [IconComponent],
3152
+ host: {
3153
+ '[class]': 'hostClasses()',
3154
+ '[attr.data-collapsed]': 'displayCollapsed()',
3155
+ },
3156
+ template: `
3157
+ <div class="flex h-full items-center gap-3 px-3">
3158
+ <div [class]="contentClasses()">
3159
+ <ng-content />
3160
+ </div>
3161
+
3162
+ @if (showToggle()) {
3163
+ <button
3164
+ type="button"
3165
+ class="inline-flex h-8 w-8 shrink-0 items-center justify-center bg-transparent text-foreground/80 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
3166
+ [attr.aria-label]="toggleAriaLabel()"
3167
+ [attr.title]="toggleAriaLabel()"
3168
+ data-navigation-header-toggle="true"
3169
+ (click)="toggleCollapsed()">
3170
+ <Icon data-navigation-header-toggle-icon="true" [name]="toggleIconName()" [size]="18" />
3171
+ </button>
3172
+ }
3173
+ </div>
3174
+ `,
3175
+ }]
3176
+ }], propDecorators: { toggle: [{ type: i0.Input, args: [{ isSignal: true, alias: "toggle", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
3177
+
3178
+ /**
3179
+ * Type sidebar vertikal untuk `<Navigation>`. Slot `NavigationHeader` dan
3180
+ * `NavigationFooter` opsional; `NavigationContent` selalu dirender (default
3181
+ * otomatis bila tidak diproyeksikan).
3182
+ */
3183
+ class NavigationSidebarComponent {
3184
+ container = inject(NavigationContainerComponent);
3185
+ destroyRef = inject(DestroyRef);
3186
+ position = input(null, /* @ts-ignore */
3187
+ ...(ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []));
3188
+ collapsed = input(null, /* @ts-ignore */
3189
+ ...(ngDevMode ? [{ debugName: "collapsed" }] : /* istanbul ignore next */ []));
3190
+ collapse = input(false, { ...(ngDevMode ? { debugName: "collapse" } : /* istanbul ignore next */ {}), alias: 'nav-sidebar-collapse', transform: booleanAttribute });
3191
+ previewExpanded = input(false, /* @ts-ignore */
3192
+ ...(ngDevMode ? [{ debugName: "previewExpanded" }] : /* istanbul ignore next */ []));
3193
+ class = input('', { ...(ngDevMode ? { debugName: "class" } : /* istanbul ignore next */ {}), alias: 'class' });
3194
+ headerSlot = contentChild(NavigationHeaderComponent, /* @ts-ignore */
3195
+ ...(ngDevMode ? [{ debugName: "headerSlot" }] : /* istanbul ignore next */ []));
3196
+ contentSlot = contentChild(NavigationContentComponent, /* @ts-ignore */
3197
+ ...(ngDevMode ? [{ debugName: "contentSlot" }] : /* istanbul ignore next */ []));
3198
+ footerSlot = contentChild(NavigationFooterComponent, /* @ts-ignore */
3199
+ ...(ngDevMode ? [{ debugName: "footerSlot" }] : /* istanbul ignore next */ []));
3200
+ hostClasses = computed(() => cn(this.container.shellClasses(), this.class()), /* @ts-ignore */
3201
+ ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
3202
+ constructor() {
3203
+ const config = {
3204
+ orientation: 'vertical',
3205
+ type: 'sidebar',
3206
+ position: this.position,
3207
+ collapsed: this.collapsed,
3208
+ sidebarCollapse: this.collapse,
3209
+ previewExpanded: this.previewExpanded,
3210
+ };
3211
+ this.container.registerType(config);
3212
+ this.destroyRef.onDestroy(() => this.container.unregisterType(config));
3213
+ }
3214
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: NavigationSidebarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3215
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: NavigationSidebarComponent, isStandalone: true, selector: "NavigationSidebar", inputs: { position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, collapse: { classPropertyName: "collapse", publicName: "nav-sidebar-collapse", isSignal: true, isRequired: false, transformFunction: null }, previewExpanded: { classPropertyName: "previewExpanded", publicName: "previewExpanded", isSignal: true, isRequired: false, transformFunction: null }, class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "hostClasses()" } }, queries: [{ propertyName: "headerSlot", first: true, predicate: NavigationHeaderComponent, descendants: true, isSignal: true }, { propertyName: "contentSlot", first: true, predicate: NavigationContentComponent, descendants: true, isSignal: true }, { propertyName: "footerSlot", first: true, predicate: NavigationFooterComponent, descendants: true, isSignal: true }], ngImport: i0, template: `
3216
+ @if (headerSlot()) {
3217
+ <ng-content select="NavigationHeader" />
3218
+ }
3219
+
3220
+ @if (contentSlot()) {
3221
+ <ng-content select="NavigationContent" />
3222
+ } @else {
3223
+ <NavigationContent />
3224
+ }
3225
+
3226
+ @if (footerSlot()) {
3227
+ <ng-content select="NavigationFooter" />
3228
+ }
3229
+ `, isInline: true, dependencies: [{ kind: "component", type: NavigationContentComponent, selector: "NavigationContent", inputs: ["class"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3230
+ }
3231
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: NavigationSidebarComponent, decorators: [{
3232
+ type: Component,
3233
+ args: [{
3234
+ selector: 'NavigationSidebar',
3235
+ changeDetection: ChangeDetectionStrategy.OnPush,
3236
+ imports: [NavigationContentComponent],
3237
+ host: {
3238
+ '[class]': 'hostClasses()',
3239
+ },
3240
+ template: `
3241
+ @if (headerSlot()) {
3242
+ <ng-content select="NavigationHeader" />
3243
+ }
3244
+
3245
+ @if (contentSlot()) {
3246
+ <ng-content select="NavigationContent" />
3247
+ } @else {
3248
+ <NavigationContent />
3249
+ }
3250
+
3251
+ @if (footerSlot()) {
3252
+ <ng-content select="NavigationFooter" />
3253
+ }
3254
+ `,
3255
+ }]
3256
+ }], ctorParameters: () => [], propDecorators: { position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }], collapse: [{ type: i0.Input, args: [{ isSignal: true, alias: "nav-sidebar-collapse", required: false }] }], previewExpanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "previewExpanded", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], headerSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NavigationHeaderComponent), { isSignal: true }] }], contentSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NavigationContentComponent), { isSignal: true }] }], footerSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NavigationFooterComponent), { isSignal: true }] }] } });
3257
+
3258
+ /**
3259
+ * Type dockbar vertikal (rail ikon + aside) untuk `<Navigation>`. Slot
3260
+ * `NavigationHeader` dan `NavigationFooter` opsional; `NavigationContent`
3261
+ * selalu dirender (default otomatis bila tidak diproyeksikan).
3262
+ */
3263
+ class NavigationDockbarComponent {
3264
+ container = inject(NavigationContainerComponent);
3265
+ destroyRef = inject(DestroyRef);
3266
+ mode = input(null, /* @ts-ignore */
3267
+ ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
3268
+ position = input(null, /* @ts-ignore */
3269
+ ...(ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []));
3270
+ class = input('', { ...(ngDevMode ? { debugName: "class" } : /* istanbul ignore next */ {}), alias: 'class' });
3271
+ headerSlot = contentChild(NavigationHeaderComponent, /* @ts-ignore */
3272
+ ...(ngDevMode ? [{ debugName: "headerSlot" }] : /* istanbul ignore next */ []));
3273
+ contentSlot = contentChild(NavigationContentComponent, /* @ts-ignore */
3274
+ ...(ngDevMode ? [{ debugName: "contentSlot" }] : /* istanbul ignore next */ []));
3275
+ footerSlot = contentChild(NavigationFooterComponent, /* @ts-ignore */
3276
+ ...(ngDevMode ? [{ debugName: "footerSlot" }] : /* istanbul ignore next */ []));
3277
+ hostClasses = computed(() => cn(this.container.shellClasses(), this.class()), /* @ts-ignore */
3278
+ ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
3279
+ constructor() {
3280
+ const config = {
3281
+ orientation: 'vertical',
3282
+ type: 'dockbar',
3283
+ position: this.position,
3284
+ dockbarMode: this.mode,
3285
+ };
3286
+ this.container.registerType(config);
3287
+ this.destroyRef.onDestroy(() => this.container.unregisterType(config));
3288
+ }
3289
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: NavigationDockbarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3290
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: NavigationDockbarComponent, isStandalone: true, selector: "NavigationDockbar", inputs: { mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "hostClasses()" } }, queries: [{ propertyName: "headerSlot", first: true, predicate: NavigationHeaderComponent, descendants: true, isSignal: true }, { propertyName: "contentSlot", first: true, predicate: NavigationContentComponent, descendants: true, isSignal: true }, { propertyName: "footerSlot", first: true, predicate: NavigationFooterComponent, descendants: true, isSignal: true }], ngImport: i0, template: `
3291
+ @if (headerSlot()) {
3292
+ <ng-content select="NavigationHeader" />
3293
+ }
3294
+
3295
+ @if (contentSlot()) {
3296
+ <ng-content select="NavigationContent" />
3297
+ } @else {
3298
+ <NavigationContent />
3299
+ }
3300
+
3301
+ @if (footerSlot()) {
3302
+ <ng-content select="NavigationFooter" />
3303
+ }
3304
+ `, isInline: true, dependencies: [{ kind: "component", type: NavigationContentComponent, selector: "NavigationContent", inputs: ["class"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3305
+ }
3306
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: NavigationDockbarComponent, decorators: [{
3307
+ type: Component,
3308
+ args: [{
3309
+ selector: 'NavigationDockbar',
3310
+ changeDetection: ChangeDetectionStrategy.OnPush,
3311
+ imports: [NavigationContentComponent],
3312
+ host: {
3313
+ '[class]': 'hostClasses()',
3314
+ },
3315
+ template: `
3316
+ @if (headerSlot()) {
3317
+ <ng-content select="NavigationHeader" />
3318
+ }
3319
+
3320
+ @if (contentSlot()) {
3321
+ <ng-content select="NavigationContent" />
3322
+ } @else {
3323
+ <NavigationContent />
3324
+ }
3325
+
3326
+ @if (footerSlot()) {
3327
+ <ng-content select="NavigationFooter" />
3328
+ }
3329
+ `,
3330
+ }]
3331
+ }], ctorParameters: () => [], propDecorators: { mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], headerSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NavigationHeaderComponent), { isSignal: true }] }], contentSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NavigationContentComponent), { isSignal: true }] }], footerSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NavigationFooterComponent), { isSignal: true }] }] } });
3332
+
3333
+ /**
3334
+ * Type navbar horizontal untuk `<Navigation>`. Slot `NavigationHeader`
3335
+ * dan `NavigationFooter` opsional; `NavigationContent` selalu dirender
3336
+ * (default otomatis bila tidak diproyeksikan).
3337
+ */
3338
+ class NavigationNavbarComponent {
3339
+ container = inject(NavigationContainerComponent);
3340
+ destroyRef = inject(DestroyRef);
3341
+ /** Posisi bar terhadap konten layout: `top` (default) atau `bottom` — panel grid membuka ke arah sebaliknya. */
3342
+ position = input(null, { ...(ngDevMode ? { debugName: "position" } : /* istanbul ignore next */ {}), alias: 'nav-position' });
3343
+ typeStyle = input('default', { ...(ngDevMode ? { debugName: "typeStyle" } : /* istanbul ignore next */ {}), alias: 'nav-type-style' });
3344
+ class = input('', { ...(ngDevMode ? { debugName: "class" } : /* istanbul ignore next */ {}), alias: 'class' });
3345
+ headerSlot = contentChild(NavigationHeaderComponent, /* @ts-ignore */
3346
+ ...(ngDevMode ? [{ debugName: "headerSlot" }] : /* istanbul ignore next */ []));
3347
+ contentSlot = contentChild(NavigationContentComponent, /* @ts-ignore */
3348
+ ...(ngDevMode ? [{ debugName: "contentSlot" }] : /* istanbul ignore next */ []));
3349
+ footerSlot = contentChild(NavigationFooterComponent, /* @ts-ignore */
3350
+ ...(ngDevMode ? [{ debugName: "footerSlot" }] : /* istanbul ignore next */ []));
3351
+ hostClasses = computed(() => cn(this.container.shellClasses(), this.class()), /* @ts-ignore */
3352
+ ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
3353
+ constructor() {
3354
+ const config = {
3355
+ orientation: 'horizontal',
3356
+ type: 'navbar',
3357
+ position: this.position,
3358
+ typeStyle: this.typeStyle,
3359
+ };
3360
+ this.container.registerType(config);
3361
+ this.destroyRef.onDestroy(() => this.container.unregisterType(config));
3362
+ }
3363
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: NavigationNavbarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3364
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: NavigationNavbarComponent, isStandalone: true, selector: "NavigationNavbar", inputs: { position: { classPropertyName: "position", publicName: "nav-position", isSignal: true, isRequired: false, transformFunction: null }, typeStyle: { classPropertyName: "typeStyle", publicName: "nav-type-style", isSignal: true, isRequired: false, transformFunction: null }, class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "hostClasses()" } }, queries: [{ propertyName: "headerSlot", first: true, predicate: NavigationHeaderComponent, descendants: true, isSignal: true }, { propertyName: "contentSlot", first: true, predicate: NavigationContentComponent, descendants: true, isSignal: true }, { propertyName: "footerSlot", first: true, predicate: NavigationFooterComponent, descendants: true, isSignal: true }], ngImport: i0, template: `
3365
+ @if (headerSlot()) {
3366
+ <ng-content select="NavigationHeader" />
3367
+ }
3368
+
3369
+ @if (contentSlot()) {
3370
+ <ng-content select="NavigationContent" />
3371
+ } @else {
3372
+ <NavigationContent />
3373
+ }
3374
+
3375
+ @if (footerSlot()) {
3376
+ <ng-content select="NavigationFooter" />
3377
+ }
3378
+ `, isInline: true, dependencies: [{ kind: "component", type: NavigationContentComponent, selector: "NavigationContent", inputs: ["class"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3379
+ }
3380
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: NavigationNavbarComponent, decorators: [{
3381
+ type: Component,
3382
+ args: [{
3383
+ selector: 'NavigationNavbar',
3384
+ changeDetection: ChangeDetectionStrategy.OnPush,
3385
+ imports: [NavigationContentComponent],
3386
+ host: {
3387
+ '[class]': 'hostClasses()',
3388
+ },
3389
+ template: `
3390
+ @if (headerSlot()) {
3391
+ <ng-content select="NavigationHeader" />
3392
+ }
3393
+
3394
+ @if (contentSlot()) {
3395
+ <ng-content select="NavigationContent" />
3396
+ } @else {
3397
+ <NavigationContent />
3398
+ }
3399
+
3400
+ @if (footerSlot()) {
3401
+ <ng-content select="NavigationFooter" />
3402
+ }
3403
+ `,
3404
+ }]
3405
+ }], ctorParameters: () => [], propDecorators: { position: [{ type: i0.Input, args: [{ isSignal: true, alias: "nav-position", required: false }] }], typeStyle: [{ type: i0.Input, args: [{ isSignal: true, alias: "nav-type-style", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], headerSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NavigationHeaderComponent), { isSignal: true }] }], contentSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NavigationContentComponent), { isSignal: true }] }], footerSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NavigationFooterComponent), { isSignal: true }] }] } });
3406
+
3407
+ /**
3408
+ * Type flyout horizontal (trigger + panel menu) untuk `<Navigation>`.
3409
+ * Slot `NavigationHeader` dan `NavigationFooter` opsional; `NavigationContent`
3410
+ * selalu dirender (default otomatis bila tidak diproyeksikan).
3411
+ */
3412
+ class NavigationFlyoutComponent {
3413
+ container = inject(NavigationContainerComponent);
3414
+ destroyRef = inject(DestroyRef);
3415
+ label = input('Menu', /* @ts-ignore */
3416
+ ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
3417
+ /** Nama Material Symbols untuk trigger (mis. `apps`, `menu`); `null` = label saja. */
3418
+ icon = input(null, /* @ts-ignore */
3419
+ ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
3420
+ /** Trigger hanya menampilkan ikon; label tetap dipakai sebagai aria-label/title. */
3421
+ iconOnly = input(false, { ...(ngDevMode ? { debugName: "iconOnly" } : /* istanbul ignore next */ {}), alias: 'icon-only', transform: booleanAttribute });
3422
+ /** Penempatan ikon relatif terhadap label: `start` (default) atau `end`. */
3423
+ iconPosition = input('start', { ...(ngDevMode ? { debugName: "iconPosition" } : /* istanbul ignore next */ {}), alias: 'icon-position' });
3424
+ /** Posisi bar terhadap konten layout: `top` (default) atau `bottom` — panel membuka ke arah sebaliknya. */
3425
+ position = input(null, { ...(ngDevMode ? { debugName: "position" } : /* istanbul ignore next */ {}), alias: 'nav-position' });
3426
+ typeStyle = input('default', { ...(ngDevMode ? { debugName: "typeStyle" } : /* istanbul ignore next */ {}), alias: 'nav-type-style' });
3427
+ class = input('', { ...(ngDevMode ? { debugName: "class" } : /* istanbul ignore next */ {}), alias: 'class' });
3428
+ headerSlot = contentChild(NavigationHeaderComponent, /* @ts-ignore */
3429
+ ...(ngDevMode ? [{ debugName: "headerSlot" }] : /* istanbul ignore next */ []));
3430
+ contentSlot = contentChild(NavigationContentComponent, /* @ts-ignore */
3431
+ ...(ngDevMode ? [{ debugName: "contentSlot" }] : /* istanbul ignore next */ []));
3432
+ footerSlot = contentChild(NavigationFooterComponent, /* @ts-ignore */
3433
+ ...(ngDevMode ? [{ debugName: "footerSlot" }] : /* istanbul ignore next */ []));
3434
+ hostClasses = computed(() => cn(this.container.shellClasses(), this.class()), /* @ts-ignore */
3435
+ ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
3436
+ constructor() {
3437
+ const config = {
3438
+ orientation: 'horizontal',
3439
+ type: 'flyout',
3440
+ position: this.position,
3441
+ flyoutLabel: this.label,
3442
+ flyoutIcon: this.icon,
3443
+ flyoutIconOnly: this.iconOnly,
3444
+ flyoutIconPosition: this.iconPosition,
3445
+ typeStyle: this.typeStyle,
3446
+ };
3447
+ this.container.registerType(config);
3448
+ this.destroyRef.onDestroy(() => this.container.unregisterType(config));
3449
+ }
3450
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: NavigationFlyoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3451
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: NavigationFlyoutComponent, isStandalone: true, selector: "NavigationFlyout", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, iconOnly: { classPropertyName: "iconOnly", publicName: "icon-only", isSignal: true, isRequired: false, transformFunction: null }, iconPosition: { classPropertyName: "iconPosition", publicName: "icon-position", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "nav-position", isSignal: true, isRequired: false, transformFunction: null }, typeStyle: { classPropertyName: "typeStyle", publicName: "nav-type-style", isSignal: true, isRequired: false, transformFunction: null }, class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "hostClasses()" } }, queries: [{ propertyName: "headerSlot", first: true, predicate: NavigationHeaderComponent, descendants: true, isSignal: true }, { propertyName: "contentSlot", first: true, predicate: NavigationContentComponent, descendants: true, isSignal: true }, { propertyName: "footerSlot", first: true, predicate: NavigationFooterComponent, descendants: true, isSignal: true }], ngImport: i0, template: `
3452
+ @if (headerSlot()) {
3453
+ <ng-content select="NavigationHeader" />
3454
+ }
3455
+
3456
+ @if (contentSlot()) {
3457
+ <ng-content select="NavigationContent" />
3458
+ } @else {
3459
+ <NavigationContent />
3460
+ }
3461
+
3462
+ @if (footerSlot()) {
3463
+ <ng-content select="NavigationFooter" />
3464
+ }
3465
+ `, isInline: true, dependencies: [{ kind: "component", type: NavigationContentComponent, selector: "NavigationContent", inputs: ["class"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3466
+ }
3467
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: NavigationFlyoutComponent, decorators: [{
3468
+ type: Component,
3469
+ args: [{
3470
+ selector: 'NavigationFlyout',
3471
+ changeDetection: ChangeDetectionStrategy.OnPush,
3472
+ imports: [NavigationContentComponent],
3473
+ host: {
3474
+ '[class]': 'hostClasses()',
3475
+ },
3476
+ template: `
3477
+ @if (headerSlot()) {
3478
+ <ng-content select="NavigationHeader" />
3479
+ }
3480
+
3481
+ @if (contentSlot()) {
3482
+ <ng-content select="NavigationContent" />
3483
+ } @else {
3484
+ <NavigationContent />
3485
+ }
3486
+
3487
+ @if (footerSlot()) {
3488
+ <ng-content select="NavigationFooter" />
3489
+ }
3490
+ `,
3491
+ }]
3492
+ }], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], iconOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon-only", required: false }] }], iconPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon-position", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "nav-position", required: false }] }], typeStyle: [{ type: i0.Input, args: [{ isSignal: true, alias: "nav-type-style", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], headerSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NavigationHeaderComponent), { isSignal: true }] }], contentSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NavigationContentComponent), { isSignal: true }] }], footerSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NavigationFooterComponent), { isSignal: true }] }] } });
3493
+
3494
+ /**
3495
+ * Tampilan compact aktif saat sidebar collapsible sedang collapsed, atau saat
3496
+ * type dockbar aktif — rail dockbar selalu selebar ikon (`w-16`) sehingga
3497
+ * konten expanded tidak pernah muat.
3498
+ */
3499
+ function isCompactDisplay(shell) {
3500
+ const state = shell.displayState();
3501
+ if (state.orientation !== 'vertical') {
3502
+ return false;
3503
+ }
3504
+ if (state.type === 'dockbar') {
3505
+ return true;
3506
+ }
3507
+ return shell.collapseEnabled() && state.collapsed;
3508
+ }
3509
+ class NavigationCollapseRootDirective {
3510
+ shell = inject(NAVIGATION_SHELL);
3511
+ collapseEnabled = computed(() => {
3512
+ const state = this.shell.displayState();
3513
+ return state.orientation === 'vertical' && (this.shell.collapseEnabled() || state.type === 'dockbar');
3514
+ }, /* @ts-ignore */
3515
+ ...(ngDevMode ? [{ debugName: "collapseEnabled" }] : /* istanbul ignore next */ []));
3516
+ displayCollapsed = computed(() => isCompactDisplay(this.shell), /* @ts-ignore */
3517
+ ...(ngDevMode ? [{ debugName: "displayCollapsed" }] : /* istanbul ignore next */ []));
3518
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: NavigationCollapseRootDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
3519
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: NavigationCollapseRootDirective, isStandalone: true, selector: "[NavigationCollapseRoot]", host: { properties: { "class.w-full": "collapseEnabled()", "class.justify-center": "displayCollapsed()" } }, ngImport: i0 });
3520
+ }
3521
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: NavigationCollapseRootDirective, decorators: [{
3522
+ type: Directive,
3523
+ args: [{
3524
+ selector: '[NavigationCollapseRoot]',
3525
+ host: {
3526
+ '[class.w-full]': 'collapseEnabled()',
3527
+ '[class.justify-center]': 'displayCollapsed()',
3528
+ },
3529
+ }]
3530
+ }] });
3531
+ class NavigationCollapseExpandedDirective {
3532
+ shell = inject(NAVIGATION_SHELL);
3533
+ templateRef = inject((TemplateRef));
3534
+ viewContainer = inject(ViewContainerRef);
3535
+ hasView = false;
3536
+ shouldRender = computed(() => !isCompactDisplay(this.shell), /* @ts-ignore */
3537
+ ...(ngDevMode ? [{ debugName: "shouldRender" }] : /* istanbul ignore next */ []));
3538
+ constructor() {
3539
+ effect(() => {
3540
+ if (this.shouldRender()) {
3541
+ if (!this.hasView) {
3542
+ this.viewContainer.createEmbeddedView(this.templateRef);
3543
+ this.hasView = true;
3544
+ }
3545
+ return;
3546
+ }
3547
+ if (this.hasView) {
3548
+ this.viewContainer.clear();
3549
+ this.hasView = false;
3550
+ }
3551
+ });
3552
+ }
3553
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: NavigationCollapseExpandedDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
3554
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: NavigationCollapseExpandedDirective, isStandalone: true, selector: "[NavigationCollapseExpanded]", ngImport: i0 });
3555
+ }
3556
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: NavigationCollapseExpandedDirective, decorators: [{
3557
+ type: Directive,
3558
+ args: [{
3559
+ selector: '[NavigationCollapseExpanded]',
3560
+ }]
3561
+ }], ctorParameters: () => [] });
3562
+
3563
+ /**
3564
+ * Generated bundle index. Do not edit.
3565
+ */
3566
+
3567
+ export { NavigationCollapseExpandedDirective, NavigationCollapseRootDirective, NavigationContainerComponent, NavigationContentComponent, NavigationDockbarComponent, NavigationFlyoutComponent, NavigationFooterComponent, NavigationHeaderComponent, NavigationIconDirective, NavigationNavbarComponent, NavigationSidebarComponent, normalizeUiNavItems };
3568
+ //# sourceMappingURL=ojiepermana-angular-navigation.mjs.map