Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Position nodes absolutely when outroing them, if animations are involved #1457

Merged
merged 4 commits into from
May 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions src/compile/nodes/EachBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,13 +314,20 @@ export default class EachBlock extends Node {

const dynamic = this.block.hasUpdateMethod;

const rects = block.getUniqueName('rects');
const destroy = this.block.animation
? `@fixAndOutroAndDestroyBlock`
: this.block.hasOutroMethod
? `@outroAndDestroyBlock`
: `@destroyBlock`;

block.builders.update.addBlock(deindent`
const ${this.each_block_value} = ${snippet};

${this.block.hasOutroMethod && `@transitionManager.groupOutros();`}
${this.block.animation && `const rects = @measure(${blocks});`}
${blocks} = @updateKeyedEach(${blocks}, #component, changed, ${get_key}, ${dynamic ? '1' : '0'}, ctx, ${this.each_block_value}, ${lookup}, ${updateMountNode}, ${String(this.block.hasOutroMethod)}, ${create_each_block}, "${mountOrIntro}", ${anchor}, ${this.get_each_context});
${this.block.animation && `@animate(${blocks}, rects, %animations-${this.children[0].animation.name}, {});`}
${this.block.animation && `const ${rects} = @measure(${blocks});`}
${blocks} = @updateKeyedEach(${blocks}, #component, changed, ${get_key}, ${dynamic ? '1' : '0'}, ctx, ${this.each_block_value}, ${lookup}, ${updateMountNode}, ${destroy}, ${create_each_block}, "${mountOrIntro}", ${anchor}, ${this.get_each_context});
${this.block.animation && `@animate(${blocks}, ${rects}, %animations-${this.children[0].animation.name}, {});`}
`);

if (this.compiler.options.nestedTransitions) {
Expand Down
27 changes: 24 additions & 3 deletions src/shared/keyed-each.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,30 @@ export function outroAndDestroyBlock(block, lookup) {
});
}

