formiojs
Version:
Common js library for client side interaction with <form.io>
537 lines (450 loc) • 18.8 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };
var _Webform = require('./Webform');
var _Webform2 = _interopRequireDefault(_Webform);
var _Component2 = require('./components/_classes/component/Component');
var _Component3 = _interopRequireDefault(_Component2);
var _dragula = require('dragula');
var _dragula2 = _interopRequireDefault(_dragula);
var _Components = require('./components/Components');
var _Components2 = _interopRequireDefault(_Components);
var _builder = require('./utils/builder');
var _builder2 = _interopRequireDefault(_builder);
var _utils = require('./utils/utils');
var _nativePromiseOnly = require('native-promise-only');
var _nativePromiseOnly2 = _interopRequireDefault(_nativePromiseOnly);
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /* global $ */
require('./components/builder');
var WebformBuilder = function (_Component) {
_inherits(WebformBuilder, _Component);
function WebformBuilder(options) {
_classCallCheck(this, WebformBuilder);
var _this = _possibleConstructorReturn(this, (WebformBuilder.__proto__ || Object.getPrototypeOf(WebformBuilder)).call(this, options));
_this.schemas = {};
_this.sideBarScroll = _lodash2.default.get(_this.options, 'sideBarScroll', true);
_this.sideBarScrollOffset = _lodash2.default.get(_this.options, 'sideBarScrollOffset', 0);
// Setup the builder options.
_this.builder = _lodash2.default.defaultsDeep({}, _this.options.builder, _this.defaultGroups);
// Turn off if explicitely said to do so...
_lodash2.default.each(_this.defaultGroups, function (config, key) {
if (config === false) {
_this.options.builder[key] = false;
}
});
// Add the groups.
_this.groups = {};
_this.groupOrder = [];
for (var group in _this.builder) {
if (_this.builder[group]) {
_this.builder[group].key = group;
_this.groups[group] = _this.builder[group];
_this.groupOrder.push(_this.builder[group]);
}
}
_this.groupOrder = _this.groupOrder.sort(function (a, b) {
return a.weight - b.weight;
}).map(function (group) {
return group.key;
});
for (var type in _Components2.default.components) {
var component = _Components2.default.components[type];
if (component.builderInfo) {
_this.schemas[type] = component.builderInfo.schema;
component.type = type;
var builderInfo = component.builderInfo;
builderInfo.key = component.type;
_this.addBuilderComponentInfo(builderInfo);
}
}
// Need to create a component order for each group.
for (var _group in _this.groups) {
if (_this.groups[_group] && _this.groups[_group].components) {
_this.groups[_group].componentOrder = _this.groups[_group].componentOrder.sort(function (a, b) {
return a.weight - b.weight;
}).map(function (component) {
return component.key;
});
}
}
options.hooks = options.hooks || {};
options.hooks.renderComponents = function (html, _ref) {
var components = _ref.components,
self = _ref.self;
if (!components || !components.length && !components.nodrop || self.type === 'form' && components.length <= 1) {
html = _this.renderTemplate('builderPlaceholder', {}) + html;
}
return _this.renderTemplate('builderComponents', {
html: html
});
};
options.hooks.attachComponents = function (element, components, container, component) {
// Attach container and component to element for later reference.
var containerElement = element.querySelector('[ref="container"]');
containerElement.formioContainer = container;
containerElement.formioComponent = component;
// Add container to draggable list.
_this.dragula.containers.push(containerElement);
// Since we added a wrapper, need to return the original element so that we can find the components inside it.
return element.children[0];
};
options.hooks.renderComponent = function (html, _ref2) {
var self = _ref2.self;
if (self.type === 'form') {
return html;
}
return _this.renderTemplate('builderComponent', {
html: html
});
};
options.hooks.attachComponent = function (element, component) {
// Add component to element for later reference.
element.formioComponent = component;
component.loadRefs(element, {
removeComponent: 'single',
editComponent: 'single'
});
var parent = _this.getParentElement(element);
component.addEventListener(component.refs.editComponent, 'click', function () {
return _this.editComponent(component.component, parent);
});
component.addEventListener(component.refs.removeComponent, 'click', function () {
return _this.removeComponent(component.component, parent);
});
return element;
};
_this.webform = new _Webform2.default(options);
return _this;
}
_createClass(WebformBuilder, [{
key: 'render',
value: function render() {
return this.renderTemplate('builder', {
sidebar: this.renderTemplate('builderSidebar', {
groupOrder: this.groupOrder,
groups: this.groups
}),
form: this.webform.render()
});
}
}, {
key: 'attach',
value: function attach(element) {
var _this2 = this;
this.element = element;
this.loadRefs(element, {
form: 'single',
sidebar: 'single',
'container': 'multiple',
'sidebar-anchor': 'multiple',
'sidebar-group': 'multiple',
'sidebar-container': 'multiple'
});
this.sideBarTop = this.refs.sidebar.getBoundingClientRect().top + window.scrollY;
if (this.sideBarScroll) {
this.addEventListener(window, 'scroll', _lodash2.default.throttle(this.scrollSidebar.bind(this), 10));
}
// See if we have bootstrap.js installed.
var hasBootstrapJS = typeof $ === 'function' && typeof $().collapse === 'function';
if (!hasBootstrapJS) {
// Initialize
this.refs['sidebar-group'].forEach(function (group) {
group.style.display = group.getAttribute('data-default') === 'true' ? 'inherit' : 'none';
});
// Click event
this.refs['sidebar-anchor'].forEach(function (anchor, index) {
_this2.addEventListener(anchor, 'click', function () {
_this2.refs['sidebar-group'].forEach(function (group, groupIndex) {
group.style.display = groupIndex === index ? 'inherit' : 'none';
});
});
});
}
this.dragula = (0, _dragula2.default)(Array.prototype.slice.call(this.refs['sidebar-container']), {
moves: function moves(el) {
return !el.classList.contains('no-drag');
},
copy: function copy(el) {
return el.classList.contains('drag-copy');
},
accepts: function accepts(el, target) {
return !target.classList.contains('no-drop');
}
}).on('drop', function (element, target, source, sibling) {
return _this2.onDrop(element, target, source, sibling);
});
this.webform.attach(this.refs.form);
}
}, {
key: 'detach',
value: function detach() {
// if (this.dragula) {
// this.dragula.destroy();
// }
// this.dragula = null;
_get(WebformBuilder.prototype.__proto__ || Object.getPrototypeOf(WebformBuilder.prototype), 'detach', this).call(this);
}
}, {
key: 'onDrop',
value: function onDrop(element, target, source, sibling) {
if (!target) {
return;
}
var type = element.getAttribute('data-type');
var info = void 0,
isNew = void 0;
if (type) {
// This is a new component
info = this.schemas[type];
isNew = true;
} else {
// Grab and remove the component from the source container.
info = source.formioContainer.splice(_lodash2.default.findIndex(source.formioContainer, { key: element.formioComponent.component.key }), 1);
// Since splice returns an array of one object, we need to destructure it.
info = info[0];
// If the target is different from the source, rebuild the source now that the item has been removed.
if (target !== source) {
source.formioComponent.rebuild();
}
}
// Insert in the new container.
if (sibling) {
var index = 0;
if (!sibling.getAttribute('data-noattach')) {
index = _lodash2.default.findIndex(target.formioContainer, { key: sibling.formioComponent.component.key });
}
target.formioContainer.splice(index, 0, info);
} else {
target.formioContainer.push(info);
}
if (isNew) {
this.editComponent(info, target, isNew);
}
// Cause parent to rebuild so component becomes visible.
target.formioComponent.rebuild();
}
}, {
key: 'scrollSidebar',
value: function scrollSidebar() {
var newTop = window.scrollY - this.sideBarTop + this.sideBarScrollOffset;
var shouldScroll = newTop > 0;
if (shouldScroll && newTop + this.refs.sidebar.offsetHeight < this.element.offsetHeight) {
this.refs.sidebar.style.marginTop = newTop + 'px';
} else if (shouldScroll && this.refs.sidebar.offsetHeight < this.element.offsetHeight) {
this.refs.sidebar.style.marginTop = this.element.offsetHeight - this.refs.sidebar.offsetHeight + 'px';
} else {
this.refs.sidebar.style.marginTop = '0px';
}
}
}, {
key: 'removeComponent',
value: function removeComponent(component, parent) {
if (!parent) {
return;
}
var remove = true;
// if (component.type === 'components' && component.getComponents().length > 0) {
// const message = 'Removing this component will also remove all of its children. Are you sure you want to do this?';
// remove = window.confirm(this.t(message));
// }
if (remove) {
this.emit('removeComponent', component);
parent.formioContainer.splice(parent.formioContainer.indexOf(component), 1);
parent.formioComponent.rebuild();
}
return remove;
}
}, {
key: 'updateComponent',
value: function updateComponent(component, keyModified, isNew) {
// Update the preview.
if (this.preview) {
this.preview.form = { components: [component] };
this.componentEdit.querySelector('[ref="preview"]').innerHTML = this.preview.render();
}
// Ensure this component has a key.
if (isNew) {
if (!keyModified) {
component.key = _lodash2.default.camelCase(component.label || component.placeholder || component.type);
}
// Set a unique key for this component.
_builder2.default.uniquify(this._form, component);
}
// Change the "default value" field to be reflective of this component.
if (this.defaultValueComponent) {
_lodash2.default.assign(this.defaultValueComponent.component, _lodash2.default.omit(component, ['key', 'label', 'placeholder', 'tooltip', 'validate']));
}
// Called when we update a component.
this.emit('updateComponent', component);
}
}, {
key: 'editComponent',
value: function editComponent(component, parent, isNew) {
var _this3 = this;
var componentCopy = _lodash2.default.cloneDeep(component);
var componentClass = _Components2.default.components[componentCopy.type];
// Make sure we only have one dialog open at a time.
if (this.dialog) {
this.dialog.close();
}
// This is the render step.
this.editForm = new _Webform2.default(_lodash2.default.omit(this.options, ['hooks', 'builder', 'events']));
this.editForm.form = componentClass.editForm();
// Pass along the form being edited.
this.editForm.editForm = this._form;
this.editForm.submission = {
data: componentCopy
};
this.preview = new _Webform2.default(_lodash2.default.omit(this.options, ['hooks', 'builder', 'events']));
this.componentEdit = this.ce('div');
this.componentEdit.innerHTML = this.renderTemplate('builderEditForm', {
componentInfo: componentClass.builderInfo,
editForm: this.editForm.render(),
preview: this.preview.render()
});
this.dialog = this.createModal(this.componentEdit);
// This is the attach step.
var editForm = this.componentEdit.querySelector('[ref="editForm"]');
this.editForm.attach(editForm);
this.defaultValueComponent = (0, _utils.getComponent)(this.editForm.components, 'defaultValue');
this.updateComponent(componentCopy);
this.editForm.on('change', function (event) {
if (event.changed) {
_this3.updateComponent(event.data, event.data.key === component.key);
}
});
this.addEventListener(this.componentEdit.querySelector('[ref="cancelButton"]'), 'click', function (event) {
event.preventDefault();
_this3.editForm.detach();
_this3.emit('cancelComponent', component);
_this3.dialog.close();
});
this.addEventListener(this.componentEdit.querySelector('[ref="removeButton"]'), 'click', function (event) {
event.preventDefault();
_this3.editForm.detach();
_this3.removeComponent(component, parent);
_this3.dialog.close();
});
this.addEventListener(this.componentEdit.querySelector('[ref="saveButton"]'), 'click', function (event) {
event.preventDefault();
_this3.editForm.detach();
parent.formioContainer[parent.formioContainer.indexOf(component)] = _this3.editForm.submission.data;
parent.formioComponent.rebuild();
_this3.emit('saveComponent', component);
_this3.dialog.close();
});
this.addEventListener(this.dialog, 'close', function () {
_this3.editForm.detach();
if (isNew) {
// this.removeComponent(component);
}
});
// Called when we edit a component.
this.emit('editComponent', component);
}
}, {
key: 'getParentElement',
value: function getParentElement(element) {
var container = element;
do {
container = container.parentNode;
} while (container && !container.formioContainer);
return container;
}
}, {
key: 'addBuilderComponentInfo',
value: function addBuilderComponentInfo(component) {
if (!component || !component.group || !this.groups[component.group]) {
return;
}
component = _lodash2.default.clone(component);
var groupInfo = this.groups[component.group];
if (!groupInfo.components) {
groupInfo.components = {};
}
if (!groupInfo.componentOrder) {
groupInfo.componentOrder = [];
}
groupInfo.componentOrder.push(component);
if (!groupInfo.components.hasOwnProperty(component.key)) {
groupInfo.components[component.key] = component;
}
return component;
}
}, {
key: 'ready',
get: function get() {
return _nativePromiseOnly2.default.resolve(this);
}
}, {
key: 'defaultGroups',
get: function get() {
return {
basic: {
title: 'Basic Components',
weight: 0,
default: true
},
advanced: {
title: 'Advanced',
weight: 10
},
layout: {
title: 'Layout',
weight: 20
},
data: {
title: 'Data',
weight: 30
}
};
}
}, {
key: 'options',
get: function get() {
if (this.webform) {
return this.webform.options;
}
return {};
},
set: function set(value) {
if (this.webform) {
return this.webform.options = value;
}
}
}, {
key: 'form',
get: function get() {
return this.webform.form;
},
set: function set(value) {
if (!value.components) {
value.components = [];
}
// Ensure there is at least a submit button.
if (!value.components.length) {
value.components.push({
type: 'button',
label: 'Submit',
key: 'submit',
size: 'md',
block: false,
action: 'submit',
disableOnInvalid: true,
theme: 'primary'
});
}
this.webform.form = value;
}
}]);
return WebformBuilder;
}(_Component3.default);
exports.default = WebformBuilder;