mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Merge pull request #2613 from dmitrylyzo/scrollmanager-search-parent
ScrollManager: fix parent search
This commit is contained in:
commit
5d2fc18664
3 changed files with 105 additions and 37 deletions
|
@ -173,6 +173,15 @@ import layoutManager from './layoutManager';
|
||||||
return Math.min(document.documentElement.clientHeight, document.body.clientHeight);
|
return Math.min(document.documentElement.clientHeight, document.body.clientHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns attribute value.
|
||||||
|
* @param {string} attributeName - Attibute name.
|
||||||
|
* @return {string} Attibute value.
|
||||||
|
*/
|
||||||
|
getAttribute(attributeName) {
|
||||||
|
return document.body.getAttribute(attributeName);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns bounding client rect.
|
* Returns bounding client rect.
|
||||||
* @return {Rect} Bounding client rect.
|
* @return {Rect} Bounding client rect.
|
||||||
|
@ -201,6 +210,21 @@ import layoutManager from './layoutManager';
|
||||||
*/
|
*/
|
||||||
const documentScroller = new DocumentScroller();
|
const documentScroller = new DocumentScroller();
|
||||||
|
|
||||||
|
const scrollerHints = {
|
||||||
|
x: {
|
||||||
|
nameScroll: 'scrollWidth',
|
||||||
|
nameClient: 'clientWidth',
|
||||||
|
nameStyle: 'overflowX',
|
||||||
|
nameScrollMode: 'data-scroll-mode-x'
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
nameScroll: 'scrollHeight',
|
||||||
|
nameClient: 'clientHeight',
|
||||||
|
nameStyle: 'overflowY',
|
||||||
|
nameScrollMode: 'data-scroll-mode-y'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns parent element that can be scrolled. If no such, returns document scroller.
|
* Returns parent element that can be scrolled. If no such, returns document scroller.
|
||||||
*
|
*
|
||||||
|
@ -210,23 +234,28 @@ import layoutManager from './layoutManager';
|
||||||
*/
|
*/
|
||||||
function getScrollableParent(element, vertical) {
|
function getScrollableParent(element, vertical) {
|
||||||
if (element) {
|
if (element) {
|
||||||
let nameScroll = 'scrollWidth';
|
const scrollerHint = vertical ? scrollerHints.y : scrollerHints.x;
|
||||||
let nameClient = 'clientWidth';
|
|
||||||
let nameClass = 'scrollX';
|
|
||||||
|
|
||||||
if (vertical) {
|
|
||||||
nameScroll = 'scrollHeight';
|
|
||||||
nameClient = 'clientHeight';
|
|
||||||
nameClass = 'scrollY';
|
|
||||||
}
|
|
||||||
|
|
||||||
let parent = element.parentElement;
|
let parent = element.parentElement;
|
||||||
|
|
||||||
while (parent) {
|
while (parent && parent !== document.body) {
|
||||||
// Skip 'emby-scroller' and 'emby-tabs' because they scroll by themselves
|
const scrollMode = parent.getAttribute(scrollerHint.nameScrollMode);
|
||||||
if (!parent.classList.contains('emby-scroller') &&
|
|
||||||
!parent.classList.contains('emby-tabs') &&
|
// Stop on self-scrolled containers
|
||||||
parent[nameScroll] > parent[nameClient] && parent.classList.contains(nameClass)) {
|
if (scrollMode === 'custom') {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = window.getComputedStyle(parent);
|
||||||
|
|
||||||
|
// Stop on fixed parent
|
||||||
|
if (styles.position === 'fixed') {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
const overflow = styles[scrollerHint.nameStyle];
|
||||||
|
|
||||||
|
if (overflow === 'scroll' || overflow === 'auto' && parent[scrollerHint.nameScroll] > parent[scrollerHint.nameClient]) {
|
||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,6 +271,8 @@ import layoutManager from './layoutManager';
|
||||||
* @property {number} scrollPos - Current scroll position.
|
* @property {number} scrollPos - Current scroll position.
|
||||||
* @property {number} scrollSize - Scroll size.
|
* @property {number} scrollSize - Scroll size.
|
||||||
* @property {number} clientSize - Client size.
|
* @property {number} clientSize - Client size.
|
||||||
|
* @property {string} mode - Scrolling mode.
|
||||||
|
* @property {boolean} custom - Custom scrolling mode.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -258,12 +289,16 @@ import layoutManager from './layoutManager';
|
||||||
data.scrollPos = scroller.scrollLeft;
|
data.scrollPos = scroller.scrollLeft;
|
||||||
data.scrollSize = scroller.scrollWidth;
|
data.scrollSize = scroller.scrollWidth;
|
||||||
data.clientSize = scroller.clientWidth;
|
data.clientSize = scroller.clientWidth;
|
||||||
|
data.mode = scroller.getAttribute(scrollerHints.x.nameScrollMode);
|
||||||
} else {
|
} else {
|
||||||
data.scrollPos = scroller.scrollTop;
|
data.scrollPos = scroller.scrollTop;
|
||||||
data.scrollSize = scroller.scrollHeight;
|
data.scrollSize = scroller.scrollHeight;
|
||||||
data.clientSize = scroller.clientHeight;
|
data.clientSize = scroller.clientHeight;
|
||||||
|
data.mode = scroller.getAttribute(scrollerHints.y.nameScrollMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data.custom = data.mode === 'custom';
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -348,9 +383,13 @@ import layoutManager from './layoutManager';
|
||||||
const scrollBehavior = smooth ? 'smooth' : 'instant';
|
const scrollBehavior = smooth ? 'smooth' : 'instant';
|
||||||
|
|
||||||
if (xScroller !== yScroller) {
|
if (xScroller !== yScroller) {
|
||||||
|
if (xScroller) {
|
||||||
scrollToHelper(xScroller, {left: scrollX, behavior: scrollBehavior});
|
scrollToHelper(xScroller, {left: scrollX, behavior: scrollBehavior});
|
||||||
|
}
|
||||||
|
if (yScroller) {
|
||||||
scrollToHelper(yScroller, {top: scrollY, behavior: scrollBehavior});
|
scrollToHelper(yScroller, {top: scrollY, behavior: scrollBehavior});
|
||||||
} else {
|
}
|
||||||
|
} else if (xScroller) {
|
||||||
scrollToHelper(xScroller, {left: scrollX, top: scrollY, behavior: scrollBehavior});
|
scrollToHelper(xScroller, {left: scrollX, top: scrollY, behavior: scrollBehavior});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -377,8 +416,8 @@ import layoutManager from './layoutManager';
|
||||||
* @param {number} scrollY - Vertical coordinate.
|
* @param {number} scrollY - Vertical coordinate.
|
||||||
*/
|
*/
|
||||||
function animateScroll(xScroller, scrollX, yScroller, scrollY) {
|
function animateScroll(xScroller, scrollX, yScroller, scrollY) {
|
||||||
const ox = xScroller.scrollLeft;
|
const ox = xScroller ? xScroller.scrollLeft : scrollX;
|
||||||
const oy = yScroller.scrollTop;
|
const oy = yScroller ? yScroller.scrollTop : scrollY;
|
||||||
const dx = scrollX - ox;
|
const dx = scrollX - ox;
|
||||||
const dy = scrollY - oy;
|
const dy = scrollY - oy;
|
||||||
|
|
||||||
|
@ -502,19 +541,37 @@ import layoutManager from './layoutManager';
|
||||||
scrollCenterX = scrollCenterY = false;
|
scrollCenterX = scrollCenterY = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const xScroller = getScrollableParent(element, false);
|
let xScroller = getScrollableParent(element, false);
|
||||||
const yScroller = getScrollableParent(element, true);
|
let yScroller = getScrollableParent(element, true);
|
||||||
|
|
||||||
const elementRect = element.getBoundingClientRect();
|
|
||||||
|
|
||||||
const xScrollerData = getScrollerData(xScroller, false);
|
const xScrollerData = getScrollerData(xScroller, false);
|
||||||
const yScrollerData = getScrollerData(yScroller, true);
|
const yScrollerData = getScrollerData(yScroller, true);
|
||||||
|
|
||||||
const xPos = getScrollerChildPos(xScroller, element, false);
|
// Exit, since we have no control over scrolling in this container
|
||||||
const yPos = getScrollerChildPos(yScroller, element, true);
|
if (xScroller === yScroller && (xScrollerData.custom || yScrollerData.custom)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const scrollX = calcScroll(xScrollerData, xPos, elementRect.width, scrollCenterX);
|
// Exit, since we have no control over scrolling in these containers
|
||||||
let scrollY = calcScroll(yScrollerData, yPos, elementRect.height, scrollCenterY);
|
if (xScrollerData.custom && yScrollerData.custom) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const elementRect = element.getBoundingClientRect();
|
||||||
|
|
||||||
|
let scrollX = 0;
|
||||||
|
let scrollY = 0;
|
||||||
|
|
||||||
|
if (!xScrollerData.custom) {
|
||||||
|
const xPos = getScrollerChildPos(xScroller, element, false);
|
||||||
|
scrollX = calcScroll(xScrollerData, xPos, elementRect.width, scrollCenterX);
|
||||||
|
} else {
|
||||||
|
xScroller = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!yScrollerData.custom) {
|
||||||
|
const yPos = getScrollerChildPos(yScroller, element, true);
|
||||||
|
scrollY = calcScroll(yScrollerData, yPos, elementRect.height, scrollCenterY);
|
||||||
|
|
||||||
// HACK: Scroll to top for top menu because it is hidden
|
// HACK: Scroll to top for top menu because it is hidden
|
||||||
// FIXME: Need a marker to scroll top/bottom
|
// FIXME: Need a marker to scroll top/bottom
|
||||||
|
@ -527,6 +584,9 @@ import layoutManager from './layoutManager';
|
||||||
if (scrollY < minimumScrollY() && yScroller === documentScroller) {
|
if (scrollY < minimumScrollY() && yScroller === documentScroller) {
|
||||||
scrollY = 0;
|
scrollY = 0;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
yScroller = null;
|
||||||
|
}
|
||||||
|
|
||||||
doScroll(xScroller, scrollX, yScroller, scrollY, smooth);
|
doScroll(xScroller, scrollX, yScroller, scrollY, smooth);
|
||||||
}
|
}
|
||||||
|
|
|
@ -630,6 +630,8 @@ const scrollerFactory = function (frame, options) {
|
||||||
//passive: true
|
//passive: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
scrollSource.removeAttribute(`data-scroll-mode-${o.horizontal ? 'x' : 'y'}`);
|
||||||
|
|
||||||
// Reset initialized status and return the instance
|
// Reset initialized status and return the instance
|
||||||
self.initialized = 0;
|
self.initialized = 0;
|
||||||
return self;
|
return self;
|
||||||
|
@ -751,6 +753,8 @@ const scrollerFactory = function (frame, options) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scrollSource.setAttribute(`data-scroll-mode-${o.horizontal ? 'x' : 'y'}`, 'custom');
|
||||||
|
|
||||||
if (transform || layoutManager.tv) {
|
if (transform || layoutManager.tv) {
|
||||||
// This can prevent others from being able to listen to mouse events
|
// This can prevent others from being able to listen to mouse events
|
||||||
dom.addEventListener(dragSourceElement, 'mousedown', dragInitSlidee, {
|
dom.addEventListener(dragSourceElement, 'mousedown', dragInitSlidee, {
|
||||||
|
|
|
@ -103,6 +103,8 @@ function centerOnFocusVertical(e) {
|
||||||
|
|
||||||
export const centerFocus = {
|
export const centerFocus = {
|
||||||
on: function (element, horizontal) {
|
on: function (element, horizontal) {
|
||||||
|
element.setAttribute(`data-scroll-mode-${horizontal ? 'x' : 'y'}`, 'custom');
|
||||||
|
|
||||||
if (horizontal) {
|
if (horizontal) {
|
||||||
dom.addEventListener(element, 'focus', centerOnFocusHorizontal, {
|
dom.addEventListener(element, 'focus', centerOnFocusHorizontal, {
|
||||||
capture: true,
|
capture: true,
|
||||||
|
@ -116,6 +118,8 @@ export const centerFocus = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
off: function (element, horizontal) {
|
off: function (element, horizontal) {
|
||||||
|
element.removeAttribute(`data-scroll-mode-${horizontal ? 'x' : 'y'}`);
|
||||||
|
|
||||||
if (horizontal) {
|
if (horizontal) {
|
||||||
dom.removeEventListener(element, 'focus', centerOnFocusHorizontal, {
|
dom.removeEventListener(element, 'focus', centerOnFocusHorizontal, {
|
||||||
capture: true,
|
capture: true,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue