|
95 | 95 |
|
96 | 96 | /** @type {import('$lib/types').Adapter | undefined} */
|
97 | 97 | 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 | + } |
98 | 108 |
|
99 | 109 | onMount(() => {
|
| 110 | + function on_iframe_load() { |
| 111 | + iframe.classList.add('loaded'); |
| 112 | + } |
100 | 113 | function destroy() {
|
| 114 | + iframe.removeEventListener('load', on_iframe_load); |
101 | 115 | if (adapter) {
|
102 | 116 | adapter.destroy();
|
103 | 117 | }
|
104 | 118 | }
|
105 | 119 |
|
106 | 120 | document.addEventListener('pagehide', destroy);
|
| 121 | + iframe.addEventListener('load', on_iframe_load); |
107 | 122 | return destroy;
|
108 | 123 | });
|
109 | 124 |
|
|
122 | 137 | loading = true;
|
123 | 138 |
|
124 | 139 | reset_complete_states();
|
| 140 | + reset_history(); |
125 | 141 |
|
126 | 142 | await reset_adapter($files);
|
127 | 143 |
|
|
254 | 270 | if (e.origin !== adapter.base) return;
|
255 | 271 |
|
256 | 272 | 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 | + } |
258 | 283 |
|
259 | 284 | clearTimeout(timeout);
|
260 | 285 | timeout = setTimeout(() => {
|
|
297 | 322 | // change the src without adding a history entry, which
|
298 | 323 | // would make back/forward traversal very annoying
|
299 | 324 | const parentNode = /** @type {HTMLElement} */ (iframe.parentNode);
|
| 325 | + iframe.classList.remove('loaded'); |
300 | 326 | parentNode?.removeChild(iframe);
|
301 | 327 | iframe.src = src;
|
302 | 328 | parentNode?.appendChild(iframe);
|
303 | 329 | }
|
| 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 | + } |
304 | 366 | </script>
|
305 | 367 |
|
306 | 368 | <svelte:window on:message={handle_message} bind:innerWidth={width} />
|
|
400 | 462 |
|
401 | 463 | <section slot="b" class="preview">
|
402 | 464 | <Chrome
|
| 465 | + {history_bwd} |
| 466 | + {history_fwd} |
403 | 467 | {path}
|
404 | 468 | {loading}
|
405 | 469 | on:refresh={() => {
|
406 | 470 | if (adapter) {
|
407 | 471 | set_iframe_src(adapter.base + path);
|
408 | 472 | }
|
409 | 473 | }}
|
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} |
417 | 477 | />
|
418 | 478 |
|
419 | 479 | <div class="content">
|
|
443 | 503 | .content {
|
444 | 504 | display: flex;
|
445 | 505 | flex-direction: column;
|
| 506 | + position: relative; |
446 | 507 | min-height: 0;
|
447 | 508 | height: 100%;
|
448 | 509 | max-height: 100%;
|
|
485 | 546 | flex-direction: column;
|
486 | 547 | }
|
487 | 548 |
|
488 |
| - .content { |
489 |
| - position: relative; |
490 |
| - } |
491 |
| -
|
492 | 549 | iframe {
|
493 | 550 | width: 100%;
|
494 | 551 | height: 100%;
|
|
499 | 556 | background: var(--sk-back-2);
|
500 | 557 | }
|
501 | 558 |
|
| 559 | + iframe:not(.loaded) { |
| 560 | + display: none; |
| 561 | + } |
| 562 | +
|
502 | 563 | .editor-container {
|
503 | 564 | position: relative;
|
504 | 565 | background-color: var(--sk-back-3);
|
|
0 commit comments