@ojiepermana/angular-theme 22.0.36 → 22.0.41

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 (41) hide show
  1. package/README.md +25 -12
  2. package/fesm2022/ojiepermana-angular-theme-layout-wrapper.mjs +72 -22
  3. package/fesm2022/ojiepermana-angular-theme-layout.mjs +14 -2
  4. package/fesm2022/ojiepermana-angular-theme-page.mjs +251 -101
  5. package/fesm2022/ojiepermana-angular-theme-styles.mjs +19 -0
  6. package/layout/README.md +21 -19
  7. package/package.json +3 -3
  8. package/page/README.md +53 -15
  9. package/styles/README.md +11 -3
  10. package/styles/css/base/theme.css +26 -46
  11. package/styles/css/base/tokens.css +46 -38
  12. package/styles/css/color/amber.css +2 -0
  13. package/styles/css/color/blue.css +2 -0
  14. package/styles/css/color/cyan.css +2 -0
  15. package/styles/css/color/emerald.css +2 -0
  16. package/styles/css/color/fuchsia.css +2 -0
  17. package/styles/css/color/green.css +2 -0
  18. package/styles/css/color/indigo.css +2 -0
  19. package/styles/css/color/lime.css +2 -0
  20. package/styles/css/color/orange.css +2 -0
  21. package/styles/css/color/pink.css +2 -0
  22. package/styles/css/color/purple.css +2 -0
  23. package/styles/css/color/red.css +2 -0
  24. package/styles/css/color/rose.css +2 -0
  25. package/styles/css/color/sky.css +2 -0
  26. package/styles/css/color/teal.css +2 -0
  27. package/styles/css/color/violet.css +2 -0
  28. package/styles/css/color/yellow.css +2 -0
  29. package/styles/css/neutral/gray.css +2 -0
  30. package/styles/css/neutral/mauve.css +2 -0
  31. package/styles/css/neutral/mist.css +2 -0
  32. package/styles/css/neutral/neutral.css +2 -0
  33. package/styles/css/neutral/olive.css +2 -0
  34. package/styles/css/neutral/slate.css +2 -0
  35. package/styles/css/neutral/stone.css +2 -0
  36. package/styles/css/neutral/taupe.css +2 -0
  37. package/styles/css/neutral/zinc.css +2 -0
  38. package/types/ojiepermana-angular-theme-layout-wrapper.d.ts +39 -6
  39. package/types/ojiepermana-angular-theme-layout.d.ts +1 -0
  40. package/types/ojiepermana-angular-theme-page.d.ts +88 -36
  41. package/types/ojiepermana-angular-theme-styles.d.ts +1 -0
package/README.md CHANGED
@@ -11,13 +11,13 @@ bun add @ojiepermana/angular-theme
11
11
 
12
12
  ## Entry points
13
13
 
14
- | Import path | Contents |
15
- | ----------------------------------------- | ------------------------------------------------------- |
16
- | `@ojiepermana/angular-theme/styles` | `provideUiTheme`, `ThemeModeService`, … |
17
- | `@ojiepermana/angular-theme/layout` | Layout shell building blocks |
18
- | `@ojiepermana/angular-theme/page` | Page-level scaffolding |
19
- | `@ojiepermana/angular-theme/theme.css` | Base tokens + component styles (CSS) |
20
- | `@ojiepermana/angular-theme/styles/css/*` | Raw CSS assets (color + neutral palettes, Tailwind map) |
14
+ | Import path | Contents |
15
+ | ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
16
+ | `@ojiepermana/angular-theme/styles` | `provideUiTheme`, `ThemeModeService`, `ThemeColorService`, `ThemeBrandService`, `ThemeRadiusService`, `ThemeSpaceService`, |
17
+ | `@ojiepermana/angular-theme/layout` | Layout shell building blocks |
18
+ | `@ojiepermana/angular-theme/page` | Page-level scaffolding |
19
+ | `@ojiepermana/angular-theme/theme.css` | Base tokens + component styles (CSS) |
20
+ | `@ojiepermana/angular-theme/styles/css/*` | Raw CSS assets (color + neutral palettes, Tailwind map) |
21
21
 
22
22
  ## Tailwind v4 setup
