]> BookStack Code Mirror - bookstack/commitdiff
Updated tri-layout on mobile to be tab based
authorDan Brown <redacted>
Sat, 13 Apr 2019 16:36:27 +0000 (17:36 +0100)
committerDan Brown <redacted>
Sat, 13 Apr 2019 16:36:27 +0000 (17:36 +0100)
resources/assets/js/components/tri-layout.js
resources/assets/sass/_header.scss
resources/assets/sass/_layout.scss
resources/lang/en/common.php
resources/views/tri-layout.blade.php

index d18d37d60318f606841057ec9f502825825e7ed6..0ae7df976b37e3ad97835a3da3f61e1a20659bd0 100644 (file)
@@ -3,18 +3,23 @@ class TriLayout {
 
     constructor(elem) {
         this.elem = elem;
-        this.middle = elem.querySelector('.tri-layout-middle');
-        this.right = elem.querySelector('.tri-layout-right');
-        this.left = elem.querySelector('.tri-layout-left');
 
         this.lastLayoutType = 'none';
         this.onDestroy = null;
+        this.scrollCache = {
+            'content': 0,
+            'info': 0,
+        };
+        this.lastTabShown = 'content';
 
+        // Bind any listeners
+        this.mobileTabClick = this.mobileTabClick.bind(this);
 
+        // Watch layout changes
         this.updateLayout();
         window.addEventListener('resize', event => {
             this.updateLayout();
-        });
+        }, {passive: true});
     }
 
     updateLayout() {
@@ -38,16 +43,15 @@ class TriLayout {
     }
 
     setupMobile() {
-        const mobileSidebarClickBound = this.mobileSidebarClick.bind(this);
-        const mobileContentClickBound = this.mobileContentClick.bind(this);
-        this.left.addEventListener('click', mobileSidebarClickBound);
-        this.right.addEventListener('click', mobileSidebarClickBound);
-        this.middle.addEventListener('click', mobileContentClickBound);
+        const layoutTabs = document.querySelectorAll('[tri-layout-mobile-tab]');
+        for (let tab of layoutTabs) {
+            tab.addEventListener('click', this.mobileTabClick);
+        }
 
         this.onDestroy = () => {
-            this.left.removeEventListener('click', mobileSidebarClickBound);
-            this.right.removeEventListener('click', mobileSidebarClickBound);
-            this.middle.removeEventListener('click', mobileContentClickBound);
+            for (let tab of layoutTabs) {
+                tab.removeEventListener('click', this.mobileTabClick);
+            }
         }
     }
 
@@ -55,27 +59,35 @@ class TriLayout {
         //
     }
 
-    /**
-     * Slide the main content back into view if clicked and
-     * currently slid out of view.
-     * @param event
-     */
-    mobileContentClick(event) {
-        this.elem.classList.remove('mobile-open');
-    }
 
     /**
-     * On sidebar click, Show the content by sliding the main content out.
+     * Action to run when the mobile info toggle bar is clicked/tapped
      * @param event
      */
-    mobileSidebarClick(event) {
-        if (this.elem.classList.contains('mobile-open')) {
-            this.elem.classList.remove('mobile-open');
-        } else {
-            event.preventDefault();
-            event.stopPropagation();
-            this.elem.classList.add('mobile-open');
+    mobileTabClick(event) {
+        const tab = event.target.getAttribute('tri-layout-mobile-tab');
+        this.scrollCache[this.lastTabShown] = document.documentElement.scrollTop;
+
+        // Set tab status
+        const activeTabs = document.querySelectorAll('.tri-layout-mobile-tab.active');
+        for (let tab of activeTabs) {
+            tab.classList.remove('active');
         }
+        event.target.classList.add('active');
+
+        // Toggle section
+        const showInfo = (tab === 'info');
+        this.elem.classList.toggle('show-info', showInfo);
+
+        // Set the scroll position from cache
+        const pageHeader = document.querySelector('header');
+        const defaultScrollTop = pageHeader.getBoundingClientRect().bottom;
+        document.documentElement.scrollTop = this.scrollCache[tab] || defaultScrollTop;
+        setTimeout(() => {
+            document.documentElement.scrollTop = this.scrollCache[tab] || defaultScrollTop;
+        }, 50);
+
+        this.lastTabShown = tab;
     }
 
 }
index f71a077d254be0fdb27484acbc27337c40055ca4..cbd85fd25751c1b1efaae67c5b95887a922a33d6 100644 (file)
@@ -9,13 +9,14 @@ header .grid {
 @include smaller-than($l) {
   header .grid {
     grid-template-columns: 1fr;
+    grid-row-gap: 0;
   }
 }
 
 header {
   position: relative;
   display: block;
-  z-index: 2;
+  z-index: 6;
   top: 0;
   background-color: $primary-dark;
   color: #fff;
@@ -202,6 +203,25 @@ header .search-box {
   }
 }
 
+.tri-layout-mobile-tabs {
+  position: sticky;
+  top: 0;
+  z-index: 5;
+  background-color: #FFF;
+  border-bottom: 1px solid #DDD;
+  box-shadow: $bs-card;
+}
+.tri-layout-mobile-tab {
+  text-align: center;
+  border-bottom: 3px solid #BBB;
+  &:first-child {
+    border-right: 1px solid #DDD;
+  }
+  &.active {
+    border-bottom-color: currentColor;
+  }
+}
+
 .breadcrumbs {
   display: flex;
   flex-direction: row;
@@ -226,6 +246,18 @@ header .search-box {
   }
 }
 
+@include smaller-than($l) {
+  .breadcrumbs .icon-list-item {
+    padding: $-xs;
+    > span + span {
+      display: none;
+    }
+    > span:first-child {
+      margin-right: 0;
+    }
+  }
+}
+
 .breadcrumb-listing {
   position: relative;
   .breadcrumb-listing-toggle {
index 53381fc143162971a84ed190ee8a4290522e5543..30bbafc101e8a4b7fa0df68ec0c13a77d10786fe 100644 (file)
   &.v-center {
     align-items: center;
   }
+  &.no-gap {
+    grid-row-gap: 0;
+    grid-column-gap: 0;
+  }
 }
 
 @include smaller-than($m) {
   .grid.third {
     grid-template-columns: 1fr 1fr;
   }
-  .grid.half, .grid.left-focus, .grid.right-focus {
+  .grid.half:not(.no-break), .grid.left-focus:not(.no-break), .grid.right-focus:not(.no-break) {
     grid-template-columns: 1fr;
   }
   .grid.half.collapse-xs {
@@ -241,8 +245,10 @@ body.flexbox {
 @include smaller-than($l) {
   .tri-layout-container {
     grid-template-areas:  none;
-    grid-template-columns: 10% 90%;
+    grid-template-columns: 1fr;
     grid-column-gap: 0;
+    padding-right: $-xs;
+    padding-left: $-xs;
     .tri-layout-left-contents, .tri-layout-right-contents {
       padding-left: $-m;
       padding-right: $-m;
@@ -252,33 +258,50 @@ body.flexbox {
       z-index: 0;
     }
     .tri-layout-left > *, .tri-layout-right > * {
+      display: none;
       pointer-events: none;
     }
-    .tri-layout-right, .tri-layout-left, .tri-layout-middle {
+    .tri-layout-left, .tri-layout-right {
       grid-area: none;
-      grid-column: 1/3;
+      grid-column: 1/1;
       grid-row: 1;
+      padding-top: 0 !important;
     }
     .tri-layout-middle {
-      grid-row: 1/3;
-      grid-column: 2/3;
+      grid-area: none;
+      grid-row: 3;
+      grid-column: 1/1;
       z-index: 1;
+      overflow: hidden;
       transition: transform ease-in-out 240ms;
     }
     .tri-layout-left {
       grid-row: 2;
     }
-    &.mobile-open {
+    &.show-info {
       overflow: hidden;
       .tri-layout-middle {
-        transform: translateX(90%);
+        display: none;
       }
       .tri-layout-right  > *, .tri-layout-left > * {
+        display: block;
         pointer-events: auto;
       }
     }
   }
 }
+@include larger-than($l) {
+  .tri-layout-mobile-tabs {
+    display: none;
+  }
+}
+
+@include smaller-than($m) {
+  .tri-layout-container {
+    margin-left: 0;
+    margin-right: 0;
+  }
+}
 
 .tri-layout-left-contents > div, .tri-layout-right-contents > div {
   opacity: 0.6;
index c821ecc7ec053858fa5f99b2bd904ce6e5105149..ed880afcf9ab24d0bdd0f30dcf6ae8f5b3de7394 100644 (file)
@@ -60,6 +60,10 @@ return [
     'view_profile' => 'View Profile',
     'edit_profile' => 'Edit Profile',
 
+    // Layout tabs
+    'tab_info' => 'Info',
+    'tab_content' => 'Content',
+
     // Email Content
     'email_action_help' => 'If you’re having trouble clicking the ":actionText" button, copy and paste the URL below into your web browser:',
     'email_rights' => 'All rights reserved',
index 879fce140833957a0acc7a013e232a1228f55f91..00e9df2f970fbf9828bb11793eb3bbc3d64d4e17 100644 (file)
@@ -4,6 +4,17 @@
 
 @section('content')
 
+    <div class="tri-layout-mobile-tabs text-primary" >
+        <div class="grid half no-break no-gap">
+            <div class="tri-layout-mobile-tab px-m py-s" tri-layout-mobile-tab="info">
+                {{ trans('common.tab_info') }}
+            </div>
+            <div class="tri-layout-mobile-tab px-m py-s active" tri-layout-mobile-tab="content">
+                {{ trans('common.tab_content') }}
+            </div>
+        </div>
+    </div>
+
     <div class="tri-layout-container" tri-layout @yield('container-attrs') >
 
         <div class="tri-layout-left print-hidden pt-m" id="sidebar">