@playcanvas/web-components 0.18.0 → 0.20.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 (56) hide show
  1. package/dist/app.d.cts +38 -3
  2. package/dist/app.d.ts +38 -3
  3. package/dist/components/anim-clip.d.cts +0 -2
  4. package/dist/components/anim-clip.d.ts +0 -2
  5. package/dist/components/button-component.d.cts +9 -5
  6. package/dist/components/button-component.d.ts +9 -5
  7. package/dist/components/joint-component.d.cts +24 -10
  8. package/dist/components/joint-component.d.ts +24 -10
  9. package/dist/components/script-component.d.cts +4 -2
  10. package/dist/components/script-component.d.ts +4 -2
  11. package/dist/components/script-instance.d.cts +14 -6
  12. package/dist/components/script-instance.d.ts +14 -6
  13. package/dist/components/scroll-view-component.d.cts +24 -12
  14. package/dist/components/scroll-view-component.d.ts +24 -12
  15. package/dist/components/scrollbar-component.d.cts +6 -3
  16. package/dist/components/scrollbar-component.d.ts +6 -3
  17. package/dist/custom-elements.json +93 -23
  18. package/dist/entity-base.d.cts +4 -3
  19. package/dist/entity-base.d.ts +4 -3
  20. package/dist/entity.d.cts +8 -2
  21. package/dist/entity.d.ts +8 -2
  22. package/dist/model.d.cts +6 -0
  23. package/dist/model.d.ts +6 -0
  24. package/dist/node.d.cts +6 -0
  25. package/dist/node.d.ts +6 -0
  26. package/dist/parse.d.cts +7 -2
  27. package/dist/parse.d.ts +7 -2
  28. package/dist/pwc.cjs +706 -289
  29. package/dist/pwc.cjs.map +1 -1
  30. package/dist/pwc.js +706 -289
  31. package/dist/pwc.js.map +1 -1
  32. package/dist/pwc.min.js +1 -1
  33. package/dist/pwc.min.js.map +1 -1
  34. package/dist/pwc.min.mjs +1 -1
  35. package/dist/pwc.min.mjs.map +1 -1
  36. package/dist/pwc.mjs +706 -289
  37. package/dist/pwc.mjs.map +1 -1
  38. package/dist/scene.d.cts +17 -1
  39. package/dist/scene.d.ts +17 -1
  40. package/dist/vscode.html-custom-data.json +34 -14
  41. package/dist/web-types.json +78 -24
  42. package/package.json +3 -3
  43. package/src/app.ts +197 -87
  44. package/src/components/anim-clip.ts +0 -2
  45. package/src/components/button-component.ts +18 -10
  46. package/src/components/joint-component.ts +29 -15
  47. package/src/components/script-component.ts +25 -12
  48. package/src/components/script-instance.ts +14 -6
  49. package/src/components/scroll-view-component.ts +49 -29
  50. package/src/components/scrollbar-component.ts +13 -8
  51. package/src/entity-base.ts +27 -15
  52. package/src/entity.ts +11 -4
  53. package/src/model.ts +9 -2
  54. package/src/node.ts +9 -2
  55. package/src/parse.ts +213 -16
  56. package/src/scene.ts +33 -2
@@ -15,7 +15,9 @@ import { parseBool } from '../parse';
15
15
  * Values are parsed according to the type of the attribute's current value — initially the
16
16
  * script's declared default (numbers, booleans, strings, Vec2/3/4, Color, Quat as Euler
17
17
  * angles) — and the `asset:`/`entity:`/`vec2:`/`vec3:`/`vec4:`/`color:` prefixes may be used
18
- * to be explicit.
18
+ * to be explicit. An `entity:` reference is an entity name — resolved against the nearest
19
+ * enclosing entity first, then outward, then the document — or a document-wide `#` selector
20
+ * (`entity:#id`); a bare value is always a name, never an element id.
19
21
  * - **The `attributes` JSON attribute**: an object supporting nested structures and attribute
20
22
  * names that collide with reserved HTML attribute names (e.g. `title`).
21
23
  *
@@ -31,7 +33,8 @@ import { parseBool } from '../parse';
31
33
  *
32
34
  * @elementSummary The `<pc-script-instance>` element attaches one script class, named by `name`, to
