@playcanvas/web-components 0.3.0 → 0.5.0

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 (40) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +84 -84
  3. package/dist/components/light-component.d.ts +48 -0
  4. package/dist/components/splat-component.d.ts +0 -13
  5. package/dist/pwc.cjs +275 -207
  6. package/dist/pwc.cjs.map +1 -1
  7. package/dist/pwc.js +275 -207
  8. package/dist/pwc.js.map +1 -1
  9. package/dist/pwc.min.js +1 -1
  10. package/dist/pwc.min.js.map +1 -1
  11. package/dist/pwc.mjs +276 -208
  12. package/dist/pwc.mjs.map +1 -1
  13. package/package.json +76 -66
  14. package/src/app.ts +612 -606
  15. package/src/asset.ts +159 -159
  16. package/src/async-element.ts +46 -46
  17. package/src/colors.ts +150 -150
  18. package/src/components/camera-component.ts +557 -557
  19. package/src/components/collision-component.ts +183 -183
  20. package/src/components/component.ts +97 -97
  21. package/src/components/element-component.ts +367 -367
  22. package/src/components/light-component.ts +570 -466
  23. package/src/components/listener-component.ts +30 -30
  24. package/src/components/particlesystem-component.ts +155 -155
  25. package/src/components/render-component.ts +147 -147
  26. package/src/components/rigidbody-component.ts +227 -227
  27. package/src/components/screen-component.ts +157 -157
  28. package/src/components/script-component.ts +270 -270
  29. package/src/components/script.ts +90 -90
  30. package/src/components/sound-component.ts +230 -230
  31. package/src/components/sound-slot.ts +288 -288
  32. package/src/components/splat-component.ts +102 -133
  33. package/src/entity.ts +360 -360
  34. package/src/index.ts +63 -63
  35. package/src/material.ts +141 -141
  36. package/src/model.ts +111 -111
  37. package/src/module.ts +43 -43
  38. package/src/scene.ts +217 -217
  39. package/src/sky.ts +293 -293
  40. package/src/utils.ts +71 -71
