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.
Files changed (172) hide show
  1. package/dist/components/Button.d.ts +185 -0
  2. package/dist/components/Button.d.ts.map +1 -0
  3. package/dist/components/Canvas.d.ts +17 -0
  4. package/dist/components/Canvas.d.ts.map +1 -0
  5. package/dist/components/Container.d.ts +86 -0
  6. package/dist/components/Container.d.ts.map +1 -0
  7. package/dist/components/DOMContainer.d.ts +98 -0
  8. package/dist/components/DOMContainer.d.ts.map +1 -0
  9. package/dist/components/DOMElement.d.ts +54 -0
  10. package/dist/components/DOMElement.d.ts.map +1 -0
  11. package/dist/components/DOMSprite.d.ts +127 -0
  12. package/dist/components/DOMSprite.d.ts.map +1 -0
  13. package/dist/components/DisplayObject.d.ts +94 -0
  14. package/dist/components/DisplayObject.d.ts.map +1 -0
  15. package/dist/components/FocusContainer.d.ts +129 -0
  16. package/dist/components/FocusContainer.d.ts.map +1 -0
  17. package/dist/components/Graphic.d.ts +64 -0
  18. package/dist/components/Graphic.d.ts.map +1 -0
  19. package/dist/components/Joystick.d.ts +36 -0
  20. package/dist/components/Joystick.d.ts.map +1 -0
  21. package/dist/components/Mesh.d.ts +208 -0
  22. package/dist/components/Mesh.d.ts.map +1 -0
  23. package/dist/components/NineSliceSprite.d.ts +16 -0
  24. package/dist/components/NineSliceSprite.d.ts.map +1 -0
  25. package/dist/components/ParticleEmitter.d.ts +4 -0
  26. package/dist/components/ParticleEmitter.d.ts.map +1 -0
  27. package/dist/components/Scene.d.ts +2 -0
  28. package/dist/components/Scene.d.ts.map +1 -0
  29. package/dist/components/Sprite.d.ts +242 -0
  30. package/dist/components/Sprite.d.ts.map +1 -0
  31. package/dist/components/Text.d.ts +25 -0
  32. package/dist/components/Text.d.ts.map +1 -0
  33. package/dist/components/TilingSprite.d.ts +17 -0
  34. package/dist/components/TilingSprite.d.ts.map +1 -0
  35. package/dist/components/Video.d.ts +14 -0
  36. package/dist/components/Video.d.ts.map +1 -0
  37. package/dist/components/Viewport.d.ts +121 -0
  38. package/dist/components/Viewport.d.ts.map +1 -0
  39. package/dist/components/index.d.ts +20 -0
  40. package/dist/components/index.d.ts.map +1 -0
  41. package/dist/components/types/DisplayObject.d.ts +106 -0
  42. package/dist/components/types/DisplayObject.d.ts.map +1 -0
  43. package/dist/components/types/MouseEvent.d.ts +4 -0
  44. package/dist/components/types/MouseEvent.d.ts.map +1 -0
  45. package/dist/components/types/Spritesheet.d.ts +248 -0
  46. package/dist/components/types/Spritesheet.d.ts.map +1 -0
  47. package/dist/components/types/index.d.ts +4 -0
  48. package/dist/components/types/index.d.ts.map +1 -0
  49. package/dist/directives/Controls.d.ts +112 -0
  50. package/dist/directives/Controls.d.ts.map +1 -0
  51. package/dist/directives/ControlsBase.d.ts +199 -0
  52. package/dist/directives/ControlsBase.d.ts.map +1 -0
  53. package/dist/directives/Drag.d.ts +69 -0
  54. package/dist/directives/Drag.d.ts.map +1 -0
  55. package/dist/directives/Flash.d.ts +116 -0
  56. package/dist/directives/Flash.d.ts.map +1 -0
  57. package/dist/directives/FocusNavigation.d.ts +52 -0
  58. package/dist/directives/FocusNavigation.d.ts.map +1 -0
  59. package/dist/directives/GamepadControls.d.ts +224 -0
  60. package/dist/directives/GamepadControls.d.ts.map +1 -0
  61. package/dist/directives/JoystickControls.d.ts +171 -0
  62. package/dist/directives/JoystickControls.d.ts.map +1 -0
  63. package/dist/directives/KeyboardControls.d.ts +219 -0
  64. package/dist/directives/KeyboardControls.d.ts.map +1 -0
  65. package/dist/directives/Scheduler.d.ts +35 -0
  66. package/dist/directives/Scheduler.d.ts.map +1 -0
  67. package/dist/directives/Shake.d.ts +98 -0
  68. package/dist/directives/Shake.d.ts.map +1 -0
  69. package/dist/directives/Sound.d.ts +25 -0
  70. package/dist/directives/Sound.d.ts.map +1 -0
  71. package/dist/directives/Transition.d.ts +10 -0
  72. package/dist/directives/Transition.d.ts.map +1 -0
  73. package/dist/directives/ViewportCull.d.ts +11 -0
  74. package/dist/directives/ViewportCull.d.ts.map +1 -0
  75. package/dist/directives/ViewportFollow.d.ts +18 -0
  76. package/dist/directives/ViewportFollow.d.ts.map +1 -0
  77. package/dist/directives/index.d.ts +13 -0
  78. package/dist/directives/index.d.ts.map +1 -0
  79. package/dist/engine/FocusManager.d.ts +174 -0
  80. package/dist/engine/FocusManager.d.ts.map +1 -0
  81. package/dist/engine/animation.d.ts +72 -0
  82. package/dist/engine/animation.d.ts.map +1 -0
  83. package/dist/engine/bootstrap.d.ts +48 -0
  84. package/dist/engine/bootstrap.d.ts.map +1 -0
  85. package/dist/engine/directive.d.ts +13 -0
  86. package/dist/engine/directive.d.ts.map +1 -0
  87. package/dist/engine/reactive.d.ts +134 -0
  88. package/dist/engine/reactive.d.ts.map +1 -0
  89. package/dist/engine/signal.d.ts +71 -0
  90. package/dist/engine/signal.d.ts.map +1 -0
  91. package/dist/engine/trigger.d.ts +54 -0
  92. package/dist/engine/trigger.d.ts.map +1 -0
  93. package/dist/engine/utils.d.ts +89 -0
  94. package/dist/engine/utils.d.ts.map +1 -0
  95. package/dist/hooks/addContext.d.ts +2 -0
  96. package/dist/hooks/addContext.d.ts.map +1 -0
  97. package/dist/hooks/useFocus.d.ts +60 -0
  98. package/dist/hooks/useFocus.d.ts.map +1 -0
  99. package/dist/hooks/useProps.d.ts +42 -0
  100. package/dist/hooks/useProps.d.ts.map +1 -0
  101. package/dist/hooks/useRef.d.ts +4 -0
  102. package/dist/hooks/useRef.d.ts.map +1 -0
  103. package/dist/index-DaGekQUW.js +2218 -0
  104. package/dist/index-DaGekQUW.js.map +1 -0
  105. package/dist/index.d.ts +19 -1099
  106. package/dist/index.d.ts.map +1 -0
  107. package/dist/index.global.js +5 -0
  108. package/dist/index.global.js.map +1 -0
  109. package/dist/index.js +11749 -2901
  110. package/dist/index.js.map +1 -1
  111. package/dist/utils/Ease.d.ts +17 -0
  112. package/dist/utils/Ease.d.ts.map +1 -0
  113. package/dist/utils/GlobalAssetLoader.d.ts +141 -0
  114. package/dist/utils/GlobalAssetLoader.d.ts.map +1 -0
  115. package/dist/utils/RadialGradient.d.ts +57 -0
  116. package/dist/utils/RadialGradient.d.ts.map +1 -0
  117. package/dist/utils/functions.d.ts +2 -0
  118. package/dist/utils/functions.d.ts.map +1 -0
  119. package/dist/utils/tabindex.d.ts +16 -0
  120. package/dist/utils/tabindex.d.ts.map +1 -0
  121. package/package.json +13 -7
  122. package/src/components/Button.ts +399 -0
  123. package/src/components/Canvas.ts +62 -46
  124. package/src/components/Container.ts +21 -2
  125. package/src/components/DOMContainer.ts +379 -0
  126. package/src/components/DOMElement.ts +556 -0
  127. package/src/components/DOMSprite.ts +1040 -0
  128. package/src/components/DisplayObject.ts +392 -201
  129. package/src/components/FocusContainer.ts +368 -0
  130. package/src/components/Graphic.ts +227 -66
  131. package/src/components/Joystick.ts +363 -0
  132. package/src/components/Mesh.ts +222 -0
  133. package/src/components/NineSliceSprite.ts +4 -1
  134. package/src/components/ParticleEmitter.ts +12 -8
  135. package/src/components/Sprite.ts +297 -31
  136. package/src/components/Text.ts +125 -18
  137. package/src/components/Video.ts +2 -2
  138. package/src/components/Viewport.ts +118 -63
  139. package/src/components/index.ts +9 -2
  140. package/src/components/types/DisplayObject.ts +41 -4
  141. package/src/components/types/Spritesheet.ts +0 -118
  142. package/src/directives/Controls.ts +254 -0
  143. package/src/directives/ControlsBase.ts +267 -0
  144. package/src/directives/Drag.ts +357 -52
  145. package/src/directives/Flash.ts +419 -0
  146. package/src/directives/FocusNavigation.ts +113 -0
  147. package/src/directives/GamepadControls.ts +537 -0
  148. package/src/directives/JoystickControls.ts +396 -0
  149. package/src/directives/KeyboardControls.ts +85 -430
  150. package/src/directives/Scheduler.ts +12 -4
  151. package/src/directives/Shake.ts +298 -0
  152. package/src/directives/Sound.ts +94 -31
  153. package/src/directives/ViewportFollow.ts +40 -9
  154. package/src/directives/index.ts +12 -6
  155. package/src/engine/FocusManager.ts +510 -0
  156. package/src/engine/animation.ts +175 -21
  157. package/src/engine/bootstrap.ts +93 -3
  158. package/src/engine/directive.ts +4 -4
  159. package/src/engine/reactive.ts +901 -161
  160. package/src/engine/signal.ts +113 -25
  161. package/src/engine/trigger.ts +34 -7
  162. package/src/engine/utils.ts +19 -3
  163. package/src/hooks/useFocus.ts +91 -0
  164. package/src/hooks/useProps.ts +1 -1
  165. package/src/index.ts +8 -2
  166. package/src/types/pixi-cull.d.ts +7 -0
  167. package/src/utils/GlobalAssetLoader.ts +257 -0
  168. package/src/utils/functions.ts +7 -0
  169. package/src/utils/tabindex.ts +70 -0
  170. package/testing/index.ts +35 -4
  171. package/tsconfig.json +18 -0
  172. package/vite.config.ts +39 -0
