From 6045aff33a3c57f03b380bd10919bb47a55440c6 Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Mon, 30 Jun 2025 13:19:45 +0100 Subject: [PATCH 1/3] Layout: Improved sidebar sizing, and dropdown consideration - Updated tri-layout sidebars to have less padding and to avoid cutting off content when in single-sidebar mode. - Updated dropdown handling to consider the parent scroll container when deciding to drop upwards, to help prevent cut-off. --- resources/js/components/dropdown.js | 5 +++-- resources/js/services/dom.ts | 18 ++++++++++++++++++ resources/sass/_layout.scss | 5 ++++- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/resources/js/components/dropdown.js b/resources/js/components/dropdown.js index 5dd5dd93b01..d2b044ee1ca 100644 --- a/resources/js/components/dropdown.js +++ b/resources/js/components/dropdown.js @@ -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'; @@ -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 diff --git a/resources/js/services/dom.ts b/resources/js/services/dom.ts index c3817536c85..8696fe81639 100644 --- a/resources/js/services/dom.ts +++ b/resources/js/services/dom.ts @@ -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; } \ No newline at end of file diff --git a/resources/sass/_layout.scss b/resources/sass/_layout.scss index 8175db948a5..58c06f4ac99 100644 --- a/resources/sass/_layout.scss +++ b/resources/sass/_layout.scss @@ -431,7 +431,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; @@ -452,6 +453,8 @@ body.flexbox { height: 100%; scrollbar-width: none; -ms-overflow-style: none; + padding-inline: vars.$m; + margin-inline: -(vars.$m); &::-webkit-scrollbar { display: none; } From 9186e77d27ea9c620ba0e45de77bdb64c198ca8c Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Mon, 30 Jun 2025 14:10:48 +0100 Subject: [PATCH 2/3] Layout: Added scroll fade to the sidebars --- resources/js/components/tri-layout.js | 27 +++++++++++++++++++++++++++ resources/sass/_layout.scss | 24 ++++++++++++++++++++++++ resources/views/layouts/tri.blade.php | 6 +++--- 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/resources/js/components/tri-layout.js b/resources/js/components/tri-layout.js index be9388e8d46..85533da5eed 100644 --- a/resources/js/components/tri-layout.js +++ b/resources/js/components/tri-layout.js @@ -5,6 +5,7 @@ export class TriLayout extends Component { setup() { this.container = this.$refs.container; this.tabs = this.$manyRefs.tab; + this.sidebarScrollContainers = this.$manyRefs.sidebarScrollContainer; this.lastLayoutType = 'none'; this.onDestroy = null; @@ -22,6 +23,8 @@ export class TriLayout extends Component { window.addEventListener('resize', () => { this.updateLayout(); }, {passive: true}); + + this.setupSidebarScrollHandlers(); } updateLayout() { @@ -108,4 +111,28 @@ export class TriLayout extends Component { this.lastTabShown = tabName; } + setupSidebarScrollHandlers() { + 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) { + const scrollable = sidebar.clientHeight !== sidebar.scrollHeight; + const atTop = sidebar.scrollTop === 0; + const atBottom = (sidebar.scrollTop + sidebar.clientHeight) === sidebar.scrollHeight; + + sidebar.parentElement.classList.toggle('scroll-away-from-top', !atTop && scrollable); + sidebar.parentElement.classList.toggle('scroll-away-from-bottom', !atBottom && scrollable); + } + } diff --git a/resources/sass/_layout.scss b/resources/sass/_layout.scss index 58c06f4ac99..48b4b0ca22e 100644 --- a/resources/sass/_layout.scss +++ b/resources/sass/_layout.scss @@ -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) { @@ -523,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); } \ No newline at end of file diff --git a/resources/views/layouts/tri.blade.php b/resources/views/layouts/tri.blade.php index c3cedf0fbc2..061cc69945c 100644 --- a/resources/views/layouts/tri.blade.php +++ b/resources/views/layouts/tri.blade.php @@ -28,15 +28,15 @@ class="tri-layout-mobile-tab px-m py-m text-link active">