23
23
 
@@ -44,24 +44,33 @@ export const appConfig = {
44
44
  mode: 'light',
45
45
  color: 'base', // accent palette (base, red…rose, brand)
46
46
  neutral: 'base', // gray family (base, slate, gray, zinc, …)
47
+ radius: 'md', // corner radius preset (none, sm, md, lg, xl, full)
48
+ space: 'normal', // spacing density preset (compact, normal, relaxed, spacious)
47
49
  brand: { color: '221 83% 53%', foreground: '0 0% 100%' }, // consumer brand
48
50
  }),
49
51
  ],
50
52
  };
51
53
  ```
52
54
 
53
- - `mode` — bootstraps `ThemeModeService` and persists the default mode.
55
+ - `mode` — bootstraps `ThemeModeService` and persists the default mode (`light` / `dark` / `system`).
54
56
  - `color` — bootstraps `ThemeColorService`; initial accent palette (`<html theme-color>`).
55
57
  - `neutral` — initial neutral family (`<html theme-neutral>`); composes with any accent.
58
+ - `radius` — bootstraps `ThemeRadiusService`; initial corner-radius preset (`<html theme-radius>`).
59
+ Drives the single `--radius-base` knob so the whole `--radius-*` scale and `rounded-*`
60
+ utilities follow. Values: `none`, `sm`, `md` (default), `lg`, `xl`, `full`.
61
+ - `space` — bootstraps `ThemeSpaceService`; initial spacing-density preset (`<html theme-space>`).
62
+ Drives the single `--spacing-base` knob so every `p-*` / `m-*` / `gap-*` / `w-*` / `h-*`
63
+ utility follows. Values: `compact`, `normal` (default), `relaxed`, `spacious`.
56
64
  - `brand` — bootstraps `ThemeBrandService`; sets `--brand` / `bg-brand` and the
57
- `theme-color='brand'` accent preset. Settable at runtime via `setBrand()`.
65
+ `theme-color='brand'` accent preset. Accepts an HSL triplet string (`'221 83% 53%'`)
66
+ or `{ color, foreground }`. Settable at runtime via `setBrand()`.
58
67
 
59
- A persisted choice (localStorage `theme-color` / `theme-neutral` / `theme-brand`)
60
- always wins over the configured default.
68
+ A persisted choice (localStorage `theme-color` / `theme-neutral` / `theme-radius` /
69
+ `theme-space` / `theme-brand`) always wins over the configured default.
61
70
 
62
71
  ### Color system (FluxUI-style)
63
72
 
64
- Two independent axes switch at runtime via attribute selectors on `<html>`:
73
+ Four independent axes switch at runtime via attribute selectors on `<html>`:
65
74
 
66
75
  - **accent** (`theme-color`) — `base` (core), `red … rose`, and `brand`. Each
67
76
  re-tints the full palette. `base` = no override.
@@ -69,6 +78,10 @@ Two independent axes switch at runtime via attribute selectors on `<html>`:
69
78
  `stone`, `mauve`, `olive`, `mist`, `taupe`. Overrides only the gray family and is
70
79
  layered after accent so it wins the shared neutral tokens — letting you pair any
71
80
  accent with any neutral.
81
+ - **radius** (`theme-radius`) — `none`, `sm`, `md` (default), `lg`, `xl`, `full`.
82
+ Sets the `--radius-base` knob; the full `--radius-*` scale and `rounded-*` utilities follow.
83
+ - **space** (`theme-space`) — `compact`, `normal` (default), `relaxed`, `spacious`.
84
+ Sets the `--spacing-base` knob; every `p-*` / `m-*` / `gap-*` / `w-*` / `h-*` utility follows.
72
85
 
73
86
  `styles/css/index.css` bundles the core base theme plus every accent and neutral
74
87
  palette, so switching needs no runtime CSS loading.
@@ -1,8 +1,9 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, booleanAttribute, Component, computed } from '@angular/core';
2
+ import { signal, Service, input, booleanAttribute, Component, computed, inject, effect } from '@angular/core';
3
3
  import { NavigationHeaderInitialComponent, NavigationHeaderTitleComponent, NavigationFooterActionComponent, NavigationFooterInitialComponent, NavigationFooterTitleComponent, NavigationSidebarComponent, NavigationHeaderComponent, NavigationFooterComponent, NavigationDockbarComponent, NavigationNavbarComponent, NavigationFlyoutComponent, NavigationContainerComponent } from '@ojiepermana/angular-navigation';
4
4
  import { IconComponent } from '@ojiepermana/angular-component/icon';
5
5
  import { NgTemplateOutlet } from '@angular/common';
6
+ import { cn } from '@ojiepermana/angular-component/utils';
6
7
  import { LayoutComponent, LayoutVerticalComponent, LayoutHorizontalComponent, LayoutEmptyComponent, LayoutFluidComponent, LayoutNavigationComponent, LayoutContentComponent } from '@ojiepermana/angular-theme/layout';
7
8
  import { LAYOUT_DEFAULT_SURFACE, LAYOUT_DEFAULT_APPEARANCE, LAYOUT_DEFAULT_WIDTH, LAYOUT_DEFAULT_TYPE } from '@ojiepermana/angular-theme/layout/types';
8
9
 
@@ -11,6 +12,31 @@ import { LAYOUT_DEFAULT_SURFACE, LAYOUT_DEFAULT_APPEARANCE, LAYOUT_DEFAULT_WIDTH
11
12
  * strukturnya; nilai konkret (brand/user) disediakan consumer sebagai input.
12
13
  */
13
14
 
15
+ /**
16
+ * Registry identitas brand/user yang dipublikasikan shell (mis. `LayoutWrapperDefault`).
17
+ * Surface lain — seperti apps-launcher di `Page` — membacanya agar menampilkan brand/user
18
+ * yang sama dengan flyout nav tanpa menerima input langsung.
19
+ */
20
+ class LayoutIdentityService {
21
+ brandState = signal(null, /* @ts-ignore */
22
+ ...(ngDevMode ? [{ debugName: "brandState" }] : /* istanbul ignore next */ []));
23
+ userState = signal(null, /* @ts-ignore */
24
+ ...(ngDevMode ? [{ debugName: "userState" }] : /* istanbul ignore next */ []));
25
+ brand = this.brandState.asReadonly();
26
+ user = this.userState.asReadonly();
27
+ setBrand(brand) {
28
+ this.brandState.set(brand);
29
+ }
30
+ setUser(user) {
31
+ this.userState.set(user);
32
+ }
33
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: LayoutIdentityService, deps: [], target: i0.ɵɵFactoryTarget.Service });
34
+ static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.0.3", ngImport: i0, type: LayoutIdentityService });
35
+ }
36
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: LayoutIdentityService, decorators: [{
37
+ type: Service
38
+ }] });
39
+
14
40
  /**
15
41
  * Slot brand untuk `<NavigationHeader>`: inisial/logo + judul. Murni input-driven.
16
42
  *
@@ -129,21 +155,27 @@ class LayoutNavSidebar {
129
155
  ...(ngDevMode ? [{ debugName: "brand" }] : /* istanbul ignore next */ []));
130
156
  user = input.required(/* @ts-ignore */
131
157
  ...(ngDevMode ? [{ debugName: "user" }] : /* istanbul ignore next */ []));
132
- /** Appearance dari shell (`[appearance]`); menentukan ketebalan border kanan pemisah konten. */
158
+ /** Appearance dari shell (`[appearance]`); menentukan ketebalan border pemisah. */
133
159
  appearance = input('flat', /* @ts-ignore */
134
160
  ...(ngDevMode ? [{ debugName: "appearance" }] : /* istanbul ignore next */ []));
161
+ isBorderRail = computed(() => this.appearance() === 'border-rail', /* @ts-ignore */
162
+ ...(ngDevMode ? [{ debugName: "isBorderRail" }] : /* istanbul ignore next */ []));
135
163
  /** Border kanan pemisah; ketebalan ikut appearance: `border-rail` 1.5px, `flat` 1px. */
136
- shellClass = computed(() => this.appearance() === 'border-rail'
137
- ? 'border-r-[1.5px] border-border'
138
- : 'border-r border-border', /* @ts-ignore */
164
+ shellClass = computed(() => this.isBorderRail() ? 'border-r-[1.5px] border-border' : 'border-r border-border', /* @ts-ignore */
139
165
  ...(ngDevMode ? [{ debugName: "shellClass" }] : /* istanbul ignore next */ []));
166
+ /** Header divider menebal jadi 1.5px di `border-rail` (selaras nav/shell); `flat` pakai default. */
167
+ headerClass = computed(() => this.isBorderRail() ? 'border-b-[1.5px] border-border' : '', /* @ts-ignore */
168
+ ...(ngDevMode ? [{ debugName: "headerClass" }] : /* istanbul ignore next */ []));
169
+ /** Footer divider menebal jadi 1.5px di `border-rail`; `px-3` tetap dipertahankan. */
170
+ footerClass = computed(() => this.isBorderRail() ? 'px-3 border-t-[1.5px] border-border' : 'px-3', /* @ts-ignore */
171
+ ...(ngDevMode ? [{ debugName: "footerClass" }] : /* istanbul ignore next */ []));
140
172
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: LayoutNavSidebar, deps: [], target: i0.ɵɵFactoryTarget.Component });
141
173
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.3", type: LayoutNavSidebar, isStandalone: true, selector: "LayoutNavSidebar", inputs: { brand: { classPropertyName: "brand", publicName: "brand", isSignal: true, isRequired: true, transformFunction: null }, user: { classPropertyName: "user", publicName: "user", isSignal: true, isRequired: true, transformFunction: null }, appearance: { classPropertyName: "appearance", publicName: "appearance", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "contents" }, ngImport: i0, template: `
142
174
  <NavigationSidebar nav-sidebar-collapse [class]="shellClass()">
143
- <NavigationHeader>
175
+ <NavigationHeader [class]="headerClass()">
144
176
  <LayoutBrand [brand]="brand()" />
145
177
  </NavigationHeader>
146
- <NavigationFooter class="px-3">
178
+ <NavigationFooter [class]="footerClass()">
147
179
  <LayoutUser [user]="user()" detailed />
148
180
  </NavigationFooter>
149
181
  </NavigationSidebar>
@@ -163,10 +195,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImpor
163
195
  host: { class: 'contents' },
164
196
  template: `
165
197
  <NavigationSidebar nav-sidebar-collapse [class]="shellClass()">
166
- <NavigationHeader>
198
+ <NavigationHeader [class]="headerClass()">
167
199
  <LayoutBrand [brand]="brand()" />
168
200
  </NavigationHeader>
169
- <NavigationFooter class="px-3">
201
+ <NavigationFooter [class]="footerClass()">
170
202
  <LayoutUser [user]="user()" detailed />
171
203
  </NavigationFooter>
172
204
  </NavigationSidebar>
@@ -183,21 +215,27 @@ class LayoutNavDockbar {
183
215
  ...(ngDevMode ? [{ debugName: "brand" }] : /* istanbul ignore next */ []));
184
216
  user = input.required(/* @ts-ignore */
185
217
  ...(ngDevMode ? [{ debugName: "user" }] : /* istanbul ignore next */ []));
186
- /** Appearance dari shell (`[appearance]`); menentukan ketebalan border kanan pemisah konten. */
218
+ /** Appearance dari shell (`[appearance]`); menentukan ketebalan border pemisah. */
187
219
  appearance = input('flat', /* @ts-ignore */
188
220
  ...(ngDevMode ? [{ debugName: "appearance" }] : /* istanbul ignore next */ []));
221
+ isBorderRail = computed(() => this.appearance() === 'border-rail', /* @ts-ignore */
222
+ ...(ngDevMode ? [{ debugName: "isBorderRail" }] : /* istanbul ignore next */ []));
189
223
  /** Border kanan pemisah; ketebalan ikut appearance: `border-rail` 1.5px, `flat` 1px. */
190
- shellClass = computed(() => this.appearance() === 'border-rail'
191
- ? 'border-r-[1.5px] border-border'
192
- : 'border-r border-border', /* @ts-ignore */
224
+ shellClass = computed(() => this.isBorderRail() ? 'border-r-[1.5px] border-border' : 'border-r border-border', /* @ts-ignore */
193
225
  ...(ngDevMode ? [{ debugName: "shellClass" }] : /* istanbul ignore next */ []));
226
+ /** Header divider menebal jadi 1.5px di `border-rail` (selaras nav/shell); `flat` pakai default. */
227
+ headerClass = computed(() => this.isBorderRail() ? 'border-b-[1.5px] border-border' : '', /* @ts-ignore */
228
+ ...(ngDevMode ? [{ debugName: "headerClass" }] : /* istanbul ignore next */ []));
229
+ /** Footer divider menebal jadi 1.5px di `border-rail`; `px-3` tetap dipertahankan. */
230
+ footerClass = computed(() => this.isBorderRail() ? 'px-3 border-t-[1.5px] border-border' : 'px-3', /* @ts-ignore */
231
+ ...(ngDevMode ? [{ debugName: "footerClass" }] : /* istanbul ignore next */ []));
194
232
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: LayoutNavDockbar, deps: [], target: i0.ɵɵFactoryTarget.Component });
195
233
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.3", type: LayoutNavDockbar, isStandalone: true, selector: "LayoutNavDockbar", inputs: { brand: { classPropertyName: "brand", publicName: "brand", isSignal: true, isRequired: true, transformFunction: null }, user: { classPropertyName: "user", publicName: "user", isSignal: true, isRequired: true, transformFunction: null }, appearance: { classPropertyName: "appearance", publicName: "appearance", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "contents" }, ngImport: i0, template: `
196
234
  <NavigationDockbar [class]="shellClass()">
