@playcanvas/web-components 0.9.0 → 0.10.1

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 (74) hide show
  1. package/README.md +18 -0
  2. package/dist/app.d.ts +55 -9
  3. package/dist/asset.d.ts +25 -1
  4. package/dist/async-element.d.ts +15 -2
  5. package/dist/components/button-component.d.ts +1 -1
  6. package/dist/components/camera-component.d.ts +1 -1
  7. package/dist/components/collision-component.d.ts +1 -1
  8. package/dist/components/component.d.ts +6 -5
  9. package/dist/components/element-component.d.ts +1 -1
  10. package/dist/components/gsplat-component.d.ts +1 -1
  11. package/dist/components/layoutchild-component.d.ts +1 -1
  12. package/dist/components/layoutgroup-component.d.ts +1 -1
  13. package/dist/components/light-component.d.ts +1 -1
  14. package/dist/components/particlesystem-component.d.ts +1 -1
  15. package/dist/components/render-component.d.ts +1 -1
  16. package/dist/components/rigidbody-component.d.ts +1 -1
  17. package/dist/components/screen-component.d.ts +1 -1
  18. package/dist/components/script.d.ts +8 -1
  19. package/dist/components/scrollbar-component.d.ts +1 -1
  20. package/dist/components/scrollview-component.d.ts +1 -1
  21. package/dist/components/sound-component.d.ts +1 -1
  22. package/dist/components/sound-slot.d.ts +9 -1
  23. package/dist/custom-elements.json +16704 -0
  24. package/dist/entity.d.ts +24 -5
  25. package/dist/loading-bar.d.ts +35 -0
  26. package/dist/material.d.ts +972 -4
  27. package/dist/model.d.ts +1 -1
  28. package/dist/module.d.ts +10 -0
  29. package/dist/{utils.d.ts → parse.d.ts} +63 -33
  30. package/dist/pwc.cjs +3070 -699
  31. package/dist/pwc.cjs.map +1 -1
  32. package/dist/pwc.js +3070 -699
  33. package/dist/pwc.js.map +1 -1
  34. package/dist/pwc.min.js +1 -1
  35. package/dist/pwc.min.js.map +1 -1
  36. package/dist/pwc.min.mjs +2 -0
  37. package/dist/pwc.min.mjs.map +1 -0
  38. package/dist/pwc.mjs +3071 -700
  39. package/dist/pwc.mjs.map +1 -1
  40. package/dist/scene.d.ts +12 -4
  41. package/dist/sky.d.ts +1 -1
  42. package/dist/vscode.html-custom-data.json +1800 -0
  43. package/dist/web-types.json +3836 -0
  44. package/package.json +29 -11
  45. package/src/app.ts +178 -78
  46. package/src/asset.ts +44 -2
  47. package/src/async-element.ts +17 -4
  48. package/src/components/button-component.ts +6 -6
  49. package/src/components/camera-component.ts +2 -2
  50. package/src/components/collision-component.ts +2 -2
  51. package/src/components/component.ts +8 -7
  52. package/src/components/element-component.ts +6 -6
  53. package/src/components/gsplat-component.ts +3 -3
  54. package/src/components/layoutchild-component.ts +2 -2
  55. package/src/components/layoutgroup-component.ts +2 -2
  56. package/src/components/light-component.ts +2 -2
  57. package/src/components/particlesystem-component.ts +2 -2
  58. package/src/components/render-component.ts +10 -5
  59. package/src/components/rigidbody-component.ts +2 -2
  60. package/src/components/screen-component.ts +2 -2
  61. package/src/components/script-component.ts +4 -4
  62. package/src/components/script.ts +9 -2
  63. package/src/components/scrollbar-component.ts +3 -3
  64. package/src/components/scrollview-component.ts +6 -6
  65. package/src/components/sound-component.ts +2 -2
  66. package/src/components/sound-slot.ts +31 -9
  67. package/src/entity.ts +56 -22
  68. package/src/loading-bar.ts +122 -0
  69. package/src/material.ts +2402 -59
  70. package/src/model.ts +2 -2
  71. package/src/module.ts +10 -0
  72. package/src/{utils.ts → parse.ts} +104 -65
  73. package/src/scene.ts +51 -21
  74. package/src/sky.ts +3 -3
