canvasengine 2.0.0-beta.6 → 2.0.0-beta.60

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 +25 -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 +35 -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 +48 -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 +134 -0
  82. package/dist/engine/reactive.d.ts.map +1 -0
  83. package/dist/engine/signal.d.ts +71 -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 +5 -0
  100. package/dist/index.global.js.map +1 -0
  101. package/dist/index.js +9768 -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 +15 -9
  114. package/src/components/Button.ts +399 -0
  115. package/src/components/Canvas.ts +62 -46
  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 +419 -201
  121. package/src/components/FocusContainer.ts +368 -0
  122. package/src/components/Graphic.ts +227 -66
  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 +125 -18
  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 +12 -4
  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 +93 -3
  150. package/src/engine/directive.ts +4 -4
  151. package/src/engine/reactive.ts +979 -176
  152. package/src/engine/signal.ts +113 -25
  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,41 @@ 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
+ super.destroy();
303
399
  }
304
400
 
305
401
  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]);
402
+ this.layout = { flexDirection: direction };
313
403
  }
314
404
 
315
405
  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]);
406
+ this.layout = { flexWrap: wrap };
337
407
  }
338
408
 
339
409
  setAlignContent(align: AlignContent) {
340
- this.#setAlign("setAlignContent", align);
410
+ this.layout = { alignContent: align };
341
411
  }
342
412
 
343
413
  setAlignSelf(align: AlignContent) {
344
- this.#setAlign("setAlignSelf", align);
414
+ this.layout = { alignSelf: align };
345
415
  }
346
416
 
347
417
  setAlignItems(align: AlignContent) {
348
- this.#setAlign("setAlignItems", align);
418
+ this.layout = { alignItems: align };
349
419
  }
350
420
 
351
421
  setJustifyContent(
@@ -356,103 +426,251 @@ export function DisplayObject(extendClass) {
356
426
  | "space-between"
357
427
  | "space-around"
358
428
  ) {
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
- }
429
+ this.layout = { justifyContent };
384
430
  }
385
431
 
386
432
  setPosition(position: EdgeSize) {
387
- this.#setEdgeSize("setPosition", position);
433
+ if (position instanceof Array) {
434
+ if (position.length === 2) {
435
+ this.layout = {
436
+ positionY: position[0],
437
+ positionX: position[1],
438
+ };
439
+ } else if (position.length === 4) {
440
+ this.layout = {
441
+ positionTop: position[0],
442
+ positionRight: position[1],
443
+ positionBottom: position[2],
444
+ positionLeft: position[3],
445
+ };
446
+ }
447
+ } else {
448
+ this.layout = { position };
449
+ }
388
450
  }
389
451
 
390
452
  setX(x: number) {
391
453
  x = x + this.getWidth() * this._anchorPoints.x;
392
- if (!this.parent.isFlex) {
454
+ if (!this.parentIsFlex) {
455
+ this.x = x;
456
+ } else {
393
457
  this.x = x;
458
+ this.layout = { x };
394
459
  }
395
- this.node.setPosition(this.yoga.EDGE_LEFT, x);
396
460
  }
397
461
 
398
462
  setY(y: number) {
399
463
  y = y + this.getHeight() * this._anchorPoints.y;
400
- if (!this.parent.isFlex) {
464
+ if (!this.parentIsFlex) {
401
465
  this.y = y;
466
+ } else {
467
+ this.y = y;
468
+ this.layout = { y };
402
469
  }
403
- this.node.setPosition(this.yoga.EDGE_TOP, y);
404
470
  }
405
471
 
406
472
  setPadding(padding: EdgeSize) {
407
- this.#setEdgeSize("setPadding", padding);
473
+ if (padding instanceof Array) {
474
+ if (padding.length === 2) {
475
+ this.layout = {
476
+ paddingVertical: padding[0],
477
+ paddingHorizontal: padding[1],
478
+ };
479
+ } else if (padding.length === 4) {
480
+ this.layout = {
481
+ paddingTop: padding[0],
482
+ paddingRight: padding[1],
483
+ paddingBottom: padding[2],
484
+ paddingLeft: padding[3],
485
+ };
486
+ }
487
+ } else {
488
+ this.layout = { padding };
489
+ }
408
490
  }
409
491
 
410
492
  setMargin(margin: EdgeSize) {
411
- this.#setEdgeSize("setMargin", margin);
493
+ if (margin instanceof Array) {
494
+ if (margin.length === 2) {
495
+ this.layout = {
496
+ marginVertical: margin[0],
497
+ marginHorizontal: margin[1],
498
+ };
499
+ } else if (margin.length === 4) {
500
+ this.layout = {
501
+ marginTop: margin[0],
502
+ marginRight: margin[1],
503
+ marginBottom: margin[2],
504
+ marginLeft: margin[3],
505
+ };
506
+ }
507
+ } else {
508
+ this.layout = { margin };
509
+ }
412
510
  }
413
511
 
414
512
  setGap(gap: EdgeSize) {
415
- this.node.setGap(this.yoga.GAP_ALL, +gap);
513
+ this.layout = { gap };
416
514
  }
417
515
 
418
516
  setBorder(border: EdgeSize) {
419
- this.#setEdgeSize("setBorder", border);
517
+ if (border instanceof Array) {
518
+ if (border.length === 2) {
519
+ this.layout = {
520
+ borderVertical: border[0],
521
+ borderHorizontal: border[1],
522
+ };
523
+ } else if (border.length === 4) {
524
+ this.layout = {
525
+ borderTop: border[0],
526
+ borderRight: border[1],
527
+ borderBottom: border[2],
528
+ borderLeft: border[3],
529
+ };
530
+ }
531
+ } else {
532
+ this.layout = { border };
533
+ }
420
534
  }
421
535
 
422
536
  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);
537
+ this.layout = { position: positionType };
438
538
  }
