Skip to content

Commit 48531fc

Browse files
committed
feat: onRemove and onAdd events
1 parent 11b3e96 commit 48531fc

File tree

6 files changed

+285
-0
lines changed

6 files changed

+285
-0
lines changed

src/demo/js/options/config.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ const config = {
2222
},
2323
},
2424
fields: {
25+
all: {
26+
events: {
27+
onRemove: evt => {
28+
console.log(`You just removed the field with the id "${evt.target.id}"`, evt)
29+
},
30+
},
31+
},
2532
checkbox: {
2633
actionButtons: {
2734
// buttons: ['edit'], // array of allow action buttons

src/lib/js/common/events.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,16 @@ import components, { Columns, Controls } from '../components/index.js'
22
import {
33
ANIMATION_SPEED_BASE,
44
ANIMATION_SPEED_FAST,
5+
EVENT_FORMEO_ADDED_COLUMN,
6+
EVENT_FORMEO_ADDED_FIELD,
7+
EVENT_FORMEO_ADDED_ROW,
58
EVENT_FORMEO_CHANGED,
69
EVENT_FORMEO_CLEARED,
710
EVENT_FORMEO_CONDITION_UPDATED,
811
EVENT_FORMEO_ON_RENDER,
12+
EVENT_FORMEO_REMOVED_COLUMN,
13+
EVENT_FORMEO_REMOVED_FIELD,
14+
EVENT_FORMEO_REMOVED_ROW,
915
EVENT_FORMEO_SAVED,
1016
EVENT_FORMEO_UPDATED,
1117
EVENT_FORMEO_UPDATED_COLUMN,
@@ -33,6 +39,12 @@ const defaults = {
3339
onUpdateRow: evt => events.opts?.debug && console.log(evt),
3440
onUpdateColumn: evt => events.opts?.debug && console.log(evt),
3541
onUpdateField: evt => events.opts?.debug && console.log(evt),
42+
onAddRow: evt => events.opts?.debug && console.log(evt),
43+
onAddColumn: evt => events.opts?.debug && console.log(evt),
44+
onAddField: evt => events.opts?.debug && console.log(evt),
45+
onRemoveRow: evt => events.opts?.debug && console.log(evt),
46+
onRemoveColumn: evt => events.opts?.debug && console.log(evt),
47+
onRemoveField: evt => events.opts?.debug && console.log(evt),
3648
onRender: evt => events.opts?.debug && console.log(evt),
3749
onSave: _evt => {},
3850
confirmClearAll: evt => {
@@ -74,6 +86,12 @@ const events = {
7486
formeoCleared: evt => defaultCustomEvent(evt, EVENT_FORMEO_CLEARED),
7587
formeoOnRender: evt => defaultCustomEvent(evt, EVENT_FORMEO_ON_RENDER),
7688
formeoConditionUpdated: evt => defaultCustomEvent(evt, EVENT_FORMEO_CONDITION_UPDATED),
89+
formeoAddedRow: evt => defaultCustomEvent(evt, EVENT_FORMEO_ADDED_ROW),
90+
formeoAddedColumn: evt => defaultCustomEvent(evt, EVENT_FORMEO_ADDED_COLUMN),
91+
formeoAddedField: evt => defaultCustomEvent(evt, EVENT_FORMEO_ADDED_FIELD),
92+
formeoRemovedRow: evt => defaultCustomEvent(evt, EVENT_FORMEO_REMOVED_ROW),
93+
formeoRemovedColumn: evt => defaultCustomEvent(evt, EVENT_FORMEO_REMOVED_COLUMN),
94+
formeoRemovedField: evt => defaultCustomEvent(evt, EVENT_FORMEO_REMOVED_FIELD),
7795
}
7896

7997
const formeoUpdatedThrottled = throttle(() => {
@@ -115,6 +133,42 @@ document.addEventListener(EVENT_FORMEO_UPDATED_FIELD, evt => {
115133
events.opts.onUpdateField(eventData)
116134
})
117135

136+
document.addEventListener(EVENT_FORMEO_ADDED_ROW, evt => {
137+
const { timeStamp, type, detail } = evt
138+
const eventData = { timeStamp, type, detail }
139+
events.opts.onAddRow(eventData)
140+
})
141+
142+
document.addEventListener(EVENT_FORMEO_ADDED_COLUMN, evt => {
143+
const { timeStamp, type, detail } = evt
144+
const eventData = { timeStamp, type, detail }
145+
events.opts.onAddColumn(eventData)
146+
})
147+
148+
document.addEventListener(EVENT_FORMEO_ADDED_FIELD, evt => {
149+
const { timeStamp, type, detail } = evt
150+
const eventData = { timeStamp, type, detail }
151+
events.opts.onAddField(eventData)
152+
})
153+
154+
document.addEventListener(EVENT_FORMEO_REMOVED_ROW, evt => {
155+
const { timeStamp, type, detail } = evt
156+
const eventData = { timeStamp, type, detail }
157+
events.opts.onRemoveRow(eventData)
158+
})
159+
160+
document.addEventListener(EVENT_FORMEO_REMOVED_COLUMN, evt => {
161+
const { timeStamp, type, detail } = evt
162+
const eventData = { timeStamp, type, detail }
163+
events.opts.onRemoveColumn(eventData)
164+
})
165+
166+
document.addEventListener(EVENT_FORMEO_REMOVED_FIELD, evt => {
167+
const { timeStamp, type, detail } = evt
168+
const eventData = { timeStamp, type, detail }
169+
events.opts.onRemoveField(eventData)
170+
})
171+
118172
document.addEventListener(EVENT_FORMEO_ON_RENDER, evt => {
119173
const { timeStamp, type, detail } = evt
120174
events.opts.onRender({

src/lib/js/common/events.test.js

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,16 @@ import { strict as assert } from 'node:assert'
77
import { clear } from 'node:console'
88
import { afterEach, beforeEach, describe, it, mock } from 'node:test'
99
import {
10+
EVENT_FORMEO_ADDED_COLUMN,
11+
EVENT_FORMEO_ADDED_FIELD,
12+
EVENT_FORMEO_ADDED_ROW,
1013
EVENT_FORMEO_CHANGED,
1114
EVENT_FORMEO_CLEARED,
1215
EVENT_FORMEO_CONDITION_UPDATED,
1316
EVENT_FORMEO_ON_RENDER,
17+
EVENT_FORMEO_REMOVED_COLUMN,
18+
EVENT_FORMEO_REMOVED_FIELD,
19+
EVENT_FORMEO_REMOVED_ROW,
1420
EVENT_FORMEO_SAVED,
1521
EVENT_FORMEO_UPDATED,
1622
EVENT_FORMEO_UPDATED_COLUMN,
@@ -380,4 +386,170 @@ describe('Events System', () => {
380386
consoleSpy.mock.restore()
381387
})
382388
})
389+
390+
describe('Add events', () => {
391+
it('should dispatch formeoAddedRow event', () => {
392+
return new Promise(resolve => {
393+
const testData = { componentId: 'row-123', componentType: 'row' }
394+
395+
addTestListener(EVENT_FORMEO_ADDED_ROW, evt => {
396+
assert.ok(evt.type === EVENT_FORMEO_ADDED_ROW)
397+
assert.deepEqual(evt.detail, testData)
398+
resolve()
399+
})
400+
401+
Events.formeoAddedRow(testData)
402+
})
403+
})
404+
405+
it('should dispatch formeoAddedColumn event', () => {
406+
return new Promise(resolve => {
407+
const testData = { componentId: 'column-123', componentType: 'column' }
408+
409+
addTestListener(EVENT_FORMEO_ADDED_COLUMN, evt => {
410+
assert.ok(evt.type === EVENT_FORMEO_ADDED_COLUMN)
411+
assert.deepEqual(evt.detail, testData)
412+
resolve()
413+
})
414+
415+
Events.formeoAddedColumn(testData)
416+
})
417+
})
418+
419+
it('should dispatch formeoAddedField event', () => {
420+
return new Promise(resolve => {
421+
const testData = { componentId: 'field-123', componentType: 'field' }
422+
423+
addTestListener(EVENT_FORMEO_ADDED_FIELD, evt => {
424+
assert.ok(evt.type === EVENT_FORMEO_ADDED_FIELD)
425+
assert.deepEqual(evt.detail, testData)
426+
resolve()
427+
})
428+
429+
Events.formeoAddedField(testData)
430+
})
431+
})
432+
433+
it('should call onAddRow callback for row additions', () => {
434+
return new Promise(resolve => {
435+
const onAddRow = mock.fn(evt => {
436+
assert.equal(evt.type, EVENT_FORMEO_ADDED_ROW)
437+
resolve()
438+
})
439+
440+
Events.init({ onAddRow })
441+
442+
Events.formeoAddedRow({ componentId: 'row-test' })
443+
})
444+
})
445+
446+
it('should call onAddColumn callback for column additions', () => {
447+
return new Promise(resolve => {
448+
const onAddColumn = mock.fn(evt => {
449+
assert.equal(evt.type, EVENT_FORMEO_ADDED_COLUMN)
450+
resolve()
451+
})
452+
453+
Events.init({ onAddColumn })
454+
455+
Events.formeoAddedColumn({ componentId: 'column-test' })
456+
})
457+
})
458+
459+
it('should call onAddField callback for field additions', () => {
460+
return new Promise(resolve => {
461+
const onAddField = mock.fn(evt => {
462+
assert.equal(evt.type, EVENT_FORMEO_ADDED_FIELD)
463+
resolve()
464+
})
465+
466+
Events.init({ onAddField })
467+
468+
Events.formeoAddedField({ componentId: 'field-test' })
469+
})
470+
})
471+
})
472+
473+
describe('Remove events', () => {
474+
it('should dispatch formeoRemovedRow event', () => {
475+
return new Promise(resolve => {
476+
const testData = { componentId: 'row-123', componentType: 'row' }
477+
478+
addTestListener(EVENT_FORMEO_REMOVED_ROW, evt => {
479+
assert.ok(evt.type === EVENT_FORMEO_REMOVED_ROW)
480+
assert.deepEqual(evt.detail, testData)
481+
resolve()
482+
})
483+
484+
Events.formeoRemovedRow(testData)
485+
})
486+
})
487+
488+
it('should dispatch formeoRemovedColumn event', () => {
489+
return new Promise(resolve => {
490+
const testData = { componentId: 'column-123', componentType: 'column' }
491+
492+
addTestListener(EVENT_FORMEO_REMOVED_COLUMN, evt => {
493+
assert.ok(evt.type === EVENT_FORMEO_REMOVED_COLUMN)
494+
assert.deepEqual(evt.detail, testData)
495+
resolve()
496+
})
497+
498+
Events.formeoRemovedColumn(testData)
499+
})
500+
})
501+
502+
it('should dispatch formeoRemovedField event', () => {
503+
return new Promise(resolve => {
504+
const testData = { componentId: 'field-123', componentType: 'field' }
505+
506+
addTestListener(EVENT_FORMEO_REMOVED_FIELD, evt => {
507+
assert.ok(evt.type === EVENT_FORMEO_REMOVED_FIELD)
508+
assert.deepEqual(evt.detail, testData)
509+
resolve()
510+
})
511+
512+
Events.formeoRemovedField(testData)
513+
})
514+
})
515+
516+
it('should call onRemoveRow callback for row removals', () => {
517+
return new Promise(resolve => {
518+
const onRemoveRow = mock.fn(evt => {
519+
assert.equal(evt.type, EVENT_FORMEO_REMOVED_ROW)
520+
resolve()
521+
})
522+
523+
Events.init({ onRemoveRow })
524+
525+
Events.formeoRemovedRow({ componentId: 'row-test' })
526+
})
527+
})
528+
529+
it('should call onRemoveColumn callback for column removals', () => {
530+
return new Promise(resolve => {
531+
const onRemoveColumn = mock.fn(evt => {
532+
assert.equal(evt.type, EVENT_FORMEO_REMOVED_COLUMN)
533+
resolve()
534+
})
535+
536+
Events.init({ onRemoveColumn })
537+
538+
Events.formeoRemovedColumn({ componentId: 'column-test' })
539+
})
540+
})
541+
542+
it('should call onRemoveField callback for field removals', () => {
543+
return new Promise(resolve => {
544+
const onRemoveField = mock.fn(evt => {
545+
assert.equal(evt.type, EVENT_FORMEO_REMOVED_FIELD)
546+
resolve()
547+
})
548+
549+
Events.init({ onRemoveField })
550+
551+
Events.formeoRemovedField({ componentId: 'field-test' })
552+
})
553+
})
554+
})
383555
})

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import events from '../common/events.js'
12
import { clone, merge, parseData, uuid } from '../common/utils/index.mjs'
23
import { get } from '../common/utils/object.mjs'
4+
import { EVENT_FORMEO_ADDED_COLUMN, EVENT_FORMEO_ADDED_FIELD, EVENT_FORMEO_ADDED_ROW } from '../constants.js'
35
import Data from './data.js'
46

57
export default class ComponentData extends Data {
@@ -34,6 +36,26 @@ export default class ComponentData extends Data {
3436
// this.set(elemId, component)
3537
this.active = component
3638

39+
// Dispatch add events based on component type
40+
const componentEventMap = {
41+
row: EVENT_FORMEO_ADDED_ROW,
42+
column: EVENT_FORMEO_ADDED_COLUMN,
43+
field: EVENT_FORMEO_ADDED_FIELD,
44+
}
45+
46+
const addEvent = componentEventMap[this.name]
47+
if (addEvent) {
48+
events.formeoUpdated(
49+
{
50+
entity: component,
51+
componentId: elemId,
52+
componentType: this.name,
53+
data: component.data,
54+
},
55+
addEvent
56+
)
57+
}
58+
3759
return component
3860
}
3961

src/lib/js/components/component.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import animate from '../common/animation.js'
44
import dom from '../common/dom.js'
5+
import events from '../common/events.js'
56
import { forEach, indexOfNode, isInt, map } from '../common/helpers.mjs'
67
import { clone, componentType, identity, merge, remove, unique, uuid } from '../common/utils/index.mjs'
78
import { get, objectFromStringArray, set } from '../common/utils/object.mjs'
@@ -13,6 +14,9 @@ import {
1314
COMPONENT_TYPE_CLASSNAMES,
1415
COMPONENT_TYPE_MAP,
1516
CONTROL_GROUP_CLASSNAME,
17+
EVENT_FORMEO_REMOVED_COLUMN,
18+
EVENT_FORMEO_REMOVED_FIELD,
19+
EVENT_FORMEO_REMOVED_ROW,
1620
PARENT_TYPE_MAP,
1721
PROPERTY_OPTIONS,
1822
} from '../constants.js'
@@ -97,6 +101,7 @@ export default class Component extends Data {
97101
// Create event data with component context
98102
const fullEventData = {
99103
component: this,
104+
target: this,
100105
type: eventName,
101106
timestamp: Date.now(),
102107
...eventData,
@@ -202,6 +207,25 @@ export default class Component extends Data {
202207
parent.autoColumnWidths()
203208
}
204209

210+
// Dispatch remove events based on component type
211+
const componentEventMap = {
212+
row: EVENT_FORMEO_REMOVED_ROW,
213+
column: EVENT_FORMEO_REMOVED_COLUMN,
214+
field: EVENT_FORMEO_REMOVED_FIELD,
215+
}
216+
217+
const removeEvent = componentEventMap[this.name]
218+
if (removeEvent) {
219+
events.formeoUpdated(
220+
{
221+
componentId: this.id,
222+
componentType: this.name,
223+
parent: parent,
224+
},
225+
removeEvent
226+
)
227+
}
228+
205229
return Components[`${this.name}s`].delete(this.id)
206230
}
207231

src/lib/js/constants.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,12 @@ export const EVENT_FORMEO_UPDATED_FIELD = 'formeoUpdatedField'
161161
export const EVENT_FORMEO_CLEARED = 'formeoCleared'
162162
export const EVENT_FORMEO_ON_RENDER = 'formeoOnRender'
163163
export const EVENT_FORMEO_CONDITION_UPDATED = 'formeoConditionUpdated'
164+
export const EVENT_FORMEO_ADDED_ROW = 'formeoAddedRow'
165+
export const EVENT_FORMEO_ADDED_COLUMN = 'formeoAddedColumn'
166+
export const EVENT_FORMEO_ADDED_FIELD = 'formeoAddedField'
167+
export const EVENT_FORMEO_REMOVED_ROW = 'formeoRemovedRow'
168+
export const EVENT_FORMEO_REMOVED_COLUMN = 'formeoRemovedColumn'
169+
export const EVENT_FORMEO_REMOVED_FIELD = 'formeoRemovedField'
164170
export const COMPARISON_OPERATORS = {
165171
equals: '==',
166172
notEquals: '!=',

0 commit comments

Comments
 (0)