refactor du système de routing (EXPORTS DES SETTINGS DRUPAL)
This commit is contained in:
		
							
								
								
									
										18
									
								
								config/sync/rest.resource.entity.path_alias.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								config/sync/rest.resource.entity.path_alias.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| uuid: f904b753-75c5-477e-bd9c-7259f9bfdf9b | ||||
| langcode: fr | ||||
| status: false | ||||
| dependencies: | ||||
|   module: | ||||
|     - path_alias | ||||
|     - serialization | ||||
|     - user | ||||
| id: entity.path_alias | ||||
| plugin_id: 'entity:path_alias' | ||||
| granularity: resource | ||||
| configuration: | ||||
|   methods: | ||||
|     - GET | ||||
|   formats: | ||||
|     - json | ||||
|   authentication: | ||||
|     - cookie | ||||
| @@ -269,7 +269,7 @@ display: | ||||
|             popupAnchor: | ||||
|               x: '' | ||||
|               'y': '' | ||||
|             html: "<div></div>\r\n<div></div>\r\n<div></div>\r\n<div class=\"nid\">{{ nid }}</div>\r\n<div class=\"couleur\">{{ field_couleur }}</div>" | ||||
|             html: "<div></div>\r\n<div></div>\r\n<div></div>\r\n<div class=\"url\">[node:url]</div>\r\n<div class=\"couleur\">[node:field_couleur]</div>" | ||||
|             html_class: 'leaflet-map-divicon ' | ||||
|             circle_marker_options: '{"radius":100,"color":"red","fillColor":"#f03","fillOpacity":0.5}' | ||||
|           leaflet_markercluster: | ||||
|   | ||||
| @@ -1,253 +1,49 @@ | ||||
| import { createApp } from 'vue' | ||||
| import { createPinia } from 'pinia' | ||||
| import { initVueContentModale } from './utils/vue-setup'; | ||||
| import { processClickableElements } from './utils/process-clickable-elements'; | ||||
| import { setMenuToggle, setHamburgerWhenLogged } from './utils/layout-setup'; | ||||
| import { initFirstLoadRouting, handleClickableElements } from './utils/handle-navigation'; | ||||
| import { setupMapStore } from './utils/map-setup'; | ||||
|  | ||||
| import '../scss/main.scss' | ||||
| import Modale from './vuejs/Modale.vue' | ||||
|  | ||||
| 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 | ||||
| // https://developer.mozilla.org/en-US/docs/Web/API/History_API/Working_with_the_History_API | ||||
|  | ||||
| // /** | ||||
| //  * @file | ||||
| //  * reha behaviors. | ||||
| //  * https://www.drupal.org/docs/drupal-apis/javascript-api/javascript-api-overview | ||||
| //  */ | ||||
| // (function (Drupal) { | ||||
|  | ||||
| //   'use strict'; | ||||
|  | ||||
| //   Drupal.behaviors.reha = { | ||||
| //     attach: function (context, settings) { | ||||
| //       console.log('It works!'); | ||||
| //     } | ||||
| //   }; | ||||
|  | ||||
| // } (Drupal)); | ||||
| // https://www.drupal.org/docs/drupal-apis/javascript-api/javascript-api-overview | ||||
|  | ||||
| (function ($, Drupal, drupalSettings) { | ||||
|   const CaravaneTheme = function () { | ||||
|     const _is_front = drupalSettings.path.isFront; | ||||
|     console.log('drupalSettings', drupalSettings); | ||||
|     const CaravaneTheme = function () { | ||||
|         function init () { | ||||
|             console.log('DrupalSettings', drupalSettings);             | ||||
|  | ||||
|     function init () { | ||||
|       console.log('CaravaneTheme init()'); | ||||
|       initVues(); | ||||
|       toggleMenu(); | ||||
|     } | ||||
|             const baseUrl = window.location.protocol + "//" + window.location.host; | ||||
|             const siteName = document.querySelector('#site_name').innerText; | ||||
|             const { store, mapStore, router, route } = initVueContentModale();             | ||||
|  | ||||
|     function initVues(){ | ||||
|       initVueContentModale(); | ||||
|             setMenuToggle(); | ||||
|             setHamburgerWhenLogged(drupalSettings); | ||||
|  | ||||
|     } | ||||
|             // https://www.drupal.org/docs/extending-drupal/contributed-modules/contributed-module-documentation/leaflet/leaflet-api | ||||
|  | ||||
|     function initVueContentModale(){ | ||||
|       const app = createApp(Modale) | ||||
|         .use(createPinia()).use(router) | ||||
|         .use(VueImageZoomer); | ||||
|       const store = useContentStore(); | ||||
|       const mapStore = useMapStore(); | ||||
|       app.mount('#content-modale'); | ||||
|             Drupal.behaviors.customLeafletInteraction = { | ||||
|                 attach: function(context, settings) { | ||||
|                     $(context).on('leafletMapInit', function (e, settings, map, mapid, markers) {  | ||||
|                         const {  | ||||
|                             etapeListLinks, | ||||
|                             generalListLinks, | ||||
|                             logoLink, | ||||
|                             mapIcons, | ||||
|                         } = processClickableElements(); | ||||
|                         const clickableElements = [...etapeListLinks, ...generalListLinks, logoLink, ...mapIcons]; | ||||
|  | ||||
|       setHamburgerWhenLogged(); | ||||
|                         setupMapStore(mapStore, map, settings); | ||||
|  | ||||
|       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; | ||||
|                         initFirstLoadRouting(store, router, baseUrl, siteName); | ||||
|  | ||||
|  | ||||
|             initFirstLoadRouting(store, map); | ||||
|             processEtapeLinks(store, map); | ||||
|             processStaticLinks(store, map); | ||||
|             processHeaderLogo(store, map); | ||||
|             setupEtapeMapPopup(store, map); | ||||
|           }); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     function initFirstLoadRouting(store, map){ | ||||
|       var decoupled_origin = JSON.parse(window.localStorage.getItem('decoupled_origin')); | ||||
|       console.log('decoupled_origin', decoupled_origin); | ||||
|  | ||||
|  | ||||
|       if(decoupled_origin && decoupled_origin.entity_id){ | ||||
|         // Si c'était moi je ne ferais qu'une seule function fetchdata capable de dealer avec les différent type de contenus | ||||
|         switch (decoupled_origin.entity_bundle) { | ||||
|           case 'etape': | ||||
|             store.fetchEtapeData(decoupled_origin.entity_id, map); | ||||
|             break; | ||||
|           case 'static': | ||||
|             store.fetchEtapeData(decoupled_origin.entity_id, map); | ||||
|             break; | ||||
|                         handleClickableElements(clickableElements, store, router, baseUrl, siteName, mapStore); | ||||
|                     }); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         router.push({ | ||||
|           // name: decoupled_origin.entity_bundle, | ||||
|           path: decoupled_origin.url, | ||||
|           // params: { | ||||
|           //   title: decoupled_origin.entity_uuid | ||||
|           // }, | ||||
|           // props: { | ||||
|           //   nid: decoupled_origin.entity_id | ||||
|           // } | ||||
|         }); | ||||
|  | ||||
|         // reset the storage | ||||
|         window.localStorage.removeItem("decoupled_origin"); | ||||
|       } | ||||
|         init() | ||||
|     } | ||||
|  | ||||
|     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.nid; | ||||
|  | ||||
|       if (category === 'etape') { | ||||
|         store.fetchEtapeData(nid, map); | ||||
|       } else if (category === 'static') { | ||||
|         if (nid) { | ||||
|           store.fetchStaticData(nid, map); | ||||
|         } else { | ||||
|           store.emptyAll(null, map); | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       return null; | ||||
|     } | ||||
|  | ||||
|     function processStaticLinks(store, map) { | ||||
|       let general_link_fields = document.querySelectorAll('#menu > ul > li > a'); | ||||
|       for (let i =1; i <  general_link_fields.length; i ++) { | ||||
|         let general_link_path = general_link_fields[i].getAttribute('data-drupal-link-system-path'); | ||||
|         const match = [...general_link_path.match(/^node\/(\d+)$/)]; | ||||
|         if (match) { | ||||
|           const nid = match[1]; | ||||
|           general_link_fields[i].setAttribute('data-nid', parseInt(nid)); | ||||
|           general_link_fields[i].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, map) { | ||||
|       let etape_li = document.querySelectorAll('#etapes-liste li'); | ||||
|       etape_li.forEach((li) => { | ||||
|         let etape_link = li.querySelector('a.etape-link'); | ||||
|         let nid = etape_link.dataset.nid; | ||||
|          | ||||
|         if (nid) { | ||||
|           li.addEventListener('click', (e) => onClickContentLink(e, store, map, 'etape')); | ||||
|         } | ||||
|         let couleur = etape_link.dataset.couleur; | ||||
|         let iconElements = li.querySelectorAll('.icone-arret > div'); | ||||
|         for (let element of iconElements) { | ||||
|           element.style.backgroundColor = couleur; | ||||
|         } | ||||
|  | ||||
|       }) | ||||
|     } | ||||
|  | ||||
|     function toggleMenu() { | ||||
|       const menuButton = document.querySelector('#block-caravane-mainnavigation > #menu'); | ||||
|       const menuContainer = document.querySelector('#block-caravane-mainnavigation > #menu > ul'); | ||||
|       const menuTitle = document.querySelector('#menu-title'); | ||||
|       const menuBurger = document.querySelector('#hamburger'); | ||||
|       const menuH2 = document.querySelector('#menu > h2'); | ||||
|       menuButton.addEventListener('click', (e) => {  | ||||
|         setTimeout(() => { | ||||
|           menuContainer.classList.toggle('open'); | ||||
|           menuTitle.classList.toggle('open'); | ||||
|           menuBurger.classList.toggle('open'); | ||||
|           menuH2.classList.toggle('open'); | ||||
|         }, 50); | ||||
|       }) | ||||
|       document.addEventListener('click', (e) => { | ||||
|         if (!menuContainer.contains(e.target) && !menuBurger.contains(e.target)) { | ||||
|           menuContainer.classList.remove('open'); | ||||
|           menuTitle.classList.remove('open'); | ||||
|           menuBurger.classList.remove('open'); | ||||
|           menuH2.classList.remove('open');   | ||||
|         } | ||||
|       }) | ||||
|     } | ||||
|  | ||||
|     function setHamburgerWhenLogged() { | ||||
|       if (drupalSettings.user.uid != 0) { | ||||
|         const menuBurger = document.querySelector('#hamburger'); | ||||
|         const menuTitle = document.querySelector('#menu-title'); | ||||
|         const menuContainer = document.querySelector('#block-caravane-mainnavigation > #menu > ul'); | ||||
|         const header = document.querySelector('.dialog-off-canvas-main-canvas'); | ||||
|         const headerTop = header.getBoundingClientRect().top; | ||||
|  | ||||
|         menuTitle.style.top = `${headerTop}px`;         | ||||
|         menuBurger.style.top = `${headerTop}px`; | ||||
|         menuContainer.style.paddingTop = `${headerTop}px`; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     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; | ||||
|  | ||||
|         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]; | ||||
|         }) | ||||
|       } | ||||
|     } | ||||
|      | ||||
|     init() | ||||
|   } // end CaravaneTheme() | ||||
|  | ||||
|   CaravaneTheme() | ||||
|     CaravaneTheme() | ||||
| })(jQuery, Drupal, drupalSettings) | ||||
| @@ -6,6 +6,7 @@ if(drupalDecoupled.redirect){ | ||||
|   console.log('window.location', window.location); | ||||
|  | ||||
|   drupalDecoupled.sys_path = drupalDecoupled.sys_path.replace(/^\//, ''); | ||||
|    | ||||
|   drupalDecoupled.url = window.location.pathname; | ||||
|   drupalDecoupled.hash = window.location.hash; | ||||
|   window.localStorage.setItem('decoupled_origin', JSON.stringify(drupalDecoupled)); | ||||
|   | ||||
| @@ -2,17 +2,23 @@ import { createRouter, createWebHistory } from 'vue-router'; | ||||
| import ModaleView from '../vuejs/Modale.vue'; | ||||
|  | ||||
| const routes = [ | ||||
|     {  | ||||
| /*     {  | ||||
|       name: 'etape', | ||||
|       path: '/etapes/:title?', | ||||
|       component: ModaleView, | ||||
|       props: {id: null} | ||||
|       props: {id: null}, | ||||
|     }, | ||||
|     {  | ||||
|       name: 'home', | ||||
|       path: '/', | ||||
|       component: ModaleView | ||||
|       component: ModaleView, | ||||
|     }, | ||||
|  */ | ||||
|  // Not much to do here nah ? | ||||
|     { | ||||
|       path: '/:catchAll(.*)', | ||||
|       component: ModaleView, | ||||
|     } | ||||
|   ]; | ||||
|    | ||||
|   const router = createRouter({ | ||||
|   | ||||
| @@ -5,33 +5,17 @@ import REST from '../api/rest-axios'; | ||||
|  | ||||
| export const useContentStore = defineStore('content', { | ||||
|     state: () => ({ | ||||
|         href: '', | ||||
|         map: {}, | ||||
|         etape: { | ||||
|             title: '', | ||||
|             adresse: {}, | ||||
|         contentType: '', | ||||
|         pageTitle: '', | ||||
|         content: { | ||||
|             contentTitle: '', | ||||
|             coordinates: {}, | ||||
|             adresse: {}, | ||||
|             etape_number: '', | ||||
|             vignette: {}, | ||||
|             couleur: '', | ||||
|             previous: {}, | ||||
|             dates: {}, | ||||
|             previous : {}, | ||||
|             next: {}, | ||||
|             dates: { | ||||
|                 start: { | ||||
|                     d: '', | ||||
|                     m: '', | ||||
|                     y: '', | ||||
|                 }, | ||||
|                 end: { | ||||
|                     d: '', | ||||
|                     m: '', | ||||
|                     y: '', | ||||
|                 }, | ||||
|             }, | ||||
|             parties: [], | ||||
|         }, | ||||
|         page: { | ||||
|             title: '', | ||||
|             vignette: {}, | ||||
|             parties: [], | ||||
|         }, | ||||
| @@ -39,174 +23,159 @@ export const useContentStore = defineStore('content', { | ||||
|         error: null, | ||||
|     }), | ||||
|     actions: { | ||||
|         async fetchEtapeData(nid, map) { | ||||
|             this.resetStore(); | ||||
|             this.map = map; | ||||
|         async fetchContentData(path) { | ||||
|             this.resetStore(false); | ||||
|             const contentTypes = [ 'etape', 'static' ]; | ||||
|             try { | ||||
|                 const response = await REST.get(`/jsonapi/node/etape/`); | ||||
|                 for (let etape of response.data.data) { | ||||
|                     if (etape.attributes.drupal_internal__nid == nid) { | ||||
|                         for (let metatag of etape.attributes.metatag) { | ||||
|                           if (metatag.tag === "link") { | ||||
|                             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; | ||||
|                         const vignetteFetch = await this.fetchContent('field_vignette', etape.relationships); | ||||
|                         if (vignetteFetch) { | ||||
|                             this.etape.vignette = {  | ||||
|                                 url: vignetteFetch.attributes.uri.url, | ||||
|                                 alt: etape.relationships.field_vignette.data.meta.alt | ||||
|                             }; | ||||
|                         } | ||||
|                         this.etape.couleur = etape.attributes.field_couleur; | ||||
|                 let rawContent,  | ||||
|                     contentType, | ||||
|                     response; | ||||
|  | ||||
|                         this.etape.dates = { | ||||
|                           start: this.getCleanDate(etape.attributes.field_dates.value), | ||||
|                           end: this.getCleanDate(etape.attributes.field_dates.end_value), | ||||
|                         } | ||||
|  | ||||
|                         const partiesFetch = await this.fetchContent('field_parties', etape.relationships); | ||||
|                         if (partiesFetch) { | ||||
|                             this.etape.parties = []; | ||||
|                             for (let partie of partiesFetch) { | ||||
|                                 const partieType = partie.type.replace(/^paragraph--/, ""); | ||||
|                                 let partieContent = { | ||||
|                                     type: partieType, | ||||
|                                 }; | ||||
|                                  | ||||
|                                 switch (partieType) { | ||||
|                                     case 'carte_sensible': | ||||
|                                         const carteSensibleFetch = await this.fetchContent('field_image_carte', partie.relationships); | ||||
|                                         if (carteSensibleFetch) { | ||||
|                                             partieContent.carteSensible = { | ||||
|                                                 url: carteSensibleFetch.attributes.uri.url, | ||||
|                                                 alt: partie.relationships.field_image_carte.data.meta.alt, | ||||
|                                             }; | ||||
|                                         } | ||||
|                                         break; | ||||
|                                     case 'titre_texte': | ||||
|                                         partieContent.titre = partie.attributes.field_titre; | ||||
|                                         partieContent.texte = partie.attributes.field_texte.value;                                         | ||||
|                                         break; | ||||
|                                     case 'chiffres_cles': | ||||
|                                         const chiffresClesFetch = await this.fetchContent('field_chiffres_clefs', partie.relationships); | ||||
|                                         if (chiffresClesFetch) { | ||||
|                                             partieContent.chiffresCles = []; | ||||
|                                             for (let chiffre of chiffresClesFetch) { | ||||
|                                                 partieContent.chiffresCles.push({ | ||||
|                                                     chiffre: chiffre.attributes.field_chiffre, | ||||
|                                                     description: chiffre.attributes.field_description, | ||||
|                                                 }); | ||||
|                                             } | ||||
|                                         } | ||||
|                                         break; | ||||
|                                     case 'diaporama': | ||||
|                                         const diaporamaFetch = await this.fetchContent('field_diaporama', partie.relationships); | ||||
|                                         if (diaporamaFetch) { | ||||
|                                             partieContent.diaporama = []; | ||||
|                                             for (let [index, image] of diaporamaFetch.entries()) { | ||||
|                                                 partieContent.diaporama.push({ | ||||
|                                                     url: image.attributes.uri.url, | ||||
|                                                     alt: partie.relationships.field_diaporama.data[index].meta.alt, | ||||
|                                                 }); | ||||
|                                             } | ||||
|                                         } | ||||
|                                         break; | ||||
|                                     case 'entretien': | ||||
|                                         partieContent.entretien = {}; | ||||
|                                         const personnesFetch = await this.fetchContent('field_personne_s', partie.relationships); | ||||
|                                         const questionsReponsesFetch = await this.fetchContent('field_questions_reponses', partie.relationships); | ||||
|                                         if (personnesFetch && questionsReponsesFetch) { | ||||
|                                             partieContent.entretien.personnes = []; | ||||
|                                             for (let personne of personnesFetch) { | ||||
|                                                 const portraitFetch = await this.fetchContent('field_portrait', personne.relationships); | ||||
|                                                 if (portraitFetch) {                                                     | ||||
|                                                     partieContent.entretien.personnes.push({ | ||||
|                                                         portrait: portraitFetch.attributes.uri.url, | ||||
|                                                         alt: personne.relationships.field_portrait.data.meta.alt, | ||||
|                                                         description: personne.attributes.field_description, | ||||
|                                                     }); | ||||
|                                                 } | ||||
|                                             } | ||||
|                                             partieContent.entretien.questionsReponses = []; | ||||
|                                             for (let qr of questionsReponsesFetch) { | ||||
|                                                 partieContent.entretien.questionsReponses.push({ | ||||
|                                                     question: qr.attributes.field_question, | ||||
|                                                     reponse: qr.attributes.field_reponse.value, | ||||
|                                                 }); | ||||
|                                             }                                            | ||||
|                                         } | ||||
|                                         break; | ||||
|                                     case 'exergue': | ||||
|                                         partieContent.exergue = partie.attributes.field_texte_exergue.value; | ||||
|                                         break; | ||||
|                                     case 'video': | ||||
|                                         partieContent.videos = []; | ||||
|                                         for (let video of partie.attributes.field_videos) { | ||||
|                                             const videoId = video.split('?v=')[1]; | ||||
|                                             const videoUrl = `https://www.youtube.com/embed/${videoId}`; | ||||
|                                             partieContent.videos.push(videoUrl);                       | ||||
|                                         } | ||||
|                                         break; | ||||
|                                 } | ||||
|                                 this.etape.parties.push(partieContent); | ||||
|                             } | ||||
|                         } | ||||
|                          | ||||
|                         // get previous and next étape infos | ||||
|                         // the list from the json api /node is not ordered | ||||
|                         // and i need authentification to get the json view ordered list | ||||
|                         // so i get it from the displayed list in the page | ||||
|                         const orderedEtapesList = document.querySelectorAll('#etapes-liste li'); | ||||
|  | ||||
|                         if (orderedEtapesList) {                             | ||||
|                             const processEtape = async (etapeItemNid, etapesList, key) => { | ||||
|                                 for (let etape of etapesList) { | ||||
|                                     if (etape.attributes.drupal_internal__nid == etapeItemNid) { | ||||
|                                         const vignetteFetch = await REST.get(etape.relationships.field_vignette.links.related.href); | ||||
|                                         this.etape[key] = { | ||||
|                                             nid: etape.attributes.drupal_internal__nid, | ||||
|                                             couleur: etape.attributes.field_couleur, | ||||
|                                             title: etape.attributes.title, | ||||
|                                             postalCode: etape.attributes.field_adresse.postal_code, | ||||
|                                             dates: { | ||||
|                                                 start: this.getCleanDate(etape.attributes.field_dates.value), | ||||
|                                                 end: this.getCleanDate(etape.attributes.field_dates.end_value), | ||||
|                                             }, | ||||
|                                             vignette: { | ||||
|                                                 url: vignetteFetch.data.data.attributes.uri.url, | ||||
|                                                 alt: etape.relationships.field_vignette.data.meta.alt, | ||||
|                                             }, | ||||
|                                         }; | ||||
|                                         break; | ||||
|                                     } | ||||
|                                 } | ||||
|                             }; | ||||
|                              | ||||
|                             for (let li of orderedEtapesList) { | ||||
|                                 if (li.querySelector('a').dataset.nodeNid == nid) {                                     | ||||
|                                     const previousEtapeItemNid = li.previousElementSibling?.querySelector('a').dataset.nodeNid; | ||||
|                                     const nextEtapeItemNid = li.nextElementSibling?.querySelector('a').dataset.nodeNid; | ||||
|                                     if (previousEtapeItemNid) { | ||||
|                                         await processEtape(previousEtapeItemNid, response.data.data, 'previous'); | ||||
|                                     } | ||||
|                                     if (nextEtapeItemNid) { | ||||
|                                         await processEtape(nextEtapeItemNid, response.data.data, 'next'); | ||||
|                                     } | ||||
|                                 } | ||||
|                 contentTypesLoop: | ||||
|                 for (let type of contentTypes) { | ||||
|                     response = await REST.get(`/jsonapi/node/${type}/`); | ||||
|                     for (let content of response.data.data) { | ||||
|                         for (let tag of content.attributes.metatag) { | ||||
|                             if (tag.tag === "link" && tag.attributes.href === path) { | ||||
|                                 this.contentType = type; | ||||
|                                 rawContent = content; | ||||
|                                 contentType = type; | ||||
|                                 break contentTypesLoop; | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 // pageTitle | ||||
|                 for (let tag of rawContent.attributes.metatag) { | ||||
|                     if (tag.tag === "meta") { | ||||
|                         this.pageTitle = tag.attributes.content; | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|                 this.setActiveItemInMenu(nid); | ||||
|                 // contentTitle | ||||
|                 this.content.contentTitle = rawContent.attributes.title; | ||||
|  | ||||
|                 // vignette | ||||
|                 const vignetteFetch = await this.fetchDeeperContent('field_vignette', rawContent.relationships); | ||||
|                 if (vignetteFetch) { | ||||
|                     this.content.vignette = {  | ||||
|                         url: vignetteFetch.attributes.uri.url, | ||||
|                         alt: rawContent.relationships.field_vignette.data.meta.alt | ||||
|                     }; | ||||
|                 }                 | ||||
|  | ||||
|                 if (contentType === 'etape') { | ||||
|                     // coordinates | ||||
|                     this.content.coordinates = { | ||||
|                         lat: rawContent.attributes.field_geofield.lat, | ||||
|                         lon: rawContent.attributes.field_geofield.lon, | ||||
|                     }; | ||||
|                     // adresse | ||||
|                     this.content.adresse = rawContent.attributes.field_adresse; | ||||
|                     // étape number | ||||
|                     this.content.etape_number = rawContent.attributes.field_arret_numero; | ||||
|                     // couleur | ||||
|                     this.content.couleur = rawContent.attributes.field_couleur; | ||||
|                     // dates | ||||
|                     this.content.dates = { | ||||
|                         start: this.getCleanDate(rawContent.attributes.field_dates.value), | ||||
|                         end: this.getCleanDate(rawContent.attributes.field_dates.end_value), | ||||
|                     } | ||||
|                     // previous / next | ||||
|                     await this.getRelatedEtape('previous', response.data.data, path); | ||||
|                     await this.getRelatedEtape('next', response.data.data, path); | ||||
|                 }                 | ||||
|  | ||||
|                 // parties | ||||
|                 const fieldParties = contentType === 'etape' ? 'field_parties' : 'field_parties_static'; | ||||
|                 const partiesFetch = await this.fetchDeeperContent(fieldParties, rawContent.relationships); | ||||
|                  | ||||
|                 if (partiesFetch) { | ||||
|                     this.content.parties = []; | ||||
|                     for (let partie of partiesFetch) { | ||||
|                         const partieType = partie.type.replace(/^paragraph--/, ""); | ||||
|                         let partieContent = { | ||||
|                             type: partieType, | ||||
|                         }; | ||||
|  | ||||
|                         switch (partieType) { | ||||
|                             case 'carte_sensible': | ||||
|                                 const carteSensibleFetch = await this.fetchDeeperContent('field_image_carte', partie.relationships);                                 | ||||
|                                 if (carteSensibleFetch) { | ||||
|                                     partieContent.carteSensible = { | ||||
|                                         url: carteSensibleFetch.attributes.uri.url, | ||||
|                                         alt: partie.relationships.field_image_carte.data.meta.alt, | ||||
|                                     }; | ||||
|                                 } | ||||
|                                 break; | ||||
|                             case 'titre_texte': | ||||
|                                 partieContent.titre = partie.attributes.field_titre; | ||||
|                                 partieContent.texte = partie.attributes.field_texte.value;                                         | ||||
|                                 break; | ||||
|                             case 'chiffres_cles': | ||||
|                                 const chiffresClesFetch = await this.fetchDeeperContent('field_chiffres_clefs', partie.relationships); | ||||
|                                 if (chiffresClesFetch) { | ||||
|                                     partieContent.chiffresCles = []; | ||||
|                                     for (let chiffre of chiffresClesFetch) { | ||||
|                                         partieContent.chiffresCles.push({ | ||||
|                                             chiffre: chiffre.attributes.field_chiffre, | ||||
|                                             description: chiffre.attributes.field_description, | ||||
|                                         }); | ||||
|                                     } | ||||
|                                 } | ||||
|                                 break; | ||||
|                             case 'diaporama': | ||||
|                                 const diaporamaFetch = await this.fetchDeeperContent('field_diaporama', partie.relationships); | ||||
|                                 if (diaporamaFetch) { | ||||
|                                     partieContent.diaporama = []; | ||||
|                                     for (let [index, image] of diaporamaFetch.entries()) { | ||||
|                                         partieContent.diaporama.push({ | ||||
|                                             url: image.attributes.uri.url, | ||||
|                                             alt: partie.relationships.field_diaporama.data[index].meta.alt, | ||||
|                                         }); | ||||
|                                     } | ||||
|                                 } | ||||
|                                 break; | ||||
|                             case 'entretien': | ||||
|                                 partieContent.entretien = {}; | ||||
|                                 const personnesFetch = await this.fetchDeeperContent('field_personne_s', partie.relationships); | ||||
|                                 const questionsReponsesFetch = await this.fetchDeeperContent('field_questions_reponses', partie.relationships); | ||||
|                                 if (personnesFetch && questionsReponsesFetch) { | ||||
|                                     partieContent.entretien.personnes = []; | ||||
|                                     for (let personne of personnesFetch) { | ||||
|                                         const portraitFetch = await this.fetchDeeperContent('field_portrait', personne.relationships); | ||||
|                                         if (portraitFetch) {                                                     | ||||
|                                             partieContent.entretien.personnes.push({ | ||||
|                                                 portrait: portraitFetch.attributes.uri.url, | ||||
|                                                 alt: personne.relationships.field_portrait.data.meta.alt, | ||||
|                                                 description: personne.attributes.field_description, | ||||
|                                             }); | ||||
|                                         } | ||||
|                                     } | ||||
|                                     partieContent.entretien.questionsReponses = []; | ||||
|                                     for (let qr of questionsReponsesFetch) { | ||||
|                                         partieContent.entretien.questionsReponses.push({ | ||||
|                                             question: qr.attributes.field_question, | ||||
|                                             reponse: qr.attributes.field_reponse.value, | ||||
|                                         }); | ||||
|                                     }                                            | ||||
|                                 } | ||||
|                                 break; | ||||
|                             case 'exergue': | ||||
|                                 partieContent.exergue = partie.attributes.field_texte_exergue.value; | ||||
|                                 break; | ||||
|                             case 'video': | ||||
|                                 partieContent.videos = []; | ||||
|                                 for (let video of partie.attributes.field_videos) { | ||||
|                                     const videoId = video.split('?v=')[1]; | ||||
|                                     const videoUrl = `https://www.youtube.com/embed/${videoId}`; | ||||
|                                     partieContent.videos.push(videoUrl);                       | ||||
|                                 } | ||||
|                                 break; | ||||
|                         } | ||||
|                         this.content.parties.push(partieContent); | ||||
|                     } | ||||
|                 } | ||||
|             } catch (error) { | ||||
|                 this.error = 'Failed to fetch data'; | ||||
|                 console.error('Issue with getNodeData', error); | ||||
| @@ -214,145 +183,77 @@ export const useContentStore = defineStore('content', { | ||||
|                 this.loading = false;                 | ||||
|             } | ||||
|         }, | ||||
|         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) {                     | ||||
|                     if (page.attributes.drupal_internal__nid == nid) { | ||||
|                         for (let metatag of page.attributes.metatag) { | ||||
|                             if (metatag.tag === "link") { | ||||
|                               this.href = metatag.attributes.href; | ||||
|                             } | ||||
|                         } | ||||
|                         this.page.title = page.attributes.title; | ||||
|                         const vignetteFetch = await this.fetchContent('field_vignette', page.relationships); | ||||
|                         if (vignetteFetch) { | ||||
|                             this.page.vignette = {  | ||||
|                                 url: vignetteFetch.attributes.uri.url, | ||||
|                                 alt: page.relationships.field_vignette.data.meta.alt | ||||
|                             }; | ||||
|                         } | ||||
|                          | ||||
|                         const partiesFetch = await this.fetchContent('field_parties_static', page.relationships); | ||||
|                         if (partiesFetch) { | ||||
|                             this.page.parties = []; | ||||
|  | ||||
|                             for (let partie of partiesFetch) { | ||||
|                                 const partieType = partie.type.replace(/^paragraph--/, ""); | ||||
|                                 let partieContent = { | ||||
|                                     type: partieType, | ||||
|                                 };                                | ||||
|                                  | ||||
|                                 switch (partieType) { | ||||
|                                     case 'titre_texte': | ||||
|                                         partieContent.titre = partie.attributes.field_titre; | ||||
|                                         partieContent.texte = partie.attributes.field_texte.value;                                         | ||||
|                                         break; | ||||
|                                     case 'diaporama': | ||||
|                                         const diaporamaFetch = await this.fetchContent('field_diaporama', partie.relationships); | ||||
|                                         if (diaporamaFetch) { | ||||
|                                             partieContent.diaporama = []; | ||||
|                                             for (let [index, image] of diaporamaFetch.entries()) { | ||||
|                                                 partieContent.diaporama.push({ | ||||
|                                                     url: image.attributes.uri.url, | ||||
|                                                     alt: partie.relationships.field_diaporama.data[index].meta.alt, | ||||
|                                                 }); | ||||
|                                             } | ||||
|                                         } | ||||
|                                         break; | ||||
|                                     case 'exergue': | ||||
|                                         partieContent.exergue = partie.attributes.field_texte_exergue.value; | ||||
|                                         break; | ||||
|                                     case 'video': | ||||
|                                         partieContent.videos = []; | ||||
|                                         for (let video of partie.attributes.field_videos) { | ||||
|                                             const videoId = video.split('?v=')[1]; | ||||
|                                             const videoUrl = `https://www.youtube.com/embed/${videoId}`; | ||||
|                                             partieContent.videos.push(videoUrl);                       | ||||
|                                         } | ||||
|                                         break; | ||||
|                                 } | ||||
|                                 this.page.parties.push(partieContent); | ||||
|         getCleanDate(date) { | ||||
|             return { | ||||
|               d: date.split('-')[2], | ||||
|               m: new Intl.DateTimeFormat('fr-FR', { month: 'long' }).format(new Date(date)), | ||||
|               y: date.split('-')[0], | ||||
|             } | ||||
|         }, | ||||
|         async getRelatedEtape(direction, allEtapesData, path) { | ||||
|             const getRelatedEtapeContent = async (relatedPath, allEtapesData) => { | ||||
|                 const baseUrl = window.location.protocol + "//" + window.location.host; | ||||
|                 for (let etape of allEtapesData) { | ||||
|                     for (let tag of etape.attributes.metatag) { | ||||
|                         if (tag.tag === "link" && tag.attributes.href === baseUrl + relatedPath) {                             | ||||
|                             const vignetteFetch = await REST.get(etape.relationships.field_vignette.links.related.href); | ||||
|                             this.content[direction] = { | ||||
|                                 url: tag.attributes.href, | ||||
|                                 couleur: etape.attributes.field_couleur, | ||||
|                                 title: etape.attributes.title, | ||||
|                                 postalCode: etape.attributes.field_adresse.postal_code, | ||||
|                                 dates: { | ||||
|                                     start: this.getCleanDate(etape.attributes.field_dates.value), | ||||
|                                     end: this.getCleanDate(etape.attributes.field_dates.end_value), | ||||
|                                 }, | ||||
|                                 vignette: { | ||||
|                                     url: vignetteFetch.data.data.attributes.uri.url, | ||||
|                                     alt: etape.relationships.field_vignette.data.meta.alt, | ||||
|                                 }, | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 this.setActiveItemInMenu(nid); | ||||
|             } catch (error) { | ||||
|             } | ||||
|  | ||||
|             const orderedEtapesList = document.querySelectorAll('#etapes-liste li'); | ||||
|             if (orderedEtapesList) { | ||||
|                 for (let li of orderedEtapesList) { | ||||
|                     const liHref = li.querySelector('a').getAttribute('href'); | ||||
|                     if (path.endsWith(liHref)) { | ||||
|                         const previousEtapeItemPath = li.previousElementSibling?.querySelector('a').getAttribute('href'); | ||||
|                         const nextEtapeItemPath = li.nextElementSibling?.querySelector('a').getAttribute('href'); | ||||
|                          | ||||
|                         if (previousEtapeItemPath && direction === 'previous') { | ||||
|                             let prevContent = await getRelatedEtapeContent(previousEtapeItemPath, allEtapesData); | ||||
|                             return prevContent; | ||||
|                         } | ||||
|                         if (nextEtapeItemPath && direction === 'next') { | ||||
|                             let nextContent = await getRelatedEtapeContent(nextEtapeItemPath, allEtapesData); | ||||
|                             return nextContent; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         async fetchDeeperContent(field, relationships) { | ||||
|             if (relationships[field].data) { | ||||
|               try { | ||||
|                 const contentLink = relationships[field].links.related.href; | ||||
|                 const contentFetch = await REST.get(contentLink); | ||||
|                 return contentFetch.data.data; | ||||
|               } catch (error) { | ||||
|                 this.error = 'Failed to fetch data'; | ||||
|                 console.error('Issue with getNodeData', error); | ||||
|             } finally { | ||||
|                 this.loading = false; | ||||
|             } | ||||
|         }, | ||||
|         async fetchContent(field, relationships) { | ||||
|           if (relationships[field].data) { | ||||
|             try { | ||||
|               const contentLink = relationships[field].links.related.href; | ||||
|               const contentFetch = await REST.get(contentLink); | ||||
|               return contentFetch.data.data; | ||||
|             } catch (error) { | ||||
|               this.error = 'Failed to fetch data'; | ||||
|               console.error('Issue with getNodeData', error); | ||||
|             } | ||||
|           } | ||||
|         }, | ||||
|         emptyAll(nid, map) { | ||||
|           this.href = ''; | ||||
|           this.map = map; | ||||
|           this.etape = {}; | ||||
|           this.page = {}; | ||||
|           this.setActiveItemInMenu(nid); | ||||
|         }, | ||||
|         setActiveItemInMenu(nid) { | ||||
|           const title = this.etape.title || this.page.title; | ||||
|    | ||||
|           const generalLinks = document.querySelectorAll('#menu > ul > li > a'); | ||||
|           if (Object.entries(this.etape).length === 0 && Object.entries(this.page).length === 0) { | ||||
|             for (let link of generalLinks) { | ||||
|               link.classList.remove('is-active'); | ||||
|             } | ||||
|             generalLinks[0].classList.add('is-active'); | ||||
|           } else { | ||||
|             for (let link of generalLinks) { | ||||
|               if (link.dataset.nodeNid == nid) { | ||||
|                 link.classList.add('is-active'); | ||||
|               } else { | ||||
|                 link.classList.remove('is-active'); | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|    | ||||
|           const etapeLinks = document.querySelectorAll('#etapes-liste li'); | ||||
|           for (let link of etapeLinks) { | ||||
|             const a = link.querySelector('a'); | ||||
|             if (a.innerText === title) { | ||||
|               link.classList.remove('inactive'); | ||||
|             } else { | ||||
|               link.classList.add('inactive'); | ||||
|             } | ||||
|           } | ||||
|           const inactiveLinks = document.querySelectorAll('#etapes-liste li.inactive'); | ||||
|           if (inactiveLinks.length === etapeLinks.length) { | ||||
|             for (let link of inactiveLinks) { | ||||
|               link.classList.remove('inactive'); | ||||
|             } | ||||
|           } | ||||
|         }, | ||||
|         resetStore() { | ||||
|             this.loading = true; | ||||
|         resetStore(forFrontDisplay) { | ||||
|             this.contentType = ''; | ||||
|             this.pageTitle = ''; | ||||
|             this.content = {}; | ||||
|             this.loading = !forFrontDisplay; | ||||
|             this.error = null; | ||||
|             this.etape = {}; | ||||
|             this.page = {}; | ||||
|         }, | ||||
|         getCleanDate(date) { | ||||
|           return { | ||||
|             d: date.split('-')[2], | ||||
|             m: new Intl.DateTimeFormat('fr-FR', { month: 'long' }).format(new Date(date)), | ||||
|             y: date.split('-')[0], | ||||
|            } | ||||
|         }, | ||||
|         } | ||||
|     }, | ||||
| }); | ||||
| @@ -2,6 +2,7 @@ import { defineStore } from 'pinia'; | ||||
|  | ||||
| export const useMapStore = defineStore('mapState', { | ||||
|     state: () => ({ | ||||
|         map: Object, | ||||
|         defaultZoom: Number, | ||||
|         defaultMapCenter: Object, | ||||
|         currentPlace: Object, | ||||
| @@ -10,36 +11,36 @@ export const useMapStore = defineStore('mapState', { | ||||
|         duration: 3, | ||||
|     }), | ||||
|     actions: { | ||||
|         zoomToPlace(map, lat, long) { | ||||
|             map.flyTo([lat, long], this.maxZoom, { duration: this.duration }); | ||||
|         zoomToPlace(lat, long) { | ||||
|             this.map.flyTo([lat, long], this.maxZoom, { duration: this.duration }); | ||||
|             this.currentZoom = this.maxZoom;             | ||||
|         }, | ||||
|         resetMap(map) { | ||||
|             map.flyTo(this.defaultMapCenter, this.defaultZoom, { duration: this.duration }); | ||||
|         resetMap() { | ||||
|             this.map.flyTo(this.defaultMapCenter, this.defaultZoom, { duration: this.duration }); | ||||
|             this.currentZoom = this.defaultZoom; | ||||
|         }, | ||||
|         lockMap(map) { | ||||
|         lockMap() { | ||||
|             setTimeout(() => { | ||||
|                 map.options.minZoom = this.currentZoom; | ||||
|                 map.options.maxZoom = this.currentZoom; | ||||
|                 this.map.options.minZoom = this.currentZoom; | ||||
|                 this.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(); | ||||
|             this.map.dragging.disable(); | ||||
|             this.map.touchZoom.disable(); | ||||
|             this.map.doubleClickZoom.disable(); | ||||
|             this.map.scrollWheelZoom.disable(); | ||||
|             this.map.boxZoom.disable(); | ||||
|             this.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(); | ||||
|           unlockMap() { | ||||
|             this.map.options.minZoom = this.defaultZoom; | ||||
|             this.map.options.maxZoom = this.maxZoom; | ||||
|             this.map.dragging.enable(); | ||||
|             this.map.touchZoom.enable(); | ||||
|             this.map.doubleClickZoom.enable(); | ||||
|             this.map.scrollWheelZoom.enable(); | ||||
|             this.map.boxZoom.enable(); | ||||
|             this.map.keyboard.enable(); | ||||
|             // map.tap.enable(); | ||||
|           }, | ||||
|     }, | ||||
|   | ||||
| @@ -0,0 +1,37 @@ | ||||
| import { setActiveNavItem } from "./set-active-nav-item"; | ||||
|  | ||||
| export async function initFirstLoadRouting(store, router, baseUrl, siteName) { | ||||
|     const decoupled_origin = JSON.parse(window.localStorage.getItem('decoupled_origin')); | ||||
|  | ||||
|     if(decoupled_origin) {        | ||||
|         await store.fetchContentData(baseUrl + decoupled_origin.url); | ||||
|         router.push(decoupled_origin.url); | ||||
|         window.localStorage.removeItem("decoupled_origin"); | ||||
|         document.title = store.pageTitle;  | ||||
|         setActiveNavItem(store.contentType, decoupled_origin.url); | ||||
|     } else { | ||||
|         document.title = siteName; | ||||
|     } | ||||
| } | ||||
|  | ||||
| export function handleClickableElements(clickableElements, store, router, baseUrl, siteName, mapStore) { | ||||
|     for (const link of clickableElements) { | ||||
|         let href = link.href || link.dataset.href; | ||||
|         if (href.startsWith(baseUrl)) href = href.replace(baseUrl, ''); | ||||
|  | ||||
|         link.onclick = async function (e) { | ||||
|             router.push(href); | ||||
|             if (href !== window.location.pathname) { | ||||
|                 if (href === '/') { | ||||
|                     store.resetStore(true); | ||||
|                     document.title = siteName; | ||||
|                     mapStore.resetMap(); | ||||
|                 } else { | ||||
|                     await store.fetchContentData(baseUrl + href); | ||||
|                     document.title = store.pageTitle;                 | ||||
|                 } | ||||
|                 setActiveNavItem(store.contentType, href); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										37
									
								
								web/themes/custom/caravane/assets/js/utils/layout-setup.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								web/themes/custom/caravane/assets/js/utils/layout-setup.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| export function setMenuToggle() { | ||||
|     const menuButton = document.querySelector('#block-caravane-mainnavigation > #menu'); | ||||
|     const menuContainer = document.querySelector('#block-caravane-mainnavigation > #menu > ul'); | ||||
|     const menuTitle = document.querySelector('#menu-title'); | ||||
|     const menuBurger = document.querySelector('#hamburger'); | ||||
|     const menuH2 = document.querySelector('#menu > h2'); | ||||
|     menuButton.addEventListener('click', (e) => {  | ||||
|       setTimeout(() => { | ||||
|         menuContainer.classList.toggle('open'); | ||||
|         menuTitle.classList.toggle('open'); | ||||
|         menuBurger.classList.toggle('open'); | ||||
|         menuH2.classList.toggle('open'); | ||||
|       }, 50); | ||||
|     }); | ||||
|     document.addEventListener('click', (e) => { | ||||
|       if (!menuContainer.contains(e.target) && !menuBurger.contains(e.target)) { | ||||
|         menuContainer.classList.remove('open'); | ||||
|         menuTitle.classList.remove('open'); | ||||
|         menuBurger.classList.remove('open'); | ||||
|         menuH2.classList.remove('open');   | ||||
|       } | ||||
|     }); | ||||
| } | ||||
|  | ||||
| export function setHamburgerWhenLogged(drupalSettings) { | ||||
|     if (drupalSettings.user.uid != 0) { | ||||
|       const menuBurger = document.querySelector('#hamburger'); | ||||
|       const menuTitle = document.querySelector('#menu-title'); | ||||
|       const menuContainer = document.querySelector('#block-caravane-mainnavigation > #menu > ul'); | ||||
|       const header = document.querySelector('.dialog-off-canvas-main-canvas'); | ||||
|       const headerTop = header.getBoundingClientRect().top; | ||||
|  | ||||
|       menuTitle.style.top = `${headerTop}px`;         | ||||
|       menuBurger.style.top = `${headerTop}px`; | ||||
|       menuContainer.style.paddingTop = `${headerTop}px`; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										6
									
								
								web/themes/custom/caravane/assets/js/utils/map-setup.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								web/themes/custom/caravane/assets/js/utils/map-setup.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| export function setupMapStore(mapStore, map, settings) { | ||||
|     mapStore.map = map; | ||||
|     mapStore.defaultMapCenter = map.getCenter(); | ||||
|     mapStore.maxZoom = settings.settings.maxZoom; | ||||
|     mapStore.defaultZoom = settings.settings.minZoom; | ||||
| } | ||||
| @@ -0,0 +1,80 @@ | ||||
| export function processClickableElements() { | ||||
|     return {  | ||||
|         etapeListLinks: processEtapeLinks(), | ||||
|         generalListLinks: processStaticLinks(), | ||||
|         logoLink: processLogoLink(), | ||||
|         mapIcons: processMapIcons(), | ||||
|     }; | ||||
| } | ||||
|  | ||||
| function processEtapeLinks() { | ||||
|     const etape_li = document.querySelectorAll('#etapes-liste li'); | ||||
|     etape_li.forEach((li) => { | ||||
|         const etape_link = li.querySelector('a.etape-link'); | ||||
|         etape_link.addEventListener('click', (e) => e.preventDefault()); | ||||
|         const couleur = etape_link.dataset.couleur; | ||||
|         li.dataset.href = etape_link.attributes.href.value; | ||||
|         const iconElements = li.querySelectorAll('.icone-arret > div'); | ||||
|         for (let element of iconElements) { | ||||
|           element.style.backgroundColor = couleur; | ||||
|         } | ||||
|     }); | ||||
|  | ||||
|     return etape_li; | ||||
| } | ||||
|  | ||||
| function processStaticLinks() { | ||||
|     const general_link_fields = document.querySelectorAll('#menu > ul > li > a'); | ||||
|     for (let i = 0; i <  general_link_fields.length; i ++) { | ||||
|         let general_link_path = general_link_fields[i].getAttribute('data-drupal-link-system-path'); | ||||
|         if (general_link_path && general_link_path !== '<front>') {            | ||||
|             const match = [...general_link_path.match(/^node\/(\d+)$/)]; | ||||
|             if (match) { | ||||
|                 const nid = match[1]; | ||||
|                 general_link_fields[i].setAttribute('data-nid', parseInt(nid)); | ||||
|             } | ||||
|         } | ||||
|         general_link_fields[i].addEventListener('click', (e) => e.preventDefault()); | ||||
|     } | ||||
|  | ||||
|     return general_link_fields; | ||||
| } | ||||
|  | ||||
| function processLogoLink() { | ||||
|     const logo = document.querySelector('#block-caravane-logocaravane a'); | ||||
|     logo.addEventListener('click', (e) => e.preventDefault()); | ||||
|  | ||||
|     return logo; | ||||
| } | ||||
|  | ||||
| function processMapIcons() { | ||||
|     const icons = document.querySelectorAll('.leaflet-map-divicon'); | ||||
|     for (let icon of icons) { | ||||
|         icon.setAttribute('title', ''); | ||||
|  | ||||
|         const hrefContainer = icon.querySelector('.url'); | ||||
|         icon.dataset.href = hrefContainer.innerText; | ||||
|         hrefContainer.style.display = "none"; | ||||
|  | ||||
|         const colorContainer = icon.querySelector('.couleur'); | ||||
|         let color = colorContainer.innerText; | ||||
|         colorContainer.style.display = "none"; | ||||
|  | ||||
|         const iconElements = icon.querySelectorAll('div'); | ||||
|         for (let iconElement of iconElements) { | ||||
|           iconElement.style.backgroundColor = color; | ||||
|         } | ||||
|  | ||||
|         icon.addEventListener('mouseenter', () => { | ||||
|             icon.style.transform = `${icon.style.transform} scale(1.1)`; | ||||
|             const popup = document.querySelector('.leaflet-tooltip-center > div'); | ||||
|             popup.style.opacity = "1"; | ||||
|         }); | ||||
|    | ||||
|         icon.addEventListener('mouseleave', () => { | ||||
|             icon.style.transform = icon.style.transform.split(' ')[0] + icon.style.transform.split(' ')[1] + icon.style.transform.split(' ')[2]; | ||||
|         });   | ||||
|     } | ||||
|  | ||||
|     return icons; | ||||
| } | ||||
| @@ -0,0 +1,35 @@ | ||||
| export function setActiveNavItem(contentType, href) { | ||||
|     const staticNavItems = document.querySelectorAll('#menu > ul > li > a'); | ||||
|     const etapeNavItems = document.querySelectorAll('#etapes-liste li a'); | ||||
|      | ||||
|     for (let item of staticNavItems) { | ||||
|         item.classList.remove('is-active'); | ||||
|     } | ||||
|  | ||||
|     for (let item of etapeNavItems) { | ||||
|         item.closest('li').classList.add('inactive'); | ||||
|     } | ||||
|  | ||||
|     if (href === '/' || href === '') { | ||||
|         staticNavItems[0].classList.add('is-active'); | ||||
|         for (let item of etapeNavItems) { | ||||
|             item.closest('li').classList.remove('inactive'); | ||||
|         } | ||||
|      | ||||
|     } else { | ||||
|         if (contentType === 'static') { | ||||
|             for (let item of staticNavItems) { | ||||
|                 if (item.getAttribute('href') === href) { | ||||
|                     item.classList.add('is-active'); | ||||
|                 } | ||||
|             } | ||||
|         } else if (contentType === 'etape') { | ||||
|             for (let item of etapeNavItems) { | ||||
|                 if (item.getAttribute('href') === href) { | ||||
|                     item.closest('li').classList.remove('inactive'); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										22
									
								
								web/themes/custom/caravane/assets/js/utils/vue-setup.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								web/themes/custom/caravane/assets/js/utils/vue-setup.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| import { createApp } from 'vue'; | ||||
| import { createPinia } from 'pinia'; | ||||
| import router from '../router/router'; | ||||
| import Modale from '../vuejs/Modale.vue'; | ||||
| import VueImageZoomer from 'vue-image-zoomer'; | ||||
| import 'vue-image-zoomer/dist/style.css'; | ||||
|  | ||||
| import { useContentStore } from '../stores/content'; | ||||
| import { useMapStore } from '../stores/mapState'; | ||||
|  | ||||
| export function initVueContentModale() { | ||||
|     const app = createApp(Modale) | ||||
|         .use(createPinia()) | ||||
|         .use(router) | ||||
|         .use(VueImageZoomer); | ||||
|  | ||||
|     const store = useContentStore(); | ||||
|     const mapStore = useMapStore(); | ||||
|     app.mount('#content-modale');     | ||||
|  | ||||
|   return { store, mapStore, router }; | ||||
| } | ||||
| @@ -1,44 +1,46 @@ | ||||
| <template> | ||||
|     <Transition> | ||||
|         <div v-if="isEtapeValid || isPageValid"> | ||||
|         <div v-if="!loading && (contentType === 'etape' || contentType === 'static')"> | ||||
|             <div class="content-wrapper"> | ||||
|                 <ModaleHeader | ||||
|                     :content="etape.title ? etape : page" | ||||
|                     :couleur="etape.couleur || brandColor" /> | ||||
|                     :contentType="contentType" | ||||
|                     :content="content" | ||||
|                     :couleur="content.couleur || brandColor" /> | ||||
|                 <main> | ||||
|                     <div v-for="partie in etape.parties || page.parties" class="partie"> | ||||
|                     <div v-for="partie in content.parties" class="partie"> | ||||
|                         <ModaleCarteSensible  | ||||
|                             v-if="partie.type === 'carte_sensible'"  | ||||
|                             :partie="partie" /> | ||||
|                         <ModaleTitreTexte | ||||
|                             v-if="partie.type === 'titre_texte'" | ||||
|                             :partie="partie" | ||||
|                             :couleur="etape.couleur || brandColor" /> | ||||
|                             :couleur="content.couleur || brandColor" /> | ||||
|                         <ModaleChiffresCles | ||||
|                             v-if="partie.type === 'chiffres_cles'" | ||||
|                             :partie="partie" | ||||
|                             :couleur="etape.couleur || brandColor" /> | ||||
|                             :couleur="content.couleur || brandColor" /> | ||||
|                         <ModaleDiaporama | ||||
|                             v-if="partie.type === 'diaporama'" | ||||
|                             :partie="partie" | ||||
|                             :couleur="etape.couleur || brandColor" /> | ||||
|                             :couleur="content.couleur || brandColor" /> | ||||
|                         <ModaleEntretien | ||||
|                             v-if="partie.type === 'entretien'" | ||||
|                             :partie="partie" | ||||
|                             :couleur="etape.couleur || brandColor" /> | ||||
|                             :couleur="content.couleur || brandColor" /> | ||||
|                         <ModaleExergue | ||||
|                             v-if="partie.type === 'exergue'" | ||||
|                             :partie="partie" | ||||
|                             :couleur="etape.couleur || brandColor" /> | ||||
|                             :couleur="content.couleur || brandColor" /> | ||||
|                         <ModaleVideos | ||||
|                             v-if="partie.type === 'video'" | ||||
|                             :partie="partie" /> | ||||
|                     </div> | ||||
|                 </main> | ||||
|                 <ModaleFooter | ||||
|                     :content="etape || page" | ||||
|                     :couleur="etape.couleur || brandColor" | ||||
|                     :map="map" /> | ||||
|                     :contentType="contentType" | ||||
|                     :content="content" | ||||
|                     :couleur="content.couleur || brandColor" | ||||
|                     /> | ||||
|             </div> | ||||
|         </div> | ||||
|     </Transition> | ||||
| @@ -46,10 +48,10 @@ | ||||
|  | ||||
| <script setup> | ||||
| 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'; | ||||
| import ModaleFooter from './components/ModaleFooter.vue'; | ||||
| @@ -62,132 +64,95 @@ import ModaleEntretien from './components/parties/ModaleEntretien.vue'; | ||||
| import ModaleExergue from './components/parties/ModaleExergue.vue'; | ||||
| import ModaleVideos from './components/parties/ModaleVideos.vue'; | ||||
|  | ||||
| import { useUtils } from './composables/useUtils'; | ||||
| const { isObjectEmpty, scrollTop } = useUtils(); | ||||
|  | ||||
| const router = useRouter(); | ||||
| const store = useContentStore(); | ||||
| const mapState = useMapStore(); | ||||
| const route = useRoute(); | ||||
|  | ||||
| const { loading, error, href, map, etape, page } = storeToRefs(store); | ||||
| const { duration } = storeToRefs(mapState); | ||||
| const { | ||||
|     contentType, | ||||
|     content, | ||||
|     loading, | ||||
|     error,  | ||||
| } = storeToRefs(store); | ||||
|  | ||||
| const isEtapeValid = computed(() => !error.value && !loading.value && etape.value && !isObjectEmpty(etape.value)); | ||||
| const isPageValid = computed(() => !error.value && !loading.value && page.value && !isObjectEmpty(page.value)); | ||||
| const { map, duration } = storeToRefs(mapState); | ||||
|  | ||||
| let isModaleEtape, wasModaleEtape; | ||||
|  | ||||
| const brandColor = "#80c8bf"; | ||||
|  | ||||
| let isProgrammaticNavigation = false; | ||||
|  | ||||
| const handleRouteChange = () => { | ||||
|   watch( | ||||
|     () => route.params.id, | ||||
|     (newId) => {       | ||||
|       if (isProgrammaticNavigation) { | ||||
|         isProgrammaticNavigation = false; | ||||
|         return; | ||||
|       } | ||||
|  | ||||
|       if (!newId) { | ||||
|         store.emptyAll(map.value); | ||||
|       } else { | ||||
|         store.fetchEtapeData(newId, map.value); | ||||
|         if (!etape.value?.data) { | ||||
|           store.fetchStaticData(newId, map.value); | ||||
|         } | ||||
|         scrollTop(); | ||||
|       } | ||||
|     }, | ||||
|     { immediate: true } | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| const handleColorChange = () => { | ||||
|   watch( | ||||
|     () => href.value, | ||||
|     () => content.value.couleur, | ||||
|     () => {     | ||||
|         document.documentElement.style.setProperty('--etape-couleur', etape.value.couleur || brandColor); | ||||
|     } | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| const handleHrefChange = () => { | ||||
|   watch( | ||||
|     () => href.value, | ||||
|     (newHref) => { | ||||
|       const relativePath = newHref.split('.fr')[1]; | ||||
|       isProgrammaticNavigation = true; | ||||
|       if (newHref == '') { | ||||
|         router.push('/'); | ||||
|         mapState.unlockMap(map.value) | ||||
|       } else { | ||||
|         if (relativePath && relativePath !== '' && relativePath !== '/') {   | ||||
|           mapState.lockMap(map.value);      | ||||
|           router.push(relativePath); | ||||
|           scrollTop(); | ||||
|         if (contentType.value === 'etape' && content.value.couleur) { | ||||
|             document.documentElement.style.setProperty('--etape-couleur', content.value.couleur || brandColor); | ||||
|         }    | ||||
|       } | ||||
|     } | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| const handleMapMovement = () => { | ||||
|   watch( | ||||
|     () => href.value, | ||||
|     () => loading.value, | ||||
|     () => { | ||||
|       isModaleEtape = !isObjectEmpty(etape.value); | ||||
|         if (!loading.value) { | ||||
|             isModaleEtape = contentType.value === 'etape';             | ||||
|              | ||||
|       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); | ||||
|             if (!wasModaleEtape && isModaleEtape) { | ||||
|                 // national -> détail | ||||
|                 document.documentElement.style.setProperty('--modale-enter-delay', `${duration.value}s`); | ||||
|                 mapState.zoomToPlace(content.value.coordinates.lat, content.value.coordinates.lon); | ||||
|             } else if (wasModaleEtape && isModaleEtape) {  | ||||
|                 // détail -> détail | ||||
|                 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(content.value.coordinates.lat, content.value.coordinates.lon); | ||||
|                 }, duration.value * 1000); | ||||
|      | ||||
|       } 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) {  | ||||
|                 // détail -> national | ||||
|                 document.documentElement.style.setProperty('--modale-leave-delay', 0); | ||||
|                 document.documentElement.style.setProperty('--modale-enter-delay', `${duration.value}s`); | ||||
|                 mapState.resetMap(); | ||||
|             } else if (!wasModaleEtape && !isModaleEtape) { | ||||
|                 // national -> national | ||||
|                 console.log('national -> national'); | ||||
|                 document.documentElement.style.setProperty('--modale-leave-delay', 0); | ||||
|                 document.documentElement.style.setProperty('--modale-enter-delay', '0.5s'); | ||||
|             } | ||||
|      | ||||
|       } else if (wasModaleEtape && !isModaleEtape) { | ||||
|         document.documentElement.style.setProperty('--modale-leave-delay', 0); | ||||
|         mapState.resetMap(map.value); | ||||
|       } | ||||
|  | ||||
|       wasModaleEtape = isModaleEtape; | ||||
|             wasModaleEtape = isModaleEtape; | ||||
|         } | ||||
|     }, | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| onMounted(() => { | ||||
|   isModaleEtape = !isObjectEmpty(etape.value); | ||||
|   wasModaleEtape = isModaleEtape; | ||||
|   handleRouteChange(); | ||||
|   handleColorChange(); | ||||
|   handleHrefChange(); | ||||
|   handleMapMovement(); | ||||
|     isModaleEtape = contentType.value === 'etape'; | ||||
|     wasModaleEtape = isModaleEtape;     | ||||
|     handleColorChange(); | ||||
|     handleMapMovement(); | ||||
| }); | ||||
| </script> | ||||
|  | ||||
| <style scss> | ||||
| <style scoped scss> | ||||
| .v-enter-active { | ||||
|   transition: all 0.5s linear var(--modale-enter-delay); | ||||
|   transition: margin-top 0.5s ease-out var(--modale-enter-delay); | ||||
| } | ||||
|  | ||||
| .v-leave-active { | ||||
|   transition: all 0.5s linear var(--modale-leave-delay); | ||||
|     transition: margin-top 0.5s ease-in var(--modale-leave-delay); | ||||
| } | ||||
|  | ||||
| .v-enter-from, | ||||
| .v-leave-to { | ||||
|   transform: translateY(20vh); | ||||
|   margin-top: 150vh; | ||||
| } | ||||
|  | ||||
| .v-enter-to, | ||||
| .v-leave-from { | ||||
|   transform: translateY(0vh); | ||||
|     margin-top: 0vh; | ||||
| } | ||||
| </style> | ||||
| @@ -4,8 +4,8 @@ | ||||
|             <div class="pattern"></div> | ||||
|         </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, map)"> | ||||
|         <div v-if="contentType === 'etape' && (content.previous || content.next)" class="related-etape-links"> | ||||
|             <div v-if="content.previous" class="card previous" @click="displayRelatedElement(content.previous.url)"> | ||||
|                 <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, map)"> | ||||
|             <div v-if="content.next" class="card next" @click="displayRelatedElement(content.next.url)"> | ||||
|                 <div class="icon"> | ||||
|                     <div :style="{ backgroundColor: content.next.couleur }"></div> | ||||
|                     <div :style="{ backgroundColor: content.next.couleur }"></div> | ||||
| @@ -42,14 +42,25 @@ | ||||
| </template> | ||||
|  | ||||
| <script setup> | ||||
| import router from '../../router/router.js'; | ||||
| import { useContentStore } from '../../stores/content'; | ||||
|  | ||||
| const brandColor = "#80c8bf"; | ||||
|  | ||||
| const store = useContentStore(); | ||||
| const props = defineProps({ | ||||
|   contentType: String, | ||||
|   content: Object, | ||||
|   couleur: String, | ||||
|   map: Object, | ||||
| }); | ||||
|  | ||||
| async function displayRelatedElement(href) { | ||||
|     const baseUrl = window.location.protocol + "//" + window.location.host; | ||||
|     if (href.startsWith(baseUrl)) href = href.replace(baseUrl, '');   | ||||
|  | ||||
|     router.push(href); | ||||
|     await store.fetchContentData(baseUrl + href); | ||||
|     document.title = store.pageTitle; | ||||
| } | ||||
| </script> | ||||
| @@ -1,9 +1,9 @@ | ||||
| <template> | ||||
|     <header :style="content.vignette ? '' : { marginTop: '20vh' }"> | ||||
|         <div class="cover"> | ||||
|         <img v-if="content.vignette" :src="content.vignette.url" :alt="content.vignette.alt"> | ||||
|             <img v-if="content.vignette" :src="content.vignette.url" :alt="content.vignette.alt"> | ||||
|         </div> | ||||
|         <div v-if="content.dates" class="cartouche" :style="{ backgroundColor: couleur }"> | ||||
|         <div v-if="contentType === 'etape' && content.dates" class="cartouche" :style="{ backgroundColor: couleur }"> | ||||
|             <p>Étape n°{{content.etape_number}}</p> | ||||
|             <p>Du {{content.dates.start.d}} {{content.dates.start.m}} au {{ content.dates.end.d }} {{ content.dates.end.m }} {{ content.dates.end.y }}</p> | ||||
|         </div> | ||||
| @@ -13,7 +13,7 @@ | ||||
|         <div class="locality"> | ||||
|             <div class="top-triangle"></div> | ||||
|             <div class="locality-title"> | ||||
|                 <h1>{{content.title}} <em v-if="content.adresse">({{ content.adresse.postal_code.slice(0, 2) }})</em></h1> | ||||
|                 <h1>{{content.contentTitle}} <em v-if="content.adresse">({{ content.adresse.postal_code.slice(0, 2) }})</em></h1> | ||||
|             </div> | ||||
|         </div> | ||||
|     </header> | ||||
| @@ -21,6 +21,7 @@ | ||||
|  | ||||
| <script setup> | ||||
| const props = defineProps({ | ||||
|   contentType: String, | ||||
|   content: Object, | ||||
|   couleur: String, | ||||
| }); | ||||
|   | ||||
| @@ -1,21 +0,0 @@ | ||||
| export function useUtils() { | ||||
|     const isObjectEmpty = (obj) => { | ||||
|         if (!obj || typeof obj !== 'object') return true; | ||||
|  | ||||
|         return !Object.keys(obj).some((key) => { | ||||
|         const value = obj[key]; | ||||
|         if (Array.isArray(value)) return value.length > 0; | ||||
|         if (typeof value === 'object') return !isObjectEmpty(value); | ||||
|         return value !== null && value !== undefined && value !== ''; | ||||
|         }); | ||||
|     }; | ||||
|  | ||||
|     const scrollTop = () => { | ||||
|         window.scrollTo({ top: 0, behavior: 'smooth' }); | ||||
|     }; | ||||
|  | ||||
|     return { | ||||
|         isObjectEmpty, | ||||
|         scrollTop, | ||||
|     }; | ||||
| } | ||||
| @@ -253,6 +253,9 @@ body{ | ||||
|               top: 0; | ||||
|               width: 100vw; | ||||
|               .leaflet-container { | ||||
|                 .leaflet-popup { | ||||
|                   display: none; | ||||
|                 } | ||||
|                 // add map style here | ||||
|                 .leaflet-control-zoom { | ||||
|                   border: none; | ||||
|   | ||||
| @@ -27,7 +27,8 @@ function caravane_preprocess_html(&$variables) { | ||||
|  * Implements hook_preprocess_HOOK() for page.html.twig. | ||||
|  */ | ||||
| function caravane_preprocess_page(&$variables) { | ||||
| 
 | ||||
|   $config = \Drupal::config('system.site'); | ||||
|   $variables['site_name'] = $config->get('name'); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|   | ||||
| @@ -89,3 +89,4 @@ | ||||
|   {% endif %} | ||||
|  | ||||
| </div>{# /.layout-container #} | ||||
| <div id="site_name" style="display: none;">{{ site_name }}</div> | ||||
		Reference in New Issue
	
	Block a user
	 Valentin
					Valentin