Skip to content

breaking: play transitions on mount by default #12351

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/hip-garlics-tap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'svelte': patch
---

breaking: play transitions on `mount` by default
13 changes: 9 additions & 4 deletions packages/svelte/src/internal/client/dev/hmr.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
/** @import { Source, Effect } from '#client' */
import { empty } from '../dom/operations.js';
import { block, branch, destroy_effect } from '../reactivity/effects.js';
import { set_should_intro } from '../render.js';
import { get } from '../runtime.js';
import { check_target } from './legacy.js';

/**
* @template {(anchor: Comment, props: any) => any} Component
Expand All @@ -20,6 +18,8 @@ export function hmr(source) {
/** @type {Effect} */
let effect;

let ran = false;

block(() => {
const component = get(source);

Expand All @@ -30,7 +30,9 @@ export function hmr(source) {
}

effect = branch(() => {
set_should_intro(false);
// when the component is invalidated, replace it without transitions
if (ran) set_should_intro(false);

// preserve getters/setters
Object.defineProperties(
instance,
Expand All @@ -39,10 +41,13 @@ export function hmr(source) {
new.target ? new component(anchor, props) : component(anchor, props)
)
);
set_should_intro(true);

if (ran) set_should_intro(true);
});
});

ran = true;

return instance;
};
}
10 changes: 6 additions & 4 deletions packages/svelte/src/internal/client/render.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ export const root_event_handles = new Set();

/**
* This is normally true — block effects should run their intro transitions —
* but is false during hydration and mounting (unless `options.intro` is `true`)
* and when creating the children of a `<svelte:element>` that just changed tag
* but is false during hydration (unless `options.intro` is `true`) and
* when creating the children of a `<svelte:element>` that just changed tag
*/
export let should_intro = true;

Expand Down Expand Up @@ -66,7 +66,8 @@ export function slot(anchor, slot_fn, slot_props, fallback_fn) {
}

/**
* Mounts a component to the given target and returns the exports and potentially the props (if compiled with `accessors: true`) of the component
* Mounts a component to the given target and returns the exports and potentially the props (if compiled with `accessors: true`) of the component.
* Transitions will play during the initial render unless the `intro` option is set to `false`.
*
* @template {Record<string, any>} Props
* @template {Record<string, any>} Exports
Expand Down Expand Up @@ -126,6 +127,7 @@ export function hydrate(component, options) {
validate_component(component);
}

options.intro = options.intro ?? false;
const target = options.target;
const previous_hydrate_nodes = hydrate_nodes;

Expand Down Expand Up @@ -190,7 +192,7 @@ export function hydrate(component, options) {
* }} options
* @returns {Exports}
*/
function _mount(Component, { target, anchor, props = {}, events, context, intro = false }) {
function _mount(Component, { target, anchor, props = {}, events, context, intro = true }) {
init_operations();

const registered_events = new Set();
Expand Down
2 changes: 1 addition & 1 deletion packages/svelte/src/legacy/legacy-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ class Svelte4Component {
target: options.target,
props,
context: options.context,
intro: options.intro,
intro: options.intro ?? false,
recover: options.recover
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<script>
import { fade } from 'svelte/transition';
</script>

<div in:fade|global={{ duration: 100 }}>DIV</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { flushSync } from 'svelte';
import { ok, test } from '../../test';

export default test({
async test({ assert, target, raf }) {
const [btn1, btn2] = target.querySelectorAll('button');
const div = target.querySelector('div');
ok(div);

btn1.click();
flushSync();
assert.htmlEqual(div.innerHTML, `<div style="opacity: 0;">DIV</div>`);

raf.tick(100);
assert.htmlEqual(div.innerHTML, `<div style="">DIV</div>`);

btn2.click();
flushSync();
assert.htmlEqual(div.innerHTML, `<div>DIV</div>`);
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<script>
import { mount, unmount } from 'svelte';
import Component from './Component.svelte';

let el;
let instance;

function intro(animate) {
if (instance) unmount(instance);

instance = mount(Component, {
target: el,
intro: animate
});
}
</script>

<div bind:this={el}></div>

<button onclick={() => intro()}>mount with intro transition</button>
<button onclick={() => intro(false)}>mount without intro transition</button>
3 changes: 2 additions & 1 deletion packages/svelte/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,8 @@ declare module 'svelte' {
/** Anything except a function */
type NotFunction<T> = T extends Function ? never : T;
/**
* Mounts a component to the given target and returns the exports and potentially the props (if compiled with `accessors: true`) of the component
* Mounts a component to the given target and returns the exports and potentially the props (if compiled with `accessors: true`) of the component.
* Transitions will play during the initial render unless the `intro` option is set to `false`.
*
* */
export function mount<Props extends Record<string, any>, Exports extends Record<string, any>>(component: ComponentType<SvelteComponent<Props>> | Component<Props, Exports, any>, options: {} extends Props ? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,3 +281,7 @@ In Svelte 4, `<svelte:element this="div">` is valid code. This makes little sens
```

Note that whereas Svelte 4 would treat `<svelte:element this="input">` (for example) identically to `<input>` for the purposes of determining which `bind:` directives could be applied, Svelte 5 does not.

### `mount` plays transitions by default

The `mount` function used to render a component tree plays transitions by default unless the `intro` option is set to `false`. This is different from legacy class components which, when manually instantiated, didn't play transitions by default.
Loading