186 lines
5 KiB
Vue
186 lines
5 KiB
Vue
<template>
|
|
<div class="dialog__inner">
|
|
<header class="tracks-header | flex">
|
|
<div class="tracks">
|
|
<Selector
|
|
v-for="(track, index) in tracks"
|
|
:key="track.slug"
|
|
:label="track.title"
|
|
:items="track.variations"
|
|
:isCompareModeEnabled="isCompareModeEnabled"
|
|
:index="index"
|
|
/>
|
|
</div>
|
|
|
|
<button
|
|
class="btn | ml-auto"
|
|
:class="{ 'btn--secondary': isCompareModeEnabled }"
|
|
@click="isCompareModeEnabled = !isCompareModeEnabled"
|
|
>
|
|
<span>{{
|
|
isCompareModeEnabled
|
|
? 'Quitter le mode comparer'
|
|
: 'Comparer les pistes'
|
|
}}</span>
|
|
</button>
|
|
</header>
|
|
|
|
<div class="track">
|
|
<template
|
|
v-for="activeTrack in activeTracks"
|
|
:key="activeTrack.slug || activeTrack.title"
|
|
>
|
|
<!-- accès sûr avec optional chaining -->
|
|
<Interactive360
|
|
v-if="activeTrack?.files?.length > 1"
|
|
:activeTrack="activeTrack"
|
|
/>
|
|
<SingleImage
|
|
v-else-if="activeTrack?.files?.length === 1"
|
|
:file="activeTrack.files[0]"
|
|
:backgroundColor="activeTrack.backgroundColor"
|
|
/>
|
|
<div v-else class="track-empty | bg-white rounded-xl w-full p-32">
|
|
<p>Contenu non disponible pour cette piste</p>
|
|
</div>
|
|
</template>
|
|
|
|
<div
|
|
v-if="isCompareModeEnabled && activeTracks.length < 2"
|
|
class="track-empty | bg-white rounded-xl w-full p-32"
|
|
>
|
|
<p>Sélectionnez sur la piste que vous souhaitez comparer</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { computed, watch, onMounted, onBeforeMount } from 'vue';
|
|
import { storeToRefs } from 'pinia';
|
|
import { usePageStore } from '../../../stores/page';
|
|
import { useDialogStore } from '../../../stores/dialog';
|
|
import { useVirtualSampleStore } from '../../../stores/virtualSample';
|
|
import { useRoute } from 'vue-router';
|
|
import Interactive360 from './Interactive360.vue';
|
|
import SingleImage from './SingleImage.vue';
|
|
import Selector from '../../Selector.vue';
|
|
import slugify from 'slugify';
|
|
|
|
const route = useRoute();
|
|
|
|
const { page } = storeToRefs(usePageStore());
|
|
const { isCommentsOpen, isCommentPanelEnabled, activeTracks, openedFile } =
|
|
storeToRefs(useDialogStore());
|
|
const { isCompareModeEnabled } = storeToRefs(useVirtualSampleStore());
|
|
|
|
// computed tracks à partir de la structure page
|
|
const tracks = computed(() => {
|
|
const raw =
|
|
page.value.steps.find((step) => step.slug === 'virtual-sample')?.files
|
|
?.dynamic || {};
|
|
const list = [];
|
|
|
|
for (const key in raw) {
|
|
list.push({
|
|
title: key,
|
|
slug: slugify(key),
|
|
variations: raw[key] || [],
|
|
});
|
|
}
|
|
|
|
return list;
|
|
});
|
|
|
|
// ---------- INITIALISATION ----------
|
|
// onBeforeMount : on initialise toujours activeTracks avec une VARIATION (jamais un track)
|
|
onBeforeMount(() => {
|
|
// essayer la hash en priorité
|
|
let initialVariation = null;
|
|
|
|
if (route?.hash && route.hash.length > 0) {
|
|
const variations = tracks.value.flatMap((t) => t.variations || []);
|
|
initialVariation =
|
|
variations.find((v) => v.slug === route.hash.substring(1)) || null;
|
|
}
|
|
|
|
// fallback : première variation du premier track
|
|
if (!initialVariation) {
|
|
initialVariation = tracks.value[0]?.variations?.[0] || null;
|
|
}
|
|
|
|
if (initialVariation) {
|
|
activeTracks.value = [initialVariation];
|
|
} else {
|
|
activeTracks.value = []; // aucun contenu disponible
|
|
}
|
|
});
|
|
|
|
// scroll si hash présent
|
|
onMounted(() => {
|
|
if (route.query?.comments) isCommentsOpen.value = true;
|
|
if (!route?.hash || route.hash.length === 0) return;
|
|
|
|
const selector = route.hash.replace('#', '#track--');
|
|
const targetBtn = document.querySelector(selector);
|
|
if (targetBtn) targetBtn.scrollIntoView();
|
|
});
|
|
|
|
// ---------- COMPUTED / WATCH ----------
|
|
|
|
const isSingleImage = computed(() => {
|
|
return (
|
|
activeTracks.value?.length === 1 &&
|
|
activeTracks.value[0]?.files?.length === 1
|
|
);
|
|
});
|
|
|
|
const singleFile = computed(() => {
|
|
return isSingleImage.value ? activeTracks.value[0].files[0] : null;
|
|
});
|
|
|
|
watch(
|
|
singleFile,
|
|
(newValue) => {
|
|
if (newValue) openedFile.value = newValue;
|
|
},
|
|
{ immediate: true }
|
|
);
|
|
|
|
// gestion du mode comparaison : fermer les commentaires, etc.
|
|
watch(isCompareModeEnabled, (newValue) => {
|
|
if (newValue) {
|
|
isCommentsOpen.value = false;
|
|
isCommentPanelEnabled.value = false;
|
|
} else {
|
|
isCommentPanelEnabled.value = true;
|
|
}
|
|
|
|
// quand on quitte le mode comparaison on retire l'élément secondaire si nécessaire
|
|
if (!newValue && activeTracks.value.length === 2) {
|
|
activeTracks.value.pop();
|
|
}
|
|
});
|
|
|
|
// ---------- UTIL / helper ----------
|
|
function getCommentsCount(track) {
|
|
if (!track || !Array.isArray(track.files)) return undefined;
|
|
let count = 0;
|
|
for (const file of track.files) {
|
|
count += file?.comments?.length || 0;
|
|
}
|
|
return count > 0 ? count : undefined;
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
.track figure {
|
|
position: relative;
|
|
}
|
|
|
|
.track .drag-zone {
|
|
position: absolute;
|
|
inset: 0;
|
|
z-index: 2;
|
|
}
|
|
</style>
|