@@ -1,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
255
  }
198
256
  }
199
257
 
200
- #flexRender(props) {
201
- if (!this.parent) return;
202
- if (props.flexDirection || props.justifyContent) {
203
- this.isFlex = true;
204
- this.#applyFlexLayout();
205
- }
206
- }
207
-
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,24 @@ 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;
247
321
  }
248
322
  }
249
323
  if (props.blendMode) this.blendMode = props.blendMode;
250
324
  if (props.filterArea) this.filterArea = props.filterArea;
251
325
  const currentFilters = this.filters || [];
252
326
 
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
- }
327
+ // TODO: Fix DropShadowFilter import issue
328
+ // if (props.shadow) {
329
+ // let dropShadowFilter = currentFilters.find(
330
+ // (filter) => filter instanceof FILTERS.DropShadowFilter
331
+ // );
332
+ // if (!dropShadowFilter) {
333
+ // dropShadowFilter = new FILTERS.DropShadowFilter();
334
+ // currentFilters.push(dropShadowFilter);
335
+ // }
336
+ // Object.assign(dropShadowFilter, props.shadow);
337
+ // }
263
338
 
264
339
  if (props.blur) {
265
340
  let blurFilter = currentFilters.find(
@@ -279,73 +354,41 @@ export function DisplayObject(extendClass) {
279
354
  }
280
355
 
281
356
  this.filters = currentFilters;
282
-
283
- this.#flexRender(props);
284
- }
285
-
286
- onDestroy() {
287
- super.destroy();
288
- this.node?.freeRecursive();
289
357
  }
290
358
 
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
- }
359
+ async onDestroy(parent: Element, afterDestroy?: () => void) {
360
+ // Remove all registered event listeners
361
+ for (const [eventName, eventHandler] of this.#registeredEvents) {
362
+ this.off(eventName, eventHandler);
363
+ }
364
+ this.#registeredEvents.clear();
365
+ this.#element = null;
300
366
 
301
- calculateLayout() {
302
- this.node.calculateLayout();
367
+ if (this.onBeforeDestroy) {
368
+ await this.onBeforeDestroy();
369
+ }
370
+ if (afterDestroy) afterDestroy();
371
+ super.destroy();
303
372
  }
304
373
 
305
374
  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]);
