This commit is contained in:
parent
5deb07f09d
commit
8eaa893994
4 changed files with 104 additions and 71 deletions
|
|
@ -114,7 +114,7 @@
|
|||
import dayjs from 'dayjs';
|
||||
import 'dayjs/locale/fr';
|
||||
import uniqid from 'uniqid';
|
||||
import { watch, ref, computed } from 'vue';
|
||||
import { watch, ref, computed, onBeforeUnmount } from 'vue';
|
||||
import { useUserStore } from '../../stores/user';
|
||||
import { usePageStore } from '../../stores/page';
|
||||
import { useApiStore } from '../../stores/api';
|
||||
|
|
@ -158,10 +158,12 @@ watch(isAddOpen, (newVal) => {
|
|||
}
|
||||
});
|
||||
|
||||
const viewContainer =
|
||||
openedFile.value?.type === 'document'
|
||||
const viewContainer = computed(() => {
|
||||
if (!openedFile.value) return null;
|
||||
return openedFile.value.type === 'document'
|
||||
? document.querySelector('.vpv-pages-inner-container')
|
||||
: document.querySelector('.track');
|
||||
});
|
||||
|
||||
window.addEventListener('keydown', (event) => {
|
||||
if (
|
||||
|
|
@ -256,20 +258,8 @@ function closeComment() {
|
|||
openedComment.value = null;
|
||||
}
|
||||
|
||||
function toggleCommentPositionMode(enable) {
|
||||
if (enable) {
|
||||
waitingForCommentPosition.value = true;
|
||||
viewContainer.classList.add('waiting-comment');
|
||||
viewContainer.addEventListener('click', handleCommentPositionClick);
|
||||
} else {
|
||||
waitingForCommentPosition.value = false;
|
||||
viewContainer.classList.remove('waiting-comment');
|
||||
viewContainer.removeEventListener('click', handleCommentPositionClick);
|
||||
}
|
||||
}
|
||||
|
||||
function handleCommentPositionClick(event) {
|
||||
if (openedFile.value.type === 'document') {
|
||||
if (openedFile.value?.type === 'document') {
|
||||
prepareDraftCommentInPdf(event);
|
||||
} else {
|
||||
prepareDraftCommentInImage(event);
|
||||
|
|
@ -278,6 +268,21 @@ function handleCommentPositionClick(event) {
|
|||
toggleCommentPositionMode(false);
|
||||
}
|
||||
|
||||
function toggleCommentPositionMode(enable) {
|
||||
const container = viewContainer.value;
|
||||
if (!container) return; // sécurité
|
||||
|
||||
if (enable) {
|
||||
waitingForCommentPosition.value = true;
|
||||
container.classList.add('waiting-comment');
|
||||
container.addEventListener('click', handleCommentPositionClick);
|
||||
} else {
|
||||
waitingForCommentPosition.value = false;
|
||||
container.classList.remove('waiting-comment');
|
||||
container.removeEventListener('click', handleCommentPositionClick);
|
||||
}
|
||||
}
|
||||
|
||||
function prepareDraftCommentInPdf(event) {
|
||||
const pageContainer = event.target.closest('.page-inner-container');
|
||||
if (!pageContainer || !viewContainer) return;
|
||||
|
|
@ -287,7 +292,10 @@ function prepareDraftCommentInPdf(event) {
|
|||
.getAttribute('aria-label');
|
||||
const pageIndex = pageLabel.charAt(pageLabel.length - 1);
|
||||
|
||||
const viewRect = viewContainer.getBoundingClientRect();
|
||||
const container = viewContainer.value;
|
||||
if (!container) return;
|
||||
const viewRect = container.getBoundingClientRect();
|
||||
|
||||
const pageRect = pageContainer.getBoundingClientRect();
|
||||
const pageScroll = viewRect.top - pageRect.top;
|
||||
|
||||
|
|
@ -310,7 +318,9 @@ function prepareDraftCommentInPdf(event) {
|
|||
function prepareDraftCommentInImage(event) {
|
||||
if (!viewContainer) return;
|
||||
|
||||
const imageRect = viewContainer.getBoundingClientRect();
|
||||
const container = viewContainer.value;
|
||||
if (!container) return; // sécurité
|
||||
const imageRect = container.getBoundingClientRect();
|
||||
|
||||
const mouseTop = event.clientY;
|
||||
const mouseLeft = event.clientX;
|
||||
|
|
@ -339,4 +349,11 @@ function showCorrespondingView() {
|
|||
(file) => file.uuid === openedComment.value.location.file.uuid
|
||||
);
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
const container = viewContainer.value;
|
||||
if (container) {
|
||||
container.removeEventListener('click', handleCommentPositionClick);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@
|
|||
:items="track.variations"
|
||||
:isCompareModeEnabled="isCompareModeEnabled"
|
||||
:index="index"
|
||||
@update:selectedItems="selectTrack"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="btn | ml-auto"
|
||||
:class="{ 'btn--secondary': isCompareModeEnabled }"
|
||||
|
|
@ -26,17 +26,25 @@
|
|||
</header>
|
||||
|
||||
<div class="track">
|
||||
<template v-for="activeTrack in activeTracks" :key="activeTrack.title">
|
||||
<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"
|
||||
v-if="activeTrack?.files?.length > 1"
|
||||
:activeTrack="activeTrack"
|
||||
/>
|
||||
<SingleImage
|
||||
v-else
|
||||
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"
|
||||
|
|
@ -46,6 +54,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, watch, onMounted, onBeforeMount } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
|
@ -63,56 +72,61 @@ const route = useRoute();
|
|||
const { page } = storeToRefs(usePageStore());
|
||||
const { isCommentsOpen, isCommentPanelEnabled, activeTracks, openedFile } =
|
||||
storeToRefs(useDialogStore());
|
||||
|
||||
const { isCompareModeEnabled } = storeToRefs(useVirtualSampleStore());
|
||||
|
||||
const rawTracks = page.value.steps.find(
|
||||
(step) => step.slug === 'virtual-sample'
|
||||
).files.dynamic;
|
||||
// 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 = [];
|
||||
|
||||
onBeforeMount(() => {
|
||||
const firstTrack = rawTracks[Object.keys(rawTracks)[0]];
|
||||
const firstVariation = firstTrack[0];
|
||||
activeTracks.value = [firstVariation];
|
||||
for (const key in raw) {
|
||||
list.push({
|
||||
title: key,
|
||||
slug: slugify(key),
|
||||
variations: raw[key] || [],
|
||||
});
|
||||
}
|
||||
|
||||
// TO RE-ENABLE
|
||||
// if (route.hash.length > 0) {
|
||||
// const trackToOpen = tracks.value.find(
|
||||
// (track) => track.slug === route.hash.substring(1)
|
||||
// );
|
||||
// activeTracks.value = [trackToOpen];
|
||||
// } else {
|
||||
// activeTracks.value = [tracks.value[0]];
|
||||
// }
|
||||
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.hash.length === 0) return;
|
||||
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();
|
||||
}
|
||||
if (targetBtn) targetBtn.scrollIntoView();
|
||||
});
|
||||
|
||||
const tracks = computed(() => {
|
||||
const rawTracks = page.value.steps.find(
|
||||
(step) => step.slug === 'virtual-sample'
|
||||
).files.dynamic;
|
||||
|
||||
const tracks = [];
|
||||
|
||||
for (const key in rawTracks) {
|
||||
tracks.push({
|
||||
title: key,
|
||||
slug: slugify(key),
|
||||
variations: rawTracks[key],
|
||||
});
|
||||
}
|
||||
|
||||
return tracks;
|
||||
});
|
||||
// ---------- COMPUTED / WATCH ----------
|
||||
|
||||
const isSingleImage = computed(() => {
|
||||
return (
|
||||
|
|
@ -122,19 +136,18 @@ const isSingleImage = computed(() => {
|
|||
});
|
||||
|
||||
const singleFile = computed(() => {
|
||||
return isSingleImage.value && activeTracks.value[0].files[0];
|
||||
return isSingleImage.value ? activeTracks.value[0].files[0] : null;
|
||||
});
|
||||
|
||||
watch(
|
||||
singleFile,
|
||||
(newValue) => {
|
||||
if (newValue) {
|
||||
openedFile.value = newValue;
|
||||
}
|
||||
if (newValue) openedFile.value = newValue;
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
// gestion du mode comparaison : fermer les commentaires, etc.
|
||||
watch(isCompareModeEnabled, (newValue) => {
|
||||
if (newValue) {
|
||||
isCommentsOpen.value = false;
|
||||
|
|
@ -143,12 +156,15 @@ watch(isCompareModeEnabled, (newValue) => {
|
|||
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;
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export const useUserStore = defineStore('user', () => {
|
|||
const { projects } = storeToRefs(useProjectsStore());
|
||||
|
||||
const notifications = computed(() => {
|
||||
return projects.value.flatMap((project) => {
|
||||
return projects.value?.flatMap((project) => {
|
||||
if (!project.notifications) return [];
|
||||
|
||||
return project.notifications
|
||||
|
|
|
|||
|
|
@ -12,12 +12,12 @@ export const useVirtualSampleStore = defineStore('virtual-sample', () => {
|
|||
const isLoopAnimationEnabled = ref(false);
|
||||
const isDownloadTriggered = ref(false);
|
||||
|
||||
const step = computed(() => {
|
||||
return page.value.steps.find((step) => step.id === 'virtualSample');
|
||||
});
|
||||
const step = computed(
|
||||
() => page.value?.steps?.find((s) => s.id === 'virtualSample') ?? []
|
||||
);
|
||||
|
||||
const activeTab = computed(() =>
|
||||
step.value.files.dynamic ? 'dynamic' : 'static'
|
||||
step.value.files?.dynamic ? 'dynamic' : 'static'
|
||||
);
|
||||
|
||||
const allVariations = computed(() =>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue