sveltejs / svelte

web development for the rest of us
https://svelte.dev
MIT License
80.15k stars 4.26k forks source link

Svelte 5: Run outro transition on `unmount` #13728

Open typhonrt opened 1 month ago

typhonrt commented 1 month ago

Describe the problem

With Svelte 3 & 4 using a crafty workaround with code example at the end of this issue it is possible to destroy components after outro transitions are run.

Svelte 3&4 related issue: https://github.com/sveltejs/svelte/issues/4056

Is there any comparable svelte/internal mechanisms accessible in Svelte 5 to achieve the same result? I have not found a corresponding capability yet in upgrading my framework to Svelte 5. This would be a rather debilitating situation without a solution for various use cases required by my framework.

It would be nice if unmount in Svelte could take an options object with { outro: boolean } and return a Promise. IE run outro transition, destroy / unmount component and then resolve the Promise.

This would make the declaration:

/**
 * Unmounts a component that was previously mounted using `mount` or `hydrate`. When `outro` is true outro transitions
 * are run before unmounting.
 */
export async function unmount(component: Record<string, any>, options?: { outro: boolean }): Promise<void>;

// Alternate suggestion given that `unmount` should stay synchronous:

/**
 * Unmounts a component that was previously mounted using `mount` or `hydrate` and run any outro transition.
 */
export async function unmountWithOutro(component: Record<string, any>): Promise<void>;

The Svelte 3 & 4 solution to the problem:

import { check_outros, group_outros, transition_out } from 'svelte/internal';

/**
 * Runs outro transition then destroys Svelte component.
 *
 * Workaround for https://github.com/sveltejs/svelte/issues/4056
 *
 * @param {SvelteComponent}  instance - A Svelte component.
 *
 * @returns {Promise<void>} Promise resolved after outro transition completes and component destroyed.
 */
export async function outroAndDestroy(instance)
{
   return new Promise((resolve) =>
   {
      if (instance.$$.fragment && instance.$$.fragment.o)
      {
         group_outros();
         transition_out(instance.$$.fragment, 0, 0, () =>
         {
            instance.$destroy();
            resolve();
         });
         check_outros();
      }
      else
      {
         instance.$destroy();
         resolve();
      }
   });
}

Describe the proposed solution

Modify unmount to take an options object indicating that the outro transition should run and complete before unmounting returning a Promise. Or provide an alternate unmountWithOutro function.

Importance

i cannot use svelte without it / blocking upgrade to Svelte 5.

Serator commented 2 weeks ago

Is there a workaround to solve this issue? I'd like to use a portal based on mount and Svelte transitions, but unmount destroys the content ignoring outro transitions.

So far the only thing that comes to mind is to wrap the contents of the portaral in a condition and switch this condition first, and call unmount on the outroend event, but I would like a simpler solution. Thanks.

Azarattum commented 1 week ago

I'm looking for a way to run transitions without component destruction (assuming I control DOM removal myself). This feature request would greatly help with that. Something like this could be perfect for my use-case:

export async function unmount(component: Record<string, any>, options?: { outro?: boolean, destroy?: boolean }): Promise<void>;