Skip to content

Commit

Permalink
Allow importing from URL
Browse files Browse the repository at this point in the history
  • Loading branch information
cyderize committed Nov 26, 2023
1 parent 0151a70 commit 0a20eed
Show file tree
Hide file tree
Showing 4 changed files with 266 additions and 85 deletions.
34 changes: 27 additions & 7 deletions src/App.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<script>
import { onMount } from 'svelte';
import Playground from './lib/Playground.svelte';
import { loadFromUrl } from './lib/loadFromUrl';
let playground;
let project = {
Expand Down Expand Up @@ -42,7 +43,7 @@
Array(6)
.fill(0)
.map(
() => alphabet[Math.floor(Math.random() * alphabet.length)]
() => alphabet[Math.floor(Math.random() * alphabet.length)],
)
.join('');
let id = genId();
Expand All @@ -64,7 +65,7 @@
return p;
}
function hashChange() {
async function hashChange() {
if (currentSession && playground.hasFiles()) {
const project = playground.getProject();
settings.sessions[currentSession] = project;
Expand All @@ -73,14 +74,14 @@
if (window.location.hash.startsWith('#project=')) {
try {
const json = decodeURIComponent(
window.location.hash.substring(9)
window.location.hash.substring(9),
);
project = migrateProject(JSON.parse(json));
currentSession = newSession();
window.history.replaceState(
undefined,
undefined,
`#session=${currentSession}`
`#session=${currentSession}`,
);
return;
} catch (e) {
Expand All @@ -91,7 +92,7 @@
if (window.location.hash.startsWith('#code=')) {
try {
const contents = decodeURIComponent(
window.location.hash.substring(6)
window.location.hash.substring(6),
);
project = {
files: [
Expand All @@ -106,8 +107,27 @@
window.history.replaceState(
undefined,
undefined,
`#session=${currentSession}`
`#session=${currentSession}`,
);
return;
} catch (e) {
console.error(e);
}
}
if (window.location.hash.startsWith('#url=')) {
try {
const url = decodeURIComponent(
window.location.hash.substring(5),
);
project = await loadFromUrl(url);
currentSession = newSession();
window.history.replaceState(
undefined,
undefined,
`#session=${currentSession}`,
);
return;
} catch (e) {
console.error(e);
}
Expand All @@ -118,7 +138,7 @@
window.history.replaceState(
undefined,
undefined,
`#session=${newSession()}`
`#session=${newSession()}`,
);
}
Expand Down
224 changes: 148 additions & 76 deletions src/lib/NewFileModal.svelte
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
<script>
import { createEventDispatcher, tick } from 'svelte';
import Modal from './Modal.svelte';
import { loadFromUrl } from './loadFromUrl';
export let active = false;
const dispatch = createEventDispatcher();
let fileInput;
let files;
let element;
let url = '';
let importingFromUrl = false;
let error = null;
$: reset(active);
function reset(active) {
importingFromUrl = false;
url = '';
error = null;
}
async function setFocus() {
await tick();
Expand All @@ -25,97 +37,150 @@
};
reader.onerror = (e) => reject(e);
reader.readAsText(file);
})
}),
);
}
const result = await Promise.all(promises);
dispatch('open', { files: result });
}
async function importFromUrl() {
try {
dispatch('open', await loadFromUrl(url));
} catch (e) {
error = `Failed to import from URL: ${e.message || e}`;
console.error(e);
}
}
</script>

