canvasengine 2.0.0-beta.3 → 2.0.0-beta.31

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 (130) hide show
  1. package/dist/DebugRenderer-DrlzuIVv.js +172 -0
  2. package/dist/DebugRenderer-DrlzuIVv.js.map +1 -0
  3. package/dist/components/Button.d.ts +136 -0
  4. package/dist/components/Button.d.ts.map +1 -0
  5. package/dist/components/Canvas.d.ts +18 -0
  6. package/dist/components/Canvas.d.ts.map +1 -0
  7. package/dist/components/Container.d.ts +80 -0
  8. package/dist/components/Container.d.ts.map +1 -0
  9. package/dist/components/DOMContainer.d.ts +77 -0
  10. package/dist/components/DOMContainer.d.ts.map +1 -0
  11. package/dist/components/DOMElement.d.ts +44 -0
  12. package/dist/components/DOMElement.d.ts.map +1 -0
  13. package/dist/components/DisplayObject.d.ts +82 -0
  14. package/dist/components/DisplayObject.d.ts.map +1 -0
  15. package/dist/components/Graphic.d.ts +65 -0
  16. package/dist/components/Graphic.d.ts.map +1 -0
  17. package/dist/components/Mesh.d.ts +202 -0
  18. package/dist/components/Mesh.d.ts.map +1 -0
  19. package/dist/components/NineSliceSprite.d.ts +17 -0
  20. package/dist/components/NineSliceSprite.d.ts.map +1 -0
  21. package/dist/components/ParticleEmitter.d.ts +5 -0
  22. package/dist/components/ParticleEmitter.d.ts.map +1 -0
  23. package/dist/components/Scene.d.ts +2 -0
  24. package/dist/components/Scene.d.ts.map +1 -0
  25. package/dist/components/Sprite.d.ts +174 -0
  26. package/dist/components/Sprite.d.ts.map +1 -0
  27. package/dist/components/Text.d.ts +21 -0
  28. package/dist/components/Text.d.ts.map +1 -0
  29. package/dist/components/TilingSprite.d.ts +18 -0
  30. package/dist/components/TilingSprite.d.ts.map +1 -0
  31. package/dist/components/Video.d.ts +15 -0
  32. package/dist/components/Video.d.ts.map +1 -0
  33. package/dist/components/Viewport.d.ts +106 -0
  34. package/dist/components/Viewport.d.ts.map +1 -0
  35. package/dist/components/index.d.ts +17 -0
  36. package/dist/components/index.d.ts.map +1 -0
  37. package/dist/components/types/DisplayObject.d.ts +106 -0
  38. package/dist/components/types/DisplayObject.d.ts.map +1 -0
  39. package/dist/components/types/MouseEvent.d.ts +4 -0
  40. package/dist/components/types/MouseEvent.d.ts.map +1 -0
  41. package/dist/components/types/Spritesheet.d.ts +366 -0
  42. package/dist/components/types/Spritesheet.d.ts.map +1 -0
  43. package/dist/components/types/index.d.ts +5 -0
  44. package/dist/components/types/index.d.ts.map +1 -0
  45. package/dist/directives/Drag.d.ts +70 -0
  46. package/dist/directives/Drag.d.ts.map +1 -0
  47. package/dist/directives/KeyboardControls.d.ts +530 -0
  48. package/dist/directives/KeyboardControls.d.ts.map +1 -0
  49. package/dist/directives/Scheduler.d.ts +36 -0
  50. package/dist/directives/Scheduler.d.ts.map +1 -0
  51. package/dist/directives/Sound.d.ts +26 -0
  52. package/dist/directives/Sound.d.ts.map +1 -0
  53. package/dist/directives/Transition.d.ts +11 -0
  54. package/dist/directives/Transition.d.ts.map +1 -0
  55. package/dist/directives/ViewportCull.d.ts +12 -0
  56. package/dist/directives/ViewportCull.d.ts.map +1 -0
  57. package/dist/directives/ViewportFollow.d.ts +19 -0
  58. package/dist/directives/ViewportFollow.d.ts.map +1 -0
  59. package/dist/directives/index.d.ts +2 -0
  60. package/dist/directives/index.d.ts.map +1 -0
  61. package/dist/engine/animation.d.ts +59 -0
  62. package/dist/engine/animation.d.ts.map +1 -0
  63. package/dist/engine/bootstrap.d.ts +16 -0
  64. package/dist/engine/bootstrap.d.ts.map +1 -0
  65. package/dist/engine/directive.d.ts +14 -0
  66. package/dist/engine/directive.d.ts.map +1 -0
  67. package/dist/engine/reactive.d.ts +95 -0
  68. package/dist/engine/reactive.d.ts.map +1 -0
  69. package/dist/engine/signal.d.ts +72 -0
  70. package/dist/engine/signal.d.ts.map +1 -0
  71. package/dist/engine/trigger.d.ts +51 -0
  72. package/dist/engine/trigger.d.ts.map +1 -0
  73. package/dist/engine/utils.d.ts +90 -0
  74. package/dist/engine/utils.d.ts.map +1 -0
  75. package/dist/hooks/addContext.d.ts +2 -0
  76. package/dist/hooks/addContext.d.ts.map +1 -0
  77. package/dist/hooks/useProps.d.ts +42 -0
  78. package/dist/hooks/useProps.d.ts.map +1 -0
  79. package/dist/hooks/useRef.d.ts +5 -0
  80. package/dist/hooks/useRef.d.ts.map +1 -0
  81. package/dist/index-DNDNQN-q.js +11088 -0
  82. package/dist/index-DNDNQN-q.js.map +1 -0
  83. package/dist/index.d.ts +15 -919
  84. package/dist/index.d.ts.map +1 -0
  85. package/dist/index.global.js +29 -0
  86. package/dist/index.global.js.map +1 -0
  87. package/dist/index.js +63 -2950
  88. package/dist/index.js.map +1 -1
  89. package/dist/utils/Ease.d.ts +17 -0
  90. package/dist/utils/Ease.d.ts.map +1 -0
  91. package/dist/utils/RadialGradient.d.ts +58 -0
  92. package/dist/utils/RadialGradient.d.ts.map +1 -0
  93. package/dist/utils/functions.d.ts +2 -0
  94. package/dist/utils/functions.d.ts.map +1 -0
  95. package/index.d.ts +4 -0
  96. package/package.json +12 -7
  97. package/src/components/Button.ts +269 -0
  98. package/src/components/Canvas.ts +53 -45
  99. package/src/components/Container.ts +2 -2
  100. package/src/components/DOMContainer.ts +123 -0
  101. package/src/components/DOMElement.ts +421 -0
  102. package/src/components/DisplayObject.ts +283 -190
  103. package/src/components/Graphic.ts +200 -34
  104. package/src/components/Mesh.ts +222 -0
  105. package/src/components/NineSliceSprite.ts +4 -1
  106. package/src/components/ParticleEmitter.ts +12 -8
  107. package/src/components/Sprite.ts +92 -22
  108. package/src/components/Text.ts +34 -14
  109. package/src/components/Video.ts +110 -0
  110. package/src/components/Viewport.ts +59 -43
  111. package/src/components/index.ts +7 -2
  112. package/src/components/types/DisplayObject.ts +30 -0
  113. package/src/directives/Drag.ts +357 -52
  114. package/src/directives/KeyboardControls.ts +3 -1
  115. package/src/directives/Sound.ts +94 -31
  116. package/src/directives/ViewportFollow.ts +35 -7
  117. package/src/engine/animation.ts +41 -5
  118. package/src/engine/bootstrap.ts +23 -3
  119. package/src/engine/directive.ts +2 -2
  120. package/src/engine/reactive.ts +542 -170
  121. package/src/engine/signal.ts +22 -2
  122. package/src/engine/trigger.ts +65 -9
  123. package/src/engine/utils.ts +97 -9
  124. package/src/hooks/useProps.ts +1 -1
  125. package/src/index.ts +4 -1
  126. package/src/utils/RadialGradient.ts +29 -0
  127. package/src/utils/functions.ts +7 -0
  128. package/testing/index.ts +12 -0
  129. package/tsconfig.json +17 -0
  130. package/vite.config.ts +39 -0
