block by curran 71dfa9cf182473fd2cb2f2d027f3828e

Hello Svelte

Full Screen

A first experiment getting to know Svelte 3.

Bootstrapped from Svelte Template.

Strongly considering adding Svelte support to VizHub.

Seeing huge potential for Svelte with interactive data visualization!

index.html

<!doctype html>
<html>
<head>
	<meta charset='utf8'>
	<meta name='viewport' content='width=device-width'>

	<title>Svelte app</title>

	<link rel='stylesheet' href='global.css'>
</head>

<body>
	<script src='bundle.js'></script>
</body>
</html>

App.svelte

<script>
	export let width;
	export let height;
  export let n = 50;

	let numbers = [];

  const f = (x, t) => Math.sin(x * t + t) * 200;

  const setNumbers = t => {
    for(let i = 0; i < n; i++){
      const x = i / n;
      numbers[i] = f(x, t);
    }
  };

  const animate = timestamp => {
    setNumbers(timestamp / 1000);
    requestAnimationFrame(animate);
  };
  requestAnimationFrame(animate);
</script>

<svg width={width} height={height}>
  {#each numbers as y, i}
    <circle
      cx={i / n * width}
      cy={y + height / 2}
      r="10"
    />
    {#if i < n - 1}
      <line
        x1={i / n * width}
        y1={y + height / 2}
        x2={(i + 1) / n * width}
        y2={numbers[i + 1] + height / 2}
      />
    {/if}
  {/each}
</svg>

bundle.js

var app = (function () {
	'use strict';

	function noop() {}

	function add_location(element, file, line, column, char) {
		element.__svelte_meta = {
			loc: { file, line, column, char }
		};
	}

	function run(fn) {
		return fn();
	}

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

	function run_all(fns) {
		fns.forEach(run);
	}

	function is_function(thing) {
		return typeof thing === 'function';
	}

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

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

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

	function destroy_each(iterations, detaching) {
		for (let i = 0; i < iterations.length; i += 1) {
			if (iterations[i]) iterations[i].d(detaching);
		}
	}

	function svg_element(name) {
		return document.createElementNS('http://www.w3.org/2000/svg', name);
	}

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

	function empty() {
		return text('');
	}

	function attr(node, attribute, value) {
		if (value == null) node.removeAttribute(attribute);
		else node.setAttribute(attribute, value);
	}

	function children(element) {
		return Array.from(element.childNodes);
	}

	let current_component;

	function set_current_component(component) {
		current_component = component;
	}

	const dirty_components = [];

	let update_promise;
	const binding_callbacks = [];
	const render_callbacks = [];
	const flush_callbacks = [];

	function schedule_update() {
		if (!update_promise) {
			update_promise = Promise.resolve();
			update_promise.then(flush);
		}
	}

	function add_render_callback(fn) {
		render_callbacks.push(fn);
	}

	function flush() {
		const seen_callbacks = new Set();

		do {
			// first, call beforeUpdate functions
			// and update components
			while (dirty_components.length) {
				const component = dirty_components.shift();
				set_current_component(component);
				update(component.$$);
			}

			while (binding_callbacks.length) binding_callbacks.shift()();

			// then, once components are updated, call
			// afterUpdate functions. This may cause
			// subsequent updates...
			while (render_callbacks.length) {
				const callback = render_callbacks.pop();
				if (!seen_callbacks.has(callback)) {
					callback();

					// ...so guard against infinite loops
					seen_callbacks.add(callback);
				}
			}
		} while (dirty_components.length);

		while (flush_callbacks.length) {
			flush_callbacks.pop()();
		}

		update_promise = null;
	}

	function update($$) {
		if ($$.fragment) {
			$$.update($$.dirty);
			run_all($$.before_render);
			$$.fragment.p($$.dirty, $$.ctx);
			$$.dirty = null;

			$$.after_render.forEach(add_render_callback);
		}
	}

	function mount_component(component, target, anchor) {
		const { fragment, on_mount, on_destroy, after_render } = component.$$;

		fragment.m(target, anchor);

		// onMount happens after the initial afterUpdate. Because
		// afterUpdate callbacks happen in reverse order (inner first)
		// we schedule onMount callbacks before afterUpdate callbacks
		add_render_callback(() => {
			const new_on_destroy = on_mount.map(run).filter(is_function);
			if (on_destroy) {
				on_destroy.push(...new_on_destroy);
			} else {
				// Edge case - component was destroyed immediately,
				// most likely as a result of a binding initialising
				run_all(new_on_destroy);
			}
			component.$$.on_mount = [];
		});

		after_render.forEach(add_render_callback);
	}

	function destroy(component, detaching) {
		if (component.$$) {
			run_all(component.$$.on_destroy);
			component.$$.fragment.d(detaching);

			// TODO null out other refs, including component.$$ (but need to
			// preserve final state?)
			component.$$.on_destroy = component.$$.fragment = null;
			component.$$.ctx = {};
		}
	}

	function make_dirty(component, key) {
		if (!component.$$.dirty) {
			dirty_components.push(component);
			schedule_update();
			component.$$.dirty = {};
		}
		component.$$.dirty[key] = true;
	}

	function init(component, options, instance, create_fragment, not_equal$$1, prop_names) {
		const parent_component = current_component;
		set_current_component(component);

		const props = options.props || {};

		const $$ = component.$$ = {
			fragment: null,
			ctx: null,

			// state
			props: prop_names,
			update: noop,
			not_equal: not_equal$$1,
			bound: blank_object(),

			// lifecycle
			on_mount: [],
			on_destroy: [],
			before_render: [],
			after_render: [],
			context: new Map(parent_component ? parent_component.$$.context : []),

			// everything else
			callbacks: blank_object(),
			dirty: null
		};

		let ready = false;

		$$.ctx = instance
			? instance(component, props, (key, value) => {
				if ($$.ctx && not_equal$$1($$.ctx[key], $$.ctx[key] = value)) {
					if ($$.bound[key]) $$.bound[key](value);
					if (ready) make_dirty(component, key);
				}
			})
			: props;

		$$.update();
		ready = true;
		run_all($$.before_render);
		$$.fragment = create_fragment($$.ctx);

		if (options.target) {
			if (options.hydrate) {
				$$.fragment.l(children(options.target));
			} else {
				$$.fragment.c();
			}

			if (options.intro && component.$$.fragment.i) component.$$.fragment.i();
			mount_component(component, options.target, options.anchor);
			flush();
		}

		set_current_component(parent_component);
	}

	class SvelteComponent {
		$destroy() {
			destroy(this, true);
			this.$destroy = noop;
		}

		$on(type, callback) {
			const callbacks = (this.$$.callbacks[type] || (this.$$.callbacks[type] = []));
			callbacks.push(callback);

			return () => {
				const index = callbacks.indexOf(callback);
				if (index !== -1) callbacks.splice(index, 1);
			};
		}

		$set() {
			// overridden by instance, if it has props
		}
	}

	class SvelteComponentDev extends SvelteComponent {
		constructor(options) {
			if (!options || (!options.target && !options.$$inline)) {
				throw new Error(`'target' is a required option`);
			}

			super();
		}

		$destroy() {
			super.$destroy();
			this.$destroy = () => {
				console.warn(`Component was already destroyed`); // eslint-disable-line no-console
			};
		}
	}

	/* App.svelte generated by Svelte v3.0.0 */

	const file = "App.svelte";

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

	// (31:4) {#if i < n - 1}
	function create_if_block(ctx) {
		var line, line_x__value, line_y__value, line_x__value_1, line_y__value_1;

		return {
			c: function create() {
				line = svg_element("line");
				attr(line, "x1", line_x__value = ctx.i / ctx.n * ctx.width);
				attr(line, "y1", line_y__value = ctx.y + ctx.height / 2);
				attr(line, "x2", line_x__value_1 = (ctx.i + 1) / ctx.n * ctx.width);
				attr(line, "y2", line_y__value_1 = ctx.numbers[ctx.i + 1] + ctx.height / 2);
				add_location(line, file, 31, 6, 587);
			},

			m: function mount(target, anchor) {
				insert(target, line, anchor);
			},

			p: function update(changed, ctx) {
				if ((changed.n || changed.width) && line_x__value !== (line_x__value = ctx.i / ctx.n * ctx.width)) {
					attr(line, "x1", line_x__value);
				}

				if ((changed.numbers || changed.height) && line_y__value !== (line_y__value = ctx.y + ctx.height / 2)) {
					attr(line, "y1", line_y__value);
				}

				if ((changed.n || changed.width) && line_x__value_1 !== (line_x__value_1 = (ctx.i + 1) / ctx.n * ctx.width)) {
					attr(line, "x2", line_x__value_1);
				}

				if ((changed.numbers || changed.height) && line_y__value_1 !== (line_y__value_1 = ctx.numbers[ctx.i + 1] + ctx.height / 2)) {
					attr(line, "y2", line_y__value_1);
				}
			},

			d: function destroy(detaching) {
				if (detaching) {
					detach(line);
				}
			}
		};
	}

	// (25:2) {#each numbers as y, i}
	function create_each_block(ctx) {
		var circle, circle_cx_value, circle_cy_value, if_block_anchor;

		var if_block = (ctx.i < ctx.n - 1) && create_if_block(ctx);

		return {
			c: function create() {
				circle = svg_element("circle");
				if (if_block) if_block.c();
				if_block_anchor = empty();
				attr(circle, "cx", circle_cx_value = ctx.i / ctx.n * ctx.width);
				attr(circle, "cy", circle_cy_value = ctx.y + ctx.height / 2);
				attr(circle, "r", "10");
				add_location(circle, file, 25, 4, 482);
			},

			m: function mount(target, anchor) {
				insert(target, circle, anchor);
				if (if_block) if_block.m(target, anchor);
				insert(target, if_block_anchor, anchor);
			},

			p: function update(changed, ctx) {
				if ((changed.n || changed.width) && circle_cx_value !== (circle_cx_value = ctx.i / ctx.n * ctx.width)) {
					attr(circle, "cx", circle_cx_value);
				}

				if ((changed.numbers || changed.height) && circle_cy_value !== (circle_cy_value = ctx.y + ctx.height / 2)) {
					attr(circle, "cy", circle_cy_value);
				}

				if (ctx.i < ctx.n - 1) {
					if (if_block) {
						if_block.p(changed, ctx);
					} else {
						if_block = create_if_block(ctx);
						if_block.c();
						if_block.m(if_block_anchor.parentNode, if_block_anchor);
					}
				} else if (if_block) {
					if_block.d(1);
					if_block = null;
				}
			},

			d: function destroy(detaching) {
				if (detaching) {
					detach(circle);
				}

				if (if_block) if_block.d(detaching);

				if (detaching) {
					detach(if_block_anchor);
				}
			}
		};
	}

	function create_fragment(ctx) {
		var svg;

		var each_value = ctx.numbers;

		var each_blocks = [];

		for (var i = 0; i < each_value.length; i += 1) {
			each_blocks[i] = create_each_block(get_each_context(ctx, each_value, i));
		}

		return {
			c: function create() {
				svg = svg_element("svg");

				for (var i = 0; i < each_blocks.length; i += 1) {
					each_blocks[i].c();
				}
				attr(svg, "width", ctx.width);
				attr(svg, "height", ctx.height);
				add_location(svg, file, 23, 0, 416);
			},

			l: function claim(nodes) {
				throw new Error("options.hydrate only works if the component was compiled with the `hydratable: true` option");
			},

			m: function mount(target, anchor) {
				insert(target, svg, anchor);

				for (var i = 0; i < each_blocks.length; i += 1) {
					each_blocks[i].m(svg, null);
				}
			},

			p: function update(changed, ctx) {
				if (changed.n || changed.width || changed.numbers || changed.height) {
					each_value = ctx.numbers;

					for (var i = 0; i < each_value.length; i += 1) {
						const child_ctx = get_each_context(ctx, each_value, i);

						if (each_blocks[i]) {
							each_blocks[i].p(changed, child_ctx);
						} else {
							each_blocks[i] = create_each_block(child_ctx);
							each_blocks[i].c();
							each_blocks[i].m(svg, null);
						}
					}

					for (; i < each_blocks.length; i += 1) {
						each_blocks[i].d(1);
					}
					each_blocks.length = each_value.length;
				}

				if (changed.width) {
					attr(svg, "width", ctx.width);
				}

				if (changed.height) {
					attr(svg, "height", ctx.height);
				}
			},

			i: noop,
			o: noop,

			d: function destroy(detaching) {
				if (detaching) {
					detach(svg);
				}

				destroy_each(each_blocks, detaching);
			}
		};
	}

	function instance($$self, $$props, $$invalidate) {
		let { width, height, n = 50 } = $$props;

		let numbers = [];

	  const f = (x, t) => Math.sin(x * t + t) * 200;

	  const setNumbers = t => {
	    for(let i = 0; i < n; i++){
	      const x = i / n;
	      numbers[i] = f(x, t); $$invalidate('numbers', numbers);
	    }
	  };

	  const animate = timestamp => {
	    setNumbers(timestamp / 1000);
	    requestAnimationFrame(animate);
	  };
	  requestAnimationFrame(animate);

		$$self.$set = $$props => {
			if ('width' in $$props) $$invalidate('width', width = $$props.width);
			if ('height' in $$props) $$invalidate('height', height = $$props.height);
			if ('n' in $$props) $$invalidate('n', n = $$props.n);
		};

		return { width, height, n, numbers };
	}

	class App extends SvelteComponentDev {
		constructor(options) {
			super(options);
			init(this, options, instance, create_fragment, safe_not_equal, ["width", "height", "n"]);

			const { ctx } = this.$$;
			const props = options.props || {};
			if (ctx.width === undefined && !('width' in props)) {
				console.warn("<App> was created without expected prop 'width'");
			}
			if (ctx.height === undefined && !('height' in props)) {
				console.warn("<App> was created without expected prop 'height'");
			}
			if (ctx.n === undefined && !('n' in props)) {
				console.warn("<App> was created without expected prop 'n'");
			}
		}

		get width() {
			throw new Error("<App>: Props cannot be read directly from the component instance unless compiling with 'accessors: true' or '<svelte:options accessors/>'");
		}

		set width(value) {
			throw new Error("<App>: Props cannot be set directly on the component instance unless compiling with 'accessors: true' or '<svelte:options accessors/>'");
		}

		get height() {
			throw new Error("<App>: Props cannot be read directly from the component instance unless compiling with 'accessors: true' or '<svelte:options accessors/>'");
		}

		set height(value) {
			throw new Error("<App>: Props cannot be set directly on the component instance unless compiling with 'accessors: true' or '<svelte:options accessors/>'");
		}

		get n() {
			throw new Error("<App>: Props cannot be read directly from the component instance unless compiling with 'accessors: true' or '<svelte:options accessors/>'");
		}

		set n(value) {
			throw new Error("<App>: Props cannot be set directly on the component instance unless compiling with 'accessors: true' or '<svelte:options accessors/>'");
		}
	}

	var main = new App({
		target: document.body,
	  props: {
	    width: 960,
	    height: 500
	  }
	});

	return main;

}());
//# sourceMappingURL=bundle.js.map

global.css

body {
  margin: 0;
}
line {
  stroke: gray;
  stroke-width: 2px;
}

main.js

import App from './App.svelte';

export default new App({
	target: document.body,
  props: {
    width: 960,
    height: 500
  }
});

package.json

{
  "name": "svelte-app",
  "version": "1.0.0",
  "devDependencies": {
    "npm-run-all": "^4.1.5",
    "rollup": "^1.10.1",
    "rollup-plugin-commonjs": "^9.3.4",
    "rollup-plugin-node-resolve": "^4.2.3",
    "rollup-plugin-svelte": "^5.0.3",
    "rollup-plugin-terser": "^4.0.4",
    "sirv-cli": "^0.3.1",
    "svelte": "^3.0.0"
  },
  "scripts": {
    "build": "rollup -c",
    "autobuild": "rollup -c -w",
    "dev": "run-p start:dev autobuild",
    "start": "sirv .",
    "start:dev": "sirv . --dev"
  }
}

rollup.config.js

import svelte from 'rollup-plugin-svelte';
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import { terser } from 'rollup-plugin-terser';

const production = !process.env.ROLLUP_WATCH;

export default {
	input: 'main.js',
	output: {
		sourcemap: true,
		format: 'iife',
		name: 'app',
		file: 'bundle.js'
	},
	plugins: [
		svelte({
			// enable run-time checks when not in production
			dev: !production,
			// we'll extract any component CSS out into
			// a separate file — better for performance
			css: css => {
				css.write('public/bundle.css');
			}
		}),

		// If you have external dependencies installed from
		// npm, you'll most likely need these plugins. In
		// some cases you'll need additional configuration —
		// consult the documentation for details:
		// https://github.com/rollup/rollup-plugin-commonjs
		resolve(),
		commonjs(),

		// If we're building for production (npm run build
		// instead of npm run dev), minify
		production && terser()
	]
};