@playcanvas/web-components 0.11.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/app.d.ts +31 -49
- package/dist/asset.d.ts +152 -12
- package/dist/async-element.d.ts +26 -9
- package/dist/components/button-component.d.ts +3 -7
- package/dist/components/camera-component.d.ts +3 -7
- package/dist/components/collision-component.d.ts +19 -7
- package/dist/components/component.d.ts +41 -4
- package/dist/components/element-component.d.ts +4 -8
- package/dist/components/gsplat-component.d.ts +2 -7
- package/dist/components/layoutchild-component.d.ts +2 -7
- package/dist/components/layoutgroup-component.d.ts +3 -7
- package/dist/components/light-component.d.ts +3 -7
- package/dist/components/listener-component.d.ts +1 -6
- package/dist/components/particlesystem-component.d.ts +2 -7
- package/dist/components/render-component.d.ts +2 -7
- package/dist/components/rigidbody-component.d.ts +3 -7
- package/dist/components/screen-component.d.ts +3 -7
- package/dist/components/script-component.d.ts +8 -20
- package/dist/components/script.d.ts +2 -22
- package/dist/components/scrollbar-component.d.ts +2 -7
- package/dist/components/scrollview-component.d.ts +3 -7
- package/dist/components/sound-component.d.ts +2 -7
- package/dist/components/sound-slot.d.ts +8 -6
- package/dist/custom-elements.json +5824 -10581
- package/dist/entity-base.d.ts +67 -0
- package/dist/entity.d.ts +7 -48
- package/dist/index.d.ts +41 -1
- package/dist/material.d.ts +14 -13
- package/dist/model.d.ts +43 -5
- package/dist/module.d.ts +0 -6
- package/dist/node.d.ts +253 -0
- package/dist/parse.d.ts +2 -1
- package/dist/pwc.cjs +1830 -274
- package/dist/pwc.cjs.map +1 -1
- package/dist/pwc.js +1830 -274
- 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 +1830 -276
- package/dist/pwc.mjs.map +1 -1
- package/dist/scene.d.ts +4 -7
- package/dist/sky.d.ts +13 -5
- package/dist/vscode.html-custom-data.json +148 -26
- package/dist/web-types.json +894 -581
- package/package.json +9 -8
- package/src/app.ts +163 -88
- package/src/asset.ts +472 -36
- package/src/async-element.ts +39 -12
- package/src/components/button-component.ts +5 -9
- package/src/components/camera-component.ts +24 -10
- package/src/components/collision-component.ts +61 -15
- package/src/components/component.ts +151 -11
- package/src/components/element-component.ts +26 -30
- package/src/components/gsplat-component.ts +4 -9
- package/src/components/layoutchild-component.ts +4 -9
- package/src/components/layoutgroup-component.ts +14 -9
- package/src/components/light-component.ts +42 -12
- package/src/components/listener-component.ts +1 -7
- package/src/components/particlesystem-component.ts +7 -15
- package/src/components/render-component.ts +5 -10
- package/src/components/rigidbody-component.ts +23 -16
- package/src/components/screen-component.ts +5 -9
- package/src/components/script-component.ts +108 -46
- package/src/components/script.ts +38 -33
- package/src/components/scrollbar-component.ts +6 -16
- package/src/components/scrollview-component.ts +16 -11
- package/src/components/sound-component.ts +10 -15
- package/src/components/sound-slot.ts +30 -20
- package/src/entity-base.ts +136 -0
- package/src/entity.ts +47 -118
- package/src/index.ts +50 -1
- package/src/loading-bar.ts +8 -8
- package/src/material.ts +65 -39
- package/src/model.ts +140 -17
- package/src/module.ts +8 -7
- package/src/node.ts +715 -0
- package/src/parse.ts +62 -17
- package/src/scene.ts +12 -9
- package/src/sky.ts +50 -10
package/src/async-element.ts
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
|
-
import { AppElement } from './app';
|
|
2
|
-
import {
|
|
1
|
+
import type { AppElement } from './app';
|
|
2
|
+
import type { EntityBaseElement } from './entity-base';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Base class for all PlayCanvas Web Components that initialize asynchronously.
|
|
6
6
|
*
|
|
7
|
-
* @fires {CustomEvent} ready - Fired
|
|
8
|
-
*
|
|
7
|
+
* @fires {CustomEvent} ready - Fired when the element is fully initialized — once per readiness
|
|
8
|
+
* cycle, so an element that is torn down and re-initialized (for example by removing and
|
|
9
|
+
* re-inserting it) fires it again. Bubbles and is composed.
|
|
9
10
|
*/
|
|
10
11
|
class AsyncElement extends HTMLElement {
|
|
11
12
|
private _readyPromise: Promise<void>;
|
|
12
13
|
|
|
13
14
|
private _readyResolve!: () => void;
|
|
14
15
|
|
|
16
|
+
private _readyResolved = false;
|
|
17
|
+
|
|
15
18
|
/** @ignore */
|
|
16
19
|
constructor() {
|
|
17
20
|
super();
|
|
@@ -26,31 +29,55 @@ class AsyncElement extends HTMLElement {
|
|
|
26
29
|
* @returns The closest app element, or `null`.
|
|
27
30
|
*/
|
|
28
31
|
get closestApp(): AppElement | null {
|
|
29
|
-
return this.parentElement?.closest('pc-app') as AppElement | null ?? null;
|
|
32
|
+
return (this.parentElement?.closest('pc-app') as AppElement | null) ?? null;
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
/**
|
|
33
|
-
* The nearest ancestor
|
|
34
|
-
* ancestor. The search starts at the parent, so an element
|
|
35
|
-
*
|
|
36
|
+
* The nearest ancestor element that fronts an entity — `<pc-entity>` or `<pc-node>` — or
|
|
37
|
+
* `null` if this element has no such ancestor. The search starts at the parent, so an element
|
|
38
|
+
* never resolves to itself.
|
|
39
|
+
* @returns The closest entity-fronting element, or `null`.
|
|
36
40
|
*/
|
|
37
|
-
get closestEntity():
|
|
38
|
-
return this.parentElement?.closest('pc-entity') as
|
|
41
|
+
get closestEntity(): EntityBaseElement | null {
|
|
42
|
+
return (this.parentElement?.closest('pc-entity, pc-node') as EntityBaseElement | null) ?? null;
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
/**
|
|
42
46
|
* Called when the element is fully initialized and ready. Subclasses should call this when
|
|
43
47
|
* they're ready. Resolves the ready promise and dispatches a bubbling, composed `ready`
|
|
44
|
-
* event.
|
|
48
|
+
* event. Signals at most once per readiness cycle: a repeat call before {@link _resetReady}
|
|
49
|
+
* has re-armed the promise does nothing.
|
|
45
50
|
*/
|
|
46
51
|
protected _onReady() {
|
|
52
|
+
if (this._readyResolved) return;
|
|
53
|
+
this._readyResolved = true;
|
|
47
54
|
this._readyResolve();
|
|
48
55
|
this.dispatchEvent(new CustomEvent('ready', { bubbles: true, composed: true }));
|
|
49
56
|
}
|
|
50
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Returns the ready promise to its pending state. Subclasses should call this when the
|
|
60
|
+
* resource their readiness announced is torn down (typically from `disconnectedCallback`),
|
|
61
|
+
* so that a later re-initialization can signal readiness again. Does nothing while the
|
|
62
|
+
* promise is still pending — an in-flight waiter carries over to the next readiness cycle
|
|
63
|
+
* rather than being stranded on a promise nothing will ever resolve.
|
|
64
|
+
*/
|
|
65
|
+
protected _resetReady() {
|
|
66
|
+
if (!this._readyResolved) return;
|
|
67
|
+
this._readyResolved = false;
|
|
68
|
+
this._readyPromise = new Promise<void>((resolve) => {
|
|
69
|
+
this._readyResolve = resolve;
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
51
73
|
/**
|
|
52
74
|
* Returns a promise that resolves with this element when it's ready. This is the low-level
|
|
53
75
|
* primitive underlying {@link whenReady}, which is the recommended way to wait for elements.
|
|
76
|
+
*
|
|
77
|
+
* Readiness tracks the element's current lifecycle: once a ready element is torn down (for
|
|
78
|
+
* example by removing it from the document), this returns a fresh promise that resolves when
|
|
79
|
+
* the element is next ready. A promise obtained earlier stays resolved — call this again
|
|
80
|
+
* after re-inserting an element rather than reusing a promise from before its removal.
|
|
54
81
|
* @returns A promise that resolves with this element when it's ready.
|
|
55
82
|
*/
|
|
56
83
|
ready(): Promise<this> {
|
|
@@ -63,7 +90,7 @@ class AsyncElement extends HTMLElement {
|
|
|
63
90
|
* classes extend {@link AsyncElement}).
|
|
64
91
|
*/
|
|
65
92
|
type AsyncElementTagName = {
|
|
66
|
-
[K in keyof HTMLElementTagNameMap]: HTMLElementTagNameMap[K] extends AsyncElement ? K : never
|
|
93
|
+
[K in keyof HTMLElementTagNameMap]: HTMLElementTagNameMap[K] extends AsyncElement ? K : never;
|
|
67
94
|
}[keyof HTMLElementTagNameMap];
|
|
68
95
|
|
|
69
96
|
/**
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { ButtonComponent } from 'playcanvas';
|
|
2
|
+
import { BUTTON_TRANSITION_MODE_SPRITE_CHANGE, BUTTON_TRANSITION_MODE_TINT, Color, Vec4 } from 'playcanvas';
|
|
2
3
|
|
|
3
4
|
import { AssetElement } from '../asset';
|
|
4
|
-
import { ComponentElement } from './component';
|
|
5
5
|
import { getEntity, parseBool, parseColor, parseEnum, parseNumber, parseVec4 } from '../parse';
|
|
6
6
|
|
|
7
|
+
import { ComponentElement } from './component';
|
|
8
|
+
|
|
7
9
|
const transitionModes = new Map<'tint' | 'sprite', number>([
|
|
8
10
|
['tint', BUTTON_TRANSITION_MODE_TINT],
|
|
9
11
|
['sprite', BUTTON_TRANSITION_MODE_SPRITE_CHANGE]
|
|
@@ -51,7 +53,7 @@ class ButtonComponentElement extends ComponentElement {
|
|
|
51
53
|
super('button');
|
|
52
54
|
}
|
|
53
55
|
|
|
54
|
-
getInitialComponentData() {
|
|
56
|
+
protected getInitialComponentData() {
|
|
55
57
|
const data: Record<string, any> = {
|
|
56
58
|
active: this._active,
|
|
57
59
|
hitPadding: this._hitPadding,
|
|
@@ -448,10 +450,4 @@ class ButtonComponentElement extends ComponentElement {
|
|
|
448
450
|
|
|
449
451
|
customElements.define('pc-button', ButtonComponentElement);
|
|
450
452
|
|
|
451
|
-
declare global {
|
|
452
|
-
interface HTMLElementTagNameMap {
|
|
453
|
-
'pc-button': ButtonComponentElement;
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
|
|
457
453
|
export { ButtonComponentElement };
|
|
@@ -1,8 +1,25 @@
|
|
|
1
|
-
import { CameraComponent
|
|
1
|
+
import type { CameraComponent } from 'playcanvas';
|
|
2
|
+
import {
|
|
3
|
+
Color,
|
|
4
|
+
Vec4,
|
|
5
|
+
GAMMA_NONE,
|
|
6
|
+
GAMMA_SRGB,
|
|
7
|
+
PROJECTION_ORTHOGRAPHIC,
|
|
8
|
+
PROJECTION_PERSPECTIVE,
|
|
9
|
+
TONEMAP_LINEAR,
|
|
10
|
+
TONEMAP_FILMIC,
|
|
11
|
+
TONEMAP_NEUTRAL,
|
|
12
|
+
TONEMAP_ACES2,
|
|
13
|
+
TONEMAP_ACES,
|
|
14
|
+
TONEMAP_HEJL,
|
|
15
|
+
TONEMAP_NONE,
|
|
16
|
+
XRTYPE_VR
|
|
17
|
+
} from 'playcanvas';
|
|
2
18
|
|
|
3
|
-
import { ComponentElement } from './component';
|
|
4
19
|
import { parseBool, parseColor, parseEnum, parseNumber, parseVec4 } from '../parse';
|
|
5
20
|
|
|
21
|
+
import { ComponentElement } from './component';
|
|
22
|
+
|
|
6
23
|
const projections = new Map<'perspective' | 'orthographic', number>([
|
|
7
24
|
['perspective', PROJECTION_PERSPECTIVE],
|
|
8
25
|
['orthographic', PROJECTION_ORTHOGRAPHIC]
|
|
@@ -68,7 +85,7 @@ class CameraComponentElement extends ComponentElement {
|
|
|
68
85
|
super('camera');
|
|
69
86
|
}
|
|
70
87
|
|
|
71
|
-
getInitialComponentData() {
|
|
88
|
+
protected getInitialComponentData() {
|
|
72
89
|
return {
|
|
73
90
|
clearColor: this._clearColor,
|
|
74
91
|
clearColorBuffer: this._clearColorBuffer,
|
|
@@ -101,7 +118,10 @@ class CameraComponentElement extends ComponentElement {
|
|
|
101
118
|
* @param type - The type of XR mode to start.
|
|
102
119
|
* @param space - The space to start the camera in.
|
|
103
120
|
*/
|
|
104
|
-
startXr(
|
|
121
|
+
startXr(
|
|
122
|
+
type: 'immersive-ar' | 'immersive-vr',
|
|
123
|
+
space: 'bounded-floor' | 'local' | 'local-floor' | 'unbounded' | 'viewer'
|
|
124
|
+
) {
|
|
105
125
|
if (this.component && this.xrAvailable) {
|
|
106
126
|
this.component.startXr(type, space, {
|
|
107
127
|
callback: (err: any) => {
|
|
@@ -559,10 +579,4 @@ class CameraComponentElement extends ComponentElement {
|
|
|
559
579
|
|
|
560
580
|
customElements.define('pc-camera', CameraComponentElement);
|
|
561
581
|
|
|
562
|
-
declare global {
|
|
563
|
-
interface HTMLElementTagNameMap {
|
|
564
|
-
'pc-camera': CameraComponentElement;
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
|
|
568
582
|
export { CameraComponentElement };
|
|
@@ -1,30 +1,38 @@
|
|
|
1
|
-
import { CollisionComponent
|
|
1
|
+
import type { CollisionComponent } from 'playcanvas';
|
|
2
|
+
import { Quat, Vec3 } from 'playcanvas';
|
|
2
3
|
|
|
3
|
-
import { ComponentElement } from './component';
|
|
4
4
|
import { parseBool, parseEnum, parseNumber, parseQuat, parseVec3 } from '../parse';
|
|
5
5
|
|
|
6
|
+
import { ComponentElement } from './component';
|
|
7
|
+
|
|
6
8
|
/**
|
|
7
9
|
* The CollisionComponentElement interface provides properties and methods for manipulating
|
|
8
10
|
* {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-collision/ | `<pc-collision>`} elements.
|
|
9
11
|
* The CollisionComponentElement interface also inherits the properties and methods of the
|
|
10
12
|
* {@link HTMLElement} interface.
|
|
11
13
|
*
|
|
14
|
+
* For `type="mesh"`, the collision geometry defaults to the host entity's own render component
|
|
15
|
+
* (its render asset) — a collider matching the visible mesh, which is what a mesh collider on a
|
|
16
|
+
* glTF node means. The default resolves each time the component applies, so a `pc-node` that
|
|
17
|
+
* retargets or rebinds picks up the new node's geometry. An entity with no asset-backed render
|
|
18
|
+
* component warns, and the collider has no shape.
|
|
19
|
+
*
|
|
12
20
|
* @category Components
|
|
13
21
|
*/
|
|
14
22
|
class CollisionComponentElement extends ComponentElement {
|
|
15
23
|
private _angularOffset: Quat = new Quat();
|
|
16
24
|
|
|
17
|
-
private _axis
|
|
25
|
+
private _axis = 1;
|
|
18
26
|
|
|
19
|
-
private _convexHull
|
|
27
|
+
private _convexHull = false;
|
|
20
28
|
|
|
21
29
|
private _halfExtents: Vec3 = new Vec3(0.5, 0.5, 0.5);
|
|
22
30
|
|
|
23
|
-
private _height
|
|
31
|
+
private _height = 2;
|
|
24
32
|
|
|
25
33
|
private _linearOffset: Vec3 = new Vec3();
|
|
26
34
|
|
|
27
|
-
private _radius
|
|
35
|
+
private _radius = 0.5;
|
|
28
36
|
|
|
29
37
|
private _type: 'box' | 'capsule' | 'compound' | 'cone' | 'cylinder' | 'mesh' | 'sphere' = 'box';
|
|
30
38
|
|
|
@@ -33,7 +41,7 @@ class CollisionComponentElement extends ComponentElement {
|
|
|
33
41
|
super('collision');
|
|
34
42
|
}
|
|
35
43
|
|
|
36
|
-
getInitialComponentData() {
|
|
44
|
+
protected getInitialComponentData() {
|
|
37
45
|
return {
|
|
38
46
|
axis: this._axis,
|
|
39
47
|
angularOffset: this._angularOffset,
|
|
@@ -46,6 +54,34 @@ class CollisionComponentElement extends ComponentElement {
|
|
|
46
54
|
};
|
|
47
55
|
}
|
|
48
56
|
|
|
57
|
+
protected initComponent() {
|
|
58
|
+
this._applyMeshGeometryDefault();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Defaults a mesh collider's geometry to the host entity's own render component. The
|
|
63
|
+
* engine's mesh collider only works with explicitly supplied geometry, and the element has
|
|
64
|
+
* no attribute to supply it - so the host's visible geometry, the meaning a mesh collider
|
|
65
|
+
* on a glTF node carries, fills the gap. Runs on every application (so a rebound `pc-node`
|
|
66
|
+
* recomputes it) and on a runtime switch to `type="mesh"`; an explicitly assigned
|
|
67
|
+
* `renderAsset` is never overwritten.
|
|
68
|
+
*/
|
|
69
|
+
private _applyMeshGeometryDefault() {
|
|
70
|
+
const component = this.component;
|
|
71
|
+
if (!component || this._type !== 'mesh' || component.renderAsset !== null) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const asset = component.entity.render?.asset ?? null;
|
|
76
|
+
if (asset === null) {
|
|
77
|
+
console.warn(
|
|
78
|
+
`pc-collision type="mesh" on '${component.entity.name}' found no asset-backed render component to take geometry from - collider has no shape`
|
|
79
|
+
);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
component.renderAsset = asset;
|
|
83
|
+
}
|
|
84
|
+
|
|
49
85
|
/**
|
|
50
86
|
* Gets the underlying PlayCanvas collision component.
|
|
51
87
|
* @returns The collision component.
|
|
@@ -135,6 +171,7 @@ class CollisionComponentElement extends ComponentElement {
|
|
|
135
171
|
this._type = value;
|
|
136
172
|
if (this.component) {
|
|
137
173
|
this.component.type = value;
|
|
174
|
+
this._applyMeshGeometryDefault();
|
|
138
175
|
}
|
|
139
176
|
}
|
|
140
177
|
|
|
@@ -143,7 +180,17 @@ class CollisionComponentElement extends ComponentElement {
|
|
|
143
180
|
}
|
|
144
181
|
|
|
145
182
|
static get observedAttributes() {
|
|
146
|
-
return [
|
|
183
|
+
return [
|
|
184
|
+
...super.observedAttributes,
|
|
185
|
+
'angular-offset',
|
|
186
|
+
'axis',
|
|
187
|
+
'convex-hull',
|
|
188
|
+
'half-extents',
|
|
189
|
+
'height',
|
|
190
|
+
'linear-offset',
|
|
191
|
+
'radius',
|
|
192
|
+
'type'
|
|
193
|
+
];
|
|
147
194
|
}
|
|
148
195
|
|
|
149
196
|
attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {
|
|
@@ -172,7 +219,12 @@ class CollisionComponentElement extends ComponentElement {
|
|
|
172
219
|
this.radius = parseNumber(newValue, 0.5, name);
|
|
173
220
|
break;
|
|
174
221
|
case 'type':
|
|
175
|
-
this.type = parseEnum(
|
|
222
|
+
this.type = parseEnum(
|
|
223
|
+
newValue,
|
|
224
|
+
['box', 'capsule', 'compound', 'cone', 'cylinder', 'mesh', 'sphere'],
|
|
225
|
+
'box',
|
|
226
|
+
name
|
|
227
|
+
);
|
|
176
228
|
break;
|
|
177
229
|
}
|
|
178
230
|
}
|
|
@@ -180,10 +232,4 @@ class CollisionComponentElement extends ComponentElement {
|
|
|
180
232
|
|
|
181
233
|
customElements.define('pc-collision', CollisionComponentElement);
|
|
182
234
|
|
|
183
|
-
declare global {
|
|
184
|
-
interface HTMLElementTagNameMap {
|
|
185
|
-
'pc-collision': CollisionComponentElement;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
235
|
export { CollisionComponentElement };
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { Component } from 'playcanvas';
|
|
1
|
+
import type { Component } from 'playcanvas';
|
|
2
2
|
|
|
3
|
-
import { AppElement } from '../app';
|
|
3
|
+
import type { AppElement } from '../app';
|
|
4
4
|
import { AsyncElement } from '../async-element';
|
|
5
|
+
import type { EntityBaseElement } from '../entity-base';
|
|
5
6
|
import { parseBool } from '../parse';
|
|
6
7
|
|
|
7
8
|
/**
|
|
@@ -18,6 +19,27 @@ class ComponentElement extends AsyncElement {
|
|
|
18
19
|
|
|
19
20
|
private _appElement: AppElement | null = null;
|
|
20
21
|
|
|
22
|
+
/**
|
|
23
|
+
* The element hosting this component, held so the host's readiness cycles can be observed
|
|
24
|
+
* even after `closestEntity` would no longer resolve (during teardown).
|
|
25
|
+
*/
|
|
26
|
+
private _hostElement: EntityBaseElement | null = null;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The listener re-applying this component when the host's readiness cycles. Held for
|
|
30
|
+
* removal on disconnect.
|
|
31
|
+
*/
|
|
32
|
+
private _hostReadyListener: EventListener | null = null;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Incremented on every connect and disconnect. connectedCallback captures the value on entry
|
|
36
|
+
* and abandons itself wherever it resumes from an await if the value has moved on — so a
|
|
37
|
+
* callback whose element was removed cannot act on a torn-down tree, and one whose element
|
|
38
|
+
* was removed and re-inserted (which runs a callback of its own) cannot add the component a
|
|
39
|
+
* second time.
|
|
40
|
+
*/
|
|
41
|
+
private _connectionGeneration = 0;
|
|
42
|
+
|
|
21
43
|
/**
|
|
22
44
|
* Creates a new ComponentElement instance.
|
|
23
45
|
*
|
|
@@ -30,38 +52,155 @@ class ComponentElement extends AsyncElement {
|
|
|
30
52
|
this._componentName = componentName;
|
|
31
53
|
}
|
|
32
54
|
|
|
33
|
-
|
|
34
|
-
|
|
55
|
+
/**
|
|
56
|
+
* Returns the data the component is created with. Overridden by subclasses to supply the
|
|
57
|
+
* initial values of their cached properties.
|
|
58
|
+
*
|
|
59
|
+
* @returns The initial component data.
|
|
60
|
+
*/
|
|
61
|
+
protected getInitialComponentData() {
|
|
35
62
|
return {};
|
|
36
63
|
}
|
|
37
64
|
|
|
38
|
-
|
|
65
|
+
/**
|
|
66
|
+
* Creates the component on the host's current entity, removing it first from a previous
|
|
67
|
+
* entity that is still alive (a retargeted `<pc-node>` moves its decorations with it). When
|
|
68
|
+
* the entity already has a component of this type — a glTF node arriving with its authored
|
|
69
|
+
* `render` component, say — warns and leaves `component` null. The element-level warning is
|
|
70
|
+
* load-bearing: the engine's own duplicate-addComponent warning is Debug-stripped from
|
|
71
|
+
* production builds, which would otherwise leave a silent null.
|
|
72
|
+
*/
|
|
73
|
+
private _applyComponent() {
|
|
74
|
+
const entity = this._hostElement?.entity ?? null;
|
|
75
|
+
if (this._component && this._component.entity === entity) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// A retarget leaves the previous component on a still-live entity - remove it so the
|
|
80
|
+
// decoration follows the element, or vanishes with a dissolved binding. A destroyed
|
|
81
|
+
// entity took its components with it.
|
|
82
|
+
const previous = this._component;
|
|
83
|
+
if (previous?.entity && previous.entity.c[this._componentName] === previous) {
|
|
84
|
+
previous.entity.removeComponent(this._componentName);
|
|
85
|
+
}
|
|
86
|
+
this._component = null;
|
|
87
|
+
|
|
88
|
+
if (!entity) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (entity.c[this._componentName]) {
|
|
93
|
+
const label = this.id ? ` '${this.id}'` : '';
|
|
94
|
+
console.warn(
|
|
95
|
+
`${this.tagName.toLowerCase()}${label} - '${entity.name}' already has a '${this._componentName}' component - component not added`
|
|
96
|
+
);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
this._component = entity.addComponent(this._componentName, this.getInitialComponentData());
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private async _addComponent() {
|
|
104
|
+
const generation = this._connectionGeneration;
|
|
105
|
+
|
|
39
106
|
const entityElement = this.closestEntity;
|
|
40
107
|
if (!entityElement) {
|
|
41
108
|
// A component can only exist on an entity, so an element placed outside one is inert.
|
|
42
109
|
// It still becomes ready (with a null `component`), so warn rather than fail silently
|
|
43
110
|
const label = this.id ? ` '${this.id}'` : '';
|
|
44
|
-
console.warn(
|
|
111
|
+
console.warn(
|
|
112
|
+
`${this.tagName.toLowerCase()}${label} must be a descendant of pc-entity - component not added`
|
|
113
|
+
);
|
|
45
114
|
return;
|
|
46
115
|
}
|
|
47
116
|
|
|
48
117
|
await entityElement.ready();
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
118
|
+
|
|
119
|
+
// The element may have been removed, or removed and re-inserted, while the entity became
|
|
120
|
+
// ready — the component belongs to the connection that owns the current generation.
|
|
121
|
+
if (generation !== this._connectionGeneration) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
this._hostElement = entityElement;
|
|
126
|
+
this._applyComponent();
|
|
127
|
+
|
|
128
|
+
// Re-apply when the host's readiness cycles without this element disconnecting: a
|
|
129
|
+
// `<pc-node>` rebinding after its model reloads or retargets, or a re-created entity.
|
|
130
|
+
// The 'ready' event bubbles, so events from descendants pass through this host - only
|
|
131
|
+
// the host's own cycles count. Readiness is cycled here too, so decorations one level
|
|
132
|
+
// down re-apply the same way.
|
|
133
|
+
this._hostReadyListener = (event: Event) => {
|
|
134
|
+
if (event.target !== this._hostElement) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (generation !== this._connectionGeneration) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
this._hostCycled();
|
|
141
|
+
};
|
|
142
|
+
entityElement.addEventListener('ready', this._hostReadyListener);
|
|
52
143
|
}
|
|
53
144
|
|
|
54
|
-
|
|
145
|
+
/**
|
|
146
|
+
* Re-evaluates this component against the host's current entity: applied to a new entity,
|
|
147
|
+
* moved from a still-live old one, or removed when the host no longer fronts an entity at
|
|
148
|
+
* all. Readiness follows - it cycles with a re-application and stays unresolved while the
|
|
149
|
+
* host is unbound. Called by the host-ready listener, and directly by a `<pc-node>`
|
|
150
|
+
* dissolving its binding: the one transition that fires no ready event to ride.
|
|
151
|
+
*
|
|
152
|
+
* @internal
|
|
153
|
+
*/
|
|
154
|
+
_hostCycled() {
|
|
155
|
+
this._resetReady();
|
|
156
|
+
this._applyComponent();
|
|
157
|
+
if (this._hostElement?.entity) {
|
|
158
|
+
this.initComponent();
|
|
159
|
+
this._onReady();
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Configures the newly added component. Overridden by subclasses whose setup goes beyond
|
|
165
|
+
* the initial data — child-element handling, asset resolution and the like.
|
|
166
|
+
*/
|
|
167
|
+
protected initComponent() {
|
|
168
|
+
// optional hook
|
|
169
|
+
}
|
|
55
170
|
|
|
56
171
|
async connectedCallback() {
|
|
172
|
+
const generation = ++this._connectionGeneration;
|
|
173
|
+
|
|
57
174
|
this._appElement = this.closestApp ?? null;
|
|
58
175
|
await this._appElement?.ready();
|
|
59
|
-
|
|
176
|
+
|
|
177
|
+
// The element may have been removed, or removed and re-inserted, while the application
|
|
178
|
+
// became ready. A re-insertion runs a connectedCallback of its own, so a stale resume
|
|
179
|
+
// must not add the component alongside it.
|
|
180
|
+
if (generation !== this._connectionGeneration) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
await this._addComponent();
|
|
185
|
+
|
|
186
|
+
if (generation !== this._connectionGeneration) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
60
190
|
this.initComponent();
|
|
61
191
|
this._onReady();
|
|
62
192
|
}
|
|
63
193
|
|
|
64
194
|
disconnectedCallback() {
|
|
195
|
+
// Invalidate any connectedCallback still suspended on an await
|
|
196
|
+
this._connectionGeneration++;
|
|
197
|
+
|
|
198
|
+
if (this._hostElement && this._hostReadyListener) {
|
|
199
|
+
this._hostElement.removeEventListener('ready', this._hostReadyListener);
|
|
200
|
+
}
|
|
201
|
+
this._hostElement = null;
|
|
202
|
+
this._hostReadyListener = null;
|
|
203
|
+
|
|
65
204
|
// Remove the component when the element is disconnected. Skip this when the owning
|
|
66
205
|
// application has already been destroyed — removing a <pc-app> disconnects it before
|
|
67
206
|
// its children, taking the component systems with it.
|
|
@@ -70,6 +209,7 @@ class ComponentElement extends AsyncElement {
|
|
|
70
209
|
}
|
|
71
210
|
this._component = null;
|
|
72
211
|
this._appElement = null;
|
|
212
|
+
this._resetReady();
|
|
73
213
|
}
|
|
74
214
|
|
|
75
215
|
/**
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { ElementComponent } from 'playcanvas';
|
|
2
|
+
import { Color, Vec2, Vec4 } from 'playcanvas';
|
|
2
3
|
|
|
3
4
|
import { AssetElement } from '../asset';
|
|
4
|
-
import { ComponentElement } from './component';
|
|
5
5
|
import { parseBool, parseColor, parseEnum, parseNumber, parseVec2, parseVec4 } from '../parse';
|
|
6
6
|
|
|
7
|
+
import { ComponentElement } from './component';
|
|
8
|
+
|
|
7
9
|
/**
|
|
8
10
|
* The ElementComponentElement interface provides properties and methods for manipulating
|
|
9
11
|
* {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-element/ | `<pc-element>`} elements.
|
|
@@ -15,62 +17,62 @@ import { parseBool, parseColor, parseEnum, parseNumber, parseVec2, parseVec4 } f
|
|
|
15
17
|
class ElementComponentElement extends ComponentElement {
|
|
16
18
|
private _anchor: Vec4 = new Vec4(0.5, 0.5, 0.5, 0.5);
|
|
17
19
|
|
|
18
|
-
private _autoWidth
|
|
20
|
+
private _autoWidth = true;
|
|
19
21
|
|
|
20
|
-
private _autoHeight
|
|
22
|
+
private _autoHeight = true;
|
|
21
23
|
|
|
22
|
-
private _autoFitWidth
|
|
24
|
+
private _autoFitWidth = false;
|
|
23
25
|
|
|
24
|
-
private _autoFitHeight
|
|
26
|
+
private _autoFitHeight = false;
|
|
25
27
|
|
|
26
28
|
private _color: Color = new Color(1, 1, 1, 1);
|
|
27
29
|
|
|
28
|
-
private _enableMarkup
|
|
30
|
+
private _enableMarkup = false;
|
|
29
31
|
|
|
30
|
-
private _fontAsset
|
|
32
|
+
private _fontAsset = '';
|
|
31
33
|
|
|
32
|
-
private _fontSize
|
|
34
|
+
private _fontSize = 32;
|
|
33
35
|
|
|
34
|
-
private _maxFontSize
|
|
36
|
+
private _maxFontSize = 32;
|
|
35
37
|
|
|
36
|
-
private _minFontSize
|
|
38
|
+
private _minFontSize = 8;
|
|
37
39
|
|
|
38
|
-
private _height
|
|
40
|
+
private _height = 0;
|
|
39
41
|
|
|
40
|
-
private _lineHeight
|
|
42
|
+
private _lineHeight = 32;
|
|
41
43
|
|
|
42
44
|
private _margin: Vec4 | null = null;
|
|
43
45
|
|
|
44
|
-
private _mask
|
|
46
|
+
private _mask = false;
|
|
45
47
|
|
|
46
|
-
private _opacity
|
|
48
|
+
private _opacity = 1;
|
|
47
49
|
|
|
48
50
|
private _pivot: Vec2 = new Vec2(0.5, 0.5);
|
|
49
51
|
|
|
50
52
|
private _pixelsPerUnit: number | null = null;
|
|
51
53
|
|
|
52
|
-
private _spriteAsset
|
|
54
|
+
private _spriteAsset = '';
|
|
53
55
|
|
|
54
|
-
private _spriteFrame
|
|
56
|
+
private _spriteFrame = 0;
|
|
55
57
|
|
|
56
|
-
private _text
|
|
58
|
+
private _text = '';
|
|
57
59
|
|
|
58
|
-
private _textureAsset
|
|
60
|
+
private _textureAsset = '';
|
|
59
61
|
|
|
60
62
|
private _type: 'group' | 'image' | 'text' = 'group';
|
|
61
63
|
|
|
62
|
-
private _useInput
|
|
64
|
+
private _useInput = false;
|
|
63
65
|
|
|
64
|
-
private _width
|
|
66
|
+
private _width = 0;
|
|
65
67
|
|
|
66
|
-
private _wrapLines
|
|
68
|
+
private _wrapLines = false;
|
|
67
69
|
|
|
68
70
|
/** @ignore */
|
|
69
71
|
constructor() {
|
|
70
72
|
super('element');
|
|
71
73
|
}
|
|
72
74
|
|
|
73
|
-
initComponent() {
|
|
75
|
+
protected initComponent() {
|
|
74
76
|
const component = this.component as any;
|
|
75
77
|
if (!component) {
|
|
76
78
|
return;
|
|
@@ -89,7 +91,7 @@ class ElementComponentElement extends ComponentElement {
|
|
|
89
91
|
component._dirtifyMask?.();
|
|
90
92
|
}
|
|
91
93
|
|
|
92
|
-
getInitialComponentData() {
|
|
94
|
+
protected getInitialComponentData() {
|
|
93
95
|
const data: Record<string, any> = {
|
|
94
96
|
anchor: this._anchor,
|
|
95
97
|
autoWidth: this._autoWidth,
|
|
@@ -774,10 +776,4 @@ class ElementComponentElement extends ComponentElement {
|
|
|
774
776
|
|
|
775
777
|
customElements.define('pc-element', ElementComponentElement);
|
|
776
778
|
|
|
777
|
-
declare global {
|
|
778
|
-
interface HTMLElementTagNameMap {
|
|
779
|
-
'pc-element': ElementComponentElement;
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
|
|
783
779
|
export { ElementComponentElement };
|