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

263 lines (262 loc) 14.4 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 g = Object.defineProperty; var k = (d, n, a) => n in d ? g(d, n, { enumerable: !0, configurable: !0, writable: !0, value: a }) : d[n] = a; var i = (d, n, a) => k(d, typeof n != "symbol" ? n + "" : n, a); import { PopupHeader as $ } from "./PopupHeader.mjs"; import { PopupFooter as w } from "./PopupFooter.mjs"; class S { constructor(n, a = {}) { i(this, "popup"); i(this, "header", null); i(this, "content"); i(this, "overlay"); i(this, "footer", null); i(this, "isVisible", !1); i(this, "boundDocumentKeydown"); i(this, "editor"); this.editor = n; const { className: e = "", closeOnClickOutside: s = !0, items: t = [] } = a; this.popup = this.createPopup(e), this.content = this.createContent(), this.overlay = this.createOverlay(), a.title && (this.header = new $(a.title, () => this.hide()).getElement(), this.popup.appendChild(this.header)), this.popup.appendChild(this.content), a.buttons && (this.footer = new w(a.buttons), this.popup.appendChild(this.footer.getElement())), t.length > 0 && this.setContent(this.createContentFromItems(t)), this.editor.getDOMContext().appendChild(this.overlay), this.editor.getDOMContext().appendChild(this.popup), s && this.overlay.addEventListener("mousedown", (l) => { l.stopPropagation(), this.isVisible && !this.popup.contains(l.target) && this.hide(); }), this.boundDocumentKeydown = (l) => { this.isVisible && l.key === "Escape" && this.hide(); }, this.editor.getDOMContext().addEventListener("keydown", this.boundDocumentKeydown); } createPopup(n) { const a = document.createElement("div"); return a.className = `popup ${n}`, a.style.display = "none", a; } createContent() { const n = document.createElement("div"); return n.className = "popup-content", n; } createContentFromItems(n) { const a = document.createElement("div"); return a.className = "popup-items-container", n.forEach((e) => { var l, f, E, N, C; const s = document.createElement("div"); if (s.className = `popup-item ${e.className || ""}`, e.label) { const o = document.createElement("label"); o.className = "popup-item-label", o.textContent = e.label, s.appendChild(o); } let t; switch (e.type) { case "progress": t = document.createElement("div"), t.className = `popup-progress ${e.className || ""}`; const o = document.createElement("div"); o.className = "popup-progress-bar", o.style.width = `${e.value || 0}%`, t.appendChild(o); break; case "loader": t = document.createElement("div"), t.className = `popup-loader ${e.className || ""}`; const r = document.createElement("div"); r.className = "popup-loader-spinner", t.appendChild(r); break; case "input": t = document.createElement("input"), t.setAttribute("type", "text"), t.className = `popup-item-input ${e.className || ""}`, t.setAttribute("placeholder", e.placeholder || ""), t.setAttribute("value", e.value || ""); break; case "text": t = document.createElement("div"), t.className = `popup-item-input ${e.className || ""}`, t.innerText = e.value || "", t.setAttribute("placeholder", e.placeholder || ""); break; case "checkbox": t = document.createElement("input"), t.setAttribute("type", "checkbox"), t.className = `popup-item-input ${e.className || ""}`, e.value && (t.checked = !0); break; case "list": t = document.createElement("select"), t.className = `popup-item-input ${e.className || ""}`, (l = e.options) == null || l.forEach((c) => { const p = document.createElement("option"); p.value = c, p.textContent = c, e.value && c === e.value && (p.selected = !0), t.appendChild(p); }); break; case "textarea": t = document.createElement("textarea"), t.className = `popup-item-input ${e.className || ""}`, t.setAttribute("placeholder", e.placeholder || ""), t.textContent = e.value || ""; break; case "radio": t = document.createElement("div"), t.className = `popup-item-input ${e.className || ""}`, (f = e.options) == null || f.forEach((c) => { const p = document.createElement("div"); p.className = "popup-radio-container"; const m = document.createElement("input"); m.setAttribute("type", "radio"), m.setAttribute("name", e.id), m.setAttribute("value", c), m.className = "popup-radio-input", e.value === c && (m.checked = !0); const v = document.createElement("label"); v.className = "popup-radio-label", v.textContent = c, p.appendChild(m), p.appendChild(v), t.appendChild(p); }); break; case "date": t = document.createElement("input"), t.setAttribute("type", "date"), t.className = `popup-item-input ${e.className || ""}`, t.setAttribute("value", e.value || ""); break; case "range": t = document.createElement("input"), t.setAttribute("type", "range"), t.className = `popup-item-input ${e.className || ""}`, t.setAttribute("min", ((E = e.min) == null ? void 0 : E.toString()) || "0"), t.setAttribute("max", ((N = e.max) == null ? void 0 : N.toString()) || "100"), t.setAttribute("step", ((C = e.step) == null ? void 0 : C.toString()) || "1"), t.setAttribute("value", e.value || "50"); break; case "file": t = document.createElement("input"), t.setAttribute("type", "file"), t.className = `popup-item-input ${e.className || ""}`, e.accept && t.setAttribute("accept", e.accept); break; case "number": t = document.createElement("input"), t.setAttribute("type", "number"), t.className = `popup-item-input ${e.className || ""}`, t.setAttribute("value", e.value || ""), e.min !== void 0 && t.setAttribute("min", e.min.toString()), e.max !== void 0 && t.setAttribute("max", e.max.toString()), e.step !== void 0 && t.setAttribute("step", e.step.toString()); break; case "password": t = document.createElement("input"), t.setAttribute("type", "password"), t.className = `popup-item-input ${e.className || ""}`, t.setAttribute("placeholder", e.placeholder || ""); break; case "email": t = document.createElement("input"), t.setAttribute("type", "email"), t.className = `popup-item-input ${e.className || ""}`, t.setAttribute("placeholder", e.placeholder || ""); break; case "url": t = document.createElement("input"), t.setAttribute("type", "url"), t.className = `popup-item-input ${e.className || ""}`, t.setAttribute("placeholder", e.placeholder || ""); break; case "color": t = document.createElement("div"), t.className = `color-picker-container ${e.className || ""}`; const y = document.createElement("h3"); y.className = "text-sm font-medium text-gray-700 mb-2", y.textContent = "Custom Color", t.appendChild(y); const b = document.createElement("div"); b.className = "flex items-center gap-2", t.appendChild(b); const h = document.createElement("input"); h.type = "color", h.className = "custom-color", h.value = (e.value || "#000000").toString(), b.appendChild(h); const u = document.createElement("input"); u.type = "text", u.className = "color-input", u.placeholder = "#000000", u.pattern = "^#[0-9A-Fa-f]{6}$", u.value = (e.value || "#000000").toString(), b.appendChild(u), h.addEventListener("input", (c) => { u.value = c.target.value.toUpperCase(); }), u.addEventListener("input", (c) => { const p = c.target.value.toUpperCase(); /^#[0-9A-Fa-f]{6}$/.test(p) && (h.value = p); }); break; case "time": t = document.createElement("input"), t.setAttribute("type", "time"), t.className = `popup-item-input ${e.className || ""}`, t.setAttribute("value", e.value || ""); break; case "datetime-local": t = document.createElement("input"), t.setAttribute("type", "datetime-local"), t.className = `popup-item-input ${e.className || ""}`, t.setAttribute("value", e.value || ""); break; case "button": t = document.createElement("button"), t.className = `popup-button ${e.buttonVariant || "primary"} ${e.className || ""}`, e.icon && (t.innerHTML = e.icon), t.insertAdjacentHTML("beforeend", e.text || "Button"), e.onChange && t.addEventListener("click", () => e.onChange(e.value)); break; case "custom": if (e.content) t = typeof e.content == "function" ? e.content() : e.content; else throw new Error("Custom type requires a content property."); break; case "divider": t = document.createElement("div"), t.className = `popup-divider ${e.className || ""}`, t.style.borderTop = "1px solid #e5e7eb", t.style.margin = "8px 0"; break; default: throw new Error(`Unsupported item type: ${e.type}`); } e.onChange && e.type !== "button" && e.type !== "progress" && e.type !== "loader" && (t.addEventListener("change", (o) => { let r; e.type === "checkbox" ? r = o.target.checked : e.type === "range" || e.type === "number" ? r = parseFloat(o.target.value) : r = o.target.value, e.onChange(r, o); }), t.addEventListener("input", (o) => { let r; e.type === "checkbox" ? r = o.target.checked : e.type === "range" || e.type === "number" ? r = parseFloat(o.target.value) : r = o.target.value, e.onChange(r, o); })), t.id = e.id, s.appendChild(t), a.appendChild(s); }), a; } visibleShow(n) { const a = this.popup.querySelector(`#${n}`); a && (a.style.display = "block"); } visibleHide(n) { const a = this.popup.querySelector(`#${n}`); a && (a.style.display = "none"); } /** * Получает значение элемента попапа по его ID. * @param id - Уникальный идентификатор элемента. * @returns Значение элемента или null, если элемент не найден. */ getValue(n) { const a = this.popup.querySelector(`#${n}`); if (!a) return console.warn(`Element with id "${n}" not found.`), null; if (a.classList.contains("color-picker-container")) { const e = a.querySelector(".custom-color"); return e ? e.value : null; } switch (a.tagName.toLowerCase()) { case "input": const e = a; return e.type === "checkbox" ? e.checked : e.type === "range" || e.type === "number" ? parseFloat(e.value) : e.value; case "textarea": return a.value; case "select": return a.value; default: return console.warn(`Unsupported element type for id "${n}".`), null; } } setValue(n, a) { const e = this.popup.querySelector(`#${n}`); if (!e) { console.warn(`Element with id "${n}" not found.`); return; } if (e.classList.contains("color-picker-container")) { const s = e.querySelector(".custom-color"), t = e.querySelector(".color-input"); if (s && t) { const l = a.toString().toUpperCase(); /^#[0-9A-Fa-f]{6}$/.test(l) ? (s.value = l, t.value = l) : console.warn( `Invalid color value for id "${n}". Expected a hex color (e.g., "#FF0000").` ); } return; } switch (e.tagName.toLowerCase()) { case "input": const s = e; s.type === "checkbox" ? s.checked = !!a : s.type === "range" || s.type === "number" ? s.value = String(a) : s.value = a; break; case "textarea": e.value = a; break; case "select": e.value = a; break; default: console.warn(`Unsupported element type for id "${n}".`); } } setFocus(n) { const a = this.popup.querySelector(`#${n}`); if (!a) { console.warn(`Element with id "${n}" not found.`); return; } a.focus ? a.focus() : console.warn(`Element with id "${n}" does not support focus.`); } createOverlay() { const n = document.createElement("div"); return n.className = "popup-overlay", n; } rerender(n) { this.content.innerHTML = ""; const a = this.createContentFromItems(n); this.content.appendChild(a); } updateFooterButtonCallback(n, a) { this.footer ? this.footer.updateButtonCallback(n, a) : console.warn("Footer is not initialized. Cannot update button callback."); } setItems(n) { this.setContent(this.createContentFromItems(n)); } setContent(n) { typeof n == "string" ? this.content.innerHTML = n : (this.content.innerHTML = "", this.content.appendChild(n)); } show(n, a) { if (this.popup.style.display = "flex", this.overlay.style.display = "block", this.isVisible = !0, document.body.classList.add("popup-open"), typeof n == "number" && typeof a == "number") { this.popup.style.position = "fixed", this.popup.style.left = `${n}px`, this.popup.style.top = `${a}px`; const e = this.popup.getBoundingClientRect(); e.right > window.innerWidth && (this.popup.style.left = `${n - e.width}px`), e.bottom > window.innerHeight && (this.popup.style.top = `${a - e.height}px`); } else this.popup.style.position = "fixed", this.popup.style.left = "50%", this.popup.style.top = "50%", this.popup.style.transform = "translate(-50%, -50%)"; } hide() { this.popup.style.display = "none", this.overlay.style.display = "none", this.isVisible = !1, document.body.classList.remove("popup-open"); } isOpen() { return this.isVisible; } getElement() { return this.popup; } destroy() { this.boundDocumentKeydown && (this.editor.getDOMContext().removeEventListener("keydown", this.boundDocumentKeydown), this.boundDocumentKeydown = void 0), this.popup && this.popup.parentNode && this.popup.remove(), this.overlay && this.overlay.parentNode && this.overlay.remove(); } } export { S as PopupManager };