@schukai/monster 4.43.15 → 4.43.17

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 CHANGED
@@ -2,6 +2,22 @@
2
2
 
3
3
 
4
4
 
5
+ ## [4.43.17] - 2025-10-27
6
+
7
+ ### Bug Fixes
8
+
9
+ - remove x-default from list
10
+
11
+
12
+
13
+ ## [4.43.16] - 2025-10-24
14
+
15
+ ### Bug Fixes
16
+
17
+ - site navigation with touch devices
18
+
19
+
20
+
5
21
  ## [4.43.15] - 2025-10-21
6
22
 
7
23
  ### Bug Fixes
package/package.json CHANGED
@@ -1 +1 @@
1
- {"author":"schukai GmbH","dependencies":{"@floating-ui/dom":"^1.7.4","@popperjs/core":"^2.11.8"},"description":"Monster is a simple library for creating fast, robust and lightweight websites.","homepage":"https://monsterjs.org/","keywords":["framework","web","dom","css","sass","mobile-first","app","front-end","templates","schukai","core","shopcloud","alvine","monster","buildmap","stack","observer","observable","uuid","node","nodelist","css-in-js","logger","log","theme"],"license":"AGPL 3.0","main":"source/monster.mjs","module":"source/monster.mjs","name":"@schukai/monster","repository":{"type":"git","url":"https://gitlab.schukai.com/oss/libraries/javascript/monster.git"},"type":"module","version":"4.43.15"}
1
+ {"author":"schukai GmbH","dependencies":{"@floating-ui/dom":"^1.7.4","@popperjs/core":"^2.11.8"},"description":"Monster is a simple library for creating fast, robust and lightweight websites.","homepage":"https://monsterjs.org/","keywords":["framework","web","dom","css","sass","mobile-first","app","front-end","templates","schukai","core","shopcloud","alvine","monster","buildmap","stack","observer","observable","uuid","node","nodelist","css-in-js","logger","log","theme"],"license":"AGPL 3.0","main":"source/monster.mjs","module":"source/monster.mjs","name":"@schukai/monster","repository":{"type":"git","url":"https://gitlab.schukai.com/oss/libraries/javascript/monster.git"},"type":"module","version":"4.43.17"}
@@ -13,8 +13,8 @@ import { instanceSymbol } from "../../constants.mjs";
13
13
  import { ATTRIBUTE_ROLE } from "../../dom/constants.mjs";
14
14
  import { CustomElement } from "../../dom/customelement.mjs";
15
15
  import {
16
- assembleMethodSymbol,
17
- registerCustomElement,
16
+ assembleMethodSymbol,
17
+ registerCustomElement,
18
18
  } from "../../dom/customelement.mjs";
19
19
  import { isFunction, isObject } from "../../types/is.mjs";
20
20
  import { LocalePickerStyleSheet } from "./stylesheet/locale-picker.mjs";
@@ -43,63 +43,63 @@ const detectedLanguagesSymbol = Symbol("detectedLanguages");
43
43
  * @return {string}
44
44
  */
