fix: make snippet effects transparent for transitions (#11195)

pull/11192/head
Rich Harris 6 months ago committed by GitHub
parent 8fef412dbb
commit 1c33ff0107
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: make snippet effects transparent for transitions

@ -10,7 +10,9 @@ export const DIRTY = 1 << 9;
export const MAYBE_DIRTY = 1 << 10;
export const INERT = 1 << 11;
export const DESTROYED = 1 << 12;
export const IS_ELSEIF = 1 << 13;
export const EFFECT_RAN = 1 << 14;
export const EFFECT_RAN = 1 << 13;
/** 'Transparent' effects do not create a transition boundary */
export const EFFECT_TRANSPARENT = 1 << 14;
export const STATE_SYMBOL = Symbol('$state');

@ -1,4 +1,4 @@
import { IS_ELSEIF } from '../../constants.js';
import { EFFECT_TRANSPARENT } from '../../constants.js';
import { hydrate_nodes, hydrating, set_hydrating } from '../hydration.js';
import { remove } from '../reconciler.js';
import { block, branch, pause_effect, resume_effect } from '../../reactivity/effects.js';
@ -79,6 +79,6 @@ export function if_block(
});
if (elseif) {
effect.f |= IS_ELSEIF;
effect.f |= EFFECT_TRANSPARENT;
}
}

@ -1,3 +1,4 @@
import { EFFECT_TRANSPARENT } from '../../constants.js';
import { branch, render_effect } from '../../reactivity/effects.js';
/**
@ -11,11 +12,13 @@ export function snippet(get_snippet, node, ...args) {
/** @type {SnippetFn | null | undefined} */
var snippet;
render_effect(() => {
var effect = render_effect(() => {
if (snippet === (snippet = get_snippet())) return;
if (snippet) {
branch(() => /** @type {SnippetFn} */ (snippet)(node, ...args));
}
});
effect.f |= EFFECT_TRANSPARENT;
}

@ -7,7 +7,7 @@ import { should_intro } from '../../render.js';
import { is_function } from '../../utils.js';
import { current_each_item } from '../blocks/each.js';
import { TRANSITION_GLOBAL, TRANSITION_IN, TRANSITION_OUT } from '../../../../constants.js';
import { EFFECT_RAN } from '../../constants.js';
import { BLOCK_EFFECT, EFFECT_RAN } from '../../constants.js';
/**
* @template T
@ -212,6 +212,11 @@ export function transition(flags, element, get_fn, get_params) {
if (is_intro && should_intro) {
var parent = /** @type {import('#client').Effect} */ (e.parent);
// e.g snippets are implemented as render effects — keep going until we find the parent block
while ((parent.f & BLOCK_EFFECT) === 0 && parent.parent) {
parent = parent.parent;
}
if (is_global || (parent.f & EFFECT_RAN) !== 0) {
effect(() => {
untrack(() => transition.in());

@ -24,7 +24,7 @@ import {
EFFECT_RAN,
BLOCK_EFFECT,
ROOT_EFFECT,
IS_ELSEIF
EFFECT_TRANSPARENT
} from '../constants.js';
import { set } from './sources.js';
import { remove } from '../dom/reconciler.js';
@ -345,7 +345,7 @@ export function pause_children(effect, transitions, local) {
while (child !== null) {
var sibling = child.next;
var transparent = (child.f & IS_ELSEIF) !== 0 || (child.f & BRANCH_EFFECT) !== 0;
var transparent = (child.f & EFFECT_TRANSPARENT) !== 0 || (child.f & BRANCH_EFFECT) !== 0;
// TODO we don't need to call pause_children recursively with a linked list in place
// it's slightly more involved though as we have to account for `transparent` changing
// through the tree.
@ -381,7 +381,7 @@ function resume_children(effect, local) {
while (child !== null) {
var sibling = child.next;
var transparent = (child.f & IS_ELSEIF) !== 0 || (child.f & BRANCH_EFFECT) !== 0;
var transparent = (child.f & EFFECT_TRANSPARENT) !== 0 || (child.f & BRANCH_EFFECT) !== 0;
// TODO we don't need to call resume_children recursively with a linked list in place
// it's slightly more involved though as we have to account for `transparent` changing
// through the tree.

@ -0,0 +1,12 @@
<script>
/** @type {{ children: import('svelte').Snippet }} */
let { children } = $props();
let visible = $state(false);
</script>
<button onclick={() => visible = !visible}>toggle</button>
{#if visible}
{@render children()}
{/if}

@ -0,0 +1,20 @@
import { flushSync } from 'svelte';
import { ok, test } from '../../test';
export default test({
test({ assert, target, raf }) {
const button = target.querySelector('button');
ok(button);
flushSync(() => button.click());
raf.tick(50);
assert.htmlEqual(target.innerHTML, '<button>toggle</button><p style="opacity: 0.5;">hello</p>');
flushSync(() => button.click());
raf.tick(75);
assert.htmlEqual(
target.innerHTML,
'<button>toggle</button><p style="opacity: 0.25;">hello</p>'
);
}
});

@ -0,0 +1,9 @@
<script>
import { fade } from 'svelte/transition';
import { linear } from 'svelte/easing';
import Container from './Container.svelte';
</script>
<Container>
<p style="opacity: 1" transition:fade={{ duration: 100, easing: linear }}>hello</p>
</Container>
Loading…
Cancel
Save