canvasengine 2.0.0-beta.6 → 2.0.0-beta.61

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 (164) 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/DOMElement.d.ts +54 -0
  8. package/dist/components/DOMElement.d.ts.map +1 -0
  9. package/dist/components/DOMSprite.d.ts +127 -0
  10. package/dist/components/DOMSprite.d.ts.map +1 -0
  11. package/dist/components/FocusContainer.d.ts +129 -0
  12. package/dist/components/FocusContainer.d.ts.map +1 -0
  13. package/dist/components/Graphic.d.ts +64 -0
  14. package/dist/components/Graphic.d.ts.map +1 -0
  15. package/dist/components/Joystick.d.ts +36 -0
  16. package/dist/components/Joystick.d.ts.map +1 -0
  17. package/dist/components/NineSliceSprite.d.ts +16 -0
  18. package/dist/components/NineSliceSprite.d.ts.map +1 -0
  19. package/dist/components/ParticleEmitter.d.ts +4 -0
  20. package/dist/components/ParticleEmitter.d.ts.map +1 -0
  21. package/dist/components/Scene.d.ts +2 -0
  22. package/dist/components/Scene.d.ts.map +1 -0
  23. package/dist/components/Text.d.ts +24 -0
  24. package/dist/components/Text.d.ts.map +1 -0
  25. package/dist/components/TilingSprite.d.ts +17 -0
  26. package/dist/components/TilingSprite.d.ts.map +1 -0
  27. package/dist/components/Video.d.ts +14 -0
  28. package/dist/components/Video.d.ts.map +1 -0
  29. package/dist/components/index.d.ts +20 -0
  30. package/dist/components/index.d.ts.map +1 -0
  31. package/dist/components/types/DisplayObject.d.ts +118 -0
  32. package/dist/components/types/DisplayObject.d.ts.map +1 -0
  33. package/dist/components/types/MouseEvent.d.ts +4 -0
  34. package/dist/components/types/MouseEvent.d.ts.map +1 -0
  35. package/dist/components/types/Spritesheet.d.ts +248 -0
  36. package/dist/components/types/Spritesheet.d.ts.map +1 -0
  37. package/dist/components/types/index.d.ts +4 -0
  38. package/dist/components/types/index.d.ts.map +1 -0
  39. package/dist/directives/Controls.d.ts +112 -0
  40. package/dist/directives/Controls.d.ts.map +1 -0
  41. package/dist/directives/ControlsBase.d.ts +199 -0
  42. package/dist/directives/ControlsBase.d.ts.map +1 -0
  43. package/dist/directives/Drag.d.ts +69 -0
  44. package/dist/directives/Drag.d.ts.map +1 -0
  45. package/dist/directives/Flash.d.ts +116 -0
  46. package/dist/directives/Flash.d.ts.map +1 -0
  47. package/dist/directives/FocusNavigation.d.ts +52 -0
  48. package/dist/directives/FocusNavigation.d.ts.map +1 -0
  49. package/dist/directives/FogVisibility.d.ts +47 -0
  50. package/dist/directives/FogVisibility.d.ts.map +1 -0
  51. package/dist/directives/GamepadControls.d.ts +224 -0
  52. package/dist/directives/GamepadControls.d.ts.map +1 -0
  53. package/dist/directives/JoystickControls.d.ts +171 -0
  54. package/dist/directives/JoystickControls.d.ts.map +1 -0
  55. package/dist/directives/KeyboardControls.d.ts +219 -0
  56. package/dist/directives/KeyboardControls.d.ts.map +1 -0
  57. package/dist/directives/Scheduler.d.ts +36 -0
  58. package/dist/directives/Scheduler.d.ts.map +1 -0
  59. package/dist/directives/Shake.d.ts +98 -0
  60. package/dist/directives/Shake.d.ts.map +1 -0
  61. package/dist/directives/Sound.d.ts +25 -0
  62. package/dist/directives/Sound.d.ts.map +1 -0
  63. package/dist/directives/Transition.d.ts +10 -0
  64. package/dist/directives/Transition.d.ts.map +1 -0
  65. package/dist/directives/ViewportCull.d.ts +11 -0
  66. package/dist/directives/ViewportCull.d.ts.map +1 -0
  67. package/dist/directives/ViewportFollow.d.ts +18 -0
  68. package/dist/directives/ViewportFollow.d.ts.map +1 -0
  69. package/dist/directives/index.d.ts +14 -0
  70. package/dist/directives/index.d.ts.map +1 -0
  71. package/dist/dist-BOOc43Qm.js +778 -0
  72. package/dist/dist-BOOc43Qm.js.map +1 -0
  73. package/dist/engine/FocusManager.d.ts +174 -0
  74. package/dist/engine/FocusManager.d.ts.map +1 -0
  75. package/dist/engine/animation.d.ts +72 -0
  76. package/dist/engine/animation.d.ts.map +1 -0
  77. package/dist/engine/bootstrap.d.ts +52 -0
  78. package/dist/engine/bootstrap.d.ts.map +1 -0
  79. package/dist/engine/directive.d.ts +13 -0
  80. package/dist/engine/directive.d.ts.map +1 -0
  81. package/dist/engine/reactive.d.ts +135 -0
  82. package/dist/engine/reactive.d.ts.map +1 -0
  83. package/dist/engine/signal.d.ts +73 -0
  84. package/dist/engine/signal.d.ts.map +1 -0
  85. package/dist/engine/trigger.d.ts +54 -0
  86. package/dist/engine/trigger.d.ts.map +1 -0
  87. package/dist/engine/utils.d.ts +89 -0
  88. package/dist/engine/utils.d.ts.map +1 -0
  89. package/dist/hooks/addContext.d.ts +2 -0
  90. package/dist/hooks/addContext.d.ts.map +1 -0
  91. package/dist/hooks/useFocus.d.ts +60 -0
  92. package/dist/hooks/useFocus.d.ts.map +1 -0
  93. package/dist/hooks/useProps.d.ts +42 -0
  94. package/dist/hooks/useProps.d.ts.map +1 -0
  95. package/dist/hooks/useRef.d.ts +4 -0
  96. package/dist/hooks/useRef.d.ts.map +1 -0
  97. package/dist/index.d.ts +19 -1107
  98. package/dist/index.d.ts.map +1 -0
  99. package/dist/index.global.js +8 -0
  100. package/dist/index.global.js.map +1 -0
  101. package/dist/index.js +14708 -3135
  102. package/dist/index.js.map +1 -1
  103. package/dist/utils/Ease.d.ts +17 -0
  104. package/dist/utils/Ease.d.ts.map +1 -0
  105. package/dist/utils/GlobalAssetLoader.d.ts +141 -0
  106. package/dist/utils/GlobalAssetLoader.d.ts.map +1 -0
  107. package/dist/utils/RadialGradient.d.ts +57 -0
  108. package/dist/utils/RadialGradient.d.ts.map +1 -0
  109. package/dist/utils/functions.d.ts +2 -0
  110. package/dist/utils/functions.d.ts.map +1 -0
  111. package/dist/utils/tabindex.d.ts +16 -0
  112. package/dist/utils/tabindex.d.ts.map +1 -0
  113. package/package.json +16 -9
  114. package/src/components/Button.ts +399 -0
  115. package/src/components/Canvas.ts +82 -51
  116. package/src/components/Container.ts +21 -2
  117. package/src/components/DOMContainer.ts +379 -0
  118. package/src/components/DOMElement.ts +556 -0
  119. package/src/components/DOMSprite.ts +1040 -0
  120. package/src/components/DisplayObject.ts +422 -201
  121. package/src/components/FocusContainer.ts +368 -0
  122. package/src/components/Graphic.ts +239 -73
  123. package/src/components/Joystick.ts +363 -0
  124. package/src/components/Mesh.ts +222 -0
  125. package/src/components/NineSliceSprite.ts +4 -1
  126. package/src/components/ParticleEmitter.ts +12 -8
  127. package/src/components/Sprite.ts +418 -52
  128. package/src/components/Text.ts +270 -26
  129. package/src/components/Viewport.ts +122 -63
  130. package/src/components/index.ts +9 -2
  131. package/src/components/types/DisplayObject.ts +53 -5
  132. package/src/components/types/Spritesheet.ts +0 -118
  133. package/src/directives/Controls.ts +254 -0
  134. package/src/directives/ControlsBase.ts +267 -0
  135. package/src/directives/Drag.ts +357 -52
  136. package/src/directives/Flash.ts +419 -0
  137. package/src/directives/FocusNavigation.ts +113 -0
  138. package/src/directives/FogVisibility.ts +273 -0
  139. package/src/directives/GamepadControls.ts +537 -0
  140. package/src/directives/JoystickControls.ts +396 -0
  141. package/src/directives/KeyboardControls.ts +85 -430
  142. package/src/directives/Scheduler.ts +21 -5
  143. package/src/directives/Shake.ts +298 -0
  144. package/src/directives/Sound.ts +94 -31
  145. package/src/directives/ViewportFollow.ts +40 -9
  146. package/src/directives/index.ts +13 -6
  147. package/src/engine/FocusManager.ts +510 -0
  148. package/src/engine/animation.ts +175 -21
  149. package/src/engine/bootstrap.ts +140 -6
  150. package/src/engine/directive.ts +4 -4
  151. package/src/engine/reactive.ts +980 -177
  152. package/src/engine/signal.ts +241 -47
  153. package/src/engine/trigger.ts +34 -7
  154. package/src/engine/utils.ts +19 -3
  155. package/src/hooks/useFocus.ts +91 -0
  156. package/src/hooks/useProps.ts +1 -1
  157. package/src/index.ts +8 -2
  158. package/src/types/pixi-cull.d.ts +7 -0
  159. package/src/utils/GlobalAssetLoader.ts +257 -0
  160. package/src/utils/functions.ts +7 -0
  161. package/src/utils/tabindex.ts +70 -0
  162. package/testing/index.ts +35 -4
  163. package/tsconfig.json +18 -0
  164. package/vite.config.ts +39 -0
