@playcanvas/web-components 0.19.0 → 0.20.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 (38) hide show
  1. package/dist/app.d.cts +14 -0
  2. package/dist/app.d.ts +14 -0
  3. package/dist/components/button-component.d.cts +9 -5
  4. package/dist/components/button-component.d.ts +9 -5
  5. package/dist/components/joint-component.d.cts +24 -10
  6. package/dist/components/joint-component.d.ts +24 -10
  7. package/dist/components/script-component.d.cts +4 -2
  8. package/dist/components/script-component.d.ts +4 -2
  9. package/dist/components/script-instance.d.cts +14 -6
  10. package/dist/components/script-instance.d.ts +14 -6
  11. package/dist/components/scroll-view-component.d.cts +24 -12
  12. package/dist/components/scroll-view-component.d.ts +24 -12
  13. package/dist/components/scrollbar-component.d.cts +6 -3
  14. package/dist/components/scrollbar-component.d.ts +6 -3
  15. package/dist/custom-elements.json +21 -21
  16. package/dist/parse.d.cts +7 -2
  17. package/dist/parse.d.ts +7 -2
  18. package/dist/pwc.cjs +406 -128
  19. package/dist/pwc.cjs.map +1 -1
  20. package/dist/pwc.js +406 -128
  21. package/dist/pwc.js.map +1 -1
  22. package/dist/pwc.min.js +1 -1
  23. package/dist/pwc.min.js.map +1 -1
  24. package/dist/pwc.min.mjs +1 -1
  25. package/dist/pwc.min.mjs.map +1 -1
  26. package/dist/pwc.mjs +406 -128
  27. package/dist/pwc.mjs.map +1 -1
  28. package/dist/vscode.html-custom-data.json +10 -10
  29. package/dist/web-types.json +20 -20
  30. package/package.json +3 -3
  31. package/src/app.ts +76 -41
  32. package/src/components/button-component.ts +18 -10
  33. package/src/components/joint-component.ts +29 -15
  34. package/src/components/script-component.ts +25 -12
  35. package/src/components/script-instance.ts +14 -6
  36. package/src/components/scroll-view-component.ts +49 -29
  37. package/src/components/scrollbar-component.ts +13 -8
  38. package/src/parse.ts +213 -16
@@ -1153,7 +1153,7 @@
1153
1153
  },
1154
1154
  {
1155
1155
  "name": "image",
1156
- "description": "The reference to the `<pc-entity>` whose image element is used for visual transitions.",
1156
+ "description": "The reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#` selector) to the entity whose image element is used for visual transitions, or empty for the button's own entity.",
1157
1157
  "values": []
1158
1158
  },
1159
1159
  {
@@ -1648,12 +1648,12 @@
1648
1648
  },
1649
1649
  {
1650
1650
  "name": "entity-a",
1651
- "description": "The reference to the `<pc-entity>` providing the first constrained body.",
1651
+ "description": "The reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#` selector) to the element providing the first constrained body.",
1652
1652
  "values": []
1653
1653
  },
1654
1654
  {
1655
1655
  "name": "entity-b",
1656
- "description": "The reference to the `<pc-entity>` providing the second constrained body.",
1656
+ "description": "The reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#` selector) to the element providing the second constrained body, or empty for the world-space case.",
1657
1657
  "values": []
1658
1658
  },
1659
1659
  {
@@ -2192,11 +2192,11 @@
2192
2192
  },
