@playcanvas/web-components 0.8.2 → 0.10.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 +18 -0
- package/dist/app.d.ts +13 -4
- package/dist/asset.d.ts +33 -3
- package/dist/async-element.d.ts +54 -4
- package/dist/components/button-component.d.ts +11 -5
- package/dist/components/camera-component.d.ts +10 -5
- package/dist/components/collision-component.d.ts +10 -5
- package/dist/components/component.d.ts +8 -2
- package/dist/components/element-component.d.ts +18 -13
- package/dist/components/gsplat-component.d.ts +7 -2
- package/dist/components/layoutchild-component.d.ts +7 -2
- package/dist/components/layoutgroup-component.d.ts +22 -16
- package/dist/components/light-component.d.ts +13 -7
- package/dist/components/listener-component.d.ts +6 -1
- package/dist/components/particlesystem-component.d.ts +7 -2
- package/dist/components/render-component.d.ts +14 -5
- package/dist/components/rigidbody-component.d.ts +10 -5
- package/dist/components/screen-component.d.ts +7 -2
- package/dist/components/script-component.d.ts +116 -16
- package/dist/components/script.d.ts +79 -8
- package/dist/components/scrollbar-component.d.ts +11 -5
- package/dist/components/scrollview-component.d.ts +18 -11
- package/dist/components/sound-component.d.ts +7 -2
- package/dist/components/sound-slot.d.ts +17 -4
- package/dist/custom-elements.json +16231 -0
- package/dist/entity.d.ts +45 -3
- package/dist/index.d.ts +3 -2
- package/dist/material.d.ts +976 -4
- package/dist/model.d.ts +6 -1
- package/dist/module.d.ts +15 -0
- package/dist/parse.d.ts +144 -0
- package/dist/pwc.cjs +5735 -2923
- package/dist/pwc.cjs.map +1 -1
- package/dist/pwc.js +5735 -2923
- 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 +2 -0
- package/dist/pwc.min.mjs.map +1 -0
- package/dist/pwc.mjs +5736 -2925
- package/dist/pwc.mjs.map +1 -1
- package/dist/scene.d.ts +15 -6
- package/dist/sky.d.ts +7 -1
- package/dist/vscode.html-custom-data.json +1795 -0
- package/dist/web-types.json +3592 -0
- package/package.json +32 -14
- package/src/app.ts +42 -15
- package/src/asset.ts +78 -8
- package/src/async-element.ts +89 -5
- package/src/components/button-component.ts +31 -24
- package/src/components/camera-component.ts +30 -24
- package/src/components/collision-component.ts +20 -14
- package/src/components/component.ts +33 -14
- package/src/components/element-component.ts +62 -56
- package/src/components/gsplat-component.ts +16 -9
- package/src/components/layoutchild-component.ts +17 -10
- package/src/components/layoutgroup-component.ts +39 -32
- package/src/components/light-component.ts +34 -32
- package/src/components/listener-component.ts +8 -2
- package/src/components/particlesystem-component.ts +10 -4
- package/src/components/render-component.ts +28 -12
- package/src/components/rigidbody-component.ts +22 -16
- package/src/components/screen-component.ts +16 -10
- package/src/components/script-component.ts +512 -125
- package/src/components/script.ts +123 -16
- package/src/components/scrollbar-component.ts +21 -14
- package/src/components/scrollview-component.ts +42 -34
- package/src/components/sound-component.ts +17 -10
- package/src/components/sound-slot.ts +50 -19
- package/src/entity.ts +84 -76
- package/src/index.ts +5 -2
- package/src/material.ts +2432 -62
- package/src/model.ts +8 -2
- package/src/module.ts +16 -0
- package/src/parse.ts +298 -0
- package/src/scene.ts +26 -13
- package/src/sky.ts +36 -18
- package/dist/utils.d.ts +0 -56
- package/src/utils.ts +0 -119
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@playcanvas/web-components",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"author": "PlayCanvas <support@playcanvas.com>",
|
|
5
5
|
"homepage": "https://playcanvas.com",
|
|
6
6
|
"description": "Web Components for the PlayCanvas Engine",
|
|
@@ -25,8 +25,11 @@
|
|
|
25
25
|
},
|
|
26
26
|
"main": "dist/pwc.cjs",
|
|
27
27
|
"module": "dist/pwc.mjs",
|
|
28
|
-
"
|
|
28
|
+
"unpkg": "dist/pwc.min.js",
|
|
29
|
+
"jsdelivr": "dist/pwc.min.js",
|
|
29
30
|
"types": "dist/index.d.ts",
|
|
31
|
+
"customElements": "dist/custom-elements.json",
|
|
32
|
+
"web-types": "dist/web-types.json",
|
|
30
33
|
"exports": {
|
|
31
34
|
".": {
|
|
32
35
|
"types": "./dist/index.d.ts",
|
|
@@ -44,21 +47,31 @@
|
|
|
44
47
|
"scripts": {
|
|
45
48
|
"prebuild": "node -e \"fs.rmSync('dist', { recursive: true, force: true })\"",
|
|
46
49
|
"build": "rollup -c",
|
|
50
|
+
"postbuild": "npm run cem",
|
|
51
|
+
"cem": "cem analyze && node utils/cem/validate.mjs",
|
|
47
52
|
"dev": "concurrently \"npm run watch\" \"npm run serve\"",
|
|
48
53
|
"docs": "typedoc",
|
|
49
|
-
"lint": "eslint examples/js examples/assets/scripts src",
|
|
54
|
+
"lint": "eslint examples/js examples/assets/scripts src test vitest.config.ts",
|
|
50
55
|
"serve": "serve",
|
|
51
|
-
"test": "
|
|
56
|
+
"test": "vitest run",
|
|
57
|
+
"test:watch": "vitest",
|
|
58
|
+
"test:coverage": "vitest run --coverage",
|
|
59
|
+
"test:unit": "vitest run --project unit",
|
|
60
|
+
"test:elements": "vitest run --project elements",
|
|
61
|
+
"test:integration": "vitest run --project integration",
|
|
52
62
|
"publint": "publint",
|
|
53
|
-
"type-check": "
|
|
54
|
-
"type-check:
|
|
63
|
+
"type-check": "npm run type-check:src && npm run type-check:test",
|
|
64
|
+
"type-check:src": "tsc --noEmit -p tsconfig.json",
|
|
65
|
+
"type-check:test": "tsc --noEmit -p tsconfig.test.json",
|
|
66
|
+
"type-check:watch": "npm run type-check:src -- --watch",
|
|
55
67
|
"watch": "rollup -c -w"
|
|
56
68
|
},
|
|
57
69
|
"peerDependencies": {
|
|
58
70
|
"playcanvas": "^2.20.1"
|
|
59
71
|
},
|
|
60
72
|
"devDependencies": {
|
|
61
|
-
"@
|
|
73
|
+
"@custom-elements-manifest/analyzer": "0.11.0",
|
|
74
|
+
"@mediapipe/tasks-vision": "1.0.1",
|
|
62
75
|
"@playcanvas/eslint-config": "2.1.0",
|
|
63
76
|
"@rollup/plugin-commonjs": "29.0.3",
|
|
64
77
|
"@rollup/plugin-node-resolve": "16.0.3",
|
|
@@ -67,20 +80,25 @@
|
|
|
67
80
|
"@tweenjs/tween.js": "25.0.0",
|
|
68
81
|
"@typescript-eslint/eslint-plugin": "8.65.0",
|
|
69
82
|
"@typescript-eslint/parser": "8.65.0",
|
|
70
|
-
"
|
|
83
|
+
"@vitest/coverage-v8": "4.1.10",
|
|
84
|
+
"concurrently": "10.0.4",
|
|
85
|
+
"custom-element-jet-brains-integration": "1.7.0",
|
|
86
|
+
"custom-element-vs-code-integration": "1.5.0",
|
|
71
87
|
"earcut": "3.2.3",
|
|
72
88
|
"eslint": "9.39.5",
|
|
73
89
|
"eslint-import-resolver-typescript": "4.4.5",
|
|
74
|
-
"globals": "17.
|
|
75
|
-
"
|
|
90
|
+
"globals": "17.9.0",
|
|
91
|
+
"jsdom": "30.0.1",
|
|
92
|
+
"mediabunny": "1.52.2",
|
|
76
93
|
"opentype.js": "2.0.0",
|
|
77
|
-
"playcanvas": "2.21.
|
|
78
|
-
"publint": "0.3.
|
|
79
|
-
"rollup": "4.62.
|
|
94
|
+
"playcanvas": "2.21.3",
|
|
95
|
+
"publint": "0.3.22",
|
|
96
|
+
"rollup": "4.62.4",
|
|
80
97
|
"serve": "14.2.6",
|
|
81
98
|
"tslib": "2.8.1",
|
|
82
99
|
"typedoc": "0.28.20",
|
|
83
100
|
"typedoc-plugin-mdn-links": "5.1.1",
|
|
84
|
-
"typescript": "6.0.3"
|
|
101
|
+
"typescript": "6.0.3",
|
|
102
|
+
"vitest": "4.1.10"
|
|
85
103
|
}
|
|
86
104
|
}
|
package/src/app.ts
CHANGED
|
@@ -71,6 +71,7 @@ import { AsyncElement } from './async-element';
|
|
|
71
71
|
import { EntityElement } from './entity';
|
|
72
72
|
import { MaterialElement } from './material';
|
|
73
73
|
import { ModuleElement } from './module';
|
|
74
|
+
import { parseBool, parseEnum } from './parse';
|
|
74
75
|
|
|
75
76
|
/**
|
|
76
77
|
* The AppElement interface provides properties and methods for manipulating
|
|
@@ -86,7 +87,7 @@ class AppElement extends AsyncElement {
|
|
|
86
87
|
|
|
87
88
|
private _alpha = true;
|
|
88
89
|
|
|
89
|
-
private _backend: 'webgpu' | 'webgl2' | 'null' = '
|
|
90
|
+
private _backend: 'webgpu' | 'webgl2' | 'null' = 'webgpu';
|
|
90
91
|
|
|
91
92
|
private _antialias = true;
|
|
92
93
|
|
|
@@ -116,10 +117,16 @@ class AppElement extends AsyncElement {
|
|
|
116
117
|
pointerup: null
|
|
117
118
|
};
|
|
118
119
|
|
|
120
|
+
private _app: AppBase | null = null;
|
|
121
|
+
|
|
119
122
|
/**
|
|
120
|
-
* The PlayCanvas application instance.
|
|
123
|
+
* The PlayCanvas application instance. Available once the element is ready — await
|
|
124
|
+
* {@link whenReady} or the element's `ready()` promise before accessing it.
|
|
125
|
+
* @returns The application instance.
|
|
121
126
|
*/
|
|
122
|
-
app: AppBase
|
|
127
|
+
get app(): AppBase {
|
|
128
|
+
return this._app!;
|
|
129
|
+
}
|
|
123
130
|
|
|
124
131
|
/**
|
|
125
132
|
* Creates a new AppElement instance.
|
|
@@ -227,7 +234,7 @@ class AppElement extends AsyncElement {
|
|
|
227
234
|
createOptions.batchManager = BatchManager;
|
|
228
235
|
createOptions.xr = XrManager;
|
|
229
236
|
|
|
230
|
-
this.
|
|
237
|
+
this._app = new AppBase(this._canvas);
|
|
231
238
|
this.app.init(createOptions);
|
|
232
239
|
|
|
233
240
|
this.app.setCanvasFillMode(FILLMODE_FILL_WINDOW);
|
|
@@ -282,7 +289,7 @@ class AppElement extends AsyncElement {
|
|
|
282
289
|
// Clean up the application
|
|
283
290
|
if (this.app) {
|
|
284
291
|
this.app.destroy();
|
|
285
|
-
this.
|
|
292
|
+
this._app = null;
|
|
286
293
|
}
|
|
287
294
|
|
|
288
295
|
// Remove event listeners
|
|
@@ -314,6 +321,14 @@ class AppElement extends AsyncElement {
|
|
|
314
321
|
['pointermove', 'pointerdown', 'pointerup', 'pointerenter', 'pointerleave'].forEach((type) => {
|
|
315
322
|
this.addEventListener(`${type}:connect`, () => this._onPointerListenerAdded(type));
|
|
316
323
|
this.addEventListener(`${type}:disconnect`, () => this._onPointerListenerRemoved(type));
|
|
324
|
+
|
|
325
|
+
// Attach canvas handlers for listeners registered before this point (e.g. handlers
|
|
326
|
+
// created from onpointer* attributes when their elements were first upgraded)
|
|
327
|
+
const anyListeners = Array.from(this.querySelectorAll<EntityElement>('pc-entity'))
|
|
328
|
+
.some(entity => entity.hasListeners(type));
|
|
329
|
+
if (anyListeners) {
|
|
330
|
+
this._onPointerListenerAdded(type);
|
|
331
|
+
}
|
|
317
332
|
});
|
|
318
333
|
}
|
|
319
334
|
|
|
@@ -332,6 +347,13 @@ class AppElement extends AsyncElement {
|
|
|
332
347
|
pointerdown: null,
|
|
333
348
|
pointerup: null
|
|
334
349
|
};
|
|
350
|
+
this._hasPointerListeners = {
|
|
351
|
+
pointerenter: false,
|
|
352
|
+
pointerleave: false,
|
|
353
|
+
pointerdown: false,
|
|
354
|
+
pointerup: false,
|
|
355
|
+
pointermove: false
|
|
356
|
+
};
|
|
335
357
|
}
|
|
336
358
|
|
|
337
359
|
// New helper to convert CSS coordinates to canvas (picker) coordinates
|
|
@@ -506,7 +528,8 @@ class AppElement extends AsyncElement {
|
|
|
506
528
|
}
|
|
507
529
|
|
|
508
530
|
/**
|
|
509
|
-
* Sets the graphics backend.
|
|
531
|
+
* Sets the graphics backend. Defaults to 'webgpu', which falls back to 'webgl2' if WebGPU
|
|
532
|
+
* is not supported by the browser.
|
|
510
533
|
* @param value - The graphics backend ('webgpu', 'webgl2', or 'null').
|
|
511
534
|
*/
|
|
512
535
|
set backend(value: 'webgpu' | 'webgl2' | 'null') {
|
|
@@ -586,27 +609,25 @@ class AppElement extends AsyncElement {
|
|
|
586
609
|
return ['alpha', 'antialias', 'backend', 'depth', 'stencil', 'high-resolution'];
|
|
587
610
|
}
|
|
588
611
|
|
|
589
|
-
attributeChangedCallback(name: string, _oldValue: string, newValue: string) {
|
|
612
|
+
attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {
|
|
590
613
|
switch (name) {
|
|
591
614
|
case 'alpha':
|
|
592
|
-
this.alpha = newValue
|
|
615
|
+
this.alpha = parseBool(newValue, true);
|
|
593
616
|
break;
|
|
594
617
|
case 'antialias':
|
|
595
|
-
this.antialias = newValue
|
|
618
|
+
this.antialias = parseBool(newValue, true);
|
|
596
619
|
break;
|
|
597
620
|
case 'backend':
|
|
598
|
-
|
|
599
|
-
this.backend = newValue;
|
|
600
|
-
}
|
|
621
|
+
this.backend = parseEnum(newValue, ['webgpu', 'webgl2', 'null'], 'webgpu', name);
|
|
601
622
|
break;
|
|
602
623
|
case 'depth':
|
|
603
|
-
this.depth = newValue
|
|
624
|
+
this.depth = parseBool(newValue, true);
|
|
604
625
|
break;
|
|
605
626
|
case 'high-resolution':
|
|
606
|
-
this.highResolution = newValue
|
|
627
|
+
this.highResolution = parseBool(newValue, true);
|
|
607
628
|
break;
|
|
608
629
|
case 'stencil':
|
|
609
|
-
this.stencil = newValue
|
|
630
|
+
this.stencil = parseBool(newValue, true);
|
|
610
631
|
break;
|
|
611
632
|
}
|
|
612
633
|
}
|
|
@@ -614,4 +635,10 @@ class AppElement extends AsyncElement {
|
|
|
614
635
|
|
|
615
636
|
customElements.define('pc-app', AppElement);
|
|
616
637
|
|
|
638
|
+
declare global {
|
|
639
|
+
interface HTMLElementTagNameMap {
|
|
640
|
+
'pc-app': AppElement;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
617
644
|
export { AppElement };
|
package/src/asset.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { Asset, SPRITE_RENDERMODE_SIMPLE, SPRITE_RENDERMODE_SLICED, SPRITE_RENDERMODE_TILED } from 'playcanvas';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { AsyncElement } from './async-element';
|
|
4
|
+
import { parseBool, parseEnum, parseNumber } from './parse';
|
|
4
5
|
import { MeshoptDecoder } from '../lib/meshopt_decoder.module.js';
|
|
5
6
|
|
|
6
|
-
const renderModes = new Map<
|
|
7
|
+
const renderModes = new Map<'simple' | 'sliced' | 'tiled', number>([
|
|
7
8
|
['simple', SPRITE_RENDERMODE_SIMPLE],
|
|
8
9
|
['sliced', SPRITE_RENDERMODE_SLICED],
|
|
9
10
|
['tiled', SPRITE_RENDERMODE_TILED]
|
|
@@ -15,11 +16,13 @@ const extToType = new Map([
|
|
|
15
16
|
['frag', 'shader'],
|
|
16
17
|
['glb', 'container'],
|
|
17
18
|
['glsl', 'shader'],
|
|
19
|
+
['gltf', 'container'],
|
|
18
20
|
['hdr', 'texture'],
|
|
19
21
|
['html', 'html'],
|
|
20
22
|
['jpg', 'texture'],
|
|
21
23
|
['js', 'script'],
|
|
22
24
|
['json', 'json'],
|
|
25
|
+
['ktx2', 'texture'],
|
|
23
26
|
['mp3', 'audio'],
|
|
24
27
|
['mjs', 'script'],
|
|
25
28
|
['ply', 'gsplat'],
|
|
@@ -74,15 +77,74 @@ const processBufferView = (
|
|
|
74
77
|
* {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-asset/ | `<pc-asset>`} elements.
|
|
75
78
|
* The AssetElement interface also inherits the properties and methods of the
|
|
76
79
|
* {@link HTMLElement} interface.
|
|
80
|
+
*
|
|
81
|
+
* The element becomes ready once the containing application has started and the asset is in the
|
|
82
|
+
* state declared by the markup: loaded for preloaded assets (even if loading failed — check the
|
|
83
|
+
* asset's `resource`), or registered and awaiting a load for `lazy` assets. Elements inserted
|
|
84
|
+
* while the application is running are created and registered on insertion, and begin loading
|
|
85
|
+
* immediately unless `lazy`. A `pc-asset` must be a direct child of `pc-app` — elements placed
|
|
86
|
+
* elsewhere, or with an unsupported asset type, never become ready.
|
|
87
|
+
*
|
|
88
|
+
* Apart from `lazy`, these attributes are read once when the asset is created, so changing them
|
|
89
|
+
* later has no effect.
|
|
90
|
+
*
|
|
91
|
+
* @attribute {string} id - The identifier used to reference the asset from other elements.
|
|
92
|
+
* @attribute {string} src - The URL of the asset to load.
|
|
93
|
+
* @attribute {string} type - The asset type. Inferred from the `src` file extension when omitted.
|
|
94
|
+
* @attribute {string} data - Additional asset data, as a JSON object.
|
|
95
|
+
* @attribute {string} atlas - For a `sprite` asset, the `id` of the texture atlas asset it uses.
|
|
96
|
+
* The atlas must be declared before the sprite.
|
|
97
|
+
* @attribute {string} frame-keys - For a `sprite` asset, the atlas frame keys it uses, separated
|
|
98
|
+
* by spaces or commas.
|
|
99
|
+
* @attribute {number} pixels-per-unit - For a `sprite` asset, the number of pixels per world unit.
|
|
100
|
+
* @attribute {'simple' | 'sliced' | 'tiled'} render-mode - For a `sprite` asset, how the sprite is
|
|
101
|
+
* rendered when resized.
|
|
77
102
|
*/
|
|
78
|
-
class AssetElement extends
|
|
103
|
+
class AssetElement extends AsyncElement {
|
|
79
104
|
private _lazy: boolean = false;
|
|
80
105
|
|
|
81
106
|
/**
|
|
82
|
-
* The asset that is loaded.
|
|
107
|
+
* The asset that is loaded. Available once the element is ready — await
|
|
108
|
+
* {@link whenReady} or the element's `ready()` promise before accessing it.
|
|
83
109
|
*/
|
|
84
110
|
asset: Asset | null = null;
|
|
85
111
|
|
|
112
|
+
async connectedCallback() {
|
|
113
|
+
const appElement = this.closestApp;
|
|
114
|
+
if (!appElement) return;
|
|
115
|
+
|
|
116
|
+
// Assets must be direct children of pc-app (matches the boot query ':scope > pc-asset')
|
|
117
|
+
if (this.parentElement !== appElement) {
|
|
118
|
+
console.warn(`pc-asset '${this.getAttribute('id') ?? this.getAttribute('src')}' must be a direct child of pc-app - asset not created`);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
await appElement.ready();
|
|
123
|
+
|
|
124
|
+
// The element may have been removed or re-parented while waiting for the app
|
|
125
|
+
if (!this.isConnected || this.parentElement !== appElement) return;
|
|
126
|
+
|
|
127
|
+
// Assets present at startup are created by AppElement's boot; this branch handles
|
|
128
|
+
// elements inserted (or re-inserted) after the app is already running
|
|
129
|
+
if (!this.asset) {
|
|
130
|
+
const app = appElement.app;
|
|
131
|
+
if (!app) return; // pc-app is re-connecting; its own boot will create this asset
|
|
132
|
+
|
|
133
|
+
this.createAsset();
|
|
134
|
+
if (this.asset) {
|
|
135
|
+
app.assets.add(this.asset); // add() auto-loads when preload is true
|
|
136
|
+
if (!this.lazy) {
|
|
137
|
+
app.assets.load(this.asset);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Never ready if createAsset failed (unsupported asset type)
|
|
143
|
+
if (this.asset) {
|
|
144
|
+
this._onReady();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
86
148
|
disconnectedCallback() {
|
|
87
149
|
this.destroyAsset();
|
|
88
150
|
}
|
|
@@ -167,12 +229,12 @@ class AssetElement extends HTMLElement {
|
|
|
167
229
|
|
|
168
230
|
const pixelsPerUnit = this.getAttribute('pixels-per-unit');
|
|
169
231
|
if (pixelsPerUnit !== null) {
|
|
170
|
-
data.pixelsPerUnit =
|
|
232
|
+
data.pixelsPerUnit = parseNumber(pixelsPerUnit, 1, 'pixels-per-unit');
|
|
171
233
|
}
|
|
172
234
|
|
|
173
235
|
const renderMode = this.getAttribute('render-mode');
|
|
174
236
|
if (renderMode !== null) {
|
|
175
|
-
data.renderMode = parseEnum(renderMode, renderModes,
|
|
237
|
+
data.renderMode = renderModes.get(parseEnum(renderMode, renderModes, 'simple', 'render-mode'));
|
|
176
238
|
}
|
|
177
239
|
|
|
178
240
|
// Apply engine defaults for any values not supplied.
|
|
@@ -187,6 +249,8 @@ class AssetElement extends HTMLElement {
|
|
|
187
249
|
|
|
188
250
|
destroyAsset() {
|
|
189
251
|
if (this.asset) {
|
|
252
|
+
// Deregister first so unload() can still notify the registry
|
|
253
|
+
this.asset.registry?.remove(this.asset);
|
|
190
254
|
this.asset.unload();
|
|
191
255
|
this.asset = null;
|
|
192
256
|
}
|
|
@@ -220,13 +284,19 @@ class AssetElement extends HTMLElement {
|
|
|
220
284
|
return ['lazy'];
|
|
221
285
|
}
|
|
222
286
|
|
|
223
|
-
attributeChangedCallback(name: string, _oldValue: string,
|
|
287
|
+
attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {
|
|
224
288
|
if (name === 'lazy') {
|
|
225
|
-
this.lazy =
|
|
289
|
+
this.lazy = parseBool(newValue, false);
|
|
226
290
|
}
|
|
227
291
|
}
|
|
228
292
|
}
|
|
229
293
|
|
|
230
294
|
customElements.define('pc-asset', AssetElement);
|
|
231
295
|
|
|
296
|
+
declare global {
|
|
297
|
+
interface HTMLElementTagNameMap {
|
|
298
|
+
'pc-asset': AssetElement;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
232
302
|
export { AssetElement };
|
package/src/async-element.ts
CHANGED
|
@@ -3,6 +3,9 @@ import { EntityElement } from './entity';
|
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Base class for all PlayCanvas Web Components that initialize asynchronously.
|
|
6
|
+
*
|
|
7
|
+
* @fires {CustomEvent} ready - Fired once the element is fully initialized. Bubbles and is
|
|
8
|
+
* composed.
|
|
6
9
|
*/
|
|
7
10
|
class AsyncElement extends HTMLElement {
|
|
8
11
|
private _readyPromise: Promise<void>;
|
|
@@ -26,16 +29,18 @@ class AsyncElement extends HTMLElement {
|
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
/**
|
|
29
|
-
* Called when the element is fully initialized and ready.
|
|
30
|
-
*
|
|
32
|
+
* Called when the element is fully initialized and ready. Subclasses should call this when
|
|
33
|
+
* they're ready. Resolves the ready promise and dispatches a bubbling, composed `ready`
|
|
34
|
+
* event.
|
|
31
35
|
*/
|
|
32
36
|
protected _onReady() {
|
|
33
37
|
this._readyResolve();
|
|
34
|
-
this.dispatchEvent(new CustomEvent('ready'));
|
|
38
|
+
this.dispatchEvent(new CustomEvent('ready', { bubbles: true, composed: true }));
|
|
35
39
|
}
|
|
36
40
|
|
|
37
41
|
/**
|
|
38
|
-
* Returns a promise that resolves with this element when it's ready.
|
|
42
|
+
* Returns a promise that resolves with this element when it's ready. This is the low-level
|
|
43
|
+
* primitive underlying {@link whenReady}, which is the recommended way to wait for elements.
|
|
39
44
|
* @returns A promise that resolves with this element when it's ready.
|
|
40
45
|
*/
|
|
41
46
|
ready(): Promise<this> {
|
|
@@ -43,4 +48,83 @@ class AsyncElement extends HTMLElement {
|
|
|
43
48
|
}
|
|
44
49
|
}
|
|
45
50
|
|
|
46
|
-
|
|
51
|
+
/**
|
|
52
|
+
* A union of the tag names of all elements that initialize asynchronously (i.e. elements whose
|
|
53
|
+
* classes extend {@link AsyncElement}).
|
|
54
|
+
*/
|
|
55
|
+
type AsyncElementTagName = {
|
|
56
|
+
[K in keyof HTMLElementTagNameMap]: HTMLElementTagNameMap[K] extends AsyncElement ? K : never
|
|
57
|
+
}[keyof HTMLElementTagNameMap];
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Waits for the first element matching the given tag name to be fully initialized. Note that the
|
|
61
|
+
* promise never settles if the element cannot finish initializing (for example, a `<pc-script>`
|
|
62
|
+
* that is not a direct child of `<pc-scripts>`). A component element outside a `<pc-entity>` is
|
|
63
|
+
* the exception: it still becomes ready, but its `component` is `null`. Either way, a misplaced
|
|
64
|
+
* element logs a warning naming the parent it requires.
|
|
65
|
+
* @param target - The tag name of the element to wait for (e.g. `'pc-app'`).
|
|
66
|
+
* @returns A promise that resolves with the element once it's ready.
|
|
67
|
+
* @example
|
|
68
|
+
* const { app } = await whenReady('pc-app');
|
|
69
|
+
*/
|
|
70
|
+
function whenReady<K extends AsyncElementTagName>(target: K): Promise<HTMLElementTagNameMap[K]>;
|
|
71
|
+
/**
|
|
72
|
+
* Waits for the given element to be fully initialized. Note that the promise never settles if
|
|
73
|
+
* the element cannot finish initializing (for example, an element that is never added to the
|
|
74
|
+
* document).
|
|
75
|
+
* @param target - The element to wait for.
|
|
76
|
+
* @returns A promise that resolves with the element once it's ready.
|
|
77
|
+
* @example
|
|
78
|
+
* const appElement = document.createElement('pc-app');
|
|
79
|
+
* document.body.appendChild(appElement);
|
|
80
|
+
* const { app } = await whenReady(appElement);
|
|
81
|
+
*/
|
|
82
|
+
function whenReady<T extends AsyncElement>(target: T): Promise<T>;
|
|
83
|
+
/**
|
|
84
|
+
* Waits for the first element matching the given CSS selector to be fully initialized. Note that
|
|
85
|
+
* the promise never settles if the element cannot finish initializing (for example, a `<pc-script>`
|
|
86
|
+
* that is not a direct child of `<pc-scripts>`). A component element outside a `<pc-entity>` is
|
|
87
|
+
* the exception: it still becomes ready, but its `component` is `null`. Either way, a misplaced
|
|
88
|
+
* element logs a warning naming the parent it requires.
|
|
89
|
+
* @param target - A CSS selector matching the element to wait for (e.g. `'#my-app'`).
|
|
90
|
+
* @returns A promise that resolves with the element once it's ready.
|
|
91
|
+
* @example
|
|
92
|
+
* // In TypeScript, supply the element type when using an arbitrary selector
|
|
93
|
+
* const { entity } = await whenReady<EntityElement>('pc-entity[name="camera"]');
|
|
94
|
+
*/
|
|
95
|
+
function whenReady<T extends AsyncElement = AsyncElement, S extends string = string>(
|
|
96
|
+
target: S extends Exclude<keyof HTMLElementTagNameMap, AsyncElementTagName> ? never : S
|
|
97
|
+
): Promise<T>;
|
|
98
|
+
async function whenReady(target: string | AsyncElement): Promise<AsyncElement> {
|
|
99
|
+
let element: Element | null;
|
|
100
|
+
if (typeof target === 'string') {
|
|
101
|
+
if (document.readyState === 'loading') {
|
|
102
|
+
await new Promise((resolve) => {
|
|
103
|
+
document.addEventListener('DOMContentLoaded', resolve, { once: true });
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
element = document.querySelector(target);
|
|
109
|
+
} catch {
|
|
110
|
+
throw new Error(`whenReady: '${target}' is not a valid CSS selector`);
|
|
111
|
+
}
|
|
112
|
+
if (!element) {
|
|
113
|
+
throw new Error(`whenReady: no element found matching '${target}'`);
|
|
114
|
+
}
|
|
115
|
+
} else {
|
|
116
|
+
element = target;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (!(element instanceof AsyncElement)) {
|
|
120
|
+
const description = element instanceof Element ? `<${element.tagName.toLowerCase()}>` : String(target);
|
|
121
|
+
throw new Error(`whenReady: ${description} does not initialize asynchronously`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
await element.ready();
|
|
125
|
+
|
|
126
|
+
return element;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export { AsyncElement, whenReady };
|
|
130
|
+
export type { AsyncElementTagName };
|
|
@@ -2,9 +2,9 @@ import { BUTTON_TRANSITION_MODE_SPRITE_CHANGE, BUTTON_TRANSITION_MODE_TINT, Butt
|
|
|
2
2
|
|
|
3
3
|
import { AssetElement } from '../asset';
|
|
4
4
|
import { ComponentElement } from './component';
|
|
5
|
-
import { getEntity, parseColor, parseEnum, parseVec4 } from '../
|
|
5
|
+
import { getEntity, parseBool, parseColor, parseEnum, parseNumber, parseVec4 } from '../parse';
|
|
6
6
|
|
|
7
|
-
const transitionModes = new Map<
|
|
7
|
+
const transitionModes = new Map<'tint' | 'sprite', number>([
|
|
8
8
|
['tint', BUTTON_TRANSITION_MODE_TINT],
|
|
9
9
|
['sprite', BUTTON_TRANSITION_MODE_SPRITE_CHANGE]
|
|
10
10
|
]);
|
|
@@ -24,7 +24,7 @@ class ButtonComponentElement extends ComponentElement {
|
|
|
24
24
|
|
|
25
25
|
private _hitPadding = new Vec4(0, 0, 0, 0);
|
|
26
26
|
|
|
27
|
-
private _transitionMode:
|
|
27
|
+
private _transitionMode: 'tint' | 'sprite' = 'tint';
|
|
28
28
|
|
|
29
29
|
private _hoverTint = new Color(1, 1, 1, 1);
|
|
30
30
|
|
|
@@ -55,7 +55,7 @@ class ButtonComponentElement extends ComponentElement {
|
|
|
55
55
|
const data: Record<string, any> = {
|
|
56
56
|
active: this._active,
|
|
57
57
|
hitPadding: this._hitPadding,
|
|
58
|
-
transitionMode: this._transitionMode,
|
|
58
|
+
transitionMode: transitionModes.get(this._transitionMode),
|
|
59
59
|
hoverTint: this._hoverTint,
|
|
60
60
|
pressedTint: this._pressedTint,
|
|
61
61
|
inactiveTint: this._inactiveTint,
|
|
@@ -94,8 +94,8 @@ class ButtonComponentElement extends ComponentElement {
|
|
|
94
94
|
* Gets the underlying PlayCanvas button component.
|
|
95
95
|
* @returns The button component.
|
|
96
96
|
*/
|
|
97
|
-
get component(): ButtonComponent
|
|
98
|
-
return super.component as ButtonComponent
|
|
97
|
+
get component(): ButtonComponent {
|
|
98
|
+
return super.component as ButtonComponent;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
/**
|
|
@@ -158,13 +158,14 @@ class ButtonComponentElement extends ComponentElement {
|
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
/**
|
|
161
|
-
* Sets how the button reacts to being hovered/pressed. Can be `tint`
|
|
161
|
+
* Sets how the button reacts to being hovered/pressed. Can be `tint` or `sprite`. Defaults to
|
|
162
|
+
* `tint`.
|
|
162
163
|
* @param value - The transition mode.
|
|
163
164
|
*/
|
|
164
|
-
set transitionMode(value:
|
|
165
|
+
set transitionMode(value: 'tint' | 'sprite') {
|
|
165
166
|
this._transitionMode = value;
|
|
166
167
|
if (this.component) {
|
|
167
|
-
this.component.transitionMode = value;
|
|
168
|
+
this.component.transitionMode = transitionModes.get(value) ?? BUTTON_TRANSITION_MODE_TINT;
|
|
168
169
|
}
|
|
169
170
|
}
|
|
170
171
|
|
|
@@ -395,51 +396,51 @@ class ButtonComponentElement extends ComponentElement {
|
|
|
395
396
|
];
|
|
396
397
|
}
|
|
397
398
|
|
|
398
|
-
attributeChangedCallback(name: string, _oldValue: string, newValue: string) {
|
|
399
|
+
attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {
|
|
399
400
|
super.attributeChangedCallback(name, _oldValue, newValue);
|
|
400
401
|
|
|
401
402
|
switch (name) {
|
|
402
403
|
case 'active':
|
|
403
|
-
this.active = newValue
|
|
404
|
+
this.active = parseBool(newValue, true);
|
|
404
405
|
break;
|
|
405
406
|
case 'image':
|
|
406
|
-
this.image = newValue;
|
|
407
|
+
this.image = newValue ?? '';
|
|
407
408
|
break;
|
|
408
409
|
case 'hit-padding':
|
|
409
|
-
this.hitPadding = parseVec4(newValue);
|
|
410
|
+
this.hitPadding = parseVec4(newValue, Vec4.ZERO, name);
|
|
410
411
|
break;
|
|
411
412
|
case 'transition-mode':
|
|
412
|
-
this.transitionMode = parseEnum(newValue, transitionModes,
|
|
413
|
+
this.transitionMode = parseEnum(newValue, transitionModes, 'tint', name);
|
|
413
414
|
break;
|
|
414
415
|
case 'hover-tint':
|
|
415
|
-
this.hoverTint = parseColor(newValue);
|
|
416
|
+
this.hoverTint = parseColor(newValue, Color.WHITE, name);
|
|
416
417
|
break;
|
|
417
418
|
case 'pressed-tint':
|
|
418
|
-
this.pressedTint = parseColor(newValue);
|
|
419
|
+
this.pressedTint = parseColor(newValue, Color.WHITE, name);
|
|
419
420
|
break;
|
|
420
421
|
case 'inactive-tint':
|
|
421
|
-
this.inactiveTint = parseColor(newValue);
|
|
422
|
+
this.inactiveTint = parseColor(newValue, Color.WHITE, name);
|
|
422
423
|
break;
|
|
423
424
|
case 'fade-duration':
|
|
424
|
-
this.fadeDuration =
|
|
425
|
+
this.fadeDuration = parseNumber(newValue, 0, name);
|
|
425
426
|
break;
|
|
426
427
|
case 'hover-sprite-asset':
|
|
427
|
-
this.hoverSpriteAsset = newValue;
|
|
428
|
+
this.hoverSpriteAsset = newValue ?? '';
|
|
428
429
|
break;
|
|
429
430
|
case 'hover-sprite-frame':
|
|
430
|
-
this.hoverSpriteFrame =
|
|
431
|
+
this.hoverSpriteFrame = parseNumber(newValue, 0, name);
|
|
431
432
|
break;
|
|
432
433
|
case 'pressed-sprite-asset':
|
|
433
|
-
this.pressedSpriteAsset = newValue;
|
|
434
|
+
this.pressedSpriteAsset = newValue ?? '';
|
|
434
435
|
break;
|
|
435
436
|
case 'pressed-sprite-frame':
|
|
436
|
-
this.pressedSpriteFrame =
|
|
437
|
+
this.pressedSpriteFrame = parseNumber(newValue, 0, name);
|
|
437
438
|
break;
|
|
438
439
|
case 'inactive-sprite-asset':
|
|
439
|
-
this.inactiveSpriteAsset = newValue;
|
|
440
|
+
this.inactiveSpriteAsset = newValue ?? '';
|
|
440
441
|
break;
|
|
441
442
|
case 'inactive-sprite-frame':
|
|
442
|
-
this.inactiveSpriteFrame =
|
|
443
|
+
this.inactiveSpriteFrame = parseNumber(newValue, 0, name);
|
|
443
444
|
break;
|
|
444
445
|
}
|
|
445
446
|
}
|
|
@@ -447,4 +448,10 @@ class ButtonComponentElement extends ComponentElement {
|
|
|
447
448
|
|
|
448
449
|
customElements.define('pc-button', ButtonComponentElement);
|
|
449
450
|
|
|
451
|
+
declare global {
|
|
452
|
+
interface HTMLElementTagNameMap {
|
|
453
|
+
'pc-button': ButtonComponentElement;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
450
457
|
export { ButtonComponentElement };
|