@playcanvas/web-components 0.11.1 → 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 (96) hide show
  1. package/README.md +1 -1
  2. package/dist/app.d.cts +246 -0
  3. package/dist/app.d.ts +38 -13
  4. package/dist/asset.d.cts +218 -0
  5. package/dist/asset.d.ts +145 -6
  6. package/dist/async-element.d.cts +103 -0
  7. package/dist/async-element.d.ts +11 -8
  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 +17 -1
  15. package/dist/components/component.d.cts +82 -0
  16. package/dist/components/component.d.ts +20 -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 +1127 -179
  50. package/dist/entity-base.d.cts +67 -0
  51. package/dist/entity-base.d.ts +67 -0
  52. package/dist/entity.d.cts +131 -0
  53. package/dist/entity.d.ts +3 -38
  54. package/dist/index.d.cts +79 -0
  55. package/dist/index.d.ts +35 -32
  56. package/dist/loading-bar.d.cts +35 -0
  57. package/dist/material.d.cts +1011 -0
  58. package/dist/material.d.ts +2 -1
  59. package/dist/model.d.cts +72 -0
  60. package/dist/model.d.ts +27 -5
  61. package/dist/module.d.cts +29 -0
  62. package/dist/module.d.ts +16 -10
  63. package/dist/node.d.cts +253 -0
  64. package/dist/node.d.ts +253 -0
  65. package/dist/parse.d.cts +147 -0
  66. package/dist/pwc.cjs +1496 -200
  67. package/dist/pwc.cjs.map +1 -1
  68. package/dist/pwc.js +1497 -201
  69. package/dist/pwc.js.map +1 -1
  70. package/dist/pwc.min.js +1 -1
  71. package/dist/pwc.min.js.map +1 -1
  72. package/dist/pwc.min.mjs +1 -1
  73. package/dist/pwc.min.mjs.map +1 -1
  74. package/dist/pwc.mjs +1496 -202
  75. package/dist/pwc.mjs.map +1 -1
  76. package/dist/scene.d.cts +117 -0
  77. package/dist/scene.d.ts +1 -1
  78. package/dist/sky.d.cts +121 -0
  79. package/dist/sky.d.ts +1 -1
  80. package/dist/vscode.html-custom-data.json +127 -5
  81. package/dist/web-types.json +399 -59
  82. package/package.json +16 -7
  83. package/src/app.ts +137 -45
  84. package/src/asset.ts +439 -9
  85. package/src/async-element.ts +11 -8
  86. package/src/components/collision-component.ts +35 -0
  87. package/src/components/component.ts +93 -3
  88. package/src/entity-base.ts +136 -0
  89. package/src/entity.ts +23 -117
  90. package/src/index.ts +5 -0
  91. package/src/loading-bar.ts +2 -2
  92. package/src/material.ts +2 -2
  93. package/src/model.ts +79 -11
  94. package/src/module.ts +39 -20
  95. package/src/node.ts +715 -0
  96. package/src/sky.ts +0 -1
@@ -2,6 +2,7 @@ import type { Component } from 'playcanvas';
2
2
 
