From 4fa713b0d4abcb8a48832818272dab98a6a6392c Mon Sep 17 00:00:00 2001 From: Bas Brands Date: Thu, 29 Apr 2021 12:03:06 +0200 Subject: [PATCH] MDL-71456 theme_boost: drawers for blocks and the course index --- lang/en/moodle.php | 4 + theme/boost/amd/build/drawers.min.js | Bin 0 -> 6552 bytes theme/boost/amd/build/drawers.min.js.map | Bin 0 -> 24248 bytes theme/boost/amd/src/drawers.js | 508 +++++++++++++++++++++++ theme/boost/config.php | 4 +- theme/boost/layout/drawers.php | 89 ++++ theme/boost/scss/moodle/drawer.scss | 105 ++++- theme/boost/scss/moodle/icons.scss | 20 + theme/boost/scss/moodle/layout.scss | 133 ++++++ theme/boost/scss/moodle/modules.scss | 13 - theme/boost/scss/moodle/variables.scss | 4 + theme/boost/style/moodle.css | 239 ++++++++++- theme/boost/templates/columns2.mustache | 15 +- theme/boost/templates/drawer.mustache | 48 +++ theme/boost/templates/drawers.mustache | 152 +++++++ theme/boost/templates/footer.mustache | 6 - theme/classic/style/moodle.css | 239 ++++++++++- 17 files changed, 1522 insertions(+), 57 deletions(-) create mode 100644 theme/boost/amd/build/drawers.min.js create mode 100644 theme/boost/amd/build/drawers.min.js.map create mode 100644 theme/boost/amd/src/drawers.js create mode 100644 theme/boost/layout/drawers.php create mode 100644 theme/boost/templates/drawer.mustache create mode 100644 theme/boost/templates/drawers.mustache diff --git a/lang/en/moodle.php b/lang/en/moodle.php index 3be4816e42b..318803f28d5 100644 --- a/lang/en/moodle.php +++ b/lang/en/moodle.php @@ -255,6 +255,7 @@ $string['clickhelpiconformoreinfo'] = '... continues ... Click on the help icon $string['clickhere'] = 'Click here ...'; $string['clicktohideshow'] = 'Click to expand or collapse'; $string['clicktochangeinbrackets'] = '{$a} (Click to change)'; +$string['closedrawer'] = 'Close drawer'; $string['closewindow'] = 'Close this window'; $string['closebuttontitle'] = 'Close'; $string['collapse'] = 'Collapse'; @@ -355,6 +356,7 @@ $string['coursehelpnewsitemsnumber'] = 'Number of recent announcements appearing $string['coursehelpnumberweeks'] = 'Number of sections in the course (applies to certain course formats only).'; $string['coursehelpshowgrades'] = 'Enable the display of the gradebook. It does not prevent grades from being displayed within the individual activities.'; $string['coursehidden'] = 'This course is currently unavailable to students'; +$string['courseindex'] = 'Course index'; $string['courseoverviewfiles'] = 'Course image'; $string['courseoverviewfilesext'] = 'Course image file extensions'; $string['courseoverviewfileslimit'] = 'Course image files limit'; @@ -1528,6 +1530,8 @@ $string['numyears'] = '{$a} years'; $string['ok'] = 'OK'; $string['oldpassword'] = 'Current password'; $string['olduserdirectory'] = 'This is the OLD users directory, and is no longer needed. You may safely delete it. The files it contains have been copied to the NEW user directory.'; +$string['opendrawerblocks'] = 'Open block drawer'; +$string['opendrawerindex'] = 'Open course index drawer'; $string['optional'] = 'optional'; $string['options'] = 'options'; $string['order'] = 'Order'; diff --git a/theme/boost/amd/build/drawers.min.js b/theme/boost/amd/build/drawers.min.js new file mode 100644 index 0000000000000000000000000000000000000000..e109551058315341d1c628e272599597a5d05de4 GIT binary patch literal 6552 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 literal 0 HcmV?d00001 diff --git a/theme/boost/amd/src/drawers.js b/theme/boost/amd/src/drawers.js new file mode 100644 index 00000000000..be690e0cc90 --- /dev/null +++ b/theme/boost/amd/src/drawers.js @@ -0,0 +1,508 @@ +// 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 . + +/** + * Toggling the visibility of the secondary navigation on mobile. + * + * @module theme_boost/drawers + * @copyright 2021 Bas Brands + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +import ModalBackdrop from 'core/modal_backdrop'; +import Templates from 'core/templates'; +import * as Aria from 'core/aria'; +import {dispatchEvent} from 'core/event_dispatcher'; +import {debounce} from 'core/utils'; + +let backdropPromise = null; + +const drawerMap = new Map(); + +/** + * Maximum sizes for breakpoints. This needs to correspond with Bootstrap + * Breakpoints + * + * @private + */ +const sizes = { + medium: 991, + large: 1400 +}; + +/** + * Get the current body width. + * + * @returns {number} the current body width. + * @private + */ +const getCurrentWidth = () => { + const DomRect = document.body.getBoundingClientRect(); + return DomRect.x + DomRect.width; +}; + +/** + * Check if the user uses a small size browser. + * + * @returns {boolean} true if the body is smaller than sizes.medium max size. + * @private + */ +const isSmall = () => { + const browserWidth = getCurrentWidth(); + return browserWidth < sizes.medium; +}; + +/** + * Check if the user uses a medium size browser. + * + * @returns {boolean} true if the body is smaller than sizes.medium max size. + * @private + */ +const isMedium = () => { + const browserWidth = getCurrentWidth(); + return (browserWidth >= sizes.medium) && (browserWidth < sizes.large); +}; + +/** + * Check if the user uses a large size browser. + * + * @returns {boolean} true if the body is smaller than sizes.large max size. + * @private + */ +const isLarge = () => { + const browserWidth = getCurrentWidth(); + return browserWidth >= sizes.large; +}; + +/** + * Add a backdrop to the page. + * + * @returns {Promise} rendering of modal backdrop. + * @private + */ +const getBackdrop = () => { + if (!backdropPromise) { + backdropPromise = Templates.render('core/modal_backdrop', {}) + .then(html => new ModalBackdrop(html)) + .then(modalBackdrop => { + modalBackdrop.getAttachmentPoint().get(0).addEventListener('click', e => { + e.preventDefault(); + Drawers.closeAllDrawers(); + }); + return modalBackdrop; + }) + .catch(); + } + return backdropPromise; +}; + +/** + * The Drawers class is used to control on-screen drawer elements. + * + * It handles opening, and closing of drawer elements, as well as more detailed behaviours such as closing a drawer when + * another drawer is opened, and supports closing a drawer when the screen is resized. + * + * Drawers are instantiated on page load, and can also be toggled lazily when toggling any drawer toggle, open button, + * or close button. + * + * A range of show and hide events are also dispatched as detailed in the class + * {@link module:theme_boost/drawers#eventTypes eventTypes} object. + * + * @example Standard usage + * + * // The module just needs to be included to add drawer support. + * import 'theme_boost/drawers'; + * + * @example Manually open or close any drawer + * + * import Drawers from 'theme_boost/drawers'; + * + * const myDrawer = Drawers.getDrawerInstanceForNode(document.querySelector('.myDrawerNode'); + * myDrawer.closeDrawer(); + * + * @example Listen to the before show event and cancel it + * + * import Drawers from 'theme_boost/drawers'; + * + * document.addEventListener(Drawers.eventTypes.drawerShow, e => { + * // The drawer which will be shown. + * window.console.log(e.target); + * + * // The instance of the Drawers class for this drawer. + * window.console.log(e.detail.drawerInstance); + * + * // Prevent this drawer from being shown. + * e.preventDefault(); + * }); + * + * @example Listen to the shown event + * + * document.addEventListener(Drawers.eventTypes.drawerShown, e => { + * // The drawer which was shown. + * window.console.log(e.target); + * + * // The instance of the Drawers class for this drawer. + * window.console.log(e.detail.drawerInstance); + * }); + */ +export default class Drawers { + /** + * The underlying HTMLElement which is controlled. + */ + drawerNode = null; + + constructor(drawerNode) { + this.drawerNode = drawerNode; + + if (this.drawerNode.classList.contains('show')) { + this.openDrawer(); + } else { + Aria.hide(this.drawerNode); + } + + drawerMap.set(drawerNode, this); + } + + /** + * Whether the drawer is open. + * + * @returns {boolean} + */ + get isOpen() { + return this.drawerNode.classList.contains('show'); + } + + /** + * Whether the drawer should close when the window is resized + * + * @returns {boolean} + */ + get closeOnResize() { + return !!parseInt(this.drawerNode.dataset.closeOnResize); + } + + /** + * The list of event types. + * + * @static + * @property {String} drawerShow See {@link event:theme_boost/drawers:show} + * @property {String} drawerShown See {@link event:theme_boost/drawers:shown} + * @property {String} drawerHide See {@link event:theme_boost/drawers:hide} + * @property {String} drawerHidden See {@link event:theme_boost/drawers:hidden} + */ + static eventTypes = { + /** + * An event triggered before a drawer is shown. + * + * @event theme_boost/drawers:show + * @type {CustomEvent} + * @property {HTMLElement} target The drawer that will be opened. + */ + drawerShow: 'theme_boost/drawers:show', + + /** + * An event triggered after a drawer is shown. + * + * @event theme_boost/drawers:shown + * @type {CustomEvent} + * @property {HTMLElement} target The drawer that was be opened. + */ + drawerShown: 'theme_boost/drawers:shown', + + /** + * An event triggered before a drawer is hidden. + * + * @event theme_boost/drawers:hide + * @type {CustomEvent} + * @property {HTMLElement} target The drawer that will be hidden. + */ + drawerHide: 'theme_boost/drawers:hide', + + /** + * An event triggered after a drawer is hidden. + * + * @event theme_boost/drawers:hidden + * @type {CustomEvent} + * @property {HTMLElement} target The drawer that was be hidden. + */ + drawerHidden: 'theme_boost/drawers:hidden', + }; + + + /** + * Get the drawer instance for the specified node + * + * @param {HTMLElement} drawerNode + * @returns {module:theme_boost/drawers} + */ + static getDrawerInstanceForNode(drawerNode) { + if (!drawerMap.has(drawerNode)) { + new Drawers(drawerNode); + } + + return drawerMap.get(drawerNode); + } + + /** + * Dispatch a drawer event. + * + * @param {string} eventname the event name + * @param {boolean} cancelable if the event is cancelable + * @returns {CustomEvent} the resulting custom event + */ + dispatchEvent(eventname, cancelable = false) { + return dispatchEvent( + eventname, + { + drawerInstance: this, + }, + this.drawerNode, + { + cancelable, + } + ); + } + + /** + * Open the drawer. + */ + openDrawer() { + const showEvent = this.dispatchEvent(Drawers.eventTypes.drawerShow, true); + if (showEvent.defaultPrevented) { + return; + } + + Aria.unhide(this.drawerNode); + this.drawerNode.classList.add('show'); + + const preference = this.drawerNode.dataset.preference; + if (preference) { + M.util.set_user_preference(preference, true); + } + + const state = this.drawerNode.dataset.state; + if (state) { + const page = document.getElementById('page'); + page.classList.add(state); + } + + if (isSmall()) { + getBackdrop().then(backdrop => { + backdrop.show(); + + const pageWrapper = document.getElementById('page-wrapper'); + pageWrapper.style.overflow = 'hidden'; + return backdrop; + }) + .catch(); + } + + const closeButton = this.drawerNode.querySelector('[data-toggle="drawers"][data-action="closedrawer"]'); + closeButton.focus(); + + this.dispatchEvent(Drawers.eventTypes.drawerShown); + } + + /** + * Close the drawer. + */ + closeDrawer() { + const hideEvent = this.dispatchEvent(Drawers.eventTypes.drawerHide, true); + if (hideEvent.defaultPrevented) { + return; + } + + const preference = this.drawerNode.dataset.preference; + if (preference) { + M.util.set_user_preference(preference, false); + } + + const state = this.drawerNode.dataset.state; + if (state) { + const page = document.getElementById('page'); + page.classList.remove(state); + } + + Aria.hide(this.drawerNode); + this.drawerNode.classList.remove('show'); + + getBackdrop().then(backdrop => { + backdrop.hide(); + + if (isMedium()) { + const pageWrapper = document.getElementById('page-wrapper'); + pageWrapper.style.overflow = 'auto'; + } + return backdrop; + }) + .catch(); + + this.dispatchEvent(Drawers.eventTypes.drawerHidden); + } + + /** + * Toggle visibility of the drawer. + */ + toggleVisibility() { + if (this.drawerNode.classList.contains('show')) { + this.closeDrawer(); + } else { + this.openDrawer(); + } + } + + /** + * Close all drawers. + */ + static closeAllDrawers() { + drawerMap.forEach(drawerInstance => { + drawerInstance.closeDrawer(); + }); + } + + /** + * Close all drawers except for the specified drawer. + * + * @param {module:theme_boost/drawers} comparisonInstance + */ + static closeOtherDrawers(comparisonInstance) { + drawerMap.forEach(drawerInstance => { + if (drawerInstance === comparisonInstance) { + return; + } + + drawerInstance.closeDrawer(); + }); + } +} + +/** + * Activate the scroller helper for the drawer layout. + * + * @private + */ +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'); + } + }); +}; + +/** + * Set the last used attribute for the last used toggle button for a drawer. + * + * @param {object} toggleButton The clicked button. + */ +const setLastUsedToggle = (toggleButton) => { + if (toggleButton.dataset.target) { + document.querySelectorAll('[data-toggle="drawers"][data-target="' + toggleButton.dataset.target + '"]') + .forEach(btn => { + btn.dataset.lastused = false; + }); + toggleButton.dataset.lastused = true; + } +}; + +/** + * Set the focus to the last used button to open this drawer. + * @param {string} target The drawer target. + */ +const focusLastUsedToggle = (target) => { + const lastUsedButton = document.querySelector('[data-toggle="drawers"][data-target="' + target + '"][data-lastused="true"'); + if (lastUsedButton) { + lastUsedButton.focus(); + } +}; + +/** + * Register the event listeners for the drawer. + * + * @private + */ +const registerListeners = () => { + // Listen for show/hide events. + document.addEventListener('click', e => { + const toggleButton = e.target.closest('[data-toggle="drawers"][data-action="toggle"]'); + if (toggleButton && toggleButton.dataset.target) { + e.preventDefault(); + const targetDrawer = document.getElementById(toggleButton.dataset.target); + const drawerInstance = Drawers.getDrawerInstanceForNode(targetDrawer); + setLastUsedToggle(toggleButton); + + drawerInstance.toggleVisibility(); + } + + const openDrawerButton = e.target.closest('[data-toggle="drawers"][data-action="opendrawer"]'); + if (openDrawerButton && openDrawerButton.dataset.target) { + e.preventDefault(); + const targetDrawer = document.getElementById(openDrawerButton.dataset.target); + const drawerInstance = Drawers.getDrawerInstanceForNode(targetDrawer); + setLastUsedToggle(toggleButton); + + drawerInstance.openDrawer(); + } + + const closeDrawerButton = e.target.closest('[data-toggle="drawers"][data-action="closedrawer"]'); + if (closeDrawerButton && closeDrawerButton.dataset.target) { + e.preventDefault(); + const targetDrawer = document.getElementById(closeDrawerButton.dataset.target); + const drawerInstance = Drawers.getDrawerInstanceForNode(targetDrawer); + + drawerInstance.closeDrawer(); + focusLastUsedToggle(closeDrawerButton.dataset.target); + } + }); + + // Close drawer when another drawer opens. + document.addEventListener(Drawers.eventTypes.drawerShow, e => { + if (isLarge()) { + return; + } + Drawers.closeOtherDrawers(e.detail.drawerInstance); + }); + + const closeOnResizeListener = () => { + if (isSmall()) { + let anyOpen = false; + drawerMap.forEach(drawerInstance => { + if (drawerInstance.isOpen) { + if (drawerInstance.closeOnResize) { + drawerInstance.closeDrawer(); + } else { + anyOpen = true; + } + } + }); + + if (anyOpen) { + getBackdrop().then(backdrop => backdrop.show()).catch(); + } + } else { + getBackdrop().then(backdrop => backdrop.hide()).catch(); + } + }; + + window.addEventListener('resize', debounce(closeOnResizeListener, 400)); +}; + +scroller(); +registerListeners(); + +const drawers = document.querySelectorAll('[data-region="fixed-drawer"]'); +drawers.forEach(drawerNode => Drawers.getDrawerInstanceForNode(drawerNode)); diff --git a/theme/boost/config.php b/theme/boost/config.php index 89525838e70..4f1682d7a09 100644 --- a/theme/boost/config.php +++ b/theme/boost/config.php @@ -49,7 +49,7 @@ $THEME->layouts = [ ), // Main course page. 'course' => array( - 'file' => 'columns2.php', + 'file' => 'drawers.php', 'regions' => array('side-pre'), 'defaultregion' => 'side-pre', 'options' => array('langmenu' => true), @@ -61,7 +61,7 @@ $THEME->layouts = [ ), // Part of course, typical for modules - default page layout if $cm specified in require_login(). 'incourse' => array( - 'file' => 'columns2.php', + 'file' => 'drawers.php', 'regions' => array('side-pre'), 'defaultregion' => 'side-pre', ), diff --git a/theme/boost/layout/drawers.php b/theme/boost/layout/drawers.php new file mode 100644 index 00000000000..16607635b78 --- /dev/null +++ b/theme/boost/layout/drawers.php @@ -0,0 +1,89 @@ +. + +/** + * A drawer based layout for the boost theme. + * + * @package theme_boost + * @copyright 2021 Bas Brands + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->libdir . '/behat/lib.php'); + +user_preference_allow_ajax_update('drawer-open-nav', PARAM_ALPHA); +user_preference_allow_ajax_update('drawer-open-index', PARAM_BOOL); +user_preference_allow_ajax_update('drawer-open-block', PARAM_BOOL); + +if (isloggedin()) { + $navdraweropen = (get_user_preferences('drawer-open-nav', 'true') == 'true'); + $courseindexopen = (get_user_preferences('drawer-open-index') == true); + $blockdraweropen = (get_user_preferences('drawer-open-block') == true); +} else { + $navdraweropen = false; + $courseindexopen = false; + $blockdraweropen = false; +} + +if (defined('BEHAT_SITE_RUNNING')) { + $blockdraweropen = true; +} + +$extraclasses = ['uses-drawers']; +if ($navdraweropen) { + $extraclasses[] = 'drawer-open-left'; +} +if ($courseindexopen) { + $extraclasses[] = 'drawer-open-index'; +} + +$blockshtml = $OUTPUT->blocks('side-pre'); +$hasblocks = strpos($blockshtml, 'data-block=') !== false; +if (!$hasblocks) { + $blockdraweropen = false; +} +$courseindex = false; +if (!$courseindex) { + $courseindexopen = false; +} + +$bodyattributes = $OUTPUT->body_attributes($extraclasses); + +$buildregionmainsettings = !$PAGE->include_region_main_settings_in_header_actions(); +// If the settings menu will be included in the header then don't add it here. +$regionmainsettingsmenu = $buildregionmainsettings ? $OUTPUT->region_main_settings_menu() : false; + +$templatecontext = [ + 'sitename' => format_string($SITE->shortname, true, ['context' => context_course::instance(SITEID), "escape" => false]), + 'output' => $OUTPUT, + 'sidepreblocks' => $blockshtml, + 'hasblocks' => $hasblocks, + 'bodyattributes' => $bodyattributes, + 'navdraweropen' => $navdraweropen, + 'courseindexopen' => $courseindexopen, + 'blockdraweropen' => $blockdraweropen, + 'regionmainsettingsmenu' => $regionmainsettingsmenu, + 'courseindex' => $courseindex, + 'hasregionmainsettingsmenu' => !empty($regionmainsettingsmenu) +]; + +$nav = $PAGE->flatnav; +$templatecontext['flatnavigation'] = $nav; +$templatecontext['firstcollectionlabel'] = $nav->get_collectionlabel(); + +echo $OUTPUT->render_from_template('theme_boost/drawers', $templatecontext); diff --git a/theme/boost/scss/moodle/drawer.scss b/theme/boost/scss/moodle/drawer.scss index de68d0002d5..f18deb77538 100644 --- a/theme/boost/scss/moodle/drawer.scss +++ b/theme/boost/scss/moodle/drawer.scss @@ -93,11 +93,19 @@ body.drawer-ease { transition: margin-left 0.5s ease, margin-right 0.5s ease; } -body.drawer-open-left { +body:not(.uses-drawers).drawer-open-left { @include media-breakpoint-up(md) { margin-left: $drawer-width; } } + +body.drawer-open-left #page.drawers { + @include media-breakpoint-up(md) { + margin-left: $drawer-width; + padding-left: 1rem; + } +} + body.drawer-open-right { @include media-breakpoint-up(md) { margin-right: $drawer-width; @@ -112,7 +120,7 @@ $right-drawer-width: 320px; @include transition(right .2s ease-in-out); &.drawer { - z-index: $zindex-sticky; + z-index: $zindex-sticky + 1; position: fixed; top: $navbar-height; right: 0; @@ -141,7 +149,7 @@ $right-drawer-width: 320px; &.drawer { top: 0; height: 100%; - z-index: $zindex-fixed; + z-index: $zindex-fixed + 1; } } body.drawer-open-left, @@ -155,3 +163,94 @@ $right-drawer-width: 320px; box-shadow: 2px 2px 4px rgba(0, 0, 0, .08); } } + +@mixin drawer() { + transition: left 0.2s ease, right 0.2s ease, top 0.2s ease, bottom 0.2s ease; + background-color: $body-bg; + z-index: $zindex-modal; + position: fixed; + height: 100vh; + top: 0; +} + +@mixin drawertypes() { + &.drawer-left, + &.drawer-right { + width: $drawer-width; + max-width: $drawer-width; + } + &.drawer-right { + border-left: $border-width solid $border-color; + right: calc(-#{$drawer-width} + -10px); + @include box-shadow($box-shadow-drawer-right); + &.show { + right: 0; + } + } + &.drawer-left { + border-right: $border-width solid $border-color; + left: calc(-#{$drawer-width} + -10px); + @include box-shadow($box-shadow-drawer-left); + &.show { + left: 0; + } + } + &.drawer-bottom { + bottom: -110%; + &.show { + bottom: 0; + } + } +} + +.drawer { + @include drawer(); + @include drawertypes(); +} + +@include media-breakpoint-up(lg) { + .drawer { + z-index: 0; + } +} + +.drawer-md, +.drawer-sm { + display: none; +} + +@include media-breakpoint-down(md) { + .drawer-md { + display: block; + @include drawer(); + @include drawertypes(); + } +} + +@include media-breakpoint-down(sm) { + .drawer-sm { + display: block; + @include drawer(); + @include drawertypes(); + } +} + +.drawerheader { + padding: 0 0.75rem; + height: $navbar-height; + display: flex; + align-items: center; + border-bottom: $border-width solid $border-color; + .drawertoggle { + margin-left: auto; + } +} + +.drawercontent { + height: calc(100% - #{$navbar-height}); + display: flex; + flex-direction: column; + flex-wrap: nowrap; + overflow-y: auto; + padding: 0.5rem; +} diff --git a/theme/boost/scss/moodle/icons.scss b/theme/boost/scss/moodle/icons.scss index b78839abf09..9a78ed604fe 100644 --- a/theme/boost/scss/moodle/icons.scss +++ b/theme/boost/scss/moodle/icons.scss @@ -100,3 +100,23 @@ $iconsizes: map-merge(( .helplink .icon { margin-left: 0.5rem; } + +.icons-collapse-expand { + .expanded-icon { + display: block; + } + + .collapsed-icon { + display: none; + } + + &.collapsed { + .expanded-icon { + display: none; + } + + .collapsed-icon { + display: block; + } + } +} diff --git a/theme/boost/scss/moodle/layout.scss b/theme/boost/scss/moodle/layout.scss index 0601215339f..33ddbdfec86 100644 --- a/theme/boost/scss/moodle/layout.scss +++ b/theme/boost/scss/moodle/layout.scss @@ -32,3 +32,136 @@ } } } + +#page.drawers { + margin-top: calc(#{$navbar-height} + 2rem); + padding-left: 10px; + padding-right: 10px; + .main-inner { + max-width: 100%; + width: 100%; + margin: 0 auto; + } +} + +@include media-breakpoint-up(xl) { + .pagelayout-course { + #page.drawers .main-inner { + max-width: $course-content-maxwidth; + } + } +} + +.drawer-left-toggle { + position: fixed; + top: calc(#{$navbar-height} + 0.5rem); + left: 0; + z-index: 2; + .btn { + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; + padding: 0.375rem; + width: 2.5rem; + color: $white; + background-color: $primary; + .icon { + width: auto; + height: auto; + } + } +} +#page.drawers.show-drawer-left .drawer-left-toggle { + display: none; +} +.drawer-right-toggle { + position: fixed; + top: calc(#{$navbar-height} + 0.5rem); + right: 0; + z-index: 2; + .btn { + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; + padding: 0.375rem; + width: 2.5rem; + color: $white; + background-color: $primary; + .icon { + width: auto; + height: auto; + } + } +} +#page.drawers.show-drawer-right .drawer-right-toggle { + display: none; +} + +@include media-breakpoint-down(sm) { + //the scroll to top button + .drawer-toggles { + z-index: 100; + } + .drawer-right-toggle, + .drawer-left-toggle { + top: calc(100vh / 2 - 1rem); + } + #page.drawers.scroll-down { + .drawer-right-toggle { + transform: translateX(150%); + pointer-events: auto; + visibility: hidden; + } + .drawer-left-toggle { + transform: translateX(-150%); + pointer-events: auto; + visibility: hidden; + } + } +} + +@include media-breakpoint-up(md) { + + // Add some padding for the drawer toggle buttons + #page.drawers { + margin-top: $navbar-height; + padding-left: 3rem; + padding-right: 3rem; + } +} + +@include media-breakpoint-up(lg) { + .drawer-left, + .drawer-right { + top: calc(#{$navbar-height} + 1px); + height: calc(100vh - #{$navbar-height}); + } + + #page.drawers { + overflow-y: auto; + transition: 0.2s; + height: calc(100vh - #{$navbar-height}); + margin-top: $navbar-height; + left: 0; + right: 0; + &.show-drawer-left { + margin-left: $drawer-width; + margin-right: 0; + padding-left: 1rem; + } + &.show-drawer-right { + margin-left: 0; + margin-right: $drawer-width; + padding-right: 1rem; + } + &.show-drawer-left.show-drawer-right { + margin-left: $drawer-width; + margin-right: $drawer-width; + } + } +} + +.drawercontrolbuttons { + margin-top: 92px; + .buttons { + z-index: 1; + } +} diff --git a/theme/boost/scss/moodle/modules.scss b/theme/boost/scss/moodle/modules.scss index a3f47f9eb4b..e4262bebd26 100644 --- a/theme/boost/scss/moodle/modules.scss +++ b/theme/boost/scss/moodle/modules.scss @@ -32,19 +32,6 @@ select { } } -.path-mod-feedback .feedback_form .col-form-label { - display: block !important; /* stylelint-disable-line declaration-no-important */ -} - -// Feedback module -.path-mod-feedback .itemactions { - float: right; -} -.path-mod-feedback .itemhandle { - position: absolute; - right: 1rem; -} - // Forum module .path-mod-forum .forumsearch { diff --git a/theme/boost/scss/moodle/variables.scss b/theme/boost/scss/moodle/variables.scss index a630018bff2..70f1be5febf 100644 --- a/theme/boost/scss/moodle/variables.scss +++ b/theme/boost/scss/moodle/variables.scss @@ -23,3 +23,7 @@ $primary-nav-padding-y: ($spacer / 4) !default; $primary-nav-padding-x: ($spacer / 2) !default; $navbar-height: 50px !default; +$course-content-maxwidth: 800px; + +$box-shadow-drawer-left: -0.25rem .25rem .8rem rgba($black, .025) !default; +$box-shadow-drawer-right: 0 .25rem .8rem rgba($black, .025) !default; diff --git a/theme/boost/style/moodle.css b/theme/boost/style/moodle.css index 8e84a3b8b34..d7b4f571135 100644 --- a/theme/boost/style/moodle.css +++ b/theme/boost/style/moodle.css @@ -12042,6 +12042,18 @@ input[disabled] { .helplink .icon { margin-left: 0.5rem; } +.icons-collapse-expand .expanded-icon { + display: block; } + +.icons-collapse-expand .collapsed-icon { + display: none; } + +.icons-collapse-expand.collapsed .expanded-icon { + display: none; } + +.icons-collapse-expand.collapsed .collapsed-icon { + display: block; } + /* admin.less */ .formtable tbody th { font-weight: normal; @@ -14246,6 +14258,11 @@ body.drawer-ease { body.drawer-open-left { margin-left: 285px; } } +@media (min-width: 768px) { + body.drawer-open-left #page.drawers { + margin-left: 285px; + padding-left: 1rem; } } + @media (min-width: 768px) { body.drawer-open-right { margin-right: 285px; } } @@ -14258,7 +14275,7 @@ body.drawer-ease { [data-region=right-hand-drawer] { transition: none; } } [data-region=right-hand-drawer].drawer { - z-index: 1020; + z-index: 1021; position: fixed; top: 50px; right: 0; @@ -14282,7 +14299,7 @@ body.drawer-ease { [data-region=right-hand-drawer].drawer { top: 0; height: 100%; - z-index: 1030; } + z-index: 1031; } body.drawer-open-left, body.drawer-open-right { overflow: hidden; } } @@ -14290,6 +14307,110 @@ body.drawer-ease { .dir-rtl [data-region=right-hand-drawer] { box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.08); } +.drawer { + transition: left 0.2s ease, right 0.2s ease, top 0.2s ease, bottom 0.2s ease; + background-color: #fff; + z-index: 1050; + position: fixed; + height: 100vh; + top: 0; } + .drawer.drawer-left, .drawer.drawer-right { + width: 285px; + max-width: 285px; } + .drawer.drawer-right { + border-left: 1px solid #dee2e6; + right: calc(-285px + -10px); } + .drawer.drawer-right.show { + right: 0; } + .drawer.drawer-left { + border-right: 1px solid #dee2e6; + left: calc(-285px + -10px); } + .drawer.drawer-left.show { + left: 0; } + .drawer.drawer-bottom { + bottom: -110%; } + .drawer.drawer-bottom.show { + bottom: 0; } + +@media (min-width: 992px) { + .drawer { + z-index: 0; } } + +.drawer-md, +.drawer-sm { + display: none; } + +@media (max-width: 991.98px) { + .drawer-md { + display: block; + transition: left 0.2s ease, right 0.2s ease, top 0.2s ease, bottom 0.2s ease; + background-color: #fff; + z-index: 1050; + position: fixed; + height: 100vh; + top: 0; } + .drawer-md.drawer-left, .drawer-md.drawer-right { + width: 285px; + max-width: 285px; } + .drawer-md.drawer-right { + border-left: 1px solid #dee2e6; + right: calc(-285px + -10px); } + .drawer-md.drawer-right.show { + right: 0; } + .drawer-md.drawer-left { + border-right: 1px solid #dee2e6; + left: calc(-285px + -10px); } + .drawer-md.drawer-left.show { + left: 0; } + .drawer-md.drawer-bottom { + bottom: -110%; } + .drawer-md.drawer-bottom.show { + bottom: 0; } } + +@media (max-width: 767.98px) { + .drawer-sm { + display: block; + transition: left 0.2s ease, right 0.2s ease, top 0.2s ease, bottom 0.2s ease; + background-color: #fff; + z-index: 1050; + position: fixed; + height: 100vh; + top: 0; } + .drawer-sm.drawer-left, .drawer-sm.drawer-right { + width: 285px; + max-width: 285px; } + .drawer-sm.drawer-right { + border-left: 1px solid #dee2e6; + right: calc(-285px + -10px); } + .drawer-sm.drawer-right.show { + right: 0; } + .drawer-sm.drawer-left { + border-right: 1px solid #dee2e6; + left: calc(-285px + -10px); } + .drawer-sm.drawer-left.show { + left: 0; } + .drawer-sm.drawer-bottom { + bottom: -110%; } + .drawer-sm.drawer-bottom.show { + bottom: 0; } } + +.drawerheader { + padding: 0 0.75rem; + height: 50px; + display: flex; + align-items: center; + border-bottom: 1px solid #dee2e6; } + .drawerheader .drawertoggle { + margin-left: auto; } + +.drawercontent { + height: calc(100% - 50px); + display: flex; + flex-direction: column; + flex-wrap: nowrap; + overflow-y: auto; + padding: 0.5rem; } + #page-my-index { background-color: #f7f7f7; } @@ -16786,17 +16907,6 @@ select { .path-mod-choice .choices .option label { vertical-align: top; } -.path-mod-feedback .feedback_form .col-form-label { - display: block !important; - /* stylelint-disable-line declaration-no-important */ } - -.path-mod-feedback .itemactions { - float: right; } - -.path-mod-feedback .itemhandle { - position: absolute; - right: 1rem; } - .path-mod-forum .forumsearch input, .path-mod-forum .forumsearch .helptooltip { margin: 0 3px; } @@ -19352,6 +19462,109 @@ span[data-flexitour="container"][x-placement="right"], span[data-flexitour="cont height: 1em; font-size: 4em; } +#page.drawers { + margin-top: calc(50px + 2rem); + padding-left: 10px; + padding-right: 10px; } + #page.drawers .main-inner { + max-width: 100%; + width: 100%; + margin: 0 auto; } + +@media (min-width: 1200px) { + .pagelayout-course #page.drawers .main-inner { + max-width: 800px; } } + +.drawer-left-toggle { + position: fixed; + top: calc(50px + 0.5rem); + left: 0; + z-index: 2; } + .drawer-left-toggle .btn { + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; + padding: 0.375rem; + width: 2.5rem; + color: #fff; + background-color: #0f6fc5; } + .drawer-left-toggle .btn .icon { + width: auto; + height: auto; } + +#page.drawers.show-drawer-left .drawer-left-toggle { + display: none; } + +.drawer-right-toggle { + position: fixed; + top: calc(50px + 0.5rem); + right: 0; + z-index: 2; } + .drawer-right-toggle .btn { + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; + padding: 0.375rem; + width: 2.5rem; + color: #fff; + background-color: #0f6fc5; } + .drawer-right-toggle .btn .icon { + width: auto; + height: auto; } + +#page.drawers.show-drawer-right .drawer-right-toggle { + display: none; } + +@media (max-width: 767.98px) { + .drawer-toggles { + z-index: 100; } + .drawer-right-toggle, + .drawer-left-toggle { + top: calc(100vh / 2 - 1rem); } + #page.drawers.scroll-down .drawer-right-toggle { + transform: translateX(150%); + pointer-events: auto; + visibility: hidden; } + #page.drawers.scroll-down .drawer-left-toggle { + transform: translateX(-150%); + pointer-events: auto; + visibility: hidden; } } + +@media (min-width: 768px) { + #page.drawers { + margin-top: 50px; + padding-left: 3rem; + padding-right: 3rem; } } + +@media (min-width: 992px) { + .drawer-left, + .drawer-right { + top: calc(50px + 1px); + height: calc(100vh - 50px); } + #page.drawers { + overflow-y: auto; + transition: 0.2s; + height: calc(100vh - 50px); + margin-top: 50px; + left: 0; + right: 0; } + #page.drawers.show-drawer-left { + margin-left: 285px; + margin-right: 0; + padding-left: 1rem; } + #page.drawers.show-drawer-right { + margin-left: 0; + margin-right: 285px; + padding-right: 1rem; } + #page.drawers.show-drawer-left.show-drawer-right { + margin-left: 285px; + margin-right: 285px; + padding-left: 1rem; + padding-right: 1rem; } } + +.drawercontrolbuttons { + margin-top: 92px; } + .drawercontrolbuttons .buttons { + z-index: 1; } + .form-control:-ms-input-placeholder { color: #6c757d; } diff --git a/theme/boost/templates/columns2.mustache b/theme/boost/templates/columns2.mustache index 5feab1c709f..c198024ea18 100644 --- a/theme/boost/templates/columns2.mustache +++ b/theme/boost/templates/columns2.mustache @@ -90,6 +90,12 @@ {{{ output.standard_after_main_region_html }}} + {{> theme_boost/footer }} @@ -97,13 +103,8 @@ {{#js}} M.util.js_pending('theme_boost/loader'); -require(['theme_boost/loader'], function() { +require(['theme_boost/loader', 'theme_boost/drawer'], function(Loader, Drawer) { + Drawer.init(); M.util.js_complete('theme_boost/loader'); }); - -M.util.js_pending('theme_boost/drawer'); -require(['theme_boost/drawer'], function(drawer) { - drawer.init(); - M.util.js_complete('theme_boost/drawer'); -}); {{/js}} diff --git a/theme/boost/templates/drawer.mustache b/theme/boost/templates/drawer.mustache new file mode 100644 index 00000000000..006067e7989 --- /dev/null +++ b/theme/boost/templates/drawer.mustache @@ -0,0 +1,48 @@ +{{! + 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/drawer + + Example context (json): + { + "drawerclasses": "drawer drawer-right", + "drawertrigger": "toggleblocks", + "drawerheading": "Blocks", + "drawerconent": "Content for the blocks region" + } +}} +
+
+ {{$drawerheading}}{{/drawerheading}} + +
+
+ {{$drawercontent}}{{/drawercontent}} +
+
+{{#js}} +require(['theme_boost/drawers']); +{{/js}} diff --git a/theme/boost/templates/drawers.mustache b/theme/boost/templates/drawers.mustache new file mode 100644 index 00000000000..3df379e351a --- /dev/null +++ b/theme/boost/templates/drawers.mustache @@ -0,0 +1,152 @@ +{{! + 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/drawers + + Boost drawer template. + + Context variables required for this template: + * sitename - The name of the site + * output - The core renderer for the page + * bodyattributes - attributes for the body tag as a string of html attributes + * sidepreblocks - HTML for the blocks + * hasblocks - true if there are blocks on this page + * courseindexopen - true if the nav drawer should be open on page load + * regionmainsettingsmenu - HTML for the region main settings menu + * hasregionmainsettingsmenu - There is a region main settings menu on this page. + + Example context (json): + { + "sitename": "Moodle", + "output": { + "doctype": "", + "page_title": "Test page", + "favicon": "favicon.ico", + "main_content": "

Headings make html validators happier

" + }, + "bodyattributes":"", + "sidepreblocks": "

Blocks html goes here

", + "hasblocks":true, + "courseindexopen": true, + "navdraweropen": false, + "blockdraweropen": true, + "regionmainsettingsmenu": "", + "hasregionmainsettingsmenu": false + } +}} +{{> theme_boost/head }} + + +{{> core/local/toast/wrapper}} +
+ + {{{ output.standard_top_of_body_html }}} + + {{> theme_boost/navbar }} + {{> theme_boost/nav-drawer }} + {{#courseindex}} + {{< theme_boost/drawer }} + {{$id}}theme_boost-drawers-courseindex{{/id}} + {{$drawerclasses}}drawer drawer-left {{#courseindexopen}}show{{/courseindexopen}}{{/drawerclasses}} + {{$drawerheading}} + {{#str}} courseindex {{/str}} + {{/drawerheading}} + {{$drawercontent}} + {{{courseindex}}} + {{/drawercontent}} + {{$drawerpreferencename}}drawer-open-index{{/drawerpreferencename}} + {{$drawerstate}}show-drawer-left{{/drawerstate}} + {{/ theme_boost/drawer}} + {{/courseindex}} + {{#hasblocks}} + {{< theme_boost/drawer }} + {{$id}}theme_boost-drawers-blocks{{/id}} + {{$drawerclasses}}drawer drawer-right {{#blockdraweropen}}show{{/blockdraweropen}}{{/drawerclasses}} + {{$drawerheading}} + {{#str}} blocks {{/str}} + {{/drawerheading}} + {{$drawercontent}} +
+ {{{ sidepreblocks }}} +
+ {{/drawercontent}} + {{$drawerpreferencename}}drawer-open-block{{/drawerpreferencename}} + {{$drawerstate}}show-drawer-right{{/drawerstate}} + {{$drawercloseonresize}}1{{/drawercloseonresize}} + {{/ theme_boost/drawer}} + {{/hasblocks}} +
+
+
+ {{#courseindex}} +
+ +
+ {{/courseindex}} + {{#hasblocks}} +
+ +
+ {{/hasblocks}} +
+ {{{ output.full_header }}} +
+
+ {{#hasregionmainsettingsmenu}} +
+
{{{ output.region_main_settings_menu }}}
+
+ {{/hasregionmainsettingsmenu}} +
+ + {{#hasregionmainsettingsmenu}} +
+ {{/hasregionmainsettingsmenu}} + {{{ output.course_content_header }}} + {{{ output.main_content }}} + {{{ output.activity_navigation }}} + {{{ output.course_content_footer }}} + +
+
+
+ + {{> theme_boost/footer }} +
+
+ {{{ output.standard_after_main_region_html }}} +
+ + + +{{#js}} +M.util.js_pending('theme_boost/loader'); +require(['theme_boost/loader', 'theme_boost/drawer'], function(Loader, Drawer) { + Drawer.init(); + M.util.js_complete('theme_boost/loader'); +}); +{{/js}} diff --git a/theme/boost/templates/footer.mustache b/theme/boost/templates/footer.mustache index fca4e060938..9827bbd5c60 100644 --- a/theme/boost/templates/footer.mustache +++ b/theme/boost/templates/footer.mustache @@ -17,12 +17,6 @@ {{! Page footer. }} -