@playcanvas/web-components 0.11.0 → 0.12.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 (82) hide show
  1. package/README.md +1 -1
  2. package/dist/app.d.ts +31 -49
  3. package/dist/asset.d.ts +152 -12
  4. package/dist/async-element.d.ts +26 -9
  5. package/dist/components/button-component.d.ts +3 -7
  6. package/dist/components/camera-component.d.ts +3 -7
  7. package/dist/components/collision-component.d.ts +19 -7
  8. package/dist/components/component.d.ts +41 -4
  9. package/dist/components/element-component.d.ts +4 -8
  10. package/dist/components/gsplat-component.d.ts +2 -7
  11. package/dist/components/layoutchild-component.d.ts +2 -7
  12. package/dist/components/layoutgroup-component.d.ts +3 -7
  13. package/dist/components/light-component.d.ts +3 -7
  14. package/dist/components/listener-component.d.ts +1 -6
  15. package/dist/components/particlesystem-component.d.ts +2 -7
  16. package/dist/components/render-component.d.ts +2 -7
  17. package/dist/components/rigidbody-component.d.ts +3 -7
  18. package/dist/components/screen-component.d.ts +3 -7
  19. package/dist/components/script-component.d.ts +8 -20
  20. package/dist/components/script.d.ts +2 -22
  21. package/dist/components/scrollbar-component.d.ts +2 -7
  22. package/dist/components/scrollview-component.d.ts +3 -7
  23. package/dist/components/sound-component.d.ts +2 -7
  24. package/dist/components/sound-slot.d.ts +8 -6
  25. package/dist/custom-elements.json +5824 -10581
  26. package/dist/entity-base.d.ts +67 -0
  27. package/dist/entity.d.ts +7 -48
  28. package/dist/index.d.ts +41 -1
  29. package/dist/material.d.ts +14 -13
  30. package/dist/model.d.ts +43 -5
  31. package/dist/module.d.ts +0 -6
  32. package/dist/node.d.ts +253 -0
  33. package/dist/parse.d.ts +2 -1
  34. package/dist/pwc.cjs +1830 -274
  35. package/dist/pwc.cjs.map +1 -1
  36. package/dist/pwc.js +1830 -274
  37. package/dist/pwc.js.map +1 -1
  38. package/dist/pwc.min.js +1 -1
  39. package/dist/pwc.min.js.map +1 -1
  40. package/dist/pwc.min.mjs +1 -1
  41. package/dist/pwc.min.mjs.map +1 -1
  42. package/dist/pwc.mjs +1830 -276
  43. package/dist/pwc.mjs.map +1 -1
  44. package/dist/scene.d.ts +4 -7
  45. package/dist/sky.d.ts +13 -5
  46. package/dist/vscode.html-custom-data.json +148 -26
  47. package/dist/web-types.json +894 -581
  48. package/package.json +9 -8
  49. package/src/app.ts +163 -88
  50. package/src/asset.ts +472 -36
  51. package/src/async-element.ts +39 -12
  52. package/src/components/button-component.ts +5 -9
  53. package/src/components/camera-component.ts +24 -10
  54. package/src/components/collision-component.ts +61 -15
  55. package/src/components/component.ts +151 -11
  56. package/src/components/element-component.ts +26 -30
  57. package/src/components/gsplat-component.ts +4 -9
  58. package/src/components/layoutchild-component.ts +4 -9
  59. package/src/components/layoutgroup-component.ts +14 -9
  60. package/src/components/light-component.ts +42 -12
  61. package/src/components/listener-component.ts +1 -7
  62. package/src/components/particlesystem-component.ts +7 -15
  63. package/src/components/render-component.ts +5 -10
  64. package/src/components/rigidbody-component.ts +23 -16
  65. package/src/components/screen-component.ts +5 -9
  66. package/src/components/script-component.ts +108 -46
  67. package/src/components/script.ts +38 -33
  68. package/src/components/scrollbar-component.ts +6 -16
  69. package/src/components/scrollview-component.ts +16 -11
  70. package/src/components/sound-component.ts +10 -15
  71. package/src/components/sound-slot.ts +30 -20
  72. package/src/entity-base.ts +136 -0
  73. package/src/entity.ts +47 -118
  74. package/src/index.ts +50 -1
  75. package/src/loading-bar.ts +8 -8
  76. package/src/material.ts +65 -39
  77. package/src/model.ts +140 -17
  78. package/src/module.ts +8 -7
  79. package/src/node.ts +715 -0
  80. package/src/parse.ts +62 -17
  81. package/src/scene.ts +12 -9
  82. package/src/sky.ts +50 -10
