@playcanvas/web-components 0.12.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/README.md +1 -1
  2. package/dist/app.d.cts +246 -0
  3. package/dist/app.d.ts +26 -3
  4. package/dist/asset.d.cts +218 -0
  5. package/dist/asset.d.ts +1 -1
  6. package/dist/async-element.d.cts +103 -0
  7. package/dist/async-element.d.ts +6 -4
  8. package/dist/colors.d.cts +1 -0
  9. package/dist/components/button-component.d.cts +186 -0
  10. package/dist/components/button-component.d.ts +1 -1
  11. package/dist/components/camera-component.d.cts +253 -0
  12. package/dist/components/camera-component.d.ts +1 -1
  13. package/dist/components/collision-component.d.cts +73 -0
  14. package/dist/components/collision-component.d.ts +1 -1
  15. package/dist/components/component.d.cts +82 -0
  16. package/dist/components/component.d.ts +1 -1
  17. package/dist/components/element-component.d.cts +316 -0
  18. package/dist/components/element-component.d.ts +1 -1
  19. package/dist/components/gsplat-component.d.cts +108 -0
  20. package/dist/components/gsplat-component.d.ts +1 -1
  21. package/dist/components/layoutchild-component.d.cts +110 -0
  22. package/dist/components/layoutchild-component.d.ts +1 -1
  23. package/dist/components/layoutgroup-component.d.cts +136 -0
  24. package/dist/components/layoutgroup-component.d.ts +1 -1
  25. package/dist/components/light-component.d.cts +264 -0
  26. package/dist/components/light-component.d.ts +1 -1
  27. package/dist/components/listener-component.d.cts +20 -0
  28. package/dist/components/listener-component.d.ts +1 -1
  29. package/dist/components/particlesystem-component.d.cts +52 -0
  30. package/dist/components/particlesystem-component.d.ts +1 -1
  31. package/dist/components/render-component.d.cts +76 -0
  32. package/dist/components/render-component.d.ts +1 -1
  33. package/dist/components/rigidbody-component.d.cts +88 -0
  34. package/dist/components/rigidbody-component.d.ts +1 -1
  35. package/dist/components/screen-component.d.cts +70 -0
  36. package/dist/components/screen-component.d.ts +1 -1
  37. package/dist/components/script-component.d.cts +163 -0
  38. package/dist/components/script-component.d.ts +1 -1
  39. package/dist/components/script.d.cts +94 -0
  40. package/dist/components/script.d.ts +1 -1
  41. package/dist/components/scrollbar-component.d.cts +69 -0
  42. package/dist/components/scrollbar-component.d.ts +1 -1
  43. package/dist/components/scrollview-component.d.cts +178 -0
  44. package/dist/components/scrollview-component.d.ts +1 -1
  45. package/dist/components/sound-component.d.cts +108 -0
  46. package/dist/components/sound-component.d.ts +1 -1
  47. package/dist/components/sound-slot.d.cts +134 -0
  48. package/dist/components/sound-slot.d.ts +2 -2
  49. package/dist/custom-elements.json +79 -21
  50. package/dist/entity-base.d.cts +67 -0
  51. package/dist/entity-base.d.ts +2 -2
  52. package/dist/entity.d.cts +131 -0
  53. package/dist/entity.d.ts +1 -1
  54. package/dist/index.d.cts +79 -0
  55. package/dist/index.d.ts +33 -33
  56. package/dist/loading-bar.d.cts +35 -0
  57. package/dist/material.d.cts +1011 -0
  58. package/dist/model.d.cts +72 -0
  59. package/dist/model.d.ts +1 -1
  60. package/dist/module.d.cts +29 -0
  61. package/dist/module.d.ts +16 -10
  62. package/dist/node.d.cts +253 -0
  63. package/dist/node.d.ts +1 -1
  64. package/dist/parse.d.cts +147 -0
  65. package/dist/pwc.cjs +141 -41
  66. package/dist/pwc.cjs.map +1 -1
  67. package/dist/pwc.js +142 -42
  68. package/dist/pwc.js.map +1 -1
  69. package/dist/pwc.min.js +1 -1
  70. package/dist/pwc.min.js.map +1 -1
  71. package/dist/pwc.min.mjs +1 -1
  72. package/dist/pwc.min.mjs.map +1 -1
  73. package/dist/pwc.mjs +142 -42
  74. package/dist/pwc.mjs.map +1 -1
  75. package/dist/scene.d.cts +117 -0
  76. package/dist/scene.d.ts +1 -1
  77. package/dist/sky.d.cts +121 -0
  78. package/dist/sky.d.ts +1 -1
  79. package/dist/vscode.html-custom-data.json +2 -2
  80. package/dist/web-types.json +41 -5
  81. package/package.json +16 -7
  82. package/src/app.ts +119 -30
  83. package/src/async-element.ts +4 -2
  84. package/src/loading-bar.ts +2 -2
  85. package/src/module.ts +39 -20
