2024-03-24 20:53:43 +00:00
|
|
|
let scrollLeftStyle = null;
|
2019-06-25 01:10:51 +00:00
|
|
|
|
|
|
|
function testScrollLeftStyle() {
|
|
|
|
if ( scrollLeftStyle !== null ) {
|
|
|
|
return scrollLeftStyle;
|
|
|
|
}
|
|
|
|
// Detect which scrollLeft style the browser uses
|
|
|
|
// Adapted from <https://github.com/othree/jquery.rtl-scroll-type>.
|
|
|
|
// Original code copyright 2012 Wei-Ko Kao, licensed under the MIT License.
|
|
|
|
// Adaptation copied from OO.ui.Element.static.getScrollLeft
|
2024-03-24 20:53:43 +00:00
|
|
|
const $definer = $( '<div>' ).attr( {
|
2019-06-25 01:10:51 +00:00
|
|
|
dir: 'rtl',
|
|
|
|
style: 'font-size: 14px; width: 4px; height: 1px; position: absolute; top: -1000px; overflow: scroll;'
|
|
|
|
} ).text( 'ABCD' );
|
2020-01-06 13:03:52 +00:00
|
|
|
$definer.appendTo( document.body );
|
2024-03-24 20:53:43 +00:00
|
|
|
const definer = $definer[ 0 ];
|
2019-06-25 01:10:51 +00:00
|
|
|
if ( definer.scrollLeft > 0 ) {
|
|
|
|
// Safari, Chrome
|
|
|
|
scrollLeftStyle = 'default';
|
|
|
|
} else {
|
|
|
|
definer.scrollLeft = 1;
|
|
|
|
if ( definer.scrollLeft === 0 ) {
|
|
|
|
// Firefox, old Opera
|
|
|
|
scrollLeftStyle = 'negative';
|
|
|
|
} else {
|
|
|
|
// Internet Explorer, Edge
|
|
|
|
scrollLeftStyle = 'reverse';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$definer.remove();
|
|
|
|
return scrollLeftStyle;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* When tabs are present and one is selected, scroll the selected tab into view.
|
2024-07-27 05:52:33 +00:00
|
|
|
*
|
2024-06-26 13:23:40 +00:00
|
|
|
* @ignore
|
2019-06-25 01:10:51 +00:00
|
|
|
*/
|
|
|
|
function initTabsScrollPosition() {
|
2024-03-24 20:53:43 +00:00
|
|
|
// eslint-disable-next-line no-jquery/no-global-selector
|
|
|
|
const $selectedTab = $( '.minerva__tab.selected' );
|
|
|
|
if ( $selectedTab.length !== 1 ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const selectedTab = $selectedTab.get( 0 );
|
|
|
|
const $tabContainer = $selectedTab.closest( '.minerva__tab-container' );
|
|
|
|
const tabContainer = $tabContainer.get( 0 );
|
|
|
|
const maxScrollLeft = tabContainer.scrollWidth - tabContainer.clientWidth;
|
|
|
|
const dir = $tabContainer.css( 'direction' ) || 'ltr';
|
2019-06-25 01:10:51 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Set tabContainer.scrollLeft, with adjustments for browser inconsistencies in RTL
|
2020-06-02 21:21:44 +00:00
|
|
|
*
|
2019-06-25 01:10:51 +00:00
|
|
|
* @param {number} sl New .scrollLeft value, in 'default' (WebKit) style
|
|
|
|
*/
|
|
|
|
function setScrollLeft( sl ) {
|
|
|
|
if ( dir === 'ltr' ) {
|
|
|
|
tabContainer.scrollLeft = sl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( testScrollLeftStyle() === 'reverse' ) {
|
|
|
|
sl = maxScrollLeft - sl;
|
|
|
|
} else if ( testScrollLeftStyle() === 'negative' ) {
|
|
|
|
sl = -( maxScrollLeft - sl );
|
|
|
|
}
|
|
|
|
tabContainer.scrollLeft = sl;
|
|
|
|
}
|
|
|
|
|
2024-03-24 20:53:43 +00:00
|
|
|
const leftMostChild = dir === 'ltr' ? tabContainer.firstElementChild : tabContainer.lastElementChild;
|
|
|
|
const rightMostChild = dir === 'ltr' ? tabContainer.lastElementChild : tabContainer.firstElementChild;
|
2019-06-25 01:10:51 +00:00
|
|
|
// If the tab is wider than the container (doesn't fit), this value will be negative
|
2024-03-24 20:53:43 +00:00
|
|
|
const widthDiff = tabContainer.clientWidth - selectedTab.clientWidth;
|
2019-06-25 01:10:51 +00:00
|
|
|
|
|
|
|
if ( selectedTab === leftMostChild ) {
|
|
|
|
// The left-most tab is selected. If the tab fits, scroll all the way to the left.
|
|
|
|
// If the tab doesn't fit, align its start edge with the container's start edge.
|
|
|
|
if ( dir === 'ltr' || widthDiff >= 0 ) {
|
|
|
|
setScrollLeft( 0 );
|
|
|
|
} else {
|
|
|
|
setScrollLeft( -widthDiff );
|
|
|
|
}
|
|
|
|
} else if ( selectedTab === rightMostChild ) {
|
|
|
|
// The right-most tab is selected. If the tab fits, scroll all the way to the right.
|
|
|
|
// If the tab doesn't fit, align its start edge with the container's start edge.
|
|
|
|
if ( dir === 'rtl' || widthDiff >= 0 ) {
|
|
|
|
setScrollLeft( maxScrollLeft );
|
|
|
|
} else {
|
|
|
|
setScrollLeft( maxScrollLeft + widthDiff );
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// The selected tab is not the left-most or right-most, it's somewhere in the middle
|
2024-03-24 20:53:43 +00:00
|
|
|
const tabPosition = $selectedTab.position();
|
|
|
|
const containerPosition = $tabContainer.position();
|
2019-06-25 01:10:51 +00:00
|
|
|
// Position of the left edge of $selectedTab relative to the left edge of $tabContainer
|
2024-03-24 20:53:43 +00:00
|
|
|
const left = tabPosition.left - containerPosition.left;
|
2019-06-25 01:10:51 +00:00
|
|
|
// Because the calculations above use the existing .scrollLeft from the browser,
|
|
|
|
// we should not use setScrollLeft() here. Instead, we rely on the fact that scrollLeft
|
|
|
|
// increases to the left in the 'default' and 'negative' modes, and to the right in
|
|
|
|
// the 'reverse' mode, so we can add/subtract a delta to/from scrollLeft accordingly.
|
2024-03-24 20:53:43 +00:00
|
|
|
let increaseScrollLeft;
|
2019-06-25 01:10:51 +00:00
|
|
|
if ( widthDiff >= 0 ) {
|
|
|
|
// The tab fits, center it
|
|
|
|
increaseScrollLeft = left - widthDiff / 2;
|
|
|
|
} else if ( dir === 'ltr' ) {
|
|
|
|
// The tab doesn't fit (LTR), align its left edge with the container's left edge
|
|
|
|
increaseScrollLeft = left;
|
|
|
|
} else {
|
|
|
|
// The tab doesn't fit (RTL), align its right edge with the container's right edge
|
|
|
|
increaseScrollLeft = left - widthDiff;
|
|
|
|
}
|
|
|
|
tabContainer.scrollLeft += increaseScrollLeft *
|
|
|
|
( testScrollLeftStyle() === 'reverse' ? -1 : 1 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
initTabsScrollPosition: initTabsScrollPosition
|
|
|
|
};
|