canvasengine 2.0.1-beta.2 → 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 -3279
- 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 +17 -46
- 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 -5
- 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 +18 -23
- package/vite.config.ts +39 -0
- package/README.md +0 -79
- package/compiler/dist/index.d.ts +0 -9
- package/compiler/dist/index.js +0 -119
- package/compiler/dist/index.js.map +0 -1
- package/compiler/grammar.pegjs +0 -180
- package/compiler/index.ts +0 -166
- package/compiler/package-lock.json +0 -1850
- package/compiler/package.json +0 -27
- package/compiler/tsconfig.json +0 -29
- package/compiler/tsup.config.ts +0 -14
- 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/tsconfig.node.json +0 -10
- package/tsup.config.ts +0 -13
- package/vitest.config.ts +0 -12
package/src/engine/utils.ts
CHANGED
|
@@ -1,17 +1,37 @@
|
|
|
1
|
+
import { isSignal } from "@signe/reactive"
|
|
1
2
|
import { ObservablePoint } from "pixi.js"
|
|
3
|
+
import { Observable } from "rxjs"
|
|
2
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Checks if code is running in a browser environment
|
|
7
|
+
* @returns {boolean} True if running in browser, false otherwise
|
|
8
|
+
*/
|
|
3
9
|
export function isBrowser(): boolean {
|
|
4
10
|
return typeof window !== 'undefined'
|
|
5
11
|
}
|
|
6
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Returns current high-resolution timestamp
|
|
15
|
+
* @returns {number} Current time in milliseconds
|
|
16
|
+
*/
|
|
7
17
|
export function preciseNow(): number {
|
|
8
18
|
return typeof performance !== 'undefined' ? performance.now() : Date.now()
|
|
9
19
|
}
|
|
10
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Converts frames per second to milliseconds
|
|
23
|
+
* @param {number} fps - Frames per second
|
|
24
|
+
* @returns {number} Milliseconds per frame
|
|
25
|
+
*/
|
|
11
26
|
export function fps2ms(fps: number): number {
|
|
12
27
|
return 1000 / fps
|
|
13
28
|
}
|
|
14
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Checks if a value is a Promise
|
|
32
|
+
* @param {any} value - Value to check
|
|
33
|
+
* @returns {boolean} True if value is a Promise, false otherwise
|
|
34
|
+
*/
|
|
15
35
|
export function isPromise(value: any): boolean {
|
|
16
36
|
return value && value instanceof Promise
|
|
17
37
|
}
|
|
@@ -58,15 +78,49 @@ function deepEquals(a: any, b: any): boolean {
|
|
|
58
78
|
return false;
|
|
59
79
|
}
|
|
60
80
|
|
|
81
|
+
/**
|
|
82
|
+
* Checks if a value is a function
|
|
83
|
+
* @param {unknown} val - Value to check
|
|
84
|
+
* @returns {boolean} True if value is a function, false otherwise
|
|
85
|
+
*/
|
|
61
86
|
export function isFunction(val: unknown): boolean {
|
|
62
87
|
return {}.toString.call(val) === '[object Function]'
|
|
63
88
|
}
|
|
64
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Checks if a value is a plain object (not an instance of a class)
|
|
92
|
+
* @param {unknown} val - Value to check
|
|
93
|
+
* @returns {boolean} True if value is a plain object (not null, not array, not instance), false otherwise
|
|
94
|
+
* @example
|
|
95
|
+
* ```ts
|
|
96
|
+
* isObject({}) // true
|
|
97
|
+
* isObject(new Date()) // false
|
|
98
|
+
* isObject([]) // false
|
|
99
|
+
* isObject(null) // false
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
65
102
|
export function isObject(val: unknown): boolean {
|
|
66
|
-
return typeof val == 'object' && val != null && !Array.isArray(val)
|
|
103
|
+
return typeof val == 'object' && val != null && !Array.isArray(val) && val.constructor === Object
|
|
67
104
|
}
|
|
68
105
|
|
|
69
|
-
export function
|
|
106
|
+
export function isObservable(val: unknown): boolean {
|
|
107
|
+
return val instanceof Observable
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Sets a value in an object using a dot notation path
|
|
112
|
+
* @param {Record<string, any>} obj - Target object
|
|
113
|
+
* @param {string | string[]} path - Path to set value at (e.g., 'a.b.c' or ['a', 'b', 'c'])
|
|
114
|
+
* @param {any} value - Value to set
|
|
115
|
+
* @param {boolean} onlyPlainObject - If true, only creates plain objects in path
|
|
116
|
+
* @returns {Record<string, any>} Modified object
|
|
117
|
+
*/
|
|
118
|
+
export function set(
|
|
119
|
+
obj: Record<string, any>,
|
|
120
|
+
path: string | string[],
|
|
121
|
+
value: any,
|
|
122
|
+
onlyPlainObject = false
|
|
123
|
+
): Record<string, any> {
|
|
70
124
|
if (Object(obj) !== obj) return obj;
|
|
71
125
|
|
|
72
126
|
if (typeof path === 'string') {
|
|
@@ -79,8 +133,8 @@ export function set(obj, path, value, onlyPlainObject = false) {
|
|
|
79
133
|
let current = obj;
|
|
80
134
|
for (let i = 0; i < len - 1; i++) {
|
|
81
135
|
let segment = path[i];
|
|
82
|
-
let nextSegment = path[i + 1];
|
|
83
|
-
let isNextNumeric = !isNaN(nextSegment) && isFinite(nextSegment);
|
|
136
|
+
let nextSegment: number | string = path[i + 1];
|
|
137
|
+
let isNextNumeric = !isNaN(Number(nextSegment)) && isFinite(Number(nextSegment));
|
|
84
138
|
|
|
85
139
|
if (!current[segment] || typeof current[segment] !== 'object') {
|
|
86
140
|
current[segment] = (isNextNumeric && !onlyPlainObject) ? [] : {};
|
|
@@ -94,7 +148,13 @@ export function set(obj, path, value, onlyPlainObject = false) {
|
|
|
94
148
|
return obj;
|
|
95
149
|
}
|
|
96
150
|
|
|
97
|
-
|
|
151
|
+
/**
|
|
152
|
+
* Gets a value from an object using a dot notation path
|
|
153
|
+
* @param {Record<string, any>} obj - Source object
|
|
154
|
+
* @param {string} path - Path to get value from (e.g., 'a.b.c')
|
|
155
|
+
* @returns {any} Value at path or undefined if not found
|
|
156
|
+
*/
|
|
157
|
+
export function get(obj: Record<string, any>, path: string): any {
|
|
98
158
|
const keys = path.split('.');
|
|
99
159
|
let current = obj;
|
|
100
160
|
|
|
@@ -108,27 +168,59 @@ export function get(obj, path) {
|
|
|
108
168
|
return current;
|
|
109
169
|
}
|
|
110
170
|
|
|
111
|
-
|
|
171
|
+
/**
|
|
172
|
+
* Logs a message to console
|
|
173
|
+
* @param {any} text - Message to log
|
|
174
|
+
*/
|
|
175
|
+
export function log(text: any): void {
|
|
112
176
|
console.log(text)
|
|
113
177
|
}
|
|
114
178
|
|
|
115
|
-
|
|
179
|
+
/**
|
|
180
|
+
* Logs an error message to console
|
|
181
|
+
* @param {any} text - Error message to log
|
|
182
|
+
*/
|
|
183
|
+
export function error(text: any): void {
|
|
116
184
|
console.error(text)
|
|
117
185
|
}
|
|
118
186
|
|
|
119
|
-
|
|
187
|
+
/**
|
|
188
|
+
* Sets the position of an ObservablePoint using various input formats
|
|
189
|
+
* @param {ObservablePoint} observablePoint - The point to modify
|
|
190
|
+
* @param {Object | number | [number, number]} point - New position value
|
|
191
|
+
*/
|
|
192
|
+
export function setObservablePoint(
|
|
193
|
+
observablePoint: ObservablePoint,
|
|
194
|
+
point: { x: number, y: number } | number | [number, number]
|
|
195
|
+
): void {
|
|
120
196
|
if (typeof point === 'number') {
|
|
121
197
|
observablePoint.set(point);
|
|
122
198
|
}
|
|
123
199
|
else if (Array.isArray(point)) {
|
|
124
200
|
observablePoint.set(point[0], point[1]);
|
|
125
201
|
}
|
|
202
|
+
else if (isObject(point) && 'value' in point) {
|
|
203
|
+
observablePoint.set((point as any).value.x, (point as any).value.y);
|
|
204
|
+
}
|
|
126
205
|
else {
|
|
127
206
|
observablePoint.set(point.x, point.y);
|
|
128
207
|
}
|
|
129
208
|
}
|
|
130
209
|
|
|
131
|
-
|
|
210
|
+
/**
|
|
211
|
+
* Calculates the Euclidean distance between two points
|
|
212
|
+
* @param {number} x1 - X coordinate of first point
|
|
213
|
+
* @param {number} y1 - Y coordinate of first point
|
|
214
|
+
* @param {number} x2 - X coordinate of second point
|
|
215
|
+
* @param {number} y2 - Y coordinate of second point
|
|
216
|
+
* @returns {number} Distance between the points
|
|
217
|
+
*/
|
|
218
|
+
export function calculateDistance(
|
|
219
|
+
x1: number,
|
|
220
|
+
y1: number,
|
|
221
|
+
x2: number,
|
|
222
|
+
y2: number
|
|
223
|
+
): number {
|
|
132
224
|
const dx = x1 - x2;
|
|
133
225
|
const dy = y1 - y2;
|
|
134
226
|
return Math.sqrt(dx * dx + dy * dy);
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { Signal } from "@signe/reactive";
|
|
2
|
+
import { Element } from "../engine/reactive";
|
|
3
|
+
import { focusManager } from "../engine/FocusManager";
|
|
4
|
+
import { effect } from "@signe/reactive";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Get the current focus index signal for a container
|
|
8
|
+
*
|
|
9
|
+
* Returns a reactive signal that updates when the focus index changes.
|
|
10
|
+
*
|
|
11
|
+
* @param containerId - Container identifier
|
|
12
|
+
* @returns Signal for current focus index, or null if container not found
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* const focusIndex = useFocusIndex('myContainer');
|
|
17
|
+
* effect(() => {
|
|
18
|
+
* console.log('Current focus index:', focusIndex?.());
|
|
19
|
+
* });
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export function useFocusIndex(containerId: string): Signal<number | null> | null {
|
|
23
|
+
return focusManager.getCurrentIndexSignal(containerId);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Get the current focused element signal for a container
|
|
28
|
+
*
|
|
29
|
+
* Returns a reactive signal that updates when the focused element changes.
|
|
30
|
+
*
|
|
31
|
+
* @param containerId - Container identifier
|
|
32
|
+
* @returns Signal for current focused element, or null if container not found
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```typescript
|
|
36
|
+
* const focusedElement = useFocusedElement('myContainer');
|
|
37
|
+
* effect(() => {
|
|
38
|
+
* const element = focusedElement?.();
|
|
39
|
+
* if (element) {
|
|
40
|
+
* console.log('Focused element:', element);
|
|
41
|
+
* }
|
|
42
|
+
* });
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export function useFocusedElement(containerId: string): Signal<Element | null> | null {
|
|
46
|
+
return focusManager.getFocusedElementSignal(containerId);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Hook to react to focus changes
|
|
51
|
+
*
|
|
52
|
+
* Sets up a reactive effect that calls the callback whenever the focus changes.
|
|
53
|
+
*
|
|
54
|
+
* @param containerId - Container identifier
|
|
55
|
+
* @param callback - Function to call when focus changes
|
|
56
|
+
* @returns Cleanup function to unsubscribe
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* useFocusChange('myContainer', (index, element) => {
|
|
61
|
+
* console.log('Focus changed to index', index);
|
|
62
|
+
* if (element) {
|
|
63
|
+
* console.log('Focused element:', element);
|
|
64
|
+
* }
|
|
65
|
+
* });
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
export function useFocusChange(
|
|
69
|
+
containerId: string,
|
|
70
|
+
callback: (index: number | null, element: Element | null) => void
|
|
71
|
+
): () => void {
|
|
72
|
+
const indexSignal = focusManager.getCurrentIndexSignal(containerId);
|
|
73
|
+
const elementSignal = focusManager.getFocusedElementSignal(containerId);
|
|
74
|
+
|
|
75
|
+
if (!indexSignal || !elementSignal) {
|
|
76
|
+
console.warn(`FocusContainer with id "${containerId}" not found`);
|
|
77
|
+
return () => {};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Set up reactive effect
|
|
81
|
+
const effectResult = effect(() => {
|
|
82
|
+
const index = indexSignal();
|
|
83
|
+
const element = elementSignal();
|
|
84
|
+
callback(index, element);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Return cleanup function
|
|
88
|
+
return () => {
|
|
89
|
+
effectResult.subscription?.unsubscribe();
|
|
90
|
+
};
|
|
91
|
+
}
|
package/src/hooks/useProps.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { isSignal, signal } from "@signe/reactive"
|
|
2
2
|
import { isPrimitive } from "../engine/reactive"
|
|
3
|
+
import { currentDefinePropsTracker } from "../engine/signal"
|
|
4
|
+
import { isTrigger } from "../engine/trigger"
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* Converts props into reactive signals if they are primitive values.
|
|
@@ -25,7 +27,7 @@ export const useProps = (props, defaults = {}): any => {
|
|
|
25
27
|
}
|
|
26
28
|
for (let key in defaults) {
|
|
27
29
|
if (!(key in obj)) {
|
|
28
|
-
obj[key] = signal(defaults[key])
|
|
30
|
+
obj[key] = isPrimitive(defaults[key]) ? signal(defaults[key]) : defaults[key]
|
|
29
31
|
}
|
|
30
32
|
}
|
|
31
33
|
return obj
|
|
@@ -46,6 +48,45 @@ type PropSchema = {
|
|
|
46
48
|
[key: string]: PropType | PropType[] | PropConfig;
|
|
47
49
|
}
|
|
48
50
|
|
|
51
|
+
const DEFINE_PROPS_CALLABLE_SIGNAL_VALUE = "__canvasEngineCallableSignalValue"
|
|
52
|
+
|
|
53
|
+
const createCallablePropSignal = (value: (...args: any[]) => any) => {
|
|
54
|
+
const valueSignal: any = signal(value)
|
|
55
|
+
const callable: any = (...args: any[]) => valueSignal()(...args)
|
|
56
|
+
|
|
57
|
+
callable[DEFINE_PROPS_CALLABLE_SIGNAL_VALUE] = valueSignal
|
|
58
|
+
callable.set = valueSignal.set
|
|
59
|
+
callable.freeze = valueSignal.freeze
|
|
60
|
+
callable.unfreeze = valueSignal.unfreeze
|
|
61
|
+
callable.mutate = valueSignal.mutate
|
|
62
|
+
callable.update = valueSignal.update
|
|
63
|
+
callable.observable = valueSignal.observable
|
|
64
|
+
callable._subject = valueSignal._subject
|
|
65
|
+
|
|
66
|
+
Object.defineProperty(callable, "_isFrozen", {
|
|
67
|
+
get: () => valueSignal._isFrozen,
|
|
68
|
+
set: (value) => {
|
|
69
|
+
valueSignal._isFrozen = value
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
return callable
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const toPropSignal = (value: any) => {
|
|
77
|
+
if (isSignal(value) || isTrigger(value)) return value
|
|
78
|
+
if (typeof value === 'function') return createCallablePropSignal(value)
|
|
79
|
+
return signal(value)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const definePropSignals = (props: any): any => {
|
|
83
|
+
const obj: any = {}
|
|
84
|
+
for (let key in props) {
|
|
85
|
+
obj[key] = toPropSignal(props[key])
|
|
86
|
+
}
|
|
87
|
+
return obj
|
|
88
|
+
}
|
|
89
|
+
|
|
49
90
|
/**
|
|
50
91
|
* Validates and defines properties based on a schema.
|
|
51
92
|
*
|
|
@@ -109,18 +150,62 @@ export const useDefineProps = (props: any) => {
|
|
|
109
150
|
}
|
|
110
151
|
}
|
|
111
152
|
|
|
112
|
-
validatedProps[key] =
|
|
113
|
-
? validatedValue
|
|
114
|
-
: signal(validatedValue)
|
|
153
|
+
validatedProps[key] = toPropSignal(validatedValue)
|
|
115
154
|
}
|
|
116
155
|
|
|
117
|
-
|
|
118
|
-
...
|
|
156
|
+
const definedProps = {
|
|
157
|
+
...definePropSignals(rawProps),
|
|
119
158
|
...validatedProps
|
|
120
159
|
}
|
|
160
|
+
currentDefinePropsTracker?.(definedProps)
|
|
161
|
+
|
|
162
|
+
return definedProps
|
|
121
163
|
}
|
|
122
164
|
}
|
|
123
165
|
|
|
166
|
+
const getRawProps = (props: any) => isSignal(props) ? props() : (props ?? {})
|
|
167
|
+
|
|
168
|
+
const getEmitHandler = (handler: any) => {
|
|
169
|
+
if (handler?.[DEFINE_PROPS_CALLABLE_SIGNAL_VALUE]) return handler
|
|
170
|
+
if (isSignal(handler)) return handler()
|
|
171
|
+
return handler
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Defines event emitters from component props.
|
|
176
|
+
*
|
|
177
|
+
* @param {object} props - The raw component props containing event handlers.
|
|
178
|
+
* @returns {function} A function returning named emit callbacks.
|
|
179
|
+
*
|
|
180
|
+
* @example
|
|
181
|
+
* const { select } = useDefineEmits({ select: (item) => item.id })();
|
|
182
|
+
* select({ id: 1 });
|
|
183
|
+
*/
|
|
184
|
+
export const useDefineEmits = (props: any) => {
|
|
185
|
+
return () => new Proxy({}, {
|
|
186
|
+
get(_target, key) {
|
|
187
|
+
if (typeof key !== 'string') {
|
|
188
|
+
return undefined
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return (...args: any[]) => {
|
|
192
|
+
const rawProps = getRawProps(props)
|
|
193
|
+
const handler = getEmitHandler(rawProps[key])
|
|
194
|
+
|
|
195
|
+
if (handler === undefined || handler === null) {
|
|
196
|
+
return undefined
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (typeof handler !== 'function') {
|
|
200
|
+
throw new Error(`Invalid emit handler: "${key}" must be a function`)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return handler(...args)
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
})
|
|
207
|
+
}
|
|
208
|
+
|
|
124
209
|
/**
|
|
125
210
|
* Validates the type of a property.
|
|
126
211
|
*
|
|
@@ -152,4 +237,4 @@ function validateType(key: string, value: any, types: any[]) {
|
|
|
152
237
|
`Expected ${types.map(t => t.name).join(' or ')}`
|
|
153
238
|
)
|
|
154
239
|
}
|
|
155
|
-
}
|
|
240
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
|
+
export * from './directives'
|
|
2
3
|
export * from '@signe/reactive'
|
|
3
4
|
export { Howler } from 'howler'
|
|
4
5
|
export * from './components'
|
|
5
6
|
export * from './engine/reactive'
|
|
7
|
+
export { registerAllComponents } from './engine/reactive'
|
|
6
8
|
export * from './engine/signal'
|
|
7
9
|
export * from './engine/trigger'
|
|
8
10
|
export * from './engine/bootstrap'
|
|
9
11
|
export * from './engine/animation'
|
|
10
|
-
export {
|
|
11
|
-
|
|
12
|
-
export
|
|
13
|
-
export * from './
|
|
14
|
-
export * from './utils/
|
|
12
|
+
export { FocusManager, focusManager, type ScrollOptions } from './engine/FocusManager'
|
|
13
|
+
export { useProps, useDefineProps, useDefineEmits } from './hooks/useProps'
|
|
14
|
+
export { useFocusIndex, useFocusedElement, useFocusChange } from './hooks/useFocus'
|
|
15
|
+
export * from './utils/Ease'
|
|
16
|
+
export * from './utils/RadialGradient'
|
|
17
|
+
export * from './utils/tabindex'
|
|
18
|
+
export * from './components/DisplayObject'
|
|
19
|
+
export { isObservable } from 'rxjs'
|
|
20
|
+
export * as Utils from './engine/utils'
|
|
21
|
+
export * as Howl from 'howler'
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global Asset Loader
|
|
3
|
+
*
|
|
4
|
+
* Tracks the loading progress of all assets (images, spritesheets, etc.) across all sprites in a component tree.
|
|
5
|
+
* This allows components to know when all assets are loaded, useful for displaying loaders or progress bars.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* const loader = new GlobalAssetLoader();
|
|
10
|
+
*
|
|
11
|
+
* loader.onProgress((progress) => {
|
|
12
|
+
* console.log(`Loading: ${(progress * 100).toFixed(0)}%`);
|
|
13
|
+
* });
|
|
14
|
+
*
|
|
15
|
+
* loader.onComplete(() => {
|
|
16
|
+
* console.log('All assets loaded!');
|
|
17
|
+
* });
|
|
18
|
+
*
|
|
19
|
+
* // Register assets as they start loading
|
|
20
|
+
* const assetId = loader.registerAsset('path/to/image.png');
|
|
21
|
+
*
|
|
22
|
+
* // Update progress
|
|
23
|
+
* loader.updateProgress(assetId, 0.5);
|
|
24
|
+
*
|
|
25
|
+
* // Mark as complete
|
|
26
|
+
* loader.completeAsset(assetId);
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export class GlobalAssetLoader {
|
|
30
|
+
private assets: Map<string, { progress: number; completed: boolean }> = new Map();
|
|
31
|
+
private onProgressCallbacks: Set<(progress: number) => void> = new Set();
|
|
32
|
+
private onCompleteCallbacks: Set<() => void> = new Set();
|
|
33
|
+
private assetCounter: number = 0;
|
|
34
|
+
private isComplete: boolean = false;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Registers a new asset to track
|
|
38
|
+
*
|
|
39
|
+
* @param assetPath - The path or identifier of the asset being loaded
|
|
40
|
+
* @returns A unique ID for this asset that should be used for progress updates
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* const assetId = loader.registerAsset('path/to/image.png');
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
registerAsset(assetPath: string): string {
|
|
48
|
+
const assetId = `asset_${this.assetCounter++}_${assetPath}`;
|
|
49
|
+
this.assets.set(assetId, { progress: 0, completed: false });
|
|
50
|
+
this.isComplete = false;
|
|
51
|
+
this.updateGlobalProgress();
|
|
52
|
+
return assetId;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Updates the progress of a specific asset
|
|
57
|
+
*
|
|
58
|
+
* @param assetId - The ID returned by registerAsset
|
|
59
|
+
* @param progress - Progress value between 0 and 1
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* loader.updateProgress(assetId, 0.5); // 50% loaded
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
updateProgress(assetId: string, progress: number): void {
|
|
67
|
+
const asset = this.assets.get(assetId);
|
|
68
|
+
if (!asset) {
|
|
69
|
+
console.warn(`Asset ${assetId} not found in tracker`);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
asset.progress = Math.max(0, Math.min(1, progress));
|
|
74
|
+
this.updateGlobalProgress();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Marks an asset as completely loaded
|
|
79
|
+
*
|
|
80
|
+
* @param assetId - The ID returned by registerAsset
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```typescript
|
|
84
|
+
* loader.completeAsset(assetId);
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
completeAsset(assetId: string): void {
|
|
88
|
+
const asset = this.assets.get(assetId);
|
|
89
|
+
if (!asset) {
|
|
90
|
+
console.warn(`Asset ${assetId} not found in tracker`);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
asset.progress = 1;
|
|
95
|
+
asset.completed = true;
|
|
96
|
+
this.updateGlobalProgress();
|
|
97
|
+
this.checkCompletion();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Removes an asset from tracking (useful for cleanup)
|
|
102
|
+
*
|
|
103
|
+
* @param assetId - The ID returned by registerAsset
|
|
104
|
+
*/
|
|
105
|
+
removeAsset(assetId: string): void {
|
|
106
|
+
this.assets.delete(assetId);
|
|
107
|
+
this.updateGlobalProgress();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Registers a callback that will be called whenever the global progress changes
|
|
112
|
+
*
|
|
113
|
+
* @param callback - Function that receives the global progress (0-1)
|
|
114
|
+
* @returns A function to unregister the callback
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* ```typescript
|
|
118
|
+
* const unsubscribe = loader.onProgress((progress) => {
|
|
119
|
+
* console.log(`Loading: ${(progress * 100).toFixed(0)}%`);
|
|
120
|
+
* });
|
|
121
|
+
*
|
|
122
|
+
* // Later, to unsubscribe:
|
|
123
|
+
* unsubscribe();
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
onProgress(callback: (progress: number) => void): () => void {
|
|
127
|
+
this.onProgressCallbacks.add(callback);
|
|
128
|
+
|
|
129
|
+
// Immediately call with current progress (wrap in try-catch for safety)
|
|
130
|
+
try {
|
|
131
|
+
callback(this.getGlobalProgress());
|
|
132
|
+
} catch (error) {
|
|
133
|
+
console.error('Error in onProgress callback:', error);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return () => {
|
|
137
|
+
this.onProgressCallbacks.delete(callback);
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Registers a callback that will be called when all assets are loaded
|
|
143
|
+
*
|
|
144
|
+
* @param callback - Function to call when all assets are complete
|
|
145
|
+
* @returns A function to unregister the callback
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```typescript
|
|
149
|
+
* const unsubscribe = loader.onComplete(() => {
|
|
150
|
+
* console.log('All assets loaded!');
|
|
151
|
+
* });
|
|
152
|
+
*
|
|
153
|
+
* // Later, to unsubscribe:
|
|
154
|
+
* unsubscribe();
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
onComplete(callback: () => void): () => void {
|
|
158
|
+
this.onCompleteCallbacks.add(callback);
|
|
159
|
+
|
|
160
|
+
// If already complete, call immediately
|
|
161
|
+
if (this.isComplete) {
|
|
162
|
+
callback();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return () => {
|
|
166
|
+
this.onCompleteCallbacks.delete(callback);
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Gets the current global progress (0-1)
|
|
172
|
+
*
|
|
173
|
+
* @returns Progress value between 0 and 1
|
|
174
|
+
*/
|
|
175
|
+
getGlobalProgress(): number {
|
|
176
|
+
if (this.assets.size === 0) {
|
|
177
|
+
return 1; // No assets means everything is "loaded"
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
let totalProgress = 0;
|
|
181
|
+
for (const asset of this.assets.values()) {
|
|
182
|
+
totalProgress += asset.progress;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return totalProgress / this.assets.size;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Gets the number of assets currently being tracked
|
|
190
|
+
*
|
|
191
|
+
* @returns Number of registered assets
|
|
192
|
+
*/
|
|
193
|
+
getAssetCount(): number {
|
|
194
|
+
return this.assets.size;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Gets the number of completed assets
|
|
199
|
+
*
|
|
200
|
+
* @returns Number of completed assets
|
|
201
|
+
*/
|
|
202
|
+
getCompletedCount(): number {
|
|
203
|
+
let count = 0;
|
|
204
|
+
for (const asset of this.assets.values()) {
|
|
205
|
+
if (asset.completed) count++;
|
|
206
|
+
}
|
|
207
|
+
return count;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Checks if all assets are loaded and triggers onComplete callbacks
|
|
212
|
+
*/
|
|
213
|
+
private checkCompletion(): void {
|
|
214
|
+
if (this.isComplete) return;
|
|
215
|
+
|
|
216
|
+
const allCompleted = Array.from(this.assets.values()).every(asset => asset.completed);
|
|
217
|
+
|
|
218
|
+
if (allCompleted && this.assets.size > 0) {
|
|
219
|
+
this.isComplete = true;
|
|
220
|
+
this.onCompleteCallbacks.forEach(callback => {
|
|
221
|
+
try {
|
|
222
|
+
callback();
|
|
223
|
+
} catch (error) {
|
|
224
|
+
console.error('Error in onComplete callback:', error);
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Updates global progress and notifies all progress callbacks
|
|
232
|
+
*/
|
|
233
|
+
private updateGlobalProgress(): void {
|
|
234
|
+
const progress = this.getGlobalProgress();
|
|
235
|
+
// Create a copy of callbacks to avoid issues if callbacks modify the set
|
|
236
|
+
const callbacks = Array.from(this.onProgressCallbacks);
|
|
237
|
+
callbacks.forEach(callback => {
|
|
238
|
+
try {
|
|
239
|
+
callback(progress);
|
|
240
|
+
} catch (error) {
|
|
241
|
+
console.error('Error in onProgress callback:', error);
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Resets the loader, clearing all assets and callbacks
|
|
248
|
+
*/
|
|
249
|
+
reset(): void {
|
|
250
|
+
this.assets.clear();
|
|
251
|
+
this.onProgressCallbacks.clear();
|
|
252
|
+
this.onCompleteCallbacks.clear();
|
|
253
|
+
this.assetCounter = 0;
|
|
254
|
+
this.isComplete = false;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|