diff --git a/src/index.js b/src/index.js index 890ede9..4da734d 100644 --- a/src/index.js +++ b/src/index.js @@ -48,10 +48,21 @@ export function startViewTransition(options) { return null; } - const viewTransition = document.startViewTransition(options.update); + + let viewTransition = /** @type {ViewTransition | null} */(null); + + const afterUpdateCallback = new Promise(resolve => { + const update = async() => { + await options.update?.(); + resolve(null); + } + + viewTransition = document.startViewTransition(update); + }); start({ - viewTransition, + afterUpdateCallback, + transitionFinished: viewTransition?.finished ?? Promise.resolve(), captures: options.captures || {}, styles: options.styles || {}, classes: {both: options.classes || []} @@ -150,20 +161,14 @@ export class Velvette { handler: async () => { /** @type {ViewTransition} */ let transition; - const updateCallbackDone = new Promise(resolve => { + const afterUpdateCallback = new Promise(resolve => { transition = document.startViewTransition(async () => { await interceptor.handler(); resolve(null); }); - transition.finished.then(() => done(null)); + done(transition); }); - - this.#internal.startNavigation(nav, { - get finished() { return transition.finished }, - get ready() { return transition.ready }, - updateCallbackDone, - skipTransition() { transition.skipTransition(); } - }, "both"); + this.#internal.startNavigation(nav, afterUpdateCallback, transition.finished, "both"); } })); } @@ -211,7 +216,7 @@ export class Velvette { return; } - this.#internal.startNavigation(nav, viewTransition, "new-only"); + this.#internal.startNavigation(nav, Promise.resolve(), viewTransition.finished, "new-only"); result.dispatchEvent(new VelvetteEvent("inbound", viewTransition)) }); @@ -230,7 +235,7 @@ export class Velvette { skipTransition: () => toggle(false) }; - this.#internal.startNavigation(nav, viewTransition, "old-only"); + this.#internal.startNavigation(nav, Promise.resolve(), pageHidden, "old-only"); result.dispatchEvent(new VelvetteEvent("outbound", viewTransition)); }); return result; diff --git a/src/nav.js b/src/nav.js index 8e68d72..db94967 100644 --- a/src/nav.js +++ b/src/nav.js @@ -61,17 +61,17 @@ export function init(config) { const params = {}; /** * - * @param {URLPatternResult} result - * @returns {{[key: string]: string}} + * @param {URLPatternResult | null} result + * @returns {{[key: string]: string} | null} */ const applyParams = result => result ? - Object.assign(params, result?.pathname?.groups, result?.search?.groups) : {}; + Object.assign(params, result?.pathname?.groups, result?.search?.groups) : null; const defaultRoute = new URLPattern("*", location.href); const from = routes.get(rule.from ?? "") ?? defaultRoute; const to = routes.get(rule.to ?? "") ?? defaultRoute; - if (applyParams(from.exec(navigation.from)) && - applyParams(to.exec(navigation.to))) + if (applyParams(from.test(navigation.from) ? from.exec(navigation.from) : null) && + applyParams(to.test(navigation.to) ? to.exec(navigation.to) : null)) return {params, class: rule.class, from: rule.from, to: rule.to}; } @@ -94,10 +94,11 @@ export function init(config) { /** * * @param {NavigationInfo} nav - * @param {ViewTransition} viewTransition + * @param {PromiseLike} afterUpdateCallback + * @param {PromiseLike} transitionFinished * @param {"old-only" | "new-only" | "both"} phase */ - startNavigation(nav, viewTransition, phase) { + startNavigation(nav, afterUpdateCallback, transitionFinished, phase) { /** * * @param {string} str @@ -111,10 +112,11 @@ export function init(config) { [sub(selector), sub(name)])); return start({ - viewTransition, + afterUpdateCallback, + transitionFinished, classes: { - old: [`route-${nav.from}`], - new: [`route-${nav.to}`], + old: [`route-${nav.from}`, `with-${nav.to}`], + new: [`route-${nav.to}`, `with-${nav.from}`], both: [`from-${nav.from}`, `to-${nav.to}`, ...(nav.class ? [nav.class] : [])] }, styles: config.styles, diff --git a/src/transition.js b/src/transition.js index 5cb67cc..4f27166 100644 --- a/src/transition.js +++ b/src/transition.js @@ -13,12 +13,11 @@ export async function start(transitionParams, phase) { const oldClasses = [...(transitionParams.classes?.old || []), "old"]; const newClasses = [...(transitionParams.classes?.new || []), "new"]; - if (phase !== "new-only") { - applyClasses(oldClasses, true); applyClasses(transitionParams.classes?.both, true); + applyClasses(oldClasses, true); applyCaptures(); - await transitionParams.viewTransition.updateCallbackDone; + await transitionParams.afterUpdateCallback; applyClasses(oldClasses, false); cleanupCaptures(); } @@ -29,7 +28,7 @@ export async function start(transitionParams, phase) { applyClasses(newClasses, true); applyCaptures(); document.adoptedStyleSheets.push(stylesheet); - await transitionParams.viewTransition.finished; + await transitionParams.transitionFinished; document.adoptedStyleSheets.splice( document.adoptedStyleSheets.indexOf(stylesheet), 1); cleanupCaptures(); applyClasses(newClasses, false); diff --git a/src/types-internal.ts b/src/types-internal.ts index 9dabd17..c1ca7fe 100644 --- a/src/types-internal.ts +++ b/src/types-internal.ts @@ -1,6 +1,7 @@ /// export interface ViewTransitionParams { - viewTransition : ViewTransition + afterUpdateCallback: PromiseLike + transitionFinished: PromiseLike captures?: {[selector: string]: string} styles?: {[selector: string]: Partial} classes?: Partial<{ diff --git a/tests/resources/list-details.html b/tests/resources/list-details.html index 740eee7..bf6b1c9 100644 --- a/tests/resources/list-details.html +++ b/tests/resources/list-details.html @@ -74,14 +74,14 @@
- Home + Home
diff --git a/tests/resources/nav.css b/tests/resources/nav.css index d975c5b..2ecfc3a 100644 --- a/tests/resources/nav.css +++ b/tests/resources/nav.css @@ -16,6 +16,15 @@ main { } } +@keyframes slide-left-far { + from { + transform: translateX(120px); + } + to { + transform: none; + } +} + @keyframes slide-right { from { transform: translateX(-100px); @@ -30,4 +39,8 @@ main { } :root.vt-slide-right::view-transition-group(main) { animation-name: slide-right; +} + +:root.vt-route-details.vt-with-home::view-transition-group(main) { + animation-name: slide-left-far; } \ No newline at end of file diff --git a/tests/resources/pw-utils.js b/tests/resources/pw-utils.js index c958103..bcf342d 100644 --- a/tests/resources/pw-utils.js +++ b/tests/resources/pw-utils.js @@ -23,6 +23,8 @@ export function testInBrowser(name, label, params = {}) { const done = new Promise(resolve => {resolve_test = resolve}); await page.goto(url.href); const result = await done; + if (result === "error") + continue; if (result === "done") { for (const {actual, expected} of expectations) { expect(typeof actual).toEqual(typeof expected); @@ -38,9 +40,11 @@ export function testInBrowser(name, label, params = {}) { }) } } - break; + return; } } + + expect("this").toBe("not reached"); }); }