Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Overhaul custom control setup #1454

Merged
merged 1 commit into from
Oct 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/js/control.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ export default class control {
/**
* Retrieve the class for a specified control type
* @param {String} type type of control we are looking up
* @param {String} subtype if specified we'll try to find
* @param {String} [subtype] if specified we'll try to find
* a class mapped to this subtype. If none found, fall back to the type.
* @return {Class} control subclass as defined in the call to register
*/
Expand Down
110 changes: 7 additions & 103 deletions src/js/control/custom.js
Original file line number Diff line number Diff line change
@@ -1,122 +1,27 @@
import control from '../control'
import mi18n from 'mi18n'

/**
* Support for custom controls
* Implementing support for custom templates being passed as options to formBuilder/Render
* @extends control
*/
export default class controlCustom extends control {
/**
* Override the register method to allow passing 'templates' configuration data
* @param {Object} templates an object/hash of template data as defined https://formbuilder.online/docs/formBuilder/options/templates/
* @param {Array} fields
*/
static register(templates = {}, fields = []) {
controlCustom.customRegister = {}

if (!controlCustom.def) {
controlCustom.def = {
icon: {},
i18n: {},
}
}

// store the template data against a static property
controlCustom.templates = templates

// prepare i18n locale definition
const locale = mi18n.locale
if (!controlCustom.def.i18n[locale]) {
controlCustom.def.i18n[locale] = {}
}

// register each defined template against this class
control.register(Object.keys(templates), controlCustom)

// build the control label & icon definitions
for (const field of fields) {
let type = field.type
field.attrs = field.attrs || {}
if (!type) {
if (!field.attrs.type) {
this.error('Ignoring invalid custom field definition. Please specify a type property.')
continue
}
type = field.attrs.type
}

// default icon & label lookup
let lookup = field.subtype || type

// if there is no template defined for this type, check if we already have this type/subtype registered
if (!templates[type]) {
// check that this type is already registered
const controlClass = control.getClass(type, field.subtype)
if (!controlClass) {
this.error(
'Error while registering custom field: ' +
type +
(field.subtype ? ':' + field.subtype : '') +
'. Unable to find any existing defined control or template for rendering.',
)
continue
}

// generate a random key & map the settings against it
lookup = field.datatype ? field.datatype : `${type}-${Math.floor(Math.random() * 9000 + 1000)}`

controlCustom.customRegister[lookup] = jQuery.extend(field, {
type: type,
class: controlClass,
})
}

// map label & icon
controlCustom.def.i18n[locale][lookup] = field.label
controlCustom.def.icon[lookup] = field.icon
}
}

/**
* Returns any custom fields that map to an existing type/subtype combination
* @param {string|false} type optional type of control we want to look up
* subtypes of. If not specified will return all types
* @return {Array} registered custom lookup keys
*/
static getRegistered(type = false) {
if (type) {
return control.getRegistered(type)
}
return Object.keys(controlCustom.customRegister)
}

/**
* Retrieve the class for a specified control type
* @param {string} lookup - custom control lookup to check for
* @return {Class} control subclass as defined in the call to register
*/
static lookup(lookup) {
return controlCustom.customRegister[lookup]
}

/**
* Class configuration - return the icons & label translations defined in register
* @return {object} definition object
*/
static get definition() {
return controlCustom.def
constructor(config, preview, template) {
super(config,preview)
this.template = template
}

/**
* build a custom control defined in the templates option
* @return {{field: any, layout: any}} DOM Element to be injected into the form.
*/
build() {
let custom = controlCustom.templates[this.type]
let custom = this.template
if (!custom) {
return this.error(
'Invalid custom control type. Please ensure you have registered it correctly as a template option.',
/* istanbul ignore next */
return control.error(
`Invalid custom control type '${this.type}'. Please ensure you have registered it correctly as a template option.`,
)
}

Expand Down Expand Up @@ -158,4 +63,3 @@ export default class controlCustom extends control {
}
}
}
controlCustom.customRegister = {}
24 changes: 15 additions & 9 deletions src/js/controls.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import './control/index'
import control from './control'
import controlCustom from './control/custom'
import customControls from './customControls'
import { unique, hyphenCase, markup as m } from './utils'
import { empty } from './dom'
import fontConfig from '../fonts/config.json'
Expand All @@ -20,8 +20,6 @@ export default class Controls {
constructor(opts, d) {
this.opts = opts
this.dom = d.controls
this.custom = controlCustom
this.getClass = control.getClass
this.getRegistered = control.getRegistered
// ability for controls to have their own configuration / options
// of the format control identifier (type, or type.subtype): {options}
Expand All @@ -46,14 +44,11 @@ export default class Controls {
// load in any custom specified controls, or preloaded plugin controls
control.loadCustom(opts.controls)
// register any passed custom templates & fields
if (Object.keys(opts.fields).length) {
controlCustom.register(opts.templates, opts.fields)
}
this.custom = new customControls(opts.templates, opts.fields)

// retrieve a full list of loaded controls
const registeredControls = control.getRegistered()
this.registeredControls = registeredControls
const customFields = controlCustom.getRegistered()
const customFields = this.custom.getRegistered()
if (customFields) {
jQuery.merge(registeredControls, customFields)
}
Expand All @@ -72,7 +67,7 @@ export default class Controls {
for (let i = 0; i < registeredControls.length; i++) {
const type = registeredControls[i]
// first check if this is a custom control
let custom = controlCustom.lookup(type)
let custom = this.custom.lookup(type)
let controlClass
if (custom) {
controlClass = custom.class
Expand Down Expand Up @@ -186,4 +181,15 @@ export default class Controls {
})
this.dom.appendChild(fragment)
}

/**
* Retrieve the class for a specified control type
* @param {String} type type of control we are looking up
* @param {String} [subtype] if specified we'll try to find
* a class mapped to this subtype. If none found, fall back to the type.
* @return {Class} control subclass as defined in the call to register
*/
getClass(type, subtype) {
return this.custom.getClass(type) || control.getClass(type, subtype)
}
}
Loading