@needle-tools/engine 4.14.0-next.52fdb13 → 4.14.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 (79) hide show
  1. package/components.needle.json +1 -1
  2. package/dist/{needle-engine.bundle-DJE-Bjpa.umd.cjs → needle-engine.bundle-COL2Bar3.umd.cjs} +120 -128
  3. package/dist/{needle-engine.bundle-BwfaInTa.min.js → needle-engine.bundle-NolzHLqO.min.js} +129 -137
  4. package/dist/{needle-engine.bundle-TmE5-_na.js → needle-engine.bundle-Z_gAD7Kg.js} +5214 -5459
  5. package/dist/needle-engine.d.ts +42 -299
  6. package/dist/needle-engine.js +569 -570
  7. package/dist/needle-engine.min.js +1 -1
  8. package/dist/needle-engine.umd.cjs +1 -1
  9. package/lib/engine/engine_context.d.ts +0 -2
  10. package/lib/engine/engine_context.js +0 -7
  11. package/lib/engine/engine_context.js.map +1 -1
  12. package/lib/engine/engine_materialpropertyblock.d.ts +4 -90
  13. package/lib/engine/engine_materialpropertyblock.js +7 -97
  14. package/lib/engine/engine_materialpropertyblock.js.map +1 -1
  15. package/lib/engine/engine_math.d.ts +1 -34
  16. package/lib/engine/engine_math.js +1 -34
  17. package/lib/engine/engine_math.js.map +1 -1
  18. package/lib/engine/engine_networking.js +1 -1
  19. package/lib/engine/engine_networking.js.map +1 -1
  20. package/lib/engine/engine_types.d.ts +0 -2
  21. package/lib/engine/engine_types.js +0 -2
  22. package/lib/engine/engine_types.js.map +1 -1
  23. package/lib/engine/webcomponents/icons.js +0 -3
  24. package/lib/engine/webcomponents/icons.js.map +1 -1
  25. package/lib/engine/webcomponents/logo-element.d.ts +0 -1
  26. package/lib/engine/webcomponents/logo-element.js +1 -3
  27. package/lib/engine/webcomponents/logo-element.js.map +1 -1
  28. package/lib/engine/webcomponents/needle-button.d.ts +11 -37
  29. package/lib/engine/webcomponents/needle-button.js +11 -42
  30. package/lib/engine/webcomponents/needle-button.js.map +1 -1
  31. package/lib/engine/webcomponents/needle-engine.d.ts +2 -10
  32. package/lib/engine/webcomponents/needle-engine.js +3 -13
  33. package/lib/engine/webcomponents/needle-engine.js.map +1 -1
  34. package/lib/engine-components/Component.d.ts +2 -1
  35. package/lib/engine-components/Component.js +2 -1
  36. package/lib/engine-components/Component.js.map +1 -1
  37. package/lib/engine-components/DragControls.d.ts +0 -1
  38. package/lib/engine-components/DragControls.js +0 -21
  39. package/lib/engine-components/DragControls.js.map +1 -1
  40. package/lib/engine-components/NeedleMenu.d.ts +0 -2
  41. package/lib/engine-components/NeedleMenu.js +0 -2
  42. package/lib/engine-components/NeedleMenu.js.map +1 -1
  43. package/lib/engine-components/Networking.d.ts +3 -28
  44. package/lib/engine-components/Networking.js +3 -28
  45. package/lib/engine-components/Networking.js.map +1 -1
  46. package/lib/engine-components/ReflectionProbe.d.ts +0 -1
  47. package/lib/engine-components/ReflectionProbe.js +2 -20
  48. package/lib/engine-components/ReflectionProbe.js.map +1 -1
  49. package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.d.ts +0 -15
  50. package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.js +0 -77
  51. package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.js.map +1 -1
  52. package/lib/engine-components/ui/Button.d.ts +0 -1
  53. package/lib/engine-components/ui/Button.js +0 -11
  54. package/lib/engine-components/ui/Button.js.map +1 -1
  55. package/lib/engine-components/ui/Text.d.ts +0 -1
  56. package/lib/engine-components/ui/Text.js +0 -11
  57. package/lib/engine-components/ui/Text.js.map +1 -1
  58. package/package.json +2 -2
  59. package/src/engine/engine_context.ts +0 -9
  60. package/src/engine/engine_materialpropertyblock.ts +11 -102
  61. package/src/engine/engine_math.ts +1 -34
  62. package/src/engine/engine_networking.ts +1 -1
  63. package/src/engine/engine_types.ts +0 -5
  64. package/src/engine/webcomponents/icons.ts +0 -3
  65. package/src/engine/webcomponents/logo-element.ts +1 -4
  66. package/src/engine/webcomponents/needle-button.ts +13 -44
  67. package/src/engine/webcomponents/needle-engine.ts +7 -18
  68. package/src/engine-components/Component.ts +3 -1
  69. package/src/engine-components/DragControls.ts +4 -29
  70. package/src/engine-components/NeedleMenu.ts +3 -5
  71. package/src/engine-components/Networking.ts +4 -29
  72. package/src/engine-components/ReflectionProbe.ts +2 -21
  73. package/src/engine-components/export/usdz/extensions/behavior/BehaviourComponents.ts +32 -108
  74. package/src/engine-components/ui/Button.ts +0 -12
  75. package/src/engine-components/ui/Text.ts +0 -13
  76. package/lib/engine/engine_accessibility.d.ts +0 -58
  77. package/lib/engine/engine_accessibility.js +0 -143
  78. package/lib/engine/engine_accessibility.js.map +0 -1
  79. package/src/engine/engine_accessibility.ts +0 -178
