mirror of https://github.com/sveltejs/svelte
chore: simplify transitions (#10798)
* replace transition code * get rid of some stuff * simplify * remove some junk * not sure how we solved this before, but i guess this makes sense * oh hey i don't think we need this * make elseif transparent for transition purposes * oops * edge case * fix * do not want * rename * transition out ecah blocks when emptying * baby steps * hydration fix * tidy up * tidy up * tidy up * fallbacks * man i love deleting code * tidy up * note to self * why was this an effect * tidy up * tidy up * key blocks * temporary * fix * WIP * fix * simplify * emit events * delete delete delete * destroy child effects * simplify helper * simplify helper * fix * remove commented out code * fix wonky test * fix test * fix test * fix test * dynamic components * fix test * await * tidy up * fix * fix * fix test * tidy up * we dont need to reconcile during hydration * simplify * tidy up * fix * reduce indentation * simplify * remove some junk * remove some junk * simplify * tidy up * prefer while over do-while (this appears to have the same behaviour) * group fast paths * rename * unused import * unused exports * try this * simplify * simplify * simplify * simplify * tidy up * simplify * simplify * tidy up * simplify * simplify * more compact names * simplify * better comments * simplify * tidy up * we don't actually gain anything from this * fix binding group order bug (revealed by previous commit, but exists separately from it) * tidy up * simplify * tidy up * remove some junk * simplify * note to self * tidy up * revert this bit * tidy up * simplify * simplify * simplify * symmetry * tidy up * var * rename some stuff * tidy up * simplify * keyed each transitions * make elements inert * deferred transitions * fix * fix test * fix some tests * simplify * fix * fix test * fix * eh that'll do for now * fix * revert all these random changes * fix * fix * simplify * tidy up * simplify * simplify * tidy up * tidy up * tidy up * WIP * WIP * working * tidy up * fix * tidy up * tidy up * lerp * tidy up * rename * rename * almost everything working * tidy up * ALL TESTS PASSING * fix treeshaking * Apply suggestions from code review Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> * comment * explain elseif locality * explain flushSync * comments * this is accounted for * add some comments * remove outdated comment * add comment * add comments * rename * a few naming tweaks * explain each_item_block stuff * remove unused arg * optimise * add some comments * fix test post-optimisation * explicit comparisons * some docs * fix intro events * move effect.ran into the bitmask * docs * rename run_transitions to should_intro, add explanatory jsdoc * add some more docs * remove animation before measuring * only animate blocks that persist * note to self --------- Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>pull/10870/head
parent
f6eca83b7c
commit
e8ce41815a
File diff suppressed because it is too large
Load Diff
@ -1,140 +1,76 @@
|
|||||||
import { UNINITIALIZED, KEY_BLOCK } from '../../constants.js';
|
import { UNINITIALIZED } from '../../constants.js';
|
||||||
import { hydrate_block_anchor } from '../hydration.js';
|
import { hydrate_block_anchor } from '../hydration.js';
|
||||||
import { remove } from '../reconciler.js';
|
import { remove } from '../reconciler.js';
|
||||||
import { current_block, execute_effect } from '../../runtime.js';
|
import { pause_effect, render_effect } from '../../reactivity/effects.js';
|
||||||
import { destroy_effect, render_effect } from '../../reactivity/effects.js';
|
|
||||||
import { trigger_transitions } from '../elements/transitions.js';
|
|
||||||
import { safe_not_equal } from '../../reactivity/equality.js';
|
import { safe_not_equal } from '../../reactivity/equality.js';
|
||||||
|
|
||||||
/** @returns {import('../../types.js').KeyBlock} */
|
|
||||||
function create_key_block() {
|
|
||||||
return {
|
|
||||||
// dom
|
|
||||||
d: null,
|
|
||||||
// effect
|
|
||||||
e: null,
|
|
||||||
// parent
|
|
||||||
p: /** @type {import('../../types.js').Block} */ (current_block),
|
|
||||||
// transition
|
|
||||||
r: null,
|
|
||||||
// type
|
|
||||||
t: KEY_BLOCK
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template V
|
* @template V
|
||||||
* @param {Comment} anchor_node
|
* @param {Comment} anchor
|
||||||
* @param {() => V} key
|
* @param {() => V} get_key
|
||||||
* @param {(anchor: Node) => void} render_fn
|
* @param {(anchor: Node) => void} render_fn
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
export function key_block(anchor_node, key, render_fn) {
|
export function key_block(anchor, get_key, render_fn) {
|
||||||
const block = create_key_block();
|
const block = {};
|
||||||
|
|
||||||
/** @type {null | import('../../types.js').Render} */
|
hydrate_block_anchor(anchor);
|
||||||
let current_render = null;
|
|
||||||
hydrate_block_anchor(anchor_node);
|
|
||||||
|
|
||||||
/** @type {V | typeof UNINITIALIZED} */
|
/** @type {V | typeof UNINITIALIZED} */
|
||||||
let key_value = UNINITIALIZED;
|
let key = UNINITIALIZED;
|
||||||
let mounted = false;
|
|
||||||
block.r =
|
/** @type {import('#client').Effect} */
|
||||||
/**
|
let effect;
|
||||||
* @param {import('../../types.js').Transition} transition
|
|
||||||
* @returns {void}
|
/**
|
||||||
*/
|
* Every time `key` changes, we create a new effect. Old effects are
|
||||||
(transition) => {
|
* removed from this set when they have fully transitioned out
|
||||||
const render = /** @type {import('../../types.js').Render} */ (current_render);
|
* @type {Set<import('#client').Effect>}
|
||||||
const transitions = render.s;
|
*/
|
||||||
transitions.add(transition);
|
let effects = new Set();
|
||||||
transition.f(() => {
|
|
||||||
transitions.delete(transition);
|
|
||||||
if (transitions.size === 0) {
|
|
||||||
// If the current render has changed since, then we can remove the old render
|
|
||||||
// effect as it's stale.
|
|
||||||
if (current_render !== render && render.e !== null) {
|
|
||||||
if (render.d !== null) {
|
|
||||||
remove(render.d);
|
|
||||||
render.d = null;
|
|
||||||
}
|
|
||||||
destroy_effect(render.e);
|
|
||||||
render.e = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const create_render_effect = () => {
|
|
||||||
/** @type {import('../../types.js').Render} */
|
|
||||||
const render = {
|
|
||||||
d: null,
|
|
||||||
e: null,
|
|
||||||
s: new Set(),
|
|
||||||
p: current_render
|
|
||||||
};
|
|
||||||
const effect = render_effect(
|
|
||||||
() => {
|
|
||||||
render_fn(anchor_node);
|
|
||||||
render.d = block.d;
|
|
||||||
block.d = null;
|
|
||||||
},
|
|
||||||
block,
|
|
||||||
true,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
render.e = effect;
|
|
||||||
current_render = render;
|
|
||||||
};
|
|
||||||
const render = () => {
|
|
||||||
const render = current_render;
|
|
||||||
if (render === null) {
|
|
||||||
create_render_effect();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const transitions = render.s;
|
|
||||||
if (transitions.size === 0) {
|
|
||||||
if (render.d !== null) {
|
|
||||||
remove(render.d);
|
|
||||||
render.d = null;
|
|
||||||
}
|
|
||||||
if (render.e) {
|
|
||||||
execute_effect(render.e);
|
|
||||||
} else {
|
|
||||||
create_render_effect();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
trigger_transitions(transitions, 'out');
|
|
||||||
create_render_effect();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const key_effect = render_effect(
|
const key_effect = render_effect(
|
||||||
() => {
|
() => {
|
||||||
const prev_key_value = key_value;
|
if (safe_not_equal(key, (key = get_key()))) {
|
||||||
key_value = key();
|
if (effect) {
|
||||||
if (mounted && safe_not_equal(prev_key_value, key_value)) {
|
var e = effect;
|
||||||
render();
|
pause_effect(e, () => {
|
||||||
|
effects.delete(e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
effect = render_effect(
|
||||||
|
() => {
|
||||||
|
render_fn(anchor);
|
||||||
|
|
||||||
|
// @ts-expect-error TODO this should be unnecessary
|
||||||
|
const dom = block.d;
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (dom !== null) {
|
||||||
|
remove(dom);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
block,
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
// @ts-expect-error TODO tidy up
|
||||||
|
effect.d = block.d;
|
||||||
|
|
||||||
|
effects.add(effect);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
block,
|
block,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
// To ensure topological ordering of the key effect to the render effect,
|
|
||||||
// we trigger the effect after.
|
|
||||||
render();
|
|
||||||
mounted = true;
|
|
||||||
key_effect.ondestroy = () => {
|
key_effect.ondestroy = () => {
|
||||||
let render = current_render;
|
for (const e of effects) {
|
||||||
while (render !== null) {
|
// @ts-expect-error TODO tidy up. ondestroy should be totally unnecessary
|
||||||
const dom = render.d;
|
if (e.d) remove(e.d);
|
||||||
if (dom !== null) {
|
|
||||||
remove(dom);
|
|
||||||
}
|
|
||||||
const effect = render.e;
|
|
||||||
if (effect !== null) {
|
|
||||||
destroy_effect(effect);
|
|
||||||
}
|
|
||||||
render = render.p;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
block.e = key_effect;
|
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue