nouveau-theatre-de-besancon/site/plugins/front-comments/src/front/classes/Suggestion.js
2024-09-10 17:14:38 +02:00

224 lines
5.9 KiB
JavaScript

import dayjs from "dayjs";
import Store from "../store.js";
class Suggestion {
constructor(
author,
context,
target,
suggestion,
fieldName,
id = Date.now(),
date = dayjs().format("DD/MM/YY"),
time = dayjs().format("HH:mm")
) {
this.author = author;
this.context = context;
this.target = target;
this.suggestion = suggestion;
this.fieldName = fieldName;
this.id = id;
this.date = date;
this.time = time;
this.node;
this.highlightTargetInField();
}
highlightTargetInField() {
const highlight = document.querySelector(".fc__suggestion--edit");
if (highlight) {
this.node = highlight;
highlight.classList.remove("fc__suggestion--edit");
highlight.classList.add("fc__suggestion");
} else {
const { target, fieldName } = this;
const fieldElement = this.getFieldElement(fieldName);
const textNodes = this.getTextNodes(fieldElement);
this.highlightTarget(textNodes, target);
}
if (this.node) {
this.node.addEventListener("mouseenter", () => {
this._bubble = this.createBubble();
this.node.appendChild(this._bubble);
});
this.node.addEventListener("mouseleave", () => {
this.node.removeChild(this._bubble);
});
} else {
this.remove();
}
}
getFieldElement(fieldName) {
return document.querySelector(`[data-field-name="${fieldName}"]`);
}
getTextNodes(fieldElement) {
const textNodes = [];
const childNodes = Array.from(fieldElement.childNodes);
childNodes.forEach((node) => {
if (node.nodeType === Node.TEXT_NODE && node.textContent.trim() !== "") {
textNodes.push(node);
} else if (node.hasChildNodes()) {
textNodes.push(...this.getChildTextNodes(node));
}
});
return textNodes;
}
highlightTarget(textNodes, target) {
textNodes.forEach((textNode) => {
const targetIndex = textNode.textContent.indexOf(target);
if (
targetIndex >= 0 &&
this.matchContext(textNode, target, targetIndex)
) {
this.node = this.insertHighlightedTarget(textNode, target, targetIndex);
} else {
// this.remove();
}
});
}
matchContext(textNode, target, targetIndex) {
const beforeMatch = textNode.textContent.slice(
Math.max(0, targetIndex - 30),
targetIndex
);
const afterMatch = textNode.textContent.slice(
targetIndex + target.length,
targetIndex + target.length + 30
);
return (
beforeMatch === this.context.before && afterMatch === this.context.after
);
}
insertHighlightedTarget(textNode, target, targetIndex) {
const highlightedTarget = this.createHighlightedTarget(target);
const beforeTarget = this.createTextNode(
textNode.textContent.slice(0, targetIndex)
);
const afterTarget = this.createTextNode(
textNode.textContent.slice(targetIndex + target.length)
);
textNode.parentNode.insertBefore(beforeTarget, textNode);
textNode.parentNode.insertBefore(highlightedTarget, textNode);
textNode.parentNode.insertBefore(afterTarget, textNode);
textNode.remove();
return highlightedTarget;
}
createHighlightedTarget(target) {
const highlightedTarget = document.createElement("span");
highlightedTarget.classList.add("fc__suggestion");
highlightedTarget.textContent = target;
return highlightedTarget;
}
createTextNode(text) {
return document.createTextNode(text);
}
getChildTextNodes(node) {
const textNodes = [];
const childNodes = Array.from(node.childNodes);
childNodes.forEach((childNode) => {
if (
childNode.nodeType === Node.TEXT_NODE &&
childNode.textContent.trim() !== ""
) {
textNodes.push(childNode);
} else if (childNode.hasChildNodes()) {
textNodes.push(...this.getChildTextNodes(childNode));
}
});
return textNodes;
}
createBubble() {
const bubble = document.createElement("div");
bubble.className = "fc__suggestion-bubble";
bubble.innerHTML = `<p>${this.suggestion}</p>`;
bubble.appendChild(this.createBtns());
return bubble;
}
createBtns() {
const btns = document.createElement("div");
btns.classList.add("fc__suggestion-bubble__btns");
const decline = document.createElement("button");
decline.classList.add("fc__btn");
decline.innerHTML = `<img class="fc__icon" src="${Store.filePath}/front-comments/icons/decline.svg" />`;
const accept = document.createElement("button");
accept.classList.add("fc__btn");
accept.innerHTML = `<img class="fc__icon" src="${Store.filePath}/front-comments/icons/accept.svg" />`;
decline.addEventListener("click", () => {
this.remove();
});
accept.addEventListener("click", () => {
this.push();
});
btns.appendChild(decline);
btns.appendChild(accept);
return btns;
}
remove() {
Store.suggestions = Store.suggestions.filter(
(suggestion) => suggestion.id != this.id
);
if (this.node !== undefined) {
this.node.parentNode.removeChild(this.node);
}
Store.save("suggestions");
}
push() {
const field = this.getFieldElement(this.fieldName);
const data = {};
data[this.fieldName] = field.dataset.content.replace(
this.target,
this.suggestion
);
const init = {
method: "PATCH",
headers: {
"X-CSRF": Store.csrf,
},
body: JSON.stringify(data).replaceAll("_", ""),
};
fetch("/api/pages/" + Store.page, init)
.then((response) => response.json())
.then((response) => {
console.log(response);
console.log("Page comments successfully updated.");
})
.catch((error) => {
console.log(error);
});
Store.suggestions = Store.suggestions.filter(
(suggestion) => suggestion.id != this.id
);
Store.save("suggestions");
this.node.outerHTML = this.suggestion;
}
}
export default Suggestion;