@davebaol/angular-formio-editor
Version:
Angular component integrating Form.io builder and renderer with a json editor
1,359 lines (1,342 loc) • 70.4 kB
JavaScript
import { __decorate } from 'tslib';
import { Input, ViewChild, Component as Component$1, EventEmitter, Output, NgModule } from '@angular/core';
import { BsModalService, ModalModule } from 'ngx-bootstrap/modal';
import { FormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { FormioModule } from 'angular-formio';
import { AlertModule } from 'ngx-bootstrap/alert';
import JsonEditor from 'jsoneditor';
/*
This is heavily inspired by https://github.com/schnittstabil/merge-options
*/
const { hasOwnProperty, toString } = Object.prototype;
const { propertyIsEnumerable } = Object;
const globalThis = this;
const defaultMergeOpts = { ignoreUndefined: false };
const isPlainObject = (value) => {
if (toString.call(value) !== '[object Object]') {
return false;
}
const prototype = Object.getPrototypeOf(value);
return prototype === null || prototype === Object.prototype;
};
const ɵ0 = isPlainObject;
const defineProperty = (obj, name, value) => {
Object.defineProperty(obj, name, {
value,
writable: true,
enumerable: true,
configurable: true
});
};
const ɵ1 = defineProperty;
const getEnumerableOwnPropertyKeys = (value) => {
const keys = [];
for (const key in value) {
if (hasOwnProperty.call(value, key)) {
keys.push(key);
}
}
if (Object.getOwnPropertySymbols) {
const symbols = Object.getOwnPropertySymbols(value);
for (const symbol of symbols) {
if (propertyIsEnumerable.call(value, symbol)) {
keys.push(symbol);
}
}
}
return keys;
};
const ɵ2 = getEnumerableOwnPropertyKeys;
const clone = (value) => {
if (Array.isArray(value)) {
return cloneArray(value);
}
if (isPlainObject(value)) {
return clonePlainObject(value);
}
return value;
};
const cloneArray = (array) => {
const result = array.slice(0, 0);
getEnumerableOwnPropertyKeys(array).forEach(key => {
defineProperty(result, key, clone(array[key]));
});
return result;
};
const ɵ3 = cloneArray;
const clonePlainObject = (obj) => {
const result = Object.getPrototypeOf(obj) === null ? Object.create(null) : {};
getEnumerableOwnPropertyKeys(obj).forEach(key => {
defineProperty(result, key, clone(obj[key]));
});
return result;
};
const ɵ4 = clonePlainObject;
const mergeKeys = (merged, source, keys, config) => {
keys.forEach(key => {
if (typeof source[key] === 'undefined' && config.ignoreUndefined) {
return;
}
// Do not recurse into prototype chain of merged
if (key in merged && merged[key] !== Object.getPrototypeOf(merged)) {
defineProperty(merged, key, _merge(merged[key], source[key], config));
}
else {
defineProperty(merged, key, clone(source[key]));
}
});
return merged;
};
const ɵ5 = mergeKeys;
// tslint:disable-next-line:variable-name
const _merge = (merged, source, config) => {
if (!isPlainObject(source) || !isPlainObject(merged)) {
return clone(source);
}
return mergeKeys(merged, source, getEnumerableOwnPropertyKeys(source), config);
};
const ɵ6 = _merge;
const merge = (...options) => {
const config = _merge(clone(defaultMergeOpts), (this !== globalThis && this) || {}, defaultMergeOpts);
let merged = { _: {} };
for (const option of options) {
if (option === undefined) {
continue;
}
if (!isPlainObject(option)) {
throw new TypeError('`' + option + '` is not a plain Object');
}
merged = _merge(merged, { _: option }, config);
}
return merged._;
};
const { hasOwnProperty: hasOwnProperty$1 } = Object.prototype;
// -------------------------------
// SCHEMAS
// -------------------------------
class Schema {
constructor(component) {
this.component = component;
this.conditions = [];
this.required = false;
this.dataType = {};
if (component && component.formioComponent) {
if (component.formioComponent.conditional) {
// Add non-empty condition from the component
const cnd = component.formioComponent.conditional;
if (typeof cnd.show === 'boolean' && cnd.when) {
// console.log(component.formioComponent.type, "Pushing condition")
const c = { show: cnd.show, when: cnd.when, eq: cnd.eq };
c.key = this.generateSingleConditionKey(c);
this.conditions.push(c);
}
}
if (component.formioComponent.validate) {
this.required = component.formioComponent.validate.required;
}
}
}
/*private*/ generateSingleConditionKey(condition) {
return JSON.stringify(condition);
}
/*private*/ generateConditionsKey() {
return JSON.stringify(this.conditions, (k, v) => k === 'key' ? undefined : v);
}
prepareConditions() {
// Ensure the array has unique conditions
this.conditions = this.conditions.sort((c1, c2) => c1.key.compare(c2.key))
.filter((x, i, a) => i === 0 || x.key !== a[i - 1].key);
this.conditions.key = this.generateConditionsKey();
}
shrink() {
for (let k in this.properties) {
if (hasOwnProperty$1.call(this.properties, k)) {
let propSchema = this.properties[k];
if (propSchema instanceof MergeableObjectSchema && propSchema.component && propSchema.component.shrinkable()) {
// console.log('Shrink', propSchema.component.formioComponent.type);
propSchema.shrink();
delete this.properties[k]; // Remove shribkable schema from parent
this.merge(propSchema); // merge its properties and conditions with parent
}
}
}
return this;
}
// Subclasses overriding this method MUST call super.toJsonSchema()
toJsonSchema() {
return Object.assign({}, this.dataType);
}
}
class ObjectSchema extends Schema {
constructor(component) {
super(component);
this.dataType.type = 'object';
this.properties = {};
}
addProperty(name, schema, required) {
this.properties[name] = schema;
schema.required = required;
return this;
}
toJsonSchema() {
const jsonSchema = super.toJsonSchema();
jsonSchema.properties = {};
const required = [];
const condPropMap = {};
const conditionsMap = {};
for (let pk in this.properties) {
if (hasOwnProperty$1.call(this.properties, pk)) {
const childSchema = this.properties[pk];
if (childSchema.conditions.length === 0) {
if (childSchema.required) {
required.push(pk);
}
jsonSchema.properties[pk] = childSchema.toJsonSchema();
continue;
}
childSchema.prepareConditions();
const ck = childSchema.conditions.key;
conditionsMap[ck] = childSchema.conditions;
if (!(ck in condPropMap)) {
condPropMap[ck] = {};
}
condPropMap[ck][pk] = childSchema;
}
}
// Add required to jsonSchema if not empty
if (required.length > 0) {
jsonSchema.required = required;
}
// Generate allOf from conditional properties
const allOf = [];
for (let ck in condPropMap) {
if (hasOwnProperty$1.call(condPropMap, ck)) {
const conds = conditionsMap[ck];
const _if = {
properties: conds.reduce((acc, c) => {
acc[c.when] = c.show ? { const: c.eq } : { not: { const: c.eq } };
return acc;
}, {})
};
const then = { required: [], properties: {} };
for (let pk in condPropMap[ck]) {
if (hasOwnProperty$1.call(condPropMap[ck], pk)) {
const childSchema = condPropMap[ck][pk];
if (childSchema.required) {
then.required.push(pk);
}
then.properties[pk] = childSchema.toJsonSchema();
}
}
// Remove empty required
if (then.required.length === 0) {
delete then.required;
}
// Add if/then to allOf
allOf.push({ if: _if, then: then });
}
}
// Add allOf to jsonSchema if not empty
if (allOf.length > 0) {
jsonSchema.allOf = allOf;
}
return jsonSchema;
}
}
class MergeableObjectSchema extends ObjectSchema {
constructor(component) {
super(component);
}
merge(...sources) {
const targetProps = this.properties;
for (let i = 0, len = sources.length; i < len; i++) {
const source = sources[i];
if (source instanceof MergeableObjectSchema) {
// merge properties
const sourceProps = source.properties;
for (const key in sourceProps) {
if (hasOwnProperty$1.call(sourceProps, key)) {
// Append source schema conditions to the conditions of its sub-schemas
Array.prototype.push.apply(sourceProps[key].conditions, source.conditions);
// Merge properties recursively
if (targetProps[key] && sourceProps[key] instanceof MergeableObjectSchema) {
targetProps[key].merge(sourceProps[key]);
}
else {
targetProps[key] = sourceProps[key];
}
}
}
}
}
return this;
}
}
class ArraySchema extends Schema {
constructor(component, items) {
super(component);
this.dataType.type = 'array';
this.dataType.items = items;
}
toJsonSchema() {
const jsonSchema = super.toJsonSchema();
jsonSchema.items = this.dataType.items.toJsonSchema();
return jsonSchema;
}
}
class PrimitiveSchema extends Schema {
constructor(component, type) {
super(component);
this.dataType.type = type;
}
}
class BooleanSchema extends PrimitiveSchema {
constructor(component) {
super(component, 'boolean');
}
}
class NumberSchema extends PrimitiveSchema {
constructor(component) {
super(component, 'number');
if (component && component.formioComponent && component.formioComponent.validate) {
const validate = component.formioComponent.validate;
if (validate.min || typeof validate.min === 'number')
this.dataType.minimum = Number(validate.min);
if (validate.max || typeof validate.max === 'number')
this.dataType.maximum = Number(validate.max);
if (validate.integer)
this.dataType.type = 'integer';
}
}
}
class StringSchema extends PrimitiveSchema {
constructor(component) {
super(component, 'string');
if (component && component.formioComponent && component.formioComponent.validate) {
const validate = component.formioComponent.validate;
if (validate.minLength)
this.dataType.minLength = Number(validate.minLength);
if (validate.maxLength)
this.dataType.maxLength = Number(validate.maxLength);
if (validate.pattern)
this.dataType.pattern = validate.pattern;
}
}
}
class EnumSchema extends Schema {
constructor(component, values) {
super(component);
this.dataType.enum = values;
}
}
// -------------------------------
// COMPONENT BASE CLASSES
// -------------------------------
class Component {
constructor(formioComponent) {
this.formioComponent = formioComponent;
}
schema() {
throw new Error('Subclasses of \'Component\' have to implement the method \'schema\'!');
}
// Layout components are view-only components. From resource perspective, they are to be
// shrinked, because they don't have any value neither implicit nor expressed by user.
// So they don't contribute to the underlying resource because their 'API key' does not
// match any field inside the resource itself.
// Howewer, shrink process propagates component's condition (show/when/eq) to child components.
shrinkable() {
return !(this.formioComponent && this.formioComponent.input);
}
}
class AtomicComponent extends Component {
schema() {
const schema = this.baseSchema();
if (this.formioComponent.multiple) {
if (this.formioComponent.validate && !this.formioComponent.validate.required) {
// With multiple values enabled the component can generate null items if required is false
schema.dataType = { anyOf: [schema.dataType, { type: 'null' }] };
}
return new ArraySchema(this, schema);
}
return schema;
}
baseSchema() {
throw new Error('Subclasses of \'AtomicComponent\' have to implement the method \'baseSchema\'!');
}
isDefaultCastToString() {
return false; // cast defaults to 'auto'
}
cast(val, to) {
switch (to) {
case 'boolean':
return val.toLowerCase() === 'true';
case 'number':
return Number(val);
case 'object':
return JSON.parse(val);
case 'string':
return val;
case 'auto':
default: // Either autotype or string
if (to !== 'auto' && this.isDefaultCastToString())
return val;
if (val === "true")
return true;
if (val === "false")
return false;
if (val === "")
return val;
var v = Number(val);
return isNaN(v) ? val : v;
}
}
}
class CompoundComponent extends Component {
schema() {
let schema = new MergeableObjectSchema(this);
this.childrenSchema(schema);
return schema.shrink();
}
/*prorected*/ children() {
return this.formioComponent.components;
}
// Subclasses can override this method to provide a default class
// for children that don't have a type
/*prorected*/ defaultChildClass() {
return undefined;
}
/*prorected*/ childrenSchema(parentSchema) {
const children = this.children();
for (let i = 0, len = children.length; i < len; i++) {
let c = children[i];
// console.log(this.formioComponent.type, 'child', c)
const type = MAP[c.type] || this.defaultChildClass();
if (type) {
let schema = new (type)(c).schema();
const required = c.validate && c.validate.required;
// Dotted key means nested schema
const keyParts = c.key.split('.');
for (let j = keyParts.length - 1; j > 0; j--) {
schema = new MergeableObjectSchema(undefined).addProperty(keyParts[j], schema, required);
}
parentSchema.merge(new MergeableObjectSchema(undefined).addProperty(keyParts[0], schema, required));
}
else {
// console.log(this.formioComponent.type, ": skipping child with unknown type", c.type);
}
}
return parentSchema;
}
}
// -------------------------------
// ABSTRACT COMPONENT CLASSES
// -------------------------------
class StringComponent extends AtomicComponent {
baseSchema() {
return new StringSchema(this);
}
}
class EnumComponent extends AtomicComponent {
constructor(formioComponent, ...additionalValuesIfNotRequired) {
super(formioComponent);
this.additionalValuesIfNotRequired = additionalValuesIfNotRequired;
}
// This is needed because different components take values from different path
values() {
throw new Error('Subclasses of \'EnumComponent\' have to implement the method \'values\'!');
}
baseSchema() {
const values = this.values().map(v => this.cast(v.value, this.formioComponent.dataType));
if (this.formioComponent && this.formioComponent.validate && !this.formioComponent.validate.required) {
Array.prototype.push.apply(values, this.additionalValuesIfNotRequired);
}
return new EnumSchema(this, values);
}
}
// -------------------------------
// BASIC COMPONENTS
// -------------------------------
class CheckboxComponent extends AtomicComponent {
baseSchema() {
return new BooleanSchema(this);
}
}
class NumberComponent extends AtomicComponent {
baseSchema() {
return new NumberSchema(this);
}
}
class PasswordComponent extends StringComponent {
}
class RadioComponent extends EnumComponent {
constructor(formioComponent) {
// Empty string and null are valid values if the component is not required
super(formioComponent, '', null);
}
values() {
return this.formioComponent.values;
}
}
class SelectComponent extends EnumComponent {
constructor(formioComponent) {
// Empty string and null are valid values if the component is not required
super(formioComponent, '', null);
}
values() {
return this.formioComponent.data.values;
}
schema() {
const schema = super.schema();
// If multiple values are enabled ensure uniqueness
if (schema instanceof ArraySchema) {
schema.dataType.uniqueItems = true;
}
return schema;
}
isDefaultCastToString() {
return true; // cast defaults to 'string'
}
}
class SelectBoxesComponent extends AtomicComponent {
baseSchema() {
let schema = new ObjectSchema(this);
schema.dataType.additionalProperties = false;
let values = this.formioComponent.values
.forEach(v => schema.addProperty(v.value, new BooleanSchema(undefined), true));
if (this.formioComponent.validate && !this.formioComponent.validate.required) {
// This is needed for compatibility.
// Formio adds a boolean property with name "" when the component is not required.
// The property itself must not be required
schema.addProperty('', new BooleanSchema(undefined), false);
}
return schema;
}
}
class TextAreaComponent extends StringComponent {
}
class TextFieldComponent extends StringComponent {
}
// -------------------------------
// ADVANCED COMPONENTS
// -------------------------------
class DateTimeComponent extends StringComponent {
}
class EmailComponent extends StringComponent {
}
class UrlComponent extends StringComponent {
}
class TagsComponent extends AtomicComponent {
schema() {
return this.formioComponent.storeas === 'array' ?
new ArraySchema(this, this.baseSchema())
: this.baseSchema();
}
baseSchema() {
return new StringSchema(this);
}
}
// -------------------------------
// LAYOUT COMPONENTS
// -------------------------------
class ColumnsComponent extends CompoundComponent {
// Determines children from columns.
// Children are calculated lazily and cached into this instance.
children() {
if (!this.components) {
this.components = [];
this.formioComponent.columns.forEach(col => Array.prototype.push.apply(this.components, col.components));
}
return this.components;
}
}
class ContentComponent extends CompoundComponent {
children() {
return []; // This component never has children
}
}
class FieldSetComponent extends CompoundComponent {
}
class PanelComponent extends CompoundComponent {
}
class TableComponent extends CompoundComponent {
// Determines children from table's rows and columns.
// Children are calculated lazily and cached into this instance.
children() {
if (!this.components) {
this.components = [];
this.formioComponent.rows.forEach(row => {
row.forEach(col => Array.prototype.push.apply(this.components, col.components));
});
}
return this.components;
}
}
class TabsComponent extends CompoundComponent {
/*prorected*/ defaultChildClass() {
return CompoundComponent; // Needed because children don't have a type :(
}
}
class WellComponent extends CompoundComponent {
}
// -------------------------------
// DATA COMPONENTS
// -------------------------------
class EditGridComponent extends CompoundComponent {
schema() {
return new ArraySchema(this, super.schema());
}
}
// -------------------------------
// FORM COMPONENT
// -------------------------------
class FormComponent extends CompoundComponent {
}
const MAP = {
editgrid: EditGridComponent,
checkbox: CheckboxComponent,
columns: ColumnsComponent,
content: ContentComponent,
datetime: DateTimeComponent,
email: EmailComponent,
fieldset: FieldSetComponent,
number: NumberComponent,
password: PasswordComponent,
panel: PanelComponent,
radio: RadioComponent,
select: SelectComponent,
selectboxes: SelectBoxesComponent,
table: TableComponent,
tabs: TabsComponent,
tags: TagsComponent,
textarea: TextAreaComponent,
textfield: TextFieldComponent,
url: UrlComponent,
well: WellComponent
};
function generateFormJsonSchema(form) {
return new FormComponent(form).schema().toJsonSchema();
}
// tslint:disable:object-literal-key-quotes quotemark semicolon
var schema = {
"title": "Form",
"description": "Object containing a form.io form",
"type": "object",
"required": ["components"],
"properties": {
"display": {
"title": "Display mode",
"description": "The given name.",
"enum": ["form", "wizard"]
},
"components": {
"$ref": "components"
}
}
};
// tslint:disable:object-literal-key-quotes quotemark semicolon
var components = {
"title": "Component List",
"description": "The list of all components",
"type": "array",
"items": {
"$ref": "component"
}
};
// tslint:disable:object-literal-key-quotes quotemark semicolon
var componentStrict = {
"title": "Component",
"description": "Object containing a form.io component",
"type": "object",
"required": ["type", "key", "input"],
"properties": {
"type": {
"title": "Component Type",
"description": "The type of this component",
"type": "string"
},
"key": {
"title": "Component Key",
"description": "The API key for this component",
"type": "string"
},
"label": {
"title": "Component Label",
"description": "The HTML label to give this component",
"type": "string"
},
"placeholder": {
"title": "Component Placeholder",
"description": "The text to show in the input before they type",
"type": "string"
},
"input": {
"title": "User Input?",
"description": "Determines if this is an input from the user",
"type": "boolean"
},
"tableView": {
"title": "Component TableView",
"description": "Determines if this field will show in the data tables output",
"type": "boolean"
},
"multiple": {
"title": "Component Multiple",
"description": "If this field should collect multiple values, creating an array of values",
"type": "boolean"
},
"protected": {
"title": "Component Protected",
"description": "If the value of this field should be shown to the end user via API once it is saved",
"type": "boolean"
},
"prefix": {
"title": "Component Prefix",
"description": "The prefix text to put in front of the input",
"type": "string"
},
"suffix": {
"title": "Component Suffix",
"description": "The suffix text to put after the input",
"type": "string"
},
"defaultValue": {
"title": "Default Value",
"description": "The default value to provide to this component. Its type depends on the specific component"
},
"clearOnHide": {
"title": "Clear on Hide",
"description": "If the value of this field should be cleared when it is conditionally hidden",
"type": "boolean"
},
"unique": {
"title": "Unique",
"description": "Validates if this field should be unique amongst other submissions in the same form",
"type": "boolean"
},
"persistent": {
"title": "Persistent",
"description": "Determines if the value of this field should be saved as persistent",
"type": "boolean"
},
"hidden": {
"title": "Hidden",
"description": "Determines if this field should be hidden from view by default. This can be overridden with the conditionals.",
"type": "boolean"
},
"validate": {
"title": "Validate",
"description": "Determines validation criteria for this component.",
"type": "object",
"properties": {
"required": {
"title": "Required",
"description": "Specifies if the field is required.",
"type": "boolean"
},
"minLength": {
"title": "Min Lenngth",
"description": "For text input, this checks the minimum length of text for valid input.",
"type": ["number", "string"]
},
"maxLength": {
"title": "Max Lenngth",
"description": "For text input, this checks the maximum length of text for valid input.",
"type": ["number", "string"]
},
"pattern": {
"title": "Pattern",
"description": "For text input, this checks the text agains a Regular expression pattern.",
"type": "string"
},
"custom": {
"title": "Custom",
"description": "A custom javascript based validation or a JSON object for using JSON Logic.",
"type": ["string", "object"]
}
}
},
"conditional": {
"$ref": "conditional"
},
"errors": {
"title": "Errors",
"description": "Allows customizable errors to be displayed for each component when an error occurs.",
"type": "object",
"properties": {
"required": {
"title": "Required",
"description": "Error message for error 'required'.",
"type": "string"
},
"min": {
"title": "Min",
"description": "Error message for error 'min'.",
"type": "string"
},
"max": {
"title": "Min",
"description": "Error message for error 'max'.",
"type": "string"
},
"minLength": {
"title": "Min Length",
"description": "Error message for error 'minLength'.",
"type": "string"
},
"maxLength": {
"title": "Max Length",
"description": "Error message for error 'maxLength'.",
"type": "string"
},
"invalid_email": {
"title": "Invalid Email",
"description": "Error message for error 'invalid_email'.",
"type": "string"
},
"invalid_date": {
"title": "Invalid Date",
"description": "Error message for error 'invalid_date'.",
"type": "string"
},
"pattern": {
"title": "Pattern",
"description": "Error message for error 'pattern'.",
"type": "string"
},
"custom": {
"title": "Custom",
"description": "Error message for error 'custom'.",
"type": ["string", "object"]
}
}
},
"logic": {
"title": "Logic",
"description": "Allows changing the component definition in reaction to data entered in a form. For example, changing a field to required, disabled or hidden when a value is entered.",
"type": "array",
"items": {
"$ref": "logic"
}
}
},
"allOf": [
{
"if": { "properties": { "type": { "const": "columns" } } },
"then": { "$ref": "columns" }
},
{
"if": { "properties": { "type": { "const": "table" } } },
"then": { "$ref": "table" }
},
{
"if": { "properties": { "type": { "const": "tabs" } } },
"then": { "$ref": "tabs" },
"else": {
"properties": {
"components": {
"$ref": "components"
}
}
}
}
]
};
var componentLoose = Object.assign({}, componentStrict, { required: ['type'] });
// tslint:disable:object-literal-key-quotes quotemark semicolon
var logic = {
"title": "Logic",
"description": "...",
"type": "object",
"required": ["trigger", "actions"],
"properties": {
"trigger": {
"$ref": "trigger"
},
"actions": {
"title": "Actions",
"description": "The actions to perform when the logic is triggered",
"type": "array",
"items": {
"$ref": "action"
}
}
}
};
// tslint:disable:object-literal-key-quotes quotemark semicolon
var trigger = {
"title": "Trigger",
"description": "Determines when the logic should be triggered",
"type": "object",
"required": ["type"],
"properties": {
"type": {
"title": "Type",
"description": "The type of the trigger.",
"enum": ["simple", "javascript", "json", "event"]
}
},
"allOf": [
{
"if": {
"properties": { "type": { "const": "simple" } }
},
"then": {
"required": ["simple"],
"properties": {
"simple": {
"title": "Simple",
"description": "Defines a simple trigger.",
"required": ["when", "eq", "show"],
"type": "object",
"properties": {
"when": {
"title": "When",
"description": "The trigger field key.",
"type": "string"
},
"eq": {
"title": "Eq",
"description": "The value to equal.",
"type": "string"
},
"show": {
"title": "Show",
"description": "Whether to trigger or not when the value is equal.",
"type": "boolean"
}
}
}
}
}
},
{
"if": {
"properties": { "type": { "const": "javascript" } }
},
"then": {
"required": ["javascript"],
"properties": {
"javascript": {
"title": "Javascript",
"description": "Javascript logic.",
"type": "string"
}
}
}
},
{
"if": {
"properties": { "type": { "const": "json" } }
},
"then": {
"required": ["json"],
"properties": {
"json": {
"title": "Json",
"description": "JSON Logic object that returns true or false.",
"type": "object"
}
}
}
},
{
"if": {
"properties": { "type": { "const": "event" } }
},
"then": {
"required": ["event"],
"properties": {
"event": {
"title": "Event",
"description": "The name of the event that will trigger this logic.",
"type": "string"
}
}
}
}
]
};
// tslint:disable:object-literal-key-quotes quotemark semicolon
var action = {
"title": "Action",
"description": "An action to perform when the logic is triggered",
"required": ["type"],
"type": "object",
"properties": {
"type": {
"title": "Type",
"description": "The type of the action.",
"enum": ["property", "value"]
}
},
"allOf": [
{
"if": {
"properties": { "type": { "const": "property" } }
},
"then": {
"properties": {
"property": {
"title": "Property",
"description": "The property action.",
"required": ["type", "value"],
"type": "object",
"properties": {
"type": {
"title": "Property",
"description": "The type of the property action (either 'boolean' or 'string').",
"enum": ["boolean", "string"]
},
"value": {
"title": "Value",
"description": "The path to the property on the component definition.",
"type": "string"
}
}
}
},
"allOf": [
{
"if": {
"properties": { "type": { "const": "boolean" } }
},
"then": {
"properties": {
"state": {
"title": "Boolean State",
"description": "Used if the type of the property action is boolean.",
"type": "boolean"
}
}
}
},
{
"if": {
"properties": { "type": { "const": "string" } }
},
"then": {
"properties": {
"text": {
"title": "String Text",
"description": "Used if the type of the property action is string.",
"type": "string"
}
}
}
}
]
}
},
{
"if": {
"properties": { "type": { "const": "value" } }
},
"then": {
"properties": {
"value": {
"title": "Value",
"description": "The javascript that returns the new value. It Will be evaluated with available variables of row, data, component and result (returned from trigger).",
"type": "string"
}
}
}
}
]
};
// tslint:disable:object-literal-key-quotes quotemark semicolon
var conditional = {
"title": "Conditional",
"description": "Determines when this component should be added to the form for both processing and input.",
"type": "object",
"properties": {
"show": {
"$ref": "show",
},
"when": {
"title": "When",
"description": "The field API key that it should compare its value against to determine if the condition is triggered.",
"type": ["string", "null"]
},
"eq": {
"title": "Eq",
"description": "The value that should be checked against the comparison component.",
"type": "string"
},
"json": {
"title": "Json",
"description": "The JSON Logic to determine if this component is conditionally available.",
"type": "string"
}
}
};
// tslint:disable:object-literal-key-quotes quotemark semicolon
var showStrict = {
"title": "Show",
"description": "If the field should show if the condition is true.",
"type": ["boolean", "null"]
};
// tslint:disable:object-literal-key-quotes quotemark semicolon
var showLoose = {
"title": "Show",
"description": "If the field should show if the condition is true.",
"enum": [true, false, "true", "false", "", null]
};
// tslint:disable:object-literal-key-quotes quotemark semicolon
var columns = {
"required": ["columns"],
"not": { "required": ["components"] },
"properties": {
"columns": {
"type": "array",
"items": {
"required": ["components"],
"type": "object",
"properties": {
"components": {
"$ref": "components"
}
}
}
}
}
};
// tslint:disable:object-literal-key-quotes quotemark semicolon
var table = {
"required": ["rows", "numRows", "numCols"],
"not": { "required": ["components"] },
"properties": {
"numRows": { "type": "integer" },
"numCols": { "type": "integer" },
"rows": {
"type": "array",
"items": {
"type": "array",
"items": {
"required": ["components"],
"type": "object",
"properties": {
"components": {
"$ref": "components"
}
}
}
}
}
}
};
// tslint:disable:object-literal-key-quotes quotemark semicolon
var tabs = {
"required": ["components"],
"properties": {
"components": {
"type": "array",
"items": {
"type": "object",
"required": ["key", "components"],
"not": { "required": ["type"] },
"properties": {
"key": {
"title": "Component Key",
"description": "The API key for this component",
"type": "string"
},
"label": {
"title": "Component Label",
"description": "The HTML label to give this component",
"type": "string"
},
"components": {
"$ref": "components"
}
}
}
}
}
};
const strict = {
schema,
schemaRefs: {
columns,
components,
component: componentStrict,
logic,
trigger,
action,
conditional,
show: showStrict,
table,
tabs
}
};
const loose = {
schema,
schemaRefs: Object.assign({}, strict.schemaRefs, {
component: componentLoose,
show: showLoose
})
};
const defaultJsonEditorOptions = {
enableSort: true,
enableTransform: true,
escapeUnicode: false,
expandAll: false,
history: true,
indentation: 2,
limitDragging: false,
mode: 'view',
modes: ['code', 'tree', 'view'],
schema: loose.schema,
schemaRefs: loose.schemaRefs,
search: true,
sortObjectKeys: false
};
const defaultRendererResourceJsonEditorOptions = merge(defaultJsonEditorOptions, {
mode: 'tree',
modes: ['code', 'tree'],
schema: undefined,
schemaRefs: undefined
});
const defaultRendererSchemaJsonEditorOptions = clone(defaultRendererResourceJsonEditorOptions);
let FormioEditorComponent = class FormioEditorComponent {
constructor(modalService) {
this.modalService = modalService;
this.builderDisplayChanged = false;
this.jsonEditorChanged = false;
// tslint:disable-next-line:variable-name
this._jsonEditorErrors = [];
this.jsonEditorWarningCounter = 0;
}
get options() { return this._options; }
set options(options) { this.setOptions(options); }
get activeTab() { return this._activeTab; }
set activeTab(tab) {
this._activeTab = tab;
if (tab === 'renderer') {
this.submissionPanel = false; // Disable submission panel when the renderer tab becomes active
}
}
get jsonEditorErrors() {
return this._jsonEditorErrors;
}
set jsonEditorErrors(errors) {
this._jsonEditorErrors = errors;
this.jsonEditorWarningCounter = 0;
errors.forEach((error) => {
if (error.type === 'validation') {
this.jsonEditorWarningCounter++;
}
});
}
ngOnInit() {
if (!this.options) {
this.setOptions(); // Set default options
}
this.activeTab = ['builder', 'json', 'renderer']
.find(t => { var _a; return this.options && ((_a = this.options[t]) === null || _a === void 0 ? void 0 : _a.defaultTab) ? t : undefined; }) || 'builder';
if (this.reset) {
this.resetSubscription = this.reset.subscribe(() => this.resetFormBuilder());
}
}
ngAfterViewInit() {
this.refreshJsonEditor();
}
ngOnDestroy() {
if (this.resetSubscription) {
this.resetSubscription.unsubscribe();
}
}
setOptions(options = {}) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
this._options = options;
this.jsonEditorOptions = merge(defaultJsonEditorOptions, (_b = (_a = options === null || options === void 0 ? void 0 : options.json) === null || _a === void 0 ? void 0 : _a.input) === null || _b === void 0 ? void 0 : _b.options);
this.rendererResourceJsonEditorOptions = merge(defaultRendererResourceJsonEditorOptions, (_f = (_e = (_d = (_c = options === null || options === void 0 ? void 0 : options.renderer) === null || _c === void 0 ? void 0 : _c.submissionPanel) === null || _d === void 0 ? void 0 : _d.resourceJsonEditor) === null || _e === void 0 ? void 0 : _e.input) === null || _f === void 0 ? void 0 : _f.options);
this.rendererSchemaJsonEditorOptions = merge(defaultRendererSchemaJsonEditorOptions, (_k = (_j = (_h = (_g = options === null || options === void 0 ? void 0 : options.renderer) === null || _g === void 0 ? void 0 : _g.submissionPanel) === null || _h === void 0 ? void 0 : _h.schemaJsonEditor) === null || _j === void 0 ? void 0 : _j.input) === null || _k === void 0 ? void 0 : _k.options);
this.fullSubmission = (_m = (_l = options === null || options === void 0 ? void 0 : options.renderer) === null || _l === void 0 ? void 0 : _l.submissionPanel) === null || _m === void 0 ? void 0 : _m.fullSubmission;
}
//
// Form Builder Tab
//
resetFormBuilder(fromJsonEditor = false) {
console.log('resetFormBuilder');
// Here we have to reset builder component through *ngIf="!builderDisplayChanged"
// See https://github.com/formio/angular-formio/issues/172#issuecomment-401876490
this.builderDisplayChanged = true;
setTimeout(() => {
this.builderDisplayChanged = false;
if (!fromJsonEditor) {
this.refreshJsonEditor(true);
}
this.resetFormRendererIfActive();
});
}
onBuilderDiplayChange(event) {
console.log('onBuilderDiplayChange');
this.resetFormBuilder();
}
onBuilderChange(event) {
console.log('onBuilderChange: event', event);
this.refreshJsonEditor(true);
}
//
// JSON Tab
//
onJsonEditorError(errors) {
console.log('onJsonEditorError: found', errors.length, 'validation errors:');
this.jsonEditorErrors = errors;
}
onJsonEditorChange(event) {
console.log('onJsonEditorChange');
this.jsonEditorChanged = true;
}
onJsonEditorApply(template) {
console.log('Errors: ', this.jsonEditorErrors.length - this.jsonEditorWarningCounter, 'Warnings: ', this.jsonEditorWarningCounter);
if (this.jsonEditorWarningCounter === 0) {
this.jsonEditorApplyChanges();
}
else {
this.modalRef = this.modalService.show(template);
}
}
jsonEditorApplyChanges() {
console.log('jsonEditorApplyChanges');
this.jsonEditorChanged = false;
// Remove all properties from this form
// then copy the properties of the edited json to this form
// and reset the builder
Object.getOwnPropertyNames(this.form).forEach(p => delete this.form[p]);
Object.assign(this.form, this.jsonEditor.get());
this.resetFormBuilder(true);
}
jsonEditorDiscardChanges() {
console.log('jsonEditorDiscardChanges');
this.refreshJsonEditor();
}
refreshJsonEditor(forceReset = false) {
console.log('refreshJsonEditor');
if (forceReset) {
this.jsonEditor.reset(true);
}
// Here we use update instead of set to preserve the editor status
this.jsonEditor.update(this.form);
this.jsonEditorChanged = false;
}
//
// Form Renderer Tab
//
resetFormRendererIfActive() {
console.log('resetFormRenderer');
// Here we recreate the renderer component through *ngIf="activeTab='renderer'"
// by changing the active tab and then restoring it.
// Although this is a rather dirty hack it is hardly noticeable to the eye :)
if (this.activeTab === 'renderer') {
this.activeTab = 'builder';
setTimeout(() => { this.activeTab = 'renderer'; });
}
}
showSubmissionPanel(submission) {
var _a, _b;
this.submissionPanel = !((_b = (_a = this.options.renderer) === null || _a === void 0 ? void 0 : _a.submissionPanel) === null || _b === void 0 ? void 0 : _b.disabled);
if (this.submissionPanel) {
if (submission) {
t