Skip to content

Commit 9eb5f92

Browse files
committed
refactor stage select
1 parent 34dcc3a commit 9eb5f92

File tree

1 file changed

+39
-53
lines changed

1 file changed

+39
-53
lines changed

routes/stage-select/+page.svelte

Lines changed: 39 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -4,44 +4,34 @@ import { onMount } from "svelte";
44
import "@/ui-components/menu/menu.css";
55
import { replaceState } from "$app/navigation";
66
7-
let world: string | null = null;
8-
let selected: number | null = null;
7+
// starts from 1
8+
let world = $state<number | null>(null);
9+
let stage = $state<number | null>(null);
10+
const maxWorld = 4;
11+
const maxStage = 4;
912
1013
onMount(() => {
1114
const params = new URLSearchParams(window.location.search);
12-
world = params.get("w") ?? "1";
13-
selected = Number(params.get("s") ?? "1") - 1;
15+
world = Number(params.get("w") ?? "1");
16+
stage = Number(params.get("s") ?? "1");
1417
});
1518
16-
$: blocks = [
17-
{ label: "1", link: `/game?stage=${world}-1` },
18-
{ label: "2", link: `/game?stage=${world}-2` },
19-
{ label: "3", link: `/game?stage=${world}-3` },
20-
{ label: "4", link: `/game?stage=${world}-4` },
21-
];
22-
2319
let lastKeyTime = 0;
2420
let lastKey: string | null = null;
2521
const KEY_REPEAT_DELAY = 180; // ms
2622
27-
function prevWorld() {
28-
// wを数値として1減らす(1未満にはしない)
29-
world = String(Math.max(1, Number(world) - 1));
30-
const url = new URL(window.location.href);
31-
url.searchParams.set("w", world);
32-
replaceState(url.toString(), {});
33-
}
34-
function nextWorld() {
35-
// wを数値として1増やす(4より大きくしない)
36-
world = String(Math.min(4, Number(world) + 1));
37-
const url = new URL(window.location.href);
38-
url.searchParams.set("w", world);
39-
replaceState(url.toString(), {});
40-
}
41-
function select(index: number): void {
42-
selected = index;
23+
function changeStage(worldNum: number, stageNum: number): void {
24+
if (worldNum < 1 || worldNum > maxWorld) {
25+
throw new Error(`World number must be between 1 and ${maxWorld}, but got ${worldNum}`);
26+
}
27+
if (stageNum < 1 || stageNum > maxStage) {
28+
throw new Error(`Stage number must be between 1 and ${maxStage}, but got ${stageNum}`);
29+
}
30+
world = worldNum;
31+
stage = stageNum;
4332
const url = new URL(window.location.href);
44-
url.searchParams.set("s", String(index + 1));
33+
url.searchParams.set("w", String(world));
34+
url.searchParams.set("s", String(stageNum));
4535
replaceState(url.toString(), {});
4636
}
4737
@@ -55,30 +45,28 @@ function handleKey(e: KeyboardEvent): void {
5545
lastKey = e.key;
5646
}
5747
58-
if (selected === null) {
48+
if (stage === null || world === null) {
5949
return;
6050
}
6151
6252
if (e.key === "ArrowRight") {
63-
if (selected === blocks.length - 1) {
64-
if (world !== "4") {
65-
nextWorld();
66-
select(0);
53+
if (stage === maxStage) {
54+
if (world < maxWorld) {
55+
changeStage(world + 1, 1);
6756
}
6857
} else {
69-
select(selected + 1);
58+
changeStage(world, stage + 1);
7059
}
7160
} else if (e.key === "ArrowLeft") {
72-
if (selected === 0) {
73-
if (world !== "1") {
74-
prevWorld();
75-
select(blocks.length - 1);
61+
if (stage === 1) {
62+
if (world > 1) {
63+
changeStage(world - 1, maxStage);
7664
}
7765
} else {
78-
select(selected - 1);
66+
changeStage(world, stage - 1);
7967
}
8068
} else if (e.key === "Enter" || e.key === " ") {
81-
window.location.href = blocks[selected].link;
69+
window.location.href = `/game?stage=${world}-${stage}`;
8270
} else if (e.key === "Escape") {
8371
window.location.href = "/";
8472
e.preventDefault();
@@ -89,8 +77,6 @@ function handleKeyUp() {
8977
lastKey = null;
9078
}
9179
92-
let container: HTMLDivElement | null = null;
93-
9480
onMount(() => {
9581
document.addEventListener("keydown", handleKey);
9682
document.addEventListener("keyup", handleKeyUp);
@@ -119,40 +105,40 @@ onMount(() => {
119105
? 'invisible'
120106
: ''} hover:-translate-y-1 hover:text-gray-700 active:translate-y-0 active:text-black"
121107
aria-label="前のワールド"
122-
on:click={prevWorld}
108+
onclick={() => world !== null && world > 1 && changeStage(world - 1, 1)}
123109
disabled={Number(world) <= 1}
124110
>
125111
&lt;
126112
</button>
127113
<span>World {world}</span>
128114
<!-- 右矢印ボタン -->
129115
<button
130-
class="appearance-none focus:outline-none px-4 select-none cursor-pointer {Number(world) >= 4
116+
class="appearance-none focus:outline-none px-4 select-none cursor-pointer {Number(world) >= maxWorld
131117
? 'invisible'
132118
: ''} hover:-translate-y-1 hover:text-gray-700 active:translate-y-0 active:text-black"
133119
aria-label="次のワールド"
134-
on:click={nextWorld}
135-
disabled={Number(world) >= 4}
120+
onclick={() => world !== null && world < maxWorld && changeStage(world + 1, 1)}
121+
disabled={Number(world) >= maxWorld}
136122
>
137123
&gt;
138124
</button>
139125
</div>
140126

141127
<div class="flex justify-center items-center grow-1">
142128
<div role="button" tabindex="0" class="flex outline-none items-center">
143-
{#each blocks as block, i}
129+
{#each { length: maxStage } as _, idx}
144130
<button
145131
type="button"
146132
class={`appearance-none focus:outline-none bg-white border-6 pt-8 pb-6 pl-8 pr-6 transition-colors duration-200 text-7xl cursor-pointer ${
147-
selected === i
133+
stage === idx + 1
148134
? "border-red-500 ring ring-red-500 bg-amber-100!"
149135
: "border-base"
150136
}`}
151-
on:click={() => select(i)}
137+
onclick={() => world !== null && changeStage(world, idx + 1)}
152138
>
153-
{block.label}
139+
{idx + 1}
154140
</button>
155-
{#if i < blocks.length - 1}
141+
{#if idx + 1 < maxStage}
156142
<!-- 線(矢印や線) -->
157143
<div class="w-20 h-3 bg-black"></div>
158144
{/if}
@@ -164,13 +150,13 @@ onMount(() => {
164150
>
165151
<!-- 画像を中央に配置 -->
166152
<div class="h-full">
167-
{#if selected !== null}
153+
{#if stage !== null}
168154
{#key world}
169155
{#each { length: 4 } as _, idx}
170156
<img
171157
src="/assets/thumbnail{world}-{idx + 1}.png"
172158
alt=""
173-
class={["h-full skeleton", idx !== selected && "hidden"]}
159+
class={["h-full skeleton", idx + 1 !== stage && "hidden"]}
174160
/>
175161
{/each}
176162
{/key}

0 commit comments

Comments
 (0)