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
JavaScript
/*! 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
};