@hkdigital/lib-core 0.5.57 → 0.5.59

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,13 +84,18 @@ 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'
87
+ startPath: ROUTE_INTRO,
88
+ routes: [
89
+ ROUTE_INTRO,
90
+ ROUTE_TUTORIAL,
91
+ ROUTE_LEVEL1,
92
+ ROUTE_LEVEL2,
93
+ ROUTE_COMPLETE
94
+ ],
95
+ initialData: {
96
+ [KEY_TUTORIAL_SEEN]: false,
97
+ [KEY_HIGHEST_LEVEL]: 1,
98
+ [KEY_DIFFICULTY]: 'normal'
93
99
  }
94
100
  });
95
101
 
@@ -101,24 +107,24 @@ export class PuzzleState extends PageMachine {
101
107
  }
102
108
 
103
109
  // Computed properties for convenience
104
- get inIntro() {
105
- return this.current === STATE_INTRO;
110
+ get isOnIntro() {
111
+ return this.current === ROUTE_INTRO;
106
112
  }
107
113
 
108
- get inTutorial() {
109
- return this.current === STATE_TUTORIAL;
114
+ get isOnTutorial() {
115
+ return this.current === ROUTE_TUTORIAL;
110
116
  }
111
117
 
112
- get inLevel1() {
113
- return this.current === STATE_LEVEL1;
118
+ get isOnLevel1() {
119
+ return this.current === ROUTE_LEVEL1;
114
120
  }
115
121
 
116
- get inLevel2() {
117
- return this.current === STATE_LEVEL2;
122
+ get isOnLevel2() {
123
+ return this.current === ROUTE_LEVEL2;
118
124
  }
119
125
 
120
126
  get isComplete() {
121
- return this.current === STATE_COMPLETE;
127
+ return this.current === ROUTE_COMPLETE;
122
128
  }
123
129
 
124
130
  // Persistent settings/progress (use getData/setData)
@@ -166,12 +172,13 @@ export const [createOrGetPuzzleState, createPuzzleState, getPuzzleState] =
166
172
 
167
173
  ### 3. Sync with route in +layout.svelte component
168
174
 
169
- **This is IMPORTANT for url path to be connected to the page machine**
175
+ **This is IMPORTANT for URL path to be connected to the page machine**
170
176
 
171
177
  ```svelte
172
178
  <script>
173
179
  import { page } from '$app/stores';
174
180
  import { createOrGetPuzzleState } from '../puzzle.state.svelte.js';
181
+ import { ROUTE_INTRO } from '../puzzle.routes.js';
175
182
 
176
183
  const puzzleState = createOrGetPuzzleState();
177
184
 
@@ -186,7 +193,7 @@ export const [createOrGetPuzzleState, createPuzzleState, getPuzzleState] =
186
193
 
187
194
  if (!puzzleState.isStartPath(currentPath) &&
188
195
  currentPath.startsWith('/puzzle') &&
189
- !puzzleState.hasVisited('intro')) {
196
+ !puzzleState.hasVisited(ROUTE_INTRO)) {
190
197
  puzzleState.redirectToStartPath();
191
198
  }
192
199
  });
@@ -197,13 +204,13 @@ export const [createOrGetPuzzleState, createPuzzleState, getPuzzleState] =
197
204
  });
198
205
  </script>
199
206
 
200
- {#if puzzleState.inIntro}
207
+ {#if puzzleState.isOnIntro}
201
208
  <IntroView onComplete={() => puzzleState.markTutorialComplete()} />
202
- {:else if puzzleState.inTutorial}
209
+ {:else if puzzleState.isOnTutorial}
203
210
  <TutorialView />
204
- {:else if puzzleState.inLevel1}
211
+ {:else if puzzleState.isOnLevel1}
205
212
  <Level1View gameLogic={puzzleState.gameLogic} />
206
- {:else if puzzleState.inLevel2}
213
+ {:else if puzzleState.isOnLevel2}
207
214
  <Level2View gameLogic={puzzleState.gameLogic} />
208
215
  {:else if puzzleState.isComplete}
209
216
  <CompleteView
@@ -212,6 +219,35 @@ export const [createOrGetPuzzleState, createPuzzleState, getPuzzleState] =
212
219
  {/if}
213
220
  ```
214
221
 
222
+ ## Animations and Page-Specific Logic
223
+
224
+ For page-specific animations or initialization, use `$effect` in your
225
+ `+page.svelte` files rather than centralized hooks:
226
+
227
+ ```javascript
228
+ // +page.svelte
229
+ <script>
230
+ import { PageAnimations } from './animations.svelte.js';
231
+
232
+ const animations = new PageAnimations();
233
+
234
+ // Trigger animations when conditions are met
235
+ $effect(() => {
236
+ if (someCondition) {
237
+ animations.start();
238
+ }
239
+ });
240
+
241
+ // Or use onMount for one-time initialization
242
+ onMount(() => {
243
+ animations.initialize();
244
+ });
245
+ </script>
246
+ ```
247
+
248
+ This approach keeps animation logic co-located with the components that
249
+ render them, making it easier to understand and maintain.
250
+
215
251
  ## Key Methods
216
252
 
217
253
  All PageMachine methods are directly available on your state class:
@@ -220,30 +256,33 @@ All PageMachine methods are directly available on your state class:
220
256
  // Sync with URL path
221
257
  puzzleState.syncFromPath(currentPath)
222
258
 
223
- // Get current state
224
- puzzleState.current
259
+ // Get current route
260
+ puzzleState.current // e.g., '/puzzle/level1'
225
261
 
226
262
  // Start path management
227
263
  puzzleState.startPath // Get start path
228
- puzzleState.startState // Get start state
229
264
  puzzleState.isStartPath(path) // Check if path is start path
230
- puzzleState.isOnStartState // Check if on start state
265
+ puzzleState.isOnStartPath // Check if on start path
231
266
  puzzleState.redirectToStartPath() // Navigate to start path
232
267
 
233
- // Get route for state
234
- puzzleState.getPathForState(stateName)
235
-
236
268
  // Persistent data properties
237
269
  puzzleState.setData(KEY_NAME, value)
238
270
  puzzleState.getData(KEY_NAME)
271
+ puzzleState.getAllData()
272
+ puzzleState.updateData({ KEY1: val1, KEY2: val2 })
273
+
274
+ // Visited routes tracking
275
+ puzzleState.hasVisited(route) // e.g., hasVisited('/puzzle/intro')
276
+ puzzleState.hasVisitedStart // Has visited start path
277
+ puzzleState.getVisitedRoutes()
278
+ puzzleState.resetVisitedRoutes()
239
279
 
240
- // Visited states tracking
241
- puzzleState.hasVisited(stateName)
242
- puzzleState.getVisitedStates()
280
+ // Get routes list
281
+ puzzleState.routes // Array of all routes
243
282
 
244
283
  // Custom computed properties (from your class)
245
- puzzleState.inIntro
246
- puzzleState.inLevel1
284
+ puzzleState.isOnIntro
285
+ puzzleState.isOnLevel1
247
286
  puzzleState.hasSeenTutorial
248
287
  ```
249
288
 
@@ -259,20 +298,32 @@ Use PageMachine's data properties for **persistent settings and progress**:
259
298
  - ✅ Settings that survive page navigation
260
299
  - ✅ Data that might be saved to server
261
300
 
301
+ **IMPORTANT**: Use KEY_ constants for data keys to get:
302
+ - ✅ Autocomplete support
303
+ - ✅ Typo prevention
304
+ - ✅ Easy refactoring
305
+ - ✅ Self-documenting code
306
+
262
307
  ```javascript
308
+ // Define constants (at top of file)
263
309
  const KEY_TUTORIAL_SEEN = 'tutorial-seen';
264
310
  const KEY_DIFFICULTY = 'difficulty';
265
311
  const KEY_HIGHEST_LEVEL = 'highest-level';
266
312
 
267
- // Persistent data
268
- pageMachine.setData(KEY_TUTORIAL_SEEN, true);
269
- pageMachine.setData(KEY_DIFFICULTY, 'hard');
270
- pageMachine.setData(KEY_HIGHEST_LEVEL, 5);
313
+ // Use constants (not strings!)
314
+ pageMachine.setData(KEY_TUTORIAL_SEEN, true); // ✅ Good
315
+ pageMachine.setData(KEY_DIFFICULTY, 'hard'); // ✅ Good
316
+ pageMachine.setData(KEY_HIGHEST_LEVEL, 5); // ✅ Good
317
+
318
+ // DON'T use magic strings
319
+ pageMachine.setData('tutorial-seen', true); // ❌ Avoid
320
+ pageMachine.setData('TUTORIAL_SEEN', true); // ❌ Avoid
271
321
  ```
272
322
 
273
323
  ### When to use GameLogic with `$state`
274
324
 
275
- Use a separate GameLogic class with `$state` fields for **reactive game state**:
325
+ Use a separate GameLogic class with `$state` fields for
326
+ **reactive game state**:
276
327
 
277
328
  - ✅ Live scores, lives, health
278
329
  - ✅ Current player, selected items
@@ -295,9 +346,10 @@ export class PuzzleGameLogic {
295
346
 
296
347
  ## Important Notes
297
348
 
298
- - Not a finite state machine - allows free navigation
299
- - States map 1:1 with routes
300
- - Use state constants instead of magic strings
349
+ - Does not enforce transitions - allows free navigation via browser
350
+ - Routes are the source of truth (no state abstraction layer)
351
+ - Use route constants for clarity and maintainability
301
352
  - 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)
353
+ - Use constants for data keys (e.g., `KEY_TUTORIAL_SEEN`)
354
+ - Separate persistent data (PageMachine) from reactive state (GameLogic)
355
+ - 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.57",
3
+ "version": "0.5.59",
4
4
  "author": {
5
5
  "name": "HKdigital",
6
6
  "url": "https://hkdigital.nl"