2193
2193
  {
2194
2194
  "name": "pc-script-instance",
2195
- "description": "The `<pc-script-instance>` element attaches one script class, named by `name`, to\nthe entity of its parent `<pc-script>`. Its other attributes set script attributes of the same\nname, and `attributes` takes a JSON object instead. Must be a direct child of `<pc-script>`.\n\n---\n\n\n### **Events:**\n - **ready** - Fired when the element is fully initialized — once per readiness cycle, so an element that is torn down and re-initialized (for example by removing and re-inserting it) fires it again. Bubbles and is composed.\n- **scriptattributeschange** - Fired when the script's attributes change. The `detail` carries the new `attributes` object. Bubbles.\n- **scriptenablechange** - Fired when the script's enabled state changes. The `detail` carries the new `enabled` state. Bubbles.\n- **scriptnamechange** - Fired when the script is renamed on a live element. The `detail` carries `oldName` and `newName`. Bubbles.",
2195
+ "description": "The `<pc-script-instance>` element attaches one script class, named by `name`, to\nthe entity of its parent `<pc-script>`. Its other attributes set script attributes of the same\nname, and `attributes` takes a JSON object instead. An `entity:` value is an entity name —\nwrite `entity:#id` for an element id. Must be a direct child of `<pc-script>`.\n\n---\n\n\n### **Events:**\n - **ready** - Fired when the element is fully initialized — once per readiness cycle, so an element that is torn down and re-initialized (for example by removing and re-inserting it) fires it again. Bubbles and is composed.\n- **scriptattributeschange** - Fired when the script's attributes change. The `detail` carries the new `attributes` object. Bubbles.\n- **scriptenablechange** - Fired when the script's enabled state changes. The `detail` carries the new `enabled` state. Bubbles.\n- **scriptnamechange** - Fired when the script is renamed on a live element. The `detail` carries `oldName` and `newName`. Bubbles.",
2196
2196
  "attributes": [
2197
2197
  {
2198
2198
  "name": "attributes",
2199
- "description": "The attributes of the script.",
2199
+ "description": "The attributes of the script as an object whose `asset:`, `entity:`, `vec2:`, `vec3:`, `vec4:` and `color:` prefixed values are resolved when applied — an `entity:` value being an entity name (nearest enclosing entity first, then outward, then the document) or a document-wide `#` selector (`entity:#id`), never a bare element id.",
2200
2200
  "values": []
2201
2201
  },
2202
2202
  {
@@ -2228,7 +2228,7 @@
2228
2228
  },
2229
2229
  {
2230
2230
  "name": "content",
2231
- "description": "The reference to the `<pc-entity>` used as the content.",
2231
+ "description": "The reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#` selector) to the entity used as the content.",
2232
2232
  "values": []
2233
2233
  },
2234
2234
  {
@@ -2244,7 +2244,7 @@
2244
2244
  },
2245
2245
  {
2246
2246
  "name": "horizontal-scrollbar",
2247
- "description": "The reference to the `<pc-entity>` containing the horizontal scrollbar.",
2247
+ "description": "The reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#` selector) to the entity containing the horizontal scrollbar.",
2248
2248
  "values": []
2249
2249
  },
2250
2250
  {
@@ -2278,7 +2278,7 @@
2278
2278
  },
2279
2279
  {
2280
2280
  "name": "vertical-scrollbar",
2281
- "description": "The reference to the `<pc-entity>` containing the vertical scrollbar.",
2281
+ "description": "The reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#` selector) to the entity containing the vertical scrollbar.",
2282
2282
  "values": []
2283
2283
  },
2284
2284
  {
@@ -2288,7 +2288,7 @@
2288
2288
  },
2289
2289
  {
2290
2290
  "name": "viewport",
2291
- "description": "The reference to the `<pc-entity>` used as the viewport.",
2291
+ "description": "The reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#` selector) to the entity used as the viewport.",
2292
2292
  "values": []
2293
2293
  }
2294
2294
  ],
@@ -2310,7 +2310,7 @@
2310
2310
  },
2311
2311
  {
2312
2312
  "name": "handle",
2313
- "description": "The reference to the `<pc-entity>` used as the scrollbar handle.",
2313
+ "description": "The reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#` selector) to the entity used as the scrollbar handle.",
2314
2314
  "values": []
2315
2315
  },
