@ojiepermana/angular 0.1.1 → 21.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/README.md +41 -249
  2. package/fesm2022/ojiepermana-angular-chart.mjs +3714 -0
  3. package/fesm2022/ojiepermana-angular-chart.mjs.map +1 -0
  4. package/fesm2022/ojiepermana-angular-component.mjs +3463 -0
  5. package/fesm2022/ojiepermana-angular-component.mjs.map +1 -0
  6. package/fesm2022/ojiepermana-angular-layout.mjs +276 -408
  7. package/fesm2022/ojiepermana-angular-layout.mjs.map +1 -1
  8. package/fesm2022/ojiepermana-angular-navigation.mjs +2198 -404
  9. package/fesm2022/ojiepermana-angular-navigation.mjs.map +1 -1
  10. package/fesm2022/ojiepermana-angular-theme.mjs +381 -1
  11. package/fesm2022/ojiepermana-angular-theme.mjs.map +1 -1
  12. package/fesm2022/ojiepermana-angular.mjs +15 -1
  13. package/fesm2022/ojiepermana-angular.mjs.map +1 -1
  14. package/package.json +49 -36
  15. package/theme/styles/etos.css +38 -0
  16. package/theme/styles/index.css +32 -8
  17. package/theme/styles/themes/brand/etos/color.css +21 -0
  18. package/theme/styles/themes/brand/etos/style.css +50 -0
  19. package/theme/styles/themes/library/_components.css +63 -0
  20. package/theme/styles/themes/library/_layers.css +15 -0
  21. package/theme/styles/themes/library/_material-overrides.css +254 -0
  22. package/theme/styles/themes/library/_tokens.css +54 -0
  23. package/theme/styles/themes/library/color/amber.css +18 -0
  24. package/theme/styles/themes/library/color/blue.css +23 -0
  25. package/theme/styles/themes/library/color/green.css +18 -0
  26. package/theme/styles/themes/library/color/index.css +9 -0
  27. package/theme/styles/themes/library/color/purple.css +18 -0
  28. package/theme/styles/themes/library/color/red.css +18 -0
  29. package/theme/styles/themes/library/style/brutal.css +47 -0
  30. package/theme/styles/themes/library/style/default.css +51 -0
  31. package/theme/styles/themes/library/style/index.css +8 -0
  32. package/theme/styles/themes/library/style/sharp.css +47 -0
  33. package/theme/styles/themes/library/style/soft.css +47 -0
  34. package/theme/styles/themes/mode/dark.css +20 -0
  35. package/theme/styles/themes/mode/index.css +6 -0
  36. package/theme/styles/themes/mode/light.css +24 -0
  37. package/theme/styles/themes/taildwind.css +109 -0
  38. package/types/ojiepermana-angular-chart.d.ts +1094 -0
  39. package/types/ojiepermana-angular-component.d.ts +1174 -0
  40. package/types/ojiepermana-angular-layout.d.ts +123 -76
  41. package/types/ojiepermana-angular-navigation.d.ts +256 -116
  42. package/types/ojiepermana-angular-theme.d.ts +170 -1
  43. package/types/ojiepermana-angular.d.ts +2 -1
  44. package/fesm2022/ojiepermana-angular-internal.mjs +0 -489
  45. package/fesm2022/ojiepermana-angular-internal.mjs.map +0 -1
  46. package/fesm2022/ojiepermana-angular-navigation-horizontal.mjs +0 -721
  47. package/fesm2022/ojiepermana-angular-navigation-horizontal.mjs.map +0 -1
  48. package/fesm2022/ojiepermana-angular-navigation-vertical.mjs +0 -1647
  49. package/fesm2022/ojiepermana-angular-navigation-vertical.mjs.map +0 -1
  50. package/fesm2022/ojiepermana-angular-shell.mjs +0 -19
  51. package/fesm2022/ojiepermana-angular-shell.mjs.map +0 -1
  52. package/fesm2022/ojiepermana-angular-theme-component.mjs +0 -235
  53. package/fesm2022/ojiepermana-angular-theme-component.mjs.map +0 -1
  54. package/fesm2022/ojiepermana-angular-theme-directive.mjs +0 -29
  55. package/fesm2022/ojiepermana-angular-theme-directive.mjs.map +0 -1
  56. package/fesm2022/ojiepermana-angular-theme-service.mjs +0 -241
  57. package/fesm2022/ojiepermana-angular-theme-service.mjs.map +0 -1
  58. package/layout/README.md +0 -144
  59. package/layout/src/component/horizontal/horizontal.css +0 -130
  60. package/layout/src/component/vertical/vertical.css +0 -75
  61. package/layout/src/layout.css +0 -16
  62. package/navigation/README.md +0 -301
  63. package/navigation/horizontal/README.md +0 -49
  64. package/shell/README.md +0 -41
  65. package/styles/index.css +0 -2
  66. package/styles/resets.css +0 -22
  67. package/theme/README.md +0 -379
  68. package/theme/styles/adapters/material-ui/index.css +0 -205
  69. package/theme/styles/modes/dark.css +0 -84
  70. package/theme/styles/presets/colors/blue.css +0 -45
  71. package/theme/styles/presets/colors/brand.css +0 -52
  72. package/theme/styles/presets/colors/cyan.css +0 -45
  73. package/theme/styles/presets/colors/green.css +0 -45
  74. package/theme/styles/presets/colors/index.css +0 -7
  75. package/theme/styles/presets/colors/orange.css +0 -45
  76. package/theme/styles/presets/colors/purple.css +0 -45
  77. package/theme/styles/presets/colors/red.css +0 -45
  78. package/theme/styles/presets/styles/flat.css +0 -61
  79. package/theme/styles/presets/styles/glass.css +0 -28
  80. package/theme/styles/presets/styles/index.css +0 -2
  81. package/theme/styles/roles/index.css +0 -67
  82. package/theme/styles/tokens/foundation.css +0 -136
  83. package/theme/styles/tokens/semantic.css +0 -87
  84. package/theme/styles/utilities/index.css +0 -88
  85. package/types/ojiepermana-angular-internal.d.ts +0 -90
  86. package/types/ojiepermana-angular-navigation-horizontal.d.ts +0 -81
  87. package/types/ojiepermana-angular-navigation-vertical.d.ts +0 -262
  88. package/types/ojiepermana-angular-shell.d.ts +0 -14
  89. package/types/ojiepermana-angular-theme-component.d.ts +0 -46
  90. package/types/ojiepermana-angular-theme-directive.d.ts +0 -10
  91. package/types/ojiepermana-angular-theme-service.d.ts +0 -68
  92. /package/{navigation/vertical → chart}/README.md +0 -0
