mouvement et lock de la carte + prevent refresh homepage

This commit is contained in:
Valentin 2024-10-07 23:39:50 +02:00
parent 6dad6cc7bc
commit 80f7f43370
7 changed files with 225 additions and 96 deletions

View File

@ -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);
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;
setupEtapeMapPopup(store);
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;
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,10 +162,7 @@ import router from './router/router';
})
}
function setupEtapeMapPopup(store) {
Drupal.behaviors.customLeafletInteraction = {
attach: function(context, settings) {
$(context).on('leafletMapInit', function (e, settings, map, mapid, markers) {
function setupEtapeMapPopup(store, map) {
const icons = document.querySelectorAll('.leaflet-map-divicon');
for (let icon of icons) {
const colorContainer = icon.querySelector('.couleur');
@ -165,11 +179,9 @@ import router from './router/router';
const nidValue = nid.querySelector('.separated-content').innerText;
icon.addEventListener('click', function(event) {
store.fetchEtapeData(nidValue);
store.fetchEtapeData(nidValue, map);
});
//colorContainer.remove();
//nid.remove();
colorContainer.style.display = "none";
nid.style.display = "none";
const iconElements = icon.querySelectorAll('div');
@ -188,10 +200,6 @@ import router from './router/router';
icon.style.transform = icon.style.transform.split(' ')[0] + icon.style.transform.split(' ')[1] + icon.style.transform.split(' ')[2];
})
}
});
}
}
}
init()

View File

@ -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 = {};
},

View File

@ -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();
},
},
});

View File

@ -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;
@ -85,11 +92,11 @@ const handleRouteChange = () => {
}
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;
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>

View File

@ -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>

View File

@ -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>

View File

@ -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>