mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
Merge branch '_sticky_nav' into 139_demandeur
This commit is contained in:
commit
3f2fe6a138
@ -37,12 +37,16 @@ const messages = {
|
||||
ok: "OK",
|
||||
cancel: "Annuler",
|
||||
close: "Fermer",
|
||||
next: "Suivant",
|
||||
previous: "Précédent",
|
||||
back: "Retour",
|
||||
check_all: "cocher tout",
|
||||
reset: "réinitialiser"
|
||||
},
|
||||
nav: {
|
||||
next: "Suivant",
|
||||
previous: "Précédent",
|
||||
top: "Haut",
|
||||
bottom: "Bas",
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<banner></banner>
|
||||
<sticky-nav></sticky-nav>
|
||||
|
||||
<h1 v-if="accompanyingCourse.step === 'DRAFT'">{{ $t('course.title.draft') }}</h1>
|
||||
<h1 v-else>{{ $t('course.title.active') }}</h1>
|
||||
@ -17,6 +18,7 @@
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import Banner from './components/Banner.vue';
|
||||
import StickyNav from './components/StickyNav.vue';
|
||||
import PersonsAssociated from './components/PersonsAssociated.vue';
|
||||
import Requestor from './components/Requestor.vue';
|
||||
import SocialIssue from './components/SocialIssue.vue';
|
||||
@ -29,6 +31,7 @@ export default {
|
||||
name: 'App',
|
||||
components: {
|
||||
Banner,
|
||||
StickyNav,
|
||||
PersonsAssociated,
|
||||
Requestor,
|
||||
SocialIssue,
|
||||
@ -68,7 +71,7 @@ export default {
|
||||
}
|
||||
a[name^="section"] {
|
||||
position: absolute;
|
||||
top: -2em;
|
||||
top: -4em;
|
||||
}
|
||||
}
|
||||
padding: 0.8em 0em;
|
||||
@ -98,5 +101,6 @@ export default {
|
||||
table {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
|
@ -0,0 +1,197 @@
|
||||
<template>
|
||||
<teleport to="#content_conainter .container.content .container">
|
||||
<div id="navmap">
|
||||
<nav>
|
||||
<a href="#top">
|
||||
<i class="fa fa-fw fa-square-o"></i>
|
||||
<span>{{ $t('nav.top') }}</span>
|
||||
</a>
|
||||
<item
|
||||
v-for="item of items"
|
||||
:key="item.key"
|
||||
:item="item"
|
||||
:step="step">
|
||||
</item>
|
||||
<!--a href="#bottom">
|
||||
<i class="fa fa-fw fa-square-o"></i>
|
||||
<span>{{ $t('nav.bottom') }}</span>
|
||||
</a-->
|
||||
</nav>
|
||||
</div>
|
||||
</teleport>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Item from './StickyNav/Item.vue';
|
||||
|
||||
export default {
|
||||
name: "StickyNav",
|
||||
components: {
|
||||
Item
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
header: document.querySelector("header.navigation.container"),
|
||||
bannerName: document.querySelector("#header-accompanying_course-name"),
|
||||
bannerDetails: document.querySelector("#header-accompanying_course-details"),
|
||||
container: null,
|
||||
sumBanner: null,
|
||||
stickyNav: null,
|
||||
limit: 25,
|
||||
anchors: null,
|
||||
items: [],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
accompanyingCourse() {
|
||||
return this.$store.state.accompanyingCourse;
|
||||
},
|
||||
step() {
|
||||
return this.accompanyingCourse.step;
|
||||
},
|
||||
top() {
|
||||
return parseInt(window.getComputedStyle(this.stickyNav).getPropertyValue('top').slice(0, -2));
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.ready();
|
||||
window.addEventListener('scroll', this.handleScroll);
|
||||
},
|
||||
destroyed() {
|
||||
window.removeEventListener('scroll', this.handleScroll);
|
||||
},
|
||||
methods: {
|
||||
ready() {
|
||||
|
||||
// when mounted ready
|
||||
this.container = document.querySelector("#content_conainter .container.content .container");
|
||||
this.stickyNav = document.querySelector('#navmap');
|
||||
this.sumBanner = this.header.offsetHeight + this.bannerName.offsetHeight + this.bannerDetails.offsetHeight;
|
||||
|
||||
this.anchorsMap();
|
||||
|
||||
const resizeObserver = new ResizeObserver(entries => {
|
||||
|
||||
// Listen to elements resize changes, then recalculate height sum and anchorsMap
|
||||
// TODO only FF/Chrome!
|
||||
|
||||
this.sumBanner = this.header.offsetHeight + this.bannerName.offsetHeight + this.bannerDetails.offsetHeight;
|
||||
//console.log('calculate sum', this.sumBanner);
|
||||
|
||||
this.items = [];
|
||||
this.anchorsMap();
|
||||
|
||||
});
|
||||
resizeObserver.observe(this.header);
|
||||
resizeObserver.observe(this.bannerName);
|
||||
resizeObserver.observe(this.bannerDetails);
|
||||
resizeObserver.observe(this.container);
|
||||
|
||||
},
|
||||
anchorsMap() {
|
||||
|
||||
this.anchors = document.querySelectorAll("h2 a[name^='section']");
|
||||
|
||||
this.anchors.forEach(anchor => {
|
||||
//console.log('anchor', anchor);
|
||||
this.items.push({
|
||||
pos: this.findPosition(anchor)['y'],
|
||||
active: false,
|
||||
key: parseInt(anchor.name.slice(8).slice(0, -1)),
|
||||
name: '#' + anchor.name
|
||||
})
|
||||
});
|
||||
//console.log('items', this.items);
|
||||
|
||||
},
|
||||
handleScroll(event) {
|
||||
|
||||
let pos = this.findPosition(this.stickyNav);
|
||||
//console.log(window.scrollY);
|
||||
//console.log('sum2', this.sumBanner);
|
||||
let top = this.sumBanner + this.top - window.scrollY;
|
||||
if (top > this.limit) {
|
||||
//console.log('absolute', pos['y']);
|
||||
this.stickyNav.style.position = 'absolute';
|
||||
this.stickyNav.style.left = '-60px';
|
||||
} else {
|
||||
//console.log('fixed', pos['y']);
|
||||
this.stickyNav.style.position = 'fixed';
|
||||
this.stickyNav.style.left = pos['x'] + 'px';
|
||||
}
|
||||
|
||||
this.switchActive();
|
||||
},
|
||||
findPosition(element) {
|
||||
|
||||
let posX = 0, posY = 0;
|
||||
do {
|
||||
posX += element.offsetLeft;
|
||||
posY += element.offsetTop;
|
||||
element = element.offsetParent;
|
||||
}
|
||||
while( element != null );
|
||||
|
||||
let pos = [];
|
||||
pos['x'] = posX;
|
||||
pos['y'] = posY;
|
||||
|
||||
return pos;
|
||||
},
|
||||
switchActive() {
|
||||
|
||||
this.items.forEach((item, i) => {
|
||||
let next = (this.items[i+1]) ? this.items[i+1].pos : '1000000';
|
||||
item.active =
|
||||
(window.scrollY >= item.pos & window.scrollY < next) ? true : false;
|
||||
}, this);
|
||||
|
||||
// last item never switch active because scroll reach bottom of page
|
||||
if (document.body.scrollHeight == window.scrollY + window.innerHeight) {
|
||||
this.items[this.items.length-1].active = true;
|
||||
this.items[this.items.length-2].active = false;
|
||||
} else {
|
||||
this.items[this.items.length-1].active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
div#navmap {
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
left: -60px; //-10%;
|
||||
nav {
|
||||
font-size: small;
|
||||
a {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: -3px;
|
||||
color: #71859669;
|
||||
span {
|
||||
display: none;
|
||||
}
|
||||
&:hover,
|
||||
&.active {
|
||||
span {
|
||||
display: inline;
|
||||
padding-left: 8px;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
color: #718596b5;
|
||||
}
|
||||
&.active {
|
||||
color: #df6a27; //#e2793d
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@media only screen and (max-width: 768px) {
|
||||
div.sticky-section {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<a
|
||||
v-if="item.key <= 5"
|
||||
:href="item.name"
|
||||
:class="{ 'active': isActive }"
|
||||
>
|
||||
<i class="fa fa-fw fa-square"></i>
|
||||
<span>{{ item.key }}</span>
|
||||
</a>
|
||||
<a
|
||||
v-else-if="step === 'DRAFT'"
|
||||
:href="item.name"
|
||||
:class="{ 'active': isActive }"
|
||||
>
|
||||
<i class="fa fa-fw fa-square"></i>
|
||||
<span>{{ item.key }}</span>
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "Item",
|
||||
props: ['item', 'step'],
|
||||
computed: {
|
||||
isActive() {
|
||||
return this.item.active;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
Loading…
x
Reference in New Issue
Block a user