@@ -13,11 +13,12 @@ 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 { makeNameSafeForUSD, USDDocument, USDObject, USDZExporterContext } from "../../ThreeUSDZExporter.js";
16
+
17
+ import { makeNameSafeForUSD,USDDocument, USDObject, USDZExporterContext } from "../../ThreeUSDZExporter.js";
17
18
  import { AnimationExtension, RegisteredAnimationInfo, type UsdzAnimation } from "../Animation.js";
18
19
  import { AudioExtension } from "./AudioExtension.js";
19
20
  import type { BehaviorExtension, UsdzBehaviour } from "./Behaviour.js";
20
- import { ActionBuilder, ActionModel, BehaviorModel, EmphasizeActionMotionType, GroupActionModel, type IBehaviorElement, Target, TriggerBuilder } from "./BehavioursBuilder.js";
21
+ import { ActionBuilder, ActionModel, BehaviorModel, EmphasizeActionMotionType,GroupActionModel,type IBehaviorElement, Target, TriggerBuilder } from "./BehavioursBuilder.js";
21
22
 
22
23
  const debug = getParam("debugusdzbehaviours");
23
24
 
@@ -56,22 +57,6 @@ export class ChangeTransformOnClick extends Behaviour implements IPointerClickHa
56
57
  private targetRot = new Quaternion();
57
58
  private targetScale = new Vector3();
58
59
 
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);
73
- }
74
-
75
60
  onPointerEnter() {
76
61
  this.context.input.setCursor("pointer");
77
62
  }
@@ -83,8 +68,8 @@ export class ChangeTransformOnClick extends Behaviour implements IPointerClickHa
83
68
 
84
69
  const rbs = this.object?.getComponentsInChildren(Rigidbody);
85
70
 
