integration: collapse the mobile nav after hydration and keep fragment targets clear of the sticky header
SSR still ships the expanded menu, but phones collapse it after render, stop sticking the header below md, and use the header-offset token so case anchors no longer sit underneath the bar. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,15 +1,30 @@
|
||||
import { ChangeDetectionStrategy, Component, computed, inject, signal } from '@angular/core';
|
||||
import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router';
|
||||
import { DOCUMENT, ViewportScroller } from '@angular/common';
|
||||
import {
|
||||
afterNextRender,
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
computed,
|
||||
inject,
|
||||
Injector,
|
||||
signal,
|
||||
} from '@angular/core';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
import { NavigationEnd, Router, RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router';
|
||||
import { filter } from 'rxjs';
|
||||
import { DotBackground } from './components/dot-background/dot-background';
|
||||
import { SHELL_COPY } from './core/content/shell-copy';
|
||||
import { SITE_CONFIG } from './core/content/site-config';
|
||||
import { LOCALE_HTML_LANG, otherLocale } from './core/i18n/locale';
|
||||
import { LocaleService } from './core/i18n/locale.service';
|
||||
import { NavigationService } from './core/navigation/navigation.service';
|
||||
import { isBrowserPlatform, viewportMatches } from './core/platform/browser';
|
||||
import { CommandPalette } from './shared/command-palette/command-palette';
|
||||
import { CommandPaletteTrigger } from './shared/command-palette/command-palette-trigger/command-palette-trigger';
|
||||
import { CommandPaletteService } from './shared/command-palette/command-palette.service';
|
||||
|
||||
/** Matches `md` in `src/_breakpoints.scss` (48rem). */
|
||||
export const WIDE_NAV_QUERY = '(min-width: 48rem)';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
@@ -28,6 +43,12 @@ export class App {
|
||||
protected readonly navigation = inject(NavigationService);
|
||||
protected readonly localeService = inject(LocaleService);
|
||||
protected readonly palette = inject(CommandPaletteService);
|
||||
private readonly router = inject(Router);
|
||||
private readonly injector = inject(Injector);
|
||||
private readonly document = inject(DOCUMENT);
|
||||
private readonly viewportScroller = inject(ViewportScroller);
|
||||
private readonly isBrowser = isBrowserPlatform();
|
||||
private readonly wideNav = viewportMatches(WIDE_NAV_QUERY);
|
||||
protected readonly siteConfig = SITE_CONFIG;
|
||||
protected readonly navOpen = signal(true);
|
||||
|
||||
@@ -38,7 +59,61 @@ export class App {
|
||||
this.navOpen() ? this.shell().menuClose : this.shell().menuOpen,
|
||||
);
|
||||
|
||||
constructor() {
|
||||
afterNextRender(
|
||||
() => {
|
||||
this.collapseNavIfNarrow();
|
||||
this.bindHeaderScrollOffset();
|
||||
},
|
||||
{ injector: this.injector },
|
||||
);
|
||||
|
||||
this.router.events
|
||||
.pipe(
|
||||
filter((event): event is NavigationEnd => event instanceof NavigationEnd),
|
||||
takeUntilDestroyed(),
|
||||
)
|
||||
.subscribe(() => this.collapseNavIfNarrow());
|
||||
}
|
||||
|
||||
protected toggleNav(): void {
|
||||
this.navOpen.update((open) => !open);
|
||||
}
|
||||
|
||||
private collapseNavIfNarrow(): void {
|
||||
if (!this.isBrowser || this.wideNav) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.navOpen.set(false);
|
||||
}
|
||||
|
||||
private bindHeaderScrollOffset(): void {
|
||||
if (!this.isBrowser) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.viewportScroller.setOffset(() => [0, this.headerOffsetPx()]);
|
||||
}
|
||||
|
||||
private headerOffsetPx(): number {
|
||||
const view = this.document.defaultView;
|
||||
if (!view) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const styles = view.getComputedStyle(this.document.documentElement);
|
||||
const raw = styles.getPropertyValue('--header-offset').trim();
|
||||
const numeric = Number.parseFloat(raw);
|
||||
if (!Number.isFinite(numeric)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (raw.endsWith('rem')) {
|
||||
const rootSize = Number.parseFloat(styles.fontSize);
|
||||
return numeric * (Number.isFinite(rootSize) ? rootSize : 16);
|
||||
}
|
||||
|
||||
return numeric;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user