2316
2316
  {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://raw.githubusercontent.com/JetBrains/web-types/master/schema/web-types.json",
3
3
  "name": "@playcanvas/web-components",
4
- "version": "0.19.0",
4
+ "version": "0.20.0",
5
5
  "description-markup": "markdown",
6
6
  "contributions": {
7
7
  "html": {
@@ -2253,7 +2253,7 @@
2253
2253
  },
2254
2254
  {
2255
2255
  "name": "image",
2256
- "description": "The reference to the `<pc-entity>` whose image element is used for visual transitions.",
2256
+ "description": "The reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#` selector) to the entity whose image element is used for visual transitions, or empty for the button's own entity.",
2257
2257
  "value": { "type": "string" }
2258
2258
  },
2259
2259
  {
@@ -2346,7 +2346,7 @@
2346
2346
  },
2347
2347
  {
2348
2348
  "name": "image",
2349
- "description": "Gets the reference to the `<pc-entity>` whose image element is used for visual transitions."
2349
+ "description": "Gets the reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#`\nselector) to the entity whose image element is used for visual transitions, or empty for\nthe button's own entity."
2350
2350
  },
2351
2351
  {
2352
2352
  "name": "inactiveSpriteAsset",
@@ -3199,12 +3199,12 @@
3199
3199
  },
3200
3200
  {
3201
3201
  "name": "entity-a",
3202
- "description": "The reference to the `<pc-entity>` providing the first constrained body.",
3202
+ "description": "The reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#` selector) to the element providing the first constrained body.",
3203
3203
  "value": { "type": "string" }
3204
3204
  },
3205
3205
  {
3206
3206
  "name": "entity-b",
3207
- "description": "The reference to the `<pc-entity>` providing the second constrained body.",
3207
+ "description": "The reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#` selector) to the element providing the second constrained body, or empty for the world-space case.",
3208
3208
  "value": { "type": "string" }
3209
3209
  },
3210
3210
  {
@@ -3383,11 +3383,11 @@
3383
3383
  },
3384
3384
  {
3385
3385
  "name": "entityA",
3386
- "description": "Gets the reference to the `<pc-entity>` providing the first constrained body."
3386
+ "description": "Gets the reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#`\nselector) to the element providing the first constrained body."
3387
3387
  },
3388
3388
  {
3389
3389
  "name": "entityB",
3390
- "description": "Gets the reference to the `<pc-entity>` providing the second constrained body."
3390
+ "description": "Gets the reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#`\nselector) to the element providing the second constrained body, or empty for the\nworld-space case."
3391
3391
  },
3392
3392
  {
3393
3393
  "name": "limits",
@@ -4352,12 +4352,12 @@
4352
4352
  },
4353
4353
  {
4354
4354
  "name": "pc-script-instance",
4355
- "description": "The `<pc-script-instance>` element attaches one script class, named by `name`, to\nthe entity of its parent `<pc-script>`. Its other attributes set script attributes of the same\nname, and `attributes` takes a JSON object instead. Must be a direct child of `<pc-script>`.\n\n---\n\n\n### **Events:**\n - **ready** - Fired when the element is fully initialized — once per readiness cycle, so an element that is torn down and re-initialized (for example by removing and re-inserting it) fires it again. Bubbles and is composed.\n- **scriptattributeschange** - Fired when the script's attributes change. The `detail` carries the new `attributes` object. Bubbles.\n- **scriptenablechange** - Fired when the script's enabled state changes. The `detail` carries the new `enabled` state. Bubbles.\n- **scriptnamechange** - Fired when the script is renamed on a live element. The `detail` carries `oldName` and `newName`. Bubbles.",
4355
+ "description": "The `<pc-script-instance>` element attaches one script class, named by `name`, to\nthe entity of its parent `<pc-script>`. Its other attributes set script attributes of the same\nname, and `attributes` takes a JSON object instead. An `entity:` value is an entity name —\nwrite `entity:#id` for an element id. Must be a direct child of `<pc-script>`.\n\n---\n\n\n### **Events:**\n - **ready** - Fired when the element is fully initialized — once per readiness cycle, so an element that is torn down and re-initialized (for example by removing and re-inserting it) fires it again. Bubbles and is composed.\n- **scriptattributeschange** - Fired when the script's attributes change. The `detail` carries the new `attributes` object. Bubbles.\n- **scriptenablechange** - Fired when the script's enabled state changes. The `detail` carries the new `enabled` state. Bubbles.\n- **scriptnamechange** - Fired when the script is renamed on a live element. The `detail` carries `oldName` and `newName`. Bubbles.",
4356
4356
  "doc-url": "https://developer.playcanvas.com/user-manual/web-components/tags/pc-script-instance/",
4357
4357
  "attributes": [
4358
4358
  {
4359
4359
  "name": "attributes",
4360
- "description": "The attributes of the script.",
4360
+ "description": "The attributes of the script as an object whose `asset:`, `entity:`, `vec2:`, `vec3:`, `vec4:` and `color:` prefixed values are resolved when applied — an `entity:` value being an entity name (nearest enclosing entity first, then outward, then the document) or a document-wide `#` selector (`entity:#id`), never a bare element id.",
4361
4361
  "value": { "type": "string" }
4362
4362
  },
4363
4363
  {
@@ -4417,7 +4417,7 @@
4417
4417
  },
4418
4418
  {
4419
4419
  "name": "scriptAttributes",
4420
- "description": "Gets the attributes of the script.",
4420
+ "description": "Gets the attributes of the script as an object whose `asset:`, `entity:`, `vec2:`, `vec3:`,\n`vec4:` and `color:` prefixed values are resolved when applied — an `entity:` value being\nan entity name (nearest enclosing entity first, then outward, then the document) or a\ndocument-wide `#` selector (`entity:#id`), never a bare element id.",
4421
4421
  "type": "Record<string, any>"
4422
4422
  }
4423
4423
  ],
@@ -4457,7 +4457,7 @@
4457
4457
  },
4458
4458
  {
4459
4459
  "name": "content",
4460
- "description": "The reference to the `<pc-entity>` used as the content.",
4460
+ "description": "The reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#` selector) to the entity used as the content.",
4461
4461
  "value": { "type": "string" }
4462
4462
  },
4463
4463
  {
@@ -4477,7 +4477,7 @@
4477
4477
  },
4478
4478
  {
4479
4479
  "name": "horizontal-scrollbar",
4480
- "description": "The reference to the `<pc-entity>` containing the horizontal scrollbar.",
4480
+ "description": "The reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#` selector) to the entity containing the horizontal scrollbar.",
4481
4481
  "value": { "type": "string" }
4482
4482
  },
4483
4483
  {
@@ -4513,7 +4513,7 @@
4513
4513
  },
4514
4514
  {
4515
4515
  "name": "vertical-scrollbar",
4516
- "description": "The reference to the `<pc-entity>` containing the vertical scrollbar.",
4516
+ "description": "The reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#` selector) to the entity containing the vertical scrollbar.",
4517
4517
  "value": { "type": "string" }
4518
4518
  },
4519
4519
  {
@@ -4526,7 +4526,7 @@
4526
4526
  },
4527
4527
  {
4528
4528
  "name": "viewport",
4529
- "description": "The reference to the `<pc-entity>` used as the viewport.",
4529
+ "description": "The reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#` selector) to the entity used as the viewport.",
4530
4530
  "value": { "type": "string" }
4531
4531
  }
4532
4532
  ],
@@ -4560,7 +4560,7 @@
4560
4560
  },
