@ojiepermana/angular-theme 22.0.44 → 22.0.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,26 +11,39 @@ 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`, `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) |
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 only — lightweight, no runtime axes |
20
+ | `@ojiepermana/angular-theme/theme-full.css` | Base + every runtime axis (color, neutral, radius, space) — needed for `ThemeColorService`/`ThemeNeutralService` switching |
21
+ | `@ojiepermana/angular-theme/styles/css/*` | Raw CSS assets (per-axis color + neutral palettes, Tailwind map) |
21
22
 
22
23
  ## Tailwind v4 setup
23
24
 
24
- The CSS is published with the package and addressable by name. In your global
25
- stylesheet:
25
+ The CSS is published with the package and addressable by name. Use `theme-full.css`
26
+ for the full design system (runtime color/neutral switching); use the lightweight
27
+ `theme.css` plus only the axes you need for a smaller bundle.
26
28
 
27
29
  ```css
28
- /* styles.css */
29
- @import '@ojiepermana/angular-theme/styles/css/index.css'; /* base + all color + neutral palettes */
30
+ /* styles.css — full design system */
31
+ @import '@ojiepermana/angular-theme/theme-full.css'; /* base + all color + neutral palettes */
30
32
  @import 'tailwindcss';
31
33
  @import '@ojiepermana/angular-theme/styles/css/base/tailwind.css'; /* maps tokens → bg-primary, bg-brand, text-foreground, … */
32
34
  ```
33
35
 
36
+ For a lightweight setup, import the base then opt in to specific axes:
37
+
38
+ ```css
39
+ /* styles.css — base + only the axes you use */
40
+ @import '@ojiepermana/angular-theme/theme.css'; /* base tokens + components, no axes */
41
+ @import 'tailwindcss';
42
+ @import '@ojiepermana/angular-theme/styles/css/base/tailwind.css';
43
+ @import '@ojiepermana/angular-theme/styles/css/color/index.css'; /* opt in to accent palettes */
44
+ @import '@ojiepermana/angular-theme/styles/css/neutral/index.css'; /* opt in to neutral families */
45
+ ```
46
+
34
47
  Requires Tailwind CSS `^4.3.0`.
35
48
 
36
49
  ## Provider
@@ -83,8 +96,10 @@ Four independent axes switch at runtime via attribute selectors on `<html>`:
83
96
  - **space** (`theme-space`) — `compact`, `normal` (default), `relaxed`, `spacious`.
84
97
  Sets the `--spacing-base` knob; every `p-*` / `m-*` / `gap-*` / `w-*` / `h-*` utility follows.
85
98
 
86
- `styles/css/index.css` bundles the core base theme plus every accent and neutral
87
- palette, so switching needs no runtime CSS loading.
99
+ `theme-full.css` (i.e. `styles/css/index.css`) bundles the core base theme plus
100
+ every accent and neutral palette, so switching needs no runtime CSS loading. The
101
+ lightweight `theme.css` ships base only — import the axis files you need on top of
102
+ it (or use `theme-full.css`) to enable runtime switching.
88
103
 
89
104
  ### Material Symbols icons (opt-in)
90
105
 
@@ -160,14 +160,24 @@ class LayoutNavSidebar {
160
160
  ...(ngDevMode ? [{ debugName: "appearance" }] : /* istanbul ignore next */ []));
161
161
  isBorderRail = computed(() => this.appearance() === 'border-rail', /* @ts-ignore */
162
162
  ...(ngDevMode ? [{ debugName: "isBorderRail" }] : /* istanbul ignore next */ []));
