@needle-tools/engine 4.12.3-next.a27c12e → 4.12.4-next.4498846

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 (80) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/components.needle.json +1 -1
  3. package/dist/{gltf-progressive-Bfpfaz84.umd.cjs → gltf-progressive-BqUnxvCx.umd.cjs} +1 -1
  4. package/dist/{gltf-progressive-hFPACYio.min.js → gltf-progressive-CSaX5HQb.min.js} +2 -2
  5. package/dist/{gltf-progressive-DPunMlEM.js → gltf-progressive-ChnIhDXx.js} +27 -27
  6. package/dist/{loader.worker-DWzfDpAl.js → loader.worker-C1GG9A7C.js} +6 -6
  7. package/dist/{needle-engine.bundle-URbCchz5.js → needle-engine.bundle-C9mGVluu.js} +7444 -7358
  8. package/dist/{needle-engine.bundle-CLfNhYon.umd.cjs → needle-engine.bundle-CnmG19ga.umd.cjs} +300 -293
  9. package/dist/{needle-engine.bundle-BHCgy9h1.min.js → needle-engine.bundle-ZJOekt7-.min.js} +297 -290
  10. package/dist/needle-engine.d.ts +38 -13
  11. package/dist/needle-engine.js +4 -4
  12. package/dist/needle-engine.min.js +1 -1
  13. package/dist/needle-engine.umd.cjs +1 -1
  14. package/dist/{postprocessing-ClLv0reO.min.js → postprocessing-12-UW7je.min.js} +1 -1
  15. package/dist/{postprocessing-BHQvwehB.umd.cjs → postprocessing-B3Hu0Ryi.umd.cjs} +1 -1
  16. package/dist/{postprocessing-DLI2N3LL.js → postprocessing-R535krvT.js} +2 -2
  17. package/dist/{three-Bf2NBxAw.umd.cjs → three-BzxwLtUE.umd.cjs} +176 -176
  18. package/dist/{three-BCCkyCA5.js → three-D9pcFbxc.js} +4637 -4636
  19. package/dist/{three-W7zWTcfP.min.js → three-DMvLgxja.min.js} +176 -176
  20. package/dist/{three-examples-DB5Uoja4.min.js → three-examples-CIv2roOA.min.js} +1 -1
  21. package/dist/{three-examples-Djbk6WA4.umd.cjs → three-examples-CjSwCv_b.umd.cjs} +1 -1
  22. package/dist/{three-examples-D4rE49Ui.js → three-examples-F0MJj0vr.js} +1 -1
  23. package/dist/{three-mesh-ui-zsOOA5Pq.umd.cjs → three-mesh-ui-BLnJQzMl.umd.cjs} +1 -1
  24. package/dist/{three-mesh-ui-CIez6qJQ.min.js → three-mesh-ui-BllgajJz.min.js} +1 -1
  25. package/dist/{three-mesh-ui-3nSSizT4.js → three-mesh-ui-DYyiRn5Y.js} +1 -1
  26. package/dist/{vendor-tyBvnMF-.umd.cjs → vendor-BFgQSG2m.umd.cjs} +1 -1
  27. package/dist/{vendor-DMZcbVO1.js → vendor-BIFy-gRe.js} +1 -1
  28. package/dist/{vendor-sURMCFSI.min.js → vendor-ChgmXMYr.min.js} +1 -1
  29. package/lib/engine/debug/debug_overlay.js +13 -2
  30. package/lib/engine/debug/debug_overlay.js.map +1 -1
  31. package/lib/engine/engine_animation.d.ts +7 -0
  32. package/lib/engine/engine_animation.js +16 -0
  33. package/lib/engine/engine_animation.js.map +1 -1
  34. package/lib/engine/engine_input.js +5 -3
  35. package/lib/engine/engine_input.js.map +1 -1
  36. package/lib/engine/webcomponents/WebXRButtons.js +8 -1
  37. package/lib/engine/webcomponents/WebXRButtons.js.map +1 -1
  38. package/lib/engine/webcomponents/buttons.js +4 -0
  39. package/lib/engine/webcomponents/buttons.js.map +1 -1
  40. package/lib/engine/webcomponents/icons.js +44 -5
  41. package/lib/engine/webcomponents/icons.js.map +1 -1
  42. package/lib/engine/webcomponents/logo-element.js +0 -1
  43. package/lib/engine/webcomponents/logo-element.js.map +1 -1
  44. package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +19 -3
  45. package/lib/engine/webcomponents/needle menu/needle-menu.js +248 -161
  46. package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
  47. package/lib/engine/webcomponents/needle-engine.ar-overlay.js +1 -0
  48. package/lib/engine/webcomponents/needle-engine.ar-overlay.js.map +1 -1
  49. package/lib/engine/xr/NeedleXRSession.d.ts +2 -0
  50. package/lib/engine/xr/NeedleXRSession.js +19 -10
  51. package/lib/engine/xr/NeedleXRSession.js.map +1 -1
  52. package/lib/engine-components/Animation.js +2 -0
  53. package/lib/engine-components/Animation.js.map +1 -1
  54. package/lib/engine-components/AnimatorController.js +2 -0
  55. package/lib/engine-components/AnimatorController.js.map +1 -1
  56. package/lib/engine-components/Light.d.ts +17 -12
  57. package/lib/engine-components/Light.js +52 -36
  58. package/lib/engine-components/Light.js.map +1 -1
  59. package/lib/engine-components/webxr/WebXR.js +6 -8
  60. package/lib/engine-components/webxr/WebXR.js.map +1 -1
  61. package/lib/engine-components/webxr/WebXRImageTracking.js +9 -2
  62. package/lib/engine-components/webxr/WebXRImageTracking.js.map +1 -1
  63. package/package.json +2 -2
  64. package/plugins/common/license.js +3 -3
  65. package/src/engine/debug/debug_overlay.ts +15 -2
  66. package/src/engine/engine_animation.ts +19 -1
  67. package/src/engine/engine_input.ts +5 -3
  68. package/src/engine/webcomponents/WebXRButtons.ts +9 -1
  69. package/src/engine/webcomponents/buttons.ts +5 -0
  70. package/src/engine/webcomponents/icons.ts +47 -5
  71. package/src/engine/webcomponents/index.ts +1 -1
  72. package/src/engine/webcomponents/logo-element.ts +0 -1
  73. package/src/engine/webcomponents/needle menu/needle-menu.ts +270 -165
  74. package/src/engine/webcomponents/needle-engine.ar-overlay.ts +1 -0
  75. package/src/engine/xr/NeedleXRSession.ts +23 -10
  76. package/src/engine-components/Animation.ts +4 -1
  77. package/src/engine-components/AnimatorController.ts +3 -0
  78. package/src/engine-components/Light.ts +50 -42
  79. package/src/engine-components/webxr/WebXR.ts +6 -9
  80. package/src/engine-components/webxr/WebXRImageTracking.ts +12 -2
