MDL-66463 theme_boost: go to top link
authorFerran Recio <ferran@moodle.com>
Thu, 31 Oct 2019 15:28:49 +0000 (16:28 +0100)
committerFerran Recio <ferran@moodle.com>
Fri, 31 Jan 2020 11:37:10 +0000 (12:37 +0100)
12 files changed:
theme/boost/amd/build/loader.min.js
theme/boost/amd/build/loader.min.js.map
theme/boost/amd/build/scroll.min.js [new file with mode: 0644]
theme/boost/amd/build/scroll.min.js.map [new file with mode: 0644]
theme/boost/amd/src/loader.js
theme/boost/amd/src/scroll.js [new file with mode: 0644]
theme/boost/lang/en/theme_boost.php
theme/boost/scss/moodle/core.scss
theme/boost/scss/preset/default.scss
theme/boost/style/moodle.css
theme/boost/templates/footer.mustache
theme/classic/style/moodle.css

index 27cf1fa..91e6786 100644 (file)
Binary files a/theme/boost/amd/build/loader.min.js and b/theme/boost/amd/build/loader.min.js differ
index 6ac8fe5..eb80756 100644 (file)
Binary files a/theme/boost/amd/build/loader.min.js.map and b/theme/boost/amd/build/loader.min.js.map differ
diff --git a/theme/boost/amd/build/scroll.min.js b/theme/boost/amd/build/scroll.min.js
new file mode 100644 (file)
index 0000000..807807d
Binary files /dev/null and b/theme/boost/amd/build/scroll.min.js differ
diff --git a/theme/boost/amd/build/scroll.min.js.map b/theme/boost/amd/build/scroll.min.js.map
new file mode 100644 (file)
index 0000000..5bafaaf
Binary files /dev/null and b/theme/boost/amd/build/scroll.min.js.map differ
index 03fef8e..65193d7 100644 (file)
@@ -30,6 +30,7 @@ define(['jquery', './tether', 'core/event', 'core/custom_interaction_events'], f
     M.util.js_pending('theme_boost/loader:children');
 
     require(['theme_boost/aria',
+            'theme_boost/scroll',
             'theme_boost/pending',
             'theme_boost/util',
             'theme_boost/alert',
@@ -42,7 +43,7 @@ define(['jquery', './tether', 'core/event', 'core/custom_interaction_events'], f
             '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({
@@ -102,6 +103,8 @@ define(['jquery', './tether', 'core/event', 'core/custom_interaction_events'], f
         });
 
         Aria.init();
+        this.scroll = new MoodleScroll();
+        this.scroll.init();
         M.util.js_complete('theme_boost/loader:children');
     });
 
diff --git a/theme/boost/amd/src/scroll.js b/theme/boost/amd/src/scroll.js
new file mode 100644 (file)
index 0000000..a0ded31
--- /dev/null
@@ -0,0 +1,71 @@
+// 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');
+        }
+    }
+}
index 1133f7b..97e6983 100644 (file)
@@ -52,3 +52,4 @@ $string['region-side-pre'] = 'Right';
 $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';
index cd8d25a..555205f 100644 (file)
@@ -26,6 +26,32 @@ $bg-inverse-link-color: #fff !default;
     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;
index c44d3ac..6078384 100644 (file)
@@ -55,6 +55,9 @@ $breadcrumb-bg:                     transparent !default;
 $breadcrumb-divider: "/" !default;
 $breadcrumb-divider-rtl: "/" !default;
 
+// Floating elements positions
+$gototop-bottom-position: 50px !default;
+
 // Alerts
 $alert-border-width:                0 !default;
 
index d5542ac..6f5d986 100644 (file)
@@ -9413,6 +9413,27 @@ input[disabled] {
   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;
index 9827bbd..2655fd4 100644 (file)
 {{!
     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>
index 266ab12..5e58b08 100644 (file)
@@ -9618,6 +9618,27 @@ input[disabled] {
   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;