@@ -1,54 +1,167 @@
1
- import { effect, Signal } from "@signe/reactive";
2
- import { Graphics as PixiGraphics } from "pixi.js";
3
- import { createComponent, registerComponent } from "../engine/reactive";
4
- import { DisplayObject } from "./DisplayObject";
1
+ import { Effect, effect, isSignal, signal, Signal, WritableSignal } from "@signe/reactive";
2
+ import { Assets, Graphics as PixiGraphics } from "pixi.js";
3
+ import { createComponent, Element, registerComponent } from "../engine/reactive";
4
+ import { ComponentInstance, DisplayObject } from "./DisplayObject";
5
5
  import { DisplayObjectProps } from "./types/DisplayObject";
6
6
  import { useProps } from "../hooks/useProps";
7
+ import { SignalOrPrimitive } from "./types";
8
+ import { isPercent } from "../utils/functions";
7
9
 
8
10
  interface GraphicsProps extends DisplayObjectProps {
9
- draw?: (graphics: PixiGraphics) => void;
11
+ draw?: (graphics: PixiGraphics, width: number, height: number) => void;
10
12
  }
11
13
 
12
14
  interface RectProps extends DisplayObjectProps {
13
- width: number;
14
- height: number;
15
- color: string;
15
+ color: SignalOrPrimitive<string>;
16
16
  }
