mirror of
https://github.com/siyuan-note/siyuan.git
synced 2026-01-06 08:48:49 +01:00
1596 lines
85 KiB
TypeScript
1596 lines
85 KiB
TypeScript
import {focusBlock, focusByRange, getRangeByPoint} from "./selection";
|
||
import {
|
||
hasClosestBlock,
|
||
hasClosestByAttribute,
|
||
hasClosestByClassName,
|
||
hasClosestByTag,
|
||
hasTopClosestByAttribute,
|
||
isInEmbedBlock
|
||
} from "./hasClosest";
|
||
import {Constants} from "../../constants";
|
||
import {paste} from "./paste";
|
||
import {cancelSB, genEmptyElement, genSBElement, insertEmptyBlock} from "../../block/util";
|
||
import {transaction, turnsIntoOneTransaction} from "../wysiwyg/transaction";
|
||
import {getTopAloneElement} from "../wysiwyg/getBlock";
|
||
import {updateListOrder} from "../wysiwyg/list";
|
||
import {fetchPost, fetchSyncPost} from "../../util/fetch";
|
||
import {onGet} from "./onGet";
|
||
/// #if !MOBILE
|
||
import {getAllEditor} from "../../layout/getAll";
|
||
import {updatePanelByEditor} from "../../editor/util";
|
||
/// #endif
|
||
import {blockRender} from "../render/blockRender";
|
||
import {uploadLocalFiles} from "../upload";
|
||
import {insertHTML} from "./insertHTML";
|
||
import {isBrowser} from "../../util/functions";
|
||
import {hideElements} from "../ui/hideElements";
|
||
import {insertAttrViewBlockAnimation} from "../render/av/row";
|
||
import {dragUpload} from "../render/av/asset";
|
||
import * as dayjs from "dayjs";
|
||
import {setFold, zoomOut} from "../../menus/protyle";
|
||
/// #if !BROWSER
|
||
import {webUtils} from "electron";
|
||
/// #endif
|
||
import {addDragFill, getTypeByCellElement} from "../render/av/cell";
|
||
import {processClonePHElement} from "../render/util";
|
||
import {insertGalleryItemAnimation} from "../render/av/gallery/item";
|
||
import {clearSelect} from "./clearSelect";
|
||
import {dragoverTab} from "../render/av/view";
|
||
|
||
// position: afterbegin 为拖拽成超级块; "afterend", "beforebegin" 一般拖拽
|
||
const moveTo = async (protyle: IProtyle, sourceElements: Element[], targetElement: Element,
|
||
isSameDoc: boolean, position: InsertPosition, isCopy: boolean) => {
|
||
const doOperations: IOperation[] = [];
|
||
const undoOperations: IOperation[] = [];
|
||
const copyFoldHeadingIds: { newId: string, oldId: string }[] = [];
|
||
const targetId = targetElement.getAttribute("data-node-id");
|
||
const newSourceElements: Element[] = [];
|
||
let tempTargetElement = targetElement;
|
||
let isSameLi = true;
|
||
sourceElements.find(item => {
|
||
if (!item.classList.contains("li") || !targetElement.classList.contains("li") ||
|
||
targetElement.getAttribute("data-subtype") !== item.getAttribute("data-subtype")) {
|
||
isSameLi = false;
|
||
return true;
|
||
}
|
||
});
|
||
let newListElement: Element;
|
||
let newListId: string;
|
||
const orderListElements: { [key: string]: Element } = {};
|
||
for (let index = sourceElements.length - 1; index >= 0; index--) {
|
||
const item = sourceElements[index];
|
||
const id = item.getAttribute("data-node-id");
|
||
const parentID = item.parentElement.getAttribute("data-node-id") || protyle.block.parentID || protyle.block.rootID;
|
||
if (item.getAttribute("data-type") === "NodeListItem" && !newListId && !isSameLi) {
|
||
newListId = Lute.NewNodeID();
|
||
newListElement = document.createElement("div");
|
||
newListElement.innerHTML = `<div data-subtype="${item.getAttribute("data-subtype")}" data-node-id="${newListId}" data-type="NodeList" class="list"><div class="protyle-attr" contenteditable="false">${Constants.ZWSP}</div></div>`;
|
||
newListElement = newListElement.firstElementChild;
|
||
doOperations.push({
|
||
action: "insert",
|
||
data: newListElement.outerHTML,
|
||
id: newListId,
|
||
previousID: position === "afterbegin" ? null : (position === "afterend" ? targetId : tempTargetElement.previousElementSibling?.getAttribute("data-node-id")),
|
||
parentID: position === "afterbegin" ? targetId : (tempTargetElement.parentElement?.getAttribute("data-node-id") || protyle.block.parentID || protyle.block.rootID),
|
||
});
|
||
undoOperations.push({
|
||
action: "delete",
|
||
id: newListId
|
||
});
|
||
tempTargetElement.insertAdjacentElement(position, newListElement);
|
||
newSourceElements.push(newListElement);
|
||
}
|
||
const copyNewId = Lute.NewNodeID();
|
||
if (isCopy && item.getAttribute("data-type") === "NodeHeading" && item.getAttribute("fold") === "1") {
|
||
copyFoldHeadingIds.push({
|
||
newId: copyNewId,
|
||
oldId: id
|
||
});
|
||
}
|
||
|
||
let copyElement;
|
||
if (isCopy) {
|
||
undoOperations.push({
|
||
action: "delete",
|
||
id: copyNewId,
|
||
});
|
||
} else {
|
||
undoOperations.push({
|
||
action: "move",
|
||
id,
|
||
previousID: item.previousElementSibling?.getAttribute("data-node-id"),
|
||
parentID,
|
||
});
|
||
}
|
||
if (!isSameDoc && !isCopy) {
|
||
// 打开两个相同的文档
|
||
const sameElement = protyle.wysiwyg.element.querySelector(`[data-node-id="${id}"]`);
|
||
if (sameElement) {
|
||
sameElement.remove();
|
||
}
|
||
}
|
||
if (isCopy) {
|
||
copyElement = item.cloneNode(true) as HTMLElement;
|
||
copyElement.setAttribute("data-node-id", copyNewId);
|
||
copyElement.querySelectorAll("[data-node-id]").forEach((e) => {
|
||
const newId = Lute.NewNodeID();
|
||
e.setAttribute("data-node-id", newId);
|
||
e.setAttribute("updated", newId.split("-")[0]);
|
||
});
|
||
if (newListId) {
|
||
newListElement.insertAdjacentElement("afterbegin", copyElement);
|
||
doOperations.push({
|
||
action: "insert",
|
||
id: copyNewId,
|
||
data: copyElement.outerHTML,
|
||
parentID: newListId,
|
||
});
|
||
} else {
|
||
tempTargetElement.insertAdjacentElement(position, copyElement);
|
||
doOperations.push({
|
||
action: "insert",
|
||
id: copyNewId,
|
||
data: copyElement.outerHTML,
|
||
previousID: position === "afterbegin" ? null : (position === "afterend" ? targetId : copyElement.previousElementSibling?.getAttribute("data-node-id")), // 不能使用常量,移动后会被修改
|
||
parentID: position === "afterbegin" ? targetId : (copyElement.parentElement?.getAttribute("data-node-id") || protyle.block.parentID || protyle.block.rootID),
|
||
});
|
||
newSourceElements.push(copyElement);
|
||
}
|
||
} else {
|
||
const topSourceElement = getTopAloneElement(item);
|
||
const oldSourceParentElement = item.parentElement;
|
||
if (item.classList.contains("li") && item.getAttribute("data-subtype") === "o") {
|
||
orderListElements[item.parentElement.getAttribute("data-node-id")] = item.parentElement;
|
||
}
|
||
if (newListId) {
|
||
newListElement.insertAdjacentElement("afterbegin", item);
|
||
doOperations.push({
|
||
action: "move",
|
||
id,
|
||
parentID: newListId,
|
||
});
|
||
} else {
|
||
tempTargetElement.insertAdjacentElement(position, item);
|
||
doOperations.push({
|
||
action: "move",
|
||
id,
|
||
previousID: position === "afterbegin" ? null : (position === "afterend" ? targetId : item.previousElementSibling?.getAttribute("data-node-id")), // 不能使用常量,移动后会被修改
|
||
parentID: position === "afterbegin" ? targetId : (item.parentElement?.getAttribute("data-node-id") || protyle.block.parentID || protyle.block.rootID),
|
||
});
|
||
newSourceElements.push(item);
|
||
}
|
||
|
||
if (topSourceElement !== item) {
|
||
// 删除空元素
|
||
doOperations.push({
|
||
action: "delete",
|
||
id: topSourceElement.getAttribute("data-node-id"),
|
||
});
|
||
undoOperations.push({
|
||
action: "insert",
|
||
data: topSourceElement.outerHTML,
|
||
id: topSourceElement.getAttribute("data-node-id"),
|
||
previousID: topSourceElement.previousElementSibling?.getAttribute("data-node-id"),
|
||
parentID: topSourceElement.parentElement?.getAttribute("data-node-id") || protyle.block.parentID || protyle.block.rootID
|
||
});
|
||
const topSourceParentElement = topSourceElement.parentElement;
|
||
topSourceElement.remove();
|
||
if (!isSameDoc) {
|
||
// 打开两个相同的文档
|
||
const sameElement = protyle.wysiwyg.element.querySelector(`[data-node-id="${topSourceElement.getAttribute("data-node-id")}"]`);
|
||
if (sameElement) {
|
||
sameElement.remove();
|
||
}
|
||
}
|
||
if (topSourceParentElement.classList.contains("sb") && topSourceParentElement.childElementCount === 2) {
|
||
// 拖拽后,sb 只剩下一个元素
|
||
if (isSameDoc) {
|
||
const sbData = await cancelSB(protyle, topSourceParentElement);
|
||
doOperations.push(sbData.doOperations[0], sbData.doOperations[1]);
|
||
undoOperations.push(sbData.undoOperations[1], sbData.undoOperations[0]);
|
||
} else {
|
||
/// #if !MOBILE
|
||
const allEditor = getAllEditor();
|
||
for (let i = 0; i < allEditor.length; i++) {
|
||
if (allEditor[i].protyle.element.contains(topSourceParentElement)) {
|
||
const otherSbData = await cancelSB(allEditor[i].protyle, topSourceParentElement);
|
||
doOperations.push(otherSbData.doOperations[0], otherSbData.doOperations[1]);
|
||
undoOperations.push(otherSbData.undoOperations[1], otherSbData.undoOperations[0]);
|
||
// 需清空操作栈,否则撤销到移动出去的块的操作会抛异常
|
||
allEditor[i].protyle.undo.clear();
|
||
break;
|
||
}
|
||
}
|
||
/// #endif
|
||
}
|
||
}
|
||
} else if (oldSourceParentElement.classList.contains("sb") && oldSourceParentElement.childElementCount === 2) {
|
||
// 拖拽后,sb 只剩下一个元素
|
||
if (isSameDoc) {
|
||
const sbData = await cancelSB(protyle, oldSourceParentElement);
|
||
doOperations.push(sbData.doOperations[0], sbData.doOperations[1]);
|
||
undoOperations.push(sbData.undoOperations[1], sbData.undoOperations[0]);
|
||
} else {
|
||
/// #if !MOBILE
|
||
const allEditor = getAllEditor();
|
||
for (let i = 0; i < allEditor.length; i++) {
|
||
if (allEditor[i].protyle.element.contains(oldSourceParentElement)) {
|
||
const otherSbData = await cancelSB(allEditor[i].protyle, oldSourceParentElement);
|
||
doOperations.push(otherSbData.doOperations[0], otherSbData.doOperations[1]);
|
||
undoOperations.push(otherSbData.undoOperations[1], otherSbData.undoOperations[0]);
|
||
// 需清空操作栈,否则撤销到移动出去的块的操作会抛异常
|
||
allEditor[i].protyle.undo.clear();
|
||
break;
|
||
}
|
||
}
|
||
/// #endif
|
||
}
|
||
} else if (oldSourceParentElement.classList.contains("protyle-wysiwyg") && oldSourceParentElement.childElementCount === 0) {
|
||
/// #if !MOBILE
|
||
// 拖拽后,根文档原内容为空
|
||
getAllEditor().find(item => {
|
||
if (item.protyle.element.contains(oldSourceParentElement)) {
|
||
if (!item.protyle.block.showAll) {
|
||
const newId = Lute.NewNodeID();
|
||
doOperations.splice(0, 0, {
|
||
action: "insert",
|
||
id: newId,
|
||
data: genEmptyElement(false, false, newId).outerHTML,
|
||
parentID: item.protyle.block.parentID
|
||
});
|
||
undoOperations.splice(0, 0, {
|
||
action: "delete",
|
||
id: newId,
|
||
});
|
||
} else {
|
||
zoomOut({protyle: item.protyle, id: item.protyle.block.rootID});
|
||
}
|
||
return true;
|
||
}
|
||
});
|
||
/// #endif
|
||
}
|
||
}
|
||
|
||
if (newListId && (index === 0 ||
|
||
sourceElements[index - 1].getAttribute("data-type") !== "NodeListItem" ||
|
||
sourceElements[index - 1].getAttribute("data-subtype") !== item.getAttribute("data-subtype"))
|
||
) {
|
||
if (position === "beforebegin") {
|
||
tempTargetElement = newListElement;
|
||
}
|
||
newListId = null;
|
||
if (newListElement.getAttribute("data-subtype") === "o" && newListElement.firstElementChild.getAttribute("data-marker") !== "1.") {
|
||
Array.from(newListElement.children).forEach((listItem) => {
|
||
if (listItem.classList.contains("protyle-attr")) {
|
||
return;
|
||
}
|
||
undoOperations.push({
|
||
action: "update",
|
||
id: listItem.getAttribute("data-node-id"),
|
||
data: listItem.outerHTML
|
||
});
|
||
});
|
||
updateListOrder(newListElement, 1);
|
||
Array.from(newListElement.children).forEach((listItem) => {
|
||
if (listItem.classList.contains("protyle-attr")) {
|
||
return;
|
||
}
|
||
doOperations.push({
|
||
action: "update",
|
||
id: listItem.getAttribute("data-node-id"),
|
||
data: listItem.outerHTML
|
||
});
|
||
});
|
||
updateListOrder(newListElement, 1);
|
||
}
|
||
} else if (position === "beforebegin") {
|
||
tempTargetElement = isCopy ? copyElement : item;
|
||
}
|
||
}
|
||
Object.keys(orderListElements).forEach(key => {
|
||
Array.from(orderListElements[key].children).forEach((item) => {
|
||
if (item.classList.contains("protyle-attr")) {
|
||
return;
|
||
}
|
||
undoOperations.push({
|
||
action: "update",
|
||
id: item.getAttribute("data-node-id"),
|
||
data: item.outerHTML
|
||
});
|
||
});
|
||
updateListOrder(orderListElements[key], 1);
|
||
Array.from(orderListElements[key].children).forEach((item) => {
|
||
if (item.classList.contains("protyle-attr")) {
|
||
return;
|
||
}
|
||
doOperations.push({
|
||
action: "update",
|
||
id: item.getAttribute("data-node-id"),
|
||
data: item.outerHTML
|
||
});
|
||
});
|
||
});
|
||
undoOperations.reverse();
|
||
for (let j = 0; j < copyFoldHeadingIds.length; j++) {
|
||
const childrenItem = copyFoldHeadingIds[j];
|
||
const responseTransaction = await fetchSyncPost("/api/block/getHeadingInsertTransaction", {id: childrenItem.oldId});
|
||
responseTransaction.data.doOperations.splice(0, 1);
|
||
responseTransaction.data.doOperations[0].previousID = childrenItem.newId;
|
||
responseTransaction.data.undoOperations.splice(0, 1);
|
||
doOperations.push(...responseTransaction.data.doOperations);
|
||
undoOperations.push(...responseTransaction.data.undoOperations);
|
||
}
|
||
return {
|
||
doOperations,
|
||
undoOperations,
|
||
newSourceElements
|
||
};
|
||
};
|
||
|
||
const dragSb = async (protyle: IProtyle, sourceElements: Element[], targetElement: Element, isBottom: boolean,
|
||
direct: "col" | "row", isCopy: boolean) => {
|
||
const isSameDoc = protyle.element.contains(sourceElements[0]);
|
||
// 把列表块中的唯一一个列表项块拖拽到列表块的左侧 https://github.com/siyuan-note/siyuan/issues/16315
|
||
if (isSameDoc && sourceElements[0].classList.contains("li") && targetElement === sourceElements[0].parentElement &&
|
||
targetElement.childElementCount === sourceElements.length + 1) {
|
||
const outLiElement = sourceElements.find((element) => {
|
||
if (!targetElement.contains(element)) {
|
||
return true;
|
||
}
|
||
});
|
||
if (!outLiElement) {
|
||
return;
|
||
}
|
||
}
|
||
const undoOperations: IOperation[] = [];
|
||
const targetMoveUndo: IOperation = {
|
||
action: "move",
|
||
id: targetElement.getAttribute("data-node-id"),
|
||
previousID: targetElement.previousElementSibling?.getAttribute("data-node-id"),
|
||
parentID: targetElement.parentElement?.getAttribute("data-node-id") || protyle.block.parentID || protyle.block.rootID
|
||
};
|
||
const sbElement = genSBElement(direct);
|
||
targetElement.parentElement.replaceChild(sbElement, targetElement);
|
||
const doOperations: IOperation[] = [{
|
||
action: "insert",
|
||
data: sbElement.outerHTML,
|
||
id: sbElement.getAttribute("data-node-id"),
|
||
nextID: sbElement.nextElementSibling?.getAttribute("data-node-id"),
|
||
previousID: sbElement.previousElementSibling?.getAttribute("data-node-id"),
|
||
parentID: sbElement.parentElement.getAttribute("data-node-id") || protyle.block.parentID || protyle.block.rootID
|
||
}];
|
||
const moveToResult = await moveTo(protyle, sourceElements, sbElement, isSameDoc, "afterbegin", isCopy);
|
||
doOperations.push(...moveToResult.doOperations);
|
||
undoOperations.push(...moveToResult.undoOperations);
|
||
const newSourceParentElement = moveToResult.newSourceElements;
|
||
// 横向超级块A内两个元素拖拽成纵向超级块B,取消超级块A会导致 targetElement 被删除,需先移动再删除 https://github.com/siyuan-note/siyuan/issues/16292
|
||
let removeIndex = doOperations.length - 1;
|
||
doOperations.find((item, index) => {
|
||
if (item.action === "delete" && item.id === targetMoveUndo.parentID) {
|
||
removeIndex = index;
|
||
return true;
|
||
}
|
||
});
|
||
if (isBottom) {
|
||
// 拖拽到超级块 col 下方, 其他块右侧
|
||
sbElement.insertAdjacentElement("afterbegin", targetElement);
|
||
doOperations.splice(removeIndex, 0, {
|
||
action: "move",
|
||
id: targetElement.getAttribute("data-node-id"),
|
||
parentID: sbElement.getAttribute("data-node-id")
|
||
});
|
||
} else {
|
||
sbElement.lastElementChild.insertAdjacentElement("beforebegin", targetElement);
|
||
doOperations.splice(removeIndex, 0, {
|
||
action: "move",
|
||
id: targetElement.getAttribute("data-node-id"),
|
||
previousID: newSourceParentElement[0].getAttribute("data-node-id"),
|
||
});
|
||
}
|
||
undoOperations.push(targetMoveUndo);
|
||
undoOperations.push({
|
||
action: "delete",
|
||
id: sbElement.getAttribute("data-node-id"),
|
||
});
|
||
let hasFoldHeading = false;
|
||
newSourceParentElement.forEach(item => {
|
||
if (item.getAttribute("data-type") === "NodeHeading" && item.getAttribute("fold") === "1") {
|
||
hasFoldHeading = true;
|
||
if (item.nextElementSibling && (
|
||
item.nextElementSibling.getAttribute("data-type") !== "NodeHeading" ||
|
||
item.nextElementSibling.getAttribute("data-subtype") > item.getAttribute("data-subtype")
|
||
)) {
|
||
const foldOperations = setFold(protyle, item, true, false, false, true);
|
||
doOperations.push(...foldOperations.doOperations);
|
||
// 不折叠,否则无法撤销 undoOperations.push(...foldOperations.undoOperations);
|
||
}
|
||
return true;
|
||
}
|
||
});
|
||
if (isSameDoc || isCopy) {
|
||
transaction(protyle, doOperations, undoOperations);
|
||
} else {
|
||
// 跨文档或插入折叠标题下不支持撤销
|
||
transaction(protyle, doOperations);
|
||
}
|
||
if ((newSourceParentElement.length > 1 || hasFoldHeading) && direct === "col") {
|
||
turnsIntoOneTransaction({
|
||
protyle,
|
||
selectsElement: newSourceParentElement.reverse(),
|
||
type: "BlocksMergeSuperBlock",
|
||
level: "row"
|
||
});
|
||
}
|
||
if (document.contains(sourceElements[0])) {
|
||
focusBlock(sourceElements[0]);
|
||
} else {
|
||
focusBlock(targetElement);
|
||
}
|
||
};
|
||
|
||
const dragSame = async (protyle: IProtyle, sourceElements: Element[], targetElement: Element, isBottom: boolean, isCopy: boolean) => {
|
||
const isSameDoc = protyle.element.contains(sourceElements[0]);
|
||
const doOperations: IOperation[] = [];
|
||
const undoOperations: IOperation[] = [];
|
||
|
||
const moveToResult = await moveTo(protyle, sourceElements, targetElement, isSameDoc, isBottom ? "afterend" : "beforebegin", isCopy);
|
||
doOperations.push(...moveToResult.doOperations);
|
||
undoOperations.push(...moveToResult.undoOperations);
|
||
const newSourceParentElement = moveToResult.newSourceElements;
|
||
let foldData;
|
||
if (isBottom &&
|
||
targetElement.getAttribute("data-type") === "NodeHeading" &&
|
||
targetElement.getAttribute("fold") === "1") {
|
||
foldData = setFold(protyle, targetElement, true, false, false, true);
|
||
} else if (!isBottom && targetElement.previousElementSibling &&
|
||
targetElement.previousElementSibling.getAttribute("data-type") === "NodeHeading" &&
|
||
targetElement.previousElementSibling.getAttribute("fold") === "1") {
|
||
foldData = setFold(protyle, targetElement.previousElementSibling, true, false, false, true);
|
||
}
|
||
if (foldData) {
|
||
foldData.doOperations[0].context = {
|
||
focusId: sourceElements[0].getAttribute("data-node-id"),
|
||
};
|
||
doOperations.push(...foldData.doOperations);
|
||
undoOperations.push(...foldData.undoOperations);
|
||
}
|
||
if (targetElement.getAttribute("data-type") === "NodeListItem" &&
|
||
targetElement.getAttribute("data-subtype") === "o") {
|
||
// https://github.com/siyuan-note/insider/issues/536
|
||
Array.from(targetElement.parentElement.children).forEach((item) => {
|
||
if (item.classList.contains("protyle-attr")) {
|
||
return;
|
||
}
|
||
undoOperations.splice(0, 0, {
|
||
action: "update",
|
||
id: item.getAttribute("data-node-id"),
|
||
data: item.outerHTML
|
||
});
|
||
});
|
||
updateListOrder(targetElement.parentElement, 1);
|
||
Array.from(targetElement.parentElement.children).forEach((item) => {
|
||
if (item.classList.contains("protyle-attr")) {
|
||
return;
|
||
}
|
||
doOperations.push({
|
||
action: "update",
|
||
id: item.getAttribute("data-node-id"),
|
||
data: item.outerHTML
|
||
});
|
||
});
|
||
}
|
||
let hasFoldHeading = false;
|
||
newSourceParentElement.forEach(item => {
|
||
if (item.getAttribute("data-type") === "NodeHeading" && item.getAttribute("fold") === "1") {
|
||
hasFoldHeading = true;
|
||
if (item.nextElementSibling && (
|
||
item.nextElementSibling.getAttribute("data-type") !== "NodeHeading" ||
|
||
item.nextElementSibling.getAttribute("data-subtype") > item.getAttribute("data-subtype")
|
||
)) {
|
||
const foldOperations = setFold(protyle, item, true, false, false, true);
|
||
doOperations.push(...foldOperations.doOperations);
|
||
// 不折叠,否则无法撤销 undoOperations.push(...foldOperations.undoOperations);
|
||
}
|
||
return true;
|
||
}
|
||
});
|
||
if (isSameDoc || isCopy) {
|
||
transaction(protyle, doOperations, undoOperations);
|
||
} else {
|
||
// 跨文档或插入折叠标题下不支持撤销
|
||
transaction(protyle, doOperations);
|
||
}
|
||
if ((newSourceParentElement.length > 1 || hasFoldHeading) &&
|
||
newSourceParentElement[0].parentElement.classList.contains("sb") &&
|
||
newSourceParentElement[0].parentElement.getAttribute("data-sb-layout") === "col") {
|
||
turnsIntoOneTransaction({
|
||
protyle,
|
||
selectsElement: newSourceParentElement.reverse(),
|
||
type: "BlocksMergeSuperBlock",
|
||
level: "row"
|
||
});
|
||
}
|
||
if (document.contains(sourceElements[0])) {
|
||
focusBlock(sourceElements[0]);
|
||
} else {
|
||
focusBlock(targetElement);
|
||
}
|
||
};
|
||
|
||
export const dropEvent = (protyle: IProtyle, editorElement: HTMLElement) => {
|
||
editorElement.addEventListener("dragstart", (event) => {
|
||
if (protyle.disabled) {
|
||
event.preventDefault();
|
||
event.stopPropagation();
|
||
return;
|
||
}
|
||
let target = event.target as HTMLElement;
|
||
if (target.classList?.contains("av__gallery-img")) {
|
||
target = hasClosestByClassName(target, "av__gallery-item") as HTMLElement;
|
||
}
|
||
if (!target) {
|
||
return;
|
||
}
|
||
if (target.tagName === "IMG") {
|
||
window.siyuan.dragElement = undefined;
|
||
event.preventDefault();
|
||
return;
|
||
}
|
||
|
||
if (target.classList) {
|
||
if (hasClosestByClassName(target, "protyle-wysiwyg__embed")) {
|
||
window.siyuan.dragElement = undefined;
|
||
event.preventDefault();
|
||
} else if (target.parentElement.parentElement.classList.contains("av__views")) {
|
||
window.siyuan.dragElement = target;
|
||
target.style.width = target.clientWidth + "px";
|
||
target.style.opacity = ".36";
|
||
event.dataTransfer.setData(`${Constants.SIYUAN_DROP_GUTTER}NodeAttributeView${Constants.ZWSP}ViewTab${Constants.ZWSP}${[target.previousElementSibling?.getAttribute("data-id")]}`,
|
||
target.outerHTML);
|
||
return;
|
||
} else if (target.classList.contains("protyle-action")) {
|
||
target.parentElement.classList.add("protyle-wysiwyg--select");
|
||
const ghostElement = document.createElement("div");
|
||
ghostElement.className = protyle.wysiwyg.element.className;
|
||
ghostElement.append(processClonePHElement(target.parentElement.cloneNode(true) as Element));
|
||
ghostElement.setAttribute("style", `position:fixed;opacity:.1;width:${target.parentElement.clientWidth}px;padding:0;`);
|
||
document.body.append(ghostElement);
|
||
event.dataTransfer.setDragImage(ghostElement, 0, 0);
|
||
setTimeout(() => {
|
||
ghostElement.remove();
|
||
});
|
||
|
||
window.siyuan.dragElement = protyle.wysiwyg.element;
|
||
event.dataTransfer.setData(`${Constants.SIYUAN_DROP_GUTTER}NodeListItem${Constants.ZWSP}${target.parentElement.getAttribute("data-subtype")}${Constants.ZWSP}${[target.parentElement.getAttribute("data-node-id")]}`,
|
||
protyle.wysiwyg.element.innerHTML);
|
||
return;
|
||
} else if (target.classList.contains("av__cell--header")) {
|
||
window.siyuan.dragElement = target;
|
||
event.dataTransfer.setData(`${Constants.SIYUAN_DROP_GUTTER}NodeAttributeView${Constants.ZWSP}Col${Constants.ZWSP}${[target.getAttribute("data-col-id")]}`,
|
||
target.outerHTML);
|
||
return;
|
||
} else if (target.classList.contains("av__gallery-item")) {
|
||
const blockElement = hasClosestBlock(target);
|
||
if (blockElement) {
|
||
if (blockElement.querySelector('.block__icon[data-type="av-sort"]')?.classList.contains("block__icon--active")) {
|
||
const bodyElements = blockElement.querySelectorAll(".av__body");
|
||
if (bodyElements.length === 1) {
|
||
event.preventDefault();
|
||
event.stopPropagation();
|
||
return;
|
||
} else if (["template", "created", "updated"].includes(bodyElements[0].getAttribute("data-dtype"))) {
|
||
event.preventDefault();
|
||
event.stopPropagation();
|
||
return;
|
||
}
|
||
}
|
||
if (!target.classList.contains("av__gallery-item--select")) {
|
||
blockElement.querySelectorAll(".av__gallery-item--select").forEach(item => {
|
||
item.classList.remove("av__gallery-item--select");
|
||
});
|
||
target.classList.add("av__gallery-item--select");
|
||
}
|
||
const ghostElement = document.createElement("div");
|
||
ghostElement.className = "protyle-wysiwyg protyle-wysiwyg--attr";
|
||
const selectElements = blockElement.querySelectorAll(".av__gallery-item--select");
|
||
let galleryElement: HTMLElement;
|
||
let cloneGalleryElement = document.createElement("div");
|
||
selectElements.forEach(item => {
|
||
if (!galleryElement || !galleryElement.contains(item)) {
|
||
galleryElement = item.parentElement;
|
||
cloneGalleryElement = document.createElement("div");
|
||
cloneGalleryElement.classList.add("av__gallery");
|
||
cloneGalleryElement.setAttribute("style", `width: 100vw;margin-bottom: 16px;grid-template-columns: repeat(auto-fill, ${selectElements[0].clientWidth}px);`);
|
||
ghostElement.appendChild(cloneGalleryElement);
|
||
}
|
||
const cloneItem = processClonePHElement(item.cloneNode(true) as Element);
|
||
cloneItem.setAttribute("style", `height:${item.clientHeight}px;`);
|
||
cloneItem.querySelector(".av__gallery-fields").setAttribute("style", "background-color: var(--b3-theme-background)");
|
||
cloneGalleryElement.appendChild(cloneItem);
|
||
});
|
||
ghostElement.setAttribute("style", "top:100vh;position:fixed;opacity:.1;padding:0;z-index: 8");
|
||
document.body.append(ghostElement);
|
||
event.dataTransfer.setDragImage(ghostElement, -10, -10);
|
||
setTimeout(() => {
|
||
ghostElement.remove();
|
||
});
|
||
window.siyuan.dragElement = target;
|
||
const selectIds: string[] = [];
|
||
blockElement.querySelectorAll(".av__gallery-item--select").forEach(item => {
|
||
const bodyElement = hasClosestByClassName(item, "av__body") as HTMLElement;
|
||
const groupId = bodyElement.getAttribute("data-group-id");
|
||
selectIds.push(item.getAttribute("data-id") + (groupId ? `@${groupId}` : ""));
|
||
});
|
||
event.dataTransfer.setData(`${Constants.SIYUAN_DROP_GUTTER}NodeAttributeView${Constants.ZWSP}GalleryItem${Constants.ZWSP}${selectIds}`,
|
||
ghostElement.outerHTML);
|
||
}
|
||
return;
|
||
}
|
||
}
|
||
// 选中编辑器中的文字进行拖拽
|
||
event.dataTransfer.setData(Constants.SIYUAN_DROP_EDITOR, Constants.SIYUAN_DROP_EDITOR);
|
||
protyle.element.style.userSelect = "auto";
|
||
document.onmousemove = null;
|
||
document.onmouseup = null;
|
||
});
|
||
editorElement.addEventListener("drop", async (event: DragEvent & { target: HTMLElement }) => {
|
||
counter = 0;
|
||
if (protyle.disabled || event.dataTransfer.getData(Constants.SIYUAN_DROP_EDITOR)) {
|
||
// 只读模式/编辑器内选中文字拖拽
|
||
event.preventDefault();
|
||
event.stopPropagation();
|
||
return;
|
||
}
|
||
let gutterType = "";
|
||
for (const item of event.dataTransfer.items) {
|
||
if (item.type.startsWith(Constants.SIYUAN_DROP_GUTTER)) {
|
||
gutterType = item.type;
|
||
}
|
||
}
|
||
if (gutterType.startsWith(`${Constants.SIYUAN_DROP_GUTTER}NodeAttributeView${Constants.ZWSP}ViewTab${Constants.ZWSP}`.toLowerCase())) {
|
||
const blockElement = hasClosestBlock(window.siyuan.dragElement);
|
||
if (blockElement) {
|
||
const avID = blockElement.getAttribute("data-av-id");
|
||
const blockID = blockElement.getAttribute("data-node-id");
|
||
const id = window.siyuan.dragElement.getAttribute("data-id");
|
||
transaction(protyle, [{
|
||
action: "sortAttrViewView",
|
||
avID,
|
||
blockID,
|
||
id,
|
||
previousID: window.siyuan.dragElement.previousElementSibling?.getAttribute("data-id"),
|
||
data: "unRefresh" // 不需要重新渲染
|
||
}], [{
|
||
action: "sortAttrViewView",
|
||
avID,
|
||
blockID,
|
||
id,
|
||
previousID: gutterType.split(Constants.ZWSP).pop()
|
||
}]);
|
||
}
|
||
return;
|
||
}
|
||
const targetElement = editorElement.querySelector(".dragover__left, .dragover__right, .dragover__bottom, .dragover__top");
|
||
if (targetElement) {
|
||
targetElement.classList.remove("dragover");
|
||
targetElement.removeAttribute("select-start");
|
||
targetElement.removeAttribute("select-end");
|
||
}
|
||
if (gutterType) {
|
||
// gutter 或反链面板拖拽
|
||
const sourceElements: Element[] = [];
|
||
const gutterTypes = gutterType.replace(Constants.SIYUAN_DROP_GUTTER, "").split(Constants.ZWSP);
|
||
const selectedIds = gutterTypes[2].split(",");
|
||
if (event.altKey || event.shiftKey) {
|
||
if (event.y > protyle.wysiwyg.element.lastElementChild.getBoundingClientRect().bottom) {
|
||
insertEmptyBlock(protyle, "afterend", protyle.wysiwyg.element.lastElementChild.getAttribute("data-node-id"));
|
||
} else {
|
||
const range = getRangeByPoint(event.clientX, event.clientY);
|
||
if (hasClosestByAttribute(range.startContainer, "data-type", "NodeBlockQueryEmbed")) {
|
||
return;
|
||
} else {
|
||
focusByRange(range);
|
||
}
|
||
}
|
||
}
|
||
if (event.altKey) {
|
||
let html = "";
|
||
for (let i = 0; i < selectedIds.length; i++) {
|
||
const response = await fetchSyncPost("/api/block/getRefText", {id: selectedIds[i]});
|
||
html += protyle.lute.Md2BlockDOM(`((${selectedIds[i]} '${response.data}'))`);
|
||
}
|
||
insertHTML(html, protyle);
|
||
} else if (event.shiftKey) {
|
||
let html = "";
|
||
selectedIds.forEach(item => {
|
||
html += `{{select * from blocks where id='${item}'}}\n`;
|
||
});
|
||
insertHTML(protyle.lute.SpinBlockDOM(html), protyle, true);
|
||
blockRender(protyle, protyle.wysiwyg.element);
|
||
} else if (targetElement && targetElement.className.indexOf("dragover__") > -1) {
|
||
let queryClass = "";
|
||
selectedIds.forEach(item => {
|
||
queryClass += `[data-node-id="${item}"],`;
|
||
});
|
||
if (window.siyuan.dragElement) {
|
||
window.siyuan.dragElement.querySelectorAll(queryClass.substring(0, queryClass.length - 1)).forEach(elementItem => {
|
||
if (!isInEmbedBlock(elementItem)) {
|
||
sourceElements.push(elementItem);
|
||
}
|
||
});
|
||
} else if (window.siyuan.config.system.workspaceDir.toLowerCase() === gutterTypes[3]) {
|
||
// 跨窗口拖拽
|
||
// 不能跨工作区域拖拽 https://github.com/siyuan-note/siyuan/issues/13582
|
||
const targetProtyleElement = document.createElement("template");
|
||
targetProtyleElement.innerHTML = `<div>${event.dataTransfer.getData(gutterType)}</div>`;
|
||
targetProtyleElement.content.querySelectorAll(queryClass.substring(0, queryClass.length - 1)).forEach(elementItem => {
|
||
if (!isInEmbedBlock(elementItem)) {
|
||
sourceElements.push(elementItem);
|
||
}
|
||
});
|
||
}
|
||
|
||
const sourceIds: string [] = [];
|
||
const srcs: IOperationSrcs[] = [];
|
||
sourceElements.forEach(item => {
|
||
item.classList.remove("protyle-wysiwyg--hl");
|
||
item.removeAttribute("select-start");
|
||
item.removeAttribute("select-end");
|
||
// 反链提及有高亮,如果拖拽到正文的话,应移除
|
||
item.querySelectorAll('[data-type="search-mark"]').forEach(markItem => {
|
||
markItem.outerHTML = markItem.innerHTML;
|
||
});
|
||
const id = item.getAttribute("data-node-id");
|
||
sourceIds.push(id);
|
||
srcs.push({
|
||
itemID: Lute.NewNodeID(),
|
||
id,
|
||
isDetached: false,
|
||
});
|
||
});
|
||
|
||
hideElements(["gutter"], protyle);
|
||
|
||
const targetClass = targetElement.className.split(" ");
|
||
targetElement.classList.remove("dragover__bottom", "dragover__top", "dragover__left", "dragover__right");
|
||
|
||
if (targetElement.classList.contains("av__cell")) {
|
||
const blockElement = hasClosestBlock(targetElement);
|
||
if (blockElement) {
|
||
const avID = blockElement.getAttribute("data-av-id");
|
||
let previousID = "";
|
||
if (targetClass.includes("dragover__left")) {
|
||
if (targetElement.previousElementSibling) {
|
||
if (targetElement.previousElementSibling.classList.contains("av__colsticky")) {
|
||
previousID = targetElement.previousElementSibling.lastElementChild.getAttribute("data-col-id");
|
||
} else {
|
||
previousID = targetElement.previousElementSibling.getAttribute("data-col-id");
|
||
}
|
||
}
|
||
} else {
|
||
previousID = targetElement.getAttribute("data-col-id");
|
||
}
|
||
let oldPreviousID = "";
|
||
const rowElement = hasClosestByClassName(targetElement, "av__row");
|
||
if (rowElement) {
|
||
const oldPreviousElement = rowElement.querySelector(`[data-col-id="${gutterTypes[2]}"`)?.previousElementSibling;
|
||
if (oldPreviousElement) {
|
||
if (oldPreviousElement.classList.contains("av__colsticky")) {
|
||
oldPreviousID = oldPreviousElement.lastElementChild.getAttribute("data-col-id");
|
||
} else {
|
||
oldPreviousID = oldPreviousElement.getAttribute("data-col-id");
|
||
}
|
||
}
|
||
}
|
||
if (previousID !== oldPreviousID && previousID !== gutterTypes[2]) {
|
||
transaction(protyle, [{
|
||
action: "sortAttrViewCol",
|
||
avID,
|
||
previousID,
|
||
id: gutterTypes[2],
|
||
blockID: blockElement.dataset.nodeId,
|
||
}], [{
|
||
action: "sortAttrViewCol",
|
||
avID,
|
||
previousID: oldPreviousID,
|
||
id: gutterTypes[2],
|
||
blockID: blockElement.dataset.nodeId,
|
||
}]);
|
||
}
|
||
}
|
||
} else if (targetElement.classList.contains("av__row")) {
|
||
// 拖拽到属性视图 table 内
|
||
const blockElement = hasClosestBlock(targetElement);
|
||
if (blockElement) {
|
||
let previousID = "";
|
||
if (targetClass.includes("dragover__bottom")) {
|
||
previousID = targetElement.getAttribute("data-id") || "";
|
||
} else {
|
||
previousID = targetElement.previousElementSibling?.getAttribute("data-id") || "";
|
||
}
|
||
const avID = blockElement.getAttribute("data-av-id");
|
||
if (gutterTypes[0] === "nodeattributeviewrowmenu") {
|
||
// 行内拖拽
|
||
const doOperations: IOperation[] = [];
|
||
const undoOperations: IOperation[] = [];
|
||
const targetGroupID = targetElement.parentElement.getAttribute("data-group-id");
|
||
selectedIds.reverse().forEach(item => {
|
||
const items = item.split("@");
|
||
const id = items[0];
|
||
const groupID = items[1] || "";
|
||
const undoPreviousId = blockElement.querySelector(`.av__body${groupID ? `[data-group-id="${groupID}"]` : ""} .av__row[data-id="${id}"]`).previousElementSibling?.getAttribute("data-id") || "";
|
||
if (previousID !== id && undoPreviousId !== previousID || (
|
||
(undoPreviousId === "" && previousID === "" && targetGroupID !== groupID)
|
||
)) {
|
||
doOperations.push({
|
||
action: "sortAttrViewRow",
|
||
avID,
|
||
previousID,
|
||
id,
|
||
blockID: blockElement.dataset.nodeId,
|
||
groupID,
|
||
targetGroupID,
|
||
});
|
||
undoOperations.push({
|
||
action: "sortAttrViewRow",
|
||
avID,
|
||
previousID: undoPreviousId,
|
||
id,
|
||
blockID: blockElement.dataset.nodeId,
|
||
groupID: targetGroupID,
|
||
targetGroupID: groupID,
|
||
});
|
||
}
|
||
});
|
||
transaction(protyle, doOperations, undoOperations);
|
||
} else {
|
||
const newUpdated = dayjs().format("YYYYMMDDHHmmss");
|
||
const bodyElement = hasClosestByClassName(targetElement, "av__body");
|
||
const groupID = bodyElement && bodyElement.getAttribute("data-group-id");
|
||
transaction(protyle, [{
|
||
action: "insertAttrViewBlock",
|
||
avID,
|
||
previousID,
|
||
srcs,
|
||
blockID: blockElement.dataset.nodeId,
|
||
groupID
|
||
}, {
|
||
action: "doUpdateUpdated",
|
||
id: blockElement.dataset.nodeId,
|
||
data: newUpdated,
|
||
}], [{
|
||
action: "removeAttrViewBlock",
|
||
srcIDs: sourceIds,
|
||
avID,
|
||
}, {
|
||
action: "doUpdateUpdated",
|
||
id: blockElement.dataset.nodeId,
|
||
data: blockElement.getAttribute("updated")
|
||
}]);
|
||
blockElement.setAttribute("updated", newUpdated);
|
||
insertAttrViewBlockAnimation({
|
||
protyle,
|
||
blockElement,
|
||
srcIDs: sourceIds,
|
||
previousId: previousID,
|
||
groupID
|
||
});
|
||
}
|
||
}
|
||
} else if (targetElement.classList.contains("av__gallery-item") || targetElement.classList.contains("av__gallery-add")) {
|
||
// 拖拽到属性视图 gallery 内
|
||
const blockElement = hasClosestBlock(targetElement);
|
||
if (blockElement) {
|
||
let previousID = "";
|
||
if (targetClass.includes("dragover__right") || targetClass.includes("dragover__bottom")) {
|
||
previousID = targetElement.getAttribute("data-id") || "";
|
||
} else if (targetClass.includes("dragover__top") || targetClass.includes("dragover__left")) {
|
||
previousID = targetElement.previousElementSibling?.getAttribute("data-id") || "";
|
||
}
|
||
const avID = blockElement.getAttribute("data-av-id");
|
||
if (gutterTypes[1] === "galleryitem" && gutterTypes[0] === "nodeattributeview") {
|
||
// gallery item 内部拖拽
|
||
const doOperations: IOperation[] = [];
|
||
const undoOperations: IOperation[] = [];
|
||
const targetGroupID = targetElement.parentElement.parentElement.getAttribute("data-group-id");
|
||
selectedIds.reverse().forEach(item => {
|
||
const items = item.split("@");
|
||
const id = items[0];
|
||
const groupID = items[1] || "";
|
||
const undoPreviousId = blockElement.querySelector(`.av__body[data-group-id="${groupID}"] .av__gallery-item[data-id="${id}"]`).previousElementSibling?.getAttribute("data-id") || "";
|
||
if (previousID !== item && undoPreviousId !== previousID || (
|
||
(undoPreviousId === "" && previousID === "" && targetGroupID !== groupID)
|
||
)) {
|
||
doOperations.push({
|
||
action: "sortAttrViewRow",
|
||
avID,
|
||
previousID,
|
||
id,
|
||
blockID: blockElement.dataset.nodeId,
|
||
groupID,
|
||
targetGroupID,
|
||
});
|
||
undoOperations.push({
|
||
action: "sortAttrViewRow",
|
||
avID,
|
||
previousID: undoPreviousId,
|
||
id,
|
||
blockID: blockElement.dataset.nodeId,
|
||
groupID: targetGroupID,
|
||
targetGroupID: groupID,
|
||
});
|
||
}
|
||
});
|
||
transaction(protyle, doOperations, undoOperations);
|
||
} else {
|
||
const newUpdated = dayjs().format("YYYYMMDDHHmmss");
|
||
const bodyElement = hasClosestByClassName(targetElement, "av__body");
|
||
transaction(protyle, [{
|
||
action: "insertAttrViewBlock",
|
||
avID,
|
||
previousID,
|
||
srcs,
|
||
blockID: blockElement.dataset.nodeId,
|
||
groupID: bodyElement && bodyElement.getAttribute("data-group-id")
|
||
}, {
|
||
action: "doUpdateUpdated",
|
||
id: blockElement.dataset.nodeId,
|
||
data: newUpdated,
|
||
}], [{
|
||
action: "removeAttrViewBlock",
|
||
srcIDs: sourceIds,
|
||
avID,
|
||
}, {
|
||
action: "doUpdateUpdated",
|
||
id: blockElement.dataset.nodeId,
|
||
data: blockElement.getAttribute("updated")
|
||
}]);
|
||
blockElement.setAttribute("updated", newUpdated);
|
||
insertGalleryItemAnimation({
|
||
protyle,
|
||
blockElement,
|
||
srcIDs: sourceIds,
|
||
previousId: previousID,
|
||
groupID: targetElement.parentElement.getAttribute("data-group-id")
|
||
});
|
||
}
|
||
}
|
||
} else if (sourceElements.length > 0) {
|
||
if (targetElement.parentElement.getAttribute("data-type") === "NodeSuperBlock" &&
|
||
targetElement.parentElement.getAttribute("data-sb-layout") === "col") {
|
||
if (targetClass.includes("dragover__left") || targetClass.includes("dragover__right")) {
|
||
// Mac 上 ⌘ 无法进行拖拽
|
||
dragSame(protyle, sourceElements, targetElement, targetClass.includes("dragover__right"), event.ctrlKey);
|
||
} else {
|
||
dragSb(protyle, sourceElements, targetElement, targetClass.includes("dragover__bottom"), "row", event.ctrlKey);
|
||
}
|
||
} else {
|
||
if (targetClass.includes("dragover__left") || targetClass.includes("dragover__right")) {
|
||
dragSb(protyle, sourceElements, targetElement, targetClass.includes("dragover__right"), "col", event.ctrlKey);
|
||
} else {
|
||
dragSame(protyle, sourceElements, targetElement, targetClass.includes("dragover__bottom"), event.ctrlKey);
|
||
}
|
||
}
|
||
|
||
// https://github.com/siyuan-note/siyuan/issues/10528#issuecomment-2205165824
|
||
editorElement.querySelectorAll(".protyle-wysiwyg--empty").forEach(item => {
|
||
item.classList.remove("protyle-wysiwyg--empty");
|
||
});
|
||
|
||
// 需重新渲染 https://github.com/siyuan-note/siyuan/issues/7574
|
||
protyle.wysiwyg.element.querySelectorAll('[data-type="NodeBlockQueryEmbed"]').forEach(item => {
|
||
item.removeAttribute("data-render");
|
||
blockRender(protyle, item);
|
||
});
|
||
}
|
||
dragoverElement = undefined;
|
||
}
|
||
} else if (event.dataTransfer.getData(Constants.SIYUAN_DROP_FILE)?.split("-").length > 1) {
|
||
// 文件树拖拽
|
||
const ids = event.dataTransfer.getData(Constants.SIYUAN_DROP_FILE).split(",");
|
||
if (!event.altKey && (!targetElement || (
|
||
!targetElement.classList.contains("av__row") && !targetElement.classList.contains("av__gallery-item") &&
|
||
!targetElement.classList.contains("av__gallery-add")
|
||
))) {
|
||
if (event.y > protyle.wysiwyg.element.lastElementChild.getBoundingClientRect().bottom) {
|
||
insertEmptyBlock(protyle, "afterend", protyle.wysiwyg.element.lastElementChild.getAttribute("data-node-id"));
|
||
} else {
|
||
const range = getRangeByPoint(event.clientX, event.clientY);
|
||
if (hasClosestByAttribute(range.startContainer, "data-type", "NodeBlockQueryEmbed")) {
|
||
return;
|
||
} else {
|
||
focusByRange(range);
|
||
}
|
||
}
|
||
let html = "";
|
||
for (let i = 0; i < ids.length; i++) {
|
||
if (ids.length > 1) {
|
||
html += "- ";
|
||
}
|
||
const response = await fetchSyncPost("/api/block/getRefText", {id: ids[i]});
|
||
html += `((${ids[i]} '${response.data}'))`;
|
||
if (ids.length > 1 && i !== ids.length - 1) {
|
||
html += "\n";
|
||
}
|
||
}
|
||
insertHTML(protyle.lute.Md2BlockDOM(html), protyle);
|
||
} else if (targetElement && !protyle.options.backlinkData && targetElement.className.indexOf("dragover__") > -1) {
|
||
const scrollTop = protyle.contentElement.scrollTop;
|
||
if (targetElement.classList.contains("av__row") ||
|
||
targetElement.classList.contains("av__gallery-item") ||
|
||
targetElement.classList.contains("av__gallery-add")) {
|
||
// 拖拽到属性视图内
|
||
const blockElement = hasClosestBlock(targetElement);
|
||
if (blockElement) {
|
||
let previousID = "";
|
||
if (targetElement.classList.contains("dragover__bottom") || targetElement.classList.contains("dragover__right")) {
|
||
previousID = targetElement.getAttribute("data-id") || "";
|
||
} else if (targetElement.classList.contains("dragover__top") || targetElement.classList.contains("dragover__left")) {
|
||
previousID = targetElement.previousElementSibling?.getAttribute("data-id") || "";
|
||
}
|
||
const avID = blockElement.getAttribute("data-av-id");
|
||
const newUpdated = dayjs().format("YYYYMMDDHHmmss");
|
||
const srcs: IOperationSrcs[] = [];
|
||
const bodyElement = hasClosestByClassName(targetElement, "av__body");
|
||
const groupID = bodyElement && bodyElement.getAttribute("data-group-id");
|
||
ids.forEach(id => {
|
||
srcs.push({
|
||
itemID: Lute.NewNodeID(),
|
||
id,
|
||
isDetached: false,
|
||
});
|
||
});
|
||
transaction(protyle, [{
|
||
action: "insertAttrViewBlock",
|
||
avID,
|
||
previousID,
|
||
srcs,
|
||
blockID: blockElement.dataset.nodeId,
|
||
groupID
|
||
}, {
|
||
action: "doUpdateUpdated",
|
||
id: blockElement.dataset.nodeId,
|
||
data: newUpdated,
|
||
}], [{
|
||
action: "removeAttrViewBlock",
|
||
srcIDs: ids,
|
||
avID,
|
||
}, {
|
||
action: "doUpdateUpdated",
|
||
id: blockElement.dataset.nodeId,
|
||
data: blockElement.getAttribute("updated")
|
||
}]);
|
||
insertAttrViewBlockAnimation({
|
||
protyle,
|
||
blockElement,
|
||
srcIDs: ids,
|
||
previousId: previousID,
|
||
groupID
|
||
});
|
||
blockElement.setAttribute("updated", newUpdated);
|
||
}
|
||
} else {
|
||
if (targetElement.classList.contains("dragover__bottom")) {
|
||
for (let i = ids.length - 1; i > -1; i--) {
|
||
if (ids[i]) {
|
||
await fetchSyncPost("/api/filetree/doc2Heading", {
|
||
srcID: ids[i],
|
||
after: true,
|
||
targetID: targetElement.getAttribute("data-node-id"),
|
||
});
|
||
}
|
||
}
|
||
} else {
|
||
for (let i = 0; i < ids.length; i++) {
|
||
if (ids[i]) {
|
||
await fetchSyncPost("/api/filetree/doc2Heading", {
|
||
srcID: ids[i],
|
||
after: false,
|
||
targetID: targetElement.getAttribute("data-node-id"),
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
fetchPost("/api/filetree/getDoc", {
|
||
id: protyle.block.id,
|
||
size: window.siyuan.config.editor.dynamicLoadBlocks,
|
||
}, getResponse => {
|
||
onGet({data: getResponse, protyle});
|
||
/// #if !MOBILE
|
||
// 文档标题互转后,需更新大纲
|
||
updatePanelByEditor({
|
||
protyle,
|
||
focus: false,
|
||
pushBackStack: false,
|
||
reload: true,
|
||
resize: false,
|
||
});
|
||
/// #endif
|
||
// 文档标题互转后,编辑区会跳转到开头 https://github.com/siyuan-note/siyuan/issues/2939
|
||
setTimeout(() => {
|
||
protyle.contentElement.scrollTop = scrollTop;
|
||
protyle.scroll.lastScrollTop = scrollTop - 1;
|
||
}, Constants.TIMEOUT_LOAD);
|
||
});
|
||
}
|
||
targetElement.classList.remove("dragover__bottom", "dragover__top", "dragover__left", "dragover__right");
|
||
}
|
||
} else if (!window.siyuan.dragElement && (event.dataTransfer.types[0] === "Files" || event.dataTransfer.types.includes("text/html"))) {
|
||
event.preventDefault();
|
||
// 外部文件拖入编辑器中或者编辑器内选中文字拖拽
|
||
// https://github.com/siyuan-note/siyuan/issues/9544
|
||
const avElement = hasClosestByClassName(event.target, "av");
|
||
if (!avElement) {
|
||
focusByRange(getRangeByPoint(event.clientX, event.clientY));
|
||
if (event.dataTransfer.types[0] === "Files" && !isBrowser()) {
|
||
const files: string[] = [];
|
||
for (let i = 0; i < event.dataTransfer.files.length; i++) {
|
||
files.push(webUtils.getPathForFile(event.dataTransfer.files[i]));
|
||
}
|
||
uploadLocalFiles(files, protyle, !event.altKey);
|
||
} else {
|
||
paste(protyle, event);
|
||
}
|
||
clearSelect(["av", "img"], protyle.wysiwyg.element);
|
||
} else {
|
||
const cellElement = hasClosestByClassName(event.target, "av__cell");
|
||
if (cellElement) {
|
||
if (getTypeByCellElement(cellElement) === "mAsset" && event.dataTransfer.types[0] === "Files" && !isBrowser()) {
|
||
const files: string[] = [];
|
||
for (let i = 0; i < event.dataTransfer.files.length; i++) {
|
||
files.push(webUtils.getPathForFile(event.dataTransfer.files[i]));
|
||
}
|
||
dragUpload(files, protyle, cellElement);
|
||
clearSelect(["cell"], avElement);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (window.siyuan.dragElement) {
|
||
window.siyuan.dragElement.style.opacity = "";
|
||
window.siyuan.dragElement = undefined;
|
||
}
|
||
});
|
||
let dragoverElement: Element;
|
||
let disabledPosition: string;
|
||
editorElement.addEventListener("dragover", (event: DragEvent & { target: HTMLElement }) => {
|
||
if (protyle.disabled || event.dataTransfer.types.includes(Constants.SIYUAN_DROP_EDITOR)) {
|
||
event.preventDefault();
|
||
event.stopPropagation();
|
||
event.dataTransfer.dropEffect = "none";
|
||
return;
|
||
}
|
||
let gutterType = "";
|
||
for (const item of event.dataTransfer.items) {
|
||
if (item.type.startsWith(Constants.SIYUAN_DROP_GUTTER)) {
|
||
gutterType = item.type;
|
||
}
|
||
}
|
||
if (gutterType.startsWith(`${Constants.SIYUAN_DROP_GUTTER}NodeAttributeView${Constants.ZWSP}ViewTab${Constants.ZWSP}`.toLowerCase())) {
|
||
dragoverTab(event);
|
||
event.preventDefault();
|
||
return;
|
||
}
|
||
const contentRect = protyle.contentElement.getBoundingClientRect();
|
||
if (!hasClosestByClassName(event.target, "av__cell") &&
|
||
(event.clientY < contentRect.top + Constants.SIZE_SCROLL_TB || event.clientY > contentRect.bottom - Constants.SIZE_SCROLL_TB)) {
|
||
protyle.contentElement.scroll({
|
||
top: protyle.contentElement.scrollTop + (event.clientY < contentRect.top + Constants.SIZE_SCROLL_TB ? -Constants.SIZE_SCROLL_STEP : Constants.SIZE_SCROLL_STEP),
|
||
behavior: "smooth"
|
||
});
|
||
}
|
||
let targetElement: HTMLElement | false;
|
||
// 设置了的话 drop 就无法监听 shift/control event.dataTransfer.dropEffect = "move";
|
||
if (event.dataTransfer.types.includes("Files")) {
|
||
targetElement = hasClosestByClassName(event.target, "av__cell");
|
||
if (targetElement && targetElement.getAttribute("data-dtype") === "mAsset" &&
|
||
!targetElement.classList.contains("av__cell--header")) {
|
||
event.preventDefault(); // 不使用导致无法触发 drop
|
||
if (dragoverElement && targetElement === dragoverElement) {
|
||
return;
|
||
}
|
||
const blockElement = hasClosestBlock(targetElement);
|
||
if (blockElement) {
|
||
clearSelect(["cell", "row"], protyle.wysiwyg.element);
|
||
targetElement.classList.add("av__cell--select");
|
||
if (blockElement.getAttribute("data-av-type") !== "gallery") {
|
||
addDragFill(targetElement);
|
||
}
|
||
dragoverElement = targetElement;
|
||
}
|
||
}
|
||
// 使用 event.preventDefault(); 会导致无光标 https://github.com/siyuan-note/siyuan/issues/12857
|
||
return;
|
||
}
|
||
|
||
if (!gutterType && !window.siyuan.dragElement) {
|
||
// https://github.com/siyuan-note/siyuan/issues/6436
|
||
event.preventDefault();
|
||
return;
|
||
}
|
||
const gutterTypes = gutterType ? gutterType.replace(Constants.SIYUAN_DROP_GUTTER, "").split(Constants.ZWSP) : [];
|
||
const fileTreeIds = (event.dataTransfer.types.includes(Constants.SIYUAN_DROP_FILE) && window.siyuan.dragElement) ? window.siyuan.dragElement.innerText : "";
|
||
if (event.shiftKey || (event.altKey && fileTreeIds.indexOf("-") === -1)) {
|
||
const targetAssetElement = hasClosestBlock(event.target);
|
||
if (targetAssetElement) {
|
||
targetAssetElement.classList.remove("dragover__top", "protyle-wysiwyg--select", "dragover__bottom", "dragover__left", "dragover__right");
|
||
targetAssetElement.removeAttribute("select-start");
|
||
targetAssetElement.removeAttribute("select-end");
|
||
} else {
|
||
// https://github.com/siyuan-note/siyuan/issues/14177
|
||
editorElement.querySelectorAll(".dragover__top, .protyle-wysiwyg--select, .dragover__bottom, .dragover__left, .dragover__right").forEach((item: HTMLElement) => {
|
||
item.classList.remove("dragover__top", "protyle-wysiwyg--select", "dragover__bottom", "dragover__left", "dragover__right");
|
||
item.removeAttribute("select-start");
|
||
item.removeAttribute("select-end");
|
||
});
|
||
}
|
||
event.preventDefault();
|
||
return;
|
||
}
|
||
// 编辑器内文字拖拽或资源文件拖拽或按住 alt/shift 拖拽反链图标进入编辑器时不能运行 event.preventDefault(), 否则无光标; 需放在 !window.siyuan.dragElement 之后
|
||
event.preventDefault();
|
||
targetElement = hasClosestByClassName(event.target, "av__gallery-item") || hasClosestByClassName(event.target, "av__gallery-add") ||
|
||
hasClosestByClassName(event.target, "av__row") || hasClosestByClassName(event.target, "av__row--util") ||
|
||
hasClosestBlock(event.target);
|
||
if (targetElement && ["gallery", "kanban"].includes(targetElement.getAttribute("data-av-type")) && event.target.classList.contains("av__gallery")) {
|
||
// 拖拽到属性视图 gallery 内,但没选中 item
|
||
return;
|
||
}
|
||
const point = {x: event.clientX, y: event.clientY, className: ""};
|
||
|
||
// 超级块中有a,b两个段落块,移动到 ab 之间的间隙 targetElement 会变为超级块,需修正为 a
|
||
if (targetElement && (targetElement.classList.contains("bq") || targetElement.classList.contains("sb") || targetElement.classList.contains("list") || targetElement.classList.contains("li"))) {
|
||
let prevElement = hasClosestBlock(document.elementFromPoint(point.x, point.y - 6));
|
||
while (prevElement && targetElement.contains(prevElement)) {
|
||
if (prevElement.nextElementSibling?.getAttribute("data-node-id")) {
|
||
targetElement = prevElement;
|
||
}
|
||
prevElement = prevElement.parentElement;
|
||
}
|
||
}
|
||
|
||
if (!targetElement) {
|
||
if (event.clientY > editorElement.lastElementChild.getBoundingClientRect().bottom) {
|
||
// 命中底部
|
||
targetElement = editorElement.lastElementChild as HTMLElement;
|
||
point.className = "dragover__bottom";
|
||
} else if (event.clientY < editorElement.firstElementChild.getBoundingClientRect().top) {
|
||
// 命中顶部
|
||
targetElement = editorElement.firstElementChild as HTMLElement;
|
||
point.className = "dragover__top";
|
||
} else if (contentRect) {
|
||
const editorPosition = {
|
||
left: contentRect.left + parseInt(editorElement.style.paddingLeft),
|
||
right: contentRect.left + protyle.contentElement.clientWidth - parseInt(editorElement.style.paddingRight)
|
||
};
|
||
if (event.clientX < editorPosition.left) {
|
||
// 左侧
|
||
point.x = editorPosition.left;
|
||
point.className = "dragover__left";
|
||
} else if (event.clientX >= editorPosition.right) {
|
||
// 右侧
|
||
point.x = editorPosition.right - 6;
|
||
point.className = "dragover__right";
|
||
}
|
||
targetElement = document.elementFromPoint(point.x, point.y) as HTMLElement;
|
||
if (targetElement.classList.contains("protyle-wysiwyg")) {
|
||
// 命中间隙
|
||
targetElement = document.elementFromPoint(point.x, point.y - 6) as HTMLElement;
|
||
}
|
||
targetElement = hasTopClosestByAttribute(targetElement, "data-node-id", null);
|
||
}
|
||
} else if (targetElement && targetElement.classList.contains("list")) {
|
||
if (gutterTypes[0] !== "nodelistitem") {
|
||
targetElement = hasClosestBlock(document.elementFromPoint(event.clientX, event.clientY - 6));
|
||
} else {
|
||
targetElement = hasClosestByClassName(document.elementFromPoint(event.clientX, event.clientY - 6), "li");
|
||
}
|
||
}
|
||
if (gutterType && gutterType.startsWith(`${Constants.SIYUAN_DROP_GUTTER}NodeAttributeView${Constants.ZWSP}Col${Constants.ZWSP}`.toLowerCase())) {
|
||
// 表头只能拖拽到当前 av 的表头中
|
||
targetElement = hasClosestByClassName(event.target, "av__cell");
|
||
if (targetElement) {
|
||
const targetRowElement = hasClosestByClassName(targetElement, "av__row--header");
|
||
const dragRowElement = hasClosestByClassName(window.siyuan.dragElement, "av__row--header");
|
||
if (targetElement === window.siyuan.dragElement || !targetRowElement || !dragRowElement ||
|
||
(targetRowElement && dragRowElement && targetRowElement !== dragRowElement)
|
||
) {
|
||
targetElement = false;
|
||
}
|
||
}
|
||
} else if (targetElement && gutterType && gutterType.startsWith(`${Constants.SIYUAN_DROP_GUTTER}NodeAttributeViewRowMenu${Constants.ZWSP}`.toLowerCase())) {
|
||
if ((!targetElement.classList.contains("av__row") && !targetElement.classList.contains("av__row--util")) ||
|
||
(window.siyuan.dragElement && !window.siyuan.dragElement.contains(targetElement))) {
|
||
// 行只能拖拽当前 av 中
|
||
targetElement = false;
|
||
} else {
|
||
const bodyElement = hasClosestByClassName(targetElement, "av__body");
|
||
if (bodyElement) {
|
||
const blockElement = hasClosestBlock(bodyElement) as HTMLElement;
|
||
const groupID = bodyElement.getAttribute("data-group-id");
|
||
// 模板、创建时间、更新时间 字段作为分组方式时不允许跨分组拖拽 https://github.com/siyuan-note/siyuan/issues/15553
|
||
const isTCU = ["template", "created", "updated"].includes(bodyElement.getAttribute("data-dtype"));
|
||
// 排序只能夸组拖拽
|
||
const hasSort = blockElement.querySelector('.block__icon[data-type="av-sort"]')?.classList.contains("block__icon--active");
|
||
gutterTypes[2].split(",").find(item => {
|
||
const sourceGroupID = item ? item.split("@")[1] : "";
|
||
if (sourceGroupID !== groupID && isTCU) {
|
||
targetElement = false;
|
||
return true;
|
||
}
|
||
if (sourceGroupID === groupID && hasSort) {
|
||
targetElement = false;
|
||
return true;
|
||
}
|
||
});
|
||
}
|
||
}
|
||
} else if (targetElement && gutterType && gutterType.startsWith(`${Constants.SIYUAN_DROP_GUTTER}NodeAttributeView${Constants.ZWSP}GalleryItem${Constants.ZWSP}`.toLowerCase())) {
|
||
const containerElement = hasClosestByClassName(event.target, "av__container");
|
||
if (targetElement.classList.contains("av") || !containerElement ||
|
||
!containerElement.contains(window.siyuan.dragElement) || targetElement === window.siyuan.dragElement) {
|
||
// gallery item 只能拖拽当前 av 中
|
||
targetElement = false;
|
||
} else {
|
||
const bodyElement = hasClosestByClassName(targetElement, "av__body");
|
||
if (bodyElement) {
|
||
const blockElement = hasClosestBlock(bodyElement) as HTMLElement;
|
||
const groupID = bodyElement.getAttribute("data-group-id");
|
||
// 模板、创建时间、更新时间 字段作为分组方式时不允许跨分组拖拽 https://github.com/siyuan-note/siyuan/issues/15553
|
||
const isTCU = ["template", "created", "updated"].includes(bodyElement.getAttribute("data-dtype"));
|
||
// 排序只能夸组拖拽
|
||
const hasSort = blockElement.querySelector('.block__icon[data-type="av-sort"]')?.classList.contains("block__icon--active");
|
||
gutterTypes[2].split(",").find(item => {
|
||
const sourceGroupID = item ? item.split("@")[1] : "";
|
||
if (sourceGroupID !== groupID && isTCU) {
|
||
targetElement = false;
|
||
return true;
|
||
}
|
||
if (sourceGroupID === groupID && hasSort) {
|
||
targetElement = false;
|
||
return true;
|
||
}
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!targetElement) {
|
||
editorElement.querySelectorAll(".dragover__bottom, .dragover__top, .dragover, .dragover__left, .dragover__right").forEach((item: HTMLElement) => {
|
||
item.classList.remove("dragover__top", "dragover__bottom", "dragover", "dragover__left", "dragover__right");
|
||
});
|
||
return;
|
||
}
|
||
const isNotAvItem = !targetElement.classList.contains("av__row") &&
|
||
!targetElement.classList.contains("av__row--util") &&
|
||
!targetElement.classList.contains("av__gallery-item") &&
|
||
!targetElement.classList.contains("av__gallery-add");
|
||
if (targetElement && dragoverElement && targetElement === dragoverElement) {
|
||
// 性能优化,目标为同一个元素不再进行校验
|
||
const nodeRect = targetElement.getBoundingClientRect();
|
||
editorElement.querySelectorAll(".dragover__left, .dragover__right, .dragover__bottom, .dragover__top, .dragover").forEach((item: HTMLElement) => {
|
||
item.classList.remove("dragover__top", "dragover__bottom", "dragover__left", "dragover__right", "dragover");
|
||
item.removeAttribute("select-start");
|
||
item.removeAttribute("select-end");
|
||
});
|
||
// 文档树拖拽限制
|
||
if (fileTreeIds.indexOf("-") > -1 && isNotAvItem) {
|
||
if (!event.altKey) {
|
||
return;
|
||
} else if (fileTreeIds.split(",").includes(protyle.block.rootID) && event.altKey) {
|
||
return;
|
||
}
|
||
}
|
||
if (targetElement.getAttribute("data-type") === "NodeAttributeView" && hasClosestByTag(event.target, "TD")) {
|
||
return;
|
||
}
|
||
if (point.className) {
|
||
targetElement.classList.add(point.className);
|
||
addDragover(targetElement);
|
||
return;
|
||
}
|
||
// 忘记为什么要限定文档树的拖拽了,先放开 https://github.com/siyuan-note/siyuan/pull/13284#issuecomment-2503853135
|
||
if (targetElement.getAttribute("data-type") === "NodeListItem") {
|
||
if (event.clientY > nodeRect.top + nodeRect.height / 2) {
|
||
targetElement.classList.add("dragover__bottom");
|
||
addDragover(targetElement);
|
||
} else if (!targetElement.classList.contains("av__row--header")) {
|
||
targetElement.classList.add("dragover__top");
|
||
addDragover(targetElement);
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (targetElement.classList.contains("av__cell")) {
|
||
if (event.clientX < nodeRect.left + nodeRect.width / 2 && event.clientX > nodeRect.left &&
|
||
!targetElement.classList.contains("av__row") && targetElement.previousElementSibling !== window.siyuan.dragElement) {
|
||
targetElement.classList.add("dragover__left");
|
||
} else if (event.clientX > nodeRect.right - nodeRect.width / 2 && event.clientX <= nodeRect.right + 1 &&
|
||
!targetElement.classList.contains("av__row") && targetElement !== window.siyuan.dragElement.previousElementSibling) {
|
||
if (window.siyuan.dragElement.previousElementSibling.classList.contains("av__colsticky") &&
|
||
targetElement === window.siyuan.dragElement.previousElementSibling.lastElementChild) {
|
||
// 拖拽到固定列的最后一个元素
|
||
} else {
|
||
targetElement.classList.add("dragover__right");
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
// gallery & kanban
|
||
if (targetElement.classList.contains("av__gallery-item")) {
|
||
if (hasClosestByClassName(targetElement, "av__kanban-group")) {
|
||
const midTop = nodeRect.top + nodeRect.height / 2;
|
||
if (event.clientY < midTop && event.clientY > nodeRect.top - 13) {
|
||
targetElement.classList.add("dragover__top");
|
||
} else if (event.clientY > midTop && event.clientY <= nodeRect.bottom + 13) {
|
||
targetElement.classList.add("dragover__bottom");
|
||
}
|
||
} else {
|
||
const midLeft = nodeRect.left + nodeRect.width / 2;
|
||
if (event.clientX < midLeft && event.clientX > nodeRect.left - 13) {
|
||
targetElement.classList.add("dragover__left");
|
||
} else if (event.clientX > midLeft && event.clientX <= nodeRect.right + 13) {
|
||
targetElement.classList.add("dragover__right");
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
if (targetElement.classList.contains("av__gallery-add")) {
|
||
if (hasClosestByClassName(targetElement, "av__kanban-group")) {
|
||
targetElement.classList.add("dragover__top");
|
||
} else {
|
||
targetElement.classList.add("dragover__left");
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (event.clientX < nodeRect.left + 32 && event.clientX >= nodeRect.left - 1 &&
|
||
!targetElement.classList.contains("av__row")) {
|
||
targetElement.classList.add("dragover__left");
|
||
addDragover(targetElement);
|
||
} else if (event.clientX > nodeRect.right - 32 && event.clientX < nodeRect.right &&
|
||
!targetElement.classList.contains("av__row")) {
|
||
targetElement.classList.add("dragover__right");
|
||
addDragover(targetElement);
|
||
} else if (targetElement.classList.contains("av__row--header")) {
|
||
targetElement.classList.add("dragover__bottom");
|
||
} else if (targetElement.classList.contains("av__row--util")) {
|
||
targetElement.previousElementSibling.classList.add("dragover__bottom");
|
||
} else {
|
||
if (event.clientY > nodeRect.top + nodeRect.height / 2 && disabledPosition !== "bottom") {
|
||
targetElement.classList.add("dragover__bottom");
|
||
addDragover(targetElement);
|
||
} else if (disabledPosition !== "top") {
|
||
targetElement.classList.add("dragover__top");
|
||
addDragover(targetElement);
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (fileTreeIds.indexOf("-") > -1) {
|
||
if (fileTreeIds.split(",").includes(protyle.block.rootID) && isNotAvItem && event.altKey) {
|
||
dragoverElement = undefined;
|
||
editorElement.querySelectorAll(".dragover__left, .dragover__right, .dragover__bottom, .dragover__top, .dragover").forEach((item: HTMLElement) => {
|
||
item.classList.remove("dragover__top", "dragover__bottom", "dragover__left", "dragover__right", "dragover");
|
||
item.removeAttribute("select-start");
|
||
item.removeAttribute("select-end");
|
||
});
|
||
} else {
|
||
dragoverElement = targetElement;
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (gutterType) {
|
||
disabledPosition = "";
|
||
// gutter 文档内拖拽限制
|
||
// 排除自己及子孙
|
||
if (gutterTypes[0] === "nodeattributeview" && gutterTypes[1] === "col" && targetElement.getAttribute("data-id") === gutterTypes[2]) {
|
||
// 表头不能拖到自己上
|
||
clearDragoverElement(dragoverElement);
|
||
return;
|
||
}
|
||
if (gutterTypes[0] === "nodeattributeviewrowmenu" && gutterTypes[2].split("@")[0] === targetElement.getAttribute("data-id")) {
|
||
// 行不能拖到自己上
|
||
clearDragoverElement(dragoverElement);
|
||
return;
|
||
}
|
||
const isSelf = gutterTypes[2].split(",").find((item: string) => {
|
||
if (item && hasClosestByAttribute(targetElement as HTMLElement, "data-node-id", item)) {
|
||
return true;
|
||
}
|
||
});
|
||
if (isSelf && "nodeattributeviewrowmenu" !== gutterTypes[0]) {
|
||
clearDragoverElement(dragoverElement);
|
||
return;
|
||
}
|
||
if (isInEmbedBlock(targetElement)) {
|
||
// 不允许托入嵌入块
|
||
clearDragoverElement(dragoverElement);
|
||
return;
|
||
}
|
||
if (gutterTypes[0] === "nodelistitem" && "NodeListItem" === targetElement.getAttribute("data-type")) {
|
||
if (gutterTypes[1] !== targetElement.getAttribute("data-subtype")) {
|
||
// 排除类型不同的列表项
|
||
clearDragoverElement(dragoverElement);
|
||
return;
|
||
}
|
||
// 选中非列表不能拖拽到列表中 https://github.com/siyuan-note/siyuan/issues/13822
|
||
const notLiItem = Array.from(protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select")).find((item: HTMLElement) => {
|
||
if (!item.classList.contains("li")) {
|
||
return true;
|
||
}
|
||
});
|
||
if (notLiItem) {
|
||
clearDragoverElement(dragoverElement);
|
||
return;
|
||
}
|
||
}
|
||
if (gutterTypes[0] !== "nodelistitem" && targetElement.getAttribute("data-type") === "NodeListItem") {
|
||
// 非列表项不能拖入列表项周围
|
||
clearDragoverElement(dragoverElement);
|
||
return;
|
||
}
|
||
if (gutterTypes[0] === "nodelistitem" && targetElement.parentElement.classList.contains("li") &&
|
||
targetElement.previousElementSibling?.classList.contains("protyle-action")) {
|
||
// 列表项不能拖入列表项中第一个元素之上
|
||
disabledPosition = "top";
|
||
}
|
||
if (gutterTypes[0] === "nodelistitem" && targetElement.nextElementSibling?.classList.contains("list")) {
|
||
// 列表项不能拖入列表上方块的下面
|
||
disabledPosition = "bottom";
|
||
}
|
||
if (targetElement && targetElement.classList.contains("av__row--header")) {
|
||
// 块不能拖在表头上
|
||
disabledPosition = "top";
|
||
}
|
||
dragoverElement = targetElement;
|
||
}
|
||
});
|
||
let counter = 0;
|
||
editorElement.addEventListener("dragleave", (event: DragEvent & { target: HTMLElement }) => {
|
||
if (protyle.disabled) {
|
||
event.preventDefault();
|
||
event.stopPropagation();
|
||
return;
|
||
}
|
||
counter--;
|
||
if (counter === 0) {
|
||
editorElement.querySelectorAll(".dragover__left, .dragover__right, .dragover__bottom, .dragover__top, .dragover").forEach((item: HTMLElement) => {
|
||
item.classList.remove("dragover__top", "dragover__bottom", "dragover__left", "dragover__right", "dragover");
|
||
});
|
||
dragoverElement = undefined;
|
||
}
|
||
});
|
||
editorElement.addEventListener("dragenter", (event) => {
|
||
event.preventDefault();
|
||
counter++;
|
||
});
|
||
editorElement.addEventListener("dragend", () => {
|
||
if (window.siyuan.dragElement) {
|
||
window.siyuan.dragElement.style.opacity = "";
|
||
window.siyuan.dragElement = undefined;
|
||
document.onmousemove = null;
|
||
}
|
||
});
|
||
};
|
||
|
||
const addDragover = (element: HTMLElement) => {
|
||
if (element.classList.contains("sb") ||
|
||
element.classList.contains("li") ||
|
||
element.classList.contains("list") ||
|
||
element.classList.contains("bq")) {
|
||
element.classList.add("dragover");
|
||
}
|
||
};
|
||
|
||
// https://github.com/siyuan-note/siyuan/issues/12651
|
||
const clearDragoverElement = (element: Element) => {
|
||
if (element) {
|
||
element.classList.remove("dragover__top", "dragover__bottom", "dragover__left", "dragover__right", "dragover");
|
||
element = undefined;
|
||
}
|
||
};
|