canvasengine 2.0.0-beta.5 → 2.0.0-beta.50

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 (172) hide show
  1. package/dist/components/Button.d.ts +185 -0
  2. package/dist/components/Button.d.ts.map +1 -0
  3. package/dist/components/Canvas.d.ts +17 -0
  4. package/dist/components/Canvas.d.ts.map +1 -0
  5. package/dist/components/Container.d.ts +86 -0
  6. package/dist/components/Container.d.ts.map +1 -0
  7. package/dist/components/DOMContainer.d.ts +98 -0
  8. package/dist/components/DOMContainer.d.ts.map +1 -0
  9. package/dist/components/DOMElement.d.ts +54 -0
  10. package/dist/components/DOMElement.d.ts.map +1 -0
  11. package/dist/components/DOMSprite.d.ts +127 -0
  12. package/dist/components/DOMSprite.d.ts.map +1 -0
  13. package/dist/components/DisplayObject.d.ts +94 -0
  14. package/dist/components/DisplayObject.d.ts.map +1 -0
  15. package/dist/components/FocusContainer.d.ts +129 -0
  16. package/dist/components/FocusContainer.d.ts.map +1 -0
  17. package/dist/components/Graphic.d.ts +64 -0
  18. package/dist/components/Graphic.d.ts.map +1 -0
  19. package/dist/components/Joystick.d.ts +36 -0
  20. package/dist/components/Joystick.d.ts.map +1 -0
  21. package/dist/components/Mesh.d.ts +208 -0
  22. package/dist/components/Mesh.d.ts.map +1 -0
  23. package/dist/components/NineSliceSprite.d.ts +16 -0
  24. package/dist/components/NineSliceSprite.d.ts.map +1 -0
  25. package/dist/components/ParticleEmitter.d.ts +4 -0
  26. package/dist/components/ParticleEmitter.d.ts.map +1 -0
  27. package/dist/components/Scene.d.ts +2 -0
  28. package/dist/components/Scene.d.ts.map +1 -0
  29. package/dist/components/Sprite.d.ts +242 -0
  30. package/dist/components/Sprite.d.ts.map +1 -0
  31. package/dist/components/Text.d.ts +25 -0
  32. package/dist/components/Text.d.ts.map +1 -0
  33. package/dist/components/TilingSprite.d.ts +17 -0
  34. package/dist/components/TilingSprite.d.ts.map +1 -0
  35. package/dist/components/Video.d.ts +14 -0
  36. package/dist/components/Video.d.ts.map +1 -0
  37. package/dist/components/Viewport.d.ts +121 -0
  38. package/dist/components/Viewport.d.ts.map +1 -0
  39. package/dist/components/index.d.ts +20 -0
  40. package/dist/components/index.d.ts.map +1 -0
  41. package/dist/components/types/DisplayObject.d.ts +106 -0
  42. package/dist/components/types/DisplayObject.d.ts.map +1 -0
  43. package/dist/components/types/MouseEvent.d.ts +4 -0
  44. package/dist/components/types/MouseEvent.d.ts.map +1 -0
  45. package/dist/components/types/Spritesheet.d.ts +248 -0
  46. package/dist/components/types/Spritesheet.d.ts.map +1 -0
  47. package/dist/components/types/index.d.ts +4 -0
  48. package/dist/components/types/index.d.ts.map +1 -0
  49. package/dist/directives/Controls.d.ts +112 -0
  50. package/dist/directives/Controls.d.ts.map +1 -0
  51. package/dist/directives/ControlsBase.d.ts +199 -0
  52. package/dist/directives/ControlsBase.d.ts.map +1 -0
  53. package/dist/directives/Drag.d.ts +69 -0
  54. package/dist/directives/Drag.d.ts.map +1 -0
  55. package/dist/directives/Flash.d.ts +116 -0
  56. package/dist/directives/Flash.d.ts.map +1 -0
  57. package/dist/directives/FocusNavigation.d.ts +52 -0
  58. package/dist/directives/FocusNavigation.d.ts.map +1 -0
  59. package/dist/directives/GamepadControls.d.ts +224 -0
  60. package/dist/directives/GamepadControls.d.ts.map +1 -0
  61. package/dist/directives/JoystickControls.d.ts +171 -0
  62. package/dist/directives/JoystickControls.d.ts.map +1 -0
  63. package/dist/directives/KeyboardControls.d.ts +219 -0
  64. package/dist/directives/KeyboardControls.d.ts.map +1 -0
  65. package/dist/directives/Scheduler.d.ts +35 -0
  66. package/dist/directives/Scheduler.d.ts.map +1 -0
  67. package/dist/directives/Shake.d.ts +98 -0
  68. package/dist/directives/Shake.d.ts.map +1 -0
  69. package/dist/directives/Sound.d.ts +25 -0
  70. package/dist/directives/Sound.d.ts.map +1 -0
  71. package/dist/directives/Transition.d.ts +10 -0
  72. package/dist/directives/Transition.d.ts.map +1 -0
  73. package/dist/directives/ViewportCull.d.ts +11 -0
  74. package/dist/directives/ViewportCull.d.ts.map +1 -0
  75. package/dist/directives/ViewportFollow.d.ts +18 -0
  76. package/dist/directives/ViewportFollow.d.ts.map +1 -0
  77. package/dist/directives/index.d.ts +13 -0
  78. package/dist/directives/index.d.ts.map +1 -0
  79. package/dist/engine/FocusManager.d.ts +174 -0
  80. package/dist/engine/FocusManager.d.ts.map +1 -0
  81. package/dist/engine/animation.d.ts +72 -0
  82. package/dist/engine/animation.d.ts.map +1 -0
  83. package/dist/engine/bootstrap.d.ts +48 -0
  84. package/dist/engine/bootstrap.d.ts.map +1 -0
  85. package/dist/engine/directive.d.ts +13 -0
  86. package/dist/engine/directive.d.ts.map +1 -0
  87. package/dist/engine/reactive.d.ts +134 -0
  88. package/dist/engine/reactive.d.ts.map +1 -0
  89. package/dist/engine/signal.d.ts +71 -0
  90. package/dist/engine/signal.d.ts.map +1 -0
  91. package/dist/engine/trigger.d.ts +54 -0
  92. package/dist/engine/trigger.d.ts.map +1 -0
  93. package/dist/engine/utils.d.ts +89 -0
  94. package/dist/engine/utils.d.ts.map +1 -0
  95. package/dist/hooks/addContext.d.ts +2 -0
  96. package/dist/hooks/addContext.d.ts.map +1 -0
  97. package/dist/hooks/useFocus.d.ts +60 -0
  98. package/dist/hooks/useFocus.d.ts.map +1 -0
  99. package/dist/hooks/useProps.d.ts +42 -0
  100. package/dist/hooks/useProps.d.ts.map +1 -0
  101. package/dist/hooks/useRef.d.ts +4 -0
  102. package/dist/hooks/useRef.d.ts.map +1 -0
  103. package/dist/index-DaGekQUW.js +2218 -0
  104. package/dist/index-DaGekQUW.js.map +1 -0
  105. package/dist/index.d.ts +19 -1099
  106. package/dist/index.d.ts.map +1 -0
  107. package/dist/index.global.js +5 -0
  108. package/dist/index.global.js.map +1 -0
  109. package/dist/index.js +11749 -2901
  110. package/dist/index.js.map +1 -1
  111. package/dist/utils/Ease.d.ts +17 -0
  112. package/dist/utils/Ease.d.ts.map +1 -0
  113. package/dist/utils/GlobalAssetLoader.d.ts +141 -0
  114. package/dist/utils/GlobalAssetLoader.d.ts.map +1 -0
  115. package/dist/utils/RadialGradient.d.ts +57 -0
  116. package/dist/utils/RadialGradient.d.ts.map +1 -0
  117. package/dist/utils/functions.d.ts +2 -0
  118. package/dist/utils/functions.d.ts.map +1 -0
  119. package/dist/utils/tabindex.d.ts +16 -0
  120. package/dist/utils/tabindex.d.ts.map +1 -0
  121. package/package.json +13 -7
  122. package/src/components/Button.ts +399 -0
  123. package/src/components/Canvas.ts +62 -46
  124. package/src/components/Container.ts +21 -2
  125. package/src/components/DOMContainer.ts +379 -0
  126. package/src/components/DOMElement.ts +556 -0
  127. package/src/components/DOMSprite.ts +1040 -0
  128. package/src/components/DisplayObject.ts +392 -201
  129. package/src/components/FocusContainer.ts +368 -0
  130. package/src/components/Graphic.ts +227 -66
  131. package/src/components/Joystick.ts +363 -0
  132. package/src/components/Mesh.ts +222 -0
  133. package/src/components/NineSliceSprite.ts +4 -1
  134. package/src/components/ParticleEmitter.ts +12 -8
  135. package/src/components/Sprite.ts +297 -31
  136. package/src/components/Text.ts +125 -18
  137. package/src/components/Video.ts +2 -2
  138. package/src/components/Viewport.ts +118 -63
  139. package/src/components/index.ts +9 -2
  140. package/src/components/types/DisplayObject.ts +41 -4
  141. package/src/components/types/Spritesheet.ts +0 -118
  142. package/src/directives/Controls.ts +254 -0
  143. package/src/directives/ControlsBase.ts +267 -0
  144. package/src/directives/Drag.ts +357 -52
  145. package/src/directives/Flash.ts +419 -0
  146. package/src/directives/FocusNavigation.ts +113 -0
  147. package/src/directives/GamepadControls.ts +537 -0
  148. package/src/directives/JoystickControls.ts +396 -0
  149. package/src/directives/KeyboardControls.ts +85 -430
  150. package/src/directives/Scheduler.ts +12 -4
  151. package/src/directives/Shake.ts +298 -0
  152. package/src/directives/Sound.ts +94 -31
  153. package/src/directives/ViewportFollow.ts +40 -9
  154. package/src/directives/index.ts +12 -6
  155. package/src/engine/FocusManager.ts +510 -0
  156. package/src/engine/animation.ts +175 -21
  157. package/src/engine/bootstrap.ts +93 -3
  158. package/src/engine/directive.ts +4 -4
  159. package/src/engine/reactive.ts +901 -161
  160. package/src/engine/signal.ts +113 -25
  161. package/src/engine/trigger.ts +34 -7
  162. package/src/engine/utils.ts +19 -3
  163. package/src/hooks/useFocus.ts +91 -0
  164. package/src/hooks/useProps.ts +1 -1
  165. package/src/index.ts +8 -2
  166. package/src/types/pixi-cull.d.ts +7 -0
  167. package/src/utils/GlobalAssetLoader.ts +257 -0
  168. package/src/utils/functions.ts +7 -0
  169. package/src/utils/tabindex.ts +70 -0
  170. package/testing/index.ts +35 -4
  171. package/tsconfig.json +18 -0
  172. package/vite.config.ts +39 -0
