Skip to content

Commit 8504740

Browse files
committed
fix: migrate meta.id to config.controlId for smaller payloads
add descriptions to json schema
1 parent f6ad0a2 commit 8504740

File tree

12 files changed

+273
-184
lines changed

12 files changed

+273
-184
lines changed

src/demo/index.html

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,8 @@
3434
<span id="formeo-logo-wrap"></span>
3535
</header>
3636
<section id="main_content" class="inner">
37-
<form class="build-form clearfix"></form>
38-
<div class="render-form">
39-
<h2>Rendered Form</h2>
40-
</div>
37+
<form class="build-form"></form>
38+
<div class="render-form"></div>
4139
</section>
4240
<div class="container render-btn-wrap" id="editor-action-buttons">
4341
</div>

src/lib/js/components/autocomplete.mjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ export const labelCount = (arr, label) => {
2727
* @return {String} component label
2828
*/
2929
const getComponentLabel = ({ name, id, ...component }) => {
30-
const labelPaths = ['config.label', 'attrs.id', 'meta.id']
30+
const labelPaths = ['config.label', 'config.controlId', 'meta.id', 'attrs.id']
3131
const label = labelPaths.reduce((acc, cur) => {
3232
if (!acc) {
33-
acc = component.get(cur)
33+
return component.get(cur)
3434
}
3535
return acc
3636
}, null)

src/lib/js/components/component-data.js

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,30 @@ import { uuid, clone, merge } from '../common/utils/index.mjs'
33
import { get } from '../common/utils/object.mjs'
44

55
export default class ComponentData extends Data {
6-
load = (data = Object.create(null)) => {
6+
load = dataArg => {
7+
const data = this.parseformData(dataArg)
78
this.empty()
8-
if (typeof data === 'string') {
9-
data = JSON.parse(data)
9+
for (const [key, val] of Object.entries(data)) {
10+
this.add(key, val)
1011
}
11-
Object.entries(data).forEach(([key, val]) => this.add(key, val))
1212
return this.data
1313
}
1414

15+
/**
16+
* Retrieves data from the specified path or adds new data if no path is provided.
17+
*
18+
* @param {string} [path] - The path to retrieve data from. If not provided, new data will be added.
19+
* @returns {*} The data retrieved from the specified path or the result of adding new data.
20+
*/
1521
get = path => (path ? get(this.data, path) : this.add())
1622

23+
/**
24+
* Adds a new component with the given id and data.
25+
*
26+
* @param {string} id - The unique identifier for the component. If not provided, a new UUID will be generated.
27+
* @param {Object} [data=Object.create(null)] - The data to initialize the component with.
28+
* @returns {Object} The newly created component.
29+
*/
1730
add = (id, data = Object.create(null)) => {
1831
const elemId = id || uuid()
1932
const component = this.Component({ ...data, id: elemId })
@@ -29,16 +42,22 @@ export default class ComponentData extends Data {
2942
*/
3043
remove = componentId => {
3144
if (Array.isArray(componentId)) {
32-
componentId.forEach(id => {
45+
for (const id of componentId) {
3346
this.get(id).remove()
34-
})
47+
}
3548
} else {
3649
this.get(componentId).remove()
3750
}
3851

3952
return this.data
4053
}
4154

55+
/**
56+
* Deletes a component from the data object.
57+
*
58+
* @param {string} componentId - The ID of the component to delete.
59+
* @returns {string} The ID of the deleted component.
60+
*/
4261
delete = componentId => {
4362
delete this.data[componentId]
4463
return componentId

src/lib/js/components/component.js

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import Components from './index.js'
1616
import Data from './data.js'
1717
import animate from '../common/animation.js'
1818
import Controls from './controls/index.js'
19-
import { get } from '../common/utils/object.mjs'
19+
import { get, set } from '../common/utils/object.mjs'
2020
import { toTitleCase } from '../common/utils/string.mjs'
2121

2222
export default class Component extends Data {
@@ -355,6 +355,7 @@ export default class Component extends Data {
355355

356356
/**
357357
* Method for handling onAdd for all components
358+
* @todo improve readability of this method
358359
* @param {Object} evt
359360
* @return {Object} Component
360361
*/
@@ -397,10 +398,14 @@ export default class Component extends Data {
397398

398399
const onAddConditions = {
399400
controls: () => {
400-
const { controlData } = Controls.get(item.id)
401401
const {
402-
meta: { id: metaId },
403-
} = controlData
402+
controlData: {
403+
meta: { id: metaId },
404+
...elementData
405+
},
406+
} = Controls.get(item.id)
407+
408+
set(elementData, 'config.controlId', metaId)
404409

405410
const controlType = metaId.startsWith('layout-') ? metaId.replace(/^layout-/, '') : 'field'
406411
const targets = {
@@ -424,7 +429,7 @@ export default class Component extends Data {
424429
const depth = get(targets, `${this.name}.${controlType}`)
425430
const action = depthMap.get(depth)()
426431
dom.remove(item)
427-
const component = action(controlData, newIndex)
432+
const component = action(elementData, newIndex)
428433

429434
return component
430435
},
@@ -501,10 +506,23 @@ export default class Component extends Data {
501506
events.onRender && dom.onRender(this.dom, events.onRender)
502507
}
503508

509+
/**
510+
* Sets the configuration for the component. See src/demo/js/options/config.js for example
511+
* @param {Object} config - Configuration object with possible structures:
512+
* @param {Object} [config.all] - Global configuration applied to all components
513+
* @param {Object} [config[controlId]] - Configuration specific to a control type
514+
* @param {Object} [config[id]] - Configuration specific to a component instance
515+
* @description Merges configurations in order of precedence:
516+
* 1. Existing config (this.configVal)
517+
* 2. Global config (all)
518+
* 3. Control type specific config
519+
* 4. Instance specific config
520+
* The merged result is stored in this.configVal
521+
*/
504522
set config(config) {
505-
const metaId = get(this.data, 'meta.id')
506523
const allConfig = get(config, 'all')
507-
const typeConfig = metaId && get(config, metaId)
524+
const controlId = get(this.data, 'config.controlId')
525+
const typeConfig = controlId && get(config, controlId)
508526
const idConfig = get(config, this.id)
509527
const mergedConfig = [allConfig, typeConfig, idConfig].reduce(
510528
(acc, cur) => (cur ? merge(acc, cur) : acc),

src/lib/js/components/controls/index.js

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import layoutControls from './layout/index.js'
1616
import formControls from './form/index.js'
1717
import htmlControls from './html/index.js'
1818
import defaultOptions from './options.js'
19-
import { get } from '../../common/utils/object.mjs'
19+
import { get, set } from '../../common/utils/object.mjs'
2020

2121
const defaultElements = [...formControls, ...htmlControls, ...layoutControls]
2222

@@ -126,17 +126,17 @@ export class Controls {
126126
*/
127127
groupConfig.content = elements.filter(control => {
128128
const { controlData: field } = this.get(control.id)
129-
const fieldId = field.meta.id || ''
129+
const controlId = field.meta.id || ''
130130
const filters = [
131-
match(fieldId, this.options.disable.elements),
131+
match(controlId, this.options.disable.elements),
132132
field.meta.group === group.id,
133-
!usedElementIds.includes(field.meta.id),
133+
!usedElementIds.includes(controlId),
134134
]
135135

136136
let shouldFilter = true
137137
shouldFilter = filters.every(val => val === true)
138138
if (shouldFilter) {
139-
usedElementIds.push(fieldId)
139+
usedElementIds.push(controlId)
140140
}
141141

142142
return shouldFilter
@@ -349,24 +349,29 @@ export class Controls {
349349
return element
350350
}
351351

352+
layoutTypes = {
353+
row: () => Stages.active.addChild(),
354+
column: () => this.layoutTypes.row().addChild(),
355+
field: controlData => this.layoutTypes.column().addChild(controlData),
356+
}
357+
352358
/**
353359
* Append an element to the stage
354360
* @param {String} id of elements
355361
*/
356362
addElement = id => {
357-
const controlData = get(this.get(id), 'controlData')
358-
359363
const {
360364
meta: { group, id: metaId },
361-
} = controlData
365+
...elementData
366+
} = get(this.get(id), 'controlData')
367+
368+
set(elementData, 'config.controlId', metaId)
362369

363-
const layoutTypes = {
364-
row: () => Stages.active.addChild(),
365-
column: () => layoutTypes.row().addChild(),
366-
field: controlData => layoutTypes.column().addChild(controlData),
370+
if (group === 'layout') {
371+
return this.layoutTypes[metaId.replace('layout-', '')]()
367372
}
368373

369-
return group !== 'layout' ? layoutTypes.field(controlData) : layoutTypes[metaId.replace('layout-', '')]()
374+
return this.layoutTypes.field(elementData)
370375
}
371376

372377
applyOptions = async (controlOptions = {}) => {

src/lib/js/components/data.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,4 +100,25 @@ export default class Data {
100100
}
101101
setCallbacks = {}
102102
configVal = Object.create(null)
103+
104+
/**
105+
* Parses the provided data argument. If the argument is a string, it attempts to parse it as JSON.
106+
* If the parsing fails, it logs an error and returns an empty object.
107+
* If the argument is not a string, it returns the argument as is.
108+
*
109+
* @param {string|Object} dataArg - The data to be parsed. Can be a JSON string or an object.
110+
* @returns {Object} - The parsed object or the original object if the input was not a string.
111+
*/
112+
parseformData = (dataArg = Object.create(null)) => {
113+
if (typeof dataArg === 'string') {
114+
try {
115+
return JSON.parse(dataArg)
116+
} catch (e) {
117+
console.error('Invalid JSON string provided:', e)
118+
return Object.create(null)
119+
}
120+
}
121+
122+
return dataArg
123+
}
103124
}

src/lib/js/components/fields/edit-panel.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,8 @@ export default class EditPanel {
126126
* @param {String} attr
127127
* @param {String|Array} val
128128
*/
129-
addAttribute = (attr, val) => {
129+
addAttribute = (attr, valArg) => {
130+
let val = valArg
130131
const safeAttr = slugify(attr)
131132
const itemKey = `attrs.${safeAttr}`
132133

@@ -157,16 +158,16 @@ export default class EditPanel {
157158
* Add option to options panel
158159
*/
159160
addOption = () => {
160-
const metaId = this.field.data.meta.id
161+
const controlId = this.field.data.config.controlId
161162
const fieldOptionData = this.field.get('options')
162-
const type = metaId === 'select' ? 'option' : metaId
163+
const type = controlId === 'select' ? 'option' : controlId
163164
const newOptionLabel = i18n.get('newOptionLabel', { type }) || 'New Option'
164165
const itemKey = `options.${this.data.length}`
165-
166+
166167
const lastOptionData = fieldOptionData[fieldOptionData.length - 1]
167168
const optionTemplate = fieldOptionData.length ? lastOptionData : {}
168169
const itemData = { ...optionTemplate, label: newOptionLabel }
169-
if (metaId !== 'button') {
170+
if (controlId !== 'button') {
170171
itemData.value = slugify(newOptionLabel)
171172
}
172173
const newOption = new EditPanelItem({

src/lib/js/components/fields/field.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export default class Field extends Component {
6565
const hideLabel = !!this.get('config.hideLabel')
6666

6767
if (hideLabel) {
68-
return
68+
return null
6969
}
7070

7171
const labelVal = this.get('config.editorLabel') || this.get('config.label')
@@ -308,7 +308,7 @@ export default class Field extends Component {
308308
*/
309309
fieldPreview() {
310310
const prevData = clone(this.data)
311-
const { action = {} } = controls.get(prevData.meta.id)
311+
const { action = {} } = controls.get(prevData.config.controlId)
312312
prevData.id = `prev-${this.id}`
313313
prevData.action = action
314314

src/lib/js/components/fields/index.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import ComponentData from '../component-data.js'
22
import Field from './field.js'
33
import Controls from '../controls/index.js'
4-
import { get } from '../../common/utils/object.mjs'
4+
import { get, set } from '../../common/utils/object.mjs'
55

66
const DEFAULT_CONFIG = {
77
actionButtons: {
@@ -60,6 +60,23 @@ export class Fields extends ComponentData {
6060
return acc
6161
}, {})
6262
}
63+
64+
load = (dataArg = Object.create(null)) => {
65+
const allFieldData = this.parseformData(dataArg)
66+
this.empty()
67+
68+
for (const [key, val] of Object.entries(allFieldData)) {
69+
const { meta, ...data } = val
70+
// meta object is only for controls, we want to migrate it out of field data
71+
// we only need the control id to tie actions back to control definitons
72+
if (meta?.id) {
73+
set(data, 'config.controlId', meta?.id)
74+
}
75+
this.add(key, data)
76+
}
77+
78+
return this.data
79+
}
6380
}
6481

6582
const fields = new Fields()

0 commit comments

Comments
 (0)