4561
4561
  {
4562
4562
  "name": "content",
4563
- "description": "Gets the reference to the `<pc-entity>` used as the content."
4563
+ "description": "Gets the reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#`\nselector) to the entity used as the content."
4564
4564
  },
4565
4565
  {
4566
4566
  "name": "enabled",
@@ -4573,7 +4573,7 @@
4573
4573
  },
4574
4574
  {
4575
4575
  "name": "horizontalScrollbar",
4576
- "description": "Gets the reference to the `<pc-entity>` containing the horizontal scrollbar."
4576
+ "description": "Gets the reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#`\nselector) to the entity containing the horizontal scrollbar."
4577
4577
  },
4578
4578
  {
4579
4579
  "name": "horizontalScrollbarVisibility",
@@ -4597,7 +4597,7 @@
4597
4597
  },
4598
4598
  {
4599
4599
  "name": "verticalScrollbar",
4600
- "description": "Gets the reference to the `<pc-entity>` containing the vertical scrollbar."
4600
+ "description": "Gets the reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#`\nselector) to the entity containing the vertical scrollbar."
4601
4601
  },
4602
4602
  {
4603
4603
  "name": "verticalScrollbarVisibility",
@@ -4605,7 +4605,7 @@
4605
4605
  },
4606
4606
  {
4607
4607
  "name": "viewport",
4608
- "description": "Gets the reference to the `<pc-entity>` used as the viewport."
4608
+ "description": "Gets the reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#`\nselector) to the entity used as the viewport."
4609
4609
  }
4610
4610
  ],
4611
4611
  "events": [
@@ -4629,7 +4629,7 @@
4629
4629
  },
4630
4630
  {
4631
4631
  "name": "handle",
4632
- "description": "The reference to the `<pc-entity>` used as the scrollbar handle.",
4632
+ "description": "The reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#` selector) to the entity used as the scrollbar handle.",
4633
4633
  "value": { "type": "string" }
4634
4634
  },
4635
4635
  {
@@ -4681,7 +4681,7 @@
4681
4681
  },
4682
4682
  {
4683
4683
  "name": "handle",
4684
- "description": "Gets the reference to the `<pc-entity>` used as the scrollbar handle."
4684
+ "description": "Gets the reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#`\nselector) to the entity used as the scrollbar handle."
4685
4685
  },
