Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"rollup-plugin-esbuild": "^6.1.1",
"rollup-plugin-preserve-shebangs": "^0.2.0",
"sv": "workspace:*",
"svelte": "^5.0.0-next.1",
"typescript": "^5.6.2",
"typescript-eslint": "^8.5.0",
"unplugin-isolated-decl": "^0.6.5",
Expand Down
7 changes: 5 additions & 2 deletions packages/create/templates/demo/src/routes/+layout.svelte
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
<script>
<script lang="ts">
import Header from './Header.svelte';
import '../app.css';

/** @type {{children: import('svelte').Snippet}} */
let { children } = $props();
</script>

<div class="app">
<Header />

<main>
<slot />
{@render children()}
</main>

<footer>
Expand Down
6 changes: 3 additions & 3 deletions packages/create/templates/demo/src/routes/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script>
<script lang="ts">
import Counter from './Counter.svelte';
import welcome from '$lib/images/svelte-welcome.webp';
import welcome_fallback from '$lib/images/svelte-welcome.png';
import welcomeFallback from '$lib/images/svelte-welcome.png';
</script>

<svelte:head>
Expand All @@ -14,7 +14,7 @@
<span class="welcome">
<picture>
<source srcset={welcome} type="image/webp" />
<img src={welcome_fallback} alt="Welcome" />
<img src={welcomeFallback} alt="Welcome" />
</picture>
</span>

Expand Down
20 changes: 12 additions & 8 deletions packages/create/templates/demo/src/routes/Counter.svelte
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
<script lang="ts">
import { spring } from 'svelte/motion';

let count = 0;
let count = $state(0);

const displayed_count = spring();
$: displayed_count.set(count);
$: offset = modulo($displayed_count, 1);
// svelte-ignore state_referenced_locally
const displayedCount = spring(count);

$effect(() => {
displayedCount.set(count);
});
let offset = $derived(modulo($displayedCount, 1));

/**
* @param {number} n
Expand All @@ -18,20 +22,20 @@
</script>

<div class="counter">
<button on:click={() => (count -= 1)} aria-label="Decrease the counter by one">
<button onclick={() => (count -= 1)} aria-label="Decrease the counter by one">
<svg aria-hidden="true" viewBox="0 0 1 1">
<path d="M0,0.5 L1,0.5" />
</svg>
</button>

<div class="counter-viewport">
<div class="counter-digits" style="transform: translate(0, {100 * offset}%)">
<strong class="hidden" aria-hidden="true">{Math.floor($displayed_count + 1)}</strong>
<strong>{Math.floor($displayed_count)}</strong>
<strong class="hidden" aria-hidden="true">{Math.floor($displayedCount + 1)}</strong>
<strong>{Math.floor($displayedCount)}</strong>
</div>
</div>

<button on:click={() => (count += 1)} aria-label="Increase the counter by one">
<button onclick={() => (count += 1)} aria-label="Increase the counter by one">
<svg aria-hidden="true" viewBox="0 0 1 1">
<path d="M0,0.5 L1,0.5 M0.5,0 L0.5,1" />
</svg>
Expand Down
2 changes: 1 addition & 1 deletion packages/create/templates/demo/src/routes/Header.svelte
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<script>
<script lang="ts">
import { page } from '$app/stores';
import logo from '$lib/images/svelte-logo.svg';
import github from '$lib/images/github.svg';
Expand Down
83 changes: 46 additions & 37 deletions packages/create/templates/demo/src/routes/sverdle/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,51 +1,58 @@
<script lang="ts">
import { confetti } from '@neoconfetti/svelte';
import { enhance } from '$app/forms';
import type { PageData, ActionData } from './$types';
import { reduced_motion } from './reduced-motion';
import { confetti } from '@neoconfetti/svelte';
import type { ActionData, PageData } from './$types';
import { reducedMotion } from './reduced-motion';

/** @type {import('./$types').PageData} */
export let data: PageData;
interface Props {
data: PageData;
form: ActionData;
}
/**
* @typedef {Object} Props
* @property {import('./$types').PageData} data
* @property {import('./$types').ActionData} form
*/

/** @type {import('./$types').ActionData} */
export let form: ActionData;
/**
* @type {Props}
*/
let { data, form = $bindable() }: Props = $props();

/** Whether or not the user has won */
$: won = data.answers.at(-1) === 'xxxxx';
let won = $derived(data.answers.at(-1) === 'xxxxx');

