mouvement et lock de la carte + prevent refresh homepage
This commit is contained in:
parent
6dad6cc7bc
commit
80f7f43370
|
@ -7,6 +7,7 @@ import VueImageZoomer from 'vue-image-zoomer'
|
|||
import 'vue-image-zoomer/dist/style.css';
|
||||
|
||||
import { useContentStore } from './stores/content';
|
||||
import { useMapStore } from './stores/mapState';
|
||||
import router from './router/router';
|
||||
|
||||
// Working with the history API
|
||||
|
@ -31,19 +32,13 @@ import router from './router/router';
|
|||
|
||||
(function ($, Drupal, drupalSettings) {
|
||||
const CaravaneTheme = function () {
|
||||
const _is_front = drupalSettings.path.isFront
|
||||
console.log('drupalSettings', drupalSettings)
|
||||
const _is_front = drupalSettings.path.isFront;
|
||||
console.log('drupalSettings', drupalSettings);
|
||||
|
||||
// let _I18n
|
||||
|
||||
// ___ _ _
|
||||
// |_ _|_ _ (_) |_
|
||||
// | || ' \| | _|
|
||||
// |___|_||_|_|\__|
|
||||
function init () {
|
||||
console.log('CaravaneTheme init()')
|
||||
initVues()
|
||||
toggleMenu()
|
||||
console.log('CaravaneTheme init()');
|
||||
initVues();
|
||||
toggleMenu();
|
||||
}
|
||||
|
||||
function initVues(){
|
||||
|
@ -55,45 +50,67 @@ import router from './router/router';
|
|||
.use(createPinia()).use(router)
|
||||
.use(VueImageZoomer);
|
||||
const store = useContentStore();
|
||||
const mapStore = useMapStore();
|
||||
app.mount('#content-modale');
|
||||
|
||||
processEtapeLinks(store);
|
||||
processStaticLinks(store);
|
||||
|
||||
setupEtapeMapPopup(store);
|
||||
Drupal.behaviors.customLeafletInteraction = {
|
||||
attach: function(context, settings) {
|
||||
$(context).on('leafletMapInit', function (e, settings, map, mapid, markers) {
|
||||
mapStore.defaultMapCenter = map.getCenter();
|
||||
mapStore.maxZoom = settings.settings.maxZoom;
|
||||
mapStore.defaultZoom = settings.settings.minZoom;
|
||||
|
||||
processEtapeLinks(store, map);
|
||||
processStaticLinks(store, map);
|
||||
processHeaderLogo(store, map);
|
||||
setupEtapeMapPopup(store, map);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function onClickContentLink(e, store, category){
|
||||
function onClickContentLink(e, store, map, category){
|
||||
e.preventDefault();
|
||||
let a;
|
||||
|
||||
const li = e.target.closest('li');
|
||||
a = li.querySelector('a');
|
||||
|
||||
if (e.target.tagName !== 'IMG') {
|
||||
const li = e.target.closest('li');
|
||||
a = li.querySelector('a');
|
||||
} else {
|
||||
a = e.target.closest('a');
|
||||
}
|
||||
|
||||
let nid = a.dataset.nodeNid;
|
||||
|
||||
if (category === 'etape') {
|
||||
store.fetchEtapeData(nid);
|
||||
store.fetchEtapeData(nid, map);
|
||||
} else if (category === 'static') {
|
||||
store.fetchStaticData(nid);
|
||||
if (nid) {
|
||||
store.fetchStaticData(nid, map);
|
||||
} else {
|
||||
store.emptyAll(null, map);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function processStaticLinks(store){
|
||||
let general_link_fields = document.querySelectorAll('#menu > ul > li:not(:first-of-type) > a');
|
||||
function processStaticLinks(store, map) {
|
||||
let general_link_fields = document.querySelectorAll('#menu > ul > li > a');
|
||||
for (let field of general_link_fields) {
|
||||
let general_link_href = field.getAttribute('href');
|
||||
const nid = general_link_href.charAt(general_link_href.length-1);
|
||||
field.setAttribute('data-node-nid', nid);
|
||||
field.addEventListener('click', (e) => onClickContentLink(e, store, 'static'));
|
||||
field.setAttribute('data-node-nid', parseInt(nid));
|
||||
field.addEventListener('click', (e) => onClickContentLink(e, store, map, 'static'));
|
||||
}
|
||||
}
|
||||
|
||||
function processHeaderLogo(store, map) {
|
||||
const logo = document.querySelector('#block-caravane-logocaravane a');
|
||||
logo.addEventListener('click', (e) => onClickContentLink(e, store, map, 'static'));
|
||||
}
|
||||
|
||||
function processEtapeLinks(store){
|
||||
function processEtapeLinks(store, map) {
|
||||
let etape_li = document.querySelectorAll('#etapes-liste li');
|
||||
etape_li.forEach((li) => {
|
||||
let field = li.querySelector('div.views-field-title');
|
||||
|
@ -110,7 +127,7 @@ import router from './router/router';
|
|||
if (nid) {
|
||||
let a = field.querySelector('a');
|
||||
a.setAttribute('data-node-nid', nid);
|
||||
li.addEventListener('click', (e) => onClickContentLink(e, store, 'etape'));
|
||||
li.addEventListener('click', (e) => onClickContentLink(e, store, map, 'etape'));
|
||||
}
|
||||
let couleur = li.querySelector('.views-field-field-couleur .snippets-description').innerText;
|
||||
let iconElements = li.querySelectorAll('.icone-arret > div');
|
||||
|
@ -145,52 +162,43 @@ import router from './router/router';
|
|||
})
|
||||
}
|
||||
|
||||
function setupEtapeMapPopup(store) {
|
||||
Drupal.behaviors.customLeafletInteraction = {
|
||||
attach: function(context, settings) {
|
||||
$(context).on('leafletMapInit', function (e, settings, map, mapid, markers) {
|
||||
const icons = document.querySelectorAll('.leaflet-map-divicon');
|
||||
for (let icon of icons) {
|
||||
const colorContainer = icon.querySelector('.couleur');
|
||||
let colorDivs = colorContainer.querySelectorAll('.separated-content');
|
||||
let color;
|
||||
colorDivs.forEach((div) => {
|
||||
if (div.innerText.startsWith('<div class="snippets-description">')) {
|
||||
color = div.innerText;
|
||||
}
|
||||
});
|
||||
color = color.substring(color.indexOf('>') + 1, color.indexOf('<', color.indexOf('>') + 1)).trim();
|
||||
function setupEtapeMapPopup(store, map) {
|
||||
const icons = document.querySelectorAll('.leaflet-map-divicon');
|
||||
for (let icon of icons) {
|
||||
const colorContainer = icon.querySelector('.couleur');
|
||||
let colorDivs = colorContainer.querySelectorAll('.separated-content');
|
||||
let color;
|
||||
colorDivs.forEach((div) => {
|
||||
if (div.innerText.startsWith('<div class="snippets-description">')) {
|
||||
color = div.innerText;
|
||||
}
|
||||
});
|
||||
color = color.substring(color.indexOf('>') + 1, color.indexOf('<', color.indexOf('>') + 1)).trim();
|
||||
|
||||
const nid = icon.querySelector('.nid');
|
||||
const nidValue = nid.querySelector('.separated-content').innerText;
|
||||
const nid = icon.querySelector('.nid');
|
||||
const nidValue = nid.querySelector('.separated-content').innerText;
|
||||
|
||||
icon.addEventListener('click', function(event) {
|
||||
store.fetchEtapeData(nidValue);
|
||||
});
|
||||
|
||||
//colorContainer.remove();
|
||||
//nid.remove();
|
||||
colorContainer.style.display = "none";
|
||||
nid.style.display = "none";
|
||||
const iconElements = icon.querySelectorAll('div');
|
||||
for (let iconElement of iconElements) {
|
||||
iconElement.style.backgroundColor = color;
|
||||
}
|
||||
icon.removeAttribute('title');
|
||||
|
||||
icon.addEventListener('mouseenter', function (event) {
|
||||
icon.style.transform = `${icon.style.transform} scale(1.1)`;
|
||||
const popup = document.querySelector('.leaflet-tooltip-center > div');
|
||||
popup.style.opacity = "1";
|
||||
});
|
||||
|
||||
icon.addEventListener('mouseleave', function (event) {
|
||||
icon.style.transform = icon.style.transform.split(' ')[0] + icon.style.transform.split(' ')[1] + icon.style.transform.split(' ')[2];
|
||||
})
|
||||
}
|
||||
|
||||
});
|
||||
icon.addEventListener('click', function(event) {
|
||||
store.fetchEtapeData(nidValue, map);
|
||||
});
|
||||
|
||||
colorContainer.style.display = "none";
|
||||
nid.style.display = "none";
|
||||
const iconElements = icon.querySelectorAll('div');
|
||||
for (let iconElement of iconElements) {
|
||||
iconElement.style.backgroundColor = color;
|
||||
}
|
||||
icon.removeAttribute('title');
|
||||
|
||||
icon.addEventListener('mouseenter', function (event) {
|
||||
icon.style.transform = `${icon.style.transform} scale(1.1)`;
|
||||
const popup = document.querySelector('.leaflet-tooltip-center > div');
|
||||
popup.style.opacity = "1";
|
||||
});
|
||||
|
||||
icon.addEventListener('mouseleave', function (event) {
|
||||
icon.style.transform = icon.style.transform.split(' ')[0] + icon.style.transform.split(' ')[1] + icon.style.transform.split(' ')[2];
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,9 +6,11 @@ import REST from '../api/rest-axios';
|
|||
export const useContentStore = defineStore('content', {
|
||||
state: () => ({
|
||||
href: '',
|
||||
map: {},
|
||||
etape: {
|
||||
title: '',
|
||||
adresse: {},
|
||||
coordinates: {},
|
||||
etape_number: '',
|
||||
vignette: {},
|
||||
couleur: '',
|
||||
|
@ -37,8 +39,9 @@ export const useContentStore = defineStore('content', {
|
|||
error: null,
|
||||
}),
|
||||
actions: {
|
||||
async fetchEtapeData(nid) {
|
||||
async fetchEtapeData(nid, map) {
|
||||
this.resetStore();
|
||||
this.map = map;
|
||||
try {
|
||||
const response = await REST.get(`/jsonapi/node/etape/`);
|
||||
for (let etape of response.data.data) {
|
||||
|
@ -48,6 +51,10 @@ export const useContentStore = defineStore('content', {
|
|||
this.href = metatag.attributes.href;
|
||||
}
|
||||
}
|
||||
this.etape.coordinates = {
|
||||
lat: etape.attributes.field_geofield.lat,
|
||||
lon: etape.attributes.field_geofield.lon,
|
||||
};
|
||||
this.etape.title = etape.attributes.title;
|
||||
this.etape.adresse = etape.attributes.field_adresse;
|
||||
this.etape.etape_number = etape.attributes.field_arret_numero;
|
||||
|
@ -205,8 +212,9 @@ export const useContentStore = defineStore('content', {
|
|||
this.loading = false;
|
||||
}
|
||||
},
|
||||
async fetchStaticData(nid) {
|
||||
async fetchStaticData(nid, map) {
|
||||
this.resetStore();
|
||||
this.map = map;
|
||||
try {
|
||||
const response = await REST.get(`/jsonapi/node/static/`);
|
||||
for (let page of response.data.data) {
|
||||
|
@ -285,7 +293,9 @@ export const useContentStore = defineStore('content', {
|
|||
}
|
||||
}
|
||||
},
|
||||
emptyAll(nid) {
|
||||
emptyAll(nid, map) {
|
||||
this.href = '';
|
||||
this.map = map;
|
||||
this.etape = {};
|
||||
this.page = {};
|
||||
this.setActiveItemInMenu(nid);
|
||||
|
@ -328,7 +338,6 @@ export const useContentStore = defineStore('content', {
|
|||
resetStore() {
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
this.href = '';
|
||||
this.etape = {};
|
||||
this.page = {};
|
||||
},
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
import { defineStore } from 'pinia';
|
||||
|
||||
export const useMapStore = defineStore('mapState', {
|
||||
state: () => ({
|
||||
defaultZoom: Number,
|
||||
defaultMapCenter: Object,
|
||||
currentPlace: Object,
|
||||
maxZoom: Number,
|
||||
currentZoom: Number,
|
||||
duration: 3,
|
||||
}),
|
||||
actions: {
|
||||
zoomToPlace(map, lat, long) {
|
||||
map.flyTo([lat, long], this.maxZoom, { duration: this.duration });
|
||||
this.currentZoom = this.maxZoom;
|
||||
},
|
||||
resetMap(map) {
|
||||
map.flyTo(this.defaultMapCenter, this.defaultZoom, { duration: this.duration });
|
||||
this.currentZoom = this.defaultZoom;
|
||||
},
|
||||
lockMap(map) {
|
||||
setTimeout(() => {
|
||||
map.options.minZoom = this.currentZoom;
|
||||
map.options.maxZoom = this.currentZoom;
|
||||
}, this.duration * 1000 + 100);
|
||||
map.dragging.disable();
|
||||
map.touchZoom.disable();
|
||||
map.doubleClickZoom.disable();
|
||||
map.scrollWheelZoom.disable();
|
||||
map.boxZoom.disable();
|
||||
map.keyboard.disable();
|
||||
// map.tap.disable();
|
||||
},
|
||||
unlockMap(map) {
|
||||
map.options.minZoom = this.defaultZoom;
|
||||
map.options.maxZoom = this.maxZoom;
|
||||
map.dragging.enable();
|
||||
map.touchZoom.enable();
|
||||
map.doubleClickZoom.enable();
|
||||
map.scrollWheelZoom.enable();
|
||||
map.boxZoom.enable();
|
||||
map.keyboard.enable();
|
||||
// map.tap.enable();
|
||||
},
|
||||
},
|
||||
});
|
|
@ -20,7 +20,8 @@
|
|||
:couleur="etape.couleur || brandColor" />
|
||||
<ModaleDiaporama
|
||||
v-if="partie.type === 'diaporama'"
|
||||
:partie="partie" />
|
||||
:partie="partie"
|
||||
:couleur="etape.couleur || brandColor" />
|
||||
<ModaleEntretien
|
||||
v-if="partie.type === 'entretien'"
|
||||
:partie="partie"
|
||||
|
@ -36,7 +37,8 @@
|
|||
</main>
|
||||
<ModaleFooter
|
||||
:content="etape || page"
|
||||
:couleur="etape.couleur || brandColor" />
|
||||
:couleur="etape.couleur || brandColor"
|
||||
:map="map" />
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
|
@ -46,6 +48,7 @@
|
|||
import { computed, watch, onMounted } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useContentStore } from '../stores/content';
|
||||
import { useMapStore } from '../stores/mapState';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
import ModaleHeader from './components/ModaleHeader.vue';
|
||||
|
@ -64,13 +67,17 @@ const { isObjectEmpty, scrollTop } = useUtils();
|
|||
|
||||
const router = useRouter();
|
||||
const store = useContentStore();
|
||||
const mapState = useMapStore();
|
||||
const route = useRoute();
|
||||
|
||||
const { loading, error, href, etape, page } = storeToRefs(store);
|
||||
const { loading, error, href, map, etape, page } = storeToRefs(store);
|
||||
const { duration } = storeToRefs(mapState);
|
||||
|
||||
const isEtapeValid = computed(() => !error.value && !loading.value && etape.value && !isObjectEmpty(etape.value));
|
||||
const isPageValid = computed(() => !error.value && !loading.value && page.value && !isObjectEmpty(page.value));
|
||||
|
||||
let isModaleEtape, wasModaleEtape;
|
||||
|
||||
const brandColor = "#80c8bf";
|
||||
|
||||
let isProgrammaticNavigation = false;
|
||||
|
@ -78,18 +85,18 @@ let isProgrammaticNavigation = false;
|
|||
const handleRouteChange = () => {
|
||||
watch(
|
||||
() => route.params.id,
|
||||
(newId) => {
|
||||
(newId) => {
|
||||
if (isProgrammaticNavigation) {
|
||||
isProgrammaticNavigation = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!newId) {
|
||||
store.emptyAll();
|
||||
store.emptyAll(map.value);
|
||||
} else {
|
||||
store.fetchEtapeData(newId);
|
||||
store.fetchEtapeData(newId, map.value);
|
||||
if (!etape.value?.data) {
|
||||
store.fetchStaticData(newId);
|
||||
store.fetchStaticData(newId, map.value);
|
||||
}
|
||||
scrollTop();
|
||||
}
|
||||
|
@ -112,36 +119,82 @@ const handleHrefChange = () => {
|
|||
() => href.value,
|
||||
(newHref) => {
|
||||
const relativePath = newHref.split('.fr')[1];
|
||||
if (relativePath && relativePath !== '' && relativePath !== '/') {
|
||||
isProgrammaticNavigation = true;
|
||||
router.push(relativePath);
|
||||
scrollTop();
|
||||
isProgrammaticNavigation = true;
|
||||
if (newHref == '') {
|
||||
router.push('/');
|
||||
mapState.unlockMap(map.value)
|
||||
} else {
|
||||
if (relativePath && relativePath !== '' && relativePath !== '/') {
|
||||
mapState.lockMap(map.value);
|
||||
router.push(relativePath);
|
||||
scrollTop();
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const handleMapMovement = () => {
|
||||
watch(
|
||||
() => href.value,
|
||||
() => {
|
||||
console.log("NEW HREF");
|
||||
console.log(href.value);
|
||||
|
||||
isModaleEtape = !isObjectEmpty(etape.value);
|
||||
|
||||
console.log("CAS 1", !wasModaleEtape && isModaleEtape);
|
||||
console.log("CAS 2", wasModaleEtape && isModaleEtape);
|
||||
console.log("CAS 3", wasModaleEtape && !isModaleEtape);
|
||||
|
||||
if (!wasModaleEtape && isModaleEtape) {
|
||||
document.documentElement.style.setProperty('--modale-enter-delay', `${duration.value}s`);
|
||||
mapState.zoomToPlace(map.value, etape.value.coordinates.lat, etape.value.coordinates.lon);
|
||||
|
||||
} else if (wasModaleEtape && isModaleEtape) {
|
||||
document.documentElement.style.setProperty('--modale-leave-delay', 0);
|
||||
document.documentElement.style.setProperty('--modale-enter-delay', `${duration.value * 2}s`);
|
||||
mapState.resetMap(map.value);
|
||||
setTimeout(() => {
|
||||
mapState.zoomToPlace(map.value, etape.value.coordinates.lat, etape.value.coordinates.lon);
|
||||
}, duration.value * 1000);
|
||||
|
||||
} else if (wasModaleEtape && !isModaleEtape) {
|
||||
document.documentElement.style.setProperty('--modale-leave-delay', 0);
|
||||
mapState.resetMap(map.value);
|
||||
}
|
||||
|
||||
wasModaleEtape = isModaleEtape;
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
isModaleEtape = !isObjectEmpty(etape.value);
|
||||
wasModaleEtape = isModaleEtape;
|
||||
handleRouteChange();
|
||||
handleColorChange();
|
||||
handleHrefChange();
|
||||
handleMapMovement();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scss>
|
||||
.v-enter-active,
|
||||
.v-enter-active {
|
||||
transition: all 0.5s linear var(--modale-enter-delay);
|
||||
}
|
||||
|
||||
.v-leave-active {
|
||||
transition: all 0.3s ease;
|
||||
transition: all 0.5s linear var(--modale-leave-delay);
|
||||
}
|
||||
|
||||
.v-enter-from,
|
||||
.v-leave-to {
|
||||
transform: translateY(100%);
|
||||
opacity: 0;
|
||||
transform: translateY(20vh);
|
||||
}
|
||||
|
||||
.v-enter-to,
|
||||
.v-leave-from {
|
||||
transform: translateY(0%);
|
||||
opacity: 1;
|
||||
transform: translateY(0vh);
|
||||
}
|
||||
</style>
|
|
@ -5,7 +5,7 @@
|
|||
</div>
|
||||
|
||||
<div v-if="content.previous || content.next" class="related-etape-links">
|
||||
<div v-if="content.previous" class="card previous" @click="store.fetchEtapeData(content.previous.nid)">
|
||||
<div v-if="content.previous" class="card previous" @click="store.fetchEtapeData(content.previous.nid, map)">
|
||||
<div class="icon">
|
||||
<div :style="{ backgroundColor: content.previous.couleur }"></div>
|
||||
<div :style="{ backgroundColor: content.previous.couleur }"></div>
|
||||
|
@ -21,7 +21,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="content.next" class="card next" @click="store.fetchEtapeData(content.next.nid)">
|
||||
<div v-if="content.next" class="card next" @click="store.fetchEtapeData(content.next.nid, map)">
|
||||
<div class="icon">
|
||||
<div :style="{ backgroundColor: content.next.couleur }"></div>
|
||||
<div :style="{ backgroundColor: content.next.couleur }"></div>
|
||||
|
@ -50,5 +50,6 @@ const store = useContentStore();
|
|||
const props = defineProps({
|
||||
content: Object,
|
||||
couleur: String,
|
||||
map: Object,
|
||||
});
|
||||
</script>
|
|
@ -26,6 +26,7 @@
|
|||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted } from 'vue';
|
||||
import { useImageModal } from '../../composables/useImageModale';
|
||||
import ImageModale from '../ImageModale.vue';
|
||||
// WebComponent
|
||||
|
@ -34,7 +35,8 @@ import { register } from 'swiper/element/bundle';
|
|||
register();
|
||||
|
||||
const props = defineProps({
|
||||
partie: Object
|
||||
partie: Object,
|
||||
couleur: String,
|
||||
});
|
||||
|
||||
const {
|
||||
|
@ -62,6 +64,10 @@ const handleImageClick = (event) => {
|
|||
openImageModale(img.src, img.alt, swiperMedia);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
document.documentElement.style.setProperty('--etape-couleur', props.couleur);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
<template>
|
||||
<div class="videos">
|
||||
<iframe v-for="video in partie.videos" :src="video" frameborder="0" width="100%" style="aspect-ratio: 16 / 9;"></iframe>
|
||||
<iframe
|
||||
v-for="video in partie.videos"
|
||||
:src="video"
|
||||
frameborder="0"
|
||||
width="100%"
|
||||
style="aspect-ratio: 16 / 9;">
|
||||
</iframe>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
Loading…
Reference in New Issue