From c9527f683f786402db02a0e0e683d1834c4e69ec Mon Sep 17 00:00:00 2001 From: Jacob Wright Date: Thu, 26 Sep 2019 13:54:21 -0600 Subject: [PATCH] Allow transitions and animations to work within iframes Svelte works great in iframes except for transitions and animations. This fixes that issue. See #3624. --- src/runtime/internal/style_manager.ts | 35 +++++++---- .../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 +++ 5 files changed, 123 insertions(+), 12 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/src/runtime/internal/style_manager.ts b/src/runtime/internal/style_manager.ts index d9264e3c08..61a8540626 100644 --- a/src/runtime/internal/style_manager.ts +++ b/src/runtime/internal/style_manager.ts @@ -1,9 +1,11 @@ import { element } from './dom'; import { raf } from './environment'; -let stylesheet; +type DocStyles = [CSSStyleSheet, Record]; + +let activeDocs = new Set(); +let docStyles = new WeakMap(); let active = 0; -let current_rules = {}; // https://github.com/darkskyapp/string-hash/blob/master/index.js function hash(str: string) { @@ -25,14 +27,18 @@ 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; + activeDocs.add(doc); + let [ stylesheet, current_rules ] = (docStyles.get(doc) || []) as DocStyles; + if (!stylesheet) { + current_rules = {}; + const style = element('style'); + doc.head.appendChild(style); + stylesheet = style.sheet as CSSStyleSheet; + docStyles.set(doc, [ stylesheet, current_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); } @@ -53,14 +59,19 @@ export function delete_rule(node: Element & ElementCSSInlineStyle, name?: string ) .join(', '); - if (name && !--active) clear_rules(); + active = Math.max(0, active - 1); + if (name && !active) clear_rules(); } export function clear_rules() { raf(() => { if (active) return; - let i = stylesheet.cssRules.length; - while (i--) stylesheet.deleteRule(i); - current_rules = {}; + activeDocs.forEach(doc => { + const [ stylesheet ] = docStyles.get(doc); + let i = stylesheet.cssRules.length; + while (i--) stylesheet.deleteRule(i); + docStyles.set(doc, [ stylesheet, {} ]); + }); + activeDocs.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