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 from 'dayjs';
|
||||||
import 'dayjs/locale/fr';
|
import 'dayjs/locale/fr';
|
||||||
import uniqid from 'uniqid';
|
import uniqid from 'uniqid';
|
||||||
import { watch, ref, computed } from 'vue';
|
import { watch, ref, computed, onBeforeUnmount } from 'vue';
|
||||||
import { useUserStore } from '../../stores/user';
|
import { useUserStore } from '../../stores/user';
|
||||||
import { usePageStore } from '../../stores/page';
|
import { usePageStore } from '../../stores/page';
|
||||||
import { useApiStore } from '../../stores/api';
|
import { useApiStore } from '../../stores/api';
|
||||||
|
|
@ -158,10 +158,12 @@ watch(isAddOpen, (newVal) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const viewContainer =
|
const viewContainer = computed(() => {
|
||||||
openedFile.value?.type === 'document'
|
if (!openedFile.value) return null;
|
||||||
|
return openedFile.value.type === 'document'
|
||||||
? document.querySelector('.vpv-pages-inner-container')
|
? document.querySelector('.vpv-pages-inner-container')
|
||||||
: document.querySelector('.track');
|
: document.querySelector('.track');
|
||||||
|
});
|
||||||
|
|
||||||
window.addEventListener('keydown', (event) => {
|
window.addEventListener('keydown', (event) => {
|
||||||
if (
|
if (
|
||||||
|
|
@ -256,20 +258,8 @@ function closeComment() {
|
||||||
openedComment.value = null;
|
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) {
|
function handleCommentPositionClick(event) {
|
||||||
if (openedFile.value.type === 'document') {
|
if (openedFile.value?.type === 'document') {
|
||||||
prepareDraftCommentInPdf(event);
|
prepareDraftCommentInPdf(event);
|
||||||
} else {
|
} else {
|
||||||
prepareDraftCommentInImage(event);
|
prepareDraftCommentInImage(event);
|
||||||
|
|
@ -278,6 +268,21 @@ function handleCommentPositionClick(event) {
|
||||||
toggleCommentPositionMode(false);
|
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) {
|
function prepareDraftCommentInPdf(event) {
|
||||||
const pageContainer = event.target.closest('.page-inner-container');
|
const pageContainer = event.target.closest('.page-inner-container');
|
||||||
if (!pageContainer || !viewContainer) return;
|
if (!pageContainer || !viewContainer) return;
|
||||||
|
|
@ -287,7 +292,10 @@ function prepareDraftCommentInPdf(event) {
|
||||||
.getAttribute('aria-label');
|
.getAttribute('aria-label');
|
||||||
const pageIndex = pageLabel.charAt(pageLabel.length - 1);
|
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 pageRect = pageContainer.getBoundingClientRect();
|
||||||
const pageScroll = viewRect.top - pageRect.top;
|
const pageScroll = viewRect.top - pageRect.top;
|
||||||
|
|
||||||
|
|
@ -310,7 +318,9 @@ function prepareDraftCommentInPdf(event) {
|
||||||
function prepareDraftCommentInImage(event) {
|
function prepareDraftCommentInImage(event) {
|
||||||
if (!viewContainer) return;
|
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 mouseTop = event.clientY;
|
||||||
const mouseLeft = event.clientX;
|
const mouseLeft = event.clientX;
|
||||||
|
|
@ -339,4 +349,11 @@ function showCorrespondingView() {
|
||||||
(file) => file.uuid === openedComment.value.location.file.uuid
|
(file) => file.uuid === openedComment.value.location.file.uuid
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
const container = viewContainer.value;
|
||||||
|
if (container) {
|
||||||
|
container.removeEventListener('click', handleCommentPositionClick);
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,9 @@
|
||||||
:items="track.variations"
|
:items="track.variations"
|
||||||
:isCompareModeEnabled="isCompareModeEnabled"
|
:isCompareModeEnabled="isCompareModeEnabled"
|
||||||
:index="index"
|
:index="index"
|
||||||
@update:selectedItems="selectTrack"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="btn | ml-auto"
|
class="btn | ml-auto"
|
||||||
:class="{ 'btn--secondary': isCompareModeEnabled }"
|
:class="{ 'btn--secondary': isCompareModeEnabled }"
|
||||||
|
|
@ -26,17 +26,25 @@
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="track">
|
<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
|
<Interactive360
|
||||||
v-if="activeTrack.files.length > 1"
|
v-if="activeTrack?.files?.length > 1"
|
||||||
:activeTrack="activeTrack"
|
:activeTrack="activeTrack"
|
||||||
/>
|
/>
|
||||||
<SingleImage
|
<SingleImage
|
||||||
v-else
|
v-else-if="activeTrack?.files?.length === 1"
|
||||||
:file="activeTrack.files[0]"
|
:file="activeTrack.files[0]"
|
||||||
:backgroundColor="activeTrack.backgroundColor"
|
: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>
|
</template>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="isCompareModeEnabled && activeTracks.length < 2"
|
v-if="isCompareModeEnabled && activeTracks.length < 2"
|
||||||
class="track-empty | bg-white rounded-xl w-full p-32"
|
class="track-empty | bg-white rounded-xl w-full p-32"
|
||||||
|
|
@ -46,6 +54,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, watch, onMounted, onBeforeMount } from 'vue';
|
import { computed, watch, onMounted, onBeforeMount } from 'vue';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
|
|
@ -63,57 +72,62 @@ const route = useRoute();
|
||||||
const { page } = storeToRefs(usePageStore());
|
const { page } = storeToRefs(usePageStore());
|
||||||
const { isCommentsOpen, isCommentPanelEnabled, activeTracks, openedFile } =
|
const { isCommentsOpen, isCommentPanelEnabled, activeTracks, openedFile } =
|
||||||
storeToRefs(useDialogStore());
|
storeToRefs(useDialogStore());
|
||||||
|
|
||||||
const { isCompareModeEnabled } = storeToRefs(useVirtualSampleStore());
|
const { isCompareModeEnabled } = storeToRefs(useVirtualSampleStore());
|
||||||
|
|
||||||
const rawTracks = page.value.steps.find(
|
// computed tracks à partir de la structure page
|
||||||
(step) => step.slug === 'virtual-sample'
|
|
||||||
).files.dynamic;
|
|
||||||
|
|
||||||
onBeforeMount(() => {
|
|
||||||
const firstTrack = rawTracks[Object.keys(rawTracks)[0]];
|
|
||||||
const firstVariation = firstTrack[0];
|
|
||||||
activeTracks.value = [firstVariation];
|
|
||||||
|
|
||||||
// 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]];
|
|
||||||
// }
|
|
||||||
});
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
if (route.hash.length === 0) return;
|
|
||||||
|
|
||||||
const selector = route.hash.replace('#', '#track--');
|
|
||||||
const targetBtn = document.querySelector(selector);
|
|
||||||
if (targetBtn) {
|
|
||||||
targetBtn.scrollIntoView();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const tracks = computed(() => {
|
const tracks = computed(() => {
|
||||||
const rawTracks = page.value.steps.find(
|
const raw =
|
||||||
(step) => step.slug === 'virtual-sample'
|
page.value.steps.find((step) => step.slug === 'virtual-sample')?.files
|
||||||
).files.dynamic;
|
?.dynamic || {};
|
||||||
|
const list = [];
|
||||||
|
|
||||||
const tracks = [];
|
for (const key in raw) {
|
||||||
|
list.push({
|
||||||
for (const key in rawTracks) {
|
|
||||||
tracks.push({
|
|
||||||
title: key,
|
title: key,
|
||||||
slug: slugify(key),
|
slug: slugify(key),
|
||||||
variations: rawTracks[key],
|
variations: raw[key] || [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return tracks;
|
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(() => {
|
const isSingleImage = computed(() => {
|
||||||
return (
|
return (
|
||||||
activeTracks.value?.length === 1 &&
|
activeTracks.value?.length === 1 &&
|
||||||
|
|
@ -122,19 +136,18 @@ const isSingleImage = computed(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const singleFile = computed(() => {
|
const singleFile = computed(() => {
|
||||||
return isSingleImage.value && activeTracks.value[0].files[0];
|
return isSingleImage.value ? activeTracks.value[0].files[0] : null;
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
singleFile,
|
singleFile,
|
||||||
(newValue) => {
|
(newValue) => {
|
||||||
if (newValue) {
|
if (newValue) openedFile.value = newValue;
|
||||||
openedFile.value = newValue;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// gestion du mode comparaison : fermer les commentaires, etc.
|
||||||
watch(isCompareModeEnabled, (newValue) => {
|
watch(isCompareModeEnabled, (newValue) => {
|
||||||
if (newValue) {
|
if (newValue) {
|
||||||
isCommentsOpen.value = false;
|
isCommentsOpen.value = false;
|
||||||
|
|
@ -143,12 +156,15 @@ watch(isCompareModeEnabled, (newValue) => {
|
||||||
isCommentPanelEnabled.value = true;
|
isCommentPanelEnabled.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// quand on quitte le mode comparaison on retire l'élément secondaire si nécessaire
|
||||||
if (!newValue && activeTracks.value.length === 2) {
|
if (!newValue && activeTracks.value.length === 2) {
|
||||||
activeTracks.value.pop();
|
activeTracks.value.pop();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ---------- UTIL / helper ----------
|
||||||
function getCommentsCount(track) {
|
function getCommentsCount(track) {
|
||||||
|
if (!track || !Array.isArray(track.files)) return undefined;
|
||||||
let count = 0;
|
let count = 0;
|
||||||
for (const file of track.files) {
|
for (const file of track.files) {
|
||||||
count += file?.comments?.length || 0;
|
count += file?.comments?.length || 0;
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ export const useUserStore = defineStore('user', () => {
|
||||||
const { projects } = storeToRefs(useProjectsStore());
|
const { projects } = storeToRefs(useProjectsStore());
|
||||||
|
|
||||||
const notifications = computed(() => {
|
const notifications = computed(() => {
|
||||||
return projects.value.flatMap((project) => {
|
return projects.value?.flatMap((project) => {
|
||||||
if (!project.notifications) return [];
|
if (!project.notifications) return [];
|
||||||
|
|
||||||
return project.notifications
|
return project.notifications
|
||||||
|
|
|
||||||
|
|
@ -12,12 +12,12 @@ export const useVirtualSampleStore = defineStore('virtual-sample', () => {
|
||||||
const isLoopAnimationEnabled = ref(false);
|
const isLoopAnimationEnabled = ref(false);
|
||||||
const isDownloadTriggered = ref(false);
|
const isDownloadTriggered = ref(false);
|
||||||
|
|
||||||
const step = computed(() => {
|
const step = computed(
|
||||||
return page.value.steps.find((step) => step.id === 'virtualSample');
|
() => page.value?.steps?.find((s) => s.id === 'virtualSample') ?? []
|
||||||
});
|
);
|
||||||
|
|
||||||
const activeTab = computed(() =>
|
const activeTab = computed(() =>
|
||||||
step.value.files.dynamic ? 'dynamic' : 'static'
|
step.value.files?.dynamic ? 'dynamic' : 'static'
|
||||||
);
|
);
|
||||||
|
|
||||||
const allVariations = computed(() =>
|
const allVariations = computed(() =>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue