@needle-tools/engine 4.12.4-next.46bee95 → 4.12.5-next.1be4457

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 (32) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/README.md +2 -1
  3. package/dist/{needle-engine.bundle-CvasmiEO.min.js → needle-engine.bundle-BNb-Ipns.min.js} +17 -16
  4. package/dist/{needle-engine.bundle-CojFvJHR.umd.cjs → needle-engine.bundle-Cf9xdXZc.umd.cjs} +45 -44
  5. package/dist/{needle-engine.bundle-DGjkYJDl.js → needle-engine.bundle-DMiFIvRW.js} +540 -519
  6. package/dist/needle-engine.js +2 -2
  7. package/dist/needle-engine.min.js +1 -1
  8. package/dist/needle-engine.umd.cjs +1 -1
  9. package/lib/engine/debug/debug_overlay.js +1 -1
  10. package/lib/engine/debug/debug_overlay.js.map +1 -1
  11. package/lib/engine/engine_animation.js.map +1 -1
  12. package/lib/engine/webcomponents/WebXRButtons.js +1 -1
  13. package/lib/engine/webcomponents/WebXRButtons.js.map +1 -1
  14. package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +5 -1
  15. package/lib/engine/webcomponents/needle menu/needle-menu.js +84 -50
  16. package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
  17. package/lib/engine/webcomponents/needle-engine.ar-overlay.js +1 -0
  18. package/lib/engine/webcomponents/needle-engine.ar-overlay.js.map +1 -1
  19. package/lib/engine-components/Animation.js.map +1 -1
  20. package/lib/engine-components/utils/OpenURL.js +1 -3
  21. package/lib/engine-components/utils/OpenURL.js.map +1 -1
  22. package/lib/engine-components/webxr/WebXR.js +6 -8
  23. package/lib/engine-components/webxr/WebXR.js.map +1 -1
  24. package/package.json +1 -1
  25. package/src/engine/debug/debug_overlay.ts +1 -1
  26. package/src/engine/engine_animation.ts +1 -1
  27. package/src/engine/webcomponents/WebXRButtons.ts +1 -1
  28. package/src/engine/webcomponents/needle menu/needle-menu.ts +88 -50
  29. package/src/engine/webcomponents/needle-engine.ar-overlay.ts +1 -0
  30. package/src/engine-components/Animation.ts +1 -1
  31. package/src/engine-components/utils/OpenURL.ts +0 -2
  32. package/src/engine-components/webxr/WebXR.ts +6 -9
@@ -112,7 +112,7 @@ export function addLog(type: LogType, message: string | any[], _file?: string |
112
112
  const context = ContextRegistry.Current;
113
113
  let domElement = context?.domElement ?? document.querySelector("needle-engine");
114
114
  // check if we're in webxr dom overlay
115
- if (context.isInAR) {
115
+ if (context?.isInAR) {
116
116
  domElement = context.arOverlayElement;
117
117
  }
118
118
  if (!domElement) return;
@@ -1,6 +1,6 @@
1
1
  import { AnimationAction, AnimationClip, AnimationMixer, KeyframeTrack, Object3D, PropertyBinding, Vector3Like } from "three";
2
- import { isDevEnvironment } from "./debug/index.js";
3
2
 
3
+ import { isDevEnvironment } from "./debug/index.js";
4
4
  import type { Context } from "./engine_context.js";
5
5
  import { GLTF, IAnimationComponent, Model } from "./engine_types.js";
6
6
  import { TypeStore } from "./engine_typestore.js";
@@ -240,7 +240,7 @@ export class WebXRButtonFactory {
240
240
  private hideElementDuringXRSession(element: HTMLElement) {
241
241
  onXRSessionStart(_ => {
242
242
  element["previous-display"] = element.style.display;
243
- element.style.display = "none";
243
+ element.style.setProperty("display", "none", "important");
244
244
  });
245
245
  onXRSessionEnd(_ => {
246
246
  if (element["previous-display"] != undefined)
@@ -269,7 +269,6 @@ export class NeedleMenu {
269
269
  return;
270
270
  }
271
271
  this._muteButton = ButtonsFactory.getOrCreate().createMuteButton(this._context);
272
- this._muteButton.setAttribute("priority", "100");
273
272
  this._menu.appendChild(this._muteButton);
274
273
  }
275
274
  private _muteButton?: HTMLButtonElement;
@@ -282,7 +281,6 @@ export class NeedleMenu {
282
281
  }
283
282
  this._fullscreenButton = ButtonsFactory.getOrCreate().createFullscreenButton(this._context);
284
283
  if (this._fullscreenButton) {
285
- this._fullscreenButton.setAttribute("priority", "150");
286
284
  this._menu.appendChild(this._fullscreenButton);
287
285
  }
288
286
  }
@@ -527,9 +525,9 @@ export class NeedleMenuElement extends HTMLElement {
527
525
 
528
526
  .logo {
529
527
  cursor: pointer;
530
- padding-left: 0.0rem;
531
- padding-bottom: .02rem;
532
- margin-right: 0.5rem;
528
+ padding-left: 0.5em;
529
+ padding-bottom: .02em;
530
+ margin-right: 0.6em;
533
531
  }
534
532
  .logo-hidden {
535
533
  .logo {
@@ -538,8 +536,8 @@ export class NeedleMenuElement extends HTMLElement {
538
536
  }
539
537
  :host .has-options .logo {
540
538
  border-left: 1px solid rgba(40,40,40,.4);
541
- margin-left: 0.3rem;
542
- margin-right: 0.5rem;
539
+ margin-left: 0.3em;
540
+ margin-right: 0.6em;
543
541
  }
544
542
 
545
543
  .logo > span {
@@ -671,22 +669,23 @@ export class NeedleMenuElement extends HTMLElement {
671
669
  }
672
670
 
673
671
  .compact .options > *, .compact .options > ::slotted(*) {
674
- font-size: 1.2rem;
675
- padding: .6rem .5rem;
672
+ font-size: 1.2em;
673
+ padding: .6em .5em;
676
674
  width: 100%;
677
675
  }
678
676
  .compact.has-options {
679
- padding-left: 1rem;
677
+ padding-left: 1em;
680
678
  }
681
679
  .compact.has-options .logo {
682
680
  border: none;
683
681
  padding-left: 0;
684
- margin-bottom: .02rem;
682
+ margin-bottom: .02em;
685
683
  }
686
684
  .compact .options.compact-only {
687
685
  display: initial;
688
686
  & > * {
689
687
  min-height: 1em;
688
+ padding: .4em .4em;
690
689
  }
691
690
  }
692
691
  .compact .options {
@@ -870,7 +869,7 @@ export class NeedleMenuElement extends HTMLElement {
870
869
  connectedCallback() {
871
870
  window.addEventListener("resize", this.handleSizeChange);
872
871
  this.handleMenuVisible();
873
- this._sizeChangeInterval = setInterval(() => this.handleSizeChange(undefined, true), 5000);
872
+ this._sizeChangeInterval = setInterval(() => this.handleSizeChange(undefined, false), 5000);
874
873
  // the dom element is set after the constructor runs
875
874
  setTimeout(() => {
876
875
  this._domElement?.addEventListener("resize", this.handleSizeChange);
@@ -1073,6 +1072,8 @@ export class NeedleMenuElement extends HTMLElement {
1073
1072
  }
1074
1073
 
1075
1074
  private _isHandlingChange = false;
1075
+ /** During modification of options container (e.g. when moving items into the extra buttons container) the mutation observer should not trigger an update event immediately. This is a workaround for the total size required for all elements not being calculated reliably. */
1076
+ private _pauseMutationObserverOptionsContainer = false;
1076
1077
 
1077
1078
  /** Called when any change in the web component is detected (including in children and child attributes) */
1078
1079
  private onChangeDetected(_mut: MutationRecord[]) {
@@ -1083,7 +1084,8 @@ export class NeedleMenuElement extends HTMLElement {
1083
1084
  this.handleMenuVisible();
1084
1085
  for (const mut of _mut) {
1085
1086
  if (mut.target == this.options) {
1086
- this.onOptionsChildrenChanged(mut);
1087
+ if (!this._pauseMutationObserverOptionsContainer)
1088
+ this.onOptionsChildrenChanged(mut)
1087
1089
  }
1088
1090
  }
1089
1091
  }
@@ -1168,29 +1170,34 @@ export class NeedleMenuElement extends HTMLElement {
1168
1170
 
1169
1171
 
1170
1172
  private _lastAvailableWidthChange = 0;
1171
- private _timeoutHandle: number = 0;
1173
+ private _timeoutHandleSize: number = 0;
1174
+ private _timeoutHandleCompactItems: number = 0;
1172
1175
 
1173
1176
  private handleSizeChange = (_evt?: Event, forceOrEvent?: boolean) => {
1174
1177
  if (!this._domElement) return;
1178
+ // if (this._isApplyingSizeUpdate) return;
1175
1179
 
1176
1180
  const width = this._domElement.clientWidth;
1177
1181
  if (width < 100) {
1178
- clearTimeout(this._timeoutHandle!);
1182
+ clearTimeout(this._timeoutHandleSize!);
1179
1183
  this.root.classList.add("compact");
1180
1184
  this.foldout.classList.add("floating-panel-style");
1181
1185
  return;
1182
1186
  }
1183
1187
 
1184
- const padding = 20 * 2;
1188
+ const padding = 10 * 2;
1185
1189
  const availableWidth = width - padding;
1186
1190
 
1187
1191
  // if the available width has not changed significantly then we can skip the rest
1188
1192
  if (!forceOrEvent && Math.abs(availableWidth - this._lastAvailableWidthChange) < 1) return;
1189
1193
  this._lastAvailableWidthChange = availableWidth;
1190
1194
 
1191
- clearTimeout(this._timeoutHandle!);
1195
+ clearTimeout(this._timeoutHandleSize!);
1196
+
1197
+ this._timeoutHandleSize = setTimeout(() => {
1198
+
1199
+ // console.warn("APPLY", this.root.classList.contains("compact") ? "COMPACT" : "FULL", "MODE (available width: " + availableWidth.toFixed(0) + "px)", getCurrentWidth());
1192
1200
 
1193
- this._timeoutHandle = setTimeout(() => {
1194
1201
  const spaceLeft = getSpaceLeft();
1195
1202
  if (spaceLeft < 0) {
1196
1203
  this.root.classList.add("compact")
@@ -1205,41 +1212,19 @@ export class NeedleMenuElement extends HTMLElement {
1205
1212
  this.foldout.classList.add("floating-panel-style");
1206
1213
  }
1207
1214
  }
1215
+ this._pauseMutationObserverOptionsContainer = true;
1216
+ this.updateCompactFoldoutItem();
1217
+ window.requestAnimationFrame(() => this._pauseMutationObserverOptionsContainer = false);
1208
1218
 
1209
- if (this.root.classList.contains("compact")) {
1210
- this.optionsCompactMode.childNodes.forEach(element => {
1211
- element.remove();
1212
- });
1213
- // Find items in the folding list with the highest priority
1214
- // The one with the highest priority will be added to the visible container
1215
- let priorityItem: HTMLElement | null = null;
1216
- let priorityValue: number = -10000000;
1217
- for (let i = 0; i < this.options.children.length; i++) {
1218
- const element = this.options.children.item(i);
1219
- if (element instanceof HTMLElement) {
1220
- const priority = NeedleMenu.getElementPriority(element);
1221
- if (priority !== undefined && priority > priorityValue) {
1222
- // check if the element is hidden
1223
- // @TODO: use computed styles
1224
- const style = element.style;
1225
- if (style.display === "none") continue;
1226
- priorityItem = element;
1227
- priorityValue = priority;
1228
- }
1229
- }
1230
- }
1231
- if (priorityItem) {
1232
- const item = priorityItem;
1233
- const clone = item.cloneNode(true);
1234
- clone.addEventListener("click", () => item.click())
1235
- this.optionsCompactMode.appendChild(clone);
1236
- }
1237
- }
1238
-
1239
- }, 5) as unknown as number;
1219
+ }, 150) as unknown as number;
1240
1220
 
1241
1221
  const getCurrentWidth = () => {
1242
- return this.options.clientWidth + this.logoContainer.clientWidth;
1222
+ let totalWidthRequired = 0;
1223
+ totalWidthRequired += this.options.getBoundingClientRect().width;
1224
+ totalWidthRequired += this.optionsCompactMode.getBoundingClientRect().width;
1225
+ totalWidthRequired += 10 * this.options.childElementCount; // padding
1226
+ totalWidthRequired += this.logoContainer.style.display != "none" ? this.logoContainer.getBoundingClientRect().width : 0;;
1227
+ return totalWidthRequired;
1243
1228
  }
1244
1229
 
1245
1230
  let lastSpaceLeft = -1;
@@ -1253,6 +1238,59 @@ export class NeedleMenuElement extends HTMLElement {
1253
1238
  }
1254
1239
  }
1255
1240
 
1241
+ private updateCompactFoldoutItem() {
1242
+
1243
+ if (this.root.classList.contains("compact")) {
1244
+
1245
+ // Find items in the folding list with the highest priority
1246
+ // The one with the highest priority will be added to the visible container
1247
+ let priorityItem: HTMLElement | null = null;
1248
+ let priorityValue: number = -10000000;
1249
+ const testItem = (element: ChildNode | null) => {
1250
+ if (element instanceof HTMLElement) {
1251
+ const priority = NeedleMenu.getElementPriority(element);
1252
+ if (priority !== undefined && priority >= priorityValue) {
1253
+ // check if the element is hidden
1254
+ // @TODO: use computed styles
1255
+ const style = window.getComputedStyle(element);
1256
+ if (style.display === "none" || style.visibility === "hidden" || style.opacity === "0") {
1257
+ return;
1258
+ }
1259
+ priorityItem = element;
1260
+ priorityValue = priority;
1261
+ }
1262
+ }
1263
+ }
1264
+ for (let i = 0; i < this.options.children.length; i++) {
1265
+ testItem(this.options.children.item(i));
1266
+ }
1267
+ for (let i = 0; i < this.optionsCompactMode.children.length; i++) {
1268
+ testItem(this.optionsCompactMode.children.item(i));
1269
+ }
1270
+
1271
+ if (priorityItem && !this.optionsCompactMode.contains(priorityItem)) {
1272
+ this.optionsCompactMode.childNodes.forEach(element => {
1273
+ this.options.appendChild(element);
1274
+ });
1275
+ const item = priorityItem;
1276
+ this.optionsCompactMode.appendChild(item);
1277
+ // console.warn("In compact mode, moved item with priority " + priorityValue + " to compact foldout:", item);
1278
+ }
1279
+ else if (!priorityItem) {
1280
+ // console.warn("In compact mode but no item has priority, showing all items in foldout");
1281
+ this.optionsCompactMode.childNodes.forEach(element => {
1282
+ this.options.appendChild(element);
1283
+ });
1284
+ }
1285
+ }
1286
+ else {
1287
+ // console.warn("Not in compact mode but trying to update compact foldout item");
1288
+ this.optionsCompactMode.childNodes.forEach(element => {
1289
+ this.options.appendChild(element);
1290
+ });
1291
+ }
1292
+ }
1293
+ // private _foldoutItemVisibleInterval = 0;
1256
1294
 
1257
1295
 
1258
1296
  private ___insertDebugOptions() {
@@ -121,6 +121,7 @@ export class AROverlayHandler {
121
121
 
122
122
  const quitARSlot = document.createElement("slot");
123
123
  quitARSlot.style.display = "contents";
124
+ quitARSlot.style.padding = "10px";
124
125
  quitARSlot.setAttribute("name", "quit-ar");
125
126
  this.appendElement(quitARSlot, element);
126
127
  this._createdAROnlyElements.push(quitARSlot);
@@ -1,6 +1,6 @@
1
1
  import { AnimationAction, AnimationClip, AnimationMixer, LoopOnce, LoopRepeat } from "three";
2
- import { AnimationUtils } from "../engine/engine_animation.js";
3
2
 
3
+ import { AnimationUtils } from "../engine/engine_animation.js";
4
4
  import { Mathf } from "../engine/engine_math.js";
5
5
  import { serializable } from "../engine/engine_serialization_decorator.js";
6
6
  import { IAnimationComponent } from "../engine/engine_types.js";
@@ -57,8 +57,6 @@ export class OpenURL extends Behaviour implements IPointerClickHandler {
57
57
  url = "mailto:" + url;
58
58
  }
59
59
 
60
- if (isDevEnvironment()) showBalloonMessage("Open URL: " + url)
61
-
62
60
  switch (this.mode) {
63
61
  case OpenURLMode.NewTab:
64
62
  if (DeviceUtilities.isSafari()) {
@@ -551,8 +551,6 @@ export class WebXR extends Behaviour {
551
551
  * and device capabilities. Handles creating AR, VR, QuickLook buttons and utility buttons like QR codes.
552
552
  */
553
553
  private handleCreatingHTML() {
554
- const xrButtonsPriority = 50;
555
-
556
554
  if (this.createARButton || this.createVRButton || this.useQuicklookExport) {
557
555
  // Quicklook / iOS
558
556
  if ((DeviceUtilities.isiOS() && DeviceUtilities.isSafari()) || debugQuicklook) {
@@ -560,18 +558,18 @@ export class WebXR extends Behaviour {
560
558
  const usdzExporter = GameObject.findObjectOfType(USDZExporter);
561
559
  if (!usdzExporter || (usdzExporter && usdzExporter.allowCreateQuicklookButton)) {
562
560
  const button = this.getButtonsFactory().createQuicklookButton();
563
- this.addButton(button, xrButtonsPriority);
561
+ this.addButton(button);
564
562
  }
565
563
  }
566
564
  }
567
565
  // WebXR
568
566
  if (this.createARButton) {
569
567
  const arbutton = this.getButtonsFactory().createARButton();
570
- this.addButton(arbutton, xrButtonsPriority);
568
+ this.addButton(arbutton);
571
569
  }
572
570
  if (this.createVRButton) {
573
571
  const vrbutton = this.getButtonsFactory().createVRButton();
574
- this.addButton(vrbutton, xrButtonsPriority);
572
+ this.addButton(vrbutton);
575
573
  }
576
574
  }
577
575
 
@@ -579,7 +577,7 @@ export class WebXR extends Behaviour {
579
577
  NeedleXRSession.isVRSupported().then(supported => {
580
578
  if (!supported) {
581
579
  const button = this.getButtonsFactory().createSendToQuestButton();
582
- this.addButton(button, xrButtonsPriority);
580
+ this.addButton(button);
583
581
  }
584
582
  });
585
583
  }
@@ -592,7 +590,7 @@ export class WebXR extends Behaviour {
592
590
  }
593
591
  else if (!DeviceUtilities.isMobileDevice()) {
594
592
  const qrCode = ButtonsFactory.getOrCreate().createQRCode();
595
- this.addButton(qrCode, xrButtonsPriority);
593
+ this.addButton(qrCode,);
596
594
  }
597
595
  }
598
596
  }
@@ -607,9 +605,8 @@ export class WebXR extends Behaviour {
607
605
  * @param button The HTML element to add
608
606
  * @param priority The button's priority value (lower numbers appear first)
609
607
  */
610
- private addButton(button: HTMLElement, priority: number) {
608
+ private addButton(button: HTMLElement) {
611
609
  this._buttons.push(button);
612
- button.setAttribute("priority", priority.toString());
613
610
  this.context.menu.appendChild(button);
614
611
  }
615
612