163
- /** Border kanan pemisah; ketebalan ikut appearance: `border-rail` 1.5px, `flat` 1px. */
164
- shellClass = computed(() => this.isBorderRail() ? 'border-r-[1.5px] border-border' : 'border-r border-border', /* @ts-ignore */
163
+ /** Border kanan pemisah; ketebalan ikut appearance: `border-rail` = `--layout-rail-width` (1.21px, selaras rail layout), `flat` 1px. */
164
+ shellClass = computed(() => this.isBorderRail()
165
+ ? 'border-r-[length:var(--layout-rail-width)] border-border'
166
+ : 'border-r border-border', /* @ts-ignore */
165
167
  ...(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
+ /**
169
+ * Header divider: di `border-rail` border bawaan dimatikan (`border-b-0`) karena pemisahnya
170
+ * diambil-alih rail inset accent Layout (`data-layout-horizontal-top-rail` di `top-12`) — supaya
171
+ * tidak dempet/dobel dengan garis itu. `flat` pakai default (border bawaan NavigationHeader).
172
+ */
173
+ headerClass = computed(() => (this.isBorderRail() ? 'border-b-0' : ''), /* @ts-ignore */
168
174
  ...(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 */
175
+ /**
176
+ * Footer divider: di `border-rail` border bawaan dimatikan (`border-t-0`) karena pemisahnya
177
+ * diambil-alih rail inset accent Layout (`data-layout-horizontal-bottom-rail` di `bottom-12`);
178
+ * `px-3` tetap dipertahankan.
179
+ */
180
+ footerClass = computed(() => this.isBorderRail() ? 'px-3 border-t-0' : 'px-3', /* @ts-ignore */
171
181
  ...(ngDevMode ? [{ debugName: "footerClass" }] : /* istanbul ignore next */ []));
172
182
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: LayoutNavSidebar, deps: [], target: i0.ɵɵFactoryTarget.Component });
173
183
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.4", 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: `
@@ -220,14 +230,24 @@ class LayoutNavDockbar {
220
230
  ...(ngDevMode ? [{ debugName: "appearance" }] : /* istanbul ignore next */ []));
221
231
  isBorderRail = computed(() => this.appearance() === 'border-rail', /* @ts-ignore */
222
232
  ...(ngDevMode ? [{ debugName: "isBorderRail" }] : /* istanbul ignore next */ []));
223
- /** Border kanan pemisah; ketebalan ikut appearance: `border-rail` 1.5px, `flat` 1px. */
224
- shellClass = computed(() => this.isBorderRail() ? 'border-r-[1.5px] border-border' : 'border-r border-border', /* @ts-ignore */
233
+ /** Border kanan pemisah; ketebalan ikut appearance: `border-rail` = `--layout-rail-width` (1.21px, selaras rail layout), `flat` 1px. */
234
+ shellClass = computed(() => this.isBorderRail()
235
+ ? 'border-r-[length:var(--layout-rail-width)] border-border'
236
+ : 'border-r border-border', /* @ts-ignore */
225
237
  ...(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 */
238
+ /**
239
+ * Header divider: di `border-rail` border bawaan dimatikan (`border-b-0`) karena pemisahnya
240
+ * diambil-alih rail inset accent Layout (`data-layout-horizontal-top-rail` di `top-12`) — supaya
241
+ * tidak dempet/dobel dengan garis itu. `flat` pakai default (border bawaan NavigationHeader).
242
+ */
243
+ headerClass = computed(() => (this.isBorderRail() ? 'border-b-0' : ''), /* @ts-ignore */
228
244
  ...(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 */
245
+ /**
246
+ * Footer divider: di `border-rail` border bawaan dimatikan (`border-t-0`) karena pemisahnya
247
+ * diambil-alih rail inset accent Layout (`data-layout-horizontal-bottom-rail` di `bottom-12`);
248
+ * `px-3` tetap dipertahankan.
249
+ */
250
+ footerClass = computed(() => this.isBorderRail() ? 'px-3 border-t-0' : 'px-3', /* @ts-ignore */
231
251
  ...(ngDevMode ? [{ debugName: "footerClass" }] : /* istanbul ignore next */ []));
232
252
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: LayoutNavDockbar, deps: [], target: i0.ɵɵFactoryTarget.Component });
233
253
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.4", 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: `
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, input, computed, DestroyRef, Component, effect, ChangeDetectionStrategy, contentChild } from '@angular/core';
2
+ import { inject, input, computed, DestroyRef, Component, effect, contentChild } from '@angular/core';
3
3
  import { cn } from '@ojiepermana/angular-component/utils';
4
4
  import { LayoutLoadingService, LayoutService } from '@ojiepermana/angular-theme/layout/services';
5
5
  export { LAYOUT_LOADING_HIDE_DELAY_MS, LAYOUT_LOADING_INITIAL_PROGRESS, LAYOUT_LOADING_SKIP, LAYOUT_LOADING_TRICKLE_CEILING, LAYOUT_LOADING_TRICKLE_INTERVAL_MS, LayoutLoadingService, LayoutService, layoutLoadingInterceptor } from '@ojiepermana/angular-theme/layout/services';
@@ -377,13 +377,12 @@ class LayoutComponent {
377
377
  </div>
378
378
  </div>
379
379
  </div>
380
- `, isInline: true, dependencies: [{ kind: "component", type: LayoutLoadingComponent, selector: "LayoutLoading", inputs: ["class", "ariaLabel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
380
+ `, isInline: true, dependencies: [{ kind: "component", type: LayoutLoadingComponent, selector: "LayoutLoading", inputs: ["class", "ariaLabel"] }] });
381
381
  }
382
382
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: LayoutComponent, decorators: [{
383
383
  type: Component,
384
384
  args: [{
385
385
  selector: 'Layout',
386
- changeDetection: ChangeDetectionStrategy.OnPush,
387
386
  imports: [LayoutLoadingComponent],
388
387
  host: {
389
388
  '[class]': 'hostClasses()',
@@ -511,13 +510,12 @@ class LayoutVerticalComponent {
511
510
  this.layout.setType('vertical', { persist: false });
512
511
  }
513
512
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: LayoutVerticalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
514
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.4", type: LayoutVerticalComponent, isStandalone: true, selector: "LayoutVertical", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-layout-type": "vertical" }, properties: { "class": "classes()" } }, ngImport: i0, template: `<ng-content />`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
513
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.4", type: LayoutVerticalComponent, isStandalone: true, selector: "LayoutVertical", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-layout-type": "vertical" }, properties: { "class": "classes()" } }, ngImport: i0, template: `<ng-content />`, isInline: true });
515
514
  }
516
515
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: LayoutVerticalComponent, decorators: [{
517
516
  type: Component,
518
517
  args: [{
519
518
  selector: 'LayoutVertical',
520
- changeDetection: ChangeDetectionStrategy.OnPush,
521
519
  host: {
522
520
  '[class]': 'classes()',
523
521
  'data-layout-type': 'vertical',
@@ -536,13 +534,12 @@ class LayoutHorizontalComponent {
536
534
  this.layout.setType('horizontal', { persist: false });
537
535
  }
538
536
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: LayoutHorizontalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
539
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.4", type: LayoutHorizontalComponent, isStandalone: true, selector: "LayoutHorizontal", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-layout-type": "horizontal" }, properties: { "class": "classes()" } }, ngImport: i0, template: `<ng-content />`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
537
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.4", type: LayoutHorizontalComponent, isStandalone: true, selector: "LayoutHorizontal", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-layout-type": "horizontal" }, properties: { "class": "classes()" } }, ngImport: i0, template: `<ng-content />`, isInline: true });
540
538
  }
541
539
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: LayoutHorizontalComponent, decorators: [{
542
540
  type: Component,
543
541
  args: [{
544
542
  selector: 'LayoutHorizontal',
545
- changeDetection: ChangeDetectionStrategy.OnPush,
546
543
  host: {
547
544
  '[class]': 'classes()',
548
545
  'data-layout-type': 'horizontal',
@@ -561,13 +558,12 @@ class LayoutEmptyComponent {
561
558
  this.layout.setType('empty', { persist: false });
562
559
  }
563
560
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: LayoutEmptyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
564
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.4", type: LayoutEmptyComponent, isStandalone: true, selector: "LayoutEmpty", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-layout-type": "empty" }, properties: { "class": "classes()" } }, ngImport: i0, template: `<ng-content />`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
561
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.4", type: LayoutEmptyComponent, isStandalone: true, selector: "LayoutEmpty", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-layout-type": "empty" }, properties: { "class": "classes()" } }, ngImport: i0, template: `<ng-content />`, isInline: true });
565
562
  }
566
563
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: LayoutEmptyComponent, decorators: [{
567
564
  type: Component,
568
565
  args: [{
569
566
  selector: 'LayoutEmpty',
570
- changeDetection: ChangeDetectionStrategy.OnPush,
571
567
  host: {
572
568
  '[class]': 'classes()',
573
569
  'data-layout-type': 'empty',
@@ -586,13 +582,12 @@ class LayoutFluidComponent {
586
582
  this.layout.setType('fluid', { persist: false });
587
583
  }
588
584
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: LayoutFluidComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
589
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.4", type: LayoutFluidComponent, isStandalone: true, selector: "LayoutFluid", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-layout-type": "fluid" }, properties: { "class": "classes()" } }, ngImport: i0, template: `<ng-content />`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
585
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.4", type: LayoutFluidComponent, isStandalone: true, selector: "LayoutFluid", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-layout-type": "fluid" }, properties: { "class": "classes()" } }, ngImport: i0, template: `<ng-content />`, isInline: true });
590
586
  }