@@ -1,270 +1,270 @@
1
- import { Color, ScriptComponent, Script, Vec2, Vec3, Vec4 } from 'playcanvas';
2
-
3
- import { AssetElement } from '../asset';
4
- import { ComponentElement } from './component';
5
- import { EntityElement } from '../entity';
6
- import { ScriptElement } from './script';
7
-
8
- // Add these interfaces at the top of the file, after the imports
9
- interface ScriptAttributesChangeEvent extends CustomEvent {
10
- detail: { attributes: any };
11
- }
12
-
13
- interface ScriptEnableChangeEvent extends CustomEvent {
14
- detail: { enabled: boolean };
15
- }
16
-
17
- // Add this interface before the ScriptComponentElement class
18
- declare global {
19
- interface HTMLElementEventMap {
20
- 'scriptattributeschange': ScriptAttributesChangeEvent;
21
- 'scriptenablechange': ScriptEnableChangeEvent;
22
- }
23
- }
24
-
25
- /**
26
- * The ScriptComponentElement interface provides properties and methods for manipulating
27
- * {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-scripts/ | `<pc-scripts>`} elements.
28
- * The ScriptComponentElement interface also inherits the properties and methods of the
29
- * {@link HTMLElement} interface.
30
- *
31
- * @category Components
32
- */
33
- class ScriptComponentElement extends ComponentElement {
34
- private observer: MutationObserver;
35
-
36
- /** @ignore */
37
- constructor() {
38
- super('script');
39
-
40
- // Create mutation observer to watch for child script elements
41
- this.observer = new MutationObserver(this.handleMutations.bind(this));
42
- this.observer.observe(this, {
43
- childList: true
44
- });
45
-
46
- // Listen for script attribute and enable changes
47
- this.addEventListener('scriptattributeschange', this.handleScriptAttributesChange.bind(this));
48
- this.addEventListener('scriptenablechange', this.handleScriptEnableChange.bind(this));
49
- }
50
-
51
- initComponent() {
52
- // Handle initial script elements
53
- this.querySelectorAll<ScriptElement>(':scope > pc-script').forEach((scriptElement) => {
54
- const scriptName = scriptElement.getAttribute('name');
55
- const attributes = scriptElement.getAttribute('attributes');
56
- if (scriptName) {
57
- this.createScript(scriptName, attributes);
58
- }
59
- });
60
- }
61
-
62
- /**
63
- * Recursively converts raw attribute data into proper PlayCanvas types. Supported conversions:
64
- * - "asset:assetId" → resolves to an Asset instance
65
- * - "entity:entityId" → resolves to an Entity instance
66
- * - "vec2:1,2" → new Vec2(1,2)
67
- * - "vec3:1,2,3" → new Vec3(1,2,3)
68
- * - "vec4:1,2,3,4" → new Vec4(1,2,3,4)
69
- * - "color:1,0.5,0.5,1" → new Color(1,0.5,0.5,1)
70
- * @param item - The item to convert.
71
- * @returns The converted item.
72
- */
73
- private convertAttributes(item: any): any {
74
- if (typeof item === 'string') {
75
- if (item.startsWith('asset:')) {
76
- const assetId = item.slice(6);
77
- const assetElement = document.querySelector(`pc-asset#${assetId}`) as AssetElement;
78
- if (assetElement) {
79
- return assetElement.asset;
80
- }
81
- }
82
- if (item.startsWith('entity:')) {
83
- const entityId = item.slice(7);
84
- const entityElement = document.querySelector(`pc-entity[name="${entityId}"]`) as EntityElement;
85
- if (entityElement) {
86
- return entityElement.entity;
87
- }
88
- }
89
- if (item.startsWith('vec2:')) {
90
- const parts = item.slice(5).split(',').map(Number);
91
- if (parts.length === 2 && parts.every(v => !isNaN(v))) {
92
- return new Vec2(parts[0], parts[1]);
93
- }
94
- }
95
- if (item.startsWith('vec3:')) {
96
- const parts = item.slice(5).split(',').map(Number);
97
- if (parts.length === 3 && parts.every(v => !isNaN(v))) {
98
- return new Vec3(parts[0], parts[1], parts[2]);
99
- }
100
- }
101
- if (item.startsWith('vec4:')) {
102
- const parts = item.slice(5).split(',').map(Number);
103
- if (parts.length === 4 && parts.every(v => !isNaN(v))) {
104
- return new Vec4(parts[0], parts[1], parts[2], parts[3]);
105
- }
106
- }
107
- if (item.startsWith('color:')) {
108
- const parts = item.slice(6).split(',').map(Number);
109
- if (parts.length === 4 && parts.every(v => !isNaN(v))) {
110
- return new Color(parts[0], parts[1], parts[2], parts[3]);
111
- }
112
- }
113
- return item;
114
- }
115
-
116
- if (Array.isArray(item)) {
117
- // If it's an array of objects, convert each element individually.
118
- if (item.length > 0 && typeof item[0] === 'object') {
119
- return item.map((el: any) => this.convertAttributes(el));
120
- }
121
- // Otherwise, leave the numeric array unchanged but process each element.
122
- return item.map((el: any) => this.convertAttributes(el));
123
- }
124
-
125
- if (item && typeof item === 'object') {
126
- const result: any = {};
127
- for (const key in item) {
128
- result[key] = this.convertAttributes(item[key]);
129
- }
130
- return result;
131
- }
132
-
133
- return item;
134
- }
135
-
136
- /**
137
- * Preprocess the attributes object by converting its values.
138
- * @param attrs - The attributes object to preprocess.
139
- * @returns The preprocessed attributes object.
140
- */
141
- private preprocessAttributes(attrs: any): any {
142
- return this.convertAttributes(attrs);
143
- }
144
-
145
- /**
146
- * Recursively merge properties from source into target.
147
- * @param target - The target object to merge into.
148
- * @param source - The source object to merge from.
149
- * @returns The merged object.
150
- */
151
- private mergeDeep(target: any, source: any): any {
152
- for (const key in source) {
153
- if (
154
- source[key] &&
155
- typeof source[key] === 'object' &&
156
- !Array.isArray(source[key])
157
- ) {
158
- if (!target[key] || typeof target[key] !== 'object') {
159
- target[key] = {};
160
- }
161
- this.mergeDeep(target[key], source[key]);
162
- } else {
163
- target[key] = source[key];
164
- }
165
- }
166
- return target;
167
- }
168
-
169
- /**
170
- * Update script attributes by merging preprocessed values into the script.
171
- * @param script - The script to update.
172
- * @param attributes - The attributes to merge into the script.
173
- */
174
- private applyAttributes(script: any, attributes: string | null) {
175
- try {
176
- const attributesObject = attributes ? JSON.parse(attributes) : {};
177
- const converted = this.convertAttributes(attributesObject);
178
- this.mergeDeep(script, converted);
179
- } catch (error) {
180
- console.error(`Error parsing attributes JSON string ${attributes}:`, error);
181
- }
182
- }
183
-
184
- private handleScriptAttributesChange(event: ScriptAttributesChangeEvent) {
185
- const scriptElement = event.target as ScriptElement;
186
- const scriptName = scriptElement.getAttribute('name');
187
- if (!scriptName || !this.component) return;
188
-
189
- const script = this.component.get(scriptName);
190
- if (script) {
191
- this.applyAttributes(script, event.detail.attributes);
192
- }
193
- }
194
-
195
- private handleScriptEnableChange(event: ScriptEnableChangeEvent) {
196
- const scriptElement = event.target as ScriptElement;
197
- const scriptName = scriptElement.getAttribute('name');
198
- if (!scriptName || !this.component) return;
199
-
200
- const script = this.component.get(scriptName);
201
- if (script) {
202
- script.enabled = event.detail.enabled;
203
- }
204
- }
205
-
206
- private createScript(name: string, attributes: string | null): Script | null {
207
- if (!this.component) return null;
208
-
209
- let attributesObject = {};
210
- if (attributes) {
211
- try {
212
- attributesObject = JSON.parse(attributes);
213
- // Preprocess attributes: convert arrays or strings into vectors, colors, asset references, etc.
214
- attributesObject = this.preprocessAttributes(attributesObject);
215
- } catch (error) {
216
- console.error(`Error parsing attributes JSON string ${attributes}:`, error);
217
- }
218
- }
219
- return this.component.create(name, {
220
- properties: attributesObject
221
- });
222
- }
223
-
224
- private destroyScript(name: string): void {
225
- if (!this.component) return;
226
- this.component.destroy(name);
227
- }
228
-
229
- private handleMutations(mutations: MutationRecord[]) {
230
- for (const mutation of mutations) {
231
- // Handle added nodes
232
- mutation.addedNodes.forEach((node) => {
233
- if (node instanceof HTMLElement && node.tagName.toLowerCase() === 'pc-script') {
234
- const scriptName = node.getAttribute('name');
235
- const attributes = node.getAttribute('attributes');
236
- if (scriptName) {
237
- this.createScript(scriptName, attributes);
238
- }
239
- }
240
- });
241
-
242
- // Handle removed nodes
243
- mutation.removedNodes.forEach((node) => {
244
- if (node instanceof HTMLElement && node.tagName.toLowerCase() === 'pc-script') {
245
- const scriptName = node.getAttribute('name');
246
- if (scriptName) {
247
- this.destroyScript(scriptName);
248
- }
249
- }
250
- });
251
- }
252
- }
253
-
254
- disconnectedCallback() {
255
- this.observer.disconnect();
256
- super.disconnectedCallback?.();
257
- }
258
-
259
- /**
260
- * Gets the underlying PlayCanvas script component.
261
- * @returns The script component.
262
- */
263
- get component(): ScriptComponent | null {
264
- return super.component as ScriptComponent | null;
265
- }
266
- }
267
-
268
- customElements.define('pc-scripts', ScriptComponentElement);
269
-
270
- export { ScriptComponentElement };
1
+ import { Color, ScriptComponent, Script, Vec2, Vec3, Vec4 } from 'playcanvas';
2
+
3
+ import { AssetElement } from '../asset';
4
+ import { ComponentElement } from './component';
5
+ import { EntityElement } from '../entity';
6
+ import { ScriptElement } from './script';
7
+
8
+ // Add these interfaces at the top of the file, after the imports
9
+ interface ScriptAttributesChangeEvent extends CustomEvent {
10
+ detail: { attributes: any };
11
+ }
12
+
13
+ interface ScriptEnableChangeEvent extends CustomEvent {
14
+ detail: { enabled: boolean };
15
+ }
16
+
17
+ // Add this interface before the ScriptComponentElement class
18
+ declare global {
19
+ interface HTMLElementEventMap {
20
+ 'scriptattributeschange': ScriptAttributesChangeEvent;
21
+ 'scriptenablechange': ScriptEnableChangeEvent;
22
+ }
23
+ }
24
+
25
+ /**
26
+ * The ScriptComponentElement interface provides properties and methods for manipulating
27
+ * {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-scripts/ | `<pc-scripts>`} elements.
28
+ * The ScriptComponentElement interface also inherits the properties and methods of the
29
+ * {@link HTMLElement} interface.
30
+ *
31
+ * @category Components
32
+ */
33
+ class ScriptComponentElement extends ComponentElement {
34
+ private observer: MutationObserver;
35
+
36
+ /** @ignore */
37
+ constructor() {
38
+ super('script');
39
+
40
+ // Create mutation observer to watch for child script elements
41
+ this.observer = new MutationObserver(this.handleMutations.bind(this));
42
+ this.observer.observe(this, {
43
+ childList: true
44
+ });
45
+
46
+ // Listen for script attribute and enable changes
47
+ this.addEventListener('scriptattributeschange', this.handleScriptAttributesChange.bind(this));
48
+ this.addEventListener('scriptenablechange', this.handleScriptEnableChange.bind(this));
49
+ }
50
+
51
+ initComponent() {
52
+ // Handle initial script elements
53
+ this.querySelectorAll<ScriptElement>(':scope > pc-script').forEach((scriptElement) => {
54
+ const scriptName = scriptElement.getAttribute('name');
55
+ const attributes = scriptElement.getAttribute('attributes');
56
+ if (scriptName) {
57
+ this.createScript(scriptName, attributes);
58
+ }
59
+ });
60
+ }
61
+
62
+ /**
63
+ * Recursively converts raw attribute data into proper PlayCanvas types. Supported conversions:
64
+ * - "asset:assetId" → resolves to an Asset instance
65
+ * - "entity:entityId" → resolves to an Entity instance
66
+ * - "vec2:1,2" → new Vec2(1,2)
67
+ * - "vec3:1,2,3" → new Vec3(1,2,3)
68
+ * - "vec4:1,2,3,4" → new Vec4(1,2,3,4)
69
+ * - "color:1,0.5,0.5,1" → new Color(1,0.5,0.5,1)
70
+ * @param item - The item to convert.
71
+ * @returns The converted item.
72
+ */
73
+ private convertAttributes(item: any): any {
74
+ if (typeof item === 'string') {
75
+ if (item.startsWith('asset:')) {
76
+ const assetId = item.slice(6);
77
+ const assetElement = document.querySelector(`pc-asset#${assetId}`) as AssetElement;
78
+ if (assetElement) {
79
+ return assetElement.asset;
80
+ }
81
+ }
82
+ if (item.startsWith('entity:')) {
83
+ const entityId = item.slice(7);
84
+ const entityElement = document.querySelector(`pc-entity[name="${entityId}"]`) as EntityElement;
85
+ if (entityElement) {
86
+ return entityElement.entity;
87
+ }
88
+ }
89
+ if (item.startsWith('vec2:')) {
90
+ const parts = item.slice(5).split(',').map(Number);
91
+ if (parts.length === 2 && parts.every(v => !isNaN(v))) {
92
+ return new Vec2(parts[0], parts[1]);
93
+ }
94
+ }
95
+ if (item.startsWith('vec3:')) {
96
+ const parts = item.slice(5).split(',').map(Number);
97
+ if (parts.length === 3 && parts.every(v => !isNaN(v))) {
98
+ return new Vec3(parts[0], parts[1], parts[2]);
99
+ }
100
+ }
101
+ if (item.startsWith('vec4:')) {
102
+ const parts = item.slice(5).split(',').map(Number);
103
+ if (parts.length === 4 && parts.every(v => !isNaN(v))) {
104
+ return new Vec4(parts[0], parts[1], parts[2], parts[3]);
105
+ }
106
+ }
107
+ if (item.startsWith('color:')) {
108
+ const parts = item.slice(6).split(',').map(Number);
109
+ if (parts.length === 4 && parts.every(v => !isNaN(v))) {
110
+ return new Color(parts[0], parts[1], parts[2], parts[3]);
111
+ }
112
+ }
113
+ return item;
114
+ }
115
+
116
+ if (Array.isArray(item)) {
117
+ // If it's an array of objects, convert each element individually.
118
+ if (item.length > 0 && typeof item[0] === 'object') {
119
+ return item.map((el: any) => this.convertAttributes(el));
120
+ }
121
+ // Otherwise, leave the numeric array unchanged but process each element.
122
+ return item.map((el: any) => this.convertAttributes(el));
123
+ }
124
+
125
+ if (item && typeof item === 'object') {
126
+ const result: any = {};
127
+ for (const key in item) {
128
+ result[key] = this.convertAttributes(item[key]);
129
+ }
130
+ return result;
131
+ }
132
+
133
+ return item;
134
+ }
135
+
136
+ /**
137
+ * Preprocess the attributes object by converting its values.
138
+ * @param attrs - The attributes object to preprocess.
139
+ * @returns The preprocessed attributes object.
140
+ */
141
+ private preprocessAttributes(attrs: any): any {
142
+ return this.convertAttributes(attrs);
143
+ }
144
+
145
+ /**
146
+ * Recursively merge properties from source into target.
147
+ * @param target - The target object to merge into.
148
+ * @param source - The source object to merge from.
149
+ * @returns The merged object.
150
+ */
151
+ private mergeDeep(target: any, source: any): any {
152
+ for (const key in source) {
153
+ if (
154
+ source[key] &&
155
+ typeof source[key] === 'object' &&
156
+ !Array.isArray(source[key])
157
+ ) {
158
+ if (!target[key] || typeof target[key] !== 'object') {
159
+ target[key] = {};
160
+ }
161
+ this.mergeDeep(target[key], source[key]);
162
+ } else {
163
+ target[key] = source[key];
164
+ }
165
+ }
166
+ return target;
167
+ }
168
+
169
+ /**
170
+ * Update script attributes by merging preprocessed values into the script.
171
+ * @param script - The script to update.
172
+ * @param attributes - The attributes to merge into the script.
173
+ */
174
+ private applyAttributes(script: any, attributes: string | null) {
175
+ try {
176
+ const attributesObject = attributes ? JSON.parse(attributes) : {};
177
+ const converted = this.convertAttributes(attributesObject);
178
+ this.mergeDeep(script, converted);
179
+ } catch (error) {
180
+ console.error(`Error parsing attributes JSON string ${attributes}:`, error);
181
+ }
182
+ }
183
+
184
+ private handleScriptAttributesChange(event: ScriptAttributesChangeEvent) {
185
+ const scriptElement = event.target as ScriptElement;
186
+ const scriptName = scriptElement.getAttribute('name');
187
+ if (!scriptName || !this.component) return;
188
+
189
+ const script = this.component.get(scriptName);
190
+ if (script) {
191
+ this.applyAttributes(script, event.detail.attributes);
192
+ }
193
+ }
194
+
195
+ private handleScriptEnableChange(event: ScriptEnableChangeEvent) {
196
+ const scriptElement = event.target as ScriptElement;
197
+ const scriptName = scriptElement.getAttribute('name');
198
+ if (!scriptName || !this.component) return;
199
+
200
+ const script = this.component.get(scriptName);
201
+ if (script) {
202
+ script.enabled = event.detail.enabled;
203
+ }
204
+ }
205
+
206
+ private createScript(name: string, attributes: string | null): Script | null {
207
+ if (!this.component) return null;
208
+
209
+ let attributesObject = {};
210
+ if (attributes) {
211
+ try {
212
+ attributesObject = JSON.parse(attributes);
213
+ // Preprocess attributes: convert arrays or strings into vectors, colors, asset references, etc.
214
+ attributesObject = this.preprocessAttributes(attributesObject);
215
+ } catch (error) {
216
+ console.error(`Error parsing attributes JSON string ${attributes}:`, error);
217
+ }
218
+ }
219
+ return this.component.create(name, {
220
+ properties: attributesObject
221
+ });
222
+ }
223
+
224
+ private destroyScript(name: string): void {
225
+ if (!this.component) return;
226
+ this.component.destroy(name);
227
+ }
228
+
229
+ private handleMutations(mutations: MutationRecord[]) {
230
+ for (const mutation of mutations) {
231
+ // Handle added nodes
232
+ mutation.addedNodes.forEach((node) => {
233
+ if (node instanceof HTMLElement && node.tagName.toLowerCase() === 'pc-script') {
234
+ const scriptName = node.getAttribute('name');
235
+ const attributes = node.getAttribute('attributes');
236
+ if (scriptName) {
237
+ this.createScript(scriptName, attributes);
238
+ }
239
+ }
240
+ });
241
+
242
+ // Handle removed nodes
243
+ mutation.removedNodes.forEach((node) => {
244
+ if (node instanceof HTMLElement && node.tagName.toLowerCase() === 'pc-script') {
245
+ const scriptName = node.getAttribute('name');
246
+ if (scriptName) {
247
+ this.destroyScript(scriptName);
248
+ }
249
+ }
250
+ });
251
+ }
252
+ }
253
+
254
+ disconnectedCallback() {
255
+ this.observer.disconnect();
256
+ super.disconnectedCallback?.();
257
+ }
258
+
259
+ /**
260
+ * Gets the underlying PlayCanvas script component.
261
+ * @returns The script component.
262
+ */
263
+ get component(): ScriptComponent | null {
264
+ return super.component as ScriptComponent | null;
265
+ }
266
+ }
267
+
268
+ customElements.define('pc-scripts', ScriptComponentElement);
269
+
270
+ export { ScriptComponentElement };