Initial commit
This commit is contained in:
228
jsonform.js
Normal file
228
jsonform.js
Normal file
@@ -0,0 +1,228 @@
|
||||
|
||||
import { dom, el } from './dom.js';
|
||||
import { jsonQuery } from './json.js';
|
||||
|
||||
/**
|
||||
* This form encapsulates a form with a model and a layout.
|
||||
*
|
||||
*/
|
||||
class JsonForm {
|
||||
|
||||
#dom = null;
|
||||
#model = null;
|
||||
#layout = null;
|
||||
#editable = false;
|
||||
|
||||
set model(model) {
|
||||
this.#model = model;
|
||||
this.#layout.modelUpdated(model);
|
||||
}
|
||||
get model() {
|
||||
return this.#model;
|
||||
}
|
||||
|
||||
set editable(state) {
|
||||
this.#editable = !!state;
|
||||
this.#layout.setEditable(this.#editable);
|
||||
if (this.#editable === false) {
|
||||
this.refreshModel();
|
||||
}
|
||||
}
|
||||
get editable() {
|
||||
return this.#editable;
|
||||
}
|
||||
|
||||
get layout() {
|
||||
return this.#layout;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this.#layout = new FormLayout();
|
||||
}
|
||||
|
||||
dom() {
|
||||
if (!this.#dom) {
|
||||
this.#dom = el.div({ class: 'jsl-form' });
|
||||
}
|
||||
this.#layout.buildDom(this.#dom);
|
||||
return this.#dom;
|
||||
}
|
||||
|
||||
refreshModel() {
|
||||
// TODO go over the fields and update the model with any modified data
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class FormLayout {
|
||||
|
||||
#rows = [];
|
||||
|
||||
addRow() {
|
||||
const row = new FormRow();
|
||||
this.#rows.push(row);
|
||||
return row;
|
||||
}
|
||||
|
||||
buildDom(element) {
|
||||
element.innerHTML = '';
|
||||
this.#rows.forEach(row => {
|
||||
const rowEl = row.dom();
|
||||
|
||||
element.appendChild(rowEl);
|
||||
})
|
||||
}
|
||||
|
||||
modelUpdated(model) {
|
||||
this.#rows.forEach(row => row.modelUpdated(model));
|
||||
}
|
||||
|
||||
setEditable(state) {
|
||||
this.#rows.forEach(row => row.setEditable(state));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class FormRow {
|
||||
el = null;
|
||||
#fields = [];
|
||||
|
||||
append(field) {
|
||||
this.#fields.push(field);
|
||||
return this;
|
||||
}
|
||||
|
||||
dom() {
|
||||
if (!this.el) {
|
||||
this.el = el.div({ class: '-row' });
|
||||
}
|
||||
this.el.innerHTML = '';
|
||||
this.#fields.forEach(field => {
|
||||
this.el.appendChild(field.dom());
|
||||
})
|
||||
return this.el;
|
||||
}
|
||||
|
||||
modelUpdated(model) {
|
||||
this.#fields.forEach(field => {
|
||||
if (!field.options.path) return;
|
||||
field.value = jsonQuery(model, field.options.path);
|
||||
});
|
||||
}
|
||||
|
||||
setEditable(state) {
|
||||
this.#fields.forEach(field => {
|
||||
if (field.options.locked === true) return;
|
||||
field.setEditable(state)
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class FormField {
|
||||
#el = null;
|
||||
#label = null;
|
||||
#options = {};
|
||||
#value = null;
|
||||
|
||||
get value() {
|
||||
return this.#value;
|
||||
}
|
||||
set value(val) {
|
||||
this.#value = val;
|
||||
this.valueUpdated();
|
||||
}
|
||||
|
||||
get el() {
|
||||
return this.#el;
|
||||
}
|
||||
|
||||
get options() {
|
||||
return this.#options;
|
||||
}
|
||||
|
||||
constructor(options = {}) {
|
||||
this.#options = options || {};
|
||||
}
|
||||
|
||||
dom() {
|
||||
if (!this.#el) {
|
||||
let style = null;
|
||||
if (this.#options.width) {
|
||||
style = 'width: ' + this.#options.width + '%;';
|
||||
} else {
|
||||
style = 'flex-grow: 1;';
|
||||
}
|
||||
this.#el = el.div({ class: '-form-field', style: style ?? null });
|
||||
|
||||
this.#label = el.label({ class: '-label' });
|
||||
this.#label.innerHTML = this.options.label ?? "Unlabeled";
|
||||
this.#el.appendChild(this.#label);
|
||||
}
|
||||
this.buildField();
|
||||
return this.#el;
|
||||
}
|
||||
|
||||
buildField() {
|
||||
}
|
||||
|
||||
valueUpdated() {
|
||||
}
|
||||
|
||||
setEditable(state) {
|
||||
}
|
||||
}
|
||||
|
||||
class TextField extends FormField {
|
||||
#input = null;
|
||||
buildField() {
|
||||
if (!this.#input) {
|
||||
this.#input = el.div({ class: '-field -text-field' })
|
||||
this.el.appendChild(this.#input);
|
||||
}
|
||||
this.#input.innerText = this.value;
|
||||
}
|
||||
valueUpdated() {
|
||||
this.#input.innerText = this.value;
|
||||
}
|
||||
getValue() {
|
||||
return this.#input.innerText;
|
||||
}
|
||||
setEditable(state) {
|
||||
if (state) {
|
||||
dom.apply(this.#input, { 'contenteditable':'plaintext-only' });
|
||||
} else {
|
||||
dom.apply(this.#input, { 'contenteditable':null });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DateField extends FormField {
|
||||
#input = null;
|
||||
buildField() {
|
||||
if (!this.#input) {
|
||||
this.#input = el.div({ class: '-field -date-field' })
|
||||
this.el.appendChild(this.#input);
|
||||
}
|
||||
this.#input.innerHTML = "I'm a date!";
|
||||
}
|
||||
}
|
||||
|
||||
class SelectField extends FormField {
|
||||
|
||||
}
|
||||
|
||||
class CheckField extends FormField {
|
||||
|
||||
}
|
||||
|
||||
export {
|
||||
JsonForm,
|
||||
FormLayout,
|
||||
FormRow,
|
||||
FormField,
|
||||
TextField,
|
||||
DateField,
|
||||
SelectField,
|
||||
CheckField,
|
||||
}
|
||||
Reference in New Issue
Block a user