@playcanvas/web-components 0.15.0 → 0.17.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 (108) hide show
  1. package/dist/app.d.cts +14 -13
  2. package/dist/app.d.ts +14 -13
  3. package/dist/async-element.d.cts +13 -13
  4. package/dist/async-element.d.ts +13 -13
  5. package/dist/components/anim-clip.d.cts +127 -0
  6. package/dist/components/anim-clip.d.ts +127 -0
  7. package/dist/components/anim-component.d.cts +235 -0
  8. package/dist/components/anim-component.d.ts +235 -0
  9. package/dist/components/{listener-component.d.cts → audio-listener-component.d.cts} +7 -5
  10. package/dist/components/{listener-component.d.ts → audio-listener-component.d.ts} +7 -5
  11. package/dist/components/button-component.d.cts +5 -1
  12. package/dist/components/button-component.d.ts +5 -1
  13. package/dist/components/camera-component.d.cts +2 -0
  14. package/dist/components/camera-component.d.ts +2 -0
  15. package/dist/components/collision-component.d.cts +5 -1
  16. package/dist/components/collision-component.d.ts +5 -1
  17. package/dist/components/component.d.cts +3 -2
  18. package/dist/components/component.d.ts +3 -2
  19. package/dist/components/element-component.d.cts +7 -0
  20. package/dist/components/element-component.d.ts +7 -0
  21. package/dist/components/gsplat-component.d.cts +2 -0
  22. package/dist/components/gsplat-component.d.ts +2 -0
  23. package/dist/components/joint-component.d.cts +2 -0
  24. package/dist/components/joint-component.d.ts +2 -0
  25. package/dist/components/{layoutchild-component.d.cts → layout-child-component.d.cts} +3 -1
  26. package/dist/components/{layoutchild-component.d.ts → layout-child-component.d.ts} +3 -1
  27. package/dist/components/{layoutgroup-component.d.cts → layout-group-component.d.cts} +3 -1
  28. package/dist/components/{layoutgroup-component.d.ts → layout-group-component.d.ts} +3 -1
  29. package/dist/components/light-component.d.cts +2 -0
  30. package/dist/components/light-component.d.ts +2 -0
  31. package/dist/components/{particlesystem-component.d.cts → particle-system-component.d.cts} +3 -1
  32. package/dist/components/{particlesystem-component.d.ts → particle-system-component.d.ts} +3 -1
  33. package/dist/components/render-component.d.cts +2 -0
  34. package/dist/components/render-component.d.ts +2 -0
  35. package/dist/components/{rigidbody-component.d.cts → rigid-body-component.d.cts} +3 -1
  36. package/dist/components/{rigidbody-component.d.ts → rigid-body-component.d.ts} +3 -1
  37. package/dist/components/screen-component.d.cts +2 -0
  38. package/dist/components/screen-component.d.ts +2 -0
  39. package/dist/components/script-component.d.cts +23 -12
  40. package/dist/components/script-component.d.ts +23 -12
  41. package/dist/components/{script.d.cts → script-instance.d.cts} +7 -7
  42. package/dist/components/{script.d.ts → script-instance.d.ts} +7 -7
  43. package/dist/components/{scrollview-component.d.cts → scroll-view-component.d.cts} +3 -1
  44. package/dist/components/{scrollview-component.d.ts → scroll-view-component.d.ts} +3 -1
  45. package/dist/components/scrollbar-component.d.cts +2 -0
  46. package/dist/components/scrollbar-component.d.ts +2 -0
  47. package/dist/components/sound-component.d.cts +3 -1
  48. package/dist/components/sound-component.d.ts +3 -1
  49. package/dist/components/sound-slot.d.cts +2 -2
  50. package/dist/components/sound-slot.d.ts +2 -2
  51. package/dist/custom-elements.json +3154 -2005
  52. package/dist/entity-base.d.cts +6 -5
  53. package/dist/entity-base.d.ts +6 -5
  54. package/dist/entity-owner.d.cts +118 -0
  55. package/dist/entity-owner.d.ts +118 -0
  56. package/dist/entity.d.cts +8 -101
  57. package/dist/entity.d.ts +8 -101
  58. package/dist/index.d.cts +26 -21
  59. package/dist/index.d.ts +26 -21
  60. package/dist/model.d.cts +67 -24
  61. package/dist/model.d.ts +67 -24
  62. package/dist/pwc.cjs +1766 -619
  63. package/dist/pwc.cjs.map +1 -1
  64. package/dist/pwc.js +1766 -619
  65. package/dist/pwc.js.map +1 -1
  66. package/dist/pwc.min.js +1 -1
  67. package/dist/pwc.min.js.map +1 -1
  68. package/dist/pwc.min.mjs +1 -1
  69. package/dist/pwc.min.mjs.map +1 -1
  70. package/dist/pwc.mjs +1762 -618
  71. package/dist/pwc.mjs.map +1 -1
  72. package/dist/vscode.html-custom-data.json +203 -89
  73. package/dist/{module.d.cts → wasm.d.cts} +6 -6
  74. package/dist/{module.d.ts → wasm.d.ts} +6 -6
  75. package/dist/web-types.json +556 -273
  76. package/package.json +7 -7
  77. package/src/app.ts +45 -35
  78. package/src/async-element.ts +14 -14
  79. package/src/components/anim-clip.ts +395 -0
  80. package/src/components/anim-component.ts +719 -0
  81. package/src/components/{listener-component.ts → audio-listener-component.ts} +8 -6
  82. package/src/components/button-component.ts +5 -1
  83. package/src/components/camera-component.ts +2 -0
  84. package/src/components/collision-component.ts +5 -1
  85. package/src/components/component.ts +4 -3
  86. package/src/components/element-component.ts +7 -0
  87. package/src/components/gsplat-component.ts +2 -0
  88. package/src/components/joint-component.ts +2 -0
  89. package/src/components/{layoutchild-component.ts → layout-child-component.ts} +4 -2
  90. package/src/components/{layoutgroup-component.ts → layout-group-component.ts} +4 -2
  91. package/src/components/light-component.ts +2 -0
  92. package/src/components/{particlesystem-component.ts → particle-system-component.ts} +4 -2
  93. package/src/components/render-component.ts +2 -0
  94. package/src/components/{rigidbody-component.ts → rigid-body-component.ts} +4 -2
  95. package/src/components/screen-component.ts +2 -0
  96. package/src/components/script-component.ts +63 -42
  97. package/src/components/{script.ts → script-instance.ts} +14 -14
  98. package/src/components/{scrollview-component.ts → scroll-view-component.ts} +4 -2
  99. package/src/components/scrollbar-component.ts +2 -0
  100. package/src/components/sound-component.ts +4 -2
  101. package/src/components/sound-slot.ts +4 -4
  102. package/src/entity-base.ts +6 -5
  103. package/src/entity-owner.ts +298 -0
  104. package/src/entity.ts +15 -270
  105. package/src/index.ts +32 -24
  106. package/src/model.ts +159 -79
  107. package/src/node.ts +9 -5
  108. package/src/{module.ts → wasm.ts} +8 -8
