/
githubmirror
/
angular
Обзор
Документация
Войти
/
githubmirror
/
angular
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
packages/router/src/utils/view_transition.ts
129 строк
5 KB
arturovt
fix(router): handle errors from view transition `updateCallbackDone` promise
07 янв 2026, 23:05
07 янв 2026, 23:05
9e043de
Код
Авторство
О чём код?
/** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.dev/license */ import {DOCUMENT} from '@angular/common'; import {afterNextRender, InjectionToken, Injector, runInInjectionContext} from '@angular/core'; import {ActivatedRouteSnapshot} from '../router_state'; export const CREATE_VIEW_TRANSITION = new InjectionToken<typeof createViewTransition>( typeof ngDevMode !== 'undefined' && ngDevMode ? 'view transition helper' : '', ); export const VIEW_TRANSITION_OPTIONS = new InjectionToken< ViewTransitionsFeatureOptions & {skipNextTransition: boolean} >(typeof ngDevMode !== 'undefined' && ngDevMode ? 'view transition options' : ''); /** * Options to configure the View Transitions integration in the Router. * * @developerPreview 20.0 * @see withViewTransitions */ export interface ViewTransitionsFeatureOptions { /** * Skips the very first call to `startViewTransition`. This can be useful for disabling the * animation during the application's initial loading phase. */ skipInitialTransition?: boolean; /** * A function to run after the `ViewTransition` is created. * * This function is run in an injection context and can use `inject`. */ onViewTransitionCreated?: (transitionInfo: ViewTransitionInfo) => void; } /** * The information passed to the `onViewTransitionCreated` function provided in the * `withViewTransitions` feature options. * * @developerPreview 20.0 */ export interface ViewTransitionInfo { /** * The `ViewTransition` returned by the call to `startViewTransition`. * @see https://developer.mozilla.org/en-US/docs/Web/API/ViewTransition */ transition: ViewTransition; /** * The `ActivatedRouteSnapshot` that the navigation is transitioning from. */ from: ActivatedRouteSnapshot; /** * The `ActivatedRouteSnapshot` that the navigation is transitioning to. */ to: ActivatedRouteSnapshot; } /** * A helper function for using browser view transitions. This function skips the call to * `startViewTransition` if the browser does not support it. * * @returns A Promise that resolves when the view transition callback begins. */ export function createViewTransition( injector: Injector, from: ActivatedRouteSnapshot, to: ActivatedRouteSnapshot, ): Promise<void> { const transitionOptions = injector.get(VIEW_TRANSITION_OPTIONS); const document = injector.get(DOCUMENT); if (!document.startViewTransition || transitionOptions.skipNextTransition) { transitionOptions.skipNextTransition = false; // The timing of `startViewTransition` is closer to a macrotask. It won't be called // until the current event loop exits so we use a promise resolved in a timeout instead // of Promise.resolve(). return new Promise((resolve) => setTimeout(resolve)); } let resolveViewTransitionStarted: () => void; const viewTransitionStarted = new Promise<void>((resolve) => { resolveViewTransitionStarted = resolve; }); const transition = document.startViewTransition(() => { resolveViewTransitionStarted(); // We don't actually update dom within the transition callback. The resolving of the above // promise unblocks the Router navigation, which synchronously activates and deactivates // routes (the DOM update). This view transition waits for the next change detection to // complete (below), which includes the update phase of the routed components. return createRenderPromise(injector); }); transition.updateCallbackDone.catch((error) => { if (typeof ngDevMode === 'undefined' || ngDevMode) { console.error(error); } }); transition.ready.catch((error) => { if (typeof ngDevMode === 'undefined' || ngDevMode) { console.error(error); } }); transition.finished.catch((error) => { if (typeof ngDevMode === 'undefined' || ngDevMode) { console.error(error); } }); const {onViewTransitionCreated} = transitionOptions; if (onViewTransitionCreated) { runInInjectionContext(injector, () => onViewTransitionCreated({transition, from, to})); } return viewTransitionStarted; } /** * Creates a promise that resolves after next render. */ function createRenderPromise(injector: Injector) { return new Promise<void>((resolve) => { // Wait for the microtask queue to empty after the next render happens (by waiting a macrotask). // This ensures any follow-up renders in the microtask queue are completed before the // view transition starts animating. afterNextRender({read: () => setTimeout(resolve)}, {injector}); }); }