Closed msaglietto closed 4 years ago
Sounds like a duplicate of #1825.
You are right my bad I serached as Web Component not Custom Element =S Well maybe the workaround helps someone
Thanks for your workaround!
The ugly workaround can be made a little bit more reusable with this. I lookup the top parent element with while loop as the above fix doesn't always work with nested transitions.
// transfix.js
export default function fix(transtion) {
return function(node, params){
if (!node.hasOwnProperty('ownerDocument')) {
Object.defineProperty(node, 'ownerDocument', { get: function() { return node.parentElement; } });
let elem = node
while(elem.parentElement){ elem = elem.parentElement }
node.parentElement.head = elem
}
return transtion(node, params)
}
}
Then your transition can be applied with. This should work with any transition.
<script>
import { fly } from 'svelte/transition';
import fix from './transfix.js'
</script>
{#if visible}
<p transition:fix(fly)="{{ duration: 3000 }}">
Fly in and out working
</p>
{/if}
Thanks, this helped! I needed a slightly modified version though:
// transfix.js
export default function fix(transtion) {
return function(node, params){
Object.defineProperty(node, 'ownerDocument', { get: function() { return {head: node.parentNode}; } });
return transtion(node, params)
}
}
Here to get a fix for the following transitions in typescript: fade, scale, blur, fly, slide (I only tested fade...) Still this has to be fixed.
import type { fade, fly, scale, slide } from "svelte/types/runtime/transition";
declare type EasingFunction = (t: number) => number;
interface FadeParams {
delay?: number;
duration?: number;
easing?: EasingFunction;
}
interface BlurParams {
delay?: number;
duration?: number;
easing?: EasingFunction;
amount?: number;
opacity?: number;
}
interface FadeParams {
delay?: number;
duration?: number;
easing?: EasingFunction;
}
interface FlyParams {
delay?: number;
duration?: number;
easing?: EasingFunction;
x?: number;
y?: number;
opacity?: number;
}
interface SlideParams {
delay?: number;
duration?: number;
easing?: EasingFunction;
}
interface ScaleParams {
delay?: number;
duration?: number;
easing?: EasingFunction;
start?: number;
opacity?: number;
}
export type TransitionFunctions = typeof fade | typeof scale | typeof blur | typeof fly | typeof slide
export type TransitionParams = FadeParams | ScaleParams | SlideParams | FlyParams | BlurParams
export default function fix(transtion:TransitionFunctions) {
return function(node:Element, params: TransitionParams){
Object.defineProperty(node, 'ownerDocument', { get: function() { return {head: node.parentNode}; } });
return transtion(node, params)
}
}
transition:fix(fade)={{duration:200}}
@yannkost
Thanks for this snippet
We're considering using svelte to build our in-house component library and I'm working on a (very) small PoC of building Custom Elements with it.
Since I'm just getting started with Svelte, I'm not sure how to use that workaround. I'm trying to implement a button with a ripple effect (à la Material) and the animation, using tweened
isn't working at this point; I'm not sure if this workaround applies?
Would it be possible for you (or anyone, really) to quickly explain how to use this "fix"?''
Thanks in advance!
@OClement The problem of using custom elements is that the css of the tweened animation is applied to the document header .. since for web components the outside css doent apply the animation is not played What this workaround is doing is patching the node.ownerDocument to instead of return the document from the global scope to return the parent node of the element ... so the css is inserted in the parent node that is sill inside the web component
So if you want to use it on your button component .. you will have to apply the workaround when you mount the button or use the fixed transitions from the examples .. but you will have to make sure that the "workaround" code return an element that is still inside the web component
hope this help
I created a PR that makes it possible to use svelte inside a shadow dom. All styles and animations will work like in a normal svelte-application. You could install svelte from this branch to use it until it gets merged.
@msaglietto
Great explanation, this clarifies a lot, thanks a bunch!
@OClement Just so you know how to use the fix, personnally I put the definitions/interfaces in a fix.ts file, import the fix function where needed, then i'm applying the fix when I use a Svelte transition the following way:
<div transition:fix(slide)={{ duration: 200 }}>
@yannkost your fix works but try using a transition on hover for example on a node that is not a direct child of a webcomponent
(it has another parent which will be then it's ownerDocument
). It will keep on creating style tags and appending them under this component each time you hover.
@crisward fix works better.
An alternative method that doesn't require modification of components:
interface ExtendedDoc extends Document {
__svelte_stylesheet: StyleShim;
}
class StyleShim {
cssRules: string[] = [];
private _stylesheets: CSSStyleSheet[] = [];
constructor() {
this.register(
document.head.appendChild(document.createElement("style")).sheet
);
}
insertRule(rule: string, index = 0) {
this.cssRules.splice(index, 0, rule);
for (const sheet of this._stylesheets) {
sheet.insertRule(rule, index);
}
}
deleteRule(index: number) {
this.cssRules.splice(index, 1);
for (const sheet of this._stylesheets) {
sheet.deleteRule(index);
}
}
register(sheet: CSSStyleSheet) {
this._stylesheets.push(sheet);
}
unregister(sheet: CSSStyleSheet) {
const i = this._stylesheets.findIndex((s) => s === sheet);
if (i !== -1) this._stylesheets.splice(i, 1);
}
}
const shim = new StyleShim();
(document as ExtendedDoc).__svelte_stylesheet = shim;
export default shim;
Then, bind:this
on any element in your component, insert a new stylesheet and register it onMount
:
const styleSheet = element.parentNode.appendChild(
document.createElement("style")
).sheet;
StyleShim.register(styleSheet);
return () => StyleShim.unregister(styleSheet);
necro post
The above didn't work for me, but I was able to get it working on svelte 3.48.0
with:
export default function fix(transtion:TransitionFunctions) {
return function(node:Element, params: TransitionParams) {
node.getRootNode = () => node.parentElement
return transtion(node, params)
}
}
Describe the bug When you define a transition on an element that is a customElement (web component) the transitions animations dont show since the css for the animation are not applied
Logs It fails silently
To Reproduce Check repo but is just the transition example made web component https://github.com/msaglietto/svelte-transitions-issue Or just
Expected behavior Transitions working fine in web compoent with the shadow root restrictions
Information about your Svelte project:
Your browser and the version: Chrome 81.0.4044.103
Your operating system: Chrome OS Version 81.0.4044.10
Svelte version: 3.21
Whether your project uses Webpack or Rollup: Rollup
Severity It was blocking us from using svelte but we found a work around
Additional context The issue happen because svelte insert the css in the head of the ownerDocument: https://github.com/sveltejs/svelte/blob/master/src/runtime/internal/style_manager.ts#L32 In the case of web component the elementes inside of the shadow-root can not read outside of it so the head css dont apply
I think maybe it could be solved if you can configure where svelte insert the css
A quite ugly workaround is to redefine node ownerDocument and its head element on the transition