591
587
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: LayoutFluidComponent, decorators: [{
592
588
  type: Component,
593
589
  args: [{
594
590
  selector: 'LayoutFluid',
595
- changeDetection: ChangeDetectionStrategy.OnPush,
596
591
  host: {
597
592
  '[class]': 'classes()',
598
593
  'data-layout-type': 'fluid',
@@ -635,29 +630,31 @@ class LayoutNavigationComponent {
635
630
  <div
636
631
  aria-hidden="true"
637
632
  data-layout-nav-rail
638
- class="pointer-events-none absolute inset-y-0 right-0 z-20 w-[1.5px] transition-transform duration-300 ease-[cubic-bezier(0.22,1,0.36,1)] motion-reduce:transition-none"
633
+ class="pointer-events-none absolute inset-y-0 right-0 z-20 w-(--layout-rail-width) transition-transform duration-300 ease-[cubic-bezier(0.22,1,0.36,1)] motion-reduce:transition-none"
639
634
  [style.transform]="railTransform()"
640
635
  >
641
- <div data-layout-nav-rail-line class="absolute inset-y-0 right-0 w-[1.5px] bg-border"></div>
636
+ <div
637
+ data-layout-nav-rail-line
638
+ class="absolute inset-y-0 right-0 w-(--layout-rail-width) bg-[hsl(var(--layout-rail-color)/var(--layout-rail-opacity))]"
639
+ ></div>
642
640
  <div
643
641
  data-layout-nav-rail-top
644
- class="absolute bottom-full right-0 h-[calc((100dvh-100%)/2)] w-[1.5px] bg-border"
642
+ class="absolute bottom-full right-0 h-[calc((100dvh-100%)/2)] w-(--layout-rail-width) bg-[hsl(var(--layout-rail-color)/var(--layout-rail-opacity))]"
645
643
  ></div>
646
644
  <div
647
645
  data-layout-nav-rail-bottom
648
- class="absolute top-full right-0 h-[calc((100dvh-100%)/2)] w-[1.5px] bg-border"
646
+ class="absolute top-full right-0 h-[calc((100dvh-100%)/2)] w-(--layout-rail-width) bg-[hsl(var(--layout-rail-color)/var(--layout-rail-opacity))]"
649
647
  ></div>
650
648
  </div>
651
649
  }
652
650
 
653
651
  <ng-content />
654
- `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
652
+ `, isInline: true });
655
653
  }
656
654
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: LayoutNavigationComponent, decorators: [{
657
655
  type: Component,
658
656
  args: [{
659
657
  selector: 'LayoutNavigation',
660
- changeDetection: ChangeDetectionStrategy.OnPush,
661
658
  host: {
662
659
  '[class]': 'classes()',
663
660
  role: 'navigation',
@@ -671,17 +668,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImpor
671
668
  <div
672
669
  aria-hidden="true"
673
670
  data-layout-nav-rail
674
- class="pointer-events-none absolute inset-y-0 right-0 z-20 w-[1.5px] transition-transform duration-300 ease-[cubic-bezier(0.22,1,0.36,1)] motion-reduce:transition-none"
671
+ class="pointer-events-none absolute inset-y-0 right-0 z-20 w-(--layout-rail-width) transition-transform duration-300 ease-[cubic-bezier(0.22,1,0.36,1)] motion-reduce:transition-none"
675
672
  [style.transform]="railTransform()"
676
673
  >
677
- <div data-layout-nav-rail-line class="absolute inset-y-0 right-0 w-[1.5px] bg-border"></div>
674
+ <div
675
+ data-layout-nav-rail-line
676
+ class="absolute inset-y-0 right-0 w-(--layout-rail-width) bg-[hsl(var(--layout-rail-color)/var(--layout-rail-opacity))]"
677
+ ></div>
678
678
  <div
679
679
  data-layout-nav-rail-top
680
- class="absolute bottom-full right-0 h-[calc((100dvh-100%)/2)] w-[1.5px] bg-border"
680
+ class="absolute bottom-full right-0 h-[calc((100dvh-100%)/2)] w-(--layout-rail-width) bg-[hsl(var(--layout-rail-color)/var(--layout-rail-opacity))]"
681
681
  ></div>
682
682
  <div
683
683
  data-layout-nav-rail-bottom
684
- class="absolute top-full right-0 h-[calc((100dvh-100%)/2)] w-[1.5px] bg-border"
684
+ class="absolute top-full right-0 h-[calc((100dvh-100%)/2)] w-(--layout-rail-width) bg-[hsl(var(--layout-rail-color)/var(--layout-rail-opacity))]"
685
685
  ></div>
686
686
  </div>
687
687
  }
@@ -703,13 +703,12 @@ class LayoutContentComponent {
703
703
  }, /* @ts-ignore */
704
704
  ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
705
705
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: LayoutContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
706
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.4", type: LayoutContentComponent, isStandalone: true, selector: "LayoutContent", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()", "attr.data-layout-type": "layout.type()", "attr.data-layout-width": "layout.width()" } }, ngImport: i0, template: `<ng-content />`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
706
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.4", type: LayoutContentComponent, isStandalone: true, selector: "LayoutContent", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()", "attr.data-layout-type": "layout.type()", "attr.data-layout-width": "layout.width()" } }, ngImport: i0, template: `<ng-content />`, isInline: true });
707
707
  }