439
539
 
440
540
  setWidth(width: number) {
441
541
  this.displayWidth.set(width);
442
- this.node?.setWidth(width);
542
+ if (!this.parentIsFlex) {
543
+ this.width = width;
544
+ } else {
545
+ this.layout = { width };
546
+ }
443
547
  }
444
548
 
445
549
  setHeight(height: number) {
446
550
  this.displayHeight.set(height);
447
- this.node?.setHeight(height);
551
+ if (!this.parentIsFlex) {
552
+ this.height = height;
553
+ } else {
554
+ this.layout = { height };
555
+ }
448
556
  }
449
557
 
450
- getWidth() {
451
- return this.displayWidth();
558
+ getWidth(): number {
559
+ // If width is a percentage, use computed layout box
560
+ if (isPercent(this.fullProps.width)) {
561
+ if (this.#computedLayoutBox?.width !== undefined) {
562
+ return this.#computedLayoutBox.width;
563
+ }
564
+ // Fallback to native width if layout not yet computed
565
+ return typeof this.width === 'number' ? this.width : 0;
566
+ }
567
+ // For static values, use native PixiJS width or displayWidth signal
568
+ const staticWidth = typeof this.width === 'number' && this.width > 0
569
+ ? this.width
570
+ : (typeof this.displayWidth() === 'number' ? this.displayWidth() : 0);
571
+ return staticWidth;
572
+ }
573
+
574
+ getHeight(): number {
575
+ // If height is a percentage, use computed layout box
576
+ if (isPercent(this.fullProps.height)) {
577
+ if (this.#computedLayoutBox?.height !== undefined) {
578
+ return this.#computedLayoutBox.height;
579
+ }
580
+ // Fallback to native height if layout not yet computed
581
+ return typeof this.height === 'number' ? this.height : 0;
582
+ }
583
+ // For static values, use native PixiJS height or displayHeight signal
584
+ const staticHeight = typeof this.height === 'number' && this.height > 0
585
+ ? this.height
586
+ : (typeof this.displayHeight() === 'number' ? this.displayHeight() : 0);
587
+ return staticHeight;
588
+ }
589
+
590
+ // Min/Max constraints
591
+ setMinWidth(minWidth: number | string) {
592
+ this.layout = { minWidth };
452
593
  }
453
594
 
454
- getHeight() {
455
- return this.displayHeight();
595
+ setMinHeight(minHeight: number | string) {
596
+ this.layout = { minHeight };
597
+ }
598
+
599
+ setMaxWidth(maxWidth: number | string) {
600
+ this.layout = { maxWidth };
601
+ }
602
+
603
+ setMaxHeight(maxHeight: number | string) {
604
+ this.layout = { maxHeight };
605
+ }
606
+
607
+ // Aspect ratio
608
+ setAspectRatio(aspectRatio: number) {
609
+ this.layout = { aspectRatio };
610
+ }
611
+
612
+ // Flex properties
613
+ setFlexGrow(flexGrow: number) {
614
+ this.layout = { flexGrow };
615
+ }
616
+
617
+ setFlexShrink(flexShrink: number) {
618
+ this.layout = { flexShrink };
619
+ }
620
+
621
+ setFlexBasis(flexBasis: number | string) {
622
+ this.layout = { flexBasis };
623
+ }
624
+
625
+ // Gap properties
626
+ setRowGap(rowGap: number) {
627
+ this.layout = { rowGap };
628
+ }
629
+
630
+ setColumnGap(columnGap: number) {
631
+ this.layout = { columnGap };
632
+ }
633
+
634
+ // Position insets
635
+ setTop(top: number | string) {
636
+ this.layout = { top };
637
+ }
638
+
639
+ setLeft(left: number | string) {
640
+ this.layout = { left };
641
+ }
642
+
643
+ setRight(right: number | string) {
644
+ this.layout = { right };
645
+ }
646
+
647
+ setBottom(bottom: number | string) {
648
+ this.layout = { bottom };
649
+ }
650
+
651
+ // Object properties
652
+ setObjectFit(objectFit: ObjectFit) {
653
+ try {
654
+ this.layout = { objectFit };
655
+ } catch (error) {
656
+ // Ignore layout errors in test environments or when yoga-layout is not available
657
+ }
658
+ }
659
+
660
+ setObjectPosition(objectPosition: ObjectPosition) {
661
+ try {
662
+ this.layout = { objectPosition };
663
+ } catch (error) {
664
+ // Ignore layout errors in test environments or when yoga-layout is not available
665
+ }
666
+ }
667
+
668
+ setTransformOrigin(transformOrigin: TransformOrigin) {
669
+ try {
670
+ this.layout = { transformOrigin };
671
+ } catch (error) {
672
+ // Ignore layout errors in test environments or when yoga-layout is not available
673
+ }
456
674
  }
457
675
  };
458
676
  }