@hawsen-the-first/interactiv 0.0.1

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 (48) hide show
  1. package/README.md +326 -0
  2. package/dist/animations.css +160 -0
  3. package/dist/index.d.ts +20 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +26 -0
  6. package/dist/src/animationBus.d.ts +30 -0
  7. package/dist/src/animationBus.d.ts.map +1 -0
  8. package/dist/src/animationBus.js +125 -0
  9. package/dist/src/appBuilder.d.ts +173 -0
  10. package/dist/src/appBuilder.d.ts.map +1 -0
  11. package/dist/src/appBuilder.js +957 -0
  12. package/dist/src/eventBus.d.ts +100 -0
  13. package/dist/src/eventBus.d.ts.map +1 -0
  14. package/dist/src/eventBus.js +326 -0
  15. package/dist/src/eventManager.d.ts +87 -0
  16. package/dist/src/eventManager.d.ts.map +1 -0
  17. package/dist/src/eventManager.js +455 -0
  18. package/dist/src/garbageCollector.d.ts +68 -0
  19. package/dist/src/garbageCollector.d.ts.map +1 -0
  20. package/dist/src/garbageCollector.js +169 -0
  21. package/dist/src/logger.d.ts +11 -0
  22. package/dist/src/logger.d.ts.map +1 -0
  23. package/dist/src/logger.js +15 -0
  24. package/dist/src/navigationManager.d.ts +105 -0
  25. package/dist/src/navigationManager.d.ts.map +1 -0
  26. package/dist/src/navigationManager.js +533 -0
  27. package/dist/src/screensaverManager.d.ts +66 -0
  28. package/dist/src/screensaverManager.d.ts.map +1 -0
  29. package/dist/src/screensaverManager.js +417 -0
  30. package/dist/src/settingsManager.d.ts +48 -0
  31. package/dist/src/settingsManager.d.ts.map +1 -0
  32. package/dist/src/settingsManager.js +317 -0
  33. package/dist/src/stateManager.d.ts +58 -0
  34. package/dist/src/stateManager.d.ts.map +1 -0
  35. package/dist/src/stateManager.js +278 -0
  36. package/dist/src/types.d.ts +32 -0
  37. package/dist/src/types.d.ts.map +1 -0
  38. package/dist/src/types.js +1 -0
  39. package/dist/utils/generateGuid.d.ts +2 -0
  40. package/dist/utils/generateGuid.d.ts.map +1 -0
  41. package/dist/utils/generateGuid.js +19 -0
  42. package/dist/utils/logger.d.ts +9 -0
  43. package/dist/utils/logger.d.ts.map +1 -0
  44. package/dist/utils/logger.js +42 -0
  45. package/dist/utils/template-helpers.d.ts +32 -0
  46. package/dist/utils/template-helpers.d.ts.map +1 -0
  47. package/dist/utils/template-helpers.js +24 -0
  48. package/package.json +59 -0