@@ -1,25 +1,30 @@
1
- import { Node } from "yoga-layout";
2
- import { Element, isElement, Props } from "../engine/reactive";
1
+ import { Element, isElement, Props, isElementFrozen } from "../engine/reactive";
3
2
  import { setObservablePoint } from "../engine/utils";
4
3
  import type {
5
4
  AlignContent,
6
5
  EdgeSize,
7
6
  FlexDirection,
8
- Size,
7
+ ObjectFit,
8
+ ObjectPosition,
9
+ TransformOrigin,
9
10
  } from "./types/DisplayObject";
10
- import { effect, Signal, signal } from "@signe/reactive";
11
- import { DropShadowFilter } from "pixi-filters";
12
- import { BlurFilter, ObservablePoint } from "pixi.js";
11
+ import { signal } from "@signe/reactive";
12
+ import { BlurFilter, ObservablePoint, type Point, type Rectangle } from "pixi.js";
13
+ import * as FILTERS from "pixi-filters";
14
+ import { isPercent } from "../utils/functions";
15
+ import { BehaviorSubject, filter, Subject } from "rxjs";
13
16
 
14
17
  export interface ComponentInstance extends PixiMixins.ContainerOptions {
15
18
  id?: string;
16
19
  children?: ComponentInstance[];
17
20
  onInit?(props: Props): void;
18
21
  onUpdate?(props: Props): void;
19
- onDestroy?(parent: Element): void;
20
- onMount?(context: Element, index?: number): void;
22
+ onDestroy?(parent: Element, afterDestroy: () => void): void;
23
+ onMount?(context: Element<any>, index?: number): void;
21
24
  setWidth(width: number): void;
22
25
  setHeight(height: number): void;
26
+ getLocalBounds?(): Rectangle;
27
+ getGlobalPosition?(): Point;
23
28
  }