708
708
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: LayoutContentComponent, decorators: [{
709
709
  type: Component,
710
710
  args: [{
711
711
  selector: 'LayoutContent',
712
- changeDetection: ChangeDetectionStrategy.OnPush,
713
712
  host: {
714
713
  '[class]': 'classes()',
715
714
  '[attr.data-layout-type]': 'layout.type()',
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { effect, signal, computed, Injectable, inject, ElementRef, input, booleanAttribute, untracked, ChangeDetectionStrategy, Component, contentChild, output } from '@angular/core';
2
+ import { effect, signal, computed, Injectable, inject, ElementRef, input, booleanAttribute, untracked, Component, contentChild, output } from '@angular/core';
3
3
  import { cn } from '@ojiepermana/angular-component/utils';
4
4
  import { NavigationContainerComponent, NavigationFlyoutComponent, NavigationHeaderComponent, NavigationFooterComponent } from '@ojiepermana/angular-navigation';
5
5
  import { NavigationService } from '@ojiepermana/angular-navigation/service';
@@ -317,7 +317,7 @@ class PageFilterComponent {
317
317
  ariaHidden = computed(() => !this.isSticky() && !this.page.filterOpen() ? 'true' : null, /* @ts-ignore */
318
318
  ...(ngDevMode ? [{ debugName: "ariaHidden" }] : /* istanbul ignore next */ []));
319
319
  /** Saat panel mengambang tertutup, `inert` mengeluarkan isinya dari tab order & pohon aksesibilitas (AXE). */
320
- inertWhenClosed = computed(() => (this.isInteractive() && !this.page.filterOpen() ? '' : null), /* @ts-ignore */
320
+ inertWhenClosed = computed(() => this.isInteractive() && !this.page.filterOpen() ? '' : null, /* @ts-ignore */
321
321
  ...(ngDevMode ? [{ debugName: "inertWhenClosed" }] : /* istanbul ignore next */ []));
322
322
  dialogRole = computed(() => (this.isInteractive() ? 'dialog' : null), /* @ts-ignore */
323
323
  ...(ngDevMode ? [{ debugName: "dialogRole" }] : /* istanbul ignore next */ []));
@@ -345,10 +345,18 @@ class PageFilterComponent {
345
345
  this.isSticky() && 'shrink-0 w-[var(--page-filter-width)]', this.isSticky() && position === 'left' && 'border-r', this.isSticky() && position === 'right' && 'order-last border-l',
346
346
  // drawer → meluncur dari tepi content region, mengambang di atas content.
347
347
  this.isDrawer() &&
348
- 'absolute inset-y-0 z-20 w-[var(--page-filter-width)] border shadow-sm transition-transform duration-200 ease-out', this.isDrawer() && position === 'left' && (open ? 'left-0 translate-x-0' : 'left-0 -translate-x-full'), this.isDrawer() && position === 'right' && (open ? 'right-0 translate-x-0' : 'right-0 translate-x-full'),
348
+ 'absolute inset-y-0 z-20 w-[var(--page-filter-width)] border shadow-sm transition-transform duration-200 ease-out', this.isDrawer() &&
349
+ position === 'left' &&
350
+ (open ? 'left-0 translate-x-0' : 'left-0 -translate-x-full'), this.isDrawer() &&
351
+ position === 'right' &&
352
+ (open ? 'right-0 translate-x-0' : 'right-0 translate-x-full'),
349
353
  // overlay → seperti drawer, tapi di atas drawer (z lebih tinggi) dengan backdrop.
350
354
  this.isOverlay() &&
351
- 'absolute inset-y-0 z-30 w-[var(--page-filter-width)] border shadow-md transition-transform duration-200 ease-out', this.isOverlay() && position === 'left' && (open ? 'left-0 translate-x-0' : 'left-0 -translate-x-full'), this.isOverlay() && position === 'right' && (open ? 'right-0 translate-x-0' : 'right-0 translate-x-full'), this.class());
355
+ 'absolute inset-y-0 z-30 w-[var(--page-filter-width)] border shadow-md transition-transform duration-200 ease-out', this.isOverlay() &&
356
+ position === 'left' &&
357
+ (open ? 'left-0 translate-x-0' : 'left-0 -translate-x-full'), this.isOverlay() &&
358
+ position === 'right' &&
359
+ (open ? 'right-0 translate-x-0' : 'right-0 translate-x-full'), this.class());
352
360
  }, /* @ts-ignore */
353
361
  ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
354
362
  constructor() {
@@ -391,13 +399,12 @@ class PageFilterComponent {
391
399
  });
392
400
  }
393
401
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PageFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
394
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.4", type: PageFilterComponent, isStandalone: true, selector: "PageFilter", inputs: { placement: { classPropertyName: "placement", publicName: "placement", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, closeOnEsc: { classPropertyName: "closeOnEsc", publicName: "closeOnEsc", isSignal: true, isRequired: false, transformFunction: null }, collapsible: { classPropertyName: "collapsible", publicName: "collapsible", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()", "attr.id": "resolvedId", "attr.data-page-slot": "\"filter\"", "attr.data-page-filter-placement": "resolvedPlacement()", "attr.data-page-filter-mode": "effectiveMode()", "attr.data-page-filter-open": "page.filterOpen()", "attr.data-page-position": "resolvedPosition()", "attr.aria-hidden": "ariaHidden()", "attr.inert": "inertWhenClosed()", "attr.role": "dialogRole()", "attr.aria-modal": "ariaModal()", "attr.aria-label": "dialogLabel()", "attr.tabindex": "dialogTabindex()", "style.--page-filter-width": "resolvedWidth()" } }, ngImport: i0, template: `<ng-content />`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
402
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.4", type: PageFilterComponent, isStandalone: true, selector: "PageFilter", inputs: { placement: { classPropertyName: "placement", publicName: "placement", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, closeOnEsc: { classPropertyName: "closeOnEsc", publicName: "closeOnEsc", isSignal: true, isRequired: false, transformFunction: null }, collapsible: { classPropertyName: "collapsible", publicName: "collapsible", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()", "attr.id": "resolvedId", "attr.data-page-slot": "\"filter\"", "attr.data-page-filter-placement": "resolvedPlacement()", "attr.data-page-filter-mode": "effectiveMode()", "attr.data-page-filter-open": "page.filterOpen()", "attr.data-page-position": "resolvedPosition()", "attr.aria-hidden": "ariaHidden()", "attr.inert": "inertWhenClosed()", "attr.role": "dialogRole()", "attr.aria-modal": "ariaModal()", "attr.aria-label": "dialogLabel()", "attr.tabindex": "dialogTabindex()", "style.--page-filter-width": "resolvedWidth()" } }, ngImport: i0, template: `<ng-content />`, isInline: true });
395
403
  }
396
404
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PageFilterComponent, decorators: [{
397
405
  type: Component,
398
406
  args: [{
399
407
  selector: 'PageFilter',
400
- changeDetection: ChangeDetectionStrategy.OnPush,
401
408
  host: {
402
409
  '[class]': 'classes()',
403
410
  '[attr.id]': 'resolvedId',
@@ -440,13 +447,12 @@ class PageHeaderComponent {
440
447
  this.isBorderRail() ? 'border-b-[1.5px]' : 'border-b', this.class(), this.resolvedHeight() === 'fix' && 'h-12 overflow-hidden'), /* @ts-ignore */
441
448
  ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
442
449
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PageHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
443
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.4", type: PageHeaderComponent, isStandalone: true, selector: "PageHeader", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-page-slot": "header" }, properties: { "class": "classes()", "style.padding-right": "appsLauncherReservePadding()" } }, ngImport: i0, template: `<ng-content />`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
450
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.4", type: PageHeaderComponent, isStandalone: true, selector: "PageHeader", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-page-slot": "header" }, properties: { "class": "classes()", "style.padding-right": "appsLauncherReservePadding()" } }, ngImport: i0, template: `<ng-content />`, isInline: true });
444
451
  }
445
452
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PageHeaderComponent, decorators: [{
446
453
  type: Component,
447
454
  args: [{
448
455
  selector: 'PageHeader',
449
- changeDetection: ChangeDetectionStrategy.OnPush,
450
456
  host: {
451
457
  '[class]': 'classes()',
452
458
  // Inline style menang atas utility responsif konsumen (mis. `md:px-6`), jadi ruang
@@ -466,13 +472,12 @@ class PageContentComponent {
466
472
  classes = computed(() => buildPageBodyClasses(this.resolvedScroll(), this.class()), /* @ts-ignore */
467
473
  ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
468
474
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PageContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
469
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.4", type: PageContentComponent, isStandalone: true, selector: "PageContent", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-page-slot": "content" }, properties: { "class": "classes()" } }, ngImport: i0, template: `<ng-content />`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
475
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.4", type: PageContentComponent, isStandalone: true, selector: "PageContent", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-page-slot": "content" }, properties: { "class": "classes()" } }, ngImport: i0, template: `<ng-content />`, isInline: true });
470
476
  }
