@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
|
@@ -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
|
-
|
|
276
|
-
|
|
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
|
-
|
|
291
|
-
|
|
292
|
-
|
|
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
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
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
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
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
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
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
|
-
|
|
336
|
-
|
|
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
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
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
|
-
|
|
359
|
-
|
|
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
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
376
|
+
a {
|
|
377
|
+
color: inherit;
|
|
378
|
+
text-decoration: none;
|
|
379
|
+
}
|
|
367
380
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
381
|
+
.options {
|
|
382
|
+
display: flex;
|
|
383
|
+
flex-direction: row;
|
|
384
|
+
align-items: center;
|
|
385
|
+
}
|
|
373
386
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
387
|
+
.options > *, ::slotted(*) {
|
|
388
|
+
max-height: 2.25rem;
|
|
389
|
+
padding: .4rem .5rem;
|
|
390
|
+
}
|
|
378
391
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
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
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
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% {
|
|
429
|
-
|
|
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.
|
|
438
|
-
padding-bottom: .
|
|
439
|
-
margin-right: 0.
|
|
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.
|
|
449
|
-
margin-right: 0.
|
|
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.
|
|
578
|
-
padding: .
|
|
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-
|
|
585
|
-
|
|
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,
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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 =
|
|
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.
|
|
1069
|
-
this.
|
|
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
|
-
|
|
1112
|
+
this._pauseMutationObserverOptionsContainer = true;
|
|
1113
|
+
this.updateCompactFoldoutItem();
|
|
1114
|
+
window.requestAnimationFrame(() => this._pauseMutationObserverOptionsContainer = false);
|
|
1115
|
+
}, 150);
|
|
1085
1116
|
const getCurrentWidth = () => {
|
|
1086
|
-
|
|
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") {
|