@madj2k/fe-frontend-kit 2.0.36 → 2.0.38
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/menus/flyout-menu/flyout-menu-2.0.js +110 -33
- package/package.json +1 -1
- package/readme.md +82 -1
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* @author Steffen Kroggel <developer@steffenkroggel.de>
|
|
9
9
|
* @copyright 2025 Steffen Kroggel
|
|
10
|
-
* @version 2.0.
|
|
10
|
+
* @version 2.0.3
|
|
11
11
|
* @license GNU General Public License v3.0
|
|
12
12
|
* @see https://www.gnu.org/licenses/gpl-3.0.en.html
|
|
13
13
|
*
|
|
@@ -50,10 +50,13 @@ class Madj2kFlyoutMenu {
|
|
|
50
50
|
menuContainerClass: "js-flyout-container",
|
|
51
51
|
menuInnerClass: "js-flyout-inner",
|
|
52
52
|
heightCalculationClass: 'calculate',
|
|
53
|
+
hoverParentClass: 'nav-main',
|
|
53
54
|
heightMode: 'full',
|
|
55
|
+
eventMode: 'click',
|
|
54
56
|
paddingBehavior: 0,
|
|
55
57
|
paddingViewPortMinWidth: 0,
|
|
56
|
-
animationDuration: 500
|
|
58
|
+
animationDuration: 500,
|
|
59
|
+
scrollHelper: true,
|
|
57
60
|
};
|
|
58
61
|
|
|
59
62
|
this.settings = Object.assign({}, defaults, options);
|
|
@@ -73,6 +76,8 @@ class Madj2kFlyoutMenu {
|
|
|
73
76
|
this.settings.$menuContainer = this.settings.$menu.querySelector(`.${this.settings.menuContainerClass}`);
|
|
74
77
|
this.settings.$menuInner = this.settings.$menu.querySelector(`.${this.settings.menuInnerClass}`);
|
|
75
78
|
|
|
79
|
+
// Bind persistent event handlers
|
|
80
|
+
|
|
76
81
|
this.initNoScrollHelper();
|
|
77
82
|
this.resizeAndPositionMenu();
|
|
78
83
|
this.paddingMenu();
|
|
@@ -111,25 +116,102 @@ class Madj2kFlyoutMenu {
|
|
|
111
116
|
}
|
|
112
117
|
}
|
|
113
118
|
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Handles mouseleave on menu container
|
|
122
|
+
* @param {MouseEvent} e
|
|
123
|
+
*/
|
|
124
|
+
containerLeaveEvent(e) {
|
|
125
|
+
const to = e.relatedTarget;
|
|
126
|
+
const parentContainer = this.$element.closest(this.settings.hoverParentSelector) || null;
|
|
127
|
+
if (to && this.$element.contains(to)) return;
|
|
128
|
+
if (to && parentContainer?.contains(to)) return;
|
|
129
|
+
if (this.$element.classList.contains(this.settings.openStatusClass)) {
|
|
130
|
+
document.dispatchEvent(new CustomEvent('madj2k-flyoutmenu-close'));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Handles mouseleave on trigger element
|
|
136
|
+
* @param {MouseEvent} e
|
|
137
|
+
*/
|
|
138
|
+
triggerLeaveEvent(e) {
|
|
139
|
+
const to = e.relatedTarget;
|
|
140
|
+
const parentContainer = this.$element.closest('.' + this.settings.hoverParentClass) || null;
|
|
141
|
+
|
|
142
|
+
if (to && this.settings.$menu.contains(to)) return;
|
|
143
|
+
if (to && parentContainer?.contains(to)) return;
|
|
144
|
+
if (this.$element.classList.contains(this.settings.openStatusClass)) {
|
|
145
|
+
document.dispatchEvent(new CustomEvent('madj2k-flyoutmenu-close'));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Handles touchstart outside the flyout menu
|
|
151
|
+
* @param {TouchEvent} e
|
|
152
|
+
*/
|
|
153
|
+
touchStartEvent(e) {
|
|
154
|
+
const target = e.target;
|
|
155
|
+
if (!target) return;
|
|
156
|
+
if (this.settings.$menuInner.contains(target)) return;
|
|
157
|
+
if (this.$element.classList.contains(this.settings.openStatusClass)) {
|
|
158
|
+
document.dispatchEvent(new CustomEvent('madj2k-flyoutmenu-close'));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
114
162
|
/**
|
|
115
163
|
* Binds all necessary event listeners
|
|
116
164
|
*/
|
|
117
165
|
bindEvents() {
|
|
118
166
|
if (this.settings.$closeBtn) {
|
|
119
|
-
this.settings
|
|
167
|
+
const closeEvent = this.settings.eventMode === 'mouseover' ? 'mouseenter' : 'click';
|
|
168
|
+
this.settings.$closeBtn.addEventListener(closeEvent, e => this.closeEvent(e));
|
|
120
169
|
this.settings.$closeBtn.addEventListener('keydown', e => this.keyboardEvent(e));
|
|
121
170
|
}
|
|
122
171
|
|
|
123
|
-
this
|
|
124
|
-
|
|
172
|
+
if (this.settings.eventMode === 'mouseover') {
|
|
173
|
+
this.$element.addEventListener('mouseenter', e => this.openEvent(e));
|
|
174
|
+
this.$element.addEventListener('mouseleave',e => this.triggerLeaveEvent(e));
|
|
175
|
+
this.settings.$menuContainer?.addEventListener('mouseleave', e => this.containerLeaveEvent(e));
|
|
176
|
+
document.addEventListener('touchstart', e => this.touchStartEvent(e), { passive: true });
|
|
177
|
+
} else {
|
|
178
|
+
this.$element.addEventListener('click', e => this.toggleEvent(e));
|
|
179
|
+
}
|
|
125
180
|
|
|
181
|
+
this.$element.addEventListener('keydown', e => this.keyboardEvent(e));
|
|
126
182
|
this.settings.$menu.querySelectorAll('a,button,input,textarea,select')
|
|
127
183
|
.forEach(el => el.addEventListener('keydown', e => this.keyboardEvent(e)));
|
|
128
|
-
|
|
129
184
|
document.addEventListener('madj2k-flyoutmenu-close', e => this.closeEvent(e));
|
|
130
185
|
document.addEventListener('madj2k-flyoutmenu-resize', e => this.resizeAndPositionMenuEvent(e));
|
|
131
186
|
}
|
|
132
187
|
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Removes all event listeners (including dynamic hover/touch)
|
|
191
|
+
*/
|
|
192
|
+
destroyEvents() {
|
|
193
|
+
if (this.settings.$closeBtn) {
|
|
194
|
+
const closeEvent = this.settings.eventMode === 'mouseover' ? 'mouseenter' : 'click';
|
|
195
|
+
this.settings.$closeBtn.removeEventListener(closeEvent, e => this.closeEvent(e));
|
|
196
|
+
this.settings.$closeBtn.removeEventListener('keydown', e => this.keyboardEvent(e));
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (this.settings.eventMode === 'mouseover') {
|
|
200
|
+
this.$element.removeEventListener('mouseenter', e => this.openEvent(e));
|
|
201
|
+
this.settings.$menuContainer?.removeEventListener('mouseleave', e => this.containerLeaveEvent(e));
|
|
202
|
+
document.removeEventListener('touchstart', e => this.touchStartEvent(e), { passive: true });
|
|
203
|
+
} else {
|
|
204
|
+
this.$element.removeEventListener('click', e => this.toggleEvent(e));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
this.$element.removeEventListener('keydown', e => this.keyboardEvent(e));
|
|
208
|
+
this.settings.$menu.querySelectorAll('a,button,input,textarea,select')
|
|
209
|
+
.forEach(el => el.removeEventListener('keydown', e => this.keyboardEvent(e)));
|
|
210
|
+
document.removeEventListener('madj2k-flyoutmenu-close', e => this.closeEvent(e));
|
|
211
|
+
document.removeEventListener('madj2k-flyoutmenu-resize', e => this.resizeAndPositionMenuEvent(e));
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
|
|
133
215
|
/**
|
|
134
216
|
* Initializes ResizeObserver to auto-resize menu
|
|
135
217
|
*/
|
|
@@ -153,24 +235,6 @@ class Madj2kFlyoutMenu {
|
|
|
153
235
|
}
|
|
154
236
|
}
|
|
155
237
|
|
|
156
|
-
/**
|
|
157
|
-
* Unbinds all event listeners
|
|
158
|
-
*/
|
|
159
|
-
destroyEvents() {
|
|
160
|
-
if (this.settings.$closeBtn) {
|
|
161
|
-
this.settings.$closeBtn.removeEventListener('click', e => this.closeEvent(e));
|
|
162
|
-
this.settings.$closeBtn.removeEventListener('keydown', e => this.keyboardEvent(e));
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
this.$element.removeEventListener('click', e => this.toggleEvent(e));
|
|
166
|
-
this.$element.removeEventListener('keydown', e => this.keyboardEvent(e));
|
|
167
|
-
|
|
168
|
-
this.settings.$menu.querySelectorAll('a,button,input,textarea,select')
|
|
169
|
-
.forEach(el => el.removeEventListener('keydown', e => this.keyboardEvent(e)));
|
|
170
|
-
|
|
171
|
-
document.removeEventListener('madj2k-flyoutmenu-close', e => this.closeEvent(e));
|
|
172
|
-
document.removeEventListener('madj2k-flyoutmenu-resize', e => this.resizeAndPositionMenuEvent(e));
|
|
173
|
-
}
|
|
174
238
|
|
|
175
239
|
/**
|
|
176
240
|
* Destroys ResizeObserver
|
|
@@ -227,6 +291,16 @@ class Madj2kFlyoutMenu {
|
|
|
227
291
|
}
|
|
228
292
|
|
|
229
293
|
|
|
294
|
+
/**
|
|
295
|
+
* Handles close event
|
|
296
|
+
* @param {Event} e - The close event
|
|
297
|
+
*/
|
|
298
|
+
openEvent(e) {
|
|
299
|
+
e.preventDefault();
|
|
300
|
+
this.open();
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
|
|
230
304
|
/**
|
|
231
305
|
* Opens the flyout menu
|
|
232
306
|
*/
|
|
@@ -408,15 +482,19 @@ class Madj2kFlyoutMenu {
|
|
|
408
482
|
* Initializes the no-scroll helper elements
|
|
409
483
|
*/
|
|
410
484
|
initNoScrollHelper() {
|
|
411
|
-
const body = document.body;
|
|
412
|
-
let helper = body.querySelector('.no-scroll-helper');
|
|
413
|
-
const content = document.querySelector(`.${this.settings.contentSectionClass}`);
|
|
414
485
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
486
|
+
// heightMode "full" with deprecated fullHeight-setting as fallback
|
|
487
|
+
if (this.settings.scrollHelper) {
|
|
488
|
+
const body = document.body;
|
|
489
|
+
let helper = body.querySelector('.no-scroll-helper');
|
|
490
|
+
const content = document.querySelector(`.${this.settings.contentSectionClass}`);
|
|
491
|
+
|
|
492
|
+
if (!helper) {
|
|
493
|
+
if (content) {
|
|
494
|
+
content.innerHTML = `<div class="no-scroll-helper"><div class="no-scroll-helper-inner">${content.innerHTML}</div></div>`;
|
|
495
|
+
} else {
|
|
496
|
+
body.innerHTML = `<div class="no-scroll-helper"><div class="no-scroll-helper-inner">${body.innerHTML}</div></div>`;
|
|
497
|
+
}
|
|
420
498
|
}
|
|
421
499
|
}
|
|
422
500
|
}
|
|
@@ -451,7 +529,6 @@ class Madj2kFlyoutMenu {
|
|
|
451
529
|
body.classList.remove(this.settings.openStatusBodyClassOverflow);
|
|
452
530
|
window.scrollTo({top: scrollTop, behavior: 'instant'});
|
|
453
531
|
}
|
|
454
|
-
|
|
455
532
|
}
|
|
456
533
|
}
|
|
457
534
|
}
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -79,7 +79,7 @@ CSS:
|
|
|
79
79
|
</nav>
|
|
80
80
|
</div>
|
|
81
81
|
```
|
|
82
|
-
IMPORTANT: If the siteheader is positioned with ```position:fixed
|
|
82
|
+
IMPORTANT: If the siteheader is positioned with ```position:fixed``` and you are using full-height-mode (which is the default) you have to switch that to ```position:absolute``` when the menu is opened.
|
|
83
83
|
Otherwise in the opened menu the scrolling won't work.
|
|
84
84
|
```
|
|
85
85
|
.flyout-open {
|
|
@@ -88,6 +88,57 @@ Otherwise in the opened menu the scrolling won't work.
|
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
```
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
## Options Reference
|
|
94
|
+
|
|
95
|
+
### State & Animation Classes
|
|
96
|
+
|
|
97
|
+
| Option | Type | Default | Description |
|
|
98
|
+
|--------|------|---------|-------------|
|
|
99
|
+
| openStatusClass | string | 'open' | Applied to trigger and menu when open. |
|
|
100
|
+
| animationOpenStatusClass | string | 'opening' | Applied during opening animation. |
|
|
101
|
+
| animationCloseStatusClass | string | 'closing' | Applied during closing animation. |
|
|
102
|
+
| animationBodyClassPrefix | string | 'flyout' | Prefix for body animation classes like `flyout-opening`. |
|
|
103
|
+
| openStatusBodyClass | string | 'flyout-open' | Applied to `body` when the flyout is open. |
|
|
104
|
+
| openStatusBodyClassOverflow | string | 'flyout-open-overflow' | Applied when page height exceeds viewport (used in scroll-locking). |
|
|
105
|
+
|
|
106
|
+
### CSS Class Selectors
|
|
107
|
+
|
|
108
|
+
| Option | Type | Default | Description |
|
|
109
|
+
|------------------------|------|-----------------------|-----------------------------------------------------------------|
|
|
110
|
+
| contentSectionClass | string | 'js-main-content' | Content wrapper used when creating no-scroll helper. |
|
|
111
|
+
| menuClass | string | 'js-flyout' | Menu root element class. |
|
|
112
|
+
| menuToggleClass | string | 'js-flyout-toggle' | Class for toggle buttons. |
|
|
113
|
+
| menuCloseClass | string | 'js-flyout-close' | Class for close buttons inside the flyout. |
|
|
114
|
+
| menuContainerClass | string | 'js-flyout-container' | Container used for slide animations (`top` transition). |
|
|
115
|
+
| menuInnerClass | string | 'js-flyout-inner' | Inner content wrapper. Observed via ResizeObserver. |
|
|
116
|
+
| heightCalculationClass | string | 'calculate' | Temporary class used during height determination. |
|
|
117
|
+
| hoverParentClass | string | 'nav-main' | Class of main container of menu (used in eventMode: 'moueover'. |
|
|
118
|
+
|
|
119
|
+
### Height & Size Behavior
|
|
120
|
+
|
|
121
|
+
| Option | Type | Default | Description |
|
|
122
|
+
|--------|------|---------|-------------|
|
|
123
|
+
| heightMode | 'full' \| 'maxContent' | 'full' | Determines height behavior of the flyout. |
|
|
124
|
+
| animationDuration | number | 500 | Animation duration in milliseconds. |
|
|
125
|
+
|
|
126
|
+
### Padding & Layout Behavior
|
|
127
|
+
|
|
128
|
+
| Option | Type | Default | Description |
|
|
129
|
+
|--------|------|---------|-------------|
|
|
130
|
+
| paddingBehavior | number | 0 | Controls dynamic horizontal padding. |
|
|
131
|
+
| paddingViewPortMinWidth | number | 0 | Minimum viewport width required before padding applies. |
|
|
132
|
+
| scrollHelper | boolean | true | Creates additional wrapper structure to enable scroll-locking. |
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
### Event Handling
|
|
136
|
+
|
|
137
|
+
| Option | Type | Default | Description |
|
|
138
|
+
|--------|------|-------------|
|
|
139
|
+
| `eventMode` | `string` | `'click'` | Default event used for toggling the menu. Can be set to `click` or `mouseover`. |
|
|
140
|
+
|
|
141
|
+
|
|
91
142
|
## Special: blur/gray effect for background
|
|
92
143
|
* In order to achieve a blur/gray-effect for the background we add the following DIV to the main-content section:
|
|
93
144
|
```
|
|
@@ -199,6 +250,36 @@ CSS:
|
|
|
199
250
|
```
|
|
200
251
|
|
|
201
252
|
|
|
253
|
+
## Options Reference
|
|
254
|
+
|
|
255
|
+
### State & Structural Classes
|
|
256
|
+
|
|
257
|
+
| Option | Type | Default | Description |
|
|
258
|
+
|--------|------|---------|-------------|
|
|
259
|
+
| **openStatusClass** | `string` | `'open'` | Applied to menu, wrapper, and toggle when the pulldown is open. |
|
|
260
|
+
| **menuClass** | `string` | `'js-pulldown'` | Root class of the pulldown menu. Must match your HTML structure. |
|
|
261
|
+
| **menuToggleClass** | `string` | `'js-pulldown-toggle'` | Toggle buttons that control pulldown menus and also close others. |
|
|
262
|
+
| **menuWrapClass** | `string` | `'js-pulldown-wrap'` | Optional wrapper container that receives open/closed states. |
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
### Animation & Timing
|
|
266
|
+
|
|
267
|
+
| Option | Type | Default | Description |
|
|
268
|
+
|--------|------|---------|-------------|
|
|
269
|
+
| **animationDuration** | `number` | `500` | Reserved for future open/close animation timing. Currently no CSS transition is applied by JavaScript. |
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
### Internal Element References
|
|
273
|
+
|
|
274
|
+
These are automatically detected:
|
|
275
|
+
|
|
276
|
+
| Property | Description |
|
|
277
|
+
|----------|-------------|
|
|
278
|
+
| **menu** | The menu element referenced via `aria-controls`. |
|
|
279
|
+
| **menuWrap** | Closest parent using `menuWrapClass`. |
|
|
280
|
+
| **toggleElement** | The trigger element passed to the constructor. |
|
|
281
|
+
|
|
282
|
+
|
|
202
283
|
# JS: Banner
|
|
203
284
|
A lightweight class to show a full-page overlay (banner, popup, hint or cookie layer),
|
|
204
285
|
with opening and closing animation and optional cookie persistence.
|