375
+ this.layout = { flexDirection: direction };
313
376
  }
314
377
 
315
378
  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]);
379
+ this.layout = { flexWrap: wrap };
337
380
  }
338
381
 
339
382
  setAlignContent(align: AlignContent) {
340
- this.#setAlign("setAlignContent", align);
383
+ this.layout = { alignContent: align };
341
384
  }
342
385
 
343
386
  setAlignSelf(align: AlignContent) {
344
- this.#setAlign("setAlignSelf", align);
387
+ this.layout = { alignSelf: align };
345
388
  }
346
389
 
347
390
  setAlignItems(align: AlignContent) {
348
- this.#setAlign("setAlignItems", align);
391
+ this.layout = { alignItems: align };
349
392
  }
350
393
 
351
394
  setJustifyContent(
@@ -356,103 +399,251 @@ export function DisplayObject(extendClass) {
356
399
  | "space-between"
357
400
  | "space-around"
358
401
  ) {
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
- }
402
+ this.layout = { justifyContent };
384
403
  }
385
404
 
386
405
  setPosition(position: EdgeSize) {
387
- this.#setEdgeSize("setPosition", position);
406
+ if (position instanceof Array) {
407
+ if (position.length === 2) {
408
+ this.layout = {
409
+ positionY: position[0],
410
+ positionX: position[1],
411
+ };
412
+ } else if (position.length === 4) {
413
+ this.layout = {
414
+ positionTop: position[0],
415
+ positionRight: position[1],
416
+ positionBottom: position[2],
417
+ positionLeft: position[3],
418
+ };
419
+ }
420
+ } else {
421
+ this.layout = { position };
422
+ }
388
423
  }
