Skip to content

Commit 8ff785c

Browse files
feat: implement backward/forward navigation options to iframed window (#181)
Co-authored-by: Simon Holthausen <[email protected]>
1 parent 96df765 commit 8ff785c

File tree

5 files changed

+142
-26
lines changed

5 files changed

+142
-26
lines changed

pnpm-lock.yaml

+8-8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/routes/tutorial/[slug]/+page.svelte

+73-12
Original file line numberDiff line numberDiff line change
@@ -95,15 +95,30 @@
9595
9696
/** @type {import('$lib/types').Adapter | undefined} */
9797
let adapter;
98+
/** @type {string[]} */
99+
let history_bwd = [];
100+
/** @type {string[]} */
101+
let history_fwd = [];
102+
let ignore_path_change = false;
103+
104+
function reset_history() {
105+
history_bwd = [];
106+
history_fwd = [];
107+
}
98108
99109
onMount(() => {
110+
function on_iframe_load() {
111+
iframe.classList.add('loaded');
112+
}
100113
function destroy() {
114+
iframe.removeEventListener('load', on_iframe_load);
101115
if (adapter) {
102116
adapter.destroy();
103117
}
104118
}
105119
106120
document.addEventListener('pagehide', destroy);
121+
iframe.addEventListener('load', on_iframe_load);
107122
return destroy;
108123
});
109124
@@ -122,6 +137,7 @@
122137
loading = true;
123138
124139
reset_complete_states();
140+
reset_history();
125141
126142
await reset_adapter($files);
127143
@@ -254,7 +270,16 @@
254270
if (e.origin !== adapter.base) return;
255271
256272
if (e.data.type === 'ping') {
257-
path = e.data.data.path ?? path;
273+
const new_path = e.data.data.path ?? path;
274+
if (path !== new_path) {
275+
// skip `nav_to` step if triggered by bwd/fwd action
276+
if (ignore_path_change) {
277+
ignore_path_change = false;
278+
} else {
279+
nav_to();
280+
}
281+
path = new_path;
282+
}
258283
259284
clearTimeout(timeout);
260285
timeout = setTimeout(() => {
@@ -297,10 +322,47 @@
297322
// change the src without adding a history entry, which
298323
// would make back/forward traversal very annoying
299324
const parentNode = /** @type {HTMLElement} */ (iframe.parentNode);
325+
iframe.classList.remove('loaded');
300326
parentNode?.removeChild(iframe);
301327
iframe.src = src;
302328
parentNode?.appendChild(iframe);
303329
}
330+
331+
/** @param {string} path */
332+
function route_to(path) {
333+
if (adapter) {
334+
const url = new URL(path, adapter.base);
335+
path = url.pathname + url.search + url.hash;
336+
set_iframe_src(adapter.base + path);
337+
}
338+
}
339+
340+
/** @param {string | null} new_path */
341+
function nav_to(new_path = null) {
342+
if (path !== history_bwd[history_bwd.length - 1]) {
343+
history_bwd = [...history_bwd, path];
344+
}
345+
history_fwd = [];
346+
if (new_path) route_to(new_path);
347+
}
348+
349+
function go_bwd() {
350+
const new_path = history_bwd[history_bwd.length - 1];
351+
if (new_path) {
352+
ignore_path_change = true;
353+
[history_bwd, history_fwd] = [history_bwd.slice(0, -1), [path, ...history_fwd]];
354+
route_to(new_path);
355+
}
356+
}
357+
358+
function go_fwd() {
359+
const new_path = history_fwd[0];
360+
if (new_path) {
361+
ignore_path_change = true;
362+
[history_bwd, history_fwd] = [[...history_bwd, path], history_fwd.slice(1)];
363+
route_to(new_path);
364+
}
365+
}
304366
</script>
305367
306368
<svelte:window on:message={handle_message} bind:innerWidth={width} />
@@ -400,20 +462,18 @@
400462
401463
<section slot="b" class="preview">
402464
<Chrome
465+
{history_bwd}
466+
{history_fwd}
403467
{path}
404468
{loading}
405469
on:refresh={() => {
406470
if (adapter) {
407471
set_iframe_src(adapter.base + path);
408472
}
409473
}}
410-
on:change={(e) => {
411-
if (adapter) {
412-
const url = new URL(e.detail.value, adapter.base);
413-
path = url.pathname + url.search + url.hash;
414-
set_iframe_src(adapter.base + path);
415-
}
416-
}}
474+
on:change={(e) => nav_to(e.detail.value)}
475+
on:back={go_bwd}
476+
on:forward={go_fwd}
417477
/>
418478
419479
<div class="content">
@@ -443,6 +503,7 @@
443503
.content {
444504
display: flex;
445505
flex-direction: column;
506+
position: relative;
446507
min-height: 0;
447508
height: 100%;
448509
max-height: 100%;
@@ -485,10 +546,6 @@
485546
flex-direction: column;
486547
}
487548
488-
.content {
489-
position: relative;
490-
}
491-
492549
iframe {
493550
width: 100%;
494551
height: 100%;
@@ -499,6 +556,10 @@
499556
background: var(--sk-back-2);
500557
}
501558
559+
iframe:not(.loaded) {
560+
display: none;
561+
}
562+
502563
.editor-container {
503564
position: relative;
504565
background-color: var(--sk-back-3);

src/routes/tutorial/[slug]/Chrome.svelte

+45-3
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,38 @@
11
<script>
22
import { createEventDispatcher } from 'svelte';
3+
import chevron from './chevron.svg';
34
import refresh from './refresh.svg';
45
56
/** @type {string} */
67
export let path;
78
9+
/** @type {string[]} */
10+
export let history_bwd = [];
11+
12+
/** @type {string[]} */
13+
export let history_fwd = [];
14+
815
/** @type {boolean} */
916
export let loading;
1017
1118
const dispatch = createEventDispatcher();
19+
20+
$: [disabledBwd, disabledFwd] = [loading || !history_bwd.length, loading || !history_fwd.length];
1221
</script>
1322

1423
<div class="chrome">
15-
<button disabled={loading} on:click={() => dispatch('refresh')} aria-label="reload">
24+
<button disabled={disabledBwd} on:click={() => dispatch('back')} aria-label="go back">
25+
<img src={chevron} alt="Back icon" />
26+
</button>
27+
<button disabled={disabledFwd} on:click={() => dispatch('forward')} aria-label="go forward">
28+
<img src={chevron} style="transform: rotate(180deg)" alt="Forward icon" />
29+
</button>
30+
<button
31+
class="refresh"
32+
disabled={loading}
33+
on:click={() => dispatch('refresh')}
34+
aria-label="reload"
35+
>
1636
<img src={refresh} alt="Reload icon" />
1737
</button>
1838

@@ -43,11 +63,11 @@
4363
.chrome button img {
4464
width: 2rem;
4565
height: 2rem;
46-
transition: 0.2s ease-out;
66+
transition: transform 0.2s ease-out, opacity 0.1s ease-out;
4767
transform: none;
4868
}
4969
50-
.chrome button:active img {
70+
.chrome button.refresh:active img {
5171
transform: rotate(-360deg);
5272
transition: none;
5373
}
@@ -71,4 +91,26 @@
7191
outline: none;
7292
border: 2px solid var(--sk-theme-3);
7393
}
94+
95+
.chrome button {
96+
user-select: none;
97+
}
98+
99+
.chrome button[disabled] {
100+
opacity: 1;
101+
}
102+
103+
.chrome button[disabled] img {
104+
opacity: 0.5;
105+
}
106+
107+
.chrome button img {
108+
opacity: 0.8;
109+
}
110+
111+
.chrome button:hover img,
112+
.chrome button:active img,
113+
.chrome button:focus-visible img {
114+
opacity: 1;
115+
}
74116
</style>
+11
Loading
+5-3
Loading

0 commit comments

Comments
 (0)