24
29
 
25
30
  export const EVENTS = [
@@ -93,6 +98,8 @@ export const EVENTS = [
93
98
  "wheelcapture",
94
99
  ];
95
100
 
101
+ export type OnHook = (() => void) | (() => Promise<void> | void);
102
+
96
103
  export function DisplayObject(extendClass) {
97
104
  return class DisplayObject extends extendClass {
98
105
  #canvasContext: {
@@ -101,117 +108,162 @@ export function DisplayObject(extendClass) {
101
108
  isFlex: boolean = false;
102
109
  fullProps: Props = {};
103
110
  isMounted: boolean = false;
104
- _anchorPoints = new ObservablePoint(
105
- { _onUpdate: () => {} },
106
- 0,
107
- 0
108
- );
111
+ _anchorPoints = new ObservablePoint({ _onUpdate: () => {} }, 0, 0);
109
112
  isCustomAnchor: boolean = false;
110
113
  displayWidth = signal(0);
111
114
  displayHeight = signal(0);
112
115
  overrideProps: string[] = [];
113
- node: Node;
114
-
115
- get yoga() {
116
- return this.#canvasContext?.Yoga;
117
- }
116
+ layout = null;
117
+ onBeforeDestroy: OnHook | null = null;
118
+ onAfterMount: OnHook | null = null;
119
+ subjectInit = new BehaviorSubject(null);
120
+ disableLayout: boolean = false;
121
+ // Store registered event listeners for cleanup
122
+ #registeredEvents: Map<string, Function> = new Map();
123
+ // Store computed layout box dimensions
124
+ #computedLayoutBox: { width?: number; height?: number } | null = null;
125
+ // Store reference to element for freeze checking
126
+ #element: Element<any> | null = null;
127
+
128
+ /**
129
+ * Get the element reference for freeze checking
130
+ * @returns The element reference or null
131
+ */
132
+ getElement(): Element<any> | null {
133
+ return this.#element;
134
+ }
135
+
136
+ onLayoutComputed(_event: any) {}
118
137
 
119
138
  get deltaRatio() {
120
139
  return this.#canvasContext?.scheduler?.tick.value.deltaRatio;
121
140
  }
122
141
 
123
- onInit(props) {
142
+ get parentIsFlex() {
143
+ if (this.disableLayout) return false;
144
+ return this.parent?.isFlex;
145
+ }
146
+
147
+ onInit(props: Props) {
148
+ // Ensure layout setter from @pixi/layout is used when available.
149
+ if (Object.prototype.hasOwnProperty.call(this, "layout")) {
150
+ delete (this as any).layout;
151
+ }
124
152
  this._id = props.id;
125
153
  for (let event of EVENTS) {
126
154
  if (props[event] && !this.overrideProps.includes(event)) {
127
155
  this.eventMode = "static";
128
- this.on(event, props[event]);
156
+ const originalEventHandler = props[event];
157
+
158
+ // Wrap event handler to check freeze state
159
+ const wrappedHandler = (...args: any[]) => {
160
+ // Check if element is frozen before executing handler
161
+ if (this.#element && isElementFrozen(this.#element)) {
162
+ return;
163
+ }
164
+ return originalEventHandler(...args);
165
+ };
166
+
167
+ // Store the wrapped event handler for cleanup
168
+ if (event === 'click') {
169
+ this.on('pointertap', wrappedHandler);
170
+ this.#registeredEvents.set('pointertap', wrappedHandler);
171
+ } else {
172
+ this.on(event, wrappedHandler);
173
+ this.#registeredEvents.set(event, wrappedHandler);
174
+ }
129
175
  }
130
176
  }
177
+ if (props.onBeforeDestroy || props['on-before-destroy']) {
178
+ this.onBeforeDestroy = props.onBeforeDestroy || props['on-before-destroy'];
179
+ }
180
+ if (props.onAfterMount || props['on-after-mount']) {
181
+ this.onAfterMount = props.onAfterMount || props['on-after-mount'];
182
+ }
183
+ if (
184
+ props.justifyContent ||
185
+ props.alignItems ||
186
+ props.flexDirection ||
187
+ props.flexWrap ||
188
+ props.alignContent ||
189
+ props.display == "flex" ||
190
+ isPercent(props.width) ||
191
+ isPercent(props.height) ||
192
+ props.isRoot
193
+ ) {
194
+ this.layout = {};
195
+ this.isFlex = true;
196
+ }
197
+
198
+ this.subjectInit.next(this);
131
199
  }
132
200
 
133
- onMount({ parent, props }: Element<DisplayObject>, index?: number) {
134
- this.#canvasContext = props.context;
135
- this.node = this.yoga.Node.create();
136
- if (parent) {
137
- const instance = parent.componentInstance as DisplayObject;
138
- if (index === undefined) {
201
+ async onMount(element: Element<any>, index?: number) {
202
+ if (this.destroyed) return
203
+ this.#element = element;
204
+ this.#canvasContext = element.props.context;
205
+ if (element.parent) {
206
+ let parentElement = element.parent;
207
+ let instance = parentElement.componentInstance as DisplayObject;
208
+ if (typeof (instance as any)?.addChild !== "function") {
209
+ let search = parentElement.parent;
210
+ while (search && typeof (search.componentInstance as any)?.addChild !== "function") {
211
+ search = search.parent;
212
+ }
213
+ if (search && typeof (search.componentInstance as any)?.addChild === "function") {
214
+ parentElement = search;
215
+ instance = parentElement.componentInstance as DisplayObject;
216
+ } else {
217
+ console.warn("DisplayObject mount skipped: parent has no addChild", {
218
+ child: element.tag,
219
+ parent: element.parent?.tag,
220
+ });
221
+ return;
222
+ }
223
+ }
224
+ if (instance.isFlex && !this.layout && !this.disableLayout) {
225
+ try {
226
+ this.layout = {};
227
+ } catch (error) {
228
+ console.warn('Failed to set layout:', error);
229
+ }
230
+ }
231
+ if (index === undefined || parentElement !== element.parent || typeof (instance as any)?.addChildAt !== "function") {
139
232
  instance.addChild(this);
140
233
  } else {
141
234
  instance.addChildAt(this, index);
142
235
  }
143
- if (instance.layer) this.parentLayer = instance.layer;
144
236
  this.isMounted = true;
145
- this.effectSize(props.width, props.height);
146
- this.onUpdate(props);
147
- this.parent.node.insertChild(
148
- this.node,
149
- this.parent.node.getChildCount()
150
- );
151
- if (parent.props.flexDirection) {
152
- this.parent.node.calculateLayout();
153
- for (let child of this.parent.children) {
154
- const { left, top } = child.getComputedLayout();
155
- child.x = left;
156
- child.y = top;
237
+ this.onUpdate(element.props);
238
+
239
+ // Listen to layout events to store computed layout dimensions
240
+ const layoutHandler = (event: any) => {
241
+ if (event.computedLayout) {
242
+ this.#computedLayoutBox = {
243
+ width: event.computedLayout.width,
244
+ height: event.computedLayout.height,
245
+ };
157
246
  }
247
+ this.onLayoutComputed(event);
248
+ };
249
+ this.on('layout', layoutHandler);
250
+ this.#registeredEvents.set('layout', layoutHandler);
251
+
252
+ if (this.onAfterMount) {
253
+ await this.onAfterMount();
158
254
  }
159
-
160
- }
161
- }
162
-
163
- effectSize(width: Size, height: Size) {
164
- const handleSize = (
165
- size: Size,
166
- setter: (value: number) => void,
167
- parentSize: Signal<number>
168
- ) => {
169
- if (typeof size === "string" && size.endsWith("%")) {
170
- effect(() => {
171
- setter(parentSize() * (parseInt(size) / 100));
172
- if (this.isFlex) {
173
- this.#applyFlexLayout();
174
- }
175
- });
176
- } else {
177
- setter(+size);
178
- }
179
- };
180
-
181
- if (width != undefined)
182
- handleSize(width, this.setWidth.bind(this), this.parent.displayWidth);
183
- if (height != undefined)
184
- handleSize(
185
- height,
186
- this.setHeight.bind(this),
187
- this.parent.displayHeight
188
- );
189
- }
190
-
191
- #applyFlexLayout() {
192
- this.calculateLayout();
193
- for (let child of this.children) {
194
- const { left, top } = child.node.getComputedLayout();
195
- child.x = left;
196
- child.y = top;
197
- }
198
- }
199
-
200
- #flexRender(props) {
201
- if (!this.parent) return;
202
- if (props.flexDirection || props.justifyContent) {
203
- this.isFlex = true;
204
- this.#applyFlexLayout();
205
255
  }
206
256
  }
207
257
 
208
- onUpdate(props) {
258
+ onUpdate(props: Props) {
209
259
  this.fullProps = {
210
260
  ...this.fullProps,
211
261
  ...props,
212
262
  };
213
263
 
264
+ if (this.destroyed) return
214
265
  if (!this.#canvasContext || !this.parent) return;
266
+
215
267
  if (props.x !== undefined) this.setX(props.x);
216
268
  if (props.y !== undefined) this.setY(props.y);
217
269
  if (props.scale !== undefined)
@@ -219,6 +271,28 @@ export function DisplayObject(extendClass) {
219
271
  if (props.anchor !== undefined && !this.isCustomAnchor) {
220
272
  setObservablePoint(this.anchor, props.anchor);
221
273
  }
274
+ if (props.width !== undefined) this.setWidth(props.width);
275
+ if (props.height !== undefined) this.setHeight(props.height);
276
+ if (props.minWidth !== undefined) this.setMinWidth(props.minWidth);
277
+ if (props.minHeight !== undefined) this.setMinHeight(props.minHeight);
278
+ if (props.maxWidth !== undefined) this.setMaxWidth(props.maxWidth);
279
+ if (props.maxHeight !== undefined) this.setMaxHeight(props.maxHeight);
280
+ if (props.aspectRatio !== undefined)
281
+ this.setAspectRatio(props.aspectRatio);
282
+ if (props.flexGrow !== undefined) this.setFlexGrow(props.flexGrow);
283
+ if (props.flexShrink !== undefined) this.setFlexShrink(props.flexShrink);
284
+ if (props.flexBasis !== undefined) this.setFlexBasis(props.flexBasis);
285
+ if (props.rowGap !== undefined) this.setRowGap(props.rowGap);
286
+ if (props.columnGap !== undefined) this.setColumnGap(props.columnGap);
287
+ if (props.top !== undefined) this.setTop(props.top);
288
+ if (props.left !== undefined) this.setLeft(props.left);
289
+ if (props.right !== undefined) this.setRight(props.right);
290
+ if (props.bottom !== undefined) this.setBottom(props.bottom);
291
+ if (props.objectFit !== undefined) this.setObjectFit(props.objectFit);
292
+ if (props.objectPosition !== undefined)
293
+ this.setObjectPosition(props.objectPosition);
294
+ if (props.transformOrigin !== undefined)
295
+ this.setTransformOrigin(props.transformOrigin);
222
296
  if (props.skew !== undefined) setObservablePoint(this.skew, props.skew);
223
297
  if (props.tint) this.tint = props.tint;
224
298
  if (props.rotation !== undefined) this.rotation = props.rotation;
@@ -243,23 +317,51 @@ export function DisplayObject(extendClass) {
243
317
  if (props.filters) this.filters = props.filters;
244
318
  if (props.maskOf) {
245
319
  if (isElement(props.maskOf)) {
246
- props.maskOf.componentInstance.mask = this;
320
+ props.maskOf.componentInstance.mask = this as any;
321
+ }
322
+ }
323
+ if (props.shadowCaster !== undefined) {
324
+ const shadowCasterValue = (props.shadowCaster as any)?.value ?? props.shadowCaster;
325
+ if (
326
+ shadowCasterValue &&
327
+ typeof shadowCasterValue === "object" &&
328
+ !Array.isArray(shadowCasterValue)
329
+ ) {
330
+ const current = ((this as any).shadowCaster ?? {}) as Record<string, unknown>;
331
+ (this as any).shadowCaster = { ...current, ...shadowCasterValue };
332
+ } else {
333
+ (this as any).shadowCaster = shadowCasterValue;
334
+ }
335
+ }
336
+ if (props.footprintCaster !== undefined) {
337
+ const footprintCasterValue =
338
+ (props.footprintCaster as any)?.value ?? props.footprintCaster;
339
+ if (
340
+ footprintCasterValue &&
341
+ typeof footprintCasterValue === "object" &&
342
+ !Array.isArray(footprintCasterValue)
343
+ ) {
344
+ const current = ((this as any).footprintCaster ?? {}) as Record<string, unknown>;
345
+ (this as any).footprintCaster = { ...current, ...footprintCasterValue };
346
+ } else {
347
+ (this as any).footprintCaster = footprintCasterValue;
247
348
  }
248
349
  }
249
350
  if (props.blendMode) this.blendMode = props.blendMode;
250
351
  if (props.filterArea) this.filterArea = props.filterArea;
251
352
  const currentFilters = this.filters || [];
252
353
 
253
- if (props.shadow) {
254
- let dropShadowFilter = currentFilters.find(
255
- (filter) => filter instanceof DropShadowFilter
256
- );
257
- if (!dropShadowFilter) {
258
- dropShadowFilter = new DropShadowFilter();
259
- currentFilters.push(dropShadowFilter);
260
- }
261
- Object.assign(dropShadowFilter, props.shadow);
262
- }
354
+ // TODO: Fix DropShadowFilter import issue
355
+ // if (props.shadow) {
356
+ // let dropShadowFilter = currentFilters.find(
357
+ // (filter) => filter instanceof FILTERS.DropShadowFilter
358
+ // );
359
+ // if (!dropShadowFilter) {
360
+ // dropShadowFilter = new FILTERS.DropShadowFilter();
361
+ // currentFilters.push(dropShadowFilter);
362
+ // }
363
+ // Object.assign(dropShadowFilter, props.shadow);
364
+ // }
263
365
 
264
366
  if (props.blur) {
265
367
  let blurFilter = currentFilters.find(
@@ -279,73 +381,44 @@ export function DisplayObject(extendClass) {
279
381
  }
280
382
 
281
383
  this.filters = currentFilters;
282
-
283
- this.#flexRender(props);
284
384
  }
285
385
 
286
- onDestroy() {
287
- super.destroy();
288
- this.node?.freeRecursive();
289
- }
290
-
291
- getComputedLayout() {
292
- return this.node.getComputedLayout();
293
- }
294
-
295
- applyComputedLayout() {
296
- const layout = this.getComputedLayout();
297
- this.x = layout.left;
298
- this.y = layout.top;
299
- }
386
+ async onDestroy(parent: Element, afterDestroy?: () => void) {
387
+ // Remove all registered event listeners
388
+ for (const [eventName, eventHandler] of this.#registeredEvents) {
389
+ this.off(eventName, eventHandler);
390
+ }
391
+ this.#registeredEvents.clear();
392
+ this.#element = null;
300
393
 
301
- calculateLayout() {
302
- this.node.calculateLayout();
394
+ if (this.onBeforeDestroy) {
395
+ await this.onBeforeDestroy();
396
+ }
397
+ if (afterDestroy) afterDestroy();
398
+ if (this.parent && typeof this.parent.removeChild === "function") {
399
+ this.parent.removeChild(this);
400
+ }
401
+ super.destroy();
303
402
  }
304
403
 
305
404
  setFlexDirection(direction: FlexDirection) {
306
- const mapping = {
307
- row: this.yoga.FLEX_DIRECTION_ROW,
308
- column: this.yoga.FLEX_DIRECTION_COLUMN,
309
- "row-reverse": this.yoga.FLEX_DIRECTION_ROW_REVERSE,
310
- "column-reverse": this.yoga.FLEX_DIRECTION_COLUMN_REVERSE,
311
- };
312
- this.node.setFlexDirection(mapping[direction]);
405
+ this.layout = { flexDirection: direction };
313
406
  }
314
407
 
315
408
  setFlexWrap(wrap: "wrap" | "nowrap" | "wrap-reverse") {
316
- const mapping = {
317
- wrap: this.yoga.WRAP_WRAP,
318
- nowrap: this.yoga.WRAP_NO_WRAP,
319
- "wrap-reverse": this.yoga.WRAP_WRAP_REVERSE,
320
- };
321
- this.node.setFlexWrap(mapping[wrap]);
322
- }
323
-
324
- #setAlign(methodName: string, align: AlignContent) {
325
- const mapping = {
326
- auto: this.yoga.ALIGN_AUTO,
327
- "flex-start": this.yoga.ALIGN_FLEX_START,
328
- "flex-end": this.yoga.ALIGN_FLEX_END,
329
- center: this.yoga.ALIGN_CENTER,
330
- stretch: this.yoga.ALIGN_STRETCH,
331
- baseline: this.yoga.ALIGN_BASELINE,
332
- "space-between": this.yoga.ALIGN_SPACE_BETWEEN,
333
- "space-around": this.yoga.ALIGN_SPACE_AROUND,
334
- };
335
- const method = (this.node as any)[methodName].bind(this.node);
336
- method(mapping[align]);
409
+ this.layout = { flexWrap: wrap };
337
410
  }
338
411
 
339
412
  setAlignContent(align: AlignContent) {
340
- this.#setAlign("setAlignContent", align);
413
+ this.layout = { alignContent: align };
341
414
  }
342
415
 
343
416
  setAlignSelf(align: AlignContent) {
344
- this.#setAlign("setAlignSelf", align);
417
+ this.layout = { alignSelf: align };
345
418
  }
346
419
 
347
420
  setAlignItems(align: AlignContent) {
348
- this.#setAlign("setAlignItems", align);
421
+ this.layout = { alignItems: align };
349
422
  }
350
423
 
351
424
  setJustifyContent(
@@ -356,103 +429,251 @@ export function DisplayObject(extendClass) {
356
429
  | "space-between"
357
430
  | "space-around"
358
431
  ) {
359
- const mapping = {
360
- "flex-start": this.yoga.JUSTIFY_FLEX_START,
361
- "flex-end": this.yoga.JUSTIFY_FLEX_END,
362
- center: this.yoga.JUSTIFY_CENTER,
363
- "space-between": this.yoga.JUSTIFY_SPACE_BETWEEN,
364
- "space-around": this.yoga.JUSTIFY_SPACE_AROUND,
365
- };
366
- this.node.setJustifyContent(mapping[justifyContent]);
367
- }
368
-
369
- #setEdgeSize(methodName: string, size: EdgeSize) {
370
- const method = (this.node as any)[methodName].bind(this.node);
371
- if (size instanceof Array) {
372
- if (size.length === 2) {
373
- method(this.yoga.EDGE_VERTICAL, size[0]);
374
- method(this.yoga.EDGE_HORIZONTAL, size[1]);
375
- } else if (size.length === 4) {
376
- method(this.yoga.EDGE_TOP, size[0]);
377
- method(this.yoga.EDGE_RIGHT, size[1]);
378
- method(this.yoga.EDGE_BOTTOM, size[2]);
379
- method(this.yoga.EDGE_LEFT, size[3]);
380
- }
381
- } else {
382
- method(this.yoga.EDGE_ALL, size);
383
- }
432
+ this.layout = { justifyContent };
384
433
  }
385
434
 
386
435
  setPosition(position: EdgeSize) {
387
- this.#setEdgeSize("setPosition", position);
436
+ if (position instanceof Array) {
437
+ if (position.length === 2) {
438
+ this.layout = {
439
+ positionY: position[0],
440
+ positionX: position[1],
441
+ };
442
+ } else if (position.length === 4) {
443
+ this.layout = {
444
+ positionTop: position[0],
445
+ positionRight: position[1],
446
+ positionBottom: position[2],
447
+ positionLeft: position[3],
448
+ };
449
+ }
450
+ } else {
451
+ this.layout = { position };
452
+ }
388
453
  }
389
454
 
390
455
  setX(x: number) {
391
456
  x = x + this.getWidth() * this._anchorPoints.x;
392
- if (!this.parent.isFlex) {
457
+ if (!this.parentIsFlex) {
458
+ this.x = x;
459
+ } else {
393
460
  this.x = x;
461
+ this.layout = { x };
394
462
  }
395
- this.node.setPosition(this.yoga.EDGE_LEFT, x);
396
463
  }
397
464
 
398
465
  setY(y: number) {
399
466
  y = y + this.getHeight() * this._anchorPoints.y;
400
- if (!this.parent.isFlex) {
467
+ if (!this.parentIsFlex) {
468
+ this.y = y;
469
+ } else {
401
470
  this.y = y;
471
+ this.layout = { y };
402
472
  }
403
- this.node.setPosition(this.yoga.EDGE_TOP, y);
404
473
  }
405
474
 
406
475
  setPadding(padding: EdgeSize) {
407
- this.#setEdgeSize("setPadding", padding);
476
+ if (padding instanceof Array) {
477
+ if (padding.length === 2) {
478
+ this.layout = {
479
+ paddingVertical: padding[0],
480
+ paddingHorizontal: padding[1],
481
+ };
482
+ } else if (padding.length === 4) {
483
+ this.layout = {
484
+ paddingTop: padding[0],
485
+ paddingRight: padding[1],
486
+ paddingBottom: padding[2],
487
+ paddingLeft: padding[3],
488
+ };
489
+ }
490
+ } else {
491
+ this.layout = { padding };
492
+ }
408
493
  }
409
494
 
410
495
  setMargin(margin: EdgeSize) {
411
- this.#setEdgeSize("setMargin", margin);
496
+ if (margin instanceof Array) {
497
+ if (margin.length === 2) {
498
+ this.layout = {
499
+ marginVertical: margin[0],
500
+ marginHorizontal: margin[1],
501
+ };
502
+ } else if (margin.length === 4) {
503
+ this.layout = {
504
+ marginTop: margin[0],
505
+ marginRight: margin[1],
506
+ marginBottom: margin[2],
507
+ marginLeft: margin[3],
508
+ };
509
+ }
510
+ } else {
511
+ this.layout = { margin };
512
+ }
412
513
  }
413
514
 
414
515
  setGap(gap: EdgeSize) {
415
- this.node.setGap(this.yoga.GAP_ALL, +gap);
516
+ this.layout = { gap };
416
517
  }
417
518
 
418
519
  setBorder(border: EdgeSize) {
419
- this.#setEdgeSize("setBorder", border);
520
+ if (border instanceof Array) {
521
+ if (border.length === 2) {
522
+ this.layout = {
523
+ borderVertical: border[0],
524
+ borderHorizontal: border[1],
525
+ };
526
+ } else if (border.length === 4) {
527
+ this.layout = {
528
+ borderTop: border[0],
529
+ borderRight: border[1],
530
+ borderBottom: border[2],
531
+ borderLeft: border[3],
532
+ };
533
+ }
534
+ } else {
535
+ this.layout = { border };
536
+ }
420
537
  }
421
538
 
422
539
  setPositionType(positionType: "relative" | "absolute") {
423
- const mapping = {
424
- relative: this.yoga.POSITION_TYPE_RELATIVE,
425
- absolute: this.yoga.POSITION_TYPE_ABSOLUTE,
426
- };
427
- this.node.setPositionType(mapping[positionType]);
428
- }
429
-
430
- calculateBounds() {
431
- super.calculateBounds();
432
- if (!this._geometry) return;
433
- const bounds = this._geometry.bounds;
434
- const width = Math.abs(bounds.minX - bounds.maxX);
435
- const height = Math.abs(bounds.minY - bounds.maxY);
436
- // this.node.setWidth(width);
437
- // this.node.setHeight(height);
540
+ this.layout = { position: positionType };
438
541
  }
439
542
 
440
543
  setWidth(width: number) {
441
544
  this.displayWidth.set(width);
442
- this.node?.setWidth(width);
545
+ if (!this.parentIsFlex) {
546
+ this.width = width;
547
+ } else {
548
+ this.layout = { width };
549
+ }
443
550
  }
444
551
 
445
552
  setHeight(height: number) {
446
553
  this.displayHeight.set(height);
447
- this.node?.setHeight(height);
554
+ if (!this.parentIsFlex) {
555
+ this.height = height;
556
+ } else {
557
+ this.layout = { height };
558
+ }
559
+ }
560
+
561
+ getWidth(): number {
562
+ // If width is a percentage, use computed layout box
563
+ if (isPercent(this.fullProps.width)) {
564
+ if (this.#computedLayoutBox?.width !== undefined) {
565
+ return this.#computedLayoutBox.width;
566
+ }
567
+ // Fallback to native width if layout not yet computed
568
+ return typeof this.width === 'number' ? this.width : 0;
569
+ }
570
+ // For static values, use native PixiJS width or displayWidth signal
571
+ const staticWidth = typeof this.width === 'number' && this.width > 0
572
+ ? this.width
573
+ : (typeof this.displayWidth() === 'number' ? this.displayWidth() : 0);
574
+ return staticWidth;
575
+ }
576
+
577
+ getHeight(): number {
578
+ // If height is a percentage, use computed layout box
579
+ if (isPercent(this.fullProps.height)) {
580
+ if (this.#computedLayoutBox?.height !== undefined) {
581
+ return this.#computedLayoutBox.height;
582
+ }
583
+ // Fallback to native height if layout not yet computed
584
+ return typeof this.height === 'number' ? this.height : 0;
585
+ }
586
+ // For static values, use native PixiJS height or displayHeight signal
587
+ const staticHeight = typeof this.height === 'number' && this.height > 0
588
+ ? this.height
589
+ : (typeof this.displayHeight() === 'number' ? this.displayHeight() : 0);
590
+ return staticHeight;
591
+ }
592
+
593
+ // Min/Max constraints
594
+ setMinWidth(minWidth: number | string) {
595
+ this.layout = { minWidth };
448
596
  }
449
597
 
450
- getWidth() {
451
- return this.displayWidth();
598
+ setMinHeight(minHeight: number | string) {
599
+ this.layout = { minHeight };
452
600
  }
453
601
 
454
- getHeight() {
455
- return this.displayHeight();
602
+ setMaxWidth(maxWidth: number | string) {
603
+ this.layout = { maxWidth };
604
+ }
605
+
606
+ setMaxHeight(maxHeight: number | string) {
607
+ this.layout = { maxHeight };
608
+ }
609
+
610
+ // Aspect ratio
611
+ setAspectRatio(aspectRatio: number) {
612
+ this.layout = { aspectRatio };
613
+ }
614
+
615
+ // Flex properties
616
+ setFlexGrow(flexGrow: number) {
617
+ this.layout = { flexGrow };
618
+ }
619
+
620
+ setFlexShrink(flexShrink: number) {
621
+ this.layout = { flexShrink };
622
+ }
623
+
624
+ setFlexBasis(flexBasis: number | string) {
625
+ this.layout = { flexBasis };
626
+ }
627
+
628
+ // Gap properties
629
+ setRowGap(rowGap: number) {
630
+ this.layout = { rowGap };
631
+ }
632
+
633
+ setColumnGap(columnGap: number) {
634
+ this.layout = { columnGap };
635
+ }
636
+
637
+ // Position insets
638
+ setTop(top: number | string) {
639
+ this.layout = { top };
640
+ }
641
+
642
+ setLeft(left: number | string) {
643
+ this.layout = { left };
644
+ }
645
+
646
+ setRight(right: number | string) {
647
+ this.layout = { right };
648
+ }
649
+
650
+ setBottom(bottom: number | string) {
651
+ this.layout = { bottom };
652
+ }
653
+
654
+ // Object properties
655
+ setObjectFit(objectFit: ObjectFit) {
656
+ try {
657
+ this.layout = { objectFit };
658
+ } catch (error) {
659
+ // Ignore layout errors in test environments or when yoga-layout is not available
660
+ }
661
+ }
662
+
663
+ setObjectPosition(objectPosition: ObjectPosition) {
664
+ try {
665
+ this.layout = { objectPosition };
666
+ } catch (error) {
667
+ // Ignore layout errors in test environments or when yoga-layout is not available
668
+ }
669
+ }
670
+
671
+ setTransformOrigin(transformOrigin: TransformOrigin) {
672
+ try {
673
+ this.layout = { transformOrigin };
674
+ } catch (error) {
675
+ // Ignore layout errors in test environments or when yoga-layout is not available
676
+ }
456
677
  }
457
678
  };
458
679
  }