4686
4686
  {
4687
4687
  "name": "handleSize",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playcanvas/web-components",
3
- "version": "0.19.0",
3
+ "version": "0.20.0",
4
4
  "author": "PlayCanvas <support@playcanvas.com>",
5
5
  "homepage": "https://playcanvas.com",
6
6
  "description": "Web Components for the PlayCanvas Engine",
@@ -99,12 +99,12 @@
99
99
  "eslint": "9.39.5",
100
100
  "globals": "17.11.0",
101
101
  "jsdom": "30.0.1",
102
- "mediabunny": "1.55.3",
102
+ "mediabunny": "1.55.4",
103
103
  "opentype.js": "2.0.0",
104
104
  "playcanvas": "2.21.4",
105
105
  "prettier": "3.9.6",
106
106
  "publint": "0.3.24",
107
- "rollup": "4.63.0",
107
+ "rollup": "4.63.1",
108
108
  "serve": "14.2.6",
109
109
  "tslib": "2.8.1",
110
110
  "typedoc": "0.28.20",
package/src/app.ts CHANGED
@@ -252,6 +252,13 @@ class AppElement extends AsyncElement {
252
252
  */
253
253
  private _lastClick: { element: EntityBaseElement; time: number; count: number } | null = null;
254
254
 
255
+ /**
256
+ * Serializes dispatch of the discrete synthesized events (pointerdown, pointerup, click),
257
+ * whose picks resolve in GPU order, not canvas-event order. Replaced on teardown, so a pick
258
+ * that never resolves cannot stall the dispatches of a later boot.
259
+ */
260
+ private _dispatchChain: Promise<void> = Promise.resolve();
261
+
255
262
  private _app: AppBase | null = null;
256
263
 
257
264
  private _loadProgress = 0;
@@ -648,10 +655,9 @@ class AppElement extends AsyncElement {
648
655
  const { width, height } = this.app!.graphicsDevice;
649
656
  this._picker = new Picker(this.app!, width, height);
650
657
 
651
- // Create bound handlers but don't attach them yet. The handlers pick asynchronously, so
652
- // each is wrapped to discard the promise - a listener must not return one, and nothing
653
- // awaits the result.
654
- const listener = (handler: (event: PointerEvent) => Promise<void>): EventListener => {
658
+ // Create bound handlers but don't attach them yet. The move handler is async, so it is
659
+ // wrapped to discard the promise - a listener must not return one.
660
+ const listener = (handler: (event: PointerEvent) => void | Promise<void>): EventListener => {
655
661
  return (event: Event) => {
656
662
  handler.call(this, event as PointerEvent);
657
663
  };
@@ -690,6 +696,9 @@ class AppElement extends AsyncElement {
690
696
  this._downPicks.clear();
691
697
  this._clickListened = false;
692
698
  this._lastClick = null;
699
+
700
+ // Replace the chain: a pick that never resolves must not stall a later boot's dispatches
701
+ this._dispatchChain = Promise.resolve();
693
702
  }
694
703
 
695
704
  /**
@@ -912,9 +921,23 @@ class AppElement extends AsyncElement {
912
921
  }
913
922
  }
914
923
 
915
- private async _onPointerDown(event: PointerEvent) {
924
+ /**
925
+ * Appends a dispatch step to {@link _dispatchChain}. Must be called synchronously from the
926
+ * canvas event handler - the order of appends is what carries canvas-event order. A step
927
+ * that rejects is reported and released, so the steps queued behind it still dispatch.
928
+ *
929
+ * @param step - The dispatch work to run once every earlier step has finished.
930
+ */
931
+ private _chainDispatch(step: () => Promise<void>) {
932
+ this._dispatchChain = this._dispatchChain.then(step).catch((error) => {
933
+ console.error(error);
934
+ });
935
+ }
936
+
937
+ private _onPointerDown(event: PointerEvent) {
916
938
  if (!this._picker || !this.app) return;
917
939
 
940
+ // Picks stay concurrent - only the dispatch of the results is serialized
918
941
  const pick = this._pickNode(event);
919
942
 
920
943
  // A click concludes on the matching pointerup, which needs to know what the press
@@ -925,16 +948,18 @@ class AppElement extends AsyncElement {
925
948
  this._downPicks.set(event.pointerId, pick);
926
949
  }
927
950
 
928
- const node = await pick;
929
- if (!this._picker) return; // the element disconnected while the pick was in flight
951
+ this._chainDispatch(async () => {
952
+ const node = await pick;
953
+ if (!this._picker) return; // the element disconnected while the pick was in flight
930
954
 
931
- const entityElement = this._elementWithListener(node, 'pointerdown');
932
- if (entityElement) {
933
- entityElement.dispatchEvent(new PointerEvent('pointerdown', event));
934
- }
955
+ const entityElement = this._elementWithListener(node, 'pointerdown');
956
+ if (entityElement) {
957
+ entityElement.dispatchEvent(new PointerEvent('pointerdown', event));
958
+ }
959
+ });
935
960
  }
936
961
 
937
- private async _onPointerUp(event: PointerEvent) {
962
+ private _onPointerUp(event: PointerEvent) {
938
963
  if (!this._picker || !this.app) return;
939
964
 
940
965
  // The press pick this release may conclude as a click. Claimed synchronously, so the
@@ -942,39 +967,49 @@ class AppElement extends AsyncElement {
942
967
  const downPick = this._downPicks.get(event.pointerId);
943
968
  this._downPicks.delete(event.pointerId);
944
969
 
945
- const node = await this._pickNode(event);
946
- if (!this._picker) return; // the element disconnected while the pick was in flight
970
+ const pick = this._pickNode(event);
947
971
 
948
- const entityElement = this._elementWithListener(node, 'pointerup');
949
- if (entityElement) {
950
- entityElement.dispatchEvent(new PointerEvent('pointerup', event));
951
- }
972
+ this._chainDispatch(async () => {
973
+ const node = await pick;
974
+ if (!this._picker) return; // the element disconnected while the pick was in flight
975
+
976
+ const entityElement = this._elementWithListener(node, 'pointerup');
977
+ if (entityElement) {
978
+ entityElement.dispatchEvent(new PointerEvent('pointerup', event));
979
+ }
980
+ });
952
981
 
953
982
  // A click fires where the DOM fires it: at the nearest common inclusive ancestor of
954
- // what the press and the release picked, for the primary button only. The press pick
955
- // may still be in flight - a quick tap resolves in pick order, not event order.
983
+ // what the press and the release picked, for the primary button only. Appended after
984
+ // the release's own step, so it dispatches after the pointerup that concludes it.
956
985
  if (!downPick || event.button !== 0) return;
957
- const downNode = await downPick;
958
- if (!this._picker) return;
959
-
960
- const clickElement = this._elementWithListener(commonAncestor(downNode, node), 'click');
961
- if (clickElement) {
962
- const click = new PointerEvent('click', event);
963
-
964
- // The init above copied pointerup's `detail`, which the Pointer Events spec fixes
965
- // at 0 - but click is exempt: its detail is the click count, chained here as the
966
- // platform chains it (same target, within the double-click window). Overridden
967
- // with defineProperty because an event instance used as an init dict cannot have
968
- // single fields replaced.
969
- const time = performance.now();
970
- const last = this._lastClick;
971
- const count =
972
- last && last.element === clickElement && time - last.time <= CLICK_CHAIN_MS ? last.count + 1 : 1;
973
- this._lastClick = { element: clickElement, time, count };
974
- Object.defineProperty(click, 'detail', { value: count });
975
-
976
- clickElement.dispatchEvent(click);
977
- }
986
+
987
+ this._chainDispatch(async () => {
988
+ // A rejected pick was already reported by the press or release step that awaited it;
989
+ // here it just means no click can conclude.
990
+ const picked = await Promise.all([downPick, pick]).catch(() => null);
991
+ if (!picked || !this._picker) return;
992
+
993
+ const [downNode, upNode] = picked;
994
+ const clickElement = this._elementWithListener(commonAncestor(downNode, upNode), 'click');
995
+ if (clickElement) {
996
+ const click = new PointerEvent('click', event);
997
+
998
+ // The init above copied pointerup's `detail`, which the Pointer Events spec fixes
999
+ // at 0 - but click is exempt: its detail is the click count, chained here as the
1000
+ // platform chains it (same target, within the double-click window). Overridden
1001
+ // with defineProperty because an event instance used as an init dict cannot have
1002
+ // single fields replaced.
1003
+ const time = performance.now();
1004
+ const last = this._lastClick;
1005
+ const count =
1006
+ last && last.element === clickElement && time - last.time <= CLICK_CHAIN_MS ? last.count + 1 : 1;
1007
+ this._lastClick = { element: clickElement, time, count };
1008
+ Object.defineProperty(click, 'detail', { value: count });
1009
+
1010
+ clickElement.dispatchEvent(click);
1011
+ }
1012
+ });
978
1013
  }
979
1014
 
980
1015
  /**
@@ -2,7 +2,7 @@ import type { ButtonComponent } from 'playcanvas';
2
2
  import { BUTTON_TRANSITION_MODE_SPRITE_CHANGE, BUTTON_TRANSITION_MODE_TINT, Color, Vec4 } from 'playcanvas';
3
3
 
4
4
  import { useAsset } from '../asset';
5
- import { getEntity, parseBool, parseColor, parseEnum, parseNumber, parseVec4 } from '../parse';
5
+ import { parseBool, parseColor, parseEnum, parseNumber, parseVec4, resolveEntity } from '../parse';
6
6
 
7
7
  import { ComponentElement } from './component';
8
8
 
@@ -76,7 +76,9 @@ class ButtonComponentElement extends ComponentElement {
76
76
 
77
77
  // The image entity defaults to the button's own entity (which carries the image element)
78
78
  // when no explicit reference is provided.
79
- const imageEntity = this._image ? getEntity(this._image) : this.closestEntity?.entity;
79
+ const imageEntity = this._image
80
+ ? resolveEntity(this._image, this, 'image', 'reference ignored')
81
+ : this.closestEntity?.entity;
80
82
  if (imageEntity) {
81
83
  data.imageEntity = imageEntity;
82
84
  }
@@ -127,22 +129,28 @@ class ButtonComponentElement extends ComponentElement {
127
129
  }
128
130
 
129
131
  /**
130
- * Sets the reference (CSS selector, element id or entity name) to the `<pc-entity>` whose image
131
- * element is used for visual transitions. Defaults to the button's own entity — inside a
132
- * `<pc-model>`, that is the model's host entity, so supply an explicit reference to target a
133
- * UI entity instead.
132
+ * Sets the reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#`
133
+ * selector) to the entity whose image element is used for visual transitions. An exact name
134
+ * resolves against the nearest enclosing entity first, then outward, then the document.
135
+ * Defaults to the button's own entity — inside a `<pc-model>`, that is the model's host
136
+ * entity, so supply an explicit reference to target a UI entity instead. A non-empty
137
+ * reference that does not resolve warns and is ignored.
134
138
  * @param value - The image entity reference.
135
139
  */
136
140
  set image(value: string) {
137
141
  this._image = value;
138
- const entity = getEntity(value);
139
- if (this.component && entity) {
140
- this.component.imageEntity = entity;
142
+ if (this.component) {
143
+ const entity = resolveEntity(value, this, 'image', 'reference ignored');
144
+ if (entity) {
145
+ this.component.imageEntity = entity;
146
+ }
141
147
  }
142
148
  }
143
149
 
144
150
  /**
145
- * Gets the reference to the `<pc-entity>` whose image element is used for visual transitions.
151
+ * Gets the reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#`
152
+ * selector) to the entity whose image element is used for visual transitions, or empty for
153
+ * the button's own entity.
146
154
  * @returns The image entity reference.
147
155
  */
148
156
  get image() {
@@ -1,7 +1,7 @@
1
1
  import type { JointComponent } from 'playcanvas';
2
2
  import { Vec2, Vec3 } from 'playcanvas';
3
3
 
4
- import { getEntity, parseBool, parseEnum, parseNumber, parseVec2, parseVec3 } from '../parse';
4
+ import { parseBool, parseEnum, parseNumber, parseVec2, parseVec3, resolveEntity } from '../parse';
5
5
 
6
6
  import { ComponentElement } from './component';
7
7
 
@@ -25,7 +25,12 @@ export type MotionMode = 'locked' | 'limited' | 'free';
25
25
  * primary axis: a hinge rotates about it, a slider translates along it and a ball joint twists
26
26
  * about it. The constrained bodies are referenced by `entity-a` and `entity-b`, both of which need
27
27
  * a rigid body component; leaving `entity-b` empty constrains `entity-a` to a fixed point in world
28
- * space. The underlying engine component is in alpha, so its API may change.
28
+ * space. A reference can name any entity-fronting element `<pc-entity>`, `<pc-model>` or
29
+ * `<pc-node>`, so a ragdoll can join a model's own skeleton nodes by name — and a name resolves
30
+ * against the nearest enclosing entity first, then outward through the entity hierarchy, then the
31
+ * document, while a `#` selector resolves document-wide. A `<template>` prefab with one
32
+ * entity-fronting root can therefore wire its joints by name and stay self-contained when cloned.
33
+ * The underlying engine component is in alpha, so its API may change.
29
34
  *
30
35
  * @elementSummary The `<pc-joint>` element constrains two rigid bodies to each other — a hinged
31
36
  * door, a swinging chain, a sliding drawer. Its entity's transform is the joint frame, and
@@ -210,8 +215,8 @@ class JointComponentElement extends ComponentElement {
210
215
  breakImpulse: this._breakImpulse,
211
216
  enableCollision: this._enableCollision,
212
217
  enableLimits: this._enableLimits,
213
- entityA: getEntity(this._entityA),
214
- entityB: getEntity(this._entityB),
218
+ entityA: resolveEntity(this._entityA, this, 'entity-a', 'constraint not created'),
219
+ entityB: resolveEntity(this._entityB, this, 'entity-b', 'constraint not created'),
215
220
  limits: this._limits,
216
221
  linearDamping: this._linearDamping,
217
222
  linearEquilibrium: this._linearEquilibrium,
@@ -495,20 +500,24 @@ class JointComponentElement extends ComponentElement {
495
500
  }
496
501
 
497
502
  /**
498
- * Sets the reference (CSS selector, element id or entity name) to the `<pc-entity>` providing
499
- * the first constrained body. The reference resolves when it is set, so an entity created
500
- * later is picked up by setting the attribute again.
503
+ * Sets the reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#`
504
+ * selector) to the element providing the first constrained body. An exact name resolves
505
+ * against the nearest enclosing entity first, then outward, then the document. The reference
506
+ * resolves when it is set, so an entity created later is picked up by setting the attribute
507
+ * again. A non-empty reference that does not resolve warns, naming which of the two causes it
508
+ * hit.
501
509
  * @param value - The first body's entity reference.
502
510
  */
503
511
  set entityA(value: string) {
504
512
  this._entityA = value;
505
513
  if (this.component) {
506
- this.component.entityA = getEntity(value);
514
+ this.component.entityA = resolveEntity(value, this, 'entity-a', 'constraint not created');
507
515
  }
508
516
  }
509
517
 
510
518
  /**
511
- * Gets the reference to the `<pc-entity>` providing the first constrained body.
519
+ * Gets the reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#`
520
+ * selector) to the element providing the first constrained body.
512
521
  * @returns The first body's entity reference.
513
522
  */
514
523
  get entityA() {
@@ -516,21 +525,26 @@ class JointComponentElement extends ComponentElement {
516
525
  }
517
526
 
518
527
  /**
519
- * Sets the reference (CSS selector, element id or entity name) to the `<pc-entity>` providing
520
- * the second constrained body, or empty to constrain the first body to a fixed point in world
521
- * space. The reference resolves when it is set, so an entity created later is picked up by
522
- * setting the attribute again.
528
+ * Sets the reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#`
529
+ * selector) to the element providing the second constrained body, or empty to constrain the
530
+ * first body to a fixed point in world space. An exact name resolves against the nearest
531
+ * enclosing entity first, then outward, then the document. The reference resolves when it is
532
+ * set, so an entity created later is picked up by setting the attribute again. A non-empty
533
+ * reference that does not resolve warns; an empty one is the documented world-space case and
534
+ * stays silent.
523
535
  * @param value - The second body's entity reference.
524
536
  */
525
537
  set entityB(value: string) {
526
538
  this._entityB = value;
527
539
  if (this.component) {
528
- this.component.entityB = getEntity(value);
540
+ this.component.entityB = resolveEntity(value, this, 'entity-b', 'constraint not created');
529
541
  }
530
542
  }
531
543
 
532
544
  /**
533
- * Gets the reference to the `<pc-entity>` providing the second constrained body.
545
+ * Gets the reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#`
546
+ * selector) to the element providing the second constrained body, or empty for the
547
+ * world-space case.
534
548
  * @returns The second body's entity reference.
535
549
  */
536
550
  get entityB() {