@@ -1,8 +1,13 @@
1
1
  import {
2
- Subscription
2
+ Observable,
3
+ Subscription
3
4
  } from "rxjs";
5
+ import { isSignal } from "@signe/reactive";
4
6
  import type { Element } from "./reactive";
7
+ import { isElementFrozen, waitForDependencies } from "./reactive";
8
+ import { isPromise } from "./utils";
5
9
  import { Tick } from "../directives/Scheduler";
10
+ import { Container } from "../components";
6
11
 
7
12
  type MountFunction = (fn: (element: Element) => void) => void;
8
13
 
@@ -19,16 +24,16 @@ export let mountTracker: MountFunction | null = null;
19
24
  * @param {(element: Element) => void} fn - The function to be called on mount.
20
25
  * @example
21
26
  * ```ts
22
- * mount((el) => {
23
- * console.log('mounted', el);
27
+ * mount((el) => {
28
+ * console.log('mounted', el);
24
29
  * });
25
30
  * ```
26
31
  * Unmount the component by returning a function:
27
32
  * ```ts
28
- * mount((el) => {
29
- * console.log('mounted', el);
33
+ * mount((el) => {
34
+ * console.log('mounted', el);
30
35
  * return () => {
31
- * console.log('unmounted', el);
36
+ * console.log('unmounted', el);
32
37
  * }
33
38
  * });
34
39
  * ```