45
45
  function getLocalizedLabel() {
46
- const lang = document.documentElement.lang || navigator.language || "en";
47
- switch (lang.split("-")[0]) {
48
- case "de":
49
- return "Sprache wählen";
50
- case "fr":
51
- return "Sélectionnez une langue";
52
- case "es":
53
- return "Seleccione un idioma";
54
- case "it":
55
- return "Seleziona una lingua";
56
- case "pt":
57
- return "Selecione um idioma";
58
- case "nl":
59
- return "Selecteer een taal";
60
- case "pl":
61
- return "Wybierz język";
62
- case "ru":
63
- return "Выберите язык";
64
- case "cs":
65
- return "Vyberte jazyk";
66
- case "sk":
67
- return "Vyberte jazyk";
68
- case "bg":
69
- return "Изберете език";
70
- case "hr":
71
- return "Odaberite jezik";
72
- case "fi":
73
- return "Valitse kieli";
74
- case "sv":
75
- return "Välj ett språk";
76
- case "el":
77
- return "Επιλέξτε γλώσσα";
78
- case "hu":
79
- return "Válasszon egy nyelvet";
80
- case "ro":
81
- return "Selectați o limbă";
82
- case "da":
83
- return "Vælg et sprog";
84
- case "no":
85
- return "Velg et språk";
86
- case "hi":
87
- return "एक भाषा चुनें";
88
- case "bn":
89
- return "একটি ভাষা নির্বাচন করুন";
90
- case "ta":
91
- return "ஒரு மொழியைத் தேர்ந்தெடுக்கவும்";
92
- case "te":
93
- return "భాషను ఎంచుకోండి";
94
- case "mr":
95
- return "एक भाषा निवडा";
96
- case "zh":
97
- return "选择一种语言";
98
- case "ja":
99
- return "言語を選択してください";
100
- default:
101
- return "Select a language";
102
- }
46
+ const lang = document.documentElement.lang || navigator.language || "en";
47
+ switch (lang.split("-")[0]) {
48
+ case "de":
49
+ return "Sprache wählen";
50
+ case "fr":
51
+ return "Sélectionnez une langue";
52
+ case "es":
53
+ return "Seleccione un idioma";
54
+ case "it":
55
+ return "Seleziona una lingua";
56
+ case "pt":
57
+ return "Selecione um idioma";
58
+ case "nl":
59
+ return "Selecteer een taal";
60
+ case "pl":
61
+ return "Wybierz język";
62
+ case "ru":
63
+ return "Выберите язык";
64
+ case "cs":
65
+ return "Vyberte jazyk";
66
+ case "sk":
67
+ return "Vyberte jazyk";
68
+ case "bg":
69
+ return "Изберете език";
70
+ case "hr":
71
+ return "Odaberite jezik";
72
+ case "fi":
73
+ return "Valitse kieli";
74
+ case "sv":
75
+ return "Välj ett språk";
76
+ case "el":
77
+ return "Επιλέξτε γλώσσα";
78
+ case "hu":
79
+ return "Válasszon egy nyelvet";
80
+ case "ro":
81
+ return "Selectați o limbă";
82
+ case "da":
83
+ return "Vælg et sprog";
84
+ case "no":
85
+ return "Velg et språk";
86
+ case "hi":
87
+ return "एक भाषा चुनें";
88
+ case "bn":
89
+ return "একটি ভাষা নির্বাচন করুন";
90
+ case "ta":
91
+ return "ஒரு மொழியைத் தேர்ந்தெடுக்கவும்";
92
+ case "te":
93
+ return "భాషను ఎంచుకోండి";
94
+ case "mr":
95
+ return "एक भाषा निवडा";
96
+ case "zh":
97
+ return "选择一种语言";
98
+ case "ja":
99
+ return "言語を選択してください";
100
+ default:
101
+ return "Select a language";
102
+ }
103
103
  }
104
104
 
105
105
  /**
@@ -112,92 +112,92 @@ function getLocalizedLabel() {
112
112
  * @summary A simple language switcher as a select.
113
113
  */
