From 81eba5a70100d9f36b9a661ee8bca56b4a434982 Mon Sep 17 00:00:00 2001 From: Stephen Cameron Date: Fri, 1 Sep 2017 18:36:27 +0200 Subject: [PATCH] Refactoring MMenu for better mobile experience. WIP #1630 @5 --- js/libs/fluidbook/fluidbook.js | 43 +++++++++++++++++++++--------- js/libs/fluidbook/fluidbook.nav.js | 31 +++++++++++++++++---- style/mmenu/mmenu.less | 17 ++++++++++-- 3 files changed, 71 insertions(+), 20 deletions(-) diff --git a/js/libs/fluidbook/fluidbook.js b/js/libs/fluidbook/fluidbook.js index 905921d2..7efcc1d8 100644 --- a/js/libs/fluidbook/fluidbook.js +++ b/js/libs/fluidbook/fluidbook.js @@ -726,23 +726,21 @@ Fluidbook.prototype = { return i + 1; }, - hideMenuItems: function (exception) { - exception = exception || ''; - $('#menuList > ul > li').not(exception).fadeOut(100); + hideMenuItems: function () { + $('#menuList > ul > li, #shareLinks').fadeOut(100); }, - showMenuItems: function (exception) { - exception = exception || ''; - $('#menuList > ul > li').not(exception).fadeIn(300); + showMenuItems: function () { + $('#menuList > ul > li, #shareLinks').fadeIn(300); }, initSearchResults: function () { this.menuSearchResults = $('#menuSearchResults'); - this.resizeSearchResults(); + this.resizeMenu(); this.menuSearchResults.hide(); }, - resizeSearchResults: function () { + resizeMenu: function () { //console.warn('calling resizeSR'); //console.log('menu open?', this.nav.menuIsOpen); @@ -751,8 +749,9 @@ Fluidbook.prototype = { var wh = $(window).height(), formHeight = $('#searchForm').height(); - marginTop = marginBottom = 50, - maxHeight = wh - formHeight - marginTop - marginBottom; + marginTop = marginBottom = Math.min(Math.round(wh * 0.075), 50); // Relative margins with a max of 50 + searchResultsMaxHeight = wh - formHeight - marginTop - marginBottom, + mainMenuMaxHeight = wh - $('#menuSearch').height() - $('#shareLinks').outerHeight(); // Search results element may not exist when resize is called if (fluidbook.menuSearchResults == undefined) { @@ -760,15 +759,27 @@ Fluidbook.prototype = { return false; // initSearchResults will call this resize function anyway } + // Max-height for the top level of the main menu so it can scroll between the search box and sharing bar + $('#menuList > ul').css('maxHeight', mainMenuMaxHeight); + // Set max-height for search results section (using CSS vh units was unreliable) - fluidbook.menuSearchResults.css('maxHeight', maxHeight); - fluidbook.menuSearchResults.perfectScrollbar('update'); + if (fluidbook.menuSearchResults !== undefined) { + fluidbook.menuSearchResults.css('maxHeight', searchResultsMaxHeight); + fluidbook.menuSearchResults.css('marginTop', marginTop); + fluidbook.menuSearchResults.perfectScrollbar('update'); + } + + // Also set same max-height for search hints + if (fluidbook.menuSearchHints !== undefined) { + fluidbook.menuSearchHints.css('maxHeight', wh - formHeight); + fluidbook.menuSearchHints.perfectScrollbar('update'); + } }, initSearchHints: function () { this.menuSearchHints = $('#menuSearchHints'); - this.hideMenuItems('#menuSearch'); // Hide everything except the search form so we can have space for the hints + this.hideMenuItems(); // Hide menu items to give space for hints div this.menuSearchHints.fadeIn(300); @@ -804,6 +815,12 @@ Fluidbook.prototype = { if (this.l10n.dir == 'rtl') { left = -1 * ($(window).width() - left - $("#q").outerWidth()); } + + this.menuSearchHints.perfectScrollbar({ + suppressScrollX: true, + minScrollbarLength: 40 + }); + $("#searchHints").css({top: top, left: left}).show(); }, killLastSearchHint: function () { diff --git a/js/libs/fluidbook/fluidbook.nav.js b/js/libs/fluidbook/fluidbook.nav.js index 35965a3e..0bd0ac78 100644 --- a/js/libs/fluidbook/fluidbook.nav.js +++ b/js/libs/fluidbook/fluidbook.nav.js @@ -3,6 +3,8 @@ function FluidbookNav(fluidbook) { this._dimensions = this.fluidbook.datas.iconsDimensions; this.menuIsOpen = false; this.chaptersMenuHTML = ''; + this.searchHTML = ''; + this.shareLinksHTML = ''; this.menu = $("#menu"); // Renamed from #nav because mmenu animations weren't working (probably due to some other script interference) this.horizontalNav = $("#horizontalNav"); this._inited = {}; @@ -44,7 +46,9 @@ FluidbookNav.prototype = { $('body').addClass('menu-open'); $this.menuIsOpen = true; - $('#q').focus(); + setInterval($this.fluidbook.resizeMenu, 500); + + //$('#q').focus(); // Disabled for now because it triggers the keyboard to open on some mobile devices }); this.menuAPI.bind("close:finish", function () { @@ -64,7 +68,7 @@ FluidbookNav.prototype = { }); // Recalculate available size for menu search results - $(window).on('fluidbookresize', fluidbook.resizeSearchResults); + $(window).on('fluidbookresize', fluidbook.resizeMenu); // Handle swipe to close (it's only really practical to have this because swipe to open would interfere with main Fluidbook swipes) // We are not using MMenu's "Drag" add-on because it doesn't close the main menu, only submenus on swipe. @@ -85,6 +89,15 @@ FluidbookNav.prototype = { + // Add search and share links to main menu panel + $('#menuList').append(this.searchHTML + this.shareLinksHTML); + + // Add scrollbar to main menu + $('#menuList > ul, #chapterList').perfectScrollbar({ + suppressScrollX: true, + minScrollbarLength: 40, + //maxScrollbarLength: 60 + }); }, // getIcon: function (name) { // var src = 'data/images/' + name + '.'; @@ -218,7 +231,7 @@ FluidbookNav.prototype = { // Add Search form, or if disabled, add "Menu" label in its place var searchElement = (this.fluidbook.datas.search) ? this.getSearch() : '' + this.fluidbook.l10n.__('Menu') + ''; - $('#menuList').prepend(''); + this.searchHTML = ''; // Save for later so it can be injected after MMenu is rendered // Horizontal icon nav } else if (navType == 'horizontalNav') { @@ -340,8 +353,8 @@ FluidbookNav.prototype = { } } - // Append to menu - this.menu.find('ul').append(''); + // Save HTML so it can be added to menu once MMenu is initialised + this.shareLinksHTML = ''; } @@ -463,6 +476,8 @@ FluidbookNav.prototype = { $this.menuAPI.closeAllPanels(); // Make sure we're on the main panel with the search box } $this.menuAPI.open(); + + $('#q').focus(); // Put cursor in the search field }); // Full screen toggle @@ -671,6 +686,12 @@ FluidbookNav.prototype = { fluidbook.hideSearchHints(); fluidbook.menuSearchResults.fadeIn(300); + // On some phones, the height of the search results is miscalculated due to the + // keyboard being open and reducing the available window height. We need to wait until + // the keyboard closes before trying to resize again... + setTimeout(fluidbook.resizeMenu, 500); + + // Initialise scrollbar after populating so bars appear immediately fluidbook.menuSearchResults.perfectScrollbar({ suppressScrollX: true, diff --git a/style/mmenu/mmenu.less b/style/mmenu/mmenu.less index 4e9d8a43..592a7fdc 100644 --- a/style/mmenu/mmenu.less +++ b/style/mmenu/mmenu.less @@ -6,6 +6,7 @@ .mm-menu { &.mm-offcanvas { max-width: 320px; + width: 100%; // Max-width will limit overall width but this allows it to take up more space on very narrow screens } .mm-navbar { @@ -111,7 +112,10 @@ html.mm-opening .mm-menu.mm-opened[class*=mm-pagedim]~#mm-blocker { } #menuSearch { - margin-bottom: 23px; // Spacing between search panel and first menu item + position: fixed; // So we can scroll the other items below... + top: 0; + left: 0; + width: 100%; &:after { border: none; @@ -121,12 +125,20 @@ html.mm-opening .mm-menu.mm-opened[class*=mm-pagedim]~#mm-blocker { // Main menu with icons #menuList { overflow: hidden; - //padding-top: 80px; // Offset for the fixed search box plus some extra padding + position: relative; + height: 100%; &:before { height: 0; // Fix spacing with search box } + > ul { + margin-top: 60px; // Spacing for search box + margin-bottom: 0; + padding-top: 20px; + padding-bottom: 20px; + } + .svg-icon { width: 26px; height: 26px; @@ -295,6 +307,7 @@ html.mm-opening .mm-menu.mm-opened[class*=mm-pagedim]~#mm-blocker { .share-icons { padding-left: 29px; + white-space: nowrap; } a { -- 2.39.5