Custom Editors
A custom editor is a class extending Jedison.Editor, registered via the customEditors option. For every field in the schema, Jedison calls resolves(schema) on each registered custom editor, highest priority() first, and uses the first one that returns true — checked before any built-in editor. If none match, Jedison falls back to its built-in editors for that field.
Resolution priority
When customEditors combines editors from more than one source, two of them may both match the same field. Override the static priority() method (default 0) to make one win regardless of array order. Editors are sorted by priority, highest first, before being scanned — ties keep their original array order, so editors that don't set a priority behave exactly as before.
class MySpecificEditor extends Jedison.Editor {
static priority () {
return 10
}
static resolves (schema) {
return schema['x-format'] === 'my-specific-format'
}
}
The editor lifecycle
A subclass typically implements:
static resolves(schema)— returnstrueif this editor should handle the given schema node.build()— createsthis.control, an object whosecontainerproperty is theHTMLElementthis editor renders into. Parent editors (object, array) appendthis.control.containerinto their own DOM — it's the only property the base class strictly requires; anything else onthis.control(input,label, ...) is for the editor's own use.addEventListeners()— wires DOM events tothis.instance.setValue(value, true, 'user'), writing the user's input back onto the instance.refreshUI()— readsthis.instance.getValue()and updates the DOM. Called once after construction and again automatically every time the instance's value changes — a custom editor doesn't need to subscribe to its own change event manually.sanitize(value)— coerces a raw value (e.g. a string from an<input>) to the type the schema expects, before it's stored on the instance.
destroy() is handled by the base class: it removes this.control.container from the DOM and cleans up. A custom editor only needs its own destroy() if it registers listeners outside addEventListeners().
class RatingEditor extends Jedison.Editor {
static resolves (schema) {
return schema['x-format'] === 'rating'
}
build () {
const container = document.createElement('div')
const input = document.createElement('input')
input.type = 'range'
input.min = 1
input.max = 5
container.appendChild(input)
this.control = { container, input }
}
addEventListeners () {
this.control.input.addEventListener('input', () => {
this.instance.setValue(Number(this.control.input.value), true, 'user')
})
}
refreshUI () {
this.control.input.value = this.instance.getValue()
}
sanitize (value) {
return Number(value)
}
}
const jedison = new Jedison.Create({
container: document.getElementById('jedison-container'),
theme: new Jedison.Theme(),
schema: { type: 'number', 'x-format': 'rating' },
customEditors: [RatingEditor]
})
Expanding $ref schemas in resolves()
resolves() receives a second argument: the RefParser instance configured on Jedison.Create (or undefined if none was configured). This matters when the schema you need to inspect might be a $ref — for example, checking the items schema of an array — since a plain { $ref: '...' } node won't have the keywords you're checking for until it's expanded.
class ColorSwatchArrayEditor extends Jedison.Editor {
static resolves (schema, refParser) {
let items = schema.items
if (refParser && items) {
items = refParser.expand(items)
}
return items && items['x-format'] === 'color'
}
// build(), addEventListeners(), refreshUI(), sanitize() as usual
}