@softwarity/rail-nav 1.1.1 → 1.1.3

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
@@ -26,6 +26,9 @@ Material Design 3 Navigation Rail component for Angular. Extends Angular Materia
26
26
  <a href="https://softwarity.github.io/rail-nav/">
27
27
  <img src="assets/expanded.png" alt="Rail Nav Expanded" height="300">
28
28
  </a>
29
+ <a href="https://softwarity.github.io/rail-nav/">
30
+ <img src="assets/drawer.png" alt="Rail Nav Contextual Drawer" height="300">
31
+ </a>
29
32
  </p>
30
33
 
31
34
  ## Features
@@ -33,6 +36,8 @@ Material Design 3 Navigation Rail component for Angular. Extends Angular Materia
33
36
  - **Material Design 3** - Implements the Navigation Rail pattern from MD3
34
37
  - **Extends Material** - Built on top of `MatSidenav` for reliability
35
38
  - **Navigation Items** - `rail-nav-item` component with MD3 pill animation, badges, and router support
39
+ - **Contextual Drawer** - `[for]` on an item opens a side drawer from an `<ng-template>`, with hover-intent, content cross-fade, outside-tap dismiss and mobile support
40
+ - **Separator & Spacer** - `rail-nav-separator` to group items, `rail-nav-spacer` to anchor items at the bottom
36
41
  - **Expand/Collapse** - Smooth transition between rail and drawer with adaptive width
37
42
  - **Backdrop** - Optional backdrop overlay when expanded
38
43
  - **Position** - Support for left (start) or right (end) positioning
@@ -63,7 +68,10 @@ import {
63
68
  RailnavComponent,
64
69
  RailnavContainerComponent,
65
70
  RailnavContentComponent,
66
- RailnavItemComponent
71
+ RailnavItemComponent,
72
+ RailnavSeparatorComponent, // Optional: separator between item groups
73
+ RailnavSpacerComponent, // Optional: push following items to the bottom
74
+ RailnavBrandingDirective // Optional: for custom branding
67
75
  } from '@softwarity/rail-nav';
68
76
 
