From 05789daff97329ef941a4a1a20561ffd3e8172c4 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Mon, 8 Jan 2024 16:37:01 +0000 Subject: [PATCH] fix: allow transition undefined payload + microtask queue handling (#10117) * fix: allow transition undefined payload * cleanup * cleanup * add microtask queue handling --- .changeset/clever-chefs-relate.md | 5 +++ .../svelte/src/internal/client/runtime.js | 22 ++++++++++ .../svelte/src/internal/client/transitions.js | 31 +++++++++----- .../if-transition-undefined/_config.js | 40 +++++++++++++++++++ .../if-transition-undefined/main.svelte | 16 ++++++++ 5 files changed, 104 insertions(+), 10 deletions(-) create mode 100644 .changeset/clever-chefs-relate.md create mode 100644 packages/svelte/tests/runtime-runes/samples/if-transition-undefined/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/if-transition-undefined/main.svelte diff --git a/.changeset/clever-chefs-relate.md b/.changeset/clever-chefs-relate.md new file mode 100644 index 0000000000..46db5c6ae7 --- /dev/null +++ b/.changeset/clever-chefs-relate.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: allow transition undefined payload diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 1e5301a970..3be0afdb03 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -50,6 +50,8 @@ let current_queued_effects = []; /** @type {Array<() => void>} */ let current_queued_tasks = []; +/** @type {Array<() => void>} */ +let current_queued_microtasks = []; let flush_count = 0; // Handle signal reactivity tree dependencies and consumer @@ -579,6 +581,11 @@ function flush_queued_effects(effects) { function process_microtask() { is_micro_task_queued = false; + if (current_queued_microtasks.length > 0) { + const tasks = current_queued_microtasks.slice(); + current_queued_microtasks = []; + run_all(tasks); + } if (flush_count > 101) { return; } @@ -637,6 +644,18 @@ export function schedule_task(fn) { current_queued_tasks.push(fn); } +/** + * @param {() => void} fn + * @returns {void} + */ +export function schedule_microtask(fn) { + if (!is_micro_task_queued) { + is_micro_task_queued = true; + queueMicrotask(process_microtask); + } + current_queued_microtasks.push(fn); +} + /** * @returns {void} */ @@ -697,6 +716,9 @@ export function flushSync(fn) { if (current_queued_pre_and_render_effects.length > 0 || effects.length > 0) { flushSync(); } + if (is_micro_task_queued) { + process_microtask(); + } if (is_task_queued) { process_task(); } diff --git a/packages/svelte/src/internal/client/transitions.js b/packages/svelte/src/internal/client/transitions.js index 38225d0346..3317520e6d 100644 --- a/packages/svelte/src/internal/client/transitions.js +++ b/packages/svelte/src/internal/client/transitions.js @@ -21,7 +21,7 @@ import { managed_effect, managed_pre_effect, mark_subtree_inert, - schedule_task, + schedule_microtask, untrack } from './runtime.js'; import { raf } from './timing.js'; @@ -279,6 +279,9 @@ function create_transition(dom, init, direction, effect) { // @ts-ignore payload = payload({ direction: curr_direction }); } + if (payload == null) { + return; + } const duration = payload.duration ?? 300; const delay = payload.delay ?? 0; const css_fn = payload.css; @@ -354,11 +357,15 @@ function create_transition(dom, init, direction, effect) { cancelled = false; create_animation(); } - dispatch_event(dom, 'introstart'); - if (needs_reverse) { - /** @type {Animation | TickAnimation} */ (animation).reverse(); + if (animation === null) { + transition.x(); + } else { + dispatch_event(dom, 'introstart'); + if (needs_reverse) { + /** @type {Animation | TickAnimation} */ (animation).reverse(); + } + /** @type {Animation | TickAnimation} */ (animation).play(); } - /** @type {Animation | TickAnimation} */ (animation).play(); }, // out o() { @@ -368,11 +375,15 @@ function create_transition(dom, init, direction, effect) { cancelled = false; create_animation(); } - dispatch_event(dom, 'outrostart'); - if (needs_reverse) { - /** @type {Animation | TickAnimation} */ (animation).reverse(); + if (animation === null) { + transition.x(); } else { - /** @type {Animation | TickAnimation} */ (animation).play(); + dispatch_event(dom, 'outrostart'); + if (needs_reverse) { + /** @type {Animation | TickAnimation} */ (animation).reverse(); + } else { + /** @type {Animation | TickAnimation} */ (animation).play(); + } } }, // cancel @@ -671,7 +682,7 @@ function each_item_animate(block, transitions, index, index_is_reactive) { transition.c(); } } - schedule_task(() => { + schedule_microtask(() => { trigger_transitions(transitions, 'key', from); }); } diff --git a/packages/svelte/tests/runtime-runes/samples/if-transition-undefined/_config.js b/packages/svelte/tests/runtime-runes/samples/if-transition-undefined/_config.js new file mode 100644 index 0000000000..3098714df4 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/if-transition-undefined/_config.js @@ -0,0 +1,40 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + html: ``, + + async test({ assert, target }) { + const [btn1, btn2] = target.querySelectorAll('button'); + + flushSync(() => { + btn1.click(); + }); + + assert.htmlEqual( + target.innerHTML, + `

Hello\n!

` + ); + + flushSync(() => { + btn1.click(); + }); + + assert.htmlEqual(target.innerHTML, ``); + + flushSync(() => { + btn2.click(); + }); + + assert.htmlEqual(target.innerHTML, ``); + + flushSync(() => { + btn1.click(); + }); + + assert.htmlEqual( + target.innerHTML, + `

Hello\n!

` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/if-transition-undefined/main.svelte b/packages/svelte/tests/runtime-runes/samples/if-transition-undefined/main.svelte new file mode 100644 index 0000000000..5d5270df94 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/if-transition-undefined/main.svelte @@ -0,0 +1,16 @@ + + + + +{#if show} +

Hello {name}!

+{/if}