pragma-views2
Version:
580 lines (461 loc) • 17.9 kB
JavaScript
import {TemplateParser} from "../../lib/template-parser.js";
import {findParentWithClass} from "./../../lib/dom-helper.js";
import {performViewElementValidation} from "./../../lib/view-helper.js";
import {ActionDialogManager} from "./../../lib/action-dialog-manager.js";
import {DatasetFactory} from "./../../lib/dataset-factory.js";
import {SchemaHelper} from "./../../lib/schema-helper.js";
import {createBehavioursFromString, releaseElements} from "./../../../../baremetal/lib/binding/binding-helpers.js";
import {addExpressionTriggers, observe, addTriggers} from "./../../../../baremetal/lib/binding/observers.js";
import {ViewBinding} from "./../../../../baremetal/lib/binding/view-binding.js"
import {addEventsFeatures, removeEventsFeatures} from "./../../../../baremetal/lib/mixin-events.js";
import {executeFunctionOnPath, setValueOnPath, deleteValueOnPath} from "./../../../../baremetal/lib/objectpath-helper.js";
import {isMobile} from "./../../lib/device-helper.js";
class PragmaForm extends HTMLElement {
get isMobile() {
return isMobile();
}
get context() {
return this._context;
}
set context(newValue) {
this._context = newValue;
}
get model() {
return this._model;
}
set model(newValue) {
this._model = newValue;
}
get remote() {
return this._remote;
}
set remote(newValue) {
this._remote = newValue;
}
get schema() {
return this._schema;
};
set schema(newValue) {
this._schema = newValue;
if (newValue != null && newValue._isVisible != true) {
this.refreshFromSchema();
}
}
get selectedId() {
return this._selectedId;
}
set selectedId(newValue) {
this._selectedId = newValue;
}
get templateId() {
return this._templateId;
}
set templateId(newValue) {
this._templateId = newValue;
}
get templates() {
return this._templates;
}
set templates(newValue) {
this._templates = newValue;
}
get type() {
return this._type;
}
set type(newValue) {
this._type = newValue;
}
clear() {
this.detailsElement.innerHTML = "";
}
connectedCallback() {
addEventsFeatures(this);
this.initTemplate();
this.binding = new ViewBinding(this);
this.templates = new Map();
this.templateParser = new TemplateParser();
this.detailsElement = this.querySelector(".form-container");
if (this.remote != undefined) {
window.eventEmitter.emit("fetch-schema",
{
remote: this.remote,
type: this.type || "view",
callback: (schema) => this.schema = schema
});
}
if (this.schema != undefined) {
this.refreshFromSchema();
}
if (this.remote != undefined && this.type != undefined) {
this.remoteChanged(this.remote);
}
this.registerEvent(this, "focusin", this.focus.bind(this));
this.registerEvent(this, "focusout", this.focusOut.bind(this));
this.validateElementHandler = this.validateElement.bind(this);
window.eventEmitter.on("validate-element", this.validateElementHandler);
this.schemaHelper = new SchemaHelper(this.schema, this.model);
}
disconnectedCallback() {
removeEventsFeatures(this);
window.eventEmitter.emit("clear-assistant");
window.eventEmitter.remove("validate-element", this.validateElementHandler);
this.disposeFileInput();
this.disposeProcessManager();
this.disposeVariables(this.schema);
}
disposeFileInput() {
if (this.fileInput) {
this.fileInput.removeEventListener("change", this.changeHandler);
this.fileInput = null;
this.changeHandler = null;
}
}
disposeProcessManager() {
if (this.processManager == undefined) {
return;
}
this.processManager.dispose();
this.processManager = null;
}
disposeVariables(schema) {
if (schema == undefined) {
return;
}
if (schema.variables != undefined && schema.variables.models != undefined) {
const keys = Object.keys(schema.variables.models);
for (let key of keys) {
if (Number.isInteger(schema.variables.models[key]) == false) {
schema.variables.models[key].dispose();
schema.variables.models[key] = null;
}
}
}
schema.variables = null;
}
fetchSchema(remote, type) {
if (this.remote != undefined && this.type != undefined) {
window.eventEmitter.emit("fetch-schema",
{
remote: remote,
type: this.type || "view",
callback: (schema) => this.schema = schema
});
}
}
focus(event) {
const card = findParentWithClass(event.target, "card");
if (card != null) {
card.setAttribute("aria-selected", true);
event.target.card = card;
}
}
focusFirstInput() {
const input = this.querySelector("input");
if (input)
input.focus();
}
focusOut(event) {
if (event.target.card != undefined) {
event.target.card.setAttribute("aria-selected", false);
event.target.card = null;
}
}
/**
* Models are created externally and does not have context information, but we still want to give them context awareness without over exposing them.
* To allow you to define property defaults on the model, we inflate the values as soon as it becomes part of the form because the form understands context.
* @param model
*/
inflateModelDefaults(model) {
if (model == undefined || model.__definition == undefined) {
return;
}
for (let field of model.__definition.fields) {
if (field.default != undefined) {
const defaultValue = this.schemaHelper.getAssociatedValue(field.default);
field.initialValue = defaultValue;
model.__defaults.set(field.name, defaultValue);
model[field.name] = defaultValue;
}
}
}
inflateParameters(parameters) {
const keys = Object.keys(parameters);
for (let key of keys) {
let result = this.schemaHelper.getAssociatedValue(parameters[key]);
if (key == "model") {
result = this.inflateParameters(result);
}
parameters[key] = result;
}
return parameters;
}
initTemplate() {
const instance = document.importNode(window.templates.get("pragma-form"), true);
this.appendChild(instance);
}
loadContentFromFile(event) {
const file = event.target.files[0];
const fr = new FileReader();
fr.onload = _ => {
this.schema = JSON.parse(fr.result);
this.disposeFileInput();
};
fr.readAsText(file);
}
async loadDatasetVariable(name) {
if (this.schema.variables == undefined || this.schema.variables.models == undefined) {
return;
}
const dsValue = this.schema.variables.models[name];
if (Number.isInteger(dsValue)) {
const factory = new DatasetFactory({schema: this.schema});
const ds = factory.createDataSet(dsValue);
ds.setInitialValues({});
this.schema.variables.models[name] = ds;
factory.dispose();
this.inflateModelDefaults(ds);
}
else {
dsValue.resetToDefault();
}
}
loadHtml(html) {
const container = this.querySelector(".form-container");
container.innerHTML = html;
this.binding.parse(this, this.children);
window.eventEmitter.emit("form-updated", this);
}
loadTemplates() {
this.templates.clear();
if (this.schema.templates != undefined) {
this.templateParser.initializeResources(this.schema);
for (let template of this.schema.templates) {
const html = this.templateParser.parseElements(template.elements);
this.templates.set(template.id, html);
}
}
}
modelChanged(newValue) {
if (this.schemaHelper != undefined) {
this.schemaHelper.model = newValue;
}
}
nextProcessStep() {
this.processManager.next();
}
/**
* NOTES:
* Is there a current process running?
* If there is and the action is UI related then show it in the process UI.
* If not, and it is UI related then you need to show a dialog with the UI.
* @param id
*/
async performAction(id) {
if (Array.isArray(id)) {
for (let i of id) {
await this.performAction(i);
}
return;
}
if (id != undefined) {
const action = this.schema.actions.find(item => item.id == id);
let result;
if (action.remote != undefined) {
result = await this.performRemoteAction(action);
}
else if (this[action.action] != undefined) {
if (action.parameters != undefined) {
action.parameters = this.inflateParameters(action.parameters);
}
result = await this[action.action].call(this, action.parameters);
}
else if (action.action.indexOf(".") != -1) {
result = await executeFunctionOnPath(this, action.action, action.parameters);
}
if (action.variable != undefined) {
if (typeof result == "object") {
for (let key of Object.keys(result)) {
setValueOnPath(this.schema, `schema.variables.${action.variable}.${key}`, result[key]);
}
}
else {
setValueOnPath(this.schema, `schema.variables.${action.variable}`, result);
}
}
}
}
async performContextAction(action) {
const a = Object.create(action);
if (a.parameters != undefined) {
a.parameters = this.inflateParameters(a.parameters);
}
return await this.context.performAction(a);
}
async performProcess(id) {
const process = this.schema.processes.find(item => item.id == id);
this.showProcess(process.actions);
}
async performRemoteAction(action) {
if (this.context.performAction != undefined) {
if (action.template == undefined) {
return await this.performContextAction(action)
}
else {
if (action.model != undefined) {
this.loadDatasetVariable(action.model);
}
const html = this.templateParser.parseTemplate({template: action.template});
const div = document.createElement("div");
// JHR: todo: replace
//this.viewSource = this.dynamicViewLoader.load(html, div, this);
const dialog = new ActionDialogManager(div, async (successfull) => {
if (successfull == true) {
return await this.performContextAction(action);
}
// JHR: todo: replace
//this.dynamicViewLoader.unload(div);
dialog.dispose();
});
}
}
}
perspective(id) {
if (this.schema.perspectives == undefined) {
return undefined;
}
return this.schema.perspectives.find(item => item.id == id);
}
async postMessage(action) {
window.eventEmitter.postMessage(action.parameters.query, action.parameters);
}
async releaseElements() {
const elements = this.querySelectorAll("[behaviours]");
return releaseElements(elements);
}
remoteChanged(newValue) {
this.fetchSchema(newValue, this.type);
}
removeVariable(action) {
const varName = this.schemaHelper.getAssociatedValue(action.parameters.name);
deleteValueOnPath(this.schema, `schema.variables.${varName}`);
}
async refreshFromSchema() {
this.releaseElements();
if (this.schema == null) return;
this.schema._isVisible = true;
if (this.schemaHelper != undefined) {
this.schemaHelper.schema = this.schema;
}
if (this.templateParser) {
this.loadTemplates();
const html = this.templateId == undefined ? await this.templateParser.parse(this.schema) : this.templateParser.parseTemplate({template: this.templateId});
this.loadHtml(html);
this.setupBehaviours();
this.focusFirstInput();
this.inflateModelDefaults(this.model);
this.setupCustomActionEvents();
this.setupCustomActionTriggers();
}
}
setValue(parameters) {
const keys = Object.keys(parameters);
for (let key of keys) {
setValueOnPath(this, this.updateVariablePath(key), parameters[key]);
}
}
setVariable(action) {
const varName = this.schemaHelper.getAssociatedValue(action.parameters.name);
const varValue = this.schemaHelper.getAssociatedValue(action.parameters.value);
setValueOnPath(this.schema, `variables.${varName}`, varValue);
}
setupBehaviours() {
const elements = this.querySelectorAll("[behaviours]");
const collection = Array.from(elements);
for(let element of collection) {
createBehavioursFromString(element.getAttribute("behaviours"), element);
};
}
updateVariablePath(path) {
if (path.indexOf("@") == -1) {
return path;
}
return path.split("@").join("schema.variables.");
}
setupCustomActionTriggers() {
if (this.schema.customActionTriggers == undefined) return;
if (Array.isArray(this.schema.customActionTriggers) == false) return;
for (let customTrigger of this.schema.customActionTriggers) {
this.setupCustomActionTrigger(customTrigger);
}
}
setupCustomActionTrigger(customTrigger) {
let expression = customTrigger.trigger;
if (expression.indexOf("@") != -1) {
expression = this.updateVariablePath(expression);
}
addTriggers(this, expression, () => this.performTriggeredActions(customTrigger));
}
setupCustomActionEvents() {
if (this.schema.customActionEvents == undefined) return;
if (Array.isArray(this.schema.customActionEvents) == false) return;
for (let customEvent of this.schema.customActionEvents) {
this.setupCustomActionEvent(customEvent);
}
}
setupCustomActionEvent(customEvent) {
let expression = customEvent.event;
if (expression.indexOf("@") != -1) {
expression = this.updateVariablePath(expression);
}
addExpressionTriggers(this, expression, () => this.performTriggeredActions(customEvent));
}
performTriggeredActions(customAction) {
for (let action of customAction.actions) {
if (action.condition != undefined) {
const cnd = window.compiler.add(action.condition);
const result = cnd(this);
if (result != true) {
continue;
}
}
const parameters = this.inflateParameters(action.parameters);
if (action.action == "setValue") {
this.setValue(parameters);
}
else if (action.action = "setValidation") {
this.setValidation(parameters);
}
}
}
async showDialog(action) {
const dialog = new ActionDialogManager(action, this, () => {
console.log("perform action requested");
dialog.dispose();
});
}
async showProcess(actions) {
for (let actionId of actions) {
await this.performAction(actionId);
}
}
showSchemaTemplate(templateId) {
if (this.templates.has(templateId)) {
const html = this.templates.get(templateId);
this.clear();
this.loadHtml(html);
}
}
template(id) {
if (this.schema.templates == undefined) {
return undefined;
}
return this.schema.templates.find(item => item.id == id);
}
typeChanged(newValue) {
this.fetchSchema(this.remote, newValue);
}
validateElement(element) {
performViewElementValidation(this.viewSource.view, element);
}
}
customElements.define('pragma-form', PragmaForm);