@@ -58,6 +58,18 @@ const debugNonCommercial = getParam("debugnoncommercial");
58
58
  * @category HTML
59
59
  */
60
60
  export class NeedleMenu {
61
+ static setElementPriority(button, priority) {
62
+ button.setAttribute("priority", String(priority));
63
+ }
64
+ static getElementPriority(button) {
65
+ const priority = button.getAttribute("priority");
66
+ if (priority) {
67
+ const val = Number.parseFloat(priority);
68
+ if (!Number.isNaN(val))
69
+ return val;
70
+ }
71
+ return undefined;
72
+ }
61
73
  _context;
62
74
  _menu;
63
75
  _spatialMenu;
@@ -198,7 +210,6 @@ export class NeedleMenu {
198
210
  return;
199
211
  }
200
212
  this._muteButton = ButtonsFactory.getOrCreate().createMuteButton(this._context);
201
- this._muteButton.setAttribute("priority", "100");
202
213
  this._menu.appendChild(this._muteButton);
203
214
  }
204
215
  _muteButton;
@@ -209,7 +220,6 @@ export class NeedleMenu {
209
220
  }
210
221
  this._fullscreenButton = ButtonsFactory.getOrCreate().createFullscreenButton(this._context);
211
222
  if (this._fullscreenButton) {
212
- this._fullscreenButton.setAttribute("priority", "150");
213
223
  this._menu.appendChild(this._fullscreenButton);
214
224
  }
