generated from gitea_admin/default
lien artiste dans concert
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
:title="c.titre_concert"
|
||||
:dateISO="c.representation_concert?.[0]?.date_debut_representation"
|
||||
:dateLabel="formatDateLong(c.representation_concert?.[0]?.date_debut_representation)"
|
||||
:lien_billetterie_representation="c.representation_concert?.[0]?.lien_billetterie_representation"
|
||||
:lieu="c.representation_concert?.[0]?.lieu_representation?.nom_lieu"
|
||||
:adresse_lieu="c.representation_concert?.[0]?.lieu_representation?.nom_lieu"
|
||||
:description="c.resume_concert"
|
||||
|
||||
@@ -41,9 +41,11 @@
|
||||
<DsText as="p" tone="default" size="lg" spacing="space-0" class="programme_compositeur">
|
||||
{{ d.postes_artiste_ondif.map((p) => p.nom_poste).join(', ') }}
|
||||
</DsText>
|
||||
<DsText as="p" tone="default" size="lg" spacing="space-0" weight="semibold" class="programme_oeuvre">
|
||||
{{ d.nom_artiste_ondif }}
|
||||
</DsText>
|
||||
<button type="button" class="programme_artist_button" @click="openArtistOverlay(d, 'ondif')">
|
||||
<DsText as="span" tone="default" size="lg" spacing="space-0" weight="semibold" class="programme_oeuvre">
|
||||
{{ d.nom_artiste_ondif }}
|
||||
</DsText>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="directionsInvite.length">
|
||||
@@ -51,9 +53,11 @@
|
||||
<DsText as="p" tone="default" size="lg" spacing="space-0" v-if="d.postes_artiste_invite?.length" class="programme_compositeur direction">
|
||||
{{ d.postes_artiste_invite.map((p) => p.nom_poste).join(', ') }}
|
||||
</DsText>
|
||||
<DsText as="p" tone="default" size="lg" spacing="space-0" weight="semibold" class="programme_oeuvre">
|
||||
{{ d.nom_artiste_invite }}
|
||||
</DsText>
|
||||
<button type="button" class="programme_artist_button" @click="openArtistOverlay(d, 'invite')">
|
||||
<DsText as="span" tone="default" size="lg" spacing="space-0" weight="semibold" class="programme_oeuvre">
|
||||
{{ d.nom_artiste_invite }}
|
||||
</DsText>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="artistesOndif.length">
|
||||
@@ -61,9 +65,11 @@
|
||||
<DsText as="p" tone="default" size="lg" spacing="space-0" v-if="d.postes_artiste_ondif?.length" class="programme_compositeur">
|
||||
{{ d.postes_artiste_ondif.map((p) => p.nom_poste).join(', ') }}
|
||||
</DsText>
|
||||
<DsText as="p" tone="default" size="lg" spacing="space-0" weight="semibold" class="programme_oeuvre">
|
||||
{{ d.nom_artiste_ondif }}
|
||||
</DsText>
|
||||
<button type="button" class="programme_artist_button" @click="openArtistOverlay(d, 'ondif')">
|
||||
<DsText as="span" tone="default" size="lg" spacing="space-0" weight="semibold" class="programme_oeuvre">
|
||||
{{ d.nom_artiste_ondif }}
|
||||
</DsText>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="artistesInvite.length">
|
||||
@@ -71,9 +77,11 @@
|
||||
<DsText as="p" tone="default" size="lg" spacing="space-0" v-if="d.postes_artiste_invite?.length" class="programme_compositeur">
|
||||
{{ d.postes_artiste_invite.map((p) => p.nom_poste).join(', ') }}
|
||||
</DsText>
|
||||
<DsText as="p" tone="default" size="lg" spacing="space-0" weight="semibold" class="programme_oeuvre">
|
||||
{{ d.nom_artiste_invite }}
|
||||
</DsText>
|
||||
<button type="button" class="programme_artist_button" @click="openArtistOverlay(d, 'invite')">
|
||||
<DsText as="span" tone="default" size="lg" spacing="space-0" weight="semibold" class="programme_oeuvre">
|
||||
{{ d.nom_artiste_invite }}
|
||||
</DsText>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
@@ -190,22 +198,117 @@
|
||||
</div>
|
||||
</section>
|
||||
</PageSection>
|
||||
|
||||
<PageSection
|
||||
v-if="selectedArtist"
|
||||
padded_size=""
|
||||
:content="false"
|
||||
class="artist-overlay-section"
|
||||
@click="closeArtistOverlay"
|
||||
>
|
||||
<aside class="actus-overlay artist-overlay" aria-label="Fiche artiste" @click.stop>
|
||||
<button
|
||||
type="button"
|
||||
class="actus-overlay--close"
|
||||
aria-label="Fermer la fiche artiste"
|
||||
@click="closeArtistOverlay"
|
||||
>
|
||||
<svg class="actus-overlay--close-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
|
||||
<path d="M3 3h18v18H3V3Zm4.4 5.8 3.2 3.2-3.2 3.2 1.4 1.4 3.2-3.2 3.2 3.2 1.4-1.4-3.2-3.2 3.2-3.2-1.4-1.4-3.2 3.2-3.2-3.2-1.4 1.4Z" />
|
||||
</svg>
|
||||
<span class="sr-only">Fermer</span>
|
||||
</button>
|
||||
<div class="actus-overlay--content">
|
||||
<div v-if="selectedArtist.postesLabel" class="actus-overlay--metas">
|
||||
<DsText as="p" weight="bold" tone="default" class="uppercase">
|
||||
{{ selectedArtist.postesLabel }}
|
||||
</DsText>
|
||||
</div>
|
||||
<DsHeading ref="artistOverlayTitle" as="h1" tone="default" class="uppercase" tabindex="-1">
|
||||
{{ selectedArtist.name }}
|
||||
</DsHeading>
|
||||
</div>
|
||||
<section v-if="selectedArtist.illustration?.url" class="artist-overlay--image">
|
||||
<DsMedia
|
||||
:src="selectedArtist.illustration.url"
|
||||
:alt="selectedArtist.illustration.alternativeText || selectedArtist.name"
|
||||
ratio="3-4"
|
||||
/>
|
||||
</section>
|
||||
<div class="actus-overlay--content">
|
||||
<p v-if="selectedArtist.website">
|
||||
<a :href="selectedArtist.website" target="_blank" rel="noopener noreferrer">Site internet</a>
|
||||
</p>
|
||||
<StrapiBlocksConvert v-if="selectedArtist.descriptionBlocks?.length" :blocks="selectedArtist.descriptionBlocks" />
|
||||
</div>
|
||||
<section v-if="selectedArtist.galleryImages.length" class="img-gallery_wp">
|
||||
<div class="img-gallery">
|
||||
<DsMedia
|
||||
v-for="img in selectedArtist.galleryImages"
|
||||
:key="img.id || img.url"
|
||||
:src="img.url"
|
||||
:alt="img.alternativeText || selectedArtist.name"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
<section v-if="selectedArtist.videos.length" class="youtube_wp">
|
||||
<div class="youtube-list">
|
||||
<div v-for="v in selectedArtist.videos" :key="v.id" class="youtube-item">
|
||||
<iframe
|
||||
:src="v.src"
|
||||
title="Vidéo YouTube"
|
||||
loading="lazy"
|
||||
referrerpolicy="strict-origin-when-cross-origin"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allowfullscreen
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<div class="actus-overlay--content">
|
||||
<button
|
||||
type="button"
|
||||
class="actus-overlay--close actus-overlay--close-bottom"
|
||||
aria-label="Fermer la fiche artiste"
|
||||
@click="closeArtistOverlay"
|
||||
>
|
||||
<svg class="actus-overlay--close-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
|
||||
<path d="M3 3h18v18H3V3Zm4.4 5.8 3.2 3.2-3.2 3.2 1.4 1.4 3.2-3.2 3.2 3.2 1.4-1.4-3.2-3.2 3.2-3.2-1.4-1.4-3.2 3.2-3.2-3.2-1.4 1.4Z" />
|
||||
</svg>
|
||||
<span class="sr-only">Fermer</span>
|
||||
</button>
|
||||
</div>
|
||||
</aside>
|
||||
</PageSection>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, nextTick, onBeforeUnmount, ref, watch } from 'vue'
|
||||
import { formatDateLong } from "@/utils/dateFormat.js"
|
||||
import DsMedia from '@root/design-system/primitives/DsMedia.vue'
|
||||
import DsHeading from '@root/design-system/primitives/DsHeading.vue'
|
||||
import DsText from '@root/design-system/primitives/DsText.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const selectedArtistRequest = ref(null)
|
||||
const artistOverlayTitle = ref(null)
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// RÉCUPÉRATION DU CONTENU
|
||||
//////////////////////////////////////////////////////////////
|
||||
const concertSlug = computed(() => String(route.params.id || ''))
|
||||
const artisteOndifPopulate = {
|
||||
postes_artiste_ondif: true,
|
||||
image_illustration_artiste_ondif: true,
|
||||
}
|
||||
|
||||
const artisteInvitePopulate = {
|
||||
postes_artiste_invite: true,
|
||||
image_illustration_artiste_invite: true,
|
||||
}
|
||||
|
||||
const populate = {
|
||||
saison_concert: true,
|
||||
genre_concert: true,
|
||||
@@ -237,6 +340,31 @@
|
||||
upcomingOnly: false,
|
||||
})
|
||||
|
||||
const { items: artistesOndifItems } = useStrapi(
|
||||
"/api/__strapi__/artistes",
|
||||
{
|
||||
locale: "fr-FR",
|
||||
populate: artisteOndifPopulate,
|
||||
fetchAll: true,
|
||||
}
|
||||
)
|
||||
|
||||
const { items: artistesInviteItems } = useStrapi(
|
||||
"/api/__strapi__/artistesinvitees",
|
||||
{
|
||||
locale: "fr-FR",
|
||||
populate: artisteInvitePopulate,
|
||||
fetchAll: true,
|
||||
}
|
||||
)
|
||||
|
||||
const selectedArtist = computed(() => {
|
||||
if (!selectedArtistRequest.value) return null
|
||||
|
||||
const { artist, type } = selectedArtistRequest.value
|
||||
return normalizeArtist(findFullArtist(artist, type) || artist, type)
|
||||
})
|
||||
|
||||
const concert = computed(() => concerts.value?.[0] || {})
|
||||
|
||||
useSeoMeta({
|
||||
@@ -329,6 +457,116 @@
|
||||
})
|
||||
.filter(Boolean)
|
||||
)
|
||||
|
||||
function openArtistOverlay(artist, type) {
|
||||
selectedArtistRequest.value = { artist, type }
|
||||
}
|
||||
|
||||
function closeArtistOverlay() {
|
||||
selectedArtistRequest.value = null
|
||||
}
|
||||
|
||||
function normalizeArtist(artist, type) {
|
||||
const isInvite = type === 'invite'
|
||||
const name = isInvite ? artist?.nom_artiste_invite : artist?.nom_artiste_ondif
|
||||
const postes = extractStrapiList(isInvite ? artist?.postes_artiste_invite : artist?.postes_artiste_ondif)
|
||||
const illustration = normalizeStrapiMedia(isInvite ? artist?.image_illustration_artiste_invite : artist?.image_illustration_artiste_ondif)
|
||||
const galleryImages = extractStrapiList(isInvite ? artist?.images_artiste_invite : artist?.images_artiste_ondif)
|
||||
.map(normalizeStrapiMedia)
|
||||
.filter(Boolean)
|
||||
const youtubeItems = extractStrapiList(isInvite ? artist?.liens_youtube_artiste_invite : artist?.liens_youtube_artiste_ondif)
|
||||
|
||||
return {
|
||||
id: artist?.id || artist?.documentId || name,
|
||||
type,
|
||||
name: name || '',
|
||||
postesLabel: postes.map((p) => p?.nom_poste).filter(Boolean).join(', '),
|
||||
website: isInvite ? artist?.url_artiste_invite : artist?.url_artiste_ondif,
|
||||
descriptionBlocks: isInvite ? artist?.description_artiste_invite || [] : artist?.description_artiste_ondif || [],
|
||||
illustration,
|
||||
galleryImages,
|
||||
videos: youtubeItems
|
||||
.map((item) => {
|
||||
const id = getYoutubeId(item?.lien_youtube)
|
||||
if (!id) return null
|
||||
return {
|
||||
id: item.id || id,
|
||||
src: `https://www.youtube-nocookie.com/embed/${id}?rel=0&modestbranding=1&iv_load_policy=3&playsinline=1`,
|
||||
}
|
||||
})
|
||||
.filter(Boolean),
|
||||
}
|
||||
}
|
||||
|
||||
function findFullArtist(artist, type) {
|
||||
const rows = type === 'invite' ? artistesInviteItems.value : artistesOndifItems.value
|
||||
const slug = type === 'invite' ? artist?.slug_artiste_invite : artist?.slug_artiste_ondif
|
||||
const name = type === 'invite' ? artist?.nom_artiste_invite : artist?.nom_artiste_ondif
|
||||
|
||||
return rows.find((item) => {
|
||||
if (artist?.id && item.id === artist.id) return true
|
||||
if (slug && (type === 'invite' ? item.slug_artiste_invite : item.slug_artiste_ondif) === slug) return true
|
||||
if (name && (type === 'invite' ? item.nom_artiste_invite : item.nom_artiste_ondif) === name) return true
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
function extractStrapiList(value) {
|
||||
if (!value) return []
|
||||
if (Array.isArray(value)) return value.map(normalizeStrapiItem).filter(Boolean)
|
||||
if (value?.data) {
|
||||
const rows = Array.isArray(value.data) ? value.data : [value.data]
|
||||
return rows.map(normalizeStrapiItem).filter(Boolean)
|
||||
}
|
||||
if (typeof value === 'object') return [normalizeStrapiItem(value)].filter(Boolean)
|
||||
return []
|
||||
}
|
||||
|
||||
function normalizeStrapiItem(item) {
|
||||
if (!item || typeof item !== 'object') return null
|
||||
if (item.attributes && typeof item.attributes === 'object') {
|
||||
return { id: item.id, ...item.attributes }
|
||||
}
|
||||
return item
|
||||
}
|
||||
|
||||
function normalizeStrapiMedia(media) {
|
||||
if (media?.data) {
|
||||
const mediaItem = Array.isArray(media.data) ? media.data[0] : media.data
|
||||
return normalizeStrapiMedia(mediaItem)
|
||||
}
|
||||
|
||||
const item = normalizeStrapiItem(media)
|
||||
if (!item?.url) return null
|
||||
|
||||
return {
|
||||
id: item.id || item.documentId || item.url,
|
||||
url: item.url,
|
||||
alternativeText: item.alternativeText || item.caption || '',
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
selectedArtist,
|
||||
async (artist) => {
|
||||
if (import.meta.client) {
|
||||
document.body.style.overflow = artist ? 'hidden' : ''
|
||||
}
|
||||
|
||||
if (!artist) return
|
||||
|
||||
await nextTick()
|
||||
|
||||
const titleElement = artistOverlayTitle.value?.$el || artistOverlayTitle.value
|
||||
titleElement?.focus?.({ preventScroll: true })
|
||||
}
|
||||
)
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (import.meta.client) {
|
||||
document.body.style.overflow = ''
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@@ -610,6 +848,26 @@
|
||||
padding-left: 18px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.programme_artist_button {
|
||||
display: inline;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover,
|
||||
&:focus-visible {
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 3px;
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: 2px solid currentColor;
|
||||
outline-offset: 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.fiche_header_representation_wp {
|
||||
//background-color: aqua;
|
||||
@@ -678,6 +936,94 @@
|
||||
}
|
||||
}
|
||||
|
||||
.artist-overlay-section {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
overflow: hidden;
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
rgb(0 0 0 / 71%) 0%,
|
||||
rgb(0 0 0 / 71%) 20%,
|
||||
transparent 20%,
|
||||
transparent 100%
|
||||
);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.actus-overlay {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 80%;
|
||||
height: 100vh;
|
||||
padding: 2rem;
|
||||
overflow-y: auto;
|
||||
background: var(--c-backgroud-brandreverse);
|
||||
box-shadow: 0 0 40px rgb(0 0 0 / 18%);
|
||||
cursor: auto;
|
||||
|
||||
&--close {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
align-self: flex-end;
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
|
||||
&-icon {
|
||||
width: 4rem;
|
||||
height: 4rem;
|
||||
fill: currentColor;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
&--close-bottom {
|
||||
display: inline-flex;
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&--content {
|
||||
max-width: 680px;
|
||||
}
|
||||
|
||||
&--metas {
|
||||
display: flex;
|
||||
column-gap: 25px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.artist-overlay {
|
||||
&--image {
|
||||
max-width: 320px;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.img-gallery {
|
||||
grid-template-columns: repeat(2, minmax(0, 300px));
|
||||
justify-content: left;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 700px) {
|
||||
.actus-overlay {
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ============================ */
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
:title="c.titre_concert"
|
||||
:dateISO="c.representation_concert?.[0]?.date_debut_representation"
|
||||
:dateLabel="formatDateLong(c.representation_concert?.[0]?.date_debut_representation)"
|
||||
:lien_billetterie_representation="c.representation_concert?.[0]?.lien_billetterie_representation"
|
||||
:lieu="c.representation_concert?.[0]?.lieu_representation?.nom_lieu"
|
||||
:adresse_lieu="c.representation_concert?.[0]?.lieu_representation?.nom_lieu"
|
||||
:description="c.resume_concert"
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
:title="c.titre_concert"
|
||||
:dateISO="c.representation_concert?.[0]?.date_debut_representation"
|
||||
:dateLabel="formatDateLong(c.representation_concert?.[0]?.date_debut_representation)"
|
||||
:lien_billetterie_representation="c.representation_concert?.[0]?.lien_billetterie_representation"
|
||||
:lieu="c.representation_concert?.[0]?.lieu_representation?.nom_lieu"
|
||||
:adresse_lieu="c.representation_concert?.[0]?.lieu_representation?.nom_lieu"
|
||||
:description="c.resume_concert"
|
||||
|
||||
Reference in New Issue
Block a user