@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.
- package/README.md +326 -0
- package/dist/animations.css +160 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/src/animationBus.d.ts +30 -0
- package/dist/src/animationBus.d.ts.map +1 -0
- package/dist/src/animationBus.js +125 -0
- package/dist/src/appBuilder.d.ts +173 -0
- package/dist/src/appBuilder.d.ts.map +1 -0
- package/dist/src/appBuilder.js +957 -0
- package/dist/src/eventBus.d.ts +100 -0
- package/dist/src/eventBus.d.ts.map +1 -0
- package/dist/src/eventBus.js +326 -0
- package/dist/src/eventManager.d.ts +87 -0
- package/dist/src/eventManager.d.ts.map +1 -0
- package/dist/src/eventManager.js +455 -0
- package/dist/src/garbageCollector.d.ts +68 -0
- package/dist/src/garbageCollector.d.ts.map +1 -0
- package/dist/src/garbageCollector.js +169 -0
- package/dist/src/logger.d.ts +11 -0
- package/dist/src/logger.d.ts.map +1 -0
- package/dist/src/logger.js +15 -0
- package/dist/src/navigationManager.d.ts +105 -0
- package/dist/src/navigationManager.d.ts.map +1 -0
- package/dist/src/navigationManager.js +533 -0
- package/dist/src/screensaverManager.d.ts +66 -0
- package/dist/src/screensaverManager.d.ts.map +1 -0
- package/dist/src/screensaverManager.js +417 -0
- package/dist/src/settingsManager.d.ts +48 -0
- package/dist/src/settingsManager.d.ts.map +1 -0
- package/dist/src/settingsManager.js +317 -0
- package/dist/src/stateManager.d.ts +58 -0
- package/dist/src/stateManager.d.ts.map +1 -0
- package/dist/src/stateManager.js +278 -0
- package/dist/src/types.d.ts +32 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +1 -0
- package/dist/utils/generateGuid.d.ts +2 -0
- package/dist/utils/generateGuid.d.ts.map +1 -0
- package/dist/utils/generateGuid.js +19 -0
- package/dist/utils/logger.d.ts +9 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +42 -0
- package/dist/utils/template-helpers.d.ts +32 -0
- package/dist/utils/template-helpers.d.ts.map +1 -0
- package/dist/utils/template-helpers.js +24 -0
- 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
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -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
|
+
}
|