3
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,18 @@ 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
+
21
34
  /**
22
35
  * Incremented on every connect and disconnect. connectedCallback captures the value on entry
23
36
  * and abandons itself wherever it resumes from an await if the value has moved on — so a
@@ -49,6 +62,44 @@ class ComponentElement extends AsyncElement {
49
62
  return {};
50
63
  }
51
64
 
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
+
52
103
  private async _addComponent() {
53
104
  const generation = this._connectionGeneration;
54
105
 
@@ -71,9 +122,42 @@ class ComponentElement extends AsyncElement {
71
122
  return;
72
123
  }
73
124
 
74
- // Add the component to the entity
75
- const data = this.getInitialComponentData();
76
- this._component = entityElement.entity!.addComponent(this._componentName, data);
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);
143
+ }
144
+
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
+ }
77
161
  }
78
162
 
79
163
  /**
@@ -111,6 +195,12 @@ class ComponentElement extends AsyncElement {
111
195
  // Invalidate any connectedCallback still suspended on an await
112
196
  this._connectionGeneration++;
113
197
 
198
+ if (this._hostElement && this._hostReadyListener) {
199
+ this._hostElement.removeEventListener('ready', this._hostReadyListener);
200
+ }
201
+ this._hostElement = null;
202
+ this._hostReadyListener = null;
203
+
114
204
  // Remove the component when the element is disconnected. Skip this when the owning
115
205
  // application has already been destroyed — removing a <pc-app> disconnects it before
116
206
  // its children, taking the component systems with it.
@@ -0,0 +1,136 @@
1
+ import type { Entity } from 'playcanvas';
2
+
3
+ import type { AppElement } from './app';
4
+ import { AsyncElement } from './async-element';
5
+
6
+ /**
7
+ * The attribute names of the inline `onpointer*` event handlers, shared by every element that
8
+ * fronts an engine entity. Spread into `observedAttributes` by subclasses.
9
+ * @ignore
10
+ */
11
+ const POINTER_ATTRIBUTES = [
12
+ 'onpointerenter',
13
+ 'onpointerleave',
14
+ 'onpointerdown',
15
+ 'onpointerup',
16
+ 'onpointermove'
17
+ ] as const;
18
+
19
+ /**
20
+ * The base class for elements that front an engine {@link Entity}: `<pc-entity>`, which creates
21
+ * one, and `<pc-node>`, which binds to one inside a model's instantiated hierarchy. It carries
22
+ * what both need — the `entity` contract, registration with the owning application (which joins
23
+ * picked scene nodes back to elements by identity, never by name), and the pointer listener
24
+ * bookkeeping that lets the application lazily attach its canvas handlers.
25
+ */
26
+ class EntityBaseElement extends AsyncElement {
27
+ protected _entity: Entity | null = null;
28
+
29
+ /**
30
+ * The application element this entity is registered with, cached at registration time so the
31
+ * entity can be unregistered even once this element has left the DOM.
32
+ */
33
+ protected _appElement: AppElement | null = null;
34
+
35
+ /**
36
+ * The pointer event listeners for the entity.
37
+ */
38
+ private _listeners: Record<string, EventListener[]> = {};
39
+
40
+ /**
41
+ * The event types for which an inline `onpointer*` attribute is currently present.
42
+ */
43
+ private _inlineHandlerTypes = new Set<string>();
44
+
45
+ /**
46
+ * The PlayCanvas entity instance. `null` until the element is ready, and again once the
47
+ * entity is gone — await {@link whenReady} or the element's `ready()` promise before
48
+ * accessing it.
49
+ * @returns The entity instance, or `null`.
50
+ */
51
+ get entity(): Entity | null {
52
+ return this._entity;
53
+ }
54
+
55
+ /**
56
+ * Registers `entity` as this element's backing entity with the owning application, which
57
+ * joins engine nodes back to elements by identity (never by name).
58
+ *
59
+ * @param entity - The entity to register.
60
+ */
61
+ protected _registerEntity(entity: Entity) {
62
+ this._appElement = this.closestApp;
63
+ this._appElement?._registerEntityElement(entity, this);
64
+ }
65
+
66
+ /**
67
+ * Removes the registration for `entity`.
68
+ *
69
+ * @param entity - The entity to unregister.
70
+ */
71
+ protected _unregisterEntity(entity: Entity) {
72
+ this._appElement?._unregisterEntityElement(entity);
73
+ this._appElement = null;
74
+ }
75
+
76
+ /**
77
+ * Tracks whether an inline `onpointer*` attribute is present. The browser itself compiles and
78
+ * runs these attributes — they are standard `GlobalEventHandlers`, so setting one replaces
79
+ * the previous handler and removing it removes the handler, exactly like `onclick` on any
80
+ * HTML element. But because they bypass {@link addEventListener}, the connect/disconnect
81
+ * bookkeeping that lets the application lazily attach its canvas pointer handlers must be
82
+ * kept in sync here.
83
+ *
84
+ * @param name - The attribute name (e.g. 'onpointerdown').
85
+ * @param value - The attribute value, or `null` when the attribute has been removed.
86
+ */
87
+ protected _updateInlineHandler(name: string, value: string | null) {
88
+ const type = name.substring(2);
89
+ const had = this._inlineHandlerTypes.has(type);
90
+ const has = value !== null;
91
+
92
+ if (has && !had) {
93
+ this._inlineHandlerTypes.add(type);
94
+ this.dispatchEvent(new CustomEvent(`${type}:connect`, { bubbles: true }));
95
+ } else if (!has && had) {
96
+ this._inlineHandlerTypes.delete(type);
97
+ this.dispatchEvent(new CustomEvent(`${type}:disconnect`, { bubbles: true }));
98
+ }
99
+ }
100
+
101
+ addEventListener(type: string, listener: EventListener, options?: boolean | AddEventListenerOptions) {
102
+ if (!this._listeners[type]) {
103
+ this._listeners[type] = [];
104
+ }
105
+ this._listeners[type].push(listener);
106
+ super.addEventListener(type, listener, options);
107
+ if (type.startsWith('pointer')) {
108
+ this.dispatchEvent(new CustomEvent(`${type}:connect`, { bubbles: true }));
109
+ }
110
+ }
111
+
112
+ removeEventListener(type: string, listener: EventListener, options?: boolean | EventListenerOptions) {
113
+ if (this._listeners[type]) {
114
+ this._listeners[type] = this._listeners[type].filter((l) => l !== listener);
115
+ }
116
+ super.removeEventListener(type, listener, options);
117
+ if (type.startsWith('pointer')) {
118
+ this.dispatchEvent(new CustomEvent(`${type}:disconnect`, { bubbles: true }));
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Whether the element has a listener for an event type, registered either with
124
+ * {@link addEventListener} or with the matching inline `onpointer*` attribute. Read by the
125
+ * containing `<pc-app>` element to gate pointer event synthesis.
126
+ *
127
+ * @param type - The event type.
128
+ * @returns Whether a listener is registered.
129
+ * @internal
130
+ */
131
+ _hasListeners(type: string): boolean {
132
+ return Boolean(this._listeners[type]?.length) || this._inlineHandlerTypes.has(type);
133
+ }
134
+ }
135
+
136
+ export { EntityBaseElement, POINTER_ATTRIBUTES };
package/src/entity.ts CHANGED
@@ -1,8 +1,7 @@
1
1
  import type { AppBase } from 'playcanvas';
2
2
  import { Entity, Vec3 } from 'playcanvas';
3
3
 
4
- import type { AppElement } from './app';
5
- import { AsyncElement } from './async-element';
4
+ import { EntityBaseElement, POINTER_ATTRIBUTES } from './entity-base';
6
5
  import { parseBool, parseTags, parseVec3 } from './parse';
7
6
 
8
7
  /**
@@ -29,7 +28,7 @@ import { parseBool, parseTags, parseVec3 } from './parse';
29
28
  * @fires {PointerEvent} pointerdown - Fired when a pointer button is pressed over the entity.
30
29
  * @fires {PointerEvent} pointerup - Fired when a pointer button is released over the entity.
31
30
  */
32
- class EntityElement extends AsyncElement {
31
+ class EntityElement extends EntityBaseElement {
33
32
  /**
34
33
  * Whether the entity is enabled.
35
34
  */
@@ -60,39 +59,11 @@ class EntityElement extends AsyncElement {
60
59
  */
61
60
  private _tags: string[] = [];
62
61
 
63
- /**
64
- * The pointer event listeners for the entity.
65
- */
66
- private _listeners: Record<string, EventListener[]> = {};
67
-
68
- /**
69
- * The event types for which an inline `onpointer*` attribute is currently present.
70
- */
71
- private _inlineHandlerTypes = new Set<string>();
72
-
73
62
  /**
74
63
  * Whether the hierarchy has been built for this entity.
75
64
  */
76
65
  private _built = false;
77
66
 
78
- private _entity: Entity | null = null;
79
-
80
- /**
81
- * The application element this entity is registered with, cached at creation time so the
82
- * entity can be unregistered even once this element has left the DOM.
83
- */
84
- private _appElement: AppElement | null = null;
85
-
86
- /**
87
- * The PlayCanvas entity instance. `null` until the element is ready, and again once it has
88
- * been removed from the document — await {@link whenReady} or the element's `ready()`
89
- * promise before accessing it.
90
- * @returns The entity instance, or `null`.
91
- */
92
- get entity(): Entity | null {
93
- return this._entity;
94
- }
95
-
96
67
  /**
97
68
  * Creates the backing entity. Called by the containing `<pc-app>` element during its boot
98
69
  * sweep, and on connection for elements inserted while the application is already running.
@@ -124,13 +95,11 @@ class EntityElement extends AsyncElement {
124
95
  entity.tags.add(this._tags);
125
96
  }
126
97
 
127
- // Register with the owning application, which joins engine nodes back to elements by
128
- // identity (never by name), and hook the entity's destruction. The engine fires 'destroy'
129
- // for every entity in a destroyed subtree, so the element learns of its entity's death no
130
- // matter who causes it: this element, an ancestor, the whole application, or a user
131
- // script calling entity.destroy().
132
- this._appElement = this.closestApp;
133
- this._appElement?._registerEntityElement(entity, this);
98
+ // Register with the owning application and hook the entity's destruction. The engine
99
+ // fires 'destroy' for every entity in a destroyed subtree, so the element learns of its
100
+ // entity's death no matter who causes it: this element, an ancestor, the whole
101
+ // application, or a user script calling entity.destroy().
102
+ this._registerEntity(entity);
134
103
  entity.once('destroy', this._onEntityDestroy, this);
135
104
  }
136
105
 
@@ -144,27 +113,35 @@ class EntityElement extends AsyncElement {
144
113
  * @param entity - The entity that was destroyed.
145
114
  */
146
115
  private _onEntityDestroy(entity: Entity) {
147
- this._appElement?._unregisterEntityElement(entity);
148
- this._appElement = null;
116
+ this._unregisterEntity(entity);
149
117
  this._entity = null;
150
118
  this._built = false;
151
119
  this._resetReady();
152
120
  }
153
121
 
154
122
  /**
155
- * Parents the backing entity: under the entity of the nearest ancestor `<pc-entity>` when
156
- * there is one, and under the application root otherwise. Called by the containing `<pc-app>`
157
- * element once a sweep has created every entity, so a parent's existence never depends on
158
- * document order.
123
+ * Parents the backing entity: under the entity of the nearest ancestor `<pc-entity>` or
124
+ * `<pc-node>` when there is one, and under the application root otherwise. Called by the
125
+ * containing `<pc-app>` element once a sweep has created every entity, so a parent's
126
+ * existence never depends on document order.
159
127
  *
160
128
  * @param app - The application whose root adopts parentless entities.
161
129
  * @internal
162
130
  */
163
131
  _buildHierarchy(app: AppBase) {
164
132
  if (!this.entity || this._built) return;
165
- this._built = true;
166
133
 
167
134
  const closestEntity = this.closestEntity;
135
+
136
+ // A host element without an entity is an unresolved `<pc-node>`: building now would
137
+ // mis-anchor this entity to the application root while the host is still resolving.
138
+ // Stay unbuilt - the host drives this subtree itself once it binds.
139
+ if (closestEntity && !closestEntity.entity) {
140
+ return;
141
+ }
142
+
143
+ this._built = true;
144
+
168
145
  if (closestEntity?.entity) {
169
146
  closestEntity.entity.addChild(this.entity);
170
147
  } else {
@@ -328,45 +305,8 @@ class EntityElement extends AsyncElement {
328
305
  return this._tags;
329
306
  }
330
307
 
331
- /**
332
- * Tracks whether an inline `onpointer*` attribute is present. The browser itself compiles and
333
- * runs these attributes — they are standard `GlobalEventHandlers`, so setting one replaces
334
- * the previous handler and removing it removes the handler, exactly like `onclick` on any
335
- * HTML element. But because they bypass {@link addEventListener}, the connect/disconnect
336
- * bookkeeping that lets the application lazily attach its canvas pointer handlers must be
337
- * kept in sync here.
338
- *
339
- * @param name - The attribute name (e.g. 'onpointerdown').
340
- * @param value - The attribute value, or `null` when the attribute has been removed.
341
- */
342
- private _updateInlineHandler(name: string, value: string | null) {
343
- const type = name.substring(2);
344
- const had = this._inlineHandlerTypes.has(type);
345
- const has = value !== null;
346
-
347
- if (has && !had) {
348
- this._inlineHandlerTypes.add(type);
349
- this.dispatchEvent(new CustomEvent(`${type}:connect`, { bubbles: true }));
350
- } else if (!has && had) {
351
- this._inlineHandlerTypes.delete(type);
352
- this.dispatchEvent(new CustomEvent(`${type}:disconnect`, { bubbles: true }));
353
- }
354
- }
355
-
356
308
  static get observedAttributes() {
357
- return [
358
- 'enabled',
359
- 'name',
360
- 'position',
361
- 'rotation',
362
- 'scale',
363
- 'tags',
364
- 'onpointerenter',
365
- 'onpointerleave',
366
- 'onpointerdown',
367
- 'onpointerup',
368
- 'onpointermove'
369
- ];
309
+ return ['enabled', 'name', 'position', 'rotation', 'scale', 'tags', ...POINTER_ATTRIBUTES];
370
310
  }
371
311
 
372
312
  attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {
@@ -398,40 +338,6 @@ class EntityElement extends AsyncElement {
398
338
  break;
399
339
  }
400
340
  }
401
-
402
- addEventListener(type: string, listener: EventListener, options?: boolean | AddEventListenerOptions) {
403
- if (!this._listeners[type]) {
404
- this._listeners[type] = [];
405
- }
406
- this._listeners[type].push(listener);
407
- super.addEventListener(type, listener, options);
408
- if (type.startsWith('pointer')) {
409
- this.dispatchEvent(new CustomEvent(`${type}:connect`, { bubbles: true }));
410
- }
411
- }
412
-
413
- removeEventListener(type: string, listener: EventListener, options?: boolean | EventListenerOptions) {
414
- if (this._listeners[type]) {
415
- this._listeners[type] = this._listeners[type].filter((l) => l !== listener);
416
- }
417
- super.removeEventListener(type, listener, options);
418
- if (type.startsWith('pointer')) {
419
- this.dispatchEvent(new CustomEvent(`${type}:disconnect`, { bubbles: true }));
420
- }
421
- }
422
-
423
- /**
424
- * Whether the element has a listener for an event type, registered either with
425
- * {@link addEventListener} or with the matching inline `onpointer*` attribute. Read by the
426
- * containing `<pc-app>` element to gate pointer event synthesis.
427
- *
428
- * @param type - The event type.
429
- * @returns Whether a listener is registered.
430
- * @internal
431
- */
432
- _hasListeners(type: string): boolean {
433
- return Boolean(this._listeners[type]?.length) || this._inlineHandlerTypes.has(type);
434
- }
435
341
  }
436
342
 
437
343
  customElements.define('pc-entity', EntityElement);
package/src/index.ts CHANGED
@@ -35,8 +35,10 @@ import { ScriptElement } from './components/script';
35
35
  import { SoundComponentElement } from './components/sound-component';
36
36
  import { SoundSlotElement } from './components/sound-slot';
37
37
  import { GSplatComponentElement } from './components/gsplat-component';
38
+ import { EntityBaseElement } from './entity-base';
38
39
  import { MaterialElement } from './material';
39
40
  import { ModelElement } from './model';
41
+ import { NodeElement } from './node';
40
42
  import { SceneElement } from './scene';
41
43
  import { SkyElement } from './sky';
42
44
 
@@ -69,6 +71,7 @@ declare global {
69
71
  'pc-material': MaterialElement;
70
72
  'pc-model': ModelElement;
71
73
  'pc-module': ModuleElement;
74
+ 'pc-node': NodeElement;
72
75
  'pc-particles': ParticleSystemComponentElement;
73
76
  'pc-render': RenderComponentElement;
74
77
  'pc-rigidbody': RigidBodyComponentElement;
@@ -110,8 +113,10 @@ export {
110
113
  SoundComponentElement,
111
114
  SoundSlotElement,
112
115
  GSplatComponentElement,
116
+ EntityBaseElement,
113
117
  MaterialElement,
114
118
  ModelElement,
119
+ NodeElement,
115
120
  SceneElement,
116
121
  SkyElement,
117
122
  whenReady
@@ -28,9 +28,9 @@ class LoadingBar {
28
28
  this._track.setAttribute('aria-label', 'Loading');
29
29
  this._track.setAttribute('aria-valuemin', '0');
30
30
  this._track.setAttribute('aria-valuemax', '100');
31
- // Fixed positioning matches the canvas, which always fills the window (FILLMODE_FILL_WINDOW)
31
+ // Anchored to the pc-app element, which the library's base styles make a positioned box
32
32
  this._track.style.cssText = [
33
- 'position: fixed',
33
+ 'position: absolute',
34
34
  'top: 0',
35
35
  'left: 0',
36
36
  'width: 100%',
package/src/material.ts CHANGED
@@ -542,12 +542,12 @@ class MaterialElement extends HTMLElement {
542
542
 
543
543
  /**
544
544
  * @param slot - The material property to write.
545
- * @param texture - The loaded texture.
545
+ * @param texture - The loaded texture, applied with its sampler state untouched - anisotropy
546
+ * and friends belong to the `pc-asset`'s texture options.
546
547
  */
547
548
  private _applyMap(slot: TextureSlot, texture: Texture) {
548
549
  if (!this.material) return;
549
550
  this.material[slot] = texture;
550
- texture.anisotropy = 4;
551
551
  this._scheduleUpdate();
552
552
  }
553
553