avancées centre de ressource
This commit is contained in:
parent
9093caa557
commit
a1916e3219
@ -3,9 +3,11 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import REST from '../api/rest-axios';
|
||||
|
||||
import { useLayoutStore } from './layout';
|
||||
|
||||
import { findContentByPath } from '../utils/content/findContentByPath';
|
||||
import { getCleanDate, fetchFromRelationships, getRelatedEtape } from '../utils/content/contentFetchUtils';
|
||||
import { getCarteSensible, getTitreTexte, getChiffresCles, getDiaporama, getEntretien, getVideos } from '../utils/content/cleanParties';
|
||||
import { getCarteSensible, getTitreTexte, getChiffresCles, getDiaporama, getEntretien, getVideos, getDocument, getGallerie } from '../utils/content/cleanParties';
|
||||
import { getPartenaires, getGouvernance, getRessources } from '../utils/content/multiItemPages';
|
||||
|
||||
export const useContentStore = defineStore('content', {
|
||||
@ -24,39 +26,44 @@ export const useContentStore = defineStore('content', {
|
||||
const { contentType, rawContent } = await findContentByPath(contentTypes, path);
|
||||
this.contentType = contentType;
|
||||
|
||||
if (this.contentType === 'etape' || this.contentType === 'static') {
|
||||
if (
|
||||
this.contentType === 'etape'
|
||||
|| this.contentType === 'static'
|
||||
|| this.contentType === 'ressourceItem'
|
||||
) {
|
||||
const vignettePromise = fetchFromRelationships('field_vignette', rawContent.relationships);
|
||||
const partiesPromise = fetchFromRelationships('field_parties', rawContent.relationships);
|
||||
const partiesPromise = fetchFromRelationships(this.contentType === 'ressourceItem' ? 'field_parties_ressource' : 'field_parties', rawContent.relationships);
|
||||
|
||||
let previousEtapePromise, nextEtapePromise;
|
||||
|
||||
if (contentType === 'etape') {
|
||||
// related étapes
|
||||
|
||||
if (this.contentType === 'etape') {
|
||||
previousEtapePromise = getRelatedEtape('previous', path);
|
||||
nextEtapePromise = getRelatedEtape('next', path);
|
||||
|
||||
// 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: getCleanDate(rawContent.attributes.field_dates.value),
|
||||
end: getCleanDate(rawContent.attributes.field_dates.end_value),
|
||||
}
|
||||
}
|
||||
|
||||
if (this.contentType === 'ressourceItem') {
|
||||
this.content.ressourceType = rawContent.attributes.field_type_de_ressource;
|
||||
this.content.auteurice = rawContent.attributes.field_autheurice;
|
||||
this.content.date = getCleanDate(rawContent.attributes.field_date_ressource);
|
||||
this.content.introduction = rawContent.attributes.field_introduction?.processed;
|
||||
|
||||
useLayoutStore().hideEtapeList(true);
|
||||
}
|
||||
|
||||
// pageTitle
|
||||
this.pageTitle = rawContent.attributes.metatag.find(tag => tag.tag === "meta")?.attributes.content;
|
||||
|
||||
// contentTitle
|
||||
this.content.contentTitle = rawContent.attributes.title;
|
||||
|
||||
const [vignetteData, partiesData] = await Promise.all([vignettePromise, partiesPromise]);
|
||||
@ -76,7 +83,7 @@ export const useContentStore = defineStore('content', {
|
||||
if (partiesData) {
|
||||
const partiesPromises = partiesData.map(async (partie) => {
|
||||
const partieType = partie.type.replace(/^paragraph--/, "");
|
||||
let partieContent = { type: partieType };
|
||||
let partieContent = { type: partieType };
|
||||
|
||||
switch (partieType) {
|
||||
case 'carte_sensible':
|
||||
@ -102,10 +109,41 @@ export const useContentStore = defineStore('content', {
|
||||
case 'video':
|
||||
partieContent.videos = getVideos(partie);
|
||||
break;
|
||||
case 'document':
|
||||
partieContent.document = await getDocument(partie);
|
||||
break;
|
||||
case 'galleries':
|
||||
partieContent.gallerie = await getGallerie(partie);
|
||||
break;
|
||||
}
|
||||
return partieContent;
|
||||
});
|
||||
|
||||
// liens
|
||||
if (rawContent.attributes.field_liens?.length) {
|
||||
this.content.liens = [];
|
||||
for (let lien of rawContent.attributes.field_liens) {
|
||||
this.content.liens.push({
|
||||
title: lien.title,
|
||||
url: lien.uri,
|
||||
});
|
||||
}
|
||||
}
|
||||
// pièces jointes
|
||||
if (rawContent.relationships.field_pieces_jointes?.data.length) {
|
||||
this.content.pieces_jointes = [];
|
||||
for (let pieceJointe of rawContent.relationships.field_pieces_jointes.data) {
|
||||
if (pieceJointe.meta.display) {
|
||||
const uuid = pieceJointe.id;
|
||||
const response = await REST.get(`/jsonapi/file/file/${uuid}`);
|
||||
this.content.pieces_jointes.push({
|
||||
title: pieceJointe.meta.description,
|
||||
url: response.data.data.attributes.uri.url,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.content.parties = await Promise.all(partiesPromises);
|
||||
}
|
||||
|
||||
@ -132,6 +170,8 @@ export const useContentStore = defineStore('content', {
|
||||
switch (this.contentType) {
|
||||
case 'ressource':
|
||||
multiItemPageArray = await getRessources(rawContent);
|
||||
this.content.ressourceTypes = new Set(multiItemPageArray.map(item => item.ressourceType));
|
||||
useLayoutStore().hideEtapeList(true);
|
||||
break;
|
||||
case 'partenaire':
|
||||
multiItemPageArray = await getPartenaires(rawContent);
|
||||
@ -142,7 +182,6 @@ export const useContentStore = defineStore('content', {
|
||||
}
|
||||
|
||||
this.content[`${this.contentType}s`] = multiItemPageArray;
|
||||
console.log(this.content);
|
||||
}
|
||||
} catch (error) {
|
||||
this.error = 'Failed to fetch data';
|
||||
@ -157,6 +196,7 @@ export const useContentStore = defineStore('content', {
|
||||
this.content = {};
|
||||
this.loading = !forFrontDisplay;
|
||||
this.error = null;
|
||||
useLayoutStore().hideEtapeList(false);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
@ -46,6 +46,23 @@ export const useLayoutStore = defineStore('layout', {
|
||||
|
||||
this.toggleEtapeListScroll(isIntersecting, listeEtape, column, headerRect.height, animationToggleRect.top);
|
||||
},
|
||||
hideEtapeList(souldListHide) {
|
||||
const etapeList = document.querySelector('#etapes-liste');
|
||||
const listContainer = etapeList.parentNode;
|
||||
if (souldListHide) {
|
||||
listContainer.style.minWidth = '30vw';
|
||||
etapeList.style.opacity = '0';
|
||||
setTimeout(() => {
|
||||
etapeList.style.display = 'none';
|
||||
}, 300);
|
||||
} else {
|
||||
listContainer.style.minWidth = 'unset';
|
||||
etapeList.style.display = 'block';
|
||||
setTimeout(() => {
|
||||
etapeList.style.opacity = '1';
|
||||
}, 10);
|
||||
}
|
||||
},
|
||||
toggleEtapeListScroll(isIntersecting, listeEtape, column, headerHeight, animationToggleTop) {
|
||||
if (isIntersecting && !this.isEtapeListeScrollable
|
||||
|| !isIntersecting && this.isEtapeListeScrollable) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import REST from '../../api/rest-axios';
|
||||
import { fetchFromRelationships } from './contentFetchUtils';
|
||||
import { fetchFromRelationships, getCleanDate } from './contentFetchUtils';
|
||||
|
||||
|
||||
export async function getCarteSensible(partie) {
|
||||
@ -131,4 +131,46 @@ export function getVideos(partie) {
|
||||
videos.push(videoUrl);
|
||||
}
|
||||
return videos;
|
||||
}
|
||||
|
||||
export async function getDocument(partie) {
|
||||
// const documentFetch = await fetchFromRelationships('field_document', partie.relationships);
|
||||
const uuid = partie.relationships.field_document.data.id;
|
||||
const documentFetch = await REST.get(`/jsonapi/file/file/${uuid}`);
|
||||
const url = documentFetch.data.data.attributes.uri.url;
|
||||
|
||||
const titre = partie.attributes.field_titre;
|
||||
const sousTitre = partie.attributes.field_sous_titre;
|
||||
const date = partie.attributes.field_date ? getCleanDate(partie.attributes.field_date) : null;
|
||||
const auteurice = partie.relationships.field_autheurice_s;
|
||||
const description = partie.relationships.field_document.data.meta.description;
|
||||
|
||||
const vignetteFetch = await REST.get(`/jsonapi/file/file/${partie.relationships.field_vignette.data.id}`);
|
||||
const vignette = { url: vignetteFetch.data.data.attributes.image_style_uri.content_small, alt: partie.relationships.field_vignette.data.meta.alt };
|
||||
|
||||
return { url, titre, sousTitre, date, auteurice, description, vignette };
|
||||
}
|
||||
|
||||
export async function getGallerie(partie) {
|
||||
const gallerieFetch = await fetchFromRelationships('field_gallerie', partie.relationships);
|
||||
|
||||
if (gallerieFetch) {
|
||||
const titre = gallerieFetch.attributes.title;
|
||||
const introduction = gallerieFetch.attributes.body?.processed;
|
||||
|
||||
const imagesFetch = await fetchFromRelationships('field_images', gallerieFetch.relationships);
|
||||
let images = [];
|
||||
imagesFetch.forEach((image, index) => {
|
||||
images.push({
|
||||
url: {
|
||||
original: image.attributes.uri.url,
|
||||
small: image.attributes.image_style_uri.content_small,
|
||||
medium: image.attributes.image_style_uri.content_medium,
|
||||
large: image.attributes.image_style_uri.content_large,
|
||||
},
|
||||
alt: gallerieFetch.relationships.field_images.data[index].meta.alt,
|
||||
});
|
||||
});
|
||||
return { titre, introduction, images };
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@ import REST from '../../api/rest-axios';
|
||||
export async function fetchFromRelationships(field, relationships) {
|
||||
field = relationships[field] ? field : `${field}_static`;
|
||||
|
||||
if (relationships[field].links) {
|
||||
if (relationships[field]?.links) {
|
||||
const contentLink = relationships[field].links.related.href;
|
||||
return REST.get(contentLink)
|
||||
.then(contentFetch => contentFetch.data.data)
|
||||
|
@ -10,9 +10,9 @@ export async function findContentByPath(contentTypes, path) {
|
||||
)
|
||||
);
|
||||
|
||||
if (content) {
|
||||
if (content) {
|
||||
return {
|
||||
contentType: type,
|
||||
contentType: content.type === 'node--ressource' ? 'ressourceItem' : type,
|
||||
rawContent: content,
|
||||
};
|
||||
}
|
||||
|
@ -10,6 +10,10 @@
|
||||
:content="content"
|
||||
:couleur="content.couleur || brandColor" />
|
||||
<main>
|
||||
<RessourceItemHeader
|
||||
v-if="contentType === 'ressourceItem'"
|
||||
:content="content"
|
||||
:couleur="brandColor" />
|
||||
<div v-for="partie in content.parties" class="partie">
|
||||
<ModaleCarteSensible
|
||||
v-if="partie.type === 'carte_sensible'"
|
||||
@ -37,6 +41,14 @@
|
||||
<ModaleVideos
|
||||
v-if="partie.type === 'video'"
|
||||
:partie="partie" />
|
||||
<ModaleGallerie
|
||||
v-if="partie.type === 'galleries'"
|
||||
:partie="partie"
|
||||
:couleur="content.couleur || brandColor" />
|
||||
<ModaleDocument
|
||||
v-if="partie.type === 'document'"
|
||||
:partie="partie"
|
||||
:couleur="content.couleur || brandColor" />
|
||||
</div>
|
||||
<EquipeContent
|
||||
v-if="contentType === 'gouvernance'"
|
||||
@ -46,9 +58,9 @@
|
||||
v-if="contentType === 'partenaire'"
|
||||
:content="content" />
|
||||
<CentreDeRessource
|
||||
v-if="contentType === 'ressource'"
|
||||
:content="content"
|
||||
:couleur="brandColor" />
|
||||
v-if="contentType === 'ressource'"
|
||||
:content="content"
|
||||
:couleur="brandColor" />
|
||||
</main>
|
||||
<PiecesJointes
|
||||
:content="content"
|
||||
@ -74,6 +86,7 @@ import ModaleFooter from './components/ModaleFooter.vue';
|
||||
import EquipeContent from './components/EquipeContent.vue';
|
||||
import PartenairesContent from './components/PartenairesContent.vue';
|
||||
import CentreDeRessource from './components/CentreDeRessource.vue';
|
||||
import RessourceItemHeader from './components/RessourceItemHeader.vue';
|
||||
import PiecesJointes from './components/PiecesJointes.vue';
|
||||
|
||||
import ModaleCarteSensible from './components/parties/ModaleCarteSensible.vue';
|
||||
@ -83,6 +96,8 @@ import ModaleDiaporama from './components/parties/ModaleDiaporama.vue';
|
||||
import ModaleEntretien from './components/parties/ModaleEntretien.vue';
|
||||
import ModaleExergue from './components/parties/ModaleExergue.vue';
|
||||
import ModaleVideos from './components/parties/ModaleVideos.vue';
|
||||
import ModaleGallerie from './components/parties/ModaleGallerie.vue';
|
||||
import ModaleDocument from './components/parties/ModaleDocument.vue';
|
||||
|
||||
const store = useContentStore();
|
||||
const mapState = useMapStore();
|
||||
|
@ -1,14 +1,153 @@
|
||||
<template>
|
||||
<div id="centre-de-ressource">
|
||||
<div v-if="content.intro" class="intro">
|
||||
<p v-html="content.intro"></p>
|
||||
<!-- <p v-html="content.intro"></p> -->
|
||||
</div>
|
||||
<div class="filters">
|
||||
<input type="text" v-model="searchQuery" placeholder="Rechercher..." class="search-bar">
|
||||
<select v-model="selectedType">
|
||||
<option value="">Tous les types</option>
|
||||
<option v-for="type in content.ressourceTypes" :key="type" :value="type">
|
||||
{{ type.replace(/_/g, ' ').replace(/^\w/, char => char.toUpperCase()) }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<template v-for="type in filteredTypes" :key="type">
|
||||
<div class="type-section" v-if="ressourcesToDisplay[type] && ressourcesToDisplay[type].length > 0">
|
||||
<h3>{{ type.replace(/_/g, ' ').replace(/^\w/, char => char.toUpperCase()) }}</h3>
|
||||
<div class="ressource-list">
|
||||
<TransitionGroup name="itemFade" tag="div" appear>
|
||||
<div class="ressource-item"
|
||||
:data-href="ressource.url"
|
||||
v-for="(ressource, ressourceIndex) in ressourcesToDisplay[type]"
|
||||
:key="`${type}-${ressourceIndex}`"
|
||||
:style="{ '--index': ressourceIndex - visibleItemsPerSection }">
|
||||
<figure>
|
||||
<img :src="ressource.vignette.url" :alt="ressource.vignette.alt" />
|
||||
</figure>
|
||||
<div>
|
||||
<h4>{{ ressource.title }}</h4>
|
||||
<p>Le {{ ressource.date.d }} {{ ressource.date.m }} {{ ressource.date.y }}</p>
|
||||
<p>Par {{ ressource.auteurice }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</TransitionGroup>
|
||||
</div>
|
||||
<div class="button-container">
|
||||
<div v-if="ressourcesByType(type).length > visibleItemsPerSection
|
||||
&& ressourcesToDisplay[type].length === visibleItemsPerSection"
|
||||
class="ressource-button"
|
||||
@click="loadMore(type)">
|
||||
Voir plus ↓
|
||||
</div>
|
||||
<div v-if="ressourcesToDisplay[type].length > visibleItemsPerSection"
|
||||
class="ressource-button"
|
||||
@click="showLess(type)">
|
||||
Voir moins ↑
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, watch, onMounted } from 'vue';
|
||||
import router from '../../router/router';
|
||||
|
||||
import { useContentStore } from '../../stores/content';
|
||||
import { useMapStore } from '../../stores/map';
|
||||
|
||||
import { handleClickableElements } from '../../utils/handle-navigation.js';
|
||||
|
||||
const store = useContentStore();
|
||||
const mapStore = useMapStore();
|
||||
const siteName = document.querySelector('#site_name').innerText;
|
||||
|
||||
const props = defineProps({
|
||||
content: Object,
|
||||
couleur: String,
|
||||
});
|
||||
</script>
|
||||
|
||||
const searchQuery = ref('');
|
||||
const selectedType = ref('');
|
||||
const ressourcesToDisplay = ref({});
|
||||
const visibleItemsPerSection = 4;
|
||||
|
||||
const filteredTypes = computed(() => {
|
||||
return selectedType.value ? [selectedType.value] : props.content.ressourceTypes;
|
||||
});
|
||||
|
||||
const ressourcesByType = (type) => {
|
||||
return props.content.ressources
|
||||
.filter(ressource => ressource.ressourceType === type)
|
||||
.filter(ressource =>
|
||||
searchQuery.value === '' ||
|
||||
ressource.title.toLowerCase().includes(searchQuery.value.toLowerCase())
|
||||
);
|
||||
}
|
||||
|
||||
const initializeRessources = (type) => {
|
||||
ressourcesToDisplay.value[type] = ressourcesByType(type).slice(0, visibleItemsPerSection);
|
||||
};
|
||||
|
||||
props.content.ressourceTypes.forEach(type => initializeRessources(type));
|
||||
|
||||
const loadMore = (type) => {
|
||||
const currentLength = ressourcesToDisplay.value[type].length;
|
||||
|
||||
if (currentLength < ressourcesByType(type).length) {
|
||||
ressourcesToDisplay.value[type] = ressourcesByType(type).slice(0, currentLength + visibleItemsPerSection);
|
||||
}
|
||||
};
|
||||
|
||||
const showLess = (type) => {
|
||||
ressourcesToDisplay.value[type] = ressourcesByType(type).slice(0, visibleItemsPerSection);
|
||||
};
|
||||
|
||||
watch(searchQuery, () => {
|
||||
props.content.ressourceTypes.forEach(type => {
|
||||
initializeRessources(type);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
let relatedItemCards, baseUrl;
|
||||
|
||||
watch(ressourcesToDisplay.value, () => {
|
||||
setClickableElements();
|
||||
}, { deep: true });
|
||||
|
||||
watch(selectedType, () => {
|
||||
setClickableElements();
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
baseUrl = window.location.protocol + "//" + window.location.host;
|
||||
setClickableElements();
|
||||
});
|
||||
|
||||
function setClickableElements() {
|
||||
setTimeout(() => {
|
||||
relatedItemCards = document.querySelectorAll('.ressource-item');
|
||||
handleClickableElements(relatedItemCards, store, router, baseUrl, siteName, mapStore);
|
||||
}, 50);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.itemFade-enter-active, .itemFade-leave-active {
|
||||
transition: all 0.3s ease !important;
|
||||
transition-delay: calc(0.1s * var(--index)) !important;
|
||||
}
|
||||
|
||||
.itemFade-enter-from, .itemFade-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(-20px) !important;
|
||||
}
|
||||
|
||||
.itemFade-enter-to, .itemFade-leave-from {
|
||||
opacity: 1;
|
||||
transform: translateY(0px);
|
||||
}
|
||||
</style>
|
@ -14,7 +14,7 @@
|
||||
<div class="locality">
|
||||
<div class="top-triangle" v-if="contentType === 'etape'"></div>
|
||||
<div class="locality-title">
|
||||
<h1>{{content.contentTitle}} <em v-if="content.adresse">({{ content.adresse.postal_code.slice(0, 2) }})</em></h1>
|
||||
<h1>{{ contentType === 'ressourceItem' ? 'Centre de ressources' : content.contentTitle }} <em v-if="content.adresse">({{ content.adresse.postal_code.slice(0, 2) }})</em></h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<header id="ressource-item-header">
|
||||
<div class="retour">
|
||||
<p data-href="/ressources">← Retour au centre de ressources</p>
|
||||
</div>
|
||||
<div class="type">{{ content.displayedType }}</div>
|
||||
<div class="title">
|
||||
<h2
|
||||
:style="{ background: `linear-gradient(transparent 70%, ${couleur} 70%)` }">
|
||||
{{ content.contentTitle }}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="meta">Par {{ content.auteurice }}, le {{ content.date.d }} {{ content.date.m }} {{ content.date.y }}</div>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted } from 'vue';
|
||||
import router from '../../router/router';
|
||||
|
||||
import { useContentStore } from '../../stores/content';
|
||||
import { useMapStore } from '../../stores/map';
|
||||
|
||||
import { handleClickableElements } from '../../utils/handle-navigation.js';
|
||||
|
||||
const store = useContentStore();
|
||||
const mapStore = useMapStore();
|
||||
const siteName = document.querySelector('#site_name').innerText;
|
||||
|
||||
const props = defineProps({
|
||||
content: Object,
|
||||
couleur: String,
|
||||
});
|
||||
|
||||
function setDisplayedType() {
|
||||
const ressourceType = props.content.ressourceType;
|
||||
switch (ressourceType) {
|
||||
case 'cartes_blanches':
|
||||
props.content.displayedType = 'Carte blanche';
|
||||
break;
|
||||
case 'documents':
|
||||
props.content.displayedType = 'Document';
|
||||
break;
|
||||
case 'galleries':
|
||||
props.content.displayedType = 'Galerie';
|
||||
break;
|
||||
case 'videos':
|
||||
props.content.displayedType = 'Vidéo';
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const backToRessourcesLink = document.querySelectorAll('.retour > p');
|
||||
const baseUrl = window.location.protocol + "//" + window.location.host;
|
||||
handleClickableElements(backToRessourcesLink, store, router, baseUrl, siteName, mapStore);
|
||||
setDisplayedType();
|
||||
});
|
||||
</script>
|
@ -59,7 +59,7 @@ const handleImageClick = (event) => {
|
||||
const swiperEl = img.closest('swiper-container');
|
||||
swiperEl.querySelectorAll('swiper-slide').forEach((slide) => {
|
||||
const img = slide.querySelector('img');
|
||||
const altText = slide.querySelector('figcaption')?.textContent || '';
|
||||
const altText = slide.querySelector('figcaption')?.textContent || '';
|
||||
Object.values(props.partie.diaporama).forEach((image) => {
|
||||
if (image.url.medium === img.src) {
|
||||
swiperMedia.push({ src: image.url.large, alt: altText });
|
||||
|
@ -0,0 +1,88 @@
|
||||
<template>
|
||||
<div class="document"
|
||||
:style="{ '--couleur': couleur }">
|
||||
<div
|
||||
class="intro"
|
||||
v-html="partie.document.description">
|
||||
</div>
|
||||
<div class="document-grid">
|
||||
<figure>
|
||||
<a :href="partie.document.url" target="_blank">
|
||||
<img :src="partie.document.vignette.url" :alt="partie.document.vignette.alt" />
|
||||
</a>
|
||||
</figure>
|
||||
<div class="infos">
|
||||
<p>{{ partie.document.titre }}</p>
|
||||
<p>{{ partie.document.sousTitre }}</p>
|
||||
<p>{{ partie.document.auteurice }}</p>
|
||||
<p>{{ partie.document.date?.m }} {{ partie.document.date?.y }}</p>
|
||||
</div>
|
||||
<div class="download">
|
||||
<a :href="partie.document.url" target="_blank">Télécharger le document</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
partie: Object,
|
||||
couleur: String,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.intro {
|
||||
position: relative;
|
||||
padding-left: 1.8rem;
|
||||
margin: 3rem 0;
|
||||
&::before {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
background-color: var(--couleur);
|
||||
width: 0.8rem;
|
||||
height: 100%;
|
||||
margin-right: 1rem;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
.document-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-template-rows: auto auto;
|
||||
column-gap: 2rem;
|
||||
> figure {
|
||||
grid-column: 1 / 2;
|
||||
grid-row: 1 / 3;
|
||||
margin: 0;
|
||||
background-color: var(--couleur);
|
||||
img {
|
||||
padding: 2rem;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
object-fit: cover;
|
||||
transform: scale(1);
|
||||
transition: transform 0.2s ease-in-out;
|
||||
&:hover {
|
||||
transform: scale(1.02);
|
||||
}
|
||||
}
|
||||
}
|
||||
> .infos {
|
||||
> p {
|
||||
margin: 0;
|
||||
margin-bottom: 0.8rem;
|
||||
&:first-of-type {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
> .download {
|
||||
align-self: end;
|
||||
justify-self: end;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<div class="gallerie">
|
||||
<h3><p :style="{ background: `linear-gradient(transparent 70%, ${couleur} 70%)` }">{{ partie.gallerie.titre }}</p></h3>
|
||||
<div class="intro" v-html="partie.gallerie.introduction"></div>
|
||||
<div class="images-grid">
|
||||
<figure v-for="image in partie.gallerie.images">
|
||||
<img :src="image.url.medium" :alt="image.alt" @click="handleImageClick">
|
||||
<figcaption class="caption">{{ image.alt }}</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ImageModale
|
||||
:isOpen="isModaleOpen"
|
||||
:image="currentImage"
|
||||
:swiperContent="swiperPopupContent"
|
||||
@close="closeImageModale" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted } from 'vue';
|
||||
import { useImageModal } from '../../composables/useImageModale';
|
||||
import ImageModale from '../ImageModale.vue';
|
||||
|
||||
const props = defineProps({
|
||||
partie: Object,
|
||||
couleur: String,
|
||||
});
|
||||
|
||||
const {
|
||||
isModaleOpen,
|
||||
currentImage,
|
||||
swiperPopupContent,
|
||||
openImageModale,
|
||||
closeImageModale
|
||||
} = useImageModal();
|
||||
|
||||
const handleImageClick = (event) => {
|
||||
const img = event.target;
|
||||
if (img.tagName === 'IMG') {
|
||||
let swiperMedia = [];
|
||||
const imgGrid = img.closest('.images-grid');
|
||||
|
||||
imgGrid.querySelectorAll('figure').forEach((figure) => {
|
||||
const img = figure.querySelector('img');
|
||||
const altText = figure.querySelector('figcaption')?.textContent || '';
|
||||
Object.values(props.partie.gallerie.images).forEach((image) => {
|
||||
if (img.src === image.url.medium) {
|
||||
swiperMedia.push({ src: image.url.large, alt: altText });
|
||||
}
|
||||
});
|
||||
});
|
||||
console.log(swiperMedia);
|
||||
|
||||
|
||||
openImageModale(img.src, img.alt, swiperMedia);
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
setVerticalImages();
|
||||
});
|
||||
|
||||
function setVerticalImages() {
|
||||
const images = document.querySelectorAll('.images-grid figure img');
|
||||
images.forEach((img) => {
|
||||
if (img.naturalHeight > img.naturalWidth) {
|
||||
img.classList.add('vertical');
|
||||
img.closest('figure').querySelector('figcaption').style.textAlign = 'center';
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
@ -9,7 +9,7 @@ export function useImageModal() {
|
||||
const hamburger = document.querySelector('#hamburger');
|
||||
const menu = document.querySelector('#menu');
|
||||
|
||||
const openImageModale = (src, alt, swiperMedia) => {
|
||||
const openImageModale = (src, alt, swiperMedia) => {
|
||||
currentImage.value = { src, alt };
|
||||
swiperPopupContent.value = swiperMedia || [];
|
||||
isModaleOpen.value = true;
|
||||
|
@ -773,7 +773,8 @@ body{
|
||||
font-size: $labeur-font-size-desktop;
|
||||
width: $modale-width-desktop;
|
||||
}
|
||||
&:has(#centre-de-ressource) {
|
||||
&:has(#centre-de-ressource),
|
||||
&:has(#ressource-item-header) {
|
||||
@media screen and (min-width: $tablet-min-width) {
|
||||
left: 8vw;
|
||||
width: 84vw;
|
||||
@ -927,6 +928,9 @@ body{
|
||||
padding: 0 $modale-x-padding;
|
||||
padding-bottom: 5vh;
|
||||
box-sizing: border-box;
|
||||
&:has(#ressource-item-header) {
|
||||
padding-right: 50%;
|
||||
}
|
||||
> .partie,
|
||||
> #equipe {
|
||||
width: 100%;
|
||||
@ -954,7 +958,8 @@ body{
|
||||
}
|
||||
.partie-title,
|
||||
> .chiffres-cles,
|
||||
> .entretien {
|
||||
> .entretien,
|
||||
> .gallerie {
|
||||
> h3 {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
@ -1091,6 +1096,54 @@ body{
|
||||
> .videos iframe {
|
||||
margin: 2rem 0;
|
||||
}
|
||||
> .gallerie {
|
||||
> .intro {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
> .images-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
column-gap: 5rem;
|
||||
row-gap: 2rem;
|
||||
}
|
||||
figure {
|
||||
margin: 0;
|
||||
> img {
|
||||
cursor: pointer;
|
||||
transform: scale(1);
|
||||
transition: transform 0.2s ease-in-out;
|
||||
&:hover {
|
||||
transform: scale(1.02);
|
||||
}
|
||||
&.vertical {
|
||||
box-sizing: border-box;
|
||||
padding: 0 25%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
> .document {
|
||||
.download {
|
||||
> a {
|
||||
color: $main-color;
|
||||
text-decoration: none;
|
||||
font-size: $sm-font-size-mobile;
|
||||
background-color: $brand-color;
|
||||
font-weight: bold;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 1rem;
|
||||
display: inline-block;
|
||||
transform: scale(1);
|
||||
transition: transform 0.2s ease-in-out;
|
||||
&:hover {
|
||||
transform: scale(1.02);
|
||||
}
|
||||
@media screen and (min-width: $desktop-min-width) {
|
||||
font-size: $sm-font-size-desktop;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.caption {
|
||||
font-size: $sm-font-size-mobile;
|
||||
@ -1174,7 +1227,120 @@ body{
|
||||
}
|
||||
}
|
||||
#centre-de-ressource {
|
||||
background-color: red;
|
||||
> .intro {
|
||||
|
||||
}
|
||||
> .type-section {
|
||||
> h3 {
|
||||
display: inline-block;
|
||||
font-size: $l-font-size-mobile;
|
||||
font-family: 'Joost', sans-serif;
|
||||
margin: 0;
|
||||
margin-bottom: 2rem;
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
padding: 0 0.5rem;
|
||||
background: linear-gradient(transparent 70%, $brand-color 70%);
|
||||
@media screen and (min-width: $desktop-min-width) {
|
||||
font-size: $l-font-size-desktop;
|
||||
}
|
||||
}
|
||||
> .ressource-list > div {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
align-items: start;
|
||||
gap: 2rem;
|
||||
margin-bottom: 2.5rem;
|
||||
> .ressource-item {
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
cursor: pointer;
|
||||
transform: scale(1);
|
||||
transition: transform 0.2s ease-in-out;
|
||||
&:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
> figure {
|
||||
width: 40%;
|
||||
margin: 0;
|
||||
}
|
||||
> div {
|
||||
width: 50%;
|
||||
> h4 {
|
||||
font-size: $m-font-size-mobile;
|
||||
font-family: 'Joost', sans-serif;
|
||||
margin: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
@media screen and (min-width: $desktop-min-width) {
|
||||
font-size: $m-font-size-desktop;
|
||||
}
|
||||
}
|
||||
> p {
|
||||
margin: 0;
|
||||
font-size: $sm-font-size-mobile;
|
||||
@media screen and (min-width: $desktop-min-width) {
|
||||
font-size: $sm-font-size-desktop;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
> .button-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
> .ressource-button {
|
||||
display: inline;
|
||||
font-size: $sm-font-size-mobile;
|
||||
background-color: $brand-color;
|
||||
padding: 0.5rem 1.5rem;
|
||||
border-radius: 1rem;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s ease-in-out;
|
||||
transform: scale(1);
|
||||
&:hover {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
@media screen and (min-width: $desktop-min-width) {
|
||||
font-size: $sm-font-size-desktop;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
> #ressource-item-header {
|
||||
> .retour {
|
||||
margin-bottom: 1.5rem;
|
||||
> p {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
font-size: $sm-font-size-mobile;
|
||||
@media screen and (min-width: $desktop-min-width) {
|
||||
font-size: $sm-font-size-desktop;
|
||||
}
|
||||
}
|
||||
}
|
||||
> .type,
|
||||
> .meta {
|
||||
text-align: center;
|
||||
font-size: $m-font-size-mobile;
|
||||
font-family: 'Joost', sans-serif;
|
||||
@media screen and (min-width: $desktop-min-width) {
|
||||
font-size: $m-font-size-desktop;
|
||||
}
|
||||
}
|
||||
> .title {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
> h2 {
|
||||
display: inline-block;
|
||||
margin-top: 1rem;
|
||||
font-size: $xl-font-size-mobile;
|
||||
font-family: 'Joost', sans-serif;
|
||||
@media screen and (min-width: $desktop-min-width) {
|
||||
font-size: $xl-font-size-desktop;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
> .pieces-jointes {
|
||||
|
Loading…
x
Reference in New Issue
Block a user