export function updateKeyedEach(old_blocks, component, changed, get_key, dynamic, ctx, list, lookup, node, has_outro, create_each_block, intro_method, next, get_context) {
export function fixAndOutroAndDestroyBlock(block, lookup) {
const { node } = block;
const style = getComputedStyle(node);

if (style.position !== 'absolute' && style.position !== 'fixed') {
const { width, height } = style;
const a = node.getBoundingClientRect();
node.style.position = 'absolute';
node.style.width = width;
node.style.height = height;
const b = node.getBoundingClientRect();

if (a.left !== b.left || a.top !== b.top) {
const style = getComputedStyle(node);
const transform = style.transform === 'none' ? '' : style.transform;

node.style.transform = `${transform} translate(${a.left - b.left}px, ${a.top - b.top}px)`;
}
}

outroAndDestroyBlock(block, lookup);
}

export function updateKeyedEach(old_blocks, component, changed, get_key, dynamic, ctx, list, lookup, node, destroy, create_each_block, intro_method, next, get_context) {
var o = old_blocks.length;
var n = list.length;

Expand Down Expand Up @@ -44,8 +67,6 @@ export function updateKeyedEach(old_blocks, component, changed, get_key, dynamic
var will_move = {};
var did_move = {};

var destroy = has_outro ? outroAndDestroyBlock : destroyBlock;

function insert(block) {
block[intro_method](node, next);
lookup[block.key] = block;
Expand Down
333 changes: 333 additions & 0 deletions test/js/samples/each-block-keyed/expected-bundle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,333 @@
function noop() {}

function assign(tar, src) {
for (var k in src) tar[k] = src[k];
return tar;
}

function appendNode(node, target) {
target.appendChild(node);
}

function insertNode(node, target, anchor) {
target.insertBefore(node, anchor);
}

function detachNode(node) {
node.parentNode.removeChild(node);
}

function createElement(name) {
return document.createElement(name);
}

function createText(data) {
return document.createTextNode(data);
}

function createComment() {
return document.createComment('');
}

function destroyBlock(block, lookup) {
block.d(1);
lookup[block.key] = null;
}

function updateKeyedEach(old_blocks, component, changed, get_key, dynamic, ctx, list, lookup, node, destroy, create_each_block, intro_method, next, get_context) {
var o = old_blocks.length;
var n = list.length;

var i = o;
var old_indexes = {};
while (i--) old_indexes[old_blocks[i].key] = i;

var new_blocks = [];
var new_lookup = {};
var deltas = {};

var i = n;
while (i--) {
var child_ctx = get_context(ctx, list, i);
var key = get_key(child_ctx);
var block = lookup[key];

if (!block) {
block = create_each_block(component, key, child_ctx);
block.c();
} else if (dynamic) {
block.p(changed, child_ctx);
}

new_blocks[i] = new_lookup[key] = block;

if (key in old_indexes) deltas[key] = Math.abs(i - old_indexes[key]);
}

var will_move = {};
var did_move = {};

function insert(block) {
block[intro_method](node, next);
lookup[block.key] = block;
next = block.first;
n--;
}

while (o && n) {
var new_block = new_blocks[n - 1];
var old_block = old_blocks[o - 1];
var new_key = new_block.key;
var old_key = old_block.key;

if (new_block === old_block) {
// do nothing
next = new_block.first;
o--;
n--;
}

else if (!new_lookup[old_key]) {
// remove old block
destroy(old_block, lookup);
o--;
}

else if (!lookup[new_key] || will_move[new_key]) {
insert(new_block);
}

else if (did_move[old_key]) {
o--;

} else if (deltas[new_key] > deltas[old_key]) {
did_move[new_key] = true;
insert(new_block);

} else {
will_move[old_key] = true;
o--;
}
}

while (o--) {
var old_block = old_blocks[o];
if (!new_lookup[old_block.key]) destroy(old_block, lookup);
}

while (n) insert(new_blocks[n - 1]);

return new_blocks;
}

function blankObject() {
return Object.create(null);
}

function destroy(detach) {
this.destroy = noop;
this.fire('destroy');
this.set = noop;

this._fragment.d(detach !== false);
this._fragment = null;
this._state = {};
}

function _differs(a, b) {
return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function');
}

function fire(eventName, data) {
var handlers =
eventName in this._handlers && this._handlers[eventName].slice();
if (!handlers) return;

for (var i = 0; i < handlers.length; i += 1) {
var handler = handlers[i];

if (!handler.__calling) {
handler.__calling = true;
handler.call(this, data);
handler.__calling = false;
}
}
}

function get() {
return this._state;
}

function init(component, options) {
component._handlers = blankObject();
component._bind = options._bind;

component.options = options;
component.root = options.root || component;
component.store = component.root.store || options.store;
}

function on(eventName, handler) {
var handlers = this._handlers[eventName] || (this._handlers[eventName] = []);
handlers.push(handler);

return {
cancel: function() {
var index = handlers.indexOf(handler);
if (~index) handlers.splice(index, 1);
}
};
}

function set(newState) {
this._set(assign({}, newState));
if (this.root._lock) return;
this.root._lock = true;
callAll(this.root._beforecreate);
callAll(this.root._oncreate);
callAll(this.root._aftercreate);
this.root._lock = false;
}

function _set(newState) {
var oldState = this._state,
changed = {},
dirty = false;

for (var key in newState) {
if (this._differs(newState[key], oldState[key])) changed[key] = dirty = true;
}
if (!dirty) return;

this._state = assign(assign({}, oldState), newState);
this._recompute(changed, this._state);
if (this._bind) this._bind(changed, this._state);

if (this._fragment) {
this.fire("state", { changed: changed, current: this._state, previous: oldState });
this._fragment.p(changed, this._state);
this.fire("update", { changed: changed, current: this._state, previous: oldState });
}
}

function callAll(fns) {
while (fns && fns.length) fns.shift()();
}

function _mount(target, anchor) {
this._fragment[this._fragment.i ? 'i' : 'm'](target, anchor || null);
}

var proto = {
destroy,
get,
fire,
on,
set,
_recompute: noop,
_set,
_mount,
_differs
};

/* generated by Svelte vX.Y.Z */

function create_main_fragment(component, ctx) {
var each_blocks_1 = [], each_lookup = blankObject(), each_anchor;

var each_value = ctx.things;

const get_key = ctx => ctx.thing.id;

for (var i = 0; i < each_value.length; i += 1) {
let child_ctx = get_each_context(ctx, each_value, i);
let key = get_key(child_ctx);
each_blocks_1[i] = each_lookup[key] = create_each_block(component, key, child_ctx);
}

return {
c() {
for (i = 0; i < each_blocks_1.length; i += 1) each_blocks_1[i].c();

each_anchor = createComment();
},

m(target, anchor) {
for (i = 0; i < each_blocks_1.length; i += 1) each_blocks_1[i].m(target, anchor);

insertNode(each_anchor, target, anchor);
},

p(changed, ctx) {
const each_value = ctx.things;
each_blocks_1 = updateKeyedEach(each_blocks_1, component, changed, get_key, 1, ctx, each_value, each_lookup, each_anchor.parentNode, destroyBlock, create_each_block, "m", each_anchor, get_each_context);
},

d(detach) {
for (i = 0; i < each_blocks_1.length; i += 1) each_blocks_1[i].d(detach);

if (detach) {
detachNode(each_anchor);
}
}
};
}

// (1:0) {#each things as thing (thing.id)}
function create_each_block(component, key_1, ctx) {
var div, text_value = ctx.thing.name, text;

return {
key: key_1,

first: null,

c() {
div = createElement("div");
text = createText(text_value);
this.first = div;
},

m(target, anchor) {
insertNode(div, target, anchor);
appendNode(text, div);
},

p(changed, ctx) {
if ((changed.things) && text_value !== (text_value = ctx.thing.name)) {
text.data = text_value;
}
},

d(detach) {
if (detach) {
detachNode(div);
}
}
};
}

function get_each_context(ctx, list, i) {
const child_ctx = Object.create(ctx);
child_ctx.thing = list[i];
child_ctx.each_value = list;
child_ctx.thing_index = i;
return child_ctx;
}

function SvelteComponent(options) {
init(this, options);
this._state = assign({}, options.data);
this._intro = true;

this._fragment = create_main_fragment(this, this._state);

if (options.target) {
this._fragment.c();
this._mount(options.target, options.anchor);
}
}

assign(SvelteComponent.prototype, proto);

export default SvelteComponent;
Loading