From 4fb5fb052f75a55b59b9fb33eb5801de0ca2d578 Mon Sep 17 00:00:00 2001 From: Bas Brands Date: Wed, 20 Oct 2021 09:16:17 +0200 Subject: [PATCH] MDL-72250 theme_boost: add mobile nav drawer --- lib/classes/navigation/output/primary.php | 2 + lib/tests/navigation/output/primary_test.php | 18 ++-- theme/boost/amd/build/drawers.min.js | 2 +- theme/boost/amd/build/drawers.min.js.map | 2 +- theme/boost/amd/src/drawers.js | 16 +-- theme/boost/layout/columns2.php | 1 + theme/boost/layout/drawers.php | 1 + .../boost/scss/moodle/primarynavigation.scss | 23 +++++ theme/boost/style/moodle.css | 16 +++ theme/boost/templates/drawer.mustache | 2 +- theme/boost/templates/navbar.mustache | 28 +++++- .../templates/primary-drawer-mobile.mustache | 97 +++++++++++++++++++ theme/classic/style/moodle.css | 16 +++ 13 files changed, 203 insertions(+), 21 deletions(-) create mode 100644 theme/boost/templates/primary-drawer-mobile.mustache diff --git a/lib/classes/navigation/output/primary.php b/lib/classes/navigation/output/primary.php index 3f1b34a5f9e..f1716ac9115 100644 --- a/lib/classes/navigation/output/primary.php +++ b/lib/classes/navigation/output/primary.php @@ -57,10 +57,12 @@ class primary implements renderable, templatable { $menudata = (object) array_merge($this->get_primary_nav(), $this->get_custom_menu($output)); $moremenu = new \core\navigation\output\more_menu($menudata, 'navbar-nav', false); + $mobileprimarynav = array_merge($this->get_primary_nav(), $this->get_custom_menu($output)); $languagemenu = new \core\output\language_menu($this->page); return [ + 'mobileprimarynav' => $mobileprimarynav, 'moremenu' => $moremenu->export_for_template($output), 'lang' => !isloggedin() || isguestuser() ? $languagemenu->export_for_template($output) : [], 'user' => $this->get_user_menu($output), diff --git a/lib/tests/navigation/output/primary_test.php b/lib/tests/navigation/output/primary_test.php index b6aac00b7ab..a87a523f3cf 100644 --- a/lib/tests/navigation/output/primary_test.php +++ b/lib/tests/navigation/output/primary_test.php @@ -116,31 +116,31 @@ class primary_test extends \advanced_testcase { public function test_primary_export_provider(): array { return [ "Export the menu data when: custom menu exists; multiple langs installed; user is not logged in." => [ - true, true, '', ['moremenu', 'lang', 'user'] + true, true, '', ['mobileprimarynav', 'moremenu', 'lang', 'user'] ], "Export the menu data when: custom menu exists; langs not installed; user is not logged in." => [ - true, false, '', ['moremenu', 'user'] + true, false, '', ['mobileprimarynav', 'moremenu', 'user'] ], "Export the menu data when: custom menu exists; multiple langs installed; logged in as admin." => [ - true, true, 'admin', ['moremenu', 'user'] + true, true, 'admin', ['mobileprimarynav', 'moremenu', 'user'] ], "Export the menu data when: custom menu exists; langs not installed; logged in as admin." => [ - true, false, 'admin', ['moremenu', 'user'] + true, false, 'admin', ['mobileprimarynav', 'moremenu', 'user'] ], "Export the menu data when: custom menu exists; multiple langs installed; logged in as guest." => [ - true, true, 'guest', ['moremenu', 'lang', 'user'] + true, true, 'guest', ['mobileprimarynav', 'moremenu', 'lang', 'user'] ], "Export the menu data when: custom menu exists; langs not installed; logged in as guest." => [ - true, false, 'guest', ['moremenu', 'user'] + true, false, 'guest', ['mobileprimarynav', 'moremenu', 'user'] ], "Export the menu data when: custom menu does not exist; multiple langs installed; logged in as guest." => [ - false, true, 'guest', ['moremenu', 'lang', 'user'] + false, true, 'guest', ['mobileprimarynav', 'moremenu', 'lang', 'user'] ], "Export the menu data when: custom menu does not exist; multiple langs installed; logged in as admin." => [ - false, true, 'admin', ['moremenu', 'user'] + false, true, 'admin', ['mobileprimarynav', 'moremenu', 'user'] ], "Export the menu data when: custom menu does not exist; langs not installed; user is not logged in." => [ - false, false, '', ['moremenu', 'user'] + false, false, '', ['mobileprimarynav', 'moremenu', 'user'] ], ]; } diff --git a/theme/boost/amd/build/drawers.min.js b/theme/boost/amd/build/drawers.min.js index a097190fb8c..eaaa5971964 100644 --- a/theme/boost/amd/build/drawers.min.js +++ b/theme/boost/amd/build/drawers.min.js @@ -1,2 +1,2 @@ -function _typeof(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){_typeof=function(a){return typeof a}}else{_typeof=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return _typeof(a)}define ("theme_boost/drawers",["exports","core/modal_backdrop","core/templates","core/aria","core/event_dispatcher","core/utils"],function(a,b,c,d,e,f){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;b=i(b);c=i(c);d=h(d);function g(){if("function"!=typeof WeakMap)return null;var a=new WeakMap;g=function(){return a};return a}function h(a){if(a&&a.__esModule){return a}if(null===a||"object"!==_typeof(a)&&"function"!=typeof a){return{default:a}}var b=g();if(b&&b.has(a)){return b.get(a)}var c={},d=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var e in a){if(Object.prototype.hasOwnProperty.call(a,e)){var f=d?Object.getOwnPropertyDescriptor(a,e):null;if(f&&(f.get||f.set)){Object.defineProperty(c,e,f)}else{c[e]=a[e]}}}c.default=a;if(b){b.set(a,c)}return c}function i(a){return a&&a.__esModule?a:{default:a}}function j(a,b){if(!(a instanceof b)){throw new TypeError("Cannot call a class as a function")}}function k(a,b){for(var c=0,d;c=p.large},t=function(){if(!n){n=c.default.render("core/modal_backdrop",{}).then(function(a){return new b.default(a)}).then(function(a){a.getAttachmentPoint().get(0).addEventListener("click",function(a){a.preventDefault();u.closeAllDrawers()});return a}).catch()}return n},u=function(){function a(b){j(this,a);m(this,"drawerNode",null);this.drawerNode=b;if(this.drawerNode.classList.contains("show")){this.openDrawer({focusOnCloseButton:!1})}else if(1==this.drawerNode.dataset.forceopen){if(!r()){this.openDrawer({focusOnCloseButton:!1})}}else{d.hide(this.drawerNode)}o.set(b,this)}l(a,[{key:"dispatchEvent",value:function dispatchEvent(a){var b=1=window.innerHeight){a.classList.add("scrolled")}else{a.classList.remove("scrolled")}})})();(function registerListeners(){document.addEventListener("click",function(a){var b=a.target.closest("[data-toggle=\"drawers\"][data-action=\"toggle\"]");if(b&&b.dataset.target){a.preventDefault();var c=document.getElementById(b.dataset.target),d=u.getDrawerInstanceForNode(c);v(b);d.toggleVisibility()}var e=a.target.closest("[data-toggle=\"drawers\"][data-action=\"opendrawer\"]");if(e&&e.dataset.target){a.preventDefault();var f=document.getElementById(e.dataset.target),g=u.getDrawerInstanceForNode(f);v(b);g.openDrawer()}var h=a.target.closest("[data-toggle=\"drawers\"][data-action=\"closedrawer\"]");if(h&&h.dataset.target){a.preventDefault();var i=document.getElementById(h.dataset.target),j=u.getDrawerInstanceForNode(i);j.closeDrawer();w(h.dataset.target)}});document.addEventListener(u.eventTypes.drawerShow,function(a){if(s()){return}u.closeOtherDrawers(a.detail.drawerInstance)});window.addEventListener("resize",(0,f.debounce)(function closeOnResizeListener(){if(r()){var a=!1;o.forEach(function(b){if(b.isOpen){if(b.closeOnResize){b.closeDrawer()}else{a=!0}}});if(a){t().then(function(a){return a.show()}).catch()}}else{t().then(function(a){return a.hide()}).catch()}},400))})();var x=document.querySelectorAll("[data-region=\"fixed-drawer\"]");x.forEach(function(a){return u.getDrawerInstanceForNode(a)});return a.default}); +function _typeof(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){_typeof=function(a){return typeof a}}else{_typeof=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return _typeof(a)}define ("theme_boost/drawers",["exports","core/modal_backdrop","core/templates","core/aria","core/event_dispatcher","core/utils"],function(a,b,c,d,e,f){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;b=i(b);c=i(c);d=h(d);function g(){if("function"!=typeof WeakMap)return null;var a=new WeakMap;g=function(){return a};return a}function h(a){if(a&&a.__esModule){return a}if(null===a||"object"!==_typeof(a)&&"function"!=typeof a){return{default:a}}var b=g();if(b&&b.has(a)){return b.get(a)}var c={},d=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var e in a){if(Object.prototype.hasOwnProperty.call(a,e)){var f=d?Object.getOwnPropertyDescriptor(a,e):null;if(f&&(f.get||f.set)){Object.defineProperty(c,e,f)}else{c[e]=a[e]}}}c.default=a;if(b){b.set(a,c)}return c}function i(a){return a&&a.__esModule?a:{default:a}}function j(a,b){if(!(a instanceof b)){throw new TypeError("Cannot call a class as a function")}}function k(a,b){for(var c=0,d;c=p.large},t=function(){if(!n){n=c.default.render("core/modal_backdrop",{}).then(function(a){return new b.default(a)}).then(function(a){a.getAttachmentPoint().get(0).addEventListener("click",function(a){a.preventDefault();u.closeAllDrawers()});return a}).catch()}return n},u=function(){function a(b){j(this,a);m(this,"drawerNode",null);this.drawerNode=b;if(this.drawerNode.classList.contains("show")){this.openDrawer({focusOnCloseButton:!1})}else if(1==this.drawerNode.dataset.forceopen){if(!r()){this.openDrawer({focusOnCloseButton:!1})}}else{d.hide(this.drawerNode)}o.set(b,this)}l(a,[{key:"dispatchEvent",value:function dispatchEvent(a){var b=1=window.innerHeight){a.classList.add("scrolled")}else{a.classList.remove("scrolled")}})}})();(function registerListeners(){document.addEventListener("click",function(a){var b=a.target.closest("[data-toggle=\"drawers\"][data-action=\"toggle\"]");if(b&&b.dataset.target){a.preventDefault();var c=document.getElementById(b.dataset.target),d=u.getDrawerInstanceForNode(c);v(b);d.toggleVisibility()}var e=a.target.closest("[data-toggle=\"drawers\"][data-action=\"opendrawer\"]");if(e&&e.dataset.target){a.preventDefault();var f=document.getElementById(e.dataset.target),g=u.getDrawerInstanceForNode(f);v(b);g.openDrawer()}var h=a.target.closest("[data-toggle=\"drawers\"][data-action=\"closedrawer\"]");if(h&&h.dataset.target){a.preventDefault();var i=document.getElementById(h.dataset.target),j=u.getDrawerInstanceForNode(i);j.closeDrawer();w(h.dataset.target)}});document.addEventListener(u.eventTypes.drawerShow,function(a){if(s()){return}u.closeOtherDrawers(a.detail.drawerInstance)});window.addEventListener("resize",(0,f.debounce)(function closeOnResizeListener(){if(r()){var a=!1;o.forEach(function(b){if(b.isOpen){if(b.closeOnResize){b.closeDrawer()}else{a=!0}}});if(a){t().then(function(a){return a.show()}).catch()}}else{t().then(function(a){return a.hide()}).catch()}},400))})();var x=document.querySelectorAll("[data-region=\"fixed-drawer\"]");x.forEach(function(a){return u.getDrawerInstanceForNode(a)});return a.default}); //# sourceMappingURL=drawers.min.js.map diff --git a/theme/boost/amd/build/drawers.min.js.map b/theme/boost/amd/build/drawers.min.js.map index a771e752946..be2068610f3 100644 --- a/theme/boost/amd/build/drawers.min.js.map +++ b/theme/boost/amd/build/drawers.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/drawers.js"],"names":["backdropPromise","drawerMap","Map","sizes","medium","large","getCurrentWidth","DomRect","document","body","getBoundingClientRect","x","width","isSmall","browserWidth","isLarge","getBackdrop","Templates","render","then","html","ModalBackdrop","modalBackdrop","getAttachmentPoint","get","addEventListener","e","preventDefault","Drawers","closeAllDrawers","catch","drawerNode","classList","contains","openDrawer","focusOnCloseButton","dataset","forceopen","Aria","hide","set","eventname","cancelable","drawerInstance","showEvent","dispatchEvent","eventTypes","drawerShow","defaultPrevented","unhide","add","preference","M","util","set_user_preference","state","page","getElementById","backdrop","show","pageWrapper","style","overflow","closeButton","querySelector","focus","drawerShown","hideEvent","drawerHide","remove","drawerHidden","closeDrawer","parseInt","closeOnResize","has","forEach","comparisonInstance","setLastUsedToggle","toggleButton","target","querySelectorAll","btn","lastused","focusLastUsedToggle","lastUsedButton","scroller","drawerLayout","scrollTop","window","innerHeight","registerListeners","closest","targetDrawer","getDrawerInstanceForNode","toggleVisibility","openDrawerButton","closeDrawerButton","closeOtherDrawers","detail","closeOnResizeListener","anyOpen","isOpen","drawers"],"mappings":"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","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Toggling the visibility of the secondary navigation on mobile.\n *\n * @module theme_boost/drawers\n * @copyright 2021 Bas Brands\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport ModalBackdrop from 'core/modal_backdrop';\nimport Templates from 'core/templates';\nimport * as Aria from 'core/aria';\nimport {dispatchEvent} from 'core/event_dispatcher';\nimport {debounce} from 'core/utils';\n\nlet backdropPromise = null;\n\nconst drawerMap = new Map();\n\n/**\n * Maximum sizes for breakpoints. This needs to correspond with Bootstrap\n * Breakpoints\n *\n * @private\n */\nconst sizes = {\n medium: 991,\n large: 1400\n};\n\n/**\n * Get the current body width.\n *\n * @returns {number} the current body width.\n * @private\n */\nconst getCurrentWidth = () => {\n const DomRect = document.body.getBoundingClientRect();\n return DomRect.x + DomRect.width;\n};\n\n/**\n * Check if the user uses a small size browser.\n *\n * @returns {boolean} true if the body is smaller than sizes.medium max size.\n * @private\n */\nconst isSmall = () => {\n const browserWidth = getCurrentWidth();\n return browserWidth < sizes.medium;\n};\n\n/**\n * Check if the user uses a large size browser.\n *\n * @returns {boolean} true if the body is smaller than sizes.large max size.\n * @private\n */\nconst isLarge = () => {\n const browserWidth = getCurrentWidth();\n return browserWidth >= sizes.large;\n};\n\n/**\n * Add a backdrop to the page.\n *\n * @returns {Promise} rendering of modal backdrop.\n * @private\n */\nconst getBackdrop = () => {\n if (!backdropPromise) {\n backdropPromise = Templates.render('core/modal_backdrop', {})\n .then(html => new ModalBackdrop(html))\n .then(modalBackdrop => {\n modalBackdrop.getAttachmentPoint().get(0).addEventListener('click', e => {\n e.preventDefault();\n Drawers.closeAllDrawers();\n });\n return modalBackdrop;\n })\n .catch();\n }\n return backdropPromise;\n};\n\n/**\n * The Drawers class is used to control on-screen drawer elements.\n *\n * It handles opening, and closing of drawer elements, as well as more detailed behaviours such as closing a drawer when\n * another drawer is opened, and supports closing a drawer when the screen is resized.\n *\n * Drawers are instantiated on page load, and can also be toggled lazily when toggling any drawer toggle, open button,\n * or close button.\n *\n * A range of show and hide events are also dispatched as detailed in the class\n * {@link module:theme_boost/drawers#eventTypes eventTypes} object.\n *\n * @example Standard usage\n *\n * // The module just needs to be included to add drawer support.\n * import 'theme_boost/drawers';\n *\n * @example Manually open or close any drawer\n *\n * import Drawers from 'theme_boost/drawers';\n *\n * const myDrawer = Drawers.getDrawerInstanceForNode(document.querySelector('.myDrawerNode');\n * myDrawer.closeDrawer();\n *\n * @example Listen to the before show event and cancel it\n *\n * import Drawers from 'theme_boost/drawers';\n *\n * document.addEventListener(Drawers.eventTypes.drawerShow, e => {\n * // The drawer which will be shown.\n * window.console.log(e.target);\n *\n * // The instance of the Drawers class for this drawer.\n * window.console.log(e.detail.drawerInstance);\n *\n * // Prevent this drawer from being shown.\n * e.preventDefault();\n * });\n *\n * @example Listen to the shown event\n *\n * document.addEventListener(Drawers.eventTypes.drawerShown, e => {\n * // The drawer which was shown.\n * window.console.log(e.target);\n *\n * // The instance of the Drawers class for this drawer.\n * window.console.log(e.detail.drawerInstance);\n * });\n */\nexport default class Drawers {\n /**\n * The underlying HTMLElement which is controlled.\n */\n drawerNode = null;\n\n constructor(drawerNode) {\n this.drawerNode = drawerNode;\n\n if (this.drawerNode.classList.contains('show')) {\n this.openDrawer({focusOnCloseButton: false});\n } else if (this.drawerNode.dataset.forceopen == 1) {\n if (!isSmall()) {\n this.openDrawer({focusOnCloseButton: false});\n }\n } else {\n Aria.hide(this.drawerNode);\n }\n\n drawerMap.set(drawerNode, this);\n }\n\n /**\n * Whether the drawer is open.\n *\n * @returns {boolean}\n */\n get isOpen() {\n return this.drawerNode.classList.contains('show');\n }\n\n /**\n * Whether the drawer should close when the window is resized\n *\n * @returns {boolean}\n */\n get closeOnResize() {\n return !!parseInt(this.drawerNode.dataset.closeOnResize);\n }\n\n /**\n * The list of event types.\n *\n * @static\n * @property {String} drawerShow See {@link event:theme_boost/drawers:show}\n * @property {String} drawerShown See {@link event:theme_boost/drawers:shown}\n * @property {String} drawerHide See {@link event:theme_boost/drawers:hide}\n * @property {String} drawerHidden See {@link event:theme_boost/drawers:hidden}\n */\n static eventTypes = {\n /**\n * An event triggered before a drawer is shown.\n *\n * @event theme_boost/drawers:show\n * @type {CustomEvent}\n * @property {HTMLElement} target The drawer that will be opened.\n */\n drawerShow: 'theme_boost/drawers:show',\n\n /**\n * An event triggered after a drawer is shown.\n *\n * @event theme_boost/drawers:shown\n * @type {CustomEvent}\n * @property {HTMLElement} target The drawer that was be opened.\n */\n drawerShown: 'theme_boost/drawers:shown',\n\n /**\n * An event triggered before a drawer is hidden.\n *\n * @event theme_boost/drawers:hide\n * @type {CustomEvent}\n * @property {HTMLElement} target The drawer that will be hidden.\n */\n drawerHide: 'theme_boost/drawers:hide',\n\n /**\n * An event triggered after a drawer is hidden.\n *\n * @event theme_boost/drawers:hidden\n * @type {CustomEvent}\n * @property {HTMLElement} target The drawer that was be hidden.\n */\n drawerHidden: 'theme_boost/drawers:hidden',\n };\n\n\n /**\n * Get the drawer instance for the specified node\n *\n * @param {HTMLElement} drawerNode\n * @returns {module:theme_boost/drawers}\n */\n static getDrawerInstanceForNode(drawerNode) {\n if (!drawerMap.has(drawerNode)) {\n new Drawers(drawerNode);\n }\n\n return drawerMap.get(drawerNode);\n }\n\n /**\n * Dispatch a drawer event.\n *\n * @param {string} eventname the event name\n * @param {boolean} cancelable if the event is cancelable\n * @returns {CustomEvent} the resulting custom event\n */\n dispatchEvent(eventname, cancelable = false) {\n return dispatchEvent(\n eventname,\n {\n drawerInstance: this,\n },\n this.drawerNode,\n {\n cancelable,\n }\n );\n }\n\n /**\n * Open the drawer.\n *\n * By default, openDrawer sets the page focus to the close drawer button. However, when a drawer is open at page\n * load, this represents an accessibility problem as the initial focus changes without any user interaction. The\n * focusOnCloseButton parameter can be set to false to prevent this behaviour.\n *\n * @param {object} args\n * @param {boolean} [args.focusOnCloseButton=true] Whether to alter page focus when opening the drawer\n */\n openDrawer({focusOnCloseButton = true} = {}) {\n const showEvent = this.dispatchEvent(Drawers.eventTypes.drawerShow, true);\n if (showEvent.defaultPrevented) {\n return;\n }\n\n Aria.unhide(this.drawerNode);\n this.drawerNode.classList.add('show');\n\n const preference = this.drawerNode.dataset.preference;\n if (preference && !isSmall() && (this.drawerNode.dataset.forceopen != 1)) {\n M.util.set_user_preference(preference, true);\n }\n\n const state = this.drawerNode.dataset.state;\n if (state) {\n const page = document.getElementById('page');\n page.classList.add(state);\n }\n\n if (isSmall()) {\n getBackdrop().then(backdrop => {\n backdrop.show();\n\n const pageWrapper = document.getElementById('page-wrapper');\n pageWrapper.style.overflow = 'hidden';\n return backdrop;\n })\n .catch();\n }\n\n if (focusOnCloseButton) {\n const closeButton = this.drawerNode.querySelector('[data-toggle=\"drawers\"][data-action=\"closedrawer\"]');\n closeButton.focus();\n }\n\n this.dispatchEvent(Drawers.eventTypes.drawerShown);\n }\n\n /**\n * Close the drawer.\n */\n closeDrawer() {\n const hideEvent = this.dispatchEvent(Drawers.eventTypes.drawerHide, true);\n if (hideEvent.defaultPrevented) {\n return;\n }\n\n const preference = this.drawerNode.dataset.preference;\n if (preference) {\n M.util.set_user_preference(preference, false);\n }\n\n const state = this.drawerNode.dataset.state;\n if (state) {\n const page = document.getElementById('page');\n page.classList.remove(state);\n }\n\n Aria.hide(this.drawerNode);\n this.drawerNode.classList.remove('show');\n\n getBackdrop().then(backdrop => {\n backdrop.hide();\n\n if (isSmall()) {\n const pageWrapper = document.getElementById('page-wrapper');\n pageWrapper.style.overflow = 'auto';\n }\n return backdrop;\n })\n .catch();\n\n this.dispatchEvent(Drawers.eventTypes.drawerHidden);\n }\n\n /**\n * Toggle visibility of the drawer.\n */\n toggleVisibility() {\n if (this.drawerNode.classList.contains('show')) {\n this.closeDrawer();\n } else {\n this.openDrawer();\n }\n }\n\n /**\n * Close all drawers.\n */\n static closeAllDrawers() {\n drawerMap.forEach(drawerInstance => {\n drawerInstance.closeDrawer();\n });\n }\n\n /**\n * Close all drawers except for the specified drawer.\n *\n * @param {module:theme_boost/drawers} comparisonInstance\n */\n static closeOtherDrawers(comparisonInstance) {\n drawerMap.forEach(drawerInstance => {\n if (drawerInstance === comparisonInstance) {\n return;\n }\n\n drawerInstance.closeDrawer();\n });\n }\n}\n\n/**\n * Activate the scroller helper for the drawer layout.\n *\n * @private\n */\nconst scroller = () => {\n const body = document.querySelector('body');\n const drawerLayout = document.querySelector('#page.drawers');\n drawerLayout.addEventListener(\"scroll\", () => {\n if (drawerLayout.scrollTop >= window.innerHeight) {\n body.classList.add('scrolled');\n } else {\n body.classList.remove('scrolled');\n }\n });\n};\n\n/**\n * Set the last used attribute for the last used toggle button for a drawer.\n *\n * @param {object} toggleButton The clicked button.\n */\nconst setLastUsedToggle = (toggleButton) => {\n if (toggleButton.dataset.target) {\n document.querySelectorAll('[data-toggle=\"drawers\"][data-target=\"' + toggleButton.dataset.target + '\"]')\n .forEach(btn => {\n btn.dataset.lastused = false;\n });\n toggleButton.dataset.lastused = true;\n }\n};\n\n/**\n * Set the focus to the last used button to open this drawer.\n * @param {string} target The drawer target.\n */\nconst focusLastUsedToggle = (target) => {\n const lastUsedButton = document.querySelector('[data-toggle=\"drawers\"][data-target=\"' + target + '\"][data-lastused=\"true\"');\n if (lastUsedButton) {\n lastUsedButton.focus();\n }\n};\n\n/**\n * Register the event listeners for the drawer.\n *\n * @private\n */\nconst registerListeners = () => {\n // Listen for show/hide events.\n document.addEventListener('click', e => {\n const toggleButton = e.target.closest('[data-toggle=\"drawers\"][data-action=\"toggle\"]');\n if (toggleButton && toggleButton.dataset.target) {\n e.preventDefault();\n const targetDrawer = document.getElementById(toggleButton.dataset.target);\n const drawerInstance = Drawers.getDrawerInstanceForNode(targetDrawer);\n setLastUsedToggle(toggleButton);\n\n drawerInstance.toggleVisibility();\n }\n\n const openDrawerButton = e.target.closest('[data-toggle=\"drawers\"][data-action=\"opendrawer\"]');\n if (openDrawerButton && openDrawerButton.dataset.target) {\n e.preventDefault();\n const targetDrawer = document.getElementById(openDrawerButton.dataset.target);\n const drawerInstance = Drawers.getDrawerInstanceForNode(targetDrawer);\n setLastUsedToggle(toggleButton);\n\n drawerInstance.openDrawer();\n }\n\n const closeDrawerButton = e.target.closest('[data-toggle=\"drawers\"][data-action=\"closedrawer\"]');\n if (closeDrawerButton && closeDrawerButton.dataset.target) {\n e.preventDefault();\n const targetDrawer = document.getElementById(closeDrawerButton.dataset.target);\n const drawerInstance = Drawers.getDrawerInstanceForNode(targetDrawer);\n\n drawerInstance.closeDrawer();\n focusLastUsedToggle(closeDrawerButton.dataset.target);\n }\n });\n\n // Close drawer when another drawer opens.\n document.addEventListener(Drawers.eventTypes.drawerShow, e => {\n if (isLarge()) {\n return;\n }\n Drawers.closeOtherDrawers(e.detail.drawerInstance);\n });\n\n const closeOnResizeListener = () => {\n if (isSmall()) {\n let anyOpen = false;\n drawerMap.forEach(drawerInstance => {\n if (drawerInstance.isOpen) {\n if (drawerInstance.closeOnResize) {\n drawerInstance.closeDrawer();\n } else {\n anyOpen = true;\n }\n }\n });\n\n if (anyOpen) {\n getBackdrop().then(backdrop => backdrop.show()).catch();\n }\n } else {\n getBackdrop().then(backdrop => backdrop.hide()).catch();\n }\n };\n\n window.addEventListener('resize', debounce(closeOnResizeListener, 400));\n};\n\nscroller();\nregisterListeners();\n\nconst drawers = document.querySelectorAll('[data-region=\"fixed-drawer\"]');\ndrawers.forEach(drawerNode => Drawers.getDrawerInstanceForNode(drawerNode));\n"],"file":"drawers.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/drawers.js"],"names":["backdropPromise","drawerMap","Map","sizes","medium","large","getCurrentWidth","DomRect","document","body","getBoundingClientRect","x","width","isSmall","browserWidth","isLarge","getBackdrop","Templates","render","then","html","ModalBackdrop","modalBackdrop","getAttachmentPoint","get","addEventListener","e","preventDefault","Drawers","closeAllDrawers","catch","drawerNode","classList","contains","openDrawer","focusOnCloseButton","dataset","forceopen","Aria","hide","set","eventname","cancelable","drawerInstance","showEvent","dispatchEvent","eventTypes","drawerShow","defaultPrevented","unhide","add","preference","M","util","set_user_preference","state","page","getElementById","backdrop","show","pageWrapper","style","overflow","closeButton","querySelector","focus","drawerShown","hideEvent","drawerHide","remove","drawerHidden","closeDrawer","parseInt","closeOnResize","has","forEach","comparisonInstance","setLastUsedToggle","toggleButton","target","querySelectorAll","btn","lastused","focusLastUsedToggle","lastUsedButton","scroller","drawerLayout","scrollTop","window","innerHeight","registerListeners","closest","targetDrawer","getDrawerInstanceForNode","toggleVisibility","openDrawerButton","closeDrawerButton","closeOtherDrawers","detail","closeOnResizeListener","anyOpen","isOpen","drawers"],"mappings":"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","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Toggling the visibility of the secondary navigation on mobile.\n *\n * @module theme_boost/drawers\n * @copyright 2021 Bas Brands\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport ModalBackdrop from 'core/modal_backdrop';\nimport Templates from 'core/templates';\nimport * as Aria from 'core/aria';\nimport {dispatchEvent} from 'core/event_dispatcher';\nimport {debounce} from 'core/utils';\n\nlet backdropPromise = null;\n\nconst drawerMap = new Map();\n\n/**\n * Maximum sizes for breakpoints. This needs to correspond with Bootstrap\n * Breakpoints\n *\n * @private\n */\nconst sizes = {\n medium: 991,\n large: 1400\n};\n\n/**\n * Get the current body width.\n *\n * @returns {number} the current body width.\n * @private\n */\nconst getCurrentWidth = () => {\n const DomRect = document.body.getBoundingClientRect();\n return DomRect.x + DomRect.width;\n};\n\n/**\n * Check if the user uses a small size browser.\n *\n * @returns {boolean} true if the body is smaller than sizes.medium max size.\n * @private\n */\nconst isSmall = () => {\n const browserWidth = getCurrentWidth();\n return browserWidth < sizes.medium;\n};\n\n/**\n * Check if the user uses a large size browser.\n *\n * @returns {boolean} true if the body is smaller than sizes.large max size.\n * @private\n */\nconst isLarge = () => {\n const browserWidth = getCurrentWidth();\n return browserWidth >= sizes.large;\n};\n\n/**\n * Add a backdrop to the page.\n *\n * @returns {Promise} rendering of modal backdrop.\n * @private\n */\nconst getBackdrop = () => {\n if (!backdropPromise) {\n backdropPromise = Templates.render('core/modal_backdrop', {})\n .then(html => new ModalBackdrop(html))\n .then(modalBackdrop => {\n modalBackdrop.getAttachmentPoint().get(0).addEventListener('click', e => {\n e.preventDefault();\n Drawers.closeAllDrawers();\n });\n return modalBackdrop;\n })\n .catch();\n }\n return backdropPromise;\n};\n\n/**\n * The Drawers class is used to control on-screen drawer elements.\n *\n * It handles opening, and closing of drawer elements, as well as more detailed behaviours such as closing a drawer when\n * another drawer is opened, and supports closing a drawer when the screen is resized.\n *\n * Drawers are instantiated on page load, and can also be toggled lazily when toggling any drawer toggle, open button,\n * or close button.\n *\n * A range of show and hide events are also dispatched as detailed in the class\n * {@link module:theme_boost/drawers#eventTypes eventTypes} object.\n *\n * @example Standard usage\n *\n * // The module just needs to be included to add drawer support.\n * import 'theme_boost/drawers';\n *\n * @example Manually open or close any drawer\n *\n * import Drawers from 'theme_boost/drawers';\n *\n * const myDrawer = Drawers.getDrawerInstanceForNode(document.querySelector('.myDrawerNode');\n * myDrawer.closeDrawer();\n *\n * @example Listen to the before show event and cancel it\n *\n * import Drawers from 'theme_boost/drawers';\n *\n * document.addEventListener(Drawers.eventTypes.drawerShow, e => {\n * // The drawer which will be shown.\n * window.console.log(e.target);\n *\n * // The instance of the Drawers class for this drawer.\n * window.console.log(e.detail.drawerInstance);\n *\n * // Prevent this drawer from being shown.\n * e.preventDefault();\n * });\n *\n * @example Listen to the shown event\n *\n * document.addEventListener(Drawers.eventTypes.drawerShown, e => {\n * // The drawer which was shown.\n * window.console.log(e.target);\n *\n * // The instance of the Drawers class for this drawer.\n * window.console.log(e.detail.drawerInstance);\n * });\n */\nexport default class Drawers {\n /**\n * The underlying HTMLElement which is controlled.\n */\n drawerNode = null;\n\n constructor(drawerNode) {\n this.drawerNode = drawerNode;\n\n if (this.drawerNode.classList.contains('show')) {\n this.openDrawer({focusOnCloseButton: false});\n } else if (this.drawerNode.dataset.forceopen == 1) {\n if (!isSmall()) {\n this.openDrawer({focusOnCloseButton: false});\n }\n } else {\n Aria.hide(this.drawerNode);\n }\n\n drawerMap.set(drawerNode, this);\n }\n\n /**\n * Whether the drawer is open.\n *\n * @returns {boolean}\n */\n get isOpen() {\n return this.drawerNode.classList.contains('show');\n }\n\n /**\n * Whether the drawer should close when the window is resized\n *\n * @returns {boolean}\n */\n get closeOnResize() {\n return !!parseInt(this.drawerNode.dataset.closeOnResize);\n }\n\n /**\n * The list of event types.\n *\n * @static\n * @property {String} drawerShow See {@link event:theme_boost/drawers:show}\n * @property {String} drawerShown See {@link event:theme_boost/drawers:shown}\n * @property {String} drawerHide See {@link event:theme_boost/drawers:hide}\n * @property {String} drawerHidden See {@link event:theme_boost/drawers:hidden}\n */\n static eventTypes = {\n /**\n * An event triggered before a drawer is shown.\n *\n * @event theme_boost/drawers:show\n * @type {CustomEvent}\n * @property {HTMLElement} target The drawer that will be opened.\n */\n drawerShow: 'theme_boost/drawers:show',\n\n /**\n * An event triggered after a drawer is shown.\n *\n * @event theme_boost/drawers:shown\n * @type {CustomEvent}\n * @property {HTMLElement} target The drawer that was be opened.\n */\n drawerShown: 'theme_boost/drawers:shown',\n\n /**\n * An event triggered before a drawer is hidden.\n *\n * @event theme_boost/drawers:hide\n * @type {CustomEvent}\n * @property {HTMLElement} target The drawer that will be hidden.\n */\n drawerHide: 'theme_boost/drawers:hide',\n\n /**\n * An event triggered after a drawer is hidden.\n *\n * @event theme_boost/drawers:hidden\n * @type {CustomEvent}\n * @property {HTMLElement} target The drawer that was be hidden.\n */\n drawerHidden: 'theme_boost/drawers:hidden',\n };\n\n\n /**\n * Get the drawer instance for the specified node\n *\n * @param {HTMLElement} drawerNode\n * @returns {module:theme_boost/drawers}\n */\n static getDrawerInstanceForNode(drawerNode) {\n if (!drawerMap.has(drawerNode)) {\n new Drawers(drawerNode);\n }\n\n return drawerMap.get(drawerNode);\n }\n\n /**\n * Dispatch a drawer event.\n *\n * @param {string} eventname the event name\n * @param {boolean} cancelable if the event is cancelable\n * @returns {CustomEvent} the resulting custom event\n */\n dispatchEvent(eventname, cancelable = false) {\n return dispatchEvent(\n eventname,\n {\n drawerInstance: this,\n },\n this.drawerNode,\n {\n cancelable,\n }\n );\n }\n\n /**\n * Open the drawer.\n *\n * By default, openDrawer sets the page focus to the close drawer button. However, when a drawer is open at page\n * load, this represents an accessibility problem as the initial focus changes without any user interaction. The\n * focusOnCloseButton parameter can be set to false to prevent this behaviour.\n *\n * @param {object} args\n * @param {boolean} [args.focusOnCloseButton=true] Whether to alter page focus when opening the drawer\n */\n openDrawer({focusOnCloseButton = true} = {}) {\n const showEvent = this.dispatchEvent(Drawers.eventTypes.drawerShow, true);\n if (showEvent.defaultPrevented) {\n return;\n }\n\n Aria.unhide(this.drawerNode);\n this.drawerNode.classList.add('show');\n\n const preference = this.drawerNode.dataset.preference;\n if (preference && !isSmall() && (this.drawerNode.dataset.forceopen != 1)) {\n M.util.set_user_preference(preference, true);\n }\n\n const state = this.drawerNode.dataset.state;\n if (state) {\n const page = document.getElementById('page');\n page.classList.add(state);\n }\n\n if (isSmall()) {\n getBackdrop().then(backdrop => {\n backdrop.show();\n\n const pageWrapper = document.getElementById('page-wrapper');\n pageWrapper.style.overflow = 'hidden';\n return backdrop;\n })\n .catch();\n }\n\n if (focusOnCloseButton) {\n const closeButton = this.drawerNode.querySelector('[data-toggle=\"drawers\"][data-action=\"closedrawer\"]');\n closeButton.focus();\n }\n\n this.dispatchEvent(Drawers.eventTypes.drawerShown);\n }\n\n /**\n * Close the drawer.\n */\n closeDrawer() {\n const hideEvent = this.dispatchEvent(Drawers.eventTypes.drawerHide, true);\n if (hideEvent.defaultPrevented) {\n return;\n }\n\n const preference = this.drawerNode.dataset.preference;\n if (preference) {\n M.util.set_user_preference(preference, false);\n }\n\n const state = this.drawerNode.dataset.state;\n if (state) {\n const page = document.getElementById('page');\n page.classList.remove(state);\n }\n\n Aria.hide(this.drawerNode);\n this.drawerNode.classList.remove('show');\n\n getBackdrop().then(backdrop => {\n backdrop.hide();\n\n if (isSmall()) {\n const pageWrapper = document.getElementById('page-wrapper');\n pageWrapper.style.overflow = 'auto';\n }\n return backdrop;\n })\n .catch();\n\n this.dispatchEvent(Drawers.eventTypes.drawerHidden);\n }\n\n /**\n * Toggle visibility of the drawer.\n */\n toggleVisibility() {\n if (this.drawerNode.classList.contains('show')) {\n this.closeDrawer();\n } else {\n this.openDrawer();\n }\n }\n\n /**\n * Close all drawers.\n */\n static closeAllDrawers() {\n drawerMap.forEach(drawerInstance => {\n drawerInstance.closeDrawer();\n });\n }\n\n /**\n * Close all drawers except for the specified drawer.\n *\n * @param {module:theme_boost/drawers} comparisonInstance\n */\n static closeOtherDrawers(comparisonInstance) {\n drawerMap.forEach(drawerInstance => {\n if (drawerInstance === comparisonInstance) {\n return;\n }\n\n drawerInstance.closeDrawer();\n });\n }\n}\n\n/**\n * Activate the scroller helper for the drawer layout.\n *\n * @private\n */\nconst scroller = () => {\n const body = document.querySelector('body');\n const drawerLayout = document.querySelector('#page.drawers');\n if (drawerLayout) {\n drawerLayout.addEventListener(\"scroll\", () => {\n if (drawerLayout.scrollTop >= window.innerHeight) {\n body.classList.add('scrolled');\n } else {\n body.classList.remove('scrolled');\n }\n });\n }\n};\n\n/**\n * Set the last used attribute for the last used toggle button for a drawer.\n *\n * @param {object} toggleButton The clicked button.\n */\nconst setLastUsedToggle = (toggleButton) => {\n if (toggleButton.dataset.target) {\n document.querySelectorAll('[data-toggle=\"drawers\"][data-target=\"' + toggleButton.dataset.target + '\"]')\n .forEach(btn => {\n btn.dataset.lastused = false;\n });\n toggleButton.dataset.lastused = true;\n }\n};\n\n/**\n * Set the focus to the last used button to open this drawer.\n * @param {string} target The drawer target.\n */\nconst focusLastUsedToggle = (target) => {\n const lastUsedButton = document.querySelector('[data-toggle=\"drawers\"][data-target=\"' + target + '\"][data-lastused=\"true\"');\n if (lastUsedButton) {\n lastUsedButton.focus();\n }\n};\n\n/**\n * Register the event listeners for the drawer.\n *\n * @private\n */\nconst registerListeners = () => {\n // Listen for show/hide events.\n document.addEventListener('click', e => {\n const toggleButton = e.target.closest('[data-toggle=\"drawers\"][data-action=\"toggle\"]');\n if (toggleButton && toggleButton.dataset.target) {\n e.preventDefault();\n const targetDrawer = document.getElementById(toggleButton.dataset.target);\n const drawerInstance = Drawers.getDrawerInstanceForNode(targetDrawer);\n setLastUsedToggle(toggleButton);\n\n drawerInstance.toggleVisibility();\n }\n\n const openDrawerButton = e.target.closest('[data-toggle=\"drawers\"][data-action=\"opendrawer\"]');\n if (openDrawerButton && openDrawerButton.dataset.target) {\n e.preventDefault();\n const targetDrawer = document.getElementById(openDrawerButton.dataset.target);\n const drawerInstance = Drawers.getDrawerInstanceForNode(targetDrawer);\n setLastUsedToggle(toggleButton);\n\n drawerInstance.openDrawer();\n }\n\n const closeDrawerButton = e.target.closest('[data-toggle=\"drawers\"][data-action=\"closedrawer\"]');\n if (closeDrawerButton && closeDrawerButton.dataset.target) {\n e.preventDefault();\n const targetDrawer = document.getElementById(closeDrawerButton.dataset.target);\n const drawerInstance = Drawers.getDrawerInstanceForNode(targetDrawer);\n\n drawerInstance.closeDrawer();\n focusLastUsedToggle(closeDrawerButton.dataset.target);\n }\n });\n\n // Close drawer when another drawer opens.\n document.addEventListener(Drawers.eventTypes.drawerShow, e => {\n if (isLarge()) {\n return;\n }\n Drawers.closeOtherDrawers(e.detail.drawerInstance);\n });\n\n const closeOnResizeListener = () => {\n if (isSmall()) {\n let anyOpen = false;\n drawerMap.forEach(drawerInstance => {\n if (drawerInstance.isOpen) {\n if (drawerInstance.closeOnResize) {\n drawerInstance.closeDrawer();\n } else {\n anyOpen = true;\n }\n }\n });\n\n if (anyOpen) {\n getBackdrop().then(backdrop => backdrop.show()).catch();\n }\n } else {\n getBackdrop().then(backdrop => backdrop.hide()).catch();\n }\n };\n\n window.addEventListener('resize', debounce(closeOnResizeListener, 400));\n};\n\nscroller();\nregisterListeners();\n\nconst drawers = document.querySelectorAll('[data-region=\"fixed-drawer\"]');\ndrawers.forEach(drawerNode => Drawers.getDrawerInstanceForNode(drawerNode));\n"],"file":"drawers.min.js"} \ No newline at end of file diff --git a/theme/boost/amd/src/drawers.js b/theme/boost/amd/src/drawers.js index ce6da10641e..be83230b61e 100644 --- a/theme/boost/amd/src/drawers.js +++ b/theme/boost/amd/src/drawers.js @@ -397,13 +397,15 @@ export default class Drawers { const scroller = () => { const body = document.querySelector('body'); const drawerLayout = document.querySelector('#page.drawers'); - drawerLayout.addEventListener("scroll", () => { - if (drawerLayout.scrollTop >= window.innerHeight) { - body.classList.add('scrolled'); - } else { - body.classList.remove('scrolled'); - } - }); + if (drawerLayout) { + drawerLayout.addEventListener("scroll", () => { + if (drawerLayout.scrollTop >= window.innerHeight) { + body.classList.add('scrolled'); + } else { + body.classList.remove('scrolled'); + } + }); + } }; /** diff --git a/theme/boost/layout/columns2.php b/theme/boost/layout/columns2.php index 5a3902ede6b..314345a1679 100644 --- a/theme/boost/layout/columns2.php +++ b/theme/boost/layout/columns2.php @@ -69,6 +69,7 @@ $templatecontext = [ 'hasregionmainsettingsmenu' => !empty($regionmainsettingsmenu), 'primarymoremenu' => $primarymenu['moremenu'], 'secondarymoremenu' => $secondarynavigation, + 'mobileprimarynav' => $primarymenu['mobileprimarynav'], 'usermenu' => $primarymenu['user'], 'langmenu' => $primarymenu['lang'], ]; diff --git a/theme/boost/layout/drawers.php b/theme/boost/layout/drawers.php index 011e1a77f84..65d818d87f5 100644 --- a/theme/boost/layout/drawers.php +++ b/theme/boost/layout/drawers.php @@ -107,6 +107,7 @@ $templatecontext = [ 'hasregionmainsettingsmenu' => !empty($regionmainsettingsmenu), 'primarymoremenu' => $primarymenu['moremenu'], 'secondarymoremenu' => $secondarynavigation, + 'mobileprimarynav' => $primarymenu['mobileprimarynav'], 'usermenu' => $primarymenu['user'], 'langmenu' => $primarymenu['lang'], 'forceblockdraweropen' => $forceblockdraweropen, diff --git a/theme/boost/scss/moodle/primarynavigation.scss b/theme/boost/scss/moodle/primarynavigation.scss index 166dd91b033..587fc091d8b 100644 --- a/theme/boost/scss/moodle/primarynavigation.scss +++ b/theme/boost/scss/moodle/primarynavigation.scss @@ -7,3 +7,26 @@ } } } + +@include media-breakpoint-down(sm) { + .primary-navigation { + display: none; + } + .editmode-switch-form .custom-control-label { + font-size: 0; + color: transparent; + } +} + +.drawer-primary .drawercontent { + padding: 0; + + .list-group { + border-radius: 0; + margin-top: -1px; + .list-group-item { + border-left: 0; + border-right: 0; + } + } +} diff --git a/theme/boost/style/moodle.css b/theme/boost/style/moodle.css index 0926fb7ae73..3122618dc88 100644 --- a/theme/boost/style/moodle.css +++ b/theme/boost/style/moodle.css @@ -20996,6 +20996,22 @@ div.editor_atto_toolbar button .icon { height: 60px; color: #1d2125; } +@media (max-width: 767.98px) { + .primary-navigation { + display: none; } + .editmode-switch-form .custom-control-label { + font-size: 0; + color: transparent; } } + +.drawer-primary .drawercontent { + padding: 0; } + .drawer-primary .drawercontent .list-group { + border-radius: 0; + margin-top: -1px; } + .drawer-primary .drawercontent .list-group .list-group-item { + border-left: 0; + border-right: 0; } + .secondary-navigation { border-top: 1px solid #dee2e6; border-bottom: 1px solid #dee2e6; } diff --git a/theme/boost/templates/drawer.mustache b/theme/boost/templates/drawer.mustache index 41c4723b987..dfa055e01d6 100644 --- a/theme/boost/templates/drawer.mustache +++ b/theme/boost/templates/drawer.mustache @@ -32,7 +32,7 @@ }} data-preference="{{$drawerpreferencename}}{{/drawerpreferencename}}"{{! }} data-state="{{$drawerstate}}{{/drawerstate}}"{{! }} data-forceopen="{{$forceopen}}0{{/forceopen}}"{{! - }} data-close-on-resize="{{$drawercloseonreize}}0{{/drawercloseonreize}}"{{! + }} data-close-on-resize="{{$drawercloseonresize}}0{{/drawercloseonresize}}"{{! }}>
{{$drawerheading}}{{/drawerheading}} diff --git a/theme/boost/templates/navbar.mustache b/theme/boost/templates/navbar.mustache index 47123c1937a..3a6a7ddc3d8 100644 --- a/theme/boost/templates/navbar.mustache +++ b/theme/boost/templates/navbar.mustache @@ -34,7 +34,24 @@ "config": { "wwwroot": "#" }, - "sitename": "Moodle Site" + "sitename": "Moodle Site", + "mobileprimarynav": [ + { + "text": "Dashboard", + "url": "/my", + "isactive": true + }, + { + "text": "Site home", + "url": "/", + "isactive": false + }, + { + "text": "My courses", + "url": "/course", + "isactive": false + } + ] } }}
- + + + {{# output.should_display_navbar_logo }} {{/ output.should_display_navbar_logo }} @@ -80,3 +102,5 @@ {{{ output.edit_switch }}} + +{{> theme_boost/primary-drawer-mobile }} diff --git a/theme/boost/templates/primary-drawer-mobile.mustache b/theme/boost/templates/primary-drawer-mobile.mustache new file mode 100644 index 00000000000..22bc68f0c24 --- /dev/null +++ b/theme/boost/templates/primary-drawer-mobile.mustache @@ -0,0 +1,97 @@ +{{! + This file is part of Moodle - http://moodle.org/ + + Moodle is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Moodle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Moodle. If not, see . +}} +{{! + + @template theme_boost/primary-drawer-mobile + + This template renders the mobile version of the top navbar menu in a drawer. + + Example context (json): + { + "output": { + "should_display_navbar_logo": true, + "get_compact_logo_url": "http://placekitten.com/50/50" + }, + "mobileprimarynav": [ + { + "text": "Dashboard", + "url": "/my", + "isactive": "true" + }, + { + "text": "Site home", + "url": "/", + "isactive": "false" + }, + { + "text": "My courses", + "url": "/course", + "isactive": "false" + } + ] + } +}} + +{{< theme_boost/drawer }} + {{$id}}theme_boost-drawers-primary{{/id}} + {{$drawerclasses}}drawer drawer-left drawer-primary{{/drawerclasses}} + {{$drawercloseonresize}}1{{/drawercloseonresize}} + {{$drawerheading}} + {{# output.should_display_navbar_logo }} + + {{/ output.should_display_navbar_logo }} + {{^ output.should_display_navbar_logo }} + {{{ sitename }}} + {{/ output.should_display_navbar_logo }} + {{/drawerheading}} + {{$drawercontent}} + + {{/drawercontent}} + {{$drawerstate}}show-drawer-primary{{/drawerstate}} +{{/ theme_boost/drawer}} diff --git a/theme/classic/style/moodle.css b/theme/classic/style/moodle.css index c46cd2bdbaf..ef06814c149 100644 --- a/theme/classic/style/moodle.css +++ b/theme/classic/style/moodle.css @@ -20942,6 +20942,22 @@ div.editor_atto_toolbar button .icon { height: 50px; color: #1d2125; } +@media (max-width: 767.98px) { + .primary-navigation { + display: none; } + .editmode-switch-form .custom-control-label { + font-size: 0; + color: transparent; } } + +.drawer-primary .drawercontent { + padding: 0; } + .drawer-primary .drawercontent .list-group { + border-radius: 0; + margin-top: -1px; } + .drawer-primary .drawercontent .list-group .list-group-item { + border-left: 0; + border-right: 0; } + .secondary-navigation { border-top: 1px solid #dee2e6; border-bottom: 1px solid #dee2e6; }