pull/1864/head
Rich Harris 7 years ago
parent 56eeead1b3
commit 4aa510d2d9

@ -20,13 +20,6 @@ export default class Animation extends Node {
}); });
} }
if (!component.animations.has(this.name)) {
component.error(this, {
code: `missing-animation`,
message: `Missing animation '${this.name}'`
});
}
const block = parent.parent; const block = parent.parent;
if (!block || block.type !== 'EachBlock' || !block.key) { if (!block || block.type !== 'EachBlock' || !block.key) {
// TODO can we relax the 'immediate child' rule? // TODO can we relax the 'immediate child' rule?

@ -381,7 +381,7 @@ export default class Block {
return deindent` return deindent`
${this.comment && `// ${escape(this.comment)}`} ${this.comment && `// ${escape(this.comment)}`}
function ${this.name}(#component${this.key ? `, ${localKey}` : ''}, ctx) { function ${this.name}(${this.key ? `${localKey}, ` : ''}ctx) {
${this.getContents(localKey)} ${this.getContents(localKey)}
} }
`.replace(/(#+)(\w*)/g, (match: string, sigil: string, name: string) => { `.replace(/(#+)(\w*)/g, (match: string, sigil: string, name: string) => {

@ -125,6 +125,17 @@ export default function dom(
__create_fragment(ctx) { __create_fragment(ctx) {
${block.getContents()} ${block.getContents()}
} }
${component.exports.map(x => deindent`
get ${x.as}() {
return this.__get_state().${x.name};
}
set ${x.as}(value) {
this.__set('${x.name}', value);
@flush();
}
`)}
} }
`); `);
} }