86
- if (rbs) {
87
- for (const rb of rbs) {
71
+ if (rbs){
72
+ for (const rb of rbs) {
88
73
  rb.resetVelocities();
89
74
  rb.resetForcesAndTorques();
90
75
  }
@@ -242,22 +227,6 @@ export class ChangeMaterialOnClick extends Behaviour implements IPointerClickHan
242
227
  }
243
228
  }
244
229
 
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
-
261
230
  onPointerEnter(_args: PointerEventData) {
262
231
  this.context.input.setCursor("pointer");
263
232
  }
@@ -458,7 +427,7 @@ export class SetActiveOnClick extends Behaviour implements IPointerClickHandler,
458
427
 
459
428
  if (!this.toggleOnClick && this.hideSelf)
460
429
  this.gameObject.visible = false;
461
-
430
+
462
431
  if (this.target)
463
432
  this.target.visible = this.toggleOnClick ? !this.target.visible : this.targetState;
464
433
  }
@@ -484,7 +453,7 @@ export class SetActiveOnClick extends Behaviour implements IPointerClickHandler,
484
453
 
485
454
  beforeCreateDocument() {
486
455
  if (!this.target) return;
487
-
456
+
488
457
  // need to cache on the object itself, because otherwise different actions would override each other's visibility state
489
458
  // TODO would probably be better to have this somewhere on the exporter, not on this component
490
459
  if (this.gameObject[SetActiveOnClick.wasVisible] === undefined)
@@ -545,7 +514,7 @@ export class SetActiveOnClick extends Behaviour implements IPointerClickHandler,
545
514
  // It's much easier to reason about nested actions when we're not duplicating tons of hierarchy...
546
515
  // We can probably only do a shallow clone when the tapped object has geometry of its own, otherwise
547
516
  // we end up with nothing to tap on.
548
-
517
+
549
518
  // Option A: we deep clone ourselves. This makes hierarchical cases and nested behaviours really complex.
550
519
  // We do this currently when the object doesn't have any geometry.
551
520
  if (!this.selfModelClone.geometry) {
@@ -564,11 +533,11 @@ export class SetActiveOnClick extends Behaviour implements IPointerClickHandler,
564
533
  clone.name += "_toggle" + (SetActiveOnClick.clonedToggleIndex++);
565
534
  originalModel.add(clone);
566
535
  this.gameObject[SetActiveOnClick.toggleClone] = clone;
567
-
536
+
568
537
  console.warn("USDZExport: Toggle " + this.gameObject.name + " doesn't have geometry. It will be deep cloned and nested behaviours will likely not work.");
569
538
  }
570
539
  const clonedSelfModel = this.gameObject[SetActiveOnClick.toggleClone];
571
-
540
+
572
541
  if (!this.gameObject[SetActiveOnClick.reverseToggleClone]) {
573
542
  const clone = this.selfModelClone.clone();
574
543
  clone.setMatrix(new Matrix4());
@@ -620,7 +589,7 @@ export class SetActiveOnClick extends Behaviour implements IPointerClickHandler,
620
589
  TriggerBuilder.tapTrigger(selfModel),
621
590
  ActionBuilder.parallel(...toggleSequence)
622
591
  ));
623
-
592
+
624
593
  const reverseSequence: ActionModel[] = [];
625
594
  reverseSequence.push(ActionBuilder.fadeAction(this.toggleModel, 0, false));
626
595
  reverseSequence.push(ActionBuilder.fadeAction(selfModel, 0, true));
@@ -711,7 +680,7 @@ export class HideOnStart extends Behaviour implements UsdzBehaviour {
711
680
  }
712
681
 
713
682
  private wasVisible: boolean = false;
714
-
683
+
715
684
  beforeCreateDocument() {
716
685
  this.wasVisible = GameObject.isActiveSelf(this.gameObject);
717
686
  }
@@ -744,22 +713,6 @@ export class EmphasizeOnClick extends Behaviour implements UsdzBehaviour {
744
713
  @serializable()
745
714
  motionType: EmphasizeActionMotionType = "bounce";
746
715
 
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
-
763
716
  beforeCreateDocument() { }
764
717
 
765
718
  createBehaviours(ext, model, _context) {
@@ -822,21 +775,6 @@ export class PlayAudioOnClick extends Behaviour implements IPointerClickHandler,
822
775
  }
823
776
  }
