saving progress...

pull/4742/head
pushkine 6 years ago
parent 654bcfda7e
commit a26a75eba6

@ -1,9 +1,9 @@
module.exports = { module.exports = {
root: true, root: true,
rules: { rules: {
indent: 'off', 'indent': 'off',
'no-unused-vars': 'off', 'no-unused-vars': 'off',
semi: [2, 'always'], 'semi': [2, 'always'],
'keyword-spacing': [2, { before: true, after: true }], 'keyword-spacing': [2, { before: true, after: true }],
'space-before-blocks': [2, 'always'], 'space-before-blocks': [2, 'always'],
'no-mixed-spaces-and-tabs': [2, 'smart-tabs'], 'no-mixed-spaces-and-tabs': [2, 'smart-tabs'],
@ -16,7 +16,6 @@ module.exports = {
'no-unreachable': 2, 'no-unreachable': 2,
'valid-typeof': 2, 'valid-typeof': 2,
'quote-props': [2, 'as-needed'], 'quote-props': [2, 'as-needed'],
'one-var': [2, 'never'],
'prefer-arrow-callback': 2, 'prefer-arrow-callback': 2,
'prefer-const': [2, { destructuring: 'all' }], 'prefer-const': [2, { destructuring: 'all' }],
'arrow-spacing': 2, 'arrow-spacing': 2,
@ -32,57 +31,51 @@ module.exports = {
'@typescript-eslint/no-unused-vars': [ '@typescript-eslint/no-unused-vars': [
'error', 'error',
{ {
argsIgnorePattern: '^_' argsIgnorePattern: '^_',
} },
], ],
'@typescript-eslint/no-object-literal-type-assertion': 'off', '@typescript-eslint/no-object-literal-type-assertion': 'off',
'@typescript-eslint/no-unused-vars': 'off', '@typescript-eslint/no-unused-vars': 'off',
'@typescript-eslint/prefer-interface': 'off' '@typescript-eslint/prefer-interface': 'off',
}, },
globals: { globals: {
globalThis: false globalThis: false,
}, },
env: { env: {
es6: true, es6: true,
browser: true, browser: true,
node: true, node: true,
mocha: true mocha: true,
}, },
extends: [ extends: [
'eslint:recommended', 'eslint:recommended',
'plugin:import/errors', 'plugin:import/errors',
'plugin:import/warnings', 'plugin:import/warnings',
'plugin:import/typescript', 'plugin:import/typescript',
'plugin:@typescript-eslint/recommended' 'plugin:@typescript-eslint/recommended',
], ],
parserOptions: { parserOptions: {
ecmaVersion: 9, ecmaVersion: 9,
sourceType: 'module' sourceType: 'module',
}, },
plugins: ['svelte3'], plugins: ['svelte3'],
settings: { settings: {
'import/core-modules': [ 'import/core-modules': ['svelte', 'svelte/internal', 'svelte/store', 'svelte/easing', 'estree'],
'svelte', 'svelte3/compiler': require('./compiler'),
'svelte/internal',
'svelte/store',
'svelte/easing',
'estree'
],
'svelte3/compiler': require('./compiler')
}, },
overrides: [ overrides: [
{ {
files: ['*.js'], files: ['*.js'],
rules: { rules: {
'@typescript-eslint/no-var-requires': 'off' '@typescript-eslint/no-var-requires': 'off',
} },
}, },
{ {
files: ['*.svelte'], files: ['*.svelte'],
processor: 'svelte3/svelte3', processor: 'svelte3/svelte3',
rules: { rules: {
'@typescript-eslint/indent': 'off' '@typescript-eslint/indent': 'off',
} },
} },
] ],
}; };

@ -7,106 +7,95 @@ import sucrase from '@rollup/plugin-sucrase';
import typescript from '@rollup/plugin-typescript'; import typescript from '@rollup/plugin-typescript';
import pkg from './package.json'; import pkg from './package.json';
const esm = { format: 'esm' };
const cjs = { format: 'cjs' };
const is_publish = !!process.env.PUBLISH; const is_publish = !!process.env.PUBLISH;
const ts_plugin = is_publish const ts_plugin = is_publish
? typescript({ ? typescript({ include: 'src/**', typescript: require('typescript') })
include: 'src/**', : sucrase({ transforms: ['typescript'] });
typescript: require('typescript')
})
: sucrase({
transforms: ['typescript']
});
const external = id => id.startsWith('svelte/'); const external = (id) => id.startsWith('svelte/');
const version = replace({ __VERSION__: pkg.version });
fs.writeFileSync(`./compiler.d.ts`, `export { compile, parse, preprocess, VERSION } from './types/compiler/index';`); fs.writeFileSync(`./compiler.d.ts`, `export { compile, parse, preprocess, VERSION } from './types/compiler/index';`);
const output = (obj) =>
Object.keys(obj).flatMap((name) =>
[esm, cjs].map(({ format }) => ({
format,
file: `${name !== 'default' ? name + '/' : ''}${obj[name].file}.${format === 'esm' ? 'mjs' : 'js'}`,
paths: (id) => external(id) && `${id.replace('svelte', obj[name].path)}`,
plugins: (defaults.plugins || []).concat([replace({ __DEV__: name === 'dev' })]),
}))
);
function writeFileSync(...arr) {
arr.filter(Boolean).forEach(({ dir, content }) => fs.writeFileSync(dir, content));
}
export default [ export default [
/* runtime */ /* runtime main */
{ {
input: `src/runtime/index.ts`, input: `src/runtime/index.ts`,
output: [ output: output({
{ default: { file: 'index', path: '.' },
file: `index.mjs`, dev: { file: 'index', path: '.' },
format: 'esm', }),
paths: id => id.startsWith('svelte/') && `${id.replace('svelte', '.')}`
},
{
file: `index.js`,
format: 'cjs',
paths: id => id.startsWith('svelte/') && `${id.replace('svelte', '.')}`
}
],
external, external,
plugins: [ts_plugin] plugins: [ts_plugin],
}, },
/* svelte/[library] */
...fs.readdirSync('src/runtime') ...fs
.filter(dir => fs.statSync(`src/runtime/${dir}`).isDirectory()) .readdirSync('src/runtime')
.map(dir => ({ .filter((dir) => fs.statSync(`src/runtime/${dir}`).isDirectory())
.map((dir) => ({
input: `src/runtime/${dir}/index.ts`, input: `src/runtime/${dir}/index.ts`,
output: [ output: output({
{ [dir]: { file: 'index', path: '..' },
file: `${dir}/index.mjs`, dev: { file: dir, path: '.' },
format: 'esm', }),
paths: id => id.startsWith('svelte/') && `${id.replace('svelte', '..')}`
},
{
file: `${dir}/index.js`,
format: 'cjs',
paths: id => id.startsWith('svelte/') && `${id.replace('svelte', '..')}`
}
],
external, external,
plugins: [ plugins: [
replace({ version,
__VERSION__: pkg.version
}),
ts_plugin, ts_plugin,
{ {
writeBundle(bundle) { writeBundle(bundle) {
if (dir === 'internal') { writeFileSync(
const mod = bundle['index.mjs']; dir === 'internal' &&
if (mod) { bundle['index.mjs'] && {
fs.writeFileSync('src/compiler/compile/internal_exports.ts', `// This file is automatically generated\nexport default new Set(${JSON.stringify(mod.exports)});`); dir: `src/compiler/compile/internal_exports.ts`,
} content: `
} // This file is automatically generated
export default new Set(${JSON.stringify(bundle['index.mjs'].exports)});`,
fs.writeFileSync(`${dir}/package.json`, JSON.stringify({ },
main: './index', {
module: './index.mjs', dir: `${dir}/package.json`,
types: './index.d.ts' content: `{
}, null, ' ')); "main": "./index",
"module": "./index.mjs",
fs.writeFileSync(`${dir}/index.d.ts`, `export * from '../types/runtime/${dir}/index';`); "types": "./index.d.ts"
} }`,
},
{
dir: `${dir}/index.d.ts`,
content: `export * from '../types/runtime/${dir}/index';`,
} }
] );
},
},
],
})), })),
/* compiler.js */ /* compiler.js */
{ {
input: 'src/compiler/index.ts', input: 'src/compiler/index.ts',
plugins: [ plugins: [version, resolve(), commonjs({ include: ['node_modules/**'] }), json(), ts_plugin],
replace({
__VERSION__: pkg.version
}),
resolve(),
commonjs({
include: ['node_modules/**']
}),
json(),
ts_plugin
],
output: { output: {
file: 'compiler.js', file: 'compiler.js',
format: is_publish ? 'umd' : 'cjs', format: is_publish ? 'umd' : 'cjs',
name: 'svelte', name: 'svelte',
sourcemap: true, sourcemap: true,
}, },
external: is_publish external: is_publish ? [] : (id) => id === 'acorn' || id === 'magic-string' || id.startsWith('css-tree'),
? [] },
: id => id === 'acorn' || id === 'magic-string' || id.startsWith('css-tree')
}
]; ];

@ -41,11 +41,11 @@ export default class InlineComponentWrapper extends Wrapper {
block.add_dependencies(this.node.expression.dependencies); block.add_dependencies(this.node.expression.dependencies);
} }
this.node.attributes.forEach(attr => { this.node.attributes.forEach((attr) => {
block.add_dependencies(attr.dependencies); block.add_dependencies(attr.dependencies);
}); });
this.node.bindings.forEach(binding => { this.node.bindings.forEach((binding) => {
if (binding.is_contextual) { if (binding.is_contextual) {
// we need to ensure that the each block creates a context including // we need to ensure that the each block creates a context including
// the list and the index, if they're not otherwise referenced // the list and the index, if they're not otherwise referenced
@ -58,7 +58,7 @@ export default class InlineComponentWrapper extends Wrapper {
block.add_dependencies(binding.expression.dependencies); block.add_dependencies(binding.expression.dependencies);
}); });
this.node.handlers.forEach(handler => { this.node.handlers.forEach((handler) => {
if (handler.expression) { if (handler.expression) {
block.add_dependencies(handler.expression.dependencies); block.add_dependencies(handler.expression.dependencies);
} }
@ -66,16 +66,17 @@ export default class InlineComponentWrapper extends Wrapper {
this.var = { this.var = {
type: 'Identifier', type: 'Identifier',
name: ( name: (this.node.name === 'svelte:self'
this.node.name === 'svelte:self' ? renderer.component.name.name : ? renderer.component.name.name
this.node.name === 'svelte:component' ? 'switch_instance' : : this.node.name === 'svelte:component'
sanitize(this.node.name) ? 'switch_instance'
).toLowerCase() : sanitize(this.node.name)
).toLowerCase(),
}; };
if (this.node.children.length) { if (this.node.children.length) {
this.node.lets.forEach(l => { this.node.lets.forEach((l) => {
extract_names(l.value || l.name).forEach(name => { extract_names(l.value || l.name).forEach((name) => {
renderer.add_to_context(name, true); renderer.add_to_context(name, true);
}); });
}); });
@ -83,7 +84,7 @@ export default class InlineComponentWrapper extends Wrapper {
const default_slot = block.child({ const default_slot = block.child({
comment: create_debugging_comment(node, renderer.component), comment: create_debugging_comment(node, renderer.component),
name: renderer.component.get_unique_name(`create_default_slot`), name: renderer.component.get_unique_name(`create_default_slot`),
type: 'slot' type: 'slot',
}); });
this.renderer.blocks.push(default_slot); this.renderer.blocks.push(default_slot);
@ -94,7 +95,7 @@ export default class InlineComponentWrapper extends Wrapper {
const dependencies: Set<string> = new Set(); const dependencies: Set<string> = new Set();
// TODO is this filtering necessary? (I *think* so) // TODO is this filtering necessary? (I *think* so)
default_slot.dependencies.forEach(name => { default_slot.dependencies.forEach((name) => {
if (!this.node.scope.is_let(name)) { if (!this.node.scope.is_let(name)) {
dependencies.add(name); dependencies.add(name);
} }
@ -121,11 +122,7 @@ export default class InlineComponentWrapper extends Wrapper {
} }
} }
render( render(block: Block, parent_node: Identifier, parent_nodes: Identifier) {
block: Block,
parent_node: Identifier,
parent_nodes: Identifier
) {
this.warn_if_reactive(); this.warn_if_reactive();
const { renderer } = this; const { renderer } = this;
@ -143,14 +140,14 @@ export default class InlineComponentWrapper extends Wrapper {
const default_slot = this.slots.get('default'); const default_slot = this.slots.get('default');
this.fragment.nodes.forEach((child) => { this.fragment.nodes.forEach((child) => {
child.render(default_slot.block, null, x`#nodes` as unknown as Identifier); child.render(default_slot.block, null, (x`#nodes` as unknown) as Identifier);
}); });
} }
let props; let props;
const name_changes = block.get_unique_name(`${name.name}_changes`); const name_changes = block.get_unique_name(`${name.name}_changes`);
const uses_spread = !!this.node.attributes.find(a => a.is_spread); const uses_spread = !!this.node.attributes.find((a) => a.is_spread);
// removing empty slot // removing empty slot
for (const slot of this.slots.keys()) { for (const slot of this.slots.keys()) {
@ -160,7 +157,8 @@ export default class InlineComponentWrapper extends Wrapper {
} }
} }
const initial_props = this.slots.size > 0 const initial_props =
this.slots.size > 0
? [ ? [
p`$$slots: { p`$$slots: {
${Array.from(this.slots).map(([name, slot]) => { ${Array.from(this.slots).map(([name, slot]) => {
@ -169,14 +167,14 @@ export default class InlineComponentWrapper extends Wrapper {
}`, }`,
p`$$scope: { p`$$scope: {
ctx: #ctx ctx: #ctx
}` }`,
] ]
: []; : [];
const attribute_object = uses_spread const attribute_object = uses_spread
? x`{ ${initial_props} }` ? x`{ ${initial_props} }`
: x`{ : x`{
${this.node.attributes.map(attr => p`${attr.name}: ${attr.get_value(block)}`)}, ${this.node.attributes.map((attr) => p`${attr.name}: ${attr.get_value(block)}`)},
${initial_props} ${initial_props}
}`; }`;
@ -198,8 +196,8 @@ export default class InlineComponentWrapper extends Wrapper {
} }
const fragment_dependencies = new Set(this.fragment ? ['$$scope'] : []); const fragment_dependencies = new Set(this.fragment ? ['$$scope'] : []);
this.slots.forEach(slot => { this.slots.forEach((slot) => {
slot.block.dependencies.forEach(name => { slot.block.dependencies.forEach((name) => {
const is_let = slot.scope.is_let(name); const is_let = slot.scope.is_let(name);
const variable = renderer.component.var_lookup.get(name); const variable = renderer.component.var_lookup.get(name);
@ -207,9 +205,12 @@ export default class InlineComponentWrapper extends Wrapper {
}); });
}); });
const dynamic_attributes = this.node.attributes.filter(a => a.get_dependencies().length > 0); const dynamic_attributes = this.node.attributes.filter((a) => a.get_dependencies().length > 0);
if (!uses_spread && (dynamic_attributes.length > 0 || this.node.bindings.length > 0 || fragment_dependencies.size > 0)) { if (
!uses_spread &&
(dynamic_attributes.length > 0 || this.node.bindings.length > 0 || fragment_dependencies.size > 0)
) {
updates.push(b`const ${name_changes} = {};`); updates.push(b`const ${name_changes} = {};`);
} }
@ -222,14 +223,15 @@ export default class InlineComponentWrapper extends Wrapper {
const all_dependencies: Set<string> = new Set(); const all_dependencies: Set<string> = new Set();
this.node.attributes.forEach(attr => { this.node.attributes.forEach((attr) => {
add_to_set(all_dependencies, attr.dependencies); add_to_set(all_dependencies, attr.dependencies);
}); });
this.node.attributes.forEach((attr, i) => { this.node.attributes.forEach((attr, i) => {
const { name, dependencies } = attr; const { name, dependencies } = attr;
const condition = dependencies.size > 0 && (dependencies.size !== all_dependencies.size) const condition =
dependencies.size > 0 && dependencies.size !== all_dependencies.size
? renderer.dirty(Array.from(dependencies)) ? renderer.dirty(Array.from(dependencies))
: null; : null;
const unchanged = dependencies.size === 0; const unchanged = dependencies.size === 0;
@ -251,19 +253,11 @@ export default class InlineComponentWrapper extends Wrapper {
} }
changes.push( changes.push(
unchanged unchanged ? x`${levels}[${i}]` : condition ? x`${condition} && ${change_object}` : change_object
? x`${levels}[${i}]`
: condition
? x`${condition} && ${change_object}`
: change_object
); );
}); });
block.chunks.init.push(b` block.chunks.init.push(b`const ${levels} = [${initial_props}];`);
const ${levels} = [
${initial_props}
];
`);
statements.push(b` statements.push(b`
for (let #i = 0; #i < ${levels}.length; #i += 1) { for (let #i = 0; #i < ${levels}.length; #i += 1) {
@ -275,9 +269,7 @@ export default class InlineComponentWrapper extends Wrapper {
const condition = renderer.dirty(Array.from(all_dependencies)); const condition = renderer.dirty(Array.from(all_dependencies));
updates.push(b` updates.push(b`
const ${name_changes} = ${condition} ? @get_spread_update(${levels}, [ const ${name_changes} = ${condition} ? @get_spread_update(${levels}, [${changes}]) : {};
${changes}
]) : {}
`); `);
} else { } else {
updates.push(b` updates.push(b`
@ -305,7 +297,7 @@ export default class InlineComponentWrapper extends Wrapper {
}`); }`);
} }
const munged_bindings = this.node.bindings.map(binding => { const munged_bindings = this.node.bindings.map((binding) => {
component.has_reactive_assignments = true; component.has_reactive_assignments = true;
if (binding.name === 'this') { if (binding.name === 'this') {
@ -321,17 +313,13 @@ export default class InlineComponentWrapper extends Wrapper {
const snippet = binding.expression.manipulate(block); const snippet = binding.expression.manipulate(block);
statements.push(b` statements.push(b`if (${snippet} !== undefined) ${props}.${binding.name} = ${snippet};`);
if (${snippet} !== void 0) {
${props}.${binding.name} = ${snippet};
}`
);
updates.push(b` updates.push(b`
if (!${updating} && ${renderer.dirty(Array.from(binding.expression.dependencies))}) { if (!${updating} && ${renderer.dirty(Array.from(binding.expression.dependencies))}) {
${updating} = true; ${updating} = true;
${name_changes}.${binding.name} = ${snippet}; ${name_changes}.${binding.name} = ${snippet};
@add_flush_callback(() => ${updating} = false); @add_flush_callback(() => {${updating} = false;});
} }
`); `);
@ -350,48 +338,31 @@ export default class InlineComponentWrapper extends Wrapper {
} }
const params = [x`#value`]; const params = [x`#value`];
if (contextual_dependencies.length > 0) { let args = [];
const args = []; contextual_dependencies.forEach((name) => {
params.push({ type: 'Identifier', name });
contextual_dependencies.forEach(name => {
params.push({
type: 'Identifier',
name
});
renderer.add_to_context(name, true); renderer.add_to_context(name, true);
args.push(renderer.reference(name)); args.push(renderer.reference(name));
// TODO put this somewhere more logical
block.maintain_context = true;
}); });
block.chunks.init.push(b` block.chunks.init.push(b`
function ${id}(#value) { function ${id}(#value) {
${callee}.call(null, #value, ${args}); ${callee}.call(null, #value, ${args.length ? args : null});
} }
`); `);
block.maintain_context = true; // TODO put this somewhere more logical component.partly_hoisted.push(b`
} else {
block.chunks.init.push(b`
function ${id}(#value) {
${callee}.call(null, #value);
}
`);
}
const body = b`
function ${id}(${params}) { function ${id}(${params}) {
${lhs} = #value; ${lhs} = #value;
${renderer.invalidate(dependencies[0])}; ${renderer.invalidate(dependencies[0])};
} }
`; `);
component.partly_hoisted.push(body);
return b`@binding_callbacks.push(() => @bind(${this.var}, '${binding.name}', ${id}));`; return b`@binding_callbacks.push(() => @bind(${this.var}, '${binding.name}', ${id}));`;
}); });
const munged_handlers = this.node.handlers.map(handler => { const munged_handlers = this.node.handlers.map((handler) => {
const event_handler = new EventHandler(handler, this); const event_handler = new EventHandler(handler, this);
let snippet = event_handler.get_snippet(block); let snippet = event_handler.get_snippet(block);
if (handler.modifiers.has('once')) snippet = x`@once(${snippet})`; if (handler.modifiers.has('once')) snippet = x`@once(${snippet})`;
@ -409,8 +380,7 @@ export default class InlineComponentWrapper extends Wrapper {
var ${switch_value} = ${snippet}; var ${switch_value} = ${snippet};
function ${switch_props}(#ctx) { function ${switch_props}(#ctx) {
${(this.node.attributes.length > 0 || this.node.bindings.length > 0) && b` ${props && (this.node.attributes.length || this.node.bindings.length) && b`let ${props} = ${attribute_object};`}
${props && b`let ${props} = ${attribute_object};`}`}
${statements} ${statements}
return ${component_opts}; return ${component_opts};
} }
@ -423,14 +393,10 @@ export default class InlineComponentWrapper extends Wrapper {
} }
`); `);
block.chunks.create.push( block.chunks.create.push(b`if (${name} && ${name}.$$.fragment) ${name}.$$.fragment.c();`);
b`if (${name}) @create_component(${name}.$$.fragment);`
);
if (parent_nodes && this.renderer.options.hydratable) { if (parent_nodes && this.renderer.options.hydratable) {
block.chunks.claim.push( block.chunks.claim.push(b`if (${name} && ${name}.$$.fragment) ${name}.$$.fragment.l(${parent_nodes});`);
b`if (${name}) @claim_component(${name}.$$.fragment, ${parent_nodes});`
);
} }
block.chunks.mount.push(b` block.chunks.mount.push(b`
@ -465,7 +431,7 @@ export default class InlineComponentWrapper extends Wrapper {
${munged_bindings} ${munged_bindings}
${munged_handlers} ${munged_handlers}
@create_component(${name}.$$.fragment); if(${name}.$$.fragment) ${name}.$$.fragment.c();
@transition_in(${name}.$$.fragment, 1); @transition_in(${name}.$$.fragment, 1);
@mount_component(${name}, ${update_mount_node}, ${anchor}); @mount_component(${name}, ${update_mount_node}, ${anchor});
} else { } else {
@ -480,19 +446,18 @@ export default class InlineComponentWrapper extends Wrapper {
if (${name}) @transition_in(${name}.$$.fragment, #local); if (${name}) @transition_in(${name}.$$.fragment, #local);
`); `);
block.chunks.outro.push( block.chunks.outro.push(b`if (${name}) @transition_out(${name}.$$.fragment, #local);`);
b`if (${name}) @transition_out(${name}.$$.fragment, #local);`
);
block.chunks.destroy.push(b`if (${name}) @destroy_component(${name}, ${parent_node ? null : 'detaching'});`); block.chunks.destroy.push(b`if (${name}) @destroy_component(${name}, ${parent_node ? null : 'detaching'});`);
} else { } else {
const expression = this.node.name === 'svelte:self' const expression = this.node.name === 'svelte:self' ? component.name : this.renderer.reference(this.node.name);
? component.name
: this.renderer.reference(this.node.name);
block.chunks.init.push(b` block.chunks.init.push(b`
${(this.node.attributes.length > 0 || this.node.bindings.length > 0) && b` ${
${props && b`let ${props} = ${attribute_object};`}`} (this.node.attributes.length > 0 || this.node.bindings.length > 0) &&
b`
${props && b`let ${props} = ${attribute_object};`}`
}
${statements} ${statements}
const ${name} = new ${expression}(${component_opts}); const ${name} = new ${expression}(${component_opts});
@ -500,12 +465,10 @@ export default class InlineComponentWrapper extends Wrapper {
${munged_handlers} ${munged_handlers}
`); `);
block.chunks.create.push(b`@create_component(${name}.$$.fragment);`); block.chunks.create.push(b`if(${name}.$$.fragment) ${name}.$$.fragment.c();`);
if (parent_nodes && this.renderer.options.hydratable) { if (parent_nodes && this.renderer.options.hydratable) {
block.chunks.claim.push( block.chunks.claim.push(b`if(${name}.$$.fragment) ${name}.$$.fragment.l(${parent_nodes});`);
b`@claim_component(${name}.$$.fragment, ${parent_nodes});`
);
} }
block.chunks.mount.push( block.chunks.mount.push(
@ -527,9 +490,7 @@ export default class InlineComponentWrapper extends Wrapper {
@destroy_component(${name}, ${parent_node ? null : 'detaching'}); @destroy_component(${name}, ${parent_node ? null : 'detaching'});
`); `);
block.chunks.outro.push( block.chunks.outro.push(b`@transition_out(${name}.$$.fragment, #local);`);
b`@transition_out(${name}.$$.fragment, #local);`
);
} }
} }
} }

@ -1,14 +1,6 @@
import { cubicOut } from 'svelte/easing'; import { cubicOut } from 'svelte/easing';
import { is_function } from 'svelte/internal'; import { AnimationConfig } from 'svelte/internal';
// todo: same as Transition, should it be shared?
export interface AnimationConfig {
delay?: number;
duration?: number;
easing?: (t: number) => number;
css?: (t: number, u: number) => string;
tick?: (t: number, u: number) => void;
}
interface FlipParams { interface FlipParams {
delay: number; delay: number;
@ -16,27 +8,23 @@ interface FlipParams {
easing: (t: number) => number; easing: (t: number) => number;
} }
export function flip(node: Element, animation: { from: DOMRect; to: DOMRect }, params: FlipParams): AnimationConfig { export function flip(
const style = getComputedStyle(node); node: Element,
const transform = style.transform === 'none' ? '' : style.transform; animation: { from: DOMRect; to: DOMRect },
{ delay = 0, duration = (d: number) => Math.sqrt(d) * 120, easing = cubicOut }: FlipParams
): AnimationConfig {
const style = getComputedStyle(node).transform;
const transform = style === 'none' ? '' : style;
const scaleX = animation.from.width / node.clientWidth; const scaleX = animation.from.width / node.clientWidth;
const scaleY = animation.from.height / node.clientHeight; const scaleY = animation.from.height / node.clientHeight;
const dx = (animation.from.left - animation.to.left) / scaleX; const dx = (animation.from.left - animation.to.left) / scaleX;
const dy = (animation.from.top - animation.to.top) / scaleY; const dy = (animation.from.top - animation.to.top) / scaleY;
const d = Math.sqrt(dx * dx + dy * dy);
const {
delay = 0,
duration = (d: number) => Math.sqrt(d) * 120,
easing = cubicOut
} = params;
return { return {
delay, delay,
duration: is_function(duration) ? duration(d) : duration, duration: typeof duration === 'function' ? duration(Math.sqrt(dx * dx + dy * dy)) : duration,
easing, easing,
css: (_t, u) => `transform: ${transform} translate(${u * dx}px, ${u * dy}px);` css: (_t, u) => `transform: ${transform} translate(${u * dx}px, ${u * dy}px);`,
}; };
} }

@ -1,171 +1,69 @@
/*
Adapted from https://github.com/mattdesl
Distributed under MIT License https://github.com/mattdesl/eases/blob/master/LICENSE.md
*/
export { identity as linear } from 'svelte/internal'; export { identity as linear } from 'svelte/internal';
export const quadIn = (t: number) => t ** 2;
export function backInOut(t: number) { export const quadOut = (t: number) => 1.0 - (1.0 - t) ** 2;
const s = 1.70158 * 1.525; export const quadInOut = (t: number) => 0.5 * (t >= 0.5 ? 2 - 2 * (1.0 - t) ** 2 : (2 * t) ** 2);
if ((t *= 2) < 1) return 0.5 * (t * t * ((s + 1) * t - s)); export const cubicIn = (t: number) => t ** 3;
return 0.5 * ((t -= 2) * t * ((s + 1) * t + s) + 2); export const cubicOut = (t: number) => 1.0 - (1.0 - t) ** 3;
} export const cubicInOut = (t: number) => 0.5 * (t >= 0.5 ? 2 - (2 * (1.0 - t)) ** 3 : (2 * t) ** 3);
export const quartIn = (t: number) => t ** 4;
export function backIn(t: number) { export const quartOut = (t: number) => 1.0 - (1.0 - t) ** 4;
const s = 1.70158; export const quartInOut = (t: number) => 0.5 * (t >= 0.5 ? 2 - (2 * (1.0 - t)) ** 4 : (2 * t) ** 4);
return t * t * ((s + 1) * t - s); export const easeIn = quartIn;
} export const easeOut = quartOut;
export const easeInOut = quartInOut;
export function backOut(t: number) { export const quintIn = (t: number) => t ** 5;
const s = 1.70158; export const quintOut = (t: number) => 1.0 - (1.0 - t) ** 5;
return --t * t * ((s + 1) * t + s) + 1; export const quintInOut = (t: number) => 0.5 * (t >= 0.5 ? 2 - (2 * (1.0 - t)) ** 5 : (2 * t) ** 5);
} export const backIn = (t: number) => t * t * (2.6 * t - 1.6);
export const backOut = (t: number) => 1 - (t = 1.0 - t) * t * (2.6 * t - 1.6);
export function bounceOut(t: number) { export const backInOut = (t: number) =>
const a = 4.0 / 11.0; 0.5 * (t >= 0.5 ? 2 - (t = 2 * (1 - t)) * t * (2.6 * t - 1.6) : (t = 2 * t) * t * (2.6 * t - 1.6));
const b = 8.0 / 11.0; export const expoIn = (t: number) => (t ? Math.pow(2.0, 10.0 * (t - 1.0)) : t);
const c = 9.0 / 10.0; export const expoOut = (t: number) => (t ? 1.0 - Math.pow(2.0, -10.0 * t) : t);
export const expoInOut = (t: number) =>
const ca = 4356.0 / 361.0; !t || t === 1.0 ? t : t < 0.5 ? 0.5 * Math.pow(2.0, 20.0 * t - 10.0) : -0.5 * Math.pow(2.0, 10.0 - t * 20.0) + 1.0;
const cb = 35442.0 / 1805.0; export const elasticIn = (t: number) => Math.sin((13.0 * t * Math.PI) / 2) * Math.pow(2.0, 10.0 * (t - 1.0));
const cc = 16061.0 / 1805.0; export const elasticOut = (t: number) => Math.sin((-13.0 * (t + 1.0) * Math.PI) / 2) * Math.pow(2.0, -10.0 * t) + 1.0;
export const elasticInOut = (t: number) =>
const t2 = t * t; t < 0.5
? 0.5 * Math.sin(((+13.0 * Math.PI) / 2) * 2.0 * t) * Math.pow(2.0, 10.0 * (2.0 * t - 1.0))
return t < a : 0.5 * Math.sin(((-13.0 * Math.PI) / 2) * (2.0 * t - 1.0 + 1.0)) * Math.pow(2.0, -10.0 * (2.0 * t - 1.0)) + 1.0;
? 7.5625 * t2 export const bounceOut = (t: number) =>
: t < b 4 / 11 > t
? 9.075 * t2 - 9.9 * t + 3.4 ? 7.5625 * t * t
: t < c : 8 / 11 > t
? ca * t2 - cb * t + cc ? 3.4 + 9.075 * t * t - 9.9 * t
: 10.8 * t * t - 20.52 * t + 10.72; : 9 / 10 > t
} ? 16061.0 / 1805.0 + (4356.0 * t * t) / 361.0 - (35442.0 * t) / 1805.0
: 10.72 + 10.8 * t * t - 20.52 * t;
export function bounceInOut(t: number) { export const bounceIn = (t: number) => 1.0 - bounceOut(1.0 - t);
return t < 0.5 export const bounceInOut = (t: number) =>
? 0.5 * (1.0 - bounceOut(1.0 - t * 2.0)) t < 0.5 ? 0.5 * (1.0 - bounceOut(1.0 - t * 2.0)) : 0.5 * bounceOut(t * 2.0 - 1.0) + 0.5;
: 0.5 * bounceOut(t * 2.0 - 1.0) + 0.5; export const sineIn = (t: number) => (1e-14 > Math.abs((t = Math.cos(t * Math.PI * 0.5))) ? 1.0 : 1.0 - t);
} export const sineOut = (t: number) => Math.sin((t * Math.PI) / 2);
export const sineInOut = (t: number) => -0.5 * (Math.cos(Math.PI * t) - 1);
export function bounceIn(t: number) { export const circIn = (t: number) => 1 - Math.sin(Math.acos(t));
return 1.0 - bounceOut(1.0 - t); export const circOut = (t: number) => Math.sin(Math.acos(1 - t));
} export const circInOut = (t: number) =>
0.5 * (t >= 0.5 ? 2.0 - Math.sin(Math.acos(1.0 - 2.0 * (1.0 - t))) : Math.sin(Math.acos(1.0 - 2.0 * t)));
export function circInOut(t: number) { export const cubicBezier = (x1: number, y1: number, x2: number, y2: number) => {
if ((t *= 2) < 1) return -0.5 * (Math.sqrt(1 - t * t) - 1); const ax = 1.0 - (x2 = 3.0 * (x2 - x1) - (x1 = 3.0 * x1)) - x1,
return 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1); ay = 1.0 - (y2 = 3.0 * (y2 - y1) - (y1 = 3.0 * y1)) - y1;
} let r = Number.NaN,
s = Number.NaN,
export function circIn(t: number) { d = Number.NaN,
return 1.0 - Math.sqrt(1.0 - t * t); x = Number.NaN;
} return (t: number) => {
r = t;
export function circOut(t: number) { for (let i = 0; 32 > i; i++)
return Math.sqrt(1 - --t * t); if (1e-5 > Math.abs((x = r * r * (r * ax + x1 + x2) - t))) return r * (r * (r * ay + y2) + y1);
} else if (1e-5 > Math.abs((d = r * (r * ax * 3.0 + x2 * 2.0) + x1))) break;
else r = r - x / d;
export function cubicInOut(t: number) { if ((s = 0.0) > (r = t)) return 0;
return t < 0.5 ? 4.0 * t * t * t : 0.5 * Math.pow(2.0 * t - 2.0, 3.0) + 1.0; else if ((d = 1.0) > r) return 1;
} while (d > s)
if (1e-5 > Math.abs((x = r * (r * (r * ax + x2) + x1)) - t)) break;
export function cubicIn(t: number) { else t > x ? (s = r) : (d = r), (r = 0.5 * (d - s) + s);
return t * t * t; return r * (r * (r * ay + y2) + y1);
} };
};
export function cubicOut(t: number) {
const f = t - 1.0;
return f * f * f + 1.0;
}
export function elasticInOut(t: number) {
return t < 0.5
? 0.5 *
Math.sin(((+13.0 * Math.PI) / 2) * 2.0 * t) *
Math.pow(2.0, 10.0 * (2.0 * t - 1.0))
: 0.5 *
Math.sin(((-13.0 * Math.PI) / 2) * (2.0 * t - 1.0 + 1.0)) *
Math.pow(2.0, -10.0 * (2.0 * t - 1.0)) +
1.0;
}
export function elasticIn(t: number) {
return Math.sin((13.0 * t * Math.PI) / 2) * Math.pow(2.0, 10.0 * (t - 1.0));
}
export function elasticOut(t: number) {
return (
Math.sin((-13.0 * (t + 1.0) * Math.PI) / 2) * Math.pow(2.0, -10.0 * t) + 1.0
);
}
export function expoInOut(t: number) {
return t === 0.0 || t === 1.0
? t
: t < 0.5
? +0.5 * Math.pow(2.0, 20.0 * t - 10.0)
: -0.5 * Math.pow(2.0, 10.0 - t * 20.0) + 1.0;
}
export function expoIn(t: number) {
return t === 0.0 ? t : Math.pow(2.0, 10.0 * (t - 1.0));
}
export function expoOut(t: number) {
return t === 1.0 ? t : 1.0 - Math.pow(2.0, -10.0 * t);
}
export function quadInOut(t: number) {
t /= 0.5;
if (t < 1) return 0.5 * t * t;
t--;
return -0.5 * (t * (t - 2) - 1);
}
export function quadIn(t: number) {
return t * t;
}
export function quadOut(t: number) {
return -t * (t - 2.0);
}
export function quartInOut(t: number) {
return t < 0.5
? +8.0 * Math.pow(t, 4.0)
: -8.0 * Math.pow(t - 1.0, 4.0) + 1.0;
}
export function quartIn(t: number) {
return Math.pow(t, 4.0);
}
export function quartOut(t: number) {
return Math.pow(t - 1.0, 3.0) * (1.0 - t) + 1.0;
}
export function quintInOut(t: number) {
if ((t *= 2) < 1) return 0.5 * t * t * t * t * t;
return 0.5 * ((t -= 2) * t * t * t * t + 2);
}
export function quintIn(t: number) {
return t * t * t * t * t;
}
export function quintOut(t: number) {
return --t * t * t * t * t + 1;
}
export function sineInOut(t: number) {
return -0.5 * (Math.cos(Math.PI * t) - 1);
}
export function sineIn(t: number) {
const v = Math.cos(t * Math.PI * 0.5);
if (Math.abs(v) < 1e-14) return 1;
else return 1 - v;
}
export function sineOut(t: number) {
return Math.sin((t * Math.PI) / 2);
}

@ -1,22 +1,32 @@
import { add_render_callback, flush, schedule_update, dirty_components } from './scheduler'; import { add_render_callback, flush, schedule_update } from './scheduler';
import { current_component, set_current_component } from './lifecycle'; import { current_component, set_current_component } from './lifecycle';
import { blank_object, is_function, run, run_all, noop } from './utils'; import { blank_object, is_function, run_all, noop } from './utils';
import { children, detach } from './dom'; import { children, detach } from './dom';
import { transition_in } from './transitions'; import { transition_in } from './transitions';
interface Fragment { export interface Fragment {
key: string | null; key: string | null;
first: null; first: null;
/* create */ c: () => void; /**
/* claim */ l: (nodes: any) => void; * create
* run once
* runs hydrate if exists
*/
c: () => void;
/**
* claim
* runs hydrate if exists
* */
l: (nodes: any) => void;
/* hydrate */ h: () => void; /* hydrate */ h: () => void;
/* mount */ m: (target: HTMLElement, anchor: any) => void; /* mount */ m: (target: HTMLElement, anchor: any, is_remount: boolean) => void;
/* update */ p: (ctx: any, dirty: any) => void; /* update */ p: (ctx: any, dirty: any) => void;
/* measure */ r: () => void; /* measure */ r: () => void;
/* fix */ f: () => void; /* fix */ f: () => void;
/* animate */ a: () => void; /* animate */ a: () => void;
/* intro */ i: (local: any) => void; /* intro */ i: (local: 0 | 1) => void;
/* outro */ o: (local: any) => void; /* outro */ o: (local: 0 | 1) => void;
/* destroy */ d: (detaching: 0 | 1) => void; /* destroy */ d: (detaching: 0 | 1) => void;
} }
// eslint-disable-next-line @typescript-eslint/class-name-casing // eslint-disable-next-line @typescript-eslint/class-name-casing
@ -36,43 +46,25 @@ interface T$$ {
on_destroy: any[]; on_destroy: any[];
} }
export function bind(component, name, callback) { export function bind({ $$: { props, bound, ctx } }, name: string, callback: (prop: any) => void) {
const index = component.$$.props[name]; if (!(name in props)) return;
if (index === undefined) return; const index = props[name];
component.$$.bound[index] = callback; bound[index] = callback;
callback(component.$$.ctx[index]); callback(ctx[index]);
} }
export function mount_component({ $$: { fragment, on_mount, on_destroy, after_update } }, target, anchor) {
export function create_component(block) {
if (block) block.c();
}
export function claim_component(block, parent_nodes) {
if (block) block.l(parent_nodes);
}
export function mount_component(component, target, anchor) {
const { fragment, on_mount, on_destroy, after_update } = component.$$;
if (fragment) fragment.m(target, anchor); if (fragment) fragment.m(target, anchor);
// onMount happens before the initial afterUpdate
add_render_callback(() => { add_render_callback(() => {
const new_on_destroy = on_mount.map(run).filter(is_function); for (let i = 0, res; i < on_mount.length; i++)
if (on_destroy) { if (is_function((res = on_mount[i]())))
on_destroy.push(...new_on_destroy); if (on_destroy) on_destroy.push(res);
} else { else res(); // component already destroyed
// Edge case - component was destroyed immediately, on_mount.length = 0;
// most likely as a result of a binding initialising for (let i = 0; i < after_update.length; i++) after_update[i]();
run_all(new_on_destroy);
}
component.$$.on_mount = [];
}); });
after_update.forEach(add_render_callback);
} }
export function destroy_component({ $$ }, detaching) { export function destroy_component({ $$ }, detaching: 0 | 1) {
if ($$.fragment === null) return; if ($$.fragment === null) return;
run_all($$.on_destroy); run_all($$.on_destroy);
@ -84,17 +76,8 @@ export function destroy_component({ $$ }, detaching) {
$$.ctx = []; $$.ctx = [];
} }
function make_dirty(component, i) {
if (component.$$.dirty[0] === -1) {
dirty_components.push(component);
schedule_update();
component.$$.dirty.fill(0);
}
component.$$.dirty[(i / 31) | 0] |= 1 << i % 31;
}
export function init( export function init(
component, component: SvelteComponent,
{ props: prop_values = {}, target, hydrate, intro, anchor }, { props: prop_values = {}, target, hydrate, intro, anchor },
instance, instance,
create_fragment, create_fragment,
@ -131,8 +114,14 @@ export function init(
$$.ctx = instance $$.ctx = instance
? instance(component, prop_values, (i, res, val = res) => { ? instance(component, prop_values, (i, res, val = res) => {
if ($$.ctx && not_equal($$.ctx[i], ($$.ctx[i] = val))) { if ($$.ctx && not_equal($$.ctx[i], ($$.ctx[i] = val))) {
if ($$.bound[i]) $$.bound[i](val); if (i in $$.bound) $$.bound[i](val);
if (ready) make_dirty(component, i); if (ready) {
if (!~$$.dirty) {
schedule_update(component);
$$.dirty.fill(0);
}
$$.dirty[(i / 31) | 0] |= 1 << i % 31;
}
} }
return res; return res;
}) })
@ -144,18 +133,24 @@ export function init(
run_all($$.before_update); run_all($$.before_update);
// false when empty fragment // false when empty
$$.fragment = create_fragment ? create_fragment($$.ctx) : false; $$.fragment = create_fragment ? create_fragment($$.ctx) : false;
if (target) { if (target) {
if ($$.fragment) {
if (hydrate) { if (hydrate) {
const nodes = children(target); const nodes = children(target);
if ($$.fragment) $$.fragment.l(nodes); $$.fragment.l(nodes);
nodes.forEach(detach); nodes.forEach(detach);
} else { } else {
if ($$.fragment) $$.fragment.c(); $$.fragment.c();
}
if (intro) {
transition_in($$.fragment);
}
} else if (hydrate) {
children(target).forEach(detach);
} }
if (intro) transition_in(component.$$.fragment);
mount_component(component, target, anchor); mount_component(component, target, anchor);
flush(); flush();
} }

@ -1,7 +1,14 @@
import { noop } from './utils'; import { noop } from './utils';
import { AnimationConfig } from '../animate';
import { run_transition } from './transitions'; import { run_transition } from './transitions';
export interface AnimationConfig {
delay?: number;
duration?: number;
easing?: (t: number) => number;
css?: (t: number, u?: number) => string;
tick?: (t: number, u?: number) => void;
}
//todo: documentation says it is DOMRect, but in IE it would be ClientRect //todo: documentation says it is DOMRect, but in IE it would be ClientRect
type PositionRect = DOMRect | ClientRect; type PositionRect = DOMRect | ClientRect;
@ -19,28 +26,24 @@ export function run_animation(node: HTMLElement, from: PositionRect, fn: Animati
} }
export function fix_position(node: HTMLElement) { export function fix_position(node: HTMLElement) {
const style = getComputedStyle(node); const { position, width, height } = getComputedStyle(node);
if (position === 'absolute' || position === 'fixed') return noop;
if (style.position !== 'absolute' && style.position !== 'fixed') { const current_position = node.getBoundingClientRect();
const { width, height } = style;
const a = node.getBoundingClientRect();
const { position: og_position, width: og_width, height: og_height } = node.style; const { position: og_position, width: og_width, height: og_height } = node.style;
node.style.position = 'absolute'; node.style.position = 'absolute';
node.style.width = width; node.style.width = width;
node.style.height = height; node.style.height = height;
add_transform(node, a); add_transform(node, current_position);
return () => { return () => {
node.style.position = og_position; node.style.position = og_position;
node.style.width = og_width; node.style.width = og_width;
node.style.height = og_height; node.style.height = og_height;
node.style.transform = ''; node.style.transform = ''; // unsafe
}; };
} }
}
export function add_transform(node: HTMLElement, a: PositionRect) { export function add_transform(node: HTMLElement, a: PositionRect) {
const b = node.getBoundingClientRect(); const b = node.getBoundingClientRect();
if (a.left !== b.left || a.top !== b.top) { if (a.left !== b.left || a.top !== b.top) {
const style = getComputedStyle(node); const style = getComputedStyle(node);
const transform = style.transform === 'none' ? '' : style.transform; const transform = style.transform === 'none' ? '' : style.transform;

@ -1,22 +1,51 @@
import { custom_event, append, insert, detach, listen, attr } from './dom'; import { custom_event, append, insert, detach, listen, attr } from './dom';
import { SvelteComponent } from './Component'; import { SvelteComponent } from './Component';
import {
get_current_component,
beforeUpdate,
onMount,
afterUpdate,
onDestroy,
createEventDispatcher,
setContext,
getContext,
} from './lifecycle';
import { cubicBezier } from 'svelte/easing';
export const [
beforeUpdate_dev,
onMount_dev,
afterUpdate_dev,
onDestroy_dev,
createEventDispatcher_dev,
setContext_dev,
getContext_dev,
] = [beforeUpdate, onMount, afterUpdate, onDestroy, createEventDispatcher, setContext, getContext].map(
(fn) => (a?, b?) => {
if (!get_current_component()) throw new Error(`${fn.name} cannot be called outside of component initialization`);
return fn(a, b);
}
);
export function cubicBezier_dev(mX1, mY1, mX2, mY2) {
if (!(0 <= mX1 && mX1 <= 1 && 0 <= mX2 && mX2 <= 1)) throw new Error('CubicBezier x values must be { 0 < x < 1 }');
return cubicBezier(mX1, mY1, mX2, mY2);
}
export function dispatch_dev<T = any>(type: string, detail?: T) { export function dispatch_dev<T = any>(type: string, detail?: T) {
document.dispatchEvent(custom_event(type, { version: '__VERSION__', ...detail })); document.dispatchEvent(custom_event(type, { version: '__VERSION__', ...detail }));
} }
export function append_dev(target: Node, node: Node) { export function append_dev(target: Node, node: Node) {
dispatch_dev("SvelteDOMInsert", { target, node }); dispatch_dev('SvelteDOMInsert', { target, node });
append(target, node); append(target, node);
} }
export function insert_dev(target: Node, node: Node, anchor?: Node) { export function insert_dev(target: Node, node: Node, anchor?: Node) {
dispatch_dev("SvelteDOMInsert", { target, node, anchor }); dispatch_dev('SvelteDOMInsert', { target, node, anchor });
insert(target, node, anchor); insert(target, node, anchor);
} }
export function detach_dev(node: Node) { export function detach_dev(node: Node) {
dispatch_dev("SvelteDOMRemove", { node }); dispatch_dev('SvelteDOMRemove', { node });
detach(node); detach(node);
} }
@ -38,16 +67,23 @@ export function detach_after_dev(before: Node) {
} }
} }
export function listen_dev(node: Node, event: string, handler: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions | EventListenerOptions, has_prevent_default?: boolean, has_stop_propagation?: boolean) { export function listen_dev(
const modifiers = options === true ? [ "capture" ] : options ? Array.from(Object.keys(options)) : []; node: Node,
event: string,
handler: EventListenerOrEventListenerObject,
options?: boolean | AddEventListenerOptions | EventListenerOptions,
has_prevent_default?: boolean,
has_stop_propagation?: boolean
) {
const modifiers = options === true ? ['capture'] : options ? Array.from(Object.keys(options)) : [];
if (has_prevent_default) modifiers.push('preventDefault'); if (has_prevent_default) modifiers.push('preventDefault');
if (has_stop_propagation) modifiers.push('stopPropagation'); if (has_stop_propagation) modifiers.push('stopPropagation');
dispatch_dev("SvelteDOMAddEventListener", { node, event, handler, modifiers }); dispatch_dev('SvelteDOMAddEventListener', { node, event, handler, modifiers });
const dispose = listen(node, event, handler, options); const dispose = listen(node, event, handler, options);
return () => { return () => {
dispatch_dev("SvelteDOMRemoveEventListener", { node, event, handler, modifiers }); dispatch_dev('SvelteDOMRemoveEventListener', { node, event, handler, modifiers });
dispose(); dispose();
}; };
} }
@ -55,27 +91,27 @@ export function listen_dev(node: Node, event: string, handler: EventListenerOrEv
export function attr_dev(node: Element, attribute: string, value?: string) { export function attr_dev(node: Element, attribute: string, value?: string) {
attr(node, attribute, value); attr(node, attribute, value);
if (value == null) dispatch_dev("SvelteDOMRemoveAttribute", { node, attribute }); if (value == null) dispatch_dev('SvelteDOMRemoveAttribute', { node, attribute });
else dispatch_dev("SvelteDOMSetAttribute", { node, attribute, value }); else dispatch_dev('SvelteDOMSetAttribute', { node, attribute, value });
} }
export function prop_dev(node: Element, property: string, value?: any) { export function prop_dev(node: Element, property: string, value?: any) {
node[property] = value; node[property] = value;
dispatch_dev("SvelteDOMSetProperty", { node, property, value }); dispatch_dev('SvelteDOMSetProperty', { node, property, value });
} }
export function dataset_dev(node: HTMLElement, property: string, value?: any) { export function dataset_dev(node: HTMLElement, property: string, value?: any) {
node.dataset[property] = value; node.dataset[property] = value;
dispatch_dev("SvelteDOMSetDataset", { node, property, value }); dispatch_dev('SvelteDOMSetDataset', { node, property, value });
} }
export function set_data_dev(text, data) { export function set_data_dev(text, data) {
data = '' + data; data = '' + data;
if (text.data === data) return; if (text.data === data) return;
dispatch_dev("SvelteDOMSetData", { node: text, data }); dispatch_dev('SvelteDOMSetData', { node: text, data });
text.data = data; text.data = data;
} }

@ -1,4 +1,4 @@
import { has_prop } from "./utils"; import { has_prop } from './utils';
export function append(target: Node, node: Node) { export function append(target: Node, node: Node) {
target.appendChild(node); target.appendChild(node);
@ -25,14 +25,13 @@ export function element<K extends keyof HTMLElementTagNameMap>(name: K) {
export function element_is<K extends keyof HTMLElementTagNameMap>(name: K, is: string) { export function element_is<K extends keyof HTMLElementTagNameMap>(name: K, is: string) {
return document.createElement<K>(name, { is }); return document.createElement<K>(name, { is });
} }
export function object_without_properties<T, K extends keyof T>(obj: T, exclude: K[]) { export function object_without_properties<T, K extends keyof T>(obj: T, exclude: K[]) {
const target = {} as Pick<T, Exclude<keyof T, K>>; const target = {} as Pick<T, Exclude<keyof T, K>>;
for (const k in obj) { for (const k in obj) {
if ( if (
has_prop(obj, k) has_prop(obj, k) &&
// @ts-ignore // @ts-ignore
&& exclude.indexOf(k) === -1 exclude.indexOf(k) === -1
) { ) {
// @ts-ignore // @ts-ignore
target[k] = obj[k]; target[k] = obj[k];
@ -57,7 +56,12 @@ export function empty() {
return text(''); return text('');
} }
export function listen(node: EventTarget, event: string, handler: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions | EventListenerOptions) { export function listen(
node: EventTarget,
event: string,
handler: EventListenerOrEventListenerObject,
options?: boolean | AddEventListenerOptions | EventListenerOptions
) {
node.addEventListener(event, handler, options); node.addEventListener(event, handler, options);
return () => node.removeEventListener(event, handler, options); return () => node.removeEventListener(event, handler, options);
} }
@ -98,7 +102,7 @@ export function set_attributes(node: Element & ElementCSSInlineStyle, attributes
node.removeAttribute(key); node.removeAttribute(key);
} else if (key === 'style') { } else if (key === 'style') {
node.style.cssText = attributes[key]; node.style.cssText = attributes[key];
} else if (key === '__value' || descriptors[key] && descriptors[key].set) { } else if (key === '__value' || (descriptors[key] && descriptors[key].set)) {
node[key] = attributes[key]; node[key] = attributes[key];
} else { } else {
attr(node, key, attributes[key]); attr(node, key, attributes[key]);
@ -144,9 +148,7 @@ export function time_ranges_to_array(ranges) {
return array; return array;
} }
export function children(element) { export const children = (element: HTMLElement) => Array.from(element.childNodes);
return Array.from(element.childNodes);
}
export function claim_element(nodes, name, attributes, svg) { export function claim_element(nodes, name, attributes, svg) {
for (let i = 0; i < nodes.length; i += 1) { for (let i = 0; i < nodes.length; i += 1) {
@ -231,7 +233,7 @@ export function select_value(select) {
} }
export function select_multiple_value(select) { export function select_multiple_value(select) {
return [].map.call(select.querySelectorAll(':checked'), option => option.__value); return [].map.call(select.querySelectorAll(':checked'), (option) => option.__value);
} }
// unfortunately this can't be a constant as that wouldn't be tree-shakeable // unfortunately this can't be a constant as that wouldn't be tree-shakeable
@ -263,7 +265,8 @@ export function add_resize_listener(node: HTMLElement, fn: () => void) {
} }
const iframe = element('iframe'); const iframe = element('iframe');
iframe.setAttribute('style', iframe.setAttribute(
'style',
`display: block; position: absolute; top: 0; left: 0; width: 100%; height: 100%; ` + `display: block; position: absolute; top: 0; left: 0; width: 100%; height: 100%; ` +
`overflow: hidden; border: 0; opacity: 0; pointer-events: none; z-index: ${z_index};` `overflow: hidden; border: 0; opacity: 0; pointer-events: none; z-index: ${z_index};`
); );

@ -1,18 +1,16 @@
import { noop } from './utils'; import { noop } from './utils';
export const is_client = typeof window !== 'undefined'; export const is_client = typeof window !== 'undefined';
export const is_iframe = is_client && window.location !== window.parent.location;
export let now: () => number = is_client export let now = is_client ? window.performance.now : () => Date.now();
? () => window.performance.now()
: () => Date.now();
export let raf = is_client ? cb => requestAnimationFrame(cb) : noop; export let raf = is_client ? window.requestAnimationFrame : noop;
// used internally for testing // used internally for testing
export function set_now(fn) { export function set_now(fn) {
now = fn; now = fn;
} }
export function set_raf(fn) { export function set_raf(fn) {
raf = fn; raf = fn;
} }

@ -2,9 +2,7 @@ import { custom_event } from './dom';
export let current_component; export let current_component;
export function set_current_component(component) { export const set_current_component = (component) => (current_component = component);
current_component = component;
}
export function get_current_component() { export function get_current_component() {
if (!current_component) throw new Error(`Function called outside component initialization`); if (!current_component) throw new Error(`Function called outside component initialization`);
@ -32,15 +30,13 @@ export function createEventDispatcher() {
return (type: string, detail?: any) => { return (type: string, detail?: any) => {
const callbacks = component.$$.callbacks[type]; const callbacks = component.$$.callbacks[type];
if (!callbacks) return;
if (callbacks) {
// TODO are there situations where events could be dispatched // TODO are there situations where events could be dispatched
// in a server (non-DOM) environment? // in a server (non-DOM) environment?
const event = custom_event(type, detail); const event = custom_event(type, detail);
callbacks.slice().forEach(fn => { callbacks.forEach((fn) => {
fn.call(component, event); fn.call(component, event);
}); });
}
}; };
} }
@ -59,6 +55,6 @@ export function bubble(component, event) {
const callbacks = component.$$.callbacks[event.type]; const callbacks = component.$$.callbacks[event.type];
if (callbacks) { if (callbacks) {
callbacks.slice().forEach(fn => fn(event)); callbacks.slice().forEach((fn) => fn(event));
} }
} }

@ -43,37 +43,35 @@ export function loop(callback: TaskCallback): Task {
}; };
} }
function add(c) { function add(c: TaskCallback) {
const task = { c, f: noop }; const task = { c, f: noop };
if (!tasks.size) raf(run_tasks); if (!tasks.size) raf(run_tasks);
tasks.add(task); tasks.add(task);
return () => tasks.delete(task); return () => tasks.delete(task);
} }
const timed_tasks = []; type TimeoutTask = { t: number; c: (now: number) => void };
// sorted descending
const timed_tasks: Array<TimeoutTask> = [];
/** /**
* Callback on 1st frame after timestamp * basically
* (fn, t) => setTimeout( () => raf(fn), t )
*/ */
export function raf_timeout(callback: () => void, timestamp: number) { export function setAnimationTimeout(callback: () => void, timestamp: number) {
let i = timed_tasks.length; let i = timed_tasks.length;
let v; let v;
const task = { c: callback, t: timestamp }; const task = { c: callback, t: timestamp };
if (i) { if (i) {
while (i > 0 && timestamp > (v = timed_tasks[i - 1]).t) { while (i > 0 && timestamp > (v = timed_tasks[i - 1]).t) timed_tasks[i--] = v;
// bubble sort descending until timestamp < task.timestamp
timed_tasks[i--] = v;
}
timed_tasks[i] = task; timed_tasks[i] = task;
} else { } else {
timed_tasks.push(task); timed_tasks.push(task);
add((now) => { add((now) => {
let i = timed_tasks.length; let i = timed_tasks.length;
while (i > 0 && now >= timed_tasks[--i].t) { while (i > 0 && now >= timed_tasks[--i].t) timed_tasks.pop().c(now);
// pop() until now < task.timestamp return !!timed_tasks.length;
timed_tasks.pop().c(now);
}
return timed_tasks.length;
}); });
} }
return () => { return () => {
@ -81,11 +79,10 @@ export function raf_timeout(callback: () => void, timestamp: number) {
if (~index) timed_tasks.splice(index, 1); if (~index) timed_tasks.splice(index, 1);
}; };
} }
export function loopThen(delay: number, run: (now: number) => void, stop: () => void, end_time: number) { export const loopThen = (run: (now: number) => void, stop: () => void, duration: number, end_time: number) =>
const fn = () => add((now) => (now < end_time ? (run(now), true) : (stop(), false))); add((t) => {
if (delay < 16) return fn(); t = (end_time - t) / duration;
else { if (t >= 1) return void run(1), stop();
let cancel = raf_timeout(() => (cancel = fn()), now() + delay - 16.6667); else if (t >= 0) run(t);
return () => cancel(); return true;
} });
}

@ -1,34 +1,25 @@
import { run_all } from './utils';
import { set_current_component } from './lifecycle'; import { set_current_component } from './lifecycle';
export const dirty_components = []; const dirty_components = [];
export const intros = { enabled: false };
export const binding_callbacks = [];
const render_callbacks = [];
const flush_callbacks = [];
const resolved_promise = Promise.resolve(); const resolved_promise = Promise.resolve();
let update_scheduled = false;
export function schedule_update() { let update_scheduled = false;
export function schedule_update(component) {
dirty_components.push(component);
if (update_scheduled) return; if (update_scheduled) return;
update_scheduled = true; (update_scheduled = true), resolved_promise.then(flush);
resolved_promise.then(flush);
} }
export function tick() { export function tick() {
schedule_update(); if (!update_scheduled) (update_scheduled = true), resolved_promise.then(flush);
return resolved_promise; return resolved_promise;
} }
export const binding_callbacks = [];
const render_callbacks = [];
export const add_render_callback = (fn) => render_callbacks.push(fn);
export function add_render_callback(fn) { const flush_callbacks = [];
render_callbacks.push(fn); export const add_flush_callback = (fn) => flush_callbacks.push(fn);
}
export function add_flush_callback(fn) {
flush_callbacks.push(fn);
}
let flushing = false; let flushing = false;
const seen_callbacks = new Set(); const seen_callbacks = new Set();
@ -36,45 +27,36 @@ export function flush() {
if (flushing) return; if (flushing) return;
flushing = true; flushing = true;
do { for (; dirty_components.length; ) {
// update components + beforeUpdate // update components + beforeUpdate
for (let i = 0, component; i < dirty_components.length; i++) { for (let i = 0, $$; i < dirty_components.length; i++) {
set_current_component((component = dirty_components[i])); ({ $$ } = set_current_component(dirty_components[i]));
update(component.$$); if ($$.fragment === null) continue;
const { update, before_update, dirty, after_update } = $$;
update();
for (let j = 0; j < before_update.length; j++) before_update[j]();
$$.dirty = [-1];
if ($$.fragment) $$.fragment.p($$.ctx, dirty);
render_callbacks.push(...after_update);
} }
dirty_components.length = 0; dirty_components.length = 0;
// update bindings // update bindings in reverse order
for (let i = 0; i < binding_callbacks.length; i++) { for (let i = binding_callbacks.length - 1; i; i--) binding_callbacks[i]();
binding_callbacks[i]();
}
binding_callbacks.length = 0; binding_callbacks.length = 0;
// afterUpdate // afterUpdate
for (let i = 0, callback; i < render_callbacks.length; i++) { for (let i = 0, callback; i < render_callbacks.length; i++) {
if (seen_callbacks.has((callback = render_callbacks[i]))) continue; if (seen_callbacks.has((callback = render_callbacks[i]))) continue;
seen_callbacks.add(callback); seen_callbacks.add(callback), callback();
callback();
} }
render_callbacks.length = 0; render_callbacks.length = 0;
} while (dirty_components.length);
for (let i = 0; i < flush_callbacks.length; i++) {
flush_callbacks[i]();
} }
for (let i = 0; i < flush_callbacks.length; i++) flush_callbacks[i]();
flush_callbacks.length = 0; flush_callbacks.length = 0;
seen_callbacks.clear();
update_scheduled = false; update_scheduled = false;
flushing = false; flushing = false;
seen_callbacks.clear();
}
function update($$) {
if ($$.fragment === null) return;
$$.update();
run_all($$.before_update);
const dirty = $$.dirty;
$$.dirty = [-1];
$$.fragment && $$.fragment.p($$.ctx, dirty);
$$.after_update.forEach(add_render_callback);
} }

@ -1,71 +1,74 @@
import { element } from './dom'; import { element } from './dom';
import { raf } from './environment';
const enum SVELTE {
RULE = `__svelte_`,
STYLESHEET = `__svelte_stylesheet`,
RULESET = `__svelte_rules`,
}
const svelte_rule = `__svelte_`; let FRAME_RATE;
function calc_framerate() {
const f24 = 1000 / 24,
f60 = 1000 / 60,
f144 = 1000 / 144;
raf((t1) => {
raf((d) => {
FRAME_RATE = (d = d - t1) > f144 ? f144 : d < f24 ? f24 : d;
});
});
return (FRAME_RATE = f60);
}
interface ExtendedDoc extends Document { interface ExtendedDoc extends Document {
__svelte_stylesheet: CSSStyleSheet; [SVELTE.STYLESHEET]: CSSStyleSheet;
__svelte_rules: Set<string>; [SVELTE.RULESET]: Set<string>;
} }
const active_documents = new Set<ExtendedDoc>(); const active_documents = new Set<ExtendedDoc>();
let running_animations = 0; let running_animations = 0;
function rulesheet({ ownerDocument }): [CSSStyleSheet, Set<string>] { function add_rule(node, name, rule): [CSSStyleSheet, Set<string>] {
const doc = ownerDocument as ExtendedDoc; const { ownerDocument } = node;
if (!active_documents.has(doc)) { if (!active_documents.has(ownerDocument)) {
active_documents.add(doc); active_documents.add(ownerDocument);
if (!doc.__svelte_stylesheet) { if (!(SVELTE.STYLESHEET in ownerDocument))
doc.__svelte_stylesheet = doc.head.appendChild(element('style')).sheet as CSSStyleSheet; ownerDocument[SVELTE.STYLESHEET] = ownerDocument.head.appendChild(element('style')).sheet;
} if (!(SVELTE.RULESET in ownerDocument)) ownerDocument[SVELTE.RULESET] = new Set();
if (!doc.__svelte_rules) {
doc.__svelte_rules = new Set();
} }
} return [ownerDocument[SVELTE.STYLESHEET], ownerDocument[SVELTE.RULESET]];
return [doc.__svelte_stylesheet, doc.__svelte_rules];
} }
// https://github.com/darkskyapp/string-hash/blob/master/index.js // https://github.com/darkskyapp/string-hash/blob/master/index.js
function hash(str: string) { function hash(str: string) {
let hash = 5381; let hash = 5381;
let i = str.length; let i = str.length;
while (i--) hash = ((hash << 5) - hash) ^ str.charCodeAt(i); while (i--) hash = ((hash << 5) - hash) ^ str.charCodeAt(i);
return hash >>> 0; return hash >>> 0;
} }
export function generate_rule( const gen = (t, step, css) => {
node: HTMLElement,
a: number,
b: number,
duration: number,
delay: number,
ease: (t: number) => number,
fn: (t: number, u: number) => string
) {
const step = 16.6667 / duration;
let rule = '{\n'; let rule = '{\n';
for (let p = 0, t = 0; p <= 1; p += step) { for (; t < 1; t += step) rule += `${100 * t}%{${css(t)}}\n`;
t = a + (b - a) * ease(p); rule += `100% {${css(1)}}\n}`;
rule += p * 100 + `%{${fn(t, 1 - t)}}\n`; const name = SVELTE.RULE + hash(rule);
} return [name, `@keyframes ${name} ${rule}`];
rule += `100% {${fn(b, 1 - b)}}\n}`; };
const name = `${svelte_rule}${hash(rule)}`; export function animate_css(css: (t: number) => string, node: HTMLElement, duration: number, t = 0) {
const [stylesheet, rules] = rulesheet(node); const [name, rule] = gen(t, duration / (FRAME_RATE || calc_framerate()), css);
const [stylesheet, rules] = add_rule(node, rule);
if (!rules.has(name)) { if (!rules.has(name)) {
rules.add(name); rules.add(name);
stylesheet.insertRule(`@keyframes ${name} ${rule}`, stylesheet.cssRules.length); stylesheet.insertRule(rule, stylesheet.cssRules.length);
} }
const previous = node.style.animation || ''; const previous = node.style.animation;
node.style.animation = `${previous ? `${previous}, ` : ``}${name} ${duration}ms linear ${delay}ms 1 both`; node.style.animation = (previous ? previous + ', ' : '') + `${duration}ms linear 0ms 1 normal both running ${name}`;
running_animations++; running_animations++;
return () => { return () => {
const prev = (node.style.animation || '').split(', '); const prev = (node.style.animation || '').split(', ');
const next = prev.filter((anim) => !anim.includes(name)); const next = prev.filter((anim) => !anim.includes(name));
if (prev.length === next.length) return; if (prev.length !== next.length) node.style.animation = next.join(', ');
node.style.animation = next.join(', ');
if (--running_animations) return; if (--running_animations) return;
active_documents.forEach(({ __svelte_stylesheet, __svelte_rules }) => { active_documents.forEach(({ [SVELTE.STYLESHEET]: stylesheet, [SVELTE.RULESET]: ruleset }) => {
let i = __svelte_stylesheet.cssRules.length; let i = stylesheet.cssRules.length;
while (i--) __svelte_stylesheet.deleteRule(i); while (i--) stylesheet.deleteRule(i);
__svelte_rules.clear(); ruleset.clear();
}); });
active_documents.clear(); active_documents.clear();
}; };
@ -75,7 +78,7 @@ export function delete_rule(node: HTMLElement, name?: string) {
const next = previous.filter( const next = previous.filter(
name name
? (anim) => anim.indexOf(name) < 0 // remove specific animation ? (anim) => anim.indexOf(name) < 0 // remove specific animation
: (anim) => anim.indexOf('__svelte') === -1 // remove all Svelte animations : (anim) => anim.indexOf(SVELTE.RULE) === -1 // remove all Svelte animations
); );
const deleted = previous.length - next.length; const deleted = previous.length - next.length;
if (deleted) { if (deleted) {

@ -1,10 +1,11 @@
import { identity as linear, run_all, is_function } from './utils'; import { run_all } from './utils';
import { now } from './environment'; import { now } from './environment';
import { raf_timeout, loopThen } from './loop'; import { setAnimationTimeout, loopThen } from './loop';
import { generate_rule } from './style_manager'; import { animate_css } from './style_manager';
import { custom_event } from './dom'; import { custom_event } from './dom';
import { TransitionConfig } from '../transition'; import { TransitionConfig } from '../transition';
import { add_render_callback, add_flush_callback } from './scheduler'; import { add_render_callback, add_flush_callback } from './scheduler';
import { Fragment } from './Component';
function startStopDispatcher(node: Element, direction: boolean) { function startStopDispatcher(node: Element, direction: boolean) {
add_render_callback(() => node.dispatchEvent(custom_event(`${direction ? 'intro' : 'outro'}start`))); add_render_callback(() => node.dispatchEvent(custom_event(`${direction ? 'intro' : 'outro'}start`)));
@ -12,13 +13,13 @@ function startStopDispatcher(node: Element, direction: boolean) {
} }
const outroing = new Set(); const outroing = new Set();
let outros;
let outros;
export function group_outros() { export function group_outros() {
outros = { outros = {
/* parent group */ p: outros, /* parent group */ p: outros,
/* remaining outros */ r: 0,
/* callbacks */ c: [], /* callbacks */ c: [],
/* remaining outros */ r: 0,
}; };
} }
@ -27,13 +28,13 @@ export function check_outros() {
outros = outros.p; outros = outros.p;
} }
export function transition_in(block, local?: 0 | 1) { export function transition_in(block: Fragment, local?: 0 | 1) {
if (!block || !block.i) return; if (!block || !block.i) return;
outroing.delete(block); outroing.delete(block);
block.i(local); block.i(local);
} }
export function transition_out(block, local?: 0 | 1, detach?: 0 | 1, callback?: () => void) { export function transition_out(block: Fragment, local?: 0 | 1, detach?: 0 | 1, callback?: () => void) {
if (!block || !block.o || outroing.has(block)) return; if (!block || !block.o || outroing.has(block)) return;
outroing.add(block); outroing.add(block);
outros.c.push(() => { outros.c.push(() => {
@ -46,15 +47,16 @@ export function transition_out(block, local?: 0 | 1, detach?: 0 | 1, callback?:
block.o(local); block.o(local);
} }
const null_transition: TransitionConfig = { duration: 0 }; const eased = (fn: (t: number) => any, easing: (t: number) => number) => (easing ? (t: number) => fn(easing(t)) : fn);
const runner = (fn: (t0: number, t1: number) => any, reversed: boolean) =>
reversed ? (t: number) => fn(1 - t, t) : (t: number) => fn(t, 1 - t);
type TransitionFn = (node: HTMLElement, params: any) => TransitionConfig; type TransitionFn = (node: HTMLElement, params: any) => TransitionConfig;
export function run_transition( export function run_transition(
node: HTMLElement, node: HTMLElement,
fn: TransitionFn, fn: TransitionFn,
is_intro: boolean, is_intro = true,
params?: any, params = {},
reversed_from?: number reversed_from = -1
): StopResetReverse { ): StopResetReverse {
let config = fn(node, params); let config = fn(node, params);
let running = true; let running = true;
@ -62,51 +64,48 @@ export function run_transition(
let cancel_css; let cancel_css;
let cancel_raf; let cancel_raf;
let dispatch_end; let dispatch_end;
let start_time; let end_time;
const group = outros; const group = outros;
if (!is_intro) group.r++; if (!is_intro) group.r++;
function start({ delay = 0, duration = 300, easing = linear, tick, css } = null_transition) { function start({ delay = 0, duration = 300, easing, tick, css }: TransitionConfig) {
if (!running) return; if (!running) return;
const run = tick && (is_intro ? tick : (a, b) => tick(b, a)); const start_time = ~reversed_from ? reversed_from : now() + delay;
if (reversed_from) delay += duration - (now() - reversed_from); end_time = start_time + duration;
start_time = now() + delay; if (css)
const end_time = start_time + duration; cancel_css = animate_css(
cancel_css = css && generate_rule(node, +!is_intro, +is_intro, duration, delay, easing, css); runner(eased(css, easing), is_intro),
dispatch_end = startStopDispatcher(node, is_intro); node,
cancel_raf = cancel_raf = !run duration,
? raf_timeout(stop, end_time) (end_time - start_time) / duration
: loopThen(
delay,
(t) => ((t = easing((t - start_time) / duration)), run(t, 1 - t)),
() => (run(1, 0), stop()),
end_time
); );
dispatch_end = startStopDispatcher(node, is_intro);
cancel_raf = tick
? loopThen(runner(eased(tick, easing), is_intro), stop, duration, end_time)
: setAnimationTimeout(stop, end_time);
} }
function stop(reset_reverse?: 1 | -1) {
function stop(reset_reverse?: 1 | 2) { if (!is_intro && 1 === reset_reverse && config && 'tick' in config) config.tick(1, 0);
if (!is_intro && reset_reverse === 1 && config && 'tick' in config) config.tick(1, 0);
if (!running) return; if (!running) return;
else running = false; else running = false;
if (cancel_css) cancel_css(); if (cancel_css) cancel_css();
if (cancel_raf) cancel_raf(); if (cancel_raf) cancel_raf();
if (dispatch_end) dispatch_end(); if (dispatch_end) dispatch_end();
if (!is_intro && !--group.r) run_all(group.c); if (!is_intro && !--group.r) for (let i = 0; i < group.c.length; i++) group.c[i]();
if (reset_reverse === 2) return run_transition(node, fn, !is_intro, params, start_time); if (!~reset_reverse) return run_transition(node, fn, !is_intro, params, end_time);
else if (!~reversed_from) running_bidi.delete(node); else if (!~reversed_from) running_bidi.delete(node);
} }
// @ts-ignore // @ts-ignore
if (is_function(config)) add_flush_callback(() => start((config = config()))); if (typeof config === 'function') add_flush_callback(() => start((config = config())));
else start(config); else start(config);
return stop; return stop;
} }
export type StopResetReverse = (reset_reverse?: 1 | 2) => StopResetReverse; export type StopResetReverse = (reset_reverse?: 1 | -1) => StopResetReverse;
const running_bidi: Map<HTMLElement, StopResetReverse> = new Map(); const running_bidi: Map<HTMLElement, StopResetReverse> = new Map();
export function run_bidirectional_transition(node: HTMLElement, fn: TransitionFn, is_intro: boolean, params: any) { export function run_bidirectional_transition(node: HTMLElement, fn: TransitionFn, is_intro: boolean, params: any) {
if (running_bidi.has(node)) { let cancel;
running_bidi.set(node, running_bidi.get(node)(2)); if (running_bidi.has(node)) running_bidi.set(node, (cancel = running_bidi.get(node)(-1)));
} else { else running_bidi.set(node, (cancel = run_transition(node, fn, is_intro, params, -1)));
running_bidi.set(node, run_transition(node, fn, is_intro, params, -1)); return cancel;
}
} }

@ -1,6 +1,6 @@
export function noop() {} export function noop() {}
export const identity = x => x; export const identity = (x) => x;
export function assign<T, S>(tar: T, src: S): T & S { export function assign<T, S>(tar: T, src: S): T & S {
// @ts-ignore // @ts-ignore
@ -14,7 +14,7 @@ export function is_promise<T = any>(value: any): value is PromiseLike<T> {
export function add_location(element, file, line, column, char) { export function add_location(element, file, line, column, char) {
element.__svelte_meta = { element.__svelte_meta = {
loc: { file, line, column, char } loc: { file, line, column, char },
}; };
} }
@ -35,7 +35,7 @@ export function is_function(thing: any): thing is Function {
} }
export function safe_not_equal(a, b) { export function safe_not_equal(a, b) {
return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function'); return a != a ? b == b : a !== b || (a && typeof a === 'object') || typeof a === 'function';
} }
export function not_equal(a, b) { export function not_equal(a, b) {
@ -49,16 +49,14 @@ export function validate_store(store, name) {
} }
export function subscribe(store, ...callbacks) { export function subscribe(store, ...callbacks) {
if (store == null) { if (store == null) return noop;
return noop;
}
const unsub = store.subscribe(...callbacks); const unsub = store.subscribe(...callbacks);
return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub; return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub;
} }
export function get_store_value(store) { export function get_store_value(store) {
let value; let value;
subscribe(store, _ => value = _)(); subscribe(store, (_) => (value = _))();
return value; return value;
} }
@ -74,9 +72,7 @@ export function create_slot(definition, ctx, $$scope, fn) {
} }
export function get_slot_context(definition, ctx, $$scope, fn) { export function get_slot_context(definition, ctx, $$scope, fn) {
return definition[1] && fn return definition[1] && fn ? assign($$scope.ctx.slice(), definition[1](fn(ctx))) : $$scope.ctx;
? assign($$scope.ctx.slice(), definition[1](fn(ctx)))
: $$scope.ctx;
} }
export function get_slot_changes(definition, $$scope, dirty, fn) { export function get_slot_changes(definition, $$scope, dirty, fn) {
@ -112,7 +108,8 @@ export function exclude_internal_props(props) {
export function compute_rest_props(props, keys) { export function compute_rest_props(props, keys) {
const rest = {}; const rest = {};
keys = new Set(keys); keys = new Set(keys);
for (const k in props) if (!keys.has(k) && k[0] !== '$') rest[k] = props[k]; let k: string;
for (k in props) if (!keys.has(k) && k[0] !== '$') rest[k] = props[k];
return rest; return rest;
} }
@ -139,3 +136,5 @@ export const has_prop = (obj, prop) => Object.prototype.hasOwnProperty.call(obj,
export function action_destroyer(action_result) { export function action_destroyer(action_result) {
return action_result && is_function(action_result.destroy) ? action_result.destroy : noop; return action_result && is_function(action_result.destroy) ? action_result.destroy : noop;
} }
export const minmax = (min: number, max: number) => (v: number) => (v > min ? (v > max ? max : v) : min);

@ -1,5 +1,4 @@
import { cubicOut, cubicInOut, linear } from 'svelte/easing'; import { cubicOut, cubicInOut, linear } from 'svelte/easing';
import { is_function } from 'svelte/internal';
type EasingFunction = (t: number) => number; type EasingFunction = (t: number) => number;
@ -7,8 +6,8 @@ export interface TransitionConfig {
delay?: number; delay?: number;
duration?: number; duration?: number;
easing?: EasingFunction; easing?: EasingFunction;
css?: (t: number, u: number) => string; css?: (t: number, u?: number) => string;
tick?: (t: number, u: number) => void; tick?: (t: number, u?: number) => void;
} }
interface BlurParams { interface BlurParams {
@ -96,21 +95,20 @@ export function slide(node: Element, { delay = 0, duration = 400, easing = cubic
const margin_bottom = parseFloat(style.marginBottom); const margin_bottom = parseFloat(style.marginBottom);
const border_top_width = parseFloat(style.borderTopWidth); const border_top_width = parseFloat(style.borderTopWidth);
const border_bottom_width = parseFloat(style.borderBottomWidth); const border_bottom_width = parseFloat(style.borderBottomWidth);
return { return {
delay, delay,
duration, duration,
easing, easing,
css: (t) => css: (t) => `
`overflow: hidden;` + overflow: hidden;
`opacity: ${Math.min(t * 20, 1) * opacity};` + opacity: ${Math.min(t * 20, 1) * opacity};
`height: ${t * height}px;` + height: ${t * height}px;
`padding-top: ${t * padding_top}px;` + padding-top: ${t * padding_top}px;
`padding-bottom: ${t * padding_bottom}px;` + padding-bottom: ${t * padding_bottom}px;
`margin-top: ${t * margin_top}px;` + margin-top: ${t * margin_top}px;
`margin-bottom: ${t * margin_bottom}px;` + margin-bottom: ${t * margin_bottom}px;
`border-top-width: ${t * border_top_width}px;` + border-top-width: ${t * border_top_width}px;
`border-bottom-width: ${t * border_bottom_width}px;`, border-bottom-width: ${t * border_bottom_width}px;`,
}; };
} }
@ -129,10 +127,8 @@ export function scale(
const style = getComputedStyle(node); const style = getComputedStyle(node);
const target_opacity = +style.opacity; const target_opacity = +style.opacity;
const transform = style.transform === 'none' ? '' : style.transform; const transform = style.transform === 'none' ? '' : style.transform;
const sd = 1 - start; const sd = 1 - start;
const od = target_opacity * (1 - opacity); const od = target_opacity * (1 - opacity);
return { return {
delay, delay,
duration, duration,
@ -152,27 +148,13 @@ interface DrawParams {
} }
export function draw( export function draw(
node: SVGElement & { getTotalLength(): number }, node: SVGPathElement | SVGGeometryElement,
{ delay = 0, speed, duration, easing = cubicInOut }: DrawParams { delay = 0, speed, duration, easing = cubicInOut }: DrawParams
): TransitionConfig { ): TransitionConfig {
const len = node.getTotalLength(); const len = node.getTotalLength();
if (duration === undefined) duration = speed ? len / speed : 800;
if (duration === undefined) { else if (typeof duration === 'function') duration = duration(len);
if (speed === undefined) { return { delay, duration, easing, css: (t, u) => `stroke-dasharray: ${t * len} ${u * len};` };
duration = 800;
} else {
duration = len / speed;
}
} else if (typeof duration === 'function') {
duration = duration(len);
}
return {
delay,
duration,
easing,
css: (t, u) => `stroke-dasharray: ${t * len} ${u * len};`,
};
} }
interface CrossfadeParams { interface CrossfadeParams {
@ -190,12 +172,13 @@ type ElementMap = Map<string, Element>;
export function crossfade({ export function crossfade({
delay: default_delay = 0, delay: default_delay = 0,
easing: default_easing = cubicOut,
duration: default_duration = (d) => Math.sqrt(d) * 30, duration: default_duration = (d) => Math.sqrt(d) * 30,
easing: default_easing = cubicOut,
fallback, fallback,
}: CrossFadeConfig) { }: CrossFadeConfig) {
const to_receive: ElementMap = new Map(); const to_receive: ElementMap = new Map();
const to_send: ElementMap = new Map(); const to_send: ElementMap = new Map();
function crossfade( function crossfade(
from_node: Element, from_node: Element,
to_node: Element, to_node: Element,
@ -207,31 +190,30 @@ export function crossfade({
const dy = from.top - to.top; const dy = from.top - to.top;
const dw = from.width / to.width; const dw = from.width / to.width;
const dh = from.height / to.height; const dh = from.height / to.height;
const d = Math.sqrt(dx * dx + dy * dy); const { transform, opacity } = getComputedStyle(to_node);
const style = getComputedStyle(to_node); const prev = transform === 'none' ? '' : transform;
const transform = style.transform === 'none' ? '' : style.transform;
const opacity = +style.opacity;
return { return {
delay, delay,
easing, easing,
duration: is_function(duration) ? duration(d) : duration, duration: typeof duration === 'function' ? duration(Math.sqrt(dx * dx + dy * dy)) : duration,
css: (t, u) => ` css: (t, u) => `
opacity: ${t * opacity}; opacity: ${t * +opacity};
transform-origin: top left; transform-origin: top left;
transform: ${transform} translate(${u * dx}px,${u * dy}px) scale(${t + (1 - t) * dw}, ${t + (1 - t) * dh}); transform: ${prev} translate(${u * dx}px,${u * dy}px) scale(${t + (1 - t) * dw}, ${t + (1 - t) * dh});
`, `,
} as TransitionConfig; } as TransitionConfig;
} }
function transition(a: ElementMap, b: ElementMap, is_intro: boolean) { function transition(a: ElementMap, b: ElementMap, is_intro: boolean) {
return (node: Element, params: MarkedCrossFadeConfig) => { return (node: Element, params: MarkedCrossFadeConfig) => {
a.set(params.key, node); const key = params.key;
a.set(key, node);
return () => { return () => {
if (b.has(params.key)) { if (b.has(key)) {
const from_node = b.get(params.key); const from_node = b.get(key);
b.delete(params.key); b.delete(key);
return crossfade(from_node, node, params); return crossfade(from_node, node, params);
} else { } else {
a.delete(params.key); a.delete(key);
return fallback && fallback(node, params, is_intro); return fallback && fallback(node, params, is_intro);
} }
}; };

Loading…
Cancel
Save