471
477
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PageContentComponent, decorators: [{
472
478
  type: Component,
473
479
  args: [{
474
480
  selector: 'PageContent',
475
- changeDetection: ChangeDetectionStrategy.OnPush,
476
481
  host: {
477
482
  '[class]': 'classes()',
478
483
  'data-page-slot': 'content',
@@ -489,13 +494,12 @@ class PageDashboardComponent {
489
494
  classes = computed(() => buildPageBodyClasses(this.resolvedScroll(), this.class()), /* @ts-ignore */
490
495
  ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
491
496
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PageDashboardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
492
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.4", type: PageDashboardComponent, isStandalone: true, selector: "PageDashboard", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-page-slot": "dashboard" }, properties: { "class": "classes()" } }, ngImport: i0, template: `<ng-content />`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
497
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.4", type: PageDashboardComponent, isStandalone: true, selector: "PageDashboard", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-page-slot": "dashboard" }, properties: { "class": "classes()" } }, ngImport: i0, template: `<ng-content />`, isInline: true });
493
498
  }
494
499
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PageDashboardComponent, decorators: [{
495
500
  type: Component,
496
501
  args: [{
497
502
  selector: 'PageDashboard',
498
- changeDetection: ChangeDetectionStrategy.OnPush,
499
503
  host: {
500
504
  '[class]': 'classes()',
501
505
  'data-page-slot': 'dashboard',
@@ -516,13 +520,12 @@ class PageFooterComponent {
516
520
  this.isBorderRail() ? 'border-t-[1.5px]' : 'border-t', this.class(), this.resolvedHeight() === 'fix' && 'h-12 overflow-hidden'), /* @ts-ignore */
517
521
  ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
518
522
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PageFooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
519
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.4", type: PageFooterComponent, isStandalone: true, selector: "PageFooter", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-page-slot": "footer" }, properties: { "class": "classes()" } }, ngImport: i0, template: `<ng-content />`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
523
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.4", type: PageFooterComponent, isStandalone: true, selector: "PageFooter", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-page-slot": "footer" }, properties: { "class": "classes()" } }, ngImport: i0, template: `<ng-content />`, isInline: true });
520
524
  }
521
525
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PageFooterComponent, decorators: [{
522
526
  type: Component,
523
527
  args: [{
524
528
  selector: 'PageFooter',
525
- changeDetection: ChangeDetectionStrategy.OnPush,
526
529
  host: {
527
530
  '[class]': 'classes()',
528
531
  'data-page-slot': 'footer',
@@ -567,10 +570,10 @@ class PageSideComponent {
567
570
  ...(ngDevMode ? [{ debugName: "isInteractive" }] : /* istanbul ignore next */ []));
568
571
  resolvedScroll = computed(() => this.page.scroll() ?? PAGE_DEFAULT_SCROLL, /* @ts-ignore */
569
572
  ...(ngDevMode ? [{ debugName: "resolvedScroll" }] : /* istanbul ignore next */ []));
570
- ariaHidden = computed(() => (!this.isSticky() && !this.page.sideOpen() ? 'true' : null), /* @ts-ignore */
573
+ ariaHidden = computed(() => !this.isSticky() && !this.page.sideOpen() ? 'true' : null, /* @ts-ignore */
571
574
  ...(ngDevMode ? [{ debugName: "ariaHidden" }] : /* istanbul ignore next */ []));
572
575
  /** Saat panel mengambang tertutup, `inert` mengeluarkan isinya dari tab order & pohon aksesibilitas (AXE). */
573
- inertWhenClosed = computed(() => (this.isInteractive() && !this.page.sideOpen() ? '' : null), /* @ts-ignore */
576
+ inertWhenClosed = computed(() => this.isInteractive() && !this.page.sideOpen() ? '' : null, /* @ts-ignore */
574
577
  ...(ngDevMode ? [{ debugName: "inertWhenClosed" }] : /* istanbul ignore next */ []));
575
578
  dialogRole = computed(() => (this.isInteractive() ? 'dialog' : null), /* @ts-ignore */
576
579
  ...(ngDevMode ? [{ debugName: "dialogRole" }] : /* istanbul ignore next */ []));
@@ -586,8 +589,16 @@ class PageSideComponent {
586
589
  return cn('block min-h-0 border-border bg-background', this.resolvedScroll() === 'content' && 'h-full overflow-auto', this.resolvedScroll() === 'page' && 'overflow-visible', this.isSticky() && 'shrink-0 w-[var(--page-side-width)]',
587
590
  // Urutan DOM body = rail-lalu-content; `left` tak perlu reorder, `right` mendorong rail ke kolom kedua.
588
591
  this.isSticky() && position === 'left' && 'border-r', this.isSticky() && position === 'right' && 'order-last border-l', this.isDrawer() &&
589
- 'absolute inset-y-0 z-20 w-[var(--page-side-width)] border shadow-sm transition-transform duration-200 ease-out', this.isDrawer() && position === 'left' && (sideOpen ? 'left-0 translate-x-0' : 'left-0 -translate-x-full'), this.isDrawer() && position === 'right' && (sideOpen ? 'right-0 translate-x-0' : 'right-0 translate-x-full'), this.isOverlay() &&
590
- 'absolute inset-y-0 z-30 w-[var(--page-side-width)] border shadow-md transition-transform duration-200 ease-out', this.isOverlay() && position === 'left' && (sideOpen ? 'left-0 translate-x-0' : 'left-0 -translate-x-full'), this.isOverlay() && position === 'right' && (sideOpen ? 'right-0 translate-x-0' : 'right-0 translate-x-full'), this.class());
592
+ 'absolute inset-y-0 z-20 w-[var(--page-side-width)] border shadow-sm transition-transform duration-200 ease-out', this.isDrawer() &&
593
+ position === 'left' &&
594
+ (sideOpen ? 'left-0 translate-x-0' : 'left-0 -translate-x-full'), this.isDrawer() &&
595
+ position === 'right' &&
596
+ (sideOpen ? 'right-0 translate-x-0' : 'right-0 translate-x-full'), this.isOverlay() &&
597
+ 'absolute inset-y-0 z-30 w-[var(--page-side-width)] border shadow-md transition-transform duration-200 ease-out', this.isOverlay() &&
598
+ position === 'left' &&
599
+ (sideOpen ? 'left-0 translate-x-0' : 'left-0 -translate-x-full'), this.isOverlay() &&
600
+ position === 'right' &&
601
+ (sideOpen ? 'right-0 translate-x-0' : 'right-0 translate-x-full'), this.class());
591
602
  }, /* @ts-ignore */
592
603
  ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
593
604
  constructor() {
@@ -629,13 +640,12 @@ class PageSideComponent {
629
640
  });
630
641
  }
631
642
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PageSideComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
632
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.4", type: PageSideComponent, isStandalone: true, selector: "PageSide", inputs: { mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, closeOnEsc: { classPropertyName: "closeOnEsc", publicName: "closeOnEsc", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()", "attr.id": "resolvedId", "attr.data-page-slot": "\"side\"", "attr.data-page-side-mode": "resolvedMode()", "attr.data-page-side-open": "page.sideOpen()", "attr.data-page-position": "resolvedPosition()", "attr.aria-hidden": "ariaHidden()", "attr.inert": "inertWhenClosed()", "attr.role": "dialogRole()", "attr.aria-modal": "ariaModal()", "attr.aria-label": "dialogLabel()", "attr.tabindex": "dialogTabindex()", "style.--page-side-width": "resolvedWidth()" } }, ngImport: i0, template: `<ng-content />`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
643
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.4", type: PageSideComponent, isStandalone: true, selector: "PageSide", inputs: { mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, closeOnEsc: { classPropertyName: "closeOnEsc", publicName: "closeOnEsc", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()", "attr.id": "resolvedId", "attr.data-page-slot": "\"side\"", "attr.data-page-side-mode": "resolvedMode()", "attr.data-page-side-open": "page.sideOpen()", "attr.data-page-position": "resolvedPosition()", "attr.aria-hidden": "ariaHidden()", "attr.inert": "inertWhenClosed()", "attr.role": "dialogRole()", "attr.aria-modal": "ariaModal()", "attr.aria-label": "dialogLabel()", "attr.tabindex": "dialogTabindex()", "style.--page-side-width": "resolvedWidth()" } }, ngImport: i0, template: `<ng-content />`, isInline: true });
633
644
  }
