Skip to content

Commit

Permalink
Merge pull request #14927 from craftcms/feature/cms-14765-sticky-scro…
Browse files Browse the repository at this point in the history
…llbar

Feature/cms 14765 sticky scrollbar
  • Loading branch information
brandonkelly authored May 2, 2024
2 parents 4d71923 + f5871c7 commit a8ed31a
Show file tree
Hide file tree
Showing 9 changed files with 144 additions and 26 deletions.
1 change: 1 addition & 0 deletions CHANGELOG-WIP.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
- Element index checkboxes no longer have a lag when deselected, except within element selection modals. ([#14896](https://github.com/craftcms/cms/issues/14896))
- Improved mobile styling. ([#14910](https://github.com/craftcms/cms/pull/14910))
- Improved the look of slideouts.
- Table views within element index pages are no longer scrolled directly. ([#14927](https://github.com/craftcms/cms/pull/14927))

### Extensibility
- Added the `waitForDoubleClicks` setting to `Garnish.Select`, `Craft.BaseElementIndex`, and `Craft.BaseElementIndexView`.
2 changes: 1 addition & 1 deletion src/web/assets/cp/dist/cp.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/web/assets/cp/dist/cp.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/web/assets/cp/dist/css/cp.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/web/assets/cp/dist/css/cp.css.map

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/web/assets/cp/src/Craft.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,4 @@ import './js/CraftGlobalSidebar.js';
import './js/CraftDisclosure.js';
import './js/CraftTooltip.js';
import './js/CraftElementLabel';
import './js/CraftProxyScrollbar';
7 changes: 6 additions & 1 deletion src/web/assets/cp/src/css/_main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2653,7 +2653,6 @@ h2 + .actions {
var(--pane-padding, var(--padding)) * -1 + var(--pane-padding, var(--m))
);
padding: 0 !important;
overscroll-behavior: contain;
overflow-x: auto;

table.data {
Expand Down Expand Up @@ -3624,6 +3623,12 @@ table {
background: transparent;
}
}

craft-proxy-scrollbar {
position: sticky;
width: calc(100% + var(--xl) * 2);
margin-inline: calc(var(--xl) * -1);
}
}

.elements {
Expand Down
104 changes: 104 additions & 0 deletions src/web/assets/cp/src/js/CraftProxyScrollbar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/**
* Proxy scrollbar
*
* Display a scrollbar that is synced with another element
*
* @property {string} scroller - The selector of the element that will be scrolled
* @property {string} content - The selector of the element within the scroller containing the overflow content
* @property {boolean} hidden - Whether the scrollbar should be hidden
* @property {HTMLElement} proxy - The element that represents the scrollbar
* @property {HTMLElement} scroller - The element that will be scrolled
* @property {HTMLElement} content - The element within the scroller containing the overflow content
*/
class CraftProxyScrollbar extends HTMLElement {
static observedAttributes = ['hidden'];

get hidden() {
return this.getAttribute('hidden');
}

get hasOverflow() {
return this.content?.scrollWidth > this.scroller?.clientWidth;
}

connectedCallback() {
this.ignoreScrollEvent = false;
this.animation = false;

this.scroller = document.querySelector(this.getAttribute('scroller'));
this.content = document.querySelector(this.getAttribute('content'));

if (!this.scroller || !this.content) {
return;
}

this.proxy = document.createElement('div');
this.proxy.style.height = '1px';
this.proxy.style.width = this.content.getBoundingClientRect().width + 'px';

this.appendChild(this.proxy);

this.addEventListener('scroll', this.syncScroll(this.scroller, this));
this.scroller.addEventListener(
'scroll',
this.syncScroll(this, this.scroller)
);
window.addEventListener('resize', this.handleResize.bind(this));

Object.assign(this.style, {
display: this.hasOverflow ? 'block' : 'none',
overflowX: 'scroll',
});
}

attributeChangedCallback(name, oldValue, newValue) {
if (name === 'hidden') {
this.style.display = newValue ? 'none' : 'block';
}
}

disconnectedCallback() {
this.proxy.remove();

this.scroller.removeEventListener(
'scroll',
this.syncScroll(this.scroller, this)
);
this.scroller.removeEventListener(
'scroll',
this.syncScroll(this, this.scroller)
);

window.removeEventListener('resize', this.handleResize.bind(this));
}

handleResize() {
this.proxy.style.width = this.content.getBoundingClientRect().width + 'px';

if (this.hasOverflow) {
this.removeAttribute('hidden');
} else {
this.setAttribute('hidden', 'true');
}
}

syncScroll(a, b) {
return () => {
if (this.ignoreScrollEvent) {
return false;
}

if (this.animation) {
cancelAnimationFrame(this.animation);
}

this.animation = requestAnimationFrame(() => {
this.ignoreScrollEvent = true;
a.scrollLeft = b.scrollLeft;
this.ignoreScrollEvent = false;
});
};
}
}

customElements.define('craft-proxy-scrollbar', CraftProxyScrollbar);
49 changes: 28 additions & 21 deletions src/web/assets/cp/src/js/TableElementIndexView.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ Craft.TableElementIndexView = Craft.BaseElementIndexView.extend({
// Set the sort header
this.initTableHeaders();

this.addListener(Garnish.$win, 'resize', this.setContainerHeight);
this.setContainerHeight();
this.createScrollbar();

// Create the table sorter
if (
Expand Down Expand Up @@ -403,25 +402,6 @@ Craft.TableElementIndexView = Craft.BaseElementIndexView.extend({
Craft.cp.updateResponsiveTables();
},

setContainerHeight: function (event) {
window.requestAnimationFrame(() => {
const $tablePane = this.$container.find('.tablepane');
if (!$tablePane.length) {
return;
}

const footerHeight = $('#content > #footer').outerHeight(true) || 0;
const margin = parseInt(
getComputedStyle($tablePane[0]).getPropertyValue('--padding'),
10
);
const containerHeight =
window.innerHeight - $tablePane.offset().top - footerHeight - margin;

$tablePane.css('max-height', containerHeight);
});
},

_collapseElement: function ($toggle, force) {
if (!force && !$toggle.hasClass('expanded')) {
return false;
Expand Down Expand Up @@ -672,4 +652,31 @@ Craft.TableElementIndexView = Craft.BaseElementIndexView.extend({

this.base();
},

createScrollbar() {
const footer = document.querySelector('#content > #footer');
const stickyScrollbar = document.createElement('craft-proxy-scrollbar');
stickyScrollbar.setAttribute('scroller', '.tablepane');
stickyScrollbar.setAttribute('content', '.tablepane > table');

stickyScrollbar.style.bottom = `${
footer.getBoundingClientRect().height + 2
}px`;

let $scrollbar = $(stickyScrollbar);
const observer = new IntersectionObserver(
([ev]) => {
if (ev.intersectionRatio < 1) {
$scrollbar.insertAfter(this.$container);
} else {
$scrollbar.remove();
}
},
{
rootMargin: '0px 0px -1px 0px',
threshold: [1],
}
);
observer.observe(footer);
},
});

0 comments on commit a8ed31a

Please sign in to comment.