mirror of https://github.com/sveltejs/svelte
Merge pull request #2272 from sveltejs/gh-2216
[WIP] Don't use iframes for homepage REPLspull/2286/head
commit
29b4615d9d
@ -1,3 +1,3 @@
|
||||
{
|
||||
"title": "Adding data"
|
||||
"title": "Hello world"
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
export let once = false;
|
||||
export let top = 0;
|
||||
export let bottom = 0;
|
||||
export let left = 0;
|
||||
export let right = 0;
|
||||
|
||||
let intersecting = false;
|
||||
let container;
|
||||
|
||||
onMount(() => {
|
||||
if (typeof IntersectionObserver !== 'undefined') {
|
||||
const rootMargin = `${bottom}px ${left}px ${top}px ${right}px`;
|
||||
|
||||
const observer = new IntersectionObserver(entries => {
|
||||
intersecting = entries[0].isIntersecting;
|
||||
if (intersecting && once) {
|
||||
observer.unobserve(container);
|
||||
}
|
||||
}, {
|
||||
rootMargin
|
||||
});
|
||||
|
||||
observer.observe(container);
|
||||
return () => observer.unobserve(container);
|
||||
}
|
||||
|
||||
function handler() {
|
||||
const bcr = container.getBoundingClientRect();
|
||||
|
||||
intersecting = (
|
||||
(bcr.bottom + bottom) > 0 &&
|
||||
(bcr.right + right) > 0 &&
|
||||
(bcr.top - top) < window.innerHeight &&
|
||||
(bcr.left - left) < window.innerWidth
|
||||
);
|
||||
|
||||
if (intersecting && once) {
|
||||
window.removeEventListener('scroll', handler);
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('scroll', handler);
|
||||
return () => window.removeEventListener('scroll', handler);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
div {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div bind:this={container}>
|
||||
<slot {intersecting}></slot>
|
||||
</div>
|
@ -0,0 +1,11 @@
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let constructor;
|
||||
|
||||
onMount(async () => {
|
||||
constructor = await $$props.this();
|
||||
});
|
||||
</script>
|
||||
|
||||
<svelte:component this={constructor} {...$$props}/>
|
@ -0,0 +1,44 @@
|
||||
const workers = new Map();
|
||||
|
||||
let uid = 1;
|
||||
|
||||
export default class Bundler {
|
||||
constructor(version) {
|
||||
if (!workers.has(version)) {
|
||||
const worker = new Worker('/workers/bundler.js');
|
||||
worker.postMessage({ type: 'init', version });
|
||||
workers.set(version, worker);
|
||||
}
|
||||
|
||||
this.worker = workers.get(version);
|
||||
|
||||
this.handlers = new Map();
|
||||
|
||||
this.worker.addEventListener('message', event => {
|
||||
const handler = this.handlers.get(event.data.id);
|
||||
|
||||
if (handler) { // if no handler, was meant for a different REPL
|
||||
handler(event.data);
|
||||
this.handlers.delete(event.data.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bundle(components) {
|
||||
return new Promise(fulfil => {
|
||||
const id = uid++;
|
||||
|
||||
this.handlers.set(id, fulfil);
|
||||
|
||||
this.worker.postMessage({
|
||||
id,
|
||||
type: 'bundle',
|
||||
components
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.worker.terminate();
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
import { process_example } from './process_example.js';
|
||||
import Repl from '../../components/Repl/index.svelte';
|
||||
|
||||
export let version = 'beta';
|
||||
export let gist = null;
|
||||
export let example = null;
|
||||
export let embedded = false;
|
||||
|
||||
let repl;
|
||||
let name = 'loading...';
|
||||
|
||||
onMount(() => {
|
||||
if (version !== 'local') {
|
||||
fetch(`https://unpkg.com/svelte@${version}/package.json`)
|
||||
.then(r => r.json())
|
||||
.then(pkg => {
|
||||
version = pkg.version;
|
||||
});
|
||||
}
|
||||
|
||||
if (gist) {
|
||||
fetch(`gist/${gist}`).then(r => r.json()).then(data => {
|
||||
const { id, description, files } = data;
|
||||
|
||||
name = description;
|
||||
|
||||
const components = Object.keys(files)
|
||||
.map(file => {
|
||||
const dot = file.lastIndexOf('.');
|
||||
if (!~dot) return;
|
||||
|
||||
const source = files[file].content;
|
||||
|
||||
return {
|
||||
name: file.slice(0, dot),
|
||||
type: file.slice(dot + 1),
|
||||
source
|
||||
};
|
||||
})
|
||||
.filter(x => x.type === 'svelte' || x.type === 'js')
|
||||
.sort((a, b) => {
|
||||
if (a.name === 'App' && a.type === 'svelte') return -1;
|
||||
if (b.name === 'App' && b.type === 'svelte') return 1;
|
||||
|
||||
if (a.type !== b.type) return a.type === 'svelte' ? -1 : 1;
|
||||
|
||||
return a.name < b.name ? -1 : 1;
|
||||
});
|
||||
|
||||
repl.set({ components });
|
||||
});
|
||||
} else if (example) {
|
||||
fetch(`examples/${example}.json`).then(async response => {
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
|
||||
repl.set({
|
||||
components: process_example(data.files)
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$: if (embedded) document.title = `${name} • Svelte REPL`;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.repl-outer {
|
||||
position: relative;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: var(--back);
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
--pane-controls-h: 4.2rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="repl-outer">
|
||||
{#if process.browser}
|
||||
<Repl bind:this={repl} {version} embedded={true}/>
|
||||
{/if}
|
||||
</div>
|
@ -1,149 +1,105 @@
|
||||
|
||||
(function (){
|
||||
const importCache = {};
|
||||
|
||||
function fetchImport(id) {
|
||||
return new Promise((fulfil, reject) => {
|
||||
curl([`https://bundle.run/${id}`]).then(module => {
|
||||
importCache[id] = module;
|
||||
fulfil(module);
|
||||
}, err => {
|
||||
console.error(err.stack);
|
||||
reject(new Error(`Error loading ${id} from bundle.run`));
|
||||
(function() {
|
||||
const import_cache = {};
|
||||
|
||||
function fetch_import(id) {
|
||||
return new Promise((fulfil, reject) => {
|
||||
curl([`https://bundle.run/${id}`]).then(module => {
|
||||
import_cache[id] = module;
|
||||
fulfil(module);
|
||||
}, err => {
|
||||
console.error(err.stack);
|
||||
reject(new Error(`Error loading ${id} from bundle.run`));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function fetchImports(imports, progressFunc) {
|
||||
const missingImports = imports.filter(x => !importCache[x]);
|
||||
let pendingImports = missingImports.length;
|
||||
|
||||
if (missingImports.length) {
|
||||
let promise = Promise.all(
|
||||
missingImports.map(id => fetchImport(id).then(() => {
|
||||
pendingImports -= 1;
|
||||
if (progressFunc) progressFunc(pendingImports);
|
||||
}))
|
||||
);
|
||||
|
||||
return promise
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
function handleMessage(ev) {
|
||||
let { action, cmdId } = ev.data;
|
||||
const sendMessage = (payload) => parent.postMessage( { ...payload }, ev.origin);
|
||||
const sendReply = (payload) => sendMessage({ ...payload, cmdId })
|
||||
const sendOk = () => sendReply({ action: "cmdOk" });
|
||||
const sendError = (message, stack) => sendReply({ action: "cmdError", message, stack })
|
||||
|
||||
|
||||
if (action == "eval") {
|
||||
let { script } = ev.data.args;
|
||||
try {
|
||||
eval(script);
|
||||
sendOk();
|
||||
} catch (e) {
|
||||
sendError(e.message, e.stack);
|
||||
function fetch_imports(imports, progress_func) {
|
||||
const missing_imports = imports.filter(x => !import_cache[x]);
|
||||
let pending_imports = missing_imports.length;
|
||||
|
||||
if (missing_imports.length) {
|
||||
let promise = Promise.all(
|
||||
missing_imports.map(id => fetch_import(id).then(() => {
|
||||
pending_imports -= 1;
|
||||
if (progress_func) progress_func(pending_imports);
|
||||
}))
|
||||
);
|
||||
|
||||
return promise;
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
if (action == "bind_props") {
|
||||
let { props } = ev.data.args
|
||||
|
||||
if (!window.component) {
|
||||
// TODO can this happen?
|
||||
console.warn('no component to bind to');
|
||||
sendOk();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
props.forEach(prop => {
|
||||
// TODO should there be a public API for binding?
|
||||
// e.g. `component.$watch(prop, handler)`?
|
||||
// (answer: probably)
|
||||
window.component.$$.bound[prop] = value => {
|
||||
sendMessage({ action:"prop_update", args: { prop, value } })
|
||||
};
|
||||
});
|
||||
sendOk();
|
||||
} catch (e) {
|
||||
|
||||
sendError(e.message, e.stack);
|
||||
}
|
||||
}
|
||||
|
||||
if (action == "set_prop") {
|
||||
try {
|
||||
if (!window.component) {
|
||||
return;
|
||||
function handle_message(ev) {
|
||||
let { action, cmd_id } = ev.data;
|
||||
const send_message = (payload) => parent.postMessage( { ...payload }, ev.origin);
|
||||
const send_reply = (payload) => send_message({ ...payload, cmd_id });
|
||||
const send_ok = () => send_reply({ action: 'cmd_ok' });
|
||||
const send_error = (message, stack) => send_reply({ action: 'cmd_error', message, stack });
|
||||
|
||||
if (action === 'eval') {
|
||||
try {
|
||||
const { script } = ev.data.args;
|
||||
eval(script);
|
||||
send_ok();
|
||||
} catch (e) {
|
||||
send_error(e.message, e.stack);
|
||||
}
|
||||
let { prop, value } = ev.data.args;
|
||||
component[prop] = value;
|
||||
sendOk();
|
||||
} catch (e) {
|
||||
sendError(e.message, e.stack);
|
||||
}
|
||||
}
|
||||
|
||||
if (action == "catch_clicks") {
|
||||
try {
|
||||
let topOrigin = ev.origin;
|
||||
document.body.addEventListener('click', event => {
|
||||
if (event.which !== 1) return;
|
||||
if (event.metaKey || event.ctrlKey || event.shiftKey) return;
|
||||
if (event.defaultPrevented) return;
|
||||
|
||||
// ensure target is a link
|
||||
let el = event.target;
|
||||
while (el && el.nodeName !== 'A') el = el.parentNode;
|
||||
if (!el || el.nodeName !== 'A') return;
|
||||
|
||||
if (el.hasAttribute('download') || el.getAttribute('rel') === 'external' || el.target) return;
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
if (el.href.startsWith(topOrigin)) {
|
||||
const url = new URL(el.href);
|
||||
if (url.hash[0] === '#') {
|
||||
window.location.hash = url.hash;
|
||||
return;
|
||||
if (action === 'catch_clicks') {
|
||||
try {
|
||||
const top_origin = ev.origin;
|
||||
document.body.addEventListener('click', event => {
|
||||
if (event.which !== 1) return;
|
||||
if (event.metaKey || event.ctrlKey || event.shiftKey) return;
|
||||
if (event.defaultPrevented) return;
|
||||
|
||||
// ensure target is a link
|
||||
let el = event.target;
|
||||
while (el && el.nodeName !== 'A') el = el.parentNode;
|
||||
if (!el || el.nodeName !== 'A') return;
|
||||
|
||||
if (el.hasAttribute('download') || el.getAttribute('rel') === 'external' || el.target) return;
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
if (el.href.startsWith(top_origin)) {
|
||||
const url = new URL(el.href);
|
||||
if (url.hash[0] === '#') {
|
||||
window.location.hash = url.hash;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.open(el.href, '_blank');
|
||||
});
|
||||
sendOk();
|
||||
} catch(e) {
|
||||
sendError(e.message, e.stack);
|
||||
window.open(el.href, '_blank');
|
||||
});
|
||||
send_ok();
|
||||
} catch(e) {
|
||||
send_error(e.message, e.stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (action == "fetch_imports") {
|
||||
let { imports, import_map } = ev.data.args;
|
||||
fetchImports(imports, (remaining) => {
|
||||
sendMessage({action: "fetch_progress", args: { remaining }});
|
||||
})
|
||||
.then(() => {
|
||||
imports.forEach(x=> {
|
||||
const module = importCache[x];
|
||||
const name = import_map.get(x);
|
||||
window[name] = module;
|
||||
if (action === 'fetch_imports') {
|
||||
const { imports, import_map } = ev.data.args;
|
||||
fetch_imports(imports, (remaining) => {
|
||||
send_message({action: 'fetch_progress', args: { remaining }});
|
||||
})
|
||||
.then(() => {
|
||||
imports.forEach(x=> {
|
||||
const module = import_cache[x];
|
||||
const name = import_map.get(x);
|
||||
window[name] = module;
|
||||
});
|
||||
send_ok();
|
||||
})
|
||||
.catch(e => {
|
||||
send_error(e.message, e.stack);
|
||||
});
|
||||
sendOk();
|
||||
})
|
||||
.catch(e => {
|
||||
sendError(e.message, e.stack);
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("message", handleMessage, false)
|
||||
|
||||
console.log("repl-runner initialized");
|
||||
|
||||
window.addEventListener('message', handle_message, false);
|
||||
})();
|
||||
|
Loading…
Reference in new issue