215
225
  }
@@ -218,6 +228,7 @@ export class NeedleMenu {
218
228
  return this._menu.appendChild(child);
219
229
  }
220
230
  }
231
+ // #region Web component
221
232
  /**
222
233
  * `<needle-menu>` web component — lightweight menu used by Needle Engine.
223
234
  *
@@ -272,141 +283,144 @@ export class NeedleMenuElement extends HTMLElement {
272
283
  pointer-events: none;
273
284
  }
274
285
 
275
- #root {
276
- position: absolute;
277
- width: auto;
278
- max-width: 95%;
279
- left: 50%;
280
- transform: translateX(-50%);
281
- top: min(20px, 10vh);
282
- padding: 0.3rem;
283
- display: flex;
284
- visibility: visible;
285
- flex-direction: row-reverse; /* if we overflow this should be right aligned so the logo is always visible */
286
- pointer-events: all;
287
- z-index: 1000;
288
- }
286
+ /** we put base styles in a layer to allow overrides more easily (e.g. the button.mode requested animation should override the base styles) */
287
+ @layer base {
289
288
 
290
- /** hide the menu if it's empty **/
291
- #root.has-no-options.logo-hidden {
292
- display: none;
293
- }
289
+ #root {
290
+ position: absolute;
291
+ width: auto;
292
+ max-width: 95%;
293
+ left: 50%;
294
+ transform: translateX(-50%);
295
+ top: min(20px, 10vh);
296
+ padding: 0.3rem;
297
+ display: flex;
298
+ visibility: visible;
299
+ flex-direction: row-reverse; /* if we overflow this should be right aligned so the logo is always visible */
300
+ pointer-events: all;
301
+ z-index: 1000;
302
+ }
294
303
 
295
- /** using a div here because then we can change the class for placement **/
296
- #root.bottom {
297
- top: auto;
298
- bottom: min(30px, 10vh);
299
- }
300
- #root.top {
301
- top: calc(.7rem + env(safe-area-inset-top));
302
- }
303
-
304
- .wrapper {
305
- position: relative;
306
- display: flex;
307
- flex-direction: row;
308
- justify-content: center;
309
- align-items: stretch;
310
- gap: 0px;
311
- padding: 0 0rem;
312
- }
304
+ /** hide the menu if it's empty **/
305
+ #root.has-no-options.logo-hidden {
306
+ display: none;
307
+ }
313
308
 
314
- .wrapper > *, .options > button, .options > select, ::slotted(*) {
315
- position: relative;
316
- border: none;
317
- border-radius: 0;
318
- outline: 1px solid rgba(0,0,0,0);
319
- display: flex;
320
- justify-content: center;
321
- align-items: center;
322
- max-height: 2.3rem;
323
- max-width: 100%;
309
+ /** using a div here because then we can change the class for placement **/
310
+ #root.bottom {
311
+ top: auto;
312
+ bottom: min(30px, 10vh);
313
+ }
314
+ #root.top {
315
+ top: calc(.7rem + env(safe-area-inset-top));
316
+ }
317
+
318
+ .wrapper {
319
+ position: relative;
320
+ display: flex;
321
+ flex-direction: row;
322
+ justify-content: center;
323
+ align-items: stretch;
324
+ gap: 0px;
325
+ padding: 0 0rem;
326
+ }
324
327
 
