@needle-tools/engine 4.14.0-beta → 4.14.0-next.52fdb13
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/CHANGELOG.md +1 -1
- package/components.needle.json +1 -1
- package/dist/{needle-engine.bundle-BW2VusZV.min.js → needle-engine.bundle-BwfaInTa.min.js} +131 -123
- package/dist/{needle-engine.bundle-Cb5bKEqa.umd.cjs → needle-engine.bundle-DJE-Bjpa.umd.cjs} +124 -116
- package/dist/{needle-engine.bundle-D9VPvp5o.js → needle-engine.bundle-TmE5-_na.js} +3457 -3227
- package/dist/needle-engine.d.ts +430 -94
- package/dist/needle-engine.js +570 -569
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/dist/{postprocessing-CctM1XIO.min.js → postprocessing-06AXuvdv.min.js} +2 -2
- package/dist/{postprocessing-DGm6qJ-I.js → postprocessing-CI2x8Cln.js} +2 -2
- package/dist/{postprocessing-Dbl2PJpd.umd.cjs → postprocessing-CPDcA21P.umd.cjs} +2 -2
- package/lib/engine/api.d.ts +203 -18
- package/lib/engine/api.js +271 -18
- package/lib/engine/api.js.map +1 -1
- package/lib/engine/engine_accessibility.d.ts +58 -0
- package/lib/engine/engine_accessibility.js +143 -0
- package/lib/engine/engine_accessibility.js.map +1 -0
- package/lib/engine/engine_context.d.ts +2 -0
- package/lib/engine/engine_context.js +7 -0
- package/lib/engine/engine_context.js.map +1 -1
- package/lib/engine/engine_materialpropertyblock.d.ts +91 -5
- package/lib/engine/engine_materialpropertyblock.js +97 -7
- package/lib/engine/engine_materialpropertyblock.js.map +1 -1
- package/lib/engine/engine_math.d.ts +34 -1
- package/lib/engine/engine_math.js +34 -1
- package/lib/engine/engine_math.js.map +1 -1
- package/lib/engine/engine_networking.js +1 -1
- package/lib/engine/engine_networking.js.map +1 -1
- package/lib/engine/engine_types.d.ts +2 -0
- package/lib/engine/engine_types.js +2 -0
- package/lib/engine/engine_types.js.map +1 -1
- package/lib/engine/webcomponents/icons.js +3 -0
- package/lib/engine/webcomponents/icons.js.map +1 -1
- package/lib/engine/webcomponents/logo-element.d.ts +1 -0
- package/lib/engine/webcomponents/logo-element.js +3 -1
- package/lib/engine/webcomponents/logo-element.js.map +1 -1
- package/lib/engine/webcomponents/needle-button.d.ts +37 -11
- package/lib/engine/webcomponents/needle-button.js +42 -11
- package/lib/engine/webcomponents/needle-button.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.d.ts +10 -2
- package/lib/engine/webcomponents/needle-engine.js +13 -3
- package/lib/engine/webcomponents/needle-engine.js.map +1 -1
- package/lib/engine-components/Component.d.ts +1 -2
- package/lib/engine-components/Component.js +1 -2
- package/lib/engine-components/Component.js.map +1 -1
- package/lib/engine-components/DragControls.d.ts +1 -0
- package/lib/engine-components/DragControls.js +21 -0
- package/lib/engine-components/DragControls.js.map +1 -1
- package/lib/engine-components/GroundProjection.js.map +1 -1
- package/lib/engine-components/NeedleMenu.d.ts +2 -0
- package/lib/engine-components/NeedleMenu.js +2 -0
- package/lib/engine-components/NeedleMenu.js.map +1 -1
- package/lib/engine-components/Networking.d.ts +28 -3
- package/lib/engine-components/Networking.js +28 -3
- package/lib/engine-components/Networking.js.map +1 -1
- package/lib/engine-components/ReflectionProbe.d.ts +1 -0
- package/lib/engine-components/ReflectionProbe.js +21 -3
- package/lib/engine-components/ReflectionProbe.js.map +1 -1
- package/lib/engine-components/RendererLightmap.js +1 -1
- package/lib/engine-components/RendererLightmap.js.map +1 -1
- package/lib/engine-components/SeeThrough.js +1 -1
- package/lib/engine-components/SeeThrough.js.map +1 -1
- package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.d.ts +107 -13
- package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.js +167 -30
- package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.js.map +1 -1
- package/lib/engine-components/ui/Button.d.ts +1 -0
- package/lib/engine-components/ui/Button.js +11 -0
- package/lib/engine-components/ui/Button.js.map +1 -1
- package/lib/engine-components/ui/Text.d.ts +1 -0
- package/lib/engine-components/ui/Text.js +11 -0
- package/lib/engine-components/ui/Text.js.map +1 -1
- package/lib/engine-components/web/ViewBox.js.map +1 -1
- package/package.json +2 -2
- package/src/engine/api.ts +371 -19
- package/src/engine/engine_accessibility.ts +178 -0
- package/src/engine/engine_context.ts +9 -0
- package/src/engine/engine_materialpropertyblock.ts +103 -12
- package/src/engine/engine_math.ts +34 -1
- package/src/engine/engine_networking.ts +1 -1
- package/src/engine/engine_types.ts +5 -0
- package/src/engine/webcomponents/icons.ts +3 -0
- package/src/engine/webcomponents/logo-element.ts +4 -1
- package/src/engine/webcomponents/needle-button.ts +44 -13
- package/src/engine/webcomponents/needle-engine.ts +18 -7
- package/src/engine-components/Component.ts +1 -3
- package/src/engine-components/DragControls.ts +29 -4
- package/src/engine-components/GroundProjection.ts +1 -1
- package/src/engine-components/NeedleMenu.ts +5 -3
- package/src/engine-components/Networking.ts +29 -4
- package/src/engine-components/ReflectionProbe.ts +22 -3
- package/src/engine-components/RendererLightmap.ts +1 -1
- package/src/engine-components/SeeThrough.ts +1 -1
- package/src/engine-components/export/usdz/extensions/behavior/BehaviourComponents.ts +198 -65
- package/src/engine-components/ui/Button.ts +12 -0
- package/src/engine-components/ui/Text.ts +13 -0
- package/src/engine-components/web/ViewBox.ts +3 -3
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { Color, CubeReflectionMapping, CubeTexture, EquirectangularReflectionMapping, LinearSRGBColorSpace, Material, MeshBasicMaterial, MeshStandardMaterial, Object3D, SRGBColorSpace, Texture, Vector3 } from "three";
|
|
2
2
|
|
|
3
3
|
import { isDevEnvironment, showBalloonWarning } from "../engine/debug/index.js";
|
|
4
|
+
import { MaterialPropertyBlock } from "../engine/engine_materialpropertyblock.js";
|
|
4
5
|
import { serializable } from "../engine/engine_serialization.js";
|
|
5
6
|
import { Context } from "../engine/engine_setup.js";
|
|
6
7
|
import type { IRenderer } from "../engine/engine_types.js";
|
|
7
|
-
import { getParam } from "../engine/engine_utils.js";
|
|
8
|
+
import { getParam, resolveUrl } from "../engine/engine_utils.js";
|
|
8
9
|
import { BoxHelperComponent } from "./BoxHelperComponent.js";
|
|
9
10
|
import { Behaviour } from "./Component.js";
|
|
10
|
-
import { MaterialPropertyBlock } from "../engine/engine_materialpropertyblock.js";
|
|
11
11
|
import { EventList } from "./EventList.js";
|
|
12
|
+
import { loadPMREM } from "../engine/engine_pmrem.js";
|
|
12
13
|
|
|
13
14
|
export const debug = getParam("debugreflectionprobe");
|
|
14
15
|
const disable = getParam("noreflectionprobe");
|
|
@@ -90,10 +91,28 @@ export class ReflectionProbe extends Behaviour {
|
|
|
90
91
|
}
|
|
91
92
|
|
|
92
93
|
private _texture!: Texture;
|
|
94
|
+
private _textureUrlInFlight?: string;
|
|
93
95
|
|
|
94
|
-
@serializable(Texture)
|
|
96
|
+
@serializable([Texture, String])
|
|
95
97
|
set texture(tex: Texture) {
|
|
96
98
|
if (this._texture === tex) return;
|
|
99
|
+
|
|
100
|
+
if (typeof tex === "string") {
|
|
101
|
+
if(debug) console.debug(`[ReflectionProbe] Loading reflection probe texture from URL: ${tex}`);
|
|
102
|
+
this._textureUrlInFlight = tex;
|
|
103
|
+
const textureUrl = resolveUrl(this.sourceId, tex);
|
|
104
|
+
loadPMREM(textureUrl, this.context.renderer).then(loaded => {
|
|
105
|
+
if (this._textureUrlInFlight === tex && loaded) {
|
|
106
|
+
this._textureUrlInFlight = undefined;
|
|
107
|
+
if (debug) console.debug(`[ReflectionProbe] Successfully loaded reflection probe texture: ${tex}`);
|
|
108
|
+
this.texture = loaded;
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
this._textureUrlInFlight = undefined;
|
|
115
|
+
|
|
97
116
|
this._texture = tex;
|
|
98
117
|
|
|
99
118
|
if (debug) console.debug("[ReflectionProbe] Set reflection probe texture " + (tex?.name || "(removed)"));
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { NEEDLE_progressive } from "@needle-tools/gltf-progressive";
|
|
2
2
|
import { Group, Mesh, Object3D, ShaderMaterial, Texture, Vector2, Vector4 } from "three";
|
|
3
3
|
|
|
4
|
+
import { MaterialPropertyBlock } from "../engine/engine_materialpropertyblock.js";
|
|
4
5
|
import type { Context } from "../engine/engine_setup.js";
|
|
5
6
|
import { getParam } from "../engine/engine_utils.js";
|
|
6
|
-
import { MaterialPropertyBlock } from "../engine/engine_materialpropertyblock.js";
|
|
7
7
|
import { type Renderer } from "./Renderer.js";
|
|
8
8
|
|
|
9
9
|
const debug = getParam("debuglightmaps");
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Material, Object3D, Object3DEventMap, Vector3 } from "three";
|
|
2
2
|
|
|
3
3
|
import { Gizmos } from "../engine/engine_gizmos.js";
|
|
4
|
+
import { MaterialPropertyBlock } from "../engine/engine_materialpropertyblock.js";
|
|
4
5
|
import { Mathf } from "../engine/engine_math.js";
|
|
5
6
|
import { serializable } from "../engine/engine_serialization_decorator.js";
|
|
6
7
|
import { getTempVector } from "../engine/engine_three_utils.js";
|
|
@@ -11,7 +12,6 @@ import { IUSDExporterExtension } from "./export/usdz/Extension.js";
|
|
|
11
12
|
import { USDZExporter } from "./export/usdz/USDZExporter.js";
|
|
12
13
|
import type { OrbitControls } from "./OrbitControls.js";
|
|
13
14
|
import { Renderer } from "./Renderer.js";
|
|
14
|
-
import { MaterialPropertyBlock } from "../engine/engine_materialpropertyblock.js";
|
|
15
15
|
|
|
16
16
|
const debugSeeThrough = getParam("debugseethrough");
|
|
17
17
|
|
|
@@ -13,41 +13,40 @@ import { AudioSource } from "../../../../AudioSource.js";
|
|
|
13
13
|
import { Behaviour, GameObject } from "../../../../Component.js";
|
|
14
14
|
import { Rigidbody } from "../../../../RigidBody.js";
|
|
15
15
|
import type { IPointerClickHandler, PointerEventData } from "../../../../ui/PointerEvents.js";
|
|
16
|
-
import {
|
|
17
|
-
import { makeNameSafeForUSD,USDDocument, USDObject, USDZExporterContext } from "../../ThreeUSDZExporter.js";
|
|
16
|
+
import { makeNameSafeForUSD, USDDocument, USDObject, USDZExporterContext } from "../../ThreeUSDZExporter.js";
|
|
18
17
|
import { AnimationExtension, RegisteredAnimationInfo, type UsdzAnimation } from "../Animation.js";
|
|
19
18
|
import { AudioExtension } from "./AudioExtension.js";
|
|
20
19
|
import type { BehaviorExtension, UsdzBehaviour } from "./Behaviour.js";
|
|
21
|
-
import { ActionBuilder, ActionModel, BehaviorModel, EmphasizeActionMotionType,GroupActionModel,type IBehaviorElement, Target, TriggerBuilder } from "./BehavioursBuilder.js";
|
|
20
|
+
import { ActionBuilder, ActionModel, BehaviorModel, EmphasizeActionMotionType, GroupActionModel, type IBehaviorElement, Target, TriggerBuilder } from "./BehavioursBuilder.js";
|
|
22
21
|
|
|
23
22
|
const debug = getParam("debugusdzbehaviours");
|
|
24
23
|
|
|
25
|
-
function ensureRaycaster(obj: GameObject) {
|
|
26
|
-
if (!obj) return;
|
|
27
|
-
if (!obj.getComponentInParent(Raycaster)) {
|
|
28
|
-
if (isDevEnvironment())
|
|
29
|
-
console.debug("Raycaster on \"" + obj.name + "\" was automatically added, because no raycaster was found in the parent hierarchy.")
|
|
30
|
-
obj.addComponent(ObjectRaycaster);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
24
|
/**
|
|
35
|
-
*
|
|
25
|
+
* Moves an object to the target object's transform when clicked.
|
|
26
|
+
* Works in the browser and in USDZ/QuickLook (Everywhere Actions).
|
|
27
|
+
*
|
|
28
|
+
* @see {@link SetActiveOnClick}to toggle visibility of objects when clicked
|
|
29
|
+
* @see {@link PlayAnimationOnClick} to play animations when clicked
|
|
30
|
+
* @see [Everywhere Actions](https://engine.needle.tools/docs/everywhere-actions)
|
|
36
31
|
* @summary Moves an object to a target transform upon click
|
|
37
32
|
* @category Everywhere Actions
|
|
38
33
|
* @group Components
|
|
39
34
|
*/
|
|
40
35
|
export class ChangeTransformOnClick extends Behaviour implements IPointerClickHandler, UsdzBehaviour {
|
|
41
36
|
|
|
37
|
+
/** The object to move. */
|
|
42
38
|
@serializable(Object3D)
|
|
43
39
|
object?: Object3D;
|
|
44
40
|
|
|
41
|
+
/** The target object whose transform to move to. */
|
|
45
42
|
@serializable(Object3D)
|
|
46
43
|
target?: Object3D;
|
|
47
44
|
|
|
45
|
+
/** The duration of the movement animation in seconds. */
|
|
48
46
|
@serializable()
|
|
49
47
|
duration: number = 1;
|
|
50
48
|
|
|
49
|
+
/** If true, the motion is relative to the object's current transform instead of moving to the target's absolute position. */
|
|
51
50
|
@serializable()
|
|
52
51
|
relativeMotion: boolean = false;
|
|
53
52
|
|
|
@@ -57,8 +56,20 @@ export class ChangeTransformOnClick extends Behaviour implements IPointerClickHa
|
|
|
57
56
|
private targetRot = new Quaternion();
|
|
58
57
|
private targetScale = new Vector3();
|
|
59
58
|
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
onEnable(): void {
|
|
60
|
+
this.context.accessibility.updateElement(this, {
|
|
61
|
+
role: "button",
|
|
62
|
+
label: "Move " + (this.object?.name || "object") + " to " + (this.target?.name || "target") + " on click",
|
|
63
|
+
hidden: false,
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
onDisable(): void {
|
|
67
|
+
this.context.accessibility.updateElement(this, {
|
|
68
|
+
hidden: true,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
onDestroy(): void {
|
|
72
|
+
this.context.accessibility.removeElement(this);
|
|
62
73
|
}
|
|
63
74
|
|
|
64
75
|
onPointerEnter() {
|
|
@@ -72,8 +83,8 @@ export class ChangeTransformOnClick extends Behaviour implements IPointerClickHa
|
|
|
72
83
|
|
|
73
84
|
const rbs = this.object?.getComponentsInChildren(Rigidbody);
|
|
74
85
|
|
|
75
|
-
if (rbs){
|
|
76
|
-
for (const rb of rbs) {
|
|
86
|
+
if (rbs) {
|
|
87
|
+
for (const rb of rbs) {
|
|
77
88
|
rb.resetVelocities();
|
|
78
89
|
rb.resetForcesAndTorques();
|
|
79
90
|
}
|
|
@@ -189,7 +200,15 @@ export class ChangeTransformOnClick extends Behaviour implements IPointerClickHa
|
|
|
189
200
|
}
|
|
190
201
|
|
|
191
202
|
/**
|
|
192
|
-
*
|
|
203
|
+
* Switches the material of objects in the scene when clicked.
|
|
204
|
+
* Works in the browser and in USDZ/QuickLook (Everywhere Actions).
|
|
205
|
+
*
|
|
206
|
+
* Finds all objects in the scene that use `materialToSwitch` and replaces it with `variantMaterial`.
|
|
207
|
+
* Multiple `ChangeMaterialOnClick` components using the same `materialToSwitch` can be combined to create a material selection UI.
|
|
208
|
+
*
|
|
209
|
+
* @see {@link SetActiveOnClick} to toggle visibility of objects when clicked
|
|
210
|
+
* @see {@link PlayAnimationOnClick} to play animations when clicked
|
|
211
|
+
* @see [Everywhere Actions](https://engine.needle.tools/docs/everywhere-actions)
|
|
193
212
|
* @summary Changes the material of objects when clicked
|
|
194
213
|
* @category Everywhere Actions
|
|
195
214
|
* @group Components
|
|
@@ -218,12 +237,27 @@ export class ChangeMaterialOnClick extends Behaviour implements IPointerClickHan
|
|
|
218
237
|
start(): void {
|
|
219
238
|
// initialize the object list
|
|
220
239
|
this._objectsWithThisMaterial = this.objectsWithThisMaterial;
|
|
221
|
-
ensureRaycaster(this.gameObject);
|
|
222
240
|
if (isDevEnvironment() && this._objectsWithThisMaterial.length <= 0) {
|
|
223
241
|
console.warn("ChangeMaterialOnClick: No objects found with material \"" + this.materialToSwitch?.name + "\"");
|
|
224
242
|
}
|
|
225
243
|
}
|
|
226
244
|
|
|
245
|
+
onEnable(): void {
|
|
246
|
+
this.context.accessibility.updateElement(this, {
|
|
247
|
+
role: "button",
|
|
248
|
+
label: "Change material to " + (this.variantMaterial?.name || "unknown material"),
|
|
249
|
+
hidden: false,
|
|
250
|
+
})
|
|
251
|
+
}
|
|
252
|
+
onDisable(): void {
|
|
253
|
+
this.context.accessibility.updateElement(this, {
|
|
254
|
+
hidden: true,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
onDestroy(): void {
|
|
258
|
+
this.context.accessibility.removeElement(this);
|
|
259
|
+
}
|
|
260
|
+
|
|
227
261
|
onPointerEnter(_args: PointerEventData) {
|
|
228
262
|
this.context.input.setCursor("pointer");
|
|
229
263
|
}
|
|
@@ -345,7 +379,7 @@ export class ChangeMaterialOnClick extends Behaviour implements IPointerClickHan
|
|
|
345
379
|
);
|
|
346
380
|
ChangeMaterialOnClick._parallelStartHiddenActions.push(...myVariants);
|
|
347
381
|
if (!ChangeMaterialOnClick._startHiddenBehaviour) {
|
|
348
|
-
ChangeMaterialOnClick._startHiddenBehaviour =
|
|
382
|
+
ChangeMaterialOnClick._startHiddenBehaviour =
|
|
349
383
|
new BehaviorModel("StartHidden_" + this.selfModel.name,
|
|
350
384
|
TriggerBuilder.sceneStartTrigger(),
|
|
351
385
|
ActionBuilder.fadeAction(ChangeMaterialOnClick._parallelStartHiddenActions, fadeDuration, false));
|
|
@@ -381,29 +415,37 @@ export class ChangeMaterialOnClick extends Behaviour implements IPointerClickHan
|
|
|
381
415
|
}
|
|
382
416
|
|
|
383
417
|
/**
|
|
384
|
-
*
|
|
418
|
+
* Shows or hides a target object when this object is clicked.
|
|
419
|
+
* Works in the browser and in USDZ/QuickLook (Everywhere Actions).
|
|
420
|
+
*
|
|
421
|
+
* Optionally hides itself after being clicked (`hideSelf`), or toggles the target's visibility on each click (`toggleOnClick`).
|
|
422
|
+
*
|
|
423
|
+
* @see {@link HideOnStart}to hide an object when the scene starts
|
|
424
|
+
* @see {@link PlayAnimationOnClick} to play animations when clicked
|
|
425
|
+
* @see {@link ChangeMaterialOnClick} to change material when clicked
|
|
426
|
+
* @see [Everywhere Actions](https://engine.needle.tools/docs/everywhere-actions)
|
|
385
427
|
* @summary Sets the active state of an object when clicked
|
|
386
428
|
* @category Everywhere Actions
|
|
387
429
|
* @group Components
|
|
388
430
|
*/
|
|
389
431
|
export class SetActiveOnClick extends Behaviour implements IPointerClickHandler, UsdzBehaviour {
|
|
390
432
|
|
|
433
|
+
/** The target object to show or hide. */
|
|
391
434
|
@serializable(Object3D)
|
|
392
435
|
target?: Object3D;
|
|
393
436
|
|
|
437
|
+
/** If true, the target's visibility will be toggled on each click. When enabled, `hideSelf` and `targetState` are ignored. */
|
|
394
438
|
@serializable()
|
|
395
439
|
toggleOnClick: boolean = false;
|
|
396
440
|
|
|
441
|
+
/** The visibility state to apply to the target when clicked. Only used when `toggleOnClick` is false. */
|
|
397
442
|
@serializable()
|
|
398
443
|
targetState: boolean = true;
|
|
399
444
|
|
|
445
|
+
/** If true, this object will hide itself after being clicked. Only used when `toggleOnClick` is false. */
|
|
400
446
|
@serializable()
|
|
401
447
|
hideSelf: boolean = true;
|
|
402
448
|
|
|
403
|
-
start(): void {
|
|
404
|
-
ensureRaycaster(this.gameObject);
|
|
405
|
-
}
|
|
406
|
-
|
|
407
449
|
onPointerEnter() {
|
|
408
450
|
this.context.input.setCursor("pointer");
|
|
409
451
|
}
|
|
@@ -416,7 +458,7 @@ export class SetActiveOnClick extends Behaviour implements IPointerClickHandler,
|
|
|
416
458
|
|
|
417
459
|
if (!this.toggleOnClick && this.hideSelf)
|
|
418
460
|
this.gameObject.visible = false;
|
|
419
|
-
|
|
461
|
+
|
|
420
462
|
if (this.target)
|
|
421
463
|
this.target.visible = this.toggleOnClick ? !this.target.visible : this.targetState;
|
|
422
464
|
}
|
|
@@ -442,7 +484,7 @@ export class SetActiveOnClick extends Behaviour implements IPointerClickHandler,
|
|
|
442
484
|
|
|
443
485
|
beforeCreateDocument() {
|
|
444
486
|
if (!this.target) return;
|
|
445
|
-
|
|
487
|
+
|
|
446
488
|
// need to cache on the object itself, because otherwise different actions would override each other's visibility state
|
|
447
489
|
// TODO would probably be better to have this somewhere on the exporter, not on this component
|
|
448
490
|
if (this.gameObject[SetActiveOnClick.wasVisible] === undefined)
|
|
@@ -503,7 +545,7 @@ export class SetActiveOnClick extends Behaviour implements IPointerClickHandler,
|
|
|
503
545
|
// It's much easier to reason about nested actions when we're not duplicating tons of hierarchy...
|
|
504
546
|
// We can probably only do a shallow clone when the tapped object has geometry of its own, otherwise
|
|
505
547
|
// we end up with nothing to tap on.
|
|
506
|
-
|
|
548
|
+
|
|
507
549
|
// Option A: we deep clone ourselves. This makes hierarchical cases and nested behaviours really complex.
|
|
508
550
|
// We do this currently when the object doesn't have any geometry.
|
|
509
551
|
if (!this.selfModelClone.geometry) {
|
|
@@ -522,11 +564,11 @@ export class SetActiveOnClick extends Behaviour implements IPointerClickHandler,
|
|
|
522
564
|
clone.name += "_toggle" + (SetActiveOnClick.clonedToggleIndex++);
|
|
523
565
|
originalModel.add(clone);
|
|
524
566
|
this.gameObject[SetActiveOnClick.toggleClone] = clone;
|
|
525
|
-
|
|
567
|
+
|
|
526
568
|
console.warn("USDZExport: Toggle " + this.gameObject.name + " doesn't have geometry. It will be deep cloned and nested behaviours will likely not work.");
|
|
527
569
|
}
|
|
528
570
|
const clonedSelfModel = this.gameObject[SetActiveOnClick.toggleClone];
|
|
529
|
-
|
|
571
|
+
|
|
530
572
|
if (!this.gameObject[SetActiveOnClick.reverseToggleClone]) {
|
|
531
573
|
const clone = this.selfModelClone.clone();
|
|
532
574
|
clone.setMatrix(new Matrix4());
|
|
@@ -578,7 +620,7 @@ export class SetActiveOnClick extends Behaviour implements IPointerClickHandler,
|
|
|
578
620
|
TriggerBuilder.tapTrigger(selfModel),
|
|
579
621
|
ActionBuilder.parallel(...toggleSequence)
|
|
580
622
|
));
|
|
581
|
-
|
|
623
|
+
|
|
582
624
|
const reverseSequence: ActionModel[] = [];
|
|
583
625
|
reverseSequence.push(ActionBuilder.fadeAction(this.toggleModel, 0, false));
|
|
584
626
|
reverseSequence.push(ActionBuilder.fadeAction(selfModel, 0, true));
|
|
@@ -621,7 +663,13 @@ export class SetActiveOnClick extends Behaviour implements IPointerClickHandler,
|
|
|
621
663
|
}
|
|
622
664
|
|
|
623
665
|
/**
|
|
624
|
-
* Hides the object
|
|
666
|
+
* Hides the object when the scene starts.
|
|
667
|
+
* Works in the browser and in USDZ/QuickLook (Everywhere Actions).
|
|
668
|
+
*
|
|
669
|
+
* Useful for setting up objects that should initially be hidden and shown later via a {@link SetActiveOnClick} component.
|
|
670
|
+
*
|
|
671
|
+
* @see {@link SetActiveOnClick} to show or hide objects on click
|
|
672
|
+
* @see [Everywhere Actions](https://engine.needle.tools/docs/everywhere-actions)
|
|
625
673
|
* @summary Hides the object on scene start
|
|
626
674
|
* @category Everywhere Actions
|
|
627
675
|
* @group Components
|
|
@@ -663,29 +711,55 @@ export class HideOnStart extends Behaviour implements UsdzBehaviour {
|
|
|
663
711
|
}
|
|
664
712
|
|
|
665
713
|
private wasVisible: boolean = false;
|
|
666
|
-
|
|
714
|
+
|
|
667
715
|
beforeCreateDocument() {
|
|
668
716
|
this.wasVisible = GameObject.isActiveSelf(this.gameObject);
|
|
669
717
|
}
|
|
670
718
|
}
|
|
671
719
|
|
|
672
720
|
/**
|
|
673
|
-
*
|
|
721
|
+
* Applies an emphasis animation to a target object when this object is clicked.
|
|
722
|
+
* Works in USDZ/QuickLook (Everywhere Actions).
|
|
723
|
+
*
|
|
724
|
+
* The emphasis effect can be a bounce, jiggle, or other motion type defined by `motionType`.
|
|
725
|
+
*
|
|
726
|
+
* @see {@link PlayAnimationOnClick} to play animations when clicked
|
|
727
|
+
* @see {@link SetActiveOnClick} to toggle visibility when clicked
|
|
728
|
+
* @see [Everywhere Actions](https://engine.needle.tools/docs/everywhere-actions)
|
|
674
729
|
* @summary Emphasizes the target object when clicked
|
|
675
730
|
* @category Everywhere Actions
|
|
676
731
|
* @group Components
|
|
677
732
|
*/
|
|
678
733
|
export class EmphasizeOnClick extends Behaviour implements UsdzBehaviour {
|
|
679
734
|
|
|
735
|
+
/** The target object to emphasize. */
|
|
680
736
|
@serializable()
|
|
681
737
|
target?: Object3D;
|
|
682
738
|
|
|
739
|
+
/** The duration of the emphasis animation in seconds. */
|
|
683
740
|
@serializable()
|
|
684
741
|
duration: number = 0.5;
|
|
685
742
|
|
|
743
|
+
/** The type of motion to use for the emphasis effect (e.g. `"bounce"`, `"jiggle"`). */
|
|
686
744
|
@serializable()
|
|
687
745
|
motionType: EmphasizeActionMotionType = "bounce";
|
|
688
746
|
|
|
747
|
+
onEnable(): void {
|
|
748
|
+
this.context.accessibility.updateElement(this, {
|
|
749
|
+
role: "button",
|
|
750
|
+
label: "Emphasize " + this.target?.name + " on click",
|
|
751
|
+
hidden: false,
|
|
752
|
+
})
|
|
753
|
+
}
|
|
754
|
+
onDisable(): void {
|
|
755
|
+
this.context.accessibility.updateElement(this, {
|
|
756
|
+
hidden: true,
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
onDestroy(): void {
|
|
760
|
+
this.context.accessibility.removeElement(this);
|
|
761
|
+
}
|
|
762
|
+
|
|
689
763
|
beforeCreateDocument() { }
|
|
690
764
|
|
|
691
765
|
createBehaviours(ext, model, _context) {
|
|
@@ -704,29 +778,37 @@ export class EmphasizeOnClick extends Behaviour implements UsdzBehaviour {
|
|
|
704
778
|
}
|
|
705
779
|
|
|
706
780
|
/**
|
|
707
|
-
* Plays an audio clip when clicked.
|
|
781
|
+
* Plays an audio clip when this object is clicked.
|
|
782
|
+
* Works in the browser and in USDZ/QuickLook (Everywhere Actions).
|
|
783
|
+
*
|
|
784
|
+
* Assign a `target` {@link AudioSource} to use its spatial audio settings, or assign a `clip` URL directly.
|
|
785
|
+
* If no `target` is assigned, an {@link AudioSource} will be created automatically on this object.
|
|
786
|
+
*
|
|
787
|
+
* @see {@link AudioSource}for spatial audio settings
|
|
788
|
+
* @see {@link PlayAnimationOnClick} to play animations when clicked
|
|
789
|
+
* @see {@link SetActiveOnClick} to toggle visibility when clicked
|
|
790
|
+
* @see [Everywhere Actions](https://engine.needle.tools/docs/everywhere-actions)
|
|
708
791
|
* @summary Plays an audio clip when clicked
|
|
709
792
|
* @category Everywhere Actions
|
|
710
793
|
* @group Components
|
|
711
794
|
*/
|
|
712
795
|
export class PlayAudioOnClick extends Behaviour implements IPointerClickHandler, UsdzBehaviour {
|
|
713
796
|
|
|
797
|
+
/** The {@link AudioSource} to use for playback. If not set, one will be created automatically on this object. */
|
|
714
798
|
@serializable(AudioSource)
|
|
715
799
|
target?: AudioSource;
|
|
716
800
|
|
|
801
|
+
/** URL of the audio clip to play. If not set, the clip assigned to `target` is used. */
|
|
717
802
|
@serializable(URL)
|
|
718
803
|
clip: string = "";
|
|
719
804
|
|
|
805
|
+
/** If true, clicking again while the audio is playing will stop it. */
|
|
720
806
|
@serializable()
|
|
721
807
|
toggleOnClick: boolean = false;
|
|
722
808
|
|
|
723
809
|
// Not exposed, but used for implicit playback of PlayOnAwake audio sources
|
|
724
810
|
trigger: "tap" | "start" = "tap";
|
|
725
811
|
|
|
726
|
-
start(): void {
|
|
727
|
-
ensureRaycaster(this.gameObject);
|
|
728
|
-
}
|
|
729
|
-
|
|
730
812
|
ensureAudioSource() {
|
|
731
813
|
if (!this.target) {
|
|
732
814
|
const newAudioSource = this.gameObject.addComponent(AudioSource);
|
|
@@ -740,6 +822,21 @@ export class PlayAudioOnClick extends Behaviour implements IPointerClickHandler,
|
|
|
740
822
|
}
|
|
741
823
|
}
|
|
742
824
|
|
|
825
|
+
onEnable(): void {
|
|
826
|
+
this.context.accessibility.updateElement(this, {
|
|
827
|
+
role: "button",
|
|
828
|
+
label: "Play audio: " + (this.clip || this.target?.clip || "unknown clip"),
|
|
829
|
+
hidden: false,
|
|
830
|
+
})
|
|
831
|
+
}
|
|
832
|
+
onDisable(): void {
|
|
833
|
+
this.context.accessibility.updateElement(this, {
|
|
834
|
+
hidden: true,
|
|
835
|
+
});
|
|
836
|
+
}
|
|
837
|
+
onDestroy(): void {
|
|
838
|
+
this.context.accessibility.removeElement(this);
|
|
839
|
+
}
|
|
743
840
|
|
|
744
841
|
onPointerEnter() {
|
|
745
842
|
this.context.input.setCursor("pointer");
|
|
@@ -781,7 +878,7 @@ export class PlayAudioOnClick extends Behaviour implements IPointerClickHandler,
|
|
|
781
878
|
const clipName = AudioExtension.getName(clipUrl);
|
|
782
879
|
const volume = this.target ? this.target.volume : 1;
|
|
783
880
|
const auralMode = this.target && this.target.spatialBlend == 0 ? "nonSpatial" : "spatial";
|
|
784
|
-
|
|
881
|
+
|
|
785
882
|
// This checks if any child is clickable – if yes, the tap trigger is added; if not, we omit it.
|
|
786
883
|
let anyChildHasGeometry = false;
|
|
787
884
|
this.gameObject.traverse(c => {
|
|
@@ -790,7 +887,7 @@ export class PlayAudioOnClick extends Behaviour implements IPointerClickHandler,
|
|
|
790
887
|
// Workaround: seems iOS often simply doesn't play audio on scene start when this is NOT present.
|
|
791
888
|
// unclear why, but having a useless tap action (nothing to tap on) "fixes" it.
|
|
792
889
|
anyChildHasGeometry = true;
|
|
793
|
-
|
|
890
|
+
|
|
794
891
|
const audioClip = ext.addAudioClip(clipUrl);
|
|
795
892
|
// const stopAction: IBehaviorElement = ActionBuilder.playAudioAction(playbackTarget, audioClip, "stop", volume, auralMode);
|
|
796
893
|
let playAction: IBehaviorElement = ActionBuilder.playAudioAction(playbackTarget, audioClip, "play", volume, auralMode);
|
|
@@ -799,8 +896,7 @@ export class PlayAudioOnClick extends Behaviour implements IPointerClickHandler,
|
|
|
799
896
|
|
|
800
897
|
const behaviorName = (this.name ? "_" + this.name : "");
|
|
801
898
|
|
|
802
|
-
if (anyChildHasGeometry && this.trigger === "tap")
|
|
803
|
-
{
|
|
899
|
+
if (anyChildHasGeometry && this.trigger === "tap") {
|
|
804
900
|
// does not seem to work in iOS / QuickLook...
|
|
805
901
|
// TODO use play "type" which can be start/stop/pause
|
|
806
902
|
if (this.toggleOnClick) (playAction as ActionModel).multiplePerformOperation = "stop";
|
|
@@ -831,16 +927,30 @@ export class PlayAudioOnClick extends Behaviour implements IPointerClickHandler,
|
|
|
831
927
|
}
|
|
832
928
|
|
|
833
929
|
/**
|
|
834
|
-
* Plays an animation when clicked.
|
|
930
|
+
* Plays an animation state when this object is clicked.
|
|
931
|
+
* Works in the browser and in USDZ/QuickLook (Everywhere Actions).
|
|
932
|
+
*
|
|
933
|
+
* Assign an {@link Animator} and a `stateName` to play a specific animation state,
|
|
934
|
+
* or assign an {@link Animation} component to play a legacy animation clip.
|
|
935
|
+
*
|
|
936
|
+
* For USDZ export, the component follows animator state transitions automatically, including looping states.
|
|
937
|
+
*
|
|
938
|
+
* @see {@link Animator}for playing animator state machine animations
|
|
939
|
+
* @see {@link Animation} for playing legacy animation clips
|
|
940
|
+
* @see {@link PlayAudioOnClick} to play audio when clicked
|
|
941
|
+
* @see {@link SetActiveOnClick} to toggle visibility when clicked
|
|
942
|
+
* @see [Everywhere Actions](https://engine.needle.tools/docs/everywhere-actions)
|
|
835
943
|
* @summary Plays an animation when clicked
|
|
836
944
|
* @category Everywhere Actions
|
|
837
945
|
* @group Components
|
|
838
946
|
*/
|
|
839
947
|
export class PlayAnimationOnClick extends Behaviour implements IPointerClickHandler, UsdzBehaviour, UsdzAnimation {
|
|
840
948
|
|
|
949
|
+
/** The {@link Animator} component whose state to play when clicked. */
|
|
841
950
|
@serializable(Animator)
|
|
842
951
|
animator?: Animator;
|
|
843
952
|
|
|
953
|
+
/** The name of the animation state to play. Required when using an {@link Animator}. */
|
|
844
954
|
@serializable()
|
|
845
955
|
stateName?: string;
|
|
846
956
|
|
|
@@ -852,13 +962,25 @@ export class PlayAnimationOnClick extends Behaviour implements IPointerClickHand
|
|
|
852
962
|
|
|
853
963
|
private get target() { return this.animator?.gameObject || this.animation?.gameObject }
|
|
854
964
|
|
|
855
|
-
|
|
856
|
-
|
|
965
|
+
onEnable(): void {
|
|
966
|
+
this.context.accessibility.updateElement(this, {
|
|
967
|
+
role: "button",
|
|
968
|
+
label: "Plays animation " + (this.stateName || "") + " on " + (this.target ? this.target.name : ""),
|
|
969
|
+
hidden: false
|
|
970
|
+
});
|
|
971
|
+
}
|
|
972
|
+
onDisable(): void {
|
|
973
|
+
this.context.accessibility.updateElement(this, {
|
|
974
|
+
hidden: true,
|
|
975
|
+
});
|
|
976
|
+
}
|
|
977
|
+
onDestroy(): void {
|
|
978
|
+
this.context.accessibility.removeElement(this);
|
|
857
979
|
}
|
|
858
|
-
|
|
859
980
|
|
|
860
981
|
onPointerEnter() {
|
|
861
982
|
this.context.input.setCursor("pointer");
|
|
983
|
+
this.context.accessibility.hover(this, "Click to play animation " + (this.stateName || "") + " on " + (this.target ? this.target.name : ""));
|
|
862
984
|
}
|
|
863
985
|
onPointerExit() {
|
|
864
986
|
this.context.input.unsetCursor("pointer");
|
|
@@ -867,6 +989,7 @@ export class PlayAnimationOnClick extends Behaviour implements IPointerClickHand
|
|
|
867
989
|
args.use();
|
|
868
990
|
if (!this.target) return;
|
|
869
991
|
if (this.stateName) {
|
|
992
|
+
this.context.accessibility.focus(this);
|
|
870
993
|
// TODO this is currently quite annoying to use,
|
|
871
994
|
// as for the web we use the Animator component and its states directly,
|
|
872
995
|
// while in QuickLook we use explicit animations / states.
|
|
@@ -878,8 +1001,8 @@ export class PlayAnimationOnClick extends Behaviour implements IPointerClickHand
|
|
|
878
1001
|
|
|
879
1002
|
private stateAnimationModel: any;
|
|
880
1003
|
|
|
881
|
-
private animationSequence
|
|
882
|
-
private animationLoopAfterSequence
|
|
1004
|
+
private animationSequence?= new Array<RegisteredAnimationInfo>();
|
|
1005
|
+
private animationLoopAfterSequence?= new Array<RegisteredAnimationInfo>();
|
|
883
1006
|
private randomOffsetNormalized: number = 0;
|
|
884
1007
|
|
|
885
1008
|
createBehaviours(_ext: BehaviorExtension, model: USDObject, _context: USDZExporterContext) {
|
|
@@ -905,9 +1028,9 @@ export class PlayAnimationOnClick extends Behaviour implements IPointerClickHand
|
|
|
905
1028
|
afterCreateDocument(ext: BehaviorExtension, context: USDZExporterContext) {
|
|
906
1029
|
if ((this.animationSequence === undefined && this.animationLoopAfterSequence === undefined) || !this.stateAnimationModel) return;
|
|
907
1030
|
if (!this.target) return;
|
|
908
|
-
|
|
1031
|
+
|
|
909
1032
|
const document = context.document;
|
|
910
|
-
|
|
1033
|
+
|
|
911
1034
|
// check if the AnimationExtension has been attached and what data it has for the current object
|
|
912
1035
|
const animationExt = context.extensions.find(ext => ext instanceof AnimationExtension) as AnimationExtension;
|
|
913
1036
|
if (!animationExt) return;
|
|
@@ -921,7 +1044,7 @@ export class PlayAnimationOnClick extends Behaviour implements IPointerClickHand
|
|
|
921
1044
|
if (requiresExclusivePlayback) {
|
|
922
1045
|
if (isDevEnvironment())
|
|
923
1046
|
console.warn("Setting exclusive playback for " + this.target.name + "@" + this.stateName + " because it has " + animationExt.getClipCount(this.target) + " animations. This works around QuickLook bug FB13410767.");
|
|
924
|
-
|
|
1047
|
+
|
|
925
1048
|
PlayAnimationOnClick.rootsWithExclusivePlayback.add(this.target);
|
|
926
1049
|
}
|
|
927
1050
|
|
|
@@ -941,7 +1064,7 @@ export class PlayAnimationOnClick extends Behaviour implements IPointerClickHand
|
|
|
941
1064
|
this.trigger == "tap" ? TriggerBuilder.tapTrigger(this.selfModel) : TriggerBuilder.sceneStartTrigger(),
|
|
942
1065
|
sequence
|
|
943
1066
|
);
|
|
944
|
-
|
|
1067
|
+
|
|
945
1068
|
// See comment above for why exclusive playback is currently required when playing multiple animations on the same root.
|
|
946
1069
|
if (requiresExclusivePlayback)
|
|
947
1070
|
playAnimationOnTap.makeExclusive(true);
|
|
@@ -963,8 +1086,7 @@ export class PlayAnimationOnClick extends Behaviour implements IPointerClickHand
|
|
|
963
1086
|
|
|
964
1087
|
const sequence = ActionBuilder.sequence();
|
|
965
1088
|
|
|
966
|
-
if (animationSequence && animationSequence.length > 0)
|
|
967
|
-
{
|
|
1089
|
+
if (animationSequence && animationSequence.length > 0) {
|
|
968
1090
|
for (const anim of animationSequence) {
|
|
969
1091
|
sequence.addAction(getOrCacheAction(model, anim));
|
|
970
1092
|
}
|
|
@@ -988,11 +1110,10 @@ export class PlayAnimationOnClick extends Behaviour implements IPointerClickHand
|
|
|
988
1110
|
return sequence;
|
|
989
1111
|
}
|
|
990
1112
|
|
|
991
|
-
static getAndRegisterAnimationSequences(ext: AnimationExtension, target: GameObject, stateName?: string):
|
|
992
|
-
|
|
993
|
-
animationSequence: Array<RegisteredAnimationInfo>,
|
|
1113
|
+
static getAndRegisterAnimationSequences(ext: AnimationExtension, target: GameObject, stateName?: string): {
|
|
1114
|
+
animationSequence: Array<RegisteredAnimationInfo>,
|
|
994
1115
|
animationLoopAfterSequence: Array<RegisteredAnimationInfo>,
|
|
995
|
-
randomTimeOffset: number,
|
|
1116
|
+
randomTimeOffset: number,
|
|
996
1117
|
} | undefined {
|
|
997
1118
|
|
|
998
1119
|
if (!target) return undefined;
|
|
@@ -1010,14 +1131,14 @@ export class PlayAnimationOnClick extends Behaviour implements IPointerClickHand
|
|
|
1010
1131
|
let animationLoopAfterSequence: Array<RegisteredAnimationInfo> = [];
|
|
1011
1132
|
|
|
1012
1133
|
if (animation) {
|
|
1013
|
-
const anim = ext.registerAnimation(target, animation.clip);
|
|
1014
|
-
if (anim) {
|
|
1134
|
+
const anim = ext.registerAnimation(target, animation.clip);
|
|
1135
|
+
if (anim) {
|
|
1015
1136
|
if (animation.loop)
|
|
1016
1137
|
animationLoopAfterSequence.push(anim);
|
|
1017
1138
|
else
|
|
1018
1139
|
animationSequence.push(anim);
|
|
1019
1140
|
}
|
|
1020
|
-
|
|
1141
|
+
|
|
1021
1142
|
let randomTimeOffset = 0;
|
|
1022
1143
|
if (animation.minMaxOffsetNormalized) {
|
|
1023
1144
|
const from = animation.minMaxOffsetNormalized.x;
|
|
@@ -1111,7 +1232,7 @@ export class PlayAnimationOnClick extends Behaviour implements IPointerClickHand
|
|
|
1111
1232
|
if (lastClip) {
|
|
1112
1233
|
let clipCopy: AnimationClip | undefined;
|
|
1113
1234
|
if (ext.holdClipMap.has(lastClip)) {
|
|
1114
|
-
clipCopy = ext.holdClipMap.get(lastClip);
|
|
1235
|
+
clipCopy = ext.holdClipMap.get(lastClip);
|
|
1115
1236
|
}
|
|
1116
1237
|
else {
|
|
1117
1238
|
// We're creating a "hold" clip here; exactly 1 second long, and inteprolates exactly on the duration of the clip
|
|
@@ -1241,17 +1362,24 @@ export class PreliminaryTrigger extends Behaviour {
|
|
|
1241
1362
|
}
|
|
1242
1363
|
|
|
1243
1364
|
/**
|
|
1244
|
-
*
|
|
1365
|
+
* Action to show or hide an object.
|
|
1366
|
+
* Use together with a {@link TapGestureTrigger} to show or hide objects when tapped or clicked.
|
|
1367
|
+
*
|
|
1368
|
+
* @see {@link TapGestureTrigger} to trigger actions on tap/click
|
|
1369
|
+
* @see {@link SetActiveOnClick} for a combined trigger and action component
|
|
1370
|
+
* @see [Everywhere Actions](https://engine.needle.tools/docs/everywhere-actions)
|
|
1245
1371
|
* @summary Hides or shows the object when clicked
|
|
1246
1372
|
* @category Everywhere Actions
|
|
1247
1373
|
* @group Components
|
|
1248
1374
|
*/
|
|
1249
1375
|
export class VisibilityAction extends PreliminaryAction {
|
|
1250
1376
|
|
|
1377
|
+
/** The type of visibility action to apply. */
|
|
1251
1378
|
//@type int
|
|
1252
1379
|
@serializable()
|
|
1253
1380
|
type: VisibilityActionType = VisibilityActionType.Hide;
|
|
1254
1381
|
|
|
1382
|
+
/** The duration of the fade animation in seconds. */
|
|
1255
1383
|
@serializable()
|
|
1256
1384
|
duration: number = 1;
|
|
1257
1385
|
|
|
@@ -1268,7 +1396,12 @@ export class VisibilityAction extends PreliminaryAction {
|
|
|
1268
1396
|
}
|
|
1269
1397
|
|
|
1270
1398
|
/**
|
|
1271
|
-
* Triggers
|
|
1399
|
+
* Triggers a {@link PreliminaryAction} (such as {@link VisibilityAction}) when the object is tapped or clicked.
|
|
1400
|
+
* Works in the browser and in USDZ/QuickLook (Everywhere Actions).
|
|
1401
|
+
*
|
|
1402
|
+
* @see {@link VisibilityAction} for controlling object visibility on tap
|
|
1403
|
+
* @see {@link SetActiveOnClick} for a combined trigger and action component
|
|
1404
|
+
* @see [Everywhere Actions](https://engine.needle.tools/docs/everywhere-actions)
|
|
1272
1405
|
* @summary Triggers an action when the object is tapped/clicked
|
|
1273
1406
|
* @category Everywhere Actions
|
|
1274
1407
|
* @group Components
|