224 lines
5.9 KiB
JavaScript
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;
|