diff --git a/config/sync/rest.resource.entity.path_alias.yml b/config/sync/rest.resource.entity.path_alias.yml new file mode 100644 index 0000000..5d44279 --- /dev/null +++ b/config/sync/rest.resource.entity.path_alias.yml @@ -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 diff --git a/config/sync/views.view.etapes.yml b/config/sync/views.view.etapes.yml index 9dc6b62..352b3da 100644 --- a/config/sync/views.view.etapes.yml +++ b/config/sync/views.view.etapes.yml @@ -269,7 +269,7 @@ display: popupAnchor: x: '' 'y': '' - html: "
\r\n
\r\n
\r\n
{{ nid }}
\r\n
{{ field_couleur }}
" + html: "
\r\n
\r\n
\r\n
[node:url]
\r\n
[node:field_couleur]
" html_class: 'leaflet-map-divicon ' circle_marker_options: '{"radius":100,"color":"red","fillColor":"#f03","fillOpacity":0.5}' leaflet_markercluster: diff --git a/web/themes/custom/caravane/assets/js/main.js b/web/themes/custom/caravane/assets/js/main.js index 37219eb..eaa3815 100644 --- a/web/themes/custom/caravane/assets/js/main.js +++ b/web/themes/custom/caravane/assets/js/main.js @@ -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('
')) { - 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) \ No newline at end of file diff --git a/web/themes/custom/caravane/assets/js/redirect.js b/web/themes/custom/caravane/assets/js/redirect.js index b04aef1..9ff243e 100644 --- a/web/themes/custom/caravane/assets/js/redirect.js +++ b/web/themes/custom/caravane/assets/js/redirect.js @@ -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)); diff --git a/web/themes/custom/caravane/assets/js/router/router.js b/web/themes/custom/caravane/assets/js/router/router.js index c9ae289..453d4eb 100644 --- a/web/themes/custom/caravane/assets/js/router/router.js +++ b/web/themes/custom/caravane/assets/js/router/router.js @@ -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({ diff --git a/web/themes/custom/caravane/assets/js/stores/content.js b/web/themes/custom/caravane/assets/js/stores/content.js index 5868559..ab8ebe7 100644 --- a/web/themes/custom/caravane/assets/js/stores/content.js +++ b/web/themes/custom/caravane/assets/js/stores/content.js @@ -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,320 +23,237 @@ 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); - } finally { - this.loading = false; + } finally { + 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], - } - }, + } }, }); \ No newline at end of file diff --git a/web/themes/custom/caravane/assets/js/stores/mapState.js b/web/themes/custom/caravane/assets/js/stores/mapState.js index e8e9a2b..201c581 100644 --- a/web/themes/custom/caravane/assets/js/stores/mapState.js +++ b/web/themes/custom/caravane/assets/js/stores/mapState.js @@ -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 }); - this.currentZoom = this.maxZoom; + 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(); }, }, diff --git a/web/themes/custom/caravane/assets/js/utils/handle-navigation.js b/web/themes/custom/caravane/assets/js/utils/handle-navigation.js new file mode 100644 index 0000000..f916074 --- /dev/null +++ b/web/themes/custom/caravane/assets/js/utils/handle-navigation.js @@ -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); + } + } + } +} \ No newline at end of file diff --git a/web/themes/custom/caravane/assets/js/utils/layout-setup.js b/web/themes/custom/caravane/assets/js/utils/layout-setup.js new file mode 100644 index 0000000..33a5b58 --- /dev/null +++ b/web/themes/custom/caravane/assets/js/utils/layout-setup.js @@ -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`; + } +} \ No newline at end of file diff --git a/web/themes/custom/caravane/assets/js/utils/map-setup.js b/web/themes/custom/caravane/assets/js/utils/map-setup.js new file mode 100644 index 0000000..06a905e --- /dev/null +++ b/web/themes/custom/caravane/assets/js/utils/map-setup.js @@ -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; +} \ No newline at end of file diff --git a/web/themes/custom/caravane/assets/js/utils/process-clickable-elements.js b/web/themes/custom/caravane/assets/js/utils/process-clickable-elements.js new file mode 100644 index 0000000..9fa005c --- /dev/null +++ b/web/themes/custom/caravane/assets/js/utils/process-clickable-elements.js @@ -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 !== '') { + 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; +} \ No newline at end of file diff --git a/web/themes/custom/caravane/assets/js/utils/set-active-nav-item.js b/web/themes/custom/caravane/assets/js/utils/set-active-nav-item.js new file mode 100644 index 0000000..fa18a08 --- /dev/null +++ b/web/themes/custom/caravane/assets/js/utils/set-active-nav-item.js @@ -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'); + } + } + } + } + +} \ No newline at end of file diff --git a/web/themes/custom/caravane/assets/js/utils/vue-setup.js b/web/themes/custom/caravane/assets/js/utils/vue-setup.js new file mode 100644 index 0000000..d9be8e1 --- /dev/null +++ b/web/themes/custom/caravane/assets/js/utils/vue-setup.js @@ -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 }; +} \ No newline at end of file diff --git a/web/themes/custom/caravane/assets/js/vuejs/Modale.vue b/web/themes/custom/caravane/assets/js/vuejs/Modale.vue index c97decb..9091250 100644 --- a/web/themes/custom/caravane/assets/js/vuejs/Modale.vue +++ b/web/themes/custom/caravane/assets/js/vuejs/Modale.vue @@ -1,44 +1,46 @@ \ No newline at end of file diff --git a/web/themes/custom/caravane/assets/js/vuejs/components/ModaleHeader.vue b/web/themes/custom/caravane/assets/js/vuejs/components/ModaleHeader.vue index 3b42c7a..74d4709 100644 --- a/web/themes/custom/caravane/assets/js/vuejs/components/ModaleHeader.vue +++ b/web/themes/custom/caravane/assets/js/vuejs/components/ModaleHeader.vue @@ -1,9 +1,9 @@