389
424
 
390
425
  setX(x: number) {
391
426
  x = x + this.getWidth() * this._anchorPoints.x;
392
- if (!this.parent.isFlex) {
427
+ if (!this.parentIsFlex) {
393
428
  this.x = x;
429
+ } else {
430
+ this.x = x;
431
+ this.layout = { x };
394
432
  }
395
- this.node.setPosition(this.yoga.EDGE_LEFT, x);
396
433
  }
397
434
 
398
435
  setY(y: number) {
399
436
  y = y + this.getHeight() * this._anchorPoints.y;
400
- if (!this.parent.isFlex) {
437
+ if (!this.parentIsFlex) {
438
+ this.y = y;
439
+ } else {
401
440
  this.y = y;
441
+ this.layout = { y };
402
442
  }
403
- this.node.setPosition(this.yoga.EDGE_TOP, y);
404
443
  }
405
444
 
406
445
  setPadding(padding: EdgeSize) {
407
- this.#setEdgeSize("setPadding", padding);
446
+ if (padding instanceof Array) {
447
+ if (padding.length === 2) {
448
+ this.layout = {
449
+ paddingVertical: padding[0],
450
+ paddingHorizontal: padding[1],
451
+ };
452
+ } else if (padding.length === 4) {
453
+ this.layout = {
454
+ paddingTop: padding[0],
455
+ paddingRight: padding[1],
456
+ paddingBottom: padding[2],
457
+ paddingLeft: padding[3],
458
+ };
459
+ }
460
+ } else {
461
+ this.layout = { padding };
462
+ }
408
463
  }
409
464
 
410
465
  setMargin(margin: EdgeSize) {
411
- this.#setEdgeSize("setMargin", margin);
466
+ if (margin instanceof Array) {
467
+ if (margin.length === 2) {
468
+ this.layout = {
469
+ marginVertical: margin[0],
470
+ marginHorizontal: margin[1],
471
+ };
472
+ } else if (margin.length === 4) {
473
+ this.layout = {
474
+ marginTop: margin[0],
475
+ marginRight: margin[1],
476
+ marginBottom: margin[2],
477
+ marginLeft: margin[3],
478
+ };
479
+ }
480
+ } else {
481
+ this.layout = { margin };
482
+ }
412
483
  }
413
484
 
414
485
  setGap(gap: EdgeSize) {
415
- this.node.setGap(this.yoga.GAP_ALL, +gap);
486
+ this.layout = { gap };
416
487
  }
417
488
 
418
489
  setBorder(border: EdgeSize) {
419
- this.#setEdgeSize("setBorder", border);
490
+ if (border instanceof Array) {
491
+ if (border.length === 2) {
492
+ this.layout = {
493
+ borderVertical: border[0],
494
+ borderHorizontal: border[1],
495
+ };
496
+ } else if (border.length === 4) {
497
+ this.layout = {
498
+ borderTop: border[0],
499
+ borderRight: border[1],
500
+ borderBottom: border[2],
501
+ borderLeft: border[3],
502
+ };
503
+ }
504
+ } else {
505
+ this.layout = { border };
506
+ }
420
507
  }
421
508
 
422
509
  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);
510
+ this.layout = { position: positionType };
438
511
  }
439
512
 
440
513
  setWidth(width: number) {
441
514
  this.displayWidth.set(width);
442
- this.node?.setWidth(width);
515
+ if (!this.parentIsFlex) {
516
+ this.width = width;
517
+ } else {
518
+ this.layout = { width };
519
+ }
443
520
  }