@@ -1,464 +1,332 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, inject, PLATFORM_ID, signal, effect, Injectable, Directive, ChangeDetectionStrategy, Component, computed, isDevMode, makeEnvironmentProviders } from '@angular/core';
3
- import { DOCUMENT, isPlatformBrowser } from '@angular/common';
4
- import { LocalStorageStateAdapter, libraryLucideConfigProvider } from '@ojiepermana/angular/internal';
2
+ import { InjectionToken, inject, signal, effect, Injectable, input, computed, ChangeDetectionStrategy, Component, makeEnvironmentProviders, provideEnvironmentInitializer } from '@angular/core';
5
3
  import { RouterOutlet } from '@angular/router';
6
- import { MatIconButton } from '@angular/material/button';
7
- import { MatTooltip } from '@angular/material/tooltip';
8
- import { LucideExpand, LucideShrink, LucidePanelLeft, LucidePanelTop, LucideAppWindow } from '@lucide/angular';
9
- import { MatMenu, MatMenuItem, MatMenuTrigger } from '@angular/material/menu';
4
+ import { SidebarComponent, TopbarComponent } from '@ojiepermana/angular/navigation';
5
+ import { ThemeService } from '@ojiepermana/angular/theme';
6
+ import { DOCUMENT } from '@angular/common';
10
7
 