824
777
 
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
- }
840
778
 
841
779
  onPointerEnter() {
842
780
  this.context.input.setCursor("pointer");
@@ -878,7 +816,7 @@ export class PlayAudioOnClick extends Behaviour implements IPointerClickHandler,
878
816
  const clipName = AudioExtension.getName(clipUrl);
879
817
  const volume = this.target ? this.target.volume : 1;
880
818
  const auralMode = this.target && this.target.spatialBlend == 0 ? "nonSpatial" : "spatial";
881
-
819
+
882
820
  // This checks if any child is clickable – if yes, the tap trigger is added; if not, we omit it.
883
821
  let anyChildHasGeometry = false;
884
822
  this.gameObject.traverse(c => {
@@ -887,7 +825,7 @@ export class PlayAudioOnClick extends Behaviour implements IPointerClickHandler,
887
825
  // Workaround: seems iOS often simply doesn't play audio on scene start when this is NOT present.
888
826
  // unclear why, but having a useless tap action (nothing to tap on) "fixes" it.
889
827
  anyChildHasGeometry = true;
890
-
828
+
891
829
  const audioClip = ext.addAudioClip(clipUrl);
892
830
  // const stopAction: IBehaviorElement = ActionBuilder.playAudioAction(playbackTarget, audioClip, "stop", volume, auralMode);
893
831
  let playAction: IBehaviorElement = ActionBuilder.playAudioAction(playbackTarget, audioClip, "play", volume, auralMode);
@@ -896,7 +834,8 @@ export class PlayAudioOnClick extends Behaviour implements IPointerClickHandler,
896
834
 
897
835
  const behaviorName = (this.name ? "_" + this.name : "");
898
836
 
899
- if (anyChildHasGeometry && this.trigger === "tap") {
837
+ if (anyChildHasGeometry && this.trigger === "tap")
838
+ {
900
839
  // does not seem to work in iOS / QuickLook...
901
840
  // TODO use play "type" which can be start/stop/pause
902
841
  if (this.toggleOnClick) (playAction as ActionModel).multiplePerformOperation = "stop";
@@ -962,25 +901,9 @@ export class PlayAnimationOnClick extends Behaviour implements IPointerClickHand
962
901
 
963
902
  private get target() { return this.animator?.gameObject || this.animation?.gameObject }
964
903
 
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);
979
- }
980
904
 
981
905
  onPointerEnter() {
982
906
  this.context.input.setCursor("pointer");
983
- this.context.accessibility.hover(this, "Click to play animation " + (this.stateName || "") + " on " + (this.target ? this.target.name : ""));
984
907
  }
985
908
  onPointerExit() {
986
909
  this.context.input.unsetCursor("pointer");
@@ -989,7 +912,6 @@ export class PlayAnimationOnClick extends Behaviour implements IPointerClickHand
989
912
  args.use();
990
913
  if (!this.target) return;
991
914
  if (this.stateName) {
992
- this.context.accessibility.focus(this);
993
915
  // TODO this is currently quite annoying to use,
994
916
  // as for the web we use the Animator component and its states directly,
995
917
  // while in QuickLook we use explicit animations / states.
@@ -1001,8 +923,8 @@ export class PlayAnimationOnClick extends Behaviour implements IPointerClickHand
1001
923
 
1002
924
  private stateAnimationModel: any;
1003
925
 
1004
- private animationSequence?= new Array<RegisteredAnimationInfo>();
1005
- private animationLoopAfterSequence?= new Array<RegisteredAnimationInfo>();
926
+ private animationSequence? = new Array<RegisteredAnimationInfo>();
927
+ private animationLoopAfterSequence? = new Array<RegisteredAnimationInfo>();
1006
928
  private randomOffsetNormalized: number = 0;
1007
929
 
1008
930
  createBehaviours(_ext: BehaviorExtension, model: USDObject, _context: USDZExporterContext) {
@@ -1028,9 +950,9 @@ export class PlayAnimationOnClick extends Behaviour implements IPointerClickHand
1028
950
  afterCreateDocument(ext: BehaviorExtension, context: USDZExporterContext) {
1029
951
  if ((this.animationSequence === undefined && this.animationLoopAfterSequence === undefined) || !this.stateAnimationModel) return;
1030
952
  if (!this.target) return;
1031
-
953
+
1032
954
  const document = context.document;
1033
-
955
+
1034
956
  // check if the AnimationExtension has been attached and what data it has for the current object
1035
957
  const animationExt = context.extensions.find(ext => ext instanceof AnimationExtension) as AnimationExtension;
1036
958
  if (!animationExt) return;
@@ -1044,7 +966,7 @@ export class PlayAnimationOnClick extends Behaviour implements IPointerClickHand
1044
966
  if (requiresExclusivePlayback) {
1045
967
  if (isDevEnvironment())
1046
968
  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.");
1047
-
969
+
1048
970
  PlayAnimationOnClick.rootsWithExclusivePlayback.add(this.target);
1049
971
  }
1050
972
 
@@ -1064,7 +986,7 @@ export class PlayAnimationOnClick extends Behaviour implements IPointerClickHand
1064
986
  this.trigger == "tap" ? TriggerBuilder.tapTrigger(this.selfModel) : TriggerBuilder.sceneStartTrigger(),
1065
987
  sequence
1066
988
  );
1067
-
989
+
1068
990
  // See comment above for why exclusive playback is currently required when playing multiple animations on the same root.
1069
991
  if (requiresExclusivePlayback)
1070
992
  playAnimationOnTap.makeExclusive(true);
@@ -1086,7 +1008,8 @@ export class PlayAnimationOnClick extends Behaviour implements IPointerClickHand
1086
1008
 
1087
1009
  const sequence = ActionBuilder.sequence();
1088
1010
 
1089
- if (animationSequence && animationSequence.length > 0) {
1011
+ if (animationSequence && animationSequence.length > 0)
1012
+ {
1090
1013
  for (const anim of animationSequence) {
1091
1014
  sequence.addAction(getOrCacheAction(model, anim));
1092
1015
  }
@@ -1110,10 +1033,11 @@ export class PlayAnimationOnClick extends Behaviour implements IPointerClickHand
1110
1033
  return sequence;
1111
1034
  }
1112
1035
 
1113
- static getAndRegisterAnimationSequences(ext: AnimationExtension, target: GameObject, stateName?: string): {
1114
- animationSequence: Array<RegisteredAnimationInfo>,
1036
+ static getAndRegisterAnimationSequences(ext: AnimationExtension, target: GameObject, stateName?: string):
1037
+ {
1038
+ animationSequence: Array<RegisteredAnimationInfo>,
1115
1039
  animationLoopAfterSequence: Array<RegisteredAnimationInfo>,
1116
- randomTimeOffset: number,
1040
+ randomTimeOffset: number,
1117
1041
  } | undefined {
1118
1042
 
1119
1043
  if (!target) return undefined;
@@ -1131,14 +1055,14 @@ export class PlayAnimationOnClick extends Behaviour implements IPointerClickHand
1131
1055
  let animationLoopAfterSequence: Array<RegisteredAnimationInfo> = [];
1132
1056
 
1133
1057
  if (animation) {
1134
- const anim = ext.registerAnimation(target, animation.clip);
1135
- if (anim) {
1058
+ const anim = ext.registerAnimation(target, animation.clip);
1059
+ if (anim) {
1136
1060
  if (animation.loop)
1137
1061
  animationLoopAfterSequence.push(anim);
1138
1062
  else
1139
1063
  animationSequence.push(anim);
1140
1064
  }
1141
-
1065
+
1142
1066
  let randomTimeOffset = 0;
1143
1067
  if (animation.minMaxOffsetNormalized) {
1144
1068
  const from = animation.minMaxOffsetNormalized.x;
@@ -1232,7 +1156,7 @@ export class PlayAnimationOnClick extends Behaviour implements IPointerClickHand
1232
1156
  if (lastClip) {
1233
1157
  let clipCopy: AnimationClip | undefined;
1234
1158
  if (ext.holdClipMap.has(lastClip)) {
1235
- clipCopy = ext.holdClipMap.get(lastClip);
1159
+ clipCopy = ext.holdClipMap.get(lastClip);
1236
1160
  }
1237
1161
  else {
1238
1162
  // We're creating a "hold" clip here; exactly 1 second long, and inteprolates exactly on the duration of the clip
@@ -180,8 +180,6 @@ export class Button extends Behaviour implements IPointerEventHandler {
180
180
  this.onClick.invoke();
181
181
  args.use();
182
182
 
183
- this.context.accessibility.focus(this);
184
-
185
183
  // debug clicks for WebXR
186
184
  if (debug) {
187
185
  const pos = this.gameObject.worldPosition;
@@ -240,19 +238,9 @@ export class Button extends Behaviour implements IPointerEventHandler {
240
238
 
241
239
  onEnable() {
242
240
  super.onEnable();
243
- this.context.accessibility.updateElement(this, {
244
- role: "button",
245
- label: this.gameObject.name + " button",
246
- hidden: false
247
- });
248
- }
249
- onDisable() {
250
- super.onDisable();
251
- this.context.accessibility.updateElement(this, { hidden: true })
252
241
  }
253
242
 
254
243
  onDestroy(): void {
255
- this.context.accessibility.removeElement(this);
256
244
  if (this._isHovered) this.context.input.unsetCursor("pointer");
257
245
  }
258
246
 
@@ -102,7 +102,6 @@ export class Text extends Graphic implements IHasAlphaFactor, ICanvasEventReceiv
102
102
  this._text = val;
103
103
  this.feedText(this.text, this.supportRichText);
104
104
  this.markDirty();
105
- this.context.accessibility.updateElement(this, { label: this.text });
106
105
  }
107
106
  }
108
107
 
@@ -239,13 +238,6 @@ export class Text extends Graphic implements IHasAlphaFactor, ICanvasEventReceiv
239
238
 
240
239
  onEnable(): void {
241
240
  super.onEnable();
242
-
243
- this.context.accessibility.updateElement(this, {
244
- role: "text",
245
- label: this.text,
246
- hidden: false
247
- });
248
-
249
241
  this._didHandleTextRenderOnTop = false;
250
242
  if (this.uiObject) {
251
243
  // @ts-ignore
@@ -269,11 +261,6 @@ export class Text extends Graphic implements IHasAlphaFactor, ICanvasEventReceiv
269
261
  onDisable(): void {
270
262
  super.onDisable();
271
263
  this.canvas?.unregisterEventReceiver(this);
272
- this.context.accessibility.updateElement(this, { hidden: true });
273
- }
274
- onDestroy(): void {
275
- super.onDestroy();
276
- this.context.accessibility.removeElement(this);
277
264
  }
278
265
 
279
266
  private getAlignment(opts: ThreeMeshUIEveryOptions): ThreeMeshUIEveryOptions {
@@ -1,58 +0,0 @@
1
- import { Object3D } from "three";
2
- import type { Context } from "./engine_setup";
3
- import { IComponent } from "./engine_types";
4
- /** Data describing the accessible semantics for a 3D object or component. */
5
- type AccessibilityData = {
6
- /** ARIA role (e.g. `"button"`, `"img"`, `"region"`). */
7
- role: string;
8
- /** Human-readable label announced by screen readers. */
9
- label: string;
10
- /** When `true`, the element is hidden from the accessibility tree. */
11
- hidden?: boolean;
12
- /** When `true`, indicates the element's content is being updated. */
13
- busy?: boolean;
14
- };
15
- /**
16
- * Manages an accessible, screen-reader-friendly overlay for a Needle Engine {@link Context}.
17
- *
18
- * The manager maintains a visually-hidden DOM tree that mirrors relevant 3D scene objects
19
- * with appropriate ARIA roles and labels. It also provides a live region so that hover
20
- * events in the 3D scene can be announced to assistive technology without stealing focus.
21
- */
22
- export declare class AccessibilityManager {
23
- private readonly context;
24
- private static readonly _managers;
25
- /** Returns the {@link AccessibilityManager} associated with the given context or component. */
26
- static get(obj: Context | IComponent): AccessibilityManager | undefined;
27
- constructor(context: Context);
28
- private _enabled;
29
- /** Enables or disables the accessibility overlay. When disabled, the overlay DOM is removed. */
30
- set enabled(value: boolean);
31
- /** Removes all tracked accessibility elements, keeping only the live region. */
32
- clear(): void;
33
- /** Removes the overlay from the DOM and unregisters this manager from the context. */
34
- dispose(): void;
35
- private readonly root;
36
- private readonly liveRegion;
37
- private readonly treeElements;
38
- /**
39
- * Creates or updates the accessible DOM element for a 3D object or component.
40
- * @param obj - The scene object or component to represent.
41
- * @param data - Partial accessibility data (role, label, hidden, busy) to apply.
42
- */
43
- updateElement<T extends Object3D | IComponent>(obj: T, data: Partial<AccessibilityData>): void;
44
- /** Moves keyboard focus to the accessible element representing the given object. */
45
- focus<T extends Object3D | IComponent>(obj: T): void;
46
- /** Removes keyboard focus from the accessible element representing the given object. */
47
- unfocus<T extends Object3D | IComponent>(obj: T): void;
48
- /**
49
- * Announces a hover event to screen readers via the ARIA live region.
50
- * @param obj - The hovered object (used to look up its label if `text` is not provided).
51
- * @param text - Optional text to announce. Falls back to the element's `aria-label`.
52
- */
53
- hover<T extends Object3D | IComponent>(obj: T, text?: string): void;
54
- /** Removes the accessible DOM element for the given object and stops tracking it. */
55
- removeElement(obj: Object3D | IComponent): void;
56
- private set liveRegionMode(value);
57
- }
58
- export {};
@@ -1,143 +0,0 @@
1
- import { isComponent } from "./engine_types";
2
- /**
3
- * Manages an accessible, screen-reader-friendly overlay for a Needle Engine {@link Context}.
4
- *
5
- * The manager maintains a visually-hidden DOM tree that mirrors relevant 3D scene objects
6
- * with appropriate ARIA roles and labels. It also provides a live region so that hover
7
- * events in the 3D scene can be announced to assistive technology without stealing focus.
8
- */
9
- export class AccessibilityManager {
10
- context;
11
- static _managers = new WeakMap();
12
- /** Returns the {@link AccessibilityManager} associated with the given context or component. */
13
- static get(obj) {
14
- if (isComponent(obj)) {
15
- return this._managers.get(obj.context);
16
- }
17
- else {
18
- return this._managers.get(obj);
19
- }
20
- }
21
- constructor(context) {
22
- this.context = context;
23
- this.root.style.cssText = `
24
- position: absolute;
25
- width: 1px; height: 1px;
26
- padding: 0; margin: -1px;
27
- overflow: hidden;
28
- clip: rect(0, 0, 0, 0);
29
- white-space: nowrap;
30
- border: 0;
31
- `;
32
- this.root.setAttribute("role", "region");
33
- this.root.setAttribute("aria-label", "3D Needle Engine scene");
34
- // Live region for announcing hovered 3D elements without stealing focus
35
- this.liveRegion.setAttribute("aria-live", "polite");
36
- this.liveRegion.setAttribute("aria-atomic", "true");
37
- this.liveRegion.setAttribute("role", "status");
38
- this.root.appendChild(this.liveRegion);
39
- this.enabled = true;
40
- }
41
- _enabled;
42
- /** Enables or disables the accessibility overlay. When disabled, the overlay DOM is removed. */
43
- set enabled(value) {
44
- if (value === this._enabled)
45
- return;
46
- this._enabled = value;
47
- if (!value) {
48
- this.root.remove();
49
- }
50
- else {
51
- AccessibilityManager._managers.set(this.context, this);
52
- const target = this.context.domElement.shadowRoot || this.context.domElement;
53
- target.prepend(this.root);
54
- }
55
- }
56
- /** Removes all tracked accessibility elements, keeping only the live region. */
57
- clear() {
58
- this.root.childNodes.forEach(child => child.remove());
59
- this.root.appendChild(this.liveRegion);
60
- }
61
- /** Removes the overlay from the DOM and unregisters this manager from the context. */
62
- dispose() {
63
- this.root.remove();
64
- AccessibilityManager._managers.delete(this.context);
65
- }
66
- root = document.createElement("div");
67
- liveRegion = document.createElement("div");
68
- treeElements = new WeakMap();
69
- /**
70
- * Creates or updates the accessible DOM element for a 3D object or component.
71
- * @param obj - The scene object or component to represent.
72
- * @param data - Partial accessibility data (role, label, hidden, busy) to apply.
73
- */
74
- updateElement(obj, data) {
75
- let el = this.treeElements.get(obj);
76
- if (!el) {
77
- el = document.createElement("div");
78
- this.treeElements.set(obj, el);
79
- this.root.appendChild(el);
80
- let didSetRole = false;
81
- if (typeof data === "object") {
82
- if (data.role) {
83
- didSetRole = true;
84
- el.setAttribute("role", data.role);
85
- }
86
- if (data.label) {
87
- el.setAttribute("aria-label", data.label);
88
- }
89
- if (data.hidden !== undefined) {
90
- el.setAttribute("aria-hidden", String(data.hidden));
91
- }
92
- if (data.busy !== undefined) {
93
- el.setAttribute("aria-busy", String(data.busy));
94
- }
95
- }
96
- // if (didSetRole) {
97
- // const role = el.getAttribute("role");
98
- // if (role) {
99
- // el.setAttribute("tabindex", "0");
100
- // } else {
101
- // el.removeAttribute("tabindex");
102
- // }
103
- // }
104
- }
105
- }
106
- /** Moves keyboard focus to the accessible element representing the given object. */
107
- focus(obj) {
108
- const el = this.treeElements.get(obj);
109
- if (el) {
110
- // if (!el.hasAttribute("tabindex")) {
111
- // el.setAttribute("tabindex", "0");
112
- // }
113
- el.focus();
114
- }
115
- }
116
- /** Removes keyboard focus from the accessible element representing the given object. */
117
- unfocus(obj) {
118
- const el = this.treeElements.get(obj);
119
- if (el) {
120
- el.blur();
121
- }
122
- }
123
- /**
124
- * Announces a hover event to screen readers via the ARIA live region.
125
- * @param obj - The hovered object (used to look up its label if `text` is not provided).
126
- * @param text - Optional text to announce. Falls back to the element's `aria-label`.
127
- */
128
- hover(obj, text) {
129
- const el = this.treeElements.get(obj);
130
- // Update the live region text — screen reader announces this without stealing focus
131
- this.liveRegion.textContent = text || el?.getAttribute("aria-label") || "";
132
- }
133
- /** Removes the accessible DOM element for the given object and stops tracking it. */
134
- removeElement(obj) {
135
- const el = this.treeElements.get(obj);
136
- el?.remove();
137
- this.treeElements.delete(obj);
138
- }
139
- set liveRegionMode(mode) {
140
- this.liveRegion.setAttribute("aria-live", mode);
141
- }
142
- }
143
- //# sourceMappingURL=engine_accessibility.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"engine_accessibility.js","sourceRoot":"","sources":["../../src/engine/engine_accessibility.ts"],"names":[],"mappings":"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"}