blockless if block

blockless
Rich Harris 2 years ago
parent cf59065068
commit 2c19603069

@ -1,4 +1,3 @@
import { IF_BLOCK } from '../../block.js';
import {
current_hydration_fragment,
hydrate_block_anchor,
@ -6,35 +5,7 @@ import {
set_current_hydration_fragment
} from '../../hydration.js';
import { remove } from '../../reconciler.js';
import { current_block, destroy_signal, execute_effect, push_destroy_fn } from '../../runtime.js';
import { render_effect } from '../../reactivity/computations.js';
import { trigger_transitions } from '../../transitions.js';
/** @returns {import('../../types.js').IfBlock} */
function create_if_block() {
return {
// alternate transitions
a: null,
// alternate effect
ae: null,
// consequent transitions
c: null,
// consequent effect
ce: null,
// dom
d: null,
// effect
e: null,
// parent
p: /** @type {import('../../types.js').Block} */ (current_block),
// transition
r: null,
// type
t: IF_BLOCK,
// value
v: false
};
}
import { pause_effect, render_effect, resume_effect } from '../../reactivity/computations.js';
/**
* @param {Comment} anchor_node
@ -44,58 +15,31 @@ function create_if_block() {
* @returns {void}
*/
export function if_block(anchor_node, condition_fn, consequent_fn, alternate_fn) {
const block = create_if_block();
hydrate_block_anchor(anchor_node);
/** Whether or not there was a hydration mismatch. Needs to be a `let` or else it isn't treeshaken out */
let mismatch = false;
/** @type {null | import('../../types.js').TemplateNode | Array<import('../../types.js').TemplateNode>} */
let consequent_dom = null;
/** @type {null | import('../../types.js').TemplateNode | Array<import('../../types.js').TemplateNode>} */
let alternate_dom = null;
let has_mounted = false;
/**
* @type {import('../../types.js').EffectSignal | null}
*/
let current_branch_effect = null;
/** @type {import('../../types.js').EffectSignal | null} */
let consequent_effect;
const if_effect = render_effect(
() => {
const result = !!condition_fn();
if (block.v !== result || !has_mounted) {
block.v = result;
if (has_mounted) {
const consequent_transitions = block.c;
const alternate_transitions = block.a;
if (result) {
if (alternate_transitions === null || alternate_transitions.size === 0) {
execute_effect(alternate_effect);
} else {
trigger_transitions(alternate_transitions, 'out');
}
if (consequent_transitions === null || consequent_transitions.size === 0) {
execute_effect(consequent_effect);
} else {
trigger_transitions(consequent_transitions, 'in');
}
} else {
if (consequent_transitions === null || consequent_transitions.size === 0) {
execute_effect(consequent_effect);
} else {
trigger_transitions(consequent_transitions, 'out');
}
if (alternate_transitions === null || alternate_transitions.size === 0) {
execute_effect(alternate_effect);
} else {
trigger_transitions(alternate_transitions, 'in');
}
}
} else if (hydrating) {
/** @type {import('../../types.js').EffectSignal | null} */
let alternate_effect;
/** @type {boolean | null} */
let condition = null;
let mounted = false;
render_effect(() => {
if (condition === (condition = condition_fn())) return;
if (hydrating) {
const comment_text = /** @type {Comment} */ (current_hydration_fragment?.[0])?.data;
if (
!comment_text ||
(comment_text === 'ssr:if:true' && !result) ||
(comment_text === 'ssr:if:false' && result)
(comment_text === 'ssr:if:true' && !condition) ||
(comment_text === 'ssr:if:false' && condition)
) {
// Hydration mismatch: remove everything inside the anchor and start fresh.
// This could happen using when `{#if browser} .. {/if}` in SvelteKit.
@ -107,75 +51,33 @@ export function if_block(anchor_node, condition_fn, consequent_fn, alternate_fn)
current_hydration_fragment.shift();
}
}
has_mounted = true;
}
},
block,
false
);
// Managed effect
const consequent_effect = render_effect(
(
/** @type {any} */ _,
/** @type {import('../../types.js').EffectSignal | null} */ consequent_effect
) => {
const result = block.v;
if (!result && consequent_dom !== null) {
remove(consequent_dom);
consequent_dom = null;
}
if (result && current_branch_effect !== consequent_effect) {
consequent_fn(anchor_node);
if (mismatch && current_branch_effect === null) {
// Set fragment so that Svelte continues to operate in hydration mode
set_current_hydration_fragment([]);
}
current_branch_effect = consequent_effect;
consequent_dom = block.d;
}
block.d = null;
},
block,
true
);
block.ce = consequent_effect;
// Managed effect
const alternate_effect = render_effect(
(
/** @type {any} */ _,
/** @type {import('../../types.js').EffectSignal | null} */ alternate_effect
) => {
const result = block.v;
if (result && alternate_dom !== null) {
remove(alternate_dom);
alternate_dom = null;
}
if (!result && current_branch_effect !== alternate_effect) {
if (alternate_fn !== null) {
alternate_fn(anchor_node);
if (condition) {
if (consequent_effect) {
resume_effect(consequent_effect);
} else {
consequent_effect = render_effect(() => consequent_fn(anchor_node));
}
if (mismatch && current_branch_effect === null) {
// Set fragment so that Svelte continues to operate in hydration mode
set_current_hydration_fragment([]);
if (alternate_effect) {
pause_effect(alternate_effect, () => {
alternate_effect = null;
});
}
current_branch_effect = alternate_effect;
alternate_dom = block.d;
} else {
if (alternate_effect) {
resume_effect(alternate_effect);
} else {
alternate_effect = alternate_fn && render_effect(() => alternate_fn(anchor_node));
}
block.d = null;
},
block,
true
);
block.ae = alternate_effect;
push_destroy_fn(if_effect, () => {
if (consequent_dom !== null) {
remove(consequent_dom);
if (consequent_effect) {
pause_effect(consequent_effect, () => {
consequent_effect = null;
});
}
if (alternate_dom !== null) {
remove(alternate_dom);
}
destroy_signal(consequent_effect);
destroy_signal(alternate_effect);
});
block.e = if_effect;
mounted = true;
}

@ -18,6 +18,7 @@ import {
schedule_effect
} from '../runtime.js';
import { default_equals, safe_equal } from './equality.js';
import { remove } from '../reconciler.js';
/**
* @template V
@ -254,3 +255,20 @@ export function derived_safe_equal(fn) {
signal.e = safe_equal;
return signal;
}
/**
* @param {import('../types.js').ComputationSignal} effect
* @param {() => void} done
*/
export function pause_effect(effect, done) {
// TODO pause children
remove(effect.dom);
done(); // TODO defer until transitions have completed
}
/**
* @param {import('../types.js').ComputationSignal} signal
*/
export function resume_effect(signal) {
// TODO
}

@ -41,7 +41,8 @@ import {
push,
current_component_context,
pop,
deep_read
deep_read,
current_effect
} from './runtime.js';
import { render_effect, effect, managed_effect } from './reactivity/computations.js';
import {
@ -254,6 +255,7 @@ export function comment(anchor) {
*/
function close_template(dom, is_fragment, anchor) {
const block = /** @type {import('./types.js').Block} */ (current_block);
const effect = current_effect;
/** @type {import('./types.js').TemplateNode | Array<import('./types.js').TemplateNode>} */
const current = is_fragment
@ -265,6 +267,7 @@ function close_template(dom, is_fragment, anchor) {
insert(current, null, anchor);
}
block.d = current;
effect.dom = current;
}
/**

Loading…
Cancel
Save