@needle-tools/engine 4.4.0-alpha.4 → 4.4.0-beta

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 (67) hide show
  1. package/CHANGELOG.md +19 -1
  2. package/dist/needle-engine.bundle.js +5067 -4828
  3. package/dist/needle-engine.bundle.light.js +5062 -4823
  4. package/dist/needle-engine.bundle.light.min.js +129 -116
  5. package/dist/needle-engine.bundle.light.umd.cjs +125 -112
  6. package/dist/needle-engine.bundle.min.js +129 -116
  7. package/dist/needle-engine.bundle.umd.cjs +127 -114
  8. package/dist/needle-engine.d.ts +9 -9
  9. package/dist/needle-engine.js +91 -90
  10. package/dist/needle-engine.light.d.ts +9 -9
  11. package/dist/needle-engine.light.js +91 -90
  12. package/dist/needle-engine.light.min.js +1 -1
  13. package/dist/needle-engine.light.umd.cjs +1 -1
  14. package/dist/needle-engine.min.js +1 -1
  15. package/dist/needle-engine.umd.cjs +1 -1
  16. package/dist/three.js +1 -0
  17. package/dist/three.light.js +1 -0
  18. package/dist/three.light.min.js +16 -16
  19. package/dist/three.light.umd.cjs +15 -15
  20. package/dist/three.min.js +16 -16
  21. package/dist/three.umd.cjs +15 -15
  22. package/lib/engine/engine_input.d.ts +7 -0
  23. package/lib/engine/engine_input.js +12 -0
  24. package/lib/engine/engine_input.js.map +1 -1
  25. package/lib/engine/engine_serialization_decorator.js +4 -0
  26. package/lib/engine/engine_serialization_decorator.js.map +1 -1
  27. package/lib/engine/engine_three_utils.d.ts +26 -1
  28. package/lib/engine/engine_three_utils.js +43 -0
  29. package/lib/engine/engine_three_utils.js.map +1 -1
  30. package/lib/engine/engine_utils_format.js +11 -5
  31. package/lib/engine/engine_utils_format.js.map +1 -1
  32. package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +1 -0
  33. package/lib/engine/webcomponents/needle menu/needle-menu.js +3 -3
  34. package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
  35. package/lib/engine-components/Camera.d.ts +2 -2
  36. package/lib/engine-components/Camera.js +4 -6
  37. package/lib/engine-components/Camera.js.map +1 -1
  38. package/lib/engine-components/CameraUtils.js +32 -13
  39. package/lib/engine-components/CameraUtils.js.map +1 -1
  40. package/lib/engine-components/Component.d.ts +4 -1
  41. package/lib/engine-components/Component.js +4 -1
  42. package/lib/engine-components/Component.js.map +1 -1
  43. package/lib/engine-components/ContactShadows.js +8 -2
  44. package/lib/engine-components/ContactShadows.js.map +1 -1
  45. package/lib/engine-components/OrbitControls.d.ts +3 -0
  46. package/lib/engine-components/OrbitControls.js +34 -10
  47. package/lib/engine-components/OrbitControls.js.map +1 -1
  48. package/lib/engine-components/SyncedTransform.d.ts +3 -2
  49. package/lib/engine-components/SyncedTransform.js +28 -18
  50. package/lib/engine-components/SyncedTransform.js.map +1 -1
  51. package/lib/engine-components/utils/EnvironmentScene.d.ts +5 -0
  52. package/lib/engine-components/utils/EnvironmentScene.js +206 -0
  53. package/lib/engine-components/utils/EnvironmentScene.js.map +1 -0
  54. package/package.json +1 -1
  55. package/src/engine/engine_input.ts +13 -0
  56. package/src/engine/engine_serialization_decorator.ts +4 -0
  57. package/src/engine/engine_three_utils.ts +52 -1
  58. package/src/engine/engine_utils_format.ts +13 -5
  59. package/src/engine/webcomponents/needle menu/needle-menu.ts +3 -3
  60. package/src/engine-components/Camera.ts +9 -6
  61. package/src/engine-components/CameraUtils.ts +35 -13
  62. package/src/engine-components/Component.ts +5 -2
  63. package/src/engine-components/ContactShadows.ts +8 -2
  64. package/src/engine-components/OrbitControls.ts +35 -10
  65. package/src/engine-components/SyncedTransform.ts +37 -25
  66. package/src/engine-components/utils/EnvironmentScene.ts +246 -0
  67. package/src/engine/webcomponents/needle menu/dist/needle-menu.js +0 -662
