@hkdigital/lib-core 0.3.11 → 0.3.13

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.
Files changed (293) hide show
  1. package/README.md +173 -149
  2. package/dist/assets/autospuiten/car-paint-picker.js +41 -41
  3. package/dist/assets/autospuiten/labels.js +7 -7
  4. package/dist/classes/data/IterableTree.js +242 -242
  5. package/dist/classes/data/Selector.js +190 -190
  6. package/dist/classes/data/index.js +2 -2
  7. package/dist/classes/data/typedef.js +9 -9
  8. package/dist/classes/event-emitter/EventEmitter.js +273 -273
  9. package/dist/classes/event-emitter/index.js +2 -2
  10. package/dist/classes/index.js +4 -4
  11. package/dist/classes/promise/HkPromise.js +384 -384
  12. package/dist/classes/promise/index.js +1 -1
  13. package/dist/classes/stores/SubscribersCount.js +107 -107
  14. package/dist/classes/stores/index.js +1 -1
  15. package/dist/classes/streams/LogTransformStream.js +19 -19
  16. package/dist/classes/streams/ServerEventsStore.js +111 -111
  17. package/dist/classes/streams/TimeStampSource.js +26 -26
  18. package/dist/classes/streams/index.js +3 -3
  19. package/dist/classes/svelte/finite-state-machine/FiniteStateMachine.svelte.js +133 -133
  20. package/dist/classes/svelte/finite-state-machine/index.js +1 -1
  21. package/dist/classes/svelte/index.js +1 -11
  22. package/dist/classes/svelte/loading-state-machine/LoadingStateMachine.svelte.js +109 -109
  23. package/dist/classes/svelte/loading-state-machine/constants.js +16 -16
  24. package/dist/classes/svelte/loading-state-machine/index.js +3 -3
  25. package/dist/config/README.md +197 -196
  26. package/dist/config/generators/imagetools.js +189 -189
  27. package/dist/config/generators/vite.js +148 -142
  28. package/dist/config/imagetools.d.ts +72 -72
  29. package/dist/config/vite.js +4 -4
  30. package/dist/constants/bases/index.js +13 -13
  31. package/dist/constants/http/headers.js +6 -6
  32. package/dist/constants/http/index.js +2 -2
  33. package/dist/constants/http/methods.js +14 -14
  34. package/dist/constants/index.js +6 -6
  35. package/dist/constants/mime/application.js +5 -5
  36. package/dist/constants/mime/audio.js +13 -13
  37. package/dist/constants/mime/image.js +3 -3
  38. package/dist/constants/mime/index.js +4 -4
  39. package/dist/constants/mime/text.js +2 -2
  40. package/dist/constants/regexp/README.md +96 -95
  41. package/dist/constants/regexp/index.js +31 -31
  42. package/dist/constants/regexp/inspiratie.js__ +95 -95
  43. package/dist/constants/regexp/text.d.ts +4 -4
  44. package/dist/constants/regexp/text.js +49 -49
  45. package/dist/constants/regexp/url.js +3 -3
  46. package/dist/constants/regexp/user.js +29 -29
  47. package/dist/constants/states/drag.js +6 -6
  48. package/dist/constants/states/drop.js +6 -6
  49. package/dist/constants/states/index.js +4 -4
  50. package/dist/constants/states/input.js +11 -11
  51. package/dist/constants/states/submit.js +4 -4
  52. package/dist/constants/time/index.js +28 -28
  53. package/dist/css/utilities.css +43 -43
  54. package/dist/design/README.md +405 -405
  55. package/dist/design/config/design-config.js +73 -73
  56. package/dist/design/generators/index.js +288 -288
  57. package/dist/design/index.js +96 -96
  58. package/dist/design/plugins/skeleton.js +208 -208
  59. package/dist/design/tailwind-theme-extend.js +158 -158
  60. package/dist/design/themes/README.md +102 -102
  61. package/dist/design/themes/hkdev/components/blocks/text-block.css +34 -34
  62. package/dist/design/themes/hkdev/components/boxes/game-box.css +11 -11
  63. package/dist/design/themes/hkdev/components/buttons/button-icon-steeze.css +22 -22
  64. package/dist/design/themes/hkdev/components/buttons/button-text.css +32 -32
  65. package/dist/design/themes/hkdev/components/buttons/button.css +146 -146
  66. package/dist/design/themes/hkdev/components/buttons/skip-button.css +5 -5
  67. package/dist/design/themes/hkdev/components/drag-drop/draggable.css +73 -73
  68. package/dist/design/themes/hkdev/components/drag-drop/drop-zone.css +58 -58
  69. package/dist/design/themes/hkdev/components/icons/icon-steeze.css +15 -15
  70. package/dist/design/themes/hkdev/components/inputs/text-input.css +102 -102
  71. package/dist/design/themes/hkdev/components/panels/panel.css +25 -25
  72. package/dist/design/themes/hkdev/components/rows/panel-grid-row.css +4 -4
  73. package/dist/design/themes/hkdev/components/rows/panel-row-2.css +5 -5
  74. package/dist/design/themes/hkdev/components.css +29 -29
  75. package/dist/design/themes/hkdev/debug.css +1 -1
  76. package/dist/design/themes/hkdev/global/layout.css +32 -32
  77. package/dist/design/themes/hkdev/global/on-colors.css +32 -32
  78. package/dist/design/themes/hkdev/globals.css +3 -3
  79. package/dist/design/themes/hkdev/responsive.css +12 -12
  80. package/dist/design/themes/hkdev/theme-ext.js +12 -12
  81. package/dist/design/themes/hkdev/theme.css +218 -218
  82. package/dist/design/utils/clamp.js +66 -66
  83. package/dist/design/utils/root-vars.js +102 -102
  84. package/dist/design/utils/scaling.js +228 -228
  85. package/dist/design/utils/states.js +22 -22
  86. package/dist/errors/api.js +9 -9
  87. package/dist/errors/generic.js +20 -20
  88. package/dist/errors/http.js +16 -16
  89. package/dist/errors/index.js +5 -5
  90. package/dist/errors/jwt.js +5 -5
  91. package/dist/errors/promise.js +25 -25
  92. package/dist/logging/README.md +158 -0
  93. package/dist/logging/index.d.ts +3 -1
  94. package/dist/logging/index.js +11 -7
  95. package/dist/logging/internal/adapters/console.js +114 -114
  96. package/dist/logging/internal/adapters/index.js +2 -2
  97. package/dist/logging/internal/adapters/pino.js +160 -142
  98. package/dist/logging/internal/adapters/typedef.js +10 -10
  99. package/dist/logging/internal/{unified-logger/constants.js → constants.js} +22 -22
  100. package/dist/logging/internal/factories/client.d.ts +1 -1
  101. package/dist/logging/internal/factories/client.js +21 -21
  102. package/dist/logging/internal/factories/server.d.ts +1 -1
  103. package/dist/logging/internal/factories/server.js +22 -22
  104. package/dist/logging/internal/factories/universal.d.ts +2 -2
  105. package/dist/logging/internal/factories/universal.js +22 -22
  106. package/dist/logging/internal/{unified-logger → logger}/Logger.d.ts +2 -2
  107. package/dist/logging/internal/{unified-logger → logger}/Logger.js +217 -217
  108. package/dist/logging/internal/logger/index.d.ts +1 -0
  109. package/dist/logging/internal/logger/index.js +1 -0
  110. package/dist/logging/internal/{unified-logger/typedef.d.ts → typedef.d.ts} +2 -1
  111. package/dist/logging/internal/{unified-logger/typedef.js → typedef.js} +21 -17
  112. package/dist/network/README.md +172 -172
  113. package/dist/network/cache/IndexedDbCache.js +1407 -1407
  114. package/dist/network/cache/MemoryResponseCache.js +138 -138
  115. package/dist/network/cache/index.js +5 -5
  116. package/dist/network/cache/typedef.js +41 -41
  117. package/dist/network/cache.js +3 -3
  118. package/dist/network/http/caching.js +261 -261
  119. package/dist/network/http/errors.js +97 -97
  120. package/dist/network/http/headers.js +75 -75
  121. package/dist/network/http/http-request.js +578 -578
  122. package/dist/network/http/index.js +22 -22
  123. package/dist/network/http/json-request.js +224 -224
  124. package/dist/network/http/mocks.js +65 -65
  125. package/dist/network/http/response.js +318 -318
  126. package/dist/network/http/test-data__/content-length-test-hkdigital-small.V4HfZyBQ.avif +0 -0
  127. package/dist/network/http/typedef.js +93 -93
  128. package/dist/network/http/url.js +52 -52
  129. package/dist/network/http.js +5 -5
  130. package/dist/network/loaders/README.md +254 -254
  131. package/dist/network/loaders/audio/AudioLoader.svelte.js +58 -58
  132. package/dist/network/loaders/audio/AudioScene.svelte.js +324 -324
  133. package/dist/network/loaders/audio/mocks.js +35 -35
  134. package/dist/network/loaders/audio.js +1 -1
  135. package/dist/network/loaders/image/ImageLoader.svelte.js +44 -44
  136. package/dist/network/loaders/image/ImageScene.svelte.js +248 -248
  137. package/dist/network/loaders/image/ImageVariantsLoader.svelte.js +150 -150
  138. package/dist/network/loaders/image/index.js +4 -4
  139. package/dist/network/loaders/image/mocks.js +35 -35
  140. package/dist/network/loaders/image/typedef.js +8 -8
  141. package/dist/network/loaders/image/utils/index.js +86 -86
  142. package/dist/network/loaders/image.js +7 -7
  143. package/dist/network/loaders/typedef.js +38 -38
  144. package/dist/network/loaders.js +2 -2
  145. package/dist/network/states/NetworkLoader.svelte.js +338 -338
  146. package/dist/network/states/constants.js +3 -3
  147. package/dist/network/states/index.js +3 -3
  148. package/dist/network/states/mocks.js +30 -30
  149. package/dist/network/states/typedef.js +8 -8
  150. package/dist/network/typedef.js +9 -9
  151. package/dist/services/README.md +200 -0
  152. package/dist/services/index.d.ts +6 -1
  153. package/dist/services/index.js +8 -1
  154. package/dist/services/{internal/service-base → service-base}/ServiceBase.d.ts +2 -2
  155. package/dist/services/{internal/service-base → service-base}/ServiceBase.js +462 -462
  156. package/dist/services/{internal/service-base → service-base}/constants.d.ts +0 -12
  157. package/dist/services/{internal/service-base → service-base}/constants.js +98 -110
  158. package/dist/services/{internal/service-base → service-base}/index.js +3 -3
  159. package/dist/services/{internal/service-base → service-base}/typedef.d.ts +1 -1
  160. package/dist/services/{internal/service-base → service-base}/typedef.js +101 -101
  161. package/dist/services/{internal/service-manager → service-manager}/ServiceManager.d.ts +2 -2
  162. package/dist/services/{internal/service-manager → service-manager}/ServiceManager.js +608 -608
  163. package/dist/services/{internal/service-manager → service-manager}/constants.js +6 -6
  164. package/dist/services/{internal/service-manager → service-manager}/typedef.js +90 -90
  165. package/dist/states/index.js +1 -1
  166. package/dist/states/navigation.svelte.js +55 -55
  167. package/dist/stores/index.js +1 -1
  168. package/dist/stores/theme.js +80 -80
  169. package/dist/typedef/context.js +6 -6
  170. package/dist/typedef/drag.js +25 -25
  171. package/dist/typedef/drop.js +12 -12
  172. package/dist/typedef/index.d.ts +1 -0
  173. package/dist/typedef/index.js +4 -4
  174. package/dist/ui/components/button-group/ButtonGroup.svelte +82 -82
  175. package/dist/ui/components/button-group/typedef.js +10 -10
  176. package/dist/ui/components/compare-left-right/CompareLeftRight.svelte +179 -179
  177. package/dist/ui/components/compare-left-right/index.js +1 -1
  178. package/dist/ui/components/game-box/GameBox.svelte +577 -577
  179. package/dist/ui/components/game-box/gamebox.util.js +83 -83
  180. package/dist/ui/components/hk-app-layout/HkAppLayout.state.svelte.js +25 -25
  181. package/dist/ui/components/hk-app-layout/HkAppLayout.svelte +251 -251
  182. package/dist/ui/components/image-box/ImageBox.svelte +210 -210
  183. package/dist/ui/components/image-box/index.js +5 -5
  184. package/dist/ui/components/image-box/typedef.js +32 -32
  185. package/dist/ui/components/index.js +23 -23
  186. package/dist/ui/components/presenter/ImageSlide.svelte +64 -64
  187. package/dist/ui/components/presenter/Presenter.state.svelte.js +638 -638
  188. package/dist/ui/components/presenter/Presenter.svelte +142 -142
  189. package/dist/ui/components/presenter/constants.js +7 -7
  190. package/dist/ui/components/presenter/index.js +10 -10
  191. package/dist/ui/components/presenter/typedef.js +106 -106
  192. package/dist/ui/components/presenter/util.js +210 -210
  193. package/dist/ui/components/virtual-viewport/VirtualViewport.svelte +196 -196
  194. package/dist/ui/primitives/area/HkArea.svelte +49 -49
  195. package/dist/ui/primitives/area/HkGridArea.svelte +77 -77
  196. package/dist/ui/primitives/area/index.js +2 -2
  197. package/dist/ui/primitives/buttons/button/Button.svelte +82 -82
  198. package/dist/ui/primitives/buttons/button-icon-steeze/SteezeIconButton.svelte +30 -30
  199. package/dist/ui/primitives/buttons/button-text/TextButton.svelte +21 -21
  200. package/dist/ui/primitives/buttons/index.js +3 -3
  201. package/dist/ui/primitives/debug/debug-panel-design-scaling/DebugPanelDesignScaling.svelte +146 -146
  202. package/dist/ui/primitives/debug/index.js +1 -1
  203. package/dist/ui/primitives/drag-drop/DragController.js +44 -44
  204. package/dist/ui/primitives/drag-drop/DragDropContext.svelte +111 -111
  205. package/dist/ui/primitives/drag-drop/Draggable.svelte +519 -519
  206. package/dist/ui/primitives/drag-drop/DropZone.svelte +258 -258
  207. package/dist/ui/primitives/drag-drop/DropZoneArea.svelte +119 -119
  208. package/dist/ui/primitives/drag-drop/DropZoneList.svelte +125 -125
  209. package/dist/ui/primitives/drag-drop/actions.js +26 -26
  210. package/dist/ui/primitives/drag-drop/drag-state.svelte.js +322 -322
  211. package/dist/ui/primitives/drag-drop/index.js +7 -7
  212. package/dist/ui/primitives/drag-drop/util.js +85 -85
  213. package/dist/ui/primitives/hkdev/blocks/TextBlock.svelte +46 -46
  214. package/dist/ui/primitives/hkdev/buttons/CheckButton.svelte +62 -62
  215. package/dist/ui/primitives/icons/HkIcon.svelte +86 -86
  216. package/dist/ui/primitives/icons/HkTabIcon.svelte +116 -116
  217. package/dist/ui/primitives/icons/SteezeIcon.svelte +97 -97
  218. package/dist/ui/primitives/icons/index.js +6 -6
  219. package/dist/ui/primitives/icons/typedef.js +16 -16
  220. package/dist/ui/primitives/index.js +2 -2
  221. package/dist/ui/primitives/inputs/index.js +1 -1
  222. package/dist/ui/primitives/inputs/text-input/TestTextInput.svelte__ +102 -0
  223. package/dist/ui/primitives/inputs/text-input/TextInput.svelte +223 -223
  224. package/dist/ui/primitives/inputs/text-input/TextInput.svelte___ +83 -0
  225. package/dist/ui/primitives/inputs/text-input/assets/IconInvalid.svelte +14 -14
  226. package/dist/ui/primitives/inputs/text-input/assets/IconValid.svelte +12 -12
  227. package/dist/ui/primitives/layout/grid-layers/GridLayers.svelte +63 -63
  228. package/dist/ui/primitives/layout/grid-layers/GridLayers.svelte__heightFrom__ +372 -0
  229. package/dist/ui/primitives/layout/grid-layers/util.js +74 -74
  230. package/dist/ui/primitives/layout/index.js +1 -1
  231. package/dist/ui/primitives/panels/index.js +1 -1
  232. package/dist/ui/primitives/panels/panel/Panel.svelte +43 -43
  233. package/dist/ui/primitives/rows/index.js +3 -3
  234. package/dist/ui/primitives/rows/panel-grid-row/PanelGridRow.svelte +104 -104
  235. package/dist/ui/primitives/rows/panel-row-2/PanelRow2.svelte +40 -40
  236. package/dist/ui/primitives/tab-bar/HkTabBar.state.svelte.js +149 -149
  237. package/dist/ui/primitives/tab-bar/HkTabBar.svelte +74 -74
  238. package/dist/ui/primitives/tab-bar/HkTabBarSelector.state.svelte.js +93 -93
  239. package/dist/ui/primitives/tab-bar/HkTabBarSelector.svelte +49 -49
  240. package/dist/ui/primitives/tab-bar/index.js +17 -17
  241. package/dist/ui/primitives/tab-bar/typedef.js +11 -11
  242. package/dist/util/array/index.js +436 -436
  243. package/dist/util/bases/base58.js +262 -262
  244. package/dist/util/bases/index.js +1 -1
  245. package/dist/util/compare/index.js +247 -247
  246. package/dist/util/css/css-vars.js +83 -83
  247. package/dist/util/css/index.js +1 -1
  248. package/dist/util/env/index.js +9 -9
  249. package/dist/util/exceptions/index.d.ts +4 -3
  250. package/dist/util/exceptions/index.js +26 -23
  251. package/dist/util/expect/arrays.js +47 -47
  252. package/dist/util/expect/index.js +259 -259
  253. package/dist/util/expect/primitives.js +55 -55
  254. package/dist/util/expect/url.js +60 -60
  255. package/dist/util/function/index.js +218 -218
  256. package/dist/util/geo/index.js +26 -26
  257. package/dist/util/index.js +7 -7
  258. package/dist/util/is/index.js +147 -147
  259. package/dist/util/iterate/index.js +204 -204
  260. package/dist/util/object/index.js +1345 -1345
  261. package/dist/util/singleton/index.js +97 -97
  262. package/dist/util/string/array-path.js +75 -75
  263. package/dist/util/string/convert.js +54 -54
  264. package/dist/util/string/fs.js +226 -226
  265. package/dist/util/string/index.js +5 -5
  266. package/dist/util/string/interpolate.js +61 -61
  267. package/dist/util/string/pad.js +10 -10
  268. package/dist/util/svelte/index.js +4 -4
  269. package/dist/util/svelte/loading/loading-tracker.svelte.js +108 -108
  270. package/dist/util/svelte/observe/index.js +49 -49
  271. package/dist/util/svelte/state-context/index.js +117 -117
  272. package/dist/util/svelte/wait/index.js +38 -38
  273. package/dist/util/sveltekit/index.js +1 -1
  274. package/dist/util/sveltekit/route-folders/index.js +101 -101
  275. package/dist/util/time/index.js +328 -328
  276. package/dist/util/unique/index.js +231 -231
  277. package/dist/valibot/README.md +61 -50
  278. package/dist/valibot/index.js +8 -8
  279. package/dist/valibot/parsers/date.js__ +10 -0
  280. package/dist/valibot/parsers/email.d.ts +12 -0
  281. package/dist/valibot/parsers/email.js +34 -0
  282. package/dist/valibot/parsers/url.js +110 -110
  283. package/dist/valibot/parsers/user.js +23 -23
  284. package/dist/valibot/parsers.js +3 -3
  285. package/package.json +131 -131
  286. package/dist/logging/internal/unified-logger/index.d.ts +0 -3
  287. package/dist/logging/internal/unified-logger/index.js +0 -6
  288. package/dist/services/internal/index.d.ts +0 -6
  289. package/dist/services/internal/index.js +0 -8
  290. /package/dist/logging/internal/{unified-logger/constants.d.ts → constants.d.ts} +0 -0
  291. /package/dist/services/{internal/service-base → service-base}/index.d.ts +0 -0
  292. /package/dist/services/{internal/service-manager → service-manager}/constants.d.ts +0 -0
  293. /package/dist/services/{internal/service-manager → service-manager}/typedef.d.ts +0 -0
