fix wonky test

pull/10798/head
Rich Harris 2 years ago
parent dc18c5dfcb
commit f19255cd69

@ -1,10 +1,16 @@
import { namespace_svg } from '../../../../constants.js';
import { current_hydration_fragment, hydrate_block_anchor, hydrating } from '../hydration.js';
import { empty } from '../operations.js';
import { destroy_effect, render_effect } from '../../reactivity/effects.js';
import {
destroy_effect,
pause_effect,
render_effect,
resume_effect
} from '../../reactivity/effects.js';
import { insert, remove } from '../reconciler.js';
import { current_block, execute_effect } from '../../runtime.js';
import { is_array } from '../../utils.js';
import { run_transitions, set_run_transitions } from '../../render.js';
/**
* @param {import('#client').Block} block
@ -45,90 +51,101 @@ export function element(anchor_node, tag_fn, is_svg, render_fn) {
};
hydrate_block_anchor(anchor_node);
let has_mounted = false;
/** @type {string} */
/** @type {string | null} */
let tag;
/** @type {string | null} */
let current_tag;
/** @type {null | Element} */
let element = null;
const element_effect = render_effect(
() => {
tag = tag_fn();
if (has_mounted) {
execute_effect(render_effect_signal);
}
has_mounted = true;
},
block,
false
);
// Managed effect
const render_effect_signal = render_effect(
() => {
// We try our best infering the namespace in case it's not possible to determine statically,
// but on the first render on the client (without hydration) the parent will be undefined,
// since the anchor is not attached to its parent / the dom yet.
const ns =
is_svg || tag === 'svg'
? namespace_svg
: is_svg === false || anchor_node.parentElement?.tagName === 'foreignObject'
? null
: anchor_node.parentElement?.namespaceURI ?? null;
const next_element = tag
? hydrating
? /** @type {Element} */ (current_hydration_fragment[0])
: ns
? document.createElementNS(ns, tag)
: document.createElement(tag)
: null;
const prev_element = element;
if (prev_element !== null) {
block.d = null;
}
/** @type {import('#client').Effect | null} */
let effect;
element = next_element;
if (element !== null && render_fn !== undefined) {
let anchor;
if (hydrating) {
// Use the existing ssr comment as the anchor so that the inner open and close
// methods can pick up the existing nodes correctly
anchor = /** @type {Comment} */ (element.firstChild);
} else {
anchor = empty();
element.appendChild(anchor);
}
render_fn(element, anchor);
}
const wrapper = render_effect(() => {
const next_tag = tag_fn() || null;
if (next_tag === tag) return;
const has_prev_element = prev_element !== null;
if (has_prev_element) {
remove(prev_element);
}
if (element !== null) {
insert(element, null, anchor_node);
if (has_prev_element) {
const parent_block = block.p;
swap_block_dom(parent_block, prev_element, element);
}
// We try our best infering the namespace in case it's not possible to determine statically,
// but on the first render on the client (without hydration) the parent will be undefined,
// since the anchor is not attached to its parent / the dom yet.
const ns =
is_svg || next_tag === 'svg'
? namespace_svg
: is_svg === false || anchor_node.parentElement?.tagName === 'foreignObject'
? null
: anchor_node.parentElement?.namespaceURI ?? null;
if (effect) {
if (next_tag === null) {
// start outro
pause_effect(effect, () => {
effect = null;
current_tag = null;
element?.remove(); // TODO this should be unnecessary
});
} else if (next_tag === current_tag) {
// same tag as is currently rendered — abort outro
resume_effect(effect);
} else {
// tag is changing — destroy immediately, render contents without intro transitions
destroy_effect(effect);
set_run_transitions(false);
}
},
block,
true
);
}
element_effect.ondestroy = () => {
if (next_tag && next_tag !== current_tag) {
effect = render_effect(
() => {
const prev_element = element;
element = hydrating
? /** @type {Element} */ (current_hydration_fragment[0])
: ns
? document.createElementNS(ns, next_tag)
: document.createElement(next_tag);
if (render_fn) {
let anchor;
if (hydrating) {
// Use the existing ssr comment as the anchor so that the inner open and close
// methods can pick up the existing nodes correctly
anchor = /** @type {Comment} */ (element.firstChild);
} else {
anchor = empty();
element.appendChild(anchor);
}
render_fn(element, anchor);
}
insert(element, null, anchor_node);
if (prev_element) {
swap_block_dom(block.p, prev_element, element);
prev_element.remove();
}
},
block,
true
);
}
tag = next_tag;
if (tag) current_tag = tag;
set_run_transitions(true);
}, block);
wrapper.ondestroy = () => {
if (element !== null) {
remove(element);
block.d = null;
element = null;
}
destroy_effect(render_effect_signal);
if (effect) {
destroy_effect(effect);
}
};
block.e = element_effect;
block.e = wrapper;
}

@ -22,6 +22,11 @@ export const root_event_handles = new Set();
export let run_transitions = true;
/** @param {boolean} value */
export function set_run_transitions(value) {
run_transitions = value;
}
/**
* @param {Element} dom
* @param {() => string} value

@ -36,7 +36,7 @@ class Animation {
#keyframes;
#duration;
#timeline_offset = 0; // TODO should this be `raf.time`?
#offset = raf.time;
#finished = () => {};
#cancelled = () => {};
@ -70,7 +70,7 @@ class Animation {
}
_update() {
this.currentTime = raf.time - this.#timeline_offset;
this.currentTime = raf.time - this.#offset;
const target_frame = this.currentTime / this.#duration;
this.#apply_keyframe(target_frame);

@ -15,8 +15,9 @@ export default test({
assert.equal(h1.style.opacity, '');
assert.equal(h2.style.opacity, '');
raf.tick(50);
raf.tick(200);
component.visible = false;
assert.equal(h2.style.opacity, '0.49998000000000004');
raf.tick(250);
assert.equal(h2.style.opacity, '0.5');
}
});

Loading…
Cancel
Save