69
77
  @Component({
@@ -71,7 +79,10 @@ import {
71
79
  RailnavComponent,
72
80
  RailnavContainerComponent,
73
81
  RailnavContentComponent,
74
- RailnavItemComponent
82
+ RailnavItemComponent,
83
+ RailnavSeparatorComponent,
84
+ RailnavSpacerComponent,
85
+ RailnavBrandingDirective
75
86
  ],
76
87
  template: `
77
88
  <rail-nav-container>
@@ -62,15 +62,38 @@ class RailnavDrawerOrchestrator {
62
62
  }
63
63
  /** Opens or re-targets the overlay to the given template. No-op while the
64
64
  * rail is expanded. Cancels any pending close timer (e.g. user hovered back
65
- * from a quick traverse). */
65
+ * from a quick traverse). When the overlay is already open, re-targets it
66
+ * to the new template WITHOUT disposing — the chrome (bg/shadow/position)
67
+ * stays put while just the projected content fades in. */
66
68
  open(template) {
67
69
  if (this.rail.expanded())
68
70
  return;
69
71
  this.clearCloseTimer();
70
- this.lastOpenAt = performance.now();
71
72
  if (this.currentTemplate === template && this.overlayRef?.hasAttached()) {
72
73
  return;
73
74
  }
75
+ // Re-target: existing overlay stays, just swap the portal content.
76
+ // Note: we do NOT reset `lastOpenAt` here — otherwise hovering a
77
+ // non-trigger item right after a re-target would be blocked by the
78
+ // synthetic-event guard, leaving the drawer stuck open.
79
+ if (this.overlayRef?.hasAttached()) {
80
+ // Cross-fade: fade the OLD content out first (children only, so the
81
+ // panel chrome/bg stays opaque the whole time — no flicker), then swap
82
+ // and fade the new content in. Reads as "content dissolves, new content
83
+ // appears" with the drawer staying perfectly still.
84
+ this.fadeOutContent().then(() => {
85
+ if (!this.overlayRef)
86
+ return;
87
+ this.overlayRef.detach();
88
+ this.currentTemplate = template;
89
+ this.overlayRef.attach(new TemplatePortal(template, this.vcr));
90
+ this.applyHeight();
91
+ this.animateContentIn();
92
+ });
93
+ return;
94
+ }
95
+ // First open: arm the synthetic-event guard and build a fresh overlay.
96
+ this.lastOpenAt = performance.now();
74
97
  this.detach();
75
98
  this.currentTemplate = template;
76
99
  const positionStrategy = this.overlay
@@ -127,14 +150,8 @@ class RailnavDrawerOrchestrator {
127
150
  // make each trigger's drawer a different size) and absorbs the layout
128
151
  // variance between MatButton variants (e.g. text vs tonal padding).
129
152
  el.style.width = 'var(--rail-nav-drawer-width, 240px)';
130
- // Subtle fade-in on every (re-)open: applied via Web Animations API so we
131
- // don't ship CSS keyframes globally. Re-targeting the overlay to another
132
- // template re-runs this animation, giving a soft cross-fade feel.
133
- el.animate([
134
- { opacity: 0, transform: 'translateX(-4px)' },
135
- { opacity: 1, transform: 'translateX(0)' },
136
- ], { duration: 180, easing: 'ease-out' });
137
153
  this.overlayRef.attach(new TemplatePortal(template, this.vcr));
154
+ this.animateIn();
138
155
  // Match the rail's full height so the drawer sits flush against it
139
156
  // regardless of viewport / layout. ResizeObserver keeps it in sync.
140
157
  this.applyHeight();
@@ -148,6 +165,43 @@ class RailnavDrawerOrchestrator {
148
165
  const rect = this.railEl.nativeElement.getBoundingClientRect();
149
166
  this.overlayRef.overlayElement.style.height = `${rect.height}px`;
150
167
  }
168
+ /** Slide-in fade on the whole overlay panel — used for the FIRST open, when
169
+ * the drawer genuinely appears from the rail. Web Animations API so no
170
+ * global CSS keyframes. Material standard easing for an organic feel. */
171
+ animateIn() {
172
+ if (!this.overlayRef)
173
+ return;
174
+ this.overlayRef.overlayElement.animate([
175
+ { opacity: 0, transform: 'translateX(-8px)' },
176
+ { opacity: 1, transform: 'translateX(0)' },
177
+ ], { duration: 280, easing: 'cubic-bezier(0.4, 0, 0.2, 1)' });
178
+ }
179
+ /** Fade-in of just the content (panel children) — second half of the
180
+ * re-target cross-fade. Panel chrome stays fixed. */
181
+ animateContentIn() {
182
+ if (!this.overlayRef)
183
+ return;
184
+ for (const child of Array.from(this.overlayRef.overlayElement.children)) {
185
+ child.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 160, easing: 'ease-out' });
186
+ }
187
+ }
188
+ /** Fade-out of just the content (panel children) — first half of the
189
+ * re-target cross-fade. Resolves once faded so the caller can swap the
190
+ * template while it's invisible. Panel chrome (bg/shadow) stays opaque, so
191
+ * there's no flicker. Resolves immediately if there's nothing to fade. */
192
+ fadeOutContent() {
193
+ if (!this.overlayRef)
194
+ return Promise.resolve();
195
+ const children = Array.from(this.overlayRef.overlayElement.children);
196
+ if (children.length === 0)
197
+ return Promise.resolve();
198
+ const anims = children.map((c) => c.animate([{ opacity: 1 }, { opacity: 0 }], {
199
+ duration: 120,
200
+ easing: 'ease-in',
201
+ fill: 'forwards',
202
+ }));
203
+ return Promise.all(anims.map((a) => a.finished)).then(() => undefined);
204
+ }
151
205
  /** Arms the close timer. Cancelled if the cursor returns to the trigger or
152
206
  * enters the overlay before it fires. Suppressed during the post-open guard
153
207
  * window so synthetic mouseleave events fired right after a touch tap
@@ -283,7 +337,7 @@ class RailnavComponent extends MatSidenav {
283
337
  <nav class="rail-items">
284
338
  <ng-content />
285
339
  </nav>
286
- `, isInline: true, styles: [":host{display:flex;flex-direction:column;position:absolute;top:0;bottom:0;left:0;z-index:100;width:var(--rail-nav-collapsed-width, 72px);border:none!important;outline:none!important;box-shadow:none;background:var(--rail-nav-surface-color, var(--mat-sys-surface));transition:width .2s ease;overflow:visible;--rail-nav-separator-shift: 12px}:host(.expanded){width:var(--rail-nav-expanded-width, fit-content);box-shadow:4px 0 8px #0003;--rail-nav-separator-shift: 0}:host(.position-end){left:auto;right:0}:host(.position-end.expanded){box-shadow:-4px 0 8px #0003}.rail-header{display:flex;align-items:center;gap:12px;height:var(--rail-nav-header-height, 64px);padding:16px 16px 16px 24px;box-sizing:border-box;cursor:pointer;color:var(--rail-nav-on-surface, var(--mat-sys-on-surface))}.rail-header.position-end{padding:16px 24px 16px 16px}.rail-header.position-end .rail-burger{margin-left:auto}.rail-header:hover{background:var(--rail-nav-surface-container-high, var(--mat-sys-surface-container-high))}.rail-items{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;padding:12px;gap:0;box-sizing:border-box;width:100%}.rail-burger{position:relative;width:24px;height:24px;flex-shrink:0}.rail-burger.position-end{transform:scaleX(-1)}.rail-burger svg{position:absolute;top:0;left:0;transition:opacity .3s ease,transform .3s ease}.rail-burger .icon-menu{opacity:1;transform:rotate(0)}.rail-burger .icon-menu-open{opacity:0;transform:rotate(-90deg)}.rail-burger.expanded .icon-menu{opacity:0;transform:rotate(90deg)}.rail-burger.expanded .icon-menu-open{opacity:1;transform:rotate(0)}.rail-burger.position-end .icon-menu{transform:rotate(0)}.rail-burger.position-end .icon-menu-open{transform:rotate(90deg)}.rail-burger.position-end.expanded .icon-menu{transform:rotate(-90deg)}.rail-burger.position-end.expanded .icon-menu-open{transform:rotate(0)}.rail-branding{display:flex;flex-direction:column;justify-content:center;min-width:0;overflow:hidden;text-align:right;padding-top:5px}.rail-branding.position-end{text-align:left}.rail-title{font-size:16px;font-weight:500;line-height:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--rail-nav-on-surface, var(--mat-sys-on-surface))}.rail-subtitle{font-size:11px;line-height:1;color:var(--rail-nav-on-surface-variant, var(--mat-sys-on-surface-variant));white-space:nowrap;overflow:hidden;text-overflow:ellipsis;opacity:.7}\n"], dependencies: [{ kind: "ngmodule", type: MatRippleModule }, { kind: "directive", type: i1.MatRipple, selector: "[mat-ripple], [matRipple]", inputs: ["matRippleColor", "matRippleUnbounded", "matRippleCentered", "matRippleRadius", "matRippleAnimation", "matRippleDisabled", "matRippleTrigger"], exportAs: ["matRipple"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] }); }
340
+ `, isInline: true, styles: [":host(.mat-drawer){display:flex;flex-direction:column;position:absolute;top:0;bottom:0;left:0;z-index:100;width:var(--rail-nav-collapsed-width, 72px);border:none!important;outline:none!important;box-shadow:none;background:var(--rail-nav-surface-color, var(--mat-sys-surface));transition:width .2s ease;overflow:visible;--rail-nav-separator-shift: 12px}:host(.mat-drawer.expanded){width:var(--rail-nav-expanded-width, fit-content);box-shadow:4px 0 8px #0003;--rail-nav-separator-shift: 0}:host(.mat-drawer.position-end){left:auto;right:0}:host(.mat-drawer.position-end.expanded){box-shadow:-4px 0 8px #0003}.rail-header{display:flex;align-items:center;gap:12px;height:var(--rail-nav-header-height, 64px);padding:16px 16px 16px 24px;box-sizing:border-box;cursor:pointer;color:var(--rail-nav-on-surface, var(--mat-sys-on-surface))}.rail-header.position-end{padding:16px 24px 16px 16px}.rail-header.position-end .rail-burger{margin-left:auto}.rail-header:hover{background:var(--rail-nav-surface-container-high, var(--mat-sys-surface-container-high))}.rail-items{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;padding:12px;gap:0;box-sizing:border-box;width:100%}.rail-burger{position:relative;width:24px;height:24px;flex-shrink:0}.rail-burger.position-end{transform:scaleX(-1)}.rail-burger svg{position:absolute;top:0;left:0;transition:opacity .3s ease,transform .3s ease}.rail-burger .icon-menu{opacity:1;transform:rotate(0)}.rail-burger .icon-menu-open{opacity:0;transform:rotate(-90deg)}.rail-burger.expanded .icon-menu{opacity:0;transform:rotate(90deg)}.rail-burger.expanded .icon-menu-open{opacity:1;transform:rotate(0)}.rail-burger.position-end .icon-menu{transform:rotate(0)}.rail-burger.position-end .icon-menu-open{transform:rotate(90deg)}.rail-burger.position-end.expanded .icon-menu{transform:rotate(-90deg)}.rail-burger.position-end.expanded .icon-menu-open{transform:rotate(0)}.rail-branding{display:flex;flex-direction:column;justify-content:center;min-width:0;overflow:hidden;text-align:right;padding-top:5px}.rail-branding.position-end{text-align:left}.rail-title{font-size:16px;font-weight:500;line-height:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--rail-nav-on-surface, var(--mat-sys-on-surface))}.rail-subtitle{font-size:11px;line-height:1;color:var(--rail-nav-on-surface-variant, var(--mat-sys-on-surface-variant));white-space:nowrap;overflow:hidden;text-overflow:ellipsis;opacity:.7}\n"], dependencies: [{ kind: "ngmodule", type: MatRippleModule }, { kind: "directive", type: i1.MatRipple, selector: "[mat-ripple], [matRipple]", inputs: ["matRippleColor", "matRippleUnbounded", "matRippleCentered", "matRippleRadius", "matRippleAnimation", "matRippleDisabled", "matRippleTrigger"], exportAs: ["matRipple"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] }); }
287
341
  }
288
342
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: RailnavComponent, decorators: [{
289
343
  type: Component,
@@ -330,7 +384,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
330
384
  'class': 'mat-drawer mat-sidenav',
331
385
  '[class.expanded]': 'expanded()',
332
386
  '[class.position-end]': 'railPosition() === "end"'
333
- }, providers: [RailnavDrawerOrchestrator], styles: [":host{display:flex;flex-direction:column;position:absolute;top:0;bottom:0;left:0;z-index:100;width:var(--rail-nav-collapsed-width, 72px);border:none!important;outline:none!important;box-shadow:none;background:var(--rail-nav-surface-color, var(--mat-sys-surface));transition:width .2s ease;overflow:visible;--rail-nav-separator-shift: 12px}:host(.expanded){width:var(--rail-nav-expanded-width, fit-content);box-shadow:4px 0 8px #0003;--rail-nav-separator-shift: 0}:host(.position-end){left:auto;right:0}:host(.position-end.expanded){box-shadow:-4px 0 8px #0003}.rail-header{display:flex;align-items:center;gap:12px;height:var(--rail-nav-header-height, 64px);padding:16px 16px 16px 24px;box-sizing:border-box;cursor:pointer;color:var(--rail-nav-on-surface, var(--mat-sys-on-surface))}.rail-header.position-end{padding:16px 24px 16px 16px}.rail-header.position-end .rail-burger{margin-left:auto}.rail-header:hover{background:var(--rail-nav-surface-container-high, var(--mat-sys-surface-container-high))}.rail-items{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;padding:12px;gap:0;box-sizing:border-box;width:100%}.rail-burger{position:relative;width:24px;height:24px;flex-shrink:0}.rail-burger.position-end{transform:scaleX(-1)}.rail-burger svg{position:absolute;top:0;left:0;transition:opacity .3s ease,transform .3s ease}.rail-burger .icon-menu{opacity:1;transform:rotate(0)}.rail-burger .icon-menu-open{opacity:0;transform:rotate(-90deg)}.rail-burger.expanded .icon-menu{opacity:0;transform:rotate(90deg)}.rail-burger.expanded .icon-menu-open{opacity:1;transform:rotate(0)}.rail-burger.position-end .icon-menu{transform:rotate(0)}.rail-burger.position-end .icon-menu-open{transform:rotate(90deg)}.rail-burger.position-end.expanded .icon-menu{transform:rotate(-90deg)}.rail-burger.position-end.expanded .icon-menu-open{transform:rotate(0)}.rail-branding{display:flex;flex-direction:column;justify-content:center;min-width:0;overflow:hidden;text-align:right;padding-top:5px}.rail-branding.position-end{text-align:left}.rail-title{font-size:16px;font-weight:500;line-height:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--rail-nav-on-surface, var(--mat-sys-on-surface))}.rail-subtitle{font-size:11px;line-height:1;color:var(--rail-nav-on-surface-variant, var(--mat-sys-on-surface-variant));white-space:nowrap;overflow:hidden;text-overflow:ellipsis;opacity:.7}\n"] }]
387
+ }, providers: [RailnavDrawerOrchestrator], styles: [":host(.mat-drawer){display:flex;flex-direction:column;position:absolute;top:0;bottom:0;left:0;z-index:100;width:var(--rail-nav-collapsed-width, 72px);border:none!important;outline:none!important;box-shadow:none;background:var(--rail-nav-surface-color, var(--mat-sys-surface));transition:width .2s ease;overflow:visible;--rail-nav-separator-shift: 12px}:host(.mat-drawer.expanded){width:var(--rail-nav-expanded-width, fit-content);box-shadow:4px 0 8px #0003;--rail-nav-separator-shift: 0}:host(.mat-drawer.position-end){left:auto;right:0}:host(.mat-drawer.position-end.expanded){box-shadow:-4px 0 8px #0003}.rail-header{display:flex;align-items:center;gap:12px;height:var(--rail-nav-header-height, 64px);padding:16px 16px 16px 24px;box-sizing:border-box;cursor:pointer;color:var(--rail-nav-on-surface, var(--mat-sys-on-surface))}.rail-header.position-end{padding:16px 24px 16px 16px}.rail-header.position-end .rail-burger{margin-left:auto}.rail-header:hover{background:var(--rail-nav-surface-container-high, var(--mat-sys-surface-container-high))}.rail-items{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;padding:12px;gap:0;box-sizing:border-box;width:100%}.rail-burger{position:relative;width:24px;height:24px;flex-shrink:0}.rail-burger.position-end{transform:scaleX(-1)}.rail-burger svg{position:absolute;top:0;left:0;transition:opacity .3s ease,transform .3s ease}.rail-burger .icon-menu{opacity:1;transform:rotate(0)}.rail-burger .icon-menu-open{opacity:0;transform:rotate(-90deg)}.rail-burger.expanded .icon-menu{opacity:0;transform:rotate(90deg)}.rail-burger.expanded .icon-menu-open{opacity:1;transform:rotate(0)}.rail-burger.position-end .icon-menu{transform:rotate(0)}.rail-burger.position-end .icon-menu-open{transform:rotate(90deg)}.rail-burger.position-end.expanded .icon-menu{transform:rotate(-90deg)}.rail-burger.position-end.expanded .icon-menu-open{transform:rotate(0)}.rail-branding{display:flex;flex-direction:column;justify-content:center;min-width:0;overflow:hidden;text-align:right;padding-top:5px}.rail-branding.position-end{text-align:left}.rail-title{font-size:16px;font-weight:500;line-height:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--rail-nav-on-surface, var(--mat-sys-on-surface))}.rail-subtitle{font-size:11px;line-height:1;color:var(--rail-nav-on-surface-variant, var(--mat-sys-on-surface-variant));white-space:nowrap;overflow:hidden;text-overflow:ellipsis;opacity:.7}\n"] }]
334
388
  }], ctorParameters: () => [], propDecorators: { railPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], hideDefaultHeader: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideDefaultHeader", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], subtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtitle", required: false }] }], autoCollapse: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoCollapse", required: false }] }], customBranding: [{ type: i0.ContentChild, args: [i0.forwardRef(() => RailnavBrandingDirective), { isSignal: true }] }] } });
335
389
 
336
390
  class RailnavContainerComponent extends MatSidenavContainer {
@@ -1 +1 @@
1
- {"version":3,"file":"softwarity-rail-nav.mjs","sources":["../../src/lib/railnav-drawer.orchestrator.ts","../../src/lib/railnav.component.ts","../../src/lib/railnav-container.component.ts","../../src/lib/railnav-content.component.ts","../../src/lib/railnav-item.component.ts","../../src/lib/railnav-separator.component.ts","../../src/lib/railnav-spacer.component.ts","../../src/public-api.ts","../../src/softwarity-rail-nav.ts"],"sourcesContent":["import { Overlay, OverlayRef } from '@angular/cdk/overlay';\nimport { TemplatePortal } from '@angular/cdk/portal';\nimport { DestroyRef, ElementRef, Injectable, TemplateRef, ViewContainerRef, effect, inject } from '@angular/core';\nimport { Subscription } from 'rxjs';\nimport { RailnavComponent } from './railnav.component';\n\n/**\n * One per `RailnavComponent` (provided on its `providers`). Owns the single\n * CDK overlay used to render the currently-open `<rail-nav-drawer>`, and the\n * close timer that lets the cursor travel between the trigger item and the\n * drawer without the drawer closing on transit.\n *\n * - Anchors the overlay to the rail's host element (right edge), so the drawer\n * sits next to the collapsed rail regardless of which trigger item opened it.\n * - Auto-closes when the rail enters `expanded()` mode (mutual exclusion).\n * - Mouse enter/leave on the overlay panel itself cancels/arms the close timer,\n * so the drawer stays open while the cursor is over it.\n */\n@Injectable()\nexport class RailnavDrawerOrchestrator {\n private readonly overlay = inject(Overlay);\n private readonly vcr = inject(ViewContainerRef);\n private readonly rail = inject(RailnavComponent);\n private readonly railEl = inject(ElementRef<HTMLElement>);\n\n /** Window after which we close if nothing cancels — covers cursor transit. */\n private static readonly LEAVE_DELAY_MS = 300;\n /** Post-open grace period during which `armClose()` is a no-op. Touch devices\n * dispatch a burst of synthetic mouse events (with `pointerType === 'mouse'`)\n * right after `click` — this window swallows them. */\n private static readonly SYNTHETIC_GUARD_MS = 500;\n\n private overlayRef?: OverlayRef;\n private currentTemplate?: TemplateRef<unknown>;\n private closeTimer?: ReturnType<typeof setTimeout>;\n private resizeObserver?: ResizeObserver;\n private outsideClickSub?: Subscription;\n /** Timestamp of the last `open()` — used to gate close-on-leave so that\n * synthetic `mouseleave` events dispatched right after a touch tap\n * (with `pointerType === 'mouse'`) don't shut the drawer instantly. */\n private lastOpenAt = 0;\n\n constructor() {\n // Mutual exclusion with the rail's expanded mode: expanding closes any open\n // drawer, and `open()` short-circuits while expanded. The expanded rail is\n // itself the alternative nav surface, no point stacking a drawer on top.\n effect(() => {\n if (this.rail.expanded()) this.closeNow();\n });\n inject(DestroyRef).onDestroy(() => {\n this.clearCloseTimer();\n this.overlayRef?.dispose();\n });\n }\n\n /** Opens or re-targets the overlay to the given template. No-op while the\n * rail is expanded. Cancels any pending close timer (e.g. user hovered back\n * from a quick traverse). */\n open(template: TemplateRef<unknown>): void {\n if (this.rail.expanded()) return;\n this.clearCloseTimer();\n this.lastOpenAt = performance.now();\n if (this.currentTemplate === template && this.overlayRef?.hasAttached()) {\n return;\n }\n this.detach();\n this.currentTemplate = template;\n\n const positionStrategy = this.overlay\n .position()\n .flexibleConnectedTo(this.railEl)\n .withPositions([\n { originX: 'end', originY: 'top', overlayX: 'start', overlayY: 'top' },\n ]);\n\n this.overlayRef = this.overlay.create({\n positionStrategy,\n hasBackdrop: false,\n panelClass: 'rail-nav-drawer-panel',\n // Reposition on every scroll so the drawer stays glued to the rail\n // when the page scrolls (default `noop` strategy leaves the overlay\n // fixed to the viewport, which visually decouples it from the rail).\n scrollStrategy: this.overlay.scrollStrategies.reposition(),\n });\n\n // Tap / click outside the overlay closes it — except when the click\n // lands in the rail itself (re-clicking a `[for]` item should re-target\n // or no-op, not close). Crucial on touch where hover-leave doesn't fire.\n this.outsideClickSub = this.overlayRef.outsidePointerEvents().subscribe((event) => {\n const target = event.target as Node | null;\n if (target && this.railEl.nativeElement.contains(target)) return;\n this.closeNow();\n });\n\n // Keep the drawer open while the cursor is over it; close shortly after\n // it leaves (matching the trigger's own leave debounce). pointer* events\n // let us ignore the synthetic mouseleave fired around touch taps.\n this.overlayRef.overlayElement.addEventListener('pointerenter', this.onOverlayEnter);\n this.overlayRef.overlayElement.addEventListener('pointerleave', this.onOverlayLeave);\n\n // Inline-style the panel: the projected template content lives outside any\n // component tree, so component-encapsulated styles can't reach it without\n // ::ng-deep / Encapsulation.None. Setting styles directly on overlayElement\n // sidesteps the issue entirely. Each value reads a CSS custom property so\n // consumers can override via `--rail-nav-drawer-*` on the rail.\n //\n // The lib applies both the chrome (background / shadow / radius) AND the\n // default nav-list layout (flex column / padding / gap / min-width) so the\n // consumer's template can be just a flat list of children — they stack\n // vertically with proper spacing out of the box.\n const el = this.overlayRef.overlayElement;\n el.style.background = 'var(--rail-nav-drawer-surface, var(--mat-sys-surface))';\n el.style.color = 'var(--rail-nav-drawer-on-surface, var(--mat-sys-on-surface))';\n el.style.boxShadow = 'var(--rail-nav-drawer-shadow, var(--mat-sys-level2))';\n el.style.borderRadius = 'var(--rail-nav-drawer-radius, 0 12px 12px 0)';\n el.style.overflow = 'auto';\n el.style.boxSizing = 'border-box';\n el.style.display = 'flex';\n el.style.flexDirection = 'column';\n el.style.padding = 'var(--rail-nav-drawer-padding, 12px)';\n el.style.gap = 'var(--rail-nav-drawer-gap, 4px)';\n // Fixed width so the drawer doesn't shrink-fit its content (which would\n // make each trigger's drawer a different size) and absorbs the layout\n // variance between MatButton variants (e.g. text vs tonal padding).\n el.style.width = 'var(--rail-nav-drawer-width, 240px)';\n\n // Subtle fade-in on every (re-)open: applied via Web Animations API so we\n // don't ship CSS keyframes globally. Re-targeting the overlay to another\n // template re-runs this animation, giving a soft cross-fade feel.\n el.animate(\n [\n { opacity: 0, transform: 'translateX(-4px)' },\n { opacity: 1, transform: 'translateX(0)' },\n ],\n { duration: 180, easing: 'ease-out' },\n );\n\n this.overlayRef.attach(new TemplatePortal(template, this.vcr));\n\n // Match the rail's full height so the drawer sits flush against it\n // regardless of viewport / layout. ResizeObserver keeps it in sync.\n this.applyHeight();\n this.resizeObserver?.disconnect();\n this.resizeObserver = new ResizeObserver(() => this.applyHeight());\n this.resizeObserver.observe(this.railEl.nativeElement);\n }\n\n private applyHeight(): void {\n if (!this.overlayRef) return;\n const rect = this.railEl.nativeElement.getBoundingClientRect();\n this.overlayRef.overlayElement.style.height = `${rect.height}px`;\n }\n\n /** Arms the close timer. Cancelled if the cursor returns to the trigger or\n * enters the overlay before it fires. Suppressed during the post-open guard\n * window so synthetic mouseleave events fired right after a touch tap\n * don't shut the drawer instantly. */\n armClose(): void {\n if (performance.now() - this.lastOpenAt < RailnavDrawerOrchestrator.SYNTHETIC_GUARD_MS) {\n return;\n }\n this.clearCloseTimer();\n this.closeTimer = setTimeout(\n () => this.closeNow(),\n RailnavDrawerOrchestrator.LEAVE_DELAY_MS,\n );\n }\n\n /** Force-close immediately, e.g. on a click inside the drawer that navigates. */\n closeNow(): void {\n this.clearCloseTimer();\n this.detach();\n }\n\n private detach(): void {\n this.resizeObserver?.disconnect();\n this.resizeObserver = undefined;\n this.outsideClickSub?.unsubscribe();\n this.outsideClickSub = undefined;\n if (!this.overlayRef) return;\n this.overlayRef.overlayElement.removeEventListener('pointerenter', this.onOverlayEnter);\n this.overlayRef.overlayElement.removeEventListener('pointerleave', this.onOverlayLeave);\n // dispose (not just detach) so the next `open()` builds a fresh overlay\n // with its own position strategy + listeners — no stale state to leak.\n this.overlayRef.dispose();\n this.overlayRef = undefined;\n this.currentTemplate = undefined;\n }\n\n private clearCloseTimer(): void {\n if (this.closeTimer !== undefined) {\n clearTimeout(this.closeTimer);\n this.closeTimer = undefined;\n }\n }\n\n // Arrow-functions so the listener references stay stable across add/remove.\n // Touch / pen pointers are ignored — those flows are click-driven.\n private readonly onOverlayEnter = (event: PointerEvent): void => {\n if (event.pointerType !== 'mouse') return;\n this.clearCloseTimer();\n };\n private readonly onOverlayLeave = (event: PointerEvent): void => {\n if (event.pointerType !== 'mouse') return;\n this.armClose();\n };\n}\n","import { Component, signal, effect, input, contentChild, ElementRef, Directive, computed } from '@angular/core';\nimport { MatSidenav } from '@angular/material/sidenav';\nimport { MatRippleModule } from '@angular/material/core';\nimport { NgTemplateOutlet } from '@angular/common';\nimport { RailnavDrawerOrchestrator } from './railnav-drawer.orchestrator';\n\n/** Directive to mark custom branding content */\n@Directive({\n selector: '[railNavBranding]'\n})\nexport class RailnavBrandingDirective {}\n\n@Component({\n selector: 'rail-nav',\n imports: [MatRippleModule, NgTemplateOutlet],\n template: `\n <!-- Default branding template (title/subtitle) -->\n <ng-template #defaultBrandingTpl>\n <div class=\"rail-branding\" [class.position-end]=\"railPosition() === 'end'\">\n @if (title()) {\n <span class=\"rail-title\">{{ title() }}</span>\n }\n @if (subtitle()) {\n <span class=\"rail-subtitle\">{{ subtitle() }}</span>\n }\n </div>\n </ng-template>\n <!-- Custom branding template (projected content) -->\n <ng-template #customBrandingTpl>\n <div class=\"rail-branding rail-branding-custom\" [class.position-end]=\"railPosition() === 'end'\">\n <ng-content select=\"[railNavBranding]\" />\n </div>\n </ng-template>\n @if (!hideDefaultHeader()) {\n <div class=\"rail-header\" [class.position-end]=\"railPosition() === 'end'\" matRipple (click)=\"toggleExpanded()\">\n @if (expanded() && hasBranding() && railPosition() === 'end') {\n <ng-container [ngTemplateOutlet]=\"customBranding() ? customBrandingTpl : defaultBrandingTpl\" />\n }\n <div class=\"rail-burger\" [class.expanded]=\"expanded()\" [class.position-end]=\"railPosition() === 'end'\">\n <svg class=\"icon-menu\" xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\" width=\"24px\" fill=\"currentColor\">\n <path d=\"M120-240v-80h720v80H120Zm0-200v-80h720v80H120Zm0-200v-80h720v80H120Z\"/>\n </svg>\n <svg class=\"icon-menu-open\" xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\" width=\"24px\" fill=\"currentColor\">\n <path d=\"M120-240v-80h520v80H120Zm664-40L584-480l200-200 56 56-144 144 144 144-56 56ZM120-440v-80h400v80H120Zm0-200v-80h520v80H120Z\"/>\n </svg>\n </div>\n @if (expanded() && hasBranding() && railPosition() === 'start') {\n <ng-container [ngTemplateOutlet]=\"customBranding() ? customBrandingTpl : defaultBrandingTpl\" />\n }\n </div>\n }\n <nav class=\"rail-items\">\n <ng-content />\n </nav>\n `,\n styles: [`\n :host {\n /* flex column so .rail-items can stretch (flex: 1) and */\n /* rail-nav-spacer inside it can push siblings to the bottom. */\n display: flex;\n flex-direction: column;\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n z-index: 100;\n width: var(--rail-nav-collapsed-width, 72px);\n border: none !important;\n outline: none !important;\n box-shadow: none;\n background: var(--rail-nav-surface-color, var(--mat-sys-surface));\n transition: width 0.2s ease;\n overflow: visible;\n\n /* Collapsed default: rail items show their label-below (~20px) but */\n /* the next pill keeps its margin-top, so the inter-item gap is */\n /* asymmetric. Shift the separator down to recenter it visually. */\n --rail-nav-separator-shift: 12px;\n }\n\n :host(.expanded) {\n width: var(--rail-nav-expanded-width, fit-content);\n box-shadow: 4px 0 8px rgba(0,0,0,.2);\n /* Expanded mode has no label-below, items are flush. No shift needed. */\n --rail-nav-separator-shift: 0;\n }\n\n :host(.position-end) {\n left: auto;\n right: 0;\n }\n\n :host(.position-end.expanded) {\n box-shadow: -4px 0 8px rgba(0,0,0,.2);\n }\n\n .rail-header {\n display: flex;\n align-items: center;\n gap: 12px;\n height: var(--rail-nav-header-height, 64px);\n padding: 16px 16px 16px 24px;\n box-sizing: border-box;\n cursor: pointer;\n color: var(--rail-nav-on-surface, var(--mat-sys-on-surface));\n }\n\n .rail-header.position-end {\n padding: 16px 24px 16px 16px;\n }\n\n .rail-header.position-end .rail-burger {\n margin-left: auto;\n }\n\n .rail-header:hover {\n background: var(--rail-nav-surface-container-high, var(--mat-sys-surface-container-high));\n }\n\n .rail-items {\n display: flex;\n flex-direction: column;\n flex: 1 1 auto;\n min-height: 0;\n /* Symmetric vertical padding so the last item (typically Settings */\n /* anchored via <rail-nav-spacer/>) breathes against the rail edge. */\n padding: 12px;\n gap: 0;\n box-sizing: border-box;\n width: 100%;\n }\n\n .rail-burger {\n position: relative;\n width: 24px;\n height: 24px;\n flex-shrink: 0;\n }\n\n .rail-burger.position-end {\n transform: scaleX(-1);\n }\n\n .rail-burger svg {\n position: absolute;\n top: 0;\n left: 0;\n transition: opacity 0.3s ease, transform 0.3s ease;\n }\n\n /* Position start (left) - clockwise rotation */\n .rail-burger .icon-menu {\n opacity: 1;\n transform: rotate(0deg);\n }\n\n .rail-burger .icon-menu-open {\n opacity: 0;\n transform: rotate(-90deg);\n }\n\n .rail-burger.expanded .icon-menu {\n opacity: 0;\n transform: rotate(90deg);\n }\n\n .rail-burger.expanded .icon-menu-open {\n opacity: 1;\n transform: rotate(0deg);\n }\n\n /* Position end (right) - counter-clockwise rotation (mirrored) */\n .rail-burger.position-end .icon-menu {\n transform: rotate(0deg);\n }\n\n .rail-burger.position-end .icon-menu-open {\n transform: rotate(90deg);\n }\n\n .rail-burger.position-end.expanded .icon-menu {\n transform: rotate(-90deg);\n }\n\n .rail-burger.position-end.expanded .icon-menu-open {\n transform: rotate(0deg);\n }\n\n .rail-branding {\n display: flex;\n flex-direction: column;\n justify-content: center;\n min-width: 0;\n overflow: hidden;\n text-align: right;\n padding-top: 5px;\n }\n\n .rail-branding.position-end {\n text-align: left;\n }\n\n .rail-title {\n font-size: 16px;\n font-weight: 500;\n line-height: 1;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n color: var(--rail-nav-on-surface, var(--mat-sys-on-surface));\n }\n\n .rail-subtitle {\n font-size: 11px;\n line-height: 1;\n color: var(--rail-nav-on-surface-variant, var(--mat-sys-on-surface-variant));\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n opacity: 0.7;\n }\n `],\n host: {\n 'class': 'mat-drawer mat-sidenav',\n '[class.expanded]': 'expanded()',\n '[class.position-end]': 'railPosition() === \"end\"'\n },\n providers: [RailnavDrawerOrchestrator]\n})\nexport class RailnavComponent extends MatSidenav {\n /** Position: 'start' (left) or 'end' (right) - aliased to avoid conflict with MatSidenav.position */\n readonly railPosition = input<'start' | 'end'>('start', { alias: 'position' });\n\n /** Hide the default header (burger + title/subtitle) */\n readonly hideDefaultHeader = input(false);\n\n /** Title displayed when expanded (ignored if railNavBranding is projected) */\n readonly title = input<string>();\n\n /** Subtitle displayed when expanded (ignored if railNavBranding is projected) */\n readonly subtitle = input<string>();\n\n /** Whether to auto-collapse when an item is clicked */\n readonly autoCollapse = input(true);\n\n /** Whether the rail is expanded to show labels */\n readonly expanded = signal(false);\n\n /** Detect custom branding content projection */\n protected readonly customBranding = contentChild(RailnavBrandingDirective);\n\n /** Whether there's any branding to show (custom or default) */\n protected readonly hasBranding = computed(() =>\n !!this.customBranding() || !!this.title() || !!this.subtitle()\n );\n\n constructor() {\n super();\n // Default settings for rail behavior\n this.opened = true;\n this.disableClose = true;\n this.mode = 'side';\n\n // Sync mode with expanded state\n effect(() => {\n this.mode = this.expanded() ? 'over' : 'side';\n });\n }\n\n /** Toggle between collapsed (rail) and expanded (drawer) */\n toggleExpanded(): void {\n this.expanded.update(e => !e);\n }\n\n /** Collapse the rail */\n collapse(): void {\n this.expanded.set(false);\n }\n\n /** Expand the rail to drawer */\n expand(): void {\n this.expanded.set(true);\n }\n}\n","import { Component, ContentChild, input } from '@angular/core';\nimport { MatSidenavContainer, MatDrawerContainer } from '@angular/material/sidenav';\nimport { RailnavComponent } from './railnav.component';\n\n@Component({\n selector: 'rail-nav-container',\n template: `\n <ng-content />\n @if (showBackdrop()) {\n <div\n class=\"railnav-backdrop\"\n [class.visible]=\"railnav?.expanded()\"\n [class.position-end]=\"railnav?.railPosition() === 'end'\"\n (click)=\"railnav?.collapse()\">\n </div>\n }\n `,\n styles: [`\n :host {\n display: block;\n position: relative;\n height: 100%;\n overflow: hidden;\n }\n\n .railnav-backdrop {\n position: absolute;\n top: 0;\n bottom: 0;\n left: var(--rail-nav-collapsed-width, 72px);\n right: 0;\n background: var(--rail-nav-backdrop-color, rgba(0, 0, 0, 0.4));\n z-index: 99;\n cursor: pointer;\n opacity: 0;\n pointer-events: none;\n transition: opacity 0.3s ease-in-out;\n }\n\n .railnav-backdrop.position-end {\n left: 0;\n right: var(--rail-nav-collapsed-width, 72px);\n }\n\n .railnav-backdrop.visible {\n opacity: 1;\n pointer-events: auto;\n }\n `],\n host: {\n 'class': 'mat-drawer-container mat-sidenav-container'\n },\n providers: [\n { provide: MatDrawerContainer, useExisting: RailnavContainerComponent }\n ]\n})\nexport class RailnavContainerComponent extends MatSidenavContainer {\n /** Whether to show backdrop when expanded */\n readonly showBackdrop = input(true);\n\n @ContentChild(RailnavComponent) railnav?: RailnavComponent;\n}\n","import { Component, input, inject, computed } from '@angular/core';\nimport { MatSidenavContent } from '@angular/material/sidenav';\nimport { RailnavContainerComponent } from './railnav-container.component';\n\n@Component({\n selector: 'rail-nav-content',\n template: `<ng-content />`,\n styles: [`\n :host {\n display: block;\n height: 100%;\n overflow: auto;\n margin-left: var(--rail-nav-collapsed-width, 72px);\n }\n\n :host(.position-end) {\n margin-left: 0;\n margin-right: var(--rail-nav-collapsed-width, 72px);\n }\n `],\n host: {\n 'class': 'mat-drawer-content mat-sidenav-content',\n '[class.position-end]': 'effectivePosition() === \"end\"'\n }\n})\nexport class RailnavContentComponent extends MatSidenavContent {\n /** Optional: Position of the rail. If not set, uses the sibling rail-nav's position */\n readonly position = input<'start' | 'end' | undefined>(undefined);\n\n /** Parent container that gives access to sibling rail-nav */\n private container = inject(RailnavContainerComponent, { optional: true });\n\n /** Effective position - from input or from sibling rail-nav */\n protected readonly effectivePosition = computed(() => {\n const inputValue = this.position();\n if (inputValue !== undefined) return inputValue;\n return this.container?.railnav?.railPosition() ?? 'start';\n });\n}\n","import { Component, DestroyRef, TemplateRef, input, output, computed, inject, ChangeDetectionStrategy } from '@angular/core';\nimport { RouterLink, RouterLinkActive } from '@angular/router';\nimport { MatRippleModule } from '@angular/material/core';\nimport { RailnavComponent } from './railnav.component';\nimport { RailnavDrawerOrchestrator } from './railnav-drawer.orchestrator';\nimport { NgTemplateOutlet } from '@angular/common';\n\n@Component({\n selector: 'rail-nav-item',\n imports: [RouterLink, RouterLinkActive, MatRippleModule, NgTemplateOutlet],\n template: `\n <ng-template #iconTpl>\n <div class=\"rail-item-pill\">\n <div class=\"rail-item-ripple\" matRipple></div>\n <div class=\"rail-item-icon-wrapper\">\n <div class=\"rail-item-icon\">\n <ng-content />\n </div>\n @if (hasBadge()) {\n <span class=\"rail-badge\" [class.dot]=\"isDotBadge()\">{{ isDotBadge() ? '' : badge() }}</span>\n }\n </div>\n <span class=\"rail-item-label label-inline\">{{ label() }}</span>\n </div>\n <span class=\"rail-item-label label-below\">{{ label() }}</span>\n </ng-template>\n @if (routerLink()) {\n <a\n class=\"rail-item\"\n [class.expanded]=\"expanded()\"\n [class.position-end]=\"position() === 'end'\"\n [class.active]=\"active()\"\n [routerLink]=\"routerLink()\"\n routerLinkActive=\"active\"\n (click)=\"onRouterLinkClick()\">\n <ng-container [ngTemplateOutlet]=\"iconTpl\" />\n </a>\n } @else {\n <button\n type=\"button\"\n class=\"rail-item\"\n [class.expanded]=\"expanded()\"\n [class.position-end]=\"position() === 'end'\"\n [class.active]=\"active()\"\n (click)=\"onItemClick()\">\n <ng-container [ngTemplateOutlet]=\"iconTpl\" />\n </button>\n }\n `,\n styles: [`\n :host {\n display: block;\n }\n\n .rail-item {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n justify-content: flex-start;\n text-decoration: none;\n color: var(--rail-nav-on-surface-variant, var(--mat-sys-on-surface-variant));\n cursor: pointer;\n padding: 0;\n gap: 4px;\n background: none;\n border: none;\n font: inherit;\n min-height: 56px;\n box-sizing: border-box;\n outline: none;\n width: 100%;\n /* Transition gap so it animates in sync with the pill (height, margin) */\n /* and the label (max-height) — otherwise gap snaps instantly to 0 and */\n /* siblings jolt during the expand/collapse animation. */\n transition: gap 0.2s ease;\n }\n\n .rail-item.position-end {\n align-items: flex-end;\n }\n\n .rail-item:focus-visible .rail-item-pill {\n border-color: var(--rail-nav-primary, var(--mat-sys-primary));\n }\n\n .rail-item.position-end .rail-item-pill {\n justify-content: flex-end;\n padding-left: 0;\n padding-right: 10px;\n }\n\n .rail-item.expanded {\n gap: 0;\n }\n\n .rail-item-pill {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: flex-start;\n width: 48px;\n height: 32px;\n border-radius: 9999px;\n border: 2px solid transparent;\n overflow: visible;\n margin-top: 12px;\n padding-left: 10px;\n box-sizing: border-box;\n transition: background 0.2s ease, width 0.2s ease, height 0.2s ease, margin 0.2s ease;\n }\n\n .rail-item-ripple {\n position: absolute;\n inset: 0;\n border-radius: inherit;\n overflow: hidden;\n }\n\n .rail-item:hover .rail-item-pill {\n background: var(--rail-nav-surface-container-high, var(--mat-sys-surface-container-high));\n }\n\n .rail-item.active .rail-item-pill {\n background: var(--rail-nav-secondary-container, var(--mat-sys-secondary-container));\n color: var(--rail-nav-on-secondary-container, var(--mat-sys-on-secondary-container));\n }\n\n /* Expanded pill includes both icon and label */\n .rail-item.expanded .rail-item-pill {\n width: auto;\n height: 48px;\n padding: 0 16px 0 10px;\n gap: 12px;\n margin-top: 0;\n }\n\n .rail-item.expanded.position-end .rail-item-pill {\n flex-direction: row-reverse;\n justify-content: flex-start;\n padding: 0 10px 0 16px;\n }\n\n /* First item: reduce space after header and keep icon stable during expand */\n /* Collapsed: 8px + 16px (half of 32px) = 24px from top */\n /* Expanded: 0px + 24px (half of 48px) = 24px from top */\n :host:first-child .rail-item-pill {\n margin-top: 8px;\n }\n\n :host:first-child .rail-item.expanded .rail-item-pill {\n margin-top: 0;\n }\n\n /* Last item (typically anchored at the bottom via <rail-nav-spacer/>): */\n /* in expanded mode the pill loses both its margin-top AND the label-below */\n /* below it, so the icon center jumps up 12px relative to the rail bottom. */\n /* Mirror of the first-child fix: add a matching margin-bottom on the pill */\n /* in expanded mode to keep the icon visually anchored. */\n :host:last-child .rail-item.expanded .rail-item-pill {\n margin-bottom: 12px;\n }\n\n .rail-item-icon-wrapper {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n overflow: visible;\n }\n\n .rail-item-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n }\n\n .rail-item-icon ::ng-deep > * {\n font-size: 24px;\n width: 24px;\n height: 24px;\n }\n\n .rail-badge {\n position: absolute;\n top: -6px;\n right: -6px;\n min-width: 16px;\n height: 16px;\n padding: 0 4px;\n border-radius: 8px;\n background: var(--rail-nav-error, var(--mat-sys-error));\n color: var(--rail-nav-on-error, var(--mat-sys-on-error));\n font-size: 11px;\n font-weight: 500;\n line-height: 16px;\n text-align: center;\n box-sizing: border-box;\n z-index: 10;\n }\n\n /* Small dot badge (no text) */\n .rail-badge.dot {\n top: -2px;\n right: -2px;\n min-width: 6px;\n width: 6px;\n height: 6px;\n padding: 0;\n border-radius: 3px;\n }\n\n /* Label below icon (collapsed mode) */\n .rail-item-label.label-below {\n font-size: 12px;\n font-weight: 500;\n line-height: 16px;\n text-align: center;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n width: 48px;\n max-height: 16px;\n opacity: 1;\n transition: opacity 0.1s ease, max-height 0.2s ease;\n }\n\n .rail-item.expanded .rail-item-label.label-below {\n opacity: 0;\n max-height: 0;\n pointer-events: none;\n }\n\n /* Label inline with icon (expanded mode) */\n .rail-item-label.label-inline {\n font-size: 14px;\n font-weight: 500;\n line-height: 24px;\n text-align: left;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n width: 0;\n opacity: 0;\n pointer-events: none;\n }\n\n .rail-item.expanded .rail-item-label.label-inline {\n width: auto;\n flex: 1;\n opacity: 1;\n pointer-events: auto;\n transition: opacity 0.15s ease 0.1s;\n }\n\n .rail-item.expanded.position-end .rail-item-label.label-inline {\n text-align: right;\n }\n `],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[class.expanded]': 'expanded()',\n '[class.position-end]': 'position() === \"end\"',\n // pointer* events let us filter by pointerType: touch devices emit\n // synthetic mouseenter/leave around taps, which would arm the close timer\n // right after a click-to-open and shut the drawer instantly. Hover-intent\n // only applies for real mouse pointers.\n '(pointerenter)': 'onHostPointerEnter($event)',\n '(pointerleave)': 'onHostPointerLeave($event)'\n }\n})\nexport class RailnavItemComponent {\n /** Router link for navigation */\n readonly routerLink = input<string | any[]>();\n\n /** Label text displayed */\n readonly label = input<string>();\n\n /** Badge value (number, text, or true for dot badge) */\n readonly badge = input<string | number | boolean>();\n\n /** Whether this item is active (for non-router usage) */\n readonly active = input(false);\n\n /** Template projected as a contextual side panel when the item is hovered\n * or clicked. Aliased `for` to mirror Material's `mat-datepicker-toggle`\n * (`<rail-nav-item [for]=\"myTpl\">`). Declare the content as a plain\n * `<ng-template #myTpl>...</ng-template>` anywhere in the host template.\n *\n * When set, the item becomes a trigger; the rail's orchestrator handles\n * overlay rendering, animation, hover-intent and close timer. The panel\n * carries the `rail-nav-drawer-panel` class — style it via the theme mixin\n * `rail-nav.panel()` (or your own global rule). Suppressed while the rail\n * is `expanded()`. */\n readonly for = input<TemplateRef<unknown> | null>(null, { alias: 'for' });\n\n /** Hover-intent delay (ms) before opening the drawer. Cancelled if the\n * cursor leaves the item first. Click bypasses the delay. */\n private static readonly ENTER_DELAY_MS = 200;\n\n /** Whether to show a badge */\n protected readonly hasBadge = computed(() => {\n const b = this.badge();\n return b !== undefined && b !== null && b !== false;\n });\n\n /** Whether to show a small dot badge (no text) */\n protected readonly isDotBadge = computed(() => {\n const b = this.badge();\n return b === true || b === '';\n });\n\n /** Click event (for non-router usage) */\n readonly itemClick = output<void>();\n\n /** Reference to parent rail-nav */\n private railnav = inject(RailnavComponent);\n\n /** Owned by the parent rail (one orchestrator per rail). null in tests where\n * the item is rendered outside a real rail. */\n private orchestrator = inject(RailnavDrawerOrchestrator, { optional: true });\n\n /** Pending hover-intent timer, cleared on leave or destroy. */\n private enterTimeout?: ReturnType<typeof setTimeout>;\n\n constructor() {\n inject(DestroyRef).onDestroy(() => this.clearEnterTimeout());\n }\n\n /** Whether the rail is expanded */\n protected expanded = computed(() => this.railnav.expanded());\n\n /** Position of the rail (start or end) */\n protected position = computed(() => this.railnav.railPosition());\n\n /** Handle item click - emit event, optionally collapse rail, then open drawer.\n * Collapse must come BEFORE openDrawer: while the rail is expanded, the\n * orchestrator suppresses opens (mutual exclusion). Collapsing first ensures\n * the click on an expanded rail still opens the drawer. */\n protected onItemClick(): void {\n this.itemClick.emit();\n if (this.railnav.autoCollapse()) {\n this.railnav.collapse();\n }\n this.openDrawerIfAny();\n }\n\n /** Handle router link click - optionally collapse rail */\n protected onRouterLinkClick(): void {\n if (this.railnav.autoCollapse()) {\n this.railnav.collapse();\n }\n }\n\n /** Hover-intent: when the item carries a `for` drawer, open it after a\n * short delay. The delay is cancelled if the cursor leaves before it fires.\n * Ignored on non-mouse pointers (touch/pen) — the click path handles them. */\n protected onHostPointerEnter(event: PointerEvent): void {\n if (event.pointerType !== 'mouse') return;\n if (!this.for() || !this.orchestrator) return;\n if (this.railnav.expanded()) return;\n this.clearEnterTimeout();\n this.enterTimeout = setTimeout(\n () => this.openDrawerIfAny(),\n RailnavItemComponent.ENTER_DELAY_MS,\n );\n }\n\n /** Arm the orchestrator's close timer when leaving the trigger item. If\n * the cursor reaches the overlay before it fires, the overlay's own\n * `pointerenter` cancels it. Ignored on touch/pen for the same reason. */\n protected onHostPointerLeave(event: PointerEvent): void {\n if (event.pointerType !== 'mouse') return;\n this.clearEnterTimeout();\n if (this.for()) this.orchestrator?.armClose();\n }\n\n private openDrawerIfAny(): void {\n const drawer = this.for();\n if (!drawer || !this.orchestrator) return;\n this.clearEnterTimeout();\n this.orchestrator.open(drawer);\n }\n\n private clearEnterTimeout(): void {\n if (this.enterTimeout !== undefined) {\n clearTimeout(this.enterTimeout);\n this.enterTimeout = undefined;\n }\n }\n}\n","import { ChangeDetectionStrategy, Component } from '@angular/core';\n\n/**\n * Visual separator between groups of `<rail-nav-item>`. Renders a thin\n * horizontal rule with sensible margins so groups of items breathe.\n *\n * <rail-nav>\n * <rail-nav-item label=\"Home\">...</rail-nav-item>\n * <rail-nav-item label=\"Search\">...</rail-nav-item>\n * <rail-nav-separator />\n * <rail-nav-item label=\"Trash\">...</rail-nav-item>\n * </rail-nav>\n *\n * Color overridable via `--rail-nav-separator-color`.\n */\n@Component({\n selector: 'rail-nav-separator',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: '',\n styles: [`\n /* Fixed-height host with the 1px line centered via flex. */\n /* The parent rail sets --rail-nav-separator-shift to recenter the */\n /* line visually between items (label-below in collapsed mode makes */\n /* the inter-item gap asymmetric without this). */\n :host {\n display: flex;\n align-items: center;\n height: 20px;\n padding: 0 12px;\n margin-top: var(--rail-nav-separator-shift, 0);\n box-sizing: border-box;\n transition: margin-top 0.2s ease;\n }\n :host::before {\n content: '';\n flex: 1;\n height: 1px;\n background: var(--rail-nav-separator-color, var(--mat-sys-outline-variant));\n }\n `],\n})\nexport class RailnavSeparatorComponent {}\n","import { ChangeDetectionStrategy, Component } from '@angular/core';\n\n/**\n * Flexible spacer between rail items. Placed between two groups of items, it\n * pushes everything after it to the bottom of the rail — the classic pattern\n * for keeping primary nav at the top and secondary entries (Settings,\n * Profile…) anchored at the bottom.\n *\n * <rail-nav>\n * <rail-nav-item label=\"Home\">...</rail-nav-item>\n * <rail-nav-item label=\"Inbox\">...</rail-nav-item>\n * <rail-nav-spacer />\n * <rail-nav-item label=\"Settings\">...</rail-nav-item>\n * </rail-nav>\n *\n * Requires the rail to own its full height (already the case by default).\n */\n@Component({\n selector: 'rail-nav-spacer',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: '',\n styles: [`\n :host {\n display: block;\n flex: 1 1 auto;\n }\n `],\n})\nexport class RailnavSpacerComponent {}\n","/*\n * Public API Surface of @softwarity/rail-nav\n */\n\nexport { RailnavComponent, RailnavBrandingDirective } from './lib/railnav.component';\nexport { RailnavContainerComponent } from './lib/railnav-container.component';\nexport { RailnavContentComponent } from './lib/railnav-content.component';\nexport { RailnavItemComponent } from './lib/railnav-item.component';\nexport { RailnavSeparatorComponent } from './lib/railnav-separator.component';\nexport { RailnavSpacerComponent } from './lib/railnav-spacer.component';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"softwarity-rail-nav.mjs","sources":["../../src/lib/railnav-drawer.orchestrator.ts","../../src/lib/railnav.component.ts","../../src/lib/railnav-container.component.ts","../../src/lib/railnav-content.component.ts","../../src/lib/railnav-item.component.ts","../../src/lib/railnav-separator.component.ts","../../src/lib/railnav-spacer.component.ts","../../src/public-api.ts","../../src/softwarity-rail-nav.ts"],"sourcesContent":["import { Overlay, OverlayRef } from '@angular/cdk/overlay';\nimport { TemplatePortal } from '@angular/cdk/portal';\nimport { DestroyRef, ElementRef, Injectable, TemplateRef, ViewContainerRef, effect, inject } from '@angular/core';\nimport { Subscription } from 'rxjs';\nimport { RailnavComponent } from './railnav.component';\n\n/**\n * One per `RailnavComponent` (provided on its `providers`). Owns the single\n * CDK overlay used to render the currently-open `<rail-nav-drawer>`, and the\n * close timer that lets the cursor travel between the trigger item and the\n * drawer without the drawer closing on transit.\n *\n * - Anchors the overlay to the rail's host element (right edge), so the drawer\n * sits next to the collapsed rail regardless of which trigger item opened it.\n * - Auto-closes when the rail enters `expanded()` mode (mutual exclusion).\n * - Mouse enter/leave on the overlay panel itself cancels/arms the close timer,\n * so the drawer stays open while the cursor is over it.\n */\n@Injectable()\nexport class RailnavDrawerOrchestrator {\n private readonly overlay = inject(Overlay);\n private readonly vcr = inject(ViewContainerRef);\n private readonly rail = inject(RailnavComponent);\n private readonly railEl = inject(ElementRef<HTMLElement>);\n\n /** Window after which we close if nothing cancels — covers cursor transit. */\n private static readonly LEAVE_DELAY_MS = 300;\n /** Post-open grace period during which `armClose()` is a no-op. Touch devices\n * dispatch a burst of synthetic mouse events (with `pointerType === 'mouse'`)\n * right after `click` — this window swallows them. */\n private static readonly SYNTHETIC_GUARD_MS = 500;\n\n private overlayRef?: OverlayRef;\n private currentTemplate?: TemplateRef<unknown>;\n private closeTimer?: ReturnType<typeof setTimeout>;\n private resizeObserver?: ResizeObserver;\n private outsideClickSub?: Subscription;\n /** Timestamp of the last `open()` — used to gate close-on-leave so that\n * synthetic `mouseleave` events dispatched right after a touch tap\n * (with `pointerType === 'mouse'`) don't shut the drawer instantly. */\n private lastOpenAt = 0;\n\n constructor() {\n // Mutual exclusion with the rail's expanded mode: expanding closes any open\n // drawer, and `open()` short-circuits while expanded. The expanded rail is\n // itself the alternative nav surface, no point stacking a drawer on top.\n effect(() => {\n if (this.rail.expanded()) this.closeNow();\n });\n inject(DestroyRef).onDestroy(() => {\n this.clearCloseTimer();\n this.overlayRef?.dispose();\n });\n }\n\n /** Opens or re-targets the overlay to the given template. No-op while the\n * rail is expanded. Cancels any pending close timer (e.g. user hovered back\n * from a quick traverse). When the overlay is already open, re-targets it\n * to the new template WITHOUT disposing — the chrome (bg/shadow/position)\n * stays put while just the projected content fades in. */\n open(template: TemplateRef<unknown>): void {\n if (this.rail.expanded()) return;\n this.clearCloseTimer();\n if (this.currentTemplate === template && this.overlayRef?.hasAttached()) {\n return;\n }\n\n // Re-target: existing overlay stays, just swap the portal content.\n // Note: we do NOT reset `lastOpenAt` here — otherwise hovering a\n // non-trigger item right after a re-target would be blocked by the\n // synthetic-event guard, leaving the drawer stuck open.\n if (this.overlayRef?.hasAttached()) {\n // Cross-fade: fade the OLD content out first (children only, so the\n // panel chrome/bg stays opaque the whole time — no flicker), then swap\n // and fade the new content in. Reads as \"content dissolves, new content\n // appears\" with the drawer staying perfectly still.\n this.fadeOutContent().then(() => {\n if (!this.overlayRef) return;\n this.overlayRef.detach();\n this.currentTemplate = template;\n this.overlayRef.attach(new TemplatePortal(template, this.vcr));\n this.applyHeight();\n this.animateContentIn();\n });\n return;\n }\n\n // First open: arm the synthetic-event guard and build a fresh overlay.\n this.lastOpenAt = performance.now();\n this.detach();\n this.currentTemplate = template;\n\n const positionStrategy = this.overlay\n .position()\n .flexibleConnectedTo(this.railEl)\n .withPositions([\n { originX: 'end', originY: 'top', overlayX: 'start', overlayY: 'top' },\n ]);\n\n this.overlayRef = this.overlay.create({\n positionStrategy,\n hasBackdrop: false,\n panelClass: 'rail-nav-drawer-panel',\n // Reposition on every scroll so the drawer stays glued to the rail\n // when the page scrolls (default `noop` strategy leaves the overlay\n // fixed to the viewport, which visually decouples it from the rail).\n scrollStrategy: this.overlay.scrollStrategies.reposition(),\n });\n\n // Tap / click outside the overlay closes it — except when the click\n // lands in the rail itself (re-clicking a `[for]` item should re-target\n // or no-op, not close). Crucial on touch where hover-leave doesn't fire.\n this.outsideClickSub = this.overlayRef.outsidePointerEvents().subscribe((event) => {\n const target = event.target as Node | null;\n if (target && this.railEl.nativeElement.contains(target)) return;\n this.closeNow();\n });\n\n // Keep the drawer open while the cursor is over it; close shortly after\n // it leaves (matching the trigger's own leave debounce). pointer* events\n // let us ignore the synthetic mouseleave fired around touch taps.\n this.overlayRef.overlayElement.addEventListener('pointerenter', this.onOverlayEnter);\n this.overlayRef.overlayElement.addEventListener('pointerleave', this.onOverlayLeave);\n\n // Inline-style the panel: the projected template content lives outside any\n // component tree, so component-encapsulated styles can't reach it without\n // ::ng-deep / Encapsulation.None. Setting styles directly on overlayElement\n // sidesteps the issue entirely. Each value reads a CSS custom property so\n // consumers can override via `--rail-nav-drawer-*` on the rail.\n //\n // The lib applies both the chrome (background / shadow / radius) AND the\n // default nav-list layout (flex column / padding / gap / min-width) so the\n // consumer's template can be just a flat list of children — they stack\n // vertically with proper spacing out of the box.\n const el = this.overlayRef.overlayElement;\n el.style.background = 'var(--rail-nav-drawer-surface, var(--mat-sys-surface))';\n el.style.color = 'var(--rail-nav-drawer-on-surface, var(--mat-sys-on-surface))';\n el.style.boxShadow = 'var(--rail-nav-drawer-shadow, var(--mat-sys-level2))';\n el.style.borderRadius = 'var(--rail-nav-drawer-radius, 0 12px 12px 0)';\n el.style.overflow = 'auto';\n el.style.boxSizing = 'border-box';\n el.style.display = 'flex';\n el.style.flexDirection = 'column';\n el.style.padding = 'var(--rail-nav-drawer-padding, 12px)';\n el.style.gap = 'var(--rail-nav-drawer-gap, 4px)';\n // Fixed width so the drawer doesn't shrink-fit its content (which would\n // make each trigger's drawer a different size) and absorbs the layout\n // variance between MatButton variants (e.g. text vs tonal padding).\n el.style.width = 'var(--rail-nav-drawer-width, 240px)';\n\n this.overlayRef.attach(new TemplatePortal(template, this.vcr));\n this.animateIn();\n\n // Match the rail's full height so the drawer sits flush against it\n // regardless of viewport / layout. ResizeObserver keeps it in sync.\n this.applyHeight();\n this.resizeObserver?.disconnect();\n this.resizeObserver = new ResizeObserver(() => this.applyHeight());\n this.resizeObserver.observe(this.railEl.nativeElement);\n }\n\n private applyHeight(): void {\n if (!this.overlayRef) return;\n const rect = this.railEl.nativeElement.getBoundingClientRect();\n this.overlayRef.overlayElement.style.height = `${rect.height}px`;\n }\n\n /** Slide-in fade on the whole overlay panel — used for the FIRST open, when\n * the drawer genuinely appears from the rail. Web Animations API so no\n * global CSS keyframes. Material standard easing for an organic feel. */\n private animateIn(): void {\n if (!this.overlayRef) return;\n this.overlayRef.overlayElement.animate(\n [\n { opacity: 0, transform: 'translateX(-8px)' },\n { opacity: 1, transform: 'translateX(0)' },\n ],\n { duration: 280, easing: 'cubic-bezier(0.4, 0, 0.2, 1)' },\n );\n }\n\n /** Fade-in of just the content (panel children) — second half of the\n * re-target cross-fade. Panel chrome stays fixed. */\n private animateContentIn(): void {\n if (!this.overlayRef) return;\n for (const child of Array.from(this.overlayRef.overlayElement.children)) {\n (child as HTMLElement).animate(\n [{ opacity: 0 }, { opacity: 1 }],\n { duration: 160, easing: 'ease-out' },\n );\n }\n }\n\n /** Fade-out of just the content (panel children) — first half of the\n * re-target cross-fade. Resolves once faded so the caller can swap the\n * template while it's invisible. Panel chrome (bg/shadow) stays opaque, so\n * there's no flicker. Resolves immediately if there's nothing to fade. */\n private fadeOutContent(): Promise<void> {\n if (!this.overlayRef) return Promise.resolve();\n const children = Array.from(this.overlayRef.overlayElement.children) as HTMLElement[];\n if (children.length === 0) return Promise.resolve();\n const anims = children.map((c) =>\n c.animate([{ opacity: 1 }, { opacity: 0 }], {\n duration: 120,\n easing: 'ease-in',\n fill: 'forwards',\n }),\n );\n return Promise.all(anims.map((a) => a.finished)).then(() => undefined);\n }\n\n /** Arms the close timer. Cancelled if the cursor returns to the trigger or\n * enters the overlay before it fires. Suppressed during the post-open guard\n * window so synthetic mouseleave events fired right after a touch tap\n * don't shut the drawer instantly. */\n armClose(): void {\n if (performance.now() - this.lastOpenAt < RailnavDrawerOrchestrator.SYNTHETIC_GUARD_MS) {\n return;\n }\n this.clearCloseTimer();\n this.closeTimer = setTimeout(\n () => this.closeNow(),\n RailnavDrawerOrchestrator.LEAVE_DELAY_MS,\n );\n }\n\n /** Force-close immediately, e.g. on a click inside the drawer that navigates. */\n closeNow(): void {\n this.clearCloseTimer();\n this.detach();\n }\n\n private detach(): void {\n this.resizeObserver?.disconnect();\n this.resizeObserver = undefined;\n this.outsideClickSub?.unsubscribe();\n this.outsideClickSub = undefined;\n if (!this.overlayRef) return;\n this.overlayRef.overlayElement.removeEventListener('pointerenter', this.onOverlayEnter);\n this.overlayRef.overlayElement.removeEventListener('pointerleave', this.onOverlayLeave);\n // dispose (not just detach) so the next `open()` builds a fresh overlay\n // with its own position strategy + listeners — no stale state to leak.\n this.overlayRef.dispose();\n this.overlayRef = undefined;\n this.currentTemplate = undefined;\n }\n\n private clearCloseTimer(): void {\n if (this.closeTimer !== undefined) {\n clearTimeout(this.closeTimer);\n this.closeTimer = undefined;\n }\n }\n\n // Arrow-functions so the listener references stay stable across add/remove.\n // Touch / pen pointers are ignored — those flows are click-driven.\n private readonly onOverlayEnter = (event: PointerEvent): void => {\n if (event.pointerType !== 'mouse') return;\n this.clearCloseTimer();\n };\n private readonly onOverlayLeave = (event: PointerEvent): void => {\n if (event.pointerType !== 'mouse') return;\n this.armClose();\n };\n}\n","import { Component, signal, effect, input, contentChild, ElementRef, Directive, computed } from '@angular/core';\nimport { MatSidenav } from '@angular/material/sidenav';\nimport { MatRippleModule } from '@angular/material/core';\nimport { NgTemplateOutlet } from '@angular/common';\nimport { RailnavDrawerOrchestrator } from './railnav-drawer.orchestrator';\n\n/** Directive to mark custom branding content */\n@Directive({\n selector: '[railNavBranding]'\n})\nexport class RailnavBrandingDirective {}\n\n@Component({\n selector: 'rail-nav',\n imports: [MatRippleModule, NgTemplateOutlet],\n template: `\n <!-- Default branding template (title/subtitle) -->\n <ng-template #defaultBrandingTpl>\n <div class=\"rail-branding\" [class.position-end]=\"railPosition() === 'end'\">\n @if (title()) {\n <span class=\"rail-title\">{{ title() }}</span>\n }\n @if (subtitle()) {\n <span class=\"rail-subtitle\">{{ subtitle() }}</span>\n }\n </div>\n </ng-template>\n <!-- Custom branding template (projected content) -->\n <ng-template #customBrandingTpl>\n <div class=\"rail-branding rail-branding-custom\" [class.position-end]=\"railPosition() === 'end'\">\n <ng-content select=\"[railNavBranding]\" />\n </div>\n </ng-template>\n @if (!hideDefaultHeader()) {\n <div class=\"rail-header\" [class.position-end]=\"railPosition() === 'end'\" matRipple (click)=\"toggleExpanded()\">\n @if (expanded() && hasBranding() && railPosition() === 'end') {\n <ng-container [ngTemplateOutlet]=\"customBranding() ? customBrandingTpl : defaultBrandingTpl\" />\n }\n <div class=\"rail-burger\" [class.expanded]=\"expanded()\" [class.position-end]=\"railPosition() === 'end'\">\n <svg class=\"icon-menu\" xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\" width=\"24px\" fill=\"currentColor\">\n <path d=\"M120-240v-80h720v80H120Zm0-200v-80h720v80H120Zm0-200v-80h720v80H120Z\"/>\n </svg>\n <svg class=\"icon-menu-open\" xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\" width=\"24px\" fill=\"currentColor\">\n <path d=\"M120-240v-80h520v80H120Zm664-40L584-480l200-200 56 56-144 144 144 144-56 56ZM120-440v-80h400v80H120Zm0-200v-80h520v80H120Z\"/>\n </svg>\n </div>\n @if (expanded() && hasBranding() && railPosition() === 'start') {\n <ng-container [ngTemplateOutlet]=\"customBranding() ? customBrandingTpl : defaultBrandingTpl\" />\n }\n </div>\n }\n <nav class=\"rail-items\">\n <ng-content />\n </nav>\n `,\n styles: [`\n /* Selectors are scoped to \\`:host(.mat-drawer)\\` (not bare \\`:host\\`) on\n purpose. The host carries Material's \\`mat-drawer mat-sidenav\\` classes, and\n Material's base \\`.mat-drawer { width: 360px; background: ...surface }\\`\n rule has the SAME specificity as a bare \\`:host\\` — so the rail would only\n win by stylesheet insertion order. The moment a consumer renders another\n MatDrawer/MatSidenav (e.g. a nested mat-drawer-container in routed\n content), Angular appends Material's sidenav styles AFTER the rail's, the\n tie flips, and the rail adopts Material's defaults (360px wide, surface\n background) — overlapping the content. \\`:host(.mat-drawer)\\` is one class\n more specific, so the rail wins deterministically regardless of order. */\n :host(.mat-drawer) {\n /* flex column so .rail-items can stretch (flex: 1) and */\n /* rail-nav-spacer inside it can push siblings to the bottom. */\n display: flex;\n flex-direction: column;\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n z-index: 100;\n width: var(--rail-nav-collapsed-width, 72px);\n border: none !important;\n outline: none !important;\n box-shadow: none;\n background: var(--rail-nav-surface-color, var(--mat-sys-surface));\n transition: width 0.2s ease;\n overflow: visible;\n\n /* Collapsed default: rail items show their label-below (~20px) but */\n /* the next pill keeps its margin-top, so the inter-item gap is */\n /* asymmetric. Shift the separator down to recenter it visually. */\n --rail-nav-separator-shift: 12px;\n }\n\n :host(.mat-drawer.expanded) {\n width: var(--rail-nav-expanded-width, fit-content);\n box-shadow: 4px 0 8px rgba(0,0,0,.2);\n /* Expanded mode has no label-below, items are flush. No shift needed. */\n --rail-nav-separator-shift: 0;\n }\n\n :host(.mat-drawer.position-end) {\n left: auto;\n right: 0;\n }\n\n :host(.mat-drawer.position-end.expanded) {\n box-shadow: -4px 0 8px rgba(0,0,0,.2);\n }\n\n .rail-header {\n display: flex;\n align-items: center;\n gap: 12px;\n height: var(--rail-nav-header-height, 64px);\n padding: 16px 16px 16px 24px;\n box-sizing: border-box;\n cursor: pointer;\n color: var(--rail-nav-on-surface, var(--mat-sys-on-surface));\n }\n\n .rail-header.position-end {\n padding: 16px 24px 16px 16px;\n }\n\n .rail-header.position-end .rail-burger {\n margin-left: auto;\n }\n\n .rail-header:hover {\n background: var(--rail-nav-surface-container-high, var(--mat-sys-surface-container-high));\n }\n\n .rail-items {\n display: flex;\n flex-direction: column;\n flex: 1 1 auto;\n min-height: 0;\n /* Symmetric vertical padding so the last item (typically Settings */\n /* anchored via <rail-nav-spacer/>) breathes against the rail edge. */\n padding: 12px;\n gap: 0;\n box-sizing: border-box;\n width: 100%;\n }\n\n .rail-burger {\n position: relative;\n width: 24px;\n height: 24px;\n flex-shrink: 0;\n }\n\n .rail-burger.position-end {\n transform: scaleX(-1);\n }\n\n .rail-burger svg {\n position: absolute;\n top: 0;\n left: 0;\n transition: opacity 0.3s ease, transform 0.3s ease;\n }\n\n /* Position start (left) - clockwise rotation */\n .rail-burger .icon-menu {\n opacity: 1;\n transform: rotate(0deg);\n }\n\n .rail-burger .icon-menu-open {\n opacity: 0;\n transform: rotate(-90deg);\n }\n\n .rail-burger.expanded .icon-menu {\n opacity: 0;\n transform: rotate(90deg);\n }\n\n .rail-burger.expanded .icon-menu-open {\n opacity: 1;\n transform: rotate(0deg);\n }\n\n /* Position end (right) - counter-clockwise rotation (mirrored) */\n .rail-burger.position-end .icon-menu {\n transform: rotate(0deg);\n }\n\n .rail-burger.position-end .icon-menu-open {\n transform: rotate(90deg);\n }\n\n .rail-burger.position-end.expanded .icon-menu {\n transform: rotate(-90deg);\n }\n\n .rail-burger.position-end.expanded .icon-menu-open {\n transform: rotate(0deg);\n }\n\n .rail-branding {\n display: flex;\n flex-direction: column;\n justify-content: center;\n min-width: 0;\n overflow: hidden;\n text-align: right;\n padding-top: 5px;\n }\n\n .rail-branding.position-end {\n text-align: left;\n }\n\n .rail-title {\n font-size: 16px;\n font-weight: 500;\n line-height: 1;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n color: var(--rail-nav-on-surface, var(--mat-sys-on-surface));\n }\n\n .rail-subtitle {\n font-size: 11px;\n line-height: 1;\n color: var(--rail-nav-on-surface-variant, var(--mat-sys-on-surface-variant));\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n opacity: 0.7;\n }\n `],\n host: {\n 'class': 'mat-drawer mat-sidenav',\n '[class.expanded]': 'expanded()',\n '[class.position-end]': 'railPosition() === \"end\"'\n },\n providers: [RailnavDrawerOrchestrator]\n})\nexport class RailnavComponent extends MatSidenav {\n /** Position: 'start' (left) or 'end' (right) - aliased to avoid conflict with MatSidenav.position */\n readonly railPosition = input<'start' | 'end'>('start', { alias: 'position' });\n\n /** Hide the default header (burger + title/subtitle) */\n readonly hideDefaultHeader = input(false);\n\n /** Title displayed when expanded (ignored if railNavBranding is projected) */\n readonly title = input<string>();\n\n /** Subtitle displayed when expanded (ignored if railNavBranding is projected) */\n readonly subtitle = input<string>();\n\n /** Whether to auto-collapse when an item is clicked */\n readonly autoCollapse = input(true);\n\n /** Whether the rail is expanded to show labels */\n readonly expanded = signal(false);\n\n /** Detect custom branding content projection */\n protected readonly customBranding = contentChild(RailnavBrandingDirective);\n\n /** Whether there's any branding to show (custom or default) */\n protected readonly hasBranding = computed(() =>\n !!this.customBranding() || !!this.title() || !!this.subtitle()\n );\n\n constructor() {\n super();\n // Default settings for rail behavior\n this.opened = true;\n this.disableClose = true;\n this.mode = 'side';\n\n // Sync mode with expanded state\n effect(() => {\n this.mode = this.expanded() ? 'over' : 'side';\n });\n }\n\n /** Toggle between collapsed (rail) and expanded (drawer) */\n toggleExpanded(): void {\n this.expanded.update(e => !e);\n }\n\n /** Collapse the rail */\n collapse(): void {\n this.expanded.set(false);\n }\n\n /** Expand the rail to drawer */\n expand(): void {\n this.expanded.set(true);\n }\n}\n","import { Component, ContentChild, input } from '@angular/core';\nimport { MatSidenavContainer, MatDrawerContainer } from '@angular/material/sidenav';\nimport { RailnavComponent } from './railnav.component';\n\n@Component({\n selector: 'rail-nav-container',\n template: `\n <ng-content />\n @if (showBackdrop()) {\n <div\n class=\"railnav-backdrop\"\n [class.visible]=\"railnav?.expanded()\"\n [class.position-end]=\"railnav?.railPosition() === 'end'\"\n (click)=\"railnav?.collapse()\">\n </div>\n }\n `,\n styles: [`\n :host {\n display: block;\n position: relative;\n height: 100%;\n overflow: hidden;\n }\n\n .railnav-backdrop {\n position: absolute;\n top: 0;\n bottom: 0;\n left: var(--rail-nav-collapsed-width, 72px);\n right: 0;\n background: var(--rail-nav-backdrop-color, rgba(0, 0, 0, 0.4));\n z-index: 99;\n cursor: pointer;\n opacity: 0;\n pointer-events: none;\n transition: opacity 0.3s ease-in-out;\n }\n\n .railnav-backdrop.position-end {\n left: 0;\n right: var(--rail-nav-collapsed-width, 72px);\n }\n\n .railnav-backdrop.visible {\n opacity: 1;\n pointer-events: auto;\n }\n `],\n host: {\n 'class': 'mat-drawer-container mat-sidenav-container'\n },\n providers: [\n { provide: MatDrawerContainer, useExisting: RailnavContainerComponent }\n ]\n})\nexport class RailnavContainerComponent extends MatSidenavContainer {\n /** Whether to show backdrop when expanded */\n readonly showBackdrop = input(true);\n\n @ContentChild(RailnavComponent) railnav?: RailnavComponent;\n}\n","import { Component, input, inject, computed } from '@angular/core';\nimport { MatSidenavContent } from '@angular/material/sidenav';\nimport { RailnavContainerComponent } from './railnav-container.component';\n\n@Component({\n selector: 'rail-nav-content',\n template: `<ng-content />`,\n styles: [`\n :host {\n display: block;\n height: 100%;\n overflow: auto;\n margin-left: var(--rail-nav-collapsed-width, 72px);\n }\n\n :host(.position-end) {\n margin-left: 0;\n margin-right: var(--rail-nav-collapsed-width, 72px);\n }\n `],\n host: {\n 'class': 'mat-drawer-content mat-sidenav-content',\n '[class.position-end]': 'effectivePosition() === \"end\"'\n }\n})\nexport class RailnavContentComponent extends MatSidenavContent {\n /** Optional: Position of the rail. If not set, uses the sibling rail-nav's position */\n readonly position = input<'start' | 'end' | undefined>(undefined);\n\n /** Parent container that gives access to sibling rail-nav */\n private container = inject(RailnavContainerComponent, { optional: true });\n\n /** Effective position - from input or from sibling rail-nav */\n protected readonly effectivePosition = computed(() => {\n const inputValue = this.position();\n if (inputValue !== undefined) return inputValue;\n return this.container?.railnav?.railPosition() ?? 'start';\n });\n}\n","import { Component, DestroyRef, TemplateRef, input, output, computed, inject, ChangeDetectionStrategy } from '@angular/core';\nimport { RouterLink, RouterLinkActive } from '@angular/router';\nimport { MatRippleModule } from '@angular/material/core';\nimport { RailnavComponent } from './railnav.component';\nimport { RailnavDrawerOrchestrator } from './railnav-drawer.orchestrator';\nimport { NgTemplateOutlet } from '@angular/common';\n\n@Component({\n selector: 'rail-nav-item',\n imports: [RouterLink, RouterLinkActive, MatRippleModule, NgTemplateOutlet],\n template: `\n <ng-template #iconTpl>\n <div class=\"rail-item-pill\">\n <div class=\"rail-item-ripple\" matRipple></div>\n <div class=\"rail-item-icon-wrapper\">\n <div class=\"rail-item-icon\">\n <ng-content />\n </div>\n @if (hasBadge()) {\n <span class=\"rail-badge\" [class.dot]=\"isDotBadge()\">{{ isDotBadge() ? '' : badge() }}</span>\n }\n </div>\n <span class=\"rail-item-label label-inline\">{{ label() }}</span>\n </div>\n <span class=\"rail-item-label label-below\">{{ label() }}</span>\n </ng-template>\n @if (routerLink()) {\n <a\n class=\"rail-item\"\n [class.expanded]=\"expanded()\"\n [class.position-end]=\"position() === 'end'\"\n [class.active]=\"active()\"\n [routerLink]=\"routerLink()\"\n routerLinkActive=\"active\"\n (click)=\"onRouterLinkClick()\">\n <ng-container [ngTemplateOutlet]=\"iconTpl\" />\n </a>\n } @else {\n <button\n type=\"button\"\n class=\"rail-item\"\n [class.expanded]=\"expanded()\"\n [class.position-end]=\"position() === 'end'\"\n [class.active]=\"active()\"\n (click)=\"onItemClick()\">\n <ng-container [ngTemplateOutlet]=\"iconTpl\" />\n </button>\n }\n `,\n styles: [`\n :host {\n display: block;\n }\n\n .rail-item {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n justify-content: flex-start;\n text-decoration: none;\n color: var(--rail-nav-on-surface-variant, var(--mat-sys-on-surface-variant));\n cursor: pointer;\n padding: 0;\n gap: 4px;\n background: none;\n border: none;\n font: inherit;\n min-height: 56px;\n box-sizing: border-box;\n outline: none;\n width: 100%;\n /* Transition gap so it animates in sync with the pill (height, margin) */\n /* and the label (max-height) — otherwise gap snaps instantly to 0 and */\n /* siblings jolt during the expand/collapse animation. */\n transition: gap 0.2s ease;\n }\n\n .rail-item.position-end {\n align-items: flex-end;\n }\n\n .rail-item:focus-visible .rail-item-pill {\n border-color: var(--rail-nav-primary, var(--mat-sys-primary));\n }\n\n .rail-item.position-end .rail-item-pill {\n justify-content: flex-end;\n padding-left: 0;\n padding-right: 10px;\n }\n\n .rail-item.expanded {\n gap: 0;\n }\n\n .rail-item-pill {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: flex-start;\n width: 48px;\n height: 32px;\n border-radius: 9999px;\n border: 2px solid transparent;\n overflow: visible;\n margin-top: 12px;\n padding-left: 10px;\n box-sizing: border-box;\n transition: background 0.2s ease, width 0.2s ease, height 0.2s ease, margin 0.2s ease;\n }\n\n .rail-item-ripple {\n position: absolute;\n inset: 0;\n border-radius: inherit;\n overflow: hidden;\n }\n\n .rail-item:hover .rail-item-pill {\n background: var(--rail-nav-surface-container-high, var(--mat-sys-surface-container-high));\n }\n\n .rail-item.active .rail-item-pill {\n background: var(--rail-nav-secondary-container, var(--mat-sys-secondary-container));\n color: var(--rail-nav-on-secondary-container, var(--mat-sys-on-secondary-container));\n }\n\n /* Expanded pill includes both icon and label */\n .rail-item.expanded .rail-item-pill {\n width: auto;\n height: 48px;\n padding: 0 16px 0 10px;\n gap: 12px;\n margin-top: 0;\n }\n\n .rail-item.expanded.position-end .rail-item-pill {\n flex-direction: row-reverse;\n justify-content: flex-start;\n padding: 0 10px 0 16px;\n }\n\n /* First item: reduce space after header and keep icon stable during expand */\n /* Collapsed: 8px + 16px (half of 32px) = 24px from top */\n /* Expanded: 0px + 24px (half of 48px) = 24px from top */\n :host:first-child .rail-item-pill {\n margin-top: 8px;\n }\n\n :host:first-child .rail-item.expanded .rail-item-pill {\n margin-top: 0;\n }\n\n /* Last item (typically anchored at the bottom via <rail-nav-spacer/>): */\n /* in expanded mode the pill loses both its margin-top AND the label-below */\n /* below it, so the icon center jumps up 12px relative to the rail bottom. */\n /* Mirror of the first-child fix: add a matching margin-bottom on the pill */\n /* in expanded mode to keep the icon visually anchored. */\n :host:last-child .rail-item.expanded .rail-item-pill {\n margin-bottom: 12px;\n }\n\n .rail-item-icon-wrapper {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n overflow: visible;\n }\n\n .rail-item-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n }\n\n .rail-item-icon ::ng-deep > * {\n font-size: 24px;\n width: 24px;\n height: 24px;\n }\n\n .rail-badge {\n position: absolute;\n top: -6px;\n right: -6px;\n min-width: 16px;\n height: 16px;\n padding: 0 4px;\n border-radius: 8px;\n background: var(--rail-nav-error, var(--mat-sys-error));\n color: var(--rail-nav-on-error, var(--mat-sys-on-error));\n font-size: 11px;\n font-weight: 500;\n line-height: 16px;\n text-align: center;\n box-sizing: border-box;\n z-index: 10;\n }\n\n /* Small dot badge (no text) */\n .rail-badge.dot {\n top: -2px;\n right: -2px;\n min-width: 6px;\n width: 6px;\n height: 6px;\n padding: 0;\n border-radius: 3px;\n }\n\n /* Label below icon (collapsed mode) */\n .rail-item-label.label-below {\n font-size: 12px;\n font-weight: 500;\n line-height: 16px;\n text-align: center;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n width: 48px;\n max-height: 16px;\n opacity: 1;\n transition: opacity 0.1s ease, max-height 0.2s ease;\n }\n\n .rail-item.expanded .rail-item-label.label-below {\n opacity: 0;\n max-height: 0;\n pointer-events: none;\n }\n\n /* Label inline with icon (expanded mode) */\n .rail-item-label.label-inline {\n font-size: 14px;\n font-weight: 500;\n line-height: 24px;\n text-align: left;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n width: 0;\n opacity: 0;\n pointer-events: none;\n }\n\n .rail-item.expanded .rail-item-label.label-inline {\n width: auto;\n flex: 1;\n opacity: 1;\n pointer-events: auto;\n transition: opacity 0.15s ease 0.1s;\n }\n\n .rail-item.expanded.position-end .rail-item-label.label-inline {\n text-align: right;\n }\n `],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[class.expanded]': 'expanded()',\n '[class.position-end]': 'position() === \"end\"',\n // pointer* events let us filter by pointerType: touch devices emit\n // synthetic mouseenter/leave around taps, which would arm the close timer\n // right after a click-to-open and shut the drawer instantly. Hover-intent\n // only applies for real mouse pointers.\n '(pointerenter)': 'onHostPointerEnter($event)',\n '(pointerleave)': 'onHostPointerLeave($event)'\n }\n})\nexport class RailnavItemComponent {\n /** Router link for navigation */\n readonly routerLink = input<string | any[]>();\n\n /** Label text displayed */\n readonly label = input<string>();\n\n /** Badge value (number, text, or true for dot badge) */\n readonly badge = input<string | number | boolean>();\n\n /** Whether this item is active (for non-router usage) */\n readonly active = input(false);\n\n /** Template projected as a contextual side panel when the item is hovered\n * or clicked. Aliased `for` to mirror Material's `mat-datepicker-toggle`\n * (`<rail-nav-item [for]=\"myTpl\">`). Declare the content as a plain\n * `<ng-template #myTpl>...</ng-template>` anywhere in the host template.\n *\n * When set, the item becomes a trigger; the rail's orchestrator handles\n * overlay rendering, animation, hover-intent and close timer. The panel\n * carries the `rail-nav-drawer-panel` class — style it via the theme mixin\n * `rail-nav.panel()` (or your own global rule). Suppressed while the rail\n * is `expanded()`. */\n readonly for = input<TemplateRef<unknown> | null>(null, { alias: 'for' });\n\n /** Hover-intent delay (ms) before opening the drawer. Cancelled if the\n * cursor leaves the item first. Click bypasses the delay. */\n private static readonly ENTER_DELAY_MS = 200;\n\n /** Whether to show a badge */\n protected readonly hasBadge = computed(() => {\n const b = this.badge();\n return b !== undefined && b !== null && b !== false;\n });\n\n /** Whether to show a small dot badge (no text) */\n protected readonly isDotBadge = computed(() => {\n const b = this.badge();\n return b === true || b === '';\n });\n\n /** Click event (for non-router usage) */\n readonly itemClick = output<void>();\n\n /** Reference to parent rail-nav */\n private railnav = inject(RailnavComponent);\n\n /** Owned by the parent rail (one orchestrator per rail). null in tests where\n * the item is rendered outside a real rail. */\n private orchestrator = inject(RailnavDrawerOrchestrator, { optional: true });\n\n /** Pending hover-intent timer, cleared on leave or destroy. */\n private enterTimeout?: ReturnType<typeof setTimeout>;\n\n constructor() {\n inject(DestroyRef).onDestroy(() => this.clearEnterTimeout());\n }\n\n /** Whether the rail is expanded */\n protected expanded = computed(() => this.railnav.expanded());\n\n /** Position of the rail (start or end) */\n protected position = computed(() => this.railnav.railPosition());\n\n /** Handle item click - emit event, optionally collapse rail, then open drawer.\n * Collapse must come BEFORE openDrawer: while the rail is expanded, the\n * orchestrator suppresses opens (mutual exclusion). Collapsing first ensures\n * the click on an expanded rail still opens the drawer. */\n protected onItemClick(): void {\n this.itemClick.emit();\n if (this.railnav.autoCollapse()) {\n this.railnav.collapse();\n }\n this.openDrawerIfAny();\n }\n\n /** Handle router link click - optionally collapse rail */\n protected onRouterLinkClick(): void {\n if (this.railnav.autoCollapse()) {\n this.railnav.collapse();\n }\n }\n\n /** Hover-intent: when the item carries a `for` drawer, open it after a\n * short delay. The delay is cancelled if the cursor leaves before it fires.\n * Ignored on non-mouse pointers (touch/pen) — the click path handles them. */\n protected onHostPointerEnter(event: PointerEvent): void {\n if (event.pointerType !== 'mouse') return;\n if (!this.for() || !this.orchestrator) return;\n if (this.railnav.expanded()) return;\n this.clearEnterTimeout();\n this.enterTimeout = setTimeout(\n () => this.openDrawerIfAny(),\n RailnavItemComponent.ENTER_DELAY_MS,\n );\n }\n\n /** Arm the orchestrator's close timer when leaving the trigger item. If\n * the cursor reaches the overlay before it fires, the overlay's own\n * `pointerenter` cancels it. Ignored on touch/pen for the same reason. */\n protected onHostPointerLeave(event: PointerEvent): void {\n if (event.pointerType !== 'mouse') return;\n this.clearEnterTimeout();\n if (this.for()) this.orchestrator?.armClose();\n }\n\n private openDrawerIfAny(): void {\n const drawer = this.for();\n if (!drawer || !this.orchestrator) return;\n this.clearEnterTimeout();\n this.orchestrator.open(drawer);\n }\n\n private clearEnterTimeout(): void {\n if (this.enterTimeout !== undefined) {\n clearTimeout(this.enterTimeout);\n this.enterTimeout = undefined;\n }\n }\n}\n","import { ChangeDetectionStrategy, Component } from '@angular/core';\n\n/**\n * Visual separator between groups of `<rail-nav-item>`. Renders a thin\n * horizontal rule with sensible margins so groups of items breathe.\n *\n * <rail-nav>\n * <rail-nav-item label=\"Home\">...</rail-nav-item>\n * <rail-nav-item label=\"Search\">...</rail-nav-item>\n * <rail-nav-separator />\n * <rail-nav-item label=\"Trash\">...</rail-nav-item>\n * </rail-nav>\n *\n * Color overridable via `--rail-nav-separator-color`.\n */\n@Component({\n selector: 'rail-nav-separator',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: '',\n styles: [`\n /* Fixed-height host with the 1px line centered via flex. */\n /* The parent rail sets --rail-nav-separator-shift to recenter the */\n /* line visually between items (label-below in collapsed mode makes */\n /* the inter-item gap asymmetric without this). */\n :host {\n display: flex;\n align-items: center;\n height: 20px;\n padding: 0 12px;\n margin-top: var(--rail-nav-separator-shift, 0);\n box-sizing: border-box;\n transition: margin-top 0.2s ease;\n }\n :host::before {\n content: '';\n flex: 1;\n height: 1px;\n background: var(--rail-nav-separator-color, var(--mat-sys-outline-variant));\n }\n `],\n})\nexport class RailnavSeparatorComponent {}\n","import { ChangeDetectionStrategy, Component } from '@angular/core';\n\n/**\n * Flexible spacer between rail items. Placed between two groups of items, it\n * pushes everything after it to the bottom of the rail — the classic pattern\n * for keeping primary nav at the top and secondary entries (Settings,\n * Profile…) anchored at the bottom.\n *\n * <rail-nav>\n * <rail-nav-item label=\"Home\">...</rail-nav-item>\n * <rail-nav-item label=\"Inbox\">...</rail-nav-item>\n * <rail-nav-spacer />\n * <rail-nav-item label=\"Settings\">...</rail-nav-item>\n * </rail-nav>\n *\n * Requires the rail to own its full height (already the case by default).\n */\n@Component({\n selector: 'rail-nav-spacer',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: '',\n styles: [`\n :host {\n display: block;\n flex: 1 1 auto;\n }\n `],\n})\nexport class RailnavSpacerComponent {}\n","/*\n * Public API Surface of @softwarity/rail-nav\n */\n\nexport { RailnavComponent, RailnavBrandingDirective } from './lib/railnav.component';\nexport { RailnavContainerComponent } from './lib/railnav-container.component';\nexport { RailnavContentComponent } from './lib/railnav-content.component';\nexport { RailnavItemComponent } from './lib/railnav-item.component';\nexport { RailnavSeparatorComponent } from './lib/railnav-separator.component';\nexport { RailnavSpacerComponent } from './lib/railnav-spacer.component';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softwarity/rail-nav",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "author": "Softwarity",
5
5
  "license": "MIT",
6
6
  "description": "Angular Material Navigation Rail component - Material Design 3 compliant",