444
521
 
445
522
  setHeight(height: number) {
446
523
  this.displayHeight.set(height);
447
- this.node?.setHeight(height);
524
+ if (!this.parentIsFlex) {
525
+ this.height = height;
526
+ } else {
527
+ this.layout = { height };
528
+ }
529
+ }
530
+
531
+ getWidth(): number {
532
+ // If width is a percentage, use computed layout box
533
+ if (isPercent(this.fullProps.width)) {
534
+ if (this.#computedLayoutBox?.width !== undefined) {
535
+ return this.#computedLayoutBox.width;
536
+ }
537
+ // Fallback to native width if layout not yet computed
538
+ return typeof this.width === 'number' ? this.width : 0;
539
+ }
540
+ // For static values, use native PixiJS width or displayWidth signal
541
+ const staticWidth = typeof this.width === 'number' && this.width > 0
542
+ ? this.width
543
+ : (typeof this.displayWidth() === 'number' ? this.displayWidth() : 0);
544
+ return staticWidth;
545
+ }
546
+
547
+ getHeight(): number {
548
+ // If height is a percentage, use computed layout box
549
+ if (isPercent(this.fullProps.height)) {
550
+ if (this.#computedLayoutBox?.height !== undefined) {
551
+ return this.#computedLayoutBox.height;
552
+ }
553
+ // Fallback to native height if layout not yet computed
554
+ return typeof this.height === 'number' ? this.height : 0;
555
+ }
556
+ // For static values, use native PixiJS height or displayHeight signal
557
+ const staticHeight = typeof this.height === 'number' && this.height > 0
558
+ ? this.height
559
+ : (typeof this.displayHeight() === 'number' ? this.displayHeight() : 0);
560
+ return staticHeight;
561
+ }
562
+
563
+ // Min/Max constraints
564
+ setMinWidth(minWidth: number | string) {
565
+ this.layout = { minWidth };
566
+ }
567
+
568
+ setMinHeight(minHeight: number | string) {
569
+ this.layout = { minHeight };
448
570
  }
449
571
 
450
- getWidth() {
451
- return this.displayWidth();
572
+ setMaxWidth(maxWidth: number | string) {
573
+ this.layout = { maxWidth };
452
574
  }
453
575
 
454
- getHeight() {
455
- return this.displayHeight();
576
+ setMaxHeight(maxHeight: number | string) {
577
+ this.layout = { maxHeight };
578
+ }
579
+
580
+ // Aspect ratio
581
+ setAspectRatio(aspectRatio: number) {
582
+ this.layout = { aspectRatio };
583
+ }
584
+
585
+ // Flex properties
586
+ setFlexGrow(flexGrow: number) {
587
+ this.layout = { flexGrow };
588
+ }
589
+
590
+ setFlexShrink(flexShrink: number) {
591
+ this.layout = { flexShrink };
592
+ }
593
+
594
+ setFlexBasis(flexBasis: number | string) {
595
+ this.layout = { flexBasis };
596
+ }
597
+
598
+ // Gap properties
599
+ setRowGap(rowGap: number) {
600
+ this.layout = { rowGap };
601
+ }
602
+
603
+ setColumnGap(columnGap: number) {
604
+ this.layout = { columnGap };
605
+ }
606
+
607
+ // Position insets
608
+ setTop(top: number | string) {
609
+ this.layout = { top };
610
+ }
611
+
612
+ setLeft(left: number | string) {
613
+ this.layout = { left };
614
+ }
615
+
616
+ setRight(right: number | string) {
617
+ this.layout = { right };
618
+ }
619
+
620
+ setBottom(bottom: number | string) {
621
+ this.layout = { bottom };
622
+ }
623
+
624
+ // Object properties
625
+ setObjectFit(objectFit: ObjectFit) {
626
+ try {
627
+ this.layout = { objectFit };
628
+ } catch (error) {
629
+ // Ignore layout errors in test environments or when yoga-layout is not available
630
+ }
631
+ }
632
+
633
+ setObjectPosition(objectPosition: ObjectPosition) {
634
+ try {
635
+ this.layout = { objectPosition };
636
+ } catch (error) {
637
+ // Ignore layout errors in test environments or when yoga-layout is not available
638
+ }
639
+ }
640
+
641
+ setTransformOrigin(transformOrigin: TransformOrigin) {
642
+ try {
643
+ this.layout = { transformOrigin };
644
+ } catch (error) {
645
+ // Ignore layout errors in test environments or when yoga-layout is not available
646
+ }
456
647
  }
457
648
  };
458
649
  }