2024-10-23 09:48:27 +02:00
|
|
|
<template>
|
|
|
|
|
<aside id="comments-container" aria-labelledby="comments-label">
|
|
|
|
|
<h2 id="comments-label" class="sr-only">Commentaires</h2>
|
2024-11-06 14:59:17 +01:00
|
|
|
<div class="comments | flow" :class="{ empty: comments.length === 0 }">
|
2024-10-29 16:13:07 +01:00
|
|
|
<template v-if="comments">
|
|
|
|
|
<template v-if="!openedComment">
|
|
|
|
|
<Comment
|
2024-10-30 12:15:28 +01:00
|
|
|
v-for="(comment, commentIndex) in sortedComments"
|
2024-10-29 16:13:07 +01:00
|
|
|
:comment="comment"
|
2024-10-30 12:15:28 +01:00
|
|
|
:commentIndex="comments.length - commentIndex"
|
2024-10-29 16:13:07 +01:00
|
|
|
:key="comment.id"
|
|
|
|
|
@click="openedComment = comment"
|
2024-10-30 16:32:13 +01:00
|
|
|
@update:file="changeFile"
|
|
|
|
|
@close:comment="closeComment"
|
2024-10-29 16:13:07 +01:00
|
|
|
/>
|
|
|
|
|
</template>
|
|
|
|
|
<template v-else>
|
|
|
|
|
<button
|
|
|
|
|
class="btn | justify-start w-full | bg-white-10 text-white | px-8"
|
|
|
|
|
data-icon="chevron-single-left"
|
2024-10-29 16:51:31 +01:00
|
|
|
@click="
|
|
|
|
|
openedComment = null;
|
|
|
|
|
isAddOpen = false;
|
|
|
|
|
"
|
2024-10-29 12:11:50 +01:00
|
|
|
>
|
2024-10-29 16:13:07 +01:00
|
|
|
<span>Retour à la liste</span>
|
|
|
|
|
</button>
|
2024-10-30 16:32:13 +01:00
|
|
|
<Comment
|
|
|
|
|
:comment="openedComment"
|
|
|
|
|
data-opened="true"
|
|
|
|
|
@update:file="changeFile"
|
|
|
|
|
@close:comment="closeComment"
|
|
|
|
|
/>
|
2024-10-30 15:15:53 +01:00
|
|
|
<div v-if="sortedReplies.length > 0" class="replies | flow">
|
2024-10-29 16:13:07 +01:00
|
|
|
<Comment
|
2024-10-30 13:29:26 +01:00
|
|
|
v-for="(reply, commentIndex) in sortedReplies"
|
2024-10-29 16:13:07 +01:00
|
|
|
:comment="reply"
|
2024-10-30 13:29:26 +01:00
|
|
|
:commentIndex="sortedReplies.length - commentIndex"
|
2024-10-29 16:13:07 +01:00
|
|
|
:key="reply.id"
|
2024-10-30 16:32:13 +01:00
|
|
|
@update:file="changeFile"
|
|
|
|
|
@close:comment="closeComment"
|
2024-10-29 16:13:07 +01:00
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
2024-10-29 11:18:17 +01:00
|
|
|
</template>
|
2024-10-29 16:13:07 +01:00
|
|
|
<template v-else> état aucun commentaire </template>
|
2024-10-23 09:48:27 +02:00
|
|
|
</div>
|
|
|
|
|
<button
|
2024-10-29 17:26:23 +01:00
|
|
|
v-if="!openedComment && !isAddOpen"
|
2024-10-29 11:12:57 +01:00
|
|
|
id="create-comment"
|
2024-10-23 09:48:27 +02:00
|
|
|
class="btn btn--white-20 | w-full"
|
2024-11-06 16:38:46 +01:00
|
|
|
@click="toggleCommentPositionMode(true)"
|
2024-10-23 09:48:27 +02:00
|
|
|
>
|
|
|
|
|
Ajouter un commentaire
|
|
|
|
|
</button>
|
2024-10-29 12:11:50 +01:00
|
|
|
<button
|
2024-10-29 16:51:31 +01:00
|
|
|
v-else-if="openedComment && !isAddOpen"
|
2024-10-29 12:11:50 +01:00
|
|
|
id="reply-comment"
|
|
|
|
|
class="btn btn--white-20 | justify-start w-full | text-white-50"
|
2024-10-29 16:51:31 +01:00
|
|
|
@click="isAddOpen = true"
|
2024-10-29 12:11:50 +01:00
|
|
|
>
|
|
|
|
|
Répondre…
|
|
|
|
|
</button>
|
2024-11-06 14:59:17 +01:00
|
|
|
<!-- TODO: afficher #new-comment une fois le bouton Ajouter un commentaire cliqué -->
|
|
|
|
|
<div
|
2024-11-08 12:25:00 +01:00
|
|
|
hidden
|
2024-11-06 14:59:17 +01:00
|
|
|
id="new-comment"
|
|
|
|
|
class="bg-primary | text-sm text-white | rounded-lg | p-12"
|
|
|
|
|
>
|
2024-11-08 12:25:00 +01:00
|
|
|
<p class="flex justify-start | mb-12" data-icon="comment">
|
|
|
|
|
<strong>Nouveau commentaire</strong>
|
|
|
|
|
</p>
|
|
|
|
|
<p>
|
|
|
|
|
Dans la zone du contenu, cliquez où vous souhaitez positionner le
|
|
|
|
|
commentaire
|
|
|
|
|
</p>
|
2024-11-06 14:59:17 +01:00
|
|
|
</div>
|
2024-10-23 09:48:27 +02:00
|
|
|
<form
|
|
|
|
|
v-if="isAddOpen"
|
|
|
|
|
action=""
|
|
|
|
|
method="post"
|
2024-10-29 12:11:50 +01:00
|
|
|
class="flow | bg-white-20 | p-12 | rounded-xl"
|
2024-10-29 16:51:31 +01:00
|
|
|
@submit="handleSubmit"
|
2024-10-23 09:48:27 +02:00
|
|
|
>
|
|
|
|
|
<label class="sr-only" for="comment">Votre commentaire</label>
|
|
|
|
|
<textarea
|
|
|
|
|
name="comment"
|
|
|
|
|
id="comment"
|
2024-10-29 12:11:50 +01:00
|
|
|
placeholder="Ajouter un commentaire…"
|
|
|
|
|
rows="5"
|
2024-10-23 09:48:27 +02:00
|
|
|
class="text-sm | rounded-lg bg-black p-12"
|
|
|
|
|
v-model="newCommentText"
|
|
|
|
|
></textarea>
|
|
|
|
|
<footer class="flex">
|
2024-10-29 12:11:50 +01:00
|
|
|
<input type="submit" class="btn btn--tranparent" />
|
2024-10-29 16:51:31 +01:00
|
|
|
<button class="btn btn--white-10" @click="isAddOpen = false">
|
2024-10-23 15:45:04 +02:00
|
|
|
Annuler
|
|
|
|
|
</button>
|
2024-10-23 09:48:27 +02:00
|
|
|
</footer>
|
|
|
|
|
</form>
|
|
|
|
|
</aside>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup>
|
2024-10-23 11:32:51 +02:00
|
|
|
import dayjs from "dayjs";
|
|
|
|
|
import "dayjs/locale/fr";
|
|
|
|
|
import uniqid from "uniqid";
|
2024-11-06 16:38:46 +01:00
|
|
|
import { watch, ref } from "vue";
|
2024-10-23 09:48:27 +02:00
|
|
|
import { useUserStore } from "../../stores/user";
|
2024-10-23 11:32:51 +02:00
|
|
|
import { usePageStore } from "../../stores/page";
|
2024-10-28 17:50:40 +01:00
|
|
|
import { useApiStore } from "../../stores/api";
|
2024-10-29 16:13:07 +01:00
|
|
|
import Comment from "./Comment.vue";
|
2024-10-23 11:32:51 +02:00
|
|
|
|
|
|
|
|
dayjs.locale("fr");
|
2024-10-23 09:48:27 +02:00
|
|
|
|
2024-10-23 11:32:51 +02:00
|
|
|
const { currentPageIndex, file, comments } = defineProps({
|
|
|
|
|
currentPageIndex: Number,
|
|
|
|
|
file: Object,
|
2024-10-30 12:15:28 +01:00
|
|
|
comments: Array,
|
2024-10-23 09:48:27 +02:00
|
|
|
});
|
2024-10-29 11:18:17 +01:00
|
|
|
|
2024-10-23 09:48:27 +02:00
|
|
|
const { user } = useUserStore();
|
2024-10-23 11:32:51 +02:00
|
|
|
const { page } = usePageStore();
|
2024-10-28 17:50:40 +01:00
|
|
|
const api = useApiStore();
|
2024-10-23 09:48:27 +02:00
|
|
|
|
2024-10-29 11:18:17 +01:00
|
|
|
const openedComment = ref(null);
|
|
|
|
|
|
2024-11-06 16:38:46 +01:00
|
|
|
const newCommentPageIndex = ref(null);
|
|
|
|
|
const newCommentPosition = ref(null);
|
2024-10-23 09:48:27 +02:00
|
|
|
const newCommentText = ref("");
|
|
|
|
|
const isAddOpen = ref(false);
|
2024-10-23 15:45:04 +02:00
|
|
|
const emits = defineEmits(["update:file"]);
|
2024-11-08 12:25:00 +01:00
|
|
|
|
2024-11-06 16:38:46 +01:00
|
|
|
const sortedComments = ref(comments.reverse());
|
2024-11-08 12:25:00 +01:00
|
|
|
const sortedReplies = ref(null);
|
|
|
|
|
|
|
|
|
|
watch(openedComment, (newVal) => {
|
|
|
|
|
sortedReplies.value = newVal ? newVal.replies.slice().reverse() : null;
|
|
|
|
|
});
|
2024-11-11 17:12:26 +01:00
|
|
|
watch(
|
|
|
|
|
() => file,
|
|
|
|
|
(newVal) => {
|
|
|
|
|
if (newVal.comments) {
|
|
|
|
|
sortedComments.value = newVal.comments;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
);
|
2024-11-06 16:38:46 +01:00
|
|
|
watch(
|
|
|
|
|
() => comments,
|
|
|
|
|
(newVal) => {
|
|
|
|
|
sortedComments.value = newVal.reverse();
|
|
|
|
|
}
|
|
|
|
|
);
|
2024-11-11 17:12:26 +01:00
|
|
|
watch(isAddOpen, (newVal) => {
|
|
|
|
|
if (newVal) {
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
document.querySelector("textarea#comment").focus();
|
|
|
|
|
}, 100);
|
|
|
|
|
}
|
|
|
|
|
});
|
2024-11-06 16:38:46 +01:00
|
|
|
|
|
|
|
|
const viewContainer = document.querySelector(".vpv-pages-inner-container");
|
2024-10-23 09:48:27 +02:00
|
|
|
|
2024-11-11 17:12:26 +01:00
|
|
|
window.addEventListener("keydown", (event) => {
|
|
|
|
|
if (
|
|
|
|
|
isAddOpen.value &&
|
|
|
|
|
event.key === "Enter" &&
|
|
|
|
|
(event.metaKey || event.ctrlKey)
|
|
|
|
|
) {
|
|
|
|
|
handleSubmit();
|
|
|
|
|
}
|
|
|
|
|
});
|
2024-10-23 09:48:27 +02:00
|
|
|
// Functions
|
2024-11-11 17:12:26 +01:00
|
|
|
function handleSubmit(event = null) {
|
|
|
|
|
if (event) {
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
}
|
2024-10-23 11:32:51 +02:00
|
|
|
const date = dayjs().format();
|
2024-10-29 16:51:31 +01:00
|
|
|
const newComment = {
|
2024-10-23 11:32:51 +02:00
|
|
|
pageUri: page.uri + "/client-brief",
|
|
|
|
|
fileName: file.name,
|
2024-10-23 09:48:27 +02:00
|
|
|
userUuid: user.uuid,
|
|
|
|
|
text: newCommentText.value,
|
2024-10-23 11:32:51 +02:00
|
|
|
date,
|
2024-11-06 16:38:46 +01:00
|
|
|
position:
|
|
|
|
|
{
|
2024-11-11 15:06:56 +01:00
|
|
|
pageIndex: newCommentPageIndex.value,
|
2024-11-11 17:12:26 +01:00
|
|
|
x: newCommentPosition.value?.x,
|
|
|
|
|
y: newCommentPosition.value?.y,
|
2024-11-06 16:38:46 +01:00
|
|
|
} ?? false,
|
2024-10-23 11:32:51 +02:00
|
|
|
id: uniqid(),
|
|
|
|
|
};
|
2024-10-29 16:51:31 +01:00
|
|
|
if (openedComment.value) {
|
|
|
|
|
replyComment(newComment);
|
|
|
|
|
} else {
|
2024-11-11 15:06:56 +01:00
|
|
|
console.log(newComment);
|
2024-10-29 16:51:31 +01:00
|
|
|
addComment(newComment);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function replyComment(newComment) {
|
|
|
|
|
newComment.parentId = openedComment.value.id;
|
|
|
|
|
const newFile = await api.replyComment(newComment);
|
|
|
|
|
newCommentText.value = "";
|
|
|
|
|
isAddOpen.value = false;
|
2024-10-30 13:29:26 +01:00
|
|
|
openedComment.value = newFile.comments.find(
|
|
|
|
|
(item) => item.id === openedComment.value.id
|
|
|
|
|
);
|
2024-10-29 16:51:31 +01:00
|
|
|
emits("update:file", newFile);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function addComment(newComment) {
|
|
|
|
|
const newFile = await api.addComment(newComment);
|
2024-10-28 17:50:40 +01:00
|
|
|
newCommentText.value = "";
|
|
|
|
|
isAddOpen.value = false;
|
2024-10-29 11:12:57 +01:00
|
|
|
emits("update:file", newFile);
|
2024-10-23 11:32:51 +02:00
|
|
|
}
|
2024-10-30 16:32:13 +01:00
|
|
|
|
|
|
|
|
function changeFile(newFile) {
|
|
|
|
|
emits("update:file", newFile);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function closeComment() {
|
|
|
|
|
openedComment.value = null;
|
|
|
|
|
}
|
2024-11-06 16:38:46 +01:00
|
|
|
|
|
|
|
|
function toggleCommentPositionMode(enable) {
|
|
|
|
|
if (enable) {
|
|
|
|
|
viewContainer.classList.add("waiting-comment");
|
|
|
|
|
viewContainer.addEventListener("click", handleCommentPositionClick);
|
|
|
|
|
} else {
|
|
|
|
|
viewContainer.classList.remove("waiting-comment");
|
|
|
|
|
viewContainer.removeEventListener("click", handleCommentPositionClick);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handleCommentPositionClick(event) {
|
|
|
|
|
const pageContainer = event.target.closest(".page-inner-container");
|
2024-11-11 13:54:54 +01:00
|
|
|
if (!pageContainer) return;
|
2024-11-06 16:38:46 +01:00
|
|
|
|
|
|
|
|
const pageLabel = pageContainer
|
|
|
|
|
.closest(".vpv-page-inner-container")
|
|
|
|
|
.getAttribute("aria-label");
|
|
|
|
|
const pageIndex = pageLabel.charAt(pageLabel.length - 1);
|
|
|
|
|
|
|
|
|
|
newCommentPageIndex.value = parseInt(pageIndex);
|
|
|
|
|
|
|
|
|
|
const viewRect = viewContainer.getBoundingClientRect();
|
|
|
|
|
const pageRect = pageContainer.getBoundingClientRect();
|
|
|
|
|
const pageScroll = viewRect.top - pageRect.top;
|
|
|
|
|
|
|
|
|
|
const mouseTop = event.clientY;
|
|
|
|
|
const y = mouseTop - viewRect.top + pageScroll;
|
|
|
|
|
const x = event.clientX - pageRect.left;
|
|
|
|
|
|
2024-11-11 17:12:26 +01:00
|
|
|
const relativeX = (x / pageRect.width) * 100;
|
|
|
|
|
const relativeY = (y / pageRect.height) * 100;
|
|
|
|
|
|
2024-11-06 16:38:46 +01:00
|
|
|
newCommentPosition.value = {
|
2024-11-11 17:12:26 +01:00
|
|
|
x: relativeX,
|
|
|
|
|
y: relativeY,
|
2024-11-06 16:38:46 +01:00
|
|
|
};
|
|
|
|
|
isAddOpen.value = true;
|
|
|
|
|
toggleCommentPositionMode(false);
|
|
|
|
|
}
|
2024-10-23 09:48:27 +02:00
|
|
|
</script>
|
2024-10-30 15:15:53 +01:00
|
|
|
|
|
|
|
|
<style>
|
|
|
|
|
/* Comments */
|
|
|
|
|
#toggle-comments {
|
|
|
|
|
position: absolute;
|
|
|
|
|
right: var(--space-16);
|
|
|
|
|
bottom: var(--space-16);
|
|
|
|
|
padding: 0.625rem;
|
|
|
|
|
}
|
|
|
|
|
#comments-container {
|
|
|
|
|
background-color: black;
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 0;
|
|
|
|
|
right: 0;
|
|
|
|
|
bottom: 4.5rem;
|
2024-10-30 17:16:48 +01:00
|
|
|
width: var(--dialog-comments-w);
|
2024-10-30 15:15:53 +01:00
|
|
|
padding: var(--space-24) var(--space-32);
|
|
|
|
|
}
|
|
|
|
|
.comments {
|
2024-11-11 17:12:26 +01:00
|
|
|
scroll-behavior: smooth;
|
2024-10-30 15:15:53 +01:00
|
|
|
overflow-y: auto;
|
|
|
|
|
height: calc(100% - 3.5rem);
|
|
|
|
|
margin-bottom: 1rem;
|
|
|
|
|
margin-right: -2rem;
|
|
|
|
|
padding-right: 2rem;
|
|
|
|
|
}
|
2024-11-06 14:59:17 +01:00
|
|
|
.comments.empty::after {
|
2024-11-08 12:25:00 +01:00
|
|
|
content: "Partagez vos idées en ajoutant des commentaires";
|
2024-11-06 14:59:17 +01:00
|
|
|
height: 100%;
|
|
|
|
|
display: grid;
|
|
|
|
|
place-items: center;
|
|
|
|
|
text-align: center;
|
|
|
|
|
max-width: 24ch;
|
|
|
|
|
margin: auto;
|
|
|
|
|
font-size: var(--text-sm);
|
|
|
|
|
color: var(--color-grey-400);
|
|
|
|
|
background-image: var(--icon-comment);
|
|
|
|
|
background-position: center;
|
|
|
|
|
background-repeat: no-repeat;
|
|
|
|
|
}
|
|
|
|
|
.comments.empty::before {
|
|
|
|
|
--icon-size: 1.25rem;
|
|
|
|
|
--icon-color: var(--color-white);
|
|
|
|
|
content: "";
|
|
|
|
|
display: block;
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 50%;
|
|
|
|
|
left: 50%;
|
|
|
|
|
transform: translate(-50%, calc(-50% - 4.5rem));
|
|
|
|
|
width: var(--icon-size);
|
|
|
|
|
height: var(--icon-size);
|
|
|
|
|
background: var(--icon-color, currentColor);
|
|
|
|
|
mask-repeat: no-repeat;
|
|
|
|
|
mask-position: center;
|
|
|
|
|
mask-size: var(--icon-size);
|
|
|
|
|
mask-image: var(--icon-comment);
|
|
|
|
|
}
|
|
|
|
|
#new-comment {
|
|
|
|
|
position: absolute;
|
|
|
|
|
bottom: var(--space-24);
|
|
|
|
|
left: var(--space-32);
|
|
|
|
|
right: var(--space-32);
|
|
|
|
|
}
|
|
|
|
|
#new-comment [data-icon] {
|
|
|
|
|
--column-gap: var(--space-12);
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
}
|
2024-10-30 15:15:53 +01:00
|
|
|
#comments-container form {
|
|
|
|
|
--flow-space: 0.5rem;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
position: -webkit-sticky;
|
|
|
|
|
position: sticky;
|
|
|
|
|
bottom: 5.5rem;
|
|
|
|
|
}
|
|
|
|
|
#comments-container textarea {
|
|
|
|
|
position: sticky;
|
|
|
|
|
bottom: 0;
|
|
|
|
|
margin: 0;
|
|
|
|
|
resize: none;
|
|
|
|
|
background: none;
|
|
|
|
|
padding: 0;
|
|
|
|
|
color: var(--color-white);
|
|
|
|
|
}
|
|
|
|
|
#comments-container textarea:focus {
|
|
|
|
|
outline: none;
|
|
|
|
|
}
|
|
|
|
|
::placeholder {
|
|
|
|
|
color: var(--color-white-50);
|
|
|
|
|
}
|
|
|
|
|
#comments-container form footer {
|
|
|
|
|
gap: var(--space-12);
|
|
|
|
|
}
|
|
|
|
|
#comments-container form footer > * {
|
|
|
|
|
flex-grow: 1;
|
|
|
|
|
}
|
2024-11-06 16:38:46 +01:00
|
|
|
|
|
|
|
|
.vpv-pages-inner-container.waiting-comment .page-inner-container,
|
|
|
|
|
.vpv-pages-inner-container.waiting-comment .vpv-text-layer-text,
|
|
|
|
|
.vpv-pages-inner-container.waiting-comment .vpv-text-layer-wrapper {
|
|
|
|
|
cursor: cell !important;
|
|
|
|
|
}
|
2024-10-30 16:32:13 +01:00
|
|
|
</style>
|