634
645
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PageSideComponent, decorators: [{
635
646
  type: Component,
636
647
  args: [{
637
648
  selector: 'PageSide',
638
- changeDetection: ChangeDetectionStrategy.OnPush,
639
649
  host: {
640
650
  '[class]': 'classes()',
641
651
  '[attr.id]': 'resolvedId',
@@ -718,10 +728,14 @@ class PageComponent {
718
728
  // (diisi `registerFilter`), lalu default — pola seragam agar Page & PageFilter selalu sepakat.
719
729
  hasFilter = computed(() => this.projectedFilter() !== undefined, /* @ts-ignore */
720
730
  ...(ngDevMode ? [{ debugName: "hasFilter" }] : /* istanbul ignore next */ []));
721
- filterPlacement = computed(() => this.projectedFilter()?.placement() ?? this.page.filterPlacement() ?? PAGE_DEFAULT_FILTER_PLACEMENT, /* @ts-ignore */
731
+ filterPlacement = computed(() => this.projectedFilter()?.placement() ??
732
+ this.page.filterPlacement() ??
733
+ PAGE_DEFAULT_FILTER_PLACEMENT, /* @ts-ignore */
722
734
  ...(ngDevMode ? [{ debugName: "filterPlacement" }] : /* istanbul ignore next */ []));
723
735
  /** Mode efektif: `stacked` selalu `sticky`; `side` memakai mode pada `PageFilter`. */
724
- filterMode = computed(() => this.filterPlacement() === 'stacked' ? 'sticky' : (this.projectedFilter()?.mode() ?? this.page.filterMode()), /* @ts-ignore */
736
+ filterMode = computed(() => this.filterPlacement() === 'stacked'
737
+ ? 'sticky'
738
+ : (this.projectedFilter()?.mode() ?? this.page.filterMode()), /* @ts-ignore */
725
739
  ...(ngDevMode ? [{ debugName: "filterMode" }] : /* istanbul ignore next */ []));
726
740
  filterPosition = computed(() => this.projectedFilter()?.position() ?? this.page.filterPosition(), /* @ts-ignore */
727
741
  ...(ngDevMode ? [{ debugName: "filterPosition" }] : /* istanbul ignore next */ []));
@@ -856,7 +870,8 @@ class PageComponent {
856
870
  aria-label="Close overlay panel"
857
871
  data-page-overlay-backdrop
858
872
  class="absolute inset-0 z-20 bg-[hsl(var(--overlay-backdrop))]"
859
- (click)="handleBackdropClick()"></button>
873
+ (click)="handleBackdropClick()"
874
+ ></button>
860
875
  }
861
876
 
862
877
  <div [class]="shellClasses()">
@@ -882,13 +897,15 @@ class PageComponent {
882
897
  [id]="launcherNavId"
883
898
  [data]="appsNavData()"
884
899
  ariaLabel="Application navigation"
885
- [class]="appsLauncherHostClass()">
900
+ [class]="appsLauncherHostClass()"
901
+ >
886
902
  <NavigationFlyout
887
903
  [icon]="appsIcon()"
888
904
  icon-only
889
905
  trigger-variant="plain"
890
906
  [nav-appearance]="appsLauncherAppearance()"
891
- [label]="appsLabel()">
907
+ [label]="appsLabel()"
908
+ >
892
909
  @if (appsBrand(); as brand) {
893
910
  <NavigationHeader>
894
911
  <LayoutBrand [brand]="brand" />
@@ -902,13 +919,12 @@ class PageComponent {
902
919
  </NavigationFlyout>
903
920
  </Navigation>
904
921
  }
905
- `, isInline: true, dependencies: [{ kind: "component", type: NavigationContainerComponent, selector: "Navigation", inputs: ["id", "data", "ariaLabel", "compact", "collapse-tree", "class", "itemClass", "nav-group-class", "activeIds", "activeUrl", "openedIds"], outputs: ["openedIdsChange", "itemSelected"] }, { kind: "component", type: NavigationFlyoutComponent, selector: "NavigationFlyout", inputs: ["label", "icon", "icon-only", "icon-position", "trigger-variant", "trigger-floating", "trigger-class", "nav-position", "nav-appearance", "class"] }, { kind: "component", type: NavigationHeaderComponent, selector: "NavigationHeader", inputs: ["toggle", "class"] }, { kind: "component", type: NavigationFooterComponent, selector: "NavigationFooter", inputs: ["class"] }, { kind: "component", type: LayoutBrand, selector: "LayoutBrand", inputs: ["brand", "compact"] }, { kind: "component", type: LayoutUser, selector: "LayoutUser", inputs: ["user", "detailed", "logoutLabel", "logoutIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
922
+ `, isInline: true, dependencies: [{ kind: "component", type: NavigationContainerComponent, selector: "Navigation", inputs: ["id", "data", "ariaLabel", "compact", "collapse-tree", "class", "itemClass", "nav-group-class", "activeIds", "activeUrl", "openedIds"], outputs: ["openedIdsChange", "itemSelected"] }, { kind: "component", type: NavigationFlyoutComponent, selector: "NavigationFlyout", inputs: ["label", "icon", "icon-only", "icon-position", "trigger-variant", "trigger-floating", "trigger-class", "nav-position", "nav-appearance", "class"] }, { kind: "component", type: NavigationHeaderComponent, selector: "NavigationHeader", inputs: ["toggle", "class"] }, { kind: "component", type: NavigationFooterComponent, selector: "NavigationFooter", inputs: ["class"] }, { kind: "component", type: LayoutBrand, selector: "LayoutBrand", inputs: ["brand", "compact"] }, { kind: "component", type: LayoutUser, selector: "LayoutUser", inputs: ["user", "detailed", "logoutLabel", "logoutIcon"] }] });
906
923
  }
907
924
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PageComponent, decorators: [{
908
925
  type: Component,
909
926
  args: [{
910
927
  selector: 'Page',
911
- changeDetection: ChangeDetectionStrategy.OnPush,
912
928
  providers: [PageStateService],
913
929
  imports: [
914
930
  NavigationContainerComponent,
@@ -940,7 +956,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImpor
940
956
  aria-label="Close overlay panel"
941
957
  data-page-overlay-backdrop
942
958
  class="absolute inset-0 z-20 bg-[hsl(var(--overlay-backdrop))]"
943
- (click)="handleBackdropClick()"></button>
959
+ (click)="handleBackdropClick()"
960
+ ></button>
944
961
  }
945
962
 
946
963
  <div [class]="shellClasses()">
@@ -966,13 +983,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImpor
966
983
  [id]="launcherNavId"
967
984
  [data]="appsNavData()"
968
985
  ariaLabel="Application navigation"
969
- [class]="appsLauncherHostClass()">
986
+ [class]="appsLauncherHostClass()"
987
+ >
970
988
  <NavigationFlyout
971
989
  [icon]="appsIcon()"
972
990
  icon-only
973
991
  trigger-variant="plain"
974
992
  [nav-appearance]="appsLauncherAppearance()"
975
- [label]="appsLabel()">
993
+ [label]="appsLabel()"
994
+ >
976
995
  @if (appsBrand(); as brand) {
977
996
  <NavigationHeader>
978
997
  <LayoutBrand [brand]="brand" />
@@ -1016,7 +1035,8 @@ class PageFilterToggleComponent {
1016
1035
  [attr.aria-label]="ariaLabel()"
1017
1036
  [attr.aria-controls]="page.filterId()"
1018
1037
  [attr.aria-expanded]="page.filterOpen()"
1019
- (click)="handleClick()">
1038
+ (click)="handleClick()"
1039
+ >
1020
1040
  <ng-content>
1021
1041
  <svg
1022
1042
  aria-hidden="true"
@@ -1026,18 +1046,18 @@ class PageFilterToggleComponent {
1026
1046
  stroke="currentColor"
1027
1047
  stroke-width="2"
1028
1048
  stroke-linecap="round"
1029
- stroke-linejoin="round">
1049
+ stroke-linejoin="round"
1050
+ >
1030
1051
  <path d="M3 5h18l-7 8v5l-4 2v-7z" />
1031
1052
  </svg>
1032
1053
  </ng-content>
1033
1054
  </button>
1034
- `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
1055
+ `, isInline: true });
1035
1056
  }
1036
1057
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PageFilterToggleComponent, decorators: [{
1037
1058
  type: Component,
1038
1059
  args: [{
1039
1060
  selector: 'PageFilterToggle',
1040
- changeDetection: ChangeDetectionStrategy.OnPush,
1041
1061
  host: {
1042
1062
  '[class]': 'hostClasses()',
1043
1063
  },
@@ -1048,7 +1068,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImpor
1048
1068
  [attr.aria-label]="ariaLabel()"
1049
1069
  [attr.aria-controls]="page.filterId()"
1050
1070
  [attr.aria-expanded]="page.filterOpen()"
1051
- (click)="handleClick()">
1071
+ (click)="handleClick()"
1072
+ >
1052
1073
  <ng-content>
1053
1074
  <svg
1054
1075
  aria-hidden="true"
@@ -1058,7 +1079,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImpor
1058
1079
  stroke="currentColor"
1059
1080
  stroke-width="2"
1060
1081
  stroke-linecap="round"
1061
- stroke-linejoin="round">
1082
+ stroke-linejoin="round"
1083
+ >
1062
1084
  <path d="M3 5h18l-7 8v5l-4 2v-7z" />
1063
1085
  </svg>
1064
1086
  </ng-content>
@@ -1089,18 +1111,18 @@ class PageSideToggleComponent {
1089
1111
  [attr.aria-label]="ariaLabel()"
1090
1112
  [attr.aria-controls]="page.sideId()"
1091
1113
  [attr.aria-expanded]="page.sideOpen()"
1092
- (click)="handleClick()">
1114
+ (click)="handleClick()"
1115
+ >
1093
1116
  <ng-content>
1094
1117
  <span aria-hidden="true" class="text-lg leading-none">☰</span>
1095
1118
  </ng-content>
1096
1119
  </button>
1097
- `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
1120
+ `, isInline: true });
1098
1121
  }
1099
1122
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PageSideToggleComponent, decorators: [{
1100
1123
  type: Component,
1101
1124
  args: [{
1102
1125
  selector: 'PageSideToggle',
1103
- changeDetection: ChangeDetectionStrategy.OnPush,
1104
1126
  host: {
1105
1127
  '[class]': 'hostClasses()',
1106
1128
  },
@@ -1111,7 +1133,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImpor
1111
1133
  [attr.aria-label]="ariaLabel()"
1112
1134
  [attr.aria-controls]="page.sideId()"
1113
1135
  [attr.aria-expanded]="page.sideOpen()"
1114
- (click)="handleClick()">
1136
+ (click)="handleClick()"
1137
+ >
1115
1138
  <ng-content>
1116
1139
  <span aria-hidden="true" class="text-lg leading-none">☰</span>
1117
1140
  </ng-content>
@@ -551,7 +551,7 @@ function provideUiTheme(options = {}) {
551
551
  // request (privacy / offline / CSP friendly). Set `icons.materialSymbols: true`
552
552
  // or self-host the font and import it yourself.
553
553
  if (options.icons?.materialSymbols === true) {
554
- inject(MaterialSymbolsService).ensureLoaded();
554
+ inject(MaterialSymbolsService).load();
555
555
  }
556
556
  inject(ThemeModeService);
557
557
  inject(ThemeColorService);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ojiepermana/angular-theme",
3
- "version": "22.0.44",
3
+ "version": "22.0.45",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/edsis/angular.git"
@@ -13,15 +13,30 @@
13
13
  "@angular/common": ">=22.0.0",
14
14
  "@angular/core": ">=22.0.0",
15
15
  "@angular/router": ">=22.0.0",
16
- "@ojiepermana/angular-navigation": "^22.0.44",
17
- "@ojiepermana/angular-component": "^22.0.44",
16
+ "@ojiepermana/angular-navigation": "^22.0.45",
17
+ "@ojiepermana/angular-component": "^22.0.45",
18
18
  "rxjs": ">=7.8.0"
19
19
  },
20
+ "peerDependenciesMeta": {
21
+ "@angular/router": {
22
+ "optional": true
23
+ },
24
+ "@ojiepermana/angular-navigation": {
25
+ "optional": true
26
+ },
27
+ "rxjs": {
28
+ "optional": true
29
+ }
30
+ },
20
31
  "dependencies": {
21
32
  "tslib": "^2.8.1"
22
33
  },
23
34
  "exports": {
24
35
  "./theme.css": {
36
+ "style": "./styles/css/base/index.css",
37
+ "default": "./styles/css/base/index.css"
38
+ },
39
+ "./theme-full.css": {
25
40
  "style": "./styles/css/index.css",
26
41
  "default": "./styles/css/index.css"
27
42
  },
@@ -65,7 +80,9 @@
65
80
  "access": "public",
66
81
  "registry": "https://registry.npmjs.org/"
67
82
  },
68
- "sideEffects": false,
83
+ "sideEffects": [
84
+ "**/*.css"
85
+ ],
69
86
  "module": "fesm2022/ojiepermana-angular-theme.mjs",
70
87
  "typings": "types/ojiepermana-angular-theme.d.ts",
71
88
  "type": "module"
@@ -0,0 +1,14 @@
1
+ /*
2
+ * Lightweight default theme stylesheet (exported as `@ojiepermana/angular-theme/theme.css`).
3
+ *
4
+ * Base only: core design tokens, light/dark, and component styles. It does NOT
5
+ * pull in the runtime theme axes (color palettes, neutral families, radius and
6
+ * space presets) so consumers ship a minimal stylesheet by default.
7
+ *
8
+ * Need the runtime axes? Either import the full sheet:
9
+ * @import '@ojiepermana/angular-theme/theme-full.css';
10
+ * or opt in to only the axes you use:
11
+ * @import '@ojiepermana/angular-theme/styles/css/color/index.css';
12
+ * @import '@ojiepermana/angular-theme/styles/css/neutral/index.css';
13
+ */
14
+ @import './package.css';
@@ -88,12 +88,20 @@ declare class LayoutNavSidebar {
88
88
  /** Appearance dari shell (`[appearance]`); menentukan ketebalan border pemisah. */
89
89
  readonly appearance: _angular_core.InputSignal<"flat" | "border-rail">;
90
90
  protected readonly isBorderRail: _angular_core.Signal<boolean>;
91
- /** Border kanan pemisah; ketebalan ikut appearance: `border-rail` 1.5px, `flat` 1px. */
92
- protected readonly shellClass: _angular_core.Signal<"border-r-[1.5px] border-border" | "border-r border-border">;
93
- /** Header divider menebal jadi 1.5px di `border-rail` (selaras nav/shell); `flat` pakai default. */
94
- protected readonly headerClass: _angular_core.Signal<"border-b-[1.5px] border-border" | "">;
95
- /** Footer divider menebal jadi 1.5px di `border-rail`; `px-3` tetap dipertahankan. */
96
- protected readonly footerClass: _angular_core.Signal<"px-3 border-t-[1.5px] border-border" | "px-3">;
91
+ /** Border kanan pemisah; ketebalan ikut appearance: `border-rail` = `--layout-rail-width` (1.21px, selaras rail layout), `flat` 1px. */
92
+ protected readonly shellClass: _angular_core.Signal<"border-r-[length:var(--layout-rail-width)] border-border" | "border-r border-border">;
93
+ /**
94
+ * Header divider: di `border-rail` border bawaan dimatikan (`border-b-0`) karena pemisahnya
95
+ * diambil-alih rail inset accent Layout (`data-layout-horizontal-top-rail` di `top-12`) supaya
96
+ * tidak dempet/dobel dengan garis itu. `flat` pakai default (border bawaan NavigationHeader).
97
+ */
98
+ protected readonly headerClass: _angular_core.Signal<"border-b-0" | "">;
99
+ /**
100
+ * Footer divider: di `border-rail` border bawaan dimatikan (`border-t-0`) karena pemisahnya
101
+ * diambil-alih rail inset accent Layout (`data-layout-horizontal-bottom-rail` di `bottom-12`);
102
+ * `px-3` tetap dipertahankan.
103
+ */
104
+ protected readonly footerClass: _angular_core.Signal<"px-3 border-t-0" | "px-3">;
97
105
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<LayoutNavSidebar, never>;
98
106
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<LayoutNavSidebar, "LayoutNavSidebar", never, { "brand": { "alias": "brand"; "required": true; "isSignal": true; }; "user": { "alias": "user"; "required": true; "isSignal": true; }; "appearance": { "alias": "appearance"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
99
107
  }
@@ -108,12 +116,20 @@ declare class LayoutNavDockbar {
108
116
  /** Appearance dari shell (`[appearance]`); menentukan ketebalan border pemisah. */
109
117
  readonly appearance: _angular_core.InputSignal<"flat" | "border-rail">;
110
118
  protected readonly isBorderRail: _angular_core.Signal<boolean>;
111
- /** Border kanan pemisah; ketebalan ikut appearance: `border-rail` 1.5px, `flat` 1px. */
112
- protected readonly shellClass: _angular_core.Signal<"border-r-[1.5px] border-border" | "border-r border-border">;
113
- /** Header divider menebal jadi 1.5px di `border-rail` (selaras nav/shell); `flat` pakai default. */
114
- protected readonly headerClass: _angular_core.Signal<"border-b-[1.5px] border-border" | "">;
115
- /** Footer divider menebal jadi 1.5px di `border-rail`; `px-3` tetap dipertahankan. */
116
- protected readonly footerClass: _angular_core.Signal<"px-3 border-t-[1.5px] border-border" | "px-3">;
119
+ /** Border kanan pemisah; ketebalan ikut appearance: `border-rail` = `--layout-rail-width` (1.21px, selaras rail layout), `flat` 1px. */
120
+ protected readonly shellClass: _angular_core.Signal<"border-r-[length:var(--layout-rail-width)] border-border" | "border-r border-border">;
121
+ /**
122
+ * Header divider: di `border-rail` border bawaan dimatikan (`border-b-0`) karena pemisahnya
123
+ * diambil-alih rail inset accent Layout (`data-layout-horizontal-top-rail` di `top-12`) supaya
124
+ * tidak dempet/dobel dengan garis itu. `flat` pakai default (border bawaan NavigationHeader).
125
+ */
126
+ protected readonly headerClass: _angular_core.Signal<"border-b-0" | "">;
127
+ /**
128
+ * Footer divider: di `border-rail` border bawaan dimatikan (`border-t-0`) karena pemisahnya
129
+ * diambil-alih rail inset accent Layout (`data-layout-horizontal-bottom-rail` di `bottom-12`);
130
+ * `px-3` tetap dipertahankan.
131
+ */
132
+ protected readonly footerClass: _angular_core.Signal<"px-3 border-t-0" | "px-3">;
117
133
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<LayoutNavDockbar, never>;
118
134
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<LayoutNavDockbar, "LayoutNavDockbar", never, { "brand": { "alias": "brand"; "required": true; "isSignal": true; }; "user": { "alias": "user"; "required": true; "isSignal": true; }; "appearance": { "alias": "appearance"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
119
135
  }