11
- const DEFAULT_NG_LAYOUT_CONFIG = {
8
+ const LAYOUT_MODES = ['vertical', 'horizontal'];
9
+ const LAYOUT_WIDTHS = ['full', 'fixed'];
10
+ const MATERIAL_LAYOUT_CONFIG = new InjectionToken('MATERIAL_LAYOUT_CONFIG');
11
+ const DEFAULT_MATERIAL_LAYOUT_CONFIG = {
12
12
  defaultMode: 'vertical',
13
- defaultContainer: 'full',
13
+ defaultWidth: 'fixed',
14
+ storageKey: 'layout-mode',
15
+ widthStorageKey: 'layout-width',
14
16
  };
15
- const NG_LAYOUT_CONFIG = new InjectionToken('NG_LAYOUT_CONFIG', {
16
- providedIn: 'root',
17
- factory: () => ({ ...DEFAULT_NG_LAYOUT_CONFIG }),
18
- });
19
-
20
- const LAYOUT_MODES = ['vertical', 'horizontal', 'empty'];
21
- const LAYOUT_CONTAINERS = ['full', 'boxed'];
22
- const LAYOUT_MODE_SET = new Set(LAYOUT_MODES);
23
- const LAYOUT_CONTAINER_SET = new Set(LAYOUT_CONTAINERS);
24
17
  function isLayoutMode(value) {
25
- return typeof value === 'string' && LAYOUT_MODE_SET.has(value);
18
+ return LAYOUT_MODES.some((mode) => mode === value);
26
19
  }
27
- function isLayoutContainer(value) {
28
- return typeof value === 'string' && LAYOUT_CONTAINER_SET.has(value);
20
+ function isLayoutWidth(value) {
21
+ return LAYOUT_WIDTHS.some((width) => width === value);
29
22
  }
30
23
 
31
- const STORAGE_KEYS = {
32
- 'layout-mode': 'layout-mode',
33
- 'layout-container': 'layout-container',
34
- };
35
- const LEGACY_STORAGE_PREFIX = 'ng-theme:v2';
36
24
  class LayoutService {
37
- config = inject(NG_LAYOUT_CONFIG);
38
25
  document = inject(DOCUMENT);
39
- isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
40
- storage = new LocalStorageStateAdapter({
41
- isBrowser: this.isBrowser,
42
- storage: this.document.defaultView?.localStorage ?? null,
43
- keys: STORAGE_KEYS,
44
- legacyPrefix: LEGACY_STORAGE_PREFIX,
45
- });
46
- mode = signal(this.storage.read('layout-mode', this.config.defaultMode, isLayoutMode), ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
47
- container = signal(this.storage.read('layout-container', this.config.defaultContainer, isLayoutContainer), ...(ngDevMode ? [{ debugName: "container" }] : /* istanbul ignore next */ []));
26
+ config = this.resolveConfig();
27
+ _mode = signal(this.readPersistedMode() ?? this.config.defaultMode, ...(ngDevMode ? [{ debugName: "_mode" }] : /* istanbul ignore next */ []));
28
+ _width = signal(this.readPersistedWidth() ?? this.config.defaultWidth, ...(ngDevMode ? [{ debugName: "_width" }] : /* istanbul ignore next */ []));
29
+ mode = this._mode.asReadonly();
30
+ width = this._width.asReadonly();
48
31
  constructor() {
49
32
  effect(() => {
50
- if (this.isBrowser) {
51
- this.applyToDOM();
52
- }
33
+ this.persistMode(this._mode());
34
+ });
35
+ effect(() => {
36
+ this.persistWidth(this._width());
53
37
  });
54
38
  }
55
- /**
56
- * Updates the active shell mode and persists the value for future sessions.
57
- */
58
- setMode(value) {
59
- this.storage.persist('layout-mode', value);
60
- this.mode.set(value);
39
+ setMode(mode) {
40
+ this._mode.set(mode);
41
+ }
42
+ toggleMode() {
43
+ this._mode.update((mode) => (mode === 'vertical' ? 'horizontal' : 'vertical'));
44
+ }
45
+ setWidth(width) {
46
+ this._width.set(width);
61
47
  }
62
- /**
63
- * Updates the active container width mode and persists the value for future sessions.
64
- */
65
- setContainer(value) {
66
- this.storage.persist('layout-container', value);
67
- this.container.set(value);
48
+ toggleWidth() {
49
+ this._width.update((width) => (width === 'fixed' ? 'full' : 'fixed'));
68
50
  }
69
- /**
70
- * Clears persisted layout state and restores the configured defaults for mode and container.
71
- */
72
- reset() {
73
- this.storage.clear('layout-mode');
74
- this.storage.clear('layout-container');
75
- this.mode.set(this.config.defaultMode);
76
- this.container.set(this.config.defaultContainer);
51
+ resolveConfig() {
52
+ const config = inject(MATERIAL_LAYOUT_CONFIG, { optional: true }) ?? {};
53
+ const configuredMode = config.mode ?? config.defaultMode;
54
+ const configuredWidth = config.width;
55
+ return {
56
+ defaultMode: isLayoutMode(configuredMode) ? configuredMode : DEFAULT_MATERIAL_LAYOUT_CONFIG.defaultMode,
57
+ defaultWidth: isLayoutWidth(configuredWidth) ? configuredWidth : DEFAULT_MATERIAL_LAYOUT_CONFIG.defaultWidth,
58
+ storageKey: config.storageKey ?? DEFAULT_MATERIAL_LAYOUT_CONFIG.storageKey,
59
+ widthStorageKey: config.widthStorageKey ?? DEFAULT_MATERIAL_LAYOUT_CONFIG.widthStorageKey,
60
+ };
77
61
  }
78
- applyToDOM() {
79
- const element = this.document.documentElement;
80
- element.dataset['layoutMode'] = this.mode();
81
- element.dataset['layoutContainer'] = this.container();
62
+ readPersistedMode() {
63
+ const key = this.config.storageKey;
64
+ if (!key)
65
+ return null;
66
+ try {
67
+ const value = this.document.defaultView?.localStorage?.getItem(key);
68
+ return isLayoutMode(value) ? value : null;
69
+ }
70
+ catch {
71
+ return null;
72
+ }
73
+ }
74
+ readPersistedWidth() {
75
+ const key = this.config.widthStorageKey;
76
+ if (!key)
77
+ return null;
78
+ try {
79
+ const value = this.document.defaultView?.localStorage?.getItem(key);
80
+ return isLayoutWidth(value) ? value : null;
81
+ }
82
+ catch {
83
+ return null;
84
+ }
82
85
  }
83
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: LayoutService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
84
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: LayoutService, providedIn: 'root' });
86
+ persistMode(mode) {
87
+ const key = this.config.storageKey;
88
+ if (!key)
89
+ return;
90
+ try {
91
+ this.document.defaultView?.localStorage?.setItem(key, mode);
92
+ }
93
+ catch {
94
+ /* ignore */
95
+ }
96
+ }
97
+ persistWidth(width) {
98
+ const key = this.config.widthStorageKey;
99
+ if (!key)
100
+ return;
101
+ try {
102
+ this.document.defaultView?.localStorage?.setItem(key, width);
103
+ }
104
+ catch {
105
+ /* ignore */
106
+ }
107
+ }
108
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LayoutService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
109
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LayoutService, providedIn: 'root' });
85
110
  }
86
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: LayoutService, decorators: [{
111
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LayoutService, decorators: [{
87
112
  type: Injectable,
88
113
  args: [{ providedIn: 'root' }]
89
114
  }], ctorParameters: () => [] });
90
115
 
91
- class LayoutHostDirective {
116
+ /**
117
+ * Vertical layout — sidebar + main (scrollable).
118
+ *
119
+ * Data navigasi diambil dari `NavigationService` (register via
120
+ * `NavigationService.registerItems()` di bootstrap). Main memegang
121
+ * `<router-outlet>` dan scroll jika konten panjang.
122
+ *
123
+ * Markup:
124
+ * ```html
125
+ * <vertical>
126
+ * <!-- sidebar + router-outlet dirender oleh komponen -->
127
+ * </vertical>
128
+ * ```
129
+ */
130
+ class VerticalLayoutComponent {
92
131
  layout = inject(LayoutService);
93
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: LayoutHostDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
94
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.8", type: LayoutHostDirective, isStandalone: true, selector: "[ngtLayoutHost]", host: { properties: { "attr.data-layout-mode": "layout.mode()", "attr.data-layout-container": "layout.container()" } }, ngImport: i0 });
132
+ theme = inject(ThemeService);
133
+ sidebarAppearance = input('default', ...(ngDevMode ? [{ debugName: "sidebarAppearance" }] : /* istanbul ignore next */ []));
134
+ sidebarPosition = input('left', ...(ngDevMode ? [{ debugName: "sidebarPosition" }] : /* istanbul ignore next */ []));
135
+ sidebarMode = input('side', ...(ngDevMode ? [{ debugName: "sidebarMode" }] : /* istanbul ignore next */ []));
136
+ ariaLabel = input('Primary', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
137
+ layoutWidth = this.layout.width;
138
+ themeStyle = this.theme.style;
139
+ shellBorderWidth = computed(() => (this.layoutWidth() === 'fixed' ? 'var(--border-width)' : null), ...(ngDevMode ? [{ debugName: "shellBorderWidth" }] : /* istanbul ignore next */ []));
140
+ dividerBorderWidth = computed(() => 'var(--border-width)', ...(ngDevMode ? [{ debugName: "dividerBorderWidth" }] : /* istanbul ignore next */ []));
141
+ hostClasses = computed(() => {
142
+ const classes = ['flex', 'h-dvh', 'w-full', 'overflow-hidden', 'bg-background', 'text-foreground'];
143
+ if (this.layoutWidth() === 'fixed') {
144
+ classes.push('lg:mx-auto', 'lg:my-8', 'lg:h-[calc(100dvh-4rem)]', 'lg:w-[calc(100%-4rem)]', 'lg:border', 'lg:border-border', 'lg:rounded-lg', 'lg:shadow-sm');
145
+ }
146
+ return classes.join(' ');
147
+ }, ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
148
+ mainClasses = computed(() => {
149
+ const classes = ['flex-1', 'overflow-auto'];
150
+ if (this.layoutWidth() === 'fixed')
151
+ classes.push('lg:mx-auto', 'lg:max-w-7xl');
152
+ return classes.join(' ');
153
+ }, ...(ngDevMode ? [{ debugName: "mainClasses" }] : /* istanbul ignore next */ []));
154
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: VerticalLayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
155
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.9", type: VerticalLayoutComponent, 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 }, sidebarMode: { classPropertyName: "sidebarMode", publicName: "sidebarMode", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "hostClasses()", "attr.data-layout-width": "layoutWidth()", "attr.data-style": "themeStyle()", "style.border-width": "shellBorderWidth()" } }, ngImport: i0, template: `
156
+ <ui-sidebar
157
+ [appearance]="sidebarAppearance()"
158
+ [position]="sidebarPosition()"
159
+ [ariaLabel]="ariaLabel()"
160
+ [style.border-left-width]="dividerBorderWidth()"
161
+ [style.border-right-width]="dividerBorderWidth()" />
162
+ <main [class]="mainClasses()">
163
+ <router-outlet />
164
+ </main>
165
+ `, isInline: true, dependencies: [{ kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "component", type: SidebarComponent, selector: "ui-sidebar", inputs: ["items", "navigationId", "appearance", "position", "ariaLabel", "header", "class", "autoMobile", "autoRegister"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
95
166
  }
96
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: LayoutHostDirective, decorators: [{
97
- type: Directive,
167
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: VerticalLayoutComponent, decorators: [{
168
+ type: Component,
98
169
  args: [{
99
- selector: '[ngtLayoutHost]',
170
+ selector: 'vertical',
171
+ changeDetection: ChangeDetectionStrategy.OnPush,
172
+ imports: [RouterOutlet, SidebarComponent],
100
173
  host: {
101
- '[attr.data-layout-mode]': 'layout.mode()',
102
- '[attr.data-layout-container]': 'layout.container()',
174
+ '[class]': 'hostClasses()',
175
+ '[attr.data-layout-width]': 'layoutWidth()',
176
+ '[attr.data-style]': 'themeStyle()',
177
+ '[style.border-width]': 'shellBorderWidth()',
103
178
  },
179
+ template: `
180
+ <ui-sidebar
181
+ [appearance]="sidebarAppearance()"
182
+ [position]="sidebarPosition()"
183
+ [ariaLabel]="ariaLabel()"
184
+ [style.border-left-width]="dividerBorderWidth()"
185
+ [style.border-right-width]="dividerBorderWidth()" />
186
+ <main [class]="mainClasses()">
187
+ <router-outlet />
188
+ </main>
189
+ `,
104
190
  }]
105
- }] });
191
+ }], propDecorators: { sidebarAppearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebarAppearance", required: false }] }], sidebarPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebarPosition", required: false }] }], sidebarMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebarMode", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }] } });
106
192
 
107
- class LayoutElementDirective {
108
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: LayoutElementDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
109
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.8", type: LayoutElementDirective, isStandalone: true, selector: "layout", ngImport: i0 });
110
- }
111
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: LayoutElementDirective, decorators: [{
112
- type: Directive,
113
- args: [{
114
- selector: 'layout',
115
- }]
116
- }] });
117
- class ContentElementDirective {
118
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ContentElementDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
119
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.8", type: ContentElementDirective, isStandalone: true, selector: "content,shell-content", ngImport: i0 });
120
- }
121
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ContentElementDirective, decorators: [{
122
- type: Directive,
123
- args: [{
124
- selector: 'content,shell-content',
125
- }]
126
- }] });
127
- class BrandElementDirective {
128
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: BrandElementDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
129
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.8", type: BrandElementDirective, isStandalone: true, selector: "brand", ngImport: i0 });
130
- }
131
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: BrandElementDirective, decorators: [{
132
- type: Directive,
133
- args: [{
134
- selector: 'brand',
135
- }]
136
- }] });
137
- class NavigationElementDirective {
138
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NavigationElementDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
139
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.8", type: NavigationElementDirective, isStandalone: true, selector: "navigation", ngImport: i0 });
140
- }
141
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NavigationElementDirective, decorators: [{
142
- type: Directive,
143
- args: [{
144
- selector: 'navigation',
145
- }]
146
- }] });
147
- class ActionElementDirective {
148
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ActionElementDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
149
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.8", type: ActionElementDirective, isStandalone: true, selector: "action", ngImport: i0 });
150
- }
151
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ActionElementDirective, decorators: [{
152
- type: Directive,
153
- args: [{
154
- selector: 'action',
155
- }]
156
- }] });
157
-
158
- class LayoutHorizontalComponent {
159
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: LayoutHorizontalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
160
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.8", type: LayoutHorizontalComponent, isStandalone: true, selector: "horizontal", host: { properties: { "attr.data-layout-mode": "\"horizontal\"" } }, ngImport: i0, template: `
161
- <layout>
162
- <shell-content>
163
- <header>
164
- <brand>
165
- <ng-content select="[brand]"></ng-content>
166
- </brand>
167
-
168
- <navigation>
169
- <div class="layout-horizontal-navigation-slot">
170
- <ng-content select="[navigation]"></ng-content>
171
- </div>
172
- </navigation>
173
-
174
- <action>
175
- <ng-content select="[action]"></ng-content>
176
- </action>
177
- </header>
178
-
179
- <main>
180
- <router-outlet />
181
- </main>
182
- </shell-content>
183
- </layout>
184
- `, isInline: true, styles: [":host{display:block;block-size:100dvh;min-block-size:100dvh;overflow:hidden}:host>layout{display:block;block-size:100dvh;min-block-size:100dvh;padding:var(--layout-shell-padding);background:var(--background);color:var(--foreground);overflow:hidden;transition:var(--ngt-chrome-transition)}:host>layout>:is(content,shell-content){display:flex;flex-direction:column;inline-size:min(100%,var(--layout-shell-max-width));block-size:calc(100dvh - (var(--layout-shell-padding) * 2));min-block-size:0;margin-inline:auto;border:1px solid var(--container-border);border-radius:var(--layout-shell-radius);background:var(--container-surface);color:var(--container-foreground);box-shadow:var(--container-shadow);backdrop-filter:var(--container-backdrop);-webkit-backdrop-filter:var(--container-backdrop);overflow:hidden;transition:var(--ngt-shell-transition)}:host>layout>:is(content,shell-content)>header{display:var(--layout-header-display);grid-template-areas:\"brand navigation action\";grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:1rem;min-block-size:var(--layout-header-height);padding:.875rem 1rem;border-bottom:1px solid var(--nav-border);background:var(--nav-surface);color:var(--nav-foreground);transition:var(--ngt-chrome-transition)}:host>layout>:is(content,shell-content)>header>brand,:host>layout>:is(content,shell-content)>header>action{display:flex;min-inline-size:0;align-items:center}:host>layout>:is(content,shell-content)>header>brand{grid-area:brand;justify-content:flex-start;justify-self:start}:host>layout>:is(content,shell-content)>header>navigation{grid-area:navigation;display:flex;inline-size:100%;min-inline-size:0;align-items:center;justify-self:stretch;justify-content:center;gap:.75rem;flex-wrap:wrap}:host>layout>:is(content,shell-content)>header>navigation>.layout-horizontal-navigation-slot{flex:0 1 auto;display:block;min-inline-size:0;max-inline-size:100%}:host>layout>:is(content,shell-content)>header>action{grid-area:action;justify-content:flex-end;justify-self:end;gap:.5rem}:host>layout>:is(content,shell-content)>main{flex:1 1 auto;block-size:100%;min-inline-size:0;min-block-size:0;overflow:auto;background-color:var(--background);background-image:linear-gradient(180deg,color-mix(in oklab,var(--background) 94%,white 6%),var(--background));color:var(--foreground);transition:background-color var(--ngt-motion-duration-medium) var(--ngt-motion-ease-standard),color var(--ngt-motion-duration-fast) var(--ngt-motion-ease-standard)}@media(max-width:60rem){:host>layout>:is(content,shell-content)>header{grid-template-areas:\"brand action\" \"navigation navigation\";grid-template-columns:minmax(0,1fr) auto;align-items:flex-start}:host>layout>:is(content,shell-content)>header>navigation{justify-content:center}}@media(max-width:40rem){:host>layout>:is(content,shell-content)>header{grid-template-areas:\"brand\" \"navigation\" \"action\";grid-template-columns:1fr}:host>layout>:is(content,shell-content)>header>action{justify-content:flex-end}}\n"], dependencies: [{ kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "directive", type: LayoutElementDirective, selector: "layout" }, { kind: "directive", type: ContentElementDirective, selector: "content,shell-content" }, { kind: "directive", type: BrandElementDirective, selector: "brand" }, { kind: "directive", type: NavigationElementDirective, selector: "navigation" }, { kind: "directive", type: ActionElementDirective, selector: "action" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
185
- }
186
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: LayoutHorizontalComponent, decorators: [{
187
- type: Component,
188
- args: [{ selector: 'horizontal', imports: [
189
- RouterOutlet,
190
- LayoutElementDirective,
191
- ContentElementDirective,
192
- BrandElementDirective,
193
- NavigationElementDirective,
194
- ActionElementDirective,
195
- ], host: {
196
- '[attr.data-layout-mode]': '"horizontal"',
197
- }, template: `
198
- <layout>
199
- <shell-content>
200
- <header>
201
- <brand>
202
- <ng-content select="[brand]"></ng-content>
203
- </brand>
204
-
205
- <navigation>
206
- <div class="layout-horizontal-navigation-slot">
207
- <ng-content select="[navigation]"></ng-content>
208
- </div>
209
- </navigation>
210
-
211
- <action>
212
- <ng-content select="[action]"></ng-content>
213
- </action>
214
- </header>
215
-
216
- <main>
217
- <router-outlet />
218
- </main>
219
- </shell-content>
220
- </layout>
221
- `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block;block-size:100dvh;min-block-size:100dvh;overflow:hidden}:host>layout{display:block;block-size:100dvh;min-block-size:100dvh;padding:var(--layout-shell-padding);background:var(--background);color:var(--foreground);overflow:hidden;transition:var(--ngt-chrome-transition)}:host>layout>:is(content,shell-content){display:flex;flex-direction:column;inline-size:min(100%,var(--layout-shell-max-width));block-size:calc(100dvh - (var(--layout-shell-padding) * 2));min-block-size:0;margin-inline:auto;border:1px solid var(--container-border);border-radius:var(--layout-shell-radius);background:var(--container-surface);color:var(--container-foreground);box-shadow:var(--container-shadow);backdrop-filter:var(--container-backdrop);-webkit-backdrop-filter:var(--container-backdrop);overflow:hidden;transition:var(--ngt-shell-transition)}:host>layout>:is(content,shell-content)>header{display:var(--layout-header-display);grid-template-areas:\"brand navigation action\";grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:1rem;min-block-size:var(--layout-header-height);padding:.875rem 1rem;border-bottom:1px solid var(--nav-border);background:var(--nav-surface);color:var(--nav-foreground);transition:var(--ngt-chrome-transition)}:host>layout>:is(content,shell-content)>header>brand,:host>layout>:is(content,shell-content)>header>action{display:flex;min-inline-size:0;align-items:center}:host>layout>:is(content,shell-content)>header>brand{grid-area:brand;justify-content:flex-start;justify-self:start}:host>layout>:is(content,shell-content)>header>navigation{grid-area:navigation;display:flex;inline-size:100%;min-inline-size:0;align-items:center;justify-self:stretch;justify-content:center;gap:.75rem;flex-wrap:wrap}:host>layout>:is(content,shell-content)>header>navigation>.layout-horizontal-navigation-slot{flex:0 1 auto;display:block;min-inline-size:0;max-inline-size:100%}:host>layout>:is(content,shell-content)>header>action{grid-area:action;justify-content:flex-end;justify-self:end;gap:.5rem}:host>layout>:is(content,shell-content)>main{flex:1 1 auto;block-size:100%;min-inline-size:0;min-block-size:0;overflow:auto;background-color:var(--background);background-image:linear-gradient(180deg,color-mix(in oklab,var(--background) 94%,white 6%),var(--background));color:var(--foreground);transition:background-color var(--ngt-motion-duration-medium) var(--ngt-motion-ease-standard),color var(--ngt-motion-duration-fast) var(--ngt-motion-ease-standard)}@media(max-width:60rem){:host>layout>:is(content,shell-content)>header{grid-template-areas:\"brand action\" \"navigation navigation\";grid-template-columns:minmax(0,1fr) auto;align-items:flex-start}:host>layout>:is(content,shell-content)>header>navigation{justify-content:center}}@media(max-width:40rem){:host>layout>:is(content,shell-content)>header{grid-template-areas:\"brand\" \"navigation\" \"action\";grid-template-columns:1fr}:host>layout>:is(content,shell-content)>header>action{justify-content:flex-end}}\n"] }]
222
- }] });
223
-
224
- class LayoutVerticalComponent {
225
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: LayoutVerticalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
226
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.8", type: LayoutVerticalComponent, isStandalone: true, selector: "vertical", host: { properties: { "attr.data-layout-mode": "\"vertical\"" } }, ngImport: i0, template: `
227
- <layout>
228
- <shell-content>
229
- <aside>
230
- <ng-content select="[navigation]"></ng-content>
231
- </aside>
232
- <main>
233
- <router-outlet />
234
- </main>
235
- </shell-content>
236
- </layout>
237
- `, isInline: true, styles: [":host{display:block;block-size:100dvh;min-block-size:100dvh;overflow:hidden}:host>layout{display:block;block-size:100dvh;min-block-size:100dvh;padding:var(--layout-shell-padding);background:var(--background);color:var(--foreground);overflow:hidden;transition:var(--ngt-chrome-transition)}:host>layout>:is(content,shell-content){display:flex;flex-direction:column;inline-size:min(100%,var(--layout-shell-max-width));block-size:calc(100dvh - (var(--layout-shell-padding) * 2));min-block-size:0;margin-inline:auto;border:1px solid var(--container-border);border-radius:var(--layout-shell-radius);background:var(--container-surface);color:var(--container-foreground);box-shadow:var(--container-shadow);backdrop-filter:var(--container-backdrop);-webkit-backdrop-filter:var(--container-backdrop);overflow:hidden;transition:var(--ngt-shell-transition)}:host>layout>:is(content,shell-content)>aside{display:var(--layout-sidebar-display);flex-direction:column;inline-size:100%;min-block-size:0;border-bottom:1px solid var(--sidebar-chrome-border);background:var(--sidebar-chrome-surface);color:var(--sidebar-chrome-foreground);overflow:auto;transition:var(--ngt-chrome-transition)}:host>layout>:is(content,shell-content)>main{flex:1 1 auto;block-size:100%;min-inline-size:0;min-block-size:0;overflow:auto;background-color:var(--background);background-image:linear-gradient(180deg,color-mix(in oklab,var(--background) 94%,white 6%),var(--background));color:var(--foreground);transition:background-color var(--ngt-motion-duration-medium) var(--ngt-motion-ease-standard),color var(--ngt-motion-duration-fast) var(--ngt-motion-ease-standard)}@media(min-width:48rem){:host>layout>:is(content,shell-content){flex-direction:row}:host>layout>:is(content,shell-content)>aside{flex:0 0 var(--layout-sidebar-width);inline-size:var(--layout-sidebar-width);min-inline-size:var(--layout-sidebar-width);border-bottom:0;border-right:1px solid var(--sidebar-chrome-border)}}\n"], dependencies: [{ kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "directive", type: LayoutElementDirective, selector: "layout" }, { kind: "directive", type: ContentElementDirective, selector: "content,shell-content" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
238
- }
239
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: LayoutVerticalComponent, decorators: [{
240
- type: Component,
241
- args: [{ selector: 'vertical', imports: [RouterOutlet, LayoutElementDirective, ContentElementDirective], host: {
242
- '[attr.data-layout-mode]': '"vertical"',
243
- }, template: `
244
- <layout>
245
- <shell-content>
246
- <aside>
247
- <ng-content select="[navigation]"></ng-content>
248
- </aside>
249
- <main>
250
- <router-outlet />
251
- </main>
252
- </shell-content>
253
- </layout>
254
- `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block;block-size:100dvh;min-block-size:100dvh;overflow:hidden}:host>layout{display:block;block-size:100dvh;min-block-size:100dvh;padding:var(--layout-shell-padding);background:var(--background);color:var(--foreground);overflow:hidden;transition:var(--ngt-chrome-transition)}:host>layout>:is(content,shell-content){display:flex;flex-direction:column;inline-size:min(100%,var(--layout-shell-max-width));block-size:calc(100dvh - (var(--layout-shell-padding) * 2));min-block-size:0;margin-inline:auto;border:1px solid var(--container-border);border-radius:var(--layout-shell-radius);background:var(--container-surface);color:var(--container-foreground);box-shadow:var(--container-shadow);backdrop-filter:var(--container-backdrop);-webkit-backdrop-filter:var(--container-backdrop);overflow:hidden;transition:var(--ngt-shell-transition)}:host>layout>:is(content,shell-content)>aside{display:var(--layout-sidebar-display);flex-direction:column;inline-size:100%;min-block-size:0;border-bottom:1px solid var(--sidebar-chrome-border);background:var(--sidebar-chrome-surface);color:var(--sidebar-chrome-foreground);overflow:auto;transition:var(--ngt-chrome-transition)}:host>layout>:is(content,shell-content)>main{flex:1 1 auto;block-size:100%;min-inline-size:0;min-block-size:0;overflow:auto;background-color:var(--background);background-image:linear-gradient(180deg,color-mix(in oklab,var(--background) 94%,white 6%),var(--background));color:var(--foreground);transition:background-color var(--ngt-motion-duration-medium) var(--ngt-motion-ease-standard),color var(--ngt-motion-duration-fast) var(--ngt-motion-ease-standard)}@media(min-width:48rem){:host>layout>:is(content,shell-content){flex-direction:row}:host>layout>:is(content,shell-content)>aside{flex:0 0 var(--layout-sidebar-width);inline-size:var(--layout-sidebar-width);min-inline-size:var(--layout-sidebar-width);border-bottom:0;border-right:1px solid var(--sidebar-chrome-border)}}\n"] }]
255
- }] });
256
-
257
- class LayoutContainerSwitcherComponent {
193
+ /**
194
+ * Horizontal layout topbar (h-12) + main (scrollable).
195
+ *
196
+ * Data navigasi diambil dari `NavigationService`.
197
+ * Consumer app dapat memproyeksikan brand kiri dan profile kanan.
198
+ *
199
+ * Markup:
200
+ * ```html
201
+ * <horizontal>
202
+ * <a ui-layout-brand>Brand</a>
203
+ * <button ui-layout-profile type="button">Profile</button>
204
+ * </horizontal>
205
+ * ```
206
+ */
207
+ class HorizontalLayoutComponent {
258
208
  layout = inject(LayoutService);
259
- label = computed(() => this.layout.container() === 'boxed' ? 'Boxed layout container enabled' : 'Boxed layout container disabled', ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
260
- toggle() {
261
- this.layout.setContainer(this.layout.container() === 'full' ? 'boxed' : 'full');
262
- }
263
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: LayoutContainerSwitcherComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
264
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: LayoutContainerSwitcherComponent, isStandalone: true, selector: "layout-container-switcher", providers: [libraryLucideConfigProvider], ngImport: i0, template: `
265
- <button
266
- class="ngt-icon-button"
267
- type="button"
268
- mat-icon-button
269
- aria-label="Boxed layout container"
270
- [attr.aria-pressed]="layout.container() === 'boxed'"
271
- [matTooltip]="label()"
272
- (click)="toggle()">
273
- @if (layout.container() === 'full') {
274
- <svg lucideExpand aria-hidden="true"></svg>
275
- } @else {
276
- <svg lucideShrink aria-hidden="true"></svg>
277
- }
278
- </button>
279
- `, isInline: true, dependencies: [{ kind: "component", type: MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: LucideExpand, selector: "svg[lucideExpand]" }, { kind: "component", type: LucideShrink, selector: "svg[lucideShrink]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
209
+ theme = inject(ThemeService);
210
+ topbarAppearance = input('default', ...(ngDevMode ? [{ debugName: "topbarAppearance" }] : /* istanbul ignore next */ []));
211
+ ariaLabel = input('Primary', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
212
+ layoutWidth = this.layout.width;
213
+ themeStyle = this.theme.style;
214
+ shellBorderWidth = computed(() => (this.layoutWidth() === 'fixed' ? 'var(--border-width)' : null), ...(ngDevMode ? [{ debugName: "shellBorderWidth" }] : /* istanbul ignore next */ []));
215
+ dividerBorderWidth = computed(() => 'var(--border-width)', ...(ngDevMode ? [{ debugName: "dividerBorderWidth" }] : /* istanbul ignore next */ []));
216
+ hostClasses = computed(() => {
217
+ const classes = ['flex', 'h-dvh', 'w-full', 'flex-col', 'overflow-hidden', 'bg-background', 'text-foreground'];
218
+ if (this.layoutWidth() === 'fixed') {
219
+ classes.push('lg:mx-auto', 'lg:my-8', 'lg:max-w-7xl', 'lg:h-[calc(100dvh-4rem)]', 'lg:w-[calc(100%-4rem)]', 'lg:border', 'lg:border-border', 'lg:rounded-lg', 'lg:shadow-sm');
220
+ }
221
+ return classes.join(' ');
222
+ }, ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
223
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: HorizontalLayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
224
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.9", type: HorizontalLayoutComponent, isStandalone: true, selector: "horizontal", inputs: { topbarAppearance: { classPropertyName: "topbarAppearance", publicName: "topbarAppearance", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "hostClasses()", "attr.data-layout-width": "layoutWidth()", "attr.data-style": "themeStyle()", "style.border-width": "shellBorderWidth()" } }, ngImport: i0, template: `
225
+ <ui-topbar
226
+ class="h-12 w-full shrink-0 border-b border-border"
227
+ [style.border-bottom-width]="dividerBorderWidth()"
228
+ [appearance]="topbarAppearance()"
229
+ [ariaLabel]="ariaLabel()">
230
+ <div ui-topbar-start class="flex min-w-0 items-center">
231
+ <ng-content select="[ui-layout-brand],[ui-topbar-start]" />
232
+ </div>
233
+ <div ui-topbar-end class="flex min-w-0 items-center">
234
+ <ng-content select="[ui-layout-profile],[ui-topbar-end]" />
235
+ </div>
236
+ </ui-topbar>
237
+ <main class="flex-1 overflow-auto">
238
+ <router-outlet />
239
+ </main>
240
+ `, isInline: true, dependencies: [{ kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "component", type: TopbarComponent, selector: "ui-topbar", inputs: ["items", "navigationId", "appearance", "ariaLabel", "class", "autoRegister", "showHamburger", "hamburgerLabel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
280
241
  }
281
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: LayoutContainerSwitcherComponent, decorators: [{
242
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: HorizontalLayoutComponent, decorators: [{
282
243
  type: Component,
283
244
  args: [{
284
- selector: 'layout-container-switcher',
285
- imports: [MatIconButton, MatTooltip, LucideExpand, LucideShrink],
286
- providers: [libraryLucideConfigProvider],
245
+ selector: 'horizontal',
287
246
  changeDetection: ChangeDetectionStrategy.OnPush,
247
+ imports: [RouterOutlet, TopbarComponent],
248
+ host: {
249
+ '[class]': 'hostClasses()',
250
+ '[attr.data-layout-width]': 'layoutWidth()',
251
+ '[attr.data-style]': 'themeStyle()',
252
+ '[style.border-width]': 'shellBorderWidth()',
253
+ },
288
254
  template: `
289
- <button
290
- class="ngt-icon-button"
291
- type="button"
292
- mat-icon-button
293
- aria-label="Boxed layout container"
294
- [attr.aria-pressed]="layout.container() === 'boxed'"
295
- [matTooltip]="label()"
296
- (click)="toggle()">
297
- @if (layout.container() === 'full') {
298
- <svg lucideExpand aria-hidden="true"></svg>
299
- } @else {
300
- <svg lucideShrink aria-hidden="true"></svg>
301
- }
302
- </button>
255
+ <ui-topbar
256
+ class="h-12 w-full shrink-0 border-b border-border"
257
+ [style.border-bottom-width]="dividerBorderWidth()"
258
+ [appearance]="topbarAppearance()"
259
+ [ariaLabel]="ariaLabel()">
260
+ <div ui-topbar-start class="flex min-w-0 items-center">
261
+ <ng-content select="[ui-layout-brand],[ui-topbar-start]" />
262
+ </div>
263
+ <div ui-topbar-end class="flex min-w-0 items-center">
264
+ <ng-content select="[ui-layout-profile],[ui-topbar-end]" />
265
+ </div>
266
+ </ui-topbar>
267
+ <main class="flex-1 overflow-auto">
268
+ <router-outlet />
269
+ </main>
303
270
  `,
304
271
  }]
305
- }] });
272
+ }], propDecorators: { topbarAppearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "topbarAppearance", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }] } });
306
273
 
307
- class LayoutModeSwitcherComponent {
308
- layout = inject(LayoutService);
309
- // Empty mode remains programmatic so the UI only exposes the two navigational shell variants.
310
- options = [
311
- { value: 'vertical', label: 'Vertical Sidebar' },
312
- { value: 'horizontal', label: 'Horizontal Navbar' },
313
- ];
314
- currentOption = computed(() => this.options.find((option) => option.value === this.layout.mode()) ?? {
315
- value: 'empty',
316
- label: 'Content Only',
317
- }, ...(ngDevMode ? [{ debugName: "currentOption" }] : /* istanbul ignore next */ []));
318
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: LayoutModeSwitcherComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
319
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: LayoutModeSwitcherComponent, isStandalone: true, selector: "layout-mode-switcher", providers: [libraryLucideConfigProvider], ngImport: i0, template: `
320
- <button
321
- class="ngt-icon-button"
322
- type="button"
323
- mat-icon-button
324
- [attr.aria-label]="'Layout mode: ' + currentOption().label"
325
- [matTooltip]="'Layout mode: ' + currentOption().label"
326
- [matMenuTriggerFor]="menu">
327
- @switch (layout.mode()) {
328
- @case ('vertical') {
329
- <svg lucidePanelLeft aria-hidden="true"></svg>
330
- }
331
- @case ('horizontal') {
332
- <svg lucidePanelTop aria-hidden="true"></svg>
333
- }
334
- @default {
335
- <svg lucideAppWindow aria-hidden="true"></svg>
336
- }
337
- }
338
- </button>
339
-
340
- <mat-menu #menu="matMenu">
341
- @for (option of options; track option.value) {
342
- <button
343
- type="button"
344
- mat-menu-item
345
- role="menuitemradio"
346
- [attr.aria-checked]="layout.mode() === option.value"
347
- (click)="layout.setMode(option.value)">
348
- @switch (option.value) {
349
- @case ('vertical') {
350
- <svg lucidePanelLeft aria-hidden="true"></svg>
351
- }
352
- @case ('horizontal') {
353
- <svg lucidePanelTop aria-hidden="true"></svg>
354
- }
355
- @default {
356
- <svg lucideAppWindow aria-hidden="true"></svg>
357
- }
358
- }
359
- <span>{{ option.label }}</span>
360
- </button>
361
- }
362
- </mat-menu>
363
- `, isInline: true, dependencies: [{ kind: "component", type: MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "component", type: LucidePanelLeft, selector: "svg[lucidePanelLeft], svg[lucideSidebar]" }, { kind: "component", type: LucidePanelTop, selector: "svg[lucidePanelTop]" }, { kind: "component", type: LucideAppWindow, selector: "svg[lucideAppWindow]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
274
+ /**
275
+ * Empty layout full-viewport, flex-centered main.
276
+ *
277
+ * Cocok untuk halaman login / error / onboarding (pola shadcn `login-05`).
278
+ * Konten dirender lewat `<router-outlet>`; consumer men-style card / form
279
+ * milik halaman route sendiri.
280
+ *
281
+ * Markup:
282
+ * ```html
283
+ * <empty>
284
+ * <!-- router-outlet dirender oleh komponen -->
285
+ * </empty>
286
+ * ```
287
+ */
288
+ class EmptyLayoutComponent {
289
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: EmptyLayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
290
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.9", type: EmptyLayoutComponent, isStandalone: true, selector: "empty", host: { classAttribute: "flex min-h-dvh w-full items-center justify-center bg-background p-6 text-foreground" }, ngImport: i0, template: `
291
+ <main class="w-full max-w-sm">
292
+ <router-outlet />
293
+ </main>
294
+ `, isInline: true, dependencies: [{ kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
364
295
  }
365
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: LayoutModeSwitcherComponent, decorators: [{
296
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: EmptyLayoutComponent, decorators: [{
366
297
  type: Component,
367
298
  args: [{
368
- selector: 'layout-mode-switcher',
369
- imports: [
370
- MatIconButton,
371
- MatTooltip,
372
- MatMenu,
373
- MatMenuItem,
374
- MatMenuTrigger,
375
- LucidePanelLeft,
376
- LucidePanelTop,
377
- LucideAppWindow,
378
- ],
379
- providers: [libraryLucideConfigProvider],
299
+ selector: 'empty',
380
300
  changeDetection: ChangeDetectionStrategy.OnPush,
301
+ imports: [RouterOutlet],
302
+ host: {
303
+ class: 'flex min-h-dvh w-full items-center justify-center bg-background p-6 text-foreground',
304
+ },
381
305
  template: `
382
- <button
383
- class="ngt-icon-button"
384
- type="button"
385
- mat-icon-button
386
- [attr.aria-label]="'Layout mode: ' + currentOption().label"
387
- [matTooltip]="'Layout mode: ' + currentOption().label"
388
- [matMenuTriggerFor]="menu">
389
- @switch (layout.mode()) {
390
- @case ('vertical') {
391
- <svg lucidePanelLeft aria-hidden="true"></svg>
392
- }
393
- @case ('horizontal') {
394
- <svg lucidePanelTop aria-hidden="true"></svg>
395
- }
396
- @default {
397
- <svg lucideAppWindow aria-hidden="true"></svg>
398
- }
399
- }
400
- </button>
401
-
402
- <mat-menu #menu="matMenu">
403
- @for (option of options; track option.value) {
404
- <button
405
- type="button"
406
- mat-menu-item
407
- role="menuitemradio"
408
- [attr.aria-checked]="layout.mode() === option.value"
409
- (click)="layout.setMode(option.value)">
410
- @switch (option.value) {
411
- @case ('vertical') {
412
- <svg lucidePanelLeft aria-hidden="true"></svg>
413
- }
414
- @case ('horizontal') {
415
- <svg lucidePanelTop aria-hidden="true"></svg>
416
- }
417
- @default {
418
- <svg lucideAppWindow aria-hidden="true"></svg>
419
- }
420
- }
421
- <span>{{ option.label }}</span>
422
- </button>
423
- }
424
- </mat-menu>
306
+ <main class="w-full max-w-sm">
307
+ <router-outlet />
308
+ </main>
425
309
  `,
426
310
  }]
427
311
  }] });
428
312
 
429
- function warnInvalidLayoutConfig(message) {
430
- if (isDevMode()) {
431
- console.warn(`[provideNgLayout] ${message}`);
432
- }
433
- }
434
- function normalizeLayoutMode(value) {
435
- if (typeof value === 'undefined' || isLayoutMode(value)) {
436
- return value ?? DEFAULT_NG_LAYOUT_CONFIG.defaultMode;
437
- }
438
- warnInvalidLayoutConfig(`Ignoring invalid defaultMode ${JSON.stringify(value)}. Falling back to ${JSON.stringify(DEFAULT_NG_LAYOUT_CONFIG.defaultMode)}.`);
439
- return DEFAULT_NG_LAYOUT_CONFIG.defaultMode;
440
- }
441
- function normalizeLayoutContainer(value) {
442
- if (typeof value === 'undefined' || isLayoutContainer(value)) {
443
- return value ?? DEFAULT_NG_LAYOUT_CONFIG.defaultContainer;
444
- }
445
- warnInvalidLayoutConfig(`Ignoring invalid defaultContainer ${JSON.stringify(value)}. Falling back to ${JSON.stringify(DEFAULT_NG_LAYOUT_CONFIG.defaultContainer)}.`);
446
- return DEFAULT_NG_LAYOUT_CONFIG.defaultContainer;
447
- }
448
- function normalizeLayoutConfig(config) {
449
- return {
450
- ...DEFAULT_NG_LAYOUT_CONFIG,
451
- defaultMode: normalizeLayoutMode(config.defaultMode),
452
- defaultContainer: normalizeLayoutContainer(config.defaultContainer),
453
- };
454
- }
455
- function provideNgLayout(config = {}) {
456
- return makeEnvironmentProviders([{ provide: NG_LAYOUT_CONFIG, useValue: normalizeLayoutConfig(config) }]);
313
+ function provideMaterialLayout(config = {}) {
314
+ return makeEnvironmentProviders([
315
+ { provide: MATERIAL_LAYOUT_CONFIG, useValue: config },
316
+ provideEnvironmentInitializer(() => {
317
+ inject(LayoutService);
318
+ }),
319
+ ]);
457
320
  }
458
321
 
322
+ /*
323
+ * Public API Surface of @ojiepermana/angular/layout
324
+ */
325
+ const LAYOUT_VERSION = '0.0.1';
326
+
459
327
  /**
460
328
  * Generated bundle index. Do not edit.
461
329
  */
462
330
 
463
- export { LayoutContainerSwitcherComponent, LayoutHorizontalComponent, LayoutHostDirective, LayoutModeSwitcherComponent, LayoutService, LayoutVerticalComponent, NG_LAYOUT_CONFIG, provideNgLayout };
331
+ export { DEFAULT_MATERIAL_LAYOUT_CONFIG, EmptyLayoutComponent, HorizontalLayoutComponent, LAYOUT_MODES, LAYOUT_VERSION, LAYOUT_WIDTHS, LayoutService, MATERIAL_LAYOUT_CONFIG, VerticalLayoutComponent, isLayoutMode, isLayoutWidth, provideMaterialLayout };
464
332
  //# sourceMappingURL=ojiepermana-angular-layout.mjs.map