17
17
 
18
18
  interface CircleProps extends DisplayObjectProps {
19
- radius: number;
20
- color: string;
19
+ radius: SignalOrPrimitive<number>;
20
+ color: SignalOrPrimitive<string>;
21
21
  }
22
22
 
23
23
  interface EllipseProps extends DisplayObjectProps {
24
- width: number;
25
- height: number;
26
- color: string;
24
+ color: SignalOrPrimitive<string>;
27
25
  }
28
26
 
29
27
  interface TriangleProps extends DisplayObjectProps {
30
- base: number;
31
- height: number;
32
- color: string;
28
+ base: SignalOrPrimitive<number>;
29
+ color: SignalOrPrimitive<string>;
33
30
  }
34
31
 
35
32
  interface SvgProps extends DisplayObjectProps {
36
- svg: string;
33
+ /** SVG content as string (legacy prop) */
34
+ svg?: string;
35
+ /** URL source of the SVG file to load */
36
+ src?: string;
37
+ /** Direct SVG content as string */
38
+ content?: string;
37
39
  }
38
40
 
39
41
  class CanvasGraphics extends DisplayObject(PixiGraphics) {
40
- onInit(props: GraphicsProps) {
41
- super.onInit(props);
42
+ clearEffect: Effect;
43
+ _width: WritableSignal<number>;
44
+ _height: WritableSignal<number>;
45
+
46
+ /**
47
+ * Initializes the graphics component with reactive width and height handling.
48
+ *
49
+ * This method handles different types of width and height props:
50
+ * - **Numbers**: Direct pixel values
51
+ * - **Strings with %**: Percentage values that trigger flex layout and use layout box dimensions
52
+ * - **Signals**: Reactive values that update automatically
53
+ *
54
+ * When percentage values are detected, the component:
55
+ * 1. Sets `display: 'flex'` to enable layout calculations
56
+ * 2. Listens to layout events to get computed dimensions
57
+ * 3. Updates internal width/height signals with layout box values
58
+ *
59
+ * The draw function receives the reactive width and height signals as parameters.
60
+ *
61
+ * @param props - Component properties including width, height, and draw function
62
+ * @example
63
+ * ```typescript
64
+ * // With pixel values
65
+ * Graphics({ width: 100, height: 50, draw: (g, w, h) => g.rect(0, 0, w(), h()) });
66
+ *
67
+ * // With percentage values (uses layout box)
68
+ * Graphics({ width: "50%", height: "100%", draw: (g, w, h) => g.rect(0, 0, w(), h()) });
69
+ *
70
+ * // With signals
71
+ * const width = signal(100);
72
+ * Graphics({ width, height: 50, draw: (g, w, h) => g.rect(0, 0, w(), h()) });
73
+ * ```
74
+ */
75
+ async onInit(props) {
76
+ await super.onInit(props);
77
+ }
78
+
79
+ /**
80
+ * Called when the component is mounted to the scene graph.
81
+ * Creates the reactive effect for drawing using the original signals from propObservables.
82
+ * @param {Element<DisplayObject>} element - The element being mounted with props and propObservables.
83
+ * @param {number} [index] - The index of the component among its siblings.
84
+ */
85
+ async onMount(element: Element<any>, index?: number): Promise<void> {
86
+ await super.onMount(element, index);
87
+ const { props, propObservables } = element;
88
+
89
+ // Use original signals from propObservables if available, otherwise create new ones
90
+ const width = (isSignal(propObservables?.width) ? propObservables.width : signal(props.width || 0)) as WritableSignal<number>;
91
+ const height = (isSignal(propObservables?.height) ? propObservables.height : signal(props.height || 0)) as WritableSignal<number>;
92
+
93
+ // Store as class properties for access in other methods
94
+ this._width = width;
95
+ this._height = height;
96
+
97
+ // Check if width or height are percentages to set display flex
98
+ const isWidthPercentage = isPercent(width());
99
+ const isHeightPercentage = isPercent(height());
100
+
42
101
  if (props.draw) {
43
- effect(() => {
102
+ this.clearEffect = effect(() => {
103
+ const w = width();
104
+ const h = height();
105
+ if (typeof w == 'string' || typeof h == 'string') {
106
+ return
107
+ }
44
108
  this.clear();
45
- props.draw?.(this);
109
+ props.draw?.(this, w, h);
110
+ this.subjectInit.next(this)
46
111
  });
47
112
  }
113
+
114
+ this.on('layout', (event) => {
115
+ const layoutBox = event.computedLayout;
116
+ // Update width if it's a percentage and value has changed
117
+ if (isWidthPercentage && isSignal(width) && width() !== layoutBox.width) {
118
+ width.set(layoutBox.width);
119
+ }
120
+
121
+ // Update height if it's a percentage and value has changed
122
+ if (isHeightPercentage && isSignal(height) && height() !== layoutBox.height) {
123
+ height.set(layoutBox.height);
124
+ }
125
+ });
48
126
  }
49
- }
50
127
 
51
- interface CanvasGraphics extends PixiGraphics {}
128
+ /**
129
+ * Called when component props are updated.
130
+ * Updates the internal width and height signals when props change.
131
+ * @param props - Updated properties
132
+ */
133
+ onUpdate(props: any) {
134
+ super.onUpdate(props);
135
+
136
+ // Update width signal if width prop changed and value is different
137
+ if (props.width !== undefined && this._width && this._width() !== props.width) {
138
+ this._width.set(props.width);
139
+ }
140
+
141
+ // Update height signal if height prop changed and value is different
142
+ if (props.height !== undefined && this._height && this._height() !== props.height) {
143
+ this._height.set(props.height);
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Called when the component is about to be destroyed.
149
+ * This method should be overridden by subclasses to perform any cleanup.
150
+ * It ensures that the clearEffect subscription is unsubscribed before calling the original afterDestroy callback.
151
+ * @param parent The parent element.
152
+ * @param afterDestroy A callback function to be executed after the component's own destruction logic.
153
+ * @example
154
+ * // This method is typically called by the engine internally.
155
+ * // await component.onDestroy(parentElement, () => console.log('Component destroyed'));
156
+ */
157
+ async onDestroy(parent: Element<ComponentInstance>, afterDestroy: () => void): Promise<void> {
158
+ const _afterDestroyCallback = async () => {
159
+ this.clearEffect?.subscription.unsubscribe();
160
+ afterDestroy();
161
+ }
162
+ await super.onDestroy(parent, _afterDestroyCallback);
163
+ }
164
+ }
52
165
 
53
166
  registerComponent("Graphics", CanvasGraphics);
54
167
 
@@ -57,16 +170,17 @@ export function Graphics(props: GraphicsProps) {
57
170
  }
58
171
 
59
172
  export function Rect(props: RectProps) {
60
- const { width, height, color, borderRadius, border } = useProps(props, {
173
+ const { color, borderRadius, border } = useProps(props, {
61
174
  borderRadius: null,
62
175
  border: null
63
176
  })
177
+
64
178
  return Graphics({
65
- draw: (g) => {
179
+ draw: (g, width, height) => {
66
180
  if (borderRadius()) {
67
- g.roundRect(0, 0, width(), height(), borderRadius());
181
+ g.roundRect(0, 0, width, height, borderRadius());
68
182
  } else {
69
- g.rect(0, 0, width(), height());
183
+ g.rect(0, 0, width, height);
70
184
  }
71
185
  if (border) {
72
186
  g.stroke(border);
@@ -114,7 +228,13 @@ export function Ellipse(props: EllipseProps) {
114
228
  border: null
115
229
  })
116
230
  return Graphics({
117
- draw: (g) => drawShape(g, 'ellipse', { width, height, color, border }),
231
+ draw: (g, gWidth, gHeight) => {
232
+ g.ellipse(0, 0, gWidth / 2, gHeight / 2);
233
+ if (border()) {
234
+ g.stroke(border());
235
+ }
236
+ g.fill(color());
237
+ },
118
238
  ...props
119
239
  })
120
240
  }
@@ -125,11 +245,11 @@ export function Triangle(props: TriangleProps) {
125
245
  color: '#000'
126
246
  })
127
247
  return Graphics({
128
- draw: (g) => {
129
- g.moveTo(0, height());
130
- g.lineTo(width() / 2, 0);
131
- g.lineTo(width(), height());
132
- g.lineTo(0, height());
248
+ draw: (g, gWidth, gHeight) => {
249
+ g.moveTo(0, gHeight);
250
+ g.lineTo(gWidth / 2, 0);
251
+ g.lineTo(gWidth, gHeight);
252
+ g.lineTo(0, gHeight);
133
253
  g.fill(color());
134
254
  if (border) {
135
255
  g.stroke(border);
@@ -139,9 +259,55 @@ export function Triangle(props: TriangleProps) {
139
259
  })
140
260
  }
141
261
 
262
+ /**
263
+ * Creates an SVG component that can render SVG graphics from URL, content, or legacy svg prop.
264
+ *
265
+ * This component provides three ways to display SVG graphics:
266
+ * - **src**: Load SVG from a URL using Assets.load with parseAsGraphicsContext option
267
+ * - **content**: Render SVG directly from string content using Graphics.svg() method
268
+ * - **svg**: Legacy prop for SVG content (for backward compatibility)
269
+ *
270
+ * @param props - Component properties including src, content, or svg
271
+ * @returns A reactive SVG component
272
+ * @example
273
+ * ```typescript
274
+ * // Load from URL
275
+ * const svgFromUrl = Svg({ src: "/assets/logo.svg" });
276
+ *
277
+ * // Direct content
278
+ * const svgFromContent = Svg({
279
+ * content: `<svg viewBox="0 0 100 100">
280
+ * <circle cx="50" cy="50" r="40" fill="blue"/>
281
+ * </svg>`
282
+ * });
283
+ *
284
+ * // Legacy usage
285
+ * const svgLegacy = Svg({ svg: "<svg>...</svg>" });
286
+ * ```
287
+ */
142
288
  export function Svg(props: SvgProps) {
143
289
  return Graphics({
144
- draw: (g) => g.svg(props.svg),
290
+ draw: async (g) => {
291
+ if (props.src) {
292
+ // Load SVG from source URL with graphics context parsing
293
+ const svgData = await Assets.load({
294
+ src: props.src,
295
+ data: {
296
+ parseAsGraphicsContext: true,
297
+ },
298
+ });
299
+
300
+ // Apply the loaded graphics context
301
+ const graphics = new PixiGraphics(svgData);
302
+ g.context = graphics.context;
303
+ } else if (props.content) {
304
+ // Render SVG directly from content string
305
+ g.svg(props.content);
306
+ } else if (props.svg) {
307
+ // Legacy prop support
308
+ g.svg(props.svg);
309
+ }
310
+ },
145
311
  ...props
146
312
  })
147
313
  }
@@ -0,0 +1,222 @@
1
+ import { Effect, effect } from "@signe/reactive";
2
+ import { Mesh as PixiMesh, Geometry, Shader, Texture, Assets, BLEND_MODES } from "pixi.js";
3
+ import { createComponent, Element, registerComponent } from "../engine/reactive";
4
+ import { ComponentInstance, DisplayObject } from "./DisplayObject";
5
+ import { DisplayObjectProps } from "./types/DisplayObject";
6
+ import { useProps } from "../hooks/useProps";
7
+ import { SignalOrPrimitive } from "./types";
8
+ import { ComponentFunction } from "../engine/signal";
9
+
10
+ /**
11
+ * Interface defining the properties for a Mesh component.
12
+ * Extends DisplayObjectProps to inherit common display object properties.
13
+ */
14
+ interface MeshProps extends DisplayObjectProps {
15
+ /** The geometry defining the mesh structure (vertices, indices, UVs, etc.) */
16
+ geometry?: Geometry;
17
+ /** The shader to render the mesh with */
18
+ shader?: Shader;
19
+ /** The texture to apply to the mesh */
20
+ texture?: Texture | string;
21
+ /** The image URL to load as texture */
22
+ image?: string;
23
+ /** The tint color to apply to the mesh */
24
+ tint?: SignalOrPrimitive<number>;
25
+ /** Whether to round pixels for sharper rendering */
26
+ roundPixels?: SignalOrPrimitive<boolean>;
27
+ }
28
+
29
+ /**
30
+ * Canvas Mesh component class that extends DisplayObject with PixiMesh functionality.
31
+ * This component allows rendering of custom 3D meshes with shaders and textures.
32
+ *
33
+ * @example
34
+ * ```typescript
35
+ * // Basic mesh with geometry and texture
36
+ * const mesh = Mesh({
37
+ * geometry: myGeometry,
38
+ * texture: "path/to/texture.png",
39
+ * tint: 0xff0000
40
+ * });
41
+ *
42
+ * // Mesh with custom shader
43
+ * const customMesh = Mesh({
44
+ * geometry: myGeometry,
45
+ * shader: myCustomShader,
46
+ * draw: (mesh) => {
47
+ * // Custom mesh manipulation
48
+ * mesh.rotation += 0.01;
49
+ * }
50
+ * });
51
+ * ```
52
+ */
53
+ class CanvasMesh extends DisplayObject(PixiMesh) {
54
+ /**
55
+ * Constructor for the CanvasMesh component.
56
+ * Initializes the PixiMesh with default geometry and shader to prevent errors.
57
+ *
58
+ * @example
59
+ * ```typescript
60
+ * // This constructor is called internally by the engine
61
+ * const mesh = new CanvasMesh();
62
+ * ```
63
+ */
64
+ constructor() {
65
+ // Call parent constructor with minimal options to prevent destructuring error
66
+ // @ts-ignore - PixiMesh constructor expects options object but TypeScript doesn't recognize it
67
+ super({
68
+ geometry: new Geometry()
69
+ });
70
+ }
71
+
72
+ /**
73
+ * Initializes the mesh component with the provided properties.
74
+ * This method is called before onUpdate to set up initial state.
75
+ *
76
+ * @param props - The initial properties
77
+ * @example
78
+ * ```typescript
79
+ * // This method is called internally when the component is created
80
+ * mesh.onInit({
81
+ * geometry: myGeometry,
82
+ * texture: "texture.png"
83
+ * });
84
+ * ```
85
+ */
86
+ onInit(props: MeshProps) {
87
+ super.onInit(props);
88
+
89
+ // Set initial geometry if provided
90
+ if (props.geometry) {
91
+ try {
92
+ this.geometry = props.geometry;
93
+ } catch (error) {
94
+ console.warn('Failed to set geometry:', error);
95
+ }
96
+ }
97
+
98
+ // Set initial shader if provided
99
+ if (props.shader) {
100
+ this.shader = props.shader;
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Updates the mesh component when properties change.
106
+ * Handles texture loading, shader updates, and other property changes.
107
+ *
108
+ * @param props - The updated properties
109
+ * @example
110
+ * ```typescript
111
+ * // This method is called internally when props change
112
+ * mesh.onUpdate({
113
+ * tint: 0x00ff00,
114
+ * texture: "new-texture.png"
115
+ * });
116
+ * ```
117
+ */
118
+ async onUpdate(props: MeshProps) {
119
+ super.onUpdate(props);
120
+
121
+ // Handle geometry updates
122
+ if (props.geometry) {
123
+ try {
124
+ this.geometry = props.geometry;
125
+ } catch (error) {
126
+ console.warn('Failed to update geometry:', error);
127
+ }
128
+ }
129
+
130
+ // Handle shader/material updates
131
+ if (props.shader) {
132
+ this.shader = props.shader;
133
+ }
134
+
135
+ // Handle texture updates
136
+ if (props.texture) {
137
+ if (typeof props.texture === 'string') {
138
+ this.texture = await Assets.load(props.texture);
139
+ } else {
140
+ this.texture = props.texture;
141
+ }
142
+ } else if (props.image) {
143
+ this.texture = await Assets.load(props.image);
144
+ }
145
+
146
+ // Handle tint updates
147
+ if (props.tint !== undefined) {
148
+ this.tint = props.tint;
149
+ }
150
+
151
+ // Handle blend mode updates
152
+ if (props.blendMode !== undefined) {
153
+ this.blendMode = props.blendMode;
154
+ }
155
+
156
+ // Handle round pixels updates
157
+ if (props.roundPixels !== undefined) {
158
+ this.roundPixels = props.roundPixels;
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Called when the component is about to be destroyed.
164
+ * Cleans up the draw effect subscription and calls the parent destroy method.
165
+ *
166
+ * @param parent - The parent element
167
+ * @param afterDestroy - Callback function to execute after destruction
168
+ * @example
169
+ * ```typescript
170
+ * // This method is typically called by the engine internally
171
+ * await mesh.onDestroy(parentElement, () => console.log('Mesh destroyed'));
172
+ * ```
173
+ */
174
+ async onDestroy(parent: Element<ComponentInstance>, afterDestroy: () => void): Promise<void> {
175
+ const _afterDestroyCallback = async () => {
176
+ afterDestroy();
177
+ };
178
+ await super.onDestroy(parent, _afterDestroyCallback);
179
+ }
180
+ }
181
+
182
+ // Register the component with the engine
183
+ registerComponent("Mesh", CanvasMesh);
184
+
185
+ /**
186
+ * Creates a Mesh component with the specified properties.
187
+ * This is the main function used to create mesh instances in your application.
188
+ *
189
+ * @param props - The properties for the mesh component
190
+ * @returns A mesh component element
191
+ * @example
192
+ * ```typescript
193
+ * import { Mesh } from 'canvasengine';
194
+ *
195
+ * // Create a basic textured mesh
196
+ * const myMesh = Mesh({
197
+ * geometry: triangleGeometry,
198
+ * texture: "assets/texture.png",
199
+ * x: 100,
200
+ * y: 100,
201
+ * tint: 0xff0000
202
+ * });
203
+ *
204
+ * // Create a mesh with custom shader
205
+ * const shaderMesh = Mesh({
206
+ * geometry: planeGeometry,
207
+ * shader: customShader,
208
+ * draw: (mesh) => {
209
+ * mesh.rotation += 0.01;
210
+ * }
211
+ * });
212
+ * ```
213
+ */
214
+ export const Mesh: ComponentFunction<MeshProps> = (props) => {
215
+ return createComponent("Mesh", props);
216
+ };
217
+
218
+ // Export the component class for advanced usage
219
+ export { CanvasMesh };
220
+
221
+ // Export the props interface for TypeScript users
222
+ export type { MeshProps };
@@ -2,6 +2,7 @@ import { Assets, NineSliceSprite as PixiNineSliceSprite, Texture } from "pixi.js
2
2
  import { createComponent, registerComponent } from "../engine/reactive";
3
3
  import { DisplayObject } from "./DisplayObject";
4
4
  import { DisplayObjectProps } from "./types/DisplayObject";
5
+ import { Layout } from "@pixi/layout";
5
6
 
6
7
  interface NineSliceSpriteProps extends DisplayObjectProps {
7
8
  image?: string;
@@ -37,7 +38,9 @@ class CanvasNineSliceSprite extends DisplayObject(PixiNineSliceSprite) {
37
38
  }
38
39
  }
39
40
 
40
- interface CanvasNineSliceSprite extends PixiNineSliceSprite {}
41
+ interface CanvasNineSliceSprite extends PixiNineSliceSprite {
42
+ layout: Layout | null;
43
+ }
41
44
 
42
45
  registerComponent("NineSliceSprite", CanvasNineSliceSprite);
43
46
 
@@ -1,14 +1,15 @@
1
1
  import * as particles from "@barvynkoa/particle-emitter";
2
- import { createComponent, registerComponent } from "../engine/reactive";
2
+ import { createComponent, Element, registerComponent } from "../engine/reactive";
3
3
  import { CanvasContainer } from "./Container";
4
4
  import { Signal } from "@signe/reactive";
5
+ import { ComponentInstance } from "./DisplayObject";
5
6
 
6
7
  class CanvasParticlesEmitter extends CanvasContainer {
7
8
  private emitter: particles.Emitter | null;
8
9
  private elapsed: number = Date.now();
9
10
 
10
- onMount(params) {
11
- super.onMount(params);
11
+ async onMount(params) {
12
+ await super.onMount(params);
12
13
  const { props } = params;
13
14
  const tick: Signal = props.context.tick;
14
15
  this.emitter = new particles.Emitter(this as any, props.config);
@@ -24,11 +25,14 @@ class CanvasParticlesEmitter extends CanvasContainer {
24
25
 
25
26
  onUpdate(props) {}
26
27
 
27
- onDestroy(): void {
28
- super.onDestroy();
29
- this.emitter?.destroy();
30
- this.emitter = null;
31
- this.subscriptionTick.unsubscribe();
28
+ async onDestroy(parent: Element<ComponentInstance>, afterDestroy: () => void) {
29
+ const _afterDestroy = async () => {
30
+ this.emitter?.destroy();
31
+ this.emitter = null;
32
+ this.subscriptionTick.unsubscribe();
33
+ afterDestroy();
34
+ }
35
+ await super.onDestroy(parent, _afterDestroy);
32
36
  }
33
37
  }
34
38