diff --git a/resources/assets/js/components/tri-layout.js b/resources/assets/js/components/tri-layout.js index d18d37d60..0ae7df976 100644 --- a/resources/assets/js/components/tri-layout.js +++ b/resources/assets/js/components/tri-layout.js @@ -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; } } diff --git a/resources/assets/sass/_header.scss b/resources/assets/sass/_header.scss index f71a077d2..cbd85fd25 100644 --- a/resources/assets/sass/_header.scss +++ b/resources/assets/sass/_header.scss @@ -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 { diff --git a/resources/assets/sass/_layout.scss b/resources/assets/sass/_layout.scss index 53381fc14..30bbafc10 100644 --- a/resources/assets/sass/_layout.scss +++ b/resources/assets/sass/_layout.scss @@ -49,13 +49,17 @@ &.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; diff --git a/resources/lang/en/common.php b/resources/lang/en/common.php index c821ecc7e..ed880afcf 100644 --- a/resources/lang/en/common.php +++ b/resources/lang/en/common.php @@ -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', diff --git a/resources/views/tri-layout.blade.php b/resources/views/tri-layout.blade.php index 879fce140..00e9df2f9 100644 --- a/resources/views/tri-layout.blade.php +++ b/resources/views/tri-layout.blade.php @@ -4,6 +4,17 @@ @section('content') +