From 2ddc3c7c21333e5f11945d4ee5a63470faac146f Mon Sep 17 00:00:00 2001 From: Andreas Ehrencrona Date: Fri, 28 Jul 2023 16:29:53 +0200 Subject: [PATCH 1/4] Flag for whether to run outro transitions on $destroy --- .../02-client-side-component-api.md | 5 ++++- .../svelte/src/runtime/internal/Component.js | 22 ++++++++++++++----- .../samples/transition-js-destroy/_config.js | 15 +++++++++++++ .../samples/transition-js-destroy/main.svelte | 14 ++++++++++++ 4 files changed, 50 insertions(+), 6 deletions(-) create mode 100644 packages/svelte/test/runtime/samples/transition-js-destroy/_config.js create mode 100644 packages/svelte/test/runtime/samples/transition-js-destroy/main.svelte diff --git a/documentation/docs/04-compiler-and-api/02-client-side-component-api.md b/documentation/docs/04-compiler-and-api/02-client-side-component-api.md index f14fbe3e6622..d12021227b20 100644 --- a/documentation/docs/04-compiler-and-api/02-client-side-component-api.md +++ b/documentation/docs/04-compiler-and-api/02-client-side-component-api.md @@ -178,17 +178,20 @@ import { SvelteComponent, ComponentConstructorOptions } from 'svelte'; declare global { class Component extends SvelteComponent {} var component: Component; + var run_outro: boolean; } export {} // @filename: index.ts // ---cut--- -component.$destroy(); +component.$destroy(run_outro); ``` Removes a component from the DOM and triggers any `onDestroy` handlers. +If `run_outro` is `true`, any outro transitions will play before the component is destroyed. + ## Component props ```js diff --git a/packages/svelte/src/runtime/internal/Component.js b/packages/svelte/src/runtime/internal/Component.js index bc5b117c2ff0..09b84ceff045 100644 --- a/packages/svelte/src/runtime/internal/Component.js +++ b/packages/svelte/src/runtime/internal/Component.js @@ -17,7 +17,7 @@ import { element, attr } from './dom.js'; -import { transition_in } from './transitions.js'; +import { check_outros, group_outros, transition_in, transition_out } from './transitions.js'; /** @returns {void} */ export function bind(component, name, callback) { @@ -455,10 +455,22 @@ export class SvelteComponent { */ $$set = undefined; - /** @returns {void} */ - $destroy() { - destroy_component(this, 1); - this.$destroy = noop; + /** + * @param {boolean} [run_outro] + * @returns {void} + */ + $destroy(run_outro) { + if (run_outro && this.$$.fragment && this.$$.fragment.o) { + group_outros(); + transition_out(this.$$.fragment, 0, 0, () => { + destroy_component(this, 1); + this.$destroy = noop; + }); + check_outros(); + } else { + destroy_component(this, 1); + this.$destroy = noop; + } } /** diff --git a/packages/svelte/test/runtime/samples/transition-js-destroy/_config.js b/packages/svelte/test/runtime/samples/transition-js-destroy/_config.js new file mode 100644 index 000000000000..c795b14e0e1a --- /dev/null +++ b/packages/svelte/test/runtime/samples/transition-js-destroy/_config.js @@ -0,0 +1,15 @@ +export default { + skip_if_ssr: true, + skip_if_hydrate: true, + skip_if_hydrate_from_ssr: true, + test({ assert, component, target, raf }) { + component.$destroy(true); + + return Promise.resolve().then(() => { + const div = target.querySelector('div'); + + raf.tick(50); + assert.equal(div.transitioned, 0.5); + }); + } +}; diff --git a/packages/svelte/test/runtime/samples/transition-js-destroy/main.svelte b/packages/svelte/test/runtime/samples/transition-js-destroy/main.svelte new file mode 100644 index 000000000000..3d3eb655b40a --- /dev/null +++ b/packages/svelte/test/runtime/samples/transition-js-destroy/main.svelte @@ -0,0 +1,14 @@ + + +
From 6fa3a15922b9e48e7c75cb0bcd14eaec6d123a2c Mon Sep 17 00:00:00 2001 From: Andreas Ehrencrona Date: Mon, 31 Jul 2023 08:51:11 +0200 Subject: [PATCH 2/4] Review feedback --- .../02-client-side-component-api.md | 6 ++-- .../svelte/src/runtime/internal/Component.js | 30 +++++++++---------- packages/svelte/src/runtime/internal/dev.js | 9 ++++-- .../svelte/src/runtime/internal/private.d.ts | 4 +++ .../samples/transition-js-destroy/_config.js | 2 +- 5 files changed, 29 insertions(+), 22 deletions(-) diff --git a/documentation/docs/04-compiler-and-api/02-client-side-component-api.md b/documentation/docs/04-compiler-and-api/02-client-side-component-api.md index d12021227b20..959a1224effa 100644 --- a/documentation/docs/04-compiler-and-api/02-client-side-component-api.md +++ b/documentation/docs/04-compiler-and-api/02-client-side-component-api.md @@ -178,19 +178,19 @@ import { SvelteComponent, ComponentConstructorOptions } from 'svelte'; declare global { class Component extends SvelteComponent {} var component: Component; - var run_outro: boolean; + var options: { runOutro: boolean }; } export {} // @filename: index.ts // ---cut--- -component.$destroy(run_outro); +component.$destroy(options); ``` Removes a component from the DOM and triggers any `onDestroy` handlers. -If `run_outro` is `true`, any outro transitions will play before the component is destroyed. +`options` may contain the property `runOutro` which indicates whether to play any outro transitions before the component is destroyed. ## Component props diff --git a/packages/svelte/src/runtime/internal/Component.js b/packages/svelte/src/runtime/internal/Component.js index 09b84ceff045..0a9a384be6a4 100644 --- a/packages/svelte/src/runtime/internal/Component.js +++ b/packages/svelte/src/runtime/internal/Component.js @@ -1,23 +1,23 @@ import { - add_render_callback, - flush, - flush_render_callbacks, - schedule_update, - dirty_components -} from './scheduler.js'; -import { current_component, set_current_component } from './lifecycle.js'; -import { blank_object, is_empty, is_function, run, run_all, noop } from './utils.js'; -import { + attr, children, detach, - start_hydrating, + element, end_hydrating, get_custom_elements_slots, insert, - element, - attr + start_hydrating } from './dom.js'; +import { current_component, set_current_component } from './lifecycle.js'; +import { + add_render_callback, + dirty_components, + flush, + flush_render_callbacks, + schedule_update +} from './scheduler.js'; import { check_outros, group_outros, transition_in, transition_out } from './transitions.js'; +import { blank_object, is_empty, is_function, noop, run, run_all } from './utils.js'; /** @returns {void} */ export function bind(component, name, callback) { @@ -456,11 +456,11 @@ export class SvelteComponent { $$set = undefined; /** - * @param {boolean} [run_outro] + * @param {import('./private.js').ComponentDestroyOptions} [options] * @returns {void} */ - $destroy(run_outro) { - if (run_outro && this.$$.fragment && this.$$.fragment.o) { + $destroy(options) { + if (options?.runOutro && this.$$.fragment && this.$$.fragment.o) { group_outros(); transition_out(this.$$.fragment, 0, 0, () => { destroy_component(this, 1); diff --git a/packages/svelte/src/runtime/internal/dev.js b/packages/svelte/src/runtime/internal/dev.js index b1d89add3341..d06397c556ef 100644 --- a/packages/svelte/src/runtime/internal/dev.js +++ b/packages/svelte/src/runtime/internal/dev.js @@ -331,9 +331,12 @@ export class SvelteComponentDev extends SvelteComponent { super(); } - /** @returns {void} */ - $destroy() { - super.$destroy(); + /** + * @param {import('./private.js').ComponentDestroyOptions} [options] + * @returns {void} + */ + $destroy(options) { + super.$destroy(options); this.$destroy = () => { console.warn('Component was already destroyed'); // eslint-disable-line no-console }; diff --git a/packages/svelte/src/runtime/internal/private.d.ts b/packages/svelte/src/runtime/internal/private.d.ts index ef2245256ef7..64aea23de09a 100644 --- a/packages/svelte/src/runtime/internal/private.d.ts +++ b/packages/svelte/src/runtime/internal/private.d.ts @@ -75,6 +75,10 @@ export interface StyleInformation { rules: Record; } +export interface ComponentDestroyOptions { + runOutro?: boolean; +} + export type TaskCallback = (now: number) => boolean | void; export type TaskEntry = { c: TaskCallback; f: () => void }; diff --git a/packages/svelte/test/runtime/samples/transition-js-destroy/_config.js b/packages/svelte/test/runtime/samples/transition-js-destroy/_config.js index c795b14e0e1a..ddcea983a735 100644 --- a/packages/svelte/test/runtime/samples/transition-js-destroy/_config.js +++ b/packages/svelte/test/runtime/samples/transition-js-destroy/_config.js @@ -3,7 +3,7 @@ export default { skip_if_hydrate: true, skip_if_hydrate_from_ssr: true, test({ assert, component, target, raf }) { - component.$destroy(true); + component.$destroy({ runOutro: true }); return Promise.resolve().then(() => { const div = target.querySelector('div'); From 749c69458df5860e1de7bf4a8716218929d48945 Mon Sep 17 00:00:00 2001 From: Andreas Ehrencrona Date: Mon, 31 Jul 2023 08:52:16 +0200 Subject: [PATCH 3/4] show options are optional in docs --- .../docs/04-compiler-and-api/02-client-side-component-api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/04-compiler-and-api/02-client-side-component-api.md b/documentation/docs/04-compiler-and-api/02-client-side-component-api.md index 959a1224effa..3b59e4362d52 100644 --- a/documentation/docs/04-compiler-and-api/02-client-side-component-api.md +++ b/documentation/docs/04-compiler-and-api/02-client-side-component-api.md @@ -178,7 +178,7 @@ import { SvelteComponent, ComponentConstructorOptions } from 'svelte'; declare global { class Component extends SvelteComponent {} var component: Component; - var options: { runOutro: boolean }; + var options: { runOutro: boolean } | undefined; } export {} From 85e83ae43180c6262c1dc01bdaeadbbfb5f929c4 Mon Sep 17 00:00:00 2001 From: Andreas Ehrencrona Date: Mon, 31 Jul 2023 08:52:59 +0200 Subject: [PATCH 4/4] restoring import order in component --- .../svelte/src/runtime/internal/Component.js | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/svelte/src/runtime/internal/Component.js b/packages/svelte/src/runtime/internal/Component.js index 0a9a384be6a4..dd66ad31b92a 100644 --- a/packages/svelte/src/runtime/internal/Component.js +++ b/packages/svelte/src/runtime/internal/Component.js @@ -1,23 +1,23 @@ import { - attr, + add_render_callback, + flush, + flush_render_callbacks, + schedule_update, + dirty_components +} from './scheduler.js'; +import { current_component, set_current_component } from './lifecycle.js'; +import { blank_object, is_empty, is_function, run, run_all, noop } from './utils.js'; +import { children, detach, - element, + start_hydrating, end_hydrating, get_custom_elements_slots, insert, - start_hydrating + element, + attr } from './dom.js'; -import { current_component, set_current_component } from './lifecycle.js'; -import { - add_render_callback, - dirty_components, - flush, - flush_render_callbacks, - schedule_update -} from './scheduler.js'; import { check_outros, group_outros, transition_in, transition_out } from './transitions.js'; -import { blank_object, is_empty, is_function, noop, run, run_all } from './utils.js'; /** @returns {void} */ export function bind(component, name, callback) {