132 lines
5.2 KiB
JavaScript
132 lines
5.2 KiB
JavaScript
(function (app) {
|
|
'use strict';
|
|
|
|
const u = app.modules.util;
|
|
|
|
function makeObject(schema, ui, path, value, options) {
|
|
const fs = u.h('fieldset', { className: 'form-fieldset' });
|
|
const label = (ui && ui['ui:title']) || schema.title || options.fieldName;
|
|
if (label) {
|
|
fs.appendChild(u.h('legend', { className: 'form-fieldset__legend' }, label));
|
|
}
|
|
if (schema.description) {
|
|
fs.appendChild(u.h('div', { className: 'form-field__description' }, schema.description));
|
|
}
|
|
const errEl = u.h('div', { className: 'form-field__error', hidden: true });
|
|
fs.appendChild(errEl);
|
|
|
|
const props = schema.properties || {};
|
|
const requiredSet = {};
|
|
(schema.required || []).forEach(function (n) { requiredSet[n] = true; });
|
|
|
|
// Resolve render order: ui:order first (with '*' as "everything else"),
|
|
// then fall back to declaration order.
|
|
const declared = Object.keys(props);
|
|
const uiOrder = (ui && ui['ui:order']) || null;
|
|
const ordered = [];
|
|
const seen = {};
|
|
if (uiOrder && Array.isArray(uiOrder)) {
|
|
for (let i = 0; i < uiOrder.length; i++) {
|
|
const name = uiOrder[i];
|
|
if (name === '*') {
|
|
for (let j = 0; j < declared.length; j++) {
|
|
const dn = declared[j];
|
|
if (!seen[dn] && uiOrder.indexOf(dn) < 0) {
|
|
ordered.push(dn);
|
|
seen[dn] = true;
|
|
}
|
|
}
|
|
} else if (props[name] && !seen[name]) {
|
|
ordered.push(name);
|
|
seen[name] = true;
|
|
}
|
|
}
|
|
// Append anything declared but not mentioned in ui:order (and no '*' was used).
|
|
for (let j = 0; j < declared.length; j++) {
|
|
if (!seen[declared[j]]) {
|
|
ordered.push(declared[j]);
|
|
seen[declared[j]] = true;
|
|
}
|
|
}
|
|
} else {
|
|
for (let j = 0; j < declared.length; j++) {
|
|
ordered.push(declared[j]);
|
|
}
|
|
}
|
|
|
|
const children = {};
|
|
const dataObj = (value && typeof value === 'object' && !Array.isArray(value)) ? value : {};
|
|
|
|
for (let i = 0; i < ordered.length; i++) {
|
|
const name = ordered[i];
|
|
const childSchema = props[name];
|
|
const childUi = (ui && ui[name]) || {};
|
|
const childPath = u.ptrPush(path, name);
|
|
const childValue = dataObj[name];
|
|
const childWidget = app.modules.render.create(childSchema, childUi, childPath, childValue, {
|
|
fieldName: u.humanize(name),
|
|
required: !!requiredSet[name]
|
|
});
|
|
children[name] = childWidget;
|
|
fs.appendChild(childWidget.el);
|
|
}
|
|
|
|
// Cross-field mirror: a field with `ui:mirrorFrom: <sibling>`
|
|
// shows the live value of that sibling. Used by the project-
|
|
// rollup forms so the read-only `originator` reflects the
|
|
// selected Package (party) — the party folder is the
|
|
// originator's source of truth. Display-only: the server is
|
|
// still authoritative via the cascade's folder_fields.
|
|
for (let i = 0; i < ordered.length; i++) {
|
|
const name = ordered[i];
|
|
const mirrorFrom = ui && ui[name] && ui[name]['ui:mirrorFrom'];
|
|
if (!mirrorFrom || !children[name] || !children[mirrorFrom]) {
|
|
continue;
|
|
}
|
|
const targetInput = children[name].el.querySelector('input, select, textarea');
|
|
const sourceInput = children[mirrorFrom].el.querySelector('input, select, textarea');
|
|
if (!targetInput || !sourceInput) {
|
|
continue;
|
|
}
|
|
const sync = function () { targetInput.value = sourceInput.value; };
|
|
sourceInput.addEventListener('input', sync);
|
|
sourceInput.addEventListener('change', sync);
|
|
sync(); // initialize from any pre-filled party value
|
|
}
|
|
|
|
return {
|
|
el: fs,
|
|
path: path,
|
|
type: 'object',
|
|
read: function () {
|
|
const out = {};
|
|
const keys = Object.keys(children);
|
|
for (let i = 0; i < keys.length; i++) {
|
|
const k = keys[i];
|
|
const v = children[k].read();
|
|
if (v !== undefined) {
|
|
out[k] = v;
|
|
}
|
|
}
|
|
return out;
|
|
},
|
|
setError: function (msg) {
|
|
errEl.textContent = msg;
|
|
errEl.hidden = false;
|
|
},
|
|
clearErrors: function () {
|
|
errEl.textContent = '';
|
|
errEl.hidden = true;
|
|
const keys = Object.keys(children);
|
|
for (let i = 0; i < keys.length; i++) {
|
|
children[keys[i]].clearErrors();
|
|
}
|
|
},
|
|
child: function (name) {
|
|
return children[name] || null;
|
|
}
|
|
};
|
|
}
|
|
|
|
app.modules.object = { makeObject: makeObject };
|
|
})(window.formApp);
|