designtopack/src/components/project/virtual-sample/DynamicView.vue
isUnknown 8eaa893994 #170
2025-10-02 15:12:20 +02:00

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>