@hkdigital/lib-core 0.5.56 → 0.5.58
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/dist/logging/internal/logger/Logger.js +1 -1
- package/dist/state/machines/page-machine/PageMachine.svelte.d.ts +76 -122
- package/dist/state/machines/page-machine/PageMachine.svelte.js +97 -394
- package/dist/state/machines/page-machine/PageMachine.svelte.js__ +708 -0
- package/dist/state/machines/page-machine/README.md +102 -66
- package/package.json +1 -1
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
# PageMachine
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Route-aware data manager for tracking navigation and managing business data
|
|
4
|
+
within a route group.
|
|
4
5
|
|
|
5
6
|
## How it connects
|
|
6
7
|
|
|
7
8
|
```
|
|
8
9
|
┌─────────────────────────────────────────────────────────┐
|
|
9
10
|
│ PuzzleState (extends PageMachine) │
|
|
10
|
-
│ -
|
|
11
|
-
│ -
|
|
12
|
-
│ -
|
|
13
|
-
│ - Provides computed properties (
|
|
11
|
+
│ - Tracks current route within a route group │
|
|
12
|
+
│ - Manages business/domain data │
|
|
13
|
+
│ - Tracks visited routes │
|
|
14
|
+
│ - Provides computed properties (isOnIntro, etc.) │
|
|
14
15
|
│ - Contains GameLogic for reactive game state │
|
|
15
16
|
│ - Optional: services, preload, reset, etc. │
|
|
16
17
|
└────────────────┬────────────────────────────────────────┘
|
|
@@ -19,7 +20,7 @@ State machine for managing page view states with URL route mapping.
|
|
|
19
20
|
│
|
|
20
21
|
┌────────────────▼────────────────────────────────────────┐
|
|
21
22
|
│ +layout.svelte │
|
|
22
|
-
│ IMPORTANT: Must sync URL with
|
|
23
|
+
│ IMPORTANT: Must sync URL with machine: │
|
|
23
24
|
│ $effect(() => { │
|
|
24
25
|
│ puzzleState.syncFromPath($page.url.pathname); │
|
|
25
26
|
│ }); │
|
|
@@ -33,28 +34,28 @@ State machine for managing page view states with URL route mapping.
|
|
|
33
34
|
│ +page │ │ +page │ │ Component│
|
|
34
35
|
│ │ │ │ │ │
|
|
35
36
|
└──────────┘ └──────────┘ └──────────┘
|
|
36
|
-
Access via: puzzleState.current, puzzleState.
|
|
37
|
+
Access via: puzzleState.current, puzzleState.isOnIntro, etc.
|
|
37
38
|
```
|
|
38
39
|
|
|
39
40
|
## Main Purposes
|
|
40
41
|
|
|
41
|
-
1. **Track current
|
|
42
|
-
2. **
|
|
43
|
-
3. **Manage
|
|
44
|
-
4. **
|
|
45
|
-
5. **
|
|
42
|
+
1. **Track current route** - Which page is active
|
|
43
|
+
2. **Sync with browser navigation** - Keep state in sync with URL
|
|
44
|
+
3. **Manage business data** - Persistent settings and progress
|
|
45
|
+
4. **Track visited routes** - Know which pages user has seen
|
|
46
|
+
5. **Manage start path** - Define and navigate to the entry point
|
|
46
47
|
|
|
47
48
|
## Basic Usage
|
|
48
49
|
|
|
49
|
-
### 1. Define
|
|
50
|
+
### 1. Define route constants
|
|
50
51
|
|
|
51
52
|
```javascript
|
|
52
|
-
// puzzle.
|
|
53
|
-
export const
|
|
54
|
-
export const
|
|
55
|
-
export const
|
|
56
|
-
export const
|
|
57
|
-
export const
|
|
53
|
+
// puzzle.routes.js
|
|
54
|
+
export const ROUTE_INTRO = '/puzzle/intro';
|
|
55
|
+
export const ROUTE_TUTORIAL = '/puzzle/tutorial';
|
|
56
|
+
export const ROUTE_LEVEL1 = '/puzzle/level1';
|
|
57
|
+
export const ROUTE_LEVEL2 = '/puzzle/level2';
|
|
58
|
+
export const ROUTE_COMPLETE = '/puzzle/complete';
|
|
58
59
|
```
|
|
59
60
|
|
|
60
61
|
### 2. Create state class (extends PageMachine)
|
|
@@ -65,12 +66,12 @@ import { defineStateContext } from '@hkdigital/lib-core/state/context.js';
|
|
|
65
66
|
import PageMachine from '$lib/state/machines/PageMachine.svelte.js';
|
|
66
67
|
import PuzzleGameLogic from './puzzle.game-logic.svelte.js';
|
|
67
68
|
import {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
} from './puzzle.
|
|
69
|
+
ROUTE_INTRO,
|
|
70
|
+
ROUTE_TUTORIAL,
|
|
71
|
+
ROUTE_LEVEL1,
|
|
72
|
+
ROUTE_LEVEL2,
|
|
73
|
+
ROUTE_COMPLETE
|
|
74
|
+
} from './puzzle.routes.js';
|
|
74
75
|
|
|
75
76
|
// Data keys for persistent data
|
|
76
77
|
const KEY_TUTORIAL_SEEN = 'tutorial-seen';
|
|
@@ -83,14 +84,14 @@ export class PuzzleState extends PageMachine {
|
|
|
83
84
|
constructor() {
|
|
84
85
|
// Call PageMachine constructor with route config
|
|
85
86
|
super({
|
|
86
|
-
startPath:
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
87
|
+
startPath: ROUTE_INTRO,
|
|
88
|
+
routes: [
|
|
89
|
+
ROUTE_INTRO,
|
|
90
|
+
ROUTE_TUTORIAL,
|
|
91
|
+
ROUTE_LEVEL1,
|
|
92
|
+
ROUTE_LEVEL2,
|
|
93
|
+
ROUTE_COMPLETE
|
|
94
|
+
]
|
|
94
95
|
});
|
|
95
96
|
|
|
96
97
|
this.#gameLogic = new PuzzleGameLogic();
|
|
@@ -101,24 +102,24 @@ export class PuzzleState extends PageMachine {
|
|
|
101
102
|
}
|
|
102
103
|
|
|
103
104
|
// Computed properties for convenience
|
|
104
|
-
get
|
|
105
|
-
return this.current ===
|
|
105
|
+
get isOnIntro() {
|
|
106
|
+
return this.current === ROUTE_INTRO;
|
|
106
107
|
}
|
|
107
108
|
|
|
108
|
-
get
|
|
109
|
-
return this.current ===
|
|
109
|
+
get isOnTutorial() {
|
|
110
|
+
return this.current === ROUTE_TUTORIAL;
|
|
110
111
|
}
|
|
111
112
|
|
|
112
|
-
get
|
|
113
|
-
return this.current ===
|
|
113
|
+
get isOnLevel1() {
|
|
114
|
+
return this.current === ROUTE_LEVEL1;
|
|
114
115
|
}
|
|
115
116
|
|
|
116
|
-
get
|
|
117
|
-
return this.current ===
|
|
117
|
+
get isOnLevel2() {
|
|
118
|
+
return this.current === ROUTE_LEVEL2;
|
|
118
119
|
}
|
|
119
120
|
|
|
120
121
|
get isComplete() {
|
|
121
|
-
return this.current ===
|
|
122
|
+
return this.current === ROUTE_COMPLETE;
|
|
122
123
|
}
|
|
123
124
|
|
|
124
125
|
// Persistent settings/progress (use getData/setData)
|
|
@@ -166,12 +167,13 @@ export const [createOrGetPuzzleState, createPuzzleState, getPuzzleState] =
|
|
|
166
167
|
|
|
167
168
|
### 3. Sync with route in +layout.svelte component
|
|
168
169
|
|
|
169
|
-
**This is IMPORTANT for
|
|
170
|
+
**This is IMPORTANT for URL path to be connected to the page machine**
|
|
170
171
|
|
|
171
172
|
```svelte
|
|
172
173
|
<script>
|
|
173
174
|
import { page } from '$app/stores';
|
|
174
175
|
import { createOrGetPuzzleState } from '../puzzle.state.svelte.js';
|
|
176
|
+
import { ROUTE_INTRO } from '../puzzle.routes.js';
|
|
175
177
|
|
|
176
178
|
const puzzleState = createOrGetPuzzleState();
|
|
177
179
|
|
|
@@ -186,7 +188,7 @@ export const [createOrGetPuzzleState, createPuzzleState, getPuzzleState] =
|
|
|
186
188
|
|
|
187
189
|
if (!puzzleState.isStartPath(currentPath) &&
|
|
188
190
|
currentPath.startsWith('/puzzle') &&
|
|
189
|
-
!puzzleState.hasVisited(
|
|
191
|
+
!puzzleState.hasVisited(ROUTE_INTRO)) {
|
|
190
192
|
puzzleState.redirectToStartPath();
|
|
191
193
|
}
|
|
192
194
|
});
|
|
@@ -197,13 +199,13 @@ export const [createOrGetPuzzleState, createPuzzleState, getPuzzleState] =
|
|
|
197
199
|
});
|
|
198
200
|
</script>
|
|
199
201
|
|
|
200
|
-
{#if puzzleState.
|
|
202
|
+
{#if puzzleState.isOnIntro}
|
|
201
203
|
<IntroView onComplete={() => puzzleState.markTutorialComplete()} />
|
|
202
|
-
{:else if puzzleState.
|
|
204
|
+
{:else if puzzleState.isOnTutorial}
|
|
203
205
|
<TutorialView />
|
|
204
|
-
{:else if puzzleState.
|
|
206
|
+
{:else if puzzleState.isOnLevel1}
|
|
205
207
|
<Level1View gameLogic={puzzleState.gameLogic} />
|
|
206
|
-
{:else if puzzleState.
|
|
208
|
+
{:else if puzzleState.isOnLevel2}
|
|
207
209
|
<Level2View gameLogic={puzzleState.gameLogic} />
|
|
208
210
|
{:else if puzzleState.isComplete}
|
|
209
211
|
<CompleteView
|
|
@@ -212,6 +214,35 @@ export const [createOrGetPuzzleState, createPuzzleState, getPuzzleState] =
|
|
|
212
214
|
{/if}
|
|
213
215
|
```
|
|
214
216
|
|
|
217
|
+
## Animations and Page-Specific Logic
|
|
218
|
+
|
|
219
|
+
For page-specific animations or initialization, use `$effect` in your
|
|
220
|
+
`+page.svelte` files rather than centralized hooks:
|
|
221
|
+
|
|
222
|
+
```javascript
|
|
223
|
+
// +page.svelte
|
|
224
|
+
<script>
|
|
225
|
+
import { PageAnimations } from './animations.svelte.js';
|
|
226
|
+
|
|
227
|
+
const animations = new PageAnimations();
|
|
228
|
+
|
|
229
|
+
// Trigger animations when conditions are met
|
|
230
|
+
$effect(() => {
|
|
231
|
+
if (someCondition) {
|
|
232
|
+
animations.start();
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
// Or use onMount for one-time initialization
|
|
237
|
+
onMount(() => {
|
|
238
|
+
animations.initialize();
|
|
239
|
+
});
|
|
240
|
+
</script>
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
This approach keeps animation logic co-located with the components that
|
|
244
|
+
render them, making it easier to understand and maintain.
|
|
245
|
+
|
|
215
246
|
## Key Methods
|
|
216
247
|
|
|
217
248
|
All PageMachine methods are directly available on your state class:
|
|
@@ -220,30 +251,33 @@ All PageMachine methods are directly available on your state class:
|
|
|
220
251
|
// Sync with URL path
|
|
221
252
|
puzzleState.syncFromPath(currentPath)
|
|
222
253
|
|
|
223
|
-
// Get current
|
|
224
|
-
puzzleState.current
|
|
254
|
+
// Get current route
|
|
255
|
+
puzzleState.current // e.g., '/puzzle/level1'
|
|
225
256
|
|
|
226
257
|
// Start path management
|
|
227
258
|
puzzleState.startPath // Get start path
|
|
228
|
-
puzzleState.startState // Get start state
|
|
229
259
|
puzzleState.isStartPath(path) // Check if path is start path
|
|
230
|
-
puzzleState.
|
|
260
|
+
puzzleState.isOnStartPath // Check if on start path
|
|
231
261
|
puzzleState.redirectToStartPath() // Navigate to start path
|
|
232
262
|
|
|
233
|
-
// Get route for state
|
|
234
|
-
puzzleState.getPathForState(stateName)
|
|
235
|
-
|
|
236
263
|
// Persistent data properties
|
|
237
264
|
puzzleState.setData(KEY_NAME, value)
|
|
238
265
|
puzzleState.getData(KEY_NAME)
|
|
266
|
+
puzzleState.getAllData()
|
|
267
|
+
puzzleState.updateData({ KEY1: val1, KEY2: val2 })
|
|
268
|
+
|
|
269
|
+
// Visited routes tracking
|
|
270
|
+
puzzleState.hasVisited(route) // e.g., hasVisited('/puzzle/intro')
|
|
271
|
+
puzzleState.hasVisitedStart // Has visited start path
|
|
272
|
+
puzzleState.getVisitedRoutes()
|
|
273
|
+
puzzleState.resetVisitedRoutes()
|
|
239
274
|
|
|
240
|
-
//
|
|
241
|
-
puzzleState.
|
|
242
|
-
puzzleState.getVisitedStates()
|
|
275
|
+
// Get routes list
|
|
276
|
+
puzzleState.routes // Array of all routes
|
|
243
277
|
|
|
244
278
|
// Custom computed properties (from your class)
|
|
245
|
-
puzzleState.
|
|
246
|
-
puzzleState.
|
|
279
|
+
puzzleState.isOnIntro
|
|
280
|
+
puzzleState.isOnLevel1
|
|
247
281
|
puzzleState.hasSeenTutorial
|
|
248
282
|
```
|
|
249
283
|
|
|
@@ -272,7 +306,8 @@ pageMachine.setData(KEY_HIGHEST_LEVEL, 5);
|
|
|
272
306
|
|
|
273
307
|
### When to use GameLogic with `$state`
|
|
274
308
|
|
|
275
|
-
Use a separate GameLogic class with `$state` fields for
|
|
309
|
+
Use a separate GameLogic class with `$state` fields for
|
|
310
|
+
**reactive game state**:
|
|
276
311
|
|
|
277
312
|
- ✅ Live scores, lives, health
|
|
278
313
|
- ✅ Current player, selected items
|
|
@@ -295,9 +330,10 @@ export class PuzzleGameLogic {
|
|
|
295
330
|
|
|
296
331
|
## Important Notes
|
|
297
332
|
|
|
298
|
-
-
|
|
299
|
-
-
|
|
300
|
-
- Use
|
|
333
|
+
- Does not enforce transitions - allows free navigation via browser
|
|
334
|
+
- Routes are the source of truth (no state abstraction layer)
|
|
335
|
+
- Use route constants for clarity and maintainability
|
|
301
336
|
- Always sync in `$effect` watching `$page.url.pathname`
|
|
302
|
-
- Use constants for data keys (e.g., `KEY_TUTORIAL_SEEN
|
|
303
|
-
- Separate persistent data (PageMachine) from reactive
|
|
337
|
+
- Use constants for data keys (e.g., `KEY_TUTORIAL_SEEN`)
|
|
338
|
+
- Separate persistent data (PageMachine) from reactive state (GameLogic)
|
|
339
|
+
- Handle animations in pages using `$effect` or `onMount`
|