Add sticky scrolling to task page [SCI-8980]

This commit is contained in:
Anton 2023-08-29 15:45:07 +02:00
parent bbffce5e03
commit 6a287fa54f
6 changed files with 91 additions and 14 deletions

View file

@ -345,7 +345,6 @@ function initProtocolSectionOpenEvent() {
});
});
}
/**
* Initializes page
*/

View file

@ -115,7 +115,7 @@
.task-section-header {
align-items: center;
display: flex;
flex-wrap: wrap;
min-height: 4rem;
.actions-block {
display: flex;

View file

@ -1,15 +1,25 @@
<template>
<div v-if="protocol.id" class="task-protocol">
<div class="task-section-header" v-if="!inRepository">
<div class="portocol-header-left-part">
<a class="task-section-caret" tabindex="0" role="button" data-toggle="collapse" href="#protocol-content" aria-expanded="true" aria-controls="protocol-content">
<i class="sn-icon sn-icon-right"></i>
<div class="task-section-title">
<h2>{{ i18n.t('Protocol') }}</h2>
<div ref="header" class="task-section-header ml-[-1rem] w-[calc(100%_+_2rem)] px-4 z-[250] bg-sn-white sticky top-0 transition" v-if="!inRepository">
<div class="portocol-header-left-part truncate grow">
<template v-if="headerSticked && protocol.attributes.assignable_my_module_name">
<i class="sn-icon sn-icon-navigator sci--layout--navigator-open cursor-pointer p-1.5 border rounded border-sn-light-grey mr-4"></i>
<div @click="scrollTop" class="task-section-title w-[calc(100%_-_4rem)] cursor-pointer">
<h2 class="truncate">{{ protocol.attributes.assignable_my_module_name }}</h2>
</div>
</template>
<template v-else >
<a class="task-section-caret" tabindex="0" role="button" data-toggle="collapse" href="#protocol-content" aria-expanded="true" aria-controls="protocol-content">
<i class="sn-icon sn-icon-right"></i>
<div class="task-section-title">
<h2>{{ i18n.t('Protocol') }}</h2>
</div>
</a>
</template>
<div :class="{'hidden': headerSticked}" >
<div class="my-module-protocol-status">
<!-- protocol status dropdown gets mounted here -->
</div>
</a>
<div class="my-module-protocol-status">
<!-- protocol status dropdown gets mounted here -->
</div>
</div>
<div class="actions-block">
@ -205,6 +215,8 @@
reordering: false,
publishing: false,
stepToReload: null,
headerSticked: false,
lastScrollTop: 0,
}
},
created() {
@ -212,12 +224,20 @@
this.protocol = result.data;
this.$nextTick(() => {
this.refreshProtocolStatus();
if (!this.inRepository) {
window.addEventListener('scroll', this.initStackableHeaders, false);
}
});
$.get(this.urls.steps_url, (result) => {
this.steps = result.data
})
});
},
beforeDestroy() {
if (!this.inRepository) {
window.removeEventListener('scroll', this.initStackableHeaders, false);
}
},
methods: {
reloadStep(step) {
this.stepToReload = step;
@ -363,6 +383,58 @@
publishProtocol(comment) {
this.protocol.attributes.version_comment = comment;
$.post(this.urls.publish_url, {version_comment: comment, view: 'show'})
},
scrollTop() {
window.scrollTo(0, 0);
setTimeout(() => {
$('.my_module-name .view-mode').trigger('click');
$('.my_module-name .input-field').focus();
}, 300)
},
initStackableHeaders() {
let protocolHeader = this.$refs.header;
let secondaryNavigation = document.querySelector('#taskSecondaryMenu');
let protocolHeaderHeight = protocolHeader.offsetHeight;
let protocolHeaderTop = protocolHeader.getBoundingClientRect().top;
let secondaryNavigationHeight = secondaryNavigation.offsetHeight;
let secondaryNavigationTop = secondaryNavigation.getBoundingClientRect().top;
// Add shadow to secondary navigation when it starts fly
if (secondaryNavigation.getBoundingClientRect().top == 0 && !this.headerSticked) {
secondaryNavigation.style.boxShadow = '0px 5px 8px 0px rgba(0, 0, 0, 0.10)';
} else {
secondaryNavigation.style.boxShadow = 'none';
}
if (protocolHeaderTop < protocolHeaderHeight) { // When secondary navigation touch protocol header
secondaryNavigation.style.top = protocolHeaderTop - protocolHeaderHeight + 'px'; // Secondary navigation starts slowly disappear
protocolHeader.style.boxShadow = '0px 5px 8px 0px rgba(0, 0, 0, 0.10)'; // Flying shadow
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;
secondaryNavigation.style.top = newSecondaryTop + 'px'; // Secondary navigation starts slowly appear
protocolHeader.style.top = secondaryNavigationHeight + newSecondaryTop - 1 + 'px'; // Protocol header starts getting offset to compensate secondary navigation position
// -1 to compensate small gap between protocol 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 > secondaryNavigationHeight ) newSecondaryTop = secondaryNavigationHeight * -1;
secondaryNavigation.style.top = newSecondaryTop + 'px'; // Secondary navigation starts slowly disappear
protocolHeader.style.top = newSecondaryTop + secondaryNavigationHeight - 1 + 'px'; // Protocol header starts getting offset to compensate secondary navigation position
// -1 to compensate small gap between protocol header and secondary navigation
}
} else {
// Just reset secondary navigation and protocol header styles to initial state
secondaryNavigation.style.top = '0px';
protocolHeader.style.boxShadow = 'none';
this.headerSticked = false;
}
this.lastScrollTop = window.scrollY; // Save last scroll position to when user scroll up/down
}
}
}

View file

@ -10,7 +10,7 @@ class ProtocolSerializer < ActiveModel::Serializer
attributes :name, :id, :urls, :description, :description_view, :updated_at, :in_repository,
:created_at_formatted, :updated_at_formatted, :added_by, :authors, :keywords, :version,
:code, :published, :version_comment, :archived, :linked, :has_draft,
:published_on_formatted, :published_by, :created_from_version, :assignable_my_module_id
:published_on_formatted, :published_by, :created_from_version, :assignable_my_module_id, :assignable_my_module_name
def updated_at
object.updated_at.to_i
@ -122,6 +122,12 @@ class ProtocolSerializer < ActiveModel::Serializer
object.my_module&.id
end
def assignable_my_module_name
return if in_repository
object.my_module&.name
end
private
def load_from_repo_url

View file

@ -1,4 +1,4 @@
<div class="content-header sticky-header my-module-header">
<div class="content-header my-module-header">
<div class="my-module-title-row title-row">
<i class="sn-icon sn-icon-navigator sci--layout--navigator-open cursor-pointer p-1.5 border rounded border-sn-light-grey mr-4"></i>
<h1 class="my_module-name" data-toggle="tooltip" data-placement="bottom" title="<%= @my_module.name %>">

View file

@ -1,4 +1,4 @@
<div class="bg-sn-white border-b border-solid border-0 border-sn-sleepy-grey rounded-t px-4 py-2">
<div id="taskSecondaryMenu" class="bg-sn-white border-b border-solid border-0 border-sn-sleepy-grey rounded-t px-4 py-2 top-0 sticky z-[251]">
<div class="flex items-center gap-4">
<% if can_read_experiment?(@my_module.experiment) %>
<a class="p-3 border-b-4 border-transparent hover:no-underline uppercase text-bold capitalize <%= is_module_protocols? ? "text-sn-blue" : "text-sn-grey" %>"