From c0148b33dc7154e2bc2a241255b7a2db6257534f Mon Sep 17 00:00:00 2001 From: Kevin Chappell Date: Sat, 25 Jan 2020 18:14:20 -0800 Subject: [PATCH] feat: getCurrentFieldId fix: typeUserAttr checkbox behavior, resolves #571 --- package-lock.json | 6 - package.json | 2 - src/demo/index.html | 169 ++++++++++++++++++---------- src/demo/js/actionButtons.js | 81 +++++++++++-- src/demo/js/demo.js | 55 ++++++--- src/demo/sass/demo.scss | 36 ++++++ src/js/config.js | 16 ++- src/js/control.js | 16 ++- src/js/control/autocomplete.js | 1 + src/js/control/file.fineuploader.js | 157 ++++++++++++++------------ src/js/control/textarea.quill.js | 1 + src/js/control/textarea.tinymce.js | 1 + src/js/form-builder.js | 34 ++++-- src/js/form-render.js | 4 +- src/js/helpers.js | 5 + src/js/layout.js | 2 +- src/js/utils.js | 4 +- 17 files changed, 402 insertions(+), 188 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0f6f7bc92..b2fc33522 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4744,12 +4744,6 @@ } } }, - "eslint-config-google": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/eslint-config-google/-/eslint-config-google-0.12.0.tgz", - "integrity": "sha512-SHDM3nIRCJBACjf8c/H6FvCwRmKbphESNl3gJFBNbw4KYDLCONB3ABYLXDGF+iaVP9XSTND/Q5/PuGoFkp4xbg==", - "dev": true - }, "eslint-config-prettier": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-4.3.0.tgz", diff --git a/package.json b/package.json index b9645059a..856c3efda 100755 --- a/package.json +++ b/package.json @@ -119,7 +119,6 @@ "cross-env": "^5.2.0", "css-loader": "^2.1.1", "eslint": "^5.16.0", - "eslint-config-google": "^0.12.0", "eslint-config-prettier": "^4.2.0", "eslint-loader": "^2.1.2", "eslint-plugin-prettier": "^3.0.1", @@ -158,7 +157,6 @@ "parser": "babel-eslint", "extends": [ "eslint:recommended", - "google", "prettier" ], "plugins": [ diff --git a/src/demo/index.html b/src/demo/index.html index 6995e87a2..7ed5734ee 100644 --- a/src/demo/index.html +++ b/src/demo/index.html @@ -1,65 +1,118 @@ + + - - + + jQuery formBuilder/formRender Demo + - - jQuery formBuilder/formRender Demo - - - - | - dataType: -
-

jQuery formBuilder - - -

-

jQuery formRender - - -

-
-
-
-

Actions

- - - - - - - - - - - - - - -

i18n

- -
-
-

Actions

- - - - - - + +
+ +
+ dataType: + +
+
+ Language: + +
+
+
+

+ jQuery formBuilder - + +

+

+ jQuery formRender - + +

+
+
+
+

Actions

+
+
+ + +
+
+ + +
+
+ + +
+
+
+

Actions

