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

322 lines (321 loc) 10.5 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 y = Object.defineProperty; var S = (p, e, t) => e in p ? y(p, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : p[e] = t; var d = (p, e, t) => S(p, typeof e != "symbol" ? e + "" : e, t); /* empty css */ /* empty css */ import { createToolbarButton as T } from "../ToolbarPlugin/utils.mjs"; import { PopupManager as A } from "../../core/ui/PopupManager.mjs"; import { createContainer as u, createButton as E, createInputField as I } from "../../utils/helpers.mjs"; import B from "../../icons/style.svg.mjs"; import { AVAILABLE_STYLES as C } from "./constants.mjs"; class P { constructor() { d(this, "name", "block-style"); d(this, "editor", null); d(this, "toolbarButton", null); d(this, "popup", null); d(this, "selectedElement", null); } initialize(e) { this.editor = e, this.popup = new A(e, { title: e.t("Block Style Editor"), className: "block-style-editor", closeOnClickOutside: !0, items: this.createPopupItems(), buttons: [ { label: e.t("Apply"), variant: "primary", onClick: () => this.applyStyles() }, { label: e.t("Cancel"), variant: "secondary", onClick: () => { var t; return (t = this.popup) == null ? void 0 : t.hide(); } } ] }), this.addToolbarButton(), this.editor.on("selectionchange", () => this.handleSelectionChange()); } addToolbarButton() { var t, l; const e = (t = this.editor) == null ? void 0 : t.getToolbar(); e && (this.toolbarButton = T({ icon: B, title: (l = this.editor) == null ? void 0 : l.t("Block Style"), onClick: () => this.showPopup() }), e.appendChild(this.toolbarButton)); } handleSelectionChange() { var n, o, s, i, r, a; const e = (o = (n = this.editor) == null ? void 0 : n.getTextFormatter()) == null ? void 0 : o.getSelection(); if (!e || e.rangeCount === 0) return; const t = e.getRangeAt(0); let l = null; if (this.editor) { if (t.commonAncestorContainer.nodeType === Node.TEXT_NODE ? l = t.commonAncestorContainer.parentElement : t.commonAncestorContainer.nodeType === Node.ELEMENT_NODE && (l = t.commonAncestorContainer), l === ((s = this.editor) == null ? void 0 : s.getContainer())) { this.selectedElement = null, (i = this.toolbarButton) == null || i.classList.remove("active"); return; } if (l) { const c = this.findNearestBlockElement(l); if (c && this.isBlockElement(c)) { this.selectedElement = c, (r = this.toolbarButton) == null || r.classList.add("active"), console.log("BlockStylePlugin: Selected element:", c.tagName, c); return; } } this.selectedElement = null, (a = this.toolbarButton) == null || a.classList.remove("active"), console.log("BlockStylePlugin: No block element found"); } } findNearestBlockElement(e) { var l; let t = e; for (; t && t !== ((l = this.editor) == null ? void 0 : l.getContainer()); ) { if (this.isBlockElement(t)) return t; t = t.parentElement; } return null; } getCurrentBlockElement() { var n, o, s; const e = (o = (n = this.editor) == null ? void 0 : n.getTextFormatter()) == null ? void 0 : o.getSelection(); if (!e || e.rangeCount === 0) return null; const t = e.getRangeAt(0); let l = null; return t.commonAncestorContainer.nodeType === Node.TEXT_NODE ? l = t.commonAncestorContainer.parentElement : t.commonAncestorContainer.nodeType === Node.ELEMENT_NODE && (l = t.commonAncestorContainer), !l || l === ((s = this.editor) == null ? void 0 : s.getContainer()) ? null : this.findNearestBlockElement(l); } isBlockElement(e) { return [ "DIV", "P", "H1", "H2", "H3", "H4", "H5", "H6", "SECTION", "ARTICLE", "HEADER", "FOOTER", "NAV", "ASIDE", "MAIN", "FIGURE", "FIGCAPTION", "BLOCKQUOTE", "UL", "OL", "LI", "DL", "DT", "DD", "PRE", "HR", "ADDRESS", "TABLE", "THEAD", "TBODY", "TFOOT", "TR", "TH", "TD", "CAPTION", "FORM", "FIELDSET", "LEGEND", "DETAILS", "SUMMARY", "MENU", "DIALOG", "IFRAME", "CANVAS", "VIDEO", "AUDIO", "PICTURE", "SOURCE", "TRACK", "MAP", "OBJECT", "PARAM", "EMBED", "NOSCRIPT", "PROGRESS", "METER", "TIME", "MARK", "RUBY", "RT", "RP", "BDI", "BDO", "WBR", "INS", "DEL", "CODE", "KBD", "SAMP", "VAR", "CITE", "Q", "ABBR", "DFN", "SMALL", "STRONG", "EM", "I", "B", "U", "S", "STRIKE", "SUB", "SUP", "SPAN", "BR", "IMG", "A", "BUTTON", "INPUT", "TEXTAREA", "SELECT", "OPTION", "OPTGROUP", "LABEL", "OUTPUT", "DATALIST", "KEYGEN", "ISINDEX", "BASE", "LINK", "META", "STYLE", "SCRIPT", "TITLE", "HEAD", "BODY", "HTML" ].includes(e.tagName); } createPopupItems() { return [ { type: "custom", id: "style-editor", content: () => this.createStyleEditor() }, { type: "custom", id: "class-editor", content: () => this.createClassEditor() } ]; } createStyleEditor() { var s, i, r; const e = u("popup-item"), t = document.createElement("label"); t.textContent = ((s = this.editor) == null ? void 0 : s.t("Inline Styles")) || "Inline Styles", e.appendChild(t); const l = u("style-list"); (((r = (i = this.selectedElement) == null ? void 0 : i.getAttribute("style")) == null ? void 0 : r.split(";")) || []).forEach((a) => { if (a.trim()) { const [c, m] = a.split(":").map((h) => h.trim()); if (c && m) { const h = this.createStyleItem(c, m); l.appendChild(h); } } }); const o = E("Add Style", () => this.addStyle(l)); return o.className = "add-button", e.appendChild(l), e.appendChild(o), e; } createStyleItem(e, t) { const l = u("style-item"), n = document.createElement("select"); n.className = "style-property-select", Object.keys(C).forEach((i) => { const r = document.createElement("option"); r.value = i, r.textContent = i, r.selected = i === e, n.appendChild(r); }); const o = document.createElement("select"); o.className = "style-value-select", n.addEventListener("change", () => { this.updateValueSelect(o, n.value); }), this.updateValueSelect(o, n.value, t); const s = E("×", () => l.remove()); return s.className = "remove-button", l.appendChild(n), l.appendChild(o), l.appendChild(s), l; } /** * Обновляет выпадающий список значений в зависимости от выбранного свойства. */ updateValueSelect(e, t, l = "") { e.innerHTML = "", (C[t] || []).forEach((o) => { const s = document.createElement("option"); s.value = o, s.textContent = o, s.selected = o === l, e.appendChild(s); }); } addStyle(e) { const t = this.createStyleItem("", ""); e.appendChild(t); } createClassEditor() { var s, i, r; const e = u("popup-item"), t = document.createElement("label"); t.textContent = ((s = this.editor) == null ? void 0 : s.t("CSS Classes")) || "CSS Classes", e.appendChild(t); const l = u("class-list"); (((r = (i = this.selectedElement) == null ? void 0 : i.getAttribute("class")) == null ? void 0 : r.split(" ")) || []).forEach((a) => { if (a.trim()) { const c = this.createClassItem(a); l.appendChild(c); } }); const o = E("Add Class", () => this.addClass(l)); return o.className = "add-button", e.appendChild(l), e.appendChild(o), e; } createClassItem(e) { const t = u("class-item"), l = I("text", "Enter class (e.g., my-class)", e); l.className = "class-input"; const n = E("×", () => t.remove()); return n.className = "remove-button", t.appendChild(l), t.appendChild(n), t; } addClass(e) { const t = this.createClassItem(""); e.appendChild(t); } showPopup() { if (!this.popup) return; const e = this.getCurrentBlockElement(); if (console.log("BlockStylePlugin: showPopup called, currentBlockElement:", e), !e) { const o = document.createElement("div"); o.innerHTML = "<p>Please select a block element to style.</p>", this.popup.setContent(o), this.popup.show(); return; } this.selectedElement = e; const t = document.createElement("div"), l = this.createStyleEditor(), n = this.createClassEditor(); t.appendChild(l), t.appendChild(n), this.popup.setContent(t), this.popup.show(); } applyStyles() { var l, n, o; if (!this.selectedElement || !this.popup) return; (l = this.editor) == null || l.ensureEditorFocus(); const e = (n = this.popup.getElement()) == null ? void 0 : n.querySelector(".style-list"), t = (o = this.popup.getElement()) == null ? void 0 : o.querySelector(".class-list"); if (e) { const s = Array.from(e.querySelectorAll(".style-item")).map((i) => { const r = i.querySelector(".style-property-select"), a = i.querySelector(".style-value-select"), c = r.value.trim(), m = a.value.trim(); return c && m ? `${c}: ${m}` : ""; }).filter((i) => i).join("; "); this.selectedElement.setAttribute("style", s); } if (t) { const s = Array.from(t.querySelectorAll(".class-input")).map((i) => i.value.trim()).filter((i) => i).join(" "); this.selectedElement.setAttribute("class", s); } this.popup.hide(); } destroy() { var e, t, l; (e = this.toolbarButton) == null || e.remove(), this.toolbarButton = null, (t = this.editor) == null || t.off("selectionchange"), (l = this.popup) == null || l.destroy(), this.popup = null, this.editor = null; } } export { P as BlockStylePlugin };