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.
Files changed (212) hide show
  1. package/dist/DebugRenderer-DkjTAc48.js +1384 -0
  2. package/dist/DebugRenderer-DkjTAc48.js.map +1 -0
  3. package/dist/components/Button.d.ts +185 -0
  4. package/dist/components/Button.d.ts.map +1 -0
  5. package/dist/components/Canvas.d.ts +17 -0
  6. package/dist/components/Canvas.d.ts.map +1 -0
  7. package/dist/components/Container.d.ts +25 -0
  8. package/dist/components/Container.d.ts.map +1 -0
  9. package/dist/components/DOMContainer.d.ts +28 -0
  10. package/dist/components/DOMContainer.d.ts.map +1 -0
  11. package/dist/components/DOMElement.d.ts +54 -0
  12. package/dist/components/DOMElement.d.ts.map +1 -0
  13. package/dist/components/DOMSprite.d.ts +127 -0
  14. package/dist/components/DOMSprite.d.ts.map +1 -0
  15. package/dist/components/DisplayObject.d.ts +18 -0
  16. package/dist/components/DisplayObject.d.ts.map +1 -0
  17. package/dist/components/FocusContainer.d.ts +129 -0
  18. package/dist/components/FocusContainer.d.ts.map +1 -0
  19. package/dist/components/Graphic.d.ts +64 -0
  20. package/dist/components/Graphic.d.ts.map +1 -0
  21. package/dist/components/Joystick.d.ts +37 -0
  22. package/dist/components/Joystick.d.ts.map +1 -0
  23. package/dist/components/Mesh.d.ts +138 -0
  24. package/dist/components/Mesh.d.ts.map +1 -0
  25. package/dist/components/NineSliceSprite.d.ts +16 -0
  26. package/dist/components/NineSliceSprite.d.ts.map +1 -0
  27. package/dist/components/ParticleEmitter.d.ts +4 -0
  28. package/dist/components/ParticleEmitter.d.ts.map +1 -0
  29. package/dist/components/Scene.d.ts +2 -0
  30. package/dist/components/Scene.d.ts.map +1 -0
  31. package/dist/components/Sprite.d.ts +182 -0
  32. package/dist/components/Sprite.d.ts.map +1 -0
  33. package/dist/components/Text.d.ts +24 -0
  34. package/dist/components/Text.d.ts.map +1 -0
  35. package/dist/components/TilingSprite.d.ts +17 -0
  36. package/dist/components/TilingSprite.d.ts.map +1 -0
  37. package/dist/components/Video.d.ts +14 -0
  38. package/dist/components/Video.d.ts.map +1 -0
  39. package/dist/components/Viewport.d.ts +52 -0
  40. package/dist/components/Viewport.d.ts.map +1 -0
  41. package/dist/components/index.d.ts +20 -0
  42. package/dist/components/index.d.ts.map +1 -0
  43. package/dist/components/types/DisplayObject.d.ts +127 -0
  44. package/dist/components/types/DisplayObject.d.ts.map +1 -0
  45. package/dist/components/types/MouseEvent.d.ts +4 -0
  46. package/dist/components/types/MouseEvent.d.ts.map +1 -0
  47. package/dist/components/types/Spritesheet.d.ts +248 -0
  48. package/dist/components/types/Spritesheet.d.ts.map +1 -0
  49. package/dist/components/types/index.d.ts +4 -0
  50. package/dist/components/types/index.d.ts.map +1 -0
  51. package/dist/directives/Controls.d.ts +112 -0
  52. package/dist/directives/Controls.d.ts.map +1 -0
  53. package/dist/directives/ControlsBase.d.ts +199 -0
  54. package/dist/directives/ControlsBase.d.ts.map +1 -0
  55. package/dist/directives/Drag.d.ts +69 -0
  56. package/dist/directives/Drag.d.ts.map +1 -0
  57. package/dist/directives/Flash.d.ts +116 -0
  58. package/dist/directives/Flash.d.ts.map +1 -0
  59. package/dist/directives/FocusNavigation.d.ts +52 -0
  60. package/dist/directives/FocusNavigation.d.ts.map +1 -0
  61. package/dist/directives/FogVisibility.d.ts +47 -0
  62. package/dist/directives/FogVisibility.d.ts.map +1 -0
  63. package/dist/directives/GamepadControls.d.ts +224 -0
  64. package/dist/directives/GamepadControls.d.ts.map +1 -0
  65. package/dist/directives/JoystickControls.d.ts +171 -0
  66. package/dist/directives/JoystickControls.d.ts.map +1 -0
  67. package/dist/directives/KeyboardControls.d.ts +219 -0
  68. package/dist/directives/KeyboardControls.d.ts.map +1 -0
  69. package/dist/directives/Scheduler.d.ts +36 -0
  70. package/dist/directives/Scheduler.d.ts.map +1 -0
  71. package/dist/directives/Shake.d.ts +98 -0
  72. package/dist/directives/Shake.d.ts.map +1 -0
  73. package/dist/directives/Sound.d.ts +25 -0
  74. package/dist/directives/Sound.d.ts.map +1 -0
  75. package/dist/directives/SpriteEffects.d.ts +70 -0
  76. package/dist/directives/SpriteEffects.d.ts.map +1 -0
  77. package/dist/directives/Transition.d.ts +10 -0
  78. package/dist/directives/Transition.d.ts.map +1 -0
  79. package/dist/directives/ViewportCull.d.ts +11 -0
  80. package/dist/directives/ViewportCull.d.ts.map +1 -0
  81. package/dist/directives/ViewportFollow.d.ts +18 -0
  82. package/dist/directives/ViewportFollow.d.ts.map +1 -0
  83. package/dist/directives/index.d.ts +15 -0
  84. package/dist/directives/index.d.ts.map +1 -0
  85. package/dist/dist-BOOc43Qm.js +778 -0
  86. package/dist/dist-BOOc43Qm.js.map +1 -0
  87. package/dist/engine/FocusManager.d.ts +174 -0
  88. package/dist/engine/FocusManager.d.ts.map +1 -0
  89. package/dist/engine/animation.d.ts +72 -0
  90. package/dist/engine/animation.d.ts.map +1 -0
  91. package/dist/engine/bootstrap.d.ts +52 -0
  92. package/dist/engine/bootstrap.d.ts.map +1 -0
  93. package/dist/engine/directive.d.ts +13 -0
  94. package/dist/engine/directive.d.ts.map +1 -0
  95. package/dist/engine/reactive.d.ts +139 -0
  96. package/dist/engine/reactive.d.ts.map +1 -0
  97. package/dist/engine/signal.d.ts +74 -0
  98. package/dist/engine/signal.d.ts.map +1 -0
  99. package/dist/engine/trigger.d.ts +54 -0
  100. package/dist/engine/trigger.d.ts.map +1 -0
  101. package/dist/engine/utils.d.ts +89 -0
  102. package/dist/engine/utils.d.ts.map +1 -0
  103. package/dist/hooks/addContext.d.ts +2 -0
  104. package/dist/hooks/addContext.d.ts.map +1 -0
  105. package/dist/hooks/useFocus.d.ts +60 -0
  106. package/dist/hooks/useFocus.d.ts.map +1 -0
  107. package/dist/hooks/useProps.d.ts +53 -0
  108. package/dist/hooks/useProps.d.ts.map +1 -0
  109. package/dist/hooks/useRef.d.ts +4 -0
  110. package/dist/hooks/useRef.d.ts.map +1 -0
  111. package/dist/index.d.ts +19 -844
  112. package/dist/index.d.ts.map +1 -0
  113. package/dist/index.global.js +189 -0
  114. package/dist/index.global.js.map +1 -0
  115. package/dist/index.js +15156 -3287
  116. package/dist/index.js.map +1 -1
  117. package/dist/utils/Ease.d.ts +17 -0
  118. package/dist/utils/Ease.d.ts.map +1 -0
  119. package/dist/utils/GlobalAssetLoader.d.ts +141 -0
  120. package/dist/utils/GlobalAssetLoader.d.ts.map +1 -0
  121. package/dist/utils/RadialGradient.d.ts +57 -0
  122. package/dist/utils/RadialGradient.d.ts.map +1 -0
  123. package/dist/utils/functions.d.ts +2 -0
  124. package/dist/utils/functions.d.ts.map +1 -0
  125. package/dist/utils/tabindex.d.ts +16 -0
  126. package/dist/utils/tabindex.d.ts.map +1 -0
  127. package/index.d.ts +1 -3
  128. package/package.json +19 -52
  129. package/src/components/Button.ts +406 -0
  130. package/src/components/Canvas.ts +82 -51
  131. package/src/components/Container.ts +31 -4
  132. package/src/components/DOMContainer.ts +387 -0
  133. package/src/components/DOMElement.ts +556 -0
  134. package/src/components/DOMSprite.ts +1044 -0
  135. package/src/components/DisplayObject.ts +423 -202
  136. package/src/components/FocusContainer.ts +368 -0
  137. package/src/components/Graphic.ts +255 -68
  138. package/src/components/Joystick.ts +269 -0
  139. package/src/components/Mesh.ts +239 -0
  140. package/src/components/NineSliceSprite.ts +4 -1
  141. package/src/components/ParticleEmitter.ts +12 -8
  142. package/src/components/Sprite.ts +441 -54
  143. package/src/components/Text.ts +270 -26
  144. package/src/components/Video.ts +110 -0
  145. package/src/components/Viewport.ts +122 -63
  146. package/src/components/index.ts +12 -5
  147. package/src/components/types/DisplayObject.ts +64 -5
  148. package/src/components/types/Spritesheet.ts +0 -118
  149. package/src/directives/Controls.ts +254 -0
  150. package/src/directives/ControlsBase.ts +267 -0
  151. package/src/directives/Drag.ts +357 -52
  152. package/src/directives/Flash.ts +419 -0
  153. package/src/directives/FocusNavigation.ts +113 -0
  154. package/src/directives/FogVisibility.ts +273 -0
  155. package/src/directives/GamepadControls.ts +537 -0
  156. package/src/directives/JoystickControls.ts +396 -0
  157. package/src/directives/KeyboardControls.ts +86 -431
  158. package/src/directives/Scheduler.ts +21 -16
  159. package/src/directives/Shake.ts +298 -0
  160. package/src/directives/Sound.ts +94 -31
  161. package/src/directives/SpriteEffects.ts +461 -0
  162. package/src/directives/ViewportFollow.ts +40 -9
  163. package/src/directives/index.ts +14 -6
  164. package/src/engine/FocusManager.ts +510 -0
  165. package/src/engine/animation.ts +175 -21
  166. package/src/engine/bootstrap.ts +140 -6
  167. package/src/engine/directive.ts +4 -4
  168. package/src/engine/reactive.ts +1266 -167
  169. package/src/engine/signal.ts +335 -47
  170. package/src/engine/trigger.ts +95 -12
  171. package/src/engine/utils.ts +101 -9
  172. package/src/hooks/useFocus.ts +91 -0
  173. package/src/hooks/useProps.ts +92 -7
  174. package/src/index.ts +13 -6
  175. package/src/types/pixi-cull.d.ts +7 -0
  176. package/src/utils/GlobalAssetLoader.ts +257 -0
  177. package/src/utils/RadialGradient.ts +29 -0
  178. package/src/utils/functions.ts +7 -0
  179. package/src/utils/tabindex.ts +70 -0
  180. package/testing/index.ts +42 -0
  181. package/tsconfig.json +15 -25
  182. package/vite.config.ts +39 -0
  183. package/.cursorrules +0 -0
  184. package/.github/workflows/ci.yml +0 -29
  185. package/README.md +0 -79
  186. package/dist/compiler/vite.js +0 -119
  187. package/dist/compiler/vite.js.map +0 -1
  188. package/logo.png +0 -0
  189. package/src/compiler/grammar.pegjs +0 -180
  190. package/src/compiler/vite.ts +0 -166
  191. package/src/components/DrawMap/index.ts +0 -65
  192. package/src/components/Tilemap/Tile.ts +0 -79
  193. package/src/components/Tilemap/TileGroup.ts +0 -207
  194. package/src/components/Tilemap/TileLayer.ts +0 -163
  195. package/src/components/Tilemap/TileSet.ts +0 -41
  196. package/src/components/Tilemap/index.ts +0 -80
  197. package/src/presets/Bar.ts +0 -89
  198. package/src/presets/Button.ts +0 -0
  199. package/src/presets/Joystick.ts +0 -286
  200. package/src/presets/NightAmbiant.ts +0 -122
  201. package/src/presets/Particle.ts +0 -53
  202. package/starter/assets/logo.png +0 -0
  203. package/starter/components/app.ce +0 -18
  204. package/starter/components/hello.ce +0 -35
  205. package/starter/index.html +0 -21
  206. package/starter/main.ts +0 -6
  207. package/starter/package.json +0 -20
  208. package/starter/tsconfig.json +0 -32
  209. package/starter/vite.config.ts +0 -12
  210. package/tsconfig.node.json +0 -10
  211. package/tsup.config.ts +0 -28
  212. package/vitest.config.ts +0 -12
@@ -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 set(obj, path, value, onlyPlainObject = false) {
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
- export function get(obj, path) {
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
- export function log(text) {
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
- export function error(text) {
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
- export function setObservablePoint(observablePoint: ObservablePoint, point: { x: number, y: number } | number | [number, number]): void {
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
- export function calculateDistance(x1, y1, x2, y2) {
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
+ }
@@ -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] = isSignal(validatedValue)
113
- ? validatedValue
114
- : signal(validatedValue)
153
+ validatedProps[key] = toPropSignal(validatedValue)
115
154
  }
116
155
 
117
- return {
118
- ...useProps(rawProps),
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
- import './directives'
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 { useProps, useDefineProps } from './hooks/useProps'
11
-
12
- export * from './presets/Bar'
13
- export * from './presets/Particle'
14
- export * from './utils/Ease'
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,7 @@
1
+ declare module "pixi-cull" {
2
+ export class Simple {
3
+ constructor(options?: any);
4
+ lists: Array<any>;
5
+ cull(bounds: any): void;
6
+ }
7
+ }
@@ -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
+