@needle-tools/engine 4.12.3 → 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.
- package/CHANGELOG.md +4 -0
- package/components.needle.json +1 -1
- package/dist/{gltf-progressive-Bfpfaz84.umd.cjs → gltf-progressive-BqUnxvCx.umd.cjs} +1 -1
- package/dist/{gltf-progressive-hFPACYio.min.js → gltf-progressive-CSaX5HQb.min.js} +2 -2
- package/dist/{gltf-progressive-DPunMlEM.js → gltf-progressive-ChnIhDXx.js} +27 -27
- package/dist/{loader.worker-DWzfDpAl.js → loader.worker-C1GG9A7C.js} +6 -6
- package/dist/{needle-engine.bundle-u-rSDw6R.js → needle-engine.bundle-C9mGVluu.js} +7444 -7358
- package/dist/{needle-engine.bundle-CLPD2ttK.umd.cjs → needle-engine.bundle-CnmG19ga.umd.cjs} +300 -293
- package/dist/{needle-engine.bundle-B3ssYJS0.min.js → needle-engine.bundle-ZJOekt7-.min.js} +297 -290
- package/dist/needle-engine.d.ts +53 -28
- package/dist/needle-engine.js +4 -4
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/dist/{postprocessing-ClLv0reO.min.js → postprocessing-12-UW7je.min.js} +1 -1
- package/dist/{postprocessing-BHQvwehB.umd.cjs → postprocessing-B3Hu0Ryi.umd.cjs} +1 -1
- package/dist/{postprocessing-DLI2N3LL.js → postprocessing-R535krvT.js} +2 -2
- package/dist/{three-Bf2NBxAw.umd.cjs → three-BzxwLtUE.umd.cjs} +176 -176
- package/dist/{three-BCCkyCA5.js → three-D9pcFbxc.js} +4637 -4636
- package/dist/{three-W7zWTcfP.min.js → three-DMvLgxja.min.js} +176 -176
- package/dist/{three-examples-DB5Uoja4.min.js → three-examples-CIv2roOA.min.js} +1 -1
- package/dist/{three-examples-Djbk6WA4.umd.cjs → three-examples-CjSwCv_b.umd.cjs} +1 -1
- package/dist/{three-examples-D4rE49Ui.js → three-examples-F0MJj0vr.js} +1 -1
- package/dist/{three-mesh-ui-zsOOA5Pq.umd.cjs → three-mesh-ui-BLnJQzMl.umd.cjs} +1 -1
- package/dist/{three-mesh-ui-CIez6qJQ.min.js → three-mesh-ui-BllgajJz.min.js} +1 -1
- package/dist/{three-mesh-ui-3nSSizT4.js → three-mesh-ui-DYyiRn5Y.js} +1 -1
- package/dist/{vendor-tyBvnMF-.umd.cjs → vendor-BFgQSG2m.umd.cjs} +1 -1
- package/dist/{vendor-DMZcbVO1.js → vendor-BIFy-gRe.js} +1 -1
- package/dist/{vendor-sURMCFSI.min.js → vendor-ChgmXMYr.min.js} +1 -1
- package/lib/engine/debug/debug_overlay.js +13 -2
- package/lib/engine/debug/debug_overlay.js.map +1 -1
- package/lib/engine/engine_animation.d.ts +7 -0
- package/lib/engine/engine_animation.js +16 -0
- package/lib/engine/engine_animation.js.map +1 -1
- package/lib/engine/engine_input.js +5 -3
- package/lib/engine/engine_input.js.map +1 -1
- package/lib/engine/webcomponents/WebXRButtons.js +8 -1
- package/lib/engine/webcomponents/WebXRButtons.js.map +1 -1
- package/lib/engine/webcomponents/buttons.js +4 -0
- package/lib/engine/webcomponents/buttons.js.map +1 -1
- package/lib/engine/webcomponents/icons.js +44 -5
- package/lib/engine/webcomponents/icons.js.map +1 -1
- package/lib/engine/webcomponents/logo-element.js +0 -1
- package/lib/engine/webcomponents/logo-element.js.map +1 -1
- package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +19 -3
- package/lib/engine/webcomponents/needle menu/needle-menu.js +248 -161
- package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.ar-overlay.js +1 -0
- package/lib/engine/webcomponents/needle-engine.ar-overlay.js.map +1 -1
- package/lib/engine/xr/NeedleXRSession.d.ts +2 -0
- package/lib/engine/xr/NeedleXRSession.js +19 -10
- package/lib/engine/xr/NeedleXRSession.js.map +1 -1
- package/lib/engine-components/Animation.js +2 -0
- package/lib/engine-components/Animation.js.map +1 -1
- package/lib/engine-components/AnimatorController.js +2 -0
- package/lib/engine-components/AnimatorController.js.map +1 -1
- package/lib/engine-components/Light.d.ts +17 -12
- package/lib/engine-components/Light.js +52 -36
- package/lib/engine-components/Light.js.map +1 -1
- package/lib/engine-components/webxr/WebXR.js +6 -8
- package/lib/engine-components/webxr/WebXR.js.map +1 -1
- package/lib/engine-components/webxr/WebXRImageTracking.js +9 -2
- package/lib/engine-components/webxr/WebXRImageTracking.js.map +1 -1
- package/package.json +3 -3
- package/plugins/common/license.js +3 -3
- package/src/engine/debug/debug_overlay.ts +15 -2
- package/src/engine/engine_animation.ts +19 -1
- package/src/engine/engine_input.ts +5 -3
- package/src/engine/webcomponents/WebXRButtons.ts +9 -1
- package/src/engine/webcomponents/buttons.ts +5 -0
- package/src/engine/webcomponents/icons.ts +47 -5
- package/src/engine/webcomponents/index.ts +1 -1
- package/src/engine/webcomponents/logo-element.ts +0 -1
- package/src/engine/webcomponents/needle menu/needle-menu.ts +270 -165
- package/src/engine/webcomponents/needle-engine.ar-overlay.ts +1 -0
- package/src/engine/xr/NeedleXRSession.ts +23 -10
- package/src/engine-components/Animation.ts +4 -1
- package/src/engine-components/AnimatorController.ts +3 -0
- package/src/engine-components/Light.ts +50 -42
- package/src/engine-components/webxr/WebXR.ts +6 -9
- package/src/engine-components/webxr/WebXRImageTracking.ts +12 -2
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { TestSimulateUserData } from "../../../needle-engine.js";
|
|
1
2
|
import { showBalloonMessage } from "../../debug/debug.js";
|
|
2
3
|
import type { Context } from "../../engine_context.js";
|
|
3
4
|
import { hasCommercialLicense, onLicenseCheckResultChanged, Telemetry } from "../../engine_license.js";
|
|
5
|
+
import { LeftRoomResponse } from "../../engine_networking.js";
|
|
4
6
|
import { isLocalNetwork } from "../../engine_networking_utils.js";
|
|
5
7
|
import { DeviceUtilities, getParam } from "../../engine_utils.js";
|
|
6
8
|
import { onXRSessionStart, XRSessionEventArgs } from "../../xr/events.js";
|
|
@@ -45,9 +47,17 @@ export declare type ButtonInfo = {
|
|
|
45
47
|
label: string,
|
|
46
48
|
/** Material icon name: https://fonts.google.com/icons */
|
|
47
49
|
icon?: string,
|
|
48
|
-
/**
|
|
50
|
+
/** "left" or "right" to place the icon on the left or right side of the button. Default is "left" */
|
|
49
51
|
iconSide?: "left" | "right",
|
|
50
|
-
/**
|
|
52
|
+
/**
|
|
53
|
+
* Priority controls the order of buttons in the menu.
|
|
54
|
+
* If not enough space is available to show all buttons - the highest priority elements will always be visible
|
|
55
|
+
*
|
|
56
|
+
* **Sorting**
|
|
57
|
+
* Low priority is icon is on the left,
|
|
58
|
+
* high priority is icon is on the right.
|
|
59
|
+
* @default undefined
|
|
60
|
+
*/
|
|
51
61
|
priority?: number;
|
|
52
62
|
/** Experimental. Allows to put two buttons in one row for the compact layout */
|
|
53
63
|
class?: "row2";
|
|
@@ -101,6 +111,20 @@ export declare type ButtonInfo = {
|
|
|
101
111
|
* @category HTML
|
|
102
112
|
*/
|
|
103
113
|
export class NeedleMenu {
|
|
114
|
+
|
|
115
|
+
static setElementPriority(button: HTMLElement, priority: number) {
|
|
116
|
+
button.setAttribute("priority", String(priority));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
static getElementPriority(button: HTMLElement): number | undefined {
|
|
120
|
+
const priority = button.getAttribute("priority");
|
|
121
|
+
if (priority) {
|
|
122
|
+
const val = Number.parseFloat(priority);
|
|
123
|
+
if (!Number.isNaN(val)) return val;
|
|
124
|
+
}
|
|
125
|
+
return undefined;
|
|
126
|
+
}
|
|
127
|
+
|
|
104
128
|
private readonly _context: Context;
|
|
105
129
|
private readonly _menu: NeedleMenuElement;
|
|
106
130
|
private readonly _spatialMenu: NeedleSpatialMenu;
|
|
@@ -247,7 +271,6 @@ export class NeedleMenu {
|
|
|
247
271
|
return;
|
|
248
272
|
}
|
|
249
273
|
this._muteButton = ButtonsFactory.getOrCreate().createMuteButton(this._context);
|
|
250
|
-
this._muteButton.setAttribute("priority", "100");
|
|
251
274
|
this._menu.appendChild(this._muteButton);
|
|
252
275
|
}
|
|
253
276
|
private _muteButton?: HTMLButtonElement;
|
|
@@ -260,7 +283,6 @@ export class NeedleMenu {
|
|
|
260
283
|
}
|
|
261
284
|
this._fullscreenButton = ButtonsFactory.getOrCreate().createFullscreenButton(this._context);
|
|
262
285
|
if (this._fullscreenButton) {
|
|
263
|
-
this._fullscreenButton.setAttribute("priority", "150");
|
|
264
286
|
this._menu.appendChild(this._fullscreenButton);
|
|
265
287
|
}
|
|
266
288
|
}
|
|
@@ -274,6 +296,8 @@ export class NeedleMenu {
|
|
|
274
296
|
|
|
275
297
|
}
|
|
276
298
|
|
|
299
|
+
// #region Web component
|
|
300
|
+
|
|
277
301
|
/**
|
|
278
302
|
* `<needle-menu>` web component — lightweight menu used by Needle Engine.
|
|
279
303
|
*
|
|
@@ -333,141 +357,144 @@ export class NeedleMenuElement extends HTMLElement {
|
|
|
333
357
|
pointer-events: none;
|
|
334
358
|
}
|
|
335
359
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
width: auto;
|
|
339
|
-
max-width: 95%;
|
|
340
|
-
left: 50%;
|
|
341
|
-
transform: translateX(-50%);
|
|
342
|
-
top: min(20px, 10vh);
|
|
343
|
-
padding: 0.3rem;
|
|
344
|
-
display: flex;
|
|
345
|
-
visibility: visible;
|
|
346
|
-
flex-direction: row-reverse; /* if we overflow this should be right aligned so the logo is always visible */
|
|
347
|
-
pointer-events: all;
|
|
348
|
-
z-index: 1000;
|
|
349
|
-
}
|
|
360
|
+
/** we put base styles in a layer to allow overrides more easily (e.g. the button.mode requested animation should override the base styles) */
|
|
361
|
+
@layer base {
|
|
350
362
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
363
|
+
#root {
|
|
364
|
+
position: absolute;
|
|
365
|
+
width: auto;
|
|
366
|
+
max-width: 95%;
|
|
367
|
+
left: 50%;
|
|
368
|
+
transform: translateX(-50%);
|
|
369
|
+
top: min(20px, 10vh);
|
|
370
|
+
padding: 0.3rem;
|
|
371
|
+
display: flex;
|
|
372
|
+
visibility: visible;
|
|
373
|
+
flex-direction: row-reverse; /* if we overflow this should be right aligned so the logo is always visible */
|
|
374
|
+
pointer-events: all;
|
|
375
|
+
z-index: 1000;
|
|
376
|
+
}
|
|
355
377
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
}
|
|
361
|
-
#root.top {
|
|
362
|
-
top: calc(.7rem + env(safe-area-inset-top));
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
.wrapper {
|
|
366
|
-
position: relative;
|
|
367
|
-
display: flex;
|
|
368
|
-
flex-direction: row;
|
|
369
|
-
justify-content: center;
|
|
370
|
-
align-items: stretch;
|
|
371
|
-
gap: 0px;
|
|
372
|
-
padding: 0 0rem;
|
|
373
|
-
}
|
|
378
|
+
/** hide the menu if it's empty **/
|
|
379
|
+
#root.has-no-options.logo-hidden {
|
|
380
|
+
display: none;
|
|
381
|
+
}
|
|
374
382
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
383
|
+
/** using a div here because then we can change the class for placement **/
|
|
384
|
+
#root.bottom {
|
|
385
|
+
top: auto;
|
|
386
|
+
bottom: min(30px, 10vh);
|
|
387
|
+
}
|
|
388
|
+
#root.top {
|
|
389
|
+
top: calc(.7rem + env(safe-area-inset-top));
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
.wrapper {
|
|
393
|
+
position: relative;
|
|
394
|
+
display: flex;
|
|
395
|
+
flex-direction: row;
|
|
396
|
+
justify-content: center;
|
|
397
|
+
align-items: stretch;
|
|
398
|
+
gap: 0px;
|
|
399
|
+
padding: 0 0rem;
|
|
400
|
+
}
|
|
385
401
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
top: 0;
|
|
416
|
-
left: 0;
|
|
417
|
-
z-index: -1;
|
|
402
|
+
.wrapper > *, .options > button, .options > select, ::slotted(*) {
|
|
403
|
+
position: relative;
|
|
404
|
+
border: none;
|
|
405
|
+
border-radius: 0;
|
|
406
|
+
outline: 1px solid rgba(0,0,0,0);
|
|
407
|
+
display: flex;
|
|
408
|
+
justify-content: center;
|
|
409
|
+
align-items: center;
|
|
410
|
+
max-height: 2.3rem;
|
|
411
|
+
max-width: 100%;
|
|
412
|
+
|
|
413
|
+
/** basic font settings for all entries **/
|
|
414
|
+
font-size: 1rem;
|
|
415
|
+
font-family: 'Roboto Flex', sans-serif;
|
|
416
|
+
font-optical-sizing: auto;
|
|
417
|
+
font-weight: 400;
|
|
418
|
+
font-variation-settings: "wdth" 100;
|
|
419
|
+
color: rgb(20,20,20);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
.options > select[multiple]:hover {
|
|
423
|
+
max-height: 300px;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
.floating-panel-style {
|
|
427
|
+
background: rgba(255, 255, 255, .4);
|
|
428
|
+
outline: rgb(0 0 0 / 5%) 1px solid;
|
|
429
|
+
border: 1px solid rgba(255, 255, 255, .1);
|
|
430
|
+
box-shadow: 0px 7px 0.5rem 0px rgb(0 0 0 / 6%), inset 0px 0px 1.3rem rgba(0,0,0,.05);
|
|
418
431
|
border-radius: 1.5rem;
|
|
419
|
-
|
|
420
|
-
|
|
432
|
+
/**
|
|
433
|
+
* to make nested background filter work
|
|
434
|
+
* https://stackoverflow.com/questions/60997948/backdrop-filter-not-working-for-nested-elements-in-chrome
|
|
435
|
+
**/
|
|
436
|
+
&::before {
|
|
437
|
+
content: '';
|
|
438
|
+
position: absolute;
|
|
439
|
+
width: 100%;
|
|
440
|
+
height: 100%;
|
|
441
|
+
top: 0;
|
|
442
|
+
left: 0;
|
|
443
|
+
z-index: -1;
|
|
444
|
+
border-radius: 1.5rem;
|
|
445
|
+
-webkit-backdrop-filter: blur(8px);
|
|
446
|
+
backdrop-filter: blur(8px);
|
|
447
|
+
}
|
|
421
448
|
}
|
|
422
|
-
}
|
|
423
449
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
450
|
+
a {
|
|
451
|
+
color: inherit;
|
|
452
|
+
text-decoration: none;
|
|
453
|
+
}
|
|
428
454
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
455
|
+
.options {
|
|
456
|
+
display: flex;
|
|
457
|
+
flex-direction: row;
|
|
458
|
+
align-items: center;
|
|
459
|
+
}
|
|
434
460
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
461
|
+
.options > *, ::slotted(*) {
|
|
462
|
+
max-height: 2.25rem;
|
|
463
|
+
padding: .4rem .5rem;
|
|
464
|
+
}
|
|
439
465
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
+
:host .options > *, ::slotted(*) {
|
|
467
|
+
background: transparent;
|
|
468
|
+
border: none;
|
|
469
|
+
white-space: nowrap;
|
|
470
|
+
transition: all 0.1s linear .02s;
|
|
471
|
+
border-radius: 1.5rem;
|
|
472
|
+
user-select: none;
|
|
473
|
+
}
|
|
474
|
+
:host .options > *:hover, ::slotted(*:hover) {
|
|
475
|
+
cursor: pointer;
|
|
476
|
+
color: black;
|
|
477
|
+
background: rgba(245, 245, 245, .8);
|
|
478
|
+
box-shadow: inset 0 0 1rem rgba(0,0,30,.2);
|
|
479
|
+
outline: rgba(0,0,0,.1) 1px solid;
|
|
480
|
+
}
|
|
481
|
+
:host .options > *:active, ::slotted(*:active) {
|
|
482
|
+
background: rgba(255, 255, 255, .8);
|
|
483
|
+
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);
|
|
484
|
+
transition: all 0.05s linear;
|
|
485
|
+
}
|
|
486
|
+
:host .options > *:focus, ::slotted(*:focus) {
|
|
487
|
+
outline: rgba(255,255,255,.5) 1px solid;
|
|
488
|
+
}
|
|
489
|
+
:host .options > *:focus-visible, ::slotted(*:focus-visible) {
|
|
490
|
+
outline: rgba(0,0,0,.5) 1px solid;
|
|
491
|
+
}
|
|
466
492
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
493
|
+
:host .options > *:disabled, ::slotted(*:disabled) {
|
|
494
|
+
background: rgba(0,0,0,.05);
|
|
495
|
+
color: rgba(60,60,60,.7);
|
|
496
|
+
pointer-events: none;
|
|
497
|
+
}
|
|
471
498
|
}
|
|
472
499
|
|
|
473
500
|
button, ::slotted(button) {
|
|
@@ -475,6 +502,7 @@ export class NeedleMenuElement extends HTMLElement {
|
|
|
475
502
|
}
|
|
476
503
|
|
|
477
504
|
/** XR button animation **/
|
|
505
|
+
|
|
478
506
|
:host button.this-mode-is-requested {
|
|
479
507
|
background: repeating-linear-gradient(to right, #fff 0%, #fff 40%, #aaffff 55%, #fff 80%);
|
|
480
508
|
background-size: 200% auto;
|
|
@@ -486,8 +514,12 @@ export class NeedleMenuElement extends HTMLElement {
|
|
|
486
514
|
}
|
|
487
515
|
|
|
488
516
|
@keyframes AnimationName {
|
|
489
|
-
0% {
|
|
490
|
-
|
|
517
|
+
0% {
|
|
518
|
+
background-position: 0% 0
|
|
519
|
+
}
|
|
520
|
+
100% {
|
|
521
|
+
background-position: -200% 0
|
|
522
|
+
}
|
|
491
523
|
}
|
|
492
524
|
|
|
493
525
|
|
|
@@ -495,9 +527,9 @@ export class NeedleMenuElement extends HTMLElement {
|
|
|
495
527
|
|
|
496
528
|
.logo {
|
|
497
529
|
cursor: pointer;
|
|
498
|
-
padding-left: 0.
|
|
499
|
-
padding-bottom: .
|
|
500
|
-
margin-right: 0.
|
|
530
|
+
padding-left: 0.5em;
|
|
531
|
+
padding-bottom: .02em;
|
|
532
|
+
margin-right: 0.6em;
|
|
501
533
|
}
|
|
502
534
|
.logo-hidden {
|
|
503
535
|
.logo {
|
|
@@ -506,8 +538,8 @@ export class NeedleMenuElement extends HTMLElement {
|
|
|
506
538
|
}
|
|
507
539
|
:host .has-options .logo {
|
|
508
540
|
border-left: 1px solid rgba(40,40,40,.4);
|
|
509
|
-
margin-left: 0.
|
|
510
|
-
margin-right: 0.
|
|
541
|
+
margin-left: 0.3em;
|
|
542
|
+
margin-right: 0.6em;
|
|
511
543
|
}
|
|
512
544
|
|
|
513
545
|
.logo > span {
|
|
@@ -520,6 +552,10 @@ export class NeedleMenuElement extends HTMLElement {
|
|
|
520
552
|
|
|
521
553
|
/** Hide the menu button normally **/
|
|
522
554
|
.compact-menu-button { display: none; }
|
|
555
|
+
|
|
556
|
+
/** Hide the compact only options when not in compact mode */
|
|
557
|
+
.options.compact-only { display: none; }
|
|
558
|
+
|
|
523
559
|
/** And show it when we're in compact mode **/
|
|
524
560
|
.compact .compact-menu-button {
|
|
525
561
|
position: relative;
|
|
@@ -635,15 +671,24 @@ export class NeedleMenuElement extends HTMLElement {
|
|
|
635
671
|
}
|
|
636
672
|
|
|
637
673
|
.compact .options > *, .compact .options > ::slotted(*) {
|
|
638
|
-
font-size: 1.
|
|
639
|
-
padding: .
|
|
674
|
+
font-size: 1.2em;
|
|
675
|
+
padding: .6em .5em;
|
|
640
676
|
width: 100%;
|
|
641
677
|
}
|
|
678
|
+
.compact.has-options {
|
|
679
|
+
padding-left: 1em;
|
|
680
|
+
}
|
|
642
681
|
.compact.has-options .logo {
|
|
643
682
|
border: none;
|
|
644
683
|
padding-left: 0;
|
|
645
|
-
margin-
|
|
646
|
-
|
|
684
|
+
margin-bottom: .02em;
|
|
685
|
+
}
|
|
686
|
+
.compact .options.compact-only {
|
|
687
|
+
display: initial;
|
|
688
|
+
& > * {
|
|
689
|
+
min-height: 1em;
|
|
690
|
+
padding: .4em .4em;
|
|
691
|
+
}
|
|
647
692
|
}
|
|
648
693
|
.compact .options {
|
|
649
694
|
/** e.g. if we have a very wide menu item like a select with long option names we don't want to overflow **/
|
|
@@ -671,28 +716,15 @@ export class NeedleMenuElement extends HTMLElement {
|
|
|
671
716
|
display: none !important;
|
|
672
717
|
}
|
|
673
718
|
}
|
|
674
|
-
|
|
675
|
-
/* dark mode */
|
|
676
|
-
/*
|
|
677
|
-
@media (prefers-color-scheme: dark) {
|
|
678
|
-
:host {
|
|
679
|
-
background: rgba(0,0,0, .6);
|
|
680
|
-
}
|
|
681
|
-
:host button {
|
|
682
|
-
color: rgba(200,200,200);
|
|
683
|
-
}
|
|
684
|
-
:host button:hover {
|
|
685
|
-
background: rgba(100,100,100, .8);
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
*/
|
|
689
719
|
|
|
690
720
|
</style>
|
|
691
721
|
|
|
692
722
|
<div id="root" class="logo-hidden floating-panel-style bottom">
|
|
693
723
|
<div class="wrapper">
|
|
724
|
+
<div class="options compact-only" part="options">
|
|
725
|
+
</div>
|
|
694
726
|
<div class="foldout">
|
|
695
|
-
<div class="options" part="options">
|
|
727
|
+
<div class="options main-container" part="options">
|
|
696
728
|
<slot></slot>
|
|
697
729
|
</div>
|
|
698
730
|
<div class="options" part="options">
|
|
@@ -723,7 +755,8 @@ export class NeedleMenuElement extends HTMLElement {
|
|
|
723
755
|
this.root = shadow.querySelector("#root") as HTMLDivElement;
|
|
724
756
|
|
|
725
757
|
this.wrapper = this.root?.querySelector(".wrapper") as HTMLDivElement;
|
|
726
|
-
this.options = this.root?.querySelector(".options") as HTMLDivElement;
|
|
758
|
+
this.options = this.root?.querySelector(".options.main-container") as HTMLDivElement;
|
|
759
|
+
this.optionsCompactMode = this.root?.querySelector(".options.compact-only") as HTMLDivElement;
|
|
727
760
|
this.logoContainer = this.root?.querySelector(".logo") as HTMLDivElement;
|
|
728
761
|
this.compactMenuButton = this.root?.querySelector(".compact-menu-button") as HTMLButtonElement;
|
|
729
762
|
this.compactMenuButton.append(getIconElement("more_vert"));
|
|
@@ -838,7 +871,7 @@ export class NeedleMenuElement extends HTMLElement {
|
|
|
838
871
|
connectedCallback() {
|
|
839
872
|
window.addEventListener("resize", this.handleSizeChange);
|
|
840
873
|
this.handleMenuVisible();
|
|
841
|
-
this._sizeChangeInterval = setInterval(() => this.handleSizeChange(undefined,
|
|
874
|
+
this._sizeChangeInterval = setInterval(() => this.handleSizeChange(undefined, false), 5000);
|
|
842
875
|
// the dom element is set after the constructor runs
|
|
843
876
|
setTimeout(() => {
|
|
844
877
|
this._domElement?.addEventListener("resize", this.handleSizeChange);
|
|
@@ -932,6 +965,8 @@ export class NeedleMenuElement extends HTMLElement {
|
|
|
932
965
|
private readonly wrapper: HTMLDivElement;
|
|
933
966
|
/** @private contains the buttons and dynamic elements */
|
|
934
967
|
private readonly options: HTMLDivElement;
|
|
968
|
+
/** @private contains options visible when in compact mode */
|
|
969
|
+
private readonly optionsCompactMode: HTMLDivElement;
|
|
935
970
|
/** @private contains the needle-logo html element */
|
|
936
971
|
private readonly logoContainer: HTMLDivElement;
|
|
937
972
|
/** @private compact menu button element */
|
|
@@ -1039,6 +1074,8 @@ export class NeedleMenuElement extends HTMLElement {
|
|
|
1039
1074
|
}
|
|
1040
1075
|
|
|
1041
1076
|
private _isHandlingChange = false;
|
|
1077
|
+
/** 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. */
|
|
1078
|
+
private _pauseMutationObserverOptionsContainer = false;
|
|
1042
1079
|
|
|
1043
1080
|
/** Called when any change in the web component is detected (including in children and child attributes) */
|
|
1044
1081
|
private onChangeDetected(_mut: MutationRecord[]) {
|
|
@@ -1049,7 +1086,8 @@ export class NeedleMenuElement extends HTMLElement {
|
|
|
1049
1086
|
this.handleMenuVisible();
|
|
1050
1087
|
for (const mut of _mut) {
|
|
1051
1088
|
if (mut.target == this.options) {
|
|
1052
|
-
this.
|
|
1089
|
+
if (!this._pauseMutationObserverOptionsContainer)
|
|
1090
|
+
this.onOptionsChildrenChanged(mut)
|
|
1053
1091
|
}
|
|
1054
1092
|
}
|
|
1055
1093
|
}
|
|
@@ -1134,29 +1172,34 @@ export class NeedleMenuElement extends HTMLElement {
|
|
|
1134
1172
|
|
|
1135
1173
|
|
|
1136
1174
|
private _lastAvailableWidthChange = 0;
|
|
1137
|
-
private
|
|
1175
|
+
private _timeoutHandleSize: number = 0;
|
|
1176
|
+
private _timeoutHandleCompactItems: number = 0;
|
|
1138
1177
|
|
|
1139
1178
|
private handleSizeChange = (_evt?: Event, forceOrEvent?: boolean) => {
|
|
1140
1179
|
if (!this._domElement) return;
|
|
1180
|
+
// if (this._isApplyingSizeUpdate) return;
|
|
1141
1181
|
|
|
1142
1182
|
const width = this._domElement.clientWidth;
|
|
1143
1183
|
if (width < 100) {
|
|
1144
|
-
clearTimeout(this.
|
|
1184
|
+
clearTimeout(this._timeoutHandleSize!);
|
|
1145
1185
|
this.root.classList.add("compact");
|
|
1146
1186
|
this.foldout.classList.add("floating-panel-style");
|
|
1147
1187
|
return;
|
|
1148
1188
|
}
|
|
1149
1189
|
|
|
1150
|
-
const padding =
|
|
1190
|
+
const padding = 10 * 2;
|
|
1151
1191
|
const availableWidth = width - padding;
|
|
1152
1192
|
|
|
1153
1193
|
// if the available width has not changed significantly then we can skip the rest
|
|
1154
1194
|
if (!forceOrEvent && Math.abs(availableWidth - this._lastAvailableWidthChange) < 1) return;
|
|
1155
1195
|
this._lastAvailableWidthChange = availableWidth;
|
|
1156
1196
|
|
|
1157
|
-
clearTimeout(this.
|
|
1197
|
+
clearTimeout(this._timeoutHandleSize!);
|
|
1198
|
+
|
|
1199
|
+
this._timeoutHandleSize = setTimeout(() => {
|
|
1200
|
+
|
|
1201
|
+
// console.warn("APPLY", this.root.classList.contains("compact") ? "COMPACT" : "FULL", "MODE (available width: " + availableWidth.toFixed(0) + "px)", getCurrentWidth());
|
|
1158
1202
|
|
|
1159
|
-
this._timeoutHandle = setTimeout(() => {
|
|
1160
1203
|
const spaceLeft = getSpaceLeft();
|
|
1161
1204
|
if (spaceLeft < 0) {
|
|
1162
1205
|
this.root.classList.add("compact")
|
|
@@ -1171,10 +1214,19 @@ export class NeedleMenuElement extends HTMLElement {
|
|
|
1171
1214
|
this.foldout.classList.add("floating-panel-style");
|
|
1172
1215
|
}
|
|
1173
1216
|
}
|
|
1174
|
-
|
|
1217
|
+
this._pauseMutationObserverOptionsContainer = true;
|
|
1218
|
+
this.updateCompactFoldoutItem();
|
|
1219
|
+
window.requestAnimationFrame(() => this._pauseMutationObserverOptionsContainer = false);
|
|
1220
|
+
|
|
1221
|
+
}, 150) as unknown as number;
|
|
1175
1222
|
|
|
1176
1223
|
const getCurrentWidth = () => {
|
|
1177
|
-
|
|
1224
|
+
let totalWidthRequired = 0;
|
|
1225
|
+
totalWidthRequired += this.options.getBoundingClientRect().width;
|
|
1226
|
+
totalWidthRequired += this.optionsCompactMode.getBoundingClientRect().width;
|
|
1227
|
+
totalWidthRequired += 10 * this.options.childElementCount; // padding
|
|
1228
|
+
totalWidthRequired += this.logoContainer.style.display != "none" ? this.logoContainer.getBoundingClientRect().width : 0;;
|
|
1229
|
+
return totalWidthRequired;
|
|
1178
1230
|
}
|
|
1179
1231
|
|
|
1180
1232
|
let lastSpaceLeft = -1;
|
|
@@ -1188,6 +1240,59 @@ export class NeedleMenuElement extends HTMLElement {
|
|
|
1188
1240
|
}
|
|
1189
1241
|
}
|
|
1190
1242
|
|
|
1243
|
+
private updateCompactFoldoutItem() {
|
|
1244
|
+
|
|
1245
|
+
if (this.root.classList.contains("compact")) {
|
|
1246
|
+
|
|
1247
|
+
// Find items in the folding list with the highest priority
|
|
1248
|
+
// The one with the highest priority will be added to the visible container
|
|
1249
|
+
let priorityItem: HTMLElement | null = null;
|
|
1250
|
+
let priorityValue: number = -10000000;
|
|
1251
|
+
const testItem = (element: ChildNode | null) => {
|
|
1252
|
+
if (element instanceof HTMLElement) {
|
|
1253
|
+
const priority = NeedleMenu.getElementPriority(element);
|
|
1254
|
+
if (priority !== undefined && priority >= priorityValue) {
|
|
1255
|
+
// check if the element is hidden
|
|
1256
|
+
// @TODO: use computed styles
|
|
1257
|
+
const style = window.getComputedStyle(element);
|
|
1258
|
+
if (style.display === "none" || style.visibility === "hidden" || style.opacity === "0") {
|
|
1259
|
+
return;
|
|
1260
|
+
}
|
|
1261
|
+
priorityItem = element;
|
|
1262
|
+
priorityValue = priority;
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
for (let i = 0; i < this.options.children.length; i++) {
|
|
1267
|
+
testItem(this.options.children.item(i));
|
|
1268
|
+
}
|
|
1269
|
+
for (let i = 0; i < this.optionsCompactMode.children.length; i++) {
|
|
1270
|
+
testItem(this.optionsCompactMode.children.item(i));
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
if (priorityItem && !this.optionsCompactMode.contains(priorityItem)) {
|
|
1274
|
+
this.optionsCompactMode.childNodes.forEach(element => {
|
|
1275
|
+
this.options.appendChild(element);
|
|
1276
|
+
});
|
|
1277
|
+
const item = priorityItem;
|
|
1278
|
+
this.optionsCompactMode.appendChild(item);
|
|
1279
|
+
// console.warn("In compact mode, moved item with priority " + priorityValue + " to compact foldout:", item);
|
|
1280
|
+
}
|
|
1281
|
+
else if (!priorityItem) {
|
|
1282
|
+
// console.warn("In compact mode but no item has priority, showing all items in foldout");
|
|
1283
|
+
this.optionsCompactMode.childNodes.forEach(element => {
|
|
1284
|
+
this.options.appendChild(element);
|
|
1285
|
+
});
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
else {
|
|
1289
|
+
// console.warn("Not in compact mode but trying to update compact foldout item");
|
|
1290
|
+
this.optionsCompactMode.childNodes.forEach(element => {
|
|
1291
|
+
this.options.appendChild(element);
|
|
1292
|
+
});
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
// private _foldoutItemVisibleInterval = 0;
|
|
1191
1296
|
|
|
1192
1297
|
|
|
1193
1298
|
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);
|