Skip to content

Commit 60b3962

Browse files
committed
Migrate to Svelte 5
I used `npx sv migrate svelte-5` to start the migration but made changes from there. I removed any legacy package usage since instead of dispatching events, its not passing callbacks through props instead.
1 parent 6c7a859 commit 60b3962

12 files changed

+67
-81
lines changed

src/App.svelte

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
return { puzzle: puzzleSet }
3333
}
3434
35-
let puzzleData = generatePuzzleData()
35+
let puzzleData = $state(generatePuzzleData())
3636
3737
const saveCompletedGame = async (moveCount) => {
3838
await db.completedGames.add({
@@ -57,20 +57,20 @@
5757
}, 200)
5858
}
5959
60-
let showModal = false;
60+
let showModal = $state(false);
6161
</script>
6262

6363
<div class="app-root">
6464
<div class="app-container">
65-
<Board puzzle={puzzleData.puzzle} onCompleted={onCompleted}/>
65+
<Board bind:puzzle={puzzleData.puzzle} completed={onCompleted}/>
6666
<div class="footer-bar">
6767
<MoveCountLabel count={$moveCount}/>
68-
<RecordsButton on:click={() => showModal = true}/>
68+
<RecordsButton onclick={() => showModal = true}/>
6969
</div>
7070
</div>
7171
</div>
7272
{#if showModal}
73-
<RecordsModal on:close="{() => showModal = false}"/>
73+
<RecordsModal close={() => showModal = false}/>
7474
{/if}
7575

7676
<style>

src/Board.svelte

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,24 @@
22
import Card from './Card.svelte';
33
import { moveCount } from "./stores/moveCount"
44
5-
export let puzzle = [];
6-
export let onCompleted;
5+
let { puzzle = $bindable([]), completed } = $props();
76
8-
let opened = []
7+
let opened = $state([])
98
109
function delayOf(ms, ...args) {
1110
return new Promise(resolve => setTimeout(resolve, ms, ...args))
1211
}
1312
1413
function resetItem(item) {
1514
item.backfaceIsUp = false
16-
puzzle = puzzle
17-
// puzzle[item.index].backfaceIsUp = false
1815
}
1916
2017
function checkCompletion() {
21-
const completed = (puzzle.length === puzzle.filter((item) => item.backfaceIsUp).length)
18+
const puzzleCompleted = (puzzle.length === puzzle.filter((item) => item.backfaceIsUp).length)
2219
23-
if (completed) {
20+
if (puzzleCompleted) {
2421
delayOf(300).then(() => {
25-
onCompleted()
22+
completed()
2623
})
2724
}
2825
}
@@ -39,10 +36,8 @@
3936
return opened[0].type === opened[1].type
4037
}
4138
42-
function handleFlip(event) {
43-
const item = event.detail
44-
puzzle[item.index].backfaceIsUp = !puzzle[item.index].backfaceIsUp
45-
// puzzle = [...puzzle]
39+
function handleFlip(item) {
40+
item.backfaceIsUp = !item.backfaceIsUp
4641
4742
opened = [...opened, item]
4843
@@ -62,7 +57,7 @@
6257

6358
<div class="full-grid disable-text-selection">
6459
{#each puzzle as item, index (index)}
65-
<Card index={index} item={item} flipped={item.backfaceIsUp} on:flip={handleFlip} flippable={opened.length < 2}>
60+
<Card index={index} item={item} flipped={item.backfaceIsUp} flip={handleFlip} flippable={opened.length < 2}>
6661
{item.type.toString()}
6762
</Card>
6863
{/each}

src/Card.svelte

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
<script>
2-
import { createEventDispatcher } from 'svelte';
3-
4-
const dispatch = createEventDispatcher();
5-
6-
export let index = 0;
7-
export let item = { index: 0, type: 0, backfaceIsUp: false };
8-
export let flippable = true;
9-
export let flipped = false;
2+
let {
3+
index = 0,
4+
item = { index: 0, type: 0, backfaceIsUp: false },
5+
flippable = true,
6+
flipped = false,
7+
flip,
8+
children
9+
} = $props();
1010
1111
function clickHandler() {
1212
if (flippable && !flipped)
13-
dispatch('flip', item)
13+
flip(item)
1414
}
1515
1616
function keyUpHandler(event) {
@@ -19,11 +19,11 @@
1919
}
2020
</script>
2121

22-
<div data-testid="card-{item.type}" class="card" class:toggled={flipped} on:click={clickHandler} on:keyup={keyUpHandler} role="button" tabindex={index + 1}>
22+
<div data-testid="card-{item.type}" class="card" class:toggled={flipped} onclick={clickHandler} onkeyup={keyUpHandler} role="button" tabindex={index + 1}>
2323
<div class="card-front">
2424
</div>
2525
<div class="card-back">
26-
<slot></slot>
26+
{@render children?.()}
2727
</div>
2828
</div>
2929

src/Modal.svelte

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
<script>
2-
import { createEventDispatcher, onDestroy } from 'svelte';
2+
import { onDestroy } from 'svelte';
33
import { quintOut, sineInOut } from 'svelte/easing';
44
import { fade, slide } from 'svelte/transition';
55
6-
const dispatch = createEventDispatcher();
7-
const close = () => dispatch('close');
6+
let { header, content, close } = $props();
87
9-
let modal;
8+
let modal = $state();
109
1110
const handle_keydown = e => {
1211
if (e.key === 'Escape') {
@@ -39,16 +38,18 @@
3938
}
4039
</script>
4140

42-
<svelte:window on:keydown={handle_keydown}/>
41+
<svelte:window onkeydown={handle_keydown}/>
4342

4443
<div class="modal-overlay">
45-
<div class="modal-backdrop" transition:fade="{{ duration: 600, easing: sineInOut }}" on:pointerdown={close}>
44+
<div class="modal-backdrop" transition:fade="{{ duration: 600, easing: sineInOut }}" onpointerdown={close}>
4645
</div>
4746
<div class="modal" role="dialog" aria-modal="true" bind:this={modal} transition:slide="{{ duration: 600, easing: quintOut }}">
48-
<slot name="header">
49-
</slot>
50-
<slot name="content">
51-
</slot>
47+
{#if header}
48+
{@render header()}
49+
{/if}
50+
{#if content}
51+
{@render content()}
52+
{/if}
5253
</div>
5354
</div>
5455

src/MoveCountLabel.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script>
2-
export let count = 0
2+
let { count = 0 } = $props();
33
</script>
44

55
<div>

src/RecordsButton.svelte

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
<button class="button" on:click>
1+
<script>
2+
let { onclick } = $props();
3+
</script>
4+
5+
<button class="button" {onclick}>
26
<span class="icon">
37
<svg viewBox="0 0 24 24" width="24" height="24" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round" class="css-i6dzq1"><circle cx="12" cy="8" r="7"></circle><polyline points="8.21 13.89 7 23 12 20 17 23 15.79 13.88"></polyline></svg>
48
</span>

src/RecordsButton.test.js

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,13 @@ describe("RecordsButton", () => {
88
})
99

1010
it("forwards the click event", async () => {
11-
const { component } = render(RecordsButton)
12-
1311
let clickReceived = false
14-
const off = component.$on('click', () => clickReceived = true)
12+
13+
const { component } = render(RecordsButton, { onclick: () => clickReceived = true })
1514

1615
const button = screen.queryByRole("button", { name: "Records" })
1716
await fireEvent.click(button)
1817

1918
expect(clickReceived).toEqual(true)
20-
21-
off()
2219
})
2320
})

src/RecordsModal.svelte

Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,25 @@
11
<script>
2-
import { createEventDispatcher, onMount } from 'svelte';
32
import Modal from './Modal.svelte';
43
import RecordsTable from './RecordsTable.svelte';
54
6-
const dispatch = createEventDispatcher();
7-
const close = () => dispatch('close');
8-
9-
let persisted = false
10-
11-
onMount(async () => {
12-
if (navigator.storage && navigator.storage.persist) {
13-
persisted = await navigator.storage.persist()
14-
}
15-
})
5+
let { close } = $props();
166
</script>
177

18-
<Modal on:close>
19-
<div slot="header" class="modal-header">
20-
<!-- svelte-ignore a11y-autofocus -->
21-
<button autofocus class="button modal-close-button" on:click={close}>
22-
Close
23-
</button>
24-
</div>
25-
<div slot="content" class="records-list">
26-
<p class="text-muted">
27-
Your objective is to solve puzzles in the fewest moves possible.
28-
{#if persisted}
29-
Your history is persisted on this device.
30-
{/if}
31-
</p>
32-
<RecordsTable/>
33-
</div>
8+
<Modal close={close}>
9+
{#snippet header()}
10+
<div class="modal-header">
11+
<!-- svelte-ignore a11y_autofocus -->
12+
<button autofocus class="button modal-close-button" onclick={close}>
13+
Close
14+
</button>
15+
</div>
16+
{/snippet}
17+
{#snippet content()}
18+
<div class="records-list">
19+
<p class="text-muted">Your objective is to solve puzzles in the fewest moves possible. Your history is persisted on this device.</p>
20+
<RecordsTable/>
21+
</div>
22+
{/snippet}
3423
</Modal>
3524

3625
<style>

src/RecordsModal.test.js

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,13 @@ describe("RecordsModal", () => {
1818
})
1919

2020
it("forwards the close event", async () => {
21-
const { component } = render(RecordsModal)
22-
2321
let closeReceived = false
24-
const off = component.$on('close', () => closeReceived = true)
22+
23+
const { component } = render(RecordsModal, { close: () => closeReceived = true })
2524

2625
const button = screen.queryByRole("button", { name: "Close" })
2726
await fireEvent.click(button)
2827

2928
expect(closeReceived).toEqual(true)
30-
31-
off()
3229
})
3330
})

src/RecordsTable.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
// https://dexie.org/docs/Tutorial/Svelte
1414
let data = liveQuery(() => db.completedGames.toArray())
1515
16-
$: recordsData = groupBy($data || [], "moveCount");
16+
let recordsData = $derived(groupBy($data || [], "moveCount"));
1717
</script>
1818

1919
<table>
@@ -25,7 +25,7 @@
2525
</tr>
2626
</thead>
2727
<tbody>
28-
{#if $data && $data.length === 0 }
28+
{#if $data && $data.length === 0}
2929
<tr><td colSpan="3" class="muted centered">No games played</td></tr>
3030
{:else}
3131
{#each Object.keys(recordsData).sort(ascOrder) as moveCount, index (index)}

0 commit comments

Comments
 (0)