114
114
  class LocaleSelect extends CustomElement {
115
- /**
116
- * Used by the instanceof operator.
117
- * @returns {symbol}
118
- */
119
- static get [instanceSymbol]() {
120
- return Symbol.for(
121
- "@schukai/monster/components/accessibility/local-selector@@instance",
122
- );
123
- }
115
+ /**
116
+ * Used by the instanceof operator.
117
+ * @returns {symbol}
118
+ */
119
+ static get [instanceSymbol]() {
120
+ return Symbol.for(
121
+ "@schukai/monster/components/accessibility/local-selector@@instance",
122
+ );
123
+ }
124
124
 
125
- /**
126
- * Initializes the component.
127
- * @return {LocaleSelect}
128
- */
129
- [assembleMethodSymbol]() {
130
- super[assembleMethodSymbol]();
131
- initControlReferences.call(this);
132
- initSelectOptions.call(this);
133
- initEventHandler.call(this);
134
- return this;
135
- }
125
+ /**
126
+ * Initializes the component.
127
+ * @return {LocaleSelect}
128
+ */
129
+ [assembleMethodSymbol]() {
130
+ super[assembleMethodSymbol]();
131
+ initControlReferences.call(this);
132
+ initSelectOptions.call(this);
133
+ initEventHandler.call(this);
134
+ return this;
135
+ }
136
136
 
137
- /**
138
- * Default options
139
- */
140
- get defaults() {
141
- return Object.assign({}, super.defaults, {
142
- templates: {
143
- main: getTemplate(),
144
- },
145
- labels: {
146
- "select-an-option": getLocalizedLabel(),
147
- },
148
- callbacks: {},
149
- disabled: false,
150
- });
151
- }
137
+ /**
138
+ * Default options
139
+ */
140
+ get defaults() {
141
+ return Object.assign({}, super.defaults, {
142
+ templates: {
143
+ main: getTemplate(),
144
+ },
145
+ labels: {
146
+ "select-an-option": getLocalizedLabel(),
147
+ },
148
+ callbacks: {},
149
+ disabled: false,
150
+ });
151
+ }
152
152
 
153
- /**
154
- * connectedCallback
155
- */
156
- connectedCallback() {
157
- super.connectedCallback();
158
- this[detectedLanguagesSymbol] = detectUserLanguagePreference();
159
- initSelectOptions.call(this);
160
- }
153
+ /**
154
+ * connectedCallback
155
+ */
156
+ connectedCallback() {
157
+ super.connectedCallback();
158
+ this[detectedLanguagesSymbol] = detectUserLanguagePreference();
159
+ initSelectOptions.call(this);
160
+ }
161
161
 
162
- /**
163
- * @return {string}
164
- */
165
- static getTag() {
166
- return "monster-locale-select";
167
- }
162
+ /**
163
+ * @return {string}
164
+ */
165
+ static getTag() {
166
+ return "monster-locale-select";
167
+ }
168
168
 
169
- /**
170
- * @return {CSSStyleSheet[]}
171
- */
172
- static getCSSStyleSheet() {
173
- return [LocalePickerStyleSheet];
174
- }
169
+ /**
170
+ * @return {CSSStyleSheet[]}
171
+ */
172
+ static getCSSStyleSheet() {
173
+ return [LocalePickerStyleSheet];
174
+ }
175
175
 
176
- /**
177
- * Export parts from monster-select to make them available for styling outside.
178
- */
179
- static get exportparts() {
180
- // The parts from monster-select, as defined in source/components/form/select.mjs:
181
- return [
182
- "control",
183
- "container",
184
- "popper",
185
- "option",
186
- "option-label",
187
- "option-control",
188
- "badge",
189
- "badge-label",
190
- "remove-badge",
191
- "summary",
192
- "status-or-remove-badges",
193
- "remote-info",
194
- "no-options",
195
- "selection",
196
- "inline-filter",
197
- "popper-filter",
198
- "content",
199
- ].join(",");
200
- }
176
+ /**
177
+ * Export parts from monster-select to make them available for styling outside.
178
+ */
179
+ static get exportparts() {
180
+ // The parts from monster-select, as defined in source/components/form/select.mjs:
181
+ return [
182
+ "control",
183
+ "container",
184
+ "popper",
185
+ "option",
186
+ "option-label",
187
+ "option-control",
188
+ "badge",
189
+ "badge-label",
190
+ "remove-badge",
191
+ "summary",
192
+ "status-or-remove-badges",
193
+ "remote-info",
194
+ "no-options",
195
+ "selection",
196
+ "inline-filter",
197
+ "popper-filter",
198
+ "content",
199
+ ].join(",");
200
+ }
201
201
  }
202
202
 
