@playcanvas/web-components 0.3.0 → 0.6.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 (43) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +84 -84
  3. package/dist/components/gsplat-component.d.ts +79 -0
  4. package/dist/components/light-component.d.ts +48 -0
  5. package/dist/index.d.ts +2 -2
  6. package/dist/pwc.cjs +324 -203
  7. package/dist/pwc.cjs.map +1 -1
  8. package/dist/pwc.js +324 -203
  9. package/dist/pwc.js.map +1 -1
  10. package/dist/pwc.min.js +1 -1
  11. package/dist/pwc.min.js.map +1 -1
  12. package/dist/pwc.mjs +325 -204
  13. package/dist/pwc.mjs.map +1 -1
  14. package/package.json +76 -66
  15. package/src/app.ts +612 -606
  16. package/src/asset.ts +159 -159
  17. package/src/async-element.ts +46 -46
  18. package/src/colors.ts +150 -150
  19. package/src/components/camera-component.ts +557 -557
  20. package/src/components/collision-component.ts +183 -183
  21. package/src/components/component.ts +97 -97
  22. package/src/components/element-component.ts +367 -367
  23. package/src/components/gsplat-component.ts +161 -0
  24. package/src/components/light-component.ts +570 -466
  25. package/src/components/listener-component.ts +30 -30
  26. package/src/components/particlesystem-component.ts +155 -155
  27. package/src/components/render-component.ts +147 -147
  28. package/src/components/rigidbody-component.ts +227 -227
  29. package/src/components/screen-component.ts +157 -157
  30. package/src/components/script-component.ts +270 -270
  31. package/src/components/script.ts +90 -90
  32. package/src/components/sound-component.ts +230 -230
  33. package/src/components/sound-slot.ts +288 -288
  34. package/src/entity.ts +360 -360
  35. package/src/index.ts +63 -63
  36. package/src/material.ts +141 -141
  37. package/src/model.ts +111 -111
  38. package/src/module.ts +43 -43
  39. package/src/scene.ts +217 -217
  40. package/src/sky.ts +293 -293
  41. package/src/utils.ts +71 -71
  42. package/dist/components/splat-component.d.ts +0 -61
  43. package/src/components/splat-component.ts +0 -133
@@ -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 };