@@ -4,44 +4,34 @@ import { onMount } from "svelte";
44import " @/ui-components/menu/menu.css" ;
55import { 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
1013onMount (() => {
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-
2319let lastKeyTime = 0 ;
2420let lastKey: string | null = null ;
2521const 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-
9480onMount (() => {
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 <
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 >
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