UNPKG

on-codemerge

Version:

A WYSIWYG editor for on-codemerge is a user-friendly interface that allows users to edit and view their code in real time, exactly as it will appear in the final product

198 lines (197 loc) 9.03 kB
/*! on-codemerge v1.3.1 @author Pavel Kuzmin @license MIT @homepage https://s00d.github.io/on-codemerge/ @repository git+https://github.com/s00d/on-codemerge.git Copyright (c) 2026 Pavel Kuzmin - Built on 2026-07-02T13:39:17.947Z */ var l = Object.defineProperty; var a = (c, t, e) => t in c ? l(c, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : c[t] = e; var s = (c, t, e) => a(c, typeof t != "symbol" ? t + "" : t, e); /* empty css */ /* empty css */ import { BlockContextMenu as h } from "./components/BlockContextMenu.mjs"; import { BlockCommand as r } from "./commands/BlockCommand.mjs"; import { MergeBlocksCommand as d } from "./commands/MergeBlocksCommand.mjs"; import { createToolbarButton as k } from "../ToolbarPlugin/utils.mjs"; import u from "../../icons/block.svg.mjs"; import { Resizer as b } from "../../utils/Resizer.mjs"; class C { constructor() { s(this, "name", "block"); s(this, "hotkeys", [ { keys: "Ctrl+Alt+N", description: "Insert block", command: "block", icon: "🧱" }, { keys: "Ctrl+Alt+T", description: "Insert text block", command: "block-text", icon: "📝" }, { keys: "Ctrl+Alt+C", description: "Insert container", command: "block-container", icon: "📦" } ]); s(this, "editor", null); s(this, "contextMenu", null); s(this, "activeBlock", null); s(this, "currentResizer", null); s(this, "isProcessing", !1); s(this, "toolbarButton", null); } initialize(t) { this.contextMenu = new h(t), this.editor = t, this.addToolbarButton(), this.setupBlockEvents(), this.setupKeyboardEvents(), this.editor.on("block", () => { this.insertBlock(); }), this.editor.on("block-text", () => { this.insertTextBlock(); }), this.editor.on("block-container", () => { this.insertContainerBlock(); }); } addToolbarButton() { var e, o; const t = (e = this.editor) == null ? void 0 : e.getToolbar(); t && (this.toolbarButton = k({ icon: u, title: ((o = this.editor) == null ? void 0 : o.t("Insert Block")) || "Insert Block", onClick: () => this.insertBlock() }), t.appendChild(this.toolbarButton)); } setupBlockEvents() { if (!this.editor) return; const t = this.editor.getContainer(); this.handleBlockClick = this.handleBlockClick.bind(this), this.handleContextMenu = this.handleContextMenu.bind(this), this.handleBlockKeydown = this.handleBlockKeydown.bind(this), this.handleBlockBlur = this.handleBlockBlur.bind(this), this.handleBlockFocus = this.handleBlockFocus.bind(this), this.handleBlockMouseDown = this.handleBlockMouseDown.bind(this), t.addEventListener("click", this.handleBlockClick), t.addEventListener("contextmenu", this.handleContextMenu), t.addEventListener("keydown", this.handleBlockKeydown), t.addEventListener("blur", this.handleBlockBlur, !0), t.addEventListener("focus", this.handleBlockFocus, !0), t.addEventListener("mousedown", this.handleBlockMouseDown); } setupKeyboardEvents() { this.editor && this.editor.on("keydown", (t) => { if (t.key === "Enter" && !t.shiftKey) { const e = t.target, o = e.closest(".editor-block"); o && e.classList.contains("block-content") && (t.preventDefault(), this.createNewBlockAfter(o)); } if (t.key === "Tab") { const e = t.target, o = e.closest(".editor-block"); if (o && e.classList.contains("block-content")) if (t.preventDefault(), t.shiftKey) { const n = o.previousElementSibling; if (n && n.classList.contains("editor-block")) { const i = n.querySelector(".block-content"); i && i.contentEditable === "true" && i.focus(); } } else { const n = o.nextElementSibling; if (n && n.classList.contains("editor-block")) { const i = n.querySelector(".block-content"); i && i.contentEditable === "true" && i.focus(); } } } }); } handleBlockClick(t) { if (this.isProcessing) return; const e = t.target.closest(".editor-block"); if (!e) { this.deactivateBlock(); return; } if (this.activeBlock === e) { const o = e.querySelector(".block-content"); e.getAttribute("data-block-type") === "text" && o && o.contentEditable !== "true" && (o.contentEditable = "true"); return; } this.deactivateBlock(), this.activateBlock(e), e.getAttribute("data-block-type") === "text" && this.createResizer(e); } handleContextMenu(t) { var o; const e = t.target.closest(".editor-block"); if (e instanceof HTMLElement) { t.preventDefault(); const n = t.clientX, i = t.clientY; (o = this.contextMenu) == null || o.show(e, n, i); } } handleBlockKeydown(t) { const e = t.target, o = e.closest(".editor-block"); o && (t.key === "Backspace" && e.classList.contains("block-content") && (e.textContent || "").length === 0 && (t.preventDefault(), this.mergeWithPreviousBlock(o)), t.key === "Delete" && e.classList.contains("block-content") && (e.textContent || "").length === 0 && (t.preventDefault(), this.mergeWithNextBlock(o))); } handleBlockBlur(t) { var o; const e = (o = this.editor) == null ? void 0 : o.getContainer(); e && !e.contains(t.relatedTarget) && setTimeout(() => { this.autoMergeEmptyBlocks(); }, 100); } handleBlockFocus(t) { const e = t.target, o = e.closest(".editor-block"); o && e.classList.contains("block-content") && this.activeBlock !== o && (this.deactivateBlock(), this.activateBlock(o)); } handleBlockMouseDown(t) { const e = t.target; e.closest(".editor-block") && e.classList.contains("block-content") && e.contentEditable; } activateBlock(t) { this.activeBlock = t, t.classList.add("active"); const e = t.querySelector(".block-content"); e && (t.getAttribute("data-block-type") === "text" ? e.contentEditable = "true" : e.contentEditable = "false"); } deactivateBlock() { this.activeBlock && this.activeBlock.classList.remove("active"), this.activeBlock = null, this.currentResizer && (this.currentResizer.destroy(), this.currentResizer = null); } createResizer(t) { this.currentResizer && this.currentResizer.destroy(), this.currentResizer = new b(t, { handleSize: 8, handleColor: "#2563eb", onResizeStart: () => { var e; (e = this.editor) == null || e.disableObserver(), this.isProcessing = !0; }, onResize: (e, o) => { t.style.width = e + "px", t.style.height = o + "px"; }, onResizeEnd: () => { var e; (e = this.editor) == null || e.enableObserver(), this.isProcessing = !1; } }); } insertBlock() { if (!this.editor) return; new r(this.editor).execute(); } insertTextBlock() { if (!this.editor) return; new r(this.editor).execute({ type: "text" }); } insertContainerBlock() { if (!this.editor) return; new r(this.editor).execute({ type: "container" }); } createNewBlockAfter(t) { if (!this.editor) return; new r(this.editor).execute({ type: "text" }); } mergeWithPreviousBlock(t) { const e = t.previousElementSibling; e && e.classList.contains("editor-block") && this.mergeBlocks([e, t]); } mergeWithNextBlock(t) { const e = t.nextElementSibling; e && e.classList.contains("editor-block") && this.mergeBlocks([t, e]); } mergeBlocks(t) { if (!this.editor || t.length < 2) return; const e = new d(this.editor); e.setData({ blocks: t }), e.execute(); } autoMergeEmptyBlocks() { if (!this.editor) return; const e = this.editor.getContainer().querySelectorAll(".editor-block"); for (const o of e) { const n = o.querySelector(".block-content"); if (n && (!n.textContent || n.textContent.trim() === "")) { const i = o.nextElementSibling; if (i && i.classList.contains("editor-block")) { this.mergeBlocks([o, i]); break; } } } } destroy() { var t, e, o; if (this.editor) { const n = this.editor.getContainer(); n.removeEventListener("click", this.handleBlockClick), n.removeEventListener("contextmenu", this.handleContextMenu), n.removeEventListener("keydown", this.handleBlockKeydown), n.removeEventListener("blur", this.handleBlockBlur, !0), n.removeEventListener("focus", this.handleBlockFocus, !0), n.removeEventListener("mousedown", this.handleBlockMouseDown); } (t = this.editor) == null || t.off("block"), (e = this.editor) == null || e.off("block-text"), (o = this.editor) == null || o.off("block-container"), this.currentResizer && (this.currentResizer.destroy(), this.currentResizer = null), this.contextMenu && (this.contextMenu.destroy(), this.contextMenu = null), this.deactivateBlock(), this.toolbarButton && (this.toolbarButton.remove(), this.toolbarButton = null), this.editor = null, this.activeBlock = null; } } export { C as BlockPlugin };