Changes for page Menu Macro

Last modified by Sebastian Marsching on 2024/09/04 17:56

From version 7.1
edited by Sebastian Marsching
on 2024/08/10 15:00
Change comment: Install extension [org.xwiki.platform:xwiki-platform-menu-ui/15.10.11]
To version 1.1
edited by Sebastian Marsching
on 2022/01/09 00:05
Change comment: Install extension [org.xwiki.platform:xwiki-platform-menu-ui/13.10.2]

Summary

Details

Page properties
Content
... ... @@ -3,11 +3,9 @@
3 3  = Horizontal Menu =
4 4  
5 5  {{velocity}}
6 -#set ($menuTemplateDoc = $xwiki.getDocument('MenuTemplate'))
7 7  {{code language="none"}}
8 8  {{menu type="horizontal fixedWidth"}}
9 -## No way to escape content in the code macro, so just remove {, see https://jira.xwiki.org/browse/XRENDERING-13.
10 -$menuTemplateDoc.content.replace('{', '')
8 +$xwiki.getDocument('MenuTemplate').content
11 11  {{/menu}}
12 12  {{/code}}
13 13  {{/velocity}}
XWiki.JavaScriptExtension[0]
Code
... ... @@ -1,85 +1,26 @@
1 -define('menu-ui-translation-keys', {
2 - prefix: 'menu.ui.',
3 - keys: [
4 - "openSubMenu",
5 - "closeSubMenu"
6 - ]
7 -});
8 -require(['jquery','xwiki-l10n!menu-ui-translation-keys'], function($, l10n) {
1 +require(['jquery'], function($) {
9 9   // It's not possible to write a CSS selector that targets list items containing lists so we rely on JavaScript.
10 10   // The 'dropdown' CSS class is used only to display the down/left arrow.
11 - // All nodes on the tree.
12 - $('.menu-horizontal ul , .menu-vertical ul')
13 - .attr('role', 'menu');
14 - // All leaves on the tree.
15 - $('.menu-horizontal li, .menu-vertical li')
16 - .attr('role', 'menuitem');
17 - $('.menu-horizontal li ul, .menu-vertical li ul')
18 - .parent()
19 - .addClass('xDropdown');
4 + $('.menu-horizontal li ul').parent().addClass('xDropdown');
5 +
20 20   // Make sure the menu separators are really empty.
21 - var menus = $('.menu-horizontal, .menu-vertical');
22 - menus.find('li > br:first-child').remove();
7 + $('.menu-horizontal, .menu-vertical').find('li > br:first-child').remove();
23 23  
24 - // Add aria attributes to the menu separators.
25 - menus.find('li')
26 - .filter(function() {return this.textContent.trim() === ""; })
27 - .attr('role', 'separator')
28 - .attr('aria-hidden', 'true');
29 -
30 - // Vertical menus are initially expanded
31 - $('.menu-vertical.collapsible').each(function() {
9 + // Collapsible menu bahavior.
10 + $('.menu-vertical.collapsible').each(function(){
32 32   var open = $(this).hasClass('open');
33 33   $(this).find('li ul').each(function() {
34 34   $(this).addClass('xDropdown-menu').parent().addClass('xDropdown' + (open ? ' open' : ''));
35 - });
36 - });
37 -
38 - function setDropdownButtonTitle(dropDownButton) {
39 - var xDropdown = $(dropDownButton).parent().parent();
40 - if($(xDropdown).hasClass('open')) {
41 - $(dropDownButton).attr('title', l10n['closeSubMenu']);
42 - $(xDropdown).attr('aria-expanded', "true");
43 - } else {
44 - $(dropDownButton).attr('title', l10n['openSubMenu']);
45 - $(xDropdown).attr('aria-expanded', "false");
46 - }
47 - }
48 -
49 - $('.xDropdown').each(function() {
50 - var dropDownHeader = this.ownerDocument.createElement("div");
51 - $(dropDownHeader).addClass("xDropdown-header");
52 - var dropDownButton = this.ownerDocument.createElement("button");
53 - $(dropDownButton).addClass("xDropdown-header-toggle");
54 - setDropdownButtonTitle(dropDownButton);
55 - dropDownButton.addEventListener('click',function() {
56 - //Swaps the state of the submenu.
57 - var xDropdown = $(this).parent().parent();
58 - xDropdown.toggleClass('open');
59 - setDropdownButtonTitle(dropDownButton);
60 - });
61 - let dropDownContent = $(this).contents();
62 - // We put all the content of the entry in the header,
63 - // except for the last one which is the content of the dropdown. This dropdown stays where it is.
64 - for (let index = 0; index < dropDownContent.length - 1 ; index++) {
65 - let item = dropDownContent[index];
66 - dropDownHeader.append(item);
14 + // Wrap everything (including text nodes) before the sub-menu in a DIV that will toggle its state.
15 + var toggle = this.ownerDocument.createElement('div');
16 + $(this).parent().prepend(toggle);
17 + for(var next = toggle.nextSibling; next != this; next = toggle.nextSibling) {
18 + toggle.appendChild(next);
67 67   }
68 - dropDownHeader.append(dropDownButton);
69 - $(this).prepend(dropDownHeader);
70 - $(dropDownHeader).next().addClass('xDropdown-menu');
71 - });
72 -
73 - $('.menu-horizontal .xDropdown').each(function() {
74 - // In case of horizontal menus, make it so that a class is added on hover, instead of using the :hover pseudo-class
75 - this.addEventListener("mouseover", function() {
76 - $(this).addClass('open');
77 - setDropdownButtonTitle(this.firstChild.lastChild);
20 + $(toggle).addClass('xDropdown-toggle').on('click', function() {
21 + $(this).parent().toggleClass('open');
22 + });
78 78   });
79 - this.addEventListener("mouseout", function() {
80 - $(this).removeClass('open');
81 - setDropdownButtonTitle(this.firstChild.lastChild);
82 - });
83 83   });
84 84  
85 85   // In case of horizontal responsive menus, make sub-submenus in the navbar work on mobile devices
XWiki.StyleSheetExtension[1]
Code
... ... @@ -4,32 +4,6 @@
4 4   }
5 5  }
6 6  .menu {
7 - /* Rotate the carets when the menu is opened. */
8 - .xDropdown{
9 - > .xDropdown-header > .xDropdown-header-toggle:before {
10 - transform: rotate(90deg);
11 - }
12 - &.open > .xDropdown-header > .xDropdown-header-toggle:before {
13 - transform: rotate(0);
14 - }
15 - }
16 - .xDropdown-header-toggle {
17 - background: transparent;
18 - border:none;
19 - border-radius: @border-radius-base;
20 - margin: 0 .3em;
21 - line-height: (@line-height-computed / 2);
22 - min-width: 24px;
23 - min-height: 24px;
24 - &:hover, &:focus-within {
25 - background-color: @dropdown-bg;
26 - }
27 - &:before {
28 - .caret;
29 - margin-left: 0;
30 - content: '';
31 - }
32 - }
33 33   &.menu-vertical {
34 34   ul {
35 35   list-style-type: none;
... ... @@ -51,8 +51,32 @@
51 51   .xDropdown-menu {
52 52   display: none;
53 53   }
28 + .xDropdown-toggle {
29 + cursor: pointer;
30 + position: relative;
31 + &:hover {
32 + background-color: @nav-link-hover-bg;
33 + }
34 + &:after {
35 + .caret;
36 + content: '';
37 + /* Positioning */
38 + position: absolute;
39 + margin-top: @line-height-computed / 3;
40 + right: 1em;
41 + /* Collapsed arrow style */
42 + border-bottom: 4px solid transparent;
43 + border-right: 4px solid;
44 + border-top: 4px solid transparent;
45 + }
46 + }
54 54   .xDropdown.open {
55 - > ul {
48 + > .xDropdown-toggle:after {
49 + /* Expanded arrow style */
50 + .caret;
51 + margin-top: @line-height-computed / 2;
52 + }
53 + > .xDropdown-menu {
56 56   display: block;
57 57   }
58 58   }
... ... @@ -66,35 +66,22 @@
66 66   .box-shadow(0 2px 8px rgba(0,0,0,0.4) inset);
67 67   min-height: @navbar-height;
68 68   padding-left: 25px;
69 - .xDropdown.open {
70 - > .xDropdown-header > .xDropdown-header-toggle:before {
71 - transform: rotate(0);
72 - }
73 - > ul {
74 - display: block;
75 - }
76 - }
77 77   & > ul {
78 78   padding-left: 0;
79 79   list-style-type: none;
80 80   margin: 0;
81 - min-height: 50px;
82 - display: flex;
83 - align-items: stretch;
84 84   & > li {
85 85   position: relative;
86 - min-height: 50px;
87 - display: flex;
88 - align-items: center;
73 + display: block;
89 89   padding: @nav-link-padding;
90 - padding-top: 0;
91 - padding-bottom: 0;
75 + padding-top: @navbar-padding-vertical;
76 + padding-bottom: @navbar-padding-vertical;
92 92   @media (min-width: @grid-float-breakpoint) {
93 93   float: left;
94 94   }
95 95   line-height: @line-height-computed;
96 96   color: @navbar-default-link-color;
97 - &:hover, &:focus-within {
82 + &:hover {
98 98   color: @navbar-default-link-hover-color;
99 99   background-color: @navbar-default-link-hover-bg;
100 100   background-color: @navbar-default-link-active-bg;
... ... @@ -108,7 +108,7 @@
108 108   /* Links inside menu */
109 109   a {
110 110   color: @navbar-default-link-color;
111 - &:hover, &:focus-within {
96 + &:hover {
112 112   text-decoration: none;
113 113   }
114 114   }
... ... @@ -117,18 +117,7 @@
117 117   /* Limit the height to the nav height minus the padding and minus border */
118 118   max-height: @navbar-height - (2 * @navbar-padding-vertical) - 2px;
119 119   overflow: hidden;
120 - &.xDropdown-header{
121 - /* No border on the dropdown header */
122 - max-height: unset;
123 - }
124 124   }
125 - /* Horizontal menu top-level headers. */
126 - & > .xDropdown-header > .xDropdown-header-toggle {
127 - &:hover, &:focus-within {
128 - /* Change background color of the caret when hovering on the navbar. */
129 - background-color: @navbar-default-bg;
130 - }
131 - }
132 132   /* Separator vertical inside menu */
133 133   &:empty {
134 134   height: @navbar-height;
... ... @@ -152,6 +152,7 @@
152 152   font-size: @font-size-base;
153 153   text-align: left; // Ensures proper alignment if parent has it changed (e.g., modal footer)
154 154   background-color: @dropdown-bg;
129 + border: 1px solid @dropdown-fallback-border; // IE8 fallback
155 155   border: 1px solid @dropdown-border;
156 156   border-radius: @border-radius-base;
157 157   .box-shadow(0 6px 12px rgba(0,0,0,.175));
... ... @@ -172,7 +172,7 @@
172 172   color: @dropdown-link-color;
173 173   overflow: hidden;
174 174   text-overflow: ellipsis; // Displaying ... if the text is too long
175 - &:hover, &:focus-within {
150 + &:hover {
176 176   /* &:extend(.dropdown-menu>li>a:hover); */
177 177   text-decoration: none;
178 178   color: @dropdown-link-hover-color;
... ... @@ -190,7 +190,7 @@
190 190   color: @dropdown-link-color;
191 191   /* Empty dropdowns should have height in order to display the arrow */
192 192   min-height: 2 * @font-size-base;
193 - &:hover, &:focus-within {
168 + &:hover {
194 194   text-decoration: none;
195 195   color: @dropdown-link-hover-color;
196 196   background-color: @dropdown-link-hover-bg;
... ... @@ -200,16 +200,15 @@
200 200   }
201 201   }
202 202   /* When in dropdown we also have a link, reset the duplicated padding */
203 - & > .xDropdown-header > span > a {
178 + & > span > a {
204 204   padding: 0;
205 205   display: inherit;
206 206   }
207 - /* Reposition the toggle when in a dropdown of fixed size
208 - to avoid eating away at the bit of space we have for the text. */
209 - & > .xDropdown-header > .xDropdown-header-toggle {
182 + /* Place the arrow on the right */
183 + &:after {
210 210   position: absolute;
211 - right: 0;
212 - top: 0;
185 + margin-top: @line-height-computed / 2;
186 + right: 8px;
213 213   }
214 214   }
215 215   /* Separator horizontal inside menu */
... ... @@ -223,9 +223,17 @@
223 223   /* Stylization: Generic */
224 224   li {
225 225   /* Display submenus on hover */
226 - &.open > ul {
200 + &:hover > ul {
227 227   display: block;
228 228   }
203 + /* Display an arrow for expandable items */
204 + &.xDropdown {
205 + &:after {
206 + .caret;
207 + content: '';
208 + margin-left: .5em;
209 + }
210 + }
229 229   }
230 230   /* The only way to have a menu with more than 2 levels without JavaScript is to use a fixed width. */
231 231   &.fixedWidth {
... ... @@ -247,7 +247,7 @@
247 247   }
248 248   /* Resetting rules for mobile view */
249 249   @media (max-width: @screen-xs-max) {
250 - > ul {
232 + > ul {
251 251   margin: 0 0 0 -25px; /* Remove padding added in normal view */
252 252   > li {
253 253   &:empty {
... ... @@ -269,16 +269,19 @@
269 269   /* Links inside menu */
270 270   a {
271 271   color: @navbar-default-link-color;
254 + &:hover {
255 + /* Preserve the styling from dropdown */
256 + }
272 272   }
273 273   /* Submenus inside menu */
274 274   &.xDropdown {
275 275   color: @navbar-default-link-color;
276 - &.open {
261 + &:hover {
277 277   background-color: transparent;
278 278   color: inherit;
279 279   }
280 280   /* When in dropdown we also have a link */
281 - > span > a {
266 + > span > a {
282 282   color: @navbar-default-link-color;
283 283   }
284 284   }
XWiki.WikiMacroClass[0]
Macro code
... ... @@ -1,7 +1,6 @@
1 1  {{velocity}}
2 2  #set ($id = $xcontext.macro.params.id)
3 3  #set ($type = $xcontext.macro.params.type)
4 -#set ($label = $xcontext.macro.params.label)
5 5  #set ($colorTheme = $xwiki.getUserPreference('colorTheme'))
6 6  #if ("$!colorTheme" != '')
7 7   ## Make sure we use an absolute reference (see XWIKI-9672)
... ... @@ -9,24 +9,12 @@
9 9  #end
10 10  #set ($discard = $xwiki.ssx.use("$xcontext.macro.doc.prefixedFullName", {'colorTheme': $colorTheme}))
11 11  #set ($discard = $xwiki.jsx.use("$xcontext.macro.doc.prefixedFullName"))
12 -## Make sure the label is non-empty as otherwise the aria-label doesn't work.
13 -#if ("$!label" != '')
14 - #set ($label = $wikimacro.context.getXDOM().getIdGenerator().generateUniqueId('Menu',''))
15 -#end
16 16  #if($type.contains('horizontal'))
17 - ## Make sure the id is non-empty for horizontal menus as otherwise the toggle doesn't work.
18 - #if ("$!id" == '')
19 - #set ($id = $wikimacro.context.getXDOM().getIdGenerator().generateUniqueId("M", "GeneratedMenuId"))
20 - #end
21 - (% role='navigation' class='menu-horizontal-toggle'
22 - aria-label="${services.rendering.escape($label, 'xwiki/2.1')}" %)(((
12 + (% role="navigation" class="menu-horizontal-toggle" %)(((
23 23   (% class="navbar-header" %)(((
24 24   {{html}}
25 - <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#$!{escapetool.xml($id)}"
26 - aria-expanded="false" aria-controls="$!{escapetool.xml($id)}">
27 - <span class="sr-only">
28 - $escapetool.xml($services.localization.render('menu.ui.horizontal.toggler.description'))
29 - </span>
15 + <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#$!{id}" aria-expanded="false">
16 + <span class="sr-only"></span>
30 30   <span class="icon-bar"></span>
31 31   <span class="icon-bar"></span>
32 32   <span class="icon-bar"></span>
... ... @@ -33,14 +33,13 @@
33 33   </button>
34 34   {{/html}}
35 35   )))
36 - (% id="$!{services.rendering.escape($id, 'xwiki/2.1')}" class="menu menu-${services.rendering.escape($!type, 'xwiki/2.1')} collapse navbar-collapse" role="navigation" %)(((
37 - {{wikimacrocontent/}}
23 + (% id="${id}" class="menu menu-$!type collapse navbar-collapse" %)(((
24 + $xcontext.macro.content
38 38   )))
39 39   )))
40 40  #else
41 - (% role="navigation" #if ("$!id" != '') id="${services.rendering.escape($id, 'xwiki/2.1')}"#end class="menu menu-${services.rendering.escape($!type, 'xwiki/2.1')}"
42 - aria-label="${services.rendering.escape($label, 'xwiki/2.1')}" %)(((
43 - {{wikimacrocontent/}}
28 + (% #if ("$!id" != '') id="$id"#end class="menu menu-$!type" %)(((
29 + $xcontext.macro.content
44 44   )))
45 45  #end
46 46  {{/velocity}}
Macro content type
... ... @@ -1,1 +1,0 @@
1 -Wiki
Default category
... ... @@ -1,0 +1,1 @@
1 +Navigation
Default categories
... ... @@ -1,1 +1,0 @@
1 -Navigation
XWiki.WikiMacroParameterClass[3]
Parameter description
... ... @@ -1,1 +1,0 @@
1 -Optional menu label used to describe the content of the menu.
Parameter mandatory
... ... @@ -1,1 +1,0 @@
1 -No
Parameter name
... ... @@ -1,1 +1,0 @@
1 -label