@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.
@@ -1,16 +1,17 @@
1
1
  # PageMachine
2
2
 
3
- State machine for managing page view states with URL route mapping.
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
- │ - Maps states to URL routes
11
- │ - Tracks current state and visited states
12
- │ - Manages start path and navigation
13
- │ - Provides computed properties (inIntro, inLevel1, etc.)│
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 state:
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.inIntro, etc.
37
+ Access via: puzzleState.current, puzzleState.isOnIntro, etc.
37
38
  ```
38
39
 
39
40
  ## Main Purposes
40
41
 
41
- 1. **Track current view/step** - Which page is active
42
- 2. **Map states to URL paths** - Connect state names to routes
43
- 3. **Manage start path** - Define and navigate to the entry point
44
- 4. **Sync with browser navigation** - Keep state in sync with URL
45
- 5. **Track visited states** - Know which pages user has seen
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 state constants
50
+ ### 1. Define route constants
50
51
 
51
52
  ```javascript
52
- // puzzle.constants.js
53
- export const STATE_INTRO = 'intro';
54
- export const STATE_TUTORIAL = 'tutorial';
55
- export const STATE_LEVEL1 = 'level1';
56
- export const STATE_LEVEL2 = 'level2';
57
- export const STATE_COMPLETE = 'complete';
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
- STATE_INTRO,
69
- STATE_TUTORIAL,
70
- STATE_LEVEL1,
71
- STATE_LEVEL2,
72
- STATE_COMPLETE
73
- } from './puzzle.constants.js';
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: '/puzzle/intro',
87
- routeMap: {
88
- [STATE_INTRO]: '/puzzle/intro',
89
- [STATE_TUTORIAL]: '/puzzle/tutorial',
90
- [STATE_LEVEL1]: '/puzzle/level1',
91
- [STATE_LEVEL2]: '/puzzle/level2',
92
- [STATE_COMPLETE]: '/puzzle/complete'
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 inIntro() {
105
- return this.current === STATE_INTRO;
105
+ get isOnIntro() {
106
+ return this.current === ROUTE_INTRO;
106
107
  }
107
108
 
108
- get inTutorial() {
109
- return this.current === STATE_TUTORIAL;
109
+ get isOnTutorial() {
110
+ return this.current === ROUTE_TUTORIAL;
110
111
  }
111
112
 
112
- get inLevel1() {
113
- return this.current === STATE_LEVEL1;
113
+ get isOnLevel1() {
114
+ return this.current === ROUTE_LEVEL1;
114
115
  }
115
116
 
116
- get inLevel2() {
117
- return this.current === STATE_LEVEL2;
117
+ get isOnLevel2() {
118
+ return this.current === ROUTE_LEVEL2;
118
119
  }
119
120
 
120
121
  get isComplete() {
121
- return this.current === STATE_COMPLETE;
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 url path to be connected to the page machine**
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('intro')) {
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.inIntro}
202
+ {#if puzzleState.isOnIntro}
201
203
  <IntroView onComplete={() => puzzleState.markTutorialComplete()} />
202
- {:else if puzzleState.inTutorial}
204
+ {:else if puzzleState.isOnTutorial}
203
205
  <TutorialView />
204
- {:else if puzzleState.inLevel1}
206
+ {:else if puzzleState.isOnLevel1}
205
207
  <Level1View gameLogic={puzzleState.gameLogic} />
206
- {:else if puzzleState.inLevel2}
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 state
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.isOnStartState // Check if on start state
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
- // Visited states tracking
241
- puzzleState.hasVisited(stateName)
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.inIntro
246
- puzzleState.inLevel1
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 **reactive game state**:
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
- - Not a finite state machine - allows free navigation
299
- - States map 1:1 with routes
300
- - Use state constants instead of magic strings
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 = 'tutorial-seen'`)
303
- - Separate persistent data (PageMachine) from reactive game state (GameLogic)
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`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hkdigital/lib-core",
3
- "version": "0.5.56",
3
+ "version": "0.5.58",
4
4
  "author": {
5
5
  "name": "HKdigital",
6
6
  "url": "https://hkdigital.nl"