From 58959551f463d9939a4d20cd7e81a00ea6157b3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Luba=C5=84ski?= Date: Thu, 27 Apr 2023 11:22:34 +0200 Subject: [PATCH 1/3] feat(html): Add support for Transition API --- src/template/helpers/index.js | 3 ++ src/template/helpers/resolve.js | 33 ++++++++++++ src/template/{helpers.js => helpers/set.js} | 37 +------------- src/template/helpers/transition.js | 33 ++++++++++++ src/template/index.js | 3 +- src/template/layout.js | 1 + test/spec/html.js | 56 +++++++++++++++++++++ types/index.d.ts | 2 + 8 files changed, 132 insertions(+), 36 deletions(-) create mode 100644 src/template/helpers/index.js create mode 100644 src/template/helpers/resolve.js rename src/template/{helpers.js => helpers/set.js} (64%) create mode 100644 src/template/helpers/transition.js diff --git a/src/template/helpers/index.js b/src/template/helpers/index.js new file mode 100644 index 00000000..b549e6c7 --- /dev/null +++ b/src/template/helpers/index.js @@ -0,0 +1,3 @@ +export { default as resolve } from "./resolve.js"; +export { default as set } from "./set.js"; +export { default as transition } from "./transition.js"; diff --git a/src/template/helpers/resolve.js b/src/template/helpers/resolve.js new file mode 100644 index 00000000..f2212d01 --- /dev/null +++ b/src/template/helpers/resolve.js @@ -0,0 +1,33 @@ +import resolveTemplateValue from "../resolvers/value.js"; + +const promiseMap = new WeakMap(); +export default function resolve(promise, placeholder, delay = 200) { + return function fn(host, target) { + const useLayout = fn.useLayout; + let timeout; + + if (placeholder) { + timeout = setTimeout(() => { + timeout = undefined; + resolveTemplateValue(host, target, placeholder, undefined, useLayout); + }, delay); + } + + promiseMap.set(target, promise); + + promise.then((value) => { + if (timeout) clearTimeout(timeout); + + if (promiseMap.get(target) === promise) { + resolveTemplateValue( + host, + target, + value, + placeholder && !timeout ? placeholder : undefined, + useLayout, + ); + promiseMap.set(target, null); + } + }); + }; +} diff --git a/src/template/helpers.js b/src/template/helpers/set.js similarity index 64% rename from src/template/helpers.js rename to src/template/helpers/set.js index 9d1b4e8e..a9e1befb 100644 --- a/src/template/helpers.js +++ b/src/template/helpers/set.js @@ -1,5 +1,4 @@ -import { storePointer } from "../utils.js"; -import resolveTemplateValue from "./resolvers/value.js"; +import { storePointer } from "../../utils.js"; function resolveValue({ target, detail }, setter) { let value; @@ -34,7 +33,7 @@ function getPartialObject(name, value) { const stringCache = new Map(); -export function set(property, valueOrPath) { +export default function set(property, valueOrPath) { if (!property) { throw Error( `The first argument must be a property name or an object instance: ${property}`, @@ -87,35 +86,3 @@ export function set(property, valueOrPath) { return fn; } - -const promiseMap = new WeakMap(); -export function resolve(promise, placeholder, delay = 200) { - return function fn(host, target) { - const useLayout = fn.useLayout; - let timeout; - - if (placeholder) { - timeout = setTimeout(() => { - timeout = undefined; - resolveTemplateValue(host, target, placeholder, undefined, useLayout); - }, delay); - } - - promiseMap.set(target, promise); - - promise.then((value) => { - if (timeout) clearTimeout(timeout); - - if (promiseMap.get(target) === promise) { - resolveTemplateValue( - host, - target, - value, - placeholder && !timeout ? placeholder : undefined, - useLayout, - ); - promiseMap.set(target, null); - } - }); - }; -} diff --git a/src/template/helpers/transition.js b/src/template/helpers/transition.js new file mode 100644 index 00000000..044f8191 --- /dev/null +++ b/src/template/helpers/transition.js @@ -0,0 +1,33 @@ +import global from "../../global.js"; +import { deferred, stringifyElement } from "../../utils.js"; + +const isSupported = global.document.startViewTransition !== undefined; + +let instance; +export default function transition(template) { + // istanbul ignore next + if (!isSupported) return template; + + return function fn(host, target) { + if (instance) { + console.warn( + `${stringifyElement( + host, + )}: view transition already started in ${stringifyElement(instance)}`, + ); + template(host, target); + return; + } + + template.useLayout = fn.useLayout; + instance = host; + + global.document.startViewTransition(() => { + template(host, target); + + return deferred.then(() => { + instance = undefined; + }); + }); + }; +} diff --git a/src/template/index.js b/src/template/index.js index d19c5fe6..7f9e1ac6 100644 --- a/src/template/index.js +++ b/src/template/index.js @@ -1,6 +1,7 @@ import { compileTemplate } from "./core.js"; import { getPlaceholder } from "./utils.js"; -import * as helpers from "./helpers.js"; + +import * as helpers from "./helpers/index.js"; const PLACEHOLDER = getPlaceholder(); const PLACEHOLDER_SVG = getPlaceholder("svg"); diff --git a/src/template/layout.js b/src/template/layout.js index 6473f1eb..e4d71645 100644 --- a/src/template/layout.js +++ b/src/template/layout.js @@ -159,6 +159,7 @@ const rules = { [args[args.length - 2]]: `var(--${args.join("-")})`, }; }, + view: (props, value) => ({ "view-transition-name": value }), }; const dimensions = { diff --git a/test/spec/html.js b/test/spec/html.js index 9d7d5f9f..2f1a6ec3 100644 --- a/test/spec/html.js +++ b/test/spec/html.js @@ -1084,6 +1084,62 @@ describe("html:", () => { }); }); + describe("transition helper", () => { + let el; + + beforeEach(() => { + el = document.createElement("div"); + document.body.appendChild(el); + }); + + afterEach(() => { + document.body.removeChild(el); + }); + + it("should render component", () => { + define({ + tag: "test-html-transition-deep", + content: () => html`
Hello
`, + }); + + define({ + tag: "test-html-transition", + value: "test", + content: ({ value }) => + html.transition( + html` + + `, + ), + }); + + html``({}, el); + + return resolveTimeout(() => { + expect(el.children[0].children[0].innerHTML).toEqual("test"); + expect(el.children[0].children[1].innerHTML).toEqual( + "
Hello
", + ); + }); + }); + + it("warns if try to start transition while another is in progress", () => { + spyOn(console, "warn"); + + html` + + + `({}, el); + + return resolveTimeout(() => { + expect(console.warn).toHaveBeenCalledTimes(1); + }); + }); + }); + describe("style method", () => { const render = () => html`
content
`; diff --git a/types/index.d.ts b/types/index.d.ts index 91e054c6..80a8830e 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -342,6 +342,8 @@ declare module "hybrids" { delay?: number, ): UpdateFunction; + function transition(template: UpdateFunction): UpdateFunction; + function msg(parts: TemplateStringsArray, ...args: unknown[]): string; } From 16aa24f29d13f10199c3c8988e9f15323d266c3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Luba=C5=84ski?= Date: Thu, 27 Apr 2023 11:43:11 +0200 Subject: [PATCH 2/3] Disable test when Transition API is not supported --- test/spec/html.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/test/spec/html.js b/test/spec/html.js index 2f1a6ec3..257d4e2c 100644 --- a/test/spec/html.js +++ b/test/spec/html.js @@ -1126,18 +1126,20 @@ describe("html:", () => { }); }); - it("warns if try to start transition while another is in progress", () => { - spyOn(console, "warn"); + if (document.startViewTransition) { + it("warns if try to start transition while another is in progress", () => { + spyOn(console, "warn"); - html` - - - `({}, el); + html` + + + `({}, el); - return resolveTimeout(() => { - expect(console.warn).toHaveBeenCalledTimes(1); + return resolveTimeout(() => { + expect(console.warn).toHaveBeenCalledTimes(1); + }); }); - }); + } }); describe("style method", () => { From 4358991267c98c152d2192b0ea9cc293a449a7e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Luba=C5=84ski?= Date: Fri, 28 Apr 2023 08:23:24 +0200 Subject: [PATCH 3/3] Add layout view test --- test/spec/layout.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/test/spec/layout.js b/test/spec/layout.js index 93f464d9..9eebc195 100644 --- a/test/spec/layout.js +++ b/test/spec/layout.js @@ -1,7 +1,7 @@ import { html } from "../../src/template/index.js"; import { resolveTimeout } from "../helpers.js"; -fdescribe("layout:", () => { +describe("layout:", () => { let host; beforeEach(() => { @@ -568,4 +568,17 @@ fdescribe("layout:", () => { const styles1 = window.getComputedStyle(host.children[1]); expect(styles1.font).toBe("24px sans-serif"); }); + + if (document.startViewTransition) { + it("supports transition view name", () => { + html` + + `(host); + + const styles0 = window.getComputedStyle(host.children[0]); + expect(styles0.viewTransitionName).toBe("test"); + }); + } });