203
203
  /**
@@ -205,9 +205,9 @@ class LocaleSelect extends CustomElement {
205
205
  * @private
206
206
  */
207
207
  function initControlReferences() {
208
- this[selectElementSymbol] = this.shadowRoot.querySelector(
209
- `[${ATTRIBUTE_ROLE}="select"]`,
210
- );
208
+ this[selectElementSymbol] = this.shadowRoot.querySelector(
209
+ `[${ATTRIBUTE_ROLE}="select"]`,
210
+ );
211
211
  }
212
212
 
213
213
  /**
@@ -215,29 +215,36 @@ function initControlReferences() {
215
215
  * @private
216
216
  */
217
217
  function initSelectOptions() {
218
- const detected =
219
- this[detectedLanguagesSymbol] || detectUserLanguagePreference();
218
+ const detected =
219
+ this[detectedLanguagesSymbol] || detectUserLanguagePreference();
220
220
 
221
- let options = [];
222
- if (Array.isArray(detected.available)) {
223
- const currentLang = detected.current;
224
- options = detected.available.filter(
225
- (lang) => lang.baseLang !== currentLang && lang.fullLang !== currentLang,
226
- );
227
- } else if (Array.isArray(detected.allOfferable)) {
228
- options = detected.allOfferable;
229
- }
221
+ let options = [];
222
+ if (Array.isArray(detected.available)) {
223
+ const currentLang = detected.current;
224
+ options = detected.available.filter(
225
+ (lang) => lang.baseLang !== currentLang && lang.fullLang !== currentLang,
226
+ );
227
+ } else if (Array.isArray(detected.allOfferable)) {
228
+ options = detected.allOfferable;
229
+ }
230
230
 
231
- options = detected.allOfferable;
232
- if (this[selectElementSymbol]) {
233
- this[selectElementSymbol].setOption("mapping.labelTemplate", "${label}");
234
- this[selectElementSymbol].setOption("mapping.valueTemplate", "${href}");
235
- this[selectElementSymbol].setOption(
236
- "labels.select-an-option",
237
- this.getOption("labels.select-an-option"),
238
- );
239
- this[selectElementSymbol].importOptions(options);
240
- }
231
+ options = detected.allOfferable;
232
+
233
+ if (Array.isArray(options)) {
234
+ options = options.filter(
235
+ (lang) => lang.baseLang !== "x-default" && lang.fullLang !== "x-default",
236
+ );
237
+ }
238
+
239
+ if (this[selectElementSymbol]) {
240
+ this[selectElementSymbol].setOption("mapping.labelTemplate", "${label}");
241
+ this[selectElementSymbol].setOption("mapping.valueTemplate", "${href}");
242
+ this[selectElementSymbol].setOption(
243
+ "labels.select-an-option",
244
+ this.getOption("labels.select-an-option"),
245
+ );
246
+ this[selectElementSymbol].importOptions(options);
247
+ }
241
248
  }
242
249
 
243
250
  /**
@@ -245,14 +252,14 @@ function initSelectOptions() {
245
252
  * @private
246
253
  */
247
254
  function initEventHandler() {
248
- if (!this[selectElementSymbol]) return;
255
+ if (!this[selectElementSymbol]) return;
249
256
 
250
- this[selectElementSymbol].addEventListener("change", (event) => {
251
- const selected = event.target?.value;
252
- if (selected) {
253
- window.location.href = selected;
254
- }
255
- });
257
+ this[selectElementSymbol].addEventListener("change", (event) => {
258
+ const selected = event.target?.value;
259
+ if (selected) {
260
+ window.location.href = selected;
261
+ }
262
+ });
256
263
  }
257
264
 
258
265
  /**
@@ -261,8 +268,8 @@ function initEventHandler() {
261
268
  * @return {string}
262
269
  */
