2026-06-12 11:25:57 +02:00
|
|
|
export const RsvInlineFormBuilder = {
|
2026-06-11 19:03:29 +02:00
|
|
|
match_p(name, value) {
|
|
|
|
|
return (form) => String(form[name]) === String(value);
|
|
|
|
|
},
|
|
|
|
|
create(datasource) {
|
|
|
|
|
const fields = [];
|
|
|
|
|
|
|
|
|
|
const builder = {
|
|
|
|
|
datasource: datasource,
|
|
|
|
|
fieldset(legend, width = null) {
|
|
|
|
|
fields.push({ type: 'fieldset', legend, width });
|
|
|
|
|
return this;
|
|
|
|
|
},
|
|
|
|
|
input_text(name, label, value = '') {
|
|
|
|
|
fields.push({ type: 'text', name, label, value });
|
|
|
|
|
return this;
|
|
|
|
|
},
|
|
|
|
|
input_number(name, label, value = '') {
|
|
|
|
|
fields.push({ type: 'number', name, label, value });
|
|
|
|
|
return this;
|
|
|
|
|
},
|
|
|
|
|
input_date(name, label, value = '') {
|
|
|
|
|
fields.push({ type: 'date', name, label, value });
|
|
|
|
|
return this;
|
|
|
|
|
},
|
|
|
|
|
input_time(name, label, value = '') {
|
|
|
|
|
fields.push({ type: 'time', name, label, value });
|
|
|
|
|
return this;
|
|
|
|
|
},
|
|
|
|
|
input_textarea(name, label, value = '') {
|
|
|
|
|
fields.push({ type: 'textarea', name, label, value });
|
|
|
|
|
return this;
|
|
|
|
|
},
|
|
|
|
|
input_checkbox(name, label, checked = false) {
|
|
|
|
|
fields.push({ type: 'checkbox', name, label, checked });
|
|
|
|
|
return this;
|
|
|
|
|
},
|
|
|
|
|
input_hidden(name, value) {
|
|
|
|
|
fields.push({ type: 'hidden', name, value });
|
|
|
|
|
return this;
|
|
|
|
|
},
|
|
|
|
|
input_select(name, label, options, value = '') {
|
|
|
|
|
fields.push({ type: 'select', name, label, options, value });
|
|
|
|
|
return this;
|
|
|
|
|
},
|
|
|
|
|
show_if(predicate) {
|
|
|
|
|
const last = fields[fields.length - 1];
|
|
|
|
|
if (last) last.show_if = predicate;
|
|
|
|
|
return this;
|
|
|
|
|
},
|
|
|
|
|
build({ id, colspan = 1, save_label = 'Update', on_success, on_cancel } = {}) {
|
|
|
|
|
const td = document.createElement('td');
|
|
|
|
|
td.setAttribute('colspan', colspan);
|
|
|
|
|
|
|
|
|
|
const form = document.createElement('form');
|
|
|
|
|
const wrapper = document.createElement('div');
|
|
|
|
|
wrapper.classList.add('inline-edit-wrapper');
|
|
|
|
|
|
|
|
|
|
const hidden_inputs = [];
|
|
|
|
|
let current_fieldset = null;
|
|
|
|
|
let current_col = null;
|
|
|
|
|
const fieldsets = [];
|
|
|
|
|
const conditionals = [];
|
|
|
|
|
|
|
|
|
|
function ensure_fieldset() {
|
|
|
|
|
if (current_fieldset === null) {
|
|
|
|
|
current_fieldset = document.createElement('fieldset');
|
|
|
|
|
current_col = document.createElement('div');
|
|
|
|
|
current_col.classList.add('inline-edit-col');
|
|
|
|
|
fieldsets.push(current_fieldset);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const field of fields) {
|
|
|
|
|
if (field.type === 'hidden') {
|
|
|
|
|
hidden_inputs.push(field);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (field.type === 'fieldset') {
|
|
|
|
|
if (current_fieldset !== null) {
|
|
|
|
|
current_fieldset.appendChild(current_col);
|
|
|
|
|
}
|
|
|
|
|
current_fieldset = document.createElement('fieldset');
|
|
|
|
|
if (field.width) current_fieldset.style.width = field.width;
|
|
|
|
|
|
|
|
|
|
const legend_el = document.createElement('legend');
|
|
|
|
|
legend_el.classList.add('inline-edit-legend');
|
|
|
|
|
legend_el.innerText = field.legend;
|
|
|
|
|
current_fieldset.appendChild(legend_el);
|
|
|
|
|
|
|
|
|
|
current_col = document.createElement('div');
|
|
|
|
|
current_col.classList.add('inline-edit-col');
|
|
|
|
|
fieldsets.push(current_fieldset);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ensure_fieldset();
|
|
|
|
|
|
|
|
|
|
const label_el = document.createElement('label');
|
|
|
|
|
|
|
|
|
|
const title = document.createElement('span');
|
|
|
|
|
title.classList.add('title');
|
|
|
|
|
title.innerText = field.label;
|
|
|
|
|
|
|
|
|
|
const wrap = document.createElement('span');
|
|
|
|
|
wrap.classList.add('input-text-wrap');
|
|
|
|
|
|
|
|
|
|
let input;
|
|
|
|
|
if (field.type === 'select') {
|
|
|
|
|
input = document.createElement('select');
|
|
|
|
|
input.name = field.name;
|
|
|
|
|
for (const opt of field.options) {
|
|
|
|
|
const option = document.createElement('option');
|
|
|
|
|
if (typeof opt === 'object' && opt !== null) {
|
|
|
|
|
option.value = opt.value;
|
|
|
|
|
option.textContent = opt.label;
|
|
|
|
|
option.selected = String(opt.value) === String(field.value);
|
|
|
|
|
} else {
|
|
|
|
|
option.value = opt;
|
|
|
|
|
option.textContent = opt;
|
|
|
|
|
option.selected = opt === field.value;
|
|
|
|
|
}
|
|
|
|
|
input.appendChild(option);
|
|
|
|
|
}
|
|
|
|
|
} else if (field.type === 'textarea') {
|
|
|
|
|
input = document.createElement('textarea');
|
|
|
|
|
input.name = field.name;
|
|
|
|
|
input.rows = 5;
|
|
|
|
|
input.style.width = '100%';
|
|
|
|
|
input.value = field.value ?? '';
|
|
|
|
|
} else {
|
|
|
|
|
input = document.createElement('input');
|
|
|
|
|
input.type = field.type;
|
|
|
|
|
input.name = field.name;
|
|
|
|
|
if (field.type === 'checkbox') {
|
|
|
|
|
input.checked = field.checked;
|
|
|
|
|
} else {
|
|
|
|
|
input.value = field.value ?? '';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
wrap.appendChild(input);
|
|
|
|
|
label_el.replaceChildren(title, wrap);
|
|
|
|
|
current_col.appendChild(label_el);
|
|
|
|
|
|
|
|
|
|
if (field.show_if) conditionals.push({ label_el, predicate: field.show_if });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (current_fieldset !== null) {
|
|
|
|
|
current_fieldset.appendChild(current_col);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const save_row = document.createElement('div');
|
|
|
|
|
save_row.classList.add('inline-edit-save', 'submit');
|
|
|
|
|
|
|
|
|
|
const error = document.createElement('div');
|
|
|
|
|
error.classList.add('notice', 'notice-error', 'notice-alt', 'inline', 'hidden');
|
|
|
|
|
const error_p = document.createElement('p');
|
|
|
|
|
error_p.classList.add('error');
|
|
|
|
|
error.appendChild(error_p);
|
|
|
|
|
|
|
|
|
|
const spinner = document.createElement('span');
|
|
|
|
|
spinner.classList.add('spinner');
|
|
|
|
|
|
|
|
|
|
const save_btn = document.createElement('button');
|
|
|
|
|
save_btn.type = 'button';
|
|
|
|
|
save_btn.classList.add('save', 'button', 'button-primary');
|
|
|
|
|
save_btn.innerText = save_label;
|
|
|
|
|
save_btn.onclick = () => {
|
|
|
|
|
const form_data = Object.fromEntries(new FormData(form));
|
|
|
|
|
for (const field of fields) {
|
|
|
|
|
if (field.type === 'checkbox') {
|
|
|
|
|
form_data[field.name] = field.name in form_data;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
spinner.classList.add('is-active');
|
|
|
|
|
error.classList.add('hidden');
|
|
|
|
|
builder.datasource.put(id, form_data)
|
|
|
|
|
.then(() => { if (on_success) on_success(); })
|
|
|
|
|
.catch(err => {
|
|
|
|
|
error_p.innerText = err.message;
|
|
|
|
|
error.classList.remove('hidden');
|
|
|
|
|
})
|
|
|
|
|
.finally(() => spinner.classList.remove('is-active'));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const cancel_btn = document.createElement('button');
|
|
|
|
|
cancel_btn.type = 'button';
|
|
|
|
|
cancel_btn.classList.add('cancel', 'button');
|
|
|
|
|
cancel_btn.innerText = 'Cancel';
|
|
|
|
|
if (on_cancel) cancel_btn.onclick = on_cancel;
|
|
|
|
|
|
|
|
|
|
save_row.replaceChildren(save_btn, cancel_btn, spinner, error);
|
|
|
|
|
|
|
|
|
|
for (const h of hidden_inputs) {
|
|
|
|
|
const input = document.createElement('input');
|
|
|
|
|
input.type = 'hidden';
|
|
|
|
|
input.name = h.name;
|
|
|
|
|
input.value = h.value ?? '';
|
|
|
|
|
form.appendChild(input);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
wrapper.replaceChildren(...fieldsets, save_row);
|
|
|
|
|
form.appendChild(wrapper);
|
|
|
|
|
td.appendChild(form);
|
|
|
|
|
|
|
|
|
|
if (conditionals.length) {
|
|
|
|
|
const snapshot = () => {
|
|
|
|
|
const f = Object.fromEntries(new FormData(form));
|
|
|
|
|
for (const field of fields) if (field.type === 'checkbox') f[field.name] = field.name in f;
|
|
|
|
|
return f;
|
|
|
|
|
};
|
|
|
|
|
const sync_all = () => {
|
|
|
|
|
const f = snapshot();
|
|
|
|
|
for (const c of conditionals) c.label_el.classList.toggle('hidden', !c.predicate(f));
|
|
|
|
|
};
|
|
|
|
|
form.addEventListener('change', sync_all);
|
|
|
|
|
form.addEventListener('input', sync_all);
|
|
|
|
|
sync_all();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return td;
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return builder;
|
|
|
|
|
},
|
|
|
|
|
};
|