@madj2k/fe-frontend-kit 2.0.24 → 2.0.26
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/index.js +1 -0
- package/index.scss +1 -0
- package/menus/flyout-menu/flyout-menu-2.0.js +59 -35
- package/menus/flyout-menu/flyout-menu-2.0.scss +11 -1
- package/package.json +1 -1
- package/readme.md +199 -167
- package/sass/bootstrap-5.3/10_mixins/_nav.scss +36 -3
- package/tools/simple-fade-slider/index.js +2 -0
- package/tools/simple-fade-slider/index.scss +1 -0
- package/tools/simple-fade-slider/simple-fade-slider-2.0.js +148 -0
- package/tools/simple-fade-slider/simple-fade-slider-2.0.scss +18 -0
package/index.js
CHANGED
|
@@ -4,6 +4,7 @@ export { Madj2kBetterResizeEvent } from './tools/better-resize-event';
|
|
|
4
4
|
export { Madj2kOwlThumbnail } from './tools/owl-thumbnail';
|
|
5
5
|
export { Madj2kResizeEnd } from './tools/resize-end';
|
|
6
6
|
export { Madj2kScrolling } from './tools/scrolling';
|
|
7
|
+
export { Madj2kSimpleFadeSlider } from './tools/simple-fade-slider';
|
|
7
8
|
export { Madj2kToggledOverlay } from './tools/toggled-overlay';
|
|
8
9
|
|
|
9
10
|
// Menus
|
package/index.scss
CHANGED
|
@@ -49,6 +49,7 @@ class Madj2kFlyoutMenu {
|
|
|
49
49
|
menuCloseClass: "js-flyout-close",
|
|
50
50
|
menuContainerClass: "js-flyout-container",
|
|
51
51
|
menuInnerClass: "js-flyout-inner",
|
|
52
|
+
heightCalculationClass: 'calculate',
|
|
52
53
|
heightMode: 'full',
|
|
53
54
|
paddingBehavior: 0,
|
|
54
55
|
paddingViewPortMinWidth: 0,
|
|
@@ -230,7 +231,9 @@ class Madj2kFlyoutMenu {
|
|
|
230
231
|
* Opens the flyout menu
|
|
231
232
|
*/
|
|
232
233
|
open() {
|
|
233
|
-
const {$menu, $element, animationOpenStatusClass, openStatusClass} = this.settings;
|
|
234
|
+
const {$menu, $element, animationOpenStatusClass, openStatusClass, openStatusBodyClass, animationBodyClassPrefix} = this.settings;
|
|
235
|
+
const $body = document.body;
|
|
236
|
+
|
|
234
237
|
if (!$menu.classList.contains(openStatusClass) && !$menu.classList.contains(animationOpenStatusClass)) {
|
|
235
238
|
document.dispatchEvent(new CustomEvent('madj2k-slidemenu-close'));
|
|
236
239
|
document.dispatchEvent(new CustomEvent('madj2k-pulldownmenu-close'));
|
|
@@ -244,7 +247,8 @@ class Madj2kFlyoutMenu {
|
|
|
244
247
|
$menu.classList.add(openStatusClass, animationOpenStatusClass);
|
|
245
248
|
$element.classList.add(openStatusClass, animationOpenStatusClass);
|
|
246
249
|
$element.setAttribute('aria-expanded', true);
|
|
247
|
-
|
|
250
|
+
$body.classList.add(openStatusBodyClass);
|
|
251
|
+
$body.classList.add(`${animationBodyClassPrefix}-${animationOpenStatusClass}`);
|
|
248
252
|
|
|
249
253
|
this.settings.$menuContainer.style.transition = `top ${this.settings.animationDuration}ms`;
|
|
250
254
|
this.settings.$menuContainer.style.top = '0';
|
|
@@ -252,7 +256,7 @@ class Madj2kFlyoutMenu {
|
|
|
252
256
|
setTimeout(() => {
|
|
253
257
|
$menu.classList.remove(animationOpenStatusClass);
|
|
254
258
|
$element.classList.remove(animationOpenStatusClass);
|
|
255
|
-
|
|
259
|
+
$body.classList.remove(`${animationBodyClassPrefix}-${animationOpenStatusClass}`);
|
|
256
260
|
document.dispatchEvent(new CustomEvent('madj2k-flyoutmenu-opened'));
|
|
257
261
|
}, this.settings.animationDuration);
|
|
258
262
|
}
|
|
@@ -284,17 +288,20 @@ class Madj2kFlyoutMenu {
|
|
|
284
288
|
* Closes the flyout menu
|
|
285
289
|
*/
|
|
286
290
|
close() {
|
|
287
|
-
const {$menu, $element, animationCloseStatusClass, openStatusClass} = this.settings;
|
|
291
|
+
const {$menu, $element, animationCloseStatusClass, openStatusClass, openStatusBodyClass, animationBodyClassPrefix} = this.settings;
|
|
292
|
+
const $body = document.body;
|
|
293
|
+
|
|
288
294
|
if ($menu.classList.contains(openStatusClass) && !$menu.classList.contains(animationCloseStatusClass)) {
|
|
289
295
|
document.dispatchEvent(new CustomEvent('madj2k-flyoutmenu-closing'));
|
|
290
296
|
|
|
297
|
+
this.toggleNoScroll();
|
|
298
|
+
|
|
291
299
|
$menu.classList.add(animationCloseStatusClass);
|
|
292
300
|
$element.classList.add(animationCloseStatusClass);
|
|
293
|
-
document.body.classList.add(`${this.settings.animationBodyClassPrefix}-${animationCloseStatusClass}`);
|
|
294
301
|
$element.classList.remove(openStatusClass);
|
|
295
302
|
$element.setAttribute('aria-expanded', false);
|
|
296
|
-
|
|
297
|
-
|
|
303
|
+
$body.classList.add(`${animationBodyClassPrefix}-${animationCloseStatusClass}`);
|
|
304
|
+
$body.classList.remove(openStatusBodyClass);
|
|
298
305
|
|
|
299
306
|
this.settings.$menuContainer.style.transition = `top ${this.settings.animationDuration}ms`;
|
|
300
307
|
this.settings.$menuContainer.style.top = '-100%';
|
|
@@ -302,7 +309,7 @@ class Madj2kFlyoutMenu {
|
|
|
302
309
|
setTimeout(() => {
|
|
303
310
|
$menu.classList.remove(openStatusClass, animationCloseStatusClass);
|
|
304
311
|
$element.classList.remove(animationCloseStatusClass);
|
|
305
|
-
|
|
312
|
+
$body.classList.remove(`${animationBodyClassPrefix}-${animationCloseStatusClass}`);
|
|
306
313
|
document.dispatchEvent(new CustomEvent('madj2k-flyoutmenu-closed'));
|
|
307
314
|
}, this.settings.animationDuration);
|
|
308
315
|
}
|
|
@@ -334,29 +341,41 @@ class Madj2kFlyoutMenu {
|
|
|
334
341
|
console.warn('Option "fullHeight" is deprecated. Please use "heightMode" instead.');
|
|
335
342
|
}
|
|
336
343
|
|
|
337
|
-
|
|
344
|
+
// remove max-height for correct calculation
|
|
345
|
+
this.settings.$menu.classList.add(this.settings.heightCalculationClass);
|
|
346
|
+
|
|
347
|
+
const innerHeight = this.settings.$menuInner.offsetHeight || this.settings.$menu.offsetHeight;
|
|
338
348
|
const refObj = this.settings.$positionReference || this.$element;
|
|
339
349
|
const refPos = refObj.getBoundingClientRect();
|
|
340
|
-
const
|
|
350
|
+
const refHeight = refObj.offsetHeight;
|
|
351
|
+
const flyoutTop = refPos.top + refHeight;
|
|
352
|
+
let newHeight = '';
|
|
341
353
|
|
|
342
354
|
// heightMode "full" with deprecated fullHeight-setting as fallback
|
|
343
355
|
if (this.settings.heightMode === 'full' || this.settings.fullHeight === true) {
|
|
356
|
+
|
|
357
|
+
// we use the viewport height because it may be the case that accordions are used here
|
|
344
358
|
const viewPortHeight = window.innerHeight;
|
|
345
|
-
if (
|
|
346
|
-
|
|
359
|
+
if (innerHeight < (viewPortHeight - flyoutTop)) {
|
|
360
|
+
newHeight = `calc(100vh - ${flyoutTop}px)`;
|
|
347
361
|
} else {
|
|
348
|
-
|
|
362
|
+
newHeight = `${innerHeight}px`;
|
|
349
363
|
}
|
|
350
364
|
|
|
351
365
|
} else if (this.settings.heightMode === 'maxContent') {
|
|
352
|
-
|
|
366
|
+
newHeight = `max-content`;
|
|
353
367
|
console.warn('heightMode: maxContent is not working on Apple Safari. Please use heightMode: full instead.');
|
|
354
368
|
|
|
355
369
|
} else {
|
|
356
|
-
|
|
370
|
+
newHeight = `${innerHeight}px`;
|
|
357
371
|
}
|
|
372
|
+
|
|
373
|
+
// set max-height again so that longer flyouts do not lead to scrolling on smaller ones
|
|
374
|
+
this.settings.$menu.classList.remove(this.settings.heightCalculationClass);
|
|
375
|
+
this.settings.$menu.style.height = newHeight;
|
|
358
376
|
}
|
|
359
377
|
|
|
378
|
+
|
|
360
379
|
/**
|
|
361
380
|
* Positions the menu based on the reference element
|
|
362
381
|
*/
|
|
@@ -406,28 +425,33 @@ class Madj2kFlyoutMenu {
|
|
|
406
425
|
* Toggles scroll behavior of the page
|
|
407
426
|
*/
|
|
408
427
|
toggleNoScroll() {
|
|
409
|
-
const body = document.body;
|
|
410
|
-
const helper = body.querySelector('.no-scroll-helper');
|
|
411
|
-
const inner = body.querySelector('.no-scroll-helper-inner');
|
|
412
|
-
let noScrollClass = this.settings.openStatusBodyClass;
|
|
413
428
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
429
|
+
// heightMode "full" with deprecated fullHeight-setting as fallback
|
|
430
|
+
if (this.settings.heightMode === 'full' || this.settings.fullHeight === true) {
|
|
431
|
+
const body = document.body;
|
|
432
|
+
const helper = body.querySelector('.no-scroll-helper');
|
|
433
|
+
const inner = body.querySelector('.no-scroll-helper-inner');
|
|
434
|
+
let noScrollClass ='';
|
|
435
|
+
|
|
436
|
+
if (document.documentElement.scrollHeight > window.innerHeight) {
|
|
437
|
+
noScrollClass = this.settings.openStatusBodyClassOverflow;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
if (!body.classList.contains(this.settings.openStatusBodyClass)) {
|
|
441
|
+
const scrollTop = -document.documentElement.scrollTop;
|
|
442
|
+
helper.setAttribute('data-scroll-top', scrollTop);
|
|
443
|
+
helper.style.cssText = 'position:relative;overflow:hidden;height:100vh;width:100%';
|
|
444
|
+
inner.style.cssText = `position:absolute;top:${scrollTop}px;height:100%;width:100%`;
|
|
445
|
+
body.classList.add(noScrollClass);
|
|
446
|
+
window.scrollTo({top: 0, behavior: 'instant'});
|
|
447
|
+
} else {
|
|
448
|
+
const scrollTop = parseInt(helper.getAttribute('data-scroll-top') || '0') * -1;
|
|
449
|
+
helper.removeAttribute('style');
|
|
450
|
+
inner.removeAttribute('style');
|
|
451
|
+
body.classList.remove(this.settings.openStatusBodyClassOverflow);
|
|
452
|
+
window.scrollTo({top: scrollTop, behavior: 'instant'});
|
|
453
|
+
}
|
|
417
454
|
|
|
418
|
-
if (!body.classList.contains(this.settings.openStatusBodyClass)) {
|
|
419
|
-
const scrollTop = -document.documentElement.scrollTop;
|
|
420
|
-
helper.setAttribute('data-scroll-top', scrollTop);
|
|
421
|
-
helper.style.cssText = 'position:relative;overflow:hidden;height:100vh;width:100%';
|
|
422
|
-
inner.style.cssText = `position:absolute;top:${scrollTop}px;height:100%;width:100%`;
|
|
423
|
-
body.classList.add(...noScrollClass.split(' '));
|
|
424
|
-
window.scrollTo({top: 0, behavior: 'instant'});
|
|
425
|
-
} else {
|
|
426
|
-
const scrollTop = parseInt(helper.getAttribute('data-scroll-top') || '0') * -1;
|
|
427
|
-
helper.removeAttribute('style');
|
|
428
|
-
inner.removeAttribute('style');
|
|
429
|
-
body.classList.remove(this.settings.openStatusBodyClass, this.settings.openStatusBodyClassOverflow);
|
|
430
|
-
window.scrollTo({top: scrollTop, behavior: 'instant'});
|
|
431
455
|
}
|
|
432
456
|
}
|
|
433
457
|
}
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
html, body {
|
|
2
|
+
height: 100.1%; // enforce scrollbar
|
|
3
|
+
}
|
|
4
|
+
|
|
1
5
|
.flyout {
|
|
2
6
|
position: absolute;
|
|
3
7
|
left: 0;
|
|
@@ -5,12 +9,18 @@
|
|
|
5
9
|
|
|
6
10
|
width: 100%;
|
|
7
11
|
z-index: -1;
|
|
8
|
-
overflow:hidden;
|
|
12
|
+
overflow: hidden;
|
|
9
13
|
visibility: hidden;
|
|
14
|
+
max-height: 0;
|
|
15
|
+
|
|
16
|
+
&.calculate {
|
|
17
|
+
max-height: none;
|
|
18
|
+
}
|
|
10
19
|
|
|
11
20
|
&.open {
|
|
12
21
|
z-index: 890;
|
|
13
22
|
visibility: visible;
|
|
23
|
+
max-height: none;
|
|
14
24
|
|
|
15
25
|
.flyout-container {
|
|
16
26
|
pointer-events: auto;
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -26,6 +26,179 @@ Or import individual mixins:
|
|
|
26
26
|
### Menu components (JS and SCSS):
|
|
27
27
|
Each menu component can be used separately.
|
|
28
28
|
|
|
29
|
+
### JS components (JS and SCSS):
|
|
30
|
+
Each JS component can be used separately.
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# Flyout-Navigation
|
|
34
|
+
## Usage
|
|
35
|
+
Integrate the CSS- and JS-file into your project. Make sure jQuery is included.
|
|
36
|
+
Then init the menu with
|
|
37
|
+
```
|
|
38
|
+
import { Madj2kFlyoutMenu } from '@madj2k/fe-frontend-kit/menus/flyout-menu';
|
|
39
|
+
document.querySelectorAll('.js-flyout-toggle').forEach((el) => {
|
|
40
|
+
new Madj2kFlyoutMenu(el, { animationDuration: 800 });
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
Optional for automatically closing of the flyout menu resizing the browser window:
|
|
44
|
+
```
|
|
45
|
+
import { Madj2kBetterResizeEvent } from '@madj2k/fe-frontend-kit/tools/better-resize-event';
|
|
46
|
+
document.addEventListener('madj2k-better-resize-event', () => {
|
|
47
|
+
document.dispatchEvent(new CustomEvent('madj2k-flyoutmenu-close'));
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
CSS:
|
|
52
|
+
```
|
|
53
|
+
@use '@madj2k/fe-frontend-kit/menus/flyout-menu' as *;
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Basic HTML
|
|
57
|
+
```
|
|
58
|
+
<div class="siteheader" id="siteheader">
|
|
59
|
+
|
|
60
|
+
[...]
|
|
61
|
+
|
|
62
|
+
<nav>
|
|
63
|
+
<button class="js-flyout-toggle"
|
|
64
|
+
aria-label="Open menu"
|
|
65
|
+
aria-controls="nav-mobile"
|
|
66
|
+
aria-haspopup="true"
|
|
67
|
+
aria-expanded="false">
|
|
68
|
+
<span class="icon-bars"></span>
|
|
69
|
+
</button>
|
|
70
|
+
<div class="flyout js-flyout"
|
|
71
|
+
id="nav-mobile"
|
|
72
|
+
data-position-ref="siteheader">
|
|
73
|
+
<div class="flyout-container js-flyout-container">
|
|
74
|
+
<div class="nav-mobile-inner js-flyout-inner">
|
|
75
|
+
CONTENT HERE
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
</nav>
|
|
80
|
+
</div>
|
|
81
|
+
```
|
|
82
|
+
IMPORTANT: If the siteheader is positioned with ```position:fixed```, you have to switch that to ```position:absolute``` when the menu is opened.
|
|
83
|
+
Otherwise in the opened menu the scrolling won't work.
|
|
84
|
+
```
|
|
85
|
+
.flyout-open {
|
|
86
|
+
.siteheader {
|
|
87
|
+
position:absolute;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
## Special: blur/gray effect for background
|
|
92
|
+
* In order to achieve a blur/gray-effect for the background we add the following DIV to the main-content section:
|
|
93
|
+
```
|
|
94
|
+
<div class="background-blur"></div>
|
|
95
|
+
```
|
|
96
|
+
Then we deactivate the fullHeight of the flyout menu and make the blurry background clickable
|
|
97
|
+
```
|
|
98
|
+
import { Madj2kFlyoutdownMenu } from '@madj2k/fe-frontend-kit/menus/flyout-menu';
|
|
99
|
+
document.querySelectorAll('.js-flyout-toggle').forEach((el) => {
|
|
100
|
+
new Madj2kFlyoutMenu(el, { fullHeight: false });
|
|
101
|
+
});
|
|
102
|
+
document.querySelector('.js-background-blur').addEventListener('click', function() {
|
|
103
|
+
document.dispatchEvent(new CustomEvent('madj2k-flyoutmenu-close'));
|
|
104
|
+
});
|
|
105
|
+
```
|
|
106
|
+
* And we need this additional CSS:
|
|
107
|
+
```
|
|
108
|
+
/**
|
|
109
|
+
* background-blur for opened flyout
|
|
110
|
+
*/
|
|
111
|
+
.background-blur {
|
|
112
|
+
position:fixed;
|
|
113
|
+
top:0;
|
|
114
|
+
left:0;
|
|
115
|
+
width: 100%;
|
|
116
|
+
height: 100%;
|
|
117
|
+
backdrop-filter: blur(1px) grayscale(100%);
|
|
118
|
+
background-color: rgba(255, 255, 255, 0.7);
|
|
119
|
+
transition: opacity 0.3s ease-in-out;
|
|
120
|
+
opacity:0;
|
|
121
|
+
z-index:-1;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.flyout-open,
|
|
125
|
+
.flyout-closing {
|
|
126
|
+
.background-blur {
|
|
127
|
+
z-index:90;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.flyout-open{
|
|
132
|
+
.background-blur {
|
|
133
|
+
opacity:1;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
# Pulldown-Navigation
|
|
141
|
+
## Usage
|
|
142
|
+
Integrate the CSS- and JS-file into your project. Make sure jQuery is included.
|
|
143
|
+
Then init the menu with
|
|
144
|
+
```
|
|
145
|
+
import { Madj2kPulldownMenu } from '@madj2k/fe-frontend-kit/menus/pulldown-menu';
|
|
146
|
+
document.querySelectorAll('.js-pulldown-toggle').forEach((el) => {
|
|
147
|
+
new Madj2kPulldownMenu(el);
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
Optional for automatically closing of the flyout menu resizing the browser window:
|
|
151
|
+
```
|
|
152
|
+
import { Madj2kBetterResizeEvent } from '@madj2k/fe-frontend-kit/tools/better-resize-event';
|
|
153
|
+
document.addEventListener('madj2k-better-resize-event', () => {
|
|
154
|
+
document.dispatchEvent(new CustomEvent('madj2k-pulldownmenu-close'));
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
CSS:
|
|
159
|
+
```
|
|
160
|
+
@use '@madj2k/fe-frontend-kit/menus/pulldown-menu' as *;
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Basic HTML
|
|
164
|
+
```
|
|
165
|
+
<div class="siteheader">
|
|
166
|
+
|
|
167
|
+
[...]
|
|
168
|
+
|
|
169
|
+
<nav class="pulldown-wrap js-pulldown-wrap">
|
|
170
|
+
<button class="pulldown-toggle js-pulldown-toggle"
|
|
171
|
+
aria-label="Open Menu"
|
|
172
|
+
aria-controls="nav-language"
|
|
173
|
+
aria-haspopup="true"
|
|
174
|
+
aria-expanded="false">
|
|
175
|
+
<span>Menu-Item Level 1</span>
|
|
176
|
+
</button>
|
|
177
|
+
|
|
178
|
+
<div class="pulldown js-pulldown" id="nav-language">
|
|
179
|
+
<div class="pulldown-inner">
|
|
180
|
+
<ul>
|
|
181
|
+
<!-- used to have the right padding-top of the pulldown -->
|
|
182
|
+
<li class="pulldown-hide">
|
|
183
|
+
<a href="#" tabIndex="-1"><span>Menu-Item Level 1</span></a>
|
|
184
|
+
</li>
|
|
185
|
+
<li>
|
|
186
|
+
<a href="#"><span>Menu-Item I Level 2</span></a>
|
|
187
|
+
</li>
|
|
188
|
+
<li>
|
|
189
|
+
<a href="#"><span>Menu-Item II Level 2</span></a>
|
|
190
|
+
</li>
|
|
191
|
+
<li>
|
|
192
|
+
...
|
|
193
|
+
</li>
|
|
194
|
+
</ul>
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
</nav>
|
|
198
|
+
</div>
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
|
|
29
202
|
# JS: Banner
|
|
30
203
|
A lightweight class to show a full-page overlay (banner, popup, hint or cookie layer),
|
|
31
204
|
with opening and closing animation and optional cookie persistence.
|
|
@@ -218,177 +391,36 @@ HTML:
|
|
|
218
391
|
</div>
|
|
219
392
|
```
|
|
220
393
|
|
|
221
|
-
#
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
new Madj2kFlyoutMenu(el, { animationDuration: 800 });
|
|
229
|
-
});
|
|
230
|
-
```
|
|
231
|
-
Optional for automatically closing of the flyout menu resizing the browser window:
|
|
232
|
-
```
|
|
233
|
-
import { Madj2kBetterResizeEvent } from '@madj2k/fe-frontend-kit/tools/better-resize-event';
|
|
234
|
-
document.addEventListener('madj2k-better-resize-event', () => {
|
|
235
|
-
document.dispatchEvent(new CustomEvent('madj2k-flyoutmenu-close'));
|
|
236
|
-
});
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
CSS:
|
|
240
|
-
```
|
|
241
|
-
@use '@madj2k/fe-frontend-kit/menus/flyout-menu' as *;
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
## Basic HTML
|
|
245
|
-
```
|
|
246
|
-
<div class="siteheader" id="siteheader">
|
|
247
|
-
|
|
248
|
-
[...]
|
|
249
|
-
|
|
250
|
-
<nav>
|
|
251
|
-
<button class="js-flyout-toggle"
|
|
252
|
-
aria-label="Open menu"
|
|
253
|
-
aria-controls="nav-mobile"
|
|
254
|
-
aria-haspopup="true"
|
|
255
|
-
aria-expanded="false">
|
|
256
|
-
<span class="icon-bars"></span>
|
|
257
|
-
</button>
|
|
258
|
-
<div class="flyout js-flyout"
|
|
259
|
-
id="nav-mobile"
|
|
260
|
-
data-position-ref="siteheader">
|
|
261
|
-
<div class="flyout-container js-flyout-container">
|
|
262
|
-
<div class="nav-mobile-inner js-flyout-inner">
|
|
263
|
-
CONTENT HERE
|
|
264
|
-
</div>
|
|
265
|
-
</div>
|
|
266
|
-
</div>
|
|
267
|
-
</nav>
|
|
268
|
-
</div>
|
|
269
|
-
```
|
|
270
|
-
IMPORTANT: If the siteheader is positioned with ```position:fixed```, you have to switch that to ```position:absolute``` when the menu is opened.
|
|
271
|
-
Otherwise in the opened menu the scrolling won't work.
|
|
272
|
-
```
|
|
273
|
-
.flyout-open {
|
|
274
|
-
.siteheader {
|
|
275
|
-
position:absolute;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
```
|
|
279
|
-
## Special: blur/gray effect for background
|
|
280
|
-
* In order to achieve a blur/gray-effect for the background we add the following DIV to the main-content section:
|
|
281
|
-
```
|
|
282
|
-
<div class="background-blur"></div>
|
|
283
|
-
```
|
|
284
|
-
Then we deactivate the fullHeight of the flyout menu and make the blurry background clickable
|
|
285
|
-
```
|
|
286
|
-
import { Madj2kFlyoutdownMenu } from '@madj2k/fe-frontend-kit/menus/flyout-menu';
|
|
287
|
-
document.querySelectorAll('.js-flyout-toggle').forEach((el) => {
|
|
288
|
-
new Madj2kFlyoutMenu(el, { fullHeight: false });
|
|
289
|
-
});
|
|
290
|
-
document.querySelector('.js-background-blur').addEventListener('click', function() {
|
|
291
|
-
document.dispatchEvent(new CustomEvent('madj2k-flyoutmenu-close'));
|
|
292
|
-
});
|
|
293
|
-
```
|
|
294
|
-
* And we need this additional CSS:
|
|
295
|
-
```
|
|
296
|
-
/**
|
|
297
|
-
* Prevent jumping because of scrollbar
|
|
298
|
-
*/
|
|
299
|
-
html,body {
|
|
300
|
-
min-height: 100.1%;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
* background-blur for opened flyout
|
|
305
|
-
*/
|
|
306
|
-
.background-blur {
|
|
307
|
-
position:fixed;
|
|
308
|
-
top:0;
|
|
309
|
-
left:0;
|
|
310
|
-
width: 100%;
|
|
311
|
-
height: 100%;
|
|
312
|
-
backdrop-filter: blur(1px) grayscale(100%);
|
|
313
|
-
background-color: rgba(255, 255, 255, 0.7);
|
|
314
|
-
transition: opacity 0.3s ease-in-out;
|
|
315
|
-
opacity:0;
|
|
316
|
-
z-index:-1;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
.flyout-open,
|
|
320
|
-
.flyout-closing {
|
|
321
|
-
.background-blur {
|
|
322
|
-
z-index:90;
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
.flyout-open{
|
|
327
|
-
.background-blur {
|
|
328
|
-
opacity:1;
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
|
|
394
|
+
# JS: Simple Fade Slider
|
|
395
|
+
A lightweight fade slider using opacity and z-index.
|
|
396
|
+
- Purely DOM based (no keyframes required)
|
|
397
|
+
- Automatic looping through an arbitrary number of slides
|
|
398
|
+
- Fully accessible with aria-hidden management
|
|
399
|
+
- Configurable via options
|
|
400
|
+
- Designed for CMS contexts with dynamic content
|
|
332
401
|
|
|
402
|
+
Init with available options:
|
|
333
403
|
```
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
import { Madj2kPulldownMenu } from '@madj2k/fe-frontend-kit/menus/pulldown-menu';
|
|
341
|
-
document.querySelectorAll('.js-pulldown-toggle').forEach((el) => {
|
|
342
|
-
new Madj2kPulldownMenu(el);
|
|
343
|
-
});
|
|
344
|
-
```
|
|
345
|
-
Optional for automatically closing of the flyout menu resizing the browser window:
|
|
346
|
-
```
|
|
347
|
-
import { Madj2kBetterResizeEvent } from '@madj2k/fe-frontend-kit/tools/better-resize-event';
|
|
348
|
-
document.addEventListener('madj2k-better-resize-event', () => {
|
|
349
|
-
document.dispatchEvent(new CustomEvent('madj2k-pulldownmenu-close'));
|
|
404
|
+
import { Madj2kSimpleFadeSlider } from '@madj2k/fe-frontend-kit/tools/simple-fade-slider';
|
|
405
|
+
const fadeSlider = new Madj2kSimpleFadeSlider('.js-fade-slider', {
|
|
406
|
+
duration: 8,
|
|
407
|
+
classSlide: 'fade-slider-item',
|
|
408
|
+
classVisible: 'is-visible',
|
|
409
|
+
debug: true
|
|
350
410
|
});
|
|
351
411
|
```
|
|
352
412
|
|
|
353
|
-
|
|
354
|
-
```
|
|
355
|
-
@use '@madj2k/fe-frontend-kit/menus/pulldown-menu' as *;
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
## Basic HTML
|
|
413
|
+
HTML:
|
|
359
414
|
```
|
|
360
|
-
<
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
<
|
|
365
|
-
<
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
</button>
|
|
372
|
-
|
|
373
|
-
<div class="pulldown js-pulldown" id="nav-language">
|
|
374
|
-
<div class="pulldown-inner">
|
|
375
|
-
<ul>
|
|
376
|
-
<!-- used to have the right padding-top of the pulldown -->
|
|
377
|
-
<li class="pulldown-hide">
|
|
378
|
-
<a href="#" tabIndex="-1"><span>Menu-Item Level 1</span></a>
|
|
379
|
-
</li>
|
|
380
|
-
<li>
|
|
381
|
-
<a href="#"><span>Menu-Item I Level 2</span></a>
|
|
382
|
-
</li>
|
|
383
|
-
<li>
|
|
384
|
-
<a href="#"><span>Menu-Item II Level 2</span></a>
|
|
385
|
-
</li>
|
|
386
|
-
<li>
|
|
387
|
-
...
|
|
388
|
-
</li>
|
|
389
|
-
</ul>
|
|
390
|
-
</div>
|
|
391
|
-
</div>
|
|
392
|
-
</nav>
|
|
393
|
-
</div>
|
|
415
|
+
<section class="fade-slider js-fade-slider" aria-label="Image gallery">
|
|
416
|
+
<div class="fade-slider-item">
|
|
417
|
+
<img src="img1.jpg" alt="Description 1">
|
|
418
|
+
</div>
|
|
419
|
+
<div class="fade-slider-item">
|
|
420
|
+
<img src="img2.jpg" alt="Description 2">
|
|
421
|
+
</div>
|
|
422
|
+
<div class="fade-slider-item">
|
|
423
|
+
<img src="img3.jpg" alt="Description 3">
|
|
424
|
+
</div>
|
|
425
|
+
</section>
|
|
394
426
|
```
|
|
@@ -268,11 +268,24 @@
|
|
|
268
268
|
/// @param {Color} $icon-color - Icon color (e.g., fill or text color). Defaults to `$color-primary`.
|
|
269
269
|
/// @param {Map} $icon-mappings - Map of icon toggle states (e.g., hamburger → close). Defaults to a predefined map.
|
|
270
270
|
///
|
|
271
|
-
/// @example html
|
|
271
|
+
/// @example html with icon font
|
|
272
|
+
/// <button class="nav-toggle" aria-expanded="false">
|
|
273
|
+
/// <i class="nav-toggle-icon icon-hamburger icon"></i>
|
|
274
|
+
/// </button>
|
|
275
|
+
///
|
|
276
|
+
/// @example html with icon sprite
|
|
272
277
|
/// <button class="nav-toggle" aria-expanded="false">
|
|
273
|
-
/// <i class="nav-toggle-icon
|
|
278
|
+
/// <i class="nav-toggle-icon">
|
|
279
|
+
/// <svg class="nav-toggle-icon-opened" aria-hidden="true" focusable="false">
|
|
280
|
+
/// <use href="sprite.svg#search"></use>
|
|
281
|
+
/// </svg>
|
|
282
|
+
/// <svg class="nav-toggle-icon-closed" aria-hidden="true" focusable="false">
|
|
283
|
+
/// <use href="sprite.svg#close"></use>
|
|
284
|
+
/// </svg>
|
|
285
|
+
/// </i>
|
|
274
286
|
/// </button>
|
|
275
287
|
///
|
|
288
|
+
///
|
|
276
289
|
/// @example scss
|
|
277
290
|
/// .nav-toggle {
|
|
278
291
|
/// @include nav-toggle(
|
|
@@ -305,6 +318,7 @@
|
|
|
305
318
|
display: flex;
|
|
306
319
|
align-items: center;
|
|
307
320
|
|
|
321
|
+
// variant with icons as font
|
|
308
322
|
&[aria-expanded="true"] {
|
|
309
323
|
@each $icon-class, $content in $icon-mappings {
|
|
310
324
|
.#{$icon-class} {
|
|
@@ -314,11 +328,30 @@
|
|
|
314
328
|
}
|
|
315
329
|
}
|
|
316
330
|
}
|
|
331
|
+
|
|
332
|
+
// variant with icons in a sprite
|
|
333
|
+
.nav-toggle-icon-opened {
|
|
334
|
+
display: none;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
&[aria-expanded="true"] {
|
|
338
|
+
.nav-toggle-icon-opened {
|
|
339
|
+
display: inline-block;
|
|
340
|
+
}
|
|
341
|
+
.nav-toggle-icon-closed {
|
|
342
|
+
display: none;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
317
345
|
}
|
|
318
346
|
|
|
319
|
-
&-icon
|
|
347
|
+
&-icon:not(:has(svg)),
|
|
348
|
+
&-icon svg {
|
|
320
349
|
font-size: rem-calc($icon-size);
|
|
321
350
|
width: rem-calc($icon-size);
|
|
322
351
|
color: $icon-color;
|
|
323
352
|
}
|
|
353
|
+
|
|
354
|
+
&-icon svg {
|
|
355
|
+
height: 100%;
|
|
356
|
+
}
|
|
324
357
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@forward './simple-fade-slider-2.0';
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Madj2kSimpleFadeSlider
|
|
3
|
+
*
|
|
4
|
+
* A lightweight fade slider using opacity and z-index.
|
|
5
|
+
* - Purely DOM based (no keyframes required)
|
|
6
|
+
* - Automatic looping through an arbitrary number of slides
|
|
7
|
+
* - Fully accessible with aria-hidden management
|
|
8
|
+
* - Configurable via options
|
|
9
|
+
* - Designed for CMS contexts with dynamic content
|
|
10
|
+
*
|
|
11
|
+
* @author Steffen Kroggel <developer@steffenkroggel.de>
|
|
12
|
+
* @copyright 2025 Steffen Kroggel
|
|
13
|
+
* @version 1.0.0
|
|
14
|
+
* @license GNU General Public License v3.0
|
|
15
|
+
* @see https://www.gnu.org/licenses/gpl-3.0.en.html
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* // Initialize with defaults
|
|
19
|
+
* const slider = new Madj2kSimpleFadeSlider('.js-fade-sliderr');
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* // Initialize with custom config
|
|
23
|
+
* const slider = new Madj2kSimpleFadeSlider('.js-fade-sliderr', {
|
|
24
|
+
* duration: 8,
|
|
25
|
+
* classSlide: 'fade-slider-item',
|
|
26
|
+
* classVisible: 'is-visible',
|
|
27
|
+
* debug: true
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* // Example HTML
|
|
32
|
+
* <section class="fade-slider js-fade-sliderr" aria-label="Image gallery">
|
|
33
|
+
* <div class="fade-slider-item"><img src="img1.jpg" alt="Description 1"></div>
|
|
34
|
+
* <div class="fade-slider-item"><img src="img2.jpg" alt="Description 2"></div>
|
|
35
|
+
* <div class="fade-slider-item"><img src="img3.jpg" alt="Description 3"></div>
|
|
36
|
+
* </section>
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* // Example SCSS
|
|
40
|
+
* .fade-slider {
|
|
41
|
+
* position: relative;
|
|
42
|
+
* height: 300px;
|
|
43
|
+
* overflow: hidden;
|
|
44
|
+
* }
|
|
45
|
+
*
|
|
46
|
+
* .fade-slide, .fade-slider-item {
|
|
47
|
+
* position: absolute;
|
|
48
|
+
* inset: 0;
|
|
49
|
+
* opacity: 0;
|
|
50
|
+
* z-index: 1;
|
|
51
|
+
* transition: opacity 1s ease-in-out;
|
|
52
|
+
* }
|
|
53
|
+
*
|
|
54
|
+
* .fade-slide.is-visible, .fade-slider-item.is-visible {
|
|
55
|
+
* opacity: 1;
|
|
56
|
+
* z-index: 2;
|
|
57
|
+
* }
|
|
58
|
+
*/
|
|
59
|
+
|
|
60
|
+
class Madj2kSimpleFadeSlider {
|
|
61
|
+
config = {
|
|
62
|
+
duration: 12,
|
|
63
|
+
classSlide: 'fade-slide',
|
|
64
|
+
classVisible: 'is-visible',
|
|
65
|
+
debug: false
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @param {string} selector - CSS selector for the slider container
|
|
70
|
+
* @param {Object} config - configuration options
|
|
71
|
+
* @param {number} [config.duration=12] - time in seconds between slide changes
|
|
72
|
+
* @param {string} [config.classSlide='fade-slide'] - class for each slide
|
|
73
|
+
* @param {string} [config.classVisible='is-visible'] - class for visible slide
|
|
74
|
+
* @param {boolean} [config.debug=false] - enable debug logging
|
|
75
|
+
*/
|
|
76
|
+
constructor(selector, config = {}) {
|
|
77
|
+
this.container = document.querySelector(selector);
|
|
78
|
+
if (!this.container) return;
|
|
79
|
+
|
|
80
|
+
this.config = {
|
|
81
|
+
...this.config,
|
|
82
|
+
...config
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
this.slides = Array.from(this.container.querySelectorAll(`.${this.config.classSlide}`));
|
|
86
|
+
this.total = this.slides.length;
|
|
87
|
+
this.current = 0;
|
|
88
|
+
|
|
89
|
+
this._log('Initialized with config:', this.config);
|
|
90
|
+
|
|
91
|
+
this.setup();
|
|
92
|
+
this.start();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Initializes all slides
|
|
97
|
+
* @private
|
|
98
|
+
*/
|
|
99
|
+
setup() {
|
|
100
|
+
this.slides.forEach((slide, index) => {
|
|
101
|
+
slide.classList.add(this.config.classSlide);
|
|
102
|
+
slide.setAttribute('aria-hidden', index === 0 ? 'false' : 'true');
|
|
103
|
+
});
|
|
104
|
+
if (this.slides[0]) {
|
|
105
|
+
this.slides[0].classList.add(this.config.classVisible);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Starts the automatic fade loop
|
|
111
|
+
* @private
|
|
112
|
+
*/
|
|
113
|
+
start() {
|
|
114
|
+
setInterval(() => this.showNext(), this.config.duration * 1000);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Shows the next slide
|
|
119
|
+
* @private
|
|
120
|
+
*/
|
|
121
|
+
showNext() {
|
|
122
|
+
const currentSlide = this.slides[this.current];
|
|
123
|
+
if (currentSlide) {
|
|
124
|
+
currentSlide.classList.remove(this.config.classVisible);
|
|
125
|
+
currentSlide.setAttribute('aria-hidden', 'true');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
this.current = (this.current + 1) % this.total;
|
|
129
|
+
|
|
130
|
+
const nextSlide = this.slides[this.current];
|
|
131
|
+
if (nextSlide) {
|
|
132
|
+
nextSlide.classList.add(this.config.classVisible);
|
|
133
|
+
nextSlide.setAttribute('aria-hidden', 'false');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
this._log('Switched to slide', this.current);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Debug logging helper
|
|
141
|
+
* @private
|
|
142
|
+
*/
|
|
143
|
+
_log(...args) {
|
|
144
|
+
if (this.config.debug) {
|
|
145
|
+
console.log('[Madj2kSimpleFadeSlider]', ...args);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
.fade-slider {
|
|
2
|
+
position: relative;
|
|
3
|
+
height: 100vh;
|
|
4
|
+
overflow: hidden;
|
|
5
|
+
|
|
6
|
+
&-item {
|
|
7
|
+
position: absolute;
|
|
8
|
+
inset: 0;
|
|
9
|
+
opacity: 0;
|
|
10
|
+
z-index: 1;
|
|
11
|
+
transition: opacity 1s ease-in-out;
|
|
12
|
+
|
|
13
|
+
&.is-visible {
|
|
14
|
+
opacity: 1;
|
|
15
|
+
z-index: 2;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|