+ + + + + + +
-
-
- - - - - - - +
+ + + + + + diff --git a/src/demo/js/actionButtons.js b/src/demo/js/actionButtons.js index c76c0a5e5..340983c57 100644 --- a/src/demo/js/actionButtons.js +++ b/src/demo/js/actionButtons.js @@ -1,7 +1,4 @@ -const setFormData = - '[{"type":"text","label":"Full Name","subtype":"text","className":"form-control","name":"text-1476748004559"},{"type":"select","label":"Occupation","className":"form-control","name":"select-1476748006618","values":[{"label":"Street Sweeper","value":"option-1","selected":true},{"label":"Moth Man","value":"option-2"},{"label":"Chemist","value":"option-3"}]},{"type":"textarea","label":"Short Bio","rows":"5","className":"form-control","name":"textarea-1476748007461"}]' - -const currentFieldId = document.getElementById('currentFieldId') +import { titleCase } from './utils' export const builderActions = { showData: () => $('.build-wrap').formBuilder('showData'), @@ -10,7 +7,9 @@ export const builderActions = { console.log($('.build-wrap').formBuilder('getData')) }, setData: () => { - $('.build-wrap').formBuilder('setData', setFormData) + const { value } = document.getElementById('set-form-data-value') + window.sessionStorage.setItem('formData', value) + $('.build-wrap').formBuilder('setData', value) }, addField: () => { const field = { @@ -21,8 +20,8 @@ export const builderActions = { $('.build-wrap').formBuilder('addField', field) }, removeField: () => { - const fieldId = currentFieldId.value - $('.build-wrap').formBuilder('removeField', fieldId) + const currentFieldId = $('.build-wrap').formBuilder('getCurrentFieldId') + $('.build-wrap').formBuilder('removeField', currentFieldId) }, getXML: () => { alert($('.build-wrap').formBuilder('getData', 'xml')) @@ -35,7 +34,8 @@ export const builderActions = { console.log($('.build-wrap').formBuilder('getData')) }, toggleEdit: () => { - $('.build-wrap').formBuilder('toggleFieldEdit', currentFieldId.value) + const currentFieldId = $('.build-wrap').formBuilder('getCurrentFieldId') + $('.build-wrap').formBuilder('toggleFieldEdit', currentFieldId) }, toggleAllEdit: () => $('.build-wrap').formBuilder('toggleAllFieldEdit'), getFieldTypes: () => console.log($('.build-wrap').formBuilder('getFieldTypes')), @@ -87,3 +87,68 @@ export const demoActions = { location.reload() }, } + +const processCell = cellData => { + let cell = cellData + if (typeof cell === 'string') { + cell = { attrs: { scope: 'col' }, content: titleCase(cellData) } + } + + if (typeof cell.content === 'string') { + cell.content = document.createTextNode(cell.content) + } + + return { attrs: {}, ...cell } +} + +const generateTr = (columns, isHeader = false) => + columns.reduce((acc, cur) => { + const column = processCell(cur) + const type = isHeader ? 'th' : 'td' + const td = document.createElement(type) + td.appendChild(column.content) + Object.entries(column.attrs).forEach(([key, val]) => { + td.setAttribute(key, val) + }) + acc.appendChild(td) + return acc + }, document.createElement('tr')) + +const apiBtns = { + ...builderActions, + ...renderActions, + ...demoActions, +} + +export const generateActionTable = (actions, columns) => { + const fragment = document.createDocumentFragment() + const thead = document.createElement('thead') + thead.appendChild(generateTr(columns, true)) + const actionApiRows = Object.entries(actions).reduce((acc, [key, content]) => { + const description = { content } + const code = document.createElement('code') + code.appendChild(document.createTextNode(key)) + const action = { content: code } + let actionDemoTrigger = document.getElementById(key) + if (!actionDemoTrigger) { + actionDemoTrigger = document.createElement('button') + actionDemoTrigger.id = key + actionDemoTrigger.textContent = titleCase(key) + actionDemoTrigger.addEventListener('click', e => apiBtns[key] && apiBtns[key](e)) + } else { + const trigger = actionDemoTrigger.querySelector('.trigger') + if (trigger) { + trigger.addEventListener('click', e => apiBtns[key] && apiBtns[key](e)) + } + } + const demo = { content: actionDemoTrigger } + acc.appendChild(generateTr([action, description, demo])) + return acc + }, document.createDocumentFragment()) + const tbody = document.createElement('tbody') + tbody.appendChild(actionApiRows) + + fragment.appendChild(thead) + fragment.appendChild(tbody) + return fragment +} diff --git a/src/demo/js/demo.js b/src/demo/js/demo.js index f6e237059..350d065d0 100644 --- a/src/demo/js/demo.js +++ b/src/demo/js/demo.js @@ -1,6 +1,6 @@ import '../sass/demo.scss' import { insertStyle, removeStyle } from '../../js/utils' -import { builderActions, renderActions, demoActions } from './actionButtons' +import { demoActions, generateActionTable } from './actionButtons' const localeSessionKey = 'formBuilder-locale' const defaultLocale = 'en-US' @@ -186,6 +186,13 @@ jQuery(function($) { max: 11, }, }, + 'checkbox-group': { + randomize: { + label: 'Randomize', + type: 'checkbox', + value: false, + }, + }, } // test disabledAttrs @@ -204,7 +211,10 @@ jQuery(function($) { }, onSave: toggleEdit, onAddField: fieldId => { - document.getElementById('currentFieldId').value = fieldId + const currentFieldIds = document.querySelectorAll('.current-field-id') + currentFieldIds.forEach(field => { + field.value = fieldId + }) }, onClearAll: () => window.sessionStorage.removeItem('formData'), stickyControls: { @@ -264,25 +274,34 @@ jQuery(function($) { const fbPromise = formBuilder.promise fbPromise.then(function(fb) { - const apiBtns = { - ...builderActions, - ...renderActions, - ...demoActions, - } - - Object.keys(apiBtns).forEach(function(action) { - document.getElementById(action).addEventListener('click', function(e) { - apiBtns[action]() - }) - }) - document.querySelectorAll('.editForm').forEach(element => element.addEventListener('click', toggleEdit), false) const langSelect = document.getElementById('setLanguage') - const savedLocale = window.sessionStorage.getItem(localeSessionKey) + const savedLocale = window.sessionStorage.getItem(localeSessionKey) || defaultLocale + + langSelect.value = savedLocale + fb.actions.setLang(savedLocale) + + const columns = ['action', 'description', 'demo'] + const actions = { + getFieldTypes: 'Get the registered field types for the form.', + showData: 'Trigger a modal to appear that shows the current formData value', + clearFields: 'Removes all the fields from the template editor', + getData: 'Read the current formData', + getXML: 'Get the current formData in XML format', + getJSON: 'Get the current formData in JSON format', + getJS: 'Get the current formData in JS object format', + setData: 'set the current formData value for the editor', + toggleAllEdit: 'toggle the edit mode for all fields', + toggleEdit: 'toggle a specific field edit mode by index or id', + addField: 'programmatically add a field to the template editor', + removeField: 'remove a field by its index or id from the editor stage', + resetDemo: 'reset the demo to default state', + } - if (savedLocale && savedLocale !== defaultLocale) { - langSelect.value = savedLocale - fb.actions.setLang(savedLocale) + const actionApi = document.getElementById('action-api') + actionApi.appendChild(generateActionTable(actions, columns)) + if (formData && formData !== '[]') { + document.getElementById('set-form-data-value').value = window.JSON.stringify(JSON.parse(formData), null, ' ') } langSelect.addEventListener( diff --git a/src/demo/sass/demo.scss b/src/demo/sass/demo.scss index 04b7a80ea..34221aaaa 100644 --- a/src/demo/sass/demo.scss +++ b/src/demo/sass/demo.scss @@ -26,3 +26,39 @@ body { .form-rendered .formrender-actions { display: block; } +.api-table { + width: 100%; + border-collapse: collapse; + td { + border: 1px solid #aaa; + padding: 16px; + &:last-child { + text-align: center; + } + } + thead { + background-color: #fff; + } + tbody { + background-color: #fff; + tr { + &:nth-child(odd) { + background-color: #ddd; + } + } + } + textarea { + width: 100%; + height: 24px; + transition: height 333ms ease-in-out; + &:focus { + height: 160px; + } + } +} + +.demo-header { + display: flex; + align-items: center; + justify-content: space-between; +} diff --git a/src/js/config.js b/src/js/config.js index bcd59445f..96bc5bca9 100644 --- a/src/js/config.js +++ b/src/js/config.js @@ -1,5 +1,7 @@ import mi18n from 'mi18n' +const noop = () => null + // eslint-disable-next-line mi18n.addLanguage('en-US', FB_EN_US) @@ -43,11 +45,15 @@ export const defaultOptions = { success: console.log, warning: console.warn, }, - onAddField: (fieldData, fieldId) => fieldData, - onClearAll: () => null, - onCloseFieldEdit: () => null, - onOpenFieldEdit: () => null, - onSave: (evt, formData) => null, + onAddField: (fieldId, fieldData) => fieldData, + onClearAll: noop, + onCloseFieldEdit: noop, + onOpenFieldEdit: noop, + /** + * @param {Object} evt + * @param {Object} formData + */ + onSave: noop, prepend: false, replaceFields: [], roles: { diff --git a/src/js/control.js b/src/js/control.js index fc76c5f10..8931ed268 100755 --- a/src/js/control.js +++ b/src/js/control.js @@ -302,14 +302,20 @@ export default class control { on(eventType) { const events = { // executed just prior to the row being returned by the layout class. Receives the DOMelement about to be passed back - prerender: element => {}, - - // onRender event to execute code each time an instance of this control is injected into the DOM + /** + * @param {Node} element + */ + prerender: element => element, + + /** + * onRender event to execute code each time an instance of this control is injected into the DOM + * @param {Node} element + */ render: evt => { // check for a class render event - default to an empty function const onRender = () => { if (this.onRender) { - this.onRender() + this.onRender(evt) } } @@ -320,7 +326,7 @@ export default class control { if (this.js && !isCached(this.js)) { getScripts(this.js).done(onRender) } else { - onRender() + onRender(evt) } }, } diff --git a/src/js/control/autocomplete.js b/src/js/control/autocomplete.js index 3629cb96d..a696d5313 100755 --- a/src/js/control/autocomplete.js +++ b/src/js/control/autocomplete.js @@ -298,6 +298,7 @@ export default class controlAutocomplete extends control { this.hideList(list) } } + return evt } } diff --git a/src/js/control/file.fineuploader.js b/src/js/control/file.fineuploader.js index cc4784dc2..c2d3ea0fa 100755 --- a/src/js/control/file.fineuploader.js +++ b/src/js/control/file.fineuploader.js @@ -1,4 +1,4 @@ -import controlText from './text'; +import controlText from './text' /** * Fineuploader class - render the fineuploader tool (https://fineuploader.com) in place of the traditional file upload widget @@ -32,7 +32,6 @@ import controlText from './text'; * If you wish to define a custom template for the interface, this can be defined in controlConfig.file.template. It defaults to the gallery template provided by the Fineuploader project */ export default class controlFineUploader extends controlText { - /** * Class configuration - return the icons & label related to this control * @return {Object} definition object @@ -40,18 +39,21 @@ export default class controlFineUploader extends controlText { static get definition() { return { i18n: { - default: 'Fine Uploader' - } - }; + default: 'Fine Uploader', + }, + } } /** * configure the fineupload default settings & allow for controlConfig options */ configure() { - this.js = this.classConfig.js || '//cdnjs.cloudflare.com/ajax/libs/file-uploader/5.14.2/jquery.fine-uploader/jquery.fine-uploader.min.js'; + this.js = + this.classConfig.js || + '//cdnjs.cloudflare.com/ajax/libs/file-uploader/5.14.2/jquery.fine-uploader/jquery.fine-uploader.min.js' this.css = [ - this.classConfig.css || '//cdnjs.cloudflare.com/ajax/libs/file-uploader/5.14.2/jquery.fine-uploader/fine-uploader-gallery.min.css', + this.classConfig.css || + '//cdnjs.cloudflare.com/ajax/libs/file-uploader/5.14.2/jquery.fine-uploader/fine-uploader-gallery.min.css', { type: 'inline', id: 'fineuploader-inline', @@ -72,14 +74,16 @@ export default class controlFineUploader extends controlText { .qq-uploader .qq-error-message span { display: inline-block; text-align: left; - }` - } - ]; - this.handler = this.classConfig.handler || '/upload'; - ['js', 'css', 'handler'].forEach(key => delete this.classConfig[key]); + }`, + }, + ] + this.handler = this.classConfig.handler || '/upload' + ;['js', 'css', 'handler'].forEach(key => delete this.classConfig[key]) // fineuploader template that needs to be defined for the UI - const template = this.classConfig.template || ` + const template = + this.classConfig.template || + ` `; +
` this.fineTemplate = $('
') .attr('id', 'qq-template') - .html(template); + .html(template) } /** @@ -160,83 +164,90 @@ export default class controlFineUploader extends controlText { * @return {Object} DOM Element to be injected into the form. */ build() { - this.input = this.markup('input', null, {type: 'hidden', name: this.config.name, id: this.config.name}); - this.wrapper = this.markup('div', '', {id: this.config.name + '-wrapper'}); - return [this.input, this.wrapper]; + this.input = this.markup('input', null, { type: 'hidden', name: this.config.name, id: this.config.name }) + this.wrapper = this.markup('div', '', { id: this.config.name + '-wrapper' }) + return [this.input, this.wrapper] } /** * onRender callback */ onRender() { - const wrapper = $(this.wrapper); - const input = $(this.input); + const wrapper = $(this.wrapper) + const input = $(this.input) // we need to know where the server handler file located. I.e. where to we send the upload POST to? // to set this, define controlConfig.file.handler in the formbuilder options // defaults to '/upload' // deep copy merge in passed class configuration over any conflicting defaults - const config = jQuery.extend(true, { - request: { - endpoint: this.handler - }, - deleteFile: { - enabled: true, - endpoint: this.handler - }, - chunking: { - enabled: true, - concurrent: { - enabled: true + const config = jQuery.extend( + true, + { + request: { + endpoint: this.handler, }, - success: { - endpoint: this.handler + (this.handler.indexOf('?') == -1 ? '?' : '&') + 'done' - } - }, - resume: { - enabled: true - }, - retry: { - enableAuto: true, - showButton: true - }, - callbacks: { - onError: (id, name, errorReason, xhrOrXdr) => { - if (errorReason.slice(-1) != '.') { - errorReason += '.'; - } - const error = $('
') - .addClass('qq-error-message') - .html(`Error processing upload: ${name}.
Reason: ${errorReason}
`) - .prependTo(wrapper.find('.qq-uploader')); - setTimeout(() => { - error.fadeOut(() => { - error.remove(); - }); - }, 6000); + deleteFile: { + enabled: true, + endpoint: this.handler, + }, + chunking: { + enabled: true, + concurrent: { + enabled: true, + }, + success: { + endpoint: this.handler + (this.handler.indexOf('?') == -1 ? '?' : '&') + 'done', + }, + }, + resume: { + enabled: true, }, - onStatusChange: (id, oldStatus, newStatus) => { - const uploads = wrapper.fineUploader('getUploads'); + retry: { + enableAuto: true, + showButton: true, + }, + callbacks: { + onError: (id, name, errorReason) => { + if (errorReason.slice(-1) != '.') { + errorReason += '.' + } + const error = $('
') + .addClass('qq-error-message') + .html(`Error processing upload: ${name}.
Reason: ${errorReason}
`) + .prependTo(wrapper.find('.qq-uploader')) + const removeErrorTimeout = window.setTimeout(() => { + error.fadeOut(() => { + error.remove() + window.clearTimeout(removeErrorTimeout) + }) + }, 6000) + return id + }, + onStatusChange: (id, oldStatus, newStatus) => { + const uploads = wrapper.fineUploader('getUploads') - // retrieve an array of successfully uploaded filenames - const successful = []; - for (const upload of uploads) { - if (upload.status != 'upload successful') { - continue; + // retrieve an array of successfully uploaded filenames + const successful = [] + for (const upload of uploads) { + if (upload.status != 'upload successful') { + continue + } + successful.push(upload.name) } - successful.push(upload.name); - } - input.val(successful.join(', ')); - } + input.val(successful.join(', ')) + return { id, oldStatus, newStatus } + }, + }, + template: this.fineTemplate, }, - template: this.fineTemplate - }, this.classConfig); - wrapper.fineUploader(config); + this.classConfig, + ) + wrapper.fineUploader(config) } } // register fineuploader as a subtype to the 'file' type control (defined in text.js) // also register the default file uploader as a subtype too so it appears in the dropdown -controlText.register('file', controlText, 'file'); -controlText.register('fineuploader', controlFineUploader, 'file'); +controlText.register('file', controlText, 'file') +controlText.register('fineuploader', controlFineUploader, 'file') diff --git a/src/js/control/textarea.quill.js b/src/js/control/textarea.quill.js index 999d4e530..076a3961c 100755 --- a/src/js/control/textarea.quill.js +++ b/src/js/control/textarea.quill.js @@ -71,6 +71,7 @@ export default class controlQuill extends controlTextarea { editor.instance.on('text-change', function(delta) { editor.data = editor.data.compose(delta); }); + return evt } } diff --git a/src/js/control/textarea.tinymce.js b/src/js/control/textarea.tinymce.js index 472cbac0b..210fcdd42 100755 --- a/src/js/control/textarea.tinymce.js +++ b/src/js/control/textarea.tinymce.js @@ -85,6 +85,7 @@ export default class controlTinymce extends controlTextarea { if (this.config.userData) { window.tinymce.editors[this.id].setContent(this.parsedHtml(this.config.userData[0])) } + return evt } } diff --git a/src/js/form-builder.js b/src/js/form-builder.js index 8e10272e1..47d1e9307 100755 --- a/src/js/form-builder.js +++ b/src/js/form-builder.js @@ -47,7 +47,7 @@ const FormBuilder = function(opts, element, $) { data.layout = h.editorLayout(opts.controlPosition) h.editorUI(formID) data.formID = formID - data.lastID = `${data.formID}-fld-1` + data.lastID = `${data.formID}-fld-0` const controls = new Controls(opts, d) const subtypes = (config.subtypes = h.processSubtypes(opts.subtypes)) @@ -229,10 +229,10 @@ const FormBuilder = function(opts, element, $) { } if (isNew) { - field = Object.assign({}, field, opts.onAddField(data.lastID, field)) setTimeout(() => document.dispatchEvent(events.fieldAdded), 10) } + opts.onAddField(data.lastID, field) appendNewField(field, isNew) d.stage.classList.remove('empty') @@ -508,6 +508,7 @@ const FormBuilder = function(opts, element, $) { return ( [ ['array', ({ options }) => !!options], + ['boolean', ({ type }) => type === 'checkbox'], // automatic bool if checkbox [typeof attrData.value, () => true], // string, number, ].find(typeCondition => typeCondition[1](attrData))[0] || 'string' ) @@ -525,8 +526,15 @@ const FormBuilder = function(opts, element, $) { array: selectUserAttrs, string: inputUserAttrs, number: numberAttribute, - boolean: (attr, attrData) => - boolAttribute(attr, { ...attrData, [attr]: values[attr] }, { first: attrData.label }), + boolean: (attr, attrData) => { + let isChecked = false + if (values.hasOwnProperty(attr)) { + isChecked = values[attr] + } else if (attrData.hasOwnProperty('value') || attrData.hasOwnProperty('value')) { + isChecked = attrData.value || attrData.checked || false + } + return boolAttribute(attr, { ...attrData, [attr]: isChecked }, { first: attrData.label }) + }, } for (const attribute in typeUserAttr) { @@ -845,6 +853,8 @@ const FormBuilder = function(opts, element, $) { // Append the new field to the editor const appendNewField = function(values, isNew = true) { + data.lastID = h.incrementId(data.lastID) + const type = values.type || 'text' const label = values.label || (isNew ? i18n.get(type) || mi18n.get('label') : '') const disabledFieldButtons = opts.disabledFieldButtons[type] || values.disabledFieldButtons @@ -946,8 +956,6 @@ const FormBuilder = function(opts, element, $) { field.scrollIntoView({ behavior: 'smooth' }) } } - - data.lastID = h.incrementId(data.lastID) } // Select field html, since there may be multiple @@ -1367,6 +1375,9 @@ const FormBuilder = function(opts, element, $) { }) }, closeAllFieldEdit: h.closeAllEdit.bind(h), + getCurrentFieldId: () => { + return data.lastID + }, } // set min-height on stage onRender @@ -1403,6 +1414,7 @@ const methods = { showData: null, toggleAllFieldEdit: null, toggleFieldEdit: null, + getCurrentFieldId: null, }, get formData() { return methods.instance.actions.getData && methods.instance.actions.getData('json') @@ -1432,8 +1444,14 @@ const methods = { } jQuery.fn.formBuilder = function(methodOrOptions = {}, ...args) { - if (methods[methodOrOptions]) { - return methods[methodOrOptions].apply(this, args) + const isMethod = typeof methodOrOptions === 'string' + if (isMethod) { + if (methods[methodOrOptions]) { + if (typeof methods[methodOrOptions] === 'function') { + return methods[methodOrOptions].apply(this, args) + } + return methods[methodOrOptions] + } } else { const instance = methods.init(methodOrOptions, this) Object.assign(methods, instance) diff --git a/src/js/form-render.js b/src/js/form-render.js index 4372d1efd..a43d77d20 100755 --- a/src/js/form-render.js +++ b/src/js/form-render.js @@ -261,7 +261,7 @@ class FormRender { .filter(fieldData => fieldData.subtype === 'tinymce') .forEach(fieldData => window.tinymce.get(fieldData.name).save()) - this.instanceContainers.forEach((container, index) => { + this.instanceContainers.forEach((container) => { const userDataMap = $('select, input, textarea', container) .serializeArray() .reduce((acc, { name, value }) => { @@ -324,7 +324,7 @@ class FormRender { } } -;(function($) { +;(function() { let formRenderForms const methods = { init: (forms, options = {}) => { diff --git a/src/js/helpers.js b/src/js/helpers.js index 28b71b798..cbf815ce1 100755 --- a/src/js/helpers.js +++ b/src/js/helpers.js @@ -743,9 +743,13 @@ export default class Helpers { * Toggles the edit mode for the given field * @param {String} fieldId * @param {Boolean} animate + * @return {Node|null} field */ toggleEdit(fieldId, animate = true) { const field = document.getElementById(fieldId) + if (!field) { + return field + } const $editPanel = $('.frm-holder', field) const $preview = $('.prev-holder', field) field.classList.toggle('editing') @@ -766,6 +770,7 @@ export default class Helpers { config.opts.onCloseFieldEdit($editPanel[0]) document.dispatchEvent(events.fieldEditClosed) } + return field } /** diff --git a/src/js/layout.js b/src/js/layout.js index 8cf3afb5c..4b10b2655 100755 --- a/src/js/layout.js +++ b/src/js/layout.js @@ -58,7 +58,7 @@ export default class layout { className: processClassName(data, field) }) }, - hidden: (field, label, help, data) => { + hidden: (field) => { // no wrapper any any visible elements return field } diff --git a/src/js/utils.js b/src/js/utils.js index a2688a89e..a10038755 100755 --- a/src/js/utils.js +++ b/src/js/utils.js @@ -18,7 +18,7 @@ window.fbEditors = { * @return {Object} Object trimmed of null or undefined values */ export const trimObj = function(attrs) { - const xmlRemove = [null, undefined, '', false, 'false'] + const xmlRemove = [null, undefined, ''] for (const attr in attrs) { if (xmlRemove.includes(attrs[attr])) { delete attrs[attr] @@ -575,7 +575,7 @@ export const insertStyle = srcs => { srcs = Array.isArray(srcs) ? srcs : [srcs] const promises = srcs.map( ({ src, id }) => - new Promise((resolve, reject) => { + new Promise((resolve) => { if (window.fbLoaded.css.includes(src)) { return resolve(src) }