|
1 | 1 | <script lang="ts">
|
2 |
| - import { confetti } from '@neoconfetti/svelte'; |
3 | 2 | import { enhance } from '$app/forms';
|
4 |
| - import type { PageData, ActionData } from './$types'; |
5 |
| - import { reduced_motion } from './reduced-motion'; |
| 3 | + import { confetti } from '@neoconfetti/svelte'; |
| 4 | + import type { ActionData, PageData } from './$types'; |
| 5 | + import { reducedMotion } from './reduced-motion'; |
6 | 6 |
|
7 |
| - /** @type {import('./$types').PageData} */ |
8 |
| - export let data: PageData; |
| 7 | + interface Props { |
| 8 | + data: PageData; |
| 9 | + form: ActionData; |
| 10 | + } |
| 11 | + /** |
| 12 | + * @typedef {Object} Props |
| 13 | + * @property {import('./$types').PageData} data |
| 14 | + * @property {import('./$types').ActionData} form |
| 15 | + */ |
9 | 16 |
|
10 |
| - /** @type {import('./$types').ActionData} */ |
11 |
| - export let form: ActionData; |
| 17 | + /** |
| 18 | + * @type {Props} |
| 19 | + */ |
| 20 | + let { data, form = $bindable() }: Props = $props(); |
12 | 21 |
|
13 | 22 | /** Whether or not the user has won */
|
14 |
| - $: won = data.answers.at(-1) === 'xxxxx'; |
| 23 | + let won = $derived(data.answers.at(-1) === 'xxxxx'); |
15 | 24 |
|
16 | 25 | /** The index of the current guess */
|
17 |
| - $: i = won ? -1 : data.answers.length; |
| 26 | + let i = $derived(won ? -1 : data.answers.length); |
18 | 27 |
|
19 | 28 | /** The current guess */
|
20 |
| - $: currentGuess = data.guesses[i] || ''; |
| 29 | + // svelte-ignore state_referenced_locally |
| 30 | + let currentGuess = $state(data.guesses[i] || ''); |
21 | 31 |
|
22 |
| - /** Whether the current guess can be submitted */ |
23 |
| - $: submittable = currentGuess.length === 5; |
24 |
| -
|
25 |
| - /** |
26 |
| - * A map of classnames for all letters that have been guessed, |
27 |
| - * used for styling the keyboard |
28 |
| - * @type {Record<string, 'exact' | 'close' | 'missing'>} |
29 |
| - */ |
30 |
| - let classnames: Record<string, 'exact' | 'close' | 'missing'>; |
31 |
| -
|
32 |
| - /** |
33 |
| - * A map of descriptions for all letters that have been guessed, |
34 |
| - * used for adding text for assistive technology (e.g. screen readers) |
35 |
| - * @type {Record<string, string>} |
36 |
| - */ |
37 |
| - let description: Record<string, string>; |
38 |
| -
|
39 |
| - $: { |
40 |
| - classnames = {}; |
41 |
| - description = {}; |
| 32 | + $effect(() => { |
| 33 | + currentGuess = data.guesses[i] || ''; |
| 34 | + }); |
42 | 35 |
|
| 36 | + /** Whether the current guess can be submitted */ |
| 37 | + let submittable = $derived(currentGuess.length === 5); |
| 38 | +
|
| 39 | + const { classnames, description } = $derived.by(() => { |
| 40 | + /** |
| 41 | + * A map of classnames for all letters that have been guessed, |
| 42 | + * used for styling the keyboard |
| 43 | + * @type {Record<string, 'exact' | 'close' | 'missing'>} |
| 44 | + */ |
| 45 | + let classnames: Record<string, 'exact' | 'close' | 'missing'> = {}; |
| 46 | + /** |
| 47 | + * A map of descriptions for all letters that have been guessed, |
| 48 | + * used for adding text for assistive technology (e.g. screen readers) |
| 49 | + * @type {Record<string, string>} |
| 50 | + */ |
| 51 | + let description: Record<string, string> = {}; |
43 | 52 | data.answers.forEach((answer, i) => {
|
44 | 53 | const guess = data.guesses[i];
|
45 |
| -
|
46 | 54 | for (let i = 0; i < 5; i += 1) {
|
47 | 55 | const letter = guess[i];
|
48 |
| -
|
49 | 56 | if (answer[i] === 'x') {
|
50 | 57 | classnames[letter] = 'exact';
|
51 | 58 | description[letter] = 'correct';
|
|
55 | 62 | }
|
56 | 63 | }
|
57 | 64 | });
|
58 |
| - } |
| 65 | + return { classnames, description }; |
| 66 | + }); |
59 | 67 |
|
60 | 68 | /**
|
61 | 69 | * Modify the game state without making a trip to the server,
|
62 | 70 | * if client-side JavaScript is enabled
|
63 | 71 | * @param {MouseEvent} event
|
64 | 72 | */
|
65 | 73 | function update(event: MouseEvent) {
|
| 74 | + event.preventDefault(); |
66 | 75 | const key = /** @type {HTMLButtonElement} */ (event.target as HTMLButtonElement).getAttribute(
|
67 | 76 | 'data-key'
|
68 | 77 | );
|
|
91 | 100 | }
|
92 | 101 | </script>
|
93 | 102 |
|
94 |
| -<svelte:window on:keydown={keydown} /> |
| 103 | +<svelte:window onkeydown={keydown} /> |
95 | 104 |
|
96 | 105 | <svelte:head>
|
97 | 106 | <title>Sverdle</title>
|
|
158 | 167 | <button data-key="enter" class:selected={submittable} disabled={!submittable}>enter</button>
|
159 | 168 |
|
160 | 169 | <button
|
161 |
| - on:click|preventDefault={update} |
| 170 | + onclick={update} |
162 | 171 | data-key="backspace"
|
163 | 172 | formaction="?/update"
|
164 | 173 | name="key"
|
|
171 | 180 | <div class="row">
|
172 | 181 | {#each row as letter}
|
173 | 182 | <button
|
174 |
| - on:click|preventDefault={update} |
| 183 | + onclick={update} |
175 | 184 | data-key={letter}
|
176 | 185 | class={classnames[letter]}
|
177 | 186 | disabled={submittable}
|
|
194 | 203 | <div
|
195 | 204 | style="position: absolute; left: 50%; top: 30%"
|
196 | 205 | use:confetti={{
|
197 |
| - particleCount: $reduced_motion ? 0 : undefined, |
| 206 | + particleCount: $reducedMotion ? 0 : undefined, |
198 | 207 | force: 0.7,
|
199 | 208 | stageWidth: window.innerWidth,
|
200 | 209 | stageHeight: window.innerHeight,
|
|
0 commit comments