197
- <NavigationHeader>
235
+ <NavigationHeader [class]="headerClass()">
198
236
  <LayoutBrand [brand]="brand()" compact />
199
237
  </NavigationHeader>
200
- <NavigationFooter class="px-3">
238
+ <NavigationFooter [class]="footerClass()">
201
239
  <LayoutUser [user]="user()" />
202
240
  </NavigationFooter>
203
241
  </NavigationDockbar>
@@ -217,10 +255,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImpor
217
255
  host: { class: 'contents' },
218
256
  template: `
219
257
  <NavigationDockbar [class]="shellClass()">
220
- <NavigationHeader>
258
+ <NavigationHeader [class]="headerClass()">
221
259
  <LayoutBrand [brand]="brand()" compact />
222
260
  </NavigationHeader>
223
- <NavigationFooter class="px-3">
261
+ <NavigationFooter [class]="footerClass()">
224
262
  <LayoutUser [user]="user()" />
225
263
  </NavigationFooter>
226
264
  </NavigationDockbar>
@@ -410,6 +448,18 @@ class LayoutWrapperDefault {
410
448
  /** Identitas user. */
411
449
  user = input.required(/* @ts-ignore */
412
450
  ...(ngDevMode ? [{ debugName: "user" }] : /* istanbul ignore next */ []));
451
+ identity = inject(LayoutIdentityService);
452
+ constructor() {
453
+ // Publikasikan identitas ke registry agar surface lain (mis. apps-launcher di `Page`)
454
+ // menampilkan brand/user yang sama dengan flyout nav.
455
+ effect(() => this.identity.setBrand(this.brand()));
456
+ effect(() => this.identity.setUser(this.user()));
457
+ }
458
+ /**
459
+ * Kelas `<LayoutContent>` yang diatur consumer (mis. padding). Default kosong —
460
+ * library tidak lagi memaksa `p-6`; consumer/`<Page>` yang menentukan spacing.
461
+ */
462
+ contentClassInput = input('', { ...(ngDevMode ? { debugName: "contentClassInput" } : /* istanbul ignore next */ {}), alias: 'content-class' });
413
463
  /** Wrapper layout & nilai `<Layout [layout-type]>`: navbar/flyout = horizontal, sisanya vertical. */
414
464
  effectiveLayoutType = computed(() => {
415
465
  const layoutType = this.layoutType();
@@ -428,11 +478,11 @@ class LayoutWrapperDefault {
428
478
  /** Lebar host `<Navigation>`: penuh-tinggi (vertical) atau penuh-lebar (horizontal). */
429
479
  navClass = computed(() => this.effectiveLayoutType() === 'horizontal' ? 'w-full' : 'h-full', /* @ts-ignore */
430
480
  ...(ngDevMode ? [{ debugName: "navClass" }] : /* istanbul ignore next */ []));
431
- /** Kelas `<LayoutContent>`: `fluid` membatasi lebar & memusatkan; selain itu padding biasa. */
432
- contentClass = computed(() => this.effectiveLayoutType() === 'fluid' ? 'w-full max-w-3xl p-6' : 'p-6', /* @ts-ignore */
481
+ /** Kelas `<LayoutContent>`: `fluid` membatasi lebar & memusatkan; padding diserahkan ke consumer. */
482
+ contentClass = computed(() => cn(this.effectiveLayoutType() === 'fluid' ? 'w-full max-w-3xl' : '', this.contentClassInput()), /* @ts-ignore */
433
483
  ...(ngDevMode ? [{ debugName: "contentClass" }] : /* istanbul ignore next */ []));
434
484
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: LayoutWrapperDefault, deps: [], target: i0.ɵɵFactoryTarget.Component });
435
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.3", type: LayoutWrapperDefault, isStandalone: true, selector: "LayoutWrapperDefault", inputs: { surface: { classPropertyName: "surface", publicName: "surface", isSignal: true, isRequired: false, transformFunction: null }, appearance: { classPropertyName: "appearance", publicName: "layout-appearance", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, layoutType: { classPropertyName: "layoutType", publicName: "layout-type", isSignal: true, isRequired: false, transformFunction: null }, navType: { classPropertyName: "navType", publicName: "nav-type", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: true, transformFunction: null }, brand: { classPropertyName: "brand", publicName: "brand", isSignal: true, isRequired: true, transformFunction: null }, user: { classPropertyName: "user", publicName: "user", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "contents" }, ngImport: i0, template: `
485
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.3", type: LayoutWrapperDefault, isStandalone: true, selector: "LayoutWrapperDefault", inputs: { surface: { classPropertyName: "surface", publicName: "surface", isSignal: true, isRequired: false, transformFunction: null }, appearance: { classPropertyName: "appearance", publicName: "layout-appearance", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, layoutType: { classPropertyName: "layoutType", publicName: "layout-type", isSignal: true, isRequired: false, transformFunction: null }, navType: { classPropertyName: "navType", publicName: "nav-type", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: true, transformFunction: null }, brand: { classPropertyName: "brand", publicName: "brand", isSignal: true, isRequired: true, transformFunction: null }, user: { classPropertyName: "user", publicName: "user", isSignal: true, isRequired: true, transformFunction: null }, contentClassInput: { classPropertyName: "contentClassInput", publicName: "content-class", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "contents" }, ngImport: i0, template: `
436
486
  <Layout
