phaser-hooks 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +330 -0
- package/dist/hooks/batch-state-updates.d.ts +15 -0
- package/dist/hooks/batch-state-updates.d.ts.map +1 -0
- package/dist/hooks/batch-state-updates.js +20 -0
- package/dist/hooks/batch-state-updates.js.map +1 -0
- package/dist/hooks/index.d.ts +11 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +11 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/type.d.ts +59 -0
- package/dist/hooks/type.d.ts.map +1 -0
- package/dist/hooks/type.js +2 -0
- package/dist/hooks/type.js.map +1 -0
- package/dist/hooks/validators.d.ts +26 -0
- package/dist/hooks/validators.d.ts.map +1 -0
- package/dist/hooks/validators.js +54 -0
- package/dist/hooks/validators.js.map +1 -0
- package/dist/hooks/with-computed-state.d.ts +24 -0
- package/dist/hooks/with-computed-state.d.ts.map +1 -0
- package/dist/hooks/with-computed-state.js +40 -0
- package/dist/hooks/with-computed-state.js.map +1 -0
- package/dist/hooks/with-debounced-state.d.ts +22 -0
- package/dist/hooks/with-debounced-state.d.ts.map +1 -0
- package/dist/hooks/with-debounced-state.js +39 -0
- package/dist/hooks/with-debounced-state.js.map +1 -0
- package/dist/hooks/with-global-state.d.ts +150 -0
- package/dist/hooks/with-global-state.d.ts.map +1 -0
- package/dist/hooks/with-global-state.js +240 -0
- package/dist/hooks/with-global-state.js.map +1 -0
- package/dist/hooks/with-local-state.d.ts +69 -0
- package/dist/hooks/with-local-state.d.ts.map +1 -0
- package/dist/hooks/with-local-state.js +78 -0
- package/dist/hooks/with-local-state.js.map +1 -0
- package/dist/hooks/with-persistent-state.d.ts +19 -0
- package/dist/hooks/with-persistent-state.d.ts.map +1 -0
- package/dist/hooks/with-persistent-state.js +45 -0
- package/dist/hooks/with-persistent-state.js.map +1 -0
- package/dist/hooks/with-state-def.d.ts +75 -0
- package/dist/hooks/with-state-def.d.ts.map +1 -0
- package/dist/hooks/with-state-def.js +148 -0
- package/dist/hooks/with-state-def.js.map +1 -0
- package/dist/hooks/with-undoable-state.d.ts +32 -0
- package/dist/hooks/with-undoable-state.d.ts.map +1 -0
- package/dist/hooks/with-undoable-state.js +96 -0
- package/dist/hooks/with-undoable-state.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/is-valid-scene.d.ts +7 -0
- package/dist/utils/is-valid-scene.d.ts.map +1 -0
- package/dist/utils/is-valid-scene.js +12 -0
- package/dist/utils/is-valid-scene.js.map +1 -0
- package/dist/utils/is-valid-scene.spec.d.ts +2 -0
- package/dist/utils/is-valid-scene.spec.d.ts.map +1 -0
- package/dist/utils/is-valid-scene.spec.js +19 -0
- package/dist/utils/is-valid-scene.spec.js.map +1 -0
- package/package.json +69 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 CassinoDev
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
# Phaser Hooks (like "use" hooks in React)
|
|
2
|
+
|
|
3
|
+
A comprehensive state management library for Phaser games with React-like hooks pattern.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install phaser-hooks
|
|
9
|
+
# or
|
|
10
|
+
pnpm add phaser-hooks
|
|
11
|
+
# or
|
|
12
|
+
yarn add phaser-hooks
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Why "with" instead of "use"?
|
|
16
|
+
|
|
17
|
+
While React hooks traditionally use the "use" prefix (e.g., useState, useEffect), this library intentionally uses "with" to avoid linting issues. Many linting configurations, including ESLint's built-in hooks rules, expect functions starting with "use" to be used only within React components and in .jsx/.tsx files.
|
|
18
|
+
|
|
19
|
+
Since this library is designed to work with Phaser games, which typically use plain TypeScript/JavaScript files (.ts/.js), using the "with" prefix helps avoid false positives from linters while maintaining a clear and consistent naming convention that indicates the hook-like pattern these functions follow.
|
|
20
|
+
|
|
21
|
+
This approach allows you to use these state management utilities in your Phaser games without having to modify your linting configuration or suppress warnings.
|
|
22
|
+
|
|
23
|
+
## Available Hooks
|
|
24
|
+
|
|
25
|
+
### Core Hooks
|
|
26
|
+
|
|
27
|
+
#### `withLocalState`
|
|
28
|
+
|
|
29
|
+
Scene-specific state management that gets cleaned up when the scene is destroyed.
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
const playerState = withLocalState<PlayerData>(scene, 'player', {
|
|
33
|
+
hp: 100,
|
|
34
|
+
level: 1,
|
|
35
|
+
exp: 0,
|
|
36
|
+
});
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
#### `withGlobalState`
|
|
40
|
+
|
|
41
|
+
Application-wide state that persists across all scenes.
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
const settingsState = withGlobalState<GameSettings>('settings', {
|
|
45
|
+
soundVolume: 0.8,
|
|
46
|
+
musicEnabled: true,
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
#### `withStateDef`
|
|
51
|
+
|
|
52
|
+
Low-level state definition with custom behaviors and validation.
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
const customState = withStateDef<number>(scene, 'score', {
|
|
56
|
+
initialValue: 0,
|
|
57
|
+
validator: value => value >= 0,
|
|
58
|
+
onChange: (newValue, oldValue) => console.log('Score changed!'),
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Enhanced Hooks
|
|
63
|
+
|
|
64
|
+
#### `withPersistentState`
|
|
65
|
+
|
|
66
|
+
State with automatic localStorage persistence.
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
const persistentSettings = withPersistentState<UserSettings>('settings', {
|
|
70
|
+
volume: 0.8,
|
|
71
|
+
difficulty: 'normal',
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
#### `withComputedState`
|
|
76
|
+
|
|
77
|
+
Derived state that automatically updates when source state changes.
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
const healthPercentage = withComputedState(
|
|
81
|
+
scene,
|
|
82
|
+
'healthPercent',
|
|
83
|
+
playerState,
|
|
84
|
+
player => (player.hp / player.maxHp) * 100
|
|
85
|
+
);
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
#### `withUndoableState`
|
|
89
|
+
|
|
90
|
+
State with undo/redo functionality.
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
const undoableText = withUndoableState<string>(scene, 'text', 'initial', 10);
|
|
94
|
+
|
|
95
|
+
undoableText.set('first change');
|
|
96
|
+
undoableText.set('second change');
|
|
97
|
+
undoableText.undo(); // Back to 'first change'
|
|
98
|
+
undoableText.redo(); // Forward to 'second change'
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
#### `withDebouncedState`
|
|
102
|
+
|
|
103
|
+
State with debounced updates to prevent rapid successive changes.
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
const debouncedSearch = withDebouncedState<string>(scene, 'search', '', 300);
|
|
107
|
+
|
|
108
|
+
// These rapid calls will be debounced
|
|
109
|
+
debouncedSearch.set('a');
|
|
110
|
+
debouncedSearch.set('ab');
|
|
111
|
+
debouncedSearch.set('abc'); // Only this final value will be set after 300ms
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Utilities
|
|
115
|
+
|
|
116
|
+
#### `validators`
|
|
117
|
+
|
|
118
|
+
Pre-built validation functions for common patterns.
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
import { validators } from 'phaser-hooks';
|
|
122
|
+
|
|
123
|
+
const scoreState = withGlobalState<number>('score', 0, {
|
|
124
|
+
validator: validators.numberRange(0, 1000),
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
const nameState = withGlobalState<string>('name', '', {
|
|
128
|
+
validator: validators.nonEmptyString,
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
#### `batchStateUpdates`
|
|
133
|
+
|
|
134
|
+
Utility for batching multiple state updates.
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
batchStateUpdates(() => {
|
|
138
|
+
playerState.set({ ...playerState.get(), hp: 90 });
|
|
139
|
+
inventoryState.set([...inventoryState.get(), 'new-item']);
|
|
140
|
+
scoreState.set(scoreState.get() + 100);
|
|
141
|
+
});
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Basic Usage Example
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
import { withLocalState, withGlobalState } from 'phaser-hooks';
|
|
148
|
+
|
|
149
|
+
export class GameScene extends Phaser.Scene {
|
|
150
|
+
create() {
|
|
151
|
+
// Local state - specific to this scene
|
|
152
|
+
const playerState = withLocalState<{ hp: number; mp: number }>(
|
|
153
|
+
this,
|
|
154
|
+
'player',
|
|
155
|
+
{
|
|
156
|
+
hp: 100,
|
|
157
|
+
mp: 50,
|
|
158
|
+
}
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
// Global state - persists across scenes
|
|
162
|
+
const gameState = withGlobalState<{ score: number; level: number }>(
|
|
163
|
+
'game',
|
|
164
|
+
{
|
|
165
|
+
score: 0,
|
|
166
|
+
level: 1,
|
|
167
|
+
}
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
// Listen to changes
|
|
171
|
+
playerState.onChange((newPlayer, oldPlayer) => {
|
|
172
|
+
console.log('Player health changed:', newPlayer.hp);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// Update state
|
|
176
|
+
playerState.set({
|
|
177
|
+
...playerState.get(),
|
|
178
|
+
hp: playerState.get().hp - 10,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Advanced Example
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
import {
|
|
188
|
+
withPersistentState,
|
|
189
|
+
withComputedState,
|
|
190
|
+
withUndoableState,
|
|
191
|
+
validators,
|
|
192
|
+
} from 'phaser-hooks';
|
|
193
|
+
|
|
194
|
+
export class AdvancedGameScene extends Phaser.Scene {
|
|
195
|
+
create() {
|
|
196
|
+
// Persistent settings
|
|
197
|
+
const settings = withPersistentState<GameSettings>('settings', {
|
|
198
|
+
soundVolume: 0.8,
|
|
199
|
+
musicVolume: 0.6,
|
|
200
|
+
difficulty: 'normal',
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// Player state with validation
|
|
204
|
+
const player = withLocalState<PlayerData>(
|
|
205
|
+
this,
|
|
206
|
+
'player',
|
|
207
|
+
{
|
|
208
|
+
hp: 100,
|
|
209
|
+
maxHp: 100,
|
|
210
|
+
level: 1,
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
validator: validators.oneOf(['easy', 'normal', 'hard']),
|
|
214
|
+
}
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
// Computed health percentage
|
|
218
|
+
const healthPercent = withComputedState(this, 'healthPercent', player, p =>
|
|
219
|
+
Math.round((p.hp / p.maxHp) * 100)
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
// Undoable action system
|
|
223
|
+
const actionHistory = withUndoableState<string>(this, 'actions', 'start');
|
|
224
|
+
|
|
225
|
+
// Use the states
|
|
226
|
+
console.log('Health:', healthPercent.get() + '%');
|
|
227
|
+
|
|
228
|
+
if (healthPercent.get() < 20) {
|
|
229
|
+
console.log('Low health warning!');
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Composing Hooks
|
|
236
|
+
|
|
237
|
+
You can compose your own hooks using other with\* hooks — similar to how custom React hooks are built. This is a powerful way to isolate logic, reuse behavior, and keep your scenes clean and focused.
|
|
238
|
+
|
|
239
|
+
Example: Extracting a withPlayerEnergy hook from withPlayerState
|
|
240
|
+
|
|
241
|
+
Imagine you have a local player state like this:
|
|
242
|
+
|
|
243
|
+
```ts
|
|
244
|
+
interface PlayerAttributes {
|
|
245
|
+
energy: number;
|
|
246
|
+
stamina: number;
|
|
247
|
+
strength: number;
|
|
248
|
+
agility: number;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const playerState = withLocalState<PlayerAttributes>(scene, 'player', {
|
|
252
|
+
energy: 100,
|
|
253
|
+
stamina: 80,
|
|
254
|
+
strength: 50,
|
|
255
|
+
agility: 40,
|
|
256
|
+
});
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
You can now create a custom hook focused only on energy:
|
|
260
|
+
|
|
261
|
+
```ts
|
|
262
|
+
function withPlayerEnergy(scene: Phaser.Scene) {
|
|
263
|
+
const player = withLocalState<PlayerAttributes>(scene, 'player', {
|
|
264
|
+
energy: 100,
|
|
265
|
+
stamina: 80,
|
|
266
|
+
strength: 50,
|
|
267
|
+
agility: 40,
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
return {
|
|
271
|
+
get: () => player.get().energy,
|
|
272
|
+
set: (value: number) => player.set({ ...player.get(), energy: value }),
|
|
273
|
+
onChange: (fn: (energy: number) => void) =>
|
|
274
|
+
player.onChange(newVal => fn(newVal.energy)),
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Usage in a scene
|
|
280
|
+
|
|
281
|
+
```ts
|
|
282
|
+
const energy = withPlayerEnergy(this);
|
|
283
|
+
|
|
284
|
+
console.log('Current energy:', energy.get());
|
|
285
|
+
|
|
286
|
+
energy.set(energy.get() - 10);
|
|
287
|
+
|
|
288
|
+
energy.onChange(newEnergy => {
|
|
289
|
+
if (newEnergy <= 0) {
|
|
290
|
+
console.warn('You are out of energy!');
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Why use this pattern?
|
|
296
|
+
|
|
297
|
+
✅ Keeps your scene code focused on intent (e.g., energy.get()) rather than structure (player.get().energy)
|
|
298
|
+
|
|
299
|
+
✅ Allows centralized validation, side effects, or formatting for specific state slices
|
|
300
|
+
|
|
301
|
+
✅ Makes it easier to refactor or share logic across scenes and systems
|
|
302
|
+
|
|
303
|
+
You can extend this idea to compose computed hooks, persistent hooks, undoable hooks, and more — everything works with the same API.
|
|
304
|
+
|
|
305
|
+
## TypeScript Support
|
|
306
|
+
|
|
307
|
+
All hooks are fully typed and provide excellent TypeScript support:
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
interface PlayerData {
|
|
311
|
+
hp: number;
|
|
312
|
+
maxHp: number;
|
|
313
|
+
level: number;
|
|
314
|
+
inventory: string[];
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const playerState = withLocalState<PlayerData>(scene, 'player', {
|
|
318
|
+
hp: 100,
|
|
319
|
+
maxHp: 100,
|
|
320
|
+
level: 1,
|
|
321
|
+
inventory: [],
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
// TypeScript knows the exact type
|
|
325
|
+
const currentPlayer: PlayerData = playerState.get();
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
## License
|
|
329
|
+
|
|
330
|
+
MIT
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility to batch multiple state updates
|
|
3
|
+
* @param updateFn Function that performs multiple state updates
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```typescript
|
|
7
|
+
* batchStateUpdates(() => {
|
|
8
|
+
* playerState.set({...playerState.get(), hp: 90});
|
|
9
|
+
* inventoryState.set([...inventoryState.get(), 'new-item']);
|
|
10
|
+
* scoreState.set(scoreState.get() + 100);
|
|
11
|
+
* });
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export declare const batchStateUpdates: (updateFn: () => void) => void;
|
|
15
|
+
//# sourceMappingURL=batch-state-updates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batch-state-updates.d.ts","sourceRoot":"","sources":["../../src/hooks/batch-state-updates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,iBAAiB,GAAI,UAAU,MAAM,IAAI,KAAG,IAKxD,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility to batch multiple state updates
|
|
3
|
+
* @param updateFn Function that performs multiple state updates
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```typescript
|
|
7
|
+
* batchStateUpdates(() => {
|
|
8
|
+
* playerState.set({...playerState.get(), hp: 90});
|
|
9
|
+
* inventoryState.set([...inventoryState.get(), 'new-item']);
|
|
10
|
+
* scoreState.set(scoreState.get() + 100);
|
|
11
|
+
* });
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export const batchStateUpdates = (updateFn) => {
|
|
15
|
+
// Note: This is a placeholder for potential batching optimization
|
|
16
|
+
// In a more advanced implementation, you might collect all updates
|
|
17
|
+
// and apply them in a single registry update
|
|
18
|
+
updateFn();
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=batch-state-updates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batch-state-updates.js","sourceRoot":"","sources":["../../src/hooks/batch-state-updates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,QAAoB,EAAQ,EAAE;IAC9D,kEAAkE;IAClE,mEAAmE;IACnE,6CAA6C;IAC7C,QAAQ,EAAE,CAAC;AACb,CAAC,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from './batch-state-updates';
|
|
2
|
+
export * from './type';
|
|
3
|
+
export * from './validators';
|
|
4
|
+
export * from './with-computed-state';
|
|
5
|
+
export * from './with-debounced-state';
|
|
6
|
+
export * from './with-global-state';
|
|
7
|
+
export * from './with-local-state';
|
|
8
|
+
export * from './with-persistent-state';
|
|
9
|
+
export * from './with-state-def';
|
|
10
|
+
export * from './with-undoable-state';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from './batch-state-updates';
|
|
2
|
+
export * from './type';
|
|
3
|
+
export * from './validators';
|
|
4
|
+
export * from './with-computed-state';
|
|
5
|
+
export * from './with-debounced-state';
|
|
6
|
+
export * from './with-global-state';
|
|
7
|
+
export * from './with-local-state';
|
|
8
|
+
export * from './with-persistent-state';
|
|
9
|
+
export * from './with-state-def';
|
|
10
|
+
export * from './with-undoable-state';
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Callback function type for state changes
|
|
3
|
+
* @template T The type of the state value
|
|
4
|
+
* @param newValue The new state value after the change
|
|
5
|
+
* @param oldValue The previous state value before the change
|
|
6
|
+
*/
|
|
7
|
+
export type StateChangeCallback<T> = (newValue: T, oldValue: T) => void;
|
|
8
|
+
/**
|
|
9
|
+
* Utility type for state selectors
|
|
10
|
+
* @template T The input state type
|
|
11
|
+
* @template U The computed result type
|
|
12
|
+
*/
|
|
13
|
+
export type StateSelector<T, U> = (state: T) => U;
|
|
14
|
+
/**
|
|
15
|
+
* Utility type for state updaters
|
|
16
|
+
* @template T The state type
|
|
17
|
+
*/
|
|
18
|
+
export type StateUpdater<T> = (currentState: T) => T;
|
|
19
|
+
/**
|
|
20
|
+
* Core interface for all state management hooks in Phaser games.
|
|
21
|
+
* Provides a React-like state management API with get/set/onChange functionality.
|
|
22
|
+
*
|
|
23
|
+
* @template T The type of the state value being managed
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* // Basic usage
|
|
28
|
+
* const scoreState: HookState<number> = withLocalState(scene, 'score', 0);
|
|
29
|
+
*
|
|
30
|
+
* // Getting current value
|
|
31
|
+
* const currentScore = scoreState.get();
|
|
32
|
+
*
|
|
33
|
+
* // Setting new value
|
|
34
|
+
* scoreState.set(100);
|
|
35
|
+
*
|
|
36
|
+
* // Listening to changes
|
|
37
|
+
* scoreState.onChange((newScore, oldScore) => {
|
|
38
|
+
* console.log(`Score changed from ${oldScore} to ${newScore}`);
|
|
39
|
+
* });
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export type HookState<T> = {
|
|
43
|
+
/**
|
|
44
|
+
* Gets the current state value
|
|
45
|
+
* @returns The current state value
|
|
46
|
+
*/
|
|
47
|
+
get: () => T;
|
|
48
|
+
/**
|
|
49
|
+
* Sets a new state value and triggers change listeners
|
|
50
|
+
* @param value The new value to set
|
|
51
|
+
*/
|
|
52
|
+
set: (value: T) => void;
|
|
53
|
+
/**
|
|
54
|
+
* Registers a callback to be called whenever the state changes
|
|
55
|
+
* @param callback Function to call when state changes
|
|
56
|
+
*/
|
|
57
|
+
onChange: (callback: StateChangeCallback<T>) => void;
|
|
58
|
+
};
|
|
59
|
+
//# sourceMappingURL=type.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type.d.ts","sourceRoot":"","sources":["../../src/hooks/type.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,KAAK,IAAI,CAAC;AAExE;;;;GAIG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC;AAElD;;;GAGG;AACH,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,KAAK,CAAC,CAAC;AAErD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI;IACzB;;;OAGG;IACH,GAAG,EAAE,MAAM,CAAC,CAAC;IAEb;;;OAGG;IACH,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IAExB;;;OAGG;IACH,QAAQ,EAAE,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;CACtD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type.js","sourceRoot":"","sources":["../../src/hooks/type.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State validator function type
|
|
3
|
+
*/
|
|
4
|
+
export type StateValidator = (value: unknown) => boolean | string;
|
|
5
|
+
/**
|
|
6
|
+
* Creates a state validator function for common patterns
|
|
7
|
+
*/
|
|
8
|
+
export declare const validators: {
|
|
9
|
+
/**
|
|
10
|
+
* Validates that a number is within a range
|
|
11
|
+
*/
|
|
12
|
+
numberRange: (min: number, max: number) => StateValidator;
|
|
13
|
+
/**
|
|
14
|
+
* Validates that a string is not empty
|
|
15
|
+
*/
|
|
16
|
+
nonEmptyString: (value: unknown) => boolean | string;
|
|
17
|
+
/**
|
|
18
|
+
* Validates that an array has a specific length range
|
|
19
|
+
*/
|
|
20
|
+
arrayLength: (min: number, max?: number) => StateValidator;
|
|
21
|
+
/**
|
|
22
|
+
* Validates that a value is one of the allowed options
|
|
23
|
+
*/
|
|
24
|
+
oneOf: <T>(allowedValues: T[]) => StateValidator;
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=validators.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../../src/hooks/validators.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,GAAG,MAAM,CAAC;AAElE;;GAEG;AACH,eAAO,MAAM,UAAU;IACrB;;OAEG;uBAEK,MAAM,OAAO,MAAM,KAAG,cAAc;IAY5C;;OAEG;4BACqB,OAAO,KAAG,OAAO,GAAG,MAAM;IAQlD;;OAEG;uBAEK,MAAM,QAAQ,MAAM,KAAG,cAAc;IAe7C;;OAEG;YAEA,CAAC,iBAAiB,CAAC,EAAE,KAAG,cAAc;CAO1C,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a state validator function for common patterns
|
|
3
|
+
*/
|
|
4
|
+
export const validators = {
|
|
5
|
+
/**
|
|
6
|
+
* Validates that a number is within a range
|
|
7
|
+
*/
|
|
8
|
+
numberRange: (min, max) => (value) => {
|
|
9
|
+
const num = value;
|
|
10
|
+
if (typeof num !== 'number' || Number.isNaN(num)) {
|
|
11
|
+
return 'Value must be a number';
|
|
12
|
+
}
|
|
13
|
+
if (num < min || num > max) {
|
|
14
|
+
return `Value must be between ${min} and ${max}`;
|
|
15
|
+
}
|
|
16
|
+
return true;
|
|
17
|
+
},
|
|
18
|
+
/**
|
|
19
|
+
* Validates that a string is not empty
|
|
20
|
+
*/
|
|
21
|
+
nonEmptyString: (value) => {
|
|
22
|
+
const str = value;
|
|
23
|
+
if (typeof str !== 'string' || str.trim().length === 0) {
|
|
24
|
+
return 'Value must be a non-empty string';
|
|
25
|
+
}
|
|
26
|
+
return true;
|
|
27
|
+
},
|
|
28
|
+
/**
|
|
29
|
+
* Validates that an array has a specific length range
|
|
30
|
+
*/
|
|
31
|
+
arrayLength: (min, max) => (value) => {
|
|
32
|
+
const arr = value;
|
|
33
|
+
if (!Array.isArray(arr)) {
|
|
34
|
+
return 'Value must be an array';
|
|
35
|
+
}
|
|
36
|
+
if (arr.length < min) {
|
|
37
|
+
return `Array must have at least ${min} items`;
|
|
38
|
+
}
|
|
39
|
+
if (max !== undefined && arr.length > max) {
|
|
40
|
+
return `Array must have at most ${max} items`;
|
|
41
|
+
}
|
|
42
|
+
return true;
|
|
43
|
+
},
|
|
44
|
+
/**
|
|
45
|
+
* Validates that a value is one of the allowed options
|
|
46
|
+
*/
|
|
47
|
+
oneOf: (allowedValues) => (value) => {
|
|
48
|
+
if (!allowedValues.includes(value)) {
|
|
49
|
+
return `Value must be one of: ${allowedValues.join(', ')}`;
|
|
50
|
+
}
|
|
51
|
+
return true;
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
//# sourceMappingURL=validators.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validators.js","sourceRoot":"","sources":["../../src/hooks/validators.ts"],"names":[],"mappings":"AAKA;;GAEG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB;;OAEG;IACH,WAAW,EACT,CAAC,GAAW,EAAE,GAAW,EAAkB,EAAE,CAC7C,CAAC,KAAc,EAAoB,EAAE;QACnC,MAAM,GAAG,GAAG,KAAe,CAAC;QAC5B,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YACjD,OAAO,wBAAwB,CAAC;QAClC,CAAC;QACD,IAAI,GAAG,GAAG,GAAG,IAAI,GAAG,GAAG,GAAG,EAAE,CAAC;YAC3B,OAAO,yBAAyB,GAAG,QAAQ,GAAG,EAAE,CAAC;QACnD,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAEH;;OAEG;IACH,cAAc,EAAE,CAAC,KAAc,EAAoB,EAAE;QACnD,MAAM,GAAG,GAAG,KAAe,CAAC;QAC5B,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvD,OAAO,kCAAkC,CAAC;QAC5C,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACH,WAAW,EACT,CAAC,GAAW,EAAE,GAAY,EAAkB,EAAE,CAC9C,CAAC,KAAc,EAAoB,EAAE;QACnC,MAAM,GAAG,GAAG,KAAkB,CAAC;QAC/B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,OAAO,wBAAwB,CAAC;QAClC,CAAC;QACD,IAAI,GAAG,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;YACrB,OAAO,4BAA4B,GAAG,QAAQ,CAAC;QACjD,CAAC;QACD,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;YAC1C,OAAO,2BAA2B,GAAG,QAAQ,CAAC;QAChD,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAEH;;OAEG;IACH,KAAK,EACH,CAAI,aAAkB,EAAkB,EAAE,CAC1C,CAAC,KAAc,EAAoB,EAAE;QACnC,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,KAAU,CAAC,EAAE,CAAC;YACxC,OAAO,yBAAyB,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC7D,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;CACJ,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type HookState, type StateSelector } from './type';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a computed state that derives its value from other states
|
|
4
|
+
* @template T The input state type
|
|
5
|
+
* @template U The computed result type
|
|
6
|
+
* @param scene The Phaser scene instance
|
|
7
|
+
* @param key Unique identifier for the computed state
|
|
8
|
+
* @param sourceState The source state to derive from
|
|
9
|
+
* @param selector Function to compute the derived value
|
|
10
|
+
* @returns HookState with computed value
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* const playerState = withLocalState<{hp: number, maxHp: number}>(scene, 'player', {...});
|
|
15
|
+
* const healthPercentage = withComputedState(
|
|
16
|
+
* scene,
|
|
17
|
+
* 'healthPercent',
|
|
18
|
+
* playerState,
|
|
19
|
+
* (player) => (player.hp / player.maxHp) * 100
|
|
20
|
+
* );
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare const withComputedState: <T, U>(scene: Phaser.Scene, key: string, sourceState: HookState<T>, selector: StateSelector<T, U>) => HookState<U>;
|
|
24
|
+
//# sourceMappingURL=with-computed-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"with-computed-state.d.ts","sourceRoot":"","sources":["../../src/hooks/with-computed-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,QAAQ,CAAC;AAG5D;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,iBAAiB,GAAI,CAAC,EAAE,CAAC,EACpC,OAAO,MAAM,CAAC,KAAK,EACnB,KAAK,MAAM,EACX,aAAa,SAAS,CAAC,CAAC,CAAC,EACzB,UAAU,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,KAC5B,SAAS,CAAC,CAAC,CAoBb,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { withLocalState } from './with-local-state';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a computed state that derives its value from other states
|
|
4
|
+
* @template T The input state type
|
|
5
|
+
* @template U The computed result type
|
|
6
|
+
* @param scene The Phaser scene instance
|
|
7
|
+
* @param key Unique identifier for the computed state
|
|
8
|
+
* @param sourceState The source state to derive from
|
|
9
|
+
* @param selector Function to compute the derived value
|
|
10
|
+
* @returns HookState with computed value
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* const playerState = withLocalState<{hp: number, maxHp: number}>(scene, 'player', {...});
|
|
15
|
+
* const healthPercentage = withComputedState(
|
|
16
|
+
* scene,
|
|
17
|
+
* 'healthPercent',
|
|
18
|
+
* playerState,
|
|
19
|
+
* (player) => (player.hp / player.maxHp) * 100
|
|
20
|
+
* );
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export const withComputedState = (scene, key, sourceState, selector) => {
|
|
24
|
+
// Initialize with computed value
|
|
25
|
+
const initialValue = selector(sourceState.get());
|
|
26
|
+
const computedState = withLocalState(scene, key, initialValue);
|
|
27
|
+
// Update computed state when source changes
|
|
28
|
+
sourceState.onChange(newSourceValue => {
|
|
29
|
+
const newComputedValue = selector(newSourceValue);
|
|
30
|
+
computedState.set(newComputedValue);
|
|
31
|
+
});
|
|
32
|
+
return {
|
|
33
|
+
get: computedState.get,
|
|
34
|
+
set: () => {
|
|
35
|
+
throw new Error(`[withComputedState] Cannot directly set computed state "${key}". Update the source state instead.`);
|
|
36
|
+
},
|
|
37
|
+
onChange: computedState.onChange,
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
//# sourceMappingURL=with-computed-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"with-computed-state.js","sourceRoot":"","sources":["../../src/hooks/with-computed-state.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAC/B,KAAmB,EACnB,GAAW,EACX,WAAyB,EACzB,QAA6B,EACf,EAAE;IAChB,iCAAiC;IACjC,MAAM,YAAY,GAAG,QAAQ,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC;IACjD,MAAM,aAAa,GAAG,cAAc,CAAI,KAAK,EAAE,GAAG,EAAE,YAAY,CAAC,CAAC;IAElE,4CAA4C;IAC5C,WAAW,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE;QACpC,MAAM,gBAAgB,GAAG,QAAQ,CAAC,cAAc,CAAC,CAAC;QAClD,aAAa,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,GAAG,EAAE,aAAa,CAAC,GAAG;QACtB,GAAG,EAAE,GAAS,EAAE;YACd,MAAM,IAAI,KAAK,CACb,2DAA2D,GAAG,qCAAqC,CACpG,CAAC;QACJ,CAAC;QACD,QAAQ,EAAE,aAAa,CAAC,QAAQ;KACjC,CAAC;AACJ,CAAC,CAAC"}
|