<Modal
{active}
title="Create new file"
on:activate={setFocus}
on:cancel={() => dispatch('cancel')}
>
<aside class="menu">
<p class="menu-label">Model</p>
<ul class="menu-list">
<li>
<button
type="button"
class="button is-text"
{#if importingFromUrl}
<Modal
{active}
title="Import from URL"
on:activate={setFocus}
on:cancel={() => dispatch('cancel')}
on:submit={importFromUrl}
>
{#if error}
<div class="error">
{error}
</div>
{/if}
<div class="field">
<p class="control is-expanded">
<input
bind:this={element}
on:click={() => dispatch('new', { type: '.mzn' })}
>
Model file (.mzn)
</button>
</li>
<li>
<button
type="button"
class="button is-text"
on:click={() => dispatch('new', { type: '.mzc.mzn' })}
>
Solution checker model (.mzc.mzn)
</button>
</li>
</ul>
<p class="menu-label">Data</p>
<ul class="menu-list">
<li>
<button
type="button"
class="button is-text"
on:click={() => dispatch('new', { type: '.dzn' })}
>
Data file (.dzn)
</button>
</li>
<li>
<button
type="button"
class="button is-text"
on:click={() => dispatch('new', { type: '.json' })}
>
JSON data file (.json)
</button>
</li>
</ul>
<p class="menu-label">Visualisation</p>
<ul class="menu-list">
<li>
<button
type="button"
class="button is-text"
on:click={() => dispatch('new', { type: '.html' })}
>
Custom visualisation (.html)
</button>
</li>
</ul>
<p class="menu-label">Import</p>
<ul class="menu-list">
<li>
<button
type="button"
class="button is-text"
on:click={() => fileInput.click()}>Upload file(s)</button
>
</li>
</ul>
</aside>
</Modal>

class="input"
type="text"
pattern=".+\.(mzn|dzn|json|js|html|css|mzp|mzc)"
bind:value={url}
required
/>
</p>
</div>
<div slot="footer">
<button class="button is-primary">OK</button>
<button
class="button"
type="button"
on:click={() => dispatch('cancel')}>Cancel</button
>
</div>
</Modal>
{:else}
<Modal
{active}
title="Create new file"
on:activate={setFocus}
on:cancel={() => dispatch('cancel')}
>
<aside class="menu">
<p class="menu-label">Model</p>
<ul class="menu-list">
<li>
<button
type="button"
class="button is-text"
bind:this={element}
on:click={() => dispatch('new', { type: '.mzn' })}
>
Model file (.mzn)
</button>
</li>
<li>
<button
type="button"
class="button is-text"
on:click={() => dispatch('new', { type: '.mzc.mzn' })}
>
Solution checker model (.mzc.mzn)
</button>
</li>
</ul>
<p class="menu-label">Data</p>
<ul class="menu-list">
<li>
<button
type="button"
class="button is-text"
on:click={() => dispatch('new', { type: '.dzn' })}
>
Data file (.dzn)
</button>
</li>
<li>
<button
type="button"
class="button is-text"
on:click={() => dispatch('new', { type: '.json' })}
>
JSON data file (.json)
</button>
</li>
</ul>
<p class="menu-label">Visualisation</p>
<ul class="menu-list">
<li>
<button
type="button"
class="button is-text"
on:click={() => dispatch('new', { type: '.html' })}
>
Custom visualisation (.html)
</button>
</li>
</ul>
<p class="menu-label">Import</p>
<ul class="menu-list">
<li>
<button
type="button"
class="button is-text"
on:click={() => fileInput.click()}
>Upload file(s)</button
>
</li>
<li>
<button
type="button"
class="button is-text"
on:click={() => (importingFromUrl = true)}
>Import from URL</button
>
</li>
</ul>
</aside>
</Modal>
{/if}
<input
class="is-hidden"
type="file"
bind:this={fileInput}
bind:files
on:change={uploaded}
multiple
accept=".mzn,.mzc,.dzn,.json,.html"
accept=".mzn,.mzc,.dzn,.json,.html,.js,.css"
/>

<style>
Expand All @@ -129,4 +194,11 @@
width: 100%;
text-align: left;
}
.error {
padding: 0.5rem;
border-radius: 0.5rem;
background: rgb(255, 227, 227);
color: rgb(175, 16, 16);
margin-bottom: 1rem;
}
</style>
23 changes: 21 additions & 2 deletions src/lib/Playground.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -127,14 +127,33 @@
files = [];
openFiles(project.files, autoFocus);
currentIndex = project.tab || 0;
await loadSolvers();
if (project.solverId) {
await loadSolvers();
currentSolverIndex = solvers.findIndex(
(s) => s.id === project.solverId,
);
} else {
currentSolverIndex =
solvers.findIndex(
(s) => s.extraInfo && s.extraInfo.isDefault,
) || 0;
}
if (project.solverConfig) {
solverConfig.load(project.solverConfig);
} else {
solverConfig.reset();
}
}
async function importFiles(e) {
const offset = files.length;
openFiles(e.files);
if (e.tab !== undefined && e.tab !== null && e.tab >= 0) {
selectTab(offset + e.tab);
}
if (e.solverId) {
await loadSolvers();
currentSolverIndex = solvers.findIndex((s) => s.id === e.solverId);
}
}
Expand Down Expand Up @@ -1388,7 +1407,7 @@
active={newFileRequested}
on:cancel={() => (newFileRequested = false)}
on:new={(e) => newFile(e.detail.type)}
on:open={(e) => openFiles(e.detail.files)}
on:open={(e) => importFiles(e.detail)}
/>

<Modal
Expand Down
Loading

0 comments on commit 0a20eed

Please sign in to comment.