@playcanvas/web-components 0.16.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.
- package/dist/app.d.cts +14 -13
- package/dist/app.d.ts +14 -13
- package/dist/async-element.d.cts +13 -13
- package/dist/async-element.d.ts +13 -13
- package/dist/components/anim-component.d.cts +33 -5
- package/dist/components/anim-component.d.ts +33 -5
- package/dist/components/{listener-component.d.cts → audio-listener-component.d.cts} +7 -5
- package/dist/components/{listener-component.d.ts → audio-listener-component.d.ts} +7 -5
- package/dist/components/button-component.d.cts +5 -1
- package/dist/components/button-component.d.ts +5 -1
- package/dist/components/camera-component.d.cts +2 -0
- package/dist/components/camera-component.d.ts +2 -0
- package/dist/components/collision-component.d.cts +5 -1
- package/dist/components/collision-component.d.ts +5 -1
- package/dist/components/component.d.cts +3 -2
- package/dist/components/component.d.ts +3 -2
- package/dist/components/element-component.d.cts +7 -0
- package/dist/components/element-component.d.ts +7 -0
- package/dist/components/gsplat-component.d.cts +2 -0
- package/dist/components/gsplat-component.d.ts +2 -0
- package/dist/components/joint-component.d.cts +2 -0
- package/dist/components/joint-component.d.ts +2 -0
- package/dist/components/{layoutchild-component.d.cts → layout-child-component.d.cts} +3 -1
- package/dist/components/{layoutchild-component.d.ts → layout-child-component.d.ts} +3 -1
- package/dist/components/{layoutgroup-component.d.cts → layout-group-component.d.cts} +3 -1
- package/dist/components/{layoutgroup-component.d.ts → layout-group-component.d.ts} +3 -1
- package/dist/components/light-component.d.cts +2 -0
- package/dist/components/light-component.d.ts +2 -0
- package/dist/components/{particlesystem-component.d.cts → particle-system-component.d.cts} +3 -1
- package/dist/components/{particlesystem-component.d.ts → particle-system-component.d.ts} +3 -1
- package/dist/components/render-component.d.cts +2 -0
- package/dist/components/render-component.d.ts +2 -0
- package/dist/components/{rigidbody-component.d.cts → rigid-body-component.d.cts} +3 -1
- package/dist/components/{rigidbody-component.d.ts → rigid-body-component.d.ts} +3 -1
- package/dist/components/screen-component.d.cts +2 -0
- package/dist/components/screen-component.d.ts +2 -0
- package/dist/components/script-component.d.cts +23 -12
- package/dist/components/script-component.d.ts +23 -12
- package/dist/components/{script.d.cts → script-instance.d.cts} +7 -7
- package/dist/components/{script.d.ts → script-instance.d.ts} +7 -7
- package/dist/components/{scrollview-component.d.cts → scroll-view-component.d.cts} +3 -1
- package/dist/components/{scrollview-component.d.ts → scroll-view-component.d.ts} +3 -1
- package/dist/components/scrollbar-component.d.cts +2 -0
- package/dist/components/scrollbar-component.d.ts +2 -0
- package/dist/components/sound-component.d.cts +3 -1
- package/dist/components/sound-component.d.ts +3 -1
- package/dist/components/sound-slot.d.cts +2 -2
- package/dist/components/sound-slot.d.ts +2 -2
- package/dist/custom-elements.json +1603 -986
- package/dist/entity-base.d.cts +6 -5
- package/dist/entity-base.d.ts +6 -5
- package/dist/entity-owner.d.cts +118 -0
- package/dist/entity-owner.d.ts +118 -0
- package/dist/entity.d.cts +8 -101
- package/dist/entity.d.ts +8 -101
- package/dist/index.d.cts +22 -21
- package/dist/index.d.ts +22 -21
- package/dist/model.d.cts +67 -24
- package/dist/model.d.ts +67 -24
- package/dist/pwc.cjs +526 -268
- package/dist/pwc.cjs.map +1 -1
- package/dist/pwc.js +526 -268
- package/dist/pwc.js.map +1 -1
- package/dist/pwc.min.js +1 -1
- package/dist/pwc.min.js.map +1 -1
- package/dist/pwc.min.mjs +1 -1
- package/dist/pwc.min.mjs.map +1 -1
- package/dist/pwc.mjs +523 -266
- package/dist/pwc.mjs.map +1 -1
- package/dist/vscode.html-custom-data.json +145 -90
- package/dist/{module.d.cts → wasm.d.cts} +6 -6
- package/dist/{module.d.ts → wasm.d.ts} +6 -6
- package/dist/web-types.json +378 -250
- package/package.json +7 -7
- package/src/app.ts +45 -35
- package/src/async-element.ts +14 -14
- package/src/components/anim-component.ts +84 -14
- package/src/components/{listener-component.ts → audio-listener-component.ts} +8 -6
- package/src/components/button-component.ts +5 -1
- package/src/components/camera-component.ts +2 -0
- package/src/components/collision-component.ts +5 -1
- package/src/components/component.ts +4 -3
- package/src/components/element-component.ts +7 -0
- package/src/components/gsplat-component.ts +2 -0
- package/src/components/joint-component.ts +2 -0
- package/src/components/{layoutchild-component.ts → layout-child-component.ts} +4 -2
- package/src/components/{layoutgroup-component.ts → layout-group-component.ts} +4 -2
- package/src/components/light-component.ts +2 -0
- package/src/components/{particlesystem-component.ts → particle-system-component.ts} +4 -2
- package/src/components/render-component.ts +2 -0
- package/src/components/{rigidbody-component.ts → rigid-body-component.ts} +4 -2
- package/src/components/screen-component.ts +2 -0
- package/src/components/script-component.ts +63 -42
- package/src/components/{script.ts → script-instance.ts} +14 -14
- package/src/components/{scrollview-component.ts → scroll-view-component.ts} +4 -2
- package/src/components/scrollbar-component.ts +2 -0
- package/src/components/sound-component.ts +4 -2
- package/src/components/sound-slot.ts +4 -4
- package/src/entity-base.ts +6 -5
- package/src/entity-owner.ts +298 -0
- package/src/entity.ts +15 -270
- package/src/index.ts +26 -24
- package/src/model.ts +159 -72
- package/src/node.ts +9 -5
- package/src/{module.ts → wasm.ts} +8 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@playcanvas/web-components",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"author": "PlayCanvas <support@playcanvas.com>",
|
|
5
5
|
"homepage": "https://playcanvas.com",
|
|
6
6
|
"description": "Web Components for the PlayCanvas Engine",
|
|
@@ -91,25 +91,25 @@
|
|
|
91
91
|
"@rollup/plugin-terser": "1.0.0",
|
|
92
92
|
"@rollup/plugin-typescript": "12.3.0",
|
|
93
93
|
"@tweenjs/tween.js": "25.0.0",
|
|
94
|
-
"@vitest/coverage-v8": "4.1.
|
|
95
|
-
"concurrently": "10.0.
|
|
94
|
+
"@vitest/coverage-v8": "4.1.11",
|
|
95
|
+
"concurrently": "10.0.5",
|
|
96
96
|
"custom-element-jet-brains-integration": "1.7.0",
|
|
97
97
|
"custom-element-vs-code-integration": "1.5.0",
|
|
98
98
|
"earcut": "3.2.3",
|
|
99
99
|
"eslint": "9.39.5",
|
|
100
100
|
"globals": "17.11.0",
|
|
101
101
|
"jsdom": "30.0.1",
|
|
102
|
-
"mediabunny": "1.
|
|
102
|
+
"mediabunny": "1.55.2",
|
|
103
103
|
"opentype.js": "2.0.0",
|
|
104
104
|
"playcanvas": "2.21.4",
|
|
105
105
|
"prettier": "3.9.6",
|
|
106
|
-
"publint": "0.3.
|
|
107
|
-
"rollup": "4.62.
|
|
106
|
+
"publint": "0.3.24",
|
|
107
|
+
"rollup": "4.62.5",
|
|
108
108
|
"serve": "14.2.6",
|
|
109
109
|
"tslib": "2.8.1",
|
|
110
110
|
"typedoc": "0.28.20",
|
|
111
111
|
"typedoc-plugin-mdn-links": "5.1.1",
|
|
112
112
|
"typescript": "6.0.3",
|
|
113
|
-
"vitest": "4.1.
|
|
113
|
+
"vitest": "4.1.11"
|
|
114
114
|
}
|
|
115
115
|
}
|
package/src/app.ts
CHANGED
|
@@ -66,16 +66,23 @@ import {
|
|
|
66
66
|
|
|
67
67
|
import type { AssetElement } from './asset';
|
|
68
68
|
import { AsyncElement } from './async-element';
|
|
69
|
-
import type { EntityElement } from './entity';
|
|
70
69
|
import type { EntityBaseElement } from './entity-base';
|
|
70
|
+
import type { EntityOwnerElement } from './entity-owner';
|
|
71
71
|
import { LoadingBar } from './loading-bar';
|
|
72
72
|
import type { MaterialElement } from './material';
|
|
73
|
-
import type { ModuleElement } from './module';
|
|
74
73
|
import { parseBool, parseEnum, parseNumber } from './parse';
|
|
74
|
+
import type { WasmElement } from './wasm';
|
|
75
75
|
|
|
76
76
|
/** The pointer event types the application synthesizes on `<pc-entity>` elements via picking. */
|
|
77
77
|
const pointerEventTypes = ['pointermove', 'pointerdown', 'pointerup', 'pointerenter', 'pointerleave'] as const;
|
|
78
78
|
|
|
79
|
+
/**
|
|
80
|
+
* The event types whose listeners make an element a hover target. Hover resolution walks past
|
|
81
|
+
* elements listening for none of them, so a silent element never swallows an ancestor's
|
|
82
|
+
* enter/leave pair.
|
|
83
|
+
*/
|
|
84
|
+
const hoverEventTypes = ['pointerenter', 'pointerleave', 'pointermove'] as const;
|
|
85
|
+
|
|
79
86
|
/**
|
|
80
87
|
* Gives `pc-app` the sizing contract of a replaced element (`<video>`, `<img>`): a block-level
|
|
81
88
|
* box that the page's CSS sizes, defaulting to the canvas's own 300x150 intrinsic size, with the
|
|
@@ -163,9 +170,9 @@ class AppElement extends AsyncElement {
|
|
|
163
170
|
|
|
164
171
|
/**
|
|
165
172
|
* The elements backing this application's entities, keyed by the entity itself. Registered
|
|
166
|
-
* by
|
|
167
|
-
*
|
|
168
|
-
* identity - never by name.
|
|
173
|
+
* by entity-owning elements at creation (pc-entity, and pc-model for its host) and by
|
|
174
|
+
* NodeElement at binding, and removed when an entity is destroyed or unbound, this joins
|
|
175
|
+
* engine scene nodes back to their owning elements by identity - never by name.
|
|
169
176
|
*/
|
|
170
177
|
private _entityElements = new Map<GraphNode, EntityBaseElement>();
|
|
171
178
|
|
|
@@ -260,7 +267,7 @@ class AppElement extends AsyncElement {
|
|
|
260
267
|
// there, leaving the element permanently unready: no canvas, no entities, no application.
|
|
261
268
|
//
|
|
262
269
|
// Upgrading is the fix here rather than skipping whatever has not upgraded, because a
|
|
263
|
-
// <pc-
|
|
270
|
+
// <pc-wasm> is the one child that nothing else ever builds on its own behalf - skipping
|
|
264
271
|
// it would drop the wasm module the app asked for, silently and only for cloned apps.
|
|
265
272
|
// Upgrading runs each descendant's connectedCallback synchronously, a few lines earlier
|
|
266
273
|
// than the parser's path runs them but into the same state they see there: no application
|
|
@@ -270,11 +277,11 @@ class AppElement extends AsyncElement {
|
|
|
270
277
|
// is left completely untouched.
|
|
271
278
|
customElements.upgrade(this);
|
|
272
279
|
|
|
273
|
-
// Get all pc-
|
|
274
|
-
const
|
|
280
|
+
// Get all pc-wasm elements that are direct children of the pc-app element
|
|
281
|
+
const wasmElements = this.querySelectorAll<WasmElement>(':scope > pc-wasm');
|
|
275
282
|
|
|
276
283
|
// Wait for all modules to load
|
|
277
|
-
await Promise.all(Array.from(
|
|
284
|
+
await Promise.all(Array.from(wasmElements).map((element) => element._getLoadPromise()));
|
|
278
285
|
|
|
279
286
|
// The element may have been removed while the modules loaded. Nothing beyond the loading
|
|
280
287
|
// bar exists yet, and disconnectedCallback has already destroyed that.
|
|
@@ -462,15 +469,16 @@ class AppElement extends AsyncElement {
|
|
|
462
469
|
materialElement._createMaterial();
|
|
463
470
|
});
|
|
464
471
|
|
|
465
|
-
// Create all entities
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
472
|
+
// Create all entities. pc-model joins the sweep because it owns a host entity of its
|
|
473
|
+
// own; its instantiated content arrives later, beneath that host.
|
|
474
|
+
const ownerElements = this.querySelectorAll<EntityOwnerElement>('pc-entity, pc-model');
|
|
475
|
+
Array.from(ownerElements).forEach((ownerElement) => {
|
|
476
|
+
ownerElement._createEntity(app);
|
|
469
477
|
});
|
|
470
478
|
|
|
471
479
|
// Build hierarchy
|
|
472
|
-
|
|
473
|
-
|
|
480
|
+
ownerElements.forEach((ownerElement) => {
|
|
481
|
+
ownerElement._buildHierarchy(app);
|
|
474
482
|
});
|
|
475
483
|
|
|
476
484
|
// Building the hierarchy dispatched each entity's ready event synchronously, and a
|
|
@@ -602,9 +610,9 @@ class AppElement extends AsyncElement {
|
|
|
602
610
|
// created from onpointer* attributes when their elements were first upgraded, or
|
|
603
611
|
// listeners carried over from before a re-boot)
|
|
604
612
|
pointerEventTypes.forEach((type) => {
|
|
605
|
-
const anyListeners = Array.from(
|
|
606
|
-
(entity
|
|
607
|
-
);
|
|
613
|
+
const anyListeners = Array.from(
|
|
614
|
+
this.querySelectorAll<EntityBaseElement>('pc-entity, pc-model, pc-node')
|
|
615
|
+
).some((entity) => entity._hasListeners(type));
|
|
608
616
|
if (anyListeners) {
|
|
609
617
|
this._onPointerListenerAdded(type);
|
|
610
618
|
}
|
|
@@ -659,10 +667,10 @@ class AppElement extends AsyncElement {
|
|
|
659
667
|
}
|
|
660
668
|
|
|
661
669
|
/**
|
|
662
|
-
* Returns the `<pc-entity>` or `<pc-node>` element whose backing entity is
|
|
663
|
-
* `null` if the entity is not fronted by an element of this application - for
|
|
664
|
-
* unbound node inside a model's instantiated hierarchy, or an entity created
|
|
665
|
-
* engine API.
|
|
670
|
+
* Returns the `<pc-entity>`, `<pc-model>` or `<pc-node>` element whose backing entity is
|
|
671
|
+
* `entity`, or `null` if the entity is not fronted by an element of this application - for
|
|
672
|
+
* example, an unbound node inside a model's instantiated hierarchy, or an entity created
|
|
673
|
+
* through the engine API.
|
|
666
674
|
*
|
|
667
675
|
* @param entity - The entity to look up.
|
|
668
676
|
* @returns The element fronting the entity, or `null`.
|
|
@@ -672,18 +680,19 @@ class AppElement extends AsyncElement {
|
|
|
672
680
|
}
|
|
673
681
|
|
|
674
682
|
/**
|
|
675
|
-
* Resolves the element that owns a picked node: the nearest node up the parent
|
|
676
|
-
* starting with the node itself -
|
|
677
|
-
*
|
|
678
|
-
*
|
|
683
|
+
* Resolves the element that owns hover for a picked node: the nearest node up the parent
|
|
684
|
+
* chain - starting with the node itself - whose element listens for any of the hover event
|
|
685
|
+
* types. Skipping silent elements matches {@link _elementWithListener}, so a registered
|
|
686
|
+
* element with no hover listeners (a `<pc-model>` host, a plain child entity) is transparent
|
|
687
|
+
* to hover rather than swallowing a listening ancestor's enter/leave pair.
|
|
679
688
|
*
|
|
680
689
|
* @param node - The picked node, or `null`.
|
|
681
|
-
* @returns The owning element, or `null`.
|
|
690
|
+
* @returns The hover-owning element, or `null`.
|
|
682
691
|
*/
|
|
683
|
-
private
|
|
692
|
+
private _hoverTarget(node: GraphNode | null): EntityBaseElement | null {
|
|
684
693
|
while (node !== null) {
|
|
685
694
|
const element = this._entityElements.get(node);
|
|
686
|
-
if (element) {
|
|
695
|
+
if (element && hoverEventTypes.some((type) => element._hasListeners(type))) {
|
|
687
696
|
return element;
|
|
688
697
|
}
|
|
689
698
|
node = node.parent;
|
|
@@ -831,9 +840,10 @@ class AppElement extends AsyncElement {
|
|
|
831
840
|
const node = await this._pickNode(event);
|
|
832
841
|
if (token !== this._pickToken || !this._picker) return;
|
|
833
842
|
|
|
834
|
-
// The hovered element is the nearest one up the node's parent chain
|
|
835
|
-
//
|
|
836
|
-
|
|
843
|
+
// The hovered element is the nearest one up the node's parent chain with a hover
|
|
844
|
+
// listener - the nearest-listener rule down/up use. Dispatch is still gated per event
|
|
845
|
+
// type below: having any hover listener selects the target, each event needs its own.
|
|
846
|
+
const newHoverEntity = this._hoverTarget(node);
|
|
837
847
|
|
|
838
848
|
// Handle enter/leave events
|
|
839
849
|
if (this._hoveredEntity !== newHoverEntity) {
|
|
@@ -898,9 +908,9 @@ class AppElement extends AsyncElement {
|
|
|
898
908
|
}
|
|
899
909
|
|
|
900
910
|
private _onPointerListenerRemoved(type: string) {
|
|
901
|
-
const hasListeners = Array.from(
|
|
902
|
-
|
|
903
|
-
);
|
|
911
|
+
const hasListeners = Array.from(
|
|
912
|
+
this.querySelectorAll<EntityBaseElement>('pc-entity, pc-model, pc-node')
|
|
913
|
+
).some((entity) => entity._hasListeners(type));
|
|
904
914
|
|
|
905
915
|
if (!hasListeners && this._canvas) {
|
|
906
916
|
this._hasPointerListeners[type] = false;
|
package/src/async-element.ts
CHANGED
|
@@ -33,13 +33,13 @@ class AsyncElement extends HTMLElement {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
|
-
* The nearest ancestor element that fronts an entity — `<pc-entity
|
|
37
|
-
* `null` if this element has no such ancestor. The search starts at the
|
|
38
|
-
* never resolves to itself.
|
|
36
|
+
* The nearest ancestor element that fronts an entity — `<pc-entity>`, `<pc-model>` or
|
|
37
|
+
* `<pc-node>` — or `null` if this element has no such ancestor. The search starts at the
|
|
38
|
+
* parent, so an element never resolves to itself.
|
|
39
39
|
* @returns The closest entity-fronting element, or `null`.
|
|
40
40
|
*/
|
|
41
41
|
get closestEntity(): EntityBaseElement | null {
|
|
42
|
-
return (this.parentElement?.closest('pc-entity, pc-node') as EntityBaseElement | null) ?? null;
|
|
42
|
+
return (this.parentElement?.closest('pc-entity, pc-model, pc-node') as EntityBaseElement | null) ?? null;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
/**
|
|
@@ -95,11 +95,11 @@ type AsyncElementTagName = {
|
|
|
95
95
|
|
|
96
96
|
/**
|
|
97
97
|
* Waits for the first element matching the given tag name to be fully initialized. Note that the
|
|
98
|
-
* promise never settles if the element cannot finish initializing (for example, a `<pc-script>`
|
|
99
|
-
* that is not a direct child of `<pc-
|
|
100
|
-
* device — listen for its `error` event instead). A component element outside
|
|
101
|
-
* the exception: it still becomes ready, but its `component` is `null`. Either way, a
|
|
102
|
-
* element logs a warning naming the parent it requires.
|
|
98
|
+
* promise never settles if the element cannot finish initializing (for example, a `<pc-script-instance>`
|
|
99
|
+
* that is not a direct child of `<pc-script>`, or a `<pc-app>` that could not create a graphics
|
|
100
|
+
* device — listen for its `error` event instead). A component element outside an entity-fronting
|
|
101
|
+
* element is the exception: it still becomes ready, but its `component` is `null`. Either way, a
|
|
102
|
+
* misplaced element logs a warning naming the parent it requires.
|
|
103
103
|
* @param target - The tag name of the element to wait for (e.g. `'pc-app'`).
|
|
104
104
|
* @returns A promise that resolves with the element once it's ready.
|
|
105
105
|
* @example
|
|
@@ -120,11 +120,11 @@ function whenReady<K extends AsyncElementTagName>(target: K): Promise<HTMLElemen
|
|
|
120
120
|
function whenReady<T extends AsyncElement>(target: T): Promise<T>;
|
|
121
121
|
/**
|
|
122
122
|
* Waits for the first element matching the given CSS selector to be fully initialized. Note that
|
|
123
|
-
* the promise never settles if the element cannot finish initializing (for example, a `<pc-script>`
|
|
124
|
-
* that is not a direct child of `<pc-
|
|
125
|
-
* device — listen for its `error` event instead). A component element outside
|
|
126
|
-
* the exception: it still becomes ready, but its `component` is `null`. Either way, a
|
|
127
|
-
* element logs a warning naming the parent it requires.
|
|
123
|
+
* the promise never settles if the element cannot finish initializing (for example, a `<pc-script-instance>`
|
|
124
|
+
* that is not a direct child of `<pc-script>`, or a `<pc-app>` that could not create a graphics
|
|
125
|
+
* device — listen for its `error` event instead). A component element outside an entity-fronting
|
|
126
|
+
* element is the exception: it still becomes ready, but its `component` is `null`. Either way, a
|
|
127
|
+
* misplaced element logs a warning naming the parent it requires.
|
|
128
128
|
* @param target - A CSS selector matching the element to wait for (e.g. `'#my-app'`).
|
|
129
129
|
* @returns A promise that resolves with the element once it's ready.
|
|
130
130
|
* @example
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AnimComponent, Asset, ContainerResource } from 'playcanvas';
|
|
1
|
+
import type { AnimComponent, Asset, ContainerResource, Entity } from 'playcanvas';
|
|
2
2
|
import { ANIM_CONTROL_STATES, AnimTrack } from 'playcanvas';
|
|
3
3
|
|
|
4
4
|
import { AssetElement } from '../asset';
|
|
@@ -46,6 +46,8 @@ type PlaybackState = {
|
|
|
46
46
|
* the underlying {@link AnimComponent} (via {@link component}) for playback state beyond what
|
|
47
47
|
* this element exposes.
|
|
48
48
|
*
|
|
49
|
+
* Engine component: {@link AnimComponent} (`anim`).
|
|
50
|
+
*
|
|
49
51
|
* @category Components
|
|
50
52
|
*/
|
|
51
53
|
class AnimComponentElement extends ComponentElement {
|
|
@@ -71,6 +73,12 @@ class AnimComponentElement extends ComponentElement {
|
|
|
71
73
|
*/
|
|
72
74
|
private _clip = '';
|
|
73
75
|
|
|
76
|
+
/**
|
|
77
|
+
* The binding root this element last assigned through {@link _applyRootBone}, distinguishing
|
|
78
|
+
* its own writes from a `rootBone` assigned through the engine API — which is left alone.
|
|
79
|
+
*/
|
|
80
|
+
private _managedRootBone: Entity | null = null;
|
|
81
|
+
|
|
74
82
|
/**
|
|
75
83
|
* The element the model-readiness listener is attached to, held so disconnection can detach
|
|
76
84
|
* it after `closestEntity` no longer resolves.
|
|
@@ -101,28 +109,76 @@ class AnimComponentElement extends ComponentElement {
|
|
|
101
109
|
private _warnedClip: string | null = null;
|
|
102
110
|
|
|
103
111
|
/**
|
|
104
|
-
* Rebinds when a model
|
|
105
|
-
*
|
|
112
|
+
* Rebinds when a model that is not this component's host announces readiness — a sibling or
|
|
113
|
+
* deeper model whose content changed under the assigned clips. The engine resolves each
|
|
114
|
+
* curve once, at the first tick after assignment, and never retries — and its mesh-instance
|
|
106
115
|
* broadcast fires before an instantiated hierarchy is parented, so a model that loads after
|
|
107
|
-
* the clips were assigned would otherwise stay silently unbound.
|
|
108
|
-
*
|
|
109
|
-
*
|
|
116
|
+
* the clips were assigned would otherwise stay silently unbound. The host model's own cycles
|
|
117
|
+
* are excluded: those re-enter through {@link initComponent}, which refreshes the whole clip
|
|
118
|
+
* set against the new container.
|
|
110
119
|
*/
|
|
111
120
|
private _onModelReady = (event: Event) => {
|
|
112
121
|
if (!(event.target instanceof ModelElement) || !this.component) {
|
|
113
122
|
return;
|
|
114
123
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
124
|
+
// The host model's own readiness cycle already re-initialized this component through
|
|
125
|
+
// ComponentElement's host-ready listener, which is attached first and so has run by now.
|
|
126
|
+
// Acting here too would resolve every track twice and capture the playhead-restore
|
|
127
|
+
// snapshot mid-rebuild.
|
|
128
|
+
if (event.target === this._modelListenerTarget) {
|
|
129
|
+
return;
|
|
122
130
|
}
|
|
131
|
+
// A model cycle can replace the skeleton source's host entity (a rebuild under a
|
|
132
|
+
// retargeting pc-node), so the binding root is re-asserted before the rebind.
|
|
133
|
+
this._applyRootBone();
|
|
123
134
|
this.component.rebind();
|
|
124
135
|
};
|
|
125
136
|
|
|
137
|
+
/**
|
|
138
|
+
* The model whose host entity scopes this component's curve binding: the parent `pc-model`,
|
|
139
|
+
* or the sole `pc-model` among the parent's direct children (the arrangement where clips
|
|
140
|
+
* live in a library asset beside the skeleton). `null` when there is no such model, or more
|
|
141
|
+
* than one — an ambiguous skeleton is left to the engine's name-based resolution.
|
|
142
|
+
*/
|
|
143
|
+
private _skeletonSource(): ModelElement | null {
|
|
144
|
+
const parent = this.parentElement;
|
|
145
|
+
if (parent instanceof ModelElement) {
|
|
146
|
+
return parent;
|
|
147
|
+
}
|
|
148
|
+
const models = parent ? parent.querySelectorAll(':scope > pc-model') : null;
|
|
149
|
+
return models?.length === 1 && models[0] instanceof ModelElement ? models[0] : null;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Keeps the component's binding root pointing at the skeleton source's host entity. The host
|
|
154
|
+
* wraps the instantiated content, so left at its default — the component's own entity — the
|
|
155
|
+
* engine binder mis-resolves curves that target the asset's root node: its fallback treats
|
|
156
|
+
* the graph as the asset root once the root is no longer a direct child.
|
|
157
|
+
*
|
|
158
|
+
* Authoritative in both directions for values this element assigned: a source appearing pins
|
|
159
|
+
* its host, and a source dissolving (the model gone, or a second model making the skeleton
|
|
160
|
+
* ambiguous) clears the pin rather than leaving it on a stale host. A root assigned through
|
|
161
|
+
* the engine API is never overwritten — the user's choice outranks the managed default.
|
|
162
|
+
* Writes are skipped while unchanged, because the engine setter itself triggers a rebind.
|
|
163
|
+
*/
|
|
164
|
+
private _applyRootBone() {
|
|
165
|
+
const component = this.component;
|
|
166
|
+
|
|
167
|
+
// A non-null root this element did not assign came through the engine API. A fresh
|
|
168
|
+
// component starts at null, which is always reclaimable.
|
|
169
|
+
if (component.rootBone !== null && component.rootBone !== this._managedRootBone) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const host = this._skeletonSource()?.entity ?? null;
|
|
174
|
+
if (component.rootBone !== host) {
|
|
175
|
+
// The engine setter accepts null - restoring the component's own entity as the
|
|
176
|
+
// binding graph - but its declared type does not
|
|
177
|
+
component.rootBone = host as Entity;
|
|
178
|
+
}
|
|
179
|
+
this._managedRootBone = host;
|
|
180
|
+
}
|
|
181
|
+
|
|
126
182
|
/** @ignore */
|
|
127
183
|
constructor() {
|
|
128
184
|
super('anim');
|
|
@@ -151,7 +207,16 @@ class AnimComponentElement extends ComponentElement {
|
|
|
151
207
|
this._modelListenerTarget = host;
|
|
152
208
|
}
|
|
153
209
|
|
|
154
|
-
this.
|
|
210
|
+
this._applyRootBone();
|
|
211
|
+
|
|
212
|
+
if (this.component.baseLayer) {
|
|
213
|
+
// The component survived the host's readiness cycle (a pc-model reloading content on
|
|
214
|
+
// its stable host entity). A loaded graph cannot be reassigned in place, so drop it
|
|
215
|
+
// and reassign from the current source, restoring the active clip and playhead.
|
|
216
|
+
this._refreshClips();
|
|
217
|
+
} else {
|
|
218
|
+
this._applyClips();
|
|
219
|
+
}
|
|
155
220
|
}
|
|
156
221
|
|
|
157
222
|
disconnectedCallback() {
|
|
@@ -163,6 +228,7 @@ class AnimComponentElement extends ComponentElement {
|
|
|
163
228
|
this._sourceGeneration++;
|
|
164
229
|
this._assignedClips.clear();
|
|
165
230
|
this._autoAssigned = false;
|
|
231
|
+
this._managedRootBone = null;
|
|
166
232
|
|
|
167
233
|
super.disconnectedCallback();
|
|
168
234
|
}
|
|
@@ -353,6 +419,10 @@ class AnimComponentElement extends ComponentElement {
|
|
|
353
419
|
if (!component) {
|
|
354
420
|
return;
|
|
355
421
|
}
|
|
422
|
+
// A clip-set change is also a chance for the skeleton source to have changed shape (a
|
|
423
|
+
// clip child appearing or leaving can accompany a model coming or going) - re-derive the
|
|
424
|
+
// binding root before the reassignment binds against it.
|
|
425
|
+
this._applyRootBone();
|
|
356
426
|
const layer = component.baseLayer;
|
|
357
427
|
const restore = layer ? {
|
|
358
428
|
state: layer.activeState,
|
|
@@ -3,14 +3,16 @@ import type { AudioListenerComponent } from 'playcanvas';
|
|
|
3
3
|
import { ComponentElement } from './component';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* The
|
|
7
|
-
* {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-listener/ | `<pc-listener>`} elements.
|
|
8
|
-
* The
|
|
6
|
+
* The AudioListenerComponentElement interface provides properties and methods for manipulating
|
|
7
|
+
* {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-audio-listener/ | `<pc-audio-listener>`} elements.
|
|
8
|
+
* The AudioListenerComponentElement interface also inherits the properties and methods of the
|
|
9
9
|
* {@link HTMLElement} interface.
|
|
10
10
|
*
|
|
11
|
+
* Engine component: {@link AudioListenerComponent} (`audiolistener`).
|
|
12
|
+
*
|
|
11
13
|
* @category Components
|
|
12
14
|
*/
|
|
13
|
-
class
|
|
15
|
+
class AudioListenerComponentElement extends ComponentElement {
|
|
14
16
|
/** @ignore */
|
|
15
17
|
constructor() {
|
|
16
18
|
super('audiolistener');
|
|
@@ -25,6 +27,6 @@ class ListenerComponentElement extends ComponentElement {
|
|
|
25
27
|
}
|
|
26
28
|
}
|
|
27
29
|
|
|
28
|
-
customElements.define('pc-listener',
|
|
30
|
+
customElements.define('pc-audio-listener', AudioListenerComponentElement);
|
|
29
31
|
|
|
30
|
-
export {
|
|
32
|
+
export { AudioListenerComponentElement };
|
|
@@ -17,6 +17,8 @@ const transitionModes = new Map<'tint' | 'sprite', number>([
|
|
|
17
17
|
* The ButtonComponentElement interface also inherits the properties and methods of the
|
|
18
18
|
* {@link HTMLElement} interface.
|
|
19
19
|
*
|
|
20
|
+
* Engine component: {@link ButtonComponent} (`button`).
|
|
21
|
+
*
|
|
20
22
|
* @category Components
|
|
21
23
|
*/
|
|
22
24
|
class ButtonComponentElement extends ComponentElement {
|
|
@@ -121,7 +123,9 @@ class ButtonComponentElement extends ComponentElement {
|
|
|
121
123
|
|
|
122
124
|
/**
|
|
123
125
|
* Sets the reference (CSS selector, element id or entity name) to the `<pc-entity>` whose image
|
|
124
|
-
* element is used for visual transitions. Defaults to the button's own entity
|
|
126
|
+
* element is used for visual transitions. Defaults to the button's own entity — inside a
|
|
127
|
+
* `<pc-model>`, that is the model's host entity, so supply an explicit reference to target a
|
|
128
|
+
* UI entity instead.
|
|
125
129
|
* @param value - The image entity reference.
|
|
126
130
|
*/
|
|
127
131
|
set image(value: string) {
|
|
@@ -41,6 +41,8 @@ const tonemaps = new Map<'none' | 'linear' | 'filmic' | 'hejl' | 'aces' | 'aces2
|
|
|
41
41
|
* The CameraComponentElement interface also inherits the properties and methods of the
|
|
42
42
|
* {@link HTMLElement} interface.
|
|
43
43
|
*
|
|
44
|
+
* Engine component: {@link CameraComponent} (`camera`).
|
|
45
|
+
*
|
|
44
46
|
* @category Components
|
|
45
47
|
*/
|
|
46
48
|
class CameraComponentElement extends ComponentElement {
|
|
@@ -17,6 +17,8 @@ import { ComponentElement } from './component';
|
|
|
17
17
|
* retargets or rebinds picks up the new node's geometry. An entity with no asset-backed render
|
|
18
18
|
* component warns, and the collider has no shape.
|
|
19
19
|
*
|
|
20
|
+
* Engine component: {@link CollisionComponent} (`collision`).
|
|
21
|
+
*
|
|
20
22
|
* @category Components
|
|
21
23
|
*/
|
|
22
24
|
class CollisionComponentElement extends ComponentElement {
|
|
@@ -64,7 +66,9 @@ class CollisionComponentElement extends ComponentElement {
|
|
|
64
66
|
* no attribute to supply it - so the host's visible geometry, the meaning a mesh collider
|
|
65
67
|
* on a glTF node carries, fills the gap. Runs on every application (so a rebound `pc-node`
|
|
66
68
|
* recomputes it) and on a runtime switch to `type="mesh"`; an explicitly assigned
|
|
67
|
-
* `renderAsset` is never overwritten.
|
|
69
|
+
* `renderAsset` is never overwritten. A `pc-model` host entity never carries a render
|
|
70
|
+
* component (the instantiated content beneath it does), so a mesh collider that should take
|
|
71
|
+
* an asset's geometry belongs on a bound `pc-node`.
|
|
68
72
|
*/
|
|
69
73
|
private _applyMeshGeometryDefault() {
|
|
70
74
|
const component = this.component;
|
|
@@ -109,7 +109,7 @@ class ComponentElement extends AsyncElement {
|
|
|
109
109
|
// It still becomes ready (with a null `component`), so warn rather than fail silently
|
|
110
110
|
const label = this.id ? ` '${this.id}'` : '';
|
|
111
111
|
console.warn(
|
|
112
|
-
`${this.tagName.toLowerCase()}${label} must be a descendant of pc-entity - component not added`
|
|
112
|
+
`${this.tagName.toLowerCase()}${label} must be a descendant of pc-entity, pc-model or pc-node - component not added`
|
|
113
113
|
);
|
|
114
114
|
return;
|
|
115
115
|
}
|
|
@@ -214,8 +214,9 @@ class ComponentElement extends AsyncElement {
|
|
|
214
214
|
|
|
215
215
|
/**
|
|
216
216
|
* The PlayCanvas component instance. `null` until the element is ready, and also for an
|
|
217
|
-
* element that is not a descendant of
|
|
218
|
-
* element's `ready()` promise
|
|
217
|
+
* element that is not a descendant of an entity-fronting element (`<pc-entity>`,
|
|
218
|
+
* `<pc-model>` or `<pc-node>`) — await {@link whenReady} or the element's `ready()` promise
|
|
219
|
+
* before accessing it.
|
|
219
220
|
* @returns The component instance, or `null`.
|
|
220
221
|
*/
|
|
221
222
|
get component(): Component | null {
|
|
@@ -12,6 +12,13 @@ import { ComponentElement } from './component';
|
|
|
12
12
|
* The ElementComponentElement interface also inherits the properties and methods of the
|
|
13
13
|
* {@link HTMLElement} interface.
|
|
14
14
|
*
|
|
15
|
+
* Despite the name, this is not a base class or a generic wrapper: it is the engine's 2D UI
|
|
16
|
+
* component, which gives its host entity a rectangle in a `<pc-screen>` hierarchy that draws
|
|
17
|
+
* either an image, a line of text or nothing (`type="image"`, `"text"` or `"group"`). The tag
|
|
18
|
+
* spells the engine component it adds, as every component element does.
|
|
19
|
+
*
|
|
20
|
+
* Engine component: {@link ElementComponent} (`element`).
|
|
21
|
+
*
|
|
15
22
|
* @category Components
|
|
16
23
|
*/
|
|
17
24
|
class ElementComponentElement extends ComponentElement {
|
|
@@ -11,6 +11,8 @@ import { ComponentElement } from './component';
|
|
|
11
11
|
* The GSplatComponentElement interface also inherits the properties and methods of the
|
|
12
12
|
* {@link HTMLElement} interface.
|
|
13
13
|
*
|
|
14
|
+
* Engine component: {@link GSplatComponent} (`gsplat`).
|
|
15
|
+
*
|
|
14
16
|
* @category Components
|
|
15
17
|
*/
|
|
16
18
|
class GSplatComponentElement extends ComponentElement {
|
|
@@ -26,6 +26,8 @@ type MotionMode = 'locked' | 'limited' | 'free';
|
|
|
26
26
|
* constraint breaks. A broken joint no longer constrains its bodies; calling `refreshFrames()` on
|
|
27
27
|
* the underlying component re-attaches it. Bubbles and is composed.
|
|
28
28
|
*
|
|
29
|
+
* Engine component: {@link JointComponent} (`joint`).
|
|
30
|
+
*
|
|
29
31
|
* @category Components
|
|
30
32
|
*/
|
|
31
33
|
class JointComponentElement extends ComponentElement {
|
|
@@ -6,10 +6,12 @@ import { ComponentElement } from './component';
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* The LayoutChildComponentElement interface provides properties and methods for manipulating
|
|
9
|
-
* {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-
|
|
9
|
+
* {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-layout-child/ | `<pc-layout-child>`} elements.
|
|
10
10
|
* The LayoutChildComponentElement interface also inherits the properties and methods of the
|
|
11
11
|
* {@link HTMLElement} interface.
|
|
12
12
|
*
|
|
13
|
+
* Engine component: {@link LayoutChildComponent} (`layoutchild`).
|
|
14
|
+
*
|
|
13
15
|
* @category Components
|
|
14
16
|
*/
|
|
15
17
|
class LayoutChildComponentElement extends ComponentElement {
|
|
@@ -229,6 +231,6 @@ class LayoutChildComponentElement extends ComponentElement {
|
|
|
229
231
|
}
|
|
230
232
|
}
|
|
231
233
|
|
|
232
|
-
customElements.define('pc-
|
|
234
|
+
customElements.define('pc-layout-child', LayoutChildComponentElement);
|
|
233
235
|
|
|
234
236
|
export { LayoutChildComponentElement };
|
|
@@ -28,10 +28,12 @@ const fittings = new Map<'none' | 'stretch' | 'shrink' | 'both', number>([
|
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
30
|
* The LayoutGroupComponentElement interface provides properties and methods for manipulating
|
|
31
|
-
* {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-
|
|
31
|
+
* {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-layout-group/ | `<pc-layout-group>`} elements.
|
|
32
32
|
* The LayoutGroupComponentElement interface also inherits the properties and methods of the
|
|
33
33
|
* {@link HTMLElement} interface.
|
|
34
34
|
*
|
|
35
|
+
* Engine component: {@link LayoutGroupComponent} (`layoutgroup`).
|
|
36
|
+
*
|
|
35
37
|
* @category Components
|
|
36
38
|
*/
|
|
37
39
|
class LayoutGroupComponentElement extends ComponentElement {
|
|
@@ -304,6 +306,6 @@ class LayoutGroupComponentElement extends ComponentElement {
|
|
|
304
306
|
}
|
|
305
307
|
}
|
|
306
308
|
|
|
307
|
-
customElements.define('pc-
|
|
309
|
+
customElements.define('pc-layout-group', LayoutGroupComponentElement);
|
|
308
310
|
|
|
309
311
|
export { LayoutGroupComponentElement };
|
|
@@ -37,6 +37,8 @@ const shadowTypes = new Map<
|
|
|
37
37
|
* The LightComponentElement interface also inherits the properties and methods of the
|
|
38
38
|
* {@link HTMLElement} interface.
|
|
39
39
|
*
|
|
40
|
+
* Engine component: {@link LightComponent} (`light`).
|
|
41
|
+
*
|
|
40
42
|
* @category Components
|
|
41
43
|
*/
|
|
42
44
|
class LightComponentElement extends ComponentElement {
|
|
@@ -6,10 +6,12 @@ import { ComponentElement } from './component';
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* The ParticleSystemComponentElement interface provides properties and methods for manipulating
|
|
9
|
-
* {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-
|
|
9
|
+
* {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-particle-system/ | `<pc-particle-system>`} elements.
|
|
10
10
|
* The ParticleSystemComponentElement interface also inherits the properties and methods of the
|
|
11
11
|
* {@link HTMLElement} interface.
|
|
12
12
|
*
|
|
13
|
+
* Engine component: {@link ParticleSystemComponent} (`particlesystem`).
|
|
14
|
+
*
|
|
13
15
|
* @category Components
|
|
14
16
|
*/
|
|
15
17
|
class ParticleSystemComponentElement extends ComponentElement {
|
|
@@ -147,6 +149,6 @@ class ParticleSystemComponentElement extends ComponentElement {
|
|
|
147
149
|
}
|
|
148
150
|
}
|
|
149
151
|
|
|
150
|
-
customElements.define('pc-
|
|
152
|
+
customElements.define('pc-particle-system', ParticleSystemComponentElement);
|
|
151
153
|
|
|
152
154
|
export { ParticleSystemComponentElement };
|
|
@@ -15,6 +15,8 @@ import { ComponentElement } from './component';
|
|
|
15
15
|
* to `box`). It does not cover the engine's `asset` render type, since there is no way to supply
|
|
16
16
|
* a render asset here — use `pc-model` for glTF content instead.
|
|
17
17
|
*
|
|
18
|
+
* Engine component: {@link RenderComponent} (`render`).
|
|
19
|
+
*
|
|
18
20
|
* @category Components
|
|
19
21
|
*/
|
|
20
22
|
class RenderComponentElement extends ComponentElement {
|