@ -209,7 +209,7 @@ export default class EachBlockWrapper extends Wrapper {
// TODO neaten this up... will end up with an empty line in the block // TODO neaten this up... will end up with an empty line in the block
block.builders.init.addBlock(deindent` block.builders.init.addBlock(deindent`
if (!${this.vars.each_block_value}.${length}) { if (!${this.vars.each_block_value}.${length}) {
${each_block_else} = ${this.else.block.name}(#component, ctx); ${each_block_else} = ${this.else.block.name}(ctx);
${each_block_else}.c(); ${each_block_else}.c();
} }
`); `);
@ -227,7 +227,7 @@ export default class EachBlockWrapper extends Wrapper {
if (!${this.vars.each_block_value}.${length} && ${each_block_else}) { if (!${this.vars.each_block_value}.${length} && ${each_block_else}) {
${each_block_else}.p(changed, ctx); ${each_block_else}.p(changed, ctx);
} else if (!${this.vars.each_block_value}.${length}) { } else if (!${this.vars.each_block_value}.${length}) {
${each_block_else} = ${this.else.block.name}(#component, ctx); ${each_block_else} = ${this.else.block.name}(ctx);
${each_block_else}.c(); ${each_block_else}.c();
${each_block_else}.${mountOrIntro}(${initialMountNode}, ${this.vars.anchor}); ${each_block_else}.${mountOrIntro}(${initialMountNode}, ${this.vars.anchor});
} else if (${each_block_else}) { } else if (${each_block_else}) {
@ -243,7 +243,7 @@ export default class EachBlockWrapper extends Wrapper {
${each_block_else} = null; ${each_block_else} = null;
} }
} else if (!${each_block_else}) { } else if (!${each_block_else}) {
${each_block_else} = ${this.else.block.name}(#component, ctx); ${each_block_else} = ${this.else.block.name}(ctx);
${each_block_else}.c(); ${each_block_else}.c();
${each_block_else}.${mountOrIntro}(${initialMountNode}, ${this.vars.anchor}); ${each_block_else}.${mountOrIntro}(${initialMountNode}, ${this.vars.anchor});
} }
@ -300,7 +300,7 @@ export default class EachBlockWrapper extends Wrapper {
for (var #i = 0; #i < ${this.vars.each_block_value}.${length}; #i += 1) { for (var #i = 0; #i < ${this.vars.each_block_value}.${length}; #i += 1) {
let child_ctx = ${this.vars.get_each_context}(ctx, ${this.vars.each_block_value}, #i); let child_ctx = ${this.vars.get_each_context}(ctx, ${this.vars.each_block_value}, #i);
let key = ${get_key}(child_ctx); let key = ${get_key}(child_ctx);
${blocks}[#i] = ${lookup}[key] = ${create_each_block}(#component, key, child_ctx); ${blocks}[#i] = ${lookup}[key] = ${create_each_block}(key, child_ctx);
} }
`); `);
@ -336,7 +336,7 @@ export default class EachBlockWrapper extends Wrapper {
${this.block.hasOutros && `@groupOutros();`} ${this.block.hasOutros && `@groupOutros();`}
${this.node.hasAnimation && `for (let #i = 0; #i < ${blocks}.length; #i += 1) ${blocks}[#i].r();`} ${this.node.hasAnimation && `for (let #i = 0; #i < ${blocks}.length; #i += 1) ${blocks}[#i].r();`}
${blocks} = @updateKeyedEach(${blocks}, #component, changed, ${get_key}, ${dynamic ? '1' : '0'}, ctx, ${this.vars.each_block_value}, ${lookup}, ${updateMountNode}, ${destroy}, ${create_each_block}, "${mountOrIntro}", ${anchor}, ${this.vars.get_each_context}); ${blocks} = @updateKeyedEach(${blocks}, changed, ${get_key}, ${dynamic ? '1' : '0'}, ctx, ${this.vars.each_block_value}, ${lookup}, ${updateMountNode}, ${destroy}, ${create_each_block}, "${mountOrIntro}", ${anchor}, ${this.vars.get_each_context});
${this.node.hasAnimation && `for (let #i = 0; #i < ${blocks}.length; #i += 1) ${blocks}[#i].a();`} ${this.node.hasAnimation && `for (let #i = 0; #i < ${blocks}.length; #i += 1) ${blocks}[#i].a();`}
`); `);
@ -371,7 +371,7 @@ export default class EachBlockWrapper extends Wrapper {
var ${iterations} = []; var ${iterations} = [];
for (var #i = 0; #i < ${this.vars.each_block_value}.${length}; #i += 1) { for (var #i = 0; #i < ${this.vars.each_block_value}.${length}; #i += 1) {
${iterations}[#i] = ${create_each_block}(#component, ${this.vars.get_each_context}(ctx, ${this.vars.each_block_value}, #i)); ${iterations}[#i] = ${create_each_block}(${this.vars.get_each_context}(ctx, ${this.vars.each_block_value}, #i));
} }
`); `);
@ -434,7 +434,7 @@ export default class EachBlockWrapper extends Wrapper {
if (${iterations}[#i]) { if (${iterations}[#i]) {
${iterations}[#i].p(changed, child_ctx); ${iterations}[#i].p(changed, child_ctx);
} else { } else {
${iterations}[#i] = ${create_each_block}(#component, child_ctx); ${iterations}[#i] = ${create_each_block}(child_ctx);
${iterations}[#i].c(); ${iterations}[#i].c();
} }
${iterations}[#i].i(${updateMountNode}, ${anchor}); ${iterations}[#i].i(${updateMountNode}, ${anchor});
@ -443,13 +443,13 @@ export default class EachBlockWrapper extends Wrapper {
if (${iterations}[#i]) { if (${iterations}[#i]) {
${iterations}[#i].p(changed, child_ctx); ${iterations}[#i].p(changed, child_ctx);
} else { } else {
${iterations}[#i] = ${create_each_block}(#component, child_ctx); ${iterations}[#i] = ${create_each_block}(child_ctx);
${iterations}[#i].c(); ${iterations}[#i].c();
${iterations}[#i].m(${updateMountNode}, ${anchor}); ${iterations}[#i].m(${updateMountNode}, ${anchor});
} }
` `
: deindent` : deindent`
${iterations}[#i] = ${create_each_block}(#component, child_ctx); ${iterations}[#i] = ${create_each_block}(child_ctx);
${iterations}[#i].c(); ${iterations}[#i].c();
${iterations}[#i].${mountOrIntro}(${updateMountNode}, ${anchor}); ${iterations}[#i].${mountOrIntro}(${updateMountNode}, ${anchor});
`; `;

@ -806,7 +806,7 @@ export default class ElementWrapper extends Wrapper {
const params = this.node.animation.expression ? this.node.animation.expression.snippet : '{}'; const params = this.node.animation.expression ? this.node.animation.expression.snippet : '{}';
block.builders.animate.addBlock(deindent` block.builders.animate.addBlock(deindent`
if (${animation}) ${animation}.stop(); if (${animation}) ${animation}.stop();
${animation} = @wrapAnimation(${this.var}, ${rect}, %animations-${this.node.animation.name}, ${params}); ${animation} = @wrapAnimation(${this.var}, ${rect}, ctx.${this.node.animation.name}, ${params});
`); `);
} }

@ -0,0 +1,102 @@
import { transitionManager, linear, generateRule, hash } from './transitions.js';
export function wrapAnimation(node, from, fn, params) {
if (!from) return;
const to = node.getBoundingClientRect();
if (from.left === to.left && from.right === to.right && from.top === to.top && from.bottom === to.bottom) return;
const info = fn(node, { from, to }, params);
const duration = 'duration' in info ? info.duration : 300;
const delay = 'delay' in info ? info.delay : 0;
const ease = info.easing || linear;
const start = window.performance.now() + delay;
const end = start + duration;
const program = {
a: 0,
t: 0,
b: 1,
delta: 1,
duration,
start,
end
};
const cssText = node.style.cssText;
const animation = {
pending: delay ? program : null,
program: delay ? null : program,
running: true,
start() {
if (info.css) {
if (delay) node.style.cssText = cssText;
const rule = generateRule(program, ease, info.css);
program.name = `__svelte_${hash(rule)}`;
transitionManager.addRule(rule, program.name);
node.style.animation = (node.style.animation || '')
.split(', ')
.filter(anim => anim && (program.delta < 0 || !/__svelte/.test(anim)))
.concat(`${program.name} ${program.duration}ms linear 1 forwards`)
.join(', ');
}
animation.program = program;
animation.pending = null;
},
update: now => {
const p = now - program.start;
const t = program.a + program.delta * ease(p / program.duration);
if (info.tick) info.tick(t, 1 - t);
},
done() {
if (info.tick) info.tick(1, 0);
animation.stop();
},
stop() {
if (info.css) transitionManager.deleteRule(node, program.name);
animation.running = false;
}
};
transitionManager.add(animation);
if (info.tick) info.tick(0, 1);
if (delay) {
if (info.css) node.style.cssText += info.css(0, 1);
} else {
animation.start();
}
return animation;
}
export function fixPosition(node) {
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)`;
}
}
}

@ -0,0 +1,60 @@
import { assign, isPromise } from './utils.js';
import { groupOutros } from './transitions.js';
export function handlePromise(promise, info) {
var token = info.token = {};
function update(type, index, key, value) {
if (info.token !== token) return;
info.resolved = key && { [key]: value };
const child_ctx = assign(assign({}, info.ctx), info.resolved);
const block = type && (info.current = type)(info.component, child_ctx);
if (info.block) {
if (info.blocks) {
info.blocks.forEach((block, i) => {
if (i !== index && block) {
groupOutros();
block.o(() => {
block.d(1);
info.blocks[i] = null;
});
}
});
} else {
info.block.d(1);
}
block.c();
block[block.i ? 'i' : 'm'](info.mount(), info.anchor);
info.component.root.set({}); // flush any handlers that were created
}
info.block = block;
if (info.blocks) info.blocks[index] = block;
}
if (isPromise(promise)) {
promise.then(value => {
update(info.then, 1, info.value, value);
}, error => {
update(info.catch, 2, info.error, error);
});
// if we previously had a then/catch block, destroy it
if (info.current !== info.pending) {
update(info.pending, 0);
return true;
}
} else {
if (info.current !== info.then) {
update(info.then, 1, info.value, promise);
return true;
}
info.resolved = { [info.value]: promise };
}
}

@ -1,5 +1,10 @@
export * from './animations.js';
export * from './await-block.js';
export * from './dom.js'; export * from './dom.js';
export * from './keyed-each.js';
export * from './scheduler.js'; export * from './scheduler.js';
export * from './spread.js';
export * from './ssr.js';
export * from './transitions.js'; export * from './transitions.js';
export * from './utils.js'; export * from './utils.js';
export * from './SvelteComponent.js'; export * from './SvelteComponent.js';

@ -0,0 +1,123 @@
export function destroyBlock(block, lookup) {
block.d(1);
lookup[block.key] = null;
}
export function outroAndDestroyBlock(block, lookup) {
block.o(function() {
destroyBlock(block, lookup);
});
}
export function fixAndOutroAndDestroyBlock(block, lookup) {
block.f();
outroAndDestroyBlock(block, lookup);
}
export function updateKeyedEach(old_blocks, 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(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;
}
export function measure(blocks) {
const rects = {};
let i = blocks.length;
while (i--) rects[blocks[i].key] = blocks[i].node.getBoundingClientRect();
return rects;
}
export function animate(blocks, rects, fn, params) {
let i = blocks.length;
while (i--) {
const block = blocks[i];
const from = rects[block.key];
if (!from) continue;
const to = block.node.getBoundingClientRect();
if (from.left === to.left && from.right === to.right && from.top === to.top && from.bottom === to.bottom) continue;
}
}

@ -0,0 +1,37 @@
export function getSpreadUpdate(levels, updates) {
var update = {};
var to_null_out = {};
var accounted_for = {};
var i = levels.length;
while (i--) {
var o = levels[i];
var n = updates[i];
if (n) {
for (var key in o) {
if (!(key in n)) to_null_out[key] = 1;
}
for (var key in n) {
if (!accounted_for[key]) {
update[key] = n[key];
accounted_for[key] = 1;
}
}
levels[i] = n;
} else {
for (var key in o) {
accounted_for[key] = 1;
}
}
}
for (var key in to_null_out) {
if (!(key in update)) update[key] = undefined;
}
return update;
}

@ -0,0 +1,63 @@
// https://html.spec.whatwg.org/multipage/syntax.html#attributes-2
// https://infra.spec.whatwg.org/#noncharacter
export const invalidAttributeNameCharacter = /[\s'">\/=\u{FDD0}-\u{FDEF}\u{FFFE}\u{FFFF}\u{1FFFE}\u{1FFFF}\u{2FFFE}\u{2FFFF}\u{3FFFE}\u{3FFFF}\u{4FFFE}\u{4FFFF}\u{5FFFE}\u{5FFFF}\u{6FFFE}\u{6FFFF}\u{7FFFE}\u{7FFFF}\u{8FFFE}\u{8FFFF}\u{9FFFE}\u{9FFFF}\u{AFFFE}\u{AFFFF}\u{BFFFE}\u{BFFFF}\u{CFFFE}\u{CFFFF}\u{DFFFE}\u{DFFFF}\u{EFFFE}\u{EFFFF}\u{FFFFE}\u{FFFFF}\u{10FFFE}\u{10FFFF}]/u;
export function spread(args) {
const attributes = Object.assign({}, ...args);
let str = '';
Object.keys(attributes).forEach(name => {
if (invalidAttributeNameCharacter.test(name)) return;
const value = attributes[name];
if (value === undefined) return;
if (value === true) str += " " + name;
const escaped = String(value)
.replace(/"/g, '&#34;')
.replace(/'/g, '&#39;');
str += " " + name + "=" + JSON.stringify(escaped);
});
return str;
}
export const escaped = {
'"': '&quot;',
"'": '&#39;',
'&': '&amp;',
'<': '&lt;',
'>': '&gt;'
};
export function escape(html) {
return String(html).replace(/["'&<>]/g, match => escaped[match]);
}
export function each(items, assign, fn) {
let str = '';
for (let i = 0; i < items.length; i += 1) {
str += fn(assign(items[i], i));
}
return str;
}
export const missingComponent = {
_render: () => ''
};
export function validateSsrComponent(component, name) {
if (!component || !component._render) {
if (name === 'svelte:component') name += ' this={...}';
throw new Error(`<${name}> is not a valid SSR component. You may need to review your build config to ensure that dependencies are compiled, rather than imported as pre-compiled modules`);
}
return component;
}
export function debug(file, line, column, values) {
console.log(`{@debug} ${file ? file + ' ' : ''}(${line}:${column})`);
console.log(values);
return '';
}

@ -36,3 +36,7 @@ export function exclude(src, prop) {
export function run(fn) { export function run(fn) {
fn(); fn();
} }
export function blankObject() {
return Object.create(null);
}

@ -414,6 +414,7 @@ function readAttribute(parser: Parser, uniqueNames: Set<string>) {
function get_directive_type(name) { function get_directive_type(name) {
if (name === 'use') return 'Action'; if (name === 'use') return 'Action';
if (name === 'on') return 'EventHandler'; if (name === 'on') return 'EventHandler';
if (name === 'animate') return 'Animation';
throw new Error(`TODO directive ${name}`); throw new Error(`TODO directive ${name}`);
} }

@ -71,7 +71,6 @@ describe.only("runtime", () => {
compileOptions = config.compileOptions || {}; compileOptions = config.compileOptions || {};
compileOptions.shared = internal; compileOptions.shared = internal;
compileOptions.hydratable = hydrate; compileOptions.hydratable = hydrate;
compileOptions.store = !!config.store;
compileOptions.immutable = config.immutable; compileOptions.immutable = config.immutable;
compileOptions.skipIntroByDefault = config.skipIntroByDefault; compileOptions.skipIntroByDefault = config.skipIntroByDefault;
compileOptions.nestedTransitions = config.nestedTransitions; compileOptions.nestedTransitions = config.nestedTransitions;
@ -116,7 +115,7 @@ describe.only("runtime", () => {
try { try {
SvelteComponent = require(`./samples/${dir}/main.html`); SvelteComponent = require(`./samples/${dir}/main.html`);
} catch (err) { } catch (err) {
showOutput(cwd, { internal, format: 'cjs', hydratable: hydrate, store: !!compileOptions.store, skipIntroByDefault: compileOptions.skipIntroByDefault, nestedTransitions: compileOptions.nestedTransitions }, compile); // eslint-disable-line no-console showOutput(cwd, { internal, format: 'cjs', hydratable: hydrate, skipIntroByDefault: compileOptions.skipIntroByDefault, nestedTransitions: compileOptions.nestedTransitions }, compile); // eslint-disable-line no-console
throw err; throw err;
} }

@ -2,7 +2,6 @@
export let text = 'Perform an Action'; export let text = 'Perform an Action';
function checkForCtrl(event) { function checkForCtrl(event) {
console.log(`!!! ${event.ctrlKey}`);
if (event.ctrlKey) { if (event.ctrlKey) {
text = 'Perform an augmented Action'; text = 'Perform an augmented Action';
} else { } else {

@ -1,5 +1,5 @@
export default { export default {
data: { props: {
things: [ things: [
{ id: 1, name: 'a' }, { id: 1, name: 'a' },
{ id: 2, name: 'b' }, { id: 2, name: 'b' },
@ -33,15 +33,13 @@ export default {
}; };
}) })
component.set({ component.things = [
things: [
{ id: 5, name: 'e' }, { id: 5, name: 'e' },
{ id: 2, name: 'b' }, { id: 2, name: 'b' },
{ id: 3, name: 'c' }, { id: 3, name: 'c' },
{ id: 4, name: 'd' }, { id: 4, name: 'd' },
{ id: 1, name: 'a' } { id: 1, name: 'a' }
] ];
});
divs = document.querySelectorAll('div'); divs = document.querySelectorAll('div');
assert.ok(~divs[0].style.animation.indexOf('__svelte')); assert.ok(~divs[0].style.animation.indexOf('__svelte'));

@ -1,5 +1,5 @@
export default { export default {
data: { props: {
things: [ things: [
{ id: 1, name: 'a' }, { id: 1, name: 'a' },
{ id: 2, name: 'b' }, { id: 2, name: 'b' },
@ -33,15 +33,13 @@ export default {
}; };
}) })
component.set({ component.things = [
things: [
{ id: 5, name: 'e' }, { id: 5, name: 'e' },
{ id: 2, name: 'b' }, { id: 2, name: 'b' },
{ id: 3, name: 'c' }, { id: 3, name: 'c' },
{ id: 4, name: 'd' }, { id: 4, name: 'd' },
{ id: 1, name: 'a' } { id: 1, name: 'a' }
] ];
});
divs = document.querySelectorAll('div'); divs = document.querySelectorAll('div');
assert.equal(divs[0].dy, 120); assert.equal(divs[0].dy, 120);

@ -17,5 +17,5 @@
</script> </script>
{#each things as thing, i (thing.id)} {#each things as thing, i (thing.id)}
<div animate:flip="{delay: i * 10}">{thing.name}</div> <div animate:flip="{{delay: i * 10}}">{thing.name}</div>
{/each} {/each}

@ -1,5 +1,5 @@
export default { export default {
data: { props: {
things: [ things: [
{ id: 1, name: 'a' }, { id: 1, name: 'a' },
{ id: 2, name: 'b' }, { id: 2, name: 'b' },
@ -33,15 +33,13 @@ export default {
}; };
}) })
component.set({ component.things = [
things: [
{ id: 5, name: 'e' }, { id: 5, name: 'e' },
{ id: 2, name: 'b' }, { id: 2, name: 'b' },
{ id: 3, name: 'c' }, { id: 3, name: 'c' },
{ id: 4, name: 'd' }, { id: 4, name: 'd' },
{ id: 1, name: 'a' } { id: 1, name: 'a' }
] ];
});
divs = document.querySelectorAll('div'); divs = document.querySelectorAll('div');
assert.equal(divs[0].dy, 120); assert.equal(divs[0].dy, 120);

@ -3,7 +3,7 @@ export default {
// so it can't be server-rendered // so it can't be server-rendered
'skip-ssr': true, 'skip-ssr': true,
data: { props: {
indeterminate: true indeterminate: true
}, },
@ -15,7 +15,7 @@ export default {
const input = target.querySelector('input'); const input = target.querySelector('input');
assert.ok(input.indeterminate); assert.ok(input.indeterminate);
component.set({ indeterminate: false }); component.indeterminate = false;
assert.ok(!input.indeterminate); assert.ok(!input.indeterminate);
} }
}; };
Loading…
Cancel
Save