package/src/model.ts CHANGED
@@ -1,7 +1,10 @@
1
1
  import type { ContainerResource, Entity, EventHandle } from 'playcanvas';
2
+ import { Vec3 } from 'playcanvas';
2
3
 
3
4
  import { useAsset } from './asset';
4
- import { AsyncElement } from './async-element';
5
+ import { POINTER_ATTRIBUTES } from './entity-base';
6
+ import { buildDescendantEntities, EntityOwnerElement } from './entity-owner';
7
+ import { parseBool, parseTags, parseVec3 } from './parse';
5
8
 
6
9
  /**
7
10
  * One material assignment of a {@link HierarchyNode} with a render component: a mesh instance's
@@ -104,14 +107,42 @@ const formatHierarchy = (root: HierarchyNode, counts: ReadonlyMap<string, number
104
107
  * The ModelElement interface also inherits the properties and methods of the
105
108
  * {@link HTMLElement} interface.
106
109
  *
107
- * The element becomes ready once its container asset has loaded and the instantiated hierarchy has
108
- * been added to the scene `entity` is non-null by then. A failed load also settles readiness,
109
- * with `entity` remaining `null`: readiness means the load settled, not that it succeeded — listen
110
- * for `error`, or check `entity`, to tell the outcomes apart. Changing `asset` re-arms readiness
111
- * and instantiates anew, so a `ready()` obtained after the change resolves against the new
112
- * hierarchy. A `pc-model` outside a `pc-app`, or referencing an unknown asset id, warns and never
113
- * becomes ready.
110
+ * The element creates and fronts a stable host entity: `entity` is that host, created when the
111
+ * application builds its hierarchy and kept across `asset` changes, so the element's transform
112
+ * and tags are instance placement that composes with whatever transform the asset authored on
113
+ * its root. The instantiated content is parented beneath the host and exposed as
114
+ * {@link contentEntity}.
114
115
  *
116
+ * The element becomes ready once its current asset selection has settled: the container asset
117
+ * has loaded and its content root has been parented beneath the host, the load has failed
118
+ * (`contentEntity` stays `null` — listen for `error`, or check `contentEntity`, to tell the
119
+ * outcomes apart), or no asset is assigned at all. Changing `asset` re-arms readiness and
120
+ * instantiates anew, so a `ready()` obtained after the change resolves against the new content.
121
+ * A `pc-model` outside a `pc-app`, or referencing an unknown asset id, warns and never becomes
122
+ * ready.
123
+ *
124
+ * The pointer events below are dispatched by the containing `<pc-app>` element when the pointer
125
+ * intersects the model's geometry, exactly as for `<pc-entity>` — a hit on a content node that no
126
+ * `pc-node` fronts resolves to this element.
127
+ *
128
+ * @attribute {boolean} enabled - The enabled state of the model.
129
+ * @attribute {string} name - The name of the model.
130
+ * @attribute {string} position - The position of the model.
131
+ * @attribute {string} rotation - The rotation of the model.
132
+ * @attribute {string} scale - The scale of the model.
133
+ * @attribute {string} tags - The tags of the model.
134
+ * @attribute {string} onpointerenter - Script to run when the pointer moves onto the model.
135
+ * @attribute {string} onpointerleave - Script to run when the pointer moves off the model.
136
+ * @attribute {string} onpointermove - Script to run when the pointer moves over the model.
137
+ * @attribute {string} onpointerdown - Script to run when a pointer button is pressed over the
138
+ * model.
139
+ * @attribute {string} onpointerup - Script to run when a pointer button is released over the
140
+ * model.
141
+ * @fires {PointerEvent} pointerenter - Fired when the pointer moves onto the model.
142
+ * @fires {PointerEvent} pointerleave - Fired when the pointer moves off the model.
143
+ * @fires {PointerEvent} pointermove - Fired when the pointer moves over the model.
144
+ * @fires {PointerEvent} pointerdown - Fired when a pointer button is pressed over the model.
145
+ * @fires {PointerEvent} pointerup - Fired when a pointer button is released over the model.
115
146
  * @fires {Event} load - Fired each time a container asset finishes instantiating, including
116
147
  * re-instantiation after `asset` changes. Does not bubble — listen on this element, or use a
117
148
  * capture-phase listener on an ancestor.
@@ -119,16 +150,16 @@ const formatHierarchy = (root: HierarchyNode, counts: ReadonlyMap<string, number
119
150
  * error in `message`. Does not bubble. The element still becomes ready — readiness means the load
120
151
  * settled, not that it succeeded.
121
152
  */
