diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c910667ac..415ed8d97a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ * **breaking** Error on falsy values instead of stores passed to `derived` ([#7947](https://github.com/sveltejs/svelte/pull/7947)) * **breaking** Custom store implementers now need to pass an `update` function additionally to the `set` function ([#6750](https://github.com/sveltejs/svelte/pull/6750)) * **breaking** Change order in which preprocessors are applied ([#8618](https://github.com/sveltejs/svelte/pull/8618)) +* **breaking** apply `inert` to outroing elements ([#8627](https://github.com/sveltejs/svelte/pull/8627)) * Add a way to modify attributes for script/style preprocessors ([#8618](https://github.com/sveltejs/svelte/pull/8618)) * Improve hydration speed by adding `data-svelte-h` attribute to detect unchanged HTML elements ([#7426](https://github.com/sveltejs/svelte/pull/7426)) * Add `a11y no-noninteractive-element-interactions` rule ([#8391](https://github.com/sveltejs/svelte/pull/8391)) diff --git a/src/runtime/internal/transitions.js b/src/runtime/internal/transitions.js index 1c0711fc50..925316d75e 100644 --- a/src/runtime/internal/transitions.js +++ b/src/runtime/internal/transitions.js @@ -186,14 +186,15 @@ export function create_in_transition(node, fn, params) { * @returns {{ end(reset: any): void; }} */ export function create_out_transition(node, fn, params) { - /** - * @type {TransitionOptions} */ + /** @type {TransitionOptions} */ const options = { direction: 'out' }; let config = fn(node, params, options); let running = true; let animation_name; const group = outros; group.r += 1; + /** @type {boolean} */ + let original_inert_value; /** * @returns {void} */ @@ -205,10 +206,18 @@ export function create_out_transition(node, fn, params) { tick = noop, css } = config || null_transition; + if (css) animation_name = create_rule(node, 1, 0, duration, delay, easing, css); + const start_time = now() + delay; const end_time = start_time + duration; add_render_callback(() => dispatch(node, false, 'start')); + + if ('inert' in node) { + original_inert_value = /** @type {HTMLElement} */ (node).inert; + node.inert = true; + } + loop((now) => { if (running) { if (now >= end_time) { @@ -229,6 +238,7 @@ export function create_out_transition(node, fn, params) { return running; }); } + if (is_function(config)) { wait().then(() => { // @ts-ignore @@ -238,8 +248,12 @@ export function create_out_transition(node, fn, params) { } else { go(); } + return { end(reset) { + if (reset && 'inert' in node) { + node.inert = original_inert_value; + } if (reset && config.tick) { config.tick(1, 0); } @@ -274,6 +288,9 @@ export function create_bidirectional_transition(node, fn, params, intro) { let pending_program = null; let animation_name = null; + /** @type {boolean} */ + let original_inert_value; + /** * @returns {void} */ function clear_animation() { @@ -318,11 +335,25 @@ export function create_bidirectional_transition(node, fn, params, intro) { start: now() + delay, b }; + if (!b) { // @ts-ignore todo: improve typings program.group = outros; outros.r += 1; } + + if ('inert' in node) { + if (b) { + if (original_inert_value !== undefined) { + // aborted/reversed outro — restore previous inert value + node.inert = original_inert_value; + } + } else { + original_inert_value = /** @type {HTMLElement} */ (node).inert; + node.inert = true; + } + } + if (running_program || pending_program) { pending_program = program; } else { diff --git a/test/runtime/samples/transition-inert/_config.js b/test/runtime/samples/transition-inert/_config.js new file mode 100644 index 0000000000..7058269ab0 --- /dev/null +++ b/test/runtime/samples/transition-inert/_config.js @@ -0,0 +1,28 @@ +export default { + async test({ assert, component, target, raf }) { + // jsdom doesn't set the inert attribute, and the transition checks if it exists, so set it manually to trigger the inert logic + target.querySelector('button.a').inert = false; + target.querySelector('button.b').inert = false; + + // check and abort halfway through the outro transition + component.visible = false; + raf.tick(50); + assert.strictEqual(target.querySelector('button.a').inert, true); + assert.strictEqual(target.querySelector('button.b').inert, true); + + component.visible = true; + assert.strictEqual(target.querySelector('button.a').inert, false); + assert.strictEqual(target.querySelector('button.b').inert, false); + + // let it transition out completely and then back in + component.visible = false; + raf.tick(101); + component.visible = true; + raf.tick(50); + assert.strictEqual(target.querySelector('button.a').inert, false); + assert.strictEqual(target.querySelector('button.b').inert, false); + raf.tick(51); + assert.strictEqual(target.querySelector('button.a').inert, false); + assert.strictEqual(target.querySelector('button.b').inert, false); + } +}; diff --git a/test/runtime/samples/transition-inert/main.svelte b/test/runtime/samples/transition-inert/main.svelte new file mode 100644 index 0000000000..39fcd1d1b6 --- /dev/null +++ b/test/runtime/samples/transition-inert/main.svelte @@ -0,0 +1,16 @@ + + +{#if visible} + + +{/if}