@ojiepermana/angular-navigation 22.0.43 → 22.0.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,569 @@
1
+ import { NgTemplateOutlet } from '@angular/common';
2
+ import * as i0 from '@angular/core';
3
+ import { inject, input, output, computed, signal, Component } from '@angular/core';
4
+ import { RouterLink, RouterLinkActive } from '@angular/router';
5
+ import { IconComponent } from '@ojiepermana/angular-component/icon';
6
+ import { cn } from '@ojiepermana/angular-component/utils';
7
+ import { N as NavigationItemComponent } from './ojiepermana-angular-navigation-ojiepermana-angular-navigation-SlMGlTuA.mjs';
8
+ import { NavigationService } from '@ojiepermana/angular-navigation/service';
9
+
10
+ /**
11
+ * Grid entry ala flyout: children sebuah branch dirender sebagai entry
12
+ * icon-box + title/subtitle dalam grid responsif (1/sm:2/md:3/lg:4, dibatasi
13
+ * `columns`); entry ber-children tampil terbuka secara default dengan collapse
14
+ * vertical yang icon-nya sejajar satu garis dengan icon induk. Dipakai oleh
15
+ * flyout dan panel mega navbar.
16
+ */
17
+ class NavigationEntryGridComponent {
18
+ nav = inject(NavigationService);
19
+ navId = input('default', /* @ts-ignore */
20
+ ...(ngDevMode ? [{ debugName: "navId" }] : /* istanbul ignore next */ []));
21
+ branch = input.required(/* @ts-ignore */
22
+ ...(ngDevMode ? [{ debugName: "branch" }] : /* istanbul ignore next */ []));
23
+ typeStyle = input('flat', /* @ts-ignore */
24
+ ...(ngDevMode ? [{ debugName: "typeStyle" }] : /* istanbul ignore next */ []));
25
+ itemClass = input('', /* @ts-ignore */
26
+ ...(ngDevMode ? [{ debugName: "itemClass" }] : /* istanbul ignore next */ []));
27
+ activeIds = input(new Set(), /* @ts-ignore */
28
+ ...(ngDevMode ? [{ debugName: "activeIds" }] : /* istanbul ignore next */ []));
29
+ activeUrl = input(null, /* @ts-ignore */
30
+ ...(ngDevMode ? [{ debugName: "activeUrl" }] : /* istanbul ignore next */ []));
31
+ iconTemplate = input(undefined, /* @ts-ignore */
32
+ ...(ngDevMode ? [{ debugName: "iconTemplate" }] : /* istanbul ignore next */ []));
33
+ collapseTree = input('stairs', /* @ts-ignore */
34
+ ...(ngDevMode ? [{ debugName: "collapseTree" }] : /* istanbul ignore next */ []));
35
+ openedIds = input([], /* @ts-ignore */
36
+ ...(ngDevMode ? [{ debugName: "openedIds" }] : /* istanbul ignore next */ []));
37
+ openedIdsChange = output();
38
+ itemSelected = output();
39
+ isBorderRail = computed(() => this.typeStyle() === 'border-rail', /* @ts-ignore */
40
+ ...(ngDevMode ? [{ debugName: "isBorderRail" }] : /* istanbul ignore next */ []));
41
+ /** Kelas tambahan untuk NavigationItem bersarang saat style border-rail aktif. */
42
+ navItemClass = computed(() => cn(this.itemClass(), this.isBorderRail() && 'rounded-none hover:bg-transparent hover:text-primary'), /* @ts-ignore */
43
+ ...(ngDevMode ? [{ debugName: "navItemClass" }] : /* istanbul ignore next */ []));
44
+ /**
45
+ * Entry ber-children tampil TERBUKA secara default; set ini hanya menyimpan
46
+ * entry yang sengaja di-collapse pengguna. Instance grid dibuat ulang setiap
47
+ * panel dibuka sehingga state kembali terbuka semua.
48
+ */
49
+ collapsedEntryKeys = signal(new Set(), /* @ts-ignore */
50
+ ...(ngDevMode ? [{ debugName: "collapsedEntryKeys" }] : /* istanbul ignore next */ []));
51
+ isItemActive(item) {
52
+ return this.nav.isItemActive(item, this.activeIds(), this.activeUrl());
53
+ }
54
+ isEntryOpen(item) {
55
+ return !this.collapsedEntryKeys().has(item.stateId);
56
+ }
57
+ toggleEntry(item) {
58
+ if (item.disabled) {
59
+ return;
60
+ }
61
+ const next = new Set(this.collapsedEntryKeys());
62
+ if (next.has(item.stateId)) {
63
+ next.delete(item.stateId);
64
+ }
65
+ else {
66
+ next.add(item.stateId);
67
+ }
68
+ this.collapsedEntryKeys.set(next);
69
+ }
70
+ selectEntry(item, event) {
71
+ if (item.disabled) {
72
+ event.preventDefault();
73
+ event.stopPropagation();
74
+ return;
75
+ }
76
+ item.action?.(item.source);
77
+ this.itemSelected.emit({
78
+ item: item.source,
79
+ key: item.key,
80
+ type: item.type,
81
+ link: item.link,
82
+ external: !!this.nav.hrefFor(item),
83
+ });
84
+ this.nav.closePanel(this.navId());
85
+ this.nav.closeDrawer(this.navId());
86
+ }
87
+ isRouterItem(item) {
88
+ return this.nav.isRouterItem(item);
89
+ }
90
+ hrefFor(item) {
91
+ return this.nav.hrefFor(item);
92
+ }
93
+ relFor(item) {
94
+ return this.nav.relFor(item);
95
+ }
96
+ routerLinkActiveOptions(item) {
97
+ return this.nav.routerLinkActiveOptions(item);
98
+ }
99
+ iconContext(item, active) {
100
+ return this.nav.iconContext(item, active, 1, 'horizontal');
101
+ }
102
+ fallbackInitials(item) {
103
+ return this.nav.compactFallback(item);
104
+ }
105
+ contentGridClasses(branch) {
106
+ // Skala responsif: mobile 1, sm 2, md 3, lg ke atas 4; `columns` membatasi maksimumnya.
107
+ const columns = Math.min(Math.max(branch.columns ?? 4, 1), 4);
108
+ 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');
109
+ }
110
+ entryClasses(item, active) {
111
+ return cn('group/nav nav-text flex w-full items-center gap-3 px-3 py-2.5 text-left transition-colors', 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring', this.isBorderRail()
112
+ ? cn('rounded-none', active ? 'text-primary' : 'text-foreground hover:text-primary')
113
+ : cn('rounded-lg', active
114
+ ? 'bg-accent text-accent-foreground'
115
+ : 'text-foreground hover:bg-accent hover:text-accent-foreground'), item.disabled && 'pointer-events-none opacity-50', item.classes?.wrapper, this.itemClass());
116
+ }
117
+ entryIconClasses(active) {
118
+ if (this.isBorderRail()) {
119
+ // Kotak icon polos; bingkai digambar oleh rail line yang menjorok melewati sudut.
120
+ 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');
121
+ }
122
+ 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))]');
123
+ }
124
+ entryIconGlyphClasses(item) {
125
+ return cn('text-current', item.classes?.icon);
126
+ }
127
+ entryTitleClasses(item, active) {
128
+ return cn('block truncate font-medium', this.isBorderRail() ? 'text-current' : 'text-foreground', active && 'font-semibold', item.classes?.title);
129
+ }
130
+ entrySubtitleClasses(item) {
131
+ return cn('mt-0.5 block truncate text-xs text-muted-foreground', item.classes?.subtitle);
132
+ }
133
+ entryChevronClasses(item) {
134
+ return cn('shrink-0 self-center text-muted-foreground transition-transform duration-200', this.isEntryOpen(item) && 'rotate-90');
135
+ }
136
+ nestedListClasses() {
137
+ // Children sejajar satu garis vertical dengan icon induk: rail digambar lewat pseudo-element
138
+ // di x = 32px (px-3 entry + setengah icon box w-10); pl-2.5 membuat icon child (px-3 + slot
139
+ // w-5) ber-center tepat di garis yang sama.
140
+ 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()
141
+ ? 'before:border-dashed before:border-border/40'
142
+ : 'before:border-[hsl(var(--border)/var(--opacity-60))]');
143
+ }
144
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: NavigationEntryGridComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
145
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.4", 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: `
146
+ @let branchItem = branch();
147
+
148
+ <div
149
+ role="group"
150
+ [id]="branchItem.panelId"
151
+ [attr.aria-label]="branchItem.title || null"
152
+ [attr.data-navigation-flyout-content]="branchItem.key"
153
+ class="p-2 md:p-3"
154
+ >
155
+ <ul role="none" [class]="contentGridClasses(branchItem)">
156
+ @for (child of branchItem.children; track child.key) {
157
+ @switch (child.type) {
158
+ @case ('divider') {
159
+ <li role="separator" class="col-span-full my-1 h-px bg-border"></li>
160
+ }
161
+ @case ('spacer') {}
162
+ @default {
163
+ @if (child.children.length > 0) {
164
+ <li role="none" class="min-w-0" [attr.data-navigation-item-key]="child.key">
165
+ <button
166
+ type="button"
167
+ role="menuitem"
168
+ data-navigation-flyout-entry="true"
169
+ [class]="entryClasses(child, isItemActive(child))"
170
+ [attr.aria-expanded]="isEntryOpen(child)"
171
+ [attr.aria-controls]="child.panelId"
172
+ [disabled]="child.disabled || null"
173
+ (click)="toggleEntry(child)"
174
+ >
175
+ <ng-container
176
+ [ngTemplateOutlet]="entryContent"
177
+ [ngTemplateOutletContext]="{ $implicit: child, active: isItemActive(child) }"
178
+ />
179
+ <Icon name="chevron_right" [size]="16" [class]="entryChevronClasses(child)" />
180
+ </button>
181
+
182
+ @if (isEntryOpen(child)) {
183
+ <ul [id]="child.panelId" role="none" [class]="nestedListClasses()">
184
+ @for (nested of child.children; track nested.key) {
185
+ <li
186
+ NavigationItem
187
+ [navId]="navId()"
188
+ [item]="nested"
189
+ [level]="2"
190
+ [orientation]="'horizontal'"
191
+ [compact]="false"
192
+ [itemClass]="navItemClass()"
193
+ [activeIds]="activeIds()"
194
+ [activeUrl]="activeUrl()"
195
+ [iconTemplate]="iconTemplate()"
196
+ [collapseTree]="collapseTree()"
197
+ [openedIds]="openedIds()"
198
+ (openedIdsChange)="openedIdsChange.emit($event)"
199
+ (itemSelected)="itemSelected.emit($event)"
200
+ ></li>
201
+ }
202
+ </ul>
203
+ }
204
+ </li>
205
+ } @else {
206
+ <li role="none" class="min-w-0" [attr.data-navigation-item-key]="child.key">
207
+ @if (isRouterItem(child)) {
208
+ <a
209
+ role="menuitem"
210
+ data-navigation-flyout-entry="true"
211
+ [class]="entryClasses(child, routerActive.isActive || isItemActive(child))"
212
+ [routerLink]="child.link ?? null"
213
+ [queryParams]="child.queryParams ?? null"
214
+ [queryParamsHandling]="child.queryParamsHandling ?? null"
215
+ [fragment]="child.fragment ?? undefined"
216
+ [preserveFragment]="child.preserveFragment ?? false"
217
+ [target]="child.target ?? undefined"
218
+ routerLinkActive
219
+ #routerActive="routerLinkActive"
220
+ [routerLinkActiveOptions]="routerLinkActiveOptions(child)"
221
+ [attr.aria-current]="
222
+ routerActive.isActive || isItemActive(child) ? 'page' : null
223
+ "
224
+ [attr.aria-disabled]="child.disabled || null"
225
+ (click)="selectEntry(child, $event)"
226
+ >
227
+ <ng-container
228
+ [ngTemplateOutlet]="entryContent"
229
+ [ngTemplateOutletContext]="{
230
+ $implicit: child,
231
+ active: routerActive.isActive || isItemActive(child),
232
+ }"
233
+ />
234
+ </a>
235
+ } @else if (hrefFor(child)) {
236
+ <a
237
+ role="menuitem"
238
+ data-navigation-flyout-entry="true"
239
+ [class]="entryClasses(child, isItemActive(child))"
240
+ [attr.href]="hrefFor(child)"
241
+ [attr.target]="child.target ?? (child.externalLink ? '_blank' : null)"
242
+ [attr.rel]="relFor(child)"
243
+ [attr.aria-current]="isItemActive(child) ? 'page' : null"
244
+ [attr.aria-disabled]="child.disabled || null"
245
+ (click)="selectEntry(child, $event)"
246
+ >
247
+ <ng-container
248
+ [ngTemplateOutlet]="entryContent"
249
+ [ngTemplateOutletContext]="{
250
+ $implicit: child,
251
+ active: isItemActive(child),
252
+ }"
253
+ />
254
+ </a>
255
+ } @else {
256
+ <button
257
+ type="button"
258
+ role="menuitem"
259
+ data-navigation-flyout-entry="true"
260
+ [class]="entryClasses(child, isItemActive(child))"
261
+ [disabled]="child.disabled || null"
262
+ [attr.aria-current]="isItemActive(child) ? 'page' : null"
263
+ (click)="selectEntry(child, $event)"
264
+ >
265
+ <ng-container
266
+ [ngTemplateOutlet]="entryContent"
267
+ [ngTemplateOutletContext]="{
268
+ $implicit: child,
269
+ active: isItemActive(child),
270
+ }"
271
+ />
272
+ </button>
273
+ }
274
+ </li>
275
+ }
276
+ }
277
+ }
278
+ }
279
+ </ul>
280
+ </div>
281
+
282
+ <ng-template #entryContent let-child let-active="active">
283
+ <span [class]="entryIconClasses(active)">
284
+ @if (isBorderRail()) {
285
+ <span
286
+ aria-hidden="true"
287
+ data-navigation-icon-rail="top"
288
+ class="pointer-events-none absolute -inset-x-1.5 top-0 h-px bg-border/50"
289
+ ></span>
290
+ <span
291
+ aria-hidden="true"
292
+ data-navigation-icon-rail="bottom"
293
+ class="pointer-events-none absolute -inset-x-1.5 bottom-0 h-px bg-border/50"
294
+ ></span>
295
+ <span
296
+ aria-hidden="true"
297
+ data-navigation-icon-rail="left"
298
+ class="pointer-events-none absolute -inset-y-1.5 left-0 w-px bg-border/50"
299
+ ></span>
300
+ <span
301
+ aria-hidden="true"
302
+ data-navigation-icon-rail="right"
303
+ class="pointer-events-none absolute -inset-y-1.5 right-0 w-px bg-border/50"
304
+ ></span>
305
+ <span
306
+ aria-hidden="true"
307
+ data-navigation-icon-rail="center-horizontal"
308
+ class="pointer-events-none absolute -inset-x-1.5 top-1/2 border-t border-dashed border-border/40"
309
+ ></span>
310
+ <span
311
+ aria-hidden="true"
312
+ data-navigation-icon-rail="center-vertical"
313
+ class="pointer-events-none absolute -inset-y-1.5 left-1/2 border-l border-dashed border-border/40"
314
+ ></span>
315
+ }
316
+
317
+ @if (child.icon) {
318
+ @if (iconTemplate(); as customIcon) {
319
+ <ng-container
320
+ [ngTemplateOutlet]="customIcon.template"
321
+ [ngTemplateOutletContext]="iconContext(child, active)"
322
+ />
323
+ } @else {
324
+ <Icon [name]="child.icon" [size]="18" [class]="entryIconGlyphClasses(child)" />
325
+ }
326
+ } @else {
327
+ <span aria-hidden="true" class="text-xs font-semibold uppercase">{{
328
+ fallbackInitials(child)
329
+ }}</span>
330
+ }
331
+ </span>
332
+
333
+ <span class="min-w-0 flex-1">
334
+ <span [class]="entryTitleClasses(child, active)">{{ child.title }}</span>
335
+ @if (child.subtitle) {
336
+ <span [class]="entrySubtitleClasses(child)">{{ child.subtitle }}</span>
337
+ }
338
+ </span>
339
+
340
+ @if (child.badge; as badge) {
341
+ <span
342
+ [class]="
343
+ badge.classes ??
344
+ 'nav-badge inline-flex h-5 shrink-0 items-center self-center rounded-full bg-muted px-2 text-muted-foreground'
345
+ "
346
+ >{{ badge.title }}</span
347
+ >
348
+ }
349
+ </ng-template>
350
+ `, 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"] }] });
351
+ }
352
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: NavigationEntryGridComponent, decorators: [{
353
+ type: Component,
354
+ args: [{
355
+ selector: 'NavigationEntryGrid',
356
+ imports: [NgTemplateOutlet, RouterLink, RouterLinkActive, IconComponent, NavigationItemComponent],
357
+ host: {
358
+ class: 'contents',
359
+ },
360
+ template: `
361
+ @let branchItem = branch();
362
+
363
+ <div
364
+ role="group"
365
+ [id]="branchItem.panelId"
366
+ [attr.aria-label]="branchItem.title || null"
367
+ [attr.data-navigation-flyout-content]="branchItem.key"
368
+ class="p-2 md:p-3"
369
+ >
370
+ <ul role="none" [class]="contentGridClasses(branchItem)">
371
+ @for (child of branchItem.children; track child.key) {
372
+ @switch (child.type) {
373
+ @case ('divider') {
374
+ <li role="separator" class="col-span-full my-1 h-px bg-border"></li>
375
+ }
376
+ @case ('spacer') {}
377
+ @default {
378
+ @if (child.children.length > 0) {
379
+ <li role="none" class="min-w-0" [attr.data-navigation-item-key]="child.key">
380
+ <button
381
+ type="button"
382
+ role="menuitem"
383
+ data-navigation-flyout-entry="true"
384
+ [class]="entryClasses(child, isItemActive(child))"
385
+ [attr.aria-expanded]="isEntryOpen(child)"
386
+ [attr.aria-controls]="child.panelId"
387
+ [disabled]="child.disabled || null"
388
+ (click)="toggleEntry(child)"
389
+ >
390
+ <ng-container
391
+ [ngTemplateOutlet]="entryContent"
392
+ [ngTemplateOutletContext]="{ $implicit: child, active: isItemActive(child) }"
393
+ />
394
+ <Icon name="chevron_right" [size]="16" [class]="entryChevronClasses(child)" />
395
+ </button>
396
+
397
+ @if (isEntryOpen(child)) {
398
+ <ul [id]="child.panelId" role="none" [class]="nestedListClasses()">
399
+ @for (nested of child.children; track nested.key) {
400
+ <li
401
+ NavigationItem
402
+ [navId]="navId()"
403
+ [item]="nested"
404
+ [level]="2"
405
+ [orientation]="'horizontal'"
406
+ [compact]="false"
407
+ [itemClass]="navItemClass()"
408
+ [activeIds]="activeIds()"
409
+ [activeUrl]="activeUrl()"
410
+ [iconTemplate]="iconTemplate()"
411
+ [collapseTree]="collapseTree()"
412
+ [openedIds]="openedIds()"
413
+ (openedIdsChange)="openedIdsChange.emit($event)"
414
+ (itemSelected)="itemSelected.emit($event)"
415
+ ></li>
416
+ }
417
+ </ul>
418
+ }
419
+ </li>
420
+ } @else {
421
+ <li role="none" class="min-w-0" [attr.data-navigation-item-key]="child.key">
422
+ @if (isRouterItem(child)) {
423
+ <a
424
+ role="menuitem"
425
+ data-navigation-flyout-entry="true"
426
+ [class]="entryClasses(child, routerActive.isActive || isItemActive(child))"
427
+ [routerLink]="child.link ?? null"
428
+ [queryParams]="child.queryParams ?? null"
429
+ [queryParamsHandling]="child.queryParamsHandling ?? null"
430
+ [fragment]="child.fragment ?? undefined"
431
+ [preserveFragment]="child.preserveFragment ?? false"
432
+ [target]="child.target ?? undefined"
433
+ routerLinkActive
434
+ #routerActive="routerLinkActive"
435
+ [routerLinkActiveOptions]="routerLinkActiveOptions(child)"
436
+ [attr.aria-current]="
437
+ routerActive.isActive || isItemActive(child) ? 'page' : null
438
+ "
439
+ [attr.aria-disabled]="child.disabled || null"
440
+ (click)="selectEntry(child, $event)"
441
+ >
442
+ <ng-container
443
+ [ngTemplateOutlet]="entryContent"
444
+ [ngTemplateOutletContext]="{
445
+ $implicit: child,
446
+ active: routerActive.isActive || isItemActive(child),
447
+ }"
448
+ />
449
+ </a>
450
+ } @else if (hrefFor(child)) {
451
+ <a
452
+ role="menuitem"
453
+ data-navigation-flyout-entry="true"
454
+ [class]="entryClasses(child, isItemActive(child))"
455
+ [attr.href]="hrefFor(child)"
456
+ [attr.target]="child.target ?? (child.externalLink ? '_blank' : null)"
457
+ [attr.rel]="relFor(child)"
458
+ [attr.aria-current]="isItemActive(child) ? 'page' : null"
459
+ [attr.aria-disabled]="child.disabled || null"
460
+ (click)="selectEntry(child, $event)"
461
+ >
462
+ <ng-container
463
+ [ngTemplateOutlet]="entryContent"
464
+ [ngTemplateOutletContext]="{
465
+ $implicit: child,
466
+ active: isItemActive(child),
467
+ }"
468
+ />
469
+ </a>
470
+ } @else {
471
+ <button
472
+ type="button"
473
+ role="menuitem"
474
+ data-navigation-flyout-entry="true"
475
+ [class]="entryClasses(child, isItemActive(child))"
476
+ [disabled]="child.disabled || null"
477
+ [attr.aria-current]="isItemActive(child) ? 'page' : null"
478
+ (click)="selectEntry(child, $event)"
479
+ >
480
+ <ng-container
481
+ [ngTemplateOutlet]="entryContent"
482
+ [ngTemplateOutletContext]="{
483
+ $implicit: child,
484
+ active: isItemActive(child),
485
+ }"
486
+ />
487
+ </button>
488
+ }
489
+ </li>
490
+ }
491
+ }
492
+ }
493
+ }
494
+ </ul>
495
+ </div>
496
+
497
+ <ng-template #entryContent let-child let-active="active">
498
+ <span [class]="entryIconClasses(active)">
499
+ @if (isBorderRail()) {
500
+ <span
501
+ aria-hidden="true"
502
+ data-navigation-icon-rail="top"
503
+ class="pointer-events-none absolute -inset-x-1.5 top-0 h-px bg-border/50"
504
+ ></span>
505
+ <span
506
+ aria-hidden="true"
507
+ data-navigation-icon-rail="bottom"
508
+ class="pointer-events-none absolute -inset-x-1.5 bottom-0 h-px bg-border/50"
509
+ ></span>
510
+ <span
511
+ aria-hidden="true"
512
+ data-navigation-icon-rail="left"
513
+ class="pointer-events-none absolute -inset-y-1.5 left-0 w-px bg-border/50"
514
+ ></span>
515
+ <span
516
+ aria-hidden="true"
517
+ data-navigation-icon-rail="right"
518
+ class="pointer-events-none absolute -inset-y-1.5 right-0 w-px bg-border/50"
519
+ ></span>
520
+ <span
521
+ aria-hidden="true"
522
+ data-navigation-icon-rail="center-horizontal"
523
+ class="pointer-events-none absolute -inset-x-1.5 top-1/2 border-t border-dashed border-border/40"
524
+ ></span>
525
+ <span
526
+ aria-hidden="true"
527
+ data-navigation-icon-rail="center-vertical"
528
+ class="pointer-events-none absolute -inset-y-1.5 left-1/2 border-l border-dashed border-border/40"
529
+ ></span>
530
+ }
531
+
532
+ @if (child.icon) {
533
+ @if (iconTemplate(); as customIcon) {
534
+ <ng-container
535
+ [ngTemplateOutlet]="customIcon.template"
536
+ [ngTemplateOutletContext]="iconContext(child, active)"
537
+ />
538
+ } @else {
539
+ <Icon [name]="child.icon" [size]="18" [class]="entryIconGlyphClasses(child)" />
540
+ }
541
+ } @else {
542
+ <span aria-hidden="true" class="text-xs font-semibold uppercase">{{
543
+ fallbackInitials(child)
544
+ }}</span>
545
+ }
546
+ </span>
547
+
548
+ <span class="min-w-0 flex-1">
549
+ <span [class]="entryTitleClasses(child, active)">{{ child.title }}</span>
550
+ @if (child.subtitle) {
551
+ <span [class]="entrySubtitleClasses(child)">{{ child.subtitle }}</span>
552
+ }
553
+ </span>
554
+
555
+ @if (child.badge; as badge) {
556
+ <span
557
+ [class]="
558
+ badge.classes ??
559
+ 'nav-badge inline-flex h-5 shrink-0 items-center self-center rounded-full bg-muted px-2 text-muted-foreground'
560
+ "
561
+ >{{ badge.title }}</span
562
+ >
563
+ }
564
+ </ng-template>
565
+ `,
566
+ }]
567
+ }], 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"] }] } });
568
+
569
+ export { NavigationEntryGridComponent as N };