33
35
  * the entity of its parent `<pc-script>`. Its other attributes set script attributes of the same
34
- * name, and `attributes` takes a JSON object instead. Must be a direct child of `<pc-script>`.
36
+ * name, and `attributes` takes a JSON object instead. An `entity:` value is an entity name —
37
+ * write `entity:#id` for an element id. Must be a direct child of `<pc-script>`.
35
38
  *
36
39
  * @fires {CustomEvent} scriptattributeschange - Fired when the script's attributes change. The
37
40
  * `detail` carries the new `attributes` object. Bubbles.
@@ -54,9 +57,11 @@ class ScriptInstanceElement extends AsyncElement {
54
57
  /**
55
58
  * Sets the attributes of the script as an object. Values are converted with the same rules
56
59
  * as the `attributes` attribute: `asset:`/`entity:` references and `vec2:`/`vec3:`/`vec4:`/
57
- * `color:` prefixed strings are resolved, and a plain numeric array is converted to the
58
- * type of the attribute it targets when that attribute currently holds a Vec2, Vec3, Vec4
59
- * or Color.
60
+ * `color:` prefixed strings are resolved (an entity name against the nearest enclosing
61
+ * entity first, then outward, then the document or a document-wide `#` selector; a bare
62
+ * value is always a name, never an element id), and a plain numeric array is converted to
63
+ * the type of the attribute it targets when that attribute currently holds a Vec2, Vec3,
64
+ * Vec4 or Color.
60
65
  * @param value - The attributes of the script.
61
66
  */
62
67
  set scriptAttributes(value: Record<string, any>) {
@@ -70,7 +75,10 @@ class ScriptInstanceElement extends AsyncElement {
70
75
  }
71
76
 
72
77
  /**
73
- * Gets the attributes of the script.
78
+ * Gets the attributes of the script as an object whose `asset:`, `entity:`, `vec2:`, `vec3:`,
79
+ * `vec4:` and `color:` prefixed values are resolved when applied — an `entity:` value being
80
+ * an entity name (nearest enclosing entity first, then outward, then the document) or a
81
+ * document-wide `#` selector (`entity:#id`), never a bare element id.
74
82
  * @returns The attributes of the script.
75
83
  */
76
84
  get scriptAttributes(): Record<string, any> {
@@ -8,7 +8,7 @@ import {
8
8
  Vec2
9
9
  } from 'playcanvas';
10
10
 
11
- import { getEntity, parseBool, parseEnum, parseNumber, parseVec2 } from '../parse';
11
+ import { parseBool, parseEnum, parseNumber, parseVec2, resolveEntity } from '../parse';
12
12
 
13
13
  import { ComponentElement } from './component';
14
14
 
@@ -82,22 +82,22 @@ class ScrollViewComponentElement extends ComponentElement {
82
82
  verticalScrollbarVisibility: visibilities.get(this._verticalScrollbarVisibility)
83
83
  };
84
84
 
85
- const viewport = getEntity(this._viewport);
85
+ const viewport = resolveEntity(this._viewport, this, 'viewport', 'reference ignored');
86
86
  if (viewport) {
87
87
  data.viewportEntity = viewport;
88
88
  }
89
89
 
90
- const content = getEntity(this._content);
90
+ const content = resolveEntity(this._content, this, 'content', 'reference ignored');
91
91
  if (content) {
92
92
  data.contentEntity = content;
93
93
  }
94
94
 
95
- const horizontalScrollbar = getEntity(this._horizontalScrollbar);
95
+ const horizontalScrollbar = resolveEntity(this._horizontalScrollbar, this, 'horizontal-scrollbar', 'reference ignored');
96
96
  if (horizontalScrollbar) {
97
97
  data.horizontalScrollbarEntity = horizontalScrollbar;
98
98
  }
99
99
 
100
- const verticalScrollbar = getEntity(this._verticalScrollbar);
100
+ const verticalScrollbar = resolveEntity(this._verticalScrollbar, this, 'vertical-scrollbar', 'reference ignored');
101
101
  if (verticalScrollbar) {
102
102
  data.verticalScrollbarEntity = verticalScrollbar;
103
103
  }
@@ -294,20 +294,25 @@ class ScrollViewComponentElement extends ComponentElement {
294
294
  }
295
295
 
296
296
  /**
297
- * Sets the reference (CSS selector, element id or entity name) to the `<pc-entity>` used as the
298
- * viewport, which clips the content to the scroll view's bounds.
297
+ * Sets the reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#`
298
+ * selector) to the entity used as the viewport, which clips the content to the scroll view's
299
+ * bounds. An exact name resolves against the nearest enclosing entity first, then outward,
300
+ * then the document. A non-empty reference that does not resolve warns and is ignored.
299
301
  * @param value - The viewport entity reference.
300
302
  */
301
303
  set viewport(value: string) {
302
304
  this._viewport = value;
303
- const entity = getEntity(value);
304
- if (this.component && entity) {
305
- this.component.viewportEntity = entity;
305
+ if (this.component) {
306
+ const entity = resolveEntity(value, this, 'viewport', 'reference ignored');
307
+ if (entity) {
308
+ this.component.viewportEntity = entity;
309
+ }
306
310
  }
307
311
  }
308
312
 
309
313
  /**
310
- * Gets the reference to the `<pc-entity>` used as the viewport.
314
+ * Gets the reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#`
315
+ * selector) to the entity used as the viewport.
311
316
  * @returns The viewport entity reference.
312
317
  */
313
318
  get viewport() {
@@ -315,20 +320,25 @@ class ScrollViewComponentElement extends ComponentElement {
315
320
  }
316
321
 
317
322
  /**
318
- * Sets the reference (CSS selector, element id or entity name) to the `<pc-entity>` used as the
319
- * content, which is moved as the scroll view is scrolled.
323
+ * Sets the reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#`
324
+ * selector) to the entity used as the content, which is moved as the scroll view is
325
+ * scrolled. An exact name resolves against the nearest enclosing entity first, then outward,
326
+ * then the document. A non-empty reference that does not resolve warns and is ignored.
320
327
  * @param value - The content entity reference.
321
328
  */
322
329
  set content(value: string) {
323
330
  this._content = value;
324
- const entity = getEntity(value);
325
- if (this.component && entity) {
326
- this.component.contentEntity = entity;
331
+ if (this.component) {
332
+ const entity = resolveEntity(value, this, 'content', 'reference ignored');
333
+ if (entity) {
334
+ this.component.contentEntity = entity;
335
+ }
327
336
  }
328
337
  }
329
338
 
330
339
  /**
331
- * Gets the reference to the `<pc-entity>` used as the content.
340
+ * Gets the reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#`
341
+ * selector) to the entity used as the content.
332
342
  * @returns The content entity reference.
333
343
  */
334
344
  get content() {
@@ -336,20 +346,25 @@ class ScrollViewComponentElement extends ComponentElement {
336
346
  }
337
347
 
338
348
  /**
339
- * Sets the reference (CSS selector, element id or entity name) to the `<pc-entity>` containing
340
- * the horizontal `<pc-scrollbar>`.
349
+ * Sets the reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#`
350
+ * selector) to the entity containing the horizontal `<pc-scrollbar>`. An exact name resolves
351
+ * against the nearest enclosing entity first, then outward, then the document. A non-empty
352
+ * reference that does not resolve warns and is ignored.
341
353
  * @param value - The horizontal scrollbar entity reference.
342
354
  */
343
355
  set horizontalScrollbar(value: string) {
344
356
  this._horizontalScrollbar = value;
345
- const entity = getEntity(value);
346
- if (this.component && entity) {
347
- this.component.horizontalScrollbarEntity = entity;
357
+ if (this.component) {
358
+ const entity = resolveEntity(value, this, 'horizontal-scrollbar', 'reference ignored');
359
+ if (entity) {
360
+ this.component.horizontalScrollbarEntity = entity;
361
+ }
348
362
  }
349
363
  }
350
364
 
351
365
  /**
352
- * Gets the reference to the `<pc-entity>` containing the horizontal scrollbar.
366
+ * Gets the reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#`
367
+ * selector) to the entity containing the horizontal scrollbar.
353
368
  * @returns The horizontal scrollbar entity reference.
354
369
  */
355
370
  get horizontalScrollbar() {
@@ -357,20 +372,25 @@ class ScrollViewComponentElement extends ComponentElement {
357
372
  }
358
373
 
359
374
  /**
360
- * Sets the reference (CSS selector, element id or entity name) to the `<pc-entity>` containing
361
- * the vertical `<pc-scrollbar>`.
375
+ * Sets the reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#`
376
+ * selector) to the entity containing the vertical `<pc-scrollbar>`. An exact name resolves
377
+ * against the nearest enclosing entity first, then outward, then the document. A non-empty
378
+ * reference that does not resolve warns and is ignored.
362
379
  * @param value - The vertical scrollbar entity reference.
363
380
  */
364
381
  set verticalScrollbar(value: string) {
365
382
  this._verticalScrollbar = value;
366
- const entity = getEntity(value);
367
- if (this.component && entity) {
368
- this.component.verticalScrollbarEntity = entity;
383
+ if (this.component) {
384
+ const entity = resolveEntity(value, this, 'vertical-scrollbar', 'reference ignored');
385
+ if (entity) {
386
+ this.component.verticalScrollbarEntity = entity;
387
+ }
369
388
  }
370
389
  }
371
390
 
372
391
  /**
373
- * Gets the reference to the `<pc-entity>` containing the vertical scrollbar.
392
+ * Gets the reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#`
393
+ * selector) to the entity containing the vertical scrollbar.
374
394
  * @returns The vertical scrollbar entity reference.
375
395
  */
376
396
  get verticalScrollbar() {
@@ -1,7 +1,7 @@
1
1
  import type { ScrollbarComponent } from 'playcanvas';
2
2
  import { ORIENTATION_HORIZONTAL, ORIENTATION_VERTICAL } from 'playcanvas';
3
3
 
4
- import { getEntity, parseEnum, parseNumber } from '../parse';
4
+ import { parseEnum, parseNumber, resolveEntity } from '../parse';
5
5
 
6
6
  import { ComponentElement } from './component';
7
7
 
@@ -45,7 +45,7 @@ class ScrollbarComponentElement extends ComponentElement {
45
45
  handleSize: this._handleSize
46
46
  };
47
47
 
48
- const handle = getEntity(this._handle);
48
+ const handle = resolveEntity(this._handle, this, 'handle', 'reference ignored');
49
49
  if (handle) {
50
50
  data.handleEntity = handle;
51
51
  }
@@ -120,20 +120,25 @@ class ScrollbarComponentElement extends ComponentElement {
120
120
  }
121
121
 
122
122
  /**
123
- * Sets the reference (CSS selector, element id or entity name) to the `<pc-entity>` used as the
124
- * scrollbar handle.
123
+ * Sets the reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#`
124
+ * selector) to the entity used as the scrollbar handle. An exact name resolves against the
125
+ * nearest enclosing entity first, then outward, then the document. A non-empty reference that
126
+ * does not resolve warns and is ignored.
125
127
  * @param value - The handle entity reference.
126
128
  */
127
129
  set handle(value: string) {
128
130
  this._handle = value;
129
- const entity = getEntity(value);
130
- if (this.component && entity) {
131
- this.component.handleEntity = entity;
131
+ if (this.component) {
132
+ const entity = resolveEntity(value, this, 'handle', 'reference ignored');
133
+ if (entity) {
134
+ this.component.handleEntity = entity;
135
+ }
132
136
  }
133
137
  }
134
138
 
135
139
  /**
136
- * Gets the reference to the `<pc-entity>` used as the scrollbar handle.
140
+ * Gets the reference (a `pc-entity`, `pc-model` or `pc-node` name, or a document-wide `#`
141
+ * selector) to the entity used as the scrollbar handle.
137
142
  * @returns The handle entity reference.
138
143
  */
139
144
  get handle() {
@@ -4,18 +4,29 @@ import type { AppElement } from './app';
4
4
  import { AsyncElement } from './async-element';
5
5
 
6
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.
7
+ * The event types the containing `<pc-app>` synthesizes on entity-fronting elements via picking:
8
+ * the `pointer*` events, plus `click` which concludes a primary-button press and release, and
9
+ * is delivered as a `PointerEvent` exactly as modern browsers deliver native clicks.
9
10
  * @internal
10
11
  */
11
- export const POINTER_ATTRIBUTES = [
12
- 'onpointerenter',
13
- 'onpointerleave',
14
- 'onpointerdown',
15
- 'onpointerup',
16
- 'onpointermove'
12
+ export const SYNTHESIZED_EVENTS = [
13
+ 'pointerenter',
14
+ 'pointerleave',
15
+ 'pointerdown',
16
+ 'pointerup',
17
+ 'pointermove',
18
+ 'click'
17
19
  ] as const;
18
20
 
21
+ const SYNTHESIZED_EVENT_SET: ReadonlySet<string> = new Set(SYNTHESIZED_EVENTS);
22
+
23
+ /**
24
+ * The attribute names of the inline event handlers (`onpointerdown`, `onclick`, ...), shared by
25
+ * every element that fronts an engine entity. Spread into `observedAttributes` by subclasses.
26
+ * @internal
27
+ */
28
+ export const EVENT_ATTRIBUTES = SYNTHESIZED_EVENTS.map((type) => `on${type}`);
29
+
19
30
  /**
20
31
  * The base class for elements that front an engine {@link Entity}: `<pc-entity>` and
21
32
  * `<pc-model>`, which create one, and `<pc-node>`, which binds to one inside a model's
@@ -34,12 +45,13 @@ class EntityBaseElement extends AsyncElement {
34
45
  protected _appElement: AppElement | null = null;
35
46
 
36
47
  /**
37
- * The pointer event listeners for the entity.
48
+ * The event listeners registered on the element, by type.
38
49
  */
39
50
  private _listeners: Record<string, EventListener[]> = {};
40
51
 
41
52
  /**
42
- * The event types for which an inline `onpointer*` attribute is currently present.
53
+ * The event types for which an inline handler attribute (`onpointerdown`, `onclick`, ...)
54
+ * is currently present.
43
55
  */
44
56
  private _inlineHandlerTypes = new Set<string>();
45
57
 
@@ -75,7 +87,7 @@ class EntityBaseElement extends AsyncElement {
75
87
  }
76
88
 
77
89
  /**
78
- * Tracks whether an inline `onpointer*` attribute is present. The browser itself compiles and
90
+ * Tracks whether an inline handler attribute is present. The browser itself compiles and
79
91
  * runs these attributes — they are standard `GlobalEventHandlers`, so setting one replaces
80
92
  * the previous handler and removing it removes the handler, exactly like `onclick` on any
81
93
  * HTML element. But because they bypass {@link EventTarget.addEventListener}, the connect/disconnect
@@ -105,7 +117,7 @@ class EntityBaseElement extends AsyncElement {
105
117
  }
106
118
  this._listeners[type].push(listener);
107
119
  super.addEventListener(type, listener, options);
108
- if (type.startsWith('pointer')) {
120
+ if (SYNTHESIZED_EVENT_SET.has(type)) {
109
121
  this.dispatchEvent(new CustomEvent(`${type}:connect`, { bubbles: true }));
110
122
  }
111
123
  }
@@ -115,15 +127,15 @@ class EntityBaseElement extends AsyncElement {
115
127
  this._listeners[type] = this._listeners[type].filter((l) => l !== listener);
116
128
  }
117
129
  super.removeEventListener(type, listener, options);
118
- if (type.startsWith('pointer')) {
130
+ if (SYNTHESIZED_EVENT_SET.has(type)) {
119
131
  this.dispatchEvent(new CustomEvent(`${type}:disconnect`, { bubbles: true }));
120
132
  }
121
133
  }
122
134
 
123
135
  /**
124
136
  * Whether the element has a listener for an event type, registered either with
125
- * {@link EventTarget.addEventListener} or with the matching inline `onpointer*` attribute. Read by the
126
- * containing `<pc-app>` element to gate pointer event synthesis.
137
+ * {@link EventTarget.addEventListener} or with the matching inline handler attribute. Read by the
138
+ * containing `<pc-app>` element to gate event synthesis.
127
139
  *
128
140
  * @param type - The event type.
129
141
  * @returns Whether a listener is registered.
package/src/entity.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Vec3 } from 'playcanvas';
2
2
 
3
- import { POINTER_ATTRIBUTES } from './entity-base';
3
+ import { EVENT_ATTRIBUTES } from './entity-base';
4
4
  import { buildDescendantEntities, EntityOwnerElement } from './entity-owner';
5
5
  import { parseBool, parseTags, parseVec3 } from './parse';
6
6
 
@@ -12,8 +12,8 @@ import { parseBool, parseTags, parseVec3 } from './parse';
12
12
  *
13
13
  * The pointer events below are dispatched by the containing `<pc-app>` element when the pointer
14
14
  * intersects this entity's geometry. They are only generated while the entity has a listener for
15
- * them, registered either with {@link EventTarget.addEventListener} or with the matching inline `onpointer*`
16
- * attribute.
15
+ * them, registered either with {@link EventTarget.addEventListener} or with the matching inline
16
+ * attribute (`onpointerdown`, `onclick`, ...).
17
17
  *
18
18
  * @elementSummary The `<pc-entity>` element creates an entity: a named, transformable node of the
19
19
  * scene hierarchy, and the host for component elements such as `<pc-camera>`, `<pc-light>` and
@@ -33,11 +33,17 @@ import { parseBool, parseTags, parseVec3 } from './parse';
33
33
  * entity.
34
34
  * @attribute {string} onpointerup - Script to run when a pointer button is released over the
35
35
  * entity.
36
+ * @attribute {string} onclick - Script to run when the entity is clicked: a primary pointer
37
+ * button pressed and then released over it.
36
38
  * @fires {PointerEvent} pointerenter - Fired when the pointer moves onto the entity.
37
39
  * @fires {PointerEvent} pointerleave - Fired when the pointer moves off the entity.
38
40
  * @fires {PointerEvent} pointermove - Fired when the pointer moves over the entity.
39
41
  * @fires {PointerEvent} pointerdown - Fired when a pointer button is pressed over the entity.
40
42
  * @fires {PointerEvent} pointerup - Fired when a pointer button is released over the entity.
43
+ * @fires {PointerEvent} click - Fired when a primary pointer button is pressed and then released
44
+ * over the entity. A press and release that picked different entities fires on their nearest
45
+ * common ancestor instead, as in the DOM. `detail` carries the click count, so a double click
46
+ * arrives as a click whose `detail` is 2.
41
47
  */
42
48
  class EntityElement extends EntityOwnerElement {
43
49
  connectedCallback() {
@@ -77,7 +83,7 @@ class EntityElement extends EntityOwnerElement {
77
83
  }
78
84
 
79
85
  static get observedAttributes() {
80
- return ['enabled', 'name', 'position', 'rotation', 'scale', 'tags', ...POINTER_ATTRIBUTES];
86
+ return ['enabled', 'name', 'position', 'rotation', 'scale', 'tags', ...EVENT_ATTRIBUTES];
81
87
  }
82
88
 
83
89
  attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {
@@ -105,6 +111,7 @@ class EntityElement extends EntityOwnerElement {
105
111
  case 'onpointerdown':
106
112
  case 'onpointerup':
107
113
  case 'onpointermove':
114
+ case 'onclick':
108
115
  this._updateInlineHandler(name, newValue);
109
116
  break;
110
117
  }
package/src/model.ts CHANGED
@@ -2,7 +2,7 @@ import type { ContainerResource, Entity, EventHandle } from 'playcanvas';
2
2
  import { Vec3 } from 'playcanvas';
3
3
 
4
4
  import { useAsset } from './asset';
5
- import { POINTER_ATTRIBUTES } from './entity-base';
5
+ import { EVENT_ATTRIBUTES } from './entity-base';
6
6
  import { buildDescendantEntities, EntityOwnerElement } from './entity-owner';
7
7
  import { parseBool, parseTags, parseVec3 } from './parse';
8
8
 
@@ -143,11 +143,17 @@ const formatHierarchy = (root: HierarchyNode, counts: ReadonlyMap<string, number
143
143
  * model.
144
144
  * @attribute {string} onpointerup - Script to run when a pointer button is released over the
145
145
  * model.
146
+ * @attribute {string} onclick - Script to run when the model is clicked: a primary pointer
147
+ * button pressed and then released over it.
146
148
  * @fires {PointerEvent} pointerenter - Fired when the pointer moves onto the model.
147
149
  * @fires {PointerEvent} pointerleave - Fired when the pointer moves off the model.
148
150
  * @fires {PointerEvent} pointermove - Fired when the pointer moves over the model.
149
151
  * @fires {PointerEvent} pointerdown - Fired when a pointer button is pressed over the model.
150
152
  * @fires {PointerEvent} pointerup - Fired when a pointer button is released over the model.
153
+ * @fires {PointerEvent} click - Fired when a primary pointer button is pressed and then released
154
+ * over the model. A press and release that picked different entities fires on their nearest
155
+ * common ancestor instead, as in the DOM. `detail` carries the click count, so a double click
156
+ * arrives as a click whose `detail` is 2.
151
157
  * @fires {Event} load - Fired each time a container asset finishes instantiating, including
152
158
  * re-instantiation after `asset` changes. Does not bubble — listen on this element, or use a
153
159
  * capture-phase listener on an ancestor.
@@ -435,7 +441,7 @@ class ModelElement extends EntityOwnerElement {
435
441
  }
436
442
 
437
443
  static get observedAttributes() {
438
- return ['asset', 'enabled', 'name', 'position', 'rotation', 'scale', 'tags', ...POINTER_ATTRIBUTES];
444
+ return ['asset', 'enabled', 'name', 'position', 'rotation', 'scale', 'tags', ...EVENT_ATTRIBUTES];
439
445
  }
440
446
 
441
447
  attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {
@@ -466,6 +472,7 @@ class ModelElement extends EntityOwnerElement {
466
472
  case 'onpointerdown':
467
473
  case 'onpointerup':
468
474
  case 'onpointermove':
475
+ case 'onclick':
469
476
  this._updateInlineHandler(name, newValue);
470
477
  break;
471
478
  }
package/src/node.ts CHANGED
@@ -2,7 +2,7 @@ import type { Entity, EventHandle, GraphNode, Material, MeshInstance, Quat, Rend
2
2
  import { Vec3 } from 'playcanvas';
3
3
 
4
4
  import { ComponentElement } from './components/component';
5
- import { EntityBaseElement, POINTER_ATTRIBUTES } from './entity-base';
5
+ import { EntityBaseElement, EVENT_ATTRIBUTES } from './entity-base';
6
6
  import { buildDescendantEntities } from './entity-owner';
7
7
  import type { EntityOwnerElement } from './entity-owner';
8
8
  import { MaterialElement } from './material';
@@ -193,11 +193,17 @@ const levenshtein = (a: string, b: string): number => {
193
193
  * node.
194
194
  * @attribute {string} onpointerup - Script to run when a pointer button is released over the
195
195
  * node.
196
+ * @attribute {string} onclick - Script to run when the node is clicked: a primary pointer
197
+ * button pressed and then released over it.
196
198
  * @fires {PointerEvent} pointerenter - Fired when the pointer moves onto the node.
197
199
  * @fires {PointerEvent} pointerleave - Fired when the pointer moves off the node.
198
200
  * @fires {PointerEvent} pointermove - Fired when the pointer moves over the node.
199
201
  * @fires {PointerEvent} pointerdown - Fired when a pointer button is pressed over the node.
200
202
  * @fires {PointerEvent} pointerup - Fired when a pointer button is released over the node.
203
+ * @fires {PointerEvent} click - Fired when a primary pointer button is pressed and then released
204
+ * over the node. A press and release that picked different entities fires on their nearest
205
+ * common ancestor instead, as in the DOM. `detail` carries the click count, so a double click
206
+ * arrives as a click whose `detail` is 2.
201
207
  */
202
208
  class NodeElement extends EntityBaseElement {
203
209
  private _name = '';
@@ -933,7 +939,7 @@ class NodeElement extends EntityBaseElement {
933
939
  'rotation',
934
940
  'scale',
935
941
  'tags',
936
- ...POINTER_ATTRIBUTES
942
+ ...EVENT_ATTRIBUTES
937
943
  ];
938
944
  }
939
945
 
@@ -982,6 +988,7 @@ class NodeElement extends EntityBaseElement {
982
988
  case 'onpointerdown':
983
989
  case 'onpointerup':
984
990
  case 'onpointermove':
991
+ case 'onclick':
985
992
  this._updateInlineHandler(name, newValue);
986
993
  break;
987
994
  }