package/README.md ADDED
@@ -0,0 +1,326 @@
1
+ # Interactiv
2
+
3
+ A TypeScript framework for building embedded interactive applications with event management, state management, navigation, and screensaver functionality. The package is designed to maximise compatibility with older versions of Node/Chromium, specifically to work with BrightSign media players.
4
+
5
+ The package also emphsises support for touchscreen input, but can also be used for cursor/poiner device inputs.
6
+ test change
7
+ ## Installation
8
+
9
+ ### Local Development with npm link
10
+
11
+ Since this is a local package, you can install it in your projects using npm link:
12
+
13
+ ```bash
14
+ # In the interactiv package directory
15
+ npm link
16
+
17
+ # In your project directory
18
+ npm link interactiv
19
+ ```
20
+
21
+ ## Features
22
+
23
+ - **Event Management**: Unified pointer interactions (mouse & touch)
24
+ - **State Management**: Global and external state management with subscriptions
25
+ - **Navigation**: Page and view-based navigation system
26
+ - **Screensaver**: Built-in screensaver functionality
27
+ - **Settings Manager**: Hidden settings page with corner touch activation
28
+ - **Animation Bus**: Animation coordination system
29
+ - **App Builder**: Component-based application architecture
30
+
31
+ ## Basic Usage
32
+
33
+ ```typescript
34
+ import {
35
+ createOrchestrator,
36
+ AppBuilder,
37
+ Page,
38
+ View,
39
+ Component,
40
+ html,
41
+ css,
42
+ } from "interactiv";
43
+
44
+ // Import animations CSS
45
+ import "interactiv/animations.css";
46
+
47
+ // Create orchestrator
48
+ const orchestrator = createOrchestrator();
49
+
50
+ // Create app
51
+ const app = new AppBuilder(orchestrator);
52
+
53
+ // Create a page
54
+ const homePage = new Page("home-page", orchestrator, false);
55
+
56
+ // Create a view
57
+ const homeView = new View(
58
+ "home-view",
59
+ orchestrator,
60
+ false,
61
+ html`<div class="home"><h1>Welcome</h1></div>`,
62
+ css`.home { padding: 2rem; }`
63
+ );
64
+
65
+ // Add view to page
66
+ homePage.addView(homeView);
67
+
68
+ // Add page to app
69
+ app.addPage(homePage);
70
+
71
+ // Attach to DOM
72
+ app.attachToDom();
73
+
74
+ // Navigate to view - Two equivalent methods:
75
+
76
+ // Method 1: Via AppBuilder (convenient when you have app reference)
77
+ app.navigateToView("home-view");
78
+
79
+ // Method 2: Via Orchestrator directly (recommended in components)
80
+ orchestrator.navigateToView("home-view", {
81
+ type: "fade",
82
+ duration: 300
83
+ });
84
+ ```
85
+
86
+ ## Core Modules
87
+
88
+ ### Event Orchestrator
89
+
90
+ Create and manage event buses for communication between components:
91
+
92
+ ```typescript
93
+ import { createOrchestrator } from "interactiv";
94
+
95
+ const orchestrator = createOrchestrator();
96
+ const eventBus = orchestrator.registerEventBus("my-bus");
97
+
98
+ eventBus.on("my-event", (event) => {
99
+ console.log(event.detail);
100
+ });
101
+
102
+ eventBus.emit("my-event", { data: "Hello!" });
103
+ ```
104
+
105
+ ### State Management
106
+
107
+ Manage global application state with subscriptions:
108
+
109
+ ```typescript
110
+ import {
111
+ setGlobalState,
112
+ getGlobalState,
113
+ subscribeToGlobalState,
114
+ } from "interactiv";
115
+
116
+ // Set state
117
+ setGlobalState("user.name", "John");
118
+
119
+ // Get state
120
+ const userName = getGlobalState("user.name");
121
+
122
+ // Subscribe to changes
123
+ subscribeToGlobalState("user.name", (value) => {
124
+ console.log("Name changed:", value);
125
+ });
126
+ ```
127
+
128
+ ### Navigation Manager
129
+
130
+ **Important:** NavigationManager is a singleton service that should not be instantiated directly. It is automatically created by AppBuilder, and you access navigation methods through your AppBuilder instance.
131
+
132
+ Navigate between pages and views:
133
+
134
+ ```typescript
135
+ // ✅ Correct: Use navigation through AppBuilder
136
+ const app = new AppBuilder(orchestrator);
137
+
138
+ // Add your pages and views
139
+ app.addPage(homePage);
140
+
141
+ // Navigate to a page
142
+ app.navigateToPage("home-page");
143
+
144
+ // Navigate to a view with optional transition
145
+ app.navigateToView("home-view", {
146
+ type: "fade",
147
+ duration: 300,
148
+ easing: "ease-in-out"
149
+ });
150
+
151
+ // Get current navigation state
152
+ const currentPageId = app.getCurrentPageId();
153
+ const currentViewId = app.getCurrentViewId();
154
+ const isTransitioning = app.isTransitioning();
155
+ ```
156
+
157
+ **❌ Incorrect Usage:**
158
+ ```typescript
159
+ // Don't create NavigationManager directly - this will throw an error!
160
+ const navManager = new NavigationManager(orchestrator); // ERROR!
161
+ ```
162
+
163
+ The NavigationManager uses shared global state and event bus namespaces. Creating multiple instances would cause:
164
+ - Conflicting global state management
165
+ - Event bus namespace collisions
166
+ - Ambiguous navigation routing
167
+
168
+ For this reason, only one instance exists per application, managed by AppBuilder.
169
+
170
+ #### Navigating from Within Components
171
+
172
+ All components have access to the `orchestrator` via `this.orchestrator`, making navigation simple and consistent:
173
+
174
+ ```typescript
175
+ import { Component } from "interactiv";
176
+
177
+ class MyComponent extends Component {
178
+ constructor(id: string, orchestrator: EventOrchestrator) {
179
+ super(id, orchestrator);
180
+ this.setupNavigation();
181
+ }
182
+
183
+ private setupNavigation(): void {
184
+ // Navigate to a view when button is clicked
185
+ this.point(".nav-button", () => {
186
+ this.orchestrator.navigateToView("target-view", {
187
+ type: "slide",
188
+ direction: "left",
189
+ duration: 300
190
+ });
191
+ });
192
+
193
+ // Navigate to a page
194
+ this.point(".page-button", () => {
195
+ this.orchestrator.navigateToPage("target-page", {
196
+ type: "fade",
197
+ duration: 400
198
+ });
199
+ });
200
+ }
201
+
202
+ protected defineTemplate(): void {
203
+ this.template = html`
204
+ <div>
205
+ <button class="nav-button">Go to View</button>
206
+ <button class="page-button">Go to Page</button>
207
+ </div>
208
+ `;
209
+ }
210
+
211
+ protected defineStyles(): void {
212
+ this.styles = css`
213
+ button {
214
+ padding: 1rem;
215
+ margin: 0.5rem;
216
+ }
217
+ `;
218
+ }
219
+ }
220
+ ```
221
+
222
+ **Key Benefits:**
223
+ - Simple and direct API - no need to access event bus manually
224
+ - Consistent mental model: orchestrator coordinates everything
225
+ - Less boilerplate code
226
+ - Type-safe navigation methods
227
+
228
+ ### Event Manager
229
+
230
+ Handle pointer interactions (mouse & touch) in components:
231
+
232
+ ```typescript
233
+ import { EventManager } from "interactiv";
234
+
235
+ const eventManager = new EventManager(shadowRoot, "my-component");
236
+
237
+ // Point interaction (click/tap)
238
+ eventManager.point(".button", (data) => {
239
+ console.log("Clicked at:", data.x, data.y);
240
+ });
241
+
242
+ // Hover interaction
243
+ eventManager.hover(".item", {
244
+ enter: (data) => console.log("Hover enter"),
245
+ leave: (data) => console.log("Hover leave"),
246
+ });
247
+
248
+ // Drag interaction
249
+ eventManager.drag(".draggable", {
250
+ start: (data) => console.log("Drag start"),
251
+ move: (data) => console.log("Dragging"),
252
+ end: (data) => console.log("Drag end"),
253
+ });
254
+ ```
255
+
256
+ ### Settings Manager
257
+
258
+ Create hidden settings pages with corner touch activation. See [SETTINGS_MANAGER.md](./SETTINGS_MANAGER.md) for detailed documentation.
259
+
260
+ ## Template Helpers
261
+
262
+ Use the `html` and `css` tagged template literals for better IDE support:
263
+
264
+ ```typescript
265
+ import { html, css } from "interactiv";
266
+
267
+ const template = html`
268
+ <div class="container">
269
+ <h1>Title</h1>
270
+ </div>
271
+ `;
272
+
273
+ const styles = css`
274
+ .container {
275
+ padding: 1rem;
276
+ }
277
+ `;
278
+ ```
279
+
280
+ ## TypeScript Support
281
+
282
+ This package includes full TypeScript definitions. Import types as needed:
283
+
284
+ ```typescript
285
+ import type {
286
+ PageProps,
287
+ ViewProps,
288
+ ComponentProps,
289
+ PointerEventData,
290
+ DragCallbacks,
291
+ HoverCallbacks,
292
+ SwipeCallbacks,
293
+ StateSubscription,
294
+ } from "interactiv";
295
+ ```
296
+
297
+ ## Development
298
+
299
+ ### Building the Package
300
+
301
+ ```bash
302
+ npm run build
303
+ ```
304
+
305
+ This compiles TypeScript to JavaScript and copies the CSS file to the dist folder.
306
+
307
+ ### Linting and Formatting
308
+
309
+ ```bash
310
+ # Lint
311
+ npm run lint
312
+
313
+ # Format
314
+ npm run format
315
+
316
+ # Check both
317
+ npm run check
318
+ ```
319
+
320
+ ## License
321
+
322
+ ISC
323
+
324
+ ## Contributing
325
+
326
+ Contributions are welcome! Please ensure all code passes linting and formatting checks before submitting.
@@ -0,0 +1,160 @@
1
+ /* Base navigation item styles */
2
+ .nav-item {
3
+ position: absolute;
4
+ top: 0;
5
+ left: 0;
6
+ width: 100%;
7
+ height: 100%;
8
+ overflow: hidden;
9
+ }
10
+
11
+ .nav-item.nav-hidden {
12
+ opacity: 0 !important;
13
+ pointer-events: none;
14
+ }
15
+
16
+ /* Animation base classes */
17
+ .animated {
18
+ /*Defaults: duration of 2s and timing function linear unless otherwise specified.*/
19
+ transition-duration: 2s;
20
+ transition-timing-function: linear;
21
+ }
22
+
23
+ /* Durations */
24
+ .snap {
25
+ transition-duration: 0.5s !important;
26
+ }
27
+
28
+ .fast {
29
+ transition-duration: 1s !important;
30
+ }
31
+
32
+ .slow {
33
+ transition-duration: 5s !important;
34
+ }
35
+
36
+ .gradual {
37
+ transition-duration: 8s !important;
38
+ }
39
+
40
+ /* Snap Navigation - Immediate transitions with no animation */
41
+ .snap-active {
42
+ transition: none !important;
43
+ opacity: 1 !important;
44
+ visibility: visible !important;
45
+ transform: none !important;
46
+ }
47
+
48
+ /* Fade Animations */
49
+ .fade {
50
+ transition: opacity;
51
+ }
52
+
53
+ .fade.out {
54
+ opacity: 0;
55
+ }
56
+
57
+ .fade.in {
58
+ opacity: 1;
59
+ }
60
+
61
+ /* Fade animations for NavigationManager */
62
+ .fade-out {
63
+ opacity: 0;
64
+ }
65
+
66
+ .fade-in {
67
+ opacity: 0;
68
+ }
69
+
70
+ .fade-active {
71
+ opacity: 1;
72
+ }
73
+
74
+ .transition-right {
75
+ transition: left 2000ms linear;
76
+ position: relative !important;
77
+ left: 1000px !important;
78
+ }
79
+
80
+ /* Slide Animations */
81
+ .slide-out-left {
82
+ transform: translateX(-100%);
83
+ }
84
+
85
+ .slide-out-right {
86
+ transform: translateX(100%);
87
+ }
88
+
89
+ .slide-out-up {
90
+ transform: translateY(-100%);
91
+ }
92
+
93
+ .slide-out-down {
94
+ transform: translateY(100%);
95
+ }
96
+
97
+ .slide-in-left {
98
+ transform: translateX(-100%);
99
+ }
100
+
101
+ .slide-in-right {
102
+ transform: translateX(100%);
103
+ }
104
+
105
+ .slide-in-up {
106
+ transform: translateY(-100%);
107
+ }
108
+
109
+ .slide-in-down {
110
+ transform: translateY(100%);
111
+ }
112
+
113
+ .slide-active {
114
+ transform: translateX(0) translateY(0);
115
+ }
116
+
117
+ /* Scale Animations */
118
+ .scale-out {
119
+ transform: scale(0);
120
+ opacity: 0;
121
+ }
122
+
123
+ .scale-in {
124
+ transform: scale(0);
125
+ opacity: 0;
126
+ }
127
+
128
+ .scale-active {
129
+ transform: scale(1);
130
+ opacity: 1;
131
+ }
132
+
133
+ /* Flip Animations */
134
+ .flip-out {
135
+ transform: rotateY(-90deg);
136
+ opacity: 0;
137
+ }
138
+
139
+ .flip-in {
140
+ transform: rotateY(90deg);
141
+ opacity: 0;
142
+ }
143
+
144
+ .flip-active {
145
+ transform: rotateY(0deg);
146
+ opacity: 1;
147
+ }
148
+
149
+ /* Legacy fade classes for backward compatibility */
150
+ .fade {
151
+ transition-property: opacity;
152
+ }
153
+
154
+ .fade.in {
155
+ opacity: 1;
156
+ }
157
+
158
+ .fade.out {
159
+ opacity: 0;
160
+ }
@@ -0,0 +1,20 @@
1
+ import { AppBuilder, Page, View, Component } from "./src/appBuilder";
2
+ import { createOrchestrator, EventOrchestrator, EventBus } from "./src/eventBus";
3
+ import { NavigationManager } from "./src/navigationManager";
4
+ import { ScreensaverManager } from "./src/screensaverManager";
5
+ import { SettingsManager } from "./src/settingsManager";
6
+ import { EventManager } from "./src/eventManager";
7
+ import { stateManager, getGlobalState, setGlobalState, subscribeToGlobalState, useGlobalStateExternal, ExternalStateManager, ComponentStateManager } from "./src/stateManager";
8
+ import type { PointerEventData, DragCallbacks, HoverCallbacks, SwipeCallbacks } from "./src/eventManager";
9
+ import type { StateSubscription } from "./src/stateManager";
10
+ import type { ScreensaverConfig } from "./src/screensaverManager";
11
+ import type { SettingsConfig } from "./src/settingsManager";
12
+ import type { PageProps, ComponentProps, ViewProps } from "./src/types";
13
+ import { css, html } from "./utils/template-helpers";
14
+ import { Logger } from "./utils/logger";
15
+ import { useAnimations, AnimationManager } from "./src/animationBus";
16
+ import { configureLogger } from "./src/logger";
17
+ import { GarbageCollector, createGarbageCollector, type GarbageCollectionStats } from "./src/garbageCollector";
18
+ export { createOrchestrator, EventOrchestrator, EventBus, AppBuilder, Page, View, Component, NavigationManager, ScreensaverManager, SettingsManager, EventManager, AnimationManager, stateManager, getGlobalState, setGlobalState, subscribeToGlobalState, useGlobalStateExternal, useAnimations, ExternalStateManager, ComponentStateManager, GarbageCollector, createGarbageCollector, css, html, Logger, configureLogger, };
19
+ export type { PointerEventData, DragCallbacks, HoverCallbacks, SwipeCallbacks, StateSubscription, ScreensaverConfig, SettingsConfig, PageProps, ViewProps, ComponentProps, GarbageCollectionStats, };
20
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AACjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EACL,YAAY,EACZ,cAAc,EACd,cAAc,EACd,sBAAsB,EACtB,sBAAsB,EACtB,oBAAoB,EACpB,qBAAqB,EACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EACV,gBAAgB,EAChB,aAAa,EACb,cAAc,EACd,cAAc,EACf,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,KAAK,sBAAsB,EAC5B,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,QAAQ,EACR,UAAU,EACV,IAAI,EACJ,IAAI,EACJ,SAAS,EACT,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,YAAY,EACZ,gBAAgB,EAEhB,YAAY,EACZ,cAAc,EACd,cAAc,EACd,sBAAsB,EACtB,sBAAsB,EACtB,aAAa,EACb,oBAAoB,EACpB,qBAAqB,EAErB,gBAAgB,EAChB,sBAAsB,EACtB,GAAG,EACH,IAAI,EACJ,MAAM,EACN,eAAe,GAChB,CAAC;AAEF,YAAY,EACV,gBAAgB,EAChB,aAAa,EACb,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACd,SAAS,EACT,SAAS,EACT,cAAc,EACd,sBAAsB,GACvB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,26 @@
1
+ import { AppBuilder, Page, View, Component } from "./src/appBuilder";
2
+ import { createOrchestrator, EventOrchestrator, EventBus } from "./src/eventBus";
3
+ import { NavigationManager } from "./src/navigationManager";
4
+ import { ScreensaverManager } from "./src/screensaverManager";
5
+ import { SettingsManager } from "./src/settingsManager";
6
+ import { EventManager } from "./src/eventManager";
7
+ import { stateManager, getGlobalState, setGlobalState, subscribeToGlobalState, useGlobalStateExternal, ExternalStateManager, ComponentStateManager, } from "./src/stateManager";
8
+ import { css, html } from "./utils/template-helpers";
9
+ import { Logger } from "./utils/logger";
10
+ import { useAnimations, AnimationManager } from "./src/animationBus";
11
+ import { configureLogger } from "./src/logger";
12
+ import { GarbageCollector, createGarbageCollector, } from "./src/garbageCollector";
13
+ export { createOrchestrator, EventOrchestrator, EventBus, AppBuilder, Page, View, Component, NavigationManager, ScreensaverManager, SettingsManager, EventManager, AnimationManager,
14
+ // External State Management
15
+ stateManager, getGlobalState, setGlobalState, subscribeToGlobalState, useGlobalStateExternal, useAnimations, ExternalStateManager, ComponentStateManager,
16
+ // Garbage Collection
17
+ GarbageCollector, createGarbageCollector, css, html, Logger, configureLogger, };
18
+ // Named exports for direct import
19
+ // export {
20
+ // stateManager,
21
+ // getGlobalState,
22
+ // setGlobalState,
23
+ // subscribeToGlobalState,
24
+ // useGlobalStateExternal,
25
+ // ExternalStateManager,
26
+ // };
@@ -0,0 +1,30 @@
1
+ import { EventOrchestrator } from "./eventBus";
2
+ export declare function useAnimations(orchestrator: EventOrchestrator): AnimationManager;
3
+ export declare class AnimationManager {
4
+ private orchestrator;
5
+ private animationRecords;
6
+ constructor(orchestrator: EventOrchestrator);
7
+ private initAnimationListeners;
8
+ /**
9
+ * Remove an animation from the records array
10
+ * This is called when an animation finishes or is cancelled
11
+ */
12
+ private cleanupAnimation;
13
+ /**
14
+ * Get the current number of active animations
15
+ */
16
+ getActiveAnimationCount(): number;
17
+ /**
18
+ * Clean up stale animation records (animations that are finished but not cleaned up)
19
+ * This is a safety net for any animations that didn't fire their finish/cancel events
20
+ */
21
+ cleanupStaleAnimations(): void;
22
+ }
23
+ export interface AnimationRequest {
24
+ type: string;
25
+ target: HTMLElement;
26
+ keyframes: Keyframe[];
27
+ options: KeyframeAnimationOptions;
28
+ }
29
+ export declare function testAnimation(orchestrator: EventOrchestrator): void;
30
+ //# sourceMappingURL=animationBus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"animationBus.d.ts","sourceRoot":"","sources":["../../src/animationBus.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAKzD,wBAAgB,aAAa,CAAC,YAAY,EAAE,iBAAiB,oBAI5D;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,YAAY,CAAoB;IACxC,OAAO,CAAC,gBAAgB,CAAyB;gBAErC,YAAY,EAAE,iBAAiB;IAU3C,OAAO,CAAC,sBAAsB;IA6E9B;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IAQxB;;OAEG;IACI,uBAAuB,IAAI,MAAM;IAIxC;;;OAGG;IACI,sBAAsB,IAAI,IAAI;CActC;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,CAAC;IACpB,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,OAAO,EAAE,wBAAwB,CAAC;CACnC;AASD,wBAAgB,aAAa,CAAC,YAAY,EAAE,iBAAiB,QAQ5D"}
@@ -0,0 +1,125 @@
1
+ import { logger } from "./logger";
2
+ import { generateGUID } from "../utils/generateGuid";
3
+ const log = logger;
4
+ export function useAnimations(orchestrator) {
5
+ // Initialize the AnimationManager
6
+ const animationManager = new AnimationManager(orchestrator);
7
+ return animationManager;
8
+ }
9
+ export class AnimationManager {
10
+ orchestrator;
11
+ animationRecords = [];
12
+ constructor(orchestrator) {
13
+ this.orchestrator = orchestrator;
14
+ this.orchestrator.registerEventBus("animate");
15
+ const animationBus = this.orchestrator.getEventBus("animate");
16
+ if (animationBus) {
17
+ this.initAnimationListeners(animationBus);
18
+ }
19
+ }
20
+ initAnimationListeners(animationBus) {
21
+ const me = this;
22
+ animationBus.on("add", (e) => {
23
+ const animationRecord = {
24
+ id: generateGUID(),
25
+ request: e.detail,
26
+ };
27
+ me.animationRecords.push(animationRecord);
28
+ // Auto-start the animation immediately after adding it
29
+ const { keyframes, options, target } = animationRecord.request;
30
+ animationRecord.animation = target.animate(keyframes, options);
31
+ // Add cleanup listeners for when animation completes
32
+ animationRecord.animation.addEventListener("finish", () => {
33
+ me.cleanupAnimation(animationRecord.id);
34
+ log.trace(`Animation finished and cleaned up: ${animationRecord.id} (${animationRecord.request.type})`);
35
+ });
36
+ animationRecord.animation.addEventListener("cancel", () => {
37
+ me.cleanupAnimation(animationRecord.id);
38
+ log.trace(`Animation cancelled and cleaned up: ${animationRecord.id} (${animationRecord.request.type})`);
39
+ });
40
+ log.trace(`Animation started: ${animationRecord.id} (${animationRecord.request.type})`);
41
+ });
42
+ animationBus.on("start", (e) => {
43
+ const animationRecord = me.animationRecords.filter((a) => a.id === e.detail)[0];
44
+ if (animationRecord && !animationRecord.animation) {
45
+ const { keyframes, options, target } = animationRecord.request;
46
+ animationRecord.animation = target.animate(keyframes, options);
47
+ log.trace(`Animation manually started: ${animationRecord.id}`);
48
+ }
49
+ });
50
+ animationBus.on("stop", (e) => {
51
+ if (e.detail === "all") {
52
+ for (const animationRecord of me.animationRecords) {
53
+ if (animationRecord.animation instanceof Animation) {
54
+ animationRecord.animation.cancel();
55
+ log.trace(`Animation stopped: ${animationRecord.id}`);
56
+ }
57
+ }
58
+ // Clean up all animations after cancelling
59
+ me.animationRecords = [];
60
+ }
61
+ else {
62
+ const animationRecord = me.animationRecords.filter((a) => a.id === e.detail)[0];
63
+ if (animationRecord?.animation) {
64
+ animationRecord.animation.cancel();
65
+ log.trace(`Animation stopped: ${animationRecord.id}`);
66
+ }
67
+ // Animation will be cleaned up by the cancel event listener
68
+ }
69
+ });
70
+ animationBus.on("stopGroup", (e) => {
71
+ const groupAnimations = me.animationRecords.filter((ar) => ar.group === e.detail);
72
+ for (const a of groupAnimations) {
73
+ if (a.animation) {
74
+ a.animation.cancel();
75
+ log.trace(`Group animation stopped: ${a.id} (group: ${e.detail})`);
76
+ }
77
+ }
78
+ // Animations will be cleaned up by their cancel event listeners
79
+ });
80
+ }
81
+ /**
82
+ * Remove an animation from the records array
83
+ * This is called when an animation finishes or is cancelled
84
+ */
85
+ cleanupAnimation(animationId) {
86
+ const index = this.animationRecords.findIndex((a) => a.id === animationId);
87
+ if (index !== -1) {
88
+ this.animationRecords.splice(index, 1);
89
+ log.trace(`Animation record removed. Total active animations: ${this.animationRecords.length}`);
90
+ }
91
+ }
92
+ /**
93
+ * Get the current number of active animations
94
+ */
95
+ getActiveAnimationCount() {
96
+ return this.animationRecords.length;
97
+ }
98
+ /**
99
+ * Clean up stale animation records (animations that are finished but not cleaned up)
100
+ * This is a safety net for any animations that didn't fire their finish/cancel events
101
+ */
102
+ cleanupStaleAnimations() {
103
+ const beforeCount = this.animationRecords.length;
104
+ this.animationRecords = this.animationRecords.filter((record) => {
105
+ if (record.animation) {
106
+ const playState = record.animation.playState;
107
+ return playState !== "finished" && playState !== "idle";
108
+ }
109
+ return false; // Remove records without animation reference
110
+ });
111
+ const removedCount = beforeCount - this.animationRecords.length;
112
+ if (removedCount > 0) {
113
+ log.trace(`Cleaned up ${removedCount} stale animation records. Remaining: ${this.animationRecords.length}`);
114
+ }
115
+ }
116
+ }
117
+ export function testAnimation(orchestrator) {
118
+ const div = document.createElement("div");
119
+ orchestrator.enqueue("transition", "animate", "animation", {
120
+ type: "fade",
121
+ style: "linear",
122
+ duration: 1000,
123
+ target: div,
124
+ });
125
+ }