UNPKG

@opentiny/fluent-editor

Version:

A rich text editor based on Quill 2.0, which extends rich modules and formats on the basis of Quill. It's powerful and out-of-the-box.

370 lines (369 loc) 12.5 kB
var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); import Quill from "quill"; import { getAllConfigs } from "../config-utils.es.js"; import { expandIcon, contractIcon } from "../icons.es.js"; import { initContextMenu } from "../modules/context-menu.es.js"; import { createControlPanel } from "../modules/control-panel.es.js"; import { FlowChartResizeAction } from "../modules/custom-resize-action.es.js"; /* empty css */ const BlockEmbed = Quill.import("blots/embed"); const _FlowChartPlaceholderBlot = class _FlowChartPlaceholderBlot extends BlockEmbed { constructor(scroll, domNode) { super(scroll, domNode); __publicField(this, "quill", null); __publicField(this, "flowChart", null); __publicField(this, "data"); __publicField(this, "contextMenu", null); __publicField(this, "currentElement", null); __publicField(this, "width", 100); __publicField(this, "height", 500); __publicField(this, "parentObserver", null); __publicField(this, "nextPObserver", null); const data = _FlowChartPlaceholderBlot.value(domNode); this.width = data.width || 100; this.height = data.height || 500; this.domNode.style.width = `${this.width}${data.width ? "px" : "%"}`; this.domNode.style.height = `${this.height}px`; this.domNode.style.maxWidth = "100%"; this.domNode.style.border = "1px solid #e8e8e8"; this.domNode.setAttribute("contenteditable", "false"); this.data = _FlowChartPlaceholderBlot.value(this.domNode); this.initFlowChart(); } static value(domNode) { const dataStr = JSON.parse(domNode.getAttribute("data-flow-chart")); const value = dataStr.root ? dataStr.root : dataStr; if (domNode.hasAttribute("width")) { value.width = Number.parseInt(domNode.getAttribute("width"), 10); } if (domNode.hasAttribute("height")) { value.height = Number.parseInt(domNode.getAttribute("height"), 10); } return dataStr.root ? dataStr.root : dataStr; } static create(value) { const node = super.create(); if (value) { node.setAttribute("data-flow-chart", JSON.stringify(value)); } if (value.width) { node.setAttribute("width", String(value.width)); node.style.width = `${value.width}%`; } if (value.height) { node.setAttribute("height", String(value.height)); node.style.height = `${value.height}px`; } node.setAttribute("contenteditable", "false"); return node; } static findQuill(el) { let cur = el; while (cur) { const q = cur.__quillInstance; if (q) return q; cur = cur.parentElement; } return null; } attach() { super.attach(); this.quill = _FlowChartPlaceholderBlot.findQuill(this.domNode); } initFlowChart() { if (this.domNode.isConnected) { this.insertFlowChartEditor(); } else { const observer = new MutationObserver(() => { if (this.domNode.isConnected) { this.insertFlowChartEditor(); observer.disconnect(); } }); observer.observe(document.body, { childList: true, subtree: true }); } } insertFlowChartEditor() { this.domNode.style.width = `${this.width}${this.data.width ? "px" : "%"}`; this.domNode.style.height = `${this.height}px`; this.updateAlignmentStyle(); this.observeParentAlignment(); const { gridConfig, backgroundConfig, resizeConfig, deps } = getAllConfigs(this.quill); const { LogicFlow, DndPanel, SelectionSelect, Snapshot } = deps || window; this.flowChart = new LogicFlow({ container: this.domNode, stopScrollGraph: true, stopZoomGraph: true, allowResize: true, allowRotate: true, editable: true, preventDefaultDoubleClick: true, preventDefault: true, grid: gridConfig, background: backgroundConfig, plugins: [DndPanel, SelectionSelect, Snapshot] }); this.flowChart.setPatternItems([ { className: "lf-shape-dashed", callback: () => { this.flowChart.openSelectionSelect(); this.flowChart.once("selection:selected", () => { this.flowChart.closeSelectionSelect(); }); } }, { type: "rect", text: "矩形", className: "lf-shape-rect" }, { type: "circle", text: "圆形", className: "lf-shape-circle" }, { type: "ellipse", text: "椭圆", className: "lf-shape-ellipse" }, { type: "diamond", text: "菱形", className: "lf-shape-diamond" } ]); if (resizeConfig) { new FlowChartResizeAction(this); } createControlPanel(this, this.quill); initContextMenu(this, this.quill); this.observeOwnParentChange(); this.observeNextPElement(); this.addMouseHoverEvents(); this.flowChart.render(this.data); this.flowChart.on("graph:updated", () => { this.data = this.flowChart.getGraphData(); this.domNode.setAttribute("data-flow-chart", JSON.stringify(this.data)); }); this.flowChart.on("history:change", () => { this.data = this.flowChart.getGraphData(); this.domNode.setAttribute("data-flow-chart", JSON.stringify(this.data)); }); this.flowChart.on("node:dbclick", this.handleNodeDblClick.bind(this)); this.flowChart.on("edge:dbclick", this.handleNodeDblClick.bind(this)); this.domNode.addEventListener("click", (e) => { if (this.quill) { const flowChartBlot = Quill.find(this.domNode); const index = this.quill.getIndex(flowChartBlot); if (index && typeof index === "number") { this.quill.setSelection(index + 1, 0); } } }); } addMouseHoverEvents() { this.domNode.addEventListener("mouseenter", () => { this.showControlPanel(); }); this.domNode.addEventListener("mouseleave", () => { var _a; (_a = this.flowChart) == null ? void 0 : _a.clearSelectElements(); this.hideControlPanel(); }); } getControlElements() { const leftUpControl = this.domNode.querySelector(".lf-dndpanel"); const control = this.domNode.querySelector(".ql-flow-chart-control"); const panelStatusIcon = this.domNode.querySelector('[data-control-type="panel-status"]'); return { leftUpControl, control, panelStatusIcon }; } showControlPanel() { const { leftUpControl, control, panelStatusIcon } = this.getControlElements(); if (!leftUpControl || !control) return; leftUpControl.style.display = "block"; control.style.display = "flex"; if (panelStatusIcon) { const iconElement = panelStatusIcon.querySelector("i") || panelStatusIcon; iconElement.innerHTML = expandIcon; } } hideControlPanel() { const { leftUpControl, control, panelStatusIcon } = this.getControlElements(); if (!leftUpControl || !control) return; leftUpControl.style.display = "none"; control.style.display = "none"; if (panelStatusIcon) { const iconElement = panelStatusIcon.querySelector("i") || panelStatusIcon; iconElement.innerHTML = contractIcon; } } observeOwnParentChange() { let currentParent = this.domNode.parentElement; const observer = new MutationObserver(() => { if (this.domNode.parentElement !== currentParent) { currentParent = this.domNode.parentElement; this.observeParentAlignment(); } }); observer.observe(document.body, { attributes: false, childList: true, subtree: true }); } observeParentAlignment() { if (this.parentObserver) { this.parentObserver.disconnect(); } const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.attributeName === "class") { this.updateAlignmentStyle(); } }); }); this.parentObserver = observer; const parent = this.domNode.parentElement; if (parent) { observer.observe(parent, { attributes: true, attributeFilter: ["class"] }); this.updateAlignmentStyle(); } } updateAlignmentStyle() { const parent = this.domNode.parentElement; if (!parent) return; this.domNode.style.margin = ""; this.domNode.style.display = "block"; if (parent.classList.contains("ql-align-center")) { this.domNode.style.margin = "0 auto"; } else if (parent.classList.contains("ql-align-right")) { this.domNode.style.marginLeft = "auto"; this.domNode.style.marginRight = "0"; } else { this.domNode.style.marginLeft = "0"; this.domNode.style.marginRight = "auto"; } } observeNextPElement() { if (this.nextPObserver) { this.nextPObserver.disconnect(); } const parentElement = this.domNode.parentElement; if (!parentElement) { return; } const trackedParentElement = parentElement; const parentElementId = parentElement.getAttribute("id") || `flow-chart-parent-${Date.now()}`; parentElement.setAttribute("id", parentElementId); const observer = new MutationObserver(() => { if (!document.contains(trackedParentElement)) { const elementById = document.getElementById(parentElementId); if (!elementById) { this.remove(); observer.disconnect(); } } }); observer.observe(document.body, { childList: true, subtree: true }); this.nextPObserver = observer; } // 处理节点双击事件 handleNodeDblClick(event) { const { data, position, e } = event; if (data && data.id) { this.createEditInput(data, position, e); } } // 创建编辑输入框 createEditInput(nodeData, position, e) { var _a; const input = document.createElement("textarea"); input.className = "ql-flow-chart-edit-input"; input.value = ((_a = nodeData.text) == null ? void 0 : _a.value) || ""; const autoResize = () => { input.style.height = "auto"; input.style.height = `${input.scrollHeight}px`; }; Object.assign(input.style, { position: "absolute", boxSizing: "border-box", width: "100px", height: "35px", padding: "5px", lineHeight: "1.2", whiteSpace: "pre", textAlign: "center", background: "#fff", border: "1px solid #edefed", borderRadius: "3px", outline: "none", transform: "translate(-50%, -50%)", resize: "none", zIndex: "1000", left: `${e.pageX}px`, top: `${e.pageY}px`, overflow: "hidden" }); document.body.appendChild(input); autoResize(); input.addEventListener("input", autoResize); input.addEventListener("keydown", (e2) => { if (e2.key === "Enter" && !e2.shiftKey) { this.flowChart.updateText(nodeData.id, input.value); autoResize(); } }); input.focus(); this.flowChart.on("blank:mousedown", () => { this.flowChart.updateText(nodeData.id, input.value); input.remove(); }); this.flowChart.on("node:click", () => { this.flowChart.updateText(nodeData.id, input.value); input.remove(); }); this.flowChart.on("edge:click", () => { this.flowChart.updateText(nodeData.id, input.value); input.remove(); }); } updateText(nodeId, text) { this.flowChart.updateNode(nodeId, { text: { value: text } }); } destroyFlowChart() { if (this.flowChart) { this.flowChart.destroy(); this.flowChart = null; } const editInputs = document.querySelectorAll(".ql-flow-chart-edit-input"); editInputs.forEach((input) => input.remove()); if (this.nextPObserver) { this.nextPObserver.disconnect(); this.nextPObserver = null; } } remove() { this.destroyFlowChart(); super.remove(); } }; __publicField(_FlowChartPlaceholderBlot, "blotName", "flow-chart"); __publicField(_FlowChartPlaceholderBlot, "tagName", "div"); __publicField(_FlowChartPlaceholderBlot, "className", "ql-flow-chart-item"); let FlowChartPlaceholderBlot = _FlowChartPlaceholderBlot; export { FlowChartPlaceholderBlot as default }; //# sourceMappingURL=flow-chart-blot.es.js.map