@@ -42,8 +47,8 @@ export function mount(fn: (element: Element) => void) {
42
47
  * @param {(tickValue: Tick, element: Element) => void} fn - The function to be called on each tick.
43
48
  * @example
44
49
  * ```ts
45
- * tick((tickValue, el) => {
46
- * console.log('tick', tickValue, el);
50
+ * tick((tickValue, el) => {
51
+ * console.log('tick', tickValue, el);
47
52
  * });
48
53
  * ```
49
54
  */
@@ -53,7 +58,11 @@ export function tick(fn: (tickValue: Tick, element: Element) => void) {
53
58
  let subscription: Subscription | undefined
54
59
  if (context.tick) {
55
60
  subscription = context.tick.observable.subscribe(({ value }) => {
56
- fn(value, el)
61
+ // Block tick if element is frozen
62
+ if (isElementFrozen(el)) {
63
+ return;
64
+ }
65
+ fn(value, el)
57
66
  })
58
67
  }
59
68
  return () => {
@@ -71,29 +80,29 @@ export function tick(fn: (tickValue: Tick, element: Element) => void) {
71
80
  * @returns {ReturnType<C>}
72
81
  * @example
73
82
  * ```ts
74
- * const el = h(MyComponent, {
75
- * x: 100,
76
- * y: 100,
77
- * });
83
+ * const el = h(MyComponent, {
84
+ * x: 100,
85
+ * y: 100,
86
+ * });
78
87
  * ```
79
88
  *
80
89
  * with children:
81
90
  * ```ts
82
- * const el = h(MyComponent, {
83
- * x: 100,
84
- * y: 100,
85
- * },
86
- * h(MyChildComponent, {
87
- * x: 50,
88
- * y: 50,
89
- * }),
91
+ * const el = h(MyComponent, {
92
+ * x: 100,
93
+ * y: 100,
94
+ * },
95
+ * h(MyChildComponent, {
96
+ * x: 50,
97
+ * y: 50,
98
+ * }),
90
99
  * );
91
100
  * ```
92
101
  */
93
- export function h<C extends ComponentFunction<any>>(
94
- componentFunction: C,
102
+ function _h<C extends ComponentFunction<any>>(
103
+ componentFunction: C | Element,
95
104
  props: Parameters<C>[0] = {} as Parameters<C>[0],
96
- ...children: any[]
105
+ children: any[]
97
106
  ): ReturnType<C> {
98
107
  const allSubscriptions = new Set<Subscription>();
99
108
  const allMounts = new Set<MountFunction>();
@@ -110,7 +119,25 @@ export function h<C extends ComponentFunction<any>>(
110
119
  children = children[0]
111
120
  }
112
121
 
113
- let component = componentFunction({ ...props, children }) as Element;
122
+ let component: Element
123
+
124
+ if (Array.isArray(componentFunction)) {
125
+ if (componentFunction.length === 1) {
126
+ component = componentFunction[0]
127
+ }
128
+ else {
129
+ component = _h(Container, {}, componentFunction) as Element
130
+ }
131
+ }
132
+ else if ('tag' in componentFunction) {
133
+ component = componentFunction
134
+ }
135
+ else if (componentFunction instanceof Observable) {
136
+ component = componentFunction as any
137
+ }
138
+ else {
139
+ component = componentFunction({ ...props, children }) as Element;
140
+ }
114
141
 
115
142
  if (!component) {
116
143
  component = {} as any
@@ -122,6 +149,12 @@ export function h<C extends ComponentFunction<any>>(
122
149
  ...((component as any).effectMounts ?? [])
123
150
  ];
124
151
 
152
+ // Copy dependencies prop to the returned element so it can be used for delayed mounting
153
+ if (props?.dependencies) {
154
+ component.props = component.props || {};
155
+ component.props.dependencies = props.dependencies;
156
+ }
157
+
125
158
  // call mount hook for root component
126
159
  if (component instanceof Promise) {
127
160
  component.then((component) => {
@@ -136,3 +169,58 @@ export function h<C extends ComponentFunction<any>>(
136
169
 
137
170
  return component as ReturnType<C>;
138
171
  }
172
+
173
+ /**
174
+ * Add tracking for subscriptions and mounts, then create an element from a component function.
175
+ * @template C
176
+ * @param {C} componentFunction - The component function to create an element from.
177
+ * @param {Parameters<C>[0]} [props={}] - The props to pass to the component function.
178
+ * @param {...any[]} children - The children elements of the component.
179
+ * @returns {ReturnType<C>}
180
+ * @example
181
+ * ```ts
182
+ * const el = h(MyComponent, {
183
+ * x: 100,
184
+ * y: 100,
185
+ * });
186
+ * ```
187
+ *
188
+ * with children:
189
+ * ```ts
190
+ * const el = h(MyComponent, {
191
+ * x: 100,
192
+ * y: 100,
193
+ * },
194
+ * h(MyChildComponent, {
195
+ * x: 50,
196
+ * y: 50,
197
+ * }),
198
+ * );
199
+ * ```
200
+ */
201
+ export function h<C extends ComponentFunction<any>>(
202
+ componentFunction: C | Element,
203
+ props: Parameters<C>[0] = {} as Parameters<C>[0],
204
+ ...children: any[]
205
+ ): ReturnType<C> {
206
+ if (props?.dependencies) {
207
+ const hasPromise = props.dependencies.some(isPromise);
208
+ if (!hasPromise) {
209
+ const allReady = props.dependencies.every(dep => {
210
+ if (isSignal(dep)) return dep() !== undefined;
211
+ return dep !== undefined;
212
+ });
213
+ if (allReady) {
214
+ return _h(componentFunction, props, children);
215
+ }
216
+ }
217
+
218
+ return new Observable(subscriber => {
219
+ waitForDependencies(props.dependencies).then(() => {
220
+ const el = _h(componentFunction, props, children);
221
+ subscriber.next(el);
222
+ });
223
+ }) as any;
224
+ }
225
+ return _h(componentFunction, props, children);
226
+ }
@@ -1,6 +1,6 @@
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
5
  seed: {
6
6
  config: T | undefined;
@@ -9,7 +9,7 @@ interface Listen<T = any> {
9
9
  };
10
10
  }
11
11
 
12
- interface Trigger<T = any> {
12
+ export interface Trigger<T = any> {
13
13
  start: () => Promise<void>;
14
14
  listen: () => Listen<T> | undefined;
15
15
  }
@@ -47,13 +47,14 @@ export function trigger<T = any>(globalConfig?: T): Trigger<T> {
47
47
  return {
48
48
  start: (config?: T) => {
49
49
  return new Promise((resolve: (value: any) => void) => {
50
+ const newValue = Math.random();
50
51
  _signal.set({
51
52
  config: {
52
53
  ...globalConfig,
53
54
  ...config,
54
55
  },
55
56
  resolve,
56
- value: Math.random(),
57
+ value: newValue,
57
58
  });
58
59
  });
59
60
  },
@@ -70,27 +71,53 @@ export function trigger<T = any>(globalConfig?: T): Trigger<T> {
70
71
  * Subscribes to a trigger and executes a callback when the trigger is activated
71
72
  * @param triggerSignal - The trigger to subscribe to
72
73
  * @param callback - Function to execute when the trigger is activated
74
+ * @returns Subscription that can be unsubscribed to stop listening
73
75
  * @throws Error if triggerSignal is not a valid trigger
74
76
  * @example
75
77
  * ```ts
76
78
  * const click = trigger()
77
79
  *
78
- * on(click, () => {
80
+ * const subscription = on(click, () => {
79
81
  * console.log('Click triggered')
80
82
  * })
83
+ *
84
+ * // Later, to stop listening:
85
+ * subscription.unsubscribe()
81
86
  * ```
82
87
  */
83
88
  export function on(triggerSignal: any, callback: (config: any) => void | Promise<void>) {
84
89
  if (!isTrigger(triggerSignal)) {
85
90
  throw new Error("In 'on(arg)' must have a trigger signal type");
86
91
  }
87
- effect(() => {
92
+ let lastValue: number | undefined;
93
+
94
+ const effectResult = effect(() => {
88
95
  const result = triggerSignal.listen();
89
- if (result?.seed.value) {
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 {
90
111
  const ret = callback(result?.seed.config);
91
112
  if (ret && typeof ret.then === 'function') {
92
- ret.then(result?.seed.resolve);
113
+ ret.then((value: any) => seed.resolve(value)).catch(() => seed.resolve(undefined));
114
+ } else {
115
+ seed.resolve(ret);
93
116
  }
117
+ } catch (err) {
118
+ seed.resolve(undefined);
94
119
  }
95
120
  });
121
+
122
+ return effectResult.subscription;
96
123
  }
@@ -1,4 +1,6 @@
1
+ import { isSignal } from "@signe/reactive"
1
2
  import { ObservablePoint } from "pixi.js"
3
+ import { Observable } from "rxjs"
2
4
 
3
5
  /**
4
6
  * Checks if code is running in a browser environment
@@ -86,12 +88,23 @@ export function isFunction(val: unknown): boolean {
86
88
  }
87
89
 
88
90
  /**
89
- * Checks if a value is a plain object
91
+ * Checks if a value is a plain object (not an instance of a class)
90
92
  * @param {unknown} val - Value to check
91
- * @returns {boolean} True if value is an object (not null and not array), false otherwise
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
+ * ```
92
101
  */
93
102
  export function isObject(val: unknown): boolean {
94
- return typeof val == 'object' && val != null && !Array.isArray(val)
103
+ return typeof val == 'object' && val != null && !Array.isArray(val) && val.constructor === Object
104
+ }
105
+
106
+ export function isObservable(val: unknown): boolean {
107
+ return val instanceof Observable
95
108
  }
96
109
 
97
110
  /**
@@ -186,6 +199,9 @@ export function setObservablePoint(
186
199
  else if (Array.isArray(point)) {
187
200
  observablePoint.set(point[0], point[1]);
188
201
  }
202
+ else if (isObject(point) && 'value' in point) {
203
+ observablePoint.set((point as any).value.x, (point as any).value.y);
204
+ }
189
205
  else {
190
206
  observablePoint.set(point.x, point.y);
191
207
  }
@@ -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
+ }
@@ -25,7 +25,7 @@ export const useProps = (props, defaults = {}): any => {
25
25
  }
26
26
  for (let key in defaults) {
27
27
  if (!(key in obj)) {
28
- obj[key] = signal(defaults[key])
28
+ obj[key] = isPrimitive(defaults[key]) ? signal(defaults[key]) : defaults[key]
29
29
  }
30
30
  }
31
31
  return obj
package/src/index.ts CHANGED
@@ -1,15 +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'
12
+ export { FocusManager, focusManager, type ScrollOptions } from './engine/FocusManager'
10
13
  export { useProps, useDefineProps } from './hooks/useProps'
14
+ export { useFocusIndex, useFocusedElement, useFocusChange } from './hooks/useFocus'
11
15
  export * from './utils/Ease'
12
16
  export * from './utils/RadialGradient'
17
+ export * from './utils/tabindex'
13
18
  export * from './components/DisplayObject'
14
19
  export { isObservable } from 'rxjs'
15
- export * as Utils from './engine/utils'
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
+ }