almost everything working

pull/10798/head
Rich Harris 2 years ago
parent fa346378b2
commit 628df85977

@ -28,6 +28,7 @@ import {
AttributeAliases,
DOMBooleanAttributes,
EACH_INDEX_REACTIVE,
EACH_IS_ANIMATED,
EACH_IS_CONTROLLED,
EACH_IS_STRICT_EQUALS,
EACH_ITEM_REACTIVE,
@ -2337,6 +2338,16 @@ export const template_visitors = {
each_type |= EACH_ITEM_REACTIVE;
}
if (
node.key &&
node.body.nodes.some((child) => {
if (child.type !== 'RegularElement' && child.type !== 'SvelteElement') return false;
return child.attributes.some((attr) => attr.type === 'AnimateDirective');
})
) {
each_type |= EACH_IS_ANIMATED;
}
if (each_node_meta.is_controlled) {
each_type |= EACH_IS_CONTROLLED;
}

@ -15,12 +15,13 @@ import {
} from '../hydration.js';
import { empty } from '../operations.js';
import { insert, remove } from '../reconciler.js';
import { current_block } from '../../runtime.js';
import { current_block, untrack } from '../../runtime.js';
import {
destroy_effect,
pause_effect,
render_effect,
resume_effect
resume_effect,
user_effect
} from '../../reactivity/effects.js';
import { source, mutable_source, set } from '../../reactivity/sources.js';
import { is_array, is_frozen, map_get, map_set } from '../../utils.js';
@ -29,6 +30,14 @@ import { STATE_SYMBOL } from '../../constants.js';
var NEW_BLOCK = -1;
var LIS_BLOCK = -2;
/** @type {import('#client').EachItemBlock | null} */
export let current_each_item_block = null;
/** @param {import('#client').EachItemBlock | null} block */
export function set_current_each_item_block(block) {
current_each_item_block = block;
}
/**
* @template V
* @param {Element | Comment} anchor The next sibling node, or the parent node if this is a 'controlled' block
@ -316,13 +325,23 @@ function reconcile_tracked_array(array, each_block, anchor, render_fn, flags, ke
var b_blocks = Array(b);
var is_animated = (flags & EACH_IS_ANIMATED) !== 0;
var should_update = is_animated || (flags & (EACH_ITEM_REACTIVE | EACH_INDEX_REACTIVE)) !== 0;
var should_update = (flags & (EACH_ITEM_REACTIVE | EACH_INDEX_REACTIVE)) !== 0;
var start = 0;
var block;
/** @type {Array<import('#client').EachItemBlock>} */
var to_destroy = [];
/** @type {Array<import('#client').EachItemBlock>} */
var to_animate = [];
if (is_animated) {
for (block of a_blocks) {
// TODO can we avoid measuring blocks that will be destroyed?
block.a?.measure();
}
}
// Step 1 — trim common suffix
while (a > 0 && b > 0 && a_blocks[a - 1].k === keys[b - 1]) {
block = b_blocks[--b] = a_blocks[--a];
@ -392,8 +411,7 @@ function reconcile_tracked_array(array, each_block, anchor, render_fn, flags, ke
b_blocks[index] = block;
if (is_animated) {
// If keys are animated, we need to do updates before actual moves
update_block(block, array[index], index, flags);
to_animate.push(block);
}
}
}
@ -424,6 +442,16 @@ function reconcile_tracked_array(array, each_block, anchor, render_fn, flags, ke
}
}
if (to_animate.length > 0) {
user_effect(() => {
untrack(() => {
for (block of to_animate) {
block.a?.apply();
}
});
});
}
var remaining = to_destroy.length;
if (remaining > 0) {
var clear = () => {
@ -602,6 +630,7 @@ function create_block(item, key, index, render_fn, flags) {
/** @type {import('#client').EachItemBlock} */
var block = {
a: null,
// dom
d: null,
// effect
@ -615,13 +644,21 @@ function create_block(item, key, index, render_fn, flags) {
v: item_value
};
block.e = render_effect(
() => {
render_fn(null, block.v, block.i);
},
block,
true
);
var previous_each_item_block = current_each_item_block;
try {
current_each_item_block = block;
return block;
block.e = render_effect(
() => {
render_fn(null, block.v, block.i);
},
block,
true
);
return block;
} finally {
current_each_item_block = previous_each_item_block;
}
}

@ -11,6 +11,7 @@ import { remove } from '../reconciler.js';
import { current_block } from '../../runtime.js';
import { is_array } from '../../utils.js';
import { set_run_transitions } from '../../render.js';
import { current_each_item_block, set_current_each_item_block } from './each.js';
/**
* @param {import('#client').Block} block
@ -64,10 +65,15 @@ export function element(anchor_node, tag_fn, is_svg, render_fn) {
/** @type {import('#client').Effect | null} */
let effect;
let each_item_block = current_each_item_block;
const wrapper = render_effect(() => {
const next_tag = tag_fn() || null;
if (next_tag === tag) return;
var previous_each_item_block = current_each_item_block;
set_current_each_item_block(each_item_block);
// 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.
@ -134,6 +140,8 @@ export function element(anchor_node, tag_fn, is_svg, render_fn) {
tag = next_tag;
if (tag) current_tag = tag;
set_run_transitions(true);
set_current_each_item_block(previous_each_item_block);
}, block);
wrapper.ondestroy = () => {

@ -1,11 +1,12 @@
import { noop } from '../../../common.js';
import { user_effect } from '../../reactivity/effects.js';
import { current_effect, untrack } from '../../runtime.js';
import { current_block, current_effect, untrack } from '../../runtime.js';
import { raf } from '../../timing.js';
import { loop } from '../../loop.js';
import { run_transitions } from '../../render.js';
import { TRANSITION_GLOBAL, TRANSITION_IN, TRANSITION_OUT } from '../../constants.js';
import { is_function } from '../../utils.js';
import { current_each_item_block } from '../blocks/each.js';
/**
* @template T
@ -66,8 +67,42 @@ function css_to_keyframe(css) {
/** @param {number} t */
const linear = (t) => t;
export function animation() {
// TODO
/**
* @template P
* @param {Element} element
* @param {() => import('#client').AnimateFn<P | undefined>} get_fn
* @param {(() => P) | null} get_params
*/
export function animation(element, get_fn, get_params) {
/** @type {DOMRect} */
let from;
/** @type {DOMRect} */
let to;
/** @type {import('#client').Animation | undefined} */
let animation;
/** @type {import('#client').AnimationManager} */
const manager = {
measure() {
from = element.getBoundingClientRect();
},
apply() {
to = element.getBoundingClientRect();
const options = get_fn()(element, { from, to }, get_params?.(), {}); // TODO what is the last argument?
animation?.abort();
// TODO bail if `from` and `to` match
animation = animate(element, options, undefined, 1, () => {
animation = undefined;
});
}
};
/** @type {import('#client').EachItemBlock} */ (current_each_item_block).a = manager;
}
/**
@ -195,7 +230,7 @@ function animate(element, options, counterpart, t2, callback) {
};
}
dispatch_event(element, t2 === 1 ? 'introstart' : 'outrostart');
dispatch_event(element, t2 === 1 ? 'introstart' : 'outrostart'); // TODO not for `animate:`
var { delay = 0, duration, css, tick, easing = linear } = options;

@ -123,6 +123,8 @@ export type EachBlock = {
};
export type EachItemBlock = {
/** animation manager */
a: AnimationManager | null;
/** dom */
d: null | TemplateNode | Array<TemplateNode>;
/** effect */
@ -162,6 +164,11 @@ export interface TransitionManager {
stop: () => void;
}
export interface AnimationManager {
measure: () => void;
apply: () => void;
}
export interface Animation {
abort: () => void;
neuter: () => void;

@ -23,7 +23,6 @@ export default test({
`,
test({ assert, component, target, raf }) {
raf.tick(0);
component.tag = 'p';
assert.equal(target.querySelectorAll('p').length, 5);
@ -52,11 +51,11 @@ export default test({
];
divs = target.querySelectorAll('div');
assert.ok(~divs[0].style.transform);
assert.equal(divs[1].style.transform, 'translate(1px, 0px)');
assert.equal(divs[2].style.transform, 'translate(1px, 0px)');
assert.equal(divs[3].style.transform, 'translate(1px, 0px)');
assert.ok(~divs[4].style.transform);
assert.equal(divs[0].style.transform, 'translate(0px, 120px)');
assert.equal(divs[1].style.transform, 'translate(0px, 0px)');
assert.equal(divs[2].style.transform, 'translate(0px, 0px)');
assert.equal(divs[3].style.transform, 'translate(0px, 0px)');
assert.equal(divs[4].style.transform, 'translate(0px, -120px)');
raf.tick(100);
assert.deepEqual([divs[0].style.transform, divs[4].style.transform], ['', '']);

@ -8,11 +8,11 @@
return {
duration: 100,
css: (t, u) => `transform: translate(${u + dx}px, ${u * dy}px)`
css: (t, u) => `transform: translate(${u * dx}px, ${u * dy}px)`
};
}
</script>
{#each things as thing (thing.id)}
<svelte:element this={tag} animate:flip>{thing.name}</svelte:element>
{/each}
{/each}

Loading…
Cancel
Save