scinote-web/app/javascript/vue/mixins/stackableHeadersMixin.js
2023-10-08 16:07:05 +02:00

101 lines
4.2 KiB
JavaScript

/*
* Mixin for adjusting stackable headers on scroll.
* - Tracks scroll position to modify headers' styles & positions.
* - Observes changes in the secondary navigation's height.
* - Adjusts TinyMCE editor header offset if present.
*/
export default {
data() {
return {
lastScrollTop: 0,
headerSticked: false,
secondaryNavigation: null,
taskSecondaryMenuHeight: 0,
};
},
computed: {
headerRef() {
return this.getHeader();
},
},
mounted() {
this.secondaryNavigation = document.querySelector('#taskSecondaryMenu');
this.resizeObserver = new ResizeObserver((entries) => {
entries.forEach((entry) => {
this.taskSecondaryMenuHeight = entry.target.offsetHeight;
});
});
this.resizeObserver.observe(this.secondaryNavigation);
},
beforeDestroy() {
if (this.resizeObserver) {
this.resizeObserver.disconnect();
}
},
methods: {
initStackableHeaders() {
const header = this.headerRef;
const headerHeight = header.offsetHeight;
const headerTop = header.getBoundingClientRect().top;
const secondaryNavigationTop = this.secondaryNavigation.getBoundingClientRect().top;
// TinyMCE offset calculation
let stickyNavigationHeight = this.taskSecondaryMenuHeight;
if ($('.tox-editor-header').length > 0 && $('.tox-editor-header')[0].getBoundingClientRect().top > headerTop) {
stickyNavigationHeight += headerHeight;
}
// Add shadow to secondary navigation when it starts fly
if (this.secondaryNavigation.getBoundingClientRect().top === 0 && !this.headerSticked) {
this.secondaryNavigation.style.boxShadow = '0 9px 8px -2px rgba(0, 0, 0, 0.1)';
this.secondaryNavigation.style.zIndex = 252;
} else {
this.secondaryNavigation.style.boxShadow = 'none';
}
if (headerTop - 5 < this.taskSecondaryMenuHeight) { // When secondary navigation touch header
this.secondaryNavigation.style.top = `${headerTop - headerHeight}px`; // Secondary navigation starts slowly disappear
header.style.boxShadow = '0 9px 8px -2px rgba(0, 0, 0, 0.1)'; // Flying shadow
header.style.zIndex = 250;
this.headerSticked = true;
if (this.lastScrollTop > window.scrollY) { // When user scroll up
let newSecondaryTop = secondaryNavigationTop - (window.scrollY - this.lastScrollTop); // Calculate new top position of secondary navigation
if (newSecondaryTop > 0) newSecondaryTop = 0;
this.secondaryNavigation.style.top = `${newSecondaryTop}px`; // Secondary navigation starts slowly appear
this.secondaryNavigation.style.zIndex = 252;
header.style.top = `${this.taskSecondaryMenuHeight + newSecondaryTop - 1}px`; // Header starts getting offset to compensate secondary navigation position
// -1 to compensate small gap between header and secondary navigation
} else { // When user scroll down
let newSecondaryTop = secondaryNavigationTop - (window.scrollY - this.lastScrollTop); // Calculate new top position of secondary navigation
if (newSecondaryTop * -1 > this.taskSecondaryMenuHeight) newSecondaryTop = this.taskSecondaryMenuHeight * -1;
this.secondaryNavigation.style.top = `${newSecondaryTop}px`; // Secondary navigation starts slowly disappear
header.style.top = `${newSecondaryTop + this.taskSecondaryMenuHeight - 1}px`; // Header starts getting offset to compensate secondary navigation position
// -1 to compensate small gap between header and secondary navigation
if (newSecondaryTop * -1 >= this.taskSecondaryMenuHeight) this.secondaryNavigation.style.zIndex = 11;
}
} else {
// Just reset secondary navigation and header styles to initial state
this.secondaryNavigation.style.top = '0px';
header.style.top = '0px';
header.style.boxShadow = 'none';
header.style.zIndex = 105;
this.headerSticked = false;
}
// Apply TinyMCE offset
$('.tox-editor-header').css(
'top',
stickyNavigationHeight + parseInt($(this.secondaryNavigation).css('top'), 10)
);
this.lastScrollTop = window.scrollY; // Save last scroll position to when user scroll up/down
},
},
};