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
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type HookState } from './type';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a state hook with debounced updates
|
|
4
|
+
* @template T The type of the state value
|
|
5
|
+
* @param scene The Phaser scene instance
|
|
6
|
+
* @param key Unique identifier for the state
|
|
7
|
+
* @param initialValue Initial value for the state
|
|
8
|
+
* @param debounceMs Debounce delay in milliseconds
|
|
9
|
+
* @returns HookState with debounced set operations
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* const debouncedSearch = withDebouncedState<string>(scene, 'search', '', 300);
|
|
14
|
+
*
|
|
15
|
+
* // These rapid calls will be debounced
|
|
16
|
+
* debouncedSearch.set('a');
|
|
17
|
+
* debouncedSearch.set('ab');
|
|
18
|
+
* debouncedSearch.set('abc'); // Only this final value will be set after 300ms
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare const withDebouncedState: <T>(scene: Phaser.Scene, key: string, initialValue: T, debounceMs: number) => HookState<T>;
|
|
22
|
+
//# sourceMappingURL=with-debounced-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"with-debounced-state.d.ts","sourceRoot":"","sources":["../../src/hooks/with-debounced-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,QAAQ,CAAC;AAGxC;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,kBAAkB,GAAI,CAAC,EAClC,OAAO,MAAM,CAAC,KAAK,EACnB,KAAK,MAAM,EACX,cAAc,CAAC,EACf,YAAY,MAAM,KACjB,SAAS,CAAC,CAAC,CAoBb,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { withLocalState } from './with-local-state';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a state hook with debounced updates
|
|
4
|
+
* @template T The type of the state value
|
|
5
|
+
* @param scene The Phaser scene instance
|
|
6
|
+
* @param key Unique identifier for the state
|
|
7
|
+
* @param initialValue Initial value for the state
|
|
8
|
+
* @param debounceMs Debounce delay in milliseconds
|
|
9
|
+
* @returns HookState with debounced set operations
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* const debouncedSearch = withDebouncedState<string>(scene, 'search', '', 300);
|
|
14
|
+
*
|
|
15
|
+
* // These rapid calls will be debounced
|
|
16
|
+
* debouncedSearch.set('a');
|
|
17
|
+
* debouncedSearch.set('ab');
|
|
18
|
+
* debouncedSearch.set('abc'); // Only this final value will be set after 300ms
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export const withDebouncedState = (scene, key, initialValue, debounceMs) => {
|
|
22
|
+
const actualState = withLocalState(scene, key, initialValue);
|
|
23
|
+
let timeoutId = null;
|
|
24
|
+
const debouncedSet = (value) => {
|
|
25
|
+
if (timeoutId) {
|
|
26
|
+
clearTimeout(timeoutId);
|
|
27
|
+
}
|
|
28
|
+
timeoutId = setTimeout(() => {
|
|
29
|
+
actualState.set(value);
|
|
30
|
+
timeoutId = null;
|
|
31
|
+
}, debounceMs);
|
|
32
|
+
};
|
|
33
|
+
return {
|
|
34
|
+
get: actualState.get,
|
|
35
|
+
set: debouncedSet,
|
|
36
|
+
onChange: actualState.onChange,
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=with-debounced-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"with-debounced-state.js","sourceRoot":"","sources":["../../src/hooks/with-debounced-state.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEpD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAChC,KAAmB,EACnB,GAAW,EACX,YAAe,EACf,UAAkB,EACJ,EAAE;IAChB,MAAM,WAAW,GAAG,cAAc,CAAI,KAAK,EAAE,GAAG,EAAE,YAAY,CAAC,CAAC;IAChE,IAAI,SAAS,GAAyC,IAAI,CAAC;IAE3D,MAAM,YAAY,GAAG,CAAC,KAAQ,EAAQ,EAAE;QACtC,IAAI,SAAS,EAAE,CAAC;YACd,YAAY,CAAC,SAAS,CAAC,CAAC;QAC1B,CAAC;QAED,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE;YAC1B,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACvB,SAAS,GAAG,IAAI,CAAC;QACnB,CAAC,EAAE,UAAU,CAAC,CAAC;IACjB,CAAC,CAAC;IAEF,OAAO;QACL,GAAG,EAAE,WAAW,CAAC,GAAG;QACpB,GAAG,EAAE,YAAY;QACjB,QAAQ,EAAE,WAAW,CAAC,QAAQ;KAC/B,CAAC;AACJ,CAAC,CAAC"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { type HookState, type StateChangeCallback } from './type';
|
|
2
|
+
/**
|
|
3
|
+
* Global state manager singleton.
|
|
4
|
+
* Manages application-wide state without dependency on specific scenes.
|
|
5
|
+
*/
|
|
6
|
+
declare class GlobalState {
|
|
7
|
+
private static registry;
|
|
8
|
+
private static listeners;
|
|
9
|
+
/**
|
|
10
|
+
* Set a value in the global state
|
|
11
|
+
*/
|
|
12
|
+
static set<T>(key: string, value: T): void;
|
|
13
|
+
/**
|
|
14
|
+
* Get a value from the global state
|
|
15
|
+
*/
|
|
16
|
+
static get<T>(key: string, defaultValue?: T): T | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* Check if a key exists in the global state
|
|
19
|
+
*/
|
|
20
|
+
static has(key: string): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Register a callback for when a specific key changes
|
|
23
|
+
*/
|
|
24
|
+
static onChange<T>(key: string, callback: StateChangeCallback<T>): void;
|
|
25
|
+
/**
|
|
26
|
+
* Remove a callback for a specific key
|
|
27
|
+
*/
|
|
28
|
+
static offChange<T>(key: string, callback: StateChangeCallback<T>): void;
|
|
29
|
+
/**
|
|
30
|
+
* Clear all global state (useful for testing or resetting game)
|
|
31
|
+
*/
|
|
32
|
+
static clear(): void;
|
|
33
|
+
/**
|
|
34
|
+
* Get all keys in the global state
|
|
35
|
+
*/
|
|
36
|
+
static keys(): string[];
|
|
37
|
+
/**
|
|
38
|
+
* Get debug information about the global state
|
|
39
|
+
*/
|
|
40
|
+
static debug(): {
|
|
41
|
+
registry: Record<string, unknown>;
|
|
42
|
+
listeners: Record<string, number>;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Configuration for global state management
|
|
47
|
+
*/
|
|
48
|
+
export type GlobalStateOptions = {
|
|
49
|
+
/**
|
|
50
|
+
* Validator function to validate state values
|
|
51
|
+
*/
|
|
52
|
+
validator?: (value: unknown) => boolean | string;
|
|
53
|
+
/**
|
|
54
|
+
* Enable debug logging
|
|
55
|
+
*/
|
|
56
|
+
debug?: boolean;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Creates a global state hook that persists across all scenes in the game.
|
|
60
|
+
* Global state is managed by a singleton registry, removing dependency on specific scenes.
|
|
61
|
+
* Perfect for user settings, game progress, authentication state, or any data that
|
|
62
|
+
* should persist throughout the game session.
|
|
63
|
+
*
|
|
64
|
+
* @template T The type of the state value
|
|
65
|
+
* @param scene Phaser scene instance (kept for API compatibility, not used internally)
|
|
66
|
+
* @param key Unique identifier for the global state
|
|
67
|
+
* @param initialValue Optional initial value to set if state doesn't exist
|
|
68
|
+
* @param options Optional configuration for validation and debugging
|
|
69
|
+
* @returns HookState interface for managing the global state
|
|
70
|
+
*
|
|
71
|
+
* @throws {Error} When key is invalid
|
|
72
|
+
* @throws {Error} When validator rejects the initial value
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```typescript
|
|
76
|
+
* // User settings that persist across scenes
|
|
77
|
+
* interface UserSettings {
|
|
78
|
+
* soundVolume: number;
|
|
79
|
+
* musicEnabled: boolean;
|
|
80
|
+
* difficulty: 'easy' | 'normal' | 'hard';
|
|
81
|
+
* }
|
|
82
|
+
*
|
|
83
|
+
* const settingsState = withGlobalState<UserSettings>(scene, 'settings', {
|
|
84
|
+
* soundVolume: 0.8,
|
|
85
|
+
* musicEnabled: true,
|
|
86
|
+
* difficulty: 'normal'
|
|
87
|
+
* });
|
|
88
|
+
*
|
|
89
|
+
* // Game progress
|
|
90
|
+
* interface GameProgress {
|
|
91
|
+
* currentLevel: number;
|
|
92
|
+
* unlockedLevels: number[];
|
|
93
|
+
* totalScore: number;
|
|
94
|
+
* }
|
|
95
|
+
*
|
|
96
|
+
* const progressState = withGlobalState<GameProgress>(scene, 'progress', {
|
|
97
|
+
* currentLevel: 1,
|
|
98
|
+
* unlockedLevels: [1],
|
|
99
|
+
* totalScore: 0
|
|
100
|
+
* });
|
|
101
|
+
*
|
|
102
|
+
* // Listen to changes globally
|
|
103
|
+
* progressState.onChange((newProgress, oldProgress) => {
|
|
104
|
+
* console.log('Game progress updated:', newProgress);
|
|
105
|
+
* // Could trigger achievements, save to localStorage, etc.
|
|
106
|
+
* });
|
|
107
|
+
* ```
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* ```typescript
|
|
111
|
+
* // With validation
|
|
112
|
+
* const scoreState = withGlobalState<number>(scene, 'score', 0, {
|
|
113
|
+
* validator: (value) => {
|
|
114
|
+
* if (typeof value !== 'number' || value < 0) {
|
|
115
|
+
* return 'Score must be a positive number';
|
|
116
|
+
* }
|
|
117
|
+
* return true;
|
|
118
|
+
* },
|
|
119
|
+
* debug: true
|
|
120
|
+
* });
|
|
121
|
+
*
|
|
122
|
+
* // Authentication state example
|
|
123
|
+
* interface AuthState {
|
|
124
|
+
* isLoggedIn: boolean;
|
|
125
|
+
* username: string | null;
|
|
126
|
+
* token: string | null;
|
|
127
|
+
* }
|
|
128
|
+
*
|
|
129
|
+
* const authState = withGlobalState<AuthState>(scene, 'auth', {
|
|
130
|
+
* isLoggedIn: false,
|
|
131
|
+
* username: null,
|
|
132
|
+
* token: null
|
|
133
|
+
* });
|
|
134
|
+
*
|
|
135
|
+
* // Use from any scene - no dependency on Boot scene!
|
|
136
|
+
* if (authState.get().isLoggedIn) {
|
|
137
|
+
* // Show authenticated content
|
|
138
|
+
* }
|
|
139
|
+
* ```
|
|
140
|
+
*
|
|
141
|
+
* @see {@link withLocalState} For scene-specific state
|
|
142
|
+
* @see {@link withStateDef} For low-level state management
|
|
143
|
+
*/
|
|
144
|
+
export declare const withGlobalState: <T = unknown>(key: string, initialValue?: T, options?: GlobalStateOptions) => HookState<T>;
|
|
145
|
+
/**
|
|
146
|
+
* Export the GlobalState class for advanced use cases
|
|
147
|
+
* (testing, debugging, manual state management)
|
|
148
|
+
*/
|
|
149
|
+
export { GlobalState };
|
|
150
|
+
//# sourceMappingURL=with-global-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"with-global-state.d.ts","sourceRoot":"","sources":["../../src/hooks/with-global-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AAElE;;;GAGG;AACH,cAAM,WAAW;IACf,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA8B;IACrD,OAAO,CAAC,MAAM,CAAC,SAAS,CAGpB;IAEJ;;OAEG;IACH,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAqB1C;;OAEG;IACH,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS;IAM3D;;OAEG;IACH,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAIhC;;OAEG;IACH,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,mBAAmB,CAAC,CAAC,CAAC,GAAG,IAAI;IAOvE;;OAEG;IACH,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,mBAAmB,CAAC,CAAC,CAAC,GAAG,IAAI;IAUxE;;OAEG;IACH,MAAM,CAAC,KAAK,IAAI,IAAI;IAKpB;;OAEG;IACH,MAAM,CAAC,IAAI,IAAI,MAAM,EAAE;IAIvB;;OAEG;IACH,MAAM,CAAC,KAAK,IAAI;QACd,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAClC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACnC;CAcF;AAED;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B;;OAEG;IACH,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,GAAG,MAAM,CAAC;IAEjD;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqFG;AAIH,eAAO,MAAM,eAAe,GAAI,CAAC,GAAG,OAAO,EACzC,KAAK,MAAM,EACX,eAAe,CAAC,EAChB,UAAS,kBAAuB,KAC/B,SAAS,CAAC,CAAC,CA4Eb,CAAC;AAEF;;;GAGG;AACH,OAAO,EAAE,WAAW,EAAE,CAAC"}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global state manager singleton.
|
|
3
|
+
* Manages application-wide state without dependency on specific scenes.
|
|
4
|
+
*/
|
|
5
|
+
class GlobalState {
|
|
6
|
+
static registry = new Map();
|
|
7
|
+
static listeners = new Map();
|
|
8
|
+
/**
|
|
9
|
+
* Set a value in the global state
|
|
10
|
+
*/
|
|
11
|
+
static set(key, value) {
|
|
12
|
+
const oldValue = this.registry.get(key);
|
|
13
|
+
this.registry.set(key, value);
|
|
14
|
+
// Trigger callbacks
|
|
15
|
+
const callbacks = this.listeners.get(key);
|
|
16
|
+
if (callbacks) {
|
|
17
|
+
callbacks.forEach(callback => {
|
|
18
|
+
try {
|
|
19
|
+
callback(value, oldValue);
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
// eslint-disable-next-line no-console
|
|
23
|
+
console.error(`[GlobalState] Error in onChange callback for "${key}":`, error);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Get a value from the global state
|
|
30
|
+
*/
|
|
31
|
+
static get(key, defaultValue) {
|
|
32
|
+
return this.registry.has(key)
|
|
33
|
+
? this.registry.get(key)
|
|
34
|
+
: defaultValue;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Check if a key exists in the global state
|
|
38
|
+
*/
|
|
39
|
+
static has(key) {
|
|
40
|
+
return this.registry.has(key);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Register a callback for when a specific key changes
|
|
44
|
+
*/
|
|
45
|
+
static onChange(key, callback) {
|
|
46
|
+
if (!this.listeners.has(key)) {
|
|
47
|
+
this.listeners.set(key, new Set());
|
|
48
|
+
}
|
|
49
|
+
this.listeners.get(key)?.add(callback);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Remove a callback for a specific key
|
|
53
|
+
*/
|
|
54
|
+
static offChange(key, callback) {
|
|
55
|
+
const callbacks = this.listeners.get(key);
|
|
56
|
+
if (callbacks) {
|
|
57
|
+
callbacks.delete(callback);
|
|
58
|
+
if (callbacks.size === 0) {
|
|
59
|
+
this.listeners.delete(key);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Clear all global state (useful for testing or resetting game)
|
|
65
|
+
*/
|
|
66
|
+
static clear() {
|
|
67
|
+
this.registry.clear();
|
|
68
|
+
this.listeners.clear();
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Get all keys in the global state
|
|
72
|
+
*/
|
|
73
|
+
static keys() {
|
|
74
|
+
return Array.from(this.registry.keys());
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get debug information about the global state
|
|
78
|
+
*/
|
|
79
|
+
static debug() {
|
|
80
|
+
const debugRegistry = {};
|
|
81
|
+
const debugListeners = {};
|
|
82
|
+
this.registry.forEach((value, key) => {
|
|
83
|
+
debugRegistry[key] = value;
|
|
84
|
+
});
|
|
85
|
+
this.listeners.forEach((callbacks, key) => {
|
|
86
|
+
debugListeners[key] = callbacks.size;
|
|
87
|
+
});
|
|
88
|
+
return { registry: debugRegistry, listeners: debugListeners };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Creates a global state hook that persists across all scenes in the game.
|
|
93
|
+
* Global state is managed by a singleton registry, removing dependency on specific scenes.
|
|
94
|
+
* Perfect for user settings, game progress, authentication state, or any data that
|
|
95
|
+
* should persist throughout the game session.
|
|
96
|
+
*
|
|
97
|
+
* @template T The type of the state value
|
|
98
|
+
* @param scene Phaser scene instance (kept for API compatibility, not used internally)
|
|
99
|
+
* @param key Unique identifier for the global state
|
|
100
|
+
* @param initialValue Optional initial value to set if state doesn't exist
|
|
101
|
+
* @param options Optional configuration for validation and debugging
|
|
102
|
+
* @returns HookState interface for managing the global state
|
|
103
|
+
*
|
|
104
|
+
* @throws {Error} When key is invalid
|
|
105
|
+
* @throws {Error} When validator rejects the initial value
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```typescript
|
|
109
|
+
* // User settings that persist across scenes
|
|
110
|
+
* interface UserSettings {
|
|
111
|
+
* soundVolume: number;
|
|
112
|
+
* musicEnabled: boolean;
|
|
113
|
+
* difficulty: 'easy' | 'normal' | 'hard';
|
|
114
|
+
* }
|
|
115
|
+
*
|
|
116
|
+
* const settingsState = withGlobalState<UserSettings>(scene, 'settings', {
|
|
117
|
+
* soundVolume: 0.8,
|
|
118
|
+
* musicEnabled: true,
|
|
119
|
+
* difficulty: 'normal'
|
|
120
|
+
* });
|
|
121
|
+
*
|
|
122
|
+
* // Game progress
|
|
123
|
+
* interface GameProgress {
|
|
124
|
+
* currentLevel: number;
|
|
125
|
+
* unlockedLevels: number[];
|
|
126
|
+
* totalScore: number;
|
|
127
|
+
* }
|
|
128
|
+
*
|
|
129
|
+
* const progressState = withGlobalState<GameProgress>(scene, 'progress', {
|
|
130
|
+
* currentLevel: 1,
|
|
131
|
+
* unlockedLevels: [1],
|
|
132
|
+
* totalScore: 0
|
|
133
|
+
* });
|
|
134
|
+
*
|
|
135
|
+
* // Listen to changes globally
|
|
136
|
+
* progressState.onChange((newProgress, oldProgress) => {
|
|
137
|
+
* console.log('Game progress updated:', newProgress);
|
|
138
|
+
* // Could trigger achievements, save to localStorage, etc.
|
|
139
|
+
* });
|
|
140
|
+
* ```
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* ```typescript
|
|
144
|
+
* // With validation
|
|
145
|
+
* const scoreState = withGlobalState<number>(scene, 'score', 0, {
|
|
146
|
+
* validator: (value) => {
|
|
147
|
+
* if (typeof value !== 'number' || value < 0) {
|
|
148
|
+
* return 'Score must be a positive number';
|
|
149
|
+
* }
|
|
150
|
+
* return true;
|
|
151
|
+
* },
|
|
152
|
+
* debug: true
|
|
153
|
+
* });
|
|
154
|
+
*
|
|
155
|
+
* // Authentication state example
|
|
156
|
+
* interface AuthState {
|
|
157
|
+
* isLoggedIn: boolean;
|
|
158
|
+
* username: string | null;
|
|
159
|
+
* token: string | null;
|
|
160
|
+
* }
|
|
161
|
+
*
|
|
162
|
+
* const authState = withGlobalState<AuthState>(scene, 'auth', {
|
|
163
|
+
* isLoggedIn: false,
|
|
164
|
+
* username: null,
|
|
165
|
+
* token: null
|
|
166
|
+
* });
|
|
167
|
+
*
|
|
168
|
+
* // Use from any scene - no dependency on Boot scene!
|
|
169
|
+
* if (authState.get().isLoggedIn) {
|
|
170
|
+
* // Show authenticated content
|
|
171
|
+
* }
|
|
172
|
+
* ```
|
|
173
|
+
*
|
|
174
|
+
* @see {@link withLocalState} For scene-specific state
|
|
175
|
+
* @see {@link withStateDef} For low-level state management
|
|
176
|
+
*/
|
|
177
|
+
/* eslint-disable max-lines-per-function */
|
|
178
|
+
/* eslint-disable complexity */
|
|
179
|
+
/* eslint-disable sonarjs/cognitive-complexity */
|
|
180
|
+
export const withGlobalState = (key, initialValue, options = {}) => {
|
|
181
|
+
if (!key || typeof key !== 'string' || key.trim().length === 0) {
|
|
182
|
+
throw new Error('[withGlobalState] Key must be a non-empty string');
|
|
183
|
+
}
|
|
184
|
+
const { validator, debug } = options;
|
|
185
|
+
// Prefix the key to indicate it's global state
|
|
186
|
+
const globalKey = `global:${key}`;
|
|
187
|
+
// Initialize value if it doesn't exist
|
|
188
|
+
if (!GlobalState.has(globalKey) && initialValue !== undefined) {
|
|
189
|
+
if (validator) {
|
|
190
|
+
const validationResult = validator(initialValue);
|
|
191
|
+
if (validationResult !== true) {
|
|
192
|
+
throw new Error(`[withGlobalState] ${validationResult || 'Invalid initial value'}`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
GlobalState.set(globalKey, initialValue);
|
|
196
|
+
if (debug) {
|
|
197
|
+
// eslint-disable-next-line no-console
|
|
198
|
+
console.debug(`[withGlobalState] Initialized "${key}" with value:`, initialValue);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
get: () => {
|
|
203
|
+
const value = GlobalState.get(globalKey);
|
|
204
|
+
if (debug) {
|
|
205
|
+
// eslint-disable-next-line no-console
|
|
206
|
+
console.debug(`[withGlobalState] Getting "${key}":`, value);
|
|
207
|
+
}
|
|
208
|
+
return value ?? initialValue;
|
|
209
|
+
},
|
|
210
|
+
set: (value) => {
|
|
211
|
+
if (validator) {
|
|
212
|
+
const validationResult = validator(value);
|
|
213
|
+
if (validationResult !== true) {
|
|
214
|
+
throw new Error(`[withGlobalState] Invalid value for key "${key}": ${validationResult}`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (debug) {
|
|
218
|
+
const oldValue = GlobalState.get(globalKey);
|
|
219
|
+
// eslint-disable-next-line no-console
|
|
220
|
+
console.debug(`[withGlobalState] Setting "${key}":`, {
|
|
221
|
+
oldValue,
|
|
222
|
+
newValue: value,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
GlobalState.set(globalKey, value);
|
|
226
|
+
},
|
|
227
|
+
onChange: (callback) => {
|
|
228
|
+
if (typeof callback !== 'function') {
|
|
229
|
+
throw new Error('[withGlobalState] onChange callback must be a function');
|
|
230
|
+
}
|
|
231
|
+
GlobalState.onChange(globalKey, callback);
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
};
|
|
235
|
+
/**
|
|
236
|
+
* Export the GlobalState class for advanced use cases
|
|
237
|
+
* (testing, debugging, manual state management)
|
|
238
|
+
*/
|
|
239
|
+
export { GlobalState };
|
|
240
|
+
//# sourceMappingURL=with-global-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"with-global-state.js","sourceRoot":"","sources":["../../src/hooks/with-global-state.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import * as Phaser from 'phaser';
|
|
2
|
+
import { type HookState } from './type';
|
|
3
|
+
import { type StateDefOptions } from './with-state-def';
|
|
4
|
+
/**
|
|
5
|
+
* Creates a local state hook scoped to a specific Phaser scene.
|
|
6
|
+
* Local state is isolated to the scene instance and doesn't persist across scene changes.
|
|
7
|
+
* This is ideal for UI state, temporary game state, or scene-specific data.
|
|
8
|
+
*
|
|
9
|
+
* @template T The type of the state value
|
|
10
|
+
* @param scene The Phaser scene instance that owns this state
|
|
11
|
+
* @param key Unique identifier for the state within this scene
|
|
12
|
+
* @param initialValue Optional initial value to set if state doesn't exist
|
|
13
|
+
* @param options Optional configuration for state behavior
|
|
14
|
+
* @returns HookState interface for managing the local state
|
|
15
|
+
*
|
|
16
|
+
* @throws {Error} When scene is not available or key is invalid
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* // Simple counter state
|
|
21
|
+
* const counterState = withLocalState<number>(scene, 'counter', 0);
|
|
22
|
+
*
|
|
23
|
+
* // Complex object state
|
|
24
|
+
* interface GameUI {
|
|
25
|
+
* isMenuOpen: boolean;
|
|
26
|
+
* selectedTab: string;
|
|
27
|
+
* }
|
|
28
|
+
*
|
|
29
|
+
* const uiState = withLocalState<GameUI>(scene, 'ui', {
|
|
30
|
+
* isMenuOpen: false,
|
|
31
|
+
* selectedTab: 'main'
|
|
32
|
+
* });
|
|
33
|
+
*
|
|
34
|
+
* // Listen to changes
|
|
35
|
+
* uiState.onChange((newUI, oldUI) => {
|
|
36
|
+
* if (newUI.isMenuOpen !== oldUI.isMenuOpen) {
|
|
37
|
+
* console.log('Menu visibility changed');
|
|
38
|
+
* }
|
|
39
|
+
* });
|
|
40
|
+
*
|
|
41
|
+
* // Update state
|
|
42
|
+
* uiState.set({ ...uiState.get(), isMenuOpen: true });
|
|
43
|
+
*
|
|
44
|
+
* // Array state example
|
|
45
|
+
* const inventoryState = withLocalState<string[]>(scene, 'inventory', []);
|
|
46
|
+
* inventoryState.set([...inventoryState.get(), 'new-item']);
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```typescript
|
|
51
|
+
* // With validation
|
|
52
|
+
* const playerHealthState = withLocalState<number>(
|
|
53
|
+
* scene,
|
|
54
|
+
* 'health',
|
|
55
|
+
* 100,
|
|
56
|
+
* {
|
|
57
|
+
* validator: (value) => {
|
|
58
|
+
* const health = value as number;
|
|
59
|
+
* return health >= 0 && health <= 100 ? true : 'Health must be 0-100';
|
|
60
|
+
* }
|
|
61
|
+
* }
|
|
62
|
+
* );
|
|
63
|
+
* ```
|
|
64
|
+
*
|
|
65
|
+
* @see {@link withGlobalState} For state that persists across scenes
|
|
66
|
+
* @see {@link withStateDef} For low-level state management
|
|
67
|
+
*/
|
|
68
|
+
export declare const withLocalState: <T>(scene: Phaser.Scene, key: string, initialValue?: T, options?: StateDefOptions) => HookState<T>;
|
|
69
|
+
//# sourceMappingURL=with-local-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"with-local-state.d.ts","sourceRoot":"","sources":["../../src/hooks/with-local-state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAEjC,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAgB,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEtE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+DG;AACH,eAAO,MAAM,cAAc,GAAI,CAAC,EAC9B,OAAO,MAAM,CAAC,KAAK,EACnB,KAAK,MAAM,EACX,eAAe,CAAC,EAChB,UAAU,eAAe,KACxB,SAAS,CAAC,CAAC,CAab,CAAC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { withStateDef } from './with-state-def';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a local state hook scoped to a specific Phaser scene.
|
|
4
|
+
* Local state is isolated to the scene instance and doesn't persist across scene changes.
|
|
5
|
+
* This is ideal for UI state, temporary game state, or scene-specific data.
|
|
6
|
+
*
|
|
7
|
+
* @template T The type of the state value
|
|
8
|
+
* @param scene The Phaser scene instance that owns this state
|
|
9
|
+
* @param key Unique identifier for the state within this scene
|
|
10
|
+
* @param initialValue Optional initial value to set if state doesn't exist
|
|
11
|
+
* @param options Optional configuration for state behavior
|
|
12
|
+
* @returns HookState interface for managing the local state
|
|
13
|
+
*
|
|
14
|
+
* @throws {Error} When scene is not available or key is invalid
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* // Simple counter state
|
|
19
|
+
* const counterState = withLocalState<number>(scene, 'counter', 0);
|
|
20
|
+
*
|
|
21
|
+
* // Complex object state
|
|
22
|
+
* interface GameUI {
|
|
23
|
+
* isMenuOpen: boolean;
|
|
24
|
+
* selectedTab: string;
|
|
25
|
+
* }
|
|
26
|
+
*
|
|
27
|
+
* const uiState = withLocalState<GameUI>(scene, 'ui', {
|
|
28
|
+
* isMenuOpen: false,
|
|
29
|
+
* selectedTab: 'main'
|
|
30
|
+
* });
|
|
31
|
+
*
|
|
32
|
+
* // Listen to changes
|
|
33
|
+
* uiState.onChange((newUI, oldUI) => {
|
|
34
|
+
* if (newUI.isMenuOpen !== oldUI.isMenuOpen) {
|
|
35
|
+
* console.log('Menu visibility changed');
|
|
36
|
+
* }
|
|
37
|
+
* });
|
|
38
|
+
*
|
|
39
|
+
* // Update state
|
|
40
|
+
* uiState.set({ ...uiState.get(), isMenuOpen: true });
|
|
41
|
+
*
|
|
42
|
+
* // Array state example
|
|
43
|
+
* const inventoryState = withLocalState<string[]>(scene, 'inventory', []);
|
|
44
|
+
* inventoryState.set([...inventoryState.get(), 'new-item']);
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```typescript
|
|
49
|
+
* // With validation
|
|
50
|
+
* const playerHealthState = withLocalState<number>(
|
|
51
|
+
* scene,
|
|
52
|
+
* 'health',
|
|
53
|
+
* 100,
|
|
54
|
+
* {
|
|
55
|
+
* validator: (value) => {
|
|
56
|
+
* const health = value as number;
|
|
57
|
+
* return health >= 0 && health <= 100 ? true : 'Health must be 0-100';
|
|
58
|
+
* }
|
|
59
|
+
* }
|
|
60
|
+
* );
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
63
|
+
* @see {@link withGlobalState} For state that persists across scenes
|
|
64
|
+
* @see {@link withStateDef} For low-level state management
|
|
65
|
+
*/
|
|
66
|
+
export const withLocalState = (scene, key, initialValue, options) => {
|
|
67
|
+
if (!scene) {
|
|
68
|
+
throw new Error('[withLocalState] Scene parameter is required');
|
|
69
|
+
}
|
|
70
|
+
// Prefix the key with scene key to ensure locality
|
|
71
|
+
const sceneKey = scene.scene?.key || 'unknown-scene';
|
|
72
|
+
const localKey = `local:${sceneKey}:${key}`;
|
|
73
|
+
return withStateDef(scene, localKey, initialValue, {
|
|
74
|
+
...options,
|
|
75
|
+
persistent: false, // Local state shouldn't persist across scene changes
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
//# sourceMappingURL=with-local-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"with-local-state.js","sourceRoot":"","sources":["../../src/hooks/with-local-state.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAwB,MAAM,kBAAkB,CAAC;AAEtE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+DG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAC5B,KAAmB,EACnB,GAAW,EACX,YAAgB,EAChB,OAAyB,EACX,EAAE;IAChB,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAClE,CAAC;IAED,mDAAmD;IACnD,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,EAAE,GAAG,IAAI,eAAe,CAAC;IACrD,MAAM,QAAQ,GAAG,SAAS,QAAQ,IAAI,GAAG,EAAE,CAAC;IAE5C,OAAO,YAAY,CAAC,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE;QACjD,GAAG,OAAO;QACV,UAAU,EAAE,KAAK,EAAE,qDAAqD;KACzE,CAAC,CAAC;AACL,CAAC,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type HookState } from './type';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a state hook with automatic localStorage persistence
|
|
4
|
+
* @template T The type of the state value
|
|
5
|
+
* @param key Unique identifier for the state
|
|
6
|
+
* @param initialValue Initial value to use if no stored value exists
|
|
7
|
+
* @param storageKey Optional custom localStorage key (defaults to the state key)
|
|
8
|
+
* @returns HookState with automatic persistence
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* const persistentSettings = withPersistentState<UserSettings>(
|
|
13
|
+
* 'settings',
|
|
14
|
+
* { volume: 0.8, difficulty: 'normal' }
|
|
15
|
+
* );
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export declare const withPersistentState: <T>(key: string, initialValue: T, storageKey?: string) => HookState<T>;
|
|
19
|
+
//# sourceMappingURL=with-persistent-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"with-persistent-state.d.ts","sourceRoot":"","sources":["../../src/hooks/with-persistent-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,QAAQ,CAAC;AAGxC;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,mBAAmB,GAAI,CAAC,EACnC,KAAK,MAAM,EACX,cAAc,CAAC,EACf,aAAa,MAAM,KAClB,SAAS,CAAC,CAAC,CAkCb,CAAC"}
|