@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
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { stateManager } from "./stateManager";
|
|
2
|
+
import { logger } from "./logger";
|
|
3
|
+
const log = logger;
|
|
4
|
+
export class SettingsManager {
|
|
5
|
+
eventBus;
|
|
6
|
+
orchestrator;
|
|
7
|
+
navigationManager;
|
|
8
|
+
config = null;
|
|
9
|
+
isSettingsActive = false;
|
|
10
|
+
lastActiveViewId = null;
|
|
11
|
+
touchSequenceState = {
|
|
12
|
+
step: 0,
|
|
13
|
+
lastTouchTime: 0,
|
|
14
|
+
};
|
|
15
|
+
lastEventTime = 0; // Track last event to prevent duplicates
|
|
16
|
+
touchListeners = [];
|
|
17
|
+
// Default configuration values
|
|
18
|
+
DEFAULT_CORNER_RADIUS = 100;
|
|
19
|
+
DEFAULT_TOUCH_TIMEOUT = 3000;
|
|
20
|
+
EVENT_DEBOUNCE_MS = 50; // Ignore events within 50ms of each other
|
|
21
|
+
constructor(orchestrator, navigationManager) {
|
|
22
|
+
this.orchestrator = orchestrator;
|
|
23
|
+
this.navigationManager = navigationManager;
|
|
24
|
+
this.eventBus = orchestrator.registerEventBus("settings-manager");
|
|
25
|
+
this.setupEventListeners();
|
|
26
|
+
this.initializeGlobalState();
|
|
27
|
+
}
|
|
28
|
+
initializeGlobalState() {
|
|
29
|
+
if (!stateManager.has("settings.isActive")) {
|
|
30
|
+
stateManager.set("settings.isActive", false);
|
|
31
|
+
}
|
|
32
|
+
if (!stateManager.has("settings.lastActiveViewId")) {
|
|
33
|
+
stateManager.set("settings.lastActiveViewId", null);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
setupEventListeners() {
|
|
37
|
+
this.eventBus.on("register-settings", (e) => {
|
|
38
|
+
const { config } = e.detail;
|
|
39
|
+
this.registerSettings(config);
|
|
40
|
+
});
|
|
41
|
+
this.eventBus.on("activate-settings", () => {
|
|
42
|
+
this.activateSettings();
|
|
43
|
+
});
|
|
44
|
+
this.eventBus.on("deactivate-settings", () => {
|
|
45
|
+
this.deactivateSettings();
|
|
46
|
+
});
|
|
47
|
+
// Listen for navigation changes to track active views
|
|
48
|
+
const navBus = this.orchestrator.getEventBus("navigation-manager");
|
|
49
|
+
if (navBus) {
|
|
50
|
+
navBus.on("view-changed", (e) => {
|
|
51
|
+
const { newViewId } = e.detail;
|
|
52
|
+
if (!this.isSettingsActive && newViewId !== this.config?.view.componentId) {
|
|
53
|
+
this.lastActiveViewId = newViewId;
|
|
54
|
+
stateManager.set("settings.lastActiveViewId", newViewId);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
registerSettings(config) {
|
|
60
|
+
this.validateConfig(config);
|
|
61
|
+
// Clean up existing settings if any
|
|
62
|
+
if (this.config) {
|
|
63
|
+
this.cleanup();
|
|
64
|
+
}
|
|
65
|
+
this.config = {
|
|
66
|
+
...config,
|
|
67
|
+
exitBehavior: config.exitBehavior || "reset",
|
|
68
|
+
cornerTouchRadius: config.cornerTouchRadius || this.DEFAULT_CORNER_RADIUS,
|
|
69
|
+
touchTimeout: config.touchTimeout || this.DEFAULT_TOUCH_TIMEOUT,
|
|
70
|
+
debugMode: config.debugMode || false,
|
|
71
|
+
transitionConfig: config.transitionConfig || {
|
|
72
|
+
type: "fade",
|
|
73
|
+
duration: 500,
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
// Register the settings view with navigation manager
|
|
77
|
+
const navBus = this.orchestrator.getEventBus("navigation-manager");
|
|
78
|
+
if (navBus) {
|
|
79
|
+
navBus.emit("register-view", { view: this.config.view });
|
|
80
|
+
}
|
|
81
|
+
this.setupCornerTouchListeners();
|
|
82
|
+
this.resetTouchSequence();
|
|
83
|
+
log.trace(`Settings registered with corner touch activation (radius: ${this.config.cornerTouchRadius}px, timeout: ${this.config.touchTimeout}ms)`);
|
|
84
|
+
}
|
|
85
|
+
validateConfig(config) {
|
|
86
|
+
if (!config.view) {
|
|
87
|
+
throw new Error("Settings view is required");
|
|
88
|
+
}
|
|
89
|
+
if (config.exitBehavior === "reset" && !config.startingViewId) {
|
|
90
|
+
throw new Error('startingViewId is required when exitBehavior is "reset"');
|
|
91
|
+
}
|
|
92
|
+
if (config.exitBehavior && !["reset", "return"].includes(config.exitBehavior)) {
|
|
93
|
+
throw new Error('exitBehavior must be either "reset" or "return"');
|
|
94
|
+
}
|
|
95
|
+
if (config.cornerTouchRadius && config.cornerTouchRadius <= 0) {
|
|
96
|
+
throw new Error("cornerTouchRadius must be greater than 0");
|
|
97
|
+
}
|
|
98
|
+
if (config.touchTimeout && config.touchTimeout <= 0) {
|
|
99
|
+
throw new Error("touchTimeout must be greater than 0");
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
setupCornerTouchListeners() {
|
|
103
|
+
if (!this.config)
|
|
104
|
+
return;
|
|
105
|
+
const touchHandler = (event) => {
|
|
106
|
+
if (event instanceof TouchEvent || event instanceof MouseEvent) {
|
|
107
|
+
this.handleTouchAttempt(event);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
// Add listeners for both touch and mouse events
|
|
111
|
+
const eventTypes = ["touchstart"];
|
|
112
|
+
eventTypes.forEach((eventType) => {
|
|
113
|
+
const listener = touchHandler.bind(this);
|
|
114
|
+
document.addEventListener(eventType, listener, { passive: true });
|
|
115
|
+
this.touchListeners.push({
|
|
116
|
+
element: document,
|
|
117
|
+
type: eventType,
|
|
118
|
+
listener,
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
handleTouchAttempt(event) {
|
|
123
|
+
if (!this.config)
|
|
124
|
+
return;
|
|
125
|
+
const now = Date.now();
|
|
126
|
+
// Prevent duplicate events (touchstart + mousedown on same interaction)
|
|
127
|
+
if (now - this.lastEventTime < this.EVENT_DEBOUNCE_MS) {
|
|
128
|
+
log.trace("Ignoring duplicate event within debounce window");
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
this.lastEventTime = now;
|
|
132
|
+
// Get touch/click coordinates
|
|
133
|
+
let x, y;
|
|
134
|
+
if (event instanceof TouchEvent && event.touches.length > 0) {
|
|
135
|
+
x = event.touches[0].clientX;
|
|
136
|
+
y = event.touches[0].clientY;
|
|
137
|
+
}
|
|
138
|
+
else if (event instanceof MouseEvent) {
|
|
139
|
+
x = event.clientX;
|
|
140
|
+
y = event.clientY;
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const timeSinceLastTouch = now - this.touchSequenceState.lastTouchTime;
|
|
146
|
+
// Check if timeout exceeded
|
|
147
|
+
if (this.touchSequenceState.step > 0 && timeSinceLastTouch > this.config.touchTimeout) {
|
|
148
|
+
log.trace("Touch sequence timed out, resetting");
|
|
149
|
+
this.resetTouchSequence();
|
|
150
|
+
}
|
|
151
|
+
const cornerDetected = this.detectCornerTouch(x, y);
|
|
152
|
+
if (cornerDetected === null) {
|
|
153
|
+
// Touch outside corner zones - reset sequence
|
|
154
|
+
if (this.touchSequenceState.step > 0) {
|
|
155
|
+
log.trace("Touch outside corner zones, resetting sequence");
|
|
156
|
+
this.resetTouchSequence();
|
|
157
|
+
}
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
// Check if the correct corner was touched based on current step
|
|
161
|
+
const expectedCorner = this.getExpectedCorner();
|
|
162
|
+
if (cornerDetected === expectedCorner) {
|
|
163
|
+
log.trace(`Correct corner touched: ${cornerDetected} (step ${this.touchSequenceState.step + 1}/3)`);
|
|
164
|
+
this.touchSequenceState.step++;
|
|
165
|
+
this.touchSequenceState.lastTouchTime = now;
|
|
166
|
+
// Check if sequence is complete
|
|
167
|
+
if (this.touchSequenceState.step === 3) {
|
|
168
|
+
log.trace("Touch sequence complete! Activating settings...");
|
|
169
|
+
this.activateSettings();
|
|
170
|
+
this.resetTouchSequence();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
// Wrong corner touched - reset sequence
|
|
175
|
+
log.trace(`Wrong corner touched: expected ${expectedCorner}, got ${cornerDetected}. Resetting sequence.`);
|
|
176
|
+
this.resetTouchSequence();
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
detectCornerTouch(x, y) {
|
|
180
|
+
if (!this.config)
|
|
181
|
+
return null;
|
|
182
|
+
const radius = this.config.cornerTouchRadius;
|
|
183
|
+
const width = window.innerWidth;
|
|
184
|
+
const height = window.innerHeight;
|
|
185
|
+
// Top-left corner
|
|
186
|
+
if (x < radius && y < radius) {
|
|
187
|
+
log.trace("top-left touch", [x, y, radius]);
|
|
188
|
+
return "top-left";
|
|
189
|
+
}
|
|
190
|
+
// Top-right corner
|
|
191
|
+
if (x > width - radius && y < radius) {
|
|
192
|
+
log.trace("top-right touch");
|
|
193
|
+
return "top-right";
|
|
194
|
+
}
|
|
195
|
+
// Bottom-right corner
|
|
196
|
+
if (x > width - radius && y > height - radius) {
|
|
197
|
+
log.trace("bottom-right touch");
|
|
198
|
+
return "bottom-right";
|
|
199
|
+
}
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
getExpectedCorner() {
|
|
203
|
+
switch (this.touchSequenceState.step) {
|
|
204
|
+
case 0:
|
|
205
|
+
return "top-left";
|
|
206
|
+
case 1:
|
|
207
|
+
return "top-right";
|
|
208
|
+
case 2:
|
|
209
|
+
return "bottom-right";
|
|
210
|
+
default:
|
|
211
|
+
return "top-left";
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
resetTouchSequence() {
|
|
215
|
+
this.touchSequenceState = {
|
|
216
|
+
step: 0,
|
|
217
|
+
lastTouchTime: 0,
|
|
218
|
+
};
|
|
219
|
+
this.lastEventTime = 0; // Reset debounce timer as well
|
|
220
|
+
}
|
|
221
|
+
async activateSettings() {
|
|
222
|
+
if (!this.config || this.isSettingsActive)
|
|
223
|
+
return;
|
|
224
|
+
log.trace("Activating settings");
|
|
225
|
+
// Store the current view before switching to settings
|
|
226
|
+
const currentViewId = this.navigationManager.getCurrentViewId();
|
|
227
|
+
if (currentViewId && currentViewId !== this.config.view.componentId) {
|
|
228
|
+
this.lastActiveViewId = currentViewId;
|
|
229
|
+
stateManager.set("settings.lastActiveViewId", currentViewId);
|
|
230
|
+
}
|
|
231
|
+
this.isSettingsActive = true;
|
|
232
|
+
stateManager.set("settings.isActive", true);
|
|
233
|
+
try {
|
|
234
|
+
await this.navigationManager.navigateToView(this.config.view.componentId, this.config.transitionConfig);
|
|
235
|
+
this.eventBus.emit("settings-activated", {
|
|
236
|
+
viewId: this.config.view.componentId,
|
|
237
|
+
previousViewId: this.lastActiveViewId,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
catch (error) {
|
|
241
|
+
log.error("Failed to activate settings:", error);
|
|
242
|
+
this.isSettingsActive = false;
|
|
243
|
+
stateManager.set("settings.isActive", false);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
async handleSettingsExit() {
|
|
247
|
+
if (!this.config || !this.isSettingsActive)
|
|
248
|
+
return;
|
|
249
|
+
log.trace(`Exiting settings with '${this.config.exitBehavior}' behavior`);
|
|
250
|
+
this.isSettingsActive = false;
|
|
251
|
+
stateManager.set("settings.isActive", false);
|
|
252
|
+
let targetViewId = null;
|
|
253
|
+
if (this.config.exitBehavior === "return" && this.lastActiveViewId) {
|
|
254
|
+
targetViewId = this.lastActiveViewId;
|
|
255
|
+
}
|
|
256
|
+
else if (this.config.exitBehavior === "reset" && this.config.startingViewId) {
|
|
257
|
+
targetViewId = this.config.startingViewId;
|
|
258
|
+
}
|
|
259
|
+
if (targetViewId) {
|
|
260
|
+
try {
|
|
261
|
+
await this.navigationManager.navigateToView(targetViewId, this.config.transitionConfig);
|
|
262
|
+
this.eventBus.emit("settings-deactivated", {
|
|
263
|
+
targetViewId,
|
|
264
|
+
exitBehavior: this.config.exitBehavior,
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
catch (error) {
|
|
268
|
+
log.error("Failed to exit settings:", error);
|
|
269
|
+
// Reset state on error
|
|
270
|
+
this.isSettingsActive = true;
|
|
271
|
+
stateManager.set("settings.isActive", true);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
log.warn("No target view available for settings exit");
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
async deactivateSettings() {
|
|
279
|
+
await this.handleSettingsExit();
|
|
280
|
+
}
|
|
281
|
+
isActive() {
|
|
282
|
+
return this.isSettingsActive;
|
|
283
|
+
}
|
|
284
|
+
getCurrentConfig() {
|
|
285
|
+
return this.config;
|
|
286
|
+
}
|
|
287
|
+
getLastActiveViewId() {
|
|
288
|
+
return this.lastActiveViewId;
|
|
289
|
+
}
|
|
290
|
+
// Manual control methods
|
|
291
|
+
forceActivate() {
|
|
292
|
+
this.resetTouchSequence();
|
|
293
|
+
this.activateSettings();
|
|
294
|
+
}
|
|
295
|
+
forceDeactivate() {
|
|
296
|
+
this.deactivateSettings();
|
|
297
|
+
}
|
|
298
|
+
resetSequence() {
|
|
299
|
+
this.resetTouchSequence();
|
|
300
|
+
}
|
|
301
|
+
cleanup() {
|
|
302
|
+
// Remove all touch listeners
|
|
303
|
+
this.touchListeners.forEach(({ element, type, listener }) => {
|
|
304
|
+
element.removeEventListener(type, listener);
|
|
305
|
+
});
|
|
306
|
+
this.touchListeners.length = 0;
|
|
307
|
+
this.isSettingsActive = false;
|
|
308
|
+
stateManager.set("settings.isActive", false);
|
|
309
|
+
this.resetTouchSequence();
|
|
310
|
+
}
|
|
311
|
+
destroy() {
|
|
312
|
+
this.cleanup();
|
|
313
|
+
this.config = null;
|
|
314
|
+
this.lastActiveViewId = null;
|
|
315
|
+
log.trace("SettingsManager destroyed");
|
|
316
|
+
}
|
|
317
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
interface StoreParams {
|
|
2
|
+
mutations?: object;
|
|
3
|
+
state?: Store;
|
|
4
|
+
status?: string;
|
|
5
|
+
events?: object;
|
|
6
|
+
}
|
|
7
|
+
export interface StateSubscription {
|
|
8
|
+
unsubscribe: () => void;
|
|
9
|
+
}
|
|
10
|
+
export declare class Store {
|
|
11
|
+
private mutations;
|
|
12
|
+
private state;
|
|
13
|
+
private status;
|
|
14
|
+
private events;
|
|
15
|
+
private stateSubscriptions;
|
|
16
|
+
constructor(params: StoreParams);
|
|
17
|
+
getStateObject(): any;
|
|
18
|
+
getStateValue(key: string): any;
|
|
19
|
+
setStateValue(key: string, value: any): void;
|
|
20
|
+
subscribeToKey(key: string, callback: (value: any, key: string) => void): StateSubscription;
|
|
21
|
+
mutateStore(mutationKey: string, payload: any): boolean;
|
|
22
|
+
}
|
|
23
|
+
declare const globalStore: Store;
|
|
24
|
+
export declare class ComponentStateManager {
|
|
25
|
+
private localState;
|
|
26
|
+
private stateSubscriptions;
|
|
27
|
+
private onStateChange;
|
|
28
|
+
private stateProxy;
|
|
29
|
+
constructor(_: string, onStateChange: (key: string, value: any, isLocal: boolean) => void);
|
|
30
|
+
private createStateProxy;
|
|
31
|
+
getStateProxy(): any;
|
|
32
|
+
defineState(initialState: Record<string, any>): void;
|
|
33
|
+
useState<T>(key: string, initialValue: T): [T, (newValue: T) => void];
|
|
34
|
+
useGlobalState<T>(key: string, initialValue?: T): [T, (newValue: T) => void];
|
|
35
|
+
getLocalState(key: string): any;
|
|
36
|
+
destroy(): void;
|
|
37
|
+
}
|
|
38
|
+
export declare function createLocalStateStore(initialValue: any): any[];
|
|
39
|
+
export declare function getGlobalState(key: string): any;
|
|
40
|
+
export declare function setGlobalState(key: string, value: any): void;
|
|
41
|
+
export declare function subscribeToGlobalState(key: string, callback: (value: any, key: string) => void): StateSubscription;
|
|
42
|
+
export declare function useGlobalStateExternal<T>(key: string, initialValue?: T): [T, (newValue: T) => void];
|
|
43
|
+
export declare class ExternalStateManager {
|
|
44
|
+
private static instance;
|
|
45
|
+
private constructor();
|
|
46
|
+
static getInstance(): ExternalStateManager;
|
|
47
|
+
get<T>(key: string): T;
|
|
48
|
+
set<T>(key: string, value: T): void;
|
|
49
|
+
useState<T>(key: string, initialValue?: T): [T, (newValue: T) => void];
|
|
50
|
+
subscribe(key: string, callback: (value: any, key: string) => void): StateSubscription;
|
|
51
|
+
getAll(): any;
|
|
52
|
+
has(key: string): boolean;
|
|
53
|
+
remove(key: string): void;
|
|
54
|
+
clear(): void;
|
|
55
|
+
}
|
|
56
|
+
export declare const stateManager: ExternalStateManager;
|
|
57
|
+
export { globalStore };
|
|
58
|
+
//# sourceMappingURL=stateManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stateManager.d.ts","sourceRoot":"","sources":["../../src/stateManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/* eslint-disable no-prototype-builtins */
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-this-alias */
|
|
3
|
+
import { logger } from "./logger";
|
|
4
|
+
const log = logger;
|
|
5
|
+
class State {
|
|
6
|
+
events;
|
|
7
|
+
constructor() {
|
|
8
|
+
this.events = {};
|
|
9
|
+
}
|
|
10
|
+
subscribe(event, callback) {
|
|
11
|
+
const me = this;
|
|
12
|
+
if (!me.events.hasOwnProperty(event)) {
|
|
13
|
+
me.events[event] = [];
|
|
14
|
+
}
|
|
15
|
+
return me.events[event].push(callback);
|
|
16
|
+
}
|
|
17
|
+
publish(event, data = {}) {
|
|
18
|
+
const me = this;
|
|
19
|
+
if (!me.events.hasOwnProperty(event)) {
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
22
|
+
return me.events[event].map((callback) => callback(data));
|
|
23
|
+
}
|
|
24
|
+
unsubscribe(event, callbackIndex) {
|
|
25
|
+
if (this.events.hasOwnProperty(event) && this.events[event][callbackIndex - 1]) {
|
|
26
|
+
this.events[event].splice(callbackIndex - 1, 1);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export class Store {
|
|
31
|
+
mutations;
|
|
32
|
+
state;
|
|
33
|
+
status;
|
|
34
|
+
events;
|
|
35
|
+
stateSubscriptions = new Map();
|
|
36
|
+
constructor(params) {
|
|
37
|
+
const me = this;
|
|
38
|
+
if (params.hasOwnProperty("mutations")) {
|
|
39
|
+
this.mutations = params.mutations;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
this.mutations = {};
|
|
43
|
+
}
|
|
44
|
+
this.state = new Proxy(params.state || {}, {
|
|
45
|
+
set: function (state, key, value) {
|
|
46
|
+
const oldValue = state[key];
|
|
47
|
+
state[key] = value;
|
|
48
|
+
// Notify specific key subscribers
|
|
49
|
+
if (me.stateSubscriptions.has(key)) {
|
|
50
|
+
me.stateSubscriptions.get(key).forEach((callback) => {
|
|
51
|
+
callback(value, key);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
// Notify general state change listeners
|
|
55
|
+
me.events.publish("stateChange", { key, value, oldValue, state: me.state });
|
|
56
|
+
if (me.status !== "mutation") {
|
|
57
|
+
log.warn(`You should use a mutation to set the value for state object ${key}.`);
|
|
58
|
+
}
|
|
59
|
+
me.status = "resting";
|
|
60
|
+
return true;
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
this.status = "resting";
|
|
64
|
+
this.events = new State();
|
|
65
|
+
}
|
|
66
|
+
getStateObject() {
|
|
67
|
+
return this.state;
|
|
68
|
+
}
|
|
69
|
+
getStateValue(key) {
|
|
70
|
+
return this.state[key];
|
|
71
|
+
}
|
|
72
|
+
setStateValue(key, value) {
|
|
73
|
+
this.status = "mutation";
|
|
74
|
+
this.state[key] = value;
|
|
75
|
+
}
|
|
76
|
+
subscribeToKey(key, callback) {
|
|
77
|
+
if (!this.stateSubscriptions.has(key)) {
|
|
78
|
+
this.stateSubscriptions.set(key, new Set());
|
|
79
|
+
}
|
|
80
|
+
this.stateSubscriptions.get(key).add(callback);
|
|
81
|
+
return {
|
|
82
|
+
unsubscribe: () => {
|
|
83
|
+
const subscribers = this.stateSubscriptions.get(key);
|
|
84
|
+
if (subscribers) {
|
|
85
|
+
subscribers.delete(callback);
|
|
86
|
+
if (subscribers.size === 0) {
|
|
87
|
+
this.stateSubscriptions.delete(key);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
mutateStore(mutationKey, payload) {
|
|
94
|
+
const me = this;
|
|
95
|
+
if (typeof me.mutations[mutationKey] !== "function") {
|
|
96
|
+
log.warn(`Mutation with identifier ${mutationKey} does not exist`);
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
me.status = "mutation";
|
|
100
|
+
const newState = me.mutations[mutationKey](me.state, payload);
|
|
101
|
+
me.state = Object.assign(me.state, newState);
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Global store instance
|
|
106
|
+
const globalStore = new Store({});
|
|
107
|
+
// Component-level state management
|
|
108
|
+
export class ComponentStateManager {
|
|
109
|
+
localState = new Map();
|
|
110
|
+
stateSubscriptions = new Map();
|
|
111
|
+
onStateChange;
|
|
112
|
+
stateProxy = null;
|
|
113
|
+
constructor(_, onStateChange) {
|
|
114
|
+
this.onStateChange = onStateChange;
|
|
115
|
+
this.createStateProxy();
|
|
116
|
+
}
|
|
117
|
+
createStateProxy() {
|
|
118
|
+
this.stateProxy = new Proxy({}, {
|
|
119
|
+
get: (_, key) => {
|
|
120
|
+
return this.localState.get(key);
|
|
121
|
+
},
|
|
122
|
+
set: (_, key, value) => {
|
|
123
|
+
const oldValue = this.localState.get(key);
|
|
124
|
+
if (oldValue !== value) {
|
|
125
|
+
this.localState.set(key, value);
|
|
126
|
+
this.onStateChange(key, value, true);
|
|
127
|
+
}
|
|
128
|
+
return true;
|
|
129
|
+
},
|
|
130
|
+
has: (_, key) => {
|
|
131
|
+
return this.localState.has(key);
|
|
132
|
+
},
|
|
133
|
+
ownKeys: (_) => {
|
|
134
|
+
return Array.from(this.localState.keys());
|
|
135
|
+
},
|
|
136
|
+
getOwnPropertyDescriptor: (_, key) => {
|
|
137
|
+
if (this.localState.has(key)) {
|
|
138
|
+
return {
|
|
139
|
+
enumerable: true,
|
|
140
|
+
configurable: true,
|
|
141
|
+
value: this.localState.get(key),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
return undefined;
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
getStateProxy() {
|
|
149
|
+
return this.stateProxy;
|
|
150
|
+
}
|
|
151
|
+
defineState(initialState) {
|
|
152
|
+
Object.entries(initialState).forEach(([key, value]) => {
|
|
153
|
+
if (!this.localState.has(key)) {
|
|
154
|
+
this.localState.set(key, value);
|
|
155
|
+
// Trigger initial state change to sync with component properties
|
|
156
|
+
this.onStateChange(key, value, true);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
// Local state management
|
|
161
|
+
useState(key, initialValue) {
|
|
162
|
+
// Initialize if not exists
|
|
163
|
+
if (!this.localState.has(key)) {
|
|
164
|
+
this.localState.set(key, initialValue);
|
|
165
|
+
}
|
|
166
|
+
const currentValue = this.localState.get(key);
|
|
167
|
+
const setter = (newValue) => {
|
|
168
|
+
const oldValue = this.localState.get(key);
|
|
169
|
+
if (oldValue !== newValue) {
|
|
170
|
+
this.localState.set(key, newValue);
|
|
171
|
+
this.onStateChange(key, newValue, true);
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
return [currentValue, setter];
|
|
175
|
+
}
|
|
176
|
+
// Global state management
|
|
177
|
+
useGlobalState(key, initialValue) {
|
|
178
|
+
// Initialize global state if provided and doesn't exist
|
|
179
|
+
if (initialValue !== undefined && globalStore.getStateValue(key) === undefined) {
|
|
180
|
+
globalStore.setStateValue(key, initialValue);
|
|
181
|
+
}
|
|
182
|
+
const currentValue = globalStore.getStateValue(key);
|
|
183
|
+
// Subscribe to global state changes if not already subscribed
|
|
184
|
+
if (!this.stateSubscriptions.has(key)) {
|
|
185
|
+
const subscription = globalStore.subscribeToKey(key, (value) => {
|
|
186
|
+
this.onStateChange(key, value, false);
|
|
187
|
+
});
|
|
188
|
+
this.stateSubscriptions.set(key, subscription);
|
|
189
|
+
}
|
|
190
|
+
const setter = (newValue) => {
|
|
191
|
+
globalStore.setStateValue(key, newValue);
|
|
192
|
+
};
|
|
193
|
+
return [currentValue, setter];
|
|
194
|
+
}
|
|
195
|
+
getLocalState(key) {
|
|
196
|
+
return this.localState.get(key);
|
|
197
|
+
}
|
|
198
|
+
destroy() {
|
|
199
|
+
// Clean up all global state subscriptions
|
|
200
|
+
this.stateSubscriptions.forEach((subscription) => {
|
|
201
|
+
subscription.unsubscribe();
|
|
202
|
+
});
|
|
203
|
+
this.stateSubscriptions.clear();
|
|
204
|
+
this.localState.clear();
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
export function createLocalStateStore(initialValue) {
|
|
208
|
+
const id = crypto.randomUUID();
|
|
209
|
+
const setter = (newValue) => {
|
|
210
|
+
globalStore.setStateValue(id, newValue);
|
|
211
|
+
};
|
|
212
|
+
if (initialValue !== null) {
|
|
213
|
+
globalStore.setStateValue(id, initialValue);
|
|
214
|
+
}
|
|
215
|
+
return [globalStore.getStateValue(id), setter];
|
|
216
|
+
}
|
|
217
|
+
// External State Management Utilities
|
|
218
|
+
export function getGlobalState(key) {
|
|
219
|
+
return globalStore.getStateValue(key);
|
|
220
|
+
}
|
|
221
|
+
export function setGlobalState(key, value) {
|
|
222
|
+
globalStore.setStateValue(key, value);
|
|
223
|
+
}
|
|
224
|
+
export function subscribeToGlobalState(key, callback) {
|
|
225
|
+
return globalStore.subscribeToKey(key, callback);
|
|
226
|
+
}
|
|
227
|
+
export function useGlobalStateExternal(key, initialValue) {
|
|
228
|
+
// Initialize if provided and doesn't exist
|
|
229
|
+
if (initialValue !== undefined && globalStore.getStateValue(key) === undefined) {
|
|
230
|
+
globalStore.setStateValue(key, initialValue);
|
|
231
|
+
}
|
|
232
|
+
const currentValue = globalStore.getStateValue(key);
|
|
233
|
+
const setter = (newValue) => {
|
|
234
|
+
globalStore.setStateValue(key, newValue);
|
|
235
|
+
};
|
|
236
|
+
return [currentValue, setter];
|
|
237
|
+
}
|
|
238
|
+
// State Manager Singleton for external access
|
|
239
|
+
export class ExternalStateManager {
|
|
240
|
+
static instance;
|
|
241
|
+
constructor() { }
|
|
242
|
+
static getInstance() {
|
|
243
|
+
if (!ExternalStateManager.instance) {
|
|
244
|
+
ExternalStateManager.instance = new ExternalStateManager();
|
|
245
|
+
}
|
|
246
|
+
return ExternalStateManager.instance;
|
|
247
|
+
}
|
|
248
|
+
get(key) {
|
|
249
|
+
return globalStore.getStateValue(key);
|
|
250
|
+
}
|
|
251
|
+
set(key, value) {
|
|
252
|
+
globalStore.setStateValue(key, value);
|
|
253
|
+
}
|
|
254
|
+
useState(key, initialValue) {
|
|
255
|
+
return useGlobalStateExternal(key, initialValue);
|
|
256
|
+
}
|
|
257
|
+
subscribe(key, callback) {
|
|
258
|
+
return globalStore.subscribeToKey(key, callback);
|
|
259
|
+
}
|
|
260
|
+
getAll() {
|
|
261
|
+
return globalStore.getStateObject();
|
|
262
|
+
}
|
|
263
|
+
has(key) {
|
|
264
|
+
return globalStore.getStateValue(key) !== undefined;
|
|
265
|
+
}
|
|
266
|
+
remove(key) {
|
|
267
|
+
globalStore.setStateValue(key, undefined);
|
|
268
|
+
}
|
|
269
|
+
clear() {
|
|
270
|
+
const stateObj = globalStore.getStateObject();
|
|
271
|
+
Object.keys(stateObj).forEach((key) => {
|
|
272
|
+
globalStore.setStateValue(key, undefined);
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
// Convenience instance for immediate use
|
|
277
|
+
export const stateManager = ExternalStateManager.getInstance();
|
|
278
|
+
export { globalStore };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { EventBus } from "./appBuilder";
|
|
2
|
+
import type { EventOrchestrator } from "./eventBus";
|
|
3
|
+
export interface EventBusRecord {
|
|
4
|
+
id: string;
|
|
5
|
+
eventBus: EventBus;
|
|
6
|
+
}
|
|
7
|
+
export interface EventListenerRecord {
|
|
8
|
+
eventName: string;
|
|
9
|
+
remove: () => void;
|
|
10
|
+
}
|
|
11
|
+
export interface PageProps {
|
|
12
|
+
id: string;
|
|
13
|
+
orchestrator: EventOrchestrator;
|
|
14
|
+
bubbleEvents: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface ViewProps {
|
|
17
|
+
id: string;
|
|
18
|
+
orchestrator: EventOrchestrator;
|
|
19
|
+
bubbleEvents: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface ComponentProps {
|
|
22
|
+
id: string;
|
|
23
|
+
orchestrator: EventOrchestrator;
|
|
24
|
+
bubbleEvents: boolean;
|
|
25
|
+
}
|
|
26
|
+
export interface RenderOptions {
|
|
27
|
+
fade?: {
|
|
28
|
+
enabled: boolean;
|
|
29
|
+
duration?: number;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,QAAQ,CAAC;CACpB;AACD,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,iBAAiB,CAAC;IAChC,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,iBAAiB,CAAC;IAChC,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,iBAAiB,CAAC;IAChC,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE;QACL,OAAO,EAAE,OAAO,CAAC;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;CACH"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|