/** The index of the current guess */
$: i = won ? -1 : data.answers.length;
let i = $derived(won ? -1 : data.answers.length);

/** The current guess */
$: currentGuess = data.guesses[i] || '';
// svelte-ignore state_referenced_locally
let currentGuess = $state(data.guesses[i] || '');

/** Whether the current guess can be submitted */
$: submittable = currentGuess.length === 5;

/**
* A map of classnames for all letters that have been guessed,
* used for styling the keyboard
* @type {Record<string, 'exact' | 'close' | 'missing'>}
*/
let classnames: Record<string, 'exact' | 'close' | 'missing'>;

/**
* A map of descriptions for all letters that have been guessed,
* used for adding text for assistive technology (e.g. screen readers)
* @type {Record<string, string>}
*/
let description: Record<string, string>;

$: {
classnames = {};
description = {};
$effect(() => {
currentGuess = data.guesses[i] || '';
});

/** Whether the current guess can be submitted */
let submittable = $derived(currentGuess.length === 5);

const { classnames, description } = $derived.by(() => {
/**
* A map of classnames for all letters that have been guessed,
* used for styling the keyboard
* @type {Record<string, 'exact' | 'close' | 'missing'>}
*/
let classnames: Record<string, 'exact' | 'close' | 'missing'> = {};
/**
* A map of descriptions for all letters that have been guessed,
* used for adding text for assistive technology (e.g. screen readers)
* @type {Record<string, string>}
*/
let description: Record<string, string> = {};
data.answers.forEach((answer, i) => {
const guess = data.guesses[i];

for (let i = 0; i < 5; i += 1) {
const letter = guess[i];

if (answer[i] === 'x') {
classnames[letter] = 'exact';
description[letter] = 'correct';
Expand All @@ -55,14 +62,16 @@
}
}
});
}
return { classnames, description };
});

/**
* Modify the game state without making a trip to the server,
* if client-side JavaScript is enabled
* @param {MouseEvent} event
*/
function update(event: MouseEvent) {
event.preventDefault();
const key = /** @type {HTMLButtonElement} */ (event.target as HTMLButtonElement).getAttribute(
'data-key'
);
Expand Down Expand Up @@ -91,7 +100,7 @@
}
</script>

<svelte:window on:keydown={keydown} />
<svelte:window onkeydown={keydown} />

<svelte:head>
<title>Sverdle</title>
Expand Down Expand Up @@ -158,7 +167,7 @@
<button data-key="enter" class:selected={submittable} disabled={!submittable}>enter</button>

<button
on:click|preventDefault={update}
onclick={update}
data-key="backspace"
formaction="?/update"
name="key"
Expand All @@ -171,7 +180,7 @@
<div class="row">
{#each row as letter}
<button
on:click|preventDefault={update}
onclick={update}
data-key={letter}
class={classnames[letter]}
disabled={submittable}
Expand All @@ -194,7 +203,7 @@
<div
style="position: absolute; left: 50%; top: 30%"
use:confetti={{
particleCount: $reduced_motion ? 0 : undefined,
particleCount: $reducedMotion ? 0 : undefined,
force: 0.7,
stageWidth: window.innerWidth,
stageHeight: window.innerHeight,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
import { readable } from 'svelte/store';
import { browser } from '$app/environment';

const reduced_motion_query = '(prefers-reduced-motion: reduce)';
const reducedMotionQuery = '(prefers-reduced-motion: reduce)';

const get_initial_motion_preference = () => {
const getInitialMotionPreference = () => {
if (!browser) return false;
return window.matchMedia(reduced_motion_query).matches;
return window.matchMedia(reducedMotionQuery).matches;
};

export const reduced_motion = readable(get_initial_motion_preference(), (set) => {
export const reducedMotion = readable(getInitialMotionPreference(), (set) => {
if (browser) {
/**
* @param {MediaQueryListEvent} event
*/
const set_reduced_motion = (event: MediaQueryListEvent) => {
const setReducedMotion = (event: MediaQueryListEvent) => {
set(event.matches);
};
const media_query_list = window.matchMedia(reduced_motion_query);
media_query_list.addEventListener('change', set_reduced_motion);
const mediaQueryList = window.matchMedia(reducedMotionQuery);
mediaQueryList.addEventListener('change', setReducedMotion);

return () => {
media_query_list.removeEventListener('change', set_reduced_motion);
mediaQueryList.removeEventListener('change', setReducedMotion);
};
}
});
Loading