siyuan/app/src/protyle/wysiwyg/getBlock.ts

220 lines
9.3 KiB
TypeScript

import {hasClosestBlock, hasClosestByAttribute} from "../util/hasClosest";
import {Constants} from "../../constants";
export const getPreviousBlock = (element: Element) => {
let parentElement = element;
while (parentElement) {
if (parentElement.previousElementSibling && parentElement.previousElementSibling.getAttribute("data-node-id")) {
return parentElement.previousElementSibling;
}
const pElement = hasClosestBlock(parentElement.parentElement);
if (pElement) {
parentElement = pElement;
} else {
return false;
}
}
};
export const getLastBlock = (element: Element) => {
let lastElement;
Array.from(element.querySelectorAll("[data-node-id]")).reverse().find(item => {
if (!hasClosestByAttribute(item.parentElement, "data-type", "NodeBlockQueryEmbed")) {
lastElement = item;
return true;
}
});
return lastElement || element;
};
export const getFirstBlock = (element: Element) => {
let firstElement;
Array.from(element.querySelectorAll("[data-node-id]")).find(item => {
if (!hasClosestByAttribute(item.parentElement, "data-type", "NodeBlockQueryEmbed") &&
!item.classList.contains("li") && !item.classList.contains("sb")) {
firstElement = item;
return true;
}
});
return firstElement || element;
};
export const getNextBlock = (element: Element) => {
let parentElement = element;
while (parentElement) {
if (parentElement.nextElementSibling && !parentElement.nextElementSibling.classList.contains("protyle-attr")) {
return parentElement.nextElementSibling as HTMLElement;
}
const pElement = hasClosestBlock(parentElement.parentElement);
if (pElement) {
parentElement = pElement;
} else {
return false;
}
}
return false;
};
export const getNoContainerElement = (element: Element) => {
let childElement = element;
while (childElement) {
if (childElement.classList.contains("list") || childElement.classList.contains("li") || childElement.classList.contains("bq") || childElement.classList.contains("sb")) {
childElement = childElement.querySelector("[data-node-id]");
} else {
return childElement;
}
}
return false;
};
export const getContenteditableElement = (element: Element) => {
if (!element || (element.getAttribute("contenteditable") === "true") && !element.classList.contains("protyle-wysiwyg")) {
return element;
}
return element.querySelector('[contenteditable="true"]');
};
export const isNotEditBlock = (element: Element) => {
return ["NodeBlockQueryEmbed", "NodeThematicBreak", "NodeMathBlock", "NodeHTMLBlock", "NodeIFrame", "NodeWidget", "NodeVideo", "NodeAudio"].includes(element.getAttribute("data-type")) ||
(element.getAttribute("data-type") === "NodeCodeBlock" && element.classList.contains("render-node"));
};
export const getTopEmptyElement = (element: Element) => {
let topElement = element;
while (topElement.parentElement && !topElement.parentElement.classList.contains("protyle-wysiwyg")) {
if (!topElement.parentElement.getAttribute("data-node-id")) {
topElement = topElement.parentElement;
} else {
let hasText = false;
Array.from(topElement.parentElement.querySelectorAll('[contenteditable="true"]')).find(item => {
if (item.textContent.replace(Constants.ZWSP, "").replace("\n", "") !== "") {
hasText = true;
return true;
}
});
if (hasText || topElement.previousElementSibling?.getAttribute("data-node-id") ||
topElement.nextElementSibling?.getAttribute("data-node-id")) {
break;
} else {
topElement = topElement.parentElement;
}
}
}
return topElement;
};
export const getTopAloneElement = (topSourceElement: Element) => {
if ("NodeBlockquote" === topSourceElement.parentElement.getAttribute("data-type") && topSourceElement.parentElement.childElementCount === 2) {
while (!topSourceElement.parentElement.classList.contains("protyle-wysiwyg")) {
if (topSourceElement.parentElement.getAttribute("data-type") === "NodeBlockquote" && topSourceElement.parentElement.childElementCount === 2) {
topSourceElement = topSourceElement.parentElement;
} else {
topSourceElement = getTopAloneElement(topSourceElement);
break;
}
}
} else if ("NodeSuperBlock" === topSourceElement.parentElement.getAttribute("data-type") && topSourceElement.parentElement.childElementCount === 2) {
while (!topSourceElement.parentElement.classList.contains("protyle-wysiwyg")) {
if (topSourceElement.parentElement.getAttribute("data-type") === "NodeSuperBlock" && topSourceElement.parentElement.childElementCount === 2) {
topSourceElement = topSourceElement.parentElement;
} else {
topSourceElement = getTopAloneElement(topSourceElement);
break;
}
}
} else if ("NodeListItem" === topSourceElement.parentElement.getAttribute("data-type") && topSourceElement.parentElement.childElementCount === 3) {
while (!topSourceElement.parentElement.classList.contains("protyle-wysiwyg")) {
if (topSourceElement.parentElement.getAttribute("data-type") === "NodeListItem" && topSourceElement.parentElement.childElementCount === 3) {
topSourceElement = topSourceElement.parentElement;
} else if (topSourceElement.parentElement.getAttribute("data-type") === "NodeList" && topSourceElement.parentElement.childElementCount === 2) {
topSourceElement = topSourceElement.parentElement;
} else {
topSourceElement = getTopAloneElement(topSourceElement);
break;
}
}
} else if ("NodeList" === topSourceElement.parentElement.getAttribute("data-type") && topSourceElement.parentElement.childElementCount === 2) {
while (!topSourceElement.parentElement.classList.contains("protyle-wysiwyg")) {
if ("NodeList" === topSourceElement.parentElement.getAttribute("data-type") && topSourceElement.parentElement.childElementCount === 2) {
topSourceElement = topSourceElement.parentElement;
} else if (topSourceElement.parentElement.getAttribute("data-type") === "NodeListItem" && topSourceElement.parentElement.childElementCount === 3) {
topSourceElement = topSourceElement.parentElement;
} else {
topSourceElement = getTopAloneElement(topSourceElement);
break;
}
}
}
return topSourceElement;
};
export const hasNextSibling = (element: Node) => {
let nextSibling = element.nextSibling;
while (nextSibling) {
if (nextSibling.textContent === "" && nextSibling.nodeType === 3) {
nextSibling = nextSibling.nextSibling;
} else {
return nextSibling;
}
}
return false;
};
export const hasPreviousSibling = (element: Node) => {
let previousSibling = element.previousSibling;
while (previousSibling) {
if (previousSibling.textContent === "" && previousSibling.nodeType === 3) {
previousSibling = previousSibling.previousSibling;
} else {
return previousSibling;
}
}
return false;
};
export const getNextFileLi = (current: Element) => {
let nextElement = current.nextElementSibling;
if (nextElement) {
if (nextElement.tagName === "LI") {
return nextElement;
} else if (nextElement.tagName === "UL") {
return nextElement.firstElementChild;
}
return false;
}
nextElement = current.parentElement;
while (nextElement.tagName === "UL") {
if (!nextElement.nextElementSibling) {
nextElement = nextElement.parentElement;
} else if (nextElement.nextElementSibling.tagName === "LI") {
return nextElement.nextElementSibling;
} else if (nextElement.nextElementSibling.tagName === "UL") {
return nextElement.nextElementSibling.firstElementChild;
}
}
return false;
};
export const getPreviousFileLi = (current: Element) => {
let previousElement = current.previousElementSibling;
if (previousElement) {
if (previousElement.tagName === "LI") {
return previousElement;
} else if (previousElement.tagName === "UL") {
return previousElement.lastElementChild;
}
return false;
}
previousElement = current.parentElement;
while (previousElement.tagName === "UL") {
if (!previousElement.previousElementSibling) {
previousElement = previousElement.parentElement;
} else if (previousElement.previousElementSibling.tagName === "LI") {
return previousElement.previousElementSibling;
} else if (previousElement.previousElementSibling.tagName === "UL") {
const liElements = previousElement.previousElementSibling.querySelectorAll(".b3-list-item");
return liElements[liElements.length - 1];
}
}
return false;
};