package/src/parse.ts CHANGED
@@ -16,7 +16,8 @@
16
16
  * literal, and returns `null` instead of falling back to a default.
17
17
  */
18
18
 
19
- import { Color, Entity, Quat, Vec2, Vec3, Vec4 } from 'playcanvas';
19
+ import type { Entity } from 'playcanvas';
20
+ import { Color, Quat, Vec2, Vec3, Vec4 } from 'playcanvas';
20
21
 
21
22
  import { CSS_COLORS } from './colors';
22
23
 
@@ -31,7 +32,7 @@ import { CSS_COLORS } from './colors';
31
32
  */
32
33
  export const parseComponents = (value: string, count: number): number[] | null => {
33
34
  const components = value.trim().split(/\s+/).map(Number);
34
- if (components.length !== count || components.some(component => !Number.isFinite(component))) {
35
+ if (components.length !== count || components.some((component) => !Number.isFinite(component))) {
35
36
  return null;
36
37
  }
37
38
  return components;
@@ -77,7 +78,11 @@ export const parseBool = (value: string | null, defaultValue: boolean): boolean
77
78
  * @param attribute - The attribute name, used in the warning message.
78
79
  * @returns The parsed Color object.
79
80
  */
80
- export const parseColor = <T extends Color | null>(value: string | null, defaultValue: T, attribute: string): Color | T => {
81
+ export const parseColor = <T extends Color | null>(
82
+ value: string | null,
83
+ defaultValue: T,
84
+ attribute: string
85
+ ): Color | T => {
81
86
  if (value === null) {
82
87
  return cloneDefault(defaultValue);
83
88
  }
@@ -92,7 +97,10 @@ export const parseColor = <T extends Color | null>(value: string | null, default
92
97
  if (/^#(?:[0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i.test(value)) {
93
98
  let hex = value.slice(1);
94
99
  if (hex.length === 3 || hex.length === 4) {
95
- hex = hex.split('').map(char => char + char).join('');
100
+ hex = hex
101
+ .split('')
102
+ .map((char) => char + char)
103
+ .join('');
96
104
  }
97
105
  return new Color().fromString(`#${hex}`);
98
106
  }
@@ -103,7 +111,9 @@ export const parseColor = <T extends Color | null>(value: string | null, default
103
111
  return new Color(components);
104
112
  }
105
113
 
106
- console.warn(`Invalid value '${value}' for attribute '${attribute}'. Expected a CSS color name, a hex color or 3 or 4 space-separated numbers. Using '${defaultValue}'.`);
114
+ console.warn(
115
+ `Invalid value '${value}' for attribute '${attribute}'. Expected a CSS color name, a hex color or 3 or 4 space-separated numbers. Using '${defaultValue}'.`
116
+ );
107
117
  return cloneDefault(defaultValue);
108
118
  };
109
119
 
@@ -133,7 +143,9 @@ export const parseEnum = <T extends string>(
133
143
  if (names.includes(value as T)) {
134
144
  return value as T;
135
145
  }
136
- console.warn(`Invalid value '${value}' for attribute '${attribute}'. Valid values: ${names.join(', ')}. Using '${defaultValue}'.`);
146
+ console.warn(
147
+ `Invalid value '${value}' for attribute '${attribute}'. Valid values: ${names.join(', ')}. Using '${defaultValue}'.`
148
+ );
137
149
  return defaultValue;
138
150
  };
139
151
 
@@ -147,13 +159,19 @@ export const parseEnum = <T extends string>(
147
159
  * @param attribute - The attribute name, used in the warning message.
148
160
  * @returns The parsed number.
149
161
  */
150
- export const parseNumber = <T extends number | null>(value: string | null, defaultValue: T, attribute: string): number | T => {
162
+ export const parseNumber = <T extends number | null>(
163
+ value: string | null,
164
+ defaultValue: T,
165
+ attribute: string
166
+ ): number | T => {
151
167
  if (value === null) {
152
168
  return defaultValue;
153
169
  }
154
170
  const number = value.trim() === '' ? NaN : Number(value);
155
171
  if (!Number.isFinite(number)) {
156
- console.warn(`Invalid value '${value}' for attribute '${attribute}'. Expected a finite number. Using '${defaultValue}'.`);
172
+ console.warn(
173
+ `Invalid value '${value}' for attribute '${attribute}'. Expected a finite number. Using '${defaultValue}'.`
174
+ );
157
175
  return defaultValue;
158
176
  }
159
177
  return number;
@@ -170,13 +188,19 @@ export const parseNumber = <T extends number | null>(value: string | null, defau
170
188
  * @param attribute - The attribute name, used in the warning message.
171
189
  * @returns The parsed Quat object.
172
190
  */
173
- export const parseQuat = <T extends Quat | null>(value: string | null, defaultValue: T, attribute: string): Quat | T => {
191
+ export const parseQuat = <T extends Quat | null>(
192
+ value: string | null,
193
+ defaultValue: T,
194
+ attribute: string
195
+ ): Quat | T => {
174
196
  if (value === null) {
175
197
  return cloneDefault(defaultValue);
176
198
  }
177
199
  const components = parseComponents(value, 3);
178
200
  if (!components) {
179
- console.warn(`Invalid value '${value}' for attribute '${attribute}'. Expected 3 space-separated numbers. Using '${defaultValue}'.`);
201
+ console.warn(
202
+ `Invalid value '${value}' for attribute '${attribute}'. Expected 3 space-separated numbers. Using '${defaultValue}'.`
203
+ );
180
204
  return cloneDefault(defaultValue);
181
205
  }
182
206
  return new Quat().setFromEulerAngles(components[0], components[1], components[2]);
@@ -200,7 +224,10 @@ export const parseTags = (value: string | null, defaultValue: string[] = []): st
200
224
  // caller's default, or a later mutation would write back through it.
201
225
  return [...defaultValue];
202
226
  }
203
- return value.split(',').map(tag => tag.trim()).filter(tag => tag !== '');
227
+ return value
228
+ .split(',')
229
+ .map((tag) => tag.trim())
230
+ .filter((tag) => tag !== '');
204
231
  };
205
232
 
206
233
  /**
@@ -213,13 +240,19 @@ export const parseTags = (value: string | null, defaultValue: string[] = []): st
213
240
  * @param attribute - The attribute name, used in the warning message.
214
241
  * @returns The parsed Vec2 object.
215
242
  */
216
- export const parseVec2 = <T extends Vec2 | null>(value: string | null, defaultValue: T, attribute: string): Vec2 | T => {
243
+ export const parseVec2 = <T extends Vec2 | null>(
244
+ value: string | null,
245
+ defaultValue: T,
246
+ attribute: string
247
+ ): Vec2 | T => {
217
248
  if (value === null) {
218
249
  return cloneDefault(defaultValue);
219
250
  }
220
251
  const components = parseComponents(value, 2);
221
252
  if (!components) {
222
- console.warn(`Invalid value '${value}' for attribute '${attribute}'. Expected 2 space-separated numbers. Using '${defaultValue}'.`);
253
+ console.warn(
254
+ `Invalid value '${value}' for attribute '${attribute}'. Expected 2 space-separated numbers. Using '${defaultValue}'.`
255
+ );
223
256
  return cloneDefault(defaultValue);
224
257
  }
225
258
  return new Vec2(components);
@@ -235,13 +268,19 @@ export const parseVec2 = <T extends Vec2 | null>(value: string | null, defaultVa
235
268
  * @param attribute - The attribute name, used in the warning message.
236
269
  * @returns The parsed Vec3 object.
237
270
  */
238
- export const parseVec3 = <T extends Vec3 | null>(value: string | null, defaultValue: T, attribute: string): Vec3 | T => {
271
+ export const parseVec3 = <T extends Vec3 | null>(
272
+ value: string | null,
273
+ defaultValue: T,
274
+ attribute: string
275
+ ): Vec3 | T => {
239
276
  if (value === null) {
240
277
  return cloneDefault(defaultValue);
241
278
  }
242
279
  const components = parseComponents(value, 3);
243
280
  if (!components) {
244
- console.warn(`Invalid value '${value}' for attribute '${attribute}'. Expected 3 space-separated numbers. Using '${defaultValue}'.`);
281
+ console.warn(
282
+ `Invalid value '${value}' for attribute '${attribute}'. Expected 3 space-separated numbers. Using '${defaultValue}'.`
283
+ );
245
284
  return cloneDefault(defaultValue);
246
285
  }
247
286
  return new Vec3(components);
@@ -257,13 +296,19 @@ export const parseVec3 = <T extends Vec3 | null>(value: string | null, defaultVa
257
296
  * @param attribute - The attribute name, used in the warning message.
258
297
  * @returns The parsed Vec4 object.
259
298
  */
260
- export const parseVec4 = <T extends Vec4 | null>(value: string | null, defaultValue: T, attribute: string): Vec4 | T => {
299
+ export const parseVec4 = <T extends Vec4 | null>(
300
+ value: string | null,
301
+ defaultValue: T,
302
+ attribute: string
303
+ ): Vec4 | T => {
261
304
  if (value === null) {
262
305
  return cloneDefault(defaultValue);
263
306
  }
264
307
  const components = parseComponents(value, 4);
265
308
  if (!components) {
266
- console.warn(`Invalid value '${value}' for attribute '${attribute}'. Expected 4 space-separated numbers. Using '${defaultValue}'.`);
309
+ console.warn(
310
+ `Invalid value '${value}' for attribute '${attribute}'. Expected 4 space-separated numbers. Using '${defaultValue}'.`
311
+ );
267
312
  return cloneDefault(defaultValue);
268
313
  }
269
314
  return new Vec4(components);
package/src/scene.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { Color, Scene, Vec3 } from 'playcanvas';
1
+ import type { Scene } from 'playcanvas';
2
+ import { Color, Vec3 } from 'playcanvas';
2
3
 
3
4
  import { AsyncElement } from './async-element';
4
5
  import { parseColor, parseEnum, parseNumber, parseVec3 } from './parse';
@@ -77,12 +78,20 @@ class SceneElement extends AsyncElement {
77
78
  }
78
79
 
79
80
  this._scene = app.scene;
80
- this.updateSceneSettings();
81
+ this._updateSceneSettings();
81
82
 
82
83
  this._onReady();
83
84
  }
84
85
 
85
- updateSceneSettings() {
86
+ disconnectedCallback() {
87
+ // The scene belongs to the application, and removing this element - or the <pc-app>
88
+ // above it, which disconnects first - parts the two. Re-arm readiness so a re-inserted
89
+ // element announces the scene it acquires then, not the one it lost here.
90
+ this._scene = null;
91
+ this._resetReady();
92
+ }
93
+
94
+ private _updateSceneSettings() {
86
95
  if (this._scene) {
87
96
  this._scene.fog.type = this._fog;
88
97
  this._scene.fog.color = this._fogColor;
@@ -251,10 +260,4 @@ class SceneElement extends AsyncElement {
251
260
 
252
261
  customElements.define('pc-scene', SceneElement);
253
262
 
254
- declare global {
255
- interface HTMLElementTagNameMap {
256
- 'pc-scene': SceneElement;
257
- }
258
- }
259
-
260
263
  export { SceneElement };
package/src/sky.ts CHANGED
@@ -1,6 +1,7 @@
1
- import { Asset, EnvLighting, LAYERID_SKYBOX, Quat, Scene, Texture, Vec3 } from 'playcanvas';
1
+ import type { Asset, EventHandle, Scene, Texture } from 'playcanvas';
2
+ import { EnvLighting, LAYERID_SKYBOX, Quat, Vec3 } from 'playcanvas';
2
3
 
3
- import { AppElement } from './app';
4
+ import type { AppElement } from './app';
4
5
  import { AssetElement } from './asset';
5
6
  import { AsyncElement } from './async-element';
6
7
  import { parseBool, parseEnum, parseNumber, parseVec3 } from './parse';
@@ -31,14 +32,36 @@ class SkyElement extends AsyncElement {
31
32
 
32
33
  private _appElement: AppElement | null = null;
33
34
 
35
+ /**
36
+ * Incremented on every new load and on disconnect, and captured by a load when it starts. A
37
+ * load that resumes from an await or a load callback abandons itself if the value has moved
38
+ * on, so a superseded load cannot generate a skybox for a scene it no longer configures.
39
+ */
40
+ private _loadGeneration = 0;
41
+
42
+ /**
43
+ * The pending asset-load subscription of the current load, if it is waiting for its asset.
44
+ * Held so that whatever supersedes the load can detach the handler from the asset, rather
45
+ * than leave it registered until the asset loads (or forever, if it never does).
46
+ */
47
+ private _loadHandle: EventHandle | null = null;
48
+
34
49
  connectedCallback() {
35
50
  this._loadSkybox();
36
51
  this._onReady();
37
52
  }
38
53
 
39
54
  disconnectedCallback() {
55
+ this._loadGeneration++;
56
+ this._detachLoadHandler();
40
57
  this._unloadSkybox();
41
58
  this._appElement = null;
59
+ this._resetReady();
60
+ }
61
+
62
+ private _detachLoadHandler() {
63
+ this._loadHandle?.off();
64
+ this._loadHandle = null;
42
65
  }
43
66
 
44
67
  private _generateSkybox(asset: Asset) {
@@ -47,12 +70,18 @@ class SkyElement extends AsyncElement {
47
70
  const source = asset.resource as Texture;
48
71
 
49
72
  const skybox = EnvLighting.generateSkyboxCubemap(source);
50
- skybox.anisotropy = 4;
73
+ // This element owns what it generated (see _unloadSkybox) - replacing a skybox from an
74
+ // earlier load must release it, not orphan it on the GPU
75
+ this._scene.skybox?.destroy();
51
76
  this._scene.skybox = skybox;
52
77
 
53
78
  if (this._lighting) {
54
79
  const lighting = EnvLighting.generateLightingSource(source);
55
80
  const envAtlas = EnvLighting.generateAtlas(lighting);
81
+ // The lighting source is an intermediate: the atlas is rendered from it and it is
82
+ // not needed afterwards
83
+ lighting.destroy();
84
+ this._scene.envAtlas?.destroy();
56
85
  this._scene.envAtlas = envAtlas;
57
86
  }
58
87
 
@@ -69,7 +98,17 @@ class SkyElement extends AsyncElement {
69
98
  }
70
99
 
71
100
  private async _loadSkybox() {
101
+ // Supersede any load already in flight - only the newest load may generate the skybox
102
+ const generation = ++this._loadGeneration;
103
+ this._detachLoadHandler();
104
+
72
105
  const appElement = await this.closestApp?.ready();
106
+
107
+ // The element may have been removed, or another load started, while we waited
108
+ if (generation !== this._loadGeneration) {
109
+ return;
110
+ }
111
+
73
112
  const app = appElement?.app;
74
113
  if (!appElement || !app) {
75
114
  return;
@@ -87,7 +126,14 @@ class SkyElement extends AsyncElement {
87
126
  if (asset.loaded) {
88
127
  this._generateSkybox(asset);
89
128
  } else {
90
- asset.once('load', () => {
129
+ // The generation is re-checked even though a superseded handler is detached: the
130
+ // detach relies on how the engine's event emitter treats removal, while the check
131
+ // holds on its own.
132
+ this._loadHandle = asset.once('load', () => {
133
+ this._loadHandle = null;
134
+ if (generation !== this._loadGeneration) {
135
+ return;
136
+ }
91
137
  this._generateSkybox(asset);
92
138
  });
93
139
  app.assets.load(asset);
@@ -303,10 +349,4 @@ class SkyElement extends AsyncElement {
303
349
 
304
350
  customElements.define('pc-sky', SkyElement);
305
351
 
306
- declare global {
307
- interface HTMLElementTagNameMap {
308
- 'pc-sky': SkyElement;
309
- }
310
- }
311
-
312
352
  export { SkyElement };