From 966aae3420ed6474fee296ddb120aa4b68fb75e1 Mon Sep 17 00:00:00 2001 From: Jacob Wright Date: Sat, 14 Mar 2020 14:37:27 -0600 Subject: [PATCH] allow transitions and animations to work within iframes (#3625) --- CHANGELOG.md | 1 + src/runtime/internal/style_manager.ts | 48 ++++++++------- .../samples/transition-css-iframe/Foo.svelte | 16 +++++ .../transition-css-iframe/Frame.svelte | 58 +++++++++++++++++++ .../samples/transition-css-iframe/_config.js | 18 ++++++ .../samples/transition-css-iframe/main.svelte | 8 +++ 6 files changed, 129 insertions(+), 20 deletions(-) create mode 100644 test/runtime/samples/transition-css-iframe/Foo.svelte create mode 100644 test/runtime/samples/transition-css-iframe/Frame.svelte create mode 100644 test/runtime/samples/transition-css-iframe/_config.js create mode 100644 test/runtime/samples/transition-css-iframe/main.svelte diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b9d08ea2a..0fe0fa3381 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Allow `` to be used in a slot ([#2798](https://github.com/sveltejs/svelte/issues/2798)) * Expose object of unknown props in `$$restProps` ([#2930](https://github.com/sveltejs/svelte/issues/2930)) +* Allow transitions and animations to work within iframes ([#3624](https://github.com/sveltejs/svelte/issues/3624)) * Disallow binding directly to `const` variables ([#4479](https://github.com/sveltejs/svelte/issues/4479)) * Fix updating keyed `{#each}` blocks with `{:else}` ([#4536](https://github.com/sveltejs/svelte/issues/4536), [#4549](https://github.com/sveltejs/svelte/issues/4549)) * Fix hydration of top-level content ([#4542](https://github.com/sveltejs/svelte/issues/4542)) diff --git a/src/runtime/internal/style_manager.ts b/src/runtime/internal/style_manager.ts index d9264e3c08..31d7573a76 100644 --- a/src/runtime/internal/style_manager.ts +++ b/src/runtime/internal/style_manager.ts @@ -1,9 +1,13 @@ import { element } from './dom'; import { raf } from './environment'; -let stylesheet; +interface ExtendedDoc extends Document { + __svelte_stylesheet: CSSStyleSheet; + __svelte_rules: Record; +} + +const active_docs = new Set(); let active = 0; -let current_rules = {}; // https://github.com/darkskyapp/string-hash/blob/master/index.js function hash(str: string) { @@ -25,14 +29,12 @@ export function create_rule(node: Element & ElementCSSInlineStyle, a: number, b: const rule = keyframes + `100% {${fn(b, 1 - b)}}\n}`; const name = `__svelte_${hash(rule)}_${uid}`; + const doc = node.ownerDocument as ExtendedDoc; + active_docs.add(doc); + const stylesheet = doc.__svelte_stylesheet || (doc.__svelte_stylesheet = doc.head.appendChild(element('style') as HTMLStyleElement).sheet as CSSStyleSheet); + const current_rules = doc.__svelte_rules || (doc.__svelte_rules = {}); if (!current_rules[name]) { - if (!stylesheet) { - const style = element('style'); - document.head.appendChild(style); - stylesheet = style.sheet; - } - current_rules[name] = true; stylesheet.insertRule(`@keyframes ${name} ${rule}`, stylesheet.cssRules.length); } @@ -45,22 +47,28 @@ export function create_rule(node: Element & ElementCSSInlineStyle, a: number, b: } export function delete_rule(node: Element & ElementCSSInlineStyle, name?: string) { - node.style.animation = (node.style.animation || '') - .split(', ') - .filter(name - ? anim => anim.indexOf(name) < 0 // remove specific animation - : anim => anim.indexOf('__svelte') === -1 // remove all Svelte animations - ) - .join(', '); - - if (name && !--active) clear_rules(); + const previous = (node.style.animation || '').split(', '); + const next = previous.filter(name + ? anim => anim.indexOf(name) < 0 // remove specific animation + : anim => anim.indexOf('__svelte') === -1 // remove all Svelte animations + ); + const deleted = previous.length - next.length; + if (deleted) { + node.style.animation = next.join(', '); + active -= deleted; + if (!active) clear_rules(); + } } export function clear_rules() { raf(() => { if (active) return; - let i = stylesheet.cssRules.length; - while (i--) stylesheet.deleteRule(i); - current_rules = {}; + active_docs.forEach(doc => { + const stylesheet = doc.__svelte_stylesheet; + let i = stylesheet.cssRules.length; + while (i--) stylesheet.deleteRule(i); + doc.__svelte_rules = {}; + }); + active_docs.clear(); }); } diff --git a/test/runtime/samples/transition-css-iframe/Foo.svelte b/test/runtime/samples/transition-css-iframe/Foo.svelte new file mode 100644 index 0000000000..c79672f21b --- /dev/null +++ b/test/runtime/samples/transition-css-iframe/Foo.svelte @@ -0,0 +1,16 @@ + + +{#if visible} +
+{/if} \ No newline at end of file diff --git a/test/runtime/samples/transition-css-iframe/Frame.svelte b/test/runtime/samples/transition-css-iframe/Frame.svelte new file mode 100644 index 0000000000..c7ac9cf76f --- /dev/null +++ b/test/runtime/samples/transition-css-iframe/Frame.svelte @@ -0,0 +1,58 @@ + + + + + diff --git a/test/runtime/samples/transition-css-iframe/_config.js b/test/runtime/samples/transition-css-iframe/_config.js new file mode 100644 index 0000000000..507efe44f4 --- /dev/null +++ b/test/runtime/samples/transition-css-iframe/_config.js @@ -0,0 +1,18 @@ +export default { + skip_if_ssr: true, + + async test({ assert, component, target, window, raf }) { + const frame = target.querySelector('iframe'); + await Promise.resolve(); + + component.visible = true; + const div = frame.contentDocument.querySelector('div'); + + raf.tick(25); + + component.visible = false; + + raf.tick(26); + assert.ok(~div.style.animation.indexOf('25ms')); + }, +}; diff --git a/test/runtime/samples/transition-css-iframe/main.svelte b/test/runtime/samples/transition-css-iframe/main.svelte new file mode 100644 index 0000000000..b1686ff15e --- /dev/null +++ b/test/runtime/samples/transition-css-iframe/main.svelte @@ -0,0 +1,8 @@ + + + \ No newline at end of file