@madj2k/fe-frontend-kit 2.0.38 → 2.0.39
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 +15 -5
- package/menus/flyout-menu/flyout-menu-2.0.scss +6 -0
- package/menus/slide-menu/Examples/TYPO3/JsonMenuViewHelper.php +146 -0
- package/menus/slide-menu/Examples/TYPO3/Templates.html +165 -0
- package/menus/slide-menu/index.js +2 -0
- package/menus/slide-menu/index.scss +1 -0
- package/menus/slide-menu/slide-menu-2.0.js +848 -0
- package/menus/slide-menu/slide-menu-2.0.scss +129 -0
- package/package.json +1 -1
- package/readme.md +312 -15
- package/sass/bootstrap-5.3/10_mixins/_colors.scss +107 -13
- package/sass/bootstrap-5.3/10_mixins/_section.scss +51 -9
- package/sass/bootstrap-5.3/10_mixins/_toggle-list.scss +26 -12
- package/sass/bootstrap-5.3/10_mixins/_toggle.scss +8 -0
package/index.js
CHANGED
package/index.scss
CHANGED
|
@@ -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.4
|
|
11
11
|
* @license GNU General Public License v3.0
|
|
12
12
|
* @see https://www.gnu.org/licenses/gpl-3.0.en.html
|
|
13
13
|
*
|
|
@@ -49,9 +49,12 @@ class Madj2kFlyoutMenu {
|
|
|
49
49
|
menuCloseClass: "js-flyout-close",
|
|
50
50
|
menuContainerClass: "js-flyout-container",
|
|
51
51
|
menuInnerClass: "js-flyout-inner",
|
|
52
|
-
heightCalculationClass: 'calculate',
|
|
53
52
|
hoverParentClass: 'nav-main',
|
|
53
|
+
heightCalculationClass: 'calculate',
|
|
54
54
|
heightMode: 'full',
|
|
55
|
+
heightModeClassPrefix: 'height-mode',
|
|
56
|
+
scrollMode: 'default',
|
|
57
|
+
scrollModeClassPrefix: 'scroll-mode',
|
|
55
58
|
eventMode: 'click',
|
|
56
59
|
paddingBehavior: 0,
|
|
57
60
|
paddingViewPortMinWidth: 0,
|
|
@@ -76,8 +79,11 @@ class Madj2kFlyoutMenu {
|
|
|
76
79
|
this.settings.$menuContainer = this.settings.$menu.querySelector(`.${this.settings.menuContainerClass}`);
|
|
77
80
|
this.settings.$menuInner = this.settings.$menu.querySelector(`.${this.settings.menuInnerClass}`);
|
|
78
81
|
|
|
79
|
-
//
|
|
82
|
+
// add mode classes
|
|
83
|
+
this.settings.$menu.classList.add(this.settings.heightModeClassPrefix + '-' + this.settings.heightMode);
|
|
84
|
+
this.settings.$menuInner.classList.add(this.settings.scrollModeClassPrefix + '-' + this.settings.scrollMode);
|
|
80
85
|
|
|
86
|
+
// Bind persistent event handlers
|
|
81
87
|
this.initNoScrollHelper();
|
|
82
88
|
this.resizeAndPositionMenu();
|
|
83
89
|
this.paddingMenu();
|
|
@@ -444,6 +450,10 @@ class Madj2kFlyoutMenu {
|
|
|
444
450
|
newHeight = `${innerHeight}px`;
|
|
445
451
|
}
|
|
446
452
|
|
|
453
|
+
if (this.settings.scrollMode === 'inner') {
|
|
454
|
+
this.settings.$menuInner.style.height = newHeight;
|
|
455
|
+
}
|
|
456
|
+
|
|
447
457
|
// set max-height again so that longer flyouts do not lead to scrolling on smaller ones
|
|
448
458
|
this.settings.$menu.classList.remove(this.settings.heightCalculationClass);
|
|
449
459
|
this.settings.$menu.style.height = newHeight;
|
|
@@ -468,6 +478,7 @@ class Madj2kFlyoutMenu {
|
|
|
468
478
|
|
|
469
479
|
if (!this.settings.$paddingReference) return;
|
|
470
480
|
if (this.settings.paddingBehavior === 0) return;
|
|
481
|
+
|
|
471
482
|
// should be re-evaluated on a resize event after re-opening the menu
|
|
472
483
|
// if (this.settings.paddingBehavior === 1 && this.settings.$menuInner.hasAttribute('data-padding-set')) return;
|
|
473
484
|
|
|
@@ -504,8 +515,7 @@ class Madj2kFlyoutMenu {
|
|
|
504
515
|
*/
|
|
505
516
|
toggleNoScroll() {
|
|
506
517
|
|
|
507
|
-
|
|
508
|
-
if (this.settings.heightMode === 'full' || this.settings.fullHeight === true) {
|
|
518
|
+
if (this.settings.scrollHelper) {
|
|
509
519
|
const body = document.body;
|
|
510
520
|
const helper = body.querySelector('.no-scroll-helper');
|
|
511
521
|
const inner = body.querySelector('.no-scroll-helper-inner');
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* This file is part of the TYPO3 CMS project.
|
|
5
|
+
*
|
|
6
|
+
* It is free software; you can redistribute it and/or modify it under
|
|
7
|
+
* the terms of the GNU General Public License, either version 2
|
|
8
|
+
* of the License, or any later version.
|
|
9
|
+
*
|
|
10
|
+
* For the full copyright and license information, please read the
|
|
11
|
+
* LICENSE.txt file that was distributed with this source code.
|
|
12
|
+
*
|
|
13
|
+
* The TYPO3 project - inspiring people to share!
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
|
17
|
+
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
|
18
|
+
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
|
|
19
|
+
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Class JsonMenuViewHelper
|
|
23
|
+
*
|
|
24
|
+
* @author Steffen Kroggel <developer@steffenkroggel.de>
|
|
25
|
+
* @copyright Steffen Kroggel <developer@steffenkroggel.de>
|
|
26
|
+
* @package Madj2k_SiteDefault
|
|
27
|
+
* @license http://www.gnu.org/licenses/gpl.html GNU General Public License, version 3 or later
|
|
28
|
+
*/
|
|
29
|
+
class JsonMenuViewHelper extends AbstractViewHelper
|
|
30
|
+
{
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Initialize arguments
|
|
34
|
+
*
|
|
35
|
+
* @return void
|
|
36
|
+
*/
|
|
37
|
+
public function initializeArguments(): void
|
|
38
|
+
{
|
|
39
|
+
parent::initializeArguments();
|
|
40
|
+
$this->registerArgument('items', 'array', 'The recursive menu-array');
|
|
41
|
+
$this->registerArgument('parseFuncTSPath', 'string', 'Path to the TypoScript parseFunc setup.', false, '');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @param array $arguments
|
|
47
|
+
* @param \Closure $renderChildrenClosure
|
|
48
|
+
* @param \TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface $renderingContext
|
|
49
|
+
* @return string
|
|
50
|
+
*/
|
|
51
|
+
public static function renderStatic(
|
|
52
|
+
array $arguments,
|
|
53
|
+
\Closure $renderChildrenClosure,
|
|
54
|
+
RenderingContextInterface $renderingContext
|
|
55
|
+
): string {
|
|
56
|
+
|
|
57
|
+
/** @var array $items */
|
|
58
|
+
$items = $arguments['items'];
|
|
59
|
+
|
|
60
|
+
/** @var string $parseFuncTSPath */
|
|
61
|
+
$parseFuncTSPath = $arguments['parseFuncTSPath'];
|
|
62
|
+
|
|
63
|
+
/** @var \Psr\Http\Message\ServerRequestInterface $request */
|
|
64
|
+
$request = $renderingContext->getRequest();
|
|
65
|
+
|
|
66
|
+
/** @var \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer $contentObject */
|
|
67
|
+
$contentObject = GeneralUtility::makeInstance(ContentObjectRenderer::class);
|
|
68
|
+
$contentObject->setRequest($request);
|
|
69
|
+
$contentObject->start([]);
|
|
70
|
+
|
|
71
|
+
$result = [];
|
|
72
|
+
foreach ($items as $item) {
|
|
73
|
+
$result[] = self::callback($item, $contentObject, $parseFuncTSPath);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return json_encode($result);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @param array $item
|
|
82
|
+
* @param \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer $contentObject
|
|
83
|
+
* @param string $parseFuncTSPath
|
|
84
|
+
* @return array
|
|
85
|
+
*/
|
|
86
|
+
public static function callback (
|
|
87
|
+
array $item,
|
|
88
|
+
ContentObjectRenderer $contentObject,
|
|
89
|
+
string $parseFuncTSPath = ''
|
|
90
|
+
): array {
|
|
91
|
+
|
|
92
|
+
/*
|
|
93
|
+
Build a reduced structure:
|
|
94
|
+
"data": {"uid": 2, "pid": 1, "title": "Lorem Ipsum"},
|
|
95
|
+
"title": "Lorem Ipsum",
|
|
96
|
+
"link": "\/lorem\/Ipsum",
|
|
97
|
+
"target": "_blank",
|
|
98
|
+
"active": 1,
|
|
99
|
+
"current": 0,
|
|
100
|
+
"spacer": 0,
|
|
101
|
+
"hasSubpages": 1,
|
|
102
|
+
"children": [ ]
|
|
103
|
+
*/
|
|
104
|
+
|
|
105
|
+
$result = $item;
|
|
106
|
+
$result['data'] = [
|
|
107
|
+
'uid' => $item['data']['uid'],
|
|
108
|
+
'pid' => $item['data']['pid'],
|
|
109
|
+
'title'=> $item['data']['title'],
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
$result['children'] = [];
|
|
113
|
+
if (
|
|
114
|
+
(isset($item['children']))
|
|
115
|
+
&& (is_array($item['children']))
|
|
116
|
+
){
|
|
117
|
+
foreach ($item['children'] as $child) {
|
|
118
|
+
$result['children'][] = self::callback($child, $contentObject, $parseFuncTSPath);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (!isset($result['hasSubpages'])) {
|
|
123
|
+
$result['hasSubpages'] = $result['children'] ? 1 : 0;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (isset($item['data']['doktype'])) {
|
|
127
|
+
$result['linkType'] = $item['data']['doktype'];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Parse title with HTML-parser
|
|
131
|
+
if ($parseFuncTSPath) {
|
|
132
|
+
$result['title'] = $contentObject->parseFunc($result['title'], null, '< ' . $parseFuncTSPath);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// do not link sites, that only link to the next subpage!
|
|
136
|
+
$result['isLinked'] = 1;
|
|
137
|
+
if (
|
|
138
|
+
($item['data']['doktype'] == 4)
|
|
139
|
+
&& (in_array($item['data']['shortcut_mode'], [1,3]))
|
|
140
|
+
){
|
|
141
|
+
$result['isLinked'] = 0;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return $result;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
|
|
2
|
+
xmlns:siteDefault="http://typo3.org/ns/Madj2k/SiteDefault/ViewHelpers"
|
|
3
|
+
data-namespace-typo3-fluid="true"
|
|
4
|
+
>
|
|
5
|
+
|
|
6
|
+
<f:if condition="{menuItems}">
|
|
7
|
+
<f:then>
|
|
8
|
+
<f:comment>
|
|
9
|
+
<!--
|
|
10
|
+
Templates for mobile menu. We don't want this to be loaded on default because
|
|
11
|
+
it would duplicate the menu for search engines and would enlarge the body to parse
|
|
12
|
+
-->
|
|
13
|
+
</f:comment>
|
|
14
|
+
<template class="js-slide-nav-tmpl" data-type="menuWrap">
|
|
15
|
+
<div class="slide-nav-container js-slide-nav-container">
|
|
16
|
+
<div class="slide-nav-card js-slide-nav-card %levelClass%" id="slide-card-%uid%">
|
|
17
|
+
<div class="slide-nav-inner pd-3">
|
|
18
|
+
<ul class="slide-nav-list" role="none">
|
|
19
|
+
<li class="slide-nav-headline %levelClass%" role="none">
|
|
20
|
+
<a href="/"
|
|
21
|
+
title="{f:translate(key:'menu.home', extensionName:'siteDefault')}"
|
|
22
|
+
role="menuitem"
|
|
23
|
+
class="slide-nav-headline-link level-1">
|
|
24
|
+
<span>{f:translate(key:'menu.home', extensionName:'siteDefault')}</span>
|
|
25
|
+
</a>
|
|
26
|
+
</li>
|
|
27
|
+
|
|
28
|
+
<li class="slide-nav-wrap">
|
|
29
|
+
<ul class="slide-nav-list toggle-list" role="none">
|
|
30
|
+
%menuItems%
|
|
31
|
+
</ul>
|
|
32
|
+
</li>
|
|
33
|
+
|
|
34
|
+
<f:if condition="{topMenuItems}">
|
|
35
|
+
<li class="slide-nav-item-footer" role="none">
|
|
36
|
+
<ul class="slide-nav-meta-list" role="none">
|
|
37
|
+
<f:for each="{topMenuItems}" as="menuItem" iteration="iterator">
|
|
38
|
+
<li class="slide-nav-meta-item level1{f:if(condition:'{menuItem.active}', then:' active')}" role="none">
|
|
39
|
+
<a class="slide-nav-meta-link level1{f:if(condition:'{menuItem.active}', then:' active')}"
|
|
40
|
+
href="{menuItem.link}"
|
|
41
|
+
target="{menuItem.target}"
|
|
42
|
+
title="{menuItem.data.title}"
|
|
43
|
+
role="menuitem"
|
|
44
|
+
>
|
|
45
|
+
<span><f:format.html parseFuncTSPath="lib.parseFunc_Inline">{menuItem.title}</f:format.html></span>
|
|
46
|
+
</a>
|
|
47
|
+
</li>
|
|
48
|
+
</f:for>
|
|
49
|
+
</ul>
|
|
50
|
+
</li>
|
|
51
|
+
</f:if>
|
|
52
|
+
|
|
53
|
+
<f:if condition="{footerMenuItems}">
|
|
54
|
+
<li class="slide-nav-item-footer" role="none">
|
|
55
|
+
<ul class="slide-nav-meta-list" role="none">
|
|
56
|
+
<f:for each="{footerMenuItems}" as="menuItem" iteration="iterator">
|
|
57
|
+
<li class="slide-nav-meta-item level1{f:if(condition:'{menuItem.active}', then:' active')}" role="none">
|
|
58
|
+
<a class="slide-nav-meta-link level1{f:if(condition:'{menuItem.active}', then:' active')}"
|
|
59
|
+
href="{menuItem.link}"
|
|
60
|
+
target="{menuItem.target}"
|
|
61
|
+
title="{menuItem.data.title}"
|
|
62
|
+
role="menuitem"
|
|
63
|
+
>
|
|
64
|
+
<span><f:format.html parseFuncTSPath="lib.parseFunc_Inline">{menuItem.title}</f:format.html></span>
|
|
65
|
+
</a>
|
|
66
|
+
</li>
|
|
67
|
+
</f:for>
|
|
68
|
+
</ul>
|
|
69
|
+
</li>
|
|
70
|
+
</f:if>
|
|
71
|
+
</ul>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</template>
|
|
76
|
+
|
|
77
|
+
<template class="js-slide-nav-tmpl" data-type="menuItem">
|
|
78
|
+
<li class="slide-nav-item toggle-list-item js-slide-nav-item %activeClass% %hasChildrenClass%" role="none">
|
|
79
|
+
<f:comment><!-- normal link that opens the menu-item --></f:comment>
|
|
80
|
+
|
|
81
|
+
%ifHasNoChildrenStart%
|
|
82
|
+
<a href="%link%"
|
|
83
|
+
title="%titleRaw%"
|
|
84
|
+
role="menuitem"
|
|
85
|
+
class="slide-nav-link toggle-list-link %activeClass% %hasChildrenClass%"
|
|
86
|
+
target="%target%"
|
|
87
|
+
aria-current="%ariaCurrent%">
|
|
88
|
+
<span class="toggle-list-text">%title%</span>
|
|
89
|
+
</a>
|
|
90
|
+
%ifHasNoChildrenEnd%
|
|
91
|
+
|
|
92
|
+
<f:comment><!-- link that opens next card --></f:comment>
|
|
93
|
+
%ifHasChildrenStart%
|
|
94
|
+
<a class="slide-nav-link slide-nav-next toggle-list-link js-slide-nav-next %activeClass% %hasChildrenClass%"
|
|
95
|
+
href="#"
|
|
96
|
+
role="button"
|
|
97
|
+
title="{f:translate(key:'menu.openSubmenu', extensionName:'siteDefault')}"
|
|
98
|
+
aria-label="{f:translate(key:'menu.openSubmenu', extensionName:'siteDefault')}"
|
|
99
|
+
aria-haspopup="true"
|
|
100
|
+
aria-expanded="%ariaExpanded%"
|
|
101
|
+
aria-controls="slide-card-%uid%">
|
|
102
|
+
<span class="toggle-list-text">%title%</span><span class="icon-arrow-right icon"></span>
|
|
103
|
+
</a>
|
|
104
|
+
%ifHasChildrenEnd%
|
|
105
|
+
|
|
106
|
+
%submenu%
|
|
107
|
+
</li>
|
|
108
|
+
</template>
|
|
109
|
+
|
|
110
|
+
<template class="js-slide-nav-tmpl" data-type="subMenuWrap">
|
|
111
|
+
<div class="slide-nav-card js-slide-nav-card %activeClass% %levelClass%" id="slide-card-%uid%">
|
|
112
|
+
<div class="slide-nav-inner pd-3">
|
|
113
|
+
<ul class="slide-nav-list">
|
|
114
|
+
<li class="slide-nav-item-back" role="none">
|
|
115
|
+
<button class="slide-nav-back js-slide-nav-back"
|
|
116
|
+
title="{f:translate(key:'menu.levelUp', extensionName:'siteDefault')}"
|
|
117
|
+
aria-haspopup="true"
|
|
118
|
+
aria-label="{f:translate(key:'menu.levelUp', extensionName:'siteDefault')}"
|
|
119
|
+
aria-controls="slide-card-%uid%"
|
|
120
|
+
data-parent-card="slide-card-%parentUid%"><span class="icon-arrow-left icon"></span><span
|
|
121
|
+
class="slide-nav-back-label">{f:translate(key:'menu.back', extensionName:'siteDefault')}</span>
|
|
122
|
+
</button>
|
|
123
|
+
</li>
|
|
124
|
+
<li class="slide-nav-headline %levelClass%" role="none">
|
|
125
|
+
|
|
126
|
+
<f:comment><!-- normal parent --></f:comment>
|
|
127
|
+
%ifIsNotLinkedStart%
|
|
128
|
+
<span class="slide-nav-headline-text %currentClass% %isLinkedClass%">%title%</span>
|
|
129
|
+
%ifIsNotLinkedEnd%
|
|
130
|
+
|
|
131
|
+
<f:comment><!-- linked parent --></f:comment>
|
|
132
|
+
%ifIsLinkedStart%
|
|
133
|
+
<a href="%link%"
|
|
134
|
+
title="%titleRaw%"
|
|
135
|
+
role="menuitem"
|
|
136
|
+
class="slide-nav-headline-link %currentClass% %hasChildrenClass% %isLinkedClass%"
|
|
137
|
+
target="%target%"
|
|
138
|
+
aria-current="%ariaCurrent%">
|
|
139
|
+
<span>%title%</span>
|
|
140
|
+
</a>
|
|
141
|
+
%ifIsLinkedEnd%
|
|
142
|
+
</li>
|
|
143
|
+
|
|
144
|
+
<li class="slide-nav-wrap">
|
|
145
|
+
<ul class="slide-nav-list toggle-list" role="none">
|
|
146
|
+
%menuItems%
|
|
147
|
+
</ul>
|
|
148
|
+
</li>
|
|
149
|
+
|
|
150
|
+
</ul>
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
</template>
|
|
154
|
+
|
|
155
|
+
<script>
|
|
156
|
+
let slideNavItems = <f:format.raw><siteDefault:jsonMenu items="{menuItems}" parseFuncTSPath="lib.parseFunc_Inline" /></f:format.raw>;
|
|
157
|
+
</script>
|
|
158
|
+
</f:then>
|
|
159
|
+
<f:else>
|
|
160
|
+
<script>
|
|
161
|
+
let slideNavItems = {};
|
|
162
|
+
</script>
|
|
163
|
+
</f:else>
|
|
164
|
+
</f:if>
|
|
165
|
+
</html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@forward './slide-menu-2.0';
|