import {hasClosestByClassName} from "../protyle/util/hasClosest"; import Protyle from "../protyle"; import {genUUID} from "../util/genID"; import {setPadding} from "../protyle/ui/initUI"; import {setPosition} from "../util/setPosition"; import {hideElements} from "../protyle/ui/hideElements"; import {Constants} from "../constants"; import {disabledProtyle} from "../protyle/util/onGet"; export class BlockPanel { public element: HTMLElement; public targetElement: HTMLElement; public nodeIds: string[]; public defIds: string[] = []; public id: string; private stmt: string; public editors: Protyle[] = []; public esc: () => void; // stmt 非空且 id 为空为查询嵌入 constructor(options: { targetElement: HTMLElement, nodeIds?: string[], defIds?: string[], stmt?: string, esc?: () => void, }) { this.id = genUUID(); this.stmt = options.stmt; this.targetElement = options.targetElement; this.nodeIds = options.nodeIds; this.defIds = options.defIds || []; this.esc = options.esc; this.element = document.createElement("div"); this.element.classList.add("block__popover", "block__popover--move", "block__popover--top"); const parentElement = hasClosestByClassName(this.targetElement, "block__popover", true); let level = 1; if (parentElement) { this.element.setAttribute("data-oid", parentElement.getAttribute("data-oid")); level = parseInt(parentElement.getAttribute("data-level")) + 1; } else { this.element.setAttribute("data-oid", this.nodeIds[0]); } // 移除同层级其他更高级的 block popover this.element.setAttribute("data-level", level.toString()); for (let i = 0; i < window.siyuan.blockPanels.length; i++) { const item = window.siyuan.blockPanels[i]; if (item.element.getAttribute("data-pin") === "false" && item.targetElement && parseInt(item.element.getAttribute("data-level")) >= level) { item.destroy(); i--; } } document.body.insertAdjacentElement("beforeend", this.element); this.element.addEventListener("mousedown", (event: MouseEvent & { target: HTMLElement }) => { document.querySelectorAll(".block__popover--top").forEach(item => { item.classList.remove("block__popover--top"); }); if (this.element && window.siyuan.blockPanels.length > 1) { this.element.classList.add("block__popover--top"); } let targetElement = hasClosestByClassName(event.target, "block__icons"); let type = "move"; let x = event.clientX - parseInt(this.element.style.left); let y = event.clientY - parseInt(this.element.style.top); if (!targetElement) { x = event.clientX - this.element.clientWidth; y = event.clientY - this.element.clientHeight; targetElement = hasClosestByClassName(event.target, "block__nwse"); type = "nwse-resize"; if (!targetElement) { targetElement = hasClosestByClassName(event.target, "block__ns"); type = "ns-resize"; if (!targetElement) { targetElement = hasClosestByClassName(event.target, "block__ew"); type = "ew-resize"; if (!targetElement) { return; } } } } const documentSelf = document; this.element.style.userSelect = "none"; documentSelf.ondragstart = () => false; documentSelf.onmousemove = (moveEvent: MouseEvent) => { if (!this.element) { return; } let positionX = moveEvent.clientX - x; let positionY = moveEvent.clientY - y; if (type === "move") { if (positionX > window.innerWidth - this.element.clientWidth) { positionX = window.innerWidth - this.element.clientWidth; } if (positionY > window.innerHeight - this.element.clientHeight) { positionY = window.innerHeight - this.element.clientHeight; } this.element.style.left = Math.max(positionX, 0) + "px"; this.element.style.top = Math.max(positionY, Constants.SIZE_TOOLBAR_HEIGHT) + "px"; } else { if (positionX > 200 && positionX < window.innerWidth && (type === "nwse-resize" || type === "ew-resize")) { this.element.style.width = positionX + "px"; } if (positionY > 65 && positionY < window.innerHeight - Constants.SIZE_TOOLBAR_HEIGHT && (type === "nwse-resize" || type === "ns-resize")) { this.element.style.height = positionY + "px"; this.element.style.maxHeight = ""; } } }; documentSelf.onmouseup = () => { if (!this.element) { return; } if (window.siyuan.dragElement) { // 反向链接拖拽 https://ld246.com/article/1632915506502 window.siyuan.dragElement.style.opacity = ""; window.siyuan.dragElement = undefined; } this.element.style.userSelect = "auto"; documentSelf.onmousemove = null; documentSelf.onmouseup = null; documentSelf.ondragstart = null; documentSelf.onselectstart = null; documentSelf.onselect = null; if (type !== "move") { this.editors.forEach(item => { setPadding(item.protyle); }); } }; }); this.targetElement.style.cursor = "wait"; this.element.setAttribute("data-pin", "false"); this.element.addEventListener("dblclick", (event) => { const target = event.target as HTMLElement; const targetElement = hasClosestByClassName(target, "block__icons"); if (targetElement) { const pingElement = targetElement.querySelector('[data-type="pin"]'); if (pingElement.classList.contains("block__icon--active")) { pingElement.classList.remove("block__icon--active"); pingElement.setAttribute("aria-label", window.siyuan.languages.pin); this.element.setAttribute("data-pin", "false"); } else { pingElement.classList.add("block__icon--active"); pingElement.setAttribute("aria-label", window.siyuan.languages.unpin); this.element.setAttribute("data-pin", "true"); } event.preventDefault(); event.stopPropagation(); } }); this.element.addEventListener("click", (event) => { let target = event.target as HTMLElement; while (target && !target.isEqualNode(this.element)) { if (target.classList.contains("block__icon") || target.classList.contains("block__logo")) { const type = target.getAttribute("data-type"); if (type === "close" && this.targetElement) { this.destroy(); } else if (type === "pin") { if (target.classList.contains("block__icon--active")) { target.classList.remove("block__icon--active"); target.setAttribute("aria-label", window.siyuan.languages.pin); this.element.setAttribute("data-pin", "false"); } else { target.classList.add("block__icon--active"); target.setAttribute("aria-label", window.siyuan.languages.unpin); this.element.setAttribute("data-pin", "true"); } } event.preventDefault(); event.stopPropagation(); break; } target = target.parentElement; } }); this.render(); } private initProtyle(editorElement: HTMLElement) { const index = parseInt(editorElement.getAttribute("data-index")); const editor = new Protyle(editorElement, { blockId: this.nodeIds[index], hasContext: false, defId: this.defIds[index] ||this.defIds[0] || "", action: [Constants.CB_GET_ALL], render: { gutter: true, breadcrumbDocName: true, breadcrumbContext: true }, typewriterMode: false, after: (editor) => { if (window.siyuan.config.readonly) { disabledProtyle(editor.protyle); } editorElement.addEventListener("mouseleave", () => { hideElements(["gutter"], editor.protyle); }); } }); this.editors.push(editor); } public destroy() { window.siyuan.blockPanels.find((item, index) => { if (item.id === this.id) { window.siyuan.blockPanels.splice(index, 1); return true; } }); if (this.editors.length > 0) { this.editors.forEach(item => { item.destroy(); }); this.editors = []; } this.element.remove(); this.element = undefined; this.targetElement = undefined; // 移除弹出上使用右键菜单 window.siyuan.menus.menu.remove(); } private render() { if (!this.element.parentElement.parentElement) { this.destroy(); return; } let html = `