@@ -2,7 +2,7 @@ import { BUTTON_TRANSITION_MODE_SPRITE_CHANGE, BUTTON_TRANSITION_MODE_TINT, Butt
2
2
 
3
3
  import { AssetElement } from '../asset';
4
4
  import { ComponentElement } from './component';
5
- import { getEntity, parseBool, parseColor, parseEnum, parseNumber, parseVec4 } from '../utils';
5
+ import { getEntity, parseBool, parseColor, parseEnum, parseNumber, parseVec4 } from '../parse';
6
6
 
7
7
  const transitionModes = new Map<'tint' | 'sprite', number>([
8
8
  ['tint', BUTTON_TRANSITION_MODE_TINT],
@@ -396,7 +396,7 @@ class ButtonComponentElement extends ComponentElement {
396
396
  ];
397
397
  }
398
398
 
399
- attributeChangedCallback(name: string, _oldValue: string, newValue: string) {
399
+ attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {
400
400
  super.attributeChangedCallback(name, _oldValue, newValue);
401
401
 
402
402
  switch (name) {
@@ -404,7 +404,7 @@ class ButtonComponentElement extends ComponentElement {
404
404
  this.active = parseBool(newValue, true);
405
405
  break;
406
406
  case 'image':
407
- this.image = newValue;
407
+ this.image = newValue ?? '';
408
408
  break;
409
409
  case 'hit-padding':
410
410
  this.hitPadding = parseVec4(newValue, Vec4.ZERO, name);
@@ -425,19 +425,19 @@ class ButtonComponentElement extends ComponentElement {
425
425
  this.fadeDuration = parseNumber(newValue, 0, name);
426
426
  break;
427
427
  case 'hover-sprite-asset':
428
- this.hoverSpriteAsset = newValue;
428
+ this.hoverSpriteAsset = newValue ?? '';
429
429
  break;
430
430
  case 'hover-sprite-frame':
431
431
  this.hoverSpriteFrame = parseNumber(newValue, 0, name);
432
432
  break;
433
433
  case 'pressed-sprite-asset':
434
- this.pressedSpriteAsset = newValue;
434
+ this.pressedSpriteAsset = newValue ?? '';
435
435
  break;
436
436
  case 'pressed-sprite-frame':
437
437
  this.pressedSpriteFrame = parseNumber(newValue, 0, name);
438
438
  break;
439
439
  case 'inactive-sprite-asset':
440
- this.inactiveSpriteAsset = newValue;
440
+ this.inactiveSpriteAsset = newValue ?? '';
441
441
  break;
442
442
  case 'inactive-sprite-frame':
443
443
  this.inactiveSpriteFrame = parseNumber(newValue, 0, name);
@@ -1,7 +1,7 @@
1
1
  import { CameraComponent, Color, Vec4, GAMMA_NONE, GAMMA_SRGB, PROJECTION_ORTHOGRAPHIC, PROJECTION_PERSPECTIVE, TONEMAP_LINEAR, TONEMAP_FILMIC, TONEMAP_NEUTRAL, TONEMAP_ACES2, TONEMAP_ACES, TONEMAP_HEJL, TONEMAP_NONE, XRTYPE_VR } from 'playcanvas';
2
2
 
3
3
  import { ComponentElement } from './component';
4
- import { parseBool, parseColor, parseEnum, parseNumber, parseVec4 } from '../utils';
4
+ import { parseBool, parseColor, parseEnum, parseNumber, parseVec4 } from '../parse';
5
5
 
6
6
  const tonemaps = new Map<'none' | 'linear' | 'filmic' | 'hejl' | 'aces' | 'aces2' | 'neutral', number>([
7
7
  ['none', TONEMAP_NONE],
@@ -490,7 +490,7 @@ class CameraComponentElement extends ComponentElement {
490
490
  ];
491
491
  }
492
492
 
493
- attributeChangedCallback(name: string, _oldValue: string, newValue: string) {
493
+ attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {
494
494
  super.attributeChangedCallback(name, _oldValue, newValue);
495
495
 
496
496
  switch (name) {
@@ -1,7 +1,7 @@
1
1
  import { CollisionComponent, Quat, Vec3 } from 'playcanvas';
2
2
 
3
3
  import { ComponentElement } from './component';
4
- import { parseBool, parseEnum, parseNumber, parseQuat, parseVec3 } from '../utils';
4
+ import { parseBool, parseEnum, parseNumber, parseQuat, parseVec3 } from '../parse';
5
5
 
6
6
  /**
7
7
  * The CollisionComponentElement interface provides properties and methods for manipulating
@@ -146,7 +146,7 @@ class CollisionComponentElement extends ComponentElement {
146
146
  return [...super.observedAttributes, 'angular-offset', 'axis', 'convex-hull', 'half-extents', 'height', 'linear-offset', 'radius', 'type'];
147
147
  }
148
148
 
149
- attributeChangedCallback(name: string, _oldValue: string, newValue: string) {
149
+ attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {
150
150
  super.attributeChangedCallback(name, _oldValue, newValue);
151
151
 
152
152
  switch (name) {
@@ -2,7 +2,7 @@ import { Component } from 'playcanvas';
2
2
 
3
3
  import { AppElement } from '../app';
4
4
  import { AsyncElement } from '../async-element';
5
- import { parseBool } from '../utils';
5
+ import { parseBool } from '../parse';
6
6
 
7
7
  /**
8
8
  * Represents a component in the PlayCanvas engine.
@@ -73,12 +73,13 @@ class ComponentElement extends AsyncElement {
73
73
  }
74
74
 
75
75
  /**
76
- * The PlayCanvas component instance. Available once the element is ready await
77
- * {@link whenReady} or the element's `ready()` promise before accessing it.
78
- * @returns The component instance.
76
+ * The PlayCanvas component instance. `null` until the element is ready, and also for an
77
+ * element that is not a descendant of a `<pc-entity>` — await {@link whenReady} or the
78
+ * element's `ready()` promise before accessing it.
79
+ * @returns The component instance, or `null`.
79
80
  */
80
- get component(): Component {
81
- return this._component!;
81
+ get component(): Component | null {
82
+ return this._component;
82
83
  }
83
84
 
84
85
  /**
@@ -104,7 +105,7 @@ class ComponentElement extends AsyncElement {
104
105
  return ['enabled'];
105
106
  }
106
107
 
107
- attributeChangedCallback(name: string, _oldValue: string, newValue: string) {
108
+ attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {
108
109
  switch (name) {
109
110
  case 'enabled':
110
111
  this.enabled = parseBool(newValue, true);
@@ -2,7 +2,7 @@ import { Color, ElementComponent, Vec2, Vec4 } from 'playcanvas';
2
2
 
3
3
  import { AssetElement } from '../asset';
4
4
  import { ComponentElement } from './component';
5
- import { parseBool, parseColor, parseEnum, parseNumber, parseVec2, parseVec4 } from '../utils';
5
+ import { parseBool, parseColor, parseEnum, parseNumber, parseVec2, parseVec4 } from '../parse';
6
6
 
7
7
  /**
8
8
  * The ElementComponentElement interface provides properties and methods for manipulating
@@ -686,7 +686,7 @@ class ElementComponentElement extends ComponentElement {
686
686
  ];
687
687
  }
688
688
 
689
- attributeChangedCallback(name: string, _oldValue: string, newValue: string) {
689
+ attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {
690
690
  super.attributeChangedCallback(name, _oldValue, newValue);
691
691
 
692
692
  switch (name) {
@@ -712,7 +712,7 @@ class ElementComponentElement extends ComponentElement {
712
712
  this.enableMarkup = parseBool(newValue, false);
713
713
  break;
714
714
  case 'font-asset':
715
- this.fontAsset = newValue;
715
+ this.fontAsset = newValue ?? '';
716
716
  break;
717
717
  case 'font-size':
718
718
  this.fontSize = parseNumber(newValue, 32, name);
@@ -745,16 +745,16 @@ class ElementComponentElement extends ComponentElement {
745
745
  this.pixelsPerUnit = parseNumber(newValue, null, name);
746
746
  break;
747
747
  case 'sprite-asset':
748
- this.spriteAsset = newValue;
748
+ this.spriteAsset = newValue ?? '';
749
749
  break;
750
750
  case 'sprite-frame':
751
751
  this.spriteFrame = parseNumber(newValue, 0, name);
752
752
  break;
753
753
  case 'text':
754
- this.text = newValue;
754
+ this.text = newValue ?? '';
755
755
  break;
756
756
  case 'texture-asset':
757
- this.textureAsset = newValue;
757
+ this.textureAsset = newValue ?? '';
758
758
  break;
759
759
  case 'type':
760
760
  this.type = parseEnum(newValue, ['group', 'image', 'text'], 'group', name);
@@ -2,7 +2,7 @@ import { GSplatComponent } from 'playcanvas';
2
2
 
3
3
  import { ComponentElement } from './component';
4
4
  import { AssetElement } from '../asset';
5
- import { parseBool, parseNumber } from '../utils';
5
+ import { parseBool, parseNumber } from '../parse';
6
6
 
7
7
  /**
8
8
  * The GSplatComponentElement interface provides properties and methods for manipulating
@@ -188,12 +188,12 @@ class GSplatComponentElement extends ComponentElement {
188
188
  ];
189
189
  }
190
190
 
191
- attributeChangedCallback(name: string, _oldValue: string, newValue: string) {
191
+ attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {
192
192
  super.attributeChangedCallback(name, _oldValue, newValue);
193
193
 
194
194
  switch (name) {
195
195
  case 'asset':
196
- this.asset = newValue;
196
+ this.asset = newValue ?? '';
197
197
  break;
198
198
  case 'cast-shadows':
199
199
  this.castShadows = parseBool(newValue, false);
@@ -1,7 +1,7 @@
1
1
  import { LayoutChildComponent } from 'playcanvas';
2
2
 
3
3
  import { ComponentElement } from './component';
4
- import { parseBool, parseNumber } from '../utils';
4
+ import { parseBool, parseNumber } from '../parse';
5
5
 
6
6
  /**
7
7
  * The LayoutChildComponentElement interface provides properties and methods for manipulating
@@ -199,7 +199,7 @@ class LayoutChildComponentElement extends ComponentElement {
199
199
  ];
200
200
  }
201
201
 
202
- attributeChangedCallback(name: string, _oldValue: string, newValue: string) {
202
+ attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {
203
203
  super.attributeChangedCallback(name, _oldValue, newValue);
204
204
 
205
205
  switch (name) {
@@ -1,7 +1,7 @@
1
1
  import { FITTING_NONE, FITTING_STRETCH, FITTING_SHRINK, FITTING_BOTH, LayoutGroupComponent, ORIENTATION_HORIZONTAL, ORIENTATION_VERTICAL, Vec2, Vec4 } from 'playcanvas';
2
2
 
3
3
  import { ComponentElement } from './component';
4
- import { parseBool, parseEnum, parseVec2, parseVec4 } from '../utils';
4
+ import { parseBool, parseEnum, parseVec2, parseVec4 } from '../parse';
5
5
 
6
6
  const orientations = new Map<'horizontal' | 'vertical', number>([
7
7
  ['horizontal', ORIENTATION_HORIZONTAL],
@@ -258,7 +258,7 @@ class LayoutGroupComponentElement extends ComponentElement {
258
258
  ];
259
259
  }
260
260
 
261
- attributeChangedCallback(name: string, _oldValue: string, newValue: string) {
261
+ attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {
262
262
  super.attributeChangedCallback(name, _oldValue, newValue);
263
263
 
264
264
  switch (name) {
@@ -1,7 +1,7 @@
1
1
  import { Color, LightComponent, SHADOW_PCF1_16F, SHADOW_PCF1_32F, SHADOW_PCF3_16F, SHADOW_PCF3_32F, SHADOW_PCF5_16F, SHADOW_PCF5_32F, SHADOW_PCSS_32F, SHADOW_VSM_16F, SHADOW_VSM_32F } from 'playcanvas';
2
2
 
3
3
  import { ComponentElement } from './component';
4
- import { parseBool, parseColor, parseEnum, parseNumber } from '../utils';
4
+ import { parseBool, parseColor, parseEnum, parseNumber } from '../parse';
5
5
 
6
6
  const shadowTypes = new Map<'pcf1-16f' | 'pcf1-32f' | 'pcf3-16f' | 'pcf3-32f' | 'pcf5-16f' | 'pcf5-32f' | 'vsm-16f' | 'vsm-32f' | 'pcss-32f', number>([
7
7
  ['pcf1-16f', SHADOW_PCF1_16F],
@@ -496,7 +496,7 @@ class LightComponentElement extends ComponentElement {
496
496
  ];
497
497
  }
498
498
 
499
- attributeChangedCallback(name: string, _oldValue: string, newValue: string) {
499
+ attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {
500
500
  super.attributeChangedCallback(name, _oldValue, newValue);
501
501
 
502
502
  switch (name) {
@@ -139,12 +139,12 @@ class ParticleSystemComponentElement extends ComponentElement {
139
139
  ];
140
140
  }
141
141
 
142
- attributeChangedCallback(name: string, _oldValue: string, newValue: string) {
142
+ attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {
143
143
  super.attributeChangedCallback(name, _oldValue, newValue);
144
144
 
145
145
  switch (name) {
146
146
  case 'asset':
147
- this.asset = newValue;
147
+ this.asset = newValue ?? '';
148
148
  break;
149
149
  }
150
150
  }
@@ -2,7 +2,7 @@ import { RenderComponent, StandardMaterial } from 'playcanvas';
2
2
 
3
3
  import { ComponentElement } from './component';
4
4
  import { MaterialElement } from '../material';
5
- import { parseBool, parseEnum } from '../utils';
5
+ import { parseBool, parseEnum } from '../parse';
6
6
 
7
7
  /**
8
8
  * The RenderComponentElement interface provides properties and methods for manipulating
@@ -91,8 +91,13 @@ class RenderComponentElement extends ComponentElement {
91
91
  */
92
92
  set material(value: string) {
93
93
  this._material = value;
94
- if (this.component) {
95
- this.component.material = MaterialElement.get(value) as StandardMaterial;
94
+ const material = MaterialElement.get(value);
95
+ // Guarded like every other reference attribute in the library. Assigning an unresolved
96
+ // lookup used to write `undefined` straight through to every mesh instance, and the
97
+ // engine's MeshInstance setter takes that literally - it clears the material and skips
98
+ // the ref/transparency/key bookkeeping, leaving the mesh with no material at all.
99
+ if (this.component && material) {
100
+ this.component.material = material as StandardMaterial;
96
101
  }
97
102
  }
98
103
 
@@ -127,7 +132,7 @@ class RenderComponentElement extends ComponentElement {
127
132
  return [...super.observedAttributes, 'cast-shadows', 'material', 'receive-shadows', 'type'];
128
133
  }
129
134
 
130
- attributeChangedCallback(name: string, _oldValue: string, newValue: string) {
135
+ attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {
131
136
  super.attributeChangedCallback(name, _oldValue, newValue);
132
137
 
133
138
  switch (name) {
@@ -135,7 +140,7 @@ class RenderComponentElement extends ComponentElement {
135
140
  this.castShadows = parseBool(newValue, true);
136
141
  break;
137
142
  case 'material':
138
- this.material = newValue;
143
+ this.material = newValue ?? '';
139
144
  break;
140
145
  case 'receive-shadows':
141
146
  this.receiveShadows = parseBool(newValue, true);
@@ -1,7 +1,7 @@
1
1
  import { RigidBodyComponent, Vec3 } from 'playcanvas';
2
2
 
3
3
  import { ComponentElement } from './component';
4
- import { parseEnum, parseNumber, parseVec3 } from '../utils';
4
+ import { parseEnum, parseNumber, parseVec3 } from '../parse';
5
5
 
6
6
  /**
7
7
  * The RigidBodyComponentElement interface provides properties and methods for manipulating
@@ -187,7 +187,7 @@ class RigidBodyComponentElement extends ComponentElement {
187
187
  return [...super.observedAttributes, 'angular-damping', 'angular-factor', 'friction', 'linear-damping', 'linear-factor', 'mass', 'restitution', 'rolling-friction', 'type'];
188
188
  }
189
189
 
190
- attributeChangedCallback(name: string, _oldValue: string, newValue: string) {
190
+ attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {
191
191
  super.attributeChangedCallback(name, _oldValue, newValue);
192
192
 
193
193
  switch (name) {
@@ -1,7 +1,7 @@
1
1
  import { SCALEMODE_BLEND, SCALEMODE_NONE, ScreenComponent, Vec2 } from 'playcanvas';
2
2
 
3
3
  import { ComponentElement } from './component';
4
- import { parseBool, parseNumber, parseVec2 } from '../utils';
4
+ import { parseBool, parseNumber, parseVec2 } from '../parse';
5
5
 
6
6
  /**
7
7
  * The ScreenComponentElement interface provides properties and methods for manipulating
@@ -126,7 +126,7 @@ class ScreenComponentElement extends ComponentElement {
126
126
  ];
127
127
  }
128
128
 
129
- attributeChangedCallback(name: string, _oldValue: string, newValue: string) {
129
+ attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {
130
130
  super.attributeChangedCallback(name, _oldValue, newValue);
131
131
 
132
132
  switch (name) {
@@ -3,7 +3,7 @@ import { Color, Quat, ScriptComponent, Script, Vec2, Vec3, Vec4 } from 'playcanv
3
3
  import { AssetElement } from '../asset';
4
4
  import { ComponentElement } from './component';
5
5
  import { ScriptElement } from './script';
6
- import { getEntity, parseBool, parseColor, parseComponents, parseNumber, parseQuat, parseVec2, parseVec3, parseVec4 } from '../utils';
6
+ import { getEntity, parseBool, parseColor, parseComponents, parseNumber, parseQuat, parseVec2, parseVec3, parseVec4 } from '../parse';
7
7
 
8
8
  /**
9
9
  * Attributes on `pc-script` that never map to script attributes: the element's own API (derived
@@ -131,7 +131,7 @@ const colorConversion: Conversion = (rest, raw) => {
131
131
  };
132
132
 
133
133
  /**
134
- * The conversion prefixes recognised in script attribute values, mapped to the conversion each
134
+ * The conversion prefixes recognized in script attribute values, mapped to the conversion each
135
135
  * performs. These keys are the single source of truth for the prefix vocabulary: they drive both
136
136
  * the conversion in `convertAttributes` and the has-a-prefix test in `setScriptProperty`, so a
137
137
  * prefix added here is automatically known to both.
@@ -148,10 +148,10 @@ const CONVERSIONS = new Map<string, Conversion>([
148
148
  /**
149
149
  * Matches a value against the conversion prefixes. A prefix is the text before the first colon,
150
150
  * so a value whose remainder itself contains colons (`asset:a:b`) still resolves, and a value
151
- * with an unrecognised prefix (`https://...`) or no colon does not match.
151
+ * with an unrecognized prefix (`https://...`) or no colon does not match.
152
152
  * @param value - The value to inspect.
153
153
  * @returns The matching converter and the text after the prefix, or `null` if the value carries
154
- * no recognised prefix.
154
+ * no recognized prefix.
155
155
  */
156
156
  const matchConversion = (value: string) => {
157
157
  const index = value.indexOf(':');
@@ -1,7 +1,7 @@
1
1
  import { Script } from 'playcanvas';
2
2
 
3
3
  import { AsyncElement } from '../async-element';
4
- import { parseBool } from '../utils';
4
+ import { parseBool } from '../parse';
5
5
 
6
6
  /**
7
7
  * The ScriptElement interface provides properties and methods for manipulating
@@ -28,6 +28,13 @@ import { parseBool } from '../utils';
28
28
  *
29
29
  * The element becomes ready once its script instance has been created by the parent
30
30
  * `<pc-scripts>` element.
31
+ *
32
+ * @fires {CustomEvent} scriptattributeschange - Fired when the script's attributes change. The
33
+ * `detail` carries the new `attributes` object. Bubbles.
34
+ * @fires {CustomEvent} scriptenablechange - Fired when the script's enabled state changes. The
35
+ * `detail` carries the new `enabled` state. Bubbles.
36
+ * @fires {CustomEvent} scriptnamechange - Fired when the script is renamed on a live element. The
37
+ * `detail` carries `oldName` and `newName`. Bubbles.
31
38
  */
32
39
  class ScriptElement extends AsyncElement {
33
40
  private _attributes: Record<string, any> = {};
@@ -148,7 +155,7 @@ class ScriptElement extends AsyncElement {
148
155
  return ['attributes', 'enabled', 'name'];
149
156
  }
150
157
 
151
- attributeChangedCallback(name: string, oldValue: string, newValue: string) {
158
+ attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null) {
152
159
  switch (name) {
153
160
  case 'attributes':
154
161
  if (newValue === null) {
@@ -1,7 +1,7 @@
1
1
  import { ORIENTATION_HORIZONTAL, ORIENTATION_VERTICAL, ScrollbarComponent } from 'playcanvas';
2
2
 
3
3
  import { ComponentElement } from './component';
4
- import { getEntity, parseEnum, parseNumber } from '../utils';
4
+ import { getEntity, parseEnum, parseNumber } from '../parse';
5
5
 
6
6
  const orientations = new Map<'horizontal' | 'vertical', number>([
7
7
  ['horizontal', ORIENTATION_HORIZONTAL],
@@ -142,7 +142,7 @@ class ScrollbarComponentElement extends ComponentElement {
142
142
  ];
143
143
  }
144
144
 
145
- attributeChangedCallback(name: string, _oldValue: string, newValue: string) {
145
+ attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {
146
146
  super.attributeChangedCallback(name, _oldValue, newValue);
147
147
 
148
148
  switch (name) {
@@ -156,7 +156,7 @@ class ScrollbarComponentElement extends ComponentElement {
156
156
  this.handleSize = parseNumber(newValue, 0.5, name);
157
157
  break;
158
158
  case 'handle':
159
- this.handle = newValue;
159
+ this.handle = newValue ?? '';
160
160
  break;
161
161
  }
162
162
  }
@@ -1,7 +1,7 @@
1
1
  import { SCROLL_MODE_BOUNCE, SCROLL_MODE_CLAMP, SCROLL_MODE_INFINITE, SCROLLBAR_VISIBILITY_SHOW_ALWAYS, SCROLLBAR_VISIBILITY_SHOW_WHEN_REQUIRED, ScrollViewComponent, Vec2 } from 'playcanvas';
2
2
 
3
3
  import { ComponentElement } from './component';
4
- import { getEntity, parseBool, parseEnum, parseNumber, parseVec2 } from '../utils';
4
+ import { getEntity, parseBool, parseEnum, parseNumber, parseVec2 } from '../parse';
5
5
 
6
6
  const scrollModes = new Map<'clamp' | 'bounce' | 'infinite', number>([
7
7
  ['clamp', SCROLL_MODE_CLAMP],
@@ -377,7 +377,7 @@ class ScrollViewComponentElement extends ComponentElement {
377
377
  ];
378
378
  }
379
379
 
380
- attributeChangedCallback(name: string, _oldValue: string, newValue: string) {
380
+ attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {
381
381
  super.attributeChangedCallback(name, _oldValue, newValue);
382
382
 
383
383
  switch (name) {
@@ -409,16 +409,16 @@ class ScrollViewComponentElement extends ComponentElement {
409
409
  this.verticalScrollbarVisibility = parseEnum(newValue, visibilities, 'when-required', name);
410
410
  break;
411
411
  case 'viewport':
412
- this.viewport = newValue;
412
+ this.viewport = newValue ?? '';
413
413
  break;
414
414
  case 'content':
415
- this.content = newValue;
415
+ this.content = newValue ?? '';
416
416
  break;
417
417
  case 'horizontal-scrollbar':
418
- this.horizontalScrollbar = newValue;
418
+ this.horizontalScrollbar = newValue ?? '';
419
419
  break;
420
420
  case 'vertical-scrollbar':
421
- this.verticalScrollbar = newValue;
421
+ this.verticalScrollbar = newValue ?? '';
422
422
  break;
423
423
  }
424
424
  }
@@ -1,7 +1,7 @@
1
1
  import { SoundComponent } from 'playcanvas';
2
2
 
3
3
  import { ComponentElement } from './component';
4
- import { parseBool, parseEnum, parseNumber } from '../utils';
4
+ import { parseBool, parseEnum, parseNumber } from '../parse';
5
5
 
6
6
  /**
7
7
  * The SoundComponentElement interface provides properties and methods for manipulating
@@ -197,7 +197,7 @@ class SoundComponentElement extends ComponentElement {
197
197
  ];
198
198
  }
199
199
 
200
- attributeChangedCallback(name: string, _oldValue: string, newValue: string) {
200
+ attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {
201
201
  super.attributeChangedCallback(name, _oldValue, newValue);
202
202
 
203
203
  switch (name) {
@@ -3,7 +3,7 @@ import { SoundSlot } from 'playcanvas';
3
3
  import { AssetElement } from '../asset';
4
4
  import { AsyncElement } from '../async-element';
5
5
  import { SoundComponentElement } from './sound-component';
6
- import { parseBool, parseNumber } from '../utils';
6
+ import { parseBool, parseNumber } from '../parse';
7
7
 
8
8
  /**
9
9
  * The SoundSlotElement interface provides properties and methods for manipulating
@@ -29,13 +29,31 @@ class SoundSlotElement extends AsyncElement {
29
29
 
30
30
  private _volume: number = 1;
31
31
 
32
+ /**
33
+ * The `<pc-sounds>` this slot was added to, captured at connect time.
34
+ *
35
+ * `disconnectedCallback` cannot rediscover it: by the time the element is disconnected its
36
+ * `parentElement` is already `null`, so a lookup would both fail to find the component and
37
+ * emit a misleading "must be a direct child" warning for what is an ordinary removal.
38
+ */
39
+ private _soundElement: SoundComponentElement | null = null;
40
+
32
41
  /**
33
42
  * The sound slot.
34
43
  */
35
44
  soundSlot: SoundSlot | null = null;
36
45
 
37
46
  async connectedCallback() {
38
- await this.soundElement?.ready();
47
+ const soundElement = this.soundElement;
48
+ await soundElement?.ready();
49
+
50
+ // The element may have been removed, or its parent torn down, while we were waiting. A
51
+ // <pc-app> disconnects before its children, so by the time we resume the component can
52
+ // already be gone - see the matching guard in disconnectedCallback below.
53
+ const component = soundElement?.component;
54
+ if (!this.isConnected || !component) {
55
+ return;
56
+ }
39
57
 
40
58
  const options = {
41
59
  autoPlay: this._autoPlay,
@@ -49,7 +67,8 @@ class SoundSlotElement extends AsyncElement {
49
67
  options.duration = this._duration;
50
68
  }
51
69
 
52
- this.soundSlot = this.soundElement!.component!.addSlot(this._name, options);
70
+ this._soundElement = soundElement;
71
+ this.soundSlot = component.addSlot(this._name, options);
53
72
  this.asset = this._asset;
54
73
  if (this._autoPlay) {
55
74
  this.soundSlot!.play();
@@ -59,9 +78,12 @@ class SoundSlotElement extends AsyncElement {
59
78
  }
60
79
 
61
80
  disconnectedCallback() {
62
- // The component is null if the parent <pc-sound> (or the whole <pc-app>) is being
63
- // torn down parents disconnect first and have already removed the component.
64
- this.soundElement?.component?.removeSlot(this._name);
81
+ // Uses the cached parent rather than a fresh lookup, since parentElement is already null
82
+ // by now. The component itself is null if the parent <pc-sound> (or the whole <pc-app>) is
83
+ // being torn down — parents disconnect first and have already removed the component.
84
+ this._soundElement?.component?.removeSlot(this._name);
85
+ this._soundElement = null;
86
+ this.soundSlot = null;
65
87
  }
66
88
 
67
89
  protected get soundElement(): SoundComponentElement | null {
@@ -253,10 +275,10 @@ class SoundSlotElement extends AsyncElement {
253
275
  return ['asset', 'auto-play', 'duration', 'loop', 'name', 'overlap', 'pitch', 'start-time', 'volume'];
254
276
  }
255
277
 
256
- attributeChangedCallback(name: string, _oldValue: string, newValue: string) {
278
+ attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {
257
279
  switch (name) {
258
280
  case 'asset':
259
- this.asset = newValue;
281
+ this.asset = newValue ?? '';
260
282
  break;
261
283
  case 'auto-play':
262
284
  this.autoPlay = parseBool(newValue, false);
@@ -268,7 +290,7 @@ class SoundSlotElement extends AsyncElement {
268
290
  this.loop = parseBool(newValue, false);
269
291
  break;
270
292
  case 'name':
271
- this.name = newValue;
293
+ this.name = newValue ?? '';
272
294
  break;
273
295
  case 'overlap':
274
296
  this.overlap = parseBool(newValue, false);