add front-comments
This commit is contained in:
parent
c3ea78cab5
commit
c9f4af7e58
53 changed files with 2921 additions and 1 deletions
224
site/plugins/front-comments/src/front/classes/Suggestion.js
Normal file
224
site/plugins/front-comments/src/front/classes/Suggestion.js
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
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;
|
||||
Loading…
Add table
Add a link
Reference in a new issue