diff --git a/CHANGELOG.md b/CHANGELOG.md index 547321e14..aa666ca58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ +## [3.19.3](https://github.com/kevinchappell/formBuilder/compare/v3.19.2...v3.19.3) (2024-03-01) + + +### Bug Fixes + +* ensure that config is per instance of formBuilder ([c44dbc4](https://github.com/kevinchappell/formBuilder/commit/c44dbc46fd757cffeef96c3b7752d5749d712237)) +* selecting last field id when running in advanced bootstrap mode ([8153745](https://github.com/kevinchappell/formBuilder/commit/8153745f73e1dea2d599bdcfdf762930e2290a26)) + +## [3.19.2](https://github.com/kevinchappell/formBuilder/compare/v3.19.1...v3.19.2) (2024-03-01) + + +### Bug Fixes + +* provide the value for a number attribute in the tUA array ([76db433](https://github.com/kevinchappell/formBuilder/commit/76db4331a648a926fb963de2e90ca1a0eeaa22f5)) + ## [3.19.1](https://github.com/kevinchappell/formBuilder/compare/v3.19.0...v3.19.1) (2024-02-09) diff --git a/package-lock.json b/package-lock.json index 3f32e201f..5aa11a377 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "formBuilder", - "version": "3.19.1", + "version": "3.19.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "formBuilder", - "version": "3.19.1", + "version": "3.19.3", "license": "MIT", "dependencies": { "jquery": ">=3.4.1", diff --git a/package.json b/package.json index f540ce956..8e8292d80 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "formBuilder", - "version": "3.19.1", + "version": "3.19.3", "main": "dist/form-builder.min.js", "homepage": "https://formbuilder.online/", "repository": { diff --git a/src/js/config.js b/src/js/config.js index e65d728a1..63b8b93db 100644 --- a/src/js/config.js +++ b/src/js/config.js @@ -106,7 +106,7 @@ export const defaultI18n = { location: 'assets/lang/', } -export const config = {} +export const instanceConfig = {} export const gridClassNames = { rowWrapperClass: 'rowWrapper', diff --git a/src/js/form-builder.js b/src/js/form-builder.js index 599c433f5..a0eba09c8 100644 --- a/src/js/form-builder.js +++ b/src/js/form-builder.js @@ -10,7 +10,7 @@ import Helpers from './helpers' import { defaultOptions, defaultI18n, - config, + instanceConfig, styles, gridClassNames, defaultTimeout, @@ -33,7 +33,7 @@ import { safename, forceNumber, getContentType, - generateSelectorClassNames, + generateSelectorClassNames, firstNumberOrUndefined, } from './utils' import { attributeWillClobber, setElementContent, setSanitizerConfig } from './sanitizer' import fontConfig from '../fonts/config.json' @@ -55,6 +55,7 @@ function FormBuilder(opts, element, $) { const formID = `frmb-${Date.now()}` const data = new Data(formID) const d = new Dom(formID) + const config = instanceConfig[formID] = {} /** @var formRows Allocated rows IDs in the builder */ let formRows = [] @@ -701,8 +702,16 @@ function FormBuilder(opts, element, $) { if (attrValType !== 'undefined') { const orig = mi18n.get(attribute) const tUA = typeUserAttr[attribute] - const origValue = attrValType === 'boolean' ? tUA.value : (tUA.value || '') - tUA.value = values[attribute] || origValue + let origValue = tUA.value + if (attrValType === 'boolean') { + origValue = tUA.value + tUA[attribute] ??= tUA.value + } else if (attrValType === 'number') { + tUA[attribute] ??= firstNumberOrUndefined(values[attribute], origValue) + } else { + origValue ??= '' + tUA[attribute] ??= values[attribute] || origValue + } if (tUA.label) { i18n[attribute] = Array.isArray(tUA.label) ? mi18n.get(...tUA.label) || tUA.label[0] : tUA.label @@ -890,7 +899,7 @@ function FormBuilder(opts, element, $) { */ const numberAttribute = (attribute, values) => { const { class: classname, className, ...attrs } = values - const attrVal = (isNaN(attrs[attribute])) ? undefined : attrs[attribute] + const attrVal = (Number.isNaN(attrs[attribute])) ? undefined : attrs[attribute] const attrLabel = mi18n.get(attribute) || attribute const placeholder = mi18n.get(`placeholder.${attribute}`) @@ -2486,7 +2495,6 @@ function FormBuilder(opts, element, $) { const pluginInit = function(options,elem) { const _this = this const { i18n, ...opts } = jQuery.extend({}, defaultOptions, options, true) - config.opts = opts this.i18nOpts = jQuery.extend({}, defaultI18n, i18n, true) const notInitialised = () => { diff --git a/src/js/helpers.js b/src/js/helpers.js index 9a6f55afb..5228c23fd 100644 --- a/src/js/helpers.js +++ b/src/js/helpers.js @@ -18,7 +18,7 @@ import { getAllGridRelatedClasses, } from './utils' import events from './events' -import { config, defaultTimeout, styles } from './config' +import { instanceConfig, defaultTimeout, styles } from './config' import control from './control' import storageAvailable from 'storage-available' @@ -35,6 +35,7 @@ export default class Helpers { constructor(formId, layout, formBuilder) { this.data = instanceData[formId] this.d = instanceDom[formId] + this.config = instanceConfig[formId] this.doCancel = false this.layout = layout this.handleKeyDown = this.handleKeyDown.bind(this) @@ -82,7 +83,7 @@ export default class Helpers { */ beforeStop(event, ui) { const _this = this - const opts = config.opts + const opts = this.config.opts const form = _this.d.stage const lastIndex = form.childNodes.length - 1 const cancelArray = [] @@ -196,6 +197,7 @@ export default class Helpers { const formData = [] const d = this.d const _this = this + const config = this.config if (form.childNodes.length !== 0) { const fields = [] @@ -302,7 +304,7 @@ export default class Helpers { const data = this.data if (!formData) { - formData = config.opts.formData + formData = this.config.opts.formData } if (!formData) { @@ -319,7 +321,7 @@ export default class Helpers { }, } - data.formData = setData[config.opts.dataType](formData) || [] + data.formData = setData[this.config.opts.dataType](formData) || [] return data.formData } @@ -339,7 +341,7 @@ export default class Helpers { } // save action for current `dataType` - data.formData = doSave[config.opts.dataType](minify) + data.formData = doSave[this.config.opts.dataType](minify) // trigger formSaved event document.dispatchEvent(events.formSaved) @@ -365,6 +367,7 @@ export default class Helpers { * @return {Object} fieldData */ getAttrVals(field) { + const config = this.config const fieldData = Object.create(null) const attrs = field.querySelectorAll('[class*="fld-"]') forEach(attrs, index => { @@ -651,6 +654,7 @@ export default class Helpers { */ confirmRemoveAll(e) { const _this = this + const config = this.config const formID = e.target.id.match(/frmb-\d{13}/)[0] const stage = document.getElementById(formID) const i18n = mi18n.current @@ -683,7 +687,7 @@ export default class Helpers { addDefaultFields() { // Load default fields if none are set - config.opts.defaultFields.forEach(field => this.formBuilder.prepFieldVars(field)) + this.config.opts.defaultFields.forEach(field => this.formBuilder.prepFieldVars(field)) this.d.stage.classList.remove('empty') } @@ -694,7 +698,7 @@ export default class Helpers { */ removeAllFields(stage) { const i18n = mi18n.current - const opts = config.opts + const opts = this.config.opts const fields = stage.querySelectorAll(this.formBuilder.fieldSelector) const markEmptyArray = [] @@ -741,7 +745,7 @@ export default class Helpers { * @return {Array|false} fieldOrder */ setFieldOrder($cbUL) { - if (!config.opts.sortableControls) { + if (!this.config.opts.sortableControls) { return false } const JSON = window.JSON @@ -839,7 +843,7 @@ export default class Helpers { this.removeContainerProtection(rowContainer.attr('id')) - config.opts.onCloseFieldEdit($editPanel[0]) + this.config.opts.onCloseFieldEdit($editPanel[0]) document.dispatchEvent(events.fieldEditClosed) const prevHolder = liContainer.find('.prev-holder') @@ -909,7 +913,7 @@ export default class Helpers { liContainer.insertAfter(rowWrapper) this.formBuilder.currentEditPanel = $editPanel[0] - config.opts.onOpenFieldEdit($editPanel[0]) + this.config.opts.onOpenFieldEdit($editPanel[0]) document.dispatchEvent(events.fieldEditOpened) $(document).trigger('fieldOpened', [{ rowWrapperID: rowWrapper.attr('id') }]) @@ -939,10 +943,10 @@ export default class Helpers { * Open a dialog with the form's data */ showData() { - const formData = escapeHtml(this.getFormData(config.opts.dataType, true)) + const formData = escapeHtml(this.getFormData(this.config.opts.dataType, true)) const code = m('code', formData, { - className: `formData-${config.opts.dataType}`, + className: `formData-${this.config.opts.dataType}`, }) this.dialog(m('pre', code), false, 'data-dialog') @@ -961,7 +965,7 @@ export default class Helpers { const fields = form.getElementsByClassName('form-field') if (!fields.length) { - config.opts.notify.warning('No fields to remove') + this.config.opts.notify.warning('No fields to remove') return false } @@ -969,15 +973,15 @@ export default class Helpers { const availableIds = [].slice.call(fields).map(field => { return field.id }) - config.opts.notify.warning('fieldID required to remove specific fields.') - config.opts.notify.warning('Removing last field since no ID was supplied.') - config.opts.notify.warning('Available IDs: ' + availableIds.join(', ')) - fieldID = form.lastChild.id + this.config.opts.notify.warning('fieldID required to remove specific fields.') + this.config.opts.notify.warning('Removing last field since no ID was supplied.') + this.config.opts.notify.warning('Available IDs: ' + availableIds.join(', ')) + fieldID = availableIds[availableIds.length-1] } const field = document.getElementById(fieldID) if (!field) { - config.opts.notify.warning('Field not found') + this.config.opts.notify.warning('Field not found') return false } @@ -995,7 +999,7 @@ export default class Helpers { } }) - const userEvents = Object.assign({}, config.opts.typeUserEvents['*'], config.opts.typeUserEvents[field.type]) + const userEvents = Object.assign({}, this.config.opts.typeUserEvents['*'], this.config.opts.typeUserEvents[field.type]) if (userEvents && userEvents.onremove) { userEvents.onremove(field) @@ -1059,7 +1063,7 @@ export default class Helpers { * @return {Array} subtypes */ processSubtypes(subtypeOpts) { - const disabledSubtypes = config.opts.disabledSubtypes + const disabledSubtypes = this.config.opts.disabledSubtypes // first register any passed subtype options against the appropriate type control class for (const fieldType in subtypeOpts) { if (subtypeOpts.hasOwnProperty(fieldType)) { @@ -1136,7 +1140,7 @@ export default class Helpers { * @return {HTMLElement[]} formActions btn-group */ formActionButtons() { - const opts = config.opts + const opts = this.config.opts return opts.actionButtons .map(btnData => { if (btnData.id && opts.disabledActionButtons.indexOf(btnData.id) === -1) { @@ -1183,7 +1187,7 @@ export default class Helpers { events: { click: evt => { _this.save() - config.opts.onSave(evt, _this.data.formData) + _this.config.opts.onSave(evt, _this.data.formData) }, }, }, @@ -1196,8 +1200,8 @@ export default class Helpers { // html labels are not available using xml dataType opts.disableHTMLLabels = true } - config.opts = Object.assign({}, { actionButtons: mergedActionButtons }, { fieldEditContainer }, opts) - return config.opts + _this.config.opts = Object.assign({}, { actionButtons: mergedActionButtons }, { fieldEditContainer }, opts) + return _this.config.opts } /** diff --git a/src/js/utils.js b/src/js/utils.js index 20f19f31b..48ffa56cc 100644 --- a/src/js/utils.js +++ b/src/js/utils.js @@ -702,6 +702,10 @@ export function titleCase(str) { ) } +export function firstNumberOrUndefined(...options) { + return options.find(x => typeof x === 'number') +} + const utils = { addEventListeners, attrString, @@ -733,6 +737,7 @@ const utils = { unique, validAttr, titleCase, + firstNumberOrUndefined, } /** diff --git a/tests/form-builder.test.js b/tests/form-builder.test.js index af753bb83..a0addd853 100644 --- a/tests/form-builder.test.js +++ b/tests/form-builder.test.js @@ -199,6 +199,38 @@ describe('FormBuilder stage names translated', () => { }) }) +describe('FormBuilder attribute setup', () => { + test('number control number attributes do not inherit field value when not set', async() => { + const config = {} + const fbWrap = $('