Content-only page transitions + dependency security overrides
- Persistent ShellComponent hosts the router-outlet so header/footer mount once (no re-mount flash); pages nest under a shell route. page-shell is now content-only; page-layout removed. - Native withViewTransitions() cross-fades only the routed content (chrome gets stable view-transition-names); respects prefers-reduced-motion. - package.json overrides pin patched transitive dev/build deps: npm audit 16 (3 high/9 mod/4 low) -> 5 low; shipped app stays at 0 (npm audit --omit=dev). No Angular downgrade, no breaking Babel 8 bump. jsdom 28 -> 29. - README: page-transitions section + honest dependency-security note. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { ApplicationConfig, LOCALE_ID, provideBrowserGlobalErrorListeners } from '@angular/core';
|
||||
import { provideRouter } from '@angular/router';
|
||||
import { provideRouter, withViewTransitions } from '@angular/router';
|
||||
import { provideHttpClient, withInterceptors } from '@angular/common/http';
|
||||
import { registerLocaleData } from '@angular/common';
|
||||
import localeNl from '@angular/common/locales/nl';
|
||||
@@ -12,7 +12,7 @@ registerLocaleData(localeNl);
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [
|
||||
provideBrowserGlobalErrorListeners(),
|
||||
provideRouter(routes),
|
||||
provideRouter(routes, withViewTransitions()),
|
||||
provideHttpClient(withInterceptors([scenarioInterceptor])),
|
||||
{ provide: LOCALE_ID, useValue: 'nl' },
|
||||
]
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
import { Routes } from '@angular/router';
|
||||
import { ShellComponent } from './templates/shell/shell.component';
|
||||
|
||||
export const routes: Routes = [
|
||||
{ path: '', pathMatch: 'full', redirectTo: 'login' },
|
||||
{ path: 'login', loadComponent: () => import('./pages/login/login.page').then(m => m.LoginPage) },
|
||||
{ path: 'dashboard', loadComponent: () => import('./pages/dashboard/dashboard.page').then(m => m.DashboardPage) },
|
||||
{ path: 'registratie', loadComponent: () => import('./pages/registration-detail/registration-detail.page').then(m => m.RegistrationDetailPage) },
|
||||
{ path: 'herregistratie', loadComponent: () => import('./pages/herregistratie/herregistratie.page').then(m => m.HerregistratiePage) },
|
||||
{ path: '**', redirectTo: 'login' },
|
||||
{
|
||||
path: '',
|
||||
component: ShellComponent, // persistent header/footer; only children swap
|
||||
children: [
|
||||
{ path: '', pathMatch: 'full', redirectTo: 'login' },
|
||||
{ path: 'login', loadComponent: () => import('./pages/login/login.page').then(m => m.LoginPage) },
|
||||
{ path: 'dashboard', loadComponent: () => import('./pages/dashboard/dashboard.page').then(m => m.DashboardPage) },
|
||||
{ path: 'registratie', loadComponent: () => import('./pages/registration-detail/registration-detail.page').then(m => m.RegistrationDetailPage) },
|
||||
{ path: 'herregistratie', loadComponent: () => import('./pages/herregistratie/herregistratie.page').then(m => m.HerregistratiePage) },
|
||||
{ path: '**', redirectTo: 'login' },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,28 +1,25 @@
|
||||
import { Component, input } from '@angular/core';
|
||||
import { PageLayoutComponent } from '../page-layout/page-layout.component';
|
||||
import { HeadingComponent } from '../../atoms/heading/heading.component';
|
||||
import { LinkComponent } from '../../atoms/link/link.component';
|
||||
|
||||
/** Template: standard page body inside the chrome — optional back-link, a
|
||||
heading, optional intro, and projected content. Every content page plugs
|
||||
into this, so the heading/back-link markup lives in one place. */
|
||||
/** Template: standard page body — optional back-link, a heading, optional intro,
|
||||
and projected content. Rendered inside the persistent ShellComponent via the
|
||||
router outlet, so it owns only the content (not the header/footer chrome). */
|
||||
@Component({
|
||||
selector: 'app-page-shell',
|
||||
imports: [PageLayoutComponent, HeadingComponent, LinkComponent],
|
||||
imports: [HeadingComponent, LinkComponent],
|
||||
styles: [':host{display:block}'],
|
||||
template: `
|
||||
<app-page-layout>
|
||||
<div [style.max-width]="width() === 'narrow' ? '32rem' : null">
|
||||
@if (backLink()) {
|
||||
<p><app-link [to]="backLink()!">← {{ backLabel() }}</app-link></p>
|
||||
}
|
||||
<app-heading [level]="1">{{ heading() }}</app-heading>
|
||||
@if (intro()) {
|
||||
<p class="rhc-paragraph" style="margin-bottom:1.5rem">{{ intro() }}</p>
|
||||
}
|
||||
<ng-content />
|
||||
</div>
|
||||
</app-page-layout>
|
||||
<div [style.max-width]="width() === 'narrow' ? '32rem' : null">
|
||||
@if (backLink()) {
|
||||
<p><app-link [to]="backLink()!">← {{ backLabel() }}</app-link></p>
|
||||
}
|
||||
<app-heading [level]="1">{{ heading() }}</app-heading>
|
||||
@if (intro()) {
|
||||
<p class="rhc-paragraph" style="margin-bottom:1.5rem">{{ intro() }}</p>
|
||||
}
|
||||
<ng-content />
|
||||
</div>
|
||||
`,
|
||||
})
|
||||
export class PageShellComponent {
|
||||
|
||||
@@ -1,27 +1,25 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { RouterOutlet } from '@angular/router';
|
||||
import { SiteHeaderComponent } from '../../organisms/site-header/site-header.component';
|
||||
import { SiteFooterComponent } from '../../organisms/site-footer/site-footer.component';
|
||||
|
||||
/** Template: full-bleed header + footer with a centered content column. Wraps
|
||||
every page. The colored bars span the viewport; content lines up via the
|
||||
shared --app-content-max width. */
|
||||
/** Template: persistent app chrome. Header + footer mount once; only the routed
|
||||
content inside <router-outlet> changes (and cross-fades — see styles.scss). */
|
||||
@Component({
|
||||
selector: 'app-page-layout',
|
||||
imports: [SiteHeaderComponent, SiteFooterComponent],
|
||||
selector: 'app-shell',
|
||||
imports: [RouterOutlet, SiteHeaderComponent, SiteFooterComponent],
|
||||
styles: [':host{display:block}'],
|
||||
template: `
|
||||
<a href="#main" class="rhc-skip-link" style="position:absolute;left:-999px">Naar de inhoud</a>
|
||||
<!-- align-items:stretch overrides the design-system flex-start so the
|
||||
full-bleed header/footer bars fill the viewport width. -->
|
||||
<div class="utrecht-page-layout utrecht-page-layout--stretch" style="--app-content-max:64rem;min-height:100vh;align-items:stretch">
|
||||
<app-site-header />
|
||||
<main id="main" class="utrecht-page-content" style="flex:1;width:100%;box-sizing:border-box">
|
||||
<div style="max-width:var(--app-content-max);margin:0 auto;padding:2rem 1.5rem;box-sizing:border-box">
|
||||
<ng-content />
|
||||
<router-outlet />
|
||||
</div>
|
||||
</main>
|
||||
<app-site-footer />
|
||||
</div>
|
||||
`,
|
||||
})
|
||||
export class PageLayoutComponent {}
|
||||
export class ShellComponent {}
|
||||
Reference in New Issue
Block a user