394 lines
11 KiB
JavaScript
394 lines
11 KiB
JavaScript
const dragcontainer = document.getElementById("dragcontainer");
|
|
const actions = document.getElementById("actions");
|
|
const addBtn = document.getElementById("add-btn");
|
|
const edit = document.getElementById("edit");
|
|
const fieldTypes = document.querySelector(".field-types");
|
|
|
|
|
|
dragcontainer.addEventListener('dragstart', dragStart);
|
|
dragcontainer.addEventListener('dragend', dragEnd);
|
|
dragcontainer.addEventListener('dragover', dragOver);
|
|
dragcontainer.addEventListener('dragenter', dragEnter);
|
|
dragcontainer.addEventListener('dragleave', dragLeave);
|
|
dragcontainer.addEventListener('drop', drop);
|
|
dragcontainer.addEventListener('mouseover', over);
|
|
//dragcontainer.addEventListener('mouseout', out);
|
|
actions.addEventListener('click', actionsClick);
|
|
edit.addEventListener('click', editClick);
|
|
edit.addEventListener('change', editChange);
|
|
addBtn.addEventListener('click', addRow);
|
|
fieldTypes.addEventListener('click', addRow);
|
|
|
|
let dragged = null;
|
|
let highlighted = null;
|
|
let editElement = null;
|
|
|
|
function addRow(event) {
|
|
let type = event.target.closest("a")?.dataset.type ?? "text";
|
|
let template = document.getElementById("template").firstElementChild.cloneNode(true);
|
|
console.log(type);
|
|
template =template.outerHTML;
|
|
let newId = Math.floor(Math.random() * 10000);
|
|
template = template.replaceAll(name + '[0]', name + '[' + newId + ']').
|
|
replaceAll(name + '[#]', name + '[' + newId + ']').
|
|
replaceAll(name + '#', name + newId ).
|
|
replaceAll("[" + name +"][#]", "[" + name +"][" + newId + "]").
|
|
replace('d-none', '');
|
|
|
|
template = generateElements(template)[0];
|
|
|
|
dragcontainer.querySelector(".dragzone").append(template);
|
|
|
|
if (type != "text") {
|
|
editElement = template.querySelector(".col");
|
|
highlighted = editElement;
|
|
highlighted.querySelector("[data-v-field-type]").value = type;
|
|
renderField(template);
|
|
}
|
|
}
|
|
|
|
function addCol(event) {
|
|
const template = document.getElementById("template").firstElementChild.cloneNode(true);
|
|
dragcontainer.querySelector(".dragzone").append(template);
|
|
}
|
|
|
|
|
|
function buildParams( prefix, obj, add ) {
|
|
let rbracket = /\[\]$/;
|
|
|
|
if ( Array.isArray( obj ) ) {
|
|
|
|
// Serialize array item.
|
|
for(const key in obj) {
|
|
let v = obj[key];
|
|
if ( rbracket.test( prefix ) ) {
|
|
|
|
// Treat each array item as a scalar.
|
|
add( prefix, v );
|
|
|
|
} else {
|
|
|
|
// Item is non-scalar (array or object), encode its numeric index.
|
|
buildParams(
|
|
prefix + "[" + ( typeof v === "object" && v != null ? key : "" ) + "]",
|
|
v,
|
|
add
|
|
);
|
|
}
|
|
}
|
|
|
|
} else if ( typeof obj === "object" ) {
|
|
|
|
// Serialize object item.
|
|
for (const name in obj ) {
|
|
buildParams( prefix + "[" + name + "]", obj[ name ], add );
|
|
}
|
|
|
|
} else {
|
|
|
|
// Serialize scalar item.
|
|
add( prefix, obj );
|
|
}
|
|
}
|
|
|
|
// Serialize an array of form elements or a set of
|
|
// key/values into a query string
|
|
function nestedFormData( a ) {
|
|
let prefix,
|
|
s = [],
|
|
add = function( key, valueOrFunction ) {
|
|
|
|
// If value is a function, invoke it and use its return value
|
|
let value = typeof valueOrFunction === "function" ?
|
|
valueOrFunction() :
|
|
valueOrFunction;
|
|
|
|
s[ s.length ] = encodeURIComponent( key ) + "=" +
|
|
encodeURIComponent( value == null ? "" : value );
|
|
};
|
|
|
|
if ( a == null ) {
|
|
return "";
|
|
}
|
|
|
|
// If an array was passed in, assume that it is an array of form elements.
|
|
if ( Array.isArray( a ) || ( Object.is( a ) ) ) {
|
|
|
|
// Serialize the form elements
|
|
for(const key in object) {
|
|
let v = object[key];
|
|
//jQuery.each( a, function() {
|
|
add( key, v );
|
|
};
|
|
|
|
} else {
|
|
|
|
// If traditional, encode the "old" way (the way 1.3.2 or older
|
|
// did it), otherwise encode params recursively.
|
|
for (const prefix in a ) {
|
|
buildParams( prefix, a[ prefix ], add );
|
|
}
|
|
}
|
|
|
|
// Return the resulting serialization
|
|
return s.join( "&" );
|
|
};
|
|
|
|
|
|
function serialize(parentElement, prefix) {
|
|
let data = {}
|
|
parentElement.querySelectorAll("input[type=text],input[type=number],input[type=hidden],input[type=checkbox]:checked,input[type=radio]:checked,input[name=image],select:not(:disabled), textarea").forEach( (el, i) => {
|
|
//if (el.offsetParent)
|
|
let name = el.name;//.replace('field[', '[field][');
|
|
prefix = "";
|
|
data[prefix + name] = el.value;
|
|
});
|
|
|
|
return data;
|
|
}
|
|
|
|
function editChange(event) {
|
|
let element = event.target;
|
|
let name = element.name;
|
|
let value = element.value;
|
|
let fieldName = name.replace(/.+\[|\]/g,'');
|
|
|
|
if (fieldName == "cols") {
|
|
highlighted.setAttribute("class", "col " + value);
|
|
}
|
|
|
|
renderField(edit);
|
|
}
|
|
|
|
function renderField(editContainer) {
|
|
let data = {};
|
|
|
|
editContainer.querySelectorAll('.tab-pane').forEach((e) => {
|
|
let name = e.getAttribute('aria-labelledby');
|
|
if (name == 'conditions') return;
|
|
data = {...data, ...serialize(e, name)};
|
|
});
|
|
|
|
data["field_id"] = highlighted.dataset.id;//edit.querySelector("[data-v-field-id]")?.value;
|
|
data["type"] = editContainer.querySelector('[data-v-field-type]')?.value;//edit.querySelector("[data-v-field-id]")?.value;
|
|
data["csrf"] = document.querySelector('[data-v-csrf]')?.value;//edit.querySelector("[data-v-field-id]")?.value;
|
|
|
|
fetch(window.location.pathname + "?module=field/field-group&action=field", {
|
|
method: "POST",
|
|
headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'},
|
|
//body: new URLSearchParams(data)
|
|
body: nestedFormData(data)
|
|
})
|
|
.then((response) => {
|
|
if (!response.ok) { throw new Error(response) }
|
|
return response.json()
|
|
})
|
|
.then((data) => {
|
|
let focusName = document.activeElement?.name ?? "";
|
|
editElement.querySelector(".input").innerHTML = data['field'];
|
|
|
|
editContainer.querySelectorAll(".tab-pane")?.forEach(e => {
|
|
let name = e.getAttribute('aria-labelledby');
|
|
if (name && data[name]) {
|
|
e.innerHTML = data[name];
|
|
}
|
|
});
|
|
|
|
if (focusName) editContainer.querySelector("[name='" + focusName + "']")?.focus();
|
|
|
|
})
|
|
.catch(error => {
|
|
console.log(error);
|
|
displayToast("danger", "Error", "Error loading translations!");
|
|
});
|
|
|
|
/*
|
|
if (element.name == "type") {
|
|
template = document.getElementById(value + "-field").cloneNode(true);
|
|
highlighted.querySelector(".input").replaceChildren(...template.children);
|
|
}*/
|
|
|
|
}
|
|
|
|
function editClick(event) {
|
|
let element;
|
|
|
|
if (element = event.target.closest(".btn-edit")) {
|
|
}
|
|
|
|
if (element = event.target.closest(".nav-item > .nav-link")) {
|
|
let id = element.getAttribute("aria-controls");
|
|
let tab = edit.querySelector('[aria-labelledby="' + id + '"]');
|
|
console.log("[aria-labelledby=" + id + "]");
|
|
element.closest(".nav-tabs")?.querySelectorAll(".nav-link")?.forEach(e => e.classList.remove("active"));
|
|
element?.classList.add("active");
|
|
tab?.parentNode.querySelectorAll(".tab-pane")?.forEach(e => e.classList.remove("active"));
|
|
tab?.classList.add("active");
|
|
}
|
|
}
|
|
|
|
function actionsClick(event) {
|
|
let element;
|
|
|
|
if (element = event.target.closest(".btn-edit")) {
|
|
let editContainer = highlighted.querySelector(".edit");
|
|
if (edit.classList.contains("show")) {
|
|
element.querySelector("i").setAttribute("class", "la la-angle-down");
|
|
edit.classList.remove("show");
|
|
/*
|
|
editContainer.replaceWith(edit.cloneNode(true));
|
|
editContainer.removeAttribute("id");
|
|
*/
|
|
//editContainer.querySelectorAll(".tab-pane").forEach(e => editContainer.replaceChildren(...edit.children);
|
|
editContainer.replaceChildren(...edit.children);
|
|
|
|
editElement = null;
|
|
} else {
|
|
element.querySelector("i").setAttribute("class", "la la-angle-up");
|
|
edit.classList.add("show")
|
|
edit.replaceChildren(...editContainer.children);
|
|
/*
|
|
edit.replaceWith(editContainer.cloneNode(true));
|
|
edit.setAttribute("id", "edit");
|
|
*/
|
|
|
|
editElement = highlighted;
|
|
}
|
|
|
|
event.preventDefault();
|
|
}
|
|
|
|
if (element = event.target.closest(".btn-add")) {
|
|
let template = document.getElementById("template").firstElementChild.cloneNode(true);
|
|
if (highlighted.classList.contains("col")) {
|
|
template = template.querySelector(".col");
|
|
console.log("col");
|
|
}
|
|
highlighted.after(template);
|
|
}
|
|
|
|
if (element = event.target.closest(".btn-clone")) {
|
|
if (highlighted.classList.contains("col")) {
|
|
|
|
}
|
|
|
|
let template = highlighted.cloneNode(true);
|
|
highlighted.after(template);
|
|
}
|
|
|
|
if (element = event.target.closest(".btn-remove")) {
|
|
if (confirm("Are you sure?")) {
|
|
|
|
if (highlighted) {
|
|
let form = document.getElementById('field-form');
|
|
console.log(form);
|
|
form.append(generateElements('<input type="hidden" name="delete[field_id][]" value="' + highlighted.dataset.id + '">')[0]);
|
|
}
|
|
|
|
highlighted.remove();
|
|
highlighted = null;
|
|
actions.style.display = "none";
|
|
}
|
|
}
|
|
}
|
|
|
|
function out(event) {
|
|
}
|
|
|
|
function over(event) {
|
|
//if (event.target.parentNode == actions) return;
|
|
let classList = event.target.classList;
|
|
//if (! (classList.contains("row") || classList.contains("col")) ) {
|
|
if (! (classList.contains("col")) ) {
|
|
return;
|
|
}
|
|
|
|
if (edit.classList.contains("show")) {
|
|
return;
|
|
}
|
|
|
|
highlighted = event.target;
|
|
const rect = highlighted.getBoundingClientRect();
|
|
actions.style.opacity = "0";
|
|
actions.style.top = (rect.top + window.scrollY) + "px";
|
|
actions.style.left = (rect.x + rect.width - actions.clientWidth + window.scrollX - 1 ) + "px";
|
|
if (!edit.classList.contains("show")) {
|
|
edit.style.top = (rect.bottom + window.scrollY) + "px";
|
|
}
|
|
|
|
if (rect.top < 80) {
|
|
actions.style.display = "none";
|
|
highlighted = null;
|
|
} else {
|
|
actions.style.display = "block";
|
|
actions.style.opacity = "1";
|
|
}
|
|
}
|
|
|
|
function dragStart(event) {
|
|
event.target.classList.add("dragstart");
|
|
dragged = event.target;
|
|
actions.style.display = "none";
|
|
}
|
|
|
|
function dragEnd(event) {
|
|
event.target.classList.remove("dragstart");
|
|
}
|
|
|
|
function dragEnter(event) {
|
|
event.target.classList.add("drop");
|
|
}
|
|
|
|
function dragLeave(event) {
|
|
event.target.classList.remove("drop");
|
|
}
|
|
|
|
function dragOver(event) {
|
|
event.preventDefault();
|
|
}
|
|
|
|
function drop(event) {
|
|
let container = event.target;
|
|
let element = event.currentTarget;
|
|
event.target.classList.remove("drop");
|
|
//console.log("drop", dragged, container, element);
|
|
|
|
let parentRow = dragged.parentNode;
|
|
|
|
if (dragged.classList.contains("row")) {//row
|
|
if (container.classList.contains("container-fluid")) {//container
|
|
container.appendChild(dragged);
|
|
} else if (container.classList.contains("row")) {//row
|
|
container.after(dragged);
|
|
} else if (container.classList.contains("col")) {//col
|
|
//if row dragged inside a col create a parent col and add after
|
|
let col = generateElements('<div class="col" draggable="true"></div>')[0];
|
|
col.append(dragged);
|
|
container.after(col);
|
|
//container.append(dragged);
|
|
}
|
|
} else
|
|
if (dragged.classList.contains("col")) {//col
|
|
|
|
if (container.classList.contains("row")) {
|
|
container.appendChild(dragged);
|
|
} else if (container.classList.contains("col")) {
|
|
container.after(dragged);
|
|
} else if (container.classList.contains("container-fluid")) {//container
|
|
//if col dragged outiside a row (container) create a parent row
|
|
let row = generateElements('<div class="row" draggable="true"></div>')[0];
|
|
row.append(dragged);
|
|
container.append(row);
|
|
}
|
|
|
|
}
|
|
|
|
//remove empty rows/cols
|
|
if (!parentRow.childElementCount) {
|
|
parentRow.remove();
|
|
}
|
|
|
|
//event.dataTransfer.clearData();
|
|
}
|
|
|