M.util.js_pending('theme_boost/loader:children');
require(['theme_boost/aria',
+ 'theme_boost/scroll',
'theme_boost/pending',
'theme_boost/util',
'theme_boost/alert',
'theme_boost/tab',
'theme_boost/tooltip',
'theme_boost/popover'],
- function(Aria) {
+ function(Aria, MoodleScroll) {
// We do twice because: https://github.com/twbs/bootstrap/issues/10547
jQuery('body').popover({
});
Aria.init();
+ this.scroll = new MoodleScroll();
+ this.scroll.init();
M.util.js_complete('theme_boost/loader:children');
});
--- /dev/null
+// 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 <http://www.gnu.org/licenses/>.
+
+/**
+ * Manage user scroll in Moodle for future floating elements.
+ *
+ * @copyright 2020 Ferran Recio <ferran@moodle.org>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Moodle scroll handling. For now it just handle a "scrolled" class
+ * on the body tag but in the near future could handle more floating
+ * elements like option bars, docked elements or other active elements.
+ *
+ * @class MoodleScroll
+ */
+export default class MoodleScroll {
+
+ /**
+ * Initialise the scroll monitoring.
+ *
+ * @method init
+ * @chainable
+ * @return {Object} this.
+ */
+ init() {
+ this.scrollY = 0;
+ window.addEventListener("scroll", this.scrollHandler.bind(this));
+ return this;
+ }
+
+ /**
+ * Add special classes to body depending on scroll position.
+ *
+ * @method update
+ * @chainable
+ * @return {Integer} current scroll position.
+ */
+ getScrollPosition() {
+ return window.pageYOffset || document.documentElement.scrollTop;
+ }
+
+ /**
+ * Add special classes to body depending on scroll position.
+ *
+ * @method update
+ * @chainable
+ */
+ scrollHandler() {
+ const body = document.querySelector('body');
+ const scrollY = this.getScrollPosition();
+ if (scrollY >= window.innerHeight) {
+ body.classList.add('scrolled');
+ } else {
+ body.classList.remove('scrolled');
+ }
+ }
+}
$string['privacy:metadata:preference:draweropennav'] = 'The user\'s preference for hiding or showing the drawer menu navigation.';
$string['privacy:drawernavclosed'] = 'The current preference for the navigation drawer is closed.';
$string['privacy:drawernavopen'] = 'The current preference for the navigation drawer is open.';
+$string['totop'] = 'Go to top';
margin-top: 4px;
}
+$gototop-bottom-position: 50px !default;
+
+#goto-top-link {
+ visibility: hidden;
+ opacity: 0;
+ transition: opacity .7s ease 0s, visibility .1s ease .8s;
+ display: block;
+ position: fixed; /* IE compatibility hack */
+ @supports (position: sticky) {
+ position: sticky;
+ }
+ bottom: $gototop-bottom-position;
+
+ a {
+ position: absolute;
+ right: 0;
+ transform: translateY(-100%);
+ }
+}
+
+body.scrolled #goto-top-link {
+ opacity: 1;
+ visibility: visible;
+ transition: visibility 0s ease 0s, opacity .7s ease .1s;
+}
+
.context-header-settings-menu .dropdown-toggle > .icon,
#region-main-settings-menu .dropdown-toggle > .icon {
height: 24px;
$breadcrumb-divider: "/" !default;
$breadcrumb-divider-rtl: "/" !default;
+// Floating elements positions
+$gototop-bottom-position: 50px !default;
+
// Alerts
$alert-border-width: 0 !default;
display: block;
margin-top: 4px; }
+#goto-top-link {
+ visibility: hidden;
+ opacity: 0;
+ transition: opacity .7s ease 0s, visibility .1s ease .8s;
+ display: block;
+ position: fixed;
+ /* IE compatibility hack */
+ bottom: 50px; }
+ @supports (position: sticky) {
+ #goto-top-link {
+ position: sticky; } }
+ #goto-top-link a {
+ position: absolute;
+ right: 0;
+ transform: translateY(-100%); }
+
+body.scrolled #goto-top-link {
+ opacity: 1;
+ visibility: visible;
+ transition: visibility 0s ease 0s, opacity .7s ease .1s; }
+
.context-header-settings-menu .dropdown-toggle > .icon,
#region-main-settings-menu .dropdown-toggle > .icon {
height: 24px;
{{!
Page footer.
}}
+<div id="goto-top-link">
+ {{! go to top is sticky to footer so needs to be sibling }}
+ <a class="btn btn-light" role="button" href="#">
+ {{#pix}} i/up, core, {{#str}} totop, theme_boost {{/str}}{{/pix}}
+ </a>
+</div>
<footer id="page-footer" class="py-3 bg-dark text-light">
<div class="container">
<div id="course-footer">{{{ output.course_footer }}}</div>
display: block;
margin-top: 4px; }
+#goto-top-link {
+ visibility: hidden;
+ opacity: 0;
+ transition: opacity .7s ease 0s, visibility .1s ease .8s;
+ display: block;
+ position: fixed;
+ /* IE compatibility hack */
+ bottom: 50px; }
+ @supports (position: sticky) {
+ #goto-top-link {
+ position: sticky; } }
+ #goto-top-link a {
+ position: absolute;
+ right: 0;
+ transform: translateY(-100%); }
+
+body.scrolled #goto-top-link {
+ opacity: 1;
+ visibility: visible;
+ transition: visibility 0s ease 0s, opacity .7s ease .1s; }
+
.context-header-settings-menu .dropdown-toggle > .icon,
#region-main-settings-menu .dropdown-toggle > .icon {
height: 24px;