263
270
  function getTemplate() {
264
- // language=HTML
265
- return `
271
+ // language=HTML
272
+ return `
266
273
  <monster-select exportparts="control,container,popper,option,option-label,option-control,badge,badge-label,remove-badge,summary,status-or-remove-badges,remote-info,no-options,selection,inline-filter,popper-filter,content" data-monster-role="select"></monster-select>
267
274
  `;
268
275
  }
@@ -111,7 +111,6 @@ class SiteNavigation extends CustomElement {
111
111
  requestAnimationFrame(() => {
112
112
  populateTabs.call(this);
113
113
  });
114
-
115
114
  }
116
115
 
117
116
  disconnectedCallback() {
@@ -304,7 +303,7 @@ function detachResizeObserver() {
304
303
  }
305
304
 
306
305
  /**
307
- * Sets up interaction logic (hover or click) for a submenu.
306
+ * Sets up interaction logic (hover, click, or touch) for a submenu.
308
307
  * This function is called recursively for nested submenus.
309
308
  * @private
310
309
  * @this {SiteNavigation}
@@ -323,111 +322,174 @@ function setupSubmenu(parentLi, context = "visible", level = 1) {
323
322
  }
324
323
 
325
324
  const interaction = this.getOption("interactionModel", "auto");
326
- const useHover =
327
- interaction === "hover" ||
328
- (interaction === "auto" && context === "visible");
325
+ const isTouchDevice =
326
+ "ontouchstart" in window || navigator.maxTouchPoints > 0;
329
327
 
330
- if (useHover) {
331
- const component = this;
332
- let cleanup;
333
- let hideTimeout;
334
- let isHovering = false;
328
+ const effectiveInteraction =
329
+ interaction === "auto"
330
+ ? context === "visible"
331
+ ? "hover"
332
+ : "click"
333
+ : interaction;
334
+
335
+ const component = this;
336
+ let cleanup;
335
337
 
336
- const immediateHide = () => {
337
- submenu.style.display = "none";
338
- Object.assign(submenu.style, {
339
- maxHeight: "",
340
- overflowY: "",
338
+ const immediateHide = () => {
339
+ submenu.style.display = "none";
340
+ Object.assign(submenu.style, {
341
+ maxHeight: "",
342
+ overflowY: "",
343
+ });
344
+ submenu
345
+ .querySelectorAll(
346
+ "ul[style*='display: block'], div[part='mega-menu'][style*='display: block']",
347
+ )
348
+ .forEach((sub) => {
349
+ sub.style.display = "none";
341
350
  });
342
- submenu
343
- .querySelectorAll(
344
- "ul[style*='display: block'], div[part='mega-menu'][style*='display: block']",
345
- )
346
- .forEach((sub) => {
347
- sub.style.display = "none";
351
+ fireCustomEvent(this, "monster-submenu-hide", {
352
+ context,
353
+ trigger: parentLi,
354
+ submenu,
355
+ level,
356
+ });
357
+ if (cleanup) {
358
+ cleanup();
359
+ cleanup = null;
360
+ }
361
+ if (level === 1 && component[activeSubmenuHiderSymbol] === immediateHide) {
362
+ component[activeSubmenuHiderSymbol] = null;
363
+ }
364
+ };
365
+
366
+ const show = () => {
367
+ component[hideHamburgerMenuSymbol]?.();
368
+ if (level === 1) {
369
+ if (
370
+ component[activeSubmenuHiderSymbol] &&
371
+ component[activeSubmenuHiderSymbol] !== immediateHide
372
+ ) {
373
+ component[activeSubmenuHiderSymbol]();
374
+ }
375
+ component[activeSubmenuHiderSymbol] = immediateHide;
376
+ } else {
377
+ [...parentLi.parentElement.children]
378
+ .filter((li) => li !== parentLi)
379
+ .forEach((sibling) => {
380
+ const siblingSubmenu = sibling.querySelector(
381
+ ":scope > ul, :scope > div[part='mega-menu']",
382
+ );
383
+ if (siblingSubmenu) {
384
+ siblingSubmenu.style.display = "none";
385
+ }
386
+ });
387
+ }
388
+ submenu.style.display = "block";
389
+ fireCustomEvent(this, "monster-submenu-show", {
390
+ context,
391
+ trigger: parentLi,
392
+ submenu,
393
+ level,
394
+ });
395
+ if (!cleanup) {
396
+ cleanup = autoUpdate(parentLi, submenu, () => {
397
+ const middleware = [offset(8), flip(), shift({ padding: 8 })];
398
+ const containsSubmenus = submenu.querySelector(
399
+ "ul, div[part='mega-menu']",
400
+ );
401
+ if (!containsSubmenus) {
402
+ middleware.push(
403
+ size({
404
+ apply: ({ availableHeight, elements }) => {
405
+ Object.assign(elements.floating.style, {
406
+ maxHeight: `${availableHeight}px`,
407
+ overflowY: "auto",
408
+ });
409
+ },
410
+ padding: 8,
411
+ }),
412
+ );
413
+ }
414
+ computePosition(parentLi, submenu, {
415
+ placement: level === 1 ? "bottom-start" : "right-start",
416
+ middleware: middleware,
417
+ }).then(({ x, y, strategy }) => {
418
+ Object.assign(submenu.style, {
419
+ position: strategy,
420
+ left: `${x}px`,
421
+ top: `${y}px`,
422
+ });
348
423
  });
349
- fireCustomEvent(this, "monster-submenu-hide", {
350
- context,
351
- trigger: parentLi,
352
- submenu,
353
- level,
354
424
  });
355
- if (cleanup) {
356
- cleanup();
357
- cleanup = null;
358
- }
425
+ }
426
+ };
427
+
428
+ if (effectiveInteraction === "hover" && isTouchDevice) {
429
+ let lastTap = 0;
430
+ const DOUBLE_TAP_DELAY = 300;
431
+ const anchor = parentLi.querySelector(":scope > a");
432
+
433
+ if (!anchor) return;
434
+
435
+ const handleOutsideClickForSubmenu = (event) => {
359
436
  if (
360
- level === 1 &&
361
- component[activeSubmenuHiderSymbol] === immediateHide
437
+ submenu.style.display === "block" &&
438
+ !parentLi.contains(event.target)
362
439
  ) {
363
- component[activeSubmenuHiderSymbol] = null;
440
+ immediateHide();
441
+ document.removeEventListener(
442
+ "click",
443
+ handleOutsideClickForSubmenu,
444
+ true,
445
+ );
364
446
  }
365
447
  };
366
448
 
367
- const show = () => {
368
- component[hideHamburgerMenuSymbol]?.();
369
- if (level === 1) {
370
- if (
371
- component[activeSubmenuHiderSymbol] &&
372
- component[activeSubmenuHiderSymbol] !== immediateHide
373
- ) {
374
- component[activeSubmenuHiderSymbol]();
375
- }
376
- component[activeSubmenuHiderSymbol] = immediateHide;
449
+ anchor.addEventListener("click", (event) => {
450
+ const now = Date.now();
451
+ const timeSinceLastTap = now - lastTap;
452
+ lastTap = now;
453
+
454
+ if (timeSinceLastTap < DOUBLE_TAP_DELAY && timeSinceLastTap > 0) {
455
+ lastTap = 0;
456
+ document.removeEventListener(
457
+ "click",
458
+ handleOutsideClickForSubmenu,
459
+ true,
460
+ );
461
+ immediateHide();
377
462
  } else {
378
- [...parentLi.parentElement.children]
379
- .filter((li) => li !== parentLi)
380
- .forEach((sibling) => {
381
- const siblingSubmenu = sibling.querySelector(
382
- ":scope > ul, :scope > div[part='mega-menu']",
383
- );
384
- if (siblingSubmenu) {
385
- siblingSubmenu.style.display = "none";
386
- }
387
- });
388
- }
389
- submenu.style.display = "block";
390
- fireCustomEvent(this, "monster-submenu-show", {
391
- context,
392
- trigger: parentLi,
393
- submenu,
394
- level,
395
- });
396
- if (!cleanup) {
397
- cleanup = autoUpdate(parentLi, submenu, () => {
398
- const middleware = [offset(8), flip(), shift({ padding: 8 })];
399
- const containsSubmenus = submenu.querySelector(
400
- "ul, div[part='mega-menu']",
463
+ event.preventDefault();
464
+ event.stopPropagation();
465
+
466
+ const isMenuOpen = submenu.style.display === "block";
467
+
468
+ if (isMenuOpen) {
469
+ document.removeEventListener(
470
+ "click",
471
+ handleOutsideClickForSubmenu,
472
+ true,
401
473
  );
402
- if (!containsSubmenus) {
403
- middleware.push(
404
- size({
405
- apply: ({ availableHeight, elements }) => {
406
- Object.assign(elements.floating.style, {
407
- maxHeight: `${availableHeight}px`,
408
- overflowY: "auto",
409
- });
410
- },
411
- padding: 8,
412
- }),
474
+ immediateHide();
475
+ } else {
476
+ show();
477
+ setTimeout(() => {
478
+ document.addEventListener(
479
+ "click",
480
+ handleOutsideClickForSubmenu,
481
+ true,
413
482
  );
414
- }
415
- computePosition(parentLi, submenu, {
416
- placement: level === 1 ? "bottom-start" : "right-start",
417
- middleware: middleware,
418
- }).then(({ x, y, strategy }) => {
419
- Object.assign(submenu.style, {
420
- position: strategy,
421
- left: `${x}px`,
422
- top: `${y}px`,
423
- });
424
- });
425
- });
483
+ }, 0);
484
+ }
426
485
  }
427
- };
486
+ });
487
+ } else if (effectiveInteraction === "hover" && !isTouchDevice) {
488
+ let hideTimeout;
489
+ let isHovering = false;
428
490
 
429
491
  const handleMouseEnter = () => {
430
- isHovering = true; // Status auf "aktiv" setzen
492
+ isHovering = true;
431
493
  clearTimeout(hideTimeout);
432
494
  if (submenu.style.display !== "block") {
433
495
  show();
@@ -435,7 +497,7 @@ function setupSubmenu(parentLi, context = "visible", level = 1) {
435
497
  };
436
498
 
437
499
  const handleMouseLeave = () => {
438
- isHovering = false; // Status auf "inaktiv" setzen
500
+ isHovering = false;
439
501
  hideTimeout = setTimeout(() => {
440
502
  if (!isHovering) {
441
503
  immediateHide();
@@ -524,7 +586,6 @@ function cloneNavItem(item) {
524
586
  * @this {SiteNavigation}
525
587
  */
526
588
  function populateTabs() {
527
-
528
589
  const visibleList = this[visibleElementsSymbol];
529
590
  const hiddenList = this[hiddenElementsSymbol];
530
591
  const hamburgerButton = this[hamburgerButtonSymbol];
@@ -551,7 +612,6 @@ function populateTabs() {
551
612
  return;
552
613
  }
553
614
 
554
-
555
615
  const navWidth = navEl.clientWidth;
556
616
 
557
617
  const originalDisplay = hamburgerButton.style.display;
@@ -607,7 +667,7 @@ function populateTabs() {
607
667
  visibleList.appendChild(firstHiddenItemClone);
608
668
  const firstHiddenItemWidth =
609
669
  firstHiddenItemClone.getBoundingClientRect().width;
610
- visibleList.removeChild(firstHiddenItemClone);
670
+ visibleList.removeChild(firstHiddenItemClone);
611
671
 
612
672
  const gap = parseFloat(getComputedStyle(visibleList).gap || "0") || 0;
613
673
  if (visibleItemsWidth + gap + firstHiddenItemWidth <= navWidth) {