package/dist/pwc.js CHANGED
@@ -1,7 +1,7 @@
1
1
  (function (global, factory) {
2
2
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('playcanvas')) :
3
3
  typeof define === 'function' && define.amd ? define(['exports', 'playcanvas'], factory) :
4
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.pd = {}, global.pc));
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.pwc = {}, global.pc));
5
5
  })(this, (function (exports, playcanvas) { 'use strict';
6
6
 
7
7
  /**
@@ -112,12 +112,18 @@
112
112
 
113
113
  /**
114
114
  * The ModuleElement interface provides properties and methods for manipulating
115
- * {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-module/ | `<pc-module>`} elements.
116
- * The ModuleElement interface also inherits the properties and methods of the
117
- * {@link HTMLElement} interface.
115
+ * {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-module/ | `<pc-module>`}
116
+ * elements. The ModuleElement interface also inherits the properties and methods of the
117
+ * {@link AsyncElement} interface.
118
+ *
119
+ * The attributes are read once, when the module starts loading - on the element's first
120
+ * connection, or earlier if a containing `<pc-app>` boots first and collects it - so changing
121
+ * them later has no effect. The element becomes ready once the module has loaded. WebAssembly
122
+ * modules configure engine-global state that never unloads, so readiness is not re-armed by
123
+ * removing the element, and a re-inserted element does not load again.
118
124
  *
119
- * Note that these attributes are read once when the element is created, so changing them later
120
- * has no effect.
125
+ * A `<pc-module>` without a `name` warns and never becomes ready; a containing `<pc-app>` still
126
+ * boots.
121
127
  *
122
128
  * @attribute {string} name - The name of the WebAssembly module to configure, e.g. `Basis` or
123
129
  * `Ammo`.
@@ -126,19 +132,22 @@
126
132
  * @attribute {string} fallback - The URL of the module's asm.js fallback script, used when
127
133
  * WebAssembly is unavailable.
128
134
  */
129
- class ModuleElement extends HTMLElement {
130
- loadPromise;
131
- /** @ignore */
132
- constructor() {
133
- super();
134
- this.loadPromise = this.loadModule();
135
+ class ModuleElement extends AsyncElement {
136
+ _loadPromise = null;
137
+ connectedCallback() {
138
+ this._getLoadPromise();
135
139
  }
136
- async loadModule() {
140
+ async _loadModule() {
137
141
  const name = this.getAttribute('name');
138
- const glueUrl = this.getAttribute('glue');
139
- const wasmUrl = this.getAttribute('wasm');
140
- const fallbackUrl = this.getAttribute('fallback');
141
- const config = { glueUrl, wasmUrl, fallbackUrl };
142
+ if (!name) {
143
+ console.warn("pc-module requires a 'name' attribute - no module was configured");
144
+ return;
145
+ }
146
+ const config = {
147
+ glueUrl: this.getAttribute('glue') ?? undefined,
148
+ wasmUrl: this.getAttribute('wasm') ?? undefined,
149
+ fallbackUrl: this.getAttribute('fallback') ?? undefined
150
+ };
142
151
  if (name === 'Basis') {
143
152
  playcanvas.basisInitialize(config);
144
153
  }
@@ -148,16 +157,22 @@
148
157
  playcanvas.WasmModule.getInstance(name, () => resolve());
149
158
  });
150
159
  }
160
+ this._onReady();
151
161
  }
152
162
  /**
153
- * Returns the promise that settles when the module has loaded. Awaited by the containing
154
- * `<pc-app>` element before it creates its graphics device.
163
+ * Returns the promise that settles when the module has loaded, starting the load if it has
164
+ * not already started - a containing `<pc-app>` boots in document order, so it may collect
165
+ * this element before the element's own connectedCallback has run. A missing `name` resolves
166
+ * the promise without configuring anything, so a misconfigured module never blocks the app.
155
167
  *
156
168
  * @returns The load promise.
157
169
  * @internal
158
170
  */
159
171
  _getLoadPromise() {
160
- return this.loadPromise;
172
+ if (!this._loadPromise) {
173
+ this._loadPromise = this._loadModule();
174
+ }
175
+ return this._loadPromise;
161
176
  }
162
177
  }
163
178
  customElements.define('pc-module', ModuleElement);
@@ -187,9 +202,9 @@
187
202
  this._track.setAttribute('aria-label', 'Loading');
188
203
  this._track.setAttribute('aria-valuemin', '0');
189
204
  this._track.setAttribute('aria-valuemax', '100');
190
- // Fixed positioning matches the canvas, which always fills the window (FILLMODE_FILL_WINDOW)
205
+ // Anchored to the pc-app element, which the library's base styles make a positioned box
191
206
  this._track.style.cssText = [
192
- 'position: fixed',
207
+ 'position: absolute',
193
208
  'top: 0',
194
209
  'left: 0',
195
210
  'width: 100%',
@@ -701,16 +716,44 @@
701
716
 
702
717
  /** The pointer event types the application synthesizes on `<pc-entity>` elements via picking. */
703
718
  const pointerEventTypes = ['pointermove', 'pointerdown', 'pointerup', 'pointerenter', 'pointerleave'];
719
+ /**
720
+ * Gives `pc-app` the sizing contract of a replaced element (`<video>`, `<img>`): a block-level
721
+ * box that the page's CSS sizes, defaulting to the canvas's own 300x150 intrinsic size, with the
722
+ * canvas and loading bar anchored to it. `:where()` keeps every declaration at zero specificity,
723
+ * so any page rule - however plain - overrides these defaults.
724
+ */
725
+ const ensureBaseStyles = () => {
726
+ const id = 'pc-app-styles';
727
+ if (document.getElementById(id)) {
728
+ return;
729
+ }
730
+ const style = document.createElement('style');
731
+ style.id = id;
732
+ style.textContent = ':where(pc-app) { display: block; position: relative; width: 300px; height: 150px; }';
733
+ document.head.appendChild(style);
734
+ };
704
735
  /**
705
736
  * The AppElement interface provides properties and methods for manipulating
706
737
  * {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-app/ | `<pc-app>`} elements.
707
738
  * The AppElement interface also inherits the properties and methods of the
708
739
  * {@link HTMLElement} interface.
709
740
  *
741
+ * The element is sized like a replaced element such as `<video>`: a block-level box that the
742
+ * page's CSS controls, 300x150 by default. The application's canvas always fills the element,
743
+ * and the drawing buffer resolution follows the element's size (capped by `max-pixel-ratio`),
744
+ * tracked live via a ResizeObserver — so the element can be embedded at any size, resized by
745
+ * its container, or made fullscreen with ordinary CSS such as `width: 100vw; height: 100dvh`.
746
+ *
710
747
  * @fires {ProgressEvent} progress - Fired while the application preloads its assets. `loaded` and
711
748
  * `total` are asset counts, not bytes, and an asset that fails to load still counts as loaded.
712
749
  * Fired at least once per boot, and the final event always has `loaded` equal to `total`. Does
713
750
  * not bubble.
751
+ *
752
+ * @fires {ErrorEvent} error - Fired when the application cannot boot because no graphics device
753
+ * could be created (for example, a browser with WebGL disabled). `message` names the requested
754
+ * backends and `error` holds the underlying failure. The element never becomes ready
755
+ * and `app` stays `null` — listen for this event to show a fallback UI. Removing the element and
756
+ * re-inserting it retries the boot with its current attributes. Does not bubble.
714
757
  */
715
758
  class AppElement extends AsyncElement {
716
759
  /**
@@ -770,6 +813,12 @@
770
813
  };
771
814
  _app = null;
772
815
  _loadProgress = 0;
816
+ /**
817
+ * Tracks the element's box so the drawing buffer and picker follow it. Created per boot once
818
+ * the application exists, and disconnected on teardown. `null` where ResizeObserver is
819
+ * unavailable (jsdom), where the boot-time resolution set is the only sizing that happens.
820
+ */
821
+ _resizeObserver = null;
773
822
  /**
774
823
  * The PlayCanvas application instance. `null` until the element is ready, and again once it
775
824
  * has been removed from the document — await {@link whenReady} or the element's `ready()`
@@ -796,8 +845,6 @@
796
845
  */
797
846
  constructor() {
798
847
  super();
799
- // Bind methods to maintain 'this' context
800
- this._onWindowResize = this._onWindowResize.bind(this);
801
848
  // Track pointer listeners being added to and removed from descendant entities.
802
849
  // Registered once here rather than on every boot - the handlers no-op while there is no
803
850
  // canvas, and a re-booted element must not stack a second set.
@@ -808,6 +855,9 @@
808
855
  }
809
856
  async connectedCallback() {
810
857
  const generation = ++this._bootGeneration;
858
+ // Installed before the loading bar is created: the bar anchors to this element, which
859
+ // these styles make a positioned block box
860
+ ensureBaseStyles();
811
861
  // Created before the first await, so the bar is visible while modules and the graphics
812
862
  // device are created, and exists before any disconnect could need to clean it up
813
863
  if (this._loadingBar && !this._bar) {
@@ -822,8 +872,11 @@
822
872
  if (generation !== this._bootGeneration) {
823
873
  return;
824
874
  }
825
- // Create and append the canvas to the element
875
+ // Create and append the canvas, filling the element's content box - the page sizes the
876
+ // element, and everything else follows. touch-action: none keeps touch drags driving the
877
+ // engine's input handlers instead of scrolling the page.
826
878
  this._canvas = document.createElement('canvas');
879
+ this._canvas.style.cssText = 'display: block; width: 100%; height: 100%; touch-action: none;';
827
880
  this.appendChild(this._canvas);
828
881
  // Configure device types based on backend selection
829
882
  const backendToDeviceTypes = {
@@ -833,14 +886,44 @@
833
886
  };
834
887
  const deviceTypes = backendToDeviceTypes[this._backend] || [];
835
888
  this._optionsLocked = true;
836
- const device = await playcanvas.createGraphicsDevice(this._canvas, {
837
- // @ts-ignore - alpha needs to be documented
838
- alpha: this._alpha,
839
- antialias: this._antialias,
840
- depth: this._depthBuffer,
841
- deviceTypes: deviceTypes,
842
- stencil: this._stencilBuffer
843
- });
889
+ // createGraphicsDevice appends its final null-device fallback to the array in place, so
890
+ // the requested list is captured now for the failure message.
891
+ const requested = deviceTypes.join(', ');
892
+ let device;
893
+ try {
894
+ device = await playcanvas.createGraphicsDevice(this._canvas, {
895
+ // @ts-ignore - alpha needs to be documented
896
+ alpha: this._alpha,
897
+ antialias: this._antialias,
898
+ depth: this._depthBuffer,
899
+ deviceTypes: deviceTypes,
900
+ stencil: this._stencilBuffer
901
+ });
902
+ }
903
+ catch (error) {
904
+ // The element may have been removed while device creation was failing. The teardown
905
+ // has already cleaned up, and the failure belongs to a boot that no longer owns the
906
+ // element.
907
+ if (generation !== this._bootGeneration) {
908
+ return;
909
+ }
910
+ // Return the element to its pre-boot state - no dead canvas, no loading bar stuck at
911
+ // zero - before announcing the failure. Readiness deliberately stays pending: nothing
912
+ // it would announce (the app, the entity hierarchy) exists, so a device-less element
913
+ // joins the documented never-ready cases and the failure surfaces through the error
914
+ // event instead.
915
+ if (this._canvas && this.contains(this._canvas)) {
916
+ this.removeChild(this._canvas);
917
+ }
918
+ this._canvas = null;
919
+ this._bar?.destroy();
920
+ this._bar = null;
921
+ const reason = error instanceof Error ? error.message : String(error);
922
+ const message = `pc-app failed to create a graphics device (${requested}) - ${reason}`;
923
+ console.error(message, error);
924
+ this.dispatchEvent(new ErrorEvent('error', { message, error }));
925
+ return;
926
+ }
844
927
  // The element may have been removed while the device was created. disconnectedCallback
845
928
  // has already cleaned up the canvas; the device was created inside the await, so it is
846
929
  // this boot's to release.
@@ -919,9 +1002,19 @@
919
1002
  const app = new playcanvas.AppBase(this._canvas);
920
1003
  this._app = app;
921
1004
  app.init(createOptions);
922
- app.setCanvasFillMode(playcanvas.FILLMODE_FILL_WINDOW);
1005
+ // FILLMODE_NONE leaves the canvas's CSS sizing alone (the engine's other fill modes
1006
+ // stamp window-derived pixel sizes onto it); RESOLUTION_AUTO sizes the drawing buffer
1007
+ // from the canvas's client size
1008
+ app.setCanvasFillMode(playcanvas.FILLMODE_NONE);
923
1009
  app.setCanvasResolution(playcanvas.RESOLUTION_AUTO);
924
1010
  this._pickerCreate();
1011
+ // Track the element's box rather than the window: containers resize without any window
1012
+ // event (splitter drags, flex reflow, animations). Guarded because jsdom has no
1013
+ // ResizeObserver - there, the resolution set above is the only sizing that happens.
1014
+ if (typeof ResizeObserver !== 'undefined') {
1015
+ this._resizeObserver = new ResizeObserver(() => this._syncCanvasSize());
1016
+ this._resizeObserver.observe(this);
1017
+ }
925
1018
  // Get all pc-asset elements that are direct children of the pc-app element
926
1019
  const assetElements = this.querySelectorAll(':scope > pc-asset');
927
1020
  for (const assetElement of Array.from(assetElements)) {
@@ -997,8 +1090,6 @@
997
1090
  // Dismiss the bar only once a frame has actually rendered; ready fires before the
998
1091
  // first rAF tick
999
1092
  app.once('frameend', () => this._bar?.complete());
1000
- // Handle window resize to keep the canvas responsive
1001
- window.addEventListener('resize', this._onWindowResize);
1002
1093
  this._onReady();
1003
1094
  });
1004
1095
  }
@@ -1023,18 +1114,27 @@
1023
1114
  // no longer holds.
1024
1115
  this._hierarchyReady = false;
1025
1116
  this._resetReady();
1026
- // Remove event listeners
1027
- window.removeEventListener('resize', this._onWindowResize);
1117
+ // Stop tracking the element's size
1118
+ this._resizeObserver?.disconnect();
1119
+ this._resizeObserver = null;
1028
1120
  // Remove the canvas
1029
1121
  if (this._canvas && this.contains(this._canvas)) {
1030
1122
  this.removeChild(this._canvas);
1031
1123
  this._canvas = null;
1032
1124
  }
1033
1125
  }
1034
- _onWindowResize() {
1035
- if (this.app) {
1036
- this.app.resizeCanvas();
1126
+ /**
1127
+ * Syncs the drawing buffer and the picker to the canvas's current CSS size. The picker must
1128
+ * track the buffer, or picks would land at stale coordinates after a resize. Skipped while
1129
+ * an XR session presents - the session owns the buffer size.
1130
+ */
1131
+ _syncCanvasSize() {
1132
+ if (!this.app || this.app.xr?.active) {
1133
+ return;
1037
1134
  }
1135
+ this.app.updateCanvasSize();
1136
+ const { width, height } = this.app.graphicsDevice;
1137
+ this._picker?.resize(width, height);
1038
1138
  }
1039
1139
  _pickerCreate() {
1040
1140
  const { width, height } = this.app.graphicsDevice;
@@ -1372,7 +1472,7 @@
1372
1472
  this._maxPixelRatio = value;
1373
1473
  if (this.app) {
1374
1474
  this.app.graphicsDevice.maxPixelRatio = value;
1375
- this.app.resizeCanvas();
1475
+ this._syncCanvasSize();
1376
1476
  }
1377
1477
  }
1378
1478
  /**