mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-13 07:24:13 +01:00
N°4021 - Change approach for sticky header, use the ScrollMagic lib as in the scrolling tabs to use the same abstraction level everywhere
This commit is contained in:
@@ -3,6 +3,9 @@
|
|||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
$ibo-panel-within-modal--sticky-sentinel-top--top: -1 * $ibo-vendors-jqueryui--ui-dialog-content--padding-y !default;
|
||||||
|
$ibo-panel-within-modal--sticky-sentinel-top--height: $ibo-vendors-jqueryui--ui-dialog-content--padding-y !default;
|
||||||
|
|
||||||
$ibo-panel-within-modal--header--top--is-sticky: -1 * $ibo-vendors-jqueryui--ui-dialog-content--padding-y !default;
|
$ibo-panel-within-modal--header--top--is-sticky: -1 * $ibo-vendors-jqueryui--ui-dialog-content--padding-y !default;
|
||||||
|
|
||||||
.ui-dialog-content {
|
.ui-dialog-content {
|
||||||
@@ -13,12 +16,14 @@ $ibo-panel-within-modal--header--top--is-sticky: -1 * $ibo-vendors-jqueryui--ui-
|
|||||||
* - We don't want to hardcode the modal's markup selector if not necessary as it could change in the future (and is already different in read-only vs edition)
|
* - We don't want to hardcode the modal's markup selector if not necessary as it could change in the future (and is already different in read-only vs edition)
|
||||||
* - Unlike in JS, there no easy way to find the closest descendant
|
* - Unlike in JS, there no easy way to find the closest descendant
|
||||||
*/
|
*/
|
||||||
.ibo-panel {
|
.ibo-panel.ibo-has-sticky-header {
|
||||||
/* Sticky header rules */
|
/* Sticky header rules */
|
||||||
> .ibo-panel--header {
|
> .ibo-sticky-sentinel-top {
|
||||||
&.ibo-is-sticking {
|
top: $ibo-panel-within-modal--sticky-sentinel-top--top;
|
||||||
|
height: $ibo-panel-within-modal--sticky-sentinel-top--height;
|
||||||
|
}
|
||||||
|
> .ibo-panel--header.ibo-is-sticking {
|
||||||
top: $ibo-panel-within-modal--header--top--is-sticky;
|
top: $ibo-panel-within-modal--header--top--is-sticky;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -267,8 +267,8 @@ $ibo-panel-colors: (
|
|||||||
|
|
||||||
/* All transitions should have a specific duration except for the header's "top" property otherwise it feels laggy */
|
/* All transitions should have a specific duration except for the header's "top" property otherwise it feels laggy */
|
||||||
/* - The header itself */
|
/* - The header itself */
|
||||||
transition-property: $ibo-panel--base-transition-property, top;
|
transition-property: $ibo-panel--base-transition-property, top, background-color;
|
||||||
transition-duration: $ibo-panel--base-transition-duration, 0s;
|
transition-duration: $ibo-panel--base-transition-duration, 0s, 0s;
|
||||||
transition-timing-function: $ibo-panel--base-transition-timing-function;
|
transition-timing-function: $ibo-panel--base-transition-timing-function;
|
||||||
/* - Impacted descendants (we don't put "*" as it can get shaky otherwise) */
|
/* - Impacted descendants (we don't put "*" as it can get shaky otherwise) */
|
||||||
.ibo-panel--title,
|
.ibo-panel--title,
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ $(function () {
|
|||||||
is_sticking: 'ibo-is-sticking',
|
is_sticking: 'ibo-is-sticking',
|
||||||
sticky_sentinel: 'ibo-sticky-sentinel',
|
sticky_sentinel: 'ibo-sticky-sentinel',
|
||||||
sticky_sentinel_top: 'ibo-sticky-sentinel-top',
|
sticky_sentinel_top: 'ibo-sticky-sentinel-top',
|
||||||
sticky_sentinel_bottom: 'ibo-sticky-sentinel-bottom',
|
|
||||||
},
|
},
|
||||||
js_selectors:
|
js_selectors:
|
||||||
{
|
{
|
||||||
@@ -44,10 +43,9 @@ $(function () {
|
|||||||
modal_content: '.ui-dialog-content',
|
modal_content: '.ui-dialog-content',
|
||||||
panel_header: '[data-role="ibo-panel--header"]:first',
|
panel_header: '[data-role="ibo-panel--header"]:first',
|
||||||
panel_header_sticky_sentinel_top: '[data-role="ibo-panel--header--sticky-sentinel-top"]',
|
panel_header_sticky_sentinel_top: '[data-role="ibo-panel--header--sticky-sentinel-top"]',
|
||||||
panel_header_sticky_sentinel_bottom: '[data-role="ibo-panel--header--sticky-sentinel-bottom"]',
|
|
||||||
},
|
},
|
||||||
// {IntersectionObserver} Observer for the sticky header
|
// {ScrollMagic.Controller} SM controller for the sticky header
|
||||||
sticky_header_observer: null,
|
sticky_header_controller: null,
|
||||||
|
|
||||||
// the constructor
|
// the constructor
|
||||||
_create: function () {
|
_create: function () {
|
||||||
@@ -64,17 +62,14 @@ $(function () {
|
|||||||
if (this._isHeaderVisibleOnScroll()) {
|
if (this._isHeaderVisibleOnScroll()) {
|
||||||
this.element.addClass(this.css_classes.has_sticky_header);
|
this.element.addClass(this.css_classes.has_sticky_header);
|
||||||
|
|
||||||
// Add sentinels to the markup to detect when the element changes between scrolling & sticked states
|
// Add sentinel to the markup to detect when the element changes between scrolling & sticked states
|
||||||
$('<div></div>')
|
$('<div></div>')
|
||||||
.addClass(this.css_classes.sticky_sentinel)
|
.addClass(this.css_classes.sticky_sentinel)
|
||||||
.addClass(this.css_classes.sticky_sentinel_top)
|
.addClass(this.css_classes.sticky_sentinel_top)
|
||||||
.attr('data-role', 'ibo-panel--header--sticky-sentinel-top')
|
.attr('data-role', 'ibo-panel--header--sticky-sentinel-top')
|
||||||
.prependTo(this.element);
|
.prependTo(this.element);
|
||||||
$('<div></div>')
|
|
||||||
.addClass(this.css_classes.sticky_sentinel)
|
this._updateStickyHeaderHandler();
|
||||||
.addClass(this.css_classes.sticky_sentinel_bottom)
|
|
||||||
.attr('data-role', 'ibo-panel--header--sticky-sentinel-bottom')
|
|
||||||
.appendTo(this.element);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_bindEvents: function () {
|
_bindEvents: function () {
|
||||||
@@ -82,36 +77,27 @@ $(function () {
|
|||||||
const oBodyElem = $('body');
|
const oBodyElem = $('body');
|
||||||
|
|
||||||
if (this._isHeaderVisibleOnScroll()) {
|
if (this._isHeaderVisibleOnScroll()) {
|
||||||
this._observeStickyHeaderChanges();
|
|
||||||
|
|
||||||
// When a modal opens, it could have been for this panel. As the panel is moved into the modal's markup after this JS widget is instantiated
|
// When a modal opens, it could have been for this panel. As the panel is moved into the modal's markup after this JS widget is instantiated
|
||||||
// the viewport is not the right one and we need to update it.
|
// the viewport is not the right one and we need to update it.
|
||||||
oBodyElem.on('dialogopen', function(){
|
oBodyElem.on('dialogopen', function(){
|
||||||
me._observeStickyHeaderChanges();
|
me._updateStickyHeaderHandler();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Stickey header helpers
|
// Stickey header helpers
|
||||||
/**
|
/**
|
||||||
* Instantiate an observer on the header to toggle its "sticky" state and fire an event
|
* Create or update an handler on the header to toggle its "sticky" state.
|
||||||
|
* Update is needed when the panel was moved to other DOM node.
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_observeStickyHeaderChanges: function () {
|
_updateStickyHeaderHandler: function () {
|
||||||
const me = this;
|
const me = this;
|
||||||
|
|
||||||
// Determine in which kind of container the panel is
|
// Determine in which kind of container the panel is
|
||||||
let oNewViewportElem = null;
|
let oNewViewportElem = this.element.scrollParent()[0];
|
||||||
// - In a modal
|
|
||||||
if (this.element.closest(this.js_selectors.modal_content).length > 0) {
|
|
||||||
oNewViewportElem = this.element.closest(this.js_selectors.modal_content)[0];
|
|
||||||
}
|
|
||||||
// - In a standard page
|
|
||||||
else if (this.element.closest('#ibo-center-container').length > 0) {
|
|
||||||
oNewViewportElem = this.element.closest('#ibo-center-container')[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
// If viewport hasn't changed, there is no need to refresh the observer
|
// If viewport hasn't changed, there is no need to refresh the SM controller
|
||||||
if (oNewViewportElem === this.options.viewport_elem) {
|
if (oNewViewportElem === this.options.viewport_elem) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -119,37 +105,29 @@ $(function () {
|
|||||||
// Update the reference viewport
|
// Update the reference viewport
|
||||||
this.options.viewport_elem = oNewViewportElem;
|
this.options.viewport_elem = oNewViewportElem;
|
||||||
|
|
||||||
// Clean observer if there was already one
|
// Clean SM controller if there was already one
|
||||||
if (null !== this.sticky_header_observer) {
|
if (null !== this.sticky_header_controller) {
|
||||||
this.sticky_header_observer.disconnect();
|
this.sticky_header_controller.destroy(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare observer options
|
// Prepare SM controller
|
||||||
const oOptions = {
|
this.sticky_header_controller = new ScrollMagic.Controller({
|
||||||
root: this.options.viewport_elem,
|
container: this.options.viewport_elem,
|
||||||
threshold: 0,
|
});
|
||||||
};
|
|
||||||
|
|
||||||
// Instantiate observer and callback for the top sentinel
|
let oSMScene = new ScrollMagic.Scene({
|
||||||
this.sticky_header_observer = new IntersectionObserver(function (aEntries, oObserver) {
|
triggerElement: this.element.find(this.js_selectors.panel_header_sticky_sentinel_top)[0],
|
||||||
for (const oEntry of aEntries) {
|
triggerHook: 0,
|
||||||
const oSentinelInfo = oEntry.boundingClientRect;
|
duration: this.element.outerHeight(),
|
||||||
const oRootInfo = oEntry.rootBounds;
|
offset: this.element.find(this.js_selectors.panel_header_sticky_sentinel_top).outerHeight()
|
||||||
|
})
|
||||||
// Started sticking.
|
.on('enter', function(){
|
||||||
if (oSentinelInfo.bottom < oRootInfo.top) {
|
me._onHeaderBecomesSticky();
|
||||||
me._onHeaderBecomesSticky();
|
})
|
||||||
|
.on('leave', function(){
|
||||||
}
|
me._onHeaderStopsBeingSticky();
|
||||||
|
})
|
||||||
// Stopped sticking.
|
.addTo(this.sticky_header_controller);
|
||||||
if (oSentinelInfo.bottom >= oRootInfo.top &&
|
|
||||||
oSentinelInfo.bottom < oRootInfo.bottom) {
|
|
||||||
me._onHeaderStopsBeingSticky();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, oOptions);
|
|
||||||
this.sticky_header_observer.observe(this.element.find(this.js_selectors.panel_header_sticky_sentinel_top)[0]);
|
|
||||||
},
|
},
|
||||||
_onHeaderBecomesSticky: function () {
|
_onHeaderBecomesSticky: function () {
|
||||||
this.element.find(this.js_selectors.panel_header).addClass(this.css_classes.is_sticking);
|
this.element.find(this.js_selectors.panel_header).addClass(this.css_classes.is_sticking);
|
||||||
|
|||||||
Reference in New Issue
Block a user