325
- /** basic font settings for all entries **/
326
- font-size: 1rem;
327
- font-family: 'Roboto Flex', sans-serif;
328
- font-optical-sizing: auto;
329
- font-weight: 500;
330
- font-weight: 200;
331
- font-variation-settings: "wdth" 100;
332
- color: rgb(20,20,20);
333
- }
328
+ .wrapper > *, .options > button, .options > select, ::slotted(*) {
329
+ position: relative;
330
+ border: none;
331
+ border-radius: 0;
332
+ outline: 1px solid rgba(0,0,0,0);
333
+ display: flex;
334
+ justify-content: center;
335
+ align-items: center;
336
+ max-height: 2.3rem;
337
+ max-width: 100%;
334
338
 
335
- .options > select[multiple]:hover {
336
- max-height: 300px;
337
- }
339
+ /** basic font settings for all entries **/
340
+ font-size: 1rem;
341
+ font-family: 'Roboto Flex', sans-serif;
342
+ font-optical-sizing: auto;
343
+ font-weight: 400;
344
+ font-variation-settings: "wdth" 100;
345
+ color: rgb(20,20,20);
346
+ }
338
347
 
339
- .floating-panel-style {
340
- background: rgba(255, 255, 255, .4);
341
- outline: rgb(0 0 0 / 5%) 1px solid;
342
- border: 1px solid rgba(255, 255, 255, .1);
343
- box-shadow: 0px 7px 0.5rem 0px rgb(0 0 0 / 6%), inset 0px 0px 1.3rem rgba(0,0,0,.05);
344
- border-radius: 1.5rem;
345
- /**
346
- * to make nested background filter work
347
- * https://stackoverflow.com/questions/60997948/backdrop-filter-not-working-for-nested-elements-in-chrome
348
- **/
349
- &::before {
350
- content: '';
351
- position: absolute;
352
- width: 100%;
353
- height: 100%;
354
- top: 0;
355
- left: 0;
356
- z-index: -1;
348
+ .options > select[multiple]:hover {
349
+ max-height: 300px;
350
+ }
351
+
352
+ .floating-panel-style {
353
+ background: rgba(255, 255, 255, .4);
354
+ outline: rgb(0 0 0 / 5%) 1px solid;
355
+ border: 1px solid rgba(255, 255, 255, .1);
356
+ box-shadow: 0px 7px 0.5rem 0px rgb(0 0 0 / 6%), inset 0px 0px 1.3rem rgba(0,0,0,.05);
357
357
  border-radius: 1.5rem;
358
- -webkit-backdrop-filter: blur(8px);
359
- backdrop-filter: blur(8px);
358
+ /**
359
+ * to make nested background filter work
360
+ * https://stackoverflow.com/questions/60997948/backdrop-filter-not-working-for-nested-elements-in-chrome
361
+ **/
362
+ &::before {
363
+ content: '';
364
+ position: absolute;
365
+ width: 100%;
366
+ height: 100%;
367
+ top: 0;
368
+ left: 0;
369
+ z-index: -1;
370
+ border-radius: 1.5rem;
371
+ -webkit-backdrop-filter: blur(8px);
372
+ backdrop-filter: blur(8px);
373
+ }
360
374
  }
361
- }
362
375
 
363
- a {
364
- color: inherit;
365
- text-decoration: none;
366
- }
376
+ a {
377
+ color: inherit;
378
+ text-decoration: none;
379
+ }
367
380
 
368
- .options {
369
- display: flex;
370
- flex-direction: row;
371
- align-items: center;
372
- }
381
+ .options {
382
+ display: flex;
383
+ flex-direction: row;
384
+ align-items: center;
385
+ }
373
386
 
374
- .options > *, ::slotted(*) {
375
- max-height: 2.25rem;
376
- padding: .4rem .5rem;
377
- }
387
+ .options > *, ::slotted(*) {
388
+ max-height: 2.25rem;
389
+ padding: .4rem .5rem;
390
+ }
378
391
 
379
- :host .options > *, ::slotted(*) {
380
- background: transparent;
381
- border: none;
382
- white-space: nowrap;
383
- transition: all 0.1s linear .02s;
384
- border-radius: 1.5rem;
385
- user-select: none;
386
- }
387
- :host .options > *:hover, ::slotted(*:hover) {
388
- cursor: pointer;
389
- color: black;
390
- background: rgba(245, 245, 245, .8);
391
- box-shadow: inset 0 0 1rem rgba(0,0,30,.2);
392
- outline: rgba(0,0,0,.1) 1px solid;
393
- }
394
- :host .options > *:active, ::slotted(*:active) {
395
- background: rgba(255, 255, 255, .8);
396
- box-shadow: inset 0px 1px 1px rgba(255,255,255,.5), inset 0 0 2rem rgba(0,0,30,.2), inset 0px 2px 4px rgba(0,0,20,.5);
397
- transition: all 0.05s linear;
398
- }
399
- :host .options > *:focus, ::slotted(*:focus) {
400
- outline: rgba(255,255,255,.5) 1px solid;
401
- }
402
- :host .options > *:focus-visible, ::slotted(*:focus-visible) {
403
- outline: rgba(0,0,0,.5) 1px solid;
404
- }
392
+ :host .options > *, ::slotted(*) {
393
+ background: transparent;
394
+ border: none;
395
+ white-space: nowrap;
396
+ transition: all 0.1s linear .02s;
397
+ border-radius: 1.5rem;
398
+ user-select: none;
399
+ }
400
+ :host .options > *:hover, ::slotted(*:hover) {
401
+ cursor: pointer;
402
+ color: black;
403
+ background: rgba(245, 245, 245, .8);
404
+ box-shadow: inset 0 0 1rem rgba(0,0,30,.2);
405
+ outline: rgba(0,0,0,.1) 1px solid;
406
+ }
407
+ :host .options > *:active, ::slotted(*:active) {
408
+ background: rgba(255, 255, 255, .8);
409
+ box-shadow: inset 0px 1px 1px rgba(255,255,255,.5), inset 0 0 2rem rgba(0,0,30,.2), inset 0px 2px 4px rgba(0,0,20,.5);
410
+ transition: all 0.05s linear;
411
+ }
412
+ :host .options > *:focus, ::slotted(*:focus) {
413
+ outline: rgba(255,255,255,.5) 1px solid;
414
+ }
415
+ :host .options > *:focus-visible, ::slotted(*:focus-visible) {
416
+ outline: rgba(0,0,0,.5) 1px solid;
417
+ }
405
418
 
406
- :host .options > *:disabled, ::slotted(*:disabled) {
407
- background: rgba(0,0,0,.05);
408
- color: rgba(60,60,60,.7);
409
- pointer-events: none;
419
+ :host .options > *:disabled, ::slotted(*:disabled) {
420
+ background: rgba(0,0,0,.05);
421
+ color: rgba(60,60,60,.7);
422
+ pointer-events: none;
423
+ }
410
424
  }
411
425
 
412
426
  button, ::slotted(button) {
@@ -414,6 +428,7 @@ export class NeedleMenuElement extends HTMLElement {
414
428
  }
415
429
 
416
430
  /** XR button animation **/
431
+
417
432
  :host button.this-mode-is-requested {
418
433
  background: repeating-linear-gradient(to right, #fff 0%, #fff 40%, #aaffff 55%, #fff 80%);
419
434
  background-size: 200% auto;
@@ -425,8 +440,12 @@ export class NeedleMenuElement extends HTMLElement {
425
440
  }
426
441
 
427
442
  @keyframes AnimationName {
428
- 0% { background-position: 0% 0 }
429
- 100% { background-position: -200% 0 }
443
+ 0% {
444
+ background-position: 0% 0
445
+ }
446
+ 100% {
447
+ background-position: -200% 0
448
+ }
430
449
  }
431
450
 
432
451
 
@@ -434,9 +453,9 @@ export class NeedleMenuElement extends HTMLElement {
434
453
 
435
454
  .logo {
436
455
  cursor: pointer;
437
- padding-left: 0.0rem;
438
- padding-bottom: .02rem;
439
- margin-right: 0.5rem;
456
+ padding-left: 0.5em;
457
+ padding-bottom: .02em;
458
+ margin-right: 0.6em;
440
459
  }
441
460
  .logo-hidden {
442
461
  .logo {
@@ -445,8 +464,8 @@ export class NeedleMenuElement extends HTMLElement {
445
464
  }
446
465
  :host .has-options .logo {
447
466
  border-left: 1px solid rgba(40,40,40,.4);
448
- margin-left: 0.3rem;
449
- margin-right: 0.5rem;
467
+ margin-left: 0.3em;
468
+ margin-right: 0.6em;
450
469
  }
451
470
 
452
471
  .logo > span {
@@ -459,6 +478,10 @@ export class NeedleMenuElement extends HTMLElement {
459
478
 
460
479
  /** Hide the menu button normally **/
461
480
  .compact-menu-button { display: none; }
481
+
482
+ /** Hide the compact only options when not in compact mode */
483
+ .options.compact-only { display: none; }
484
+
462
485
  /** And show it when we're in compact mode **/
463
486
  .compact .compact-menu-button {
464
487
  position: relative;
@@ -574,15 +597,24 @@ export class NeedleMenuElement extends HTMLElement {
574
597
  }
575
598
 
576
599
  .compact .options > *, .compact .options > ::slotted(*) {
577
- font-size: 1.2rem;
578
- padding: .6rem .5rem;
600
+ font-size: 1.2em;
601
+ padding: .6em .5em;
579
602
  width: 100%;
580
603
  }
604
+ .compact.has-options {
605
+ padding-left: 1em;
606
+ }
581
607
  .compact.has-options .logo {
582
608
  border: none;
583
609
  padding-left: 0;
584
- margin-left: 1rem;
585
- margin-bottom: .02rem;
610
+ margin-bottom: .02em;
611
+ }
612
+ .compact .options.compact-only {
613
+ display: initial;
614
+ & > * {
615
+ min-height: 1em;
616
+ padding: .4em .4em;
617
+ }
586
618
  }
587
619
  .compact .options {
588
620
  /** e.g. if we have a very wide menu item like a select with long option names we don't want to overflow **/
@@ -610,28 +642,15 @@ export class NeedleMenuElement extends HTMLElement {
610
642
  display: none !important;
611
643
  }
612
644
  }
613
-
614
- /* dark mode */
615
- /*
616
- @media (prefers-color-scheme: dark) {
617
- :host {
618
- background: rgba(0,0,0, .6);
619
- }
620
- :host button {
621
- color: rgba(200,200,200);
622
- }
623
- :host button:hover {
624
- background: rgba(100,100,100, .8);
625
- }
626
- }
627
- */
628
645
 
629
646
  </style>
630
647
 
631
648
  <div id="root" class="logo-hidden floating-panel-style bottom">
632
649
  <div class="wrapper">
650
+ <div class="options compact-only" part="options">
651
+ </div>
633
652
  <div class="foldout">
634
- <div class="options" part="options">
653
+ <div class="options main-container" part="options">
635
654
  <slot></slot>
636
655
  </div>
637
656
  <div class="options" part="options">
@@ -658,7 +677,8 @@ export class NeedleMenuElement extends HTMLElement {
658
677
  shadow?.appendChild(content);
659
678
  this.root = shadow.querySelector("#root");
660
679
  this.wrapper = this.root?.querySelector(".wrapper");
661
- this.options = this.root?.querySelector(".options");
680
+ this.options = this.root?.querySelector(".options.main-container");
681
+ this.optionsCompactMode = this.root?.querySelector(".options.compact-only");
662
682
  this.logoContainer = this.root?.querySelector(".logo");
663
683
  this.compactMenuButton = this.root?.querySelector(".compact-menu-button");
664
684
  this.compactMenuButton.append(getIconElement("more_vert"));
@@ -762,7 +782,7 @@ export class NeedleMenuElement extends HTMLElement {
762
782
  connectedCallback() {
763
783
  window.addEventListener("resize", this.handleSizeChange);
764
784
  this.handleMenuVisible();
765
- this._sizeChangeInterval = setInterval(() => this.handleSizeChange(undefined, true), 5000);
785
+ this._sizeChangeInterval = setInterval(() => this.handleSizeChange(undefined, false), 5000);
766
786
  // the dom element is set after the constructor runs
767
787
  setTimeout(() => {
768
788
  this._domElement?.addEventListener("resize", this.handleSizeChange);
@@ -850,6 +870,8 @@ export class NeedleMenuElement extends HTMLElement {
850
870
  wrapper;
851
871
  /** @private contains the buttons and dynamic elements */
852
872
  options;
873
+ /** @private contains options visible when in compact mode */
874
+ optionsCompactMode;
853
875
  /** @private contains the needle-logo html element */
854
876
  logoContainer;
855
877
  /** @private compact menu button element */
@@ -955,6 +977,8 @@ export class NeedleMenuElement extends HTMLElement {
955
977
  }
956
978
  }
957
979
  _isHandlingChange = false;
980
+ /** 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. */
981
+ _pauseMutationObserverOptionsContainer = false;
958
982
  /** Called when any change in the web component is detected (including in children and child attributes) */
959
983
  onChangeDetected(_mut) {
960
984
  if (this._isHandlingChange)
@@ -965,7 +989,8 @@ export class NeedleMenuElement extends HTMLElement {
965
989
  this.handleMenuVisible();
966
990
  for (const mut of _mut) {
967
991
  if (mut.target == this.options) {
968
- this.onOptionsChildrenChanged(mut);
992
+ if (!this._pauseMutationObserverOptionsContainer)
993
+ this.onOptionsChildrenChanged(mut);
969
994
  }
970
995
  }
971
996
  }
@@ -1048,25 +1073,28 @@ export class NeedleMenuElement extends HTMLElement {
1048
1073
  return false;
1049
1074
  }
1050
1075
  _lastAvailableWidthChange = 0;
1051
- _timeoutHandle = 0;
1076
+ _timeoutHandleSize = 0;
1077
+ _timeoutHandleCompactItems = 0;
1052
1078
  handleSizeChange = (_evt, forceOrEvent) => {
1053
1079
  if (!this._domElement)
1054
1080
  return;
1081
+ // if (this._isApplyingSizeUpdate) return;
1055
1082
  const width = this._domElement.clientWidth;
1056
1083
  if (width < 100) {
1057
- clearTimeout(this._timeoutHandle);
1084
+ clearTimeout(this._timeoutHandleSize);
1058
1085
  this.root.classList.add("compact");
1059
1086
  this.foldout.classList.add("floating-panel-style");
1060
1087
  return;
1061
1088
  }
1062
- const padding = 20 * 2;
1089
+ const padding = 10 * 2;
1063
1090
  const availableWidth = width - padding;
1064
1091
  // if the available width has not changed significantly then we can skip the rest
1065
1092
  if (!forceOrEvent && Math.abs(availableWidth - this._lastAvailableWidthChange) < 1)
1066
1093
  return;
1067
1094
  this._lastAvailableWidthChange = availableWidth;
1068
- clearTimeout(this._timeoutHandle);
1069
- this._timeoutHandle = setTimeout(() => {
1095
+ clearTimeout(this._timeoutHandleSize);
1096
+ this._timeoutHandleSize = setTimeout(() => {
1097
+ // console.warn("APPLY", this.root.classList.contains("compact") ? "COMPACT" : "FULL", "MODE (available width: " + availableWidth.toFixed(0) + "px)", getCurrentWidth());
1070
1098
  const spaceLeft = getSpaceLeft();
1071
1099
  if (spaceLeft < 0) {
1072
1100
  this.root.classList.add("compact");
@@ -1081,9 +1109,18 @@ export class NeedleMenuElement extends HTMLElement {
1081
1109
  this.foldout.classList.add("floating-panel-style");
1082
1110
  }
1083
1111
  }
1084
- }, 5);
1112
+ this._pauseMutationObserverOptionsContainer = true;
1113
+ this.updateCompactFoldoutItem();
1114
+ window.requestAnimationFrame(() => this._pauseMutationObserverOptionsContainer = false);
1115
+ }, 150);
1085
1116
  const getCurrentWidth = () => {
1086
- return this.options.clientWidth + this.logoContainer.clientWidth;
1117
+ let totalWidthRequired = 0;
1118
+ totalWidthRequired += this.options.getBoundingClientRect().width;
1119
+ totalWidthRequired += this.optionsCompactMode.getBoundingClientRect().width;
1120
+ totalWidthRequired += 10 * this.options.childElementCount; // padding
1121
+ totalWidthRequired += this.logoContainer.style.display != "none" ? this.logoContainer.getBoundingClientRect().width : 0;
1122
+ ;
1123
+ return totalWidthRequired;
1087
1124
  };
1088
1125
  let lastSpaceLeft = -1;
1089
1126
  const getSpaceLeft = () => {
@@ -1095,6 +1132,56 @@ export class NeedleMenuElement extends HTMLElement {
1095
1132
  return spaceLeft;
1096
1133
  };
1097
1134
  };
1135
+ updateCompactFoldoutItem() {
1136
+ if (this.root.classList.contains("compact")) {
1137
+ // Find items in the folding list with the highest priority
1138
+ // The one with the highest priority will be added to the visible container
1139
+ let priorityItem = null;
1140
+ let priorityValue = -10000000;
1141
+ const testItem = (element) => {
1142
+ if (element instanceof HTMLElement) {
1143
+ const priority = NeedleMenu.getElementPriority(element);
1144
+ if (priority !== undefined && priority >= priorityValue) {
1145
+ // check if the element is hidden
1146
+ // @TODO: use computed styles
1147
+ const style = window.getComputedStyle(element);
1148
+ if (style.display === "none" || style.visibility === "hidden" || style.opacity === "0") {
1149
+ return;
1150
+ }
1151
+ priorityItem = element;
1152
+ priorityValue = priority;
1153
+ }
1154
+ }
1155
+ };
1156
+ for (let i = 0; i < this.options.children.length; i++) {
1157
+ testItem(this.options.children.item(i));
1158
+ }
1159
+ for (let i = 0; i < this.optionsCompactMode.children.length; i++) {
1160
+ testItem(this.optionsCompactMode.children.item(i));
1161
+ }
1162
+ if (priorityItem && !this.optionsCompactMode.contains(priorityItem)) {
1163
+ this.optionsCompactMode.childNodes.forEach(element => {
1164
+ this.options.appendChild(element);
1165
+ });
1166
+ const item = priorityItem;
1167
+ this.optionsCompactMode.appendChild(item);
1168
+ // console.warn("In compact mode, moved item with priority " + priorityValue + " to compact foldout:", item);
1169
+ }
1170
+ else if (!priorityItem) {
1171
+ // console.warn("In compact mode but no item has priority, showing all items in foldout");
1172
+ this.optionsCompactMode.childNodes.forEach(element => {
1173
+ this.options.appendChild(element);
1174
+ });
1175
+ }
1176
+ }
1177
+ else {
1178
+ // console.warn("Not in compact mode but trying to update compact foldout item");
1179
+ this.optionsCompactMode.childNodes.forEach(element => {
1180
+ this.options.appendChild(element);
1181
+ });
1182
+ }
1183
+ }
1184
+ // private _foldoutItemVisibleInterval = 0;
1098
1185
  ___insertDebugOptions() {
1099
1186
  window.addEventListener("keydown", (e) => {
1100
1187
  if (e.key === "p") {