@@ -1,662 +0,0 @@
1
- "use strict";
2
- var __extends = (this && this.__extends) || (function () {
3
- var extendStatics = function (d, b) {
4
- extendStatics = Object.setPrototypeOf ||
5
- ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6
- function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
7
- return extendStatics(d, b);
8
- };
9
- return function (d, b) {
10
- extendStatics(d, b);
11
- function __() { this.constructor = d; }
12
- d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
13
- };
14
- })();
15
- var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, privateMap) {
16
- if (!privateMap.has(receiver)) {
17
- throw new TypeError("attempted to get private field on non-instance");
18
- }
19
- return privateMap.get(receiver);
20
- };
21
- var _onClick;
22
- exports.__esModule = true;
23
- exports.NeedleMenuElement = exports.NeedleMenu = void 0;
24
- var engine_license_js_1 = require("../../engine_license.js");
25
- var engine_networking_utils_js_1 = require("../../engine_networking_utils.js");
26
- var engine_utils_js_1 = require("../../engine_utils.js");
27
- var events_js_1 = require("../../xr/events.js");
28
- var buttons_js_1 = require("../buttons.js");
29
- var fonts_js_1 = require("../fonts.js");
30
- var icons_js_1 = require("../icons.js");
31
- var logo_element_js_1 = require("../logo-element.js");
32
- var needle_menu_spatial_js_1 = require("./needle-menu-spatial.js");
33
- var elementName = "needle-menu";
34
- var debug = engine_utils_js_1.getParam("debugmenu");
35
- var debugNonCommercial = engine_utils_js_1.getParam("debugnoncommercial");
36
- /**
37
- * The NeedleMenu is a menu that can be displayed in the needle engine webcomponent or in VR/AR sessions.
38
- * The menu can be used to add buttons to the needle engine that can be used to interact with the application.
39
- * The menu can be positioned at the top or the bottom of the needle engine webcomponent
40
- *
41
- * @example Create a button using the NeedleMenu
42
- * ```typescript
43
- * onStart(ctx => {
44
- * ctx.menu.appendChild({
45
- * label: "Open Google",
46
- * icon: "google",
47
- * onClick: () => { window.open("https://www.google.com", "_blank") }
48
- * });
49
- * })
50
- * ```
51
- *
52
- * Buttons can be added to the menu using the {@link NeedleMenu#appendChild} method or by sending a postMessage event to the needle engine with the type "needle:menu". Use the {@link NeedleMenuPostMessageModel} model to create buttons with postMessage.
53
- * @example Create a button using a postmessage
54
- * ```javascript
55
- * window.postMessage({
56
- * type: "needle:menu",
57
- * button: {
58
- * label: "Open Google",
59
- * icon: "google",
60
- * onclick: "https://www.google.com",
61
- * target: "_blank",
62
- * }
63
- * }, "*");
64
- * ```
65
- */
66
- var NeedleMenu = /** @class */ (function () {
67
- function NeedleMenu(context) {
68
- var _this = this;
69
- this.onPostMessage = function (e) {
70
- // lets just allow the same origin for now
71
- if (e.origin !== globalThis.location.origin)
72
- return;
73
- if (typeof e.data === "object") {
74
- var data = e.data;
75
- var type = data.type;
76
- if (type === "needle:menu") {
77
- var buttoninfo_1 = data.button;
78
- if (buttoninfo_1) {
79
- if (!buttoninfo_1.label)
80
- return console.error("NeedleMenu: buttoninfo.label is required");
81
- if (!buttoninfo_1.onclick)
82
- return console.error("NeedleMenu: buttoninfo.onclick is required");
83
- var button = document.createElement("button");
84
- button.textContent = buttoninfo_1.label;
85
- if (buttoninfo_1.icon) {
86
- var icon = icons_js_1.getIconElement(buttoninfo_1.icon);
87
- button.prepend(icon);
88
- }
89
- if (buttoninfo_1.priority) {
90
- button.setAttribute("priority", buttoninfo_1.priority.toString());
91
- }
92
- button.onclick = function () {
93
- if (buttoninfo_1.onclick) {
94
- var isLink = buttoninfo_1.onclick.startsWith("http") || buttoninfo_1.onclick.startsWith("www.");
95
- var target = buttoninfo_1.target || "_blank";
96
- if (isLink) {
97
- globalThis.open(buttoninfo_1.onclick, target);
98
- }
99
- else
100
- console.error("NeedleMenu: onclick is not a valid link", buttoninfo_1.onclick);
101
- }
102
- };
103
- _this._menu.appendChild(button);
104
- }
105
- else if (debug)
106
- console.error("NeedleMenu: unknown postMessage event", data);
107
- }
108
- else if (debug)
109
- console.warn("NeedleMenu: unknown postMessage type", type, data);
110
- }
111
- };
112
- this.onStartXR = function (args) {
113
- if (args.session.isScreenBasedAR) {
114
- _this._menu["previousParent"] = _this._menu.parentNode;
115
- _this._context.arOverlayElement.appendChild(_this._menu);
116
- args.session.session.addEventListener("end", _this.onExitXR);
117
- }
118
- };
119
- this.onExitXR = function () {
120
- if (_this._menu["previousParent"]) {
121
- _this._menu["previousParent"].appendChild(_this._menu);
122
- delete _this._menu["previousParent"];
123
- }
124
- };
125
- this._menu = NeedleMenuElement.getOrCreate(context.domElement, context);
126
- this._context = context;
127
- this._spatialMenu = new needle_menu_spatial_js_1.NeedleSpatialMenu(context, this._menu);
128
- window.addEventListener("message", this.onPostMessage);
129
- events_js_1.onXRSessionStart(this.onStartXR);
130
- }
131
- /** @ignore internal method */
132
- NeedleMenu.prototype.onDestroy = function () {
133
- window.removeEventListener("message", this.onPostMessage);
134
- this._menu.remove();
135
- this._spatialMenu.onDestroy();
136
- };
137
- /** Experimental: Change the menu position to be at the top or the bottom of the needle engine webcomponent
138
- * @param position "top" or "bottom"
139
- */
140
- NeedleMenu.prototype.setPosition = function (position) {
141
- this._menu.setPosition(position);
142
- };
143
- /**
144
- * Call to show or hide the menu.
145
- * NOTE: Hiding the menu is a PRO feature and requires a needle engine license. Hiding the menu will not work in production without a license.
146
- */
147
- NeedleMenu.prototype.setVisible = function (visible) {
148
- this._menu.setVisible(visible);
149
- };
150
- /** When set to false, the Needle Engine logo will be hidden. Hiding the logo requires a needle engine license */
151
- NeedleMenu.prototype.showNeedleLogo = function (visible) {
152
- var _a;
153
- this._menu.showNeedleLogo(visible);
154
- (_a = this._spatialMenu) === null || _a === void 0 ? void 0 : _a.showNeedleLogo(visible);
155
- // setTimeout(()=>this.showNeedleLogo(!visible), 1000);
156
- };
157
- /** When enabled=true the menu will be visible in VR/AR sessions */
158
- NeedleMenu.prototype.showSpatialMenu = function (enabled) {
159
- this._spatialMenu.setEnabled(enabled);
160
- };
161
- /**
162
- * Call to add or remove a button to the menu to show a QR code for the current page
163
- * If enabled=true then a button will be added to the menu that will show a QR code for the current page when clicked.
164
- */
165
- NeedleMenu.prototype.showQRCodeButton = function (enabled) {
166
- if (enabled === "desktop-only") {
167
- enabled = !engine_utils_js_1.isMobileDevice();
168
- }
169
- if (!enabled) {
170
- var button = buttons_js_1.ButtonsFactory.getOrCreate().qrButton;
171
- if (button)
172
- button.style.display = "none";
173
- return button !== null && button !== void 0 ? button : null;
174
- }
175
- else {
176
- var button = buttons_js_1.ButtonsFactory.getOrCreate().createQRCode();
177
- button.style.display = "";
178
- this._menu.appendChild(button);
179
- return button;
180
- }
181
- };
182
- /** Call to add or remove a button to the menu to mute or unmute the application
183
- * Clicking the button will mute or unmute the application
184
- */
185
- NeedleMenu.prototype.showAudioPlaybackOption = function (visible) {
186
- var _a;
187
- if (!visible) {
188
- (_a = this._muteButton) === null || _a === void 0 ? void 0 : _a.remove();
189
- return;
190
- }
191
- this._muteButton = buttons_js_1.ButtonsFactory.getOrCreate().createMuteButton(this._context);
192
- this._muteButton.setAttribute("priority", "100");
193
- this._menu.appendChild(this._muteButton);
194
- };
195
- NeedleMenu.prototype.showFullscreenOption = function (visible) {
196
- var _a;
197
- if (!visible) {
198
- (_a = this._fullscreenButton) === null || _a === void 0 ? void 0 : _a.remove();
199
- return;
200
- }
201
- this._fullscreenButton = buttons_js_1.ButtonsFactory.getOrCreate().createFullscreenButton(this._context);
202
- if (this._fullscreenButton) {
203
- this._fullscreenButton.setAttribute("priority", "150");
204
- this._menu.appendChild(this._fullscreenButton);
205
- }
206
- };
207
- NeedleMenu.prototype.appendChild = function (child) {
208
- return this._menu.appendChild(child);
209
- };
210
- return NeedleMenu;
211
- }());
212
- exports.NeedleMenu = NeedleMenu;
213
- var NeedleMenuElement = /** @class */ (function (_super) {
214
- __extends(NeedleMenuElement, _super);
215
- function NeedleMenuElement() {
216
- var _a, _b, _c, _d, _e, _f;
217
- var _this = _super.call(this) || this;
218
- _this._domElement = null;
219
- _this._context = null;
220
- _onClick.set(_this, function (e) {
221
- // detect a click outside the opened foldout to automatically close it
222
- if (!e.defaultPrevented
223
- && e.target == _this._domElement
224
- && (e instanceof PointerEvent && e.button === 0)
225
- && _this.root.classList.contains("open")) {
226
- // The menu is open, it's a click outside the foldout?
227
- var rect = _this.foldout.getBoundingClientRect();
228
- var pointerEvent = e;
229
- if (!(pointerEvent.clientX > rect.left
230
- && pointerEvent.clientX < rect.right
231
- && pointerEvent.clientY > rect.top
232
- && pointerEvent.clientY < rect.bottom)) {
233
- _this.root.classList.toggle("open", false);
234
- }
235
- }
236
- });
237
- _this._userRequestedLogoVisible = undefined;
238
- _this._userRequestedMenuVisible = undefined;
239
- _this._isHandlingChange = false;
240
- _this._didSort = new Map();
241
- _this._lastAvailableWidthChange = 0;
242
- _this._timeoutHandle = 0;
243
- _this.handleSizeChange = function (_evt, forceOrEvent) {
244
- if (!_this._domElement)
245
- return;
246
- var width = _this._domElement.clientWidth;
247
- if (width < 500) {
248
- clearTimeout(_this._timeoutHandle);
249
- _this.root.classList.add("compact");
250
- _this.foldout.classList.add("floating-panel-style");
251
- return;
252
- }
253
- var padding = 20 * 2;
254
- var availableWidth = width - padding;
255
- // if the available width has not changed significantly then we can skip the rest
256
- if (!forceOrEvent && Math.abs(availableWidth - _this._lastAvailableWidthChange) < 1)
257
- return;
258
- _this._lastAvailableWidthChange = availableWidth;
259
- clearTimeout(_this._timeoutHandle);
260
- _this._timeoutHandle = setTimeout(function () {
261
- var spaceLeft = getSpaceLeft();
262
- if (spaceLeft < 0) {
263
- _this.root.classList.add("compact");
264
- _this.foldout.classList.add("floating-panel-style");
265
- }
266
- else if (spaceLeft > 0) {
267
- _this.root.classList.remove("compact");
268
- _this.foldout.classList.remove("floating-panel-style");
269
- if (getSpaceLeft() < 0) {
270
- // ensure we still have enough space left
271
- _this.root.classList.add("compact");
272
- _this.foldout.classList.add("floating-panel-style");
273
- }
274
- }
275
- }, 5);
276
- var getCurrentWidth = function () {
277
- return _this.options.clientWidth + _this.logoContainer.clientWidth;
278
- };
279
- var getSpaceLeft = function () {
280
- return availableWidth - getCurrentWidth();
281
- };
282
- };
283
- var template = document.createElement('template');
284
- // TODO: make host full size again and move the buttons to a wrapper so that we can later easily open e.g. foldouts/dropdowns / use the whole canvas space
285
- template.innerHTML = "<style>\n\n #root {\n position: absolute;\n width: auto;\n max-width: 95%;\n left: 50%;\n transform: translateX(-50%);\n top: min(20px, 10vh);\n padding: 0.3rem;\n display: flex;\n visibility: visible;\n flex-direction: row-reverse; /* if we overflow this should be right aligned so the logo is always visible */\n pointer-events: all;\n z-index: 1000;\n }\n\n /** hide the menu if it's empty **/\n #root.has-no-options.logo-hidden {\n display: none; \n }\n\n /** using a div here because then we can change the class for placement **/\n #root.bottom {\n top: auto;\n bottom: min(30px, 10vh);\n }\n \n .wrapper {\n position: relative;\n display: flex;\n flex-direction: row;\n justify-content: center;\n align-items: stretch;\n gap: 0px;\n padding: 0 .3rem;\n }\n\n .wrapper > *, .options > button, ::slotted(*) {\n position: relative;\n border: none;\n border-radius: 0;\n outline: 1px solid rgba(0,0,0,0);\n display: flex;\n justify-content: center;\n align-items: center;\n max-height: 2.3rem;\n\n /** basic font settings for all entries **/\n font-size: 1rem;\n font-family: 'Roboto Flex', sans-serif;\n font-optical-sizing: auto;\n font-weight: 500;\n font-weight: 200;\n font-variation-settings: \"wdth\" 100;\n color: rgb(20,20,20);\n }\n\n .floating-panel-style {\n background: rgba(255, 255, 255, .4);\n outline: rgb(0 0 0 / 5%) 1px solid;\n border: 1px solid rgba(255, 255, 255, .1);\n box-shadow: 0px 7px 0.5rem 0px rgb(0 0 0 / 6%), inset 0px 0px 1.3rem rgba(0,0,0,.05);\n border-radius: 1.1999999999999993rem;\n /** \n * to make nested background filter work \n * https://stackoverflow.com/questions/60997948/backdrop-filter-not-working-for-nested-elements-in-chrome \n **/\n &::before {\n content: '';\n position: absolute;\n width: 100%;\n height: 100%;\n top: 0;\n left: 0;\n z-index: -1;\n border-radius: 1.1999999999999993rem;\n -webkit-backdrop-filter: blur(8px);\n backdrop-filter: blur(8px);\n }\n }\n\n a {\n color: inherit;\n text-decoration: none;\n }\n\n .options {\n display: flex;\n flex-direction: row;\n }\n\n .options > button, ::slotted(button) {\n height: 2.25rem;\n padding: .4rem .5rem;\n }\n \n :host .options > button, ::slotted(*) {\n background: transparent;\n border: none;\n white-space: nowrap;\n transition: all 0.1s linear .02s;\n border-radius: 0.8rem;\n user-select: none;\n }\n :host .options > *:hover, ::slotted(*:hover) {\n cursor: pointer;\n color: black;\n background: rgba(245, 245, 245, .8);\n box-shadow: inset 0 0 1rem rgba(0,0,30,.2);\n outline: rgba(0,0,0,.1) 1px solid;\n }\n :host .options > *:active, ::slotted(*:active) {\n background: rgba(255, 255, 255, .8);\n 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);\n transition: all 0.05s linear;\n }\n :host .options > *:focus, ::slotted(*:focus) {\n outline: rgba(255,255,255,.5) 1px solid;\n }\n\n :host .options > *:disabled, ::slotted(*:disabled) {\n background: rgba(0,0,0,.05);\n color: rgba(60,60,60,.7);\n pointer-events: none;\n }\n\n button, ::slotted(button) {\n gap: 0.3rem;\n }\n\n /** XR button animation **/\n :host button.this-mode-is-requested {\n background: repeating-linear-gradient(to right, #fff 0%, #fff 40%, #aaffff 55%, #fff 80%);\n background-size: 200% auto;\n background-position: 0 100%;\n animation: AnimationName .7s ease infinite forwards;\n }\n :host button.other-mode-is-requested {\n opacity: .5;\n }\n \n @keyframes AnimationName {\n 0% { background-position: 0% 0 }\n 100% { background-position: -200% 0 }\n }\n\n\n\n\n .logo {\n cursor: pointer;\n padding-left: 0.6rem;\n }\n .logo-hidden {\n .logo {\n display: none;\n }\n }\n :host .has-options .logo {\n border-left: 1px solid rgba(40,40,40,.4);\n margin-left: 0.3rem;\n }\n\n .logo > span {\n white-space: nowrap;\n }\n\n\n\n /** COMPACT */\n\n /** Hide the menu button normally **/\n .compact-menu-button { display: none; }\n /** And show it when we're in compact mode **/\n .compact .compact-menu-button {\n display: block;\n background: none;\n border: none;\n border-radius: 2rem;\n\n margin: 0;\n padding: 0 .3rem;\n padding-top: .2rem;\n\n color: #000;\n\n &:hover {\n background: rgba(255,255,255,.2);\n cursor: pointer;\n }\n &:focus {\n outline: 1px solid rgba(255,255,255,.5);\n }\n } \n .has-no-options .compact-menu-button {\n display: none;\n }\n .open .compact-menu-button {\n background: rgba(255,255,255,.2);\n }\n .logo-visible .compact-menu-button { \n margin-left: .2rem;\n }\n \n /** Open and hide menu **/\n .compact .foldout { \n display: none;\n }\n .open .options, .open .foldout {\n display: flex;\n }\n .compact .wrapper {\n padding: 0;\n }\n .compact .wrapper, .compact .options {\n height: auto;\n max-height: initial;\n flex-direction: row;\n gap: .12rem;\n }\n .compact .options { \n flex-wrap: wrap;\n gap: .3rem;\n }\n .compact .top .options {\n height: auto;\n flex-direction: row;\n }\n .compact .bottom .wrapper {\n height: auto;\n flex-direction: column;\n }\n\n .compact .foldout {\n max-height: min(100ch, calc(100vh - 100px));\n overflow: auto;\n overflow-x: hidden;\n align-items: center;\n\n position: fixed;\n bottom: calc(100% + 5px);\n z-index: 100;\n width: auto;\n left: .2rem;\n right: .2rem;\n padding: .2rem;\n\n }\n .compact.logo-hidden .foldout {\n /** for when there's no logo we want to center the foldout **/\n min-width: 24ch;\n margin-left: 50%;\n transform: translateX(calc(-50% - .2rem));\n }\n \n .compact.top .foldout {\n top: calc(100% + 5px);\n bottom: auto;\n }\n\n ::-webkit-scrollbar {\n max-width: 7px;\n background: rgba(100,100,100,.2);\n border-radius: .2rem;\n }\n ::-webkit-scrollbar-thumb {\n background: rgba(255, 255, 255, .3);\n border-radius: .2rem;\n }\n ::-webkit-scrollbar-thumb:hover {\n background: rgb(150,150,150);\n }\n\n .compact .options > * {\n font-size: 1.2rem;\n padding: .6rem .5rem;\n }\n .compact.has-options .logo {\n border: none;\n padding-left: 0;\n margin-left: 1rem;\n margin-bottom: .02rem;\n }\n .compact .options > button {\n display: flex;\n flex-basis: 100%;\n min-height: 3rem;\n }\n .compact .options > button.row2 {\n //border: 1px solid red !important;\n display: flex;\n flex: 1;\n flex-basis: 30%;\n }\n\n /** If there's really not enough space then just hide all options **/\n @media (max-width: 100px) or (max-height: 100px){\n .foldout {\n display: none !important;\n }\n .compact-menu-button {\n display: none !important;\n }\n }\n \n /* dark mode */\n /*\n @media (prefers-color-scheme: dark) {\n :host {\n background: rgba(0,0,0, .6);\n }\n :host button {\n color: rgba(200,200,200);\n }\n :host button:hover {\n background: rgba(100,100,100, .8);\n }\n }\n */\n\n </style>\n \n <div id=\"root\" class=\"logo-visible floating-panel-style bottom\">\n <div class=\"wrapper\">\n <div class=\"foldout\">\n <div class=\"options\" part=\"options\">\n <slot></slot>\n </div>\n <div class=\"options\" part=\"options\">\n <slot name=\"end\"></slot>\n </div>\n </div>\n <div style=\"user-select:none\" class=\"logo\">\n <span class=\"madewith notranslate\">powered by</span>\n </div>\n </div>\n <button class=\"compact-menu-button\"></button>\n </div>\n ";
286
- // we dont need to expose the shadow root
287
- var shadow = _this.attachShadow({ mode: 'open' });
288
- // we need to add the icons to both the shadow dom as well as the HEAD to work
289
- // https://github.com/google/material-design-icons/issues/1165
290
- fonts_js_1.ensureFonts();
291
- fonts_js_1.loadFont(fonts_js_1.iconFontUrl, { loadedCallback: function () { _this.handleSizeChange(); } });
292
- fonts_js_1.loadFont(fonts_js_1.iconFontUrl, { element: shadow });
293
- var content = template.content.cloneNode(true);
294
- shadow === null || shadow === void 0 ? void 0 : shadow.appendChild(content);
295
- _this.root = shadow.querySelector("#root");
296
- _this.wrapper = (_a = _this.root) === null || _a === void 0 ? void 0 : _a.querySelector(".wrapper");
297
- _this.options = (_b = _this.root) === null || _b === void 0 ? void 0 : _b.querySelector(".options");
298
- _this.logoContainer = (_c = _this.root) === null || _c === void 0 ? void 0 : _c.querySelector(".logo");
299
- _this.compactMenuButton = (_d = _this.root) === null || _d === void 0 ? void 0 : _d.querySelector(".compact-menu-button");
300
- _this.compactMenuButton.append(icons_js_1.getIconElement("more_vert"));
301
- _this.foldout = (_e = _this.root) === null || _e === void 0 ? void 0 : _e.querySelector(".foldout");
302
- (_f = _this.root) === null || _f === void 0 ? void 0 : _f.appendChild(_this.wrapper);
303
- _this.wrapper.classList.add("wrapper");
304
- var logo = logo_element_js_1.NeedleLogoElement.create();
305
- logo.style.minHeight = "1rem";
306
- _this.logoContainer.append(logo);
307
- _this.logoContainer.addEventListener("click", function () {
308
- globalThis.open("https://needle.tools", "_blank");
309
- });
310
- // if the user has a license then we CAN hide the needle logo
311
- engine_license_js_1.onLicenseCheckResultChanged(function (res) {
312
- if (res == true && engine_license_js_1.hasCommercialLicense() && !debugNonCommercial) {
313
- var visible = _this._userRequestedLogoVisible;
314
- if (visible === undefined)
315
- visible = false;
316
- _this..call(_this, visible);
317
- }
318
- });
319
- _this.compactMenuButton.addEventListener("click", function (evt) {
320
- evt.preventDefault();
321
- _this.root.classList.toggle("open");
322
- });
323
- var context = _this._context;
324
- // we need to assign it in the timeout because the reference is set *after* the constructor did run
325
- setTimeout(function () { return context = _this._context; });
326
- // watch changes
327
- var changeEventCounter = 0;
328
- var forceVisible = function (parent, visible) {
329
- var _a, _b, _c;
330
- if (debug)
331
- console.log("Set menu visible", visible);
332
- if ((context === null || context === void 0 ? void 0 : context.isInAR) && context.arOverlayElement) {
333
- if (parent != context.arOverlayElement) {
334
- context.arOverlayElement.appendChild(_this);
335
- }
336
- }
337
- else if (_this.parentNode != ((_a = _this._domElement) === null || _a === void 0 ? void 0 : _a.shadowRoot))
338
- (_c = (_b = _this._domElement) === null || _b === void 0 ? void 0 : _b.shadowRoot) === null || _c === void 0 ? void 0 : _c.appendChild(_this);
339
- _this.style.display = visible ? "flex" : "none";
340
- _this.style.visibility = "visible";
341
- _this.style.opacity = "1";
342
- };
343
- var isHandlingMutation = false;
344
- var rootObserver = new MutationObserver(function (mutations) {
345
- var _a;
346
- if (isHandlingMutation) {
347
- return;
348
- }
349
- try {
350
- isHandlingMutation = true;
351
- _this.onChangeDetected(mutations);
352
- // ensure the menu is not hidden or removed
353
- var requiredParent_1 = _this === null || _this === void 0 ? void 0 : _this.parentNode;
354
- if (_this.style.display != "flex" || _this.style.visibility != "visible" || _this.style.opacity != "1" || requiredParent_1 != ((_a = _this._domElement) === null || _a === void 0 ? void 0 : _a.shadowRoot)) {
355
- if (!engine_license_js_1.hasCommercialLicense()) {
356
- var change = changeEventCounter++;
357
- // if a user doesn't have a local pro license *but* for development the menu is hidden then we show a warning
358
- if (engine_networking_utils_js_1.isLocalNetwork() && _this._userRequestedMenuVisible === false) {
359
- // set visible once so that the check above is not triggered again
360
- if (change === 0) {
361
- // if the user requested visible to false before this code is called for the first time we want to respect the choice just in this case
362
- forceVisible(requiredParent_1, _this._userRequestedMenuVisible);
363
- }
364
- // warn only once
365
- if (change === 1) {
366
- console.warn("Needle Menu Warning: You need a PRO license to hide the Needle Engine menu \u2192 The menu will be visible in your deployed website if you don't have a PRO license. See https://needle.tools/pricing for details.");
367
- }
368
- }
369
- else {
370
- if (change === 0) {
371
- forceVisible(requiredParent_1, true);
372
- }
373
- else {
374
- setTimeout(function () { return forceVisible(requiredParent_1, true); }, 5);
375
- }
376
- }
377
- }
378
- }
379
- }
380
- finally {
381
- isHandlingMutation = false;
382
- }
383
- });
384
- rootObserver.observe(_this.root, { childList: true, subtree: true, attributes: true });
385
- if (debug) {
386
- _this.___insertDebugOptions();
387
- }
388
- return _this;
389
- }
390
- NeedleMenuElement.create = function () {
391
- // https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement#is
392
- return document.createElement(elementName, { is: elementName });
393
- };
394
- NeedleMenuElement.getOrCreate = function (domElement, context) {
395
- var element = domElement.querySelector(elementName);
396
- if (!element && domElement.shadowRoot) {
397
- element = domElement.shadowRoot.querySelector(elementName);
398
- }
399
- if (!element) {
400
- element = NeedleMenuElement.create();
401
- if (domElement.shadowRoot)
402
- domElement.shadowRoot.appendChild(element);
403
- else
404
- domElement.appendChild(element);
405
- }
406
- element._domElement = domElement;
407
- element._context = context;
408
- return element;
409
- };
410
- NeedleMenuElement.prototype.connectedCallback = function () {
411
- var _this = this;
412
- window.addEventListener("resize", this.handleSizeChange);
413
- this.handleMenuVisible();
414
- this._sizeChangeInterval = setInterval(function () { return _this.handleSizeChange(undefined, true); }, 5000);
415
- // the dom element is set after the constructor runs
416
- setTimeout(function () {
417
- var _a, _b;
418
- (_a = _this._domElement) === null || _a === void 0 ? void 0 : _a.addEventListener("resize", _this.handleSizeChange);
419
- (_b = _this._domElement) === null || _b === void 0 ? void 0 : _b.addEventListener("click", __classPrivateFieldGet(_this, _onClick));
420
- }, 1);
421
- };
422
- NeedleMenuElement.prototype.disconnectedCallback = function () {
423
- var _a, _b;
424
- window.removeEventListener("resize", this.handleSizeChange);
425
- clearInterval(this._sizeChangeInterval);
426
- (_a = this._domElement) === null || _a === void 0 ? void 0 : _a.removeEventListener("resize", this.handleSizeChange);
427
- (_b = this._context) === null || _b === void 0 ? void 0 : _b.domElement.removeEventListener("click", __classPrivateFieldGet(this, _onClick));
428
- };
429
- NeedleMenuElement.prototype.showNeedleLogo = function (visible) {
430
- this._userRequestedLogoVisible = visible;
431
- if (!visible) {
432
- if (!engine_license_js_1.hasCommercialLicense() || debugNonCommercial) {
433
- console.warn("Needle Menu: You need a PRO license to hide the Needle Engine logo.");
434
- var localNetwork = engine_networking_utils_js_1.isLocalNetwork();
435
- if (!localNetwork)
436
- return;
437
- }
438
- }
439
- this..call(this, visible);
440
- };
441
- NeedleMenuElement.prototype. = function (visible) {
442
- this.logoContainer.style.display = "";
443
- this.logoContainer.style.opacity = "1";
444
- this.logoContainer.style.visibility = "visible";
445
- if (visible) {
446
- this.root.classList.remove("logo-hidden");
447
- this.root.classList.add("logo-visible");
448
- }
449
- else {
450
- this.root.classList.remove("logo-visible");
451
- this.root.classList.add("logo-hidden");
452
- }
453
- };
454
- NeedleMenuElement.prototype.setPosition = function (position) {
455
- // ensure the position is of a known type:
456
- if (position !== "top" && position !== "bottom") {
457
- return console.error("NeedleMenu.setPosition: invalid position", position);
458
- }
459
- this.root.classList.remove("top", "bottom");
460
- this.root.classList.add(position);
461
- };
462
- NeedleMenuElement.prototype.setVisible = function (visible) {
463
- this._userRequestedMenuVisible = visible;
464
- this.style.display = visible ? "flex" : "none";
465
- };
466
- NeedleMenuElement.prototype.append = function () {
467
- var nodes = [];
468
- for (var _i = 0; _i < arguments.length; _i++) {
469
- nodes[_i] = arguments[_i];
470
- }
471
- for (var _a = 0, nodes_1 = nodes; _a < nodes_1.length; _a++) {
472
- var node = nodes_1[_a];
473
- if (typeof node === "string") {
474
- var element = document.createTextNode(node);
475
- this.options.appendChild(element);
476
- }
477
- else {
478
- this.options.appendChild(node);
479
- }
480
- }
481
- };
482
- NeedleMenuElement.prototype.appendChild = function (node) {
483
- var _a, _b;
484
- if (!(node instanceof Node)) {
485
- var button = document.createElement("button");
486
- button.textContent = node.label;
487
- button.onclick = node.onClick;
488
- button.setAttribute("priority", (_b = (_a = node.priority) === null || _a === void 0 ? void 0 : _a.toString()) !== null && _b !== void 0 ? _b : "0");
489
- if (node.icon) {
490
- var icon = icons_js_1.getIconElement(node.icon);
491
- if (node.iconSide === "right") {
492
- button.appendChild(icon);
493
- }
494
- else {
495
- button.prepend(icon);
496
- }
497
- }
498
- if (node["class"]) {
499
- button.classList.add(node["class"]);
500
- }
501
- node = button;
502
- }
503
- var res = this.options.appendChild(node);
504
- return res;
505
- };
506
- NeedleMenuElement.prototype.prepend = function () {
507
- var nodes = [];
508
- for (var _i = 0; _i < arguments.length; _i++) {
509
- nodes[_i] = arguments[_i];
510
- }
511
- for (var _a = 0, nodes_2 = nodes; _a < nodes_2.length; _a++) {
512
- var node = nodes_2[_a];
513
- if (typeof node === "string") {
514
- var element = document.createTextNode(node);
515
- this.options.prepend(element);
516
- }
517
- else {
518
- this.options.prepend(node);
519
- }
520
- }
521
- };
522
- /** Called when any change in the web component is detected (including in children and child attributes) */
523
- NeedleMenuElement.prototype.onChangeDetected = function (_mut) {
524
- if (this._isHandlingChange)
525
- return;
526
- this._isHandlingChange = true;
527
- try {
528
- // if (debug) console.log("NeedleMenu.onChangeDetected", _mut);
529
- this.handleMenuVisible();
530
- for (var _i = 0, _mut_1 = _mut; _i < _mut_1.length; _i++) {
531
- var mut = _mut_1[_i];
532
- if (mut.target == this.options) {
533
- this.onOptionsChildrenChanged(mut);
534
- }
535
- }
536
- }
537
- finally {
538
- this._isHandlingChange = false;
539
- }
540
- };
541
- NeedleMenuElement.prototype.onOptionsChildrenChanged = function (_mut) {
542
- this.root.classList.toggle("has-options", this.hasAnyVisibleOptions);
543
- this.root.classList.toggle("has-no-options", !this.hasAnyVisibleOptions);
544
- this.handleSizeChange(undefined, true);
545
- if (_mut.type === "childList") {
546
- var needsSorting = false;
547
- var now = Date.now();
548
- // sort children by priority only when necessary
549
- for (var i = 0; i < _mut.addedNodes.length; i++) {
550
- var child = _mut.addedNodes[i];
551
- var lastTime = this._didSort.get(child);
552
- if (typeof lastTime === "number" && now - lastTime < 100)
553
- continue;
554
- this._didSort.set(child, now);
555
- needsSorting = true;
556
- }
557
- if (needsSorting) {
558
- var children = Array.from(this.options.children);
559
- children.sort(function (a, b) {
560
- var p1 = parseInt(a.getAttribute("priority") || "0");
561
- var p2 = parseInt(b.getAttribute("priority") || "0");
562
- return p1 - p2;
563
- });
564
- for (var _i = 0, children_1 = children; _i < children_1.length; _i++) {
565
- var child = children_1[_i];
566
- this.options.appendChild(child);
567
- }
568
- }
569
- }
570
- };
571
- /** checks if the menu has any content and should be rendered at all
572
- * if we dont have any content and logo then we hide the menu
573
- */
574
- NeedleMenuElement.prototype.handleMenuVisible = function () {
575
- if (debug)
576
- console.log("Update VisibleState: Any Content?", this.hasAnyContent);
577
- if (this.hasAnyContent) {
578
- this.root.style.display = "";
579
- }
580
- else {
581
- this.root.style.display = "none";
582
- }
583
- this.root.classList.toggle("has-options", this.hasAnyVisibleOptions);
584
- this.root.classList.toggle("has-no-options", !this.hasAnyVisibleOptions);
585
- };
586
- Object.defineProperty(NeedleMenuElement.prototype, "hasAnyContent", {
587
- /** @returns true if we have any content OR a logo */
588
- get: function () {
589
- // is the logo visible?
590
- if (this.logoContainer.style.display != "none")
591
- return true;
592
- if (this.hasAnyVisibleOptions)
593
- return true;
594
- return false;
595
- },
596
- enumerable: false,
597
- configurable: true
598
- });
599
- Object.defineProperty(NeedleMenuElement.prototype, "hasAnyVisibleOptions", {
600
- get: function () {
601
- // do we have any visible buttons?
602
- for (var i = 0; i < this.options.children.length; i++) {
603
- var child = this.options.children[i];
604
- // is slot?
605
- if (child.tagName === "SLOT") {
606
- var slotElement = child;
607
- var nodes = slotElement.assignedNodes();
608
- for (var _i = 0, nodes_3 = nodes; _i < nodes_3.length; _i++) {
609
- var node = nodes_3[_i];
610
- if (node instanceof HTMLElement) {
611
- if (node.style.display != "none")
612
- return true;
613
- }
614
- }
615
- }
616
- else if (child.style.display != "none")
617
- return true;
618
- }
619
- return false;
620
- },
621
- enumerable: false,
622
- configurable: true
623
- });
624
- NeedleMenuElement.prototype.___insertDebugOptions = function () {
625
- var _this = this;
626
- window.addEventListener("keydown", function (e) {
627
- if (e.key === "p") {
628
- _this.setPosition(_this.root.classList.contains("top") ? "bottom" : "top");
629
- }
630
- });
631
- var removeOptionsButton = document.createElement("button");
632
- removeOptionsButton.textContent = "Hide Buttons";
633
- removeOptionsButton.onclick = function () {
634
- var optionsChildren = new Array(_this.options.children.length);
635
- for (var i = 0; i < _this.options.children.length; i++) {
636
- optionsChildren[i] = _this.options.children[i];
637
- }
638
- for (var _i = 0, optionsChildren_1 = optionsChildren; _i < optionsChildren_1.length; _i++) {
639
- var child = optionsChildren_1[_i];
640
- _this.options.removeChild(child);
641
- }
642
- setTimeout(function () {
643
- for (var _i = 0, optionsChildren_2 = optionsChildren; _i < optionsChildren_2.length; _i++) {
644
- var child = optionsChildren_2[_i];
645
- _this.options.appendChild(child);
646
- }
647
- }, 1000);
648
- };
649
- this.appendChild(removeOptionsButton);
650
- var anotherButton = document.createElement("button");
651
- anotherButton.textContent = "Toggle Logo";
652
- anotherButton.addEventListener("click", function () {
653
- _this.logoContainer.style.display = _this.logoContainer.style.display === "none" ? "" : "none";
654
- });
655
- this.appendChild(anotherButton);
656
- };
657
- return NeedleMenuElement;
658
- }(HTMLElement));
659
- exports.NeedleMenuElement = NeedleMenuElement;
660
- _onClick = new WeakMap();
661
- if (!customElements.get(elementName))
662
- customElements.define(elementName, NeedleMenuElement);