@@ -1,638 +1,638 @@
1
- import { tick } from 'svelte';
2
-
3
- import { findFirst } from '../../../util/array/index.js';
4
-
5
- import { untrack } from 'svelte';
6
-
7
- import { HkPromise } from '../../../classes/promise/index.js';
8
-
9
- import { STAGE_BEFORE, STAGE_SHOW } from './constants.js';
10
-
11
- /**
12
- * @typedef {import("./typedef").Slide} Slide
13
- */
14
-
15
- /**
16
- * @typedef {import("./typedef").Transition} Transition
17
- */
18
-
19
- /**
20
- * @typedef {import("./typedef").Layer} Layer
21
- */
22
-
23
- /**
24
- * @typedef {import("./typedef").PresenterRef} PresenterRef
25
- */
26
-
27
- /**
28
- * @typedef {import("./typedef").LoadController} LoadController
29
- */
30
-
31
- /**
32
- * @typedef {import("./typedef").ListenerParams} ListenerParams
33
- */
34
-
35
- const Z_BACK = 0;
36
- const Z_FRONT = 10;
37
-
38
- const LABEL_A = 'A';
39
- const LABEL_B = 'B';
40
-
41
- export class PresenterState {
42
- /** @type {Slide[]} */
43
- slides = $state.raw([]);
44
-
45
- /** @type {Layer} */
46
- layerA = $state.raw({ z: Z_BACK, visible: false, stageIdle: true });
47
-
48
- /** @type {Layer} */
49
- layerB = $state.raw({ z: Z_FRONT, visible: false, stageIdle: true });
50
-
51
- /** @type {Slide|null} */
52
- slideA = $state.raw(null);
53
-
54
- /** @type {Slide|null} */
55
- slideB = $state.raw(null);
56
-
57
- /** @type {string} */
58
- currentLayerLabel = $state(LABEL_B);
59
-
60
- /** @type {string} */
61
- nextLayerLabel = $state(LABEL_A);
62
-
63
- /** @type {HkPromise[]} */
64
- transitionPromises = $state.raw([]);
65
-
66
- /** @type {HkPromise} */
67
- slideLoadingPromise = null;
68
-
69
- /** @type {boolean} */
70
- isSlideLoading = $state(false);
71
-
72
- /** @type {boolean} */
73
- loadingSpinner = $state(false);
74
-
75
- /** @type {boolean} */
76
- busy = $derived.by(() => {
77
- const { layerA, layerB, isSlideLoading } = this;
78
-
79
- const layerAStable =
80
- layerA.stageShow || layerA.stageAfter || layerA.stageIdle;
81
- const layerBStable =
82
- layerB.stageShow || layerB.stageAfter || layerB.stageIdle;
83
-
84
- return !(layerAStable && layerBStable) || isSlideLoading;
85
- });
86
-
87
- /** @type {string} */
88
- currentSlideName = $derived.by(() => {
89
- const currentSlide = this.#getSlide(this.currentLayerLabel);
90
- return currentSlide?.name || '';
91
- });
92
-
93
- /** @type {string} */
94
- nextSlideName;
95
-
96
- /** @type {string} */
97
- pendingSlideName;
98
-
99
- /** @type {boolean} */
100
- configured = false;
101
-
102
- /** @type {Map<Symbol, ( params: ListenerParams ) => void>} */
103
- onBeforeListeners = new Map();
104
-
105
- /** @type {Map<Symbol, ( params: ListenerParams ) => void>} */
106
- onShowListeners = new Map();
107
-
108
- /**
109
- * Initialize the presenter state and set up reactivity
110
- */
111
- constructor() {
112
- // this.#setupStageTransitions();
113
-
114
- let timeout;
115
-
116
- $effect((slideLoadingPromise) => {
117
- if (this.isSlideLoading) {
118
- // Enable spinner after a short delay
119
- clearTimeout(timeout);
120
- setTimeout(() => {
121
- untrack(() => {
122
- if (this.isSlideLoading) {
123
- this.loadingSpinner = true;
124
- } else {
125
- this.loadingSpinner = false;
126
- }
127
- });
128
- }, 500);
129
- } else {
130
- this.loadingSpinner = false;
131
- }
132
- });
133
- }
134
-
135
- /**
136
- * Set up reactivity for stage transitions between the before/after states
137
- * This handles the animation timing for both layers
138
- */
139
- // #setupStageTransitions() {
140
- // // Handle layer A stage transitions
141
- // $effect(() => {
142
- // if (this.layerA.stageBeforeIn || this.layerA.stageBeforeOut) {
143
- // this.layerA = this.#processStageTransition(this.layerA);
144
- // }
145
- // });
146
-
147
- // // Handle layer B stage transitions
148
- // $effect(() => {
149
- // if (this.layerB.stageBeforeIn || this.layerB.stageBeforeOut) {
150
- // this.layerB = this.#processStageTransition(this.layerB);
151
- // }
152
- // });
153
- // }
154
-
155
- /**
156
- * Process a single stage transition for a layer
157
- *
158
- * @param {Layer} layer - The layer to process
159
- * @returns {Layer} - The updated layer with new stage
160
- */
161
- #processStageTransition(layer) {
162
- const updatedLayer = { ...layer };
163
-
164
- if (updatedLayer.stageBeforeIn) {
165
- delete updatedLayer.stageBeforeIn;
166
- updatedLayer.stageIn = true;
167
- } else if (updatedLayer.stageBeforeOut) {
168
- delete updatedLayer.stageBeforeOut;
169
- updatedLayer.stageOut = true;
170
- }
171
-
172
- return updatedLayer;
173
- }
174
-
175
- /**
176
- * Waiting for all transition timing promises to finish,
177
- * this should be the same amount of time as it takes for the real
178
- * transitions to finish
179
- *
180
- * @param {HkPromise[]} promises
181
- * Array of transition promises to wait for
182
- */
183
- async #waitForTransitionPromises(promises) {
184
- try {
185
- // console.debug('waitForTransitionPromises', promises);
186
-
187
- await Promise.allSettled(promises);
188
-
189
- // console.debug('waitForTransitionPromises:done', promises);
190
-
191
- untrack(() => {
192
- this.#completeTransition();
193
- });
194
- } catch (error) {
195
- console.log('transition promises cancelled', error);
196
- }
197
- }
198
-
199
- /**
200
- * Complete the transition by updating layers and swapping them
201
- */
202
- #completeTransition() {
203
- // Hide current layer and set stage to AFTER
204
- this.#updateLayer(this.currentLayerLabel, {
205
- z: Z_BACK,
206
- visible: false,
207
- stageAfter: true
208
- });
209
-
210
- // Set next layer stage to SHOW
211
- this.#updateLayer(this.nextLayerLabel, {
212
- z: Z_FRONT,
213
- visible: true,
214
- stageShow: true
215
- });
216
-
217
- // Remove slide from current layer
218
- this.#updateSlide(this.currentLayerLabel, null);
219
-
220
- // Swap current and next layer labels
221
- this.#swapLayers();
222
- }
223
-
224
- /**
225
- * Swap the current and next layer labels
226
- */
227
- #swapLayers() {
228
- if (this.currentLayerLabel === LABEL_A) {
229
- this.currentLayerLabel = LABEL_B;
230
- this.nextLayerLabel = LABEL_A;
231
- } else {
232
- this.currentLayerLabel = LABEL_A;
233
- this.nextLayerLabel = LABEL_B;
234
- }
235
- }
236
-
237
- /**
238
- * Configure the presentation
239
- *
240
- * @param {object} _
241
- * @param {Slide[]} [_.slides] - Array of slides for the presentation
242
- */
243
- configure({ slides }) {
244
- this.configured = true;
245
-
246
- if (slides) {
247
- // Only update slides if provided
248
- this.slides = slides;
249
- }
250
- }
251
-
252
- /**
253
- * Configure the presentation slides
254
- *
255
- * @param {Slide[]} slides - Array of slides for the presentation
256
- */
257
- configureSlides(slides) {
258
- this.slides = slides ?? [];
259
- }
260
-
261
- /**
262
- * Transition to another slide by name
263
- *
264
- * @param {string} name - Name of the slide to transition to
265
- */
266
- async gotoSlide(name) {
267
- // throw new Error('gotoSlide');
268
-
269
- untrack(() => {
270
- const slide = findFirst(this.slides, { name });
271
-
272
- if (!slide) {
273
- console.log('available slides', this.slides);
274
- throw new Error(`Slide [${name}] has not been defined`);
275
- }
276
-
277
- this.#gotoSlide(slide);
278
- });
279
- }
280
-
281
- /**
282
- * Internal method to transition to another slide
283
- *
284
- * @param {Slide} slide - The slide to transition to
285
- */
286
- async #gotoSlide(slide) {
287
- if (!this.configured) {
288
- throw new Error('Not configured yet');
289
- }
290
-
291
- if (slide.name === this.currentSlideName) {
292
- //throw new Error(`gotoSlide cannot transition to current slide`);
293
- console.error(`gotoSlide cannot transition to current slide`);
294
- return;
295
- }
296
-
297
- this.nextSlideName = slide.name;
298
-
299
- if (this.busy) {
300
- this.pendingSlideName = slide.name;
301
- return;
302
- }
303
-
304
- this.#callOnBeforeListeners();
305
-
306
- this.slideLoadingPromise = null;
307
-
308
- // Get a presenter reference to pass to the slide
309
- const presenterRef = this.getPresenterRef();
310
-
311
- // Create a copy of the slide to avoid mutating the original
312
- const slideWithProps = {
313
- ...slide,
314
- data: {
315
- ...slide.data,
316
- props: {
317
- ...(slide.data.props || {}),
318
- getLoadingController: () => {
319
- this.isSlideLoading = true;
320
- this.slideLoadingPromise = new HkPromise(() => {});
321
-
322
- return this.#getLoadingController();
323
- // this.slideLoadingPromise should be a HkPromise now
324
- // console.log('slideLoadingPromise', this.slideLoadingPromise);
325
- },
326
- presenter: presenterRef // Add presenter reference to props
327
- }
328
- }
329
- };
330
-
331
- // console.debug('Checkpoint 1');
332
-
333
- // Add next slide to next layer
334
- this.#updateSlide(this.nextLayerLabel, slideWithProps);
335
-
336
- // console.debug('Checkpoint 2');
337
-
338
- await tick();
339
-
340
- // console.debug('Checkpoint 3');
341
-
342
- if (this.slideLoadingPromise) {
343
- // console.debug('Waiting for slide to load');
344
- // @ts-ignore
345
- await this.slideLoadingPromise;
346
- this.isSlideLoading = false;
347
- // console.debug('Done waiting for slide loading');
348
- }
349
-
350
- const currentSlide = this.#getSlide(this.currentLayerLabel);
351
- const nextSlide = this.#getSlide(this.nextLayerLabel);
352
-
353
- // console.debug('Checkpoint 4', currentSlide, nextSlide);
354
-
355
- // Make next layer visible, move to front, and prepare for
356
- // transition in
357
- this.#updateLayer(this.nextLayerLabel, {
358
- z: Z_FRONT,
359
- visible: true,
360
- stageBeforeIn: true,
361
- transitions: nextSlide?.intro ?? []
362
- });
363
-
364
- // Move current layer to back, keep visible, and prepare for
365
- // transition out
366
- this.#updateLayer(this.currentLayerLabel, {
367
- z: Z_BACK,
368
- visible: true,
369
- stageBeforeOut: true,
370
- transitions: currentSlide?.outro ?? []
371
- });
372
-
373
- // console.debug('Checkpoint 5');
374
-
375
- // Wait briefly to ensure the stageBeforeIn/stageBeforeOut states are rendered
376
- await tick();
377
-
378
- // Now manually process the transitions for both layers
379
- const layerA = this.layerA;
380
- const layerB = this.layerB;
381
-
382
- // Process stageBeforeIn transition for both layers
383
- if (layerA.stageBeforeIn) {
384
- this.layerA = this.#processStageTransition(layerA);
385
- }
386
-
387
- if (layerB.stageBeforeIn) {
388
- this.layerB = this.#processStageTransition(layerB);
389
- }
390
-
391
- // Wait for another tick to ensure the stageIn states are rendered
392
- await tick();
393
-
394
- // Process stageBeforeOut transition for both layers
395
- if (layerA.stageBeforeOut) {
396
- this.layerA = this.#processStageTransition(layerA);
397
- }
398
-
399
- if (layerB.stageBeforeOut) {
400
- this.layerB = this.#processStageTransition(layerB);
401
- }
402
-
403
- // console.debug('Checkpoint 7');
404
-
405
- // Start transitions
406
- this.#createTransitionPromises();
407
-
408
- // console.debug('Checkpoint 8');
409
-
410
- await this.#waitForTransitionPromises(this.transitionPromises);
411
-
412
- // Check if there's a pending slide transition
413
- if (this.pendingSlideName) {
414
- const pendingName = this.pendingSlideName;
415
-
416
- this.nextSlideName = pendingName;
417
- this.pendingSlideName = null;
418
-
419
- untrack(() => {
420
- if (pendingName !== this.currentSlideName) {
421
- this.gotoSlide(pendingName);
422
- }
423
- });
424
- } else {
425
- this.nextSlideName = null;
426
- }
427
-
428
- this.#callOnShowListeners();
429
- }
430
-
431
- #callOnBeforeListeners() {
432
- let nextSlideName = this.nextSlideName;
433
-
434
- for (const fn of this.onBeforeListeners.values()) {
435
- fn({ stage: STAGE_BEFORE, slideName: nextSlideName });
436
- }
437
- }
438
-
439
- #callOnShowListeners() {
440
- let currentSlideName = this.currentSlideName;
441
-
442
- for (const fn of this.onShowListeners.values()) {
443
- fn({ stage: STAGE_SHOW, slideName: currentSlideName });
444
- }
445
- }
446
-
447
- /**
448
- * Create transition promises that can be used to determine the timing
449
- * of the transitions between current and next slide
450
- */
451
- #createTransitionPromises() {
452
- // Cancel existing transitions
453
- let transitionPromises = this.transitionPromises;
454
-
455
- for (const current of transitionPromises) {
456
- current.tryCancel();
457
- }
458
-
459
- // Start new transitions
460
- transitionPromises = [];
461
-
462
- const currentSlide = this.#getSlide(this.currentLayerLabel);
463
- const nextSlide = this.#getSlide(this.nextLayerLabel);
464
-
465
- // Apply transitions `out` from currentslide
466
- const transitionsOut = currentSlide?.outro;
467
-
468
- // console.log('transitionsOut', transitionsOut);
469
-
470
- if (transitionsOut) {
471
- for (const transition of transitionsOut) {
472
- const promise = this.#createTransitionPromise(transition);
473
- transitionPromises.push(promise);
474
- }
475
- }
476
-
477
- // Apply transitions `in` from next slide
478
- const transitionsIn = nextSlide?.intro;
479
-
480
- // console.log('transitionsIn', transitionsIn);
481
-
482
- if (transitionsIn) {
483
- for (const transition of transitionsIn) {
484
- const promise = this.#createTransitionPromise(transition);
485
- transitionPromises.push(promise);
486
- }
487
- }
488
-
489
- this.transitionPromises = transitionPromises;
490
- }
491
-
492
- /**
493
- * Create a transition promise for the specified transition
494
- *
495
- *
496
- * @param {Transition} transition - The transition to apply
497
- *
498
- * @returns {HkPromise}
499
- * Promise that resolves after the same amount of time that it
500
- * takes for the transition to finish
501
- */
502
- #createTransitionPromise(transition) {
503
- const delay = (transition.delay ?? 0) + (transition.duration ?? 0);
504
-
505
- if (0 === delay) {
506
- const promise = new HkPromise(() => {});
507
- promise.resolve(true);
508
- return promise;
509
- }
510
-
511
- let promise = new HkPromise((/** @type {function} */ resolve) => {
512
- if (delay) {
513
- setTimeout(() => {
514
- resolve(true);
515
- }, delay);
516
- }
517
- });
518
-
519
- return promise;
520
- }
521
-
522
- /**
523
- * Get slide by layer label
524
- *
525
- * @param {string} label - Layer label (A or B)
526
- * @returns {Slide|null} The slide for the specified layer or null
527
- */
528
- #getSlide(label) {
529
- if (label === LABEL_A) {
530
- return this.slideA;
531
- }
532
-
533
- if (label === LABEL_B) {
534
- return this.slideB;
535
- }
536
-
537
- return null;
538
- }
539
-
540
- /**
541
- * Update layer by label
542
- *
543
- * @param {string} label - Layer label (A or B)
544
- * @param {Layer} data - Layer data to update
545
- */
546
- #updateLayer(label, data) {
547
- if (label === LABEL_A) {
548
- this.layerA = data;
549
- return;
550
- }
551
-
552
- if (label === LABEL_B) {
553
- this.layerB = data;
554
- return;
555
- }
556
-
557
- throw new Error(`Missing layer [${label}]`);
558
- }
559
-
560
- /**
561
- * Update slide by label
562
- *
563
- * @param {string} label - Layer label (A or B)
564
- * @param {Slide|null} data - Slide data to update or null to clear
565
- */
566
- #updateSlide(label, data) {
567
- if (label === LABEL_A) {
568
- this.slideA = data;
569
- return;
570
- }
571
-
572
- if (label === LABEL_B) {
573
- this.slideB = data;
574
- return;
575
- }
576
-
577
- throw new Error(`Missing slide [${label}]`);
578
- }
579
-
580
- /**
581
- * Returns a simplified presenter reference with essential methods
582
- * for slide components to use
583
- *
584
- * @returns {PresenterRef} A reference object with presenter methods
585
- */
586
- getPresenterRef() {
587
- return {
588
- gotoSlide: (name) => this.gotoSlide(name),
589
- getCurrentSlideName: () => this.currentSlideName,
590
- onBefore: (callback) => {
591
- const key = Symbol();
592
- this.onBeforeListeners.set(key, callback);
593
-
594
- return () => {
595
- this.onBeforeListeners.delete(key);
596
- };
597
- },
598
- onShow: (callback) => {
599
- const key = Symbol();
600
- this.onShowListeners.set(key, callback);
601
-
602
- return () => {
603
- this.onShowListeners.delete(key);
604
- };
605
- }
606
- };
607
- }
608
-
609
- /**
610
- * Returns a controller object for managing manual loading
611
- * Components can use this to signal when they're done loading
612
- * or to cancel and go back to the previous slide
613
- *
614
- * @returns {LoadController}
615
- * Object with loaded() and cancel() methods
616
- */
617
- #getLoadingController() {
618
- // console.debug('getLoadingController was called');
619
-
620
- return {
621
- /**
622
- * Call when component has finished loading
623
- */
624
- loaded: () => {
625
- // console.debug('Slide said loading has completed');
626
- this.slideLoadingPromise?.tryResolve();
627
- },
628
-
629
- /**
630
- * Call to cancel loading and return to previous slide
631
- */
632
- cancel: () => {
633
- // console.debug('Slide said loading has cancelled');
634
- this.slideLoadingPromise?.tryReject();
635
- }
636
- };
637
- }
638
- }
1
+ import { tick } from 'svelte';
2
+
3
+ import { findFirst } from '../../../util/array/index.js';
4
+
5
+ import { untrack } from 'svelte';
6
+
7
+ import { HkPromise } from '../../../classes/promise/index.js';
8
+
9
+ import { STAGE_BEFORE, STAGE_SHOW } from './constants.js';
10
+
11
+ /**
12
+ * @typedef {import("./typedef").Slide} Slide
13
+ */
14
+
15
+ /**
16
+ * @typedef {import("./typedef").Transition} Transition
17
+ */
18
+
19
+ /**
20
+ * @typedef {import("./typedef").Layer} Layer
21
+ */
22
+
23
+ /**
24
+ * @typedef {import("./typedef").PresenterRef} PresenterRef
25
+ */
26
+
27
+ /**
28
+ * @typedef {import("./typedef").LoadController} LoadController
29
+ */
30
+
31
+ /**
32
+ * @typedef {import("./typedef").ListenerParams} ListenerParams
33
+ */
34
+
35
+ const Z_BACK = 0;
36
+ const Z_FRONT = 10;
37
+
38
+ const LABEL_A = 'A';
39
+ const LABEL_B = 'B';
40
+
41
+ export class PresenterState {
42
+ /** @type {Slide[]} */
43
+ slides = $state.raw([]);
44
+
45
+ /** @type {Layer} */
46
+ layerA = $state.raw({ z: Z_BACK, visible: false, stageIdle: true });
47
+
48
+ /** @type {Layer} */
49
+ layerB = $state.raw({ z: Z_FRONT, visible: false, stageIdle: true });
50
+
51
+ /** @type {Slide|null} */
52
+ slideA = $state.raw(null);
53
+
54
+ /** @type {Slide|null} */
55
+ slideB = $state.raw(null);
56
+
57
+ /** @type {string} */
58
+ currentLayerLabel = $state(LABEL_B);
59
+
60
+ /** @type {string} */
61
+ nextLayerLabel = $state(LABEL_A);
62
+
63
+ /** @type {HkPromise[]} */
64
+ transitionPromises = $state.raw([]);
65
+
66
+ /** @type {HkPromise} */
67
+ slideLoadingPromise = null;
68
+
69
+ /** @type {boolean} */
70
+ isSlideLoading = $state(false);
71
+
72
+ /** @type {boolean} */
73
+ loadingSpinner = $state(false);
74
+
75
+ /** @type {boolean} */
76
+ busy = $derived.by(() => {
77
+ const { layerA, layerB, isSlideLoading } = this;
78
+
79
+ const layerAStable =
80
+ layerA.stageShow || layerA.stageAfter || layerA.stageIdle;
81
+ const layerBStable =
82
+ layerB.stageShow || layerB.stageAfter || layerB.stageIdle;
83
+
84
+ return !(layerAStable && layerBStable) || isSlideLoading;
85
+ });
86
+
87
+ /** @type {string} */
88
+ currentSlideName = $derived.by(() => {
89
+ const currentSlide = this.#getSlide(this.currentLayerLabel);
90
+ return currentSlide?.name || '';
91
+ });
92
+
93
+ /** @type {string} */
94
+ nextSlideName;
95
+
96
+ /** @type {string} */
97
+ pendingSlideName;
98
+
99
+ /** @type {boolean} */
100
+ configured = false;
101
+
102
+ /** @type {Map<Symbol, ( params: ListenerParams ) => void>} */
103
+ onBeforeListeners = new Map();
104
+
105
+ /** @type {Map<Symbol, ( params: ListenerParams ) => void>} */
106
+ onShowListeners = new Map();
107
+
108
+ /**
109
+ * Initialize the presenter state and set up reactivity
110
+ */
111
+ constructor() {
112
+ // this.#setupStageTransitions();
113
+
114
+ let timeout;
115
+
116
+ $effect((slideLoadingPromise) => {
117
+ if (this.isSlideLoading) {
118
+ // Enable spinner after a short delay
119
+ clearTimeout(timeout);
120
+ setTimeout(() => {
121
+ untrack(() => {
122
+ if (this.isSlideLoading) {
123
+ this.loadingSpinner = true;
124
+ } else {
125
+ this.loadingSpinner = false;
126
+ }
127
+ });
128
+ }, 500);
129
+ } else {
130
+ this.loadingSpinner = false;
131
+ }
132
+ });
133
+ }
134
+
135
+ /**
136
+ * Set up reactivity for stage transitions between the before/after states
137
+ * This handles the animation timing for both layers
138
+ */
139
+ // #setupStageTransitions() {
140
+ // // Handle layer A stage transitions
141
+ // $effect(() => {
142
+ // if (this.layerA.stageBeforeIn || this.layerA.stageBeforeOut) {
143
+ // this.layerA = this.#processStageTransition(this.layerA);
144
+ // }
145
+ // });
146
+
147
+ // // Handle layer B stage transitions
148
+ // $effect(() => {
149
+ // if (this.layerB.stageBeforeIn || this.layerB.stageBeforeOut) {
150
+ // this.layerB = this.#processStageTransition(this.layerB);
151
+ // }
152
+ // });
153
+ // }
154
+
155
+ /**
156
+ * Process a single stage transition for a layer
157
+ *
158
+ * @param {Layer} layer - The layer to process
159
+ * @returns {Layer} - The updated layer with new stage
160
+ */
161
+ #processStageTransition(layer) {
162
+ const updatedLayer = { ...layer };
163
+
164
+ if (updatedLayer.stageBeforeIn) {
165
+ delete updatedLayer.stageBeforeIn;
166
+ updatedLayer.stageIn = true;
167
+ } else if (updatedLayer.stageBeforeOut) {
168
+ delete updatedLayer.stageBeforeOut;
169
+ updatedLayer.stageOut = true;
170
+ }
171
+
172
+ return updatedLayer;
173
+ }
174
+
175
+ /**
176
+ * Waiting for all transition timing promises to finish,
177
+ * this should be the same amount of time as it takes for the real
178
+ * transitions to finish
179
+ *
180
+ * @param {HkPromise[]} promises
181
+ * Array of transition promises to wait for
182
+ */
183
+ async #waitForTransitionPromises(promises) {
184
+ try {
185
+ // console.debug('waitForTransitionPromises', promises);
186
+
187
+ await Promise.allSettled(promises);
188
+
189
+ // console.debug('waitForTransitionPromises:done', promises);
190
+
191
+ untrack(() => {
192
+ this.#completeTransition();
193
+ });
194
+ } catch (error) {
195
+ console.log('transition promises cancelled', error);
196
+ }
197
+ }
198
+
199
+ /**
200
+ * Complete the transition by updating layers and swapping them
201
+ */
202
+ #completeTransition() {
203
+ // Hide current layer and set stage to AFTER
204
+ this.#updateLayer(this.currentLayerLabel, {
205
+ z: Z_BACK,
206
+ visible: false,
207
+ stageAfter: true
208
+ });
209
+
210
+ // Set next layer stage to SHOW
211
+ this.#updateLayer(this.nextLayerLabel, {
212
+ z: Z_FRONT,
213
+ visible: true,
214
+ stageShow: true
215
+ });
216
+
217
+ // Remove slide from current layer
218
+ this.#updateSlide(this.currentLayerLabel, null);
219
+
220
+ // Swap current and next layer labels
221
+ this.#swapLayers();
222
+ }
223
+
224
+ /**
225
+ * Swap the current and next layer labels
226
+ */
227
+ #swapLayers() {
228
+ if (this.currentLayerLabel === LABEL_A) {
229
+ this.currentLayerLabel = LABEL_B;
230
+ this.nextLayerLabel = LABEL_A;
231
+ } else {
232
+ this.currentLayerLabel = LABEL_A;
233
+ this.nextLayerLabel = LABEL_B;
234
+ }
235
+ }
236
+
237
+ /**
238
+ * Configure the presentation
239
+ *
240
+ * @param {object} _
241
+ * @param {Slide[]} [_.slides] - Array of slides for the presentation
242
+ */
243
+ configure({ slides }) {
244
+ this.configured = true;
245
+
246
+ if (slides) {
247
+ // Only update slides if provided
248
+ this.slides = slides;
249
+ }
250
+ }
251
+
252
+ /**
253
+ * Configure the presentation slides
254
+ *
255
+ * @param {Slide[]} slides - Array of slides for the presentation
256
+ */
257
+ configureSlides(slides) {
258
+ this.slides = slides ?? [];
259
+ }
260
+
261
+ /**
262
+ * Transition to another slide by name
263
+ *
264
+ * @param {string} name - Name of the slide to transition to
265
+ */
266
+ async gotoSlide(name) {
267
+ // throw new Error('gotoSlide');
268
+
269
+ untrack(() => {
270
+ const slide = findFirst(this.slides, { name });
271
+
272
+ if (!slide) {
273
+ console.log('available slides', this.slides);
274
+ throw new Error(`Slide [${name}] has not been defined`);
275
+ }
276
+
277
+ this.#gotoSlide(slide);
278
+ });
279
+ }
280
+
281
+ /**
282
+ * Internal method to transition to another slide
283
+ *
284
+ * @param {Slide} slide - The slide to transition to
285
+ */
286
+ async #gotoSlide(slide) {
287
+ if (!this.configured) {
288
+ throw new Error('Not configured yet');
289
+ }
290
+
291
+ if (slide.name === this.currentSlideName) {
292
+ //throw new Error(`gotoSlide cannot transition to current slide`);
293
+ console.error(`gotoSlide cannot transition to current slide`);
294
+ return;
295
+ }
296
+
297
+ this.nextSlideName = slide.name;
298
+
299
+ if (this.busy) {
300
+ this.pendingSlideName = slide.name;
301
+ return;
302
+ }
303
+
304
+ this.#callOnBeforeListeners();
305
+
306
+ this.slideLoadingPromise = null;
307
+
308
+ // Get a presenter reference to pass to the slide
309
+ const presenterRef = this.getPresenterRef();
310
+
311
+ // Create a copy of the slide to avoid mutating the original
312
+ const slideWithProps = {
313
+ ...slide,
314
+ data: {
315
+ ...slide.data,
316
+ props: {
317
+ ...(slide.data.props || {}),
318
+ getLoadingController: () => {
319
+ this.isSlideLoading = true;
320
+ this.slideLoadingPromise = new HkPromise(() => {});
321
+
322
+ return this.#getLoadingController();
323
+ // this.slideLoadingPromise should be a HkPromise now
324
+ // console.log('slideLoadingPromise', this.slideLoadingPromise);
325
+ },
326
+ presenter: presenterRef // Add presenter reference to props
327
+ }
328
+ }
329
+ };
330
+
331
+ // console.debug('Checkpoint 1');
332
+
333
+ // Add next slide to next layer
334
+ this.#updateSlide(this.nextLayerLabel, slideWithProps);
335
+
336
+ // console.debug('Checkpoint 2');
337
+
338
+ await tick();
339
+
340
+ // console.debug('Checkpoint 3');
341
+
342
+ if (this.slideLoadingPromise) {
343
+ // console.debug('Waiting for slide to load');
344
+ // @ts-ignore
345
+ await this.slideLoadingPromise;
346
+ this.isSlideLoading = false;
347
+ // console.debug('Done waiting for slide loading');
348
+ }
349
+
350
+ const currentSlide = this.#getSlide(this.currentLayerLabel);
351
+ const nextSlide = this.#getSlide(this.nextLayerLabel);
352
+
353
+ // console.debug('Checkpoint 4', currentSlide, nextSlide);
354
+
355
+ // Make next layer visible, move to front, and prepare for
356
+ // transition in
357
+ this.#updateLayer(this.nextLayerLabel, {
358
+ z: Z_FRONT,
359
+ visible: true,
360
+ stageBeforeIn: true,
361
+ transitions: nextSlide?.intro ?? []
362
+ });
363
+
364
+ // Move current layer to back, keep visible, and prepare for
365
+ // transition out
366
+ this.#updateLayer(this.currentLayerLabel, {
367
+ z: Z_BACK,
368
+ visible: true,
369
+ stageBeforeOut: true,
370
+ transitions: currentSlide?.outro ?? []
371
+ });
372
+
373
+ // console.debug('Checkpoint 5');
374
+
375
+ // Wait briefly to ensure the stageBeforeIn/stageBeforeOut states are rendered
376
+ await tick();
377
+
378
+ // Now manually process the transitions for both layers
379
+ const layerA = this.layerA;
380
+ const layerB = this.layerB;
381
+
382
+ // Process stageBeforeIn transition for both layers
383
+ if (layerA.stageBeforeIn) {
384
+ this.layerA = this.#processStageTransition(layerA);
385
+ }
386
+
387
+ if (layerB.stageBeforeIn) {
388
+ this.layerB = this.#processStageTransition(layerB);
389
+ }
390
+
391
+ // Wait for another tick to ensure the stageIn states are rendered
392
+ await tick();
393
+
394
+ // Process stageBeforeOut transition for both layers
395
+ if (layerA.stageBeforeOut) {
396
+ this.layerA = this.#processStageTransition(layerA);
397
+ }
398
+
399
+ if (layerB.stageBeforeOut) {
400
+ this.layerB = this.#processStageTransition(layerB);
401
+ }
402
+
403
+ // console.debug('Checkpoint 7');
404
+
405
+ // Start transitions
406
+ this.#createTransitionPromises();
407
+
408
+ // console.debug('Checkpoint 8');
409
+
410
+ await this.#waitForTransitionPromises(this.transitionPromises);
411
+
412
+ // Check if there's a pending slide transition
413
+ if (this.pendingSlideName) {
414
+ const pendingName = this.pendingSlideName;
415
+
416
+ this.nextSlideName = pendingName;
417
+ this.pendingSlideName = null;
418
+
419
+ untrack(() => {
420
+ if (pendingName !== this.currentSlideName) {
421
+ this.gotoSlide(pendingName);
422
+ }
423
+ });
424
+ } else {
425
+ this.nextSlideName = null;
426
+ }
427
+
428
+ this.#callOnShowListeners();
429
+ }
430
+
431
+ #callOnBeforeListeners() {
432
+ let nextSlideName = this.nextSlideName;
433
+
434
+ for (const fn of this.onBeforeListeners.values()) {
435
+ fn({ stage: STAGE_BEFORE, slideName: nextSlideName });
436
+ }
437
+ }
438
+
439
+ #callOnShowListeners() {
440
+ let currentSlideName = this.currentSlideName;
441
+
442
+ for (const fn of this.onShowListeners.values()) {
443
+ fn({ stage: STAGE_SHOW, slideName: currentSlideName });
444
+ }
445
+ }
446
+
447
+ /**
448
+ * Create transition promises that can be used to determine the timing
449
+ * of the transitions between current and next slide
450
+ */
451
+ #createTransitionPromises() {
452
+ // Cancel existing transitions
453
+ let transitionPromises = this.transitionPromises;
454
+
455
+ for (const current of transitionPromises) {
456
+ current.tryCancel();
457
+ }
458
+
459
+ // Start new transitions
460
+ transitionPromises = [];
461
+
462
+ const currentSlide = this.#getSlide(this.currentLayerLabel);
463
+ const nextSlide = this.#getSlide(this.nextLayerLabel);
464
+
465
+ // Apply transitions `out` from currentslide
466
+ const transitionsOut = currentSlide?.outro;
467
+
468
+ // console.log('transitionsOut', transitionsOut);
469
+
470
+ if (transitionsOut) {
471
+ for (const transition of transitionsOut) {
472
+ const promise = this.#createTransitionPromise(transition);
473
+ transitionPromises.push(promise);
474
+ }
475
+ }
476
+
477
+ // Apply transitions `in` from next slide
478
+ const transitionsIn = nextSlide?.intro;
479
+
480
+ // console.log('transitionsIn', transitionsIn);
481
+
482
+ if (transitionsIn) {
483
+ for (const transition of transitionsIn) {
484
+ const promise = this.#createTransitionPromise(transition);
485
+ transitionPromises.push(promise);
486
+ }
487
+ }
488
+
489
+ this.transitionPromises = transitionPromises;
490
+ }
491
+
492
+ /**
493
+ * Create a transition promise for the specified transition
494
+ *
495
+ *
496
+ * @param {Transition} transition - The transition to apply
497
+ *
498
+ * @returns {HkPromise}
499
+ * Promise that resolves after the same amount of time that it
500
+ * takes for the transition to finish
501
+ */
502
+ #createTransitionPromise(transition) {
503
+ const delay = (transition.delay ?? 0) + (transition.duration ?? 0);
504
+
505
+ if (0 === delay) {
506
+ const promise = new HkPromise(() => {});
507
+ promise.resolve(true);
508
+ return promise;
509
+ }
510
+
511
+ let promise = new HkPromise((/** @type {function} */ resolve) => {
512
+ if (delay) {
513
+ setTimeout(() => {
514
+ resolve(true);
515
+ }, delay);
516
+ }
517
+ });
518
+
519
+ return promise;
520
+ }
521
+
522
+ /**
523
+ * Get slide by layer label
524
+ *
525
+ * @param {string} label - Layer label (A or B)
526
+ * @returns {Slide|null} The slide for the specified layer or null
527
+ */
528
+ #getSlide(label) {
529
+ if (label === LABEL_A) {
530
+ return this.slideA;
531
+ }
532
+
533
+ if (label === LABEL_B) {
534
+ return this.slideB;
535
+ }
536
+
537
+ return null;
538
+ }
539
+
540
+ /**
541
+ * Update layer by label
542
+ *
543
+ * @param {string} label - Layer label (A or B)
544
+ * @param {Layer} data - Layer data to update
545
+ */
546
+ #updateLayer(label, data) {
547
+ if (label === LABEL_A) {
548
+ this.layerA = data;
549
+ return;
550
+ }
551
+
552
+ if (label === LABEL_B) {
553
+ this.layerB = data;
554
+ return;
555
+ }
556
+
557
+ throw new Error(`Missing layer [${label}]`);
558
+ }
559
+
560
+ /**
561
+ * Update slide by label
562
+ *
563
+ * @param {string} label - Layer label (A or B)
564
+ * @param {Slide|null} data - Slide data to update or null to clear
565
+ */
566
+ #updateSlide(label, data) {
567
+ if (label === LABEL_A) {
568
+ this.slideA = data;
569
+ return;
570
+ }
571
+
572
+ if (label === LABEL_B) {
573
+ this.slideB = data;
574
+ return;
575
+ }
576
+
577
+ throw new Error(`Missing slide [${label}]`);
578
+ }
579
+
580
+ /**
581
+ * Returns a simplified presenter reference with essential methods
582
+ * for slide components to use
583
+ *
584
+ * @returns {PresenterRef} A reference object with presenter methods
585
+ */
586
+ getPresenterRef() {
587
+ return {
588
+ gotoSlide: (name) => this.gotoSlide(name),
589
+ getCurrentSlideName: () => this.currentSlideName,
590
+ onBefore: (callback) => {
591
+ const key = Symbol();
592
+ this.onBeforeListeners.set(key, callback);
593
+
594
+ return () => {
595
+ this.onBeforeListeners.delete(key);
596
+ };
597
+ },
598
+ onShow: (callback) => {
599
+ const key = Symbol();
600
+ this.onShowListeners.set(key, callback);
601
+
602
+ return () => {
603
+ this.onShowListeners.delete(key);
604
+ };
605
+ }
606
+ };
607
+ }
608
+
609
+ /**
610
+ * Returns a controller object for managing manual loading
611
+ * Components can use this to signal when they're done loading
612
+ * or to cancel and go back to the previous slide
613
+ *
614
+ * @returns {LoadController}
615
+ * Object with loaded() and cancel() methods
616
+ */
617
+ #getLoadingController() {
618
+ // console.debug('getLoadingController was called');
619
+
620
+ return {
621
+ /**
622
+ * Call when component has finished loading
623
+ */
624
+ loaded: () => {
625
+ // console.debug('Slide said loading has completed');
626
+ this.slideLoadingPromise?.tryResolve();
627
+ },
628
+
629
+ /**
630
+ * Call to cancel loading and return to previous slide
631
+ */
632
+ cancel: () => {
633
+ // console.debug('Slide said loading has cancelled');
634
+ this.slideLoadingPromise?.tryReject();
635
+ }
636
+ };
637
+ }
638
+ }