canvasengine 2.0.0-beta.15 → 2.0.0-beta.17

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.
@@ -1,6 +1,6 @@
1
1
  import { Text as PixiText, TextStyle } from "pixi.js";
2
- import { createComponent, registerComponent } from "../engine/reactive";
3
- import { DisplayObject } from "./DisplayObject";
2
+ import { createComponent, registerComponent, Element, Props } from "../engine/reactive";
3
+ import { DisplayObject, ComponentInstance } from "./DisplayObject";
4
4
  import { DisplayObjectProps } from "./types/DisplayObject";
5
5
  import { Signal } from "@signe/reactive";
6
6
  import { on } from "../engine/trigger";
@@ -9,7 +9,7 @@ enum TextEffect {
9
9
  Typewriter = "typewriter",
10
10
  }
11
11
 
12
- interface TextProps extends DisplayObjectProps {
12
+ export interface TextProps extends DisplayObjectProps {
13
13
  text?: string;
14
14
  style?: Partial<TextStyle>;
15
15
  color?: string;
@@ -21,6 +21,7 @@ interface TextProps extends DisplayObjectProps {
21
21
  onComplete?: () => void;
22
22
  skip?: () => void;
23
23
  };
24
+ context?: any; // Ensure context is available, ideally typed from a base prop or injected
24
25
  }
25
26
 
26
27
  class CanvasText extends DisplayObject(PixiText) {
@@ -32,9 +33,15 @@ class CanvasText extends DisplayObject(PixiText) {
32
33
  private typewriterOptions: any = {};
33
34
  private skipSignal?: () => void;
34
35
 
35
- onMount(args) {
36
- super.onMount(args);
37
- const { props } = args;
36
+ /**
37
+ * Called when the component is mounted to the scene graph.
38
+ * Initializes the typewriter effect if configured.
39
+ * @param {Element<CanvasText>} element - The element being mounted. Its `props` property (of type TextProps) contains component properties and context.
40
+ * @param {number} [index] - The index of the component among its siblings.
41
+ */
42
+ async onMount(element: Element<CanvasText>, index?: number): Promise<void> {
43
+ await super.onMount(element, index);
44
+ const { props } = element; // props here will be of type TextProps due to Element<CanvasText>
38
45
  const tick: Signal = props.context.tick;
39
46
 
40
47
  if (props.text && props.typewriter) {
@@ -130,13 +137,22 @@ class CanvasText extends DisplayObject(PixiText) {
130
137
  this.currentIndex = this.fullText.length;
131
138
  }
132
139
 
133
- onDestroy(): void {
134
- super.onDestroy();
135
- this.subscriptionTick.unsubscribe();
140
+ /**
141
+ * Called when the component is about to be destroyed.
142
+ * Unsubscribes from the tick observable.
143
+ * @param {Element<any>} parent - The parent element.
144
+ * @param {() => void} [afterDestroy] - An optional callback function to be executed after the component's own destruction logic.
145
+ */
146
+ async onDestroy(parent: Element<any>, afterDestroy?: () => void): Promise<void> {
147
+ const _afterDestroy = async () => {
148
+ this.subscriptionTick.unsubscribe();
149
+ afterDestroy();
150
+ }
151
+ await super.onDestroy(parent, _afterDestroy);
136
152
  }
137
153
  }
138
154
 
139
- interface CanvasText extends PixiText {}
155
+ // interface CanvasText extends PixiText {} // Removed as it's redundant and causes type conflicts
140
156
 
141
157
  registerComponent("Text", CanvasText);
142
158
 
@@ -1,8 +1,8 @@
1
1
  import { Viewport as PixiViewport } from 'pixi-viewport';
2
2
  import { Subscription } from 'rxjs';
3
- import { createComponent, registerComponent } from '../engine/reactive';
4
- import { DisplayObject } from './DisplayObject';
5
- import { effect } from '@signe/reactive';
3
+ import { createComponent, registerComponent, Element, Props } from '../engine/reactive';
4
+ import { DisplayObject, ComponentInstance } from './DisplayObject';
5
+ import { effect, Signal } from '@signe/reactive';
6
6
 
7
7
  const EVENTS = [
8
8
  'bounce-x-end',
@@ -28,6 +28,21 @@ const EVENTS = [
28
28
  'zoomed-end'
29
29
  ]
30
30
 
31
+ export interface ViewportProps extends Props {
32
+ screenWidth?: number;
33
+ screenHeight?: number;
34
+ worldWidth?: number;
35
+ worldHeight?: number;
36
+ clamp?: boolean | {
37
+ left?: number;
38
+ right?: number;
39
+ top?: number;
40
+ bottom?: number;
41
+ };
42
+ context?: any;
43
+ [key: string]: any;
44
+ }
45
+
31
46
  export class CanvasViewport extends DisplayObject(PixiViewport) {
32
47
  private tickSubscription: Subscription
33
48
  overrideProps = ['wheel']
@@ -52,9 +67,16 @@ export class CanvasViewport extends DisplayObject(PixiViewport) {
52
67
  }
53
68
  }
54
69
 
55
- onMount(element) {
56
- super.onMount(element)
57
- const { tick, renderer, canvasSize } = element.props.context
70
+ /**
71
+ * Called when the component is mounted to the scene graph.
72
+ * Initializes viewport settings and subscriptions.
73
+ * @param {Element<CanvasViewport>} element - The element being mounted. Its `props` property (of type ViewportProps) contains component properties and context.
74
+ * @param {number} [index] - The index of the component among its siblings.
75
+ */
76
+ async onMount(element: Element<CanvasViewport>, index?: number): Promise<void> {
77
+ await super.onMount(element, index);
78
+ const { props } = element;
79
+ const { tick, app, canvasSize } = props.context;
58
80
  let isDragging = false
59
81
 
60
82
  effect(() => {
@@ -62,19 +84,26 @@ export class CanvasViewport extends DisplayObject(PixiViewport) {
62
84
  this.screenHeight = canvasSize().height
63
85
  })
64
86
 
65
- renderer.events.domElement.addEventListener(
66
- 'wheel',
67
- this.input.wheelFunction
68
- );
87
+ effect(() => {
88
+ const _app = app()
89
+ if (!_app) return
90
+
91
+ const renderer = _app.renderer
92
+
93
+ renderer.events.domElement.addEventListener(
94
+ 'wheel',
95
+ this.input.wheelFunction
96
+ );
69
97
 
70
- this.options.events = renderer.events
98
+ this.options.events = renderer.events
99
+ })
71
100
 
72
101
  this.tickSubscription = tick.observable.subscribe(({ value }) => {
73
102
  this.update(value.timestamp)
74
103
  })
75
104
 
76
105
  element.props.context.viewport = this
77
- this.updateViewportSettings(element.props)
106
+ this.updateViewportSettings(props)
78
107
  }
79
108
 
80
109
  onUpdate(props) {
@@ -124,30 +153,23 @@ export class CanvasViewport extends DisplayObject(PixiViewport) {
124
153
  }
125
154
  }
126
155
 
127
- onDestroy(): void {
128
- super.onDestroy()
129
- this.tickSubscription.unsubscribe()
156
+ /**
157
+ * Called when the component is about to be destroyed.
158
+ * Unsubscribes from the tick observable.
159
+ * @param {Element<any>} parent - The parent element.
160
+ * @param {() => void} [afterDestroy] - An optional callback function to be executed after the component's own destruction logic.
161
+ */
162
+ async onDestroy(parent: Element<any>, afterDestroy?: () => void): Promise<void> {
163
+ const _afterDestroy = async () => {
164
+ this.tickSubscription.unsubscribe()
165
+ afterDestroy()
166
+ }
167
+ await super.onDestroy(parent, _afterDestroy);
130
168
  }
131
169
  }
132
170
 
133
- export interface CanvasViewport extends PixiViewport { }
134
-
135
171
  registerComponent('Viewport', CanvasViewport)
136
172
 
137
- export interface ViewportProps {
138
- screenWidth?: number;
139
- screenHeight?: number;
140
- worldWidth?: number;
141
- worldHeight?: number;
142
- clamp?: boolean | {
143
- left?: number;
144
- right?: number;
145
- top?: number;
146
- bottom?: number;
147
- };
148
- [key: string]: any;
149
- }
150
-
151
173
  export function Viewport(props: ViewportProps) {
152
174
  return createComponent('Viewport', props);
153
175
  }
@@ -8,6 +8,10 @@ export type JustifyContent = 'flex-start' | 'flex-end' | 'center' | 'space-betwe
8
8
  export type AlignContent = 'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around';
9
9
  export type Size = number | `${number}%`
10
10
  export type EdgeSize = SignalOrPrimitive<Size | [Size, Size] | [Size, Size, Size, Size]>
11
+ export type ObjectFit = 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';
12
+ export type ObjectPosition = string;
13
+ export type TransformOrigin = string;
14
+ export type PositionType = 'relative' | 'absolute' | 'static';
11
15
 
12
16
  export interface DisplayObjectProps {
13
17
  attach?: any;
@@ -16,6 +20,24 @@ export interface DisplayObjectProps {
16
20
  y?: SignalOrPrimitive<number>;
17
21
  width?: SignalOrPrimitive<Size>;
18
22
  height?: SignalOrPrimitive<Size>;
23
+ minWidth?: SignalOrPrimitive<Size>;
24
+ minHeight?: SignalOrPrimitive<Size>;
25
+ maxWidth?: SignalOrPrimitive<Size>;
26
+ maxHeight?: SignalOrPrimitive<Size>;
27
+ aspectRatio?: SignalOrPrimitive<number>;
28
+ flexGrow?: SignalOrPrimitive<number>;
29
+ flexShrink?: SignalOrPrimitive<number>;
30
+ flexBasis?: SignalOrPrimitive<Size>;
31
+ rowGap?: SignalOrPrimitive<number>;
32
+ columnGap?: SignalOrPrimitive<number>;
33
+ positionType?: PositionType;
34
+ top?: SignalOrPrimitive<Size>;
35
+ right?: SignalOrPrimitive<Size>;
36
+ bottom?: SignalOrPrimitive<Size>;
37
+ left?: SignalOrPrimitive<Size>;
38
+ objectFit?: ObjectFit;
39
+ objectPosition?: ObjectPosition;
40
+ transformOrigin?: TransformOrigin;
19
41
  children?: any[];
20
42
  flexDirection?: FlexDirection;
21
43
  justifyContent?: JustifyContent;
@@ -40,7 +40,7 @@ export class ViewportFollow extends Directive {
40
40
  radius: options.radius()
41
41
  })
42
42
  }
43
- } else {
43
+ } else if (viewportFollow === null) {
44
44
  viewport.plugins.remove('follow')
45
45
  }
46
46
  }
@@ -1,3 +1,5 @@
1
+ import '@pixi/layout';
2
+ import { Application } from "pixi.js";
1
3
  import { ComponentFunction, h } from "./signal";
2
4
 
3
5
  /**
@@ -9,11 +11,21 @@ import { ComponentFunction, h } from "./signal";
9
11
  * @throws {Error} If the provided element is not a Canvas component.
10
12
  */
11
13
  export const bootstrapCanvas = async (rootElement: HTMLElement | null, canvas: ComponentFunction<any>) => {
14
+
15
+ const app = new Application();
16
+ await app.init({
17
+ resizeTo: rootElement,
18
+ autoStart: false,
19
+ });
20
+
12
21
  const canvasElement = await h(canvas);
13
22
  if (canvasElement.tag != 'Canvas') {
14
23
  throw new Error('Canvas is required');
15
24
  }
16
- (canvasElement as any).render(rootElement);
25
+ (canvasElement as any).render(rootElement, app);
17
26
 
18
- return canvasElement;
27
+ return {
28
+ canvasElement,
29
+ app
30
+ };
19
31
  };
@@ -83,13 +83,14 @@ function destroyElement(element: Element | Element[]) {
83
83
  if (!element) {
84
84
  return;
85
85
  }
86
- element.propSubscriptions.forEach((sub) => sub.unsubscribe());
87
- element.effectSubscriptions.forEach((sub) => sub.unsubscribe());
88
- element.effectUnmounts.forEach((fn) => fn?.());
89
86
  for (let name in element.directives) {
90
87
  element.directives[name].onDestroy?.(element);
91
88
  }
92
- element.componentInstance.onDestroy?.(element.parent as any);
89
+ element.componentInstance.onDestroy(element.parent as any, () => {
90
+ element.propSubscriptions.forEach((sub) => sub.unsubscribe());
91
+ element.effectSubscriptions.forEach((sub) => sub.unsubscribe());
92
+ element.effectUnmounts.forEach((fn) => fn?.());
93
+ });
93
94
  }
94
95
 
95
96
  /**
package/src/index.ts CHANGED
@@ -1,3 +1,4 @@
1
+
1
2
  import './directives'
2
3
  export * from '@signe/reactive'
3
4
  export { Howler } from 'howler'
@@ -0,0 +1,7 @@
1
+ export function isPercent(value?: string | number) {
2
+ if (!value) return false
3
+ if (typeof value === "string") {
4
+ return value.endsWith("%")
5
+ }
6
+ return false
7
+ }
package/testing/index.ts CHANGED
@@ -5,7 +5,8 @@ export class TestBed {
5
5
  const comp = () => h(Canvas, {
6
6
  tickStart: false
7
7
  }, h(component, props, children))
8
- const canvas = await bootstrapCanvas(document.getElementById('root'), comp)
9
- return canvas.props.children?.[0]
8
+ const { canvasElement, app } = await bootstrapCanvas(document.getElementById('root'), comp)
9
+ app.render()
10
+ return canvasElement.props.children?.[0]
10
11
  }
11
12
  }