437
487
  [surface]="surface()"
438
488
  [layout-appearance]="appearance()"
@@ -575,7 +625,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImpor
575
625
  </Layout>
576
626
  `,
577
627
  }]
578
- }], propDecorators: { surface: [{ type: i0.Input, args: [{ isSignal: true, alias: "surface", required: false }] }], appearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "layout-appearance", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], layoutType: [{ type: i0.Input, args: [{ isSignal: true, alias: "layout-type", required: false }] }], navType: [{ type: i0.Input, args: [{ isSignal: true, alias: "nav-type", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: true }] }], brand: [{ type: i0.Input, args: [{ isSignal: true, alias: "brand", required: true }] }], user: [{ type: i0.Input, args: [{ isSignal: true, alias: "user", required: true }] }] } });
628
+ }], ctorParameters: () => [], propDecorators: { surface: [{ type: i0.Input, args: [{ isSignal: true, alias: "surface", required: false }] }], appearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "layout-appearance", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], layoutType: [{ type: i0.Input, args: [{ isSignal: true, alias: "layout-type", required: false }] }], navType: [{ type: i0.Input, args: [{ isSignal: true, alias: "nav-type", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: true }] }], brand: [{ type: i0.Input, args: [{ isSignal: true, alias: "brand", required: true }] }], user: [{ type: i0.Input, args: [{ isSignal: true, alias: "user", required: true }] }], contentClassInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "content-class", required: false }] }] } });
579
629
 
580
630
  /*
581
631
  * Public API of @ojiepermana/angular-theme/layout/wrapper
@@ -590,4 +640,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImpor
590
640
  * Generated bundle index. Do not edit.
591
641
  */
592
642
 
593
- export { LayoutBrand, LayoutNavDockbar, LayoutNavFlyout, LayoutNavMinimal, LayoutNavNavbar, LayoutNavSidebar, LayoutUser, LayoutWrapperDefault };
643
+ export { LayoutBrand, LayoutIdentityService, LayoutNavDockbar, LayoutNavFlyout, LayoutNavMinimal, LayoutNavNavbar, LayoutNavSidebar, LayoutUser, LayoutWrapperDefault };
@@ -116,6 +116,8 @@ class LayoutComponent {
116
116
  ...(ngDevMode ? [{ debugName: "isBorderRail" }] : /* istanbul ignore next */ []));
117
117
  isFluidFrame = computed(() => this.resolvedWidth() === 'fluid' && this.resolvedType() === 'fluid', /* @ts-ignore */
118
118
  ...(ngDevMode ? [{ debugName: "isFluidFrame" }] : /* istanbul ignore next */ []));
119
+ isFullFlat = computed(() => this.resolvedWidth() === 'full' && !this.isBorderRail(), /* @ts-ignore */
120
+ ...(ngDevMode ? [{ debugName: "isFullFlat" }] : /* istanbul ignore next */ []));
119
121
  showsInsetRails = computed(() => {
120
122
  const layoutType = this.resolvedType();
121
123
  return this.isBorderRail() && (layoutType === 'horizontal' || layoutType === 'vertical');
@@ -129,7 +131,15 @@ class LayoutComponent {
129
131
  ...(ngDevMode ? [{ debugName: "contentShellClasses" }] : /* istanbul ignore next */ []));
130
132
  hostClasses = computed(() => cn('relative isolate h-dvh w-full min-w-0 box-border overflow-hidden text-foreground', this.isFluidFrame() ? 'grid place-items-center' : 'block', this.surfaceClasses(), this.widthPaddingClasses(), this.class()), /* @ts-ignore */
131
133
  ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
132
- frameClasses = computed(() => cn('relative min-h-0 min-w-0 border-border bg-background/55 backdrop-blur-xs', this.frameSizeClasses(), this.isBorderRail() ? 'overflow-visible border-[1.5px]' : 'overflow-hidden border'), /* @ts-ignore */
134
+ frameClasses = computed(() => cn('relative min-h-0 min-w-0 border-border bg-background/55 backdrop-blur-xs', this.frameSizeClasses(),
135
+ // flat: the outer frame rounds with the active theme-radius
136
+ // (--layout-frame-radius → --radius-lg → --radius-base). border-rail keeps
137
+ // square corners for its grid-rail aesthetic.
138
+ this.isBorderRail()
139
+ ? 'overflow-visible border-[1.5px]'
140
+ : this.isFullFlat()
141
+ ? 'overflow-hidden'
142
+ : 'overflow-hidden border rounded-[var(--layout-frame-radius)]'), /* @ts-ignore */
133
143
  ...(ngDevMode ? [{ debugName: "frameClasses" }] : /* istanbul ignore next */ []));
134
144
  frameLayerClasses = computed(() => cn('col-start-1 row-start-1', this.frameClasses()), /* @ts-ignore */
135
145
  ...(ngDevMode ? [{ debugName: "frameLayerClasses" }] : /* istanbul ignore next */ []));
@@ -180,6 +190,8 @@ class LayoutComponent {
180
190
  }
181
191
  }
182
192
  widthPaddingClasses() {
193
+ if (this.isFullFlat())
194
+ return '';
183
195
  switch (this.resolvedWidth()) {
184
196
  case 'wide':
185
197
  return 'p-4 lg:p-12';
@@ -188,7 +200,7 @@ class LayoutComponent {
188
200
  case 'fluid':
189
201
  return 'p-4 sm:p-6 lg:p-8';
190
202
  default:
191
- return 'p-4';
203
+ return 'p-4'; // full + border-rail
192
204
  }
193
205
  }
194
206
  frameSizeClasses() {