canvasengine 2.0.0-beta.5 → 2.0.0-beta.51
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/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 +86 -0
- package/dist/components/Container.d.ts.map +1 -0
- package/dist/components/DOMContainer.d.ts +98 -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 +94 -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 +36 -0
- package/dist/components/Joystick.d.ts.map +1 -0
- package/dist/components/Mesh.d.ts +208 -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 +242 -0
- package/dist/components/Sprite.d.ts.map +1 -0
- package/dist/components/Text.d.ts +25 -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 +121 -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 +106 -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/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 +35 -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/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 +13 -0
- package/dist/directives/index.d.ts.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 +48 -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 +134 -0
- package/dist/engine/reactive.d.ts.map +1 -0
- package/dist/engine/signal.d.ts +71 -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 +42 -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-DaGekQUW.js +2218 -0
- package/dist/index-DaGekQUW.js.map +1 -0
- package/dist/index.d.ts +19 -1099
- package/dist/index.d.ts.map +1 -0
- package/dist/index.global.js +5 -0
- package/dist/index.global.js.map +1 -0
- package/dist/index.js +11749 -2901
- 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/package.json +13 -7
- package/src/components/Button.ts +399 -0
- package/src/components/Canvas.ts +62 -46
- package/src/components/Container.ts +21 -2
- package/src/components/DOMContainer.ts +379 -0
- package/src/components/DOMElement.ts +556 -0
- package/src/components/DOMSprite.ts +1040 -0
- package/src/components/DisplayObject.ts +392 -201
- package/src/components/FocusContainer.ts +368 -0
- package/src/components/Graphic.ts +227 -66
- package/src/components/Joystick.ts +363 -0
- package/src/components/Mesh.ts +222 -0
- package/src/components/NineSliceSprite.ts +4 -1
- package/src/components/ParticleEmitter.ts +12 -8
- package/src/components/Sprite.ts +297 -31
- package/src/components/Text.ts +125 -18
- package/src/components/Video.ts +2 -2
- package/src/components/Viewport.ts +118 -63
- package/src/components/index.ts +9 -2
- package/src/components/types/DisplayObject.ts +41 -4
- 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/GamepadControls.ts +537 -0
- package/src/directives/JoystickControls.ts +396 -0
- package/src/directives/KeyboardControls.ts +85 -430
- package/src/directives/Scheduler.ts +12 -4
- package/src/directives/Shake.ts +298 -0
- package/src/directives/Sound.ts +94 -31
- package/src/directives/ViewportFollow.ts +40 -9
- package/src/directives/index.ts +12 -6
- package/src/engine/FocusManager.ts +510 -0
- package/src/engine/animation.ts +175 -21
- package/src/engine/bootstrap.ts +93 -3
- package/src/engine/directive.ts +4 -4
- package/src/engine/reactive.ts +901 -161
- package/src/engine/signal.ts +113 -25
- package/src/engine/trigger.ts +34 -7
- package/src/engine/utils.ts +19 -3
- package/src/hooks/useFocus.ts +91 -0
- package/src/hooks/useProps.ts +1 -1
- package/src/index.ts +8 -2
- package/src/types/pixi-cull.d.ts +7 -0
- package/src/utils/GlobalAssetLoader.ts +257 -0
- package/src/utils/functions.ts +7 -0
- package/src/utils/tabindex.ts +70 -0
- package/testing/index.ts +35 -4
- package/tsconfig.json +18 -0
- package/vite.config.ts +39 -0
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
import { createComponent, registerComponent, type Element } from "../engine/reactive";
|
|
2
|
+
import { applyDirective } from "../engine/directive";
|
|
3
|
+
import { ComponentFunction } from "../engine/signal";
|
|
4
|
+
import { DisplayObjectProps } from "./types/DisplayObject";
|
|
5
|
+
import { focusManager, ScrollOptions } from "../engine/FocusManager";
|
|
6
|
+
import { signal, Signal, WritableSignal, WritableObjectSignal, isSignal } from "@signe/reactive";
|
|
7
|
+
import { CanvasViewport } from "./Viewport";
|
|
8
|
+
import { Controls } from "../directives/ControlsBase";
|
|
9
|
+
// Import FocusNavigation directive to ensure it's registered
|
|
10
|
+
import "../directives/FocusNavigation";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Properties for FocusContainer component
|
|
14
|
+
*
|
|
15
|
+
* @property tabindex - Focus index for the container (default: 0 if present)
|
|
16
|
+
* @property controls - Controls configuration for automatic navigation
|
|
17
|
+
* @property onFocusChange - Callback when focus changes
|
|
18
|
+
* @property autoScroll - Enable automatic scrolling to focused element (default: false)
|
|
19
|
+
* @property viewport - Viewport instance to use for scrolling (optional, uses context viewport by default)
|
|
20
|
+
*/
|
|
21
|
+
export interface FocusContainerProps extends DisplayObjectProps {
|
|
22
|
+
tabindex?: number;
|
|
23
|
+
controls?: Controls | Signal<Controls>;
|
|
24
|
+
onFocusChange?: (index: number, element: Element | null) => void;
|
|
25
|
+
autoScroll?: boolean | ScrollOptions;
|
|
26
|
+
viewport?: CanvasViewport;
|
|
27
|
+
context?: {
|
|
28
|
+
viewport?: CanvasViewport;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* FocusContainer component for managing focus navigation
|
|
34
|
+
*
|
|
35
|
+
* This component provides a container that manages focus navigation between
|
|
36
|
+
* focusable child elements. It supports automatic navigation via Controls
|
|
37
|
+
* (keyboard/gamepad) and automatic scrolling with Viewport.
|
|
38
|
+
*
|
|
39
|
+
* ## Features
|
|
40
|
+
*
|
|
41
|
+
* - **Focus Management**: Automatically registers focusable children
|
|
42
|
+
* - **Navigation**: Supports keyboard/gamepad navigation via Controls
|
|
43
|
+
* - **Auto-scroll**: Automatically scrolls viewport to show focused element
|
|
44
|
+
* - **Hooks**: Provides reactive signals for focus state
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```typescript
|
|
48
|
+
* // Basic usage
|
|
49
|
+
* <FocusContainer tabindex={0}>
|
|
50
|
+
* <Button tabindex={0} text="Button 1" />
|
|
51
|
+
* <Button tabindex={1} text="Button 2" />
|
|
52
|
+
* </FocusContainer>
|
|
53
|
+
*
|
|
54
|
+
* // With Controls
|
|
55
|
+
* <FocusContainer tabindex={0} controls={controlsConfig}>
|
|
56
|
+
* <Button tabindex={0} text="Button 1" />
|
|
57
|
+
* <Button tabindex={1} text="Button 2" />
|
|
58
|
+
* </FocusContainer>
|
|
59
|
+
*
|
|
60
|
+
* // With auto-scroll
|
|
61
|
+
* <Viewport worldWidth={2000} worldHeight={5000}>
|
|
62
|
+
* <FocusContainer tabindex={0} autoScroll={true}>
|
|
63
|
+
* <Button tabindex={0} y={0} text="Item 1" />
|
|
64
|
+
* <Button tabindex={1} y={100} text="Item 2" />
|
|
65
|
+
* </FocusContainer>
|
|
66
|
+
* </Viewport>
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
export class CanvasFocusContainer {
|
|
70
|
+
private containerId: string = '';
|
|
71
|
+
private currentIndexSignal: WritableSignal<number | null> | null = null;
|
|
72
|
+
private focusedElementSignal: WritableSignal<Element | null> | WritableObjectSignal<Element | null> | null = null;
|
|
73
|
+
private registeredFocusables: Set<number> = new Set();
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Initialize the focus container
|
|
77
|
+
*
|
|
78
|
+
* @param props - Component properties
|
|
79
|
+
*/
|
|
80
|
+
onInit(props: FocusContainerProps) {
|
|
81
|
+
// Generate unique container ID
|
|
82
|
+
this.containerId = `focus-container-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
83
|
+
|
|
84
|
+
// Create signals for current index and focused element
|
|
85
|
+
const currentIndex = signal<number | null>(null);
|
|
86
|
+
const focusedElement = signal<Element | null>(null) as WritableSignal<Element | null> | WritableObjectSignal<Element | null>;
|
|
87
|
+
|
|
88
|
+
this.currentIndexSignal = currentIndex;
|
|
89
|
+
this.focusedElementSignal = focusedElement;
|
|
90
|
+
|
|
91
|
+
// Get viewport from context or props
|
|
92
|
+
const viewport = props.viewport || (props.context?.viewport as CanvasViewport | undefined);
|
|
93
|
+
|
|
94
|
+
// Register container with FocusManager
|
|
95
|
+
focusManager.registerContainer(this.containerId, {
|
|
96
|
+
focusables: new Map(),
|
|
97
|
+
currentIndex,
|
|
98
|
+
focusedElement,
|
|
99
|
+
onFocusChange: props.onFocusChange,
|
|
100
|
+
autoScroll: props.autoScroll,
|
|
101
|
+
viewport
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Mount hook - register focusable children
|
|
107
|
+
*
|
|
108
|
+
* @param element - The element being mounted
|
|
109
|
+
*/
|
|
110
|
+
async onMount(element: Element<CanvasFocusContainer>): Promise<void> {
|
|
111
|
+
// Update container with element reference for freeze checking
|
|
112
|
+
focusManager.updateContainer(this.containerId, { element });
|
|
113
|
+
|
|
114
|
+
// Apply focusNavigation directive if controls are provided
|
|
115
|
+
if (element.props.controls) {
|
|
116
|
+
const focusNavDirective = applyDirective(element, 'focusNavigation');
|
|
117
|
+
if (focusNavDirective && !element.directives) {
|
|
118
|
+
element.directives = {};
|
|
119
|
+
}
|
|
120
|
+
if (focusNavDirective) {
|
|
121
|
+
element.directives.focusNavigation = focusNavDirective;
|
|
122
|
+
// Initialize the directive
|
|
123
|
+
focusNavDirective.onInit(element);
|
|
124
|
+
focusNavDirective.onMount(element);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Subscribe to allElements to detect when children are mounted
|
|
129
|
+
if (element.allElements) {
|
|
130
|
+
const subscription = element.allElements.subscribe(() => {
|
|
131
|
+
// Register children when they are mounted
|
|
132
|
+
this.registerChildren(element);
|
|
133
|
+
});
|
|
134
|
+
// Store subscription for cleanup
|
|
135
|
+
if (!element.effectSubscriptions) {
|
|
136
|
+
element.effectSubscriptions = [];
|
|
137
|
+
}
|
|
138
|
+
element.effectSubscriptions.push(subscription);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// if (element.propObservables.tabindex) {
|
|
142
|
+
// const subscription = element.propObservables.tabindex.observable.subscribe((value: any) => {
|
|
143
|
+
// console.log("tabindex changed", value);
|
|
144
|
+
// if (value !== null) {
|
|
145
|
+
// // focusManager.setIndex(this.containerId, value);
|
|
146
|
+
// }
|
|
147
|
+
// });
|
|
148
|
+
// element.effectSubscriptions.push(subscription);
|
|
149
|
+
// }
|
|
150
|
+
|
|
151
|
+
focusManager.setTabindex(this.containerId, element.propObservables?.tabindex as any);
|
|
152
|
+
|
|
153
|
+
// Register all focusable children initially
|
|
154
|
+
// Use setTimeout to ensure children are mounted
|
|
155
|
+
setTimeout(() => {
|
|
156
|
+
this.registerChildren(element);
|
|
157
|
+
}, 0);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Update hook - handle prop changes
|
|
162
|
+
*
|
|
163
|
+
* @param props - Updated properties
|
|
164
|
+
*/
|
|
165
|
+
onUpdate(props: FocusContainerProps) {
|
|
166
|
+
// Update viewport if changed
|
|
167
|
+
const viewport = props.viewport || (props.context?.viewport as CanvasViewport | undefined);
|
|
168
|
+
focusManager.updateContainer(this.containerId, {
|
|
169
|
+
viewport,
|
|
170
|
+
autoScroll: props.autoScroll,
|
|
171
|
+
onFocusChange: props.onFocusChange
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Destroy hook - cleanup
|
|
177
|
+
*
|
|
178
|
+
* @param parent - Parent element
|
|
179
|
+
* @param afterDestroy - Callback after destruction
|
|
180
|
+
*/
|
|
181
|
+
async onDestroy(parent: Element<any>, afterDestroy?: () => void): Promise<void> {
|
|
182
|
+
// Unregister all focusables
|
|
183
|
+
for (const index of this.registeredFocusables) {
|
|
184
|
+
focusManager.unregisterFocusable(this.containerId, index);
|
|
185
|
+
}
|
|
186
|
+
this.registeredFocusables.clear();
|
|
187
|
+
|
|
188
|
+
// Unregister container
|
|
189
|
+
focusManager.unregisterContainer(this.containerId);
|
|
190
|
+
if (afterDestroy) {
|
|
191
|
+
afterDestroy();
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Register focusable children from element
|
|
197
|
+
*
|
|
198
|
+
* @param element - Container element
|
|
199
|
+
*/
|
|
200
|
+
private registerChildren(element: Element<CanvasFocusContainer>) {
|
|
201
|
+
if (!element.props.children) return;
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
let registeredCount = 0;
|
|
205
|
+
const processChildren = (children: any[]) => {
|
|
206
|
+
for (const child of children) {
|
|
207
|
+
if (!child) continue;
|
|
208
|
+
|
|
209
|
+
// Handle signals/observables
|
|
210
|
+
if (isSignal(child) || (child && typeof child.subscribe === 'function')) {
|
|
211
|
+
|
|
212
|
+
// Subscribe to changes
|
|
213
|
+
const subscription = (isSignal(child) ? child.observable : child).subscribe((value: any) => {
|
|
214
|
+
// Handle FlowObservable result (from loop, cond, etc.) - has 'elements' property
|
|
215
|
+
if (value && typeof value === 'object' && 'elements' in value) {
|
|
216
|
+
const elements = value.elements || [];
|
|
217
|
+
if (Array.isArray(elements)) {
|
|
218
|
+
processChildren(elements);
|
|
219
|
+
}
|
|
220
|
+
} else if (Array.isArray(value)) {
|
|
221
|
+
processChildren(value);
|
|
222
|
+
} else if (value) {
|
|
223
|
+
processChild(value);
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
// Note: We should track subscriptions for cleanup, but for now this works
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Handle arrays
|
|
231
|
+
if (Array.isArray(child)) {
|
|
232
|
+
processChildren(child);
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Handle single element
|
|
237
|
+
processChild(child);
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
const processChild = (child: Element) => {
|
|
242
|
+
if (!child || !child.componentInstance) return;
|
|
243
|
+
if ((child.tag === "Navigation" || child.tag === "FocusContainer") && child !== (element as any)) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Check for tabindex in props
|
|
248
|
+
let tabindex: number | undefined = undefined;
|
|
249
|
+
|
|
250
|
+
// For DOMElement/DOMContainer, check attrs.tabindex
|
|
251
|
+
if (child.props?.attrs?.tabindex !== undefined) {
|
|
252
|
+
const tabindexValue = child.props.attrs.tabindex;
|
|
253
|
+
tabindex = isSignal(tabindexValue) ? tabindexValue() : tabindexValue;
|
|
254
|
+
}
|
|
255
|
+
// For other components, check tabindex prop directly
|
|
256
|
+
else if (child.props?.tabindex !== undefined) {
|
|
257
|
+
const tabindexValue = child.props.tabindex;
|
|
258
|
+
tabindex = isSignal(tabindexValue) ? tabindexValue() : tabindexValue;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Register if tabindex >= 0
|
|
262
|
+
if (tabindex !== undefined && tabindex >= 0) {
|
|
263
|
+
if (!this.registeredFocusables.has(tabindex)) {
|
|
264
|
+
focusManager.registerFocusable(this.containerId, child, tabindex);
|
|
265
|
+
this.registeredFocusables.add(tabindex);
|
|
266
|
+
registeredCount++;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Recursively process children unless we hit another FocusContainer
|
|
271
|
+
if (child.props && child.props.children) {
|
|
272
|
+
if (Array.isArray(child.props.children)) {
|
|
273
|
+
processChildren(child.props.children);
|
|
274
|
+
} else if (isSignal(child.props.children) || (child.props.children && typeof child.props.children.subscribe === 'function')) {
|
|
275
|
+
const subscription = (isSignal(child.props.children) ? child.props.children.observable : child.props.children).subscribe((value: any) => {
|
|
276
|
+
// Handle FlowObservable result (from loop, cond, etc.) - has 'elements' property
|
|
277
|
+
if (value && typeof value === 'object' && 'elements' in value) {
|
|
278
|
+
const elements = value.elements || [];
|
|
279
|
+
if (Array.isArray(elements)) {
|
|
280
|
+
processChildren(elements);
|
|
281
|
+
}
|
|
282
|
+
} else if (Array.isArray(value)) {
|
|
283
|
+
processChildren(value);
|
|
284
|
+
} else if (value) {
|
|
285
|
+
processChild(value);
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
// Store subscription for cleanup if child has effectSubscriptions
|
|
289
|
+
if (child.effectSubscriptions) {
|
|
290
|
+
child.effectSubscriptions.push(subscription);
|
|
291
|
+
}
|
|
292
|
+
} else {
|
|
293
|
+
processChild(child.props.children as any);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
if (Array.isArray(element.props.children)) {
|
|
299
|
+
processChildren(element.props.children);
|
|
300
|
+
} else if (element.props.children) {
|
|
301
|
+
if (isSignal(element.props.children) || (element.props.children && typeof element.props.children.subscribe === 'function')) {
|
|
302
|
+
const subscription = (isSignal(element.props.children) ? element.props.children.observable : element.props.children).subscribe((value: any) => {
|
|
303
|
+
// Handle FlowObservable result (from loop, cond, etc.) - has 'elements' property
|
|
304
|
+
if (value && typeof value === 'object' && 'elements' in value) {
|
|
305
|
+
const elements = value.elements || [];
|
|
306
|
+
if (Array.isArray(elements)) {
|
|
307
|
+
processChildren(elements);
|
|
308
|
+
}
|
|
309
|
+
} else if (Array.isArray(value)) {
|
|
310
|
+
processChildren(value);
|
|
311
|
+
} else if (value) {
|
|
312
|
+
processChild(value);
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
// Store subscription for cleanup
|
|
316
|
+
if (!element.effectSubscriptions) {
|
|
317
|
+
element.effectSubscriptions = [];
|
|
318
|
+
}
|
|
319
|
+
element.effectSubscriptions.push(subscription);
|
|
320
|
+
} else {
|
|
321
|
+
processChild(element.props.children as any);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Get the container ID
|
|
328
|
+
*
|
|
329
|
+
* @returns Container identifier
|
|
330
|
+
*/
|
|
331
|
+
getContainerId(): string {
|
|
332
|
+
return this.containerId;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Get current index signal
|
|
337
|
+
*
|
|
338
|
+
* @returns Signal for current focus index
|
|
339
|
+
*/
|
|
340
|
+
getCurrentIndexSignal(): Signal<number | null> | null {
|
|
341
|
+
return this.currentIndexSignal;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Get focused element signal
|
|
346
|
+
*
|
|
347
|
+
* @returns Signal for current focused element
|
|
348
|
+
*/
|
|
349
|
+
getFocusedElementSignal(): Signal<Element | null> | null {
|
|
350
|
+
return this.focusedElementSignal;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export interface CanvasFocusContainer extends DisplayObjectProps { }
|
|
355
|
+
|
|
356
|
+
registerComponent("Navigation", CanvasFocusContainer);
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* FocusContainer component function
|
|
360
|
+
*
|
|
361
|
+
* @param props - Component properties
|
|
362
|
+
* @returns FocusContainer element
|
|
363
|
+
*/
|
|
364
|
+
export const FocusContainer: ComponentFunction<FocusContainerProps> = (props) => {
|
|
365
|
+
return createComponent("Navigation", props);
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
export const Navigation = FocusContainer;
|