UNPKG

formiojs

Version:

Common js library for client side interaction with <form.io>

537 lines (450 loc) • 18.8 kB
'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;