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

385 lines (384 loc) 18 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 O = Object.defineProperty; var A = (u, e, t) => e in u ? O(u, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : u[e] = t; var a = (u, e, t) => A(u, typeof e != "symbol" ? e + "" : e, t); import { PopupManager as H } from "../../../core/ui/PopupManager.mjs"; import { FormManager as U } from "../services/FormManager.mjs"; import { FieldEditor as W } from "./FieldEditor.mjs"; import { FormPreview as q } from "./FormPreview.mjs"; import { createContainer as s, createButton as f, createLabel as D, createSelectField as K, createInputField as R, createLineBreak as _ } from "../../../utils/helpers.mjs"; import { TemplatesModal as $ } from "./TemplatesModal.mjs"; class N { constructor(e) { a(this, "editor"); a(this, "popup", null); a(this, "formManager"); a(this, "fieldEditor", null); a(this, "formPreview", null); a(this, "callback", null); a(this, "selectedFieldId", null); a(this, "previewContainer", null); a(this, "fieldsListContainer", null); a(this, "fieldEditorContainer", null); a(this, "isEditMode", !1); a(this, "existingFormElement", null); a(this, "templatesModal", null); a(this, "handleEscapeKey", (e) => { var t, i; e.key === "Escape" && ((t = this.popup) != null && t.isOpen()) && ((i = this.popup) == null || i.hide(), this.destroy()); }); // Добавляю методы drag and drop a(this, "dragStartIndex", null); this.editor = e, this.formManager = new U(e), this.templatesModal = new $(e), this.popup = new H(e, { title: e.t("Form Builder"), className: "form-builder-modal", closeOnClickOutside: !0, buttons: [ { label: e.t("Cancel"), variant: "secondary", onClick: () => { var t; (t = this.popup) == null || t.hide(), this.destroy(); } }, { label: e.t("Save Form"), variant: "primary", onClick: () => this.saveForm() } ], items: [ { type: "custom", id: "form-builder-content", content: () => this.createContent() } ] }), this.setupCloseHandlers(); } setupCloseHandlers() { this.handleEscapeKey = (e) => { var t; e.key === "Escape" && ((t = this.popup) != null && t.isOpen()) && (this.popup.hide(), this.destroy()); }, this.editor.getDOMContext().addEventListener("keydown", this.handleEscapeKey); } createContent() { const e = s("form-builder-modal-content"), t = s( "form-builder-header-panel flex justify-between items-center mb-2" ), i = s("form-builder-title text-xl font-bold"); i.textContent = this.isEditMode ? this.editor.t("Edit Form") : this.editor.t("Form Builder"); const o = f( this.editor.t("Templates"), () => this.openTemplatesModal(), "secondary" ); t.appendChild(i), t.appendChild(o), e.appendChild(t); const n = this.createFormSettings(); e.appendChild(n); const d = s("form-builder-main"), r = this.createFieldsPanel(); d.appendChild(r); const h = this.createFieldEditorPanel(); d.appendChild(h), e.appendChild(d); const l = this.createPreviewPanel(); return e.appendChild(l), e; } createFormSettings() { const e = s("form-settings-panel"), t = s("settings-title"); t.textContent = this.editor.t("Form Settings"), e.appendChild(t); const i = s("setting-group"), o = D(this.editor.t("Method:")), n = K( [ { value: "GET", label: "GET" }, { value: "POST", label: "POST" }, { value: "PUT", label: "PUT" }, { value: "DELETE", label: "DELETE" }, { value: "PATCH", label: "PATCH" } ], this.formManager.getFormMethod(), (l) => { this.formManager.updateFormMethod(l); } ); i.appendChild(o), i.appendChild(n), e.appendChild(i); const d = s("setting-group"), r = D(this.editor.t("Action URL:")), h = R( "text", this.editor.t("Enter form action URL"), this.formManager.getFormAction(), (l) => { this.formManager.updateFormAction(l); } ); return h.id = "form-action-url", d.appendChild(r), d.appendChild(h), e.appendChild(d), e; } createFieldsPanel() { const e = s("fields-panel"), t = s("fields-header"), i = s("fields-title"); i.textContent = this.editor.t("Form Fields"); const o = f("+", () => this.addField(), "primary"); return o.className = "add-field-button", t.appendChild(i), t.appendChild(o), e.appendChild(t), this.fieldsListContainer = s("fields-list"), e.appendChild(this.fieldsListContainer), this.renderFieldsList(), e; } createFieldEditorPanel() { const e = s("field-editor-panel"), t = s("editor-title"); return t.textContent = this.editor.t("Field Editor"), e.appendChild(t), this.fieldEditorContainer = s("field-editor-content"), e.appendChild(this.fieldEditorContainer), this.fieldEditor || (this.fieldEditor = new W( (i, o) => this.updateField(i, o), (i) => this.removeField(i), (i) => this.cloneField(i), this.editor, this.formManager, (i, o) => this.onFieldTypeChange(i, o), (i) => this.onOptionsChange(i) )), e; } createPreviewPanel() { const e = s("preview-panel"), t = s("preview-title"); t.textContent = this.editor.t("Form Preview"), e.appendChild(t); const i = s("preview-content"); return e.appendChild(i), this.formPreview || (this.formPreview = new q(i, this.editor)), this.previewContainer = i, e; } renderFieldsList() { if (!this.fieldsListContainer) return; this.fieldsListContainer.innerHTML = ""; const e = this.formManager.getFields(); if (e.length === 0) { const t = s("no-fields"); t.textContent = this.editor.t("No fields added yet"), this.fieldsListContainer.appendChild(t); return; } e.forEach((t, i) => { const o = s("field-item"); o.setAttribute("draggable", "true"), o.addEventListener("dragstart", (c) => this.handleDragStart(c, i)), o.addEventListener("dragover", (c) => this.handleDragOver(c, i)), o.addEventListener("drop", (c) => this.handleDrop(c, i)), this.selectedFieldId === t.id && o.classList.add("selected"); const n = s("field-title"); n.textContent = t.label || this.editor.t("Untitled Field"); const d = s("field-type"); d.textContent = t.type; const r = s("field-actions"), h = f("↑", () => this.moveField(t.id, i - 1), "secondary"); h.className = "field-action-button", h.disabled = i === 0; const l = f("↓", () => this.moveField(t.id, i + 1), "secondary"); l.className = "field-action-button", l.disabled = i === e.length - 1; const p = f("×", () => this.removeField(t.id), "danger"); p.className = "field-action-button", r.appendChild(h), r.appendChild(l), r.appendChild(p), o.appendChild(n), o.appendChild(d), o.appendChild(r), o.addEventListener("click", () => this.selectField(t.id)), this.fieldsListContainer.appendChild(o); }), this.updatePreview(); } selectField(e) { this.selectedFieldId = e, this.renderFieldsList(), this.renderFieldEditor(); } renderFieldEditor() { if (!(!this.fieldEditorContainer || !this.fieldEditor)) if (this.fieldEditorContainer.innerHTML = "", this.selectedFieldId) { const e = this.formManager.getField(this.selectedFieldId); if (e) { const t = this.fieldEditor.createFieldEditor(e); this.fieldEditorContainer.appendChild(t); } } else { const e = s("no-field-message"); e.textContent = this.editor.t("Select a field to edit"), this.fieldEditorContainer.appendChild(e); } } updateFieldEditorTitle() { if (!this.fieldEditorContainer || !this.selectedFieldId) return; const e = this.formManager.getField(this.selectedFieldId); if (!e) return; const t = this.fieldEditorContainer.querySelector(".field-title"); t && (t.textContent = e.label || this.editor.t("Untitled Field")); } addField(e = "text") { this.formManager.addField(e); const t = this.formManager.getFields(); t.length > 0 && (this.selectedFieldId = t[t.length - 1].id), this.renderFieldsList(), this.renderFieldEditor(); } renderFieldsListWithoutPreview() { if (!this.fieldsListContainer) return; this.fieldsListContainer.innerHTML = ""; const e = this.formManager.getFields(); if (e.length === 0) { const t = s("no-fields"); t.textContent = this.editor.t("No fields added yet"), this.fieldsListContainer.appendChild(t); return; } e.forEach((t, i) => { const o = s("field-item"); this.selectedFieldId === t.id && o.classList.add("selected"); const n = s("field-title"); n.textContent = t.label || this.editor.t("Untitled Field"); const d = s("field-type"); d.textContent = t.type; const r = s("field-actions"), h = f("↑", () => this.moveField(t.id, i - 1), "secondary"); h.className = "field-action-button", h.disabled = i === 0; const l = f("↓", () => this.moveField(t.id, i + 1), "secondary"); l.className = "field-action-button", l.disabled = i === e.length - 1; const p = f("×", () => this.removeField(t.id), "danger"); p.className = "field-action-button", r.appendChild(h), r.appendChild(l), r.appendChild(p), o.appendChild(n), o.appendChild(d), o.appendChild(r), o.addEventListener("click", () => this.selectField(t.id)), this.fieldsListContainer.appendChild(o); }); } updateField(e, t) { this.formManager.updateField(e, t), this.renderFieldsListWithoutPreview(), this.updateFieldEditorTitle(), this.updatePreview(); } removeField(e) { if (confirm(this.editor.t("Are you sure you want to delete this field?"))) { const i = this.formManager.getFields().findIndex((n) => n.id === e); this.formManager.removeField(e); const o = this.formManager.getFields(); o.length > 0 ? this.selectedFieldId === e && (i < o.length ? this.selectedFieldId = o[i].id : this.selectedFieldId = o[o.length - 1].id) : this.selectedFieldId = null, this.renderFieldsListWithoutPreview(), this.renderFieldEditor(); } } cloneField(e) { const t = this.formManager.getField(e); if (t) { const i = { ...t, id: `field_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, label: `${t.label} (Copy)` }; this.formManager.addField(t.type, i), this.selectedFieldId = i.id, this.renderFieldsListWithoutPreview(), this.renderFieldEditor(); } } moveField(e, t) { this.formManager.moveField(e, t), this.renderFieldsListWithoutPreview(); } updatePreview() { if (!this.previewContainer || !this.formPreview) return; const e = this.formManager.getFormConfig(); e && e.fields.length > 0 ? this.formPreview.createPreview(e) : this.previewContainer.innerHTML = '<p class="no-preview-message">No fields to preview</p>'; } saveForm() { var t, i, o; const e = this.formManager.getFormConfig(); e && (this.isEditMode && this.existingFormElement ? confirm(this.editor.t("Are you sure you want to update this form?")) && (this.existingFormElement.outerHTML = this.formManager.createForm(e), (t = this.popup) == null || t.hide(), this.destroy()) : ((i = this.callback) == null || i.call(this, e), (o = this.popup) == null || o.hide(), this.destroy())); } show(e, t = !1, i = null) { var o; this.callback = e, this.isEditMode = t, this.existingFormElement = i, t && i ? (this.formManager.loadForm(i), this.updatePopupForEditMode(), this.updateFormSettings(), this.renderFieldsListWithoutPreview(), this.renderFieldEditor()) : (this.selectedFieldId = null, this.updatePopupForCreateMode()), (o = this.popup) == null || o.show(), setTimeout(() => { const n = this.editor.getDOMContext().getElementById("form-action-url"); n && n.focus(); }, 0), this.updatePreview(); } updatePopupForEditMode() { var i, o; const e = (i = this.popup) == null ? void 0 : i.getElement().querySelector(".popup-header-title"); e && (e.textContent = this.editor.t("Edit Form")); const t = (o = this.popup) == null ? void 0 : o.getElement().querySelector('.popup-footer button[data-variant="primary"]'); t && (t.textContent = this.editor.t("Update Form")); } updatePopupForCreateMode() { var i, o; const e = (i = this.popup) == null ? void 0 : i.getElement().querySelector(".popup-header-title"); e && (e.textContent = this.editor.t("Form Builder")); const t = (o = this.popup) == null ? void 0 : o.getElement().querySelector('.popup-footer button[data-variant="primary"]'); t && (t.textContent = this.editor.t("Save Form")); } updateFormSettings() { var i, o; const e = (i = this.popup) == null ? void 0 : i.getElement().querySelector(".form-settings-panel select"); e && (e.value = this.formManager.getFormMethod()); const t = (o = this.popup) == null ? void 0 : o.getElement().querySelector('.form-settings-panel input[type="text"]'); t && (t.value = this.formManager.getFormAction() || ""); } destroy() { this.editor.getDOMContext().removeEventListener("keydown", this.handleEscapeKey), this.popup && (this.popup.destroy(), this.popup = null), this.fieldEditor && (this.fieldEditor = null), this.formPreview && (this.formPreview = null); } openTemplatesModal() { var t, i; (t = this.popup) == null || t.hide(); const e = this.editor.saveCursorPosition(); (i = this.templatesModal) == null || i.show((o) => { if (o && o.config) { const n = new N(this.editor); n.show( (d) => { e && this.editor.restoreCursorPosition(e); const r = this.formManager.createForm(d); this.editor.insertContent(r), this.editor.insertContent(_()); }, !1, null ), n.loadFormConfig(o.config); } }); } // Добавляем метод для загрузки конфигурации формы loadFormConfig(e) { this.formManager.loadFormConfig(e), this.selectedFieldId = null, this.updateFormSettings(), this.renderFieldsListWithoutPreview(), this.renderFieldEditor(), this.updatePreview(); } handleDragStart(e, t) { var i; this.dragStartIndex = t, (i = e.dataTransfer) == null || i.setData("text/plain", t.toString()); } handleDragOver(e, t) { e.preventDefault(); } handleDrop(e, t) { if (e.preventDefault(), this.dragStartIndex !== null && this.dragStartIndex !== t) { const o = this.formManager.getFields()[this.dragStartIndex]; this.formManager.moveField(o.id, t), this.renderFieldsListWithoutPreview(), this.dragStartIndex = null; } } onFieldTypeChange(e, t) { var d, r, h, l, p, c, F, g, C, v, E, M, b, w, P, y, L, x, S, I, k, T, B; const i = this.formManager.getField(e); if (!i) return; const o = { name: ((d = i.options) == null ? void 0 : d.name) || e, placeholder: ((r = i.options) == null ? void 0 : r.placeholder) || "", className: ((h = i.options) == null ? void 0 : h.className) || "", readonly: ((l = i.options) == null ? void 0 : l.readonly) || !1, disabled: ((p = i.options) == null ? void 0 : p.disabled) || !1 }; switch (t) { case "select": case "radio": if (o.options = ((c = i.options) == null ? void 0 : c.options) || [], o.multiple = t === "select" && ((F = i.options) == null ? void 0 : F.multiple) || !1, o.options.length === 0) { const m = t === "radio" ? this.editor.t("New Radio") : this.editor.t("Option 1"); o.options = [m]; } break; case "checkbox": o.value = ((g = i.options) == null ? void 0 : g.value) || i.label || this.editor.t("New Checkbox"), o.checked = ((C = i.options) == null ? void 0 : C.checked) || !1; break; case "number": case "range": o.min = ((v = i.options) == null ? void 0 : v.min) || "", o.max = ((E = i.options) == null ? void 0 : E.max) || "", o.step = ((M = i.options) == null ? void 0 : M.step) || ""; break; case "textarea": o.rows = ((b = i.options) == null ? void 0 : b.rows) || 3, o.cols = ((w = i.options) == null ? void 0 : w.cols) || 50; break; case "file": o.accept = ((P = i.options) == null ? void 0 : P.accept) || "", o.multiple = ((y = i.options) == null ? void 0 : y.multiple) || !1; break; case "date": case "time": case "datetime-local": case "month": case "week": o.min = ((L = i.options) == null ? void 0 : L.min) || "", o.max = ((x = i.options) == null ? void 0 : x.max) || ""; break; case "text": case "email": case "password": case "tel": case "url": o.maxlength = ((S = i.options) == null ? void 0 : S.maxlength) || "", o.minlength = ((I = i.options) == null ? void 0 : I.minlength) || ""; break; case "color": o.value = ((k = i.options) == null ? void 0 : k.value) || "#000000"; break; case "image": o.src = ((T = i.options) == null ? void 0 : T.src) || "", o.alt = ((B = i.options) == null ? void 0 : B.alt) || ""; break; } const n = { type: t, options: o }; if (i.validation) { const m = { ...i.validation }; t !== "email" && t !== "url" && (delete m.email, delete m.url), t !== "number" && t !== "range" && delete m.numeric, n.validation = m; } this.formManager.updateField(e, n), this.renderFieldEditor(), this.updatePreview(); } onOptionsChange(e) { this.renderFieldEditor(), this.updatePreview(); } } export { N as FormBuilderModal };