diff --git a/src/common/util/view-transition.ts b/src/common/util/view-transition.ts index 757e0c7806..a53ab4715c 100644 --- a/src/common/util/view-transition.ts +++ b/src/common/util/view-transition.ts @@ -1,30 +1,45 @@ /** - * Executes a callback within a View Transition if supported, otherwise runs it directly. + * Executes a synchronous callback within a View Transition if supported, otherwise runs it directly. * - * @param callback - Function to execute. Can be synchronous or return a Promise. The callback will be passed a boolean indicating whether the view transition is available. + * @param callback - Synchronous function to execute. The callback will be passed a boolean indicating whether the view transition is available. * @returns Promise that resolves when the transition completes (or immediately if not supported) * * @example * ```typescript - * // Synchronous callback * withViewTransition(() => { * this.large = !this.large; * }); - * - * // Async callback - * await withViewTransition(async () => { - * await this.updateData(); - * }); * ``` */ export const withViewTransition = ( - callback: (viewTransitionAvailable: boolean) => void | Promise + callback: (viewTransitionAvailable: boolean) => void ): Promise => { - if (document.startViewTransition) { - return document.startViewTransition(() => callback(true)).finished; + if (!document.startViewTransition) { + callback(false); + return Promise.resolve(); } - // Fallback: Execute callback directly without transition - const result = callback(false); - return result instanceof Promise ? result : Promise.resolve(); + let callbackInvoked = false; + + try { + // View Transitions require DOM updates to happen synchronously within + // the callback. Execute the callback immediately (synchronously). + const transition = document.startViewTransition(() => { + callbackInvoked = true; + callback(true); + }); + return transition.finished; + } catch (err) { + // eslint-disable-next-line no-console + console.warn( + "View transition failed, falling back to direct execution.", + err + ); + // Make sure the callback is invoked exactly once. + if (!callbackInvoked) { + callback(false); + return Promise.resolve(); + } + return Promise.reject(err); + } };