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.
- package/dist/DebugRenderer-DrlzuIVv.js +172 -0
- package/dist/DebugRenderer-DrlzuIVv.js.map +1 -0
- package/dist/components/Button.d.ts +136 -0
- package/dist/components/Button.d.ts.map +1 -0
- package/dist/components/Canvas.d.ts +18 -0
- package/dist/components/Canvas.d.ts.map +1 -0
- package/dist/components/Container.d.ts +80 -0
- package/dist/components/Container.d.ts.map +1 -0
- package/dist/components/DOMContainer.d.ts +77 -0
- package/dist/components/DOMContainer.d.ts.map +1 -0
- package/dist/components/DOMElement.d.ts +44 -0
- package/dist/components/DOMElement.d.ts.map +1 -0
- package/dist/components/DisplayObject.d.ts +82 -0
- package/dist/components/DisplayObject.d.ts.map +1 -0
- package/dist/components/Graphic.d.ts +65 -0
- package/dist/components/Graphic.d.ts.map +1 -0
- package/dist/components/Mesh.d.ts +202 -0
- package/dist/components/Mesh.d.ts.map +1 -0
- package/dist/components/NineSliceSprite.d.ts +17 -0
- package/dist/components/NineSliceSprite.d.ts.map +1 -0
- package/dist/components/ParticleEmitter.d.ts +5 -0
- package/dist/components/ParticleEmitter.d.ts.map +1 -0
- package/dist/components/Scene.d.ts +2 -0
- package/dist/components/Scene.d.ts.map +1 -0
- package/dist/components/Sprite.d.ts +174 -0
- package/dist/components/Sprite.d.ts.map +1 -0
- package/dist/components/Text.d.ts +21 -0
- package/dist/components/Text.d.ts.map +1 -0
- package/dist/components/TilingSprite.d.ts +18 -0
- package/dist/components/TilingSprite.d.ts.map +1 -0
- package/dist/components/Video.d.ts +15 -0
- package/dist/components/Video.d.ts.map +1 -0
- package/dist/components/Viewport.d.ts +106 -0
- package/dist/components/Viewport.d.ts.map +1 -0
- package/dist/components/index.d.ts +17 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/types/DisplayObject.d.ts +106 -0
- package/dist/components/types/DisplayObject.d.ts.map +1 -0
- package/dist/components/types/MouseEvent.d.ts +4 -0
- package/dist/components/types/MouseEvent.d.ts.map +1 -0
- package/dist/components/types/Spritesheet.d.ts +366 -0
- package/dist/components/types/Spritesheet.d.ts.map +1 -0
- package/dist/components/types/index.d.ts +5 -0
- package/dist/components/types/index.d.ts.map +1 -0
- package/dist/directives/Drag.d.ts +70 -0
- package/dist/directives/Drag.d.ts.map +1 -0
- package/dist/directives/KeyboardControls.d.ts +530 -0
- package/dist/directives/KeyboardControls.d.ts.map +1 -0
- package/dist/directives/Scheduler.d.ts +36 -0
- package/dist/directives/Scheduler.d.ts.map +1 -0
- package/dist/directives/Sound.d.ts +26 -0
- package/dist/directives/Sound.d.ts.map +1 -0
- package/dist/directives/Transition.d.ts +11 -0
- package/dist/directives/Transition.d.ts.map +1 -0
- package/dist/directives/ViewportCull.d.ts +12 -0
- package/dist/directives/ViewportCull.d.ts.map +1 -0
- package/dist/directives/ViewportFollow.d.ts +19 -0
- package/dist/directives/ViewportFollow.d.ts.map +1 -0
- package/dist/directives/index.d.ts +2 -0
- package/dist/directives/index.d.ts.map +1 -0
- package/dist/engine/animation.d.ts +59 -0
- package/dist/engine/animation.d.ts.map +1 -0
- package/dist/engine/bootstrap.d.ts +16 -0
- package/dist/engine/bootstrap.d.ts.map +1 -0
- package/dist/engine/directive.d.ts +14 -0
- package/dist/engine/directive.d.ts.map +1 -0
- package/dist/engine/reactive.d.ts +95 -0
- package/dist/engine/reactive.d.ts.map +1 -0
- package/dist/engine/signal.d.ts +72 -0
- package/dist/engine/signal.d.ts.map +1 -0
- package/dist/engine/trigger.d.ts +51 -0
- package/dist/engine/trigger.d.ts.map +1 -0
- package/dist/engine/utils.d.ts +90 -0
- package/dist/engine/utils.d.ts.map +1 -0
- package/dist/hooks/addContext.d.ts +2 -0
- package/dist/hooks/addContext.d.ts.map +1 -0
- package/dist/hooks/useProps.d.ts +42 -0
- package/dist/hooks/useProps.d.ts.map +1 -0
- package/dist/hooks/useRef.d.ts +5 -0
- package/dist/hooks/useRef.d.ts.map +1 -0
- package/dist/index-DNDNQN-q.js +11088 -0
- package/dist/index-DNDNQN-q.js.map +1 -0
- package/dist/index.d.ts +15 -919
- package/dist/index.d.ts.map +1 -0
- package/dist/index.global.js +29 -0
- package/dist/index.global.js.map +1 -0
- package/dist/index.js +63 -2950
- package/dist/index.js.map +1 -1
- package/dist/utils/Ease.d.ts +17 -0
- package/dist/utils/Ease.d.ts.map +1 -0
- package/dist/utils/RadialGradient.d.ts +58 -0
- package/dist/utils/RadialGradient.d.ts.map +1 -0
- package/dist/utils/functions.d.ts +2 -0
- package/dist/utils/functions.d.ts.map +1 -0
- package/index.d.ts +4 -0
- package/package.json +12 -7
- package/src/components/Button.ts +269 -0
- package/src/components/Canvas.ts +53 -45
- package/src/components/Container.ts +2 -2
- package/src/components/DOMContainer.ts +123 -0
- package/src/components/DOMElement.ts +421 -0
- package/src/components/DisplayObject.ts +283 -190
- package/src/components/Graphic.ts +200 -34
- package/src/components/Mesh.ts +222 -0
- package/src/components/NineSliceSprite.ts +4 -1
- package/src/components/ParticleEmitter.ts +12 -8
- package/src/components/Sprite.ts +92 -22
- package/src/components/Text.ts +34 -14
- package/src/components/Video.ts +110 -0
- package/src/components/Viewport.ts +59 -43
- package/src/components/index.ts +7 -2
- package/src/components/types/DisplayObject.ts +30 -0
- package/src/directives/Drag.ts +357 -52
- package/src/directives/KeyboardControls.ts +3 -1
- package/src/directives/Sound.ts +94 -31
- package/src/directives/ViewportFollow.ts +35 -7
- package/src/engine/animation.ts +41 -5
- package/src/engine/bootstrap.ts +23 -3
- package/src/engine/directive.ts +2 -2
- package/src/engine/reactive.ts +542 -170
- package/src/engine/signal.ts +22 -2
- package/src/engine/trigger.ts +65 -9
- package/src/engine/utils.ts +97 -9
- package/src/hooks/useProps.ts +1 -1
- package/src/index.ts +4 -1
- package/src/utils/RadialGradient.ts +29 -0
- package/src/utils/functions.ts +7 -0
- package/testing/index.ts +12 -0
- package/tsconfig.json +17 -0
- package/vite.config.ts +39 -0
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
import { DOMContainer as PixiDOMContainer } from "pixi.js";
|
|
2
|
+
import {
|
|
3
|
+
createComponent,
|
|
4
|
+
Element,
|
|
5
|
+
registerComponent,
|
|
6
|
+
} from "../engine/reactive";
|
|
7
|
+
import { ComponentInstance, DisplayObject, OnHook } from "./DisplayObject";
|
|
8
|
+
import { ComponentFunction } from "../engine/signal";
|
|
9
|
+
import { DisplayObjectProps } from "./types/DisplayObject";
|
|
10
|
+
import { isObservable } from "../engine/utils";
|
|
11
|
+
import { isSignal } from "@signe/reactive";
|
|
12
|
+
|
|
13
|
+
interface DOMContainerProps extends DisplayObjectProps {
|
|
14
|
+
element:
|
|
15
|
+
| string
|
|
16
|
+
| {
|
|
17
|
+
value: HTMLElement;
|
|
18
|
+
};
|
|
19
|
+
textContent?: string;
|
|
20
|
+
attrs?: Record<string, any> & {
|
|
21
|
+
class?:
|
|
22
|
+
| string
|
|
23
|
+
| string[]
|
|
24
|
+
| Record<string, boolean>
|
|
25
|
+
| { items?: string[] }
|
|
26
|
+
| { value?: string | string[] | Record<string, boolean> };
|
|
27
|
+
style?:
|
|
28
|
+
| string
|
|
29
|
+
| Record<string, string | number>
|
|
30
|
+
| { value?: string | Record<string, string | number> };
|
|
31
|
+
};
|
|
32
|
+
onBeforeDestroy?: OnHook;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* DOMContainer class for managing DOM elements within the canvas engine
|
|
37
|
+
*
|
|
38
|
+
* This class extends the DisplayObject functionality to handle DOM elements using
|
|
39
|
+
* PixiJS's native DOMContainer. It provides a bridge between the canvas rendering
|
|
40
|
+
* system and traditional DOM manipulation with proper transform hierarchy and visibility.
|
|
41
|
+
*
|
|
42
|
+
* The DOMContainer is especially useful for rendering standard DOM elements that handle
|
|
43
|
+
* user input, such as `<input>` or `<textarea>`. This is often simpler and more flexible
|
|
44
|
+
* than trying to implement text input directly in PixiJS.
|
|
45
|
+
*
|
|
46
|
+
* ## Form Elements with Reactive Signals
|
|
47
|
+
*
|
|
48
|
+
* For form elements (`input`, `textarea`, `select`), the component supports reactive
|
|
49
|
+
* two-way data binding using signals. When the `value` attribute is a signal, the
|
|
50
|
+
* component automatically:
|
|
51
|
+
* - Sets the initial value from the signal
|
|
52
|
+
* - Listens for `input` events and updates the signal with the new value
|
|
53
|
+
* - Updates the DOM element when the signal value changes programmatically
|
|
54
|
+
*
|
|
55
|
+
* ## Form Submission Handling
|
|
56
|
+
*
|
|
57
|
+
* When a `form` element has a `submit` event handler, the component automatically:
|
|
58
|
+
* - Prevents the default form submission behavior (stops propagation)
|
|
59
|
+
* - Collects all form data from input elements within the form
|
|
60
|
+
* - Passes both the event and the collected form data as parameters to the submit handler
|
|
61
|
+
* - Handles multiple values for the same field name (e.g., checkboxes with same name)
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```typescript
|
|
65
|
+
* import { signal } from '@signe/reactive';
|
|
66
|
+
*
|
|
67
|
+
* // Basic usage with input element
|
|
68
|
+
* const element = document.createElement('input');
|
|
69
|
+
* element.type = 'text';
|
|
70
|
+
* element.placeholder = 'Enter text...';
|
|
71
|
+
*
|
|
72
|
+
* const domContainer = new DOMContainer({
|
|
73
|
+
* element,
|
|
74
|
+
* x: 100,
|
|
75
|
+
* y: 50,
|
|
76
|
+
* anchor: { x: 0.5, y: 0.5 }
|
|
77
|
+
* });
|
|
78
|
+
*
|
|
79
|
+
* // Reactive form input with signal
|
|
80
|
+
* const inputValue = signal('');
|
|
81
|
+
*
|
|
82
|
+
* const reactiveInput = new DOMContainer({
|
|
83
|
+
* element: 'input',
|
|
84
|
+
* attrs: {
|
|
85
|
+
* type: 'text',
|
|
86
|
+
* placeholder: 'Type something...',
|
|
87
|
+
* value: inputValue // Signal for two-way binding
|
|
88
|
+
* }
|
|
89
|
+
* });
|
|
90
|
+
*
|
|
91
|
+
* // The signal will automatically update when user types
|
|
92
|
+
* inputValue.subscribe(value => {
|
|
93
|
+
* console.log('Input value changed:', value);
|
|
94
|
+
* });
|
|
95
|
+
*
|
|
96
|
+
* // You can also update the input programmatically
|
|
97
|
+
* inputValue.set('New value');
|
|
98
|
+
*
|
|
99
|
+
* // Form submission with automatic data collection
|
|
100
|
+
* const loginForm = new DOMContainer({
|
|
101
|
+
* element: 'form',
|
|
102
|
+
* attrs: {
|
|
103
|
+
* submit: (event, formData) => {
|
|
104
|
+
* // event: the submit event (already prevented)
|
|
105
|
+
* // formData: object containing all form field values
|
|
106
|
+
* console.log('Form submitted with data:', formData);
|
|
107
|
+
* // Example formData: { username: 'john', password: 'secret', remember: 'on' }
|
|
108
|
+
* }
|
|
109
|
+
* },
|
|
110
|
+
* children: [
|
|
111
|
+
* new DOMContainer({
|
|
112
|
+
* element: 'input',
|
|
113
|
+
* attrs: { name: 'username', type: 'text', placeholder: 'Username' }
|
|
114
|
+
* }),
|
|
115
|
+
* new DOMContainer({
|
|
116
|
+
* element: 'input',
|
|
117
|
+
* attrs: { name: 'password', type: 'password', placeholder: 'Password' }
|
|
118
|
+
* }),
|
|
119
|
+
* new DOMContainer({
|
|
120
|
+
* element: 'input',
|
|
121
|
+
* attrs: { name: 'remember', type: 'checkbox', value: 'on' }
|
|
122
|
+
* }),
|
|
123
|
+
* new DOMContainer({
|
|
124
|
+
* element: 'button',
|
|
125
|
+
* attrs: { type: 'submit' },
|
|
126
|
+
* textContent: 'Login'
|
|
127
|
+
* })
|
|
128
|
+
* ]
|
|
129
|
+
* });
|
|
130
|
+
*
|
|
131
|
+
* // Using different class and style formats
|
|
132
|
+
* const containerWithClasses = new DOMContainer({
|
|
133
|
+
* element: 'div',
|
|
134
|
+
* attrs: {
|
|
135
|
+
* // String format: space-separated classes
|
|
136
|
+
* class: 'container primary-theme',
|
|
137
|
+
*
|
|
138
|
+
* // Array format: array of class names
|
|
139
|
+
* // class: ['container', 'primary-theme'],
|
|
140
|
+
*
|
|
141
|
+
* // Object format: conditional classes
|
|
142
|
+
* // class: {
|
|
143
|
+
* // 'container': true,
|
|
144
|
+
* // 'primary-theme': true,
|
|
145
|
+
* // 'disabled': false
|
|
146
|
+
* // }
|
|
147
|
+
*
|
|
148
|
+
* // String format: CSS style string
|
|
149
|
+
* style: 'background-color: red; padding: 10px;',
|
|
150
|
+
*
|
|
151
|
+
* // Object format: style properties
|
|
152
|
+
* // style: {
|
|
153
|
+
* // backgroundColor: 'red',
|
|
154
|
+
* // padding: '10px',
|
|
155
|
+
* // fontSize: 16
|
|
156
|
+
* // }
|
|
157
|
+
* }
|
|
158
|
+
* });
|
|
159
|
+
* ```
|
|
160
|
+
*/
|
|
161
|
+
const EVENTS = [
|
|
162
|
+
"click",
|
|
163
|
+
"mouseover",
|
|
164
|
+
"mouseout",
|
|
165
|
+
"mouseenter",
|
|
166
|
+
"mouseleave",
|
|
167
|
+
"mousemove",
|
|
168
|
+
"mouseup",
|
|
169
|
+
"mousedown",
|
|
170
|
+
"touchstart",
|
|
171
|
+
"touchend",
|
|
172
|
+
"touchmove",
|
|
173
|
+
"touchcancel",
|
|
174
|
+
"wheel",
|
|
175
|
+
"scroll",
|
|
176
|
+
"resize",
|
|
177
|
+
"focus",
|
|
178
|
+
"blur",
|
|
179
|
+
"change",
|
|
180
|
+
"input",
|
|
181
|
+
"submit",
|
|
182
|
+
"reset",
|
|
183
|
+
"keydown",
|
|
184
|
+
"keyup",
|
|
185
|
+
"keypress",
|
|
186
|
+
"contextmenu",
|
|
187
|
+
"drag",
|
|
188
|
+
"dragend",
|
|
189
|
+
"dragenter",
|
|
190
|
+
"dragleave",
|
|
191
|
+
"dragover",
|
|
192
|
+
"drop",
|
|
193
|
+
"dragstart",
|
|
194
|
+
"select",
|
|
195
|
+
"selectstart",
|
|
196
|
+
"selectend",
|
|
197
|
+
"selectall",
|
|
198
|
+
"selectnone",
|
|
199
|
+
];
|
|
200
|
+
|
|
201
|
+
export class CanvasDOMElement {
|
|
202
|
+
public element: HTMLElement;
|
|
203
|
+
private eventListeners: Map<string, (e: Event) => void> = new Map();
|
|
204
|
+
private onBeforeDestroy: OnHook | null = null;
|
|
205
|
+
private valueSignal: any = null;
|
|
206
|
+
private isFormElementType: boolean = false;
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Checks if the element is a form element that supports the value attribute
|
|
210
|
+
* @param elementType - The element type string from props
|
|
211
|
+
* @returns true if the element is a form element with value support
|
|
212
|
+
*/
|
|
213
|
+
private isFormElement(elementType: string): boolean {
|
|
214
|
+
const formElements = ["input", "textarea", "select"];
|
|
215
|
+
return formElements.includes(elementType.toLowerCase());
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
onInit(props: DOMContainerProps) {
|
|
219
|
+
if (typeof props.element === "string") {
|
|
220
|
+
this.element = document.createElement(props.element);
|
|
221
|
+
this.isFormElementType = this.isFormElement(props.element);
|
|
222
|
+
} else {
|
|
223
|
+
this.element = props.element.value;
|
|
224
|
+
this.isFormElementType = this.isFormElement(this.element.tagName);
|
|
225
|
+
}
|
|
226
|
+
if (props.onBeforeDestroy || props["on-before-destroy"]) {
|
|
227
|
+
this.onBeforeDestroy =
|
|
228
|
+
props.onBeforeDestroy || props["on-before-destroy"];
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
for (const event of EVENTS) {
|
|
232
|
+
if (props.attrs?.[event]) {
|
|
233
|
+
const eventHandler = (e: Event) => {
|
|
234
|
+
// Special handling for form submit events
|
|
235
|
+
if (event === "submit" && this.element.tagName.toLowerCase() === "form") {
|
|
236
|
+
e.preventDefault(); // Stop form submission propagation
|
|
237
|
+
|
|
238
|
+
// Collect all form data
|
|
239
|
+
const formData = new FormData(this.element as HTMLFormElement);
|
|
240
|
+
const formObject: Record<string, any> = {};
|
|
241
|
+
|
|
242
|
+
// Convert FormData to plain object
|
|
243
|
+
formData.forEach((value, key) => {
|
|
244
|
+
if (formObject[key]) {
|
|
245
|
+
// Handle multiple values for same key (like checkboxes)
|
|
246
|
+
if (Array.isArray(formObject[key])) {
|
|
247
|
+
formObject[key].push(value);
|
|
248
|
+
} else {
|
|
249
|
+
formObject[key] = [formObject[key], value];
|
|
250
|
+
}
|
|
251
|
+
} else {
|
|
252
|
+
formObject[key] = value;
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
// Call the event handler with event and form data
|
|
257
|
+
props.attrs[event]?.(e, formObject);
|
|
258
|
+
} else {
|
|
259
|
+
props.attrs[event]?.(e);
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
this.eventListeners.set(event, eventHandler);
|
|
263
|
+
this.element.addEventListener(event, eventHandler, false);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
if (props.children) {
|
|
267
|
+
for (const child of props.children) {
|
|
268
|
+
if (isObservable(child)) {
|
|
269
|
+
child.subscribe(({ elements }) => {
|
|
270
|
+
for (const element of elements) {
|
|
271
|
+
this.element.appendChild(element.componentInstance.element);
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
} else {
|
|
275
|
+
this.element.appendChild(child.componentInstance.element);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
this.onUpdate(props);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
onMount(context: Element<CanvasDOMElement>) {
|
|
283
|
+
const props = context.propObservables;
|
|
284
|
+
const attrs = props.attrs as any;
|
|
285
|
+
// Handle form elements with signal value
|
|
286
|
+
if (
|
|
287
|
+
this.isFormElementType &&
|
|
288
|
+
attrs?.value &&
|
|
289
|
+
isSignal(attrs.value)
|
|
290
|
+
) {
|
|
291
|
+
this.valueSignal = attrs.value;
|
|
292
|
+
// Set initial value from signal
|
|
293
|
+
(
|
|
294
|
+
this.element as
|
|
295
|
+
| HTMLInputElement
|
|
296
|
+
| HTMLTextAreaElement
|
|
297
|
+
| HTMLSelectElement
|
|
298
|
+
).value = this.valueSignal();
|
|
299
|
+
|
|
300
|
+
// Listen for input events and update the signal
|
|
301
|
+
const inputHandler = (e: Event) => {
|
|
302
|
+
const target = e.target as
|
|
303
|
+
| HTMLInputElement
|
|
304
|
+
| HTMLTextAreaElement
|
|
305
|
+
| HTMLSelectElement;
|
|
306
|
+
this.valueSignal.set(target.value);
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
this.eventListeners.set("input", inputHandler);
|
|
310
|
+
this.element.addEventListener("input", inputHandler, false);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
onUpdate(props: DOMContainerProps) {
|
|
315
|
+
if (!this.element) return;
|
|
316
|
+
for (const [key, value] of Object.entries(props.attrs || {})) {
|
|
317
|
+
if (key === "class") {
|
|
318
|
+
const classList = value.items || value.value || value;
|
|
319
|
+
|
|
320
|
+
// Clear existing classes first
|
|
321
|
+
this.element.className = "";
|
|
322
|
+
|
|
323
|
+
if (typeof classList === "string") {
|
|
324
|
+
// String: space-separated class names
|
|
325
|
+
this.element.className = classList;
|
|
326
|
+
} else if (Array.isArray(classList)) {
|
|
327
|
+
// Array: array of class names
|
|
328
|
+
this.element.classList.add(...classList);
|
|
329
|
+
} else if (typeof classList === "object" && classList !== null) {
|
|
330
|
+
// Object: { className: boolean }
|
|
331
|
+
for (const [className, shouldAdd] of Object.entries(classList)) {
|
|
332
|
+
if (shouldAdd) {
|
|
333
|
+
this.element.classList.add(className);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
} else if (key === "style") {
|
|
338
|
+
const styleValue = value.items || value.value || value;
|
|
339
|
+
|
|
340
|
+
if (typeof styleValue === "string") {
|
|
341
|
+
// String: CSS style string
|
|
342
|
+
this.element.setAttribute("style", styleValue);
|
|
343
|
+
} else if (typeof styleValue === "object" && styleValue !== null) {
|
|
344
|
+
// Object: { property: value }
|
|
345
|
+
for (const [styleProp, styleVal] of Object.entries(styleValue)) {
|
|
346
|
+
if (styleVal !== null && styleVal !== undefined) {
|
|
347
|
+
(this.element.style as any)[styleProp] = styleVal;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
} else if (key === "value" && this.isFormElementType) {
|
|
352
|
+
// Handle value attribute for form elements
|
|
353
|
+
if (isSignal(value)) {
|
|
354
|
+
// If it's a signal, the value is already handled in onInit
|
|
355
|
+
// Update the DOM element value if the signal value changed
|
|
356
|
+
const currentValue = (
|
|
357
|
+
this.element as
|
|
358
|
+
| HTMLInputElement
|
|
359
|
+
| HTMLTextAreaElement
|
|
360
|
+
| HTMLSelectElement
|
|
361
|
+
).value;
|
|
362
|
+
const signalValue = value();
|
|
363
|
+
if (currentValue !== signalValue) {
|
|
364
|
+
(
|
|
365
|
+
this.element as
|
|
366
|
+
| HTMLInputElement
|
|
367
|
+
| HTMLTextAreaElement
|
|
368
|
+
| HTMLSelectElement
|
|
369
|
+
).value = signalValue;
|
|
370
|
+
}
|
|
371
|
+
} else {
|
|
372
|
+
// If it's not a signal, set the value directly
|
|
373
|
+
(
|
|
374
|
+
this.element as
|
|
375
|
+
| HTMLInputElement
|
|
376
|
+
| HTMLTextAreaElement
|
|
377
|
+
| HTMLSelectElement
|
|
378
|
+
).value = value;
|
|
379
|
+
}
|
|
380
|
+
} else if (!EVENTS.includes(key)) {
|
|
381
|
+
this.element.setAttribute(key, value);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
if (props.textContent) {
|
|
385
|
+
this.element.textContent = props.textContent;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
async onDestroy(
|
|
390
|
+
parent: Element<CanvasDOMElement>,
|
|
391
|
+
afterDestroy: () => void
|
|
392
|
+
): Promise<void> {
|
|
393
|
+
// Remove all event listeners from the DOM element
|
|
394
|
+
|
|
395
|
+
if (this.element) {
|
|
396
|
+
if (this.onBeforeDestroy) {
|
|
397
|
+
await this.onBeforeDestroy();
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
for (const [event, handler] of this.eventListeners) {
|
|
401
|
+
this.element.removeEventListener(event, handler, false);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
this.eventListeners.clear();
|
|
405
|
+
|
|
406
|
+
this.element.remove();
|
|
407
|
+
|
|
408
|
+
if (afterDestroy) {
|
|
409
|
+
afterDestroy();
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
export interface CanvasDOMElement extends DisplayObjectProps {}
|
|
416
|
+
|
|
417
|
+
registerComponent("DOMElement", CanvasDOMElement);
|
|
418
|
+
|
|
419
|
+
export const DOMElement: ComponentFunction<DOMContainerProps> = (props) => {
|
|
420
|
+
return createComponent("DOMElement", props);
|
|
421
|
+
};
|