122
- class ModelElement extends AsyncElement {
153
+ class ModelElement extends EntityOwnerElement {
123
154
  private _asset = '';
124
155
 
125
- private _entity: Entity | null = null;
156
+ private _contentEntity: Entity | null = null;
126
157
 
127
158
  /**
128
- * Incremented on every new load and on disconnect, and captured by a load when it starts. A
129
- * load that resumes from an await or a load callback abandons itself if the value has moved
130
- * on, so a superseded load can neither instantiate a second entity nor parent one that has
131
- * since been destroyed.
159
+ * Incremented on every new load, on disconnect, and when the host entity dies, and captured
160
+ * by a load when it starts. A load that resumes from an await or a load callback abandons
161
+ * itself if the value has moved on, so a superseded load can neither instantiate a second
162
+ * content root nor parent one under a host a newer cycle has already replaced.
132
163
  */
133
164
  private _loadGeneration = 0;
134
165
 
@@ -142,12 +173,13 @@ class ModelElement extends AsyncElement {
142
173
  private _errorHandle: EventHandle | null = null;
143
174
 
144
175
  /**
145
- * The root entity of the instantiated model. `null` until the container asset has loaded
146
- * and been instantiated, and again once the element has been removed from the document.
147
- * @returns The model's root entity, or `null`.
176
+ * The root entity of the instantiated model content, parented beneath the host entity.
177
+ * `null` until the container asset has loaded and been instantiated, after a failed load,
178
+ * and again once the element has been removed from the document.
179
+ * @returns The content root entity, or `null`.
148
180
  */
149
- get entity(): Entity | null {
150
- return this._entity;
181
+ get contentEntity(): Entity | null {
182
+ return this._contentEntity;
151
183
  }
152
184
 
153
185
  /**
@@ -160,12 +192,13 @@ class ModelElement extends AsyncElement {
160
192
  * is the printable form.
161
193
  *
162
194
  * The snapshot is plain data, computed afresh each call: it does not follow later changes
163
- * to the hierarchy, and mutating it changes nothing.
195
+ * to the hierarchy, and mutating it changes nothing. It covers the instantiated content
196
+ * only — the host entity the element fronts is not part of the asset's node tree.
164
197
  *
165
198
  * @returns The root of the instantiated node tree, or `null`.
166
199
  */
167
200
  hierarchy(): HierarchyNode | null {
168
- const root = this._entity;
201
+ const root = this._contentEntity;
169
202
  if (!root) {
170
203
  return null;
171
204
  }
@@ -214,19 +247,59 @@ class ModelElement extends AsyncElement {
214
247
  // A model outside an application is inert and never becomes ready, so awaiting it hangs.
215
248
  // Warn rather than fail silently, naming the parent it requires, as every other misplaced
216
249
  // element does.
217
- if (!this.closestApp) {
250
+ const closestApp = this.closestApp;
251
+ if (!closestApp) {
218
252
  const label = this._asset ? ` '${this._asset}'` : '';
219
253
  console.warn(`pc-model${label} must be a descendant of pc-app - model not created`);
220
254
  return;
221
255
  }
222
- this._loadModel();
256
+
257
+ // If the app is already running, create the host immediately; during a boot, the app's
258
+ // own sweep does it. Either way, _onBuilt starts the content load once the host is
259
+ // parented.
260
+ if (closestApp._hierarchyReady) {
261
+ const app = closestApp.app!;
262
+
263
+ this._createEntity(app);
264
+ this._buildHierarchy(app);
265
+
266
+ // A build that deferred (an unresolved pc-node above) defers the whole subtree with
267
+ // it - the node's bind sweeps it.
268
+ if (this._built) {
269
+ buildDescendantEntities(this, app);
270
+ }
271
+ }
223
272
  }
224
273
 
225
274
  disconnectedCallback() {
275
+ // Destroying the host destroys the instantiated content with it, and the destroy hook
276
+ // resets the element. The generation guard comes first so a load suspended on an await
277
+ // cannot resume against the torn-down element.
226
278
  this._loadGeneration++;
227
279
  this._detachLoadHandlers();
228
- this._unloadModel();
229
- this._resetReady();
280
+ this._entity?.destroy();
281
+ }
282
+
283
+ /**
284
+ * Starts (or restarts) the content load once the host has been parented. Readiness is not
285
+ * announced here — it tracks the content settling, not the host entering the scene graph.
286
+ */
287
+ protected override _onBuilt() {
288
+ this._loadContent();
289
+ }
290
+
291
+ /**
292
+ * Extends the owner reset for the content: the engine's destroy cascade has already taken
293
+ * the content root down with the host subtree, so only the reference and the in-flight load
294
+ * are dropped here. The next build re-creates the host and re-instantiates the content.
295
+ *
296
+ * @param entity - The host entity that was destroyed.
297
+ */
298
+ protected override _onEntityDestroy(entity: Entity) {
299
+ this._loadGeneration++;
300
+ this._detachLoadHandlers();
301
+ this._contentEntity = null;
302
+ super._onEntityDestroy(entity);
230
303
  }
231
304
 
232
305
  private _detachLoadHandlers() {
@@ -237,9 +310,9 @@ class ModelElement extends AsyncElement {
237
310
  }
238
311
 
239
312
  /**
240
- * Resolves readiness and dispatches the `load` event. Called once the instantiated hierarchy
241
- * has been parented — readiness means "in the scene graph", matching `pc-entity`, so a ready
242
- * model's entity always has world transforms.
313
+ * Resolves readiness and dispatches the `load` event. Called once the instantiated content
314
+ * has been parented beneath the host the host itself is already in the scene graph by
315
+ * then, so a ready model's content always has world transforms.
243
316
  */
244
317
  private _announceLoad() {
245
318
  this._onReady();
@@ -247,53 +320,29 @@ class ModelElement extends AsyncElement {
247
320
  }
248
321
 
249
322
  private _instantiate(container: ContainerResource) {
250
- const generation = this._loadGeneration;
251
-
252
- const entity = container.instantiateRenderEntity();
253
- this._entity = entity;
254
-
255
- // @ts-ignore
256
- if (container.animations.length > 0) {
257
- entity.addComponent('anim');
258
- // @ts-ignore
259
- entity.anim.assignAnimation('animation', container.animations[0].resource);
260
- }
323
+ const content = container.instantiateRenderEntity();
324
+ this._contentEntity = content;
325
+ this._entity!.addChild(content);
326
+ this._announceLoad();
327
+ }
261
328
 
262
- // The parent's readiness re-arms when it is torn down, so these can resume in a later
263
- // connection cycle. The entity is captured above and the generation re-checked, so a
264
- // stale resume cannot parent an entity a newer cycle has already destroyed.
265
- const parentEntityElement = this.closestEntity;
266
- if (parentEntityElement) {
267
- parentEntityElement.ready().then(() => {
268
- if (generation !== this._loadGeneration) {
269
- return;
270
- }
271
- parentEntityElement.entity!.addChild(entity);
272
- this._announceLoad();
273
- });
274
- } else {
275
- const appElement = this.closestApp;
276
- if (appElement) {
277
- appElement.ready().then(() => {
278
- if (generation !== this._loadGeneration) {
279
- return;
280
- }
281
- appElement.app!.root.addChild(entity);
282
- this._announceLoad();
283
- });
284
- }
285
- }
329
+ private _destroyContent() {
330
+ this._contentEntity?.destroy();
331
+ this._contentEntity = null;
286
332
  }
287
333
 
288
- private async _loadModel() {
289
- this._unloadModel();
334
+ private async _loadContent() {
335
+ // The old content goes down synchronously, so a reader that checks after an asset change
336
+ // never sees the outgoing hierarchy. The host survives - components and child entities
337
+ // attached to it carry over to the new content.
338
+ this._destroyContent();
290
339
 
291
340
  // Supersede any load already in flight - only the newest load may instantiate
292
341
  const generation = ++this._loadGeneration;
293
342
  this._detachLoadHandlers();
294
343
 
295
344
  // Re-arm readiness so a waiter obtained after an asset change resolves against the new
296
- // hierarchy. A no-op on first connection, where readiness is still pending.
345
+ // content. A no-op on first connection, where readiness is still pending.
297
346
  this._resetReady();
298
347
 
299
348
  const appElement = this.closestApp;
@@ -309,14 +358,25 @@ class ModelElement extends AsyncElement {
309
358
  return;
310
359
  }
311
360
 
361
+ // The host may not be parented yet - a model under a pc-node that has not bound, reached
362
+ // through the asset setter. Nothing settles here: _onBuilt re-runs this load once the
363
+ // host builds.
364
+ if (!this._entity || !this._built) {
365
+ return;
366
+ }
367
+
368
+ if (this._asset === '') {
369
+ // No asset assigned is a settled selection: the element is a usable host (components
370
+ // attach, waiters resolve) with no content. Assigning an asset later re-arms.
371
+ this._onReady();
372
+ return;
373
+ }
374
+
312
375
  const asset = useAsset(this._asset);
313
376
  if (!asset) {
314
- // An empty id is a legitimate transient (the asset may be assigned later); a
315
- // non-empty one that resolves to nothing is a dead end - say so rather than staying
377
+ // A non-empty id that resolves to nothing is a dead end - say so rather than staying
316
378
  // silently pending.
317
- if (this._asset) {
318
- console.warn(`pc-model could not find asset '${this._asset}' - model not created`);
319
- }
379
+ console.warn(`pc-model could not find asset '${this._asset}' - model not created`);
320
380
  return;
321
381
  }
322
382
 
@@ -338,7 +398,7 @@ class ModelElement extends AsyncElement {
338
398
  if (generation !== this._loadGeneration) {
339
399
  return;
340
400
  }
341
- // A failed load settles readiness with a null entity, mirroring pc-asset:
401
+ // A failed load settles readiness with a null contentEntity, mirroring pc-asset:
342
402
  // readiness means the load settled, not that it succeeded.
343
403
  this.dispatchEvent(
344
404
  new ErrorEvent('error', {
@@ -350,11 +410,6 @@ class ModelElement extends AsyncElement {
350
410
  }
351
411
  }
352
412
 
353
- private _unloadModel() {
354
- this._entity?.destroy();
355
- this._entity = null;
356
- }
357
-
358
413
  /**
359
414
  * Sets the id of the `pc-asset` to use for the model.
360
415
  * @param value - The asset ID.
@@ -362,7 +417,7 @@ class ModelElement extends AsyncElement {
362
417
  set asset(value: string) {
363
418
  this._asset = value;
364
419
  if (this.isConnected) {
365
- this._loadModel();
420
+ this._loadContent();
366
421
  }
367
422
  }
368
423
 
@@ -375,7 +430,7 @@ class ModelElement extends AsyncElement {
375
430
  }
376
431
 
377
432
  static get observedAttributes() {
378
- return ['asset'];
433
+ return ['asset', 'enabled', 'name', 'position', 'rotation', 'scale', 'tags', ...POINTER_ATTRIBUTES];
379
434
  }
380
435
 
381
436
  attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {
@@ -383,6 +438,31 @@ class ModelElement extends AsyncElement {
383
438
  case 'asset':
384
439
  this.asset = newValue ?? '';
385
440
  break;
441
+ case 'enabled':
442
+ this.enabled = parseBool(newValue, true);
443
+ break;
444
+ case 'name':
445
+ this.name = newValue ?? 'Untitled';
446
+ break;
447
+ case 'position':
448
+ this.position = parseVec3(newValue, Vec3.ZERO, name);
449
+ break;
450
+ case 'rotation':
451
+ this.rotation = parseVec3(newValue, Vec3.ZERO, name);
452
+ break;
453
+ case 'scale':
454
+ this.scale = parseVec3(newValue, Vec3.ONE, name);
455
+ break;
456
+ case 'tags':
457
+ this.tags = parseTags(newValue);
458
+ break;
459
+ case 'onpointerenter':
460
+ case 'onpointerleave':
461
+ case 'onpointerdown':
462
+ case 'onpointerup':
463
+ case 'onpointermove':
464
+ this._updateInlineHandler(name, newValue);
465
+ break;
386
466
  }
387
467
  }
388
468
  }
package/src/node.ts CHANGED
@@ -2,9 +2,9 @@ import type { Entity, EventHandle, GraphNode, Material, MeshInstance, Quat, Rend
2
2
  import { Vec3 } from 'playcanvas';
3
3
 
4
4
  import { ComponentElement } from './components/component';
5
- import { buildDescendantEntities } from './entity';
6
- import type { EntityElement } from './entity';
7
5
  import { EntityBaseElement, POINTER_ATTRIBUTES } from './entity-base';
6
+ import { buildDescendantEntities } from './entity-owner';
7
+ import type { EntityOwnerElement } from './entity-owner';
8
8
  import { MaterialElement } from './material';
9
9
  import { ModelElement } from './model';
10
10
  import { parseBool, parseTags, parseVec3 } from './parse';
@@ -318,7 +318,10 @@ class NodeElement extends EntityBaseElement {
318
318
  * is retained untouched — a redundant edit must not flicker overrides through a revert.
319
319
  */
320
320
  private _rebind() {
321
- const hostEntity = this._host?.entity ?? null;
321
+ // A model fronts a host entity of its own; the names this element resolves are the
322
+ // asset's, so the search starts at the instantiated content root, not the wrapper.
323
+ const host = this._host;
324
+ const hostEntity = (host instanceof ModelElement ? host.contentEntity : host?.entity) ?? null;
322
325
 
323
326
  if (!hostEntity || !this._name) {
324
327
  // Host not instantiated (or nothing to look up yet): return to pending. An assigned
@@ -435,8 +438,9 @@ class NodeElement extends EntityBaseElement {
435
438
  this._revertOverrides();
436
439
 
437
440
  // Attachment points anchor to the bound node, so they cannot outlive the binding. Each
438
- // destroyed entity resets its element, which the next _buildChildren re-creates.
439
- this.querySelectorAll<EntityElement>('pc-entity').forEach((child) => {
441
+ // destroyed entity resets its element, which the next _buildChildren re-creates - a
442
+ // model host among them re-instantiates its content when it rebuilds.
443
+ this.querySelectorAll<EntityOwnerElement>('pc-entity, pc-model').forEach((child) => {
440
444
  if (child.closestEntity === this) {
441
445
  child.entity?.destroy();
442
446
  }
@@ -3,9 +3,9 @@ import { basisInitialize, WasmModule } from 'playcanvas';
3
3
  import { AsyncElement } from './async-element';
4
4
 
5
5
  /**
6
- * The ModuleElement interface provides properties and methods for manipulating
7
- * {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-module/ | `<pc-module>`}
8
- * elements. The ModuleElement interface also inherits the properties and methods of the
6
+ * The WasmElement interface provides properties and methods for manipulating
7
+ * {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-wasm/ | `<pc-wasm>`}
8
+ * elements. The WasmElement interface also inherits the properties and methods of the
9
9
  * {@link AsyncElement} interface.
10
10
  *
11
11
  * The attributes are read once, when the module starts loading - on the element's first
@@ -14,7 +14,7 @@ import { AsyncElement } from './async-element';
14
14
  * modules configure engine-global state that never unloads, so readiness is not re-armed by
15
15
  * removing the element, and a re-inserted element does not load again.
16
16
  *
17
- * A `<pc-module>` without a `name` warns and never becomes ready; a containing `<pc-app>` still
17
+ * A `<pc-wasm>` without a `name` warns and never becomes ready; a containing `<pc-app>` still
18
18
  * boots.
19
19
  *
20
20
  * @attribute {string} name - The name of the WebAssembly module to configure, e.g. `Basis` or
@@ -24,7 +24,7 @@ import { AsyncElement } from './async-element';
24
24
  * @attribute {string} fallback - The URL of the module's asm.js fallback script, used when
25
25
  * WebAssembly is unavailable.
26
26
  */
27
- class ModuleElement extends AsyncElement {
27
+ class WasmElement extends AsyncElement {
28
28
  private _loadPromise: Promise<void> | null = null;
29
29
 
30
30
  connectedCallback() {
@@ -34,7 +34,7 @@ class ModuleElement extends AsyncElement {
34
34
  private async _loadModule(): Promise<void> {
35
35
  const name = this.getAttribute('name');
36
36
  if (!name) {
37
- console.warn("pc-module requires a 'name' attribute - no module was configured");
37
+ console.warn("pc-wasm requires a 'name' attribute - no module was configured");
38
38
  return;
39
39
  }
40
40
 
@@ -74,6 +74,6 @@ class ModuleElement extends AsyncElement {
74
74
  }
75
75
  }
76
76
 
77
- customElements.define('pc-module', ModuleElement);
77
+ customElements.define('pc-wasm', WasmElement);
78
78
 
79
- export { ModuleElement };
79
+ export { WasmElement };