canvasengine 2.0.1-beta.1 → 2.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/dist/DebugRenderer-DkjTAc48.js +1384 -0
- package/dist/DebugRenderer-DkjTAc48.js.map +1 -0
- package/dist/components/Button.d.ts +185 -0
- package/dist/components/Button.d.ts.map +1 -0
- package/dist/components/Canvas.d.ts +17 -0
- package/dist/components/Canvas.d.ts.map +1 -0
- package/dist/components/Container.d.ts +25 -0
- package/dist/components/Container.d.ts.map +1 -0
- package/dist/components/DOMContainer.d.ts +28 -0
- package/dist/components/DOMContainer.d.ts.map +1 -0
- package/dist/components/DOMElement.d.ts +54 -0
- package/dist/components/DOMElement.d.ts.map +1 -0
- package/dist/components/DOMSprite.d.ts +127 -0
- package/dist/components/DOMSprite.d.ts.map +1 -0
- package/dist/components/DisplayObject.d.ts +18 -0
- package/dist/components/DisplayObject.d.ts.map +1 -0
- package/dist/components/FocusContainer.d.ts +129 -0
- package/dist/components/FocusContainer.d.ts.map +1 -0
- package/dist/components/Graphic.d.ts +64 -0
- package/dist/components/Graphic.d.ts.map +1 -0
- package/dist/components/Joystick.d.ts +37 -0
- package/dist/components/Joystick.d.ts.map +1 -0
- package/dist/components/Mesh.d.ts +138 -0
- package/dist/components/Mesh.d.ts.map +1 -0
- package/dist/components/NineSliceSprite.d.ts +16 -0
- package/dist/components/NineSliceSprite.d.ts.map +1 -0
- package/dist/components/ParticleEmitter.d.ts +4 -0
- package/dist/components/ParticleEmitter.d.ts.map +1 -0
- package/dist/components/Scene.d.ts +2 -0
- package/dist/components/Scene.d.ts.map +1 -0
- package/dist/components/Sprite.d.ts +182 -0
- package/dist/components/Sprite.d.ts.map +1 -0
- package/dist/components/Text.d.ts +24 -0
- package/dist/components/Text.d.ts.map +1 -0
- package/dist/components/TilingSprite.d.ts +17 -0
- package/dist/components/TilingSprite.d.ts.map +1 -0
- package/dist/components/Video.d.ts +14 -0
- package/dist/components/Video.d.ts.map +1 -0
- package/dist/components/Viewport.d.ts +52 -0
- package/dist/components/Viewport.d.ts.map +1 -0
- package/dist/components/index.d.ts +20 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/types/DisplayObject.d.ts +127 -0
- package/dist/components/types/DisplayObject.d.ts.map +1 -0
- package/dist/components/types/MouseEvent.d.ts +4 -0
- package/dist/components/types/MouseEvent.d.ts.map +1 -0
- package/dist/components/types/Spritesheet.d.ts +248 -0
- package/dist/components/types/Spritesheet.d.ts.map +1 -0
- package/dist/components/types/index.d.ts +4 -0
- package/dist/components/types/index.d.ts.map +1 -0
- package/dist/directives/Controls.d.ts +112 -0
- package/dist/directives/Controls.d.ts.map +1 -0
- package/dist/directives/ControlsBase.d.ts +199 -0
- package/dist/directives/ControlsBase.d.ts.map +1 -0
- package/dist/directives/Drag.d.ts +69 -0
- package/dist/directives/Drag.d.ts.map +1 -0
- package/dist/directives/Flash.d.ts +116 -0
- package/dist/directives/Flash.d.ts.map +1 -0
- package/dist/directives/FocusNavigation.d.ts +52 -0
- package/dist/directives/FocusNavigation.d.ts.map +1 -0
- package/dist/directives/FogVisibility.d.ts +47 -0
- package/dist/directives/FogVisibility.d.ts.map +1 -0
- package/dist/directives/GamepadControls.d.ts +224 -0
- package/dist/directives/GamepadControls.d.ts.map +1 -0
- package/dist/directives/JoystickControls.d.ts +171 -0
- package/dist/directives/JoystickControls.d.ts.map +1 -0
- package/dist/directives/KeyboardControls.d.ts +219 -0
- package/dist/directives/KeyboardControls.d.ts.map +1 -0
- package/dist/directives/Scheduler.d.ts +36 -0
- package/dist/directives/Scheduler.d.ts.map +1 -0
- package/dist/directives/Shake.d.ts +98 -0
- package/dist/directives/Shake.d.ts.map +1 -0
- package/dist/directives/Sound.d.ts +25 -0
- package/dist/directives/Sound.d.ts.map +1 -0
- package/dist/directives/SpriteEffects.d.ts +70 -0
- package/dist/directives/SpriteEffects.d.ts.map +1 -0
- package/dist/directives/Transition.d.ts +10 -0
- package/dist/directives/Transition.d.ts.map +1 -0
- package/dist/directives/ViewportCull.d.ts +11 -0
- package/dist/directives/ViewportCull.d.ts.map +1 -0
- package/dist/directives/ViewportFollow.d.ts +18 -0
- package/dist/directives/ViewportFollow.d.ts.map +1 -0
- package/dist/directives/index.d.ts +15 -0
- package/dist/directives/index.d.ts.map +1 -0
- package/dist/dist-BOOc43Qm.js +778 -0
- package/dist/dist-BOOc43Qm.js.map +1 -0
- package/dist/engine/FocusManager.d.ts +174 -0
- package/dist/engine/FocusManager.d.ts.map +1 -0
- package/dist/engine/animation.d.ts +72 -0
- package/dist/engine/animation.d.ts.map +1 -0
- package/dist/engine/bootstrap.d.ts +52 -0
- package/dist/engine/bootstrap.d.ts.map +1 -0
- package/dist/engine/directive.d.ts +13 -0
- package/dist/engine/directive.d.ts.map +1 -0
- package/dist/engine/reactive.d.ts +139 -0
- package/dist/engine/reactive.d.ts.map +1 -0
- package/dist/engine/signal.d.ts +74 -0
- package/dist/engine/signal.d.ts.map +1 -0
- package/dist/engine/trigger.d.ts +54 -0
- package/dist/engine/trigger.d.ts.map +1 -0
- package/dist/engine/utils.d.ts +89 -0
- package/dist/engine/utils.d.ts.map +1 -0
- package/dist/hooks/addContext.d.ts +2 -0
- package/dist/hooks/addContext.d.ts.map +1 -0
- package/dist/hooks/useFocus.d.ts +60 -0
- package/dist/hooks/useFocus.d.ts.map +1 -0
- package/dist/hooks/useProps.d.ts +53 -0
- package/dist/hooks/useProps.d.ts.map +1 -0
- package/dist/hooks/useRef.d.ts +4 -0
- package/dist/hooks/useRef.d.ts.map +1 -0
- package/dist/index.d.ts +19 -844
- package/dist/index.d.ts.map +1 -0
- package/dist/index.global.js +189 -0
- package/dist/index.global.js.map +1 -0
- package/dist/index.js +15156 -3287
- package/dist/index.js.map +1 -1
- package/dist/utils/Ease.d.ts +17 -0
- package/dist/utils/Ease.d.ts.map +1 -0
- package/dist/utils/GlobalAssetLoader.d.ts +141 -0
- package/dist/utils/GlobalAssetLoader.d.ts.map +1 -0
- package/dist/utils/RadialGradient.d.ts +57 -0
- package/dist/utils/RadialGradient.d.ts.map +1 -0
- package/dist/utils/functions.d.ts +2 -0
- package/dist/utils/functions.d.ts.map +1 -0
- package/dist/utils/tabindex.d.ts +16 -0
- package/dist/utils/tabindex.d.ts.map +1 -0
- package/index.d.ts +1 -3
- package/package.json +19 -52
- package/src/components/Button.ts +406 -0
- package/src/components/Canvas.ts +82 -51
- package/src/components/Container.ts +31 -4
- package/src/components/DOMContainer.ts +387 -0
- package/src/components/DOMElement.ts +556 -0
- package/src/components/DOMSprite.ts +1044 -0
- package/src/components/DisplayObject.ts +423 -202
- package/src/components/FocusContainer.ts +368 -0
- package/src/components/Graphic.ts +255 -68
- package/src/components/Joystick.ts +269 -0
- package/src/components/Mesh.ts +239 -0
- package/src/components/NineSliceSprite.ts +4 -1
- package/src/components/ParticleEmitter.ts +12 -8
- package/src/components/Sprite.ts +441 -54
- package/src/components/Text.ts +270 -26
- package/src/components/Video.ts +110 -0
- package/src/components/Viewport.ts +122 -63
- package/src/components/index.ts +12 -5
- package/src/components/types/DisplayObject.ts +64 -5
- package/src/components/types/Spritesheet.ts +0 -118
- package/src/directives/Controls.ts +254 -0
- package/src/directives/ControlsBase.ts +267 -0
- package/src/directives/Drag.ts +357 -52
- package/src/directives/Flash.ts +419 -0
- package/src/directives/FocusNavigation.ts +113 -0
- package/src/directives/FogVisibility.ts +273 -0
- package/src/directives/GamepadControls.ts +537 -0
- package/src/directives/JoystickControls.ts +396 -0
- package/src/directives/KeyboardControls.ts +86 -431
- package/src/directives/Scheduler.ts +21 -16
- package/src/directives/Shake.ts +298 -0
- package/src/directives/Sound.ts +94 -31
- package/src/directives/SpriteEffects.ts +461 -0
- package/src/directives/ViewportFollow.ts +40 -9
- package/src/directives/index.ts +14 -6
- package/src/engine/FocusManager.ts +510 -0
- package/src/engine/animation.ts +175 -21
- package/src/engine/bootstrap.ts +140 -6
- package/src/engine/directive.ts +4 -4
- package/src/engine/reactive.ts +1266 -167
- package/src/engine/signal.ts +335 -47
- package/src/engine/trigger.ts +95 -12
- package/src/engine/utils.ts +101 -9
- package/src/hooks/useFocus.ts +91 -0
- package/src/hooks/useProps.ts +92 -7
- package/src/index.ts +13 -6
- package/src/types/pixi-cull.d.ts +7 -0
- package/src/utils/GlobalAssetLoader.ts +257 -0
- package/src/utils/RadialGradient.ts +29 -0
- package/src/utils/functions.ts +7 -0
- package/src/utils/tabindex.ts +70 -0
- package/testing/index.ts +42 -0
- package/tsconfig.json +15 -25
- package/vite.config.ts +39 -0
- package/.cursorrules +0 -0
- package/.github/workflows/ci.yml +0 -29
- package/README.md +0 -79
- package/dist/compiler/vite.js +0 -119
- package/dist/compiler/vite.js.map +0 -1
- package/logo.png +0 -0
- package/src/compiler/grammar.pegjs +0 -180
- package/src/compiler/vite.ts +0 -166
- package/src/components/DrawMap/index.ts +0 -65
- package/src/components/Tilemap/Tile.ts +0 -79
- package/src/components/Tilemap/TileGroup.ts +0 -207
- package/src/components/Tilemap/TileLayer.ts +0 -163
- package/src/components/Tilemap/TileSet.ts +0 -41
- package/src/components/Tilemap/index.ts +0 -80
- package/src/presets/Bar.ts +0 -89
- package/src/presets/Button.ts +0 -0
- package/src/presets/Joystick.ts +0 -286
- package/src/presets/NightAmbiant.ts +0 -122
- package/src/presets/Particle.ts +0 -53
- package/starter/assets/logo.png +0 -0
- package/starter/components/app.ce +0 -18
- package/starter/components/hello.ce +0 -35
- package/starter/index.html +0 -21
- package/starter/main.ts +0 -6
- package/starter/package.json +0 -20
- package/starter/tsconfig.json +0 -32
- package/starter/vite.config.ts +0 -12
- package/tsconfig.node.json +0 -10
- package/tsup.config.ts +0 -28
- package/vitest.config.ts +0 -12
package/src/engine/signal.ts
CHANGED
|
@@ -1,17 +1,65 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
Observable,
|
|
3
|
+
Subject,
|
|
4
|
+
Subscription
|
|
3
5
|
} from "rxjs";
|
|
6
|
+
import { isSignal } from "@signe/reactive";
|
|
4
7
|
import type { Element } from "./reactive";
|
|
8
|
+
import { destroyElement, isElementFrozen, waitForDependencies } from "./reactive";
|
|
9
|
+
import { isPromise } from "./utils";
|
|
5
10
|
import { Tick } from "../directives/Scheduler";
|
|
11
|
+
import { Container } from "../components";
|
|
6
12
|
|
|
7
|
-
type
|
|
13
|
+
type MountCallback = (element: Element) => any;
|
|
14
|
+
type MountFunction = (fn: MountCallback) => void;
|
|
8
15
|
|
|
9
16
|
// Define ComponentFunction type
|
|
10
17
|
export type ComponentFunction<P = {}> = (props: P) => Element | Promise<Element>;
|
|
18
|
+
type HotFlowResult = { elements: Element[] };
|
|
19
|
+
type HotComponentRecord = {
|
|
20
|
+
component: ComponentFunction<any>;
|
|
21
|
+
updates: Subject<void>;
|
|
22
|
+
wrapper?: ComponentFunction<any>;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const HOT_COMPONENT_PROPS = "__canvasEngineHotProps";
|
|
26
|
+
const HOT_COMPONENT_UPDATE_PROPS = "__canvasEngineUpdateHotProps";
|
|
27
|
+
const DEFINE_PROPS_SIGNALS = "__canvasEngineDefinePropsSignals";
|
|
11
28
|
|
|
12
29
|
export let currentSubscriptionsTracker: ((subscription: Subscription) => void) | null = null;
|
|
30
|
+
export let currentDefinePropsTracker: ((signals: Record<string, any>) => void) | null = null;
|
|
13
31
|
export let mountTracker: MountFunction | null = null;
|
|
14
32
|
|
|
33
|
+
const readSignalValue = (value: any) => {
|
|
34
|
+
const callableValueSignal = value?.__canvasEngineCallableSignalValue;
|
|
35
|
+
if (callableValueSignal) return callableValueSignal();
|
|
36
|
+
return isSignal(value) ? value() : value;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const patchDefinePropsSignals = (target: Element, source: Element) => {
|
|
40
|
+
const targetSignals = (target as any)[DEFINE_PROPS_SIGNALS];
|
|
41
|
+
const sourceSignals = (source as any)[DEFINE_PROPS_SIGNALS];
|
|
42
|
+
|
|
43
|
+
if (!targetSignals || !sourceSignals) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
Object.entries(sourceSignals as Record<string, any>).forEach(([key, sourceSignal]) => {
|
|
48
|
+
const targetSignal = targetSignals[key];
|
|
49
|
+
if (targetSignal && typeof targetSignal.set === "function") {
|
|
50
|
+
targetSignal.set(readSignalValue(sourceSignal));
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const getHotComponentRegistry = (): Map<string, HotComponentRecord> => {
|
|
56
|
+
const hotGlobal = globalThis as any;
|
|
57
|
+
if (!hotGlobal.__CANVAS_ENGINE_HOT_COMPONENTS__) {
|
|
58
|
+
hotGlobal.__CANVAS_ENGINE_HOT_COMPONENTS__ = new Map<string, HotComponentRecord>();
|
|
59
|
+
}
|
|
60
|
+
return hotGlobal.__CANVAS_ENGINE_HOT_COMPONENTS__;
|
|
61
|
+
};
|
|
62
|
+
|
|
15
63
|
/**
|
|
16
64
|
* Registers a mount function to be called when the component is mounted.
|
|
17
65
|
* To unmount the component, the function must return a function that will be called by the engine.
|
|
@@ -19,16 +67,16 @@ export let mountTracker: MountFunction | null = null;
|
|
|
19
67
|
* @param {(element: Element) => void} fn - The function to be called on mount.
|
|
20
68
|
* @example
|
|
21
69
|
* ```ts
|
|
22
|
-
|
|
23
|
-
*
|
|
70
|
+
* mount((el) => {
|
|
71
|
+
* console.log('mounted', el);
|
|
24
72
|
* });
|
|
25
73
|
* ```
|
|
26
74
|
* Unmount the component by returning a function:
|
|
27
75
|
* ```ts
|
|
28
|
-
|
|
29
|
-
*
|
|
76
|
+
* mount((el) => {
|
|
77
|
+
* console.log('mounted', el);
|
|
30
78
|
* return () => {
|
|
31
|
-
*
|
|
79
|
+
* console.log('unmounted', el);
|
|
32
80
|
* }
|
|
33
81
|
* });
|
|
34
82
|
* ```
|
|
@@ -42,8 +90,8 @@ export function mount(fn: (element: Element) => void) {
|
|
|
42
90
|
* @param {(tickValue: Tick, element: Element) => void} fn - The function to be called on each tick.
|
|
43
91
|
* @example
|
|
44
92
|
* ```ts
|
|
45
|
-
|
|
46
|
-
*
|
|
93
|
+
* tick((tickValue, el) => {
|
|
94
|
+
* console.log('tick', tickValue, el);
|
|
47
95
|
* });
|
|
48
96
|
* ```
|
|
49
97
|
*/
|
|
@@ -52,8 +100,12 @@ export function tick(fn: (tickValue: Tick, element: Element) => void) {
|
|
|
52
100
|
const { context } = el.props
|
|
53
101
|
let subscription: Subscription | undefined
|
|
54
102
|
if (context.tick) {
|
|
55
|
-
subscription = context.tick.observable.subscribe(({ value }) => {
|
|
56
|
-
|
|
103
|
+
subscription = context.tick.observable.subscribe(({ value }: { value: Tick }) => {
|
|
104
|
+
// Block tick if element is frozen
|
|
105
|
+
if (isElementFrozen(el)) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
fn(value, el)
|
|
57
109
|
})
|
|
58
110
|
}
|
|
59
111
|
return () => {
|
|
@@ -71,68 +123,304 @@ export function tick(fn: (tickValue: Tick, element: Element) => void) {
|
|
|
71
123
|
* @returns {ReturnType<C>}
|
|
72
124
|
* @example
|
|
73
125
|
* ```ts
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
126
|
+
* const el = h(MyComponent, {
|
|
127
|
+
* x: 100,
|
|
128
|
+
* y: 100,
|
|
129
|
+
* });
|
|
78
130
|
* ```
|
|
79
131
|
*
|
|
80
132
|
* with children:
|
|
81
133
|
* ```ts
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
*
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
134
|
+
* const el = h(MyComponent, {
|
|
135
|
+
* x: 100,
|
|
136
|
+
* y: 100,
|
|
137
|
+
* },
|
|
138
|
+
* h(MyChildComponent, {
|
|
139
|
+
* x: 50,
|
|
140
|
+
* y: 50,
|
|
141
|
+
* }),
|
|
90
142
|
* );
|
|
91
143
|
* ```
|
|
92
144
|
*/
|
|
93
|
-
|
|
94
|
-
componentFunction: C,
|
|
145
|
+
function _h<C extends ComponentFunction<any>>(
|
|
146
|
+
componentFunction: C | Element,
|
|
95
147
|
props: Parameters<C>[0] = {} as Parameters<C>[0],
|
|
96
|
-
|
|
148
|
+
children: any[]
|
|
149
|
+
): ReturnType<C> {
|
|
150
|
+
if (children[0] instanceof Array) {
|
|
151
|
+
children = children[0]
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
let component: Element
|
|
155
|
+
|
|
156
|
+
if (Array.isArray(componentFunction)) {
|
|
157
|
+
if (componentFunction.length === 1) {
|
|
158
|
+
component = componentFunction[0]
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
component = _h(Container, {}, componentFunction) as Element
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
else if ('tag' in componentFunction) {
|
|
165
|
+
component = componentFunction
|
|
166
|
+
}
|
|
167
|
+
else if (componentFunction instanceof Observable) {
|
|
168
|
+
component = componentFunction as any
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
component = createTrackedComponent(componentFunction, { ...props, children }) as Element;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (!component) {
|
|
175
|
+
component = {} as any
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Copy dependencies prop to the returned element so it can be used for delayed mounting
|
|
179
|
+
if (props?.dependencies) {
|
|
180
|
+
component.props = component.props || {};
|
|
181
|
+
component.props.dependencies = props.dependencies;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return component as ReturnType<C>;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function createTrackedComponent<C extends ComponentFunction<any>>(
|
|
188
|
+
componentFunction: C,
|
|
189
|
+
props: Parameters<C>[0]
|
|
97
190
|
): ReturnType<C> {
|
|
98
191
|
const allSubscriptions = new Set<Subscription>();
|
|
99
|
-
const allMounts = new Set<
|
|
192
|
+
const allMounts = new Set<MountCallback>();
|
|
193
|
+
let allDefinePropSignals: Record<string, any> | null = null;
|
|
100
194
|
|
|
101
195
|
currentSubscriptionsTracker = (subscription) => {
|
|
102
196
|
allSubscriptions.add(subscription);
|
|
103
197
|
};
|
|
104
198
|
|
|
199
|
+
currentDefinePropsTracker = (signals) => {
|
|
200
|
+
allDefinePropSignals = {
|
|
201
|
+
...(allDefinePropSignals ?? {}),
|
|
202
|
+
...signals,
|
|
203
|
+
};
|
|
204
|
+
};
|
|
205
|
+
|
|
105
206
|
mountTracker = (fn: any) => {
|
|
106
207
|
allMounts.add(fn);
|
|
107
208
|
};
|
|
108
209
|
|
|
109
|
-
|
|
110
|
-
|
|
210
|
+
let component: ReturnType<C> = undefined as any;
|
|
211
|
+
try {
|
|
212
|
+
component = componentFunction(props) as ReturnType<C>;
|
|
213
|
+
} finally {
|
|
214
|
+
currentSubscriptionsTracker = null;
|
|
215
|
+
currentDefinePropsTracker = null;
|
|
216
|
+
mountTracker = null;
|
|
111
217
|
}
|
|
112
218
|
|
|
113
|
-
|
|
219
|
+
const applyTrackedEffects = (element: Element) => {
|
|
220
|
+
if (!element) return;
|
|
221
|
+
element.effectSubscriptions = [
|
|
222
|
+
...Array.from(allSubscriptions),
|
|
223
|
+
...((element as any).effectSubscriptions ?? [])
|
|
224
|
+
];
|
|
225
|
+
element.effectMounts = [
|
|
226
|
+
...Array.from(allMounts),
|
|
227
|
+
...((element as any).effectMounts ?? [])
|
|
228
|
+
];
|
|
229
|
+
if (allDefinePropSignals) {
|
|
230
|
+
(element as any)[DEFINE_PROPS_SIGNALS] = allDefinePropSignals;
|
|
231
|
+
}
|
|
232
|
+
};
|
|
114
233
|
|
|
115
|
-
if (
|
|
116
|
-
component
|
|
234
|
+
if (component instanceof Promise) {
|
|
235
|
+
component.then((element) => {
|
|
236
|
+
applyTrackedEffects(element);
|
|
237
|
+
if (element?.props?.isRoot) {
|
|
238
|
+
allMounts.forEach((fn) => fn(element));
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
} else if (component instanceof Observable) {
|
|
242
|
+
(component as any).effectSubscriptions = [
|
|
243
|
+
...Array.from(allSubscriptions),
|
|
244
|
+
...((component as any).effectSubscriptions ?? [])
|
|
245
|
+
];
|
|
246
|
+
(component as any).effectMounts = [
|
|
247
|
+
...Array.from(allMounts),
|
|
248
|
+
...((component as any).effectMounts ?? [])
|
|
249
|
+
];
|
|
250
|
+
if (allDefinePropSignals) {
|
|
251
|
+
(component as any)[DEFINE_PROPS_SIGNALS] = allDefinePropSignals;
|
|
252
|
+
}
|
|
253
|
+
} else {
|
|
254
|
+
applyTrackedEffects(component as Element);
|
|
117
255
|
}
|
|
118
256
|
|
|
119
|
-
component
|
|
120
|
-
|
|
121
|
-
...Array.from(allMounts),
|
|
122
|
-
...((component as any).effectMounts ?? [])
|
|
123
|
-
];
|
|
257
|
+
return component;
|
|
258
|
+
}
|
|
124
259
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
260
|
+
export function createHotComponent<P>(
|
|
261
|
+
id: string,
|
|
262
|
+
component: ComponentFunction<P>
|
|
263
|
+
): ComponentFunction<P> {
|
|
264
|
+
const registry = getHotComponentRegistry();
|
|
265
|
+
let record = registry.get(id);
|
|
266
|
+
|
|
267
|
+
if (!record) {
|
|
268
|
+
record = {
|
|
269
|
+
component,
|
|
270
|
+
updates: new Subject<void>(),
|
|
271
|
+
};
|
|
272
|
+
registry.set(id, record);
|
|
273
|
+
} else {
|
|
274
|
+
record.component = component;
|
|
275
|
+
record.updates.next();
|
|
132
276
|
}
|
|
133
277
|
|
|
134
|
-
|
|
135
|
-
|
|
278
|
+
if (!record.wrapper) {
|
|
279
|
+
record.wrapper = ((props: P) => {
|
|
280
|
+
let currentProps = props;
|
|
136
281
|
|
|
137
|
-
|
|
282
|
+
const observable = new Observable<HotFlowResult>((subscriber) => {
|
|
283
|
+
let disposed = false;
|
|
284
|
+
let currentElement: Element | null = null;
|
|
285
|
+
|
|
286
|
+
const patchElement = (target: Element, source: Element) => {
|
|
287
|
+
if (target.tag !== source.tag) {
|
|
288
|
+
return false;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const nextProps = { ...source.props };
|
|
292
|
+
if (target.props.context) {
|
|
293
|
+
nextProps.context = target.props.context;
|
|
294
|
+
}
|
|
295
|
+
if (target.props.children && !source.props.children) {
|
|
296
|
+
nextProps.children = target.props.children;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
patchDefinePropsSignals(target, source);
|
|
300
|
+
|
|
301
|
+
target.props = nextProps;
|
|
302
|
+
target.propObservables = source.propObservables;
|
|
303
|
+
target.componentInstance.onUpdate?.(nextProps);
|
|
304
|
+
Object.entries(target.directives).forEach(([name, directive]) => {
|
|
305
|
+
if (name in nextProps) {
|
|
306
|
+
directive.onUpdate?.(nextProps[name], target);
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
source.propSubscriptions?.forEach((sub) => sub.unsubscribe());
|
|
311
|
+
source.effectSubscriptions?.forEach((sub) => sub.unsubscribe());
|
|
312
|
+
source.effectUnmounts?.forEach((fn) => fn?.());
|
|
313
|
+
|
|
314
|
+
return true;
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
const emit = (preserveCurrentElement = false) => {
|
|
318
|
+
const rendered = createTrackedComponent(record!.component, currentProps);
|
|
319
|
+
const next = (element: Element | null | undefined) => {
|
|
320
|
+
if (!disposed) {
|
|
321
|
+
if (
|
|
322
|
+
preserveCurrentElement &&
|
|
323
|
+
currentElement &&
|
|
324
|
+
element &&
|
|
325
|
+
patchElement(currentElement, element)
|
|
326
|
+
) {
|
|
327
|
+
subscriber.next({ elements: [currentElement] });
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
subscriber.next({ elements: element ? [element] : [] });
|
|
332
|
+
if (currentElement && currentElement !== element) {
|
|
333
|
+
destroyElement(currentElement);
|
|
334
|
+
}
|
|
335
|
+
currentElement = element ?? null;
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
if (rendered instanceof Promise) {
|
|
340
|
+
rendered.then(next).catch((error) => subscriber.error(error));
|
|
341
|
+
} else {
|
|
342
|
+
next(rendered as Element);
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
emit();
|
|
347
|
+
(observable as any)[HOT_COMPONENT_UPDATE_PROPS] = (nextProps: P) => {
|
|
348
|
+
currentProps = nextProps;
|
|
349
|
+
emit(true);
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
const subscription = record!.updates.subscribe(() => emit());
|
|
353
|
+
|
|
354
|
+
return () => {
|
|
355
|
+
disposed = true;
|
|
356
|
+
if (currentElement) {
|
|
357
|
+
destroyElement(currentElement);
|
|
358
|
+
currentElement = null;
|
|
359
|
+
}
|
|
360
|
+
subscription.unsubscribe();
|
|
361
|
+
};
|
|
362
|
+
}) as any;
|
|
363
|
+
|
|
364
|
+
(observable as any)[HOT_COMPONENT_PROPS] = props;
|
|
365
|
+
|
|
366
|
+
return observable;
|
|
367
|
+
}) as ComponentFunction<any>;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
return record.wrapper as ComponentFunction<P>;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Add tracking for subscriptions and mounts, then create an element from a component function.
|
|
375
|
+
* @template C
|
|
376
|
+
* @param {C} componentFunction - The component function to create an element from.
|
|
377
|
+
* @param {Parameters<C>[0]} [props={}] - The props to pass to the component function.
|
|
378
|
+
* @param {...any[]} children - The children elements of the component.
|
|
379
|
+
* @returns {ReturnType<C>}
|
|
380
|
+
* @example
|
|
381
|
+
* ```ts
|
|
382
|
+
* const el = h(MyComponent, {
|
|
383
|
+
* x: 100,
|
|
384
|
+
* y: 100,
|
|
385
|
+
* });
|
|
386
|
+
* ```
|
|
387
|
+
*
|
|
388
|
+
* with children:
|
|
389
|
+
* ```ts
|
|
390
|
+
* const el = h(MyComponent, {
|
|
391
|
+
* x: 100,
|
|
392
|
+
* y: 100,
|
|
393
|
+
* },
|
|
394
|
+
* h(MyChildComponent, {
|
|
395
|
+
* x: 50,
|
|
396
|
+
* y: 50,
|
|
397
|
+
* }),
|
|
398
|
+
* );
|
|
399
|
+
* ```
|
|
400
|
+
*/
|
|
401
|
+
export function h<C extends ComponentFunction<any>>(
|
|
402
|
+
componentFunction: C | Element,
|
|
403
|
+
props: Parameters<C>[0] = {} as Parameters<C>[0],
|
|
404
|
+
...children: any[]
|
|
405
|
+
): ReturnType<C> {
|
|
406
|
+
if (props?.dependencies) {
|
|
407
|
+
const hasPromise = props.dependencies.some(isPromise);
|
|
408
|
+
if (!hasPromise) {
|
|
409
|
+
const allReady = props.dependencies.every((dep: any) => {
|
|
410
|
+
if (isSignal(dep)) return dep() !== undefined;
|
|
411
|
+
return dep !== undefined;
|
|
412
|
+
});
|
|
413
|
+
if (allReady) {
|
|
414
|
+
return _h(componentFunction, props, children);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
return new Observable(subscriber => {
|
|
419
|
+
waitForDependencies(props.dependencies).then(() => {
|
|
420
|
+
const el = _h(componentFunction, props, children);
|
|
421
|
+
subscriber.next(el);
|
|
422
|
+
});
|
|
423
|
+
}) as any;
|
|
424
|
+
}
|
|
425
|
+
return _h(componentFunction, props, children);
|
|
138
426
|
}
|
package/src/engine/trigger.ts
CHANGED
|
@@ -1,40 +1,123 @@
|
|
|
1
1
|
import { effect, signal } from "@signe/reactive";
|
|
2
2
|
|
|
3
|
-
interface Listen<T = any> {
|
|
3
|
+
export interface Listen<T = any> {
|
|
4
4
|
config: T | undefined;
|
|
5
|
-
seed:
|
|
5
|
+
seed: {
|
|
6
|
+
config: T | undefined;
|
|
7
|
+
value: number;
|
|
8
|
+
resolve: (value: any) => void;
|
|
9
|
+
};
|
|
6
10
|
}
|
|
7
11
|
|
|
8
|
-
interface Trigger<T = any> {
|
|
9
|
-
start: () => void
|
|
12
|
+
export interface Trigger<T = any> {
|
|
13
|
+
start: () => Promise<void>;
|
|
10
14
|
listen: () => Listen<T> | undefined;
|
|
11
15
|
}
|
|
12
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Checks if the given argument is a Trigger object
|
|
19
|
+
* @param arg - The value to check
|
|
20
|
+
* @returns True if the argument is a Trigger object
|
|
21
|
+
*/
|
|
13
22
|
export function isTrigger(arg: any): arg is Trigger<any> {
|
|
14
23
|
return arg?.start && arg?.listen;
|
|
15
24
|
}
|
|
16
25
|
|
|
17
|
-
|
|
18
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Creates a new trigger that can be used to pass data between components
|
|
28
|
+
* @param globalConfig - Optional configuration data to be passed when the trigger is activated
|
|
29
|
+
* @returns A Trigger object with start and listen methods
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* const myTrigger = trigger()
|
|
33
|
+
*
|
|
34
|
+
* on(myTrigger, (data) => {
|
|
35
|
+
* console.log('Triggered with data:', data)
|
|
36
|
+
* })
|
|
37
|
+
*
|
|
38
|
+
* myTrigger.start({ message: 'Hello' })
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export function trigger<T = any>(globalConfig?: T): Trigger<T> {
|
|
42
|
+
const _signal = signal({
|
|
43
|
+
config: globalConfig,
|
|
44
|
+
value: 0,
|
|
45
|
+
resolve: (value: any) => void 0,
|
|
46
|
+
});
|
|
19
47
|
return {
|
|
20
|
-
start: () => {
|
|
21
|
-
|
|
48
|
+
start: (config?: T) => {
|
|
49
|
+
return new Promise((resolve: (value: any) => void) => {
|
|
50
|
+
const newValue = Math.random();
|
|
51
|
+
_signal.set({
|
|
52
|
+
config: {
|
|
53
|
+
...globalConfig,
|
|
54
|
+
...config,
|
|
55
|
+
},
|
|
56
|
+
resolve,
|
|
57
|
+
value: newValue,
|
|
58
|
+
});
|
|
59
|
+
});
|
|
22
60
|
},
|
|
23
61
|
listen: (): Listen<T> | undefined => {
|
|
24
62
|
return {
|
|
25
|
-
config,
|
|
63
|
+
config: globalConfig,
|
|
26
64
|
seed: _signal(),
|
|
27
65
|
};
|
|
28
66
|
},
|
|
29
67
|
};
|
|
30
68
|
}
|
|
31
69
|
|
|
32
|
-
|
|
70
|
+
/**
|
|
71
|
+
* Subscribes to a trigger and executes a callback when the trigger is activated
|
|
72
|
+
* @param triggerSignal - The trigger to subscribe to
|
|
73
|
+
* @param callback - Function to execute when the trigger is activated
|
|
74
|
+
* @returns Subscription that can be unsubscribed to stop listening
|
|
75
|
+
* @throws Error if triggerSignal is not a valid trigger
|
|
76
|
+
* @example
|
|
77
|
+
* ```ts
|
|
78
|
+
* const click = trigger()
|
|
79
|
+
*
|
|
80
|
+
* const subscription = on(click, () => {
|
|
81
|
+
* console.log('Click triggered')
|
|
82
|
+
* })
|
|
83
|
+
*
|
|
84
|
+
* // Later, to stop listening:
|
|
85
|
+
* subscription.unsubscribe()
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
export function on(triggerSignal: any, callback: (config: any) => void | Promise<void>) {
|
|
33
89
|
if (!isTrigger(triggerSignal)) {
|
|
34
90
|
throw new Error("In 'on(arg)' must have a trigger signal type");
|
|
35
91
|
}
|
|
36
|
-
|
|
92
|
+
let lastValue: number | undefined;
|
|
93
|
+
|
|
94
|
+
const effectResult = effect(() => {
|
|
37
95
|
const result = triggerSignal.listen();
|
|
38
|
-
|
|
96
|
+
const seed = result?.seed;
|
|
97
|
+
|
|
98
|
+
if (!seed) return;
|
|
99
|
+
|
|
100
|
+
// Only run callback when the trigger value actually changes
|
|
101
|
+
if (lastValue === undefined) {
|
|
102
|
+
lastValue = seed.value;
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (seed.value === lastValue) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
lastValue = seed.value;
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
const ret = callback(result?.seed.config);
|
|
112
|
+
if (ret && typeof ret.then === 'function') {
|
|
113
|
+
ret.then((value: any) => seed.resolve(value)).catch(() => seed.resolve(undefined));
|
|
114
|
+
} else {
|
|
115
|
+
seed.resolve(ret);
|
|
116
|
+
}
|
|
117
|
+
} catch (err) {
|
|
118
|
+
seed.resolve(undefined);
|
|
119
|
+
}
|
|
39
120
|
});
|
|
121
|
+
|
|
122
|
+
return effectResult.subscription;
|
|
40
123
|
}
|