@ojiepermana/angular 21.1.28 → 21.2.2

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.
@@ -1,1060 +0,0 @@
1
- import * as i0 from '@angular/core';
2
- import { provideEnvironmentInitializer, inject, makeEnvironmentProviders, viewChild, input, output, computed, ChangeDetectionStrategy, Component } from '@angular/core';
3
- import { provideMaterialLayout, LayoutService } from '@ojiepermana/angular/layout';
4
- import { NavigationService, DEFAULT_NAVIGATION_ID, UiNavIconComponent, TopbarComponent, SidebarComponent } from '@ojiepermana/angular/navigation';
5
- import { provideMaterialTheme, withMaterialDefaults, ThemeService } from '@ojiepermana/angular/theme';
6
- import { cn, AvatarComponent, AvatarFallbackComponent, AvatarImageComponent, ButtonComponent, PopoverContentDirective, PopoverTriggerDirective } from '@ojiepermana/angular/component';
7
- import { RouterOutlet } from '@angular/router';
8
- import { NgTemplateOutlet } from '@angular/common';
9
-
10
- const ETOS_BRAND_NAME = 'etos';
11
- const ETOS_THEME_CONFIG = {
12
- mode: 'light',
13
- brand: ETOS_BRAND_NAME,
14
- };
15
- const ETOS_LAYOUT_CONFIG = {
16
- mode: 'vertical',
17
- };
18
-
19
- function provideEtosTheme(config = {}, ...features) {
20
- return provideMaterialTheme({
21
- ...ETOS_THEME_CONFIG,
22
- ...config,
23
- brand: ETOS_BRAND_NAME,
24
- 'theme-brand': ETOS_BRAND_NAME,
25
- }, ...features);
26
- }
27
- function provideEtosLayout(config = {}) {
28
- return provideMaterialLayout({
29
- ...ETOS_LAYOUT_CONFIG,
30
- ...config,
31
- });
32
- }
33
- function provideEtosBrand(options = {}) {
34
- const providers = [
35
- provideEtosTheme(options.theme, ...(options.materialDefaults === false ? [] : [withMaterialDefaults()])),
36
- provideEtosLayout(options.layout),
37
- ];
38
- const navigation = options.navigation;
39
- if (navigation) {
40
- providers.push(provideEnvironmentInitializer(() => {
41
- inject(NavigationService).registerItems(options.navigationId ?? DEFAULT_NAVIGATION_ID, navigation);
42
- }));
43
- }
44
- return makeEnvironmentProviders(providers);
45
- }
46
-
47
- const THEME_SCHEME_OPTIONS = [
48
- { value: 'light', label: 'Light', icon: 'light_mode' },
49
- { value: 'dark', label: 'Dark', icon: 'dark_mode' },
50
- { value: 'system', label: 'System', icon: 'computer' },
51
- ];
52
- const LAYOUT_MODE_OPTIONS = [
53
- { value: 'horizontal', label: 'Horizontal', icon: 'view_column' },
54
- { value: 'vertical', label: 'Vertical', icon: 'view_sidebar' },
55
- { value: 'empty', label: 'Empty', icon: 'crop_square' },
56
- ];
57
- const LAYOUT_WIDTH_OPTIONS = [
58
- { value: 'full', label: 'Full', icon: 'fit_screen' },
59
- { value: 'container', label: 'Container', icon: 'center_focus_strong' },
60
- { value: 'wide', label: 'Wide', icon: 'width_wide' },
61
- ];
62
- const SWITCHER_PANEL_OVERLAP_OFFSET = -32;
63
- class EtosThemeSwitcherComponent {
64
- theme = inject(ThemeService);
65
- layout = inject(LayoutService);
66
- popoverTrigger = viewChild.required('trigger');
67
- class = input('', ...(ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []));
68
- userInfo = input(null, ...(ngDevMode ? [{ debugName: "userInfo" }] : /* istanbul ignore next */ []));
69
- userName = input('User', ...(ngDevMode ? [{ debugName: "userName" }] : /* istanbul ignore next */ []));
70
- userSubtitle = input('Theme and layout preferences', ...(ngDevMode ? [{ debugName: "userSubtitle" }] : /* istanbul ignore next */ []));
71
- avatarSrc = input(null, ...(ngDevMode ? [{ debugName: "avatarSrc" }] : /* istanbul ignore next */ []));
72
- avatarAlt = input('', ...(ngDevMode ? [{ debugName: "avatarAlt" }] : /* istanbul ignore next */ []));
73
- quickActions = input.required(...(ngDevMode ? [{ debugName: "quickActions" }] : /* istanbul ignore next */ []));
74
- notificationShortcut = input(null, ...(ngDevMode ? [{ debugName: "notificationShortcut" }] : /* istanbul ignore next */ []));
75
- showNotificationShortcut = input(false, ...(ngDevMode ? [{ debugName: "showNotificationShortcut" }] : /* istanbul ignore next */ []));
76
- popoverSide = input(null, ...(ngDevMode ? [{ debugName: "popoverSide" }] : /* istanbul ignore next */ []));
77
- popoverAlign = input(null, ...(ngDevMode ? [{ debugName: "popoverAlign" }] : /* istanbul ignore next */ []));
78
- popoverSideOffset = input(null, ...(ngDevMode ? [{ debugName: "popoverSideOffset" }] : /* istanbul ignore next */ []));
79
- actionSelected = output();
80
- themeMode = this.theme.mode;
81
- themeScheme = this.theme.scheme;
82
- layoutMode = this.layout.mode;
83
- layoutWidth = this.layout.width;
84
- themeSchemeOptions = THEME_SCHEME_OPTIONS;
85
- layoutModeOptions = LAYOUT_MODE_OPTIONS;
86
- layoutWidthOptions = LAYOUT_WIDTH_OPTIONS;
87
- notificationShortcutConfig = computed(() => {
88
- const shortcut = this.notificationShortcut();
89
- if (shortcut) {
90
- return {
91
- value: shortcut.value ?? 'notifications',
92
- icon: shortcut.icon ?? 'notifications',
93
- ariaLabel: shortcut.ariaLabel ?? `Open notifications for ${this.resolvedUserName()}`,
94
- };
95
- }
96
- if (!this.showNotificationShortcut()) {
97
- return null;
98
- }
99
- return {
100
- value: 'notifications',
101
- icon: 'notifications',
102
- ariaLabel: `Open notifications for ${this.resolvedUserName()}`,
103
- };
104
- }, ...(ngDevMode ? [{ debugName: "notificationShortcutConfig" }] : /* istanbul ignore next */ []));
105
- actionOptions = computed(() => {
106
- const shortcutValue = this.notificationShortcutConfig()?.value;
107
- if (!shortcutValue) {
108
- return this.quickActions();
109
- }
110
- return this.quickActions().filter((action) => action.value !== shortcutValue);
111
- }, ...(ngDevMode ? [{ debugName: "actionOptions" }] : /* istanbul ignore next */ []));
112
- hostClasses = computed(() => cn('inline-flex shrink-0 items-center gap-2', this.class()), ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
113
- resolvedPopoverSide = computed(() => this.popoverSide() ?? (this.layoutMode() === 'vertical' ? 'top' : 'bottom'), ...(ngDevMode ? [{ debugName: "resolvedPopoverSide" }] : /* istanbul ignore next */ []));
114
- resolvedPopoverAlign = computed(() => this.popoverAlign() ?? (this.layoutMode() === 'vertical' ? 'start' : 'end'), ...(ngDevMode ? [{ debugName: "resolvedPopoverAlign" }] : /* istanbul ignore next */ []));
115
- resolvedPopoverSideOffset = computed(() => this.popoverSideOffset() ?? SWITCHER_PANEL_OVERLAP_OFFSET, ...(ngDevMode ? [{ debugName: "resolvedPopoverSideOffset" }] : /* istanbul ignore next */ []));
116
- resolvedUserName = computed(() => this.userInfo()?.name?.trim() || this.userName(), ...(ngDevMode ? [{ debugName: "resolvedUserName" }] : /* istanbul ignore next */ []));
117
- resolvedUserSubtitle = computed(() => this.userInfo()?.subtitle ?? this.userSubtitle(), ...(ngDevMode ? [{ debugName: "resolvedUserSubtitle" }] : /* istanbul ignore next */ []));
118
- resolvedAvatarSrc = computed(() => this.userInfo()?.avatarSrc ?? this.avatarSrc(), ...(ngDevMode ? [{ debugName: "resolvedAvatarSrc" }] : /* istanbul ignore next */ []));
119
- resolvedAvatarAlt = computed(() => this.userInfo()?.avatarAlt ?? this.avatarAlt(), ...(ngDevMode ? [{ debugName: "resolvedAvatarAlt" }] : /* istanbul ignore next */ []));
120
- hasAvatar = computed(() => !!this.resolvedAvatarSrc(), ...(ngDevMode ? [{ debugName: "hasAvatar" }] : /* istanbul ignore next */ []));
121
- avatarImageSrc = computed(() => this.resolvedAvatarSrc() ?? '', ...(ngDevMode ? [{ debugName: "avatarImageSrc" }] : /* istanbul ignore next */ []));
122
- avatarAltText = computed(() => this.resolvedAvatarAlt() || `${this.resolvedUserName()} avatar`, ...(ngDevMode ? [{ debugName: "avatarAltText" }] : /* istanbul ignore next */ []));
123
- initials = computed(() => this.toInitials(this.resolvedUserName()), ...(ngDevMode ? [{ debugName: "initials" }] : /* istanbul ignore next */ []));
124
- triggerLabel = computed(() => `Open user info for ${this.resolvedUserName()}`, ...(ngDevMode ? [{ debugName: "triggerLabel" }] : /* istanbul ignore next */ []));
125
- triggerButtonClasses(open) {
126
- return cn('relative h-8 w-8 rounded-full p-0 transition-colors duration-150 hover:bg-muted/40 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background', open && 'bg-muted/50');
127
- }
128
- notificationButtonClasses() {
129
- return cn('h-8 w-8 rounded-[var(--layout-frame-radius)] p-0 text-muted-foreground transition-colors duration-150 hover:bg-muted/50 hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background');
130
- }
131
- segmentedOptionClasses(active) {
132
- return cn('h-10 rounded-[var(--layout-frame-radius)] border border-transparent px-[0.3125rem] py-[0.1875rem] text-foreground transition-colors', active ? 'bg-background shadow-sm' : 'text-muted-foreground hover:bg-background/70');
133
- }
134
- themeOptionClasses(active) {
135
- return this.segmentedOptionClasses(active);
136
- }
137
- themeIconClasses(active) {
138
- return active ? 'text-foreground' : 'text-muted-foreground';
139
- }
140
- actionButtonClasses(tone = 'default') {
141
- return cn('h-12 w-full justify-start gap-2.5 rounded-[var(--layout-frame-radius)] border border-transparent px-2 py-1.5 text-left transition-colors hover:bg-muted/50', tone === 'destructive' && 'hover:bg-destructive/8 focus-visible:ring-destructive/30');
142
- }
143
- actionIconClasses(tone = 'default') {
144
- return tone === 'destructive' ? 'text-destructive' : 'text-foreground';
145
- }
146
- actionLabelClasses(tone = 'default') {
147
- return tone === 'destructive' ? 'text-sm font-medium text-destructive' : 'text-sm font-medium text-foreground';
148
- }
149
- setThemeScheme(mode) {
150
- this.theme.setScheme(mode);
151
- }
152
- setLayoutMode(mode) {
153
- this.layout.setMode(mode);
154
- }
155
- setLayoutWidth(width) {
156
- this.layout.setWidth(width);
157
- }
158
- triggerAction(action) {
159
- this.actionSelected.emit(action);
160
- this.popoverTrigger().close();
161
- }
162
- triggerNotificationAction(event) {
163
- event.stopPropagation();
164
- const shortcut = this.notificationShortcutConfig();
165
- if (!shortcut) {
166
- return;
167
- }
168
- this.actionSelected.emit(shortcut.value);
169
- }
170
- labelForLayoutMode(mode) {
171
- if (mode === 'horizontal') {
172
- return 'Horizontal';
173
- }
174
- if (mode === 'empty') {
175
- return 'Empty';
176
- }
177
- return 'Vertical';
178
- }
179
- labelForLayoutWidth(width) {
180
- if (width === 'container') {
181
- return 'Container';
182
- }
183
- if (width === 'wide') {
184
- return 'Wide';
185
- }
186
- return 'Full';
187
- }
188
- toInitials(name) {
189
- const segments = name.trim().split(/\s+/).filter(Boolean);
190
- if (segments.length === 0) {
191
- return 'UI';
192
- }
193
- if (segments.length === 1) {
194
- return segments[0].slice(0, 2).toUpperCase();
195
- }
196
- return `${segments[0][0] ?? ''}${segments[1][0] ?? ''}`.toUpperCase();
197
- }
198
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: EtosThemeSwitcherComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
199
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.13", type: EtosThemeSwitcherComponent, isStandalone: true, selector: "etos-theme-switcher", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, userInfo: { classPropertyName: "userInfo", publicName: "userInfo", isSignal: true, isRequired: false, transformFunction: null }, userName: { classPropertyName: "userName", publicName: "userName", isSignal: true, isRequired: false, transformFunction: null }, userSubtitle: { classPropertyName: "userSubtitle", publicName: "userSubtitle", isSignal: true, isRequired: false, transformFunction: null }, avatarSrc: { classPropertyName: "avatarSrc", publicName: "avatarSrc", isSignal: true, isRequired: false, transformFunction: null }, avatarAlt: { classPropertyName: "avatarAlt", publicName: "avatarAlt", isSignal: true, isRequired: false, transformFunction: null }, quickActions: { classPropertyName: "quickActions", publicName: "quickActions", isSignal: true, isRequired: true, transformFunction: null }, notificationShortcut: { classPropertyName: "notificationShortcut", publicName: "notificationShortcut", isSignal: true, isRequired: false, transformFunction: null }, showNotificationShortcut: { classPropertyName: "showNotificationShortcut", publicName: "showNotificationShortcut", isSignal: true, isRequired: false, transformFunction: null }, popoverSide: { classPropertyName: "popoverSide", publicName: "popoverSide", isSignal: true, isRequired: false, transformFunction: null }, popoverAlign: { classPropertyName: "popoverAlign", publicName: "popoverAlign", isSignal: true, isRequired: false, transformFunction: null }, popoverSideOffset: { classPropertyName: "popoverSideOffset", publicName: "popoverSideOffset", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { actionSelected: "actionSelected" }, host: { properties: { "class": "hostClasses()" } }, viewQueries: [{ propertyName: "popoverTrigger", first: true, predicate: ["trigger"], descendants: true, isSignal: true }], ngImport: i0, template: `
200
- @if (notificationShortcutConfig(); as shortcut) {
201
- <button
202
- type="button"
203
- ui-button
204
- variant="ghost"
205
- size="icon"
206
- data-trigger-action="notifications"
207
- [attr.data-value]="shortcut.value"
208
- [attr.aria-label]="shortcut.ariaLabel"
209
- [class]="notificationButtonClasses()"
210
- (click)="triggerNotificationAction($event)">
211
- <ui-nav-icon [name]="shortcut.icon" [size]="18" class="text-current" />
212
- </button>
213
- }
214
-
215
- <button
216
- #trigger="uiPopoverTrigger"
217
- type="button"
218
- ui-button
219
- variant="ghost"
220
- size="icon"
221
- [uiPopoverTrigger]="preferencesPanel"
222
- [side]="resolvedPopoverSide()"
223
- [align]="resolvedPopoverAlign()"
224
- [sideOffset]="resolvedPopoverSideOffset()"
225
- [attr.aria-label]="triggerLabel()"
226
- [class]="triggerButtonClasses(trigger.isOpen())">
227
- <ui-avatar class="h-8 w-8 border border-border/60 shadow-[inset_0_1px_0_rgba(255,255,255,0.18)]">
228
- @if (hasAvatar()) {
229
- <ui-avatar-image [src]="avatarImageSrc()" [alt]="avatarAltText()" />
230
- }
231
- <ui-avatar-fallback class="bg-primary text-xs font-semibold tracking-[0.24em] text-primary-foreground">
232
- {{ initials() }}
233
- </ui-avatar-fallback>
234
- </ui-avatar>
235
- </button>
236
-
237
- <ng-template uiPopoverContent #preferencesPanel="uiPopoverContent">
238
- <section
239
- data-etos-theme-switcher-panel
240
- role="dialog"
241
- aria-label="User Info"
242
- class="w-[min(21rem,calc(100vw-1.5rem))] overflow-hidden border border-border/70 bg-background text-foreground shadow-[0_18px_48px_rgba(15,23,42,0.12)]">
243
- <header class="p-5 pb-4">
244
- <div class="flex items-center gap-4">
245
- <ui-avatar class="h-14 w-14 border border-border/60 shadow-sm">
246
- @if (hasAvatar()) {
247
- <ui-avatar-image [src]="avatarImageSrc()" [alt]="avatarAltText()" />
248
- }
249
- <ui-avatar-fallback class="bg-primary text-sm font-semibold tracking-[0.24em] text-primary-foreground">
250
- {{ initials() }}
251
- </ui-avatar-fallback>
252
- </ui-avatar>
253
-
254
- <div class="min-w-0 flex min-h-14 flex-1 flex-col justify-center self-center">
255
- <div class="space-y-px">
256
- <h2 class="truncate text-[1.1rem] font-semibold leading-none tracking-tight text-foreground">
257
- {{ resolvedUserName() }}
258
- </h2>
259
- <p class="text-sm leading-[0.95rem] text-muted-foreground">
260
- {{ resolvedUserSubtitle() }}
261
- </p>
262
- </div>
263
- </div>
264
- </div>
265
- </header>
266
-
267
- <div class="space-y-4 px-5 pb-5">
268
- <section
269
- data-setting="theme-scheme"
270
- [attr.data-current]="themeScheme()"
271
- class="rounded-(--layout-frame-radius) bg-muted/65 p-0.5">
272
- <div class="grid grid-cols-3 gap-1">
273
- @for (option of themeSchemeOptions; track option.value) {
274
- <button
275
- type="button"
276
- ui-button
277
- size="sm"
278
- variant="ghost"
279
- [class]="themeOptionClasses(themeScheme() === option.value)"
280
- data-setting-option="theme-scheme"
281
- [attr.data-value]="option.value"
282
- (click)="setThemeScheme(option.value)">
283
- <span class="inline-flex items-center gap-2.5">
284
- <ui-nav-icon
285
- [name]="option.icon"
286
- [size]="18"
287
- [class]="themeIconClasses(themeScheme() === option.value)" />
288
- <span class="text-sm font-semibold leading-none">{{ option.label }}</span>
289
- </span>
290
- </button>
291
- }
292
- </div>
293
- </section>
294
-
295
- <section data-setting="layout-mode" [attr.data-current]="layoutMode()" class="space-y-2">
296
- <div class="px-1">
297
- <p class="text-[0.72rem] font-semibold uppercase tracking-[0.22em] text-muted-foreground">Layout</p>
298
- </div>
299
- <div class="rounded-(--layout-frame-radius) bg-muted/65 p-0.5">
300
- <div class="grid grid-cols-3 gap-1">
301
- @for (option of layoutModeOptions; track option.value) {
302
- <button
303
- type="button"
304
- ui-button
305
- size="sm"
306
- variant="ghost"
307
- [class]="segmentedOptionClasses(layoutMode() === option.value)"
308
- data-setting-option="layout-mode"
309
- [attr.data-value]="option.value"
310
- (click)="setLayoutMode(option.value)">
311
- <span class="inline-flex items-center gap-2.5">
312
- <ui-nav-icon
313
- [name]="option.icon"
314
- [size]="18"
315
- [class]="themeIconClasses(layoutMode() === option.value)" />
316
- <span class="text-sm font-semibold leading-none">{{ option.label }}</span>
317
- </span>
318
- </button>
319
- }
320
- </div>
321
- </div>
322
- </section>
323
-
324
- <section data-setting="layout-width" [attr.data-current]="layoutWidth()" class="space-y-2">
325
- <div class="px-1">
326
- <p class="text-[0.72rem] font-semibold uppercase tracking-[0.22em] text-muted-foreground">Width</p>
327
- </div>
328
- <div class="rounded-(--layout-frame-radius) bg-muted/65 p-0.5">
329
- <div class="grid grid-cols-3 gap-1">
330
- @for (option of layoutWidthOptions; track option.value) {
331
- <button
332
- type="button"
333
- ui-button
334
- size="sm"
335
- variant="ghost"
336
- [class]="segmentedOptionClasses(layoutWidth() === option.value)"
337
- data-setting-option="layout-width"
338
- [attr.data-value]="option.value"
339
- (click)="setLayoutWidth(option.value)">
340
- <span class="inline-flex items-center gap-2.5">
341
- <ui-nav-icon
342
- [name]="option.icon"
343
- [size]="18"
344
- [class]="themeIconClasses(layoutWidth() === option.value)" />
345
- <span class="text-sm font-semibold leading-none">{{ option.label }}</span>
346
- </span>
347
- </button>
348
- }
349
- </div>
350
- </div>
351
- </section>
352
-
353
- <section class="space-y-1 border-t border-border/70 pt-2">
354
- <h3 class="sr-only">Quick Actions</h3>
355
- <div class="grid gap-1">
356
- @for (action of actionOptions(); track action.value) {
357
- <button
358
- type="button"
359
- ui-button
360
- variant="ghost"
361
- [class]="actionButtonClasses(action.tone ?? 'default')"
362
- data-action-option
363
- [attr.data-value]="action.value"
364
- (click)="triggerAction(action.value)">
365
- <span class="inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-muted/65">
366
- <ui-nav-icon
367
- [name]="action.icon"
368
- [size]="19"
369
- [class]="actionIconClasses(action.tone ?? 'default')" />
370
- </span>
371
- <span [class]="actionLabelClasses(action.tone ?? 'default')">
372
- {{ action.label }}
373
- </span>
374
- </button>
375
- }
376
- </div>
377
- </section>
378
- </div>
379
- </section>
380
- </ng-template>
381
- `, isInline: true, dependencies: [{ kind: "component", type: AvatarComponent, selector: "ui-avatar", inputs: ["class"] }, { kind: "component", type: AvatarFallbackComponent, selector: "ui-avatar-fallback", inputs: ["class"] }, { kind: "component", type: AvatarImageComponent, selector: "ui-avatar-image", inputs: ["src", "alt", "class"] }, { kind: "component", type: ButtonComponent, selector: "button[ui-button], a[ui-button]", inputs: ["variant", "size", "class"] }, { kind: "directive", type: PopoverContentDirective, selector: "ng-template[uiPopoverContent]", exportAs: ["uiPopoverContent"] }, { kind: "directive", type: PopoverTriggerDirective, selector: "[uiPopoverTrigger]", inputs: ["uiPopoverTrigger", "side", "align", "sideOffset", "disabled"], outputs: ["openedChange"], exportAs: ["uiPopoverTrigger"] }, { kind: "component", type: UiNavIconComponent, selector: "ui-nav-icon", inputs: ["name", "class", "size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
382
- }
383
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: EtosThemeSwitcherComponent, decorators: [{
384
- type: Component,
385
- args: [{
386
- selector: 'etos-theme-switcher',
387
- changeDetection: ChangeDetectionStrategy.OnPush,
388
- imports: [
389
- AvatarComponent,
390
- AvatarFallbackComponent,
391
- AvatarImageComponent,
392
- ButtonComponent,
393
- PopoverContentDirective,
394
- PopoverTriggerDirective,
395
- UiNavIconComponent,
396
- ],
397
- host: {
398
- '[class]': 'hostClasses()',
399
- },
400
- template: `
401
- @if (notificationShortcutConfig(); as shortcut) {
402
- <button
403
- type="button"
404
- ui-button
405
- variant="ghost"
406
- size="icon"
407
- data-trigger-action="notifications"
408
- [attr.data-value]="shortcut.value"
409
- [attr.aria-label]="shortcut.ariaLabel"
410
- [class]="notificationButtonClasses()"
411
- (click)="triggerNotificationAction($event)">
412
- <ui-nav-icon [name]="shortcut.icon" [size]="18" class="text-current" />
413
- </button>
414
- }
415
-
416
- <button
417
- #trigger="uiPopoverTrigger"
418
- type="button"
419
- ui-button
420
- variant="ghost"
421
- size="icon"
422
- [uiPopoverTrigger]="preferencesPanel"
423
- [side]="resolvedPopoverSide()"
424
- [align]="resolvedPopoverAlign()"
425
- [sideOffset]="resolvedPopoverSideOffset()"
426
- [attr.aria-label]="triggerLabel()"
427
- [class]="triggerButtonClasses(trigger.isOpen())">
428
- <ui-avatar class="h-8 w-8 border border-border/60 shadow-[inset_0_1px_0_rgba(255,255,255,0.18)]">
429
- @if (hasAvatar()) {
430
- <ui-avatar-image [src]="avatarImageSrc()" [alt]="avatarAltText()" />
431
- }
432
- <ui-avatar-fallback class="bg-primary text-xs font-semibold tracking-[0.24em] text-primary-foreground">
433
- {{ initials() }}
434
- </ui-avatar-fallback>
435
- </ui-avatar>
436
- </button>
437
-
438
- <ng-template uiPopoverContent #preferencesPanel="uiPopoverContent">
439
- <section
440
- data-etos-theme-switcher-panel
441
- role="dialog"
442
- aria-label="User Info"
443
- class="w-[min(21rem,calc(100vw-1.5rem))] overflow-hidden border border-border/70 bg-background text-foreground shadow-[0_18px_48px_rgba(15,23,42,0.12)]">
444
- <header class="p-5 pb-4">
445
- <div class="flex items-center gap-4">
446
- <ui-avatar class="h-14 w-14 border border-border/60 shadow-sm">
447
- @if (hasAvatar()) {
448
- <ui-avatar-image [src]="avatarImageSrc()" [alt]="avatarAltText()" />
449
- }
450
- <ui-avatar-fallback class="bg-primary text-sm font-semibold tracking-[0.24em] text-primary-foreground">
451
- {{ initials() }}
452
- </ui-avatar-fallback>
453
- </ui-avatar>
454
-
455
- <div class="min-w-0 flex min-h-14 flex-1 flex-col justify-center self-center">
456
- <div class="space-y-px">
457
- <h2 class="truncate text-[1.1rem] font-semibold leading-none tracking-tight text-foreground">
458
- {{ resolvedUserName() }}
459
- </h2>
460
- <p class="text-sm leading-[0.95rem] text-muted-foreground">
461
- {{ resolvedUserSubtitle() }}
462
- </p>
463
- </div>
464
- </div>
465
- </div>
466
- </header>
467
-
468
- <div class="space-y-4 px-5 pb-5">
469
- <section
470
- data-setting="theme-scheme"
471
- [attr.data-current]="themeScheme()"
472
- class="rounded-(--layout-frame-radius) bg-muted/65 p-0.5">
473
- <div class="grid grid-cols-3 gap-1">
474
- @for (option of themeSchemeOptions; track option.value) {
475
- <button
476
- type="button"
477
- ui-button
478
- size="sm"
479
- variant="ghost"
480
- [class]="themeOptionClasses(themeScheme() === option.value)"
481
- data-setting-option="theme-scheme"
482
- [attr.data-value]="option.value"
483
- (click)="setThemeScheme(option.value)">
484
- <span class="inline-flex items-center gap-2.5">
485
- <ui-nav-icon
486
- [name]="option.icon"
487
- [size]="18"
488
- [class]="themeIconClasses(themeScheme() === option.value)" />
489
- <span class="text-sm font-semibold leading-none">{{ option.label }}</span>
490
- </span>
491
- </button>
492
- }
493
- </div>
494
- </section>
495
-
496
- <section data-setting="layout-mode" [attr.data-current]="layoutMode()" class="space-y-2">
497
- <div class="px-1">
498
- <p class="text-[0.72rem] font-semibold uppercase tracking-[0.22em] text-muted-foreground">Layout</p>
499
- </div>
500
- <div class="rounded-(--layout-frame-radius) bg-muted/65 p-0.5">
501
- <div class="grid grid-cols-3 gap-1">
502
- @for (option of layoutModeOptions; track option.value) {
503
- <button
504
- type="button"
505
- ui-button
506
- size="sm"
507
- variant="ghost"
508
- [class]="segmentedOptionClasses(layoutMode() === option.value)"
509
- data-setting-option="layout-mode"
510
- [attr.data-value]="option.value"
511
- (click)="setLayoutMode(option.value)">
512
- <span class="inline-flex items-center gap-2.5">
513
- <ui-nav-icon
514
- [name]="option.icon"
515
- [size]="18"
516
- [class]="themeIconClasses(layoutMode() === option.value)" />
517
- <span class="text-sm font-semibold leading-none">{{ option.label }}</span>
518
- </span>
519
- </button>
520
- }
521
- </div>
522
- </div>
523
- </section>
524
-
525
- <section data-setting="layout-width" [attr.data-current]="layoutWidth()" class="space-y-2">
526
- <div class="px-1">
527
- <p class="text-[0.72rem] font-semibold uppercase tracking-[0.22em] text-muted-foreground">Width</p>
528
- </div>
529
- <div class="rounded-(--layout-frame-radius) bg-muted/65 p-0.5">
530
- <div class="grid grid-cols-3 gap-1">
531
- @for (option of layoutWidthOptions; track option.value) {
532
- <button
533
- type="button"
534
- ui-button
535
- size="sm"
536
- variant="ghost"
537
- [class]="segmentedOptionClasses(layoutWidth() === option.value)"
538
- data-setting-option="layout-width"
539
- [attr.data-value]="option.value"
540
- (click)="setLayoutWidth(option.value)">
541
- <span class="inline-flex items-center gap-2.5">
542
- <ui-nav-icon
543
- [name]="option.icon"
544
- [size]="18"
545
- [class]="themeIconClasses(layoutWidth() === option.value)" />
546
- <span class="text-sm font-semibold leading-none">{{ option.label }}</span>
547
- </span>
548
- </button>
549
- }
550
- </div>
551
- </div>
552
- </section>
553
-
554
- <section class="space-y-1 border-t border-border/70 pt-2">
555
- <h3 class="sr-only">Quick Actions</h3>
556
- <div class="grid gap-1">
557
- @for (action of actionOptions(); track action.value) {
558
- <button
559
- type="button"
560
- ui-button
561
- variant="ghost"
562
- [class]="actionButtonClasses(action.tone ?? 'default')"
563
- data-action-option
564
- [attr.data-value]="action.value"
565
- (click)="triggerAction(action.value)">
566
- <span class="inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-muted/65">
567
- <ui-nav-icon
568
- [name]="action.icon"
569
- [size]="19"
570
- [class]="actionIconClasses(action.tone ?? 'default')" />
571
- </span>
572
- <span [class]="actionLabelClasses(action.tone ?? 'default')">
573
- {{ action.label }}
574
- </span>
575
- </button>
576
- }
577
- </div>
578
- </section>
579
- </div>
580
- </section>
581
- </ng-template>
582
- `,
583
- }]
584
- }], propDecorators: { popoverTrigger: [{ type: i0.ViewChild, args: ['trigger', { isSignal: true }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], userInfo: [{ type: i0.Input, args: [{ isSignal: true, alias: "userInfo", required: false }] }], userName: [{ type: i0.Input, args: [{ isSignal: true, alias: "userName", required: false }] }], userSubtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "userSubtitle", required: false }] }], avatarSrc: [{ type: i0.Input, args: [{ isSignal: true, alias: "avatarSrc", required: false }] }], avatarAlt: [{ type: i0.Input, args: [{ isSignal: true, alias: "avatarAlt", required: false }] }], quickActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "quickActions", required: true }] }], notificationShortcut: [{ type: i0.Input, args: [{ isSignal: true, alias: "notificationShortcut", required: false }] }], showNotificationShortcut: [{ type: i0.Input, args: [{ isSignal: true, alias: "showNotificationShortcut", required: false }] }], popoverSide: [{ type: i0.Input, args: [{ isSignal: true, alias: "popoverSide", required: false }] }], popoverAlign: [{ type: i0.Input, args: [{ isSignal: true, alias: "popoverAlign", required: false }] }], popoverSideOffset: [{ type: i0.Input, args: [{ isSignal: true, alias: "popoverSideOffset", required: false }] }], actionSelected: [{ type: i0.Output, args: ["actionSelected"] }] } });
585
-
586
- class EmptyLayout {
587
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: EmptyLayout, deps: [], target: i0.ɵɵFactoryTarget.Component });
588
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.13", type: EmptyLayout, isStandalone: true, selector: "empty", ngImport: i0, template: `
589
- <div
590
- class="h-dvh overflow-hidden bg-neutral-200 bg-[linear-gradient(rgba(212,212,212,0.45)_1px,transparent_1px),linear-gradient(to_right,rgba(212,212,212,0.45)_1px,transparent_1px)] bg-position-[center_center] bg-size-[2.775rem_2.775rem]">
591
- <div class="relative isolate box-border h-full overflow-hidden py-3">
592
- <div aria-hidden="true" class="pointer-events-none absolute inset-y-0 inset-x-0 px-3">
593
- <div class="relative mx-auto h-full w-full">
594
- <div class="absolute inset-y-0 left-0 w-px bg-brand"></div>
595
- <div class="absolute inset-y-0 right-0 w-px bg-brand"></div>
596
- </div>
597
- </div>
598
-
599
- <div class="h-full min-h-0 border border-brand px-3">
600
- <main class="mx-auto w-full h-full min-h-0 overflow-y-auto bg-background/65">
601
- <router-outlet />
602
- </main>
603
- </div>
604
- </div>
605
- </div>
606
- `, isInline: true, styles: [""], dependencies: [{ kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
607
- }
608
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: EmptyLayout, decorators: [{
609
- type: Component,
610
- args: [{ selector: 'empty', imports: [RouterOutlet], template: `
611
- <div
612
- class="h-dvh overflow-hidden bg-neutral-200 bg-[linear-gradient(rgba(212,212,212,0.45)_1px,transparent_1px),linear-gradient(to_right,rgba(212,212,212,0.45)_1px,transparent_1px)] bg-position-[center_center] bg-size-[2.775rem_2.775rem]">
613
- <div class="relative isolate box-border h-full overflow-hidden py-3">
614
- <div aria-hidden="true" class="pointer-events-none absolute inset-y-0 inset-x-0 px-3">
615
- <div class="relative mx-auto h-full w-full">
616
- <div class="absolute inset-y-0 left-0 w-px bg-brand"></div>
617
- <div class="absolute inset-y-0 right-0 w-px bg-brand"></div>
618
- </div>
619
- </div>
620
-
621
- <div class="h-full min-h-0 border border-brand px-3">
622
- <main class="mx-auto w-full h-full min-h-0 overflow-y-auto bg-background/65">
623
- <router-outlet />
624
- </main>
625
- </div>
626
- </div>
627
- </div>
628
- `, changeDetection: ChangeDetectionStrategy.OnPush }]
629
- }] });
630
-
631
- class HorizontalLayout {
632
- layout = inject(LayoutService);
633
- brandTemplate = input(null, ...(ngDevMode ? [{ debugName: "brandTemplate" }] : /* istanbul ignore next */ []));
634
- profileTemplate = input(null, ...(ngDevMode ? [{ debugName: "profileTemplate" }] : /* istanbul ignore next */ []));
635
- layoutWidth = this.layout.width;
636
- shellClasses = computed(() => {
637
- switch (this.layoutWidth()) {
638
- case 'full':
639
- return 'relative isolate box-border h-dvh overflow-hidden py-3';
640
- case 'wide':
641
- return 'relative isolate box-border h-dvh overflow-hidden py-3 lg:py-10';
642
- default:
643
- return 'relative isolate box-border h-dvh overflow-hidden py-3 lg:py-20';
644
- }
645
- }, ...(ngDevMode ? [{ debugName: "shellClasses" }] : /* istanbul ignore next */ []));
646
- railOverlayClasses = computed(() => {
647
- const classes = ['pointer-events-none', 'absolute', 'inset-y-0', 'inset-x-0', 'z-20'];
648
- if (this.layoutWidth() === 'full') {
649
- classes.push('px-3');
650
- }
651
- else if (this.layoutWidth() === 'wide') {
652
- classes.push('px-3', 'lg:px-10');
653
- }
654
- else {
655
- classes.push('px-3', 'lg:px-10');
656
- }
657
- return classes.join(' ');
658
- }, ...(ngDevMode ? [{ debugName: "railOverlayClasses" }] : /* istanbul ignore next */ []));
659
- railGuideClasses = computed(() => this.layoutWidth() === 'container' ? 'relative mx-auto h-full w-full max-w-7xl' : 'relative mx-auto h-full w-full', ...(ngDevMode ? [{ debugName: "railGuideClasses" }] : /* istanbul ignore next */ []));
660
- frameClasses = computed(() => {
661
- const classes = ['relative', 'z-10', 'h-full', 'min-h-0', 'border', 'border-brand'];
662
- if (this.layoutWidth() === 'full') {
663
- classes.push('px-3');
664
- }
665
- else if (this.layoutWidth() === 'wide') {
666
- classes.push('px-3', 'lg:px-10');
667
- }
668
- else {
669
- classes.push('px-3', 'lg:px-10');
670
- }
671
- return classes.join(' ');
672
- }, ...(ngDevMode ? [{ debugName: "frameClasses" }] : /* istanbul ignore next */ []));
673
- maskClasses = computed(() => {
674
- const classes = ['h-full', 'bg-position-[center_center]', 'bg-size-[2.775rem_2.775rem]'];
675
- if (this.layoutWidth() === 'container') {
676
- classes.unshift('mx-auto', 'w-full', 'max-w-7xl');
677
- }
678
- else {
679
- classes.unshift('mx-auto', 'w-full');
680
- }
681
- return classes.join(' ');
682
- }, ...(ngDevMode ? [{ debugName: "maskClasses" }] : /* istanbul ignore next */ []));
683
- contentClasses = computed(() => {
684
- const classes = [
685
- 'relative',
686
- 'z-20',
687
- 'mx-auto',
688
- 'flex',
689
- 'h-full',
690
- 'min-h-0',
691
- 'flex-col',
692
- 'overflow-hidden',
693
- 'bg-background/65',
694
- ];
695
- if (this.layoutWidth() === 'container') {
696
- classes.push('w-full', 'max-w-7xl');
697
- }
698
- else {
699
- classes.push('w-full');
700
- }
701
- return classes.join(' ');
702
- }, ...(ngDevMode ? [{ debugName: "contentClasses" }] : /* istanbul ignore next */ []));
703
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: HorizontalLayout, deps: [], target: i0.ɵɵFactoryTarget.Component });
704
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.13", type: HorizontalLayout, isStandalone: true, selector: "horizontal", inputs: { brandTemplate: { classPropertyName: "brandTemplate", publicName: "brandTemplate", isSignal: true, isRequired: false, transformFunction: null }, profileTemplate: { classPropertyName: "profileTemplate", publicName: "profileTemplate", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-layout-width": "layoutWidth()" }, classAttribute: "block" }, ngImport: i0, template: `
705
- <div
706
- class="min-h-screen bg-neutral-200 text-neutral-600 bg-[linear-gradient(rgba(212,212,212,0.45)_1px,transparent_1px),linear-gradient(to_right,rgba(212,212,212,0.45)_1px,transparent_1px)] bg-position-[center_center] bg-size-[2.775rem_2.775rem]">
707
- <div [class]="shellClasses()">
708
- <div aria-hidden="true" [class]="railOverlayClasses()">
709
- <div [class]="railGuideClasses()">
710
- <div class="absolute inset-y-0 left-0 w-px bg-brand"></div>
711
- <div class="absolute inset-y-0 right-0 w-px bg-brand"></div>
712
- </div>
713
- </div>
714
-
715
- <div [class]="frameClasses()">
716
- <div aria-hidden="true" class="pointer-events-none absolute inset-0 z-0">
717
- <div class="absolute inset-x-0 top-11 h-px bg-brand"></div>
718
- <div class="absolute inset-x-0 bottom-11.75 h-px bg-brand"></div>
719
- </div>
720
- <div aria-hidden="true" class="pointer-events-none absolute inset-0 z-10">
721
- <div [class]="maskClasses()"></div>
722
- </div>
723
- <div [class]="contentClasses()">
724
- <nav class="shrink-0">
725
- <topbar class="shrink-0" [showHamburger]="false">
726
- @if (brandTemplate(); as brand) {
727
- <div topbar-start class="min-w-0">
728
- <ng-container [ngTemplateOutlet]="brand" />
729
- </div>
730
- }
731
-
732
- @if (profileTemplate(); as profile) {
733
- <div topbar-end class="flex min-w-0 items-center">
734
- <ng-container [ngTemplateOutlet]="profile" />
735
- </div>
736
- }
737
- </topbar>
738
- </nav>
739
- <main class="min-h-0 flex-1 overflow-auto">
740
- <router-outlet />
741
- </main>
742
- </div>
743
- </div>
744
- </div>
745
- </div>
746
- `, isInline: true, styles: [""], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "component", type: TopbarComponent, selector: "topbar", inputs: ["items", "navigationId", "appearance", "ariaLabel", "class", "autoRegister", "showHamburger", "hamburgerLabel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
747
- }
748
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: HorizontalLayout, decorators: [{
749
- type: Component,
750
- args: [{ selector: 'horizontal', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgTemplateOutlet, RouterOutlet, TopbarComponent], host: {
751
- class: 'block',
752
- '[attr.data-layout-width]': 'layoutWidth()',
753
- }, template: `
754
- <div
755
- class="min-h-screen bg-neutral-200 text-neutral-600 bg-[linear-gradient(rgba(212,212,212,0.45)_1px,transparent_1px),linear-gradient(to_right,rgba(212,212,212,0.45)_1px,transparent_1px)] bg-position-[center_center] bg-size-[2.775rem_2.775rem]">
756
- <div [class]="shellClasses()">
757
- <div aria-hidden="true" [class]="railOverlayClasses()">
758
- <div [class]="railGuideClasses()">
759
- <div class="absolute inset-y-0 left-0 w-px bg-brand"></div>
760
- <div class="absolute inset-y-0 right-0 w-px bg-brand"></div>
761
- </div>
762
- </div>
763
-
764
- <div [class]="frameClasses()">
765
- <div aria-hidden="true" class="pointer-events-none absolute inset-0 z-0">
766
- <div class="absolute inset-x-0 top-11 h-px bg-brand"></div>
767
- <div class="absolute inset-x-0 bottom-11.75 h-px bg-brand"></div>
768
- </div>
769
- <div aria-hidden="true" class="pointer-events-none absolute inset-0 z-10">
770
- <div [class]="maskClasses()"></div>
771
- </div>
772
- <div [class]="contentClasses()">
773
- <nav class="shrink-0">
774
- <topbar class="shrink-0" [showHamburger]="false">
775
- @if (brandTemplate(); as brand) {
776
- <div topbar-start class="min-w-0">
777
- <ng-container [ngTemplateOutlet]="brand" />
778
- </div>
779
- }
780
-
781
- @if (profileTemplate(); as profile) {
782
- <div topbar-end class="flex min-w-0 items-center">
783
- <ng-container [ngTemplateOutlet]="profile" />
784
- </div>
785
- }
786
- </topbar>
787
- </nav>
788
- <main class="min-h-0 flex-1 overflow-auto">
789
- <router-outlet />
790
- </main>
791
- </div>
792
- </div>
793
- </div>
794
- </div>
795
- ` }]
796
- }], propDecorators: { brandTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "brandTemplate", required: false }] }], profileTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "profileTemplate", required: false }] }] } });
797
-
798
- class VerticalLayout {
799
- layout = inject(LayoutService);
800
- sidebarAppearance = input('default', ...(ngDevMode ? [{ debugName: "sidebarAppearance" }] : /* istanbul ignore next */ []));
801
- sidebarPosition = input('left', ...(ngDevMode ? [{ debugName: "sidebarPosition" }] : /* istanbul ignore next */ []));
802
- ariaLabel = input('Primary', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
803
- sidebarHeaderTemplate = input(null, ...(ngDevMode ? [{ debugName: "sidebarHeaderTemplate" }] : /* istanbul ignore next */ []));
804
- sidebarFooterTemplate = input(null, ...(ngDevMode ? [{ debugName: "sidebarFooterTemplate" }] : /* istanbul ignore next */ []));
805
- layoutWidth = this.layout.width;
806
- shellClasses = computed(() => {
807
- switch (this.layoutWidth()) {
808
- case 'full':
809
- return 'relative isolate h-full overflow-hidden py-2';
810
- case 'wide':
811
- return 'relative isolate h-full overflow-hidden py-2 lg:py-10';
812
- default:
813
- return 'relative isolate h-full overflow-hidden py-2 lg:py-18';
814
- }
815
- }, ...(ngDevMode ? [{ debugName: "shellClasses" }] : /* istanbul ignore next */ []));
816
- frameClasses = computed(() => {
817
- if (this.layoutWidth() === 'wide') {
818
- return 'relative h-full border-y border-brand';
819
- }
820
- return '-mx-18 relative h-full border-y border-brand';
821
- }, ...(ngDevMode ? [{ debugName: "frameClasses" }] : /* istanbul ignore next */ []));
822
- frameBodyClasses = computed(() => {
823
- if (this.layoutWidth() === 'wide') {
824
- return 'h-full lg:px-10';
825
- }
826
- return 'h-full px-20';
827
- }, ...(ngDevMode ? [{ debugName: "frameBodyClasses" }] : /* istanbul ignore next */ []));
828
- gridClasses = computed(() => {
829
- const classes = [
830
- 'relative',
831
- 'mx-auto',
832
- 'grid',
833
- 'h-full',
834
- 'w-full',
835
- 'max-w-full',
836
- 'grid-cols-[auto_minmax(0,1fr)]',
837
- 'items-stretch',
838
- 'bg-background/65',
839
- ];
840
- switch (this.layoutWidth()) {
841
- case 'container':
842
- classes.push('lg:max-w-[var(--layout-vertical-shell-max-width)]');
843
- break;
844
- default:
845
- break;
846
- }
847
- return classes.join(' ');
848
- }, ...(ngDevMode ? [{ debugName: "gridClasses" }] : /* istanbul ignore next */ []));
849
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: VerticalLayout, deps: [], target: i0.ɵɵFactoryTarget.Component });
850
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.13", type: VerticalLayout, isStandalone: true, selector: "vertical", inputs: { sidebarAppearance: { classPropertyName: "sidebarAppearance", publicName: "sidebarAppearance", isSignal: true, isRequired: false, transformFunction: null }, sidebarPosition: { classPropertyName: "sidebarPosition", publicName: "sidebarPosition", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, sidebarHeaderTemplate: { classPropertyName: "sidebarHeaderTemplate", publicName: "sidebarHeaderTemplate", isSignal: true, isRequired: false, transformFunction: null }, sidebarFooterTemplate: { classPropertyName: "sidebarFooterTemplate", publicName: "sidebarFooterTemplate", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-layout-width": "layoutWidth()" }, classAttribute: "block h-dvh overflow-hidden" }, ngImport: i0, template: `
851
- <!-- prettier-ignore -->
852
- <div class="h-full overflow-hidden bg-neutral-200 bg-[linear-gradient(rgba(212,212,212,0.65)_1px,transparent_1px),linear-gradient(to_right,rgba(212,212,212,0.65)_1px,transparent_1px)] bg-position-[center_center] bg-size-[2.775rem_2.775rem]">
853
- <div [class]="shellClasses()">
854
- <div [class]="frameClasses()">
855
- <div aria-hidden="true" class="pointer-events-none absolute inset-x-0 inset-y-8">
856
- <div class="absolute inset-x-0 top-3.75 h-px bg-brand"></div>
857
- <div class="absolute inset-x-0 bottom-3.75 h-px bg-brand"></div>
858
- </div>
859
- <div [class]="frameBodyClasses()">
860
- <div [class]="gridClasses()">
861
- <div
862
- aria-hidden="true"
863
- class="pointer-events-none absolute bottom-[-100vh] left-0 top-[-100vh] z-20 w-px bg-brand"></div>
864
- <div
865
- aria-hidden="true"
866
- class="pointer-events-none absolute bottom-[-100vh] right-0 top-[-100vh] z-20 w-px bg-brand"></div>
867
- <nav class="relative h-full min-h-0 shrink-0 overflow-visible border-r border-primary/30">
868
- <div
869
- aria-hidden="true"
870
- data-sidebar-rail
871
- class="pointer-events-none absolute bottom-[-100vh] left-full top-[-100vh] -z-10 w-px bg-primary/30 transition-transform duration-200 ease-out"></div>
872
- <sidebar
873
- class="h-full"
874
- [appearance]="sidebarAppearance()"
875
- [borderSource]="'layout'"
876
- [position]="sidebarPosition()"
877
- [ariaLabel]="ariaLabel()">
878
- @if (sidebarHeaderTemplate(); as headerTemplate) {
879
- <div sidebar-header class="contents">
880
- <ng-container [ngTemplateOutlet]="headerTemplate" />
881
- </div>
882
- }
883
- @if (sidebarFooterTemplate(); as footerTemplate) {
884
- <div sidebar-footer class="contents">
885
- <ng-container [ngTemplateOutlet]="footerTemplate" />
886
- </div>
887
- }
888
- </sidebar>
889
- </nav>
890
- <main class="relative min-h-0 min-w-0 overflow-y-auto scrollbar-thin scrollbar-thumb-primary scrollbar-track-primary/10">
891
- <router-outlet />
892
- </main>
893
- </div>
894
- </div>
895
- </div>
896
- </div>
897
- </div>
898
- `, isInline: true, styles: ["nav:has(>sidebar[data-appearance=thin][data-expanded=true])>[data-sidebar-rail]{transform:translate(13.5rem)}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "component", type: SidebarComponent, selector: "sidebar", inputs: ["items", "navigationId", "appearance", "borderSource", "position", "ariaLabel", "header", "class", "autoMobile", "autoRegister"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
899
- }
900
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: VerticalLayout, decorators: [{
901
- type: Component,
902
- args: [{ selector: 'vertical', imports: [NgTemplateOutlet, RouterOutlet, SidebarComponent], changeDetection: ChangeDetectionStrategy.OnPush, host: {
903
- class: 'block h-dvh overflow-hidden',
904
- '[attr.data-layout-width]': 'layoutWidth()',
905
- }, template: `
906
- <!-- prettier-ignore -->
907
- <div class="h-full overflow-hidden bg-neutral-200 bg-[linear-gradient(rgba(212,212,212,0.65)_1px,transparent_1px),linear-gradient(to_right,rgba(212,212,212,0.65)_1px,transparent_1px)] bg-position-[center_center] bg-size-[2.775rem_2.775rem]">
908
- <div [class]="shellClasses()">
909
- <div [class]="frameClasses()">
910
- <div aria-hidden="true" class="pointer-events-none absolute inset-x-0 inset-y-8">
911
- <div class="absolute inset-x-0 top-3.75 h-px bg-brand"></div>
912
- <div class="absolute inset-x-0 bottom-3.75 h-px bg-brand"></div>
913
- </div>
914
- <div [class]="frameBodyClasses()">
915
- <div [class]="gridClasses()">
916
- <div
917
- aria-hidden="true"
918
- class="pointer-events-none absolute bottom-[-100vh] left-0 top-[-100vh] z-20 w-px bg-brand"></div>
919
- <div
920
- aria-hidden="true"
921
- class="pointer-events-none absolute bottom-[-100vh] right-0 top-[-100vh] z-20 w-px bg-brand"></div>
922
- <nav class="relative h-full min-h-0 shrink-0 overflow-visible border-r border-primary/30">
923
- <div
924
- aria-hidden="true"
925
- data-sidebar-rail
926
- class="pointer-events-none absolute bottom-[-100vh] left-full top-[-100vh] -z-10 w-px bg-primary/30 transition-transform duration-200 ease-out"></div>
927
- <sidebar
928
- class="h-full"
929
- [appearance]="sidebarAppearance()"
930
- [borderSource]="'layout'"
931
- [position]="sidebarPosition()"
932
- [ariaLabel]="ariaLabel()">
933
- @if (sidebarHeaderTemplate(); as headerTemplate) {
934
- <div sidebar-header class="contents">
935
- <ng-container [ngTemplateOutlet]="headerTemplate" />
936
- </div>
937
- }
938
- @if (sidebarFooterTemplate(); as footerTemplate) {
939
- <div sidebar-footer class="contents">
940
- <ng-container [ngTemplateOutlet]="footerTemplate" />
941
- </div>
942
- }
943
- </sidebar>
944
- </nav>
945
- <main class="relative min-h-0 min-w-0 overflow-y-auto scrollbar-thin scrollbar-thumb-primary scrollbar-track-primary/10">
946
- <router-outlet />
947
- </main>
948
- </div>
949
- </div>
950
- </div>
951
- </div>
952
- </div>
953
- `, styles: ["nav:has(>sidebar[data-appearance=thin][data-expanded=true])>[data-sidebar-rail]{transform:translate(13.5rem)}\n"] }]
954
- }], propDecorators: { sidebarAppearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebarAppearance", required: false }] }], sidebarPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebarPosition", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], sidebarHeaderTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebarHeaderTemplate", required: false }] }], sidebarFooterTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebarFooterTemplate", required: false }] }] } });
955
-
956
- class EtosLayoutComponent {
957
- mode = input(null, ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
958
- sidebarHeaderTemplate = input(null, ...(ngDevMode ? [{ debugName: "sidebarHeaderTemplate" }] : /* istanbul ignore next */ []));
959
- sidebarFooterTemplate = input(null, ...(ngDevMode ? [{ debugName: "sidebarFooterTemplate" }] : /* istanbul ignore next */ []));
960
- layoutBrandTemplate = input(null, ...(ngDevMode ? [{ debugName: "layoutBrandTemplate" }] : /* istanbul ignore next */ []));
961
- layoutProfileTemplate = input(null, ...(ngDevMode ? [{ debugName: "layoutProfileTemplate" }] : /* istanbul ignore next */ []));
962
- layout = inject(LayoutService);
963
- resolvedMode = computed(() => this.mode() ?? this.layout.mode(), ...(ngDevMode ? [{ debugName: "resolvedMode" }] : /* istanbul ignore next */ []));
964
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: EtosLayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
965
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.13", type: EtosLayoutComponent, isStandalone: true, selector: "etos-layout", inputs: { mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, sidebarHeaderTemplate: { classPropertyName: "sidebarHeaderTemplate", publicName: "sidebarHeaderTemplate", isSignal: true, isRequired: false, transformFunction: null }, sidebarFooterTemplate: { classPropertyName: "sidebarFooterTemplate", publicName: "sidebarFooterTemplate", isSignal: true, isRequired: false, transformFunction: null }, layoutBrandTemplate: { classPropertyName: "layoutBrandTemplate", publicName: "layoutBrandTemplate", isSignal: true, isRequired: false, transformFunction: null }, layoutProfileTemplate: { classPropertyName: "layoutProfileTemplate", publicName: "layoutProfileTemplate", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-layout-mode": "resolvedMode()" }, classAttribute: "contents" }, ngImport: i0, template: `
966
- @switch (resolvedMode()) {
967
- @case ('empty') {
968
- <empty />
969
- }
970
- @case ('horizontal') {
971
- <horizontal [brandTemplate]="layoutBrandTemplate()" [profileTemplate]="layoutProfileTemplate()" />
972
- }
973
- @default {
974
- <vertical
975
- [sidebarHeaderTemplate]="sidebarHeaderTemplate() ?? layoutBrandTemplate()"
976
- [sidebarFooterTemplate]="sidebarFooterTemplate() ?? layoutProfileTemplate()" />
977
- }
978
- }
979
- `, isInline: true, dependencies: [{ kind: "component", type: EmptyLayout, selector: "empty" }, { kind: "component", type: HorizontalLayout, selector: "horizontal", inputs: ["brandTemplate", "profileTemplate"] }, { kind: "component", type: VerticalLayout, selector: "vertical", inputs: ["sidebarAppearance", "sidebarPosition", "ariaLabel", "sidebarHeaderTemplate", "sidebarFooterTemplate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
980
- }
981
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: EtosLayoutComponent, decorators: [{
982
- type: Component,
983
- args: [{
984
- selector: 'etos-layout',
985
- changeDetection: ChangeDetectionStrategy.OnPush,
986
- imports: [EmptyLayout, HorizontalLayout, VerticalLayout],
987
- host: {
988
- class: 'contents',
989
- '[attr.data-layout-mode]': 'resolvedMode()',
990
- },
991
- template: `
992
- @switch (resolvedMode()) {
993
- @case ('empty') {
994
- <empty />
995
- }
996
- @case ('horizontal') {
997
- <horizontal [brandTemplate]="layoutBrandTemplate()" [profileTemplate]="layoutProfileTemplate()" />
998
- }
999
- @default {
1000
- <vertical
1001
- [sidebarHeaderTemplate]="sidebarHeaderTemplate() ?? layoutBrandTemplate()"
1002
- [sidebarFooterTemplate]="sidebarFooterTemplate() ?? layoutProfileTemplate()" />
1003
- }
1004
- }
1005
- `,
1006
- }]
1007
- }], propDecorators: { mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], sidebarHeaderTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebarHeaderTemplate", required: false }] }], sidebarFooterTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebarFooterTemplate", required: false }] }], layoutBrandTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "layoutBrandTemplate", required: false }] }], layoutProfileTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "layoutProfileTemplate", required: false }] }] } });
1008
-
1009
- class ShellPagesComponent {
1010
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: ShellPagesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1011
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.13", type: ShellPagesComponent, isStandalone: true, selector: "shell-pages", host: { classAttribute: "block h-full min-h-0" }, ngImport: i0, template: `
1012
- <div class="flex h-full flex-col overflow-hidden">
1013
- <header class="flex h-12 min-w-0 shrink-0 items-center gap-2 border-b border-border px-2">
1014
- <ng-content select="[shell-pages-header]" />
1015
- </header>
1016
-
1017
- <main class="min-h-0 flex-1 overflow-y-auto scrollbar-thin scrollbar-thumb-primary scrollbar-track-primary/10">
1018
- <ng-content select="[shell-pages-main]" />
1019
- </main>
1020
-
1021
- <footer class="flex h-12 min-w-0 shrink-0 items-center gap-2 border-t border-border px-2">
1022
- <ng-content select="[shell-pages-footer]" />
1023
- </footer>
1024
- </div>
1025
- `, isInline: true, styles: ["footer:empty{display:none}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1026
- }
1027
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: ShellPagesComponent, decorators: [{
1028
- type: Component,
1029
- args: [{ selector: 'shell-pages', changeDetection: ChangeDetectionStrategy.OnPush, host: {
1030
- class: 'block h-full min-h-0',
1031
- }, template: `
1032
- <div class="flex h-full flex-col overflow-hidden">
1033
- <header class="flex h-12 min-w-0 shrink-0 items-center gap-2 border-b border-border px-2">
1034
- <ng-content select="[shell-pages-header]" />
1035
- </header>
1036
-
1037
- <main class="min-h-0 flex-1 overflow-y-auto scrollbar-thin scrollbar-thumb-primary scrollbar-track-primary/10">
1038
- <ng-content select="[shell-pages-main]" />
1039
- </main>
1040
-
1041
- <footer class="flex h-12 min-w-0 shrink-0 items-center gap-2 border-t border-border px-2">
1042
- <ng-content select="[shell-pages-footer]" />
1043
- </footer>
1044
- </div>
1045
- `, styles: ["footer:empty{display:none}\n"] }]
1046
- }] });
1047
-
1048
- /*
1049
- * Public API Surface of @ojiepermana/angular/etos
1050
- *
1051
- * Etos-specific implementation lives under projects/angular/brand/etos.
1052
- */
1053
- const ETOS_BRAND_VERSION = '0.0.1';
1054
-
1055
- /**
1056
- * Generated bundle index. Do not edit.
1057
- */
1058
-
1059
- export { ETOS_BRAND_NAME, ETOS_BRAND_VERSION, ETOS_LAYOUT_CONFIG, ETOS_THEME_CONFIG, EtosLayoutComponent, EtosThemeSwitcherComponent, ShellPagesComponent, provideEtosBrand, provideEtosLayout, provideEtosTheme };
1060
- //# sourceMappingURL=ojiepermana-angular-brand-etos.mjs.map