@schukai/monster 4.43.13 → 4.43.15
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 +16 -0
- package/package.json +1 -1
- package/source/components/datatable/datatable.mjs +1283 -1284
- package/source/components/navigation/site-navigation.mjs +523 -487
- package/source/components/navigation/style/site-navigation.pcss +13 -0
- package/source/components/navigation/stylesheet/site-navigation.mjs +1 -1
|
@@ -13,20 +13,20 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
CustomElement,
|
|
17
|
+
getSlottedElements,
|
|
18
|
+
registerCustomElement,
|
|
19
|
+
assembleMethodSymbol,
|
|
20
20
|
} from "../../dom/customelement.mjs";
|
|
21
21
|
import { DeadMansSwitch } from "../../util/deadmansswitch.mjs";
|
|
22
22
|
import { SiteNavigationStyleSheet } from "./stylesheet/site-navigation.mjs";
|
|
23
23
|
import {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
24
|
+
computePosition,
|
|
25
|
+
autoUpdate,
|
|
26
|
+
flip,
|
|
27
|
+
shift,
|
|
28
|
+
offset,
|
|
29
|
+
size,
|
|
30
30
|
} from "@floating-ui/dom";
|
|
31
31
|
import { fireCustomEvent } from "../../dom/events.mjs";
|
|
32
32
|
|
|
@@ -64,59 +64,60 @@ const hamburgerCloseButtonSymbol = Symbol("hamburgerCloseButton");
|
|
|
64
64
|
* @fires monster-submenu-hide - Fired when a submenu is hidden. The event detail contains `{context, trigger, submenu, level}`.
|
|
65
65
|
*/
|
|
66
66
|
class SiteNavigation extends CustomElement {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
67
|
+
static get [instanceSymbol]() {
|
|
68
|
+
return Symbol.for("@schukai/monster/components/navigation/site@@instance");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Configuration options for the SiteNavigation component.
|
|
73
|
+
* @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
|
|
74
|
+
*
|
|
75
|
+
* To set these options via an HTML tag, use the `data-monster-options` attribute.
|
|
76
|
+
* The individual configuration values are detailed in the table below.
|
|
77
|
+
*
|
|
78
|
+
* @property {Object} templates - Template definitions.
|
|
79
|
+
* @property {string} templates.main - The main HTML template for the component.
|
|
80
|
+
* @property {string} interactionModel="auto" - Defines the interaction with submenus. Possible values: `auto`, `click`, `hover`. With `auto`, `hover` is used on desktop and `click` is used in the hamburger menu.
|
|
81
|
+
* @property {Object} features - Container for additional feature flags.
|
|
82
|
+
* @property {boolean} features.resetOnClose=true - If `true`, all open submenus within the hamburger menu will be reset when it is closed.
|
|
83
|
+
*/
|
|
84
|
+
get defaults() {
|
|
85
|
+
return Object.assign({}, super.defaults, {
|
|
86
|
+
templates: { main: getTemplate() },
|
|
87
|
+
interactionModel: "auto", // 'auto', 'click', 'hover'
|
|
88
|
+
features: {
|
|
89
|
+
resetOnClose: true,
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
[assembleMethodSymbol]() {
|
|
95
|
+
super[assembleMethodSymbol]();
|
|
96
|
+
initControlReferences.call(this);
|
|
97
|
+
initEventHandler.call(this);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
static getCSSStyleSheet() {
|
|
101
|
+
return [SiteNavigationStyleSheet];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
static getTag() {
|
|
105
|
+
return "monster-site-navigation";
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
connectedCallback() {
|
|
109
|
+
super.connectedCallback();
|
|
110
|
+
attachResizeObserver.call(this);
|
|
111
|
+
requestAnimationFrame(() => {
|
|
112
|
+
populateTabs.call(this);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
disconnectedCallback() {
|
|
118
|
+
super.disconnectedCallback();
|
|
119
|
+
detachResizeObserver.call(this);
|
|
120
|
+
}
|
|
120
121
|
}
|
|
121
122
|
|
|
122
123
|
/**
|
|
@@ -125,22 +126,22 @@ class SiteNavigation extends CustomElement {
|
|
|
125
126
|
* @this {SiteNavigation}
|
|
126
127
|
*/
|
|
127
128
|
function initControlReferences() {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
129
|
+
if (!this.shadowRoot) throw new Error("Component requires a shadowRoot.");
|
|
130
|
+
this[navElementSymbol] = this.shadowRoot.querySelector(
|
|
131
|
+
'[data-monster-role="navigation"]',
|
|
132
|
+
);
|
|
133
|
+
this[visibleElementsSymbol] =
|
|
134
|
+
this.shadowRoot.querySelector("#visible-elements");
|
|
135
|
+
this[hiddenElementsSymbol] =
|
|
136
|
+
this.shadowRoot.querySelector("#hidden-elements");
|
|
137
|
+
this[hamburgerButtonSymbol] =
|
|
138
|
+
this.shadowRoot.querySelector("#hamburger-button");
|
|
139
|
+
this[hamburgerNavSymbol] = this.shadowRoot.querySelector(
|
|
140
|
+
'[data-monster-role="hamburger-nav"]',
|
|
141
|
+
);
|
|
142
|
+
this[hamburgerCloseButtonSymbol] = this.shadowRoot.querySelector(
|
|
143
|
+
'[part="hamburger-close-button"]',
|
|
144
|
+
);
|
|
144
145
|
}
|
|
145
146
|
|
|
146
147
|
/**
|
|
@@ -149,120 +150,120 @@ function initControlReferences() {
|
|
|
149
150
|
* @this {SiteNavigation}
|
|
150
151
|
*/
|
|
151
152
|
function initEventHandler() {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
153
|
+
if (!this.shadowRoot) throw new Error("Component requires a shadowRoot.");
|
|
154
|
+
|
|
155
|
+
const hamburgerButton = this[hamburgerButtonSymbol];
|
|
156
|
+
const hamburgerNav = this[hamburgerNavSymbol];
|
|
157
|
+
const hamburgerCloseButton = this[hamburgerCloseButtonSymbol];
|
|
158
|
+
let cleanup;
|
|
159
|
+
|
|
160
|
+
if (!hamburgerButton || !hamburgerNav || !hamburgerCloseButton) return;
|
|
161
|
+
|
|
162
|
+
const getBestPositionStrategy = (element) => {
|
|
163
|
+
let parent = element.parentElement;
|
|
164
|
+
while (parent) {
|
|
165
|
+
const parentPosition = window.getComputedStyle(parent).position;
|
|
166
|
+
if (["fixed", "sticky"].includes(parentPosition)) {
|
|
167
|
+
return "fixed";
|
|
168
|
+
}
|
|
169
|
+
parent = parent.parentElement;
|
|
170
|
+
}
|
|
171
|
+
return "absolute";
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const handleOutsideClick = (event) => {
|
|
175
|
+
if (
|
|
176
|
+
!hamburgerButton.contains(event.target) &&
|
|
177
|
+
!hamburgerNav.contains(event.target)
|
|
178
|
+
) {
|
|
179
|
+
hideMenu();
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const hideMenu = () => {
|
|
184
|
+
hamburgerNav.style.display = "none";
|
|
185
|
+
document.body.classList.remove("monster-navigation-open");
|
|
186
|
+
|
|
187
|
+
fireCustomEvent(this, "monster-hamburger-hide", {
|
|
188
|
+
button: hamburgerButton,
|
|
189
|
+
menu: hamburgerNav,
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
if (this.getOption("features.resetOnClose") === true) {
|
|
193
|
+
this[hiddenElementsSymbol]
|
|
194
|
+
.querySelectorAll(".is-open")
|
|
195
|
+
.forEach((submenu) => submenu.classList.remove("is-open"));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (cleanup) {
|
|
199
|
+
cleanup();
|
|
200
|
+
cleanup = undefined;
|
|
201
|
+
}
|
|
202
|
+
document.removeEventListener("click", handleOutsideClick);
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
this[hideHamburgerMenuSymbol] = hideMenu;
|
|
206
|
+
|
|
207
|
+
const showMenu = () => {
|
|
208
|
+
this[activeSubmenuHiderSymbol]?.();
|
|
209
|
+
hamburgerNav.style.display = "block";
|
|
210
|
+
document.body.classList.add("monster-navigation-open");
|
|
211
|
+
|
|
212
|
+
fireCustomEvent(this, "monster-hamburger-show", {
|
|
213
|
+
button: hamburgerButton,
|
|
214
|
+
menu: hamburgerNav,
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
cleanup = autoUpdate(hamburgerButton, hamburgerNav, () => {
|
|
218
|
+
if (window.innerWidth > 768) {
|
|
219
|
+
const strategy = getBestPositionStrategy(this);
|
|
220
|
+
|
|
221
|
+
computePosition(hamburgerButton, hamburgerNav, {
|
|
222
|
+
placement: "bottom-end",
|
|
223
|
+
strategy: strategy,
|
|
224
|
+
middleware: [
|
|
225
|
+
offset(8),
|
|
226
|
+
flip(),
|
|
227
|
+
shift({ padding: 8 }),
|
|
228
|
+
size({
|
|
229
|
+
apply: ({ availableHeight, elements }) => {
|
|
230
|
+
Object.assign(elements.floating.style, {
|
|
231
|
+
maxHeight: `${availableHeight}px`,
|
|
232
|
+
overflowY: "auto",
|
|
233
|
+
});
|
|
234
|
+
},
|
|
235
|
+
padding: 8,
|
|
236
|
+
}),
|
|
237
|
+
],
|
|
238
|
+
}).then(({ x, y, strategy }) => {
|
|
239
|
+
Object.assign(hamburgerNav.style, {
|
|
240
|
+
position: strategy,
|
|
241
|
+
left: `${x}px`,
|
|
242
|
+
top: `${y}px`,
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
} else {
|
|
246
|
+
// Mobile view (fullscreen overlay), position is handled by CSS
|
|
247
|
+
Object.assign(hamburgerNav.style, { position: "", left: "", top: "" });
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
setTimeout(() => document.addEventListener("click", handleOutsideClick), 0);
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
hamburgerButton.addEventListener("click", (event) => {
|
|
254
|
+
event.stopPropagation();
|
|
255
|
+
const isVisible = hamburgerNav.style.display === "block";
|
|
256
|
+
if (isVisible) {
|
|
257
|
+
hideMenu();
|
|
258
|
+
} else {
|
|
259
|
+
showMenu();
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
hamburgerCloseButton.addEventListener("click", (event) => {
|
|
264
|
+
event.stopPropagation();
|
|
265
|
+
hideMenu();
|
|
266
|
+
});
|
|
266
267
|
}
|
|
267
268
|
|
|
268
269
|
/**
|
|
@@ -272,22 +273,22 @@ function initEventHandler() {
|
|
|
272
273
|
* @this {SiteNavigation}
|
|
273
274
|
*/
|
|
274
275
|
function attachResizeObserver() {
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
276
|
+
this[resizeObserverSymbol] = new ResizeObserver(() => {
|
|
277
|
+
if (this[timerCallbackSymbol] instanceof DeadMansSwitch) {
|
|
278
|
+
try {
|
|
279
|
+
this[timerCallbackSymbol].touch();
|
|
280
|
+
return;
|
|
281
|
+
} catch (e) {
|
|
282
|
+
delete this[timerCallbackSymbol];
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
this[timerCallbackSymbol] = new DeadMansSwitch(200, () => {
|
|
286
|
+
requestAnimationFrame(() => {
|
|
287
|
+
populateTabs.call(this);
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
this[resizeObserverSymbol].observe(this);
|
|
291
292
|
}
|
|
292
293
|
|
|
293
294
|
/**
|
|
@@ -296,10 +297,10 @@ function attachResizeObserver() {
|
|
|
296
297
|
* @this {SiteNavigation}
|
|
297
298
|
*/
|
|
298
299
|
function detachResizeObserver() {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
300
|
+
if (this[resizeObserverSymbol] instanceof ResizeObserver) {
|
|
301
|
+
this[resizeObserverSymbol].disconnect();
|
|
302
|
+
delete this[resizeObserverSymbol];
|
|
303
|
+
}
|
|
303
304
|
}
|
|
304
305
|
|
|
305
306
|
/**
|
|
@@ -312,177 +313,177 @@ function detachResizeObserver() {
|
|
|
312
313
|
* @param {number} level The nesting level of the submenu (starts at 1).
|
|
313
314
|
*/
|
|
314
315
|
function setupSubmenu(parentLi, context = "visible", level = 1) {
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
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
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
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
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
316
|
+
const submenu = parentLi.querySelector(
|
|
317
|
+
":scope > ul, :scope > div[part='mega-menu']",
|
|
318
|
+
);
|
|
319
|
+
if (!submenu) return;
|
|
320
|
+
|
|
321
|
+
if (submenu.tagName === "UL") {
|
|
322
|
+
submenu.setAttribute("part", "submenu");
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const interaction = this.getOption("interactionModel", "auto");
|
|
326
|
+
const useHover =
|
|
327
|
+
interaction === "hover" ||
|
|
328
|
+
(interaction === "auto" && context === "visible");
|
|
329
|
+
|
|
330
|
+
if (useHover) {
|
|
331
|
+
const component = this;
|
|
332
|
+
let cleanup;
|
|
333
|
+
let hideTimeout;
|
|
334
|
+
let isHovering = false;
|
|
335
|
+
|
|
336
|
+
const immediateHide = () => {
|
|
337
|
+
submenu.style.display = "none";
|
|
338
|
+
Object.assign(submenu.style, {
|
|
339
|
+
maxHeight: "",
|
|
340
|
+
overflowY: "",
|
|
341
|
+
});
|
|
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";
|
|
348
|
+
});
|
|
349
|
+
fireCustomEvent(this, "monster-submenu-hide", {
|
|
350
|
+
context,
|
|
351
|
+
trigger: parentLi,
|
|
352
|
+
submenu,
|
|
353
|
+
level,
|
|
354
|
+
});
|
|
355
|
+
if (cleanup) {
|
|
356
|
+
cleanup();
|
|
357
|
+
cleanup = null;
|
|
358
|
+
}
|
|
359
|
+
if (
|
|
360
|
+
level === 1 &&
|
|
361
|
+
component[activeSubmenuHiderSymbol] === immediateHide
|
|
362
|
+
) {
|
|
363
|
+
component[activeSubmenuHiderSymbol] = null;
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
|
|
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;
|
|
377
|
+
} 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']",
|
|
401
|
+
);
|
|
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
|
+
}),
|
|
413
|
+
);
|
|
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
|
+
});
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
|
|
429
|
+
const handleMouseEnter = () => {
|
|
430
|
+
isHovering = true; // Status auf "aktiv" setzen
|
|
431
|
+
clearTimeout(hideTimeout);
|
|
432
|
+
if (submenu.style.display !== "block") {
|
|
433
|
+
show();
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
const handleMouseLeave = () => {
|
|
438
|
+
isHovering = false; // Status auf "inaktiv" setzen
|
|
439
|
+
hideTimeout = setTimeout(() => {
|
|
440
|
+
if (!isHovering) {
|
|
441
|
+
immediateHide();
|
|
442
|
+
}
|
|
443
|
+
}, 250);
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
parentLi.addEventListener("mouseenter", handleMouseEnter);
|
|
447
|
+
parentLi.addEventListener("mouseleave", handleMouseLeave);
|
|
448
|
+
submenu.addEventListener("mouseenter", handleMouseEnter);
|
|
449
|
+
submenu.addEventListener("mouseleave", handleMouseLeave);
|
|
450
|
+
} else {
|
|
451
|
+
const anchor = parentLi.querySelector(":scope > a");
|
|
452
|
+
if (anchor) {
|
|
453
|
+
anchor.addEventListener("click", (event) => {
|
|
454
|
+
event.preventDefault();
|
|
455
|
+
event.stopPropagation();
|
|
456
|
+
if (!submenu.classList.contains("is-open")) {
|
|
457
|
+
[...parentLi.parentElement.children]
|
|
458
|
+
.filter((li) => li !== parentLi)
|
|
459
|
+
.forEach((sibling) => {
|
|
460
|
+
const siblingSubmenu = sibling.querySelector(
|
|
461
|
+
":scope > ul, :scope > div[part='mega-menu']",
|
|
462
|
+
);
|
|
463
|
+
if (siblingSubmenu) {
|
|
464
|
+
siblingSubmenu.classList.remove("is-open");
|
|
465
|
+
}
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
const isOpen = submenu.classList.toggle("is-open");
|
|
469
|
+
const eventName = isOpen
|
|
470
|
+
? "monster-submenu-show"
|
|
471
|
+
: "monster-submenu-hide";
|
|
472
|
+
fireCustomEvent(this, eventName, {
|
|
473
|
+
context,
|
|
474
|
+
trigger: parentLi,
|
|
475
|
+
submenu,
|
|
476
|
+
level,
|
|
477
|
+
});
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
if (submenu.tagName === "UL") {
|
|
483
|
+
submenu
|
|
484
|
+
.querySelectorAll(":scope > li")
|
|
485
|
+
.forEach((li) => setupSubmenu.call(this, li, context, level + 1));
|
|
486
|
+
}
|
|
486
487
|
}
|
|
487
488
|
|
|
488
489
|
/**
|
|
@@ -493,20 +494,20 @@ function setupSubmenu(parentLi, context = "visible", level = 1) {
|
|
|
493
494
|
* @returns {HTMLLIElement} The cloned and configured list item.
|
|
494
495
|
*/
|
|
495
496
|
function cloneNavItem(item) {
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
497
|
+
const liClone = item.cloneNode(true);
|
|
498
|
+
const aClone = liClone.querySelector("a");
|
|
499
|
+
let navItemPart = "nav-item";
|
|
500
|
+
let navLinkPart = "nav-link";
|
|
500
501
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
502
|
+
if (item.classList.contains("active")) {
|
|
503
|
+
navItemPart += " nav-item-active";
|
|
504
|
+
if (aClone) navLinkPart += " nav-link-active";
|
|
505
|
+
}
|
|
505
506
|
|
|
506
|
-
|
|
507
|
-
|
|
507
|
+
liClone.setAttribute("part", navItemPart);
|
|
508
|
+
if (aClone) aClone.setAttribute("part", navLinkPart);
|
|
508
509
|
|
|
509
|
-
|
|
510
|
+
return liClone;
|
|
510
511
|
}
|
|
511
512
|
|
|
512
513
|
/**
|
|
@@ -515,97 +516,132 @@ function cloneNavItem(item) {
|
|
|
515
516
|
* @private
|
|
516
517
|
* @this {SiteNavigation}
|
|
517
518
|
*/
|
|
519
|
+
/**
|
|
520
|
+
* Misst den verfügbaren Platz und verteilt die Navigationselemente auf die sichtbare Liste
|
|
521
|
+
* und das Hamburger-Menü. Die Methode fügt Elemente nacheinander hinzu und prüft auf
|
|
522
|
+
* Überlauf (Overflow), um zu bestimmen, welche Elemente verschoben werden müssen.
|
|
523
|
+
* @private
|
|
524
|
+
* @this {SiteNavigation}
|
|
525
|
+
*/
|
|
518
526
|
function populateTabs() {
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
527
|
+
|
|
528
|
+
const visibleList = this[visibleElementsSymbol];
|
|
529
|
+
const hiddenList = this[hiddenElementsSymbol];
|
|
530
|
+
const hamburgerButton = this[hamburgerButtonSymbol];
|
|
531
|
+
const navEl = this[navElementSymbol];
|
|
532
|
+
|
|
533
|
+
const topLevelUl = [...getSlottedElements.call(this, "ul")].find(
|
|
534
|
+
(ul) => ul.parentElement === this,
|
|
535
|
+
);
|
|
536
|
+
|
|
537
|
+
visibleList.innerHTML = "";
|
|
538
|
+
hiddenList.innerHTML = "";
|
|
539
|
+
hamburgerButton.style.display = "none";
|
|
540
|
+
this.style.visibility = "hidden";
|
|
541
|
+
|
|
542
|
+
if (!topLevelUl) {
|
|
543
|
+
this.style.visibility = "visible";
|
|
544
|
+
return; // Nichts zu tun
|
|
545
|
+
}
|
|
546
|
+
const sourceItems = Array.from(topLevelUl.children).filter(
|
|
547
|
+
(n) => n.tagName === "LI",
|
|
548
|
+
);
|
|
549
|
+
if (sourceItems.length === 0) {
|
|
550
|
+
this.style.visibility = "visible";
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
const navWidth = navEl.clientWidth;
|
|
556
|
+
|
|
557
|
+
const originalDisplay = hamburgerButton.style.display;
|
|
558
|
+
hamburgerButton.style.visibility = "hidden";
|
|
559
|
+
hamburgerButton.style.display = "flex";
|
|
560
|
+
const hamburgerWidth =
|
|
561
|
+
Math.ceil(hamburgerButton.getBoundingClientRect().width) || 0;
|
|
562
|
+
hamburgerButton.style.display = originalDisplay;
|
|
563
|
+
hamburgerButton.style.visibility = "visible";
|
|
564
|
+
|
|
565
|
+
navEl.style.overflow = "hidden";
|
|
566
|
+
visibleList.style.flexWrap = "nowrap";
|
|
567
|
+
visibleList.style.visibility = "hidden"; // Inhalt der Liste während Manipulation ausblenden
|
|
568
|
+
|
|
569
|
+
const fit = [];
|
|
570
|
+
const rest = [];
|
|
571
|
+
let hasOverflow = false;
|
|
572
|
+
|
|
573
|
+
for (let i = 0; i < sourceItems.length; i++) {
|
|
574
|
+
const item = sourceItems[i];
|
|
575
|
+
|
|
576
|
+
if (hasOverflow) {
|
|
577
|
+
rest.push(item);
|
|
578
|
+
continue;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
const liClone = cloneNavItem(item);
|
|
582
|
+
visibleList.appendChild(liClone);
|
|
583
|
+
|
|
584
|
+
const requiredWidth = liClone.offsetLeft + liClone.offsetWidth;
|
|
585
|
+
const availableWidth = navWidth - hamburgerWidth;
|
|
586
|
+
const SAFETY_MARGIN = 1; // 1px Sicherheitsmarge für Subpixel-Rendering
|
|
587
|
+
|
|
588
|
+
if (requiredWidth > availableWidth + SAFETY_MARGIN) {
|
|
589
|
+
hasOverflow = true;
|
|
590
|
+
rest.push(item);
|
|
591
|
+
} else {
|
|
592
|
+
fit.push(item);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
if (fit.length > 0 && rest.length > 0) {
|
|
597
|
+
const lastVisibleItem = visibleList.children[fit.length - 1];
|
|
598
|
+
const visibleItemsWidth =
|
|
599
|
+
lastVisibleItem.offsetLeft + lastVisibleItem.offsetWidth;
|
|
600
|
+
|
|
601
|
+
const firstHiddenItemClone = cloneNavItem(rest[0]);
|
|
602
|
+
const submenu = firstHiddenItemClone.querySelector(
|
|
603
|
+
"ul, div[part='mega-menu']",
|
|
604
|
+
);
|
|
605
|
+
if (submenu) submenu.style.display = "none";
|
|
606
|
+
|
|
607
|
+
visibleList.appendChild(firstHiddenItemClone);
|
|
608
|
+
const firstHiddenItemWidth =
|
|
609
|
+
firstHiddenItemClone.getBoundingClientRect().width;
|
|
610
|
+
visibleList.removeChild(firstHiddenItemClone);
|
|
611
|
+
|
|
612
|
+
const gap = parseFloat(getComputedStyle(visibleList).gap || "0") || 0;
|
|
613
|
+
if (visibleItemsWidth + gap + firstHiddenItemWidth <= navWidth) {
|
|
614
|
+
fit.push(rest.shift());
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
navEl.style.overflow = "";
|
|
619
|
+
visibleList.style.flexWrap = "";
|
|
620
|
+
visibleList.innerHTML = "";
|
|
621
|
+
|
|
622
|
+
if (fit.length) {
|
|
623
|
+
const clonedVisible = fit.map(cloneNavItem);
|
|
624
|
+
visibleList.append(...clonedVisible);
|
|
625
|
+
visibleList
|
|
626
|
+
.querySelectorAll(":scope > li")
|
|
627
|
+
.forEach((li) => setupSubmenu.call(this, li, "visible", 1));
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
if (rest.length) {
|
|
631
|
+
const clonedHidden = rest.map(cloneNavItem);
|
|
632
|
+
hiddenList.append(...clonedHidden);
|
|
633
|
+
hamburgerButton.style.display = "flex";
|
|
634
|
+
hiddenList
|
|
635
|
+
.querySelectorAll(":scope > li")
|
|
636
|
+
.forEach((li) => setupSubmenu.call(this, li, "hidden", 1));
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
visibleList.style.visibility = "visible";
|
|
640
|
+
this.style.visibility = "visible";
|
|
641
|
+
fireCustomEvent(this, "monster-layout-change", {
|
|
642
|
+
visibleItems: fit,
|
|
643
|
+
hiddenItems: rest,
|
|
644
|
+
});
|
|
609
645
|
}
|
|
610
646
|
|
|
611
647
|
/**
|
|
@@ -615,7 +651,7 @@ function populateTabs() {
|
|
|
615
651
|
* @returns {string} The combined string.
|
|
616
652
|
*/
|
|
617
653
|
function html(strings) {
|
|
618
|
-
|
|
654
|
+
return strings.join("");
|
|
619
655
|
}
|
|
620
656
|
|
|
621
657
|
/**
|
|
@@ -624,7 +660,7 @@ function html(strings) {
|
|
|
624
660
|
* @returns {string} The HTML template string.
|
|
625
661
|
*/
|
|
626
662
|
function getTemplate() {
|
|
627
|
-
|
|
663
|
+
return html`<div data-monster-role="control" part="control">
|
|
628
664
|
<nav data-monster-role="navigation" role="navigation" part="nav">
|
|
629
665
|
<ul id="visible-elements" part="visible-list"></ul>
|
|
630
666
|
</nav>
|