Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions resources/js/components/dropdown.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {onSelect} from '../services/dom.ts';
import {findClosestScrollContainer, onSelect} from '../services/dom.ts';
import {KeyboardNavigationHandler} from '../services/keyboard-navigation.ts';
import {Component} from './component';

Expand Down Expand Up @@ -33,7 +33,8 @@ export class Dropdown extends Component {
const menuOriginalRect = this.menu.getBoundingClientRect();
let heightOffset = 0;
const toggleHeight = this.toggle.getBoundingClientRect().height;
const dropUpwards = menuOriginalRect.bottom > window.innerHeight;
const containerBounds = findClosestScrollContainer(this.menu).getBoundingClientRect();
const dropUpwards = menuOriginalRect.bottom > containerBounds.bottom;
const containerRect = this.container.getBoundingClientRect();

// If enabled, Move to body to prevent being trapped within scrollable sections
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import {Component} from './component';

export class TriLayout extends Component {

setup() {
private container!: HTMLElement;
private tabs!: HTMLElement[];
private sidebarScrollContainers!: HTMLElement[];

private lastLayoutType = 'none';
private onDestroy: (()=>void)|null = null;
private scrollCache: Record<string, number> = {
content: 0,
info: 0,
};
private lastTabShown = 'content';

setup(): void {
this.container = this.$refs.container;
this.tabs = this.$manyRefs.tab;

this.lastLayoutType = 'none';
this.onDestroy = null;
this.scrollCache = {
content: 0,
info: 0,
};
this.lastTabShown = 'content';
this.sidebarScrollContainers = this.$manyRefs.sidebarScrollContainer;

// Bind any listeners
this.mobileTabClick = this.mobileTabClick.bind(this);
Expand All @@ -22,9 +26,11 @@ export class TriLayout extends Component {
window.addEventListener('resize', () => {
this.updateLayout();
}, {passive: true});

this.setupSidebarScrollHandlers();
}

updateLayout() {
updateLayout(): void {
let newLayout = 'tablet';
if (window.innerWidth <= 1000) newLayout = 'mobile';
if (window.innerWidth > 1400) newLayout = 'desktop';
Expand Down Expand Up @@ -56,33 +62,30 @@ export class TriLayout extends Component {
};
}

setupDesktop() {
setupDesktop(): void {
//
}

/**
* Action to run when the mobile info toggle bar is clicked/tapped
* @param event
*/
mobileTabClick(event) {
const {tab} = event.target.dataset;
mobileTabClick(event: MouseEvent): void {
const tab = (event.target as HTMLElement).dataset.tab || '';
this.showTab(tab);
}

/**
* Show the content tab.
* Used by the page-display component.
*/
showContent() {
showContent(): void {
this.showTab('content', false);
}

/**
* Show the given tab
* @param {String} tabName
* @param {Boolean }scroll
*/
showTab(tabName, scroll = true) {
showTab(tabName: string, scroll: boolean = true): void {
this.scrollCache[this.lastTabShown] = document.documentElement.scrollTop;

// Set tab status
Expand All @@ -97,7 +100,7 @@ export class TriLayout extends Component {

// Set the scroll position from cache
if (scroll) {
const pageHeader = document.querySelector('header');
const pageHeader = document.querySelector('header') as HTMLElement;
const defaultScrollTop = pageHeader.getBoundingClientRect().bottom;
document.documentElement.scrollTop = this.scrollCache[tabName] || defaultScrollTop;
setTimeout(() => {
Expand All @@ -108,4 +111,30 @@ export class TriLayout extends Component {
this.lastTabShown = tabName;
}

setupSidebarScrollHandlers(): void {
for (const sidebar of this.sidebarScrollContainers) {
sidebar.addEventListener('scroll', () => this.handleSidebarScroll(sidebar), {
passive: true,
});
this.handleSidebarScroll(sidebar);
}

window.addEventListener('resize', () => {
for (const sidebar of this.sidebarScrollContainers) {
this.handleSidebarScroll(sidebar);
}
});
}

handleSidebarScroll(sidebar: HTMLElement): void {
const scrollable = sidebar.clientHeight !== sidebar.scrollHeight;
const atTop = sidebar.scrollTop === 0;
const atBottom = (sidebar.scrollTop + sidebar.clientHeight) === sidebar.scrollHeight;

if (sidebar.parentElement) {
sidebar.parentElement.classList.toggle('scroll-away-from-top', !atTop && scrollable);
sidebar.parentElement.classList.toggle('scroll-away-from-bottom', !atBottom && scrollable);
}
}

}
18 changes: 18 additions & 0 deletions resources/js/services/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,4 +256,22 @@ export function findTargetNodeAndOffset(parentNode: HTMLElement, offset: number)
export function hashElement(element: HTMLElement): string {
const normalisedElemText = (element.textContent || '').replace(/\s{2,}/g, '');
return cyrb53(normalisedElemText);
}

/**
* Find the closest scroll container parent for the given element
* otherwise will default to the body element.
*/
export function findClosestScrollContainer(start: HTMLElement): HTMLElement {
let el: HTMLElement|null = start;
do {
const computed = window.getComputedStyle(el);
if (computed.overflowY === 'scroll') {
return el;
}

el = el.parentElement;
} while (el);

return document.body;
}
29 changes: 28 additions & 1 deletion resources/sass/_layout.scss
Original file line number Diff line number Diff line change
Expand Up @@ -389,10 +389,12 @@ body.flexbox {
.tri-layout-right {
grid-area: c;
min-width: 0;
position: relative;
}
.tri-layout-left {
grid-area: a;
min-width: 0;
position: relative;
}

@include mixins.larger-than(vars.$bp-xxl) {
Expand Down Expand Up @@ -431,7 +433,8 @@ body.flexbox {
grid-template-areas: "a b b";
grid-template-columns: 1fr 3fr;
grid-template-rows: min-content min-content 1fr;
padding-inline-end: vars.$l;
margin-inline-start: (vars.$m + vars.$xxs);
margin-inline-end: (vars.$m + vars.$xxs);
}
.tri-layout-sides {
grid-column-start: a;
Expand All @@ -452,6 +455,8 @@ body.flexbox {
height: 100%;
scrollbar-width: none;
-ms-overflow-style: none;
padding-inline: vars.$m;
margin-inline: -(vars.$m);
&::-webkit-scrollbar {
display: none;
}
Expand Down Expand Up @@ -520,4 +525,26 @@ body.flexbox {
margin-inline-start: 0;
margin-inline-end: 0;
}
}

/**
* Scroll Indicators
*/
.scroll-away-from-top:before,
.scroll-away-from-bottom:after {
content: '';
display: block;
position: absolute;
@include mixins.lightDark(color, #F2F2F2, #111);
left: 0;
top: 0;
width: 100%;
height: 50px;
background: linear-gradient(to bottom, currentColor, transparent);
z-index: 2;
}
.scroll-away-from-bottom:after {
top: auto;
bottom: 0;
background: linear-gradient(to top, currentColor, transparent);
}
6 changes: 3 additions & 3 deletions resources/views/layouts/tri.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ class="tri-layout-mobile-tab px-m py-m text-link active">
<div refs="tri-layout@container" class="tri-layout-container" @yield('container-attrs') >

<div class="tri-layout-sides print-hidden">
<div class="tri-layout-sides-content">
<div refs="tri-layout@sidebar-scroll-container" class="tri-layout-sides-content">
<div class="tri-layout-right print-hidden">
<aside class="tri-layout-right-contents">
<aside refs="tri-layout@sidebar-scroll-container" class="tri-layout-right-contents">
@yield('right')
</aside>
</div>

<div class="tri-layout-left print-hidden" id="sidebar">
<aside class="tri-layout-left-contents">
<aside refs="tri-layout@sidebar-scroll-container" class="tri-layout-left-contents">
@yield('left')
</aside>
</div>
Expand Down