mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2024-11-19 18:11:36 +01:00
240 lines
7.8 KiB
JavaScript
240 lines
7.8 KiB
JavaScript
document.addEventListener('DOMContentLoaded', () => {
|
|
const parseConfig = str => {
|
|
try {
|
|
return JSON.parse(str)
|
|
} catch (err) {
|
|
console.error('Error deserializing form config:', err)
|
|
}
|
|
}
|
|
const $config = document.getElementById('FormConfig')
|
|
let config = parseConfig($config.value) || {}
|
|
|
|
const specialFieldTypeOptions = ['fieldset', 'textarea', 'select', 'mirror']
|
|
const inputFieldTypeOptions = ['text', 'number', 'password', 'email', 'url', 'tel', 'date', 'hidden']
|
|
const fieldTypeOptions = inputFieldTypeOptions.concat(specialFieldTypeOptions)
|
|
|
|
const getFieldComponent = type => `field-type-${specialFieldTypeOptions.includes(type) ? type : 'input'}`
|
|
|
|
const fieldProps = {
|
|
type: String,
|
|
name: String,
|
|
label: String,
|
|
value: String,
|
|
helpText: String,
|
|
required: Boolean,
|
|
constant: Boolean,
|
|
options: Array,
|
|
fields: Array,
|
|
validationErrors: Array
|
|
}
|
|
|
|
const fieldTypeBase = {
|
|
props: {
|
|
// internal
|
|
path: Array,
|
|
// field config
|
|
...fieldProps
|
|
}
|
|
}
|
|
|
|
const FieldTypeInput = Vue.extend({
|
|
mixins: [fieldTypeBase],
|
|
name: 'field-type-input',
|
|
template: '#field-type-input'
|
|
})
|
|
|
|
const FieldTypeTextarea = Vue.extend({
|
|
mixins: [fieldTypeBase],
|
|
name: 'field-type-textarea',
|
|
template: '#field-type-textarea'
|
|
})
|
|
|
|
const FieldTypeSelect = Vue.extend({
|
|
mixins: [fieldTypeBase],
|
|
name: 'field-type-select',
|
|
template: '#field-type-select',
|
|
props: {
|
|
options: Array
|
|
}
|
|
})
|
|
const FieldTypeMirror = Vue.extend({
|
|
mixins: [fieldTypeBase],
|
|
name: 'field-type-mirror',
|
|
template: '#field-type-mirror'
|
|
})
|
|
|
|
const components = {
|
|
FieldTypeInput,
|
|
FieldTypeSelect,
|
|
FieldTypeTextarea,
|
|
FieldTypeMirror
|
|
}
|
|
|
|
// register fields-editor and field-type-fieldset globally in order to use them recursively
|
|
Vue.component('field-type-fieldset', {
|
|
mixins: [fieldTypeBase],
|
|
template: '#field-type-fieldset',
|
|
components,
|
|
props: {
|
|
fields: Array,
|
|
selectedField: fieldProps
|
|
}
|
|
})
|
|
|
|
Vue.component('fields-editor', {
|
|
template: '#fields-editor',
|
|
components,
|
|
props: {
|
|
path: Array,
|
|
fields: Array,
|
|
selectedField: fieldProps
|
|
},
|
|
methods: {
|
|
getFieldComponent
|
|
}
|
|
})
|
|
|
|
Vue.component('field-editor', {
|
|
template: '#field-editor',
|
|
components,
|
|
data () {
|
|
return {
|
|
fieldTypeOptions
|
|
}
|
|
},
|
|
props: {
|
|
path: Array,
|
|
field: fieldProps
|
|
},
|
|
computed: {
|
|
mirroredField() {
|
|
return this.field.type === 'mirror' &&
|
|
this.$root.allFields.find(f => f.name === this.field.value)
|
|
}
|
|
},
|
|
methods: {
|
|
getFieldComponent,
|
|
addOption (event) {
|
|
if (!this.field.options) this.$set(this.field, 'options', [])
|
|
const index = this.field.options.length + 1
|
|
this.field.options.push({ value: `newOption${index}`, text: `New option ${index}` })
|
|
},
|
|
removeOption(event, index) {
|
|
this.field.options.splice(index, 1)
|
|
},
|
|
sortOptions (event) {
|
|
const { newIndex, oldIndex } = event
|
|
this.field.options.splice(newIndex, 0, this.field.options.splice(oldIndex, 1)[0])
|
|
},
|
|
addValueMap (event) {
|
|
if (!this.field.valuemap) this.$set(this.field, 'valuemap', {})
|
|
const index = Object.keys(this.field.valuemap).length + 1;
|
|
this.$set(this.field.valuemap, `valuemap_${index}`, '')
|
|
},
|
|
updateValueMap(oldK, newK, newV) {
|
|
if (oldK !== newK) {
|
|
Vue.delete(this.field.valuemap, oldK);
|
|
}
|
|
Vue.set(this.field.valuemap, newK, newV);
|
|
},
|
|
removeValueMap(event, k) {
|
|
Vue.delete(this.field.valuemap, k);
|
|
},
|
|
}
|
|
})
|
|
|
|
Vue.use(vSortable)
|
|
Vue.use(VueSanitizeDirective.default)
|
|
|
|
new Vue({
|
|
el: '#FormEditor',
|
|
name: 'form-editor',
|
|
data () {
|
|
return {
|
|
config,
|
|
selectedField: null,
|
|
editorOffcanvas: null
|
|
}
|
|
},
|
|
computed: {
|
|
allFields() {
|
|
const getFields = (fields, path) => {
|
|
let result = [];
|
|
for (const field of fields) {
|
|
result.push(field)
|
|
if (field.fields && field.fields.length > 0)
|
|
result= result.concat(getFields(field.fields, path + field.name));
|
|
}
|
|
return result;
|
|
}
|
|
return getFields(this.fields, "")
|
|
},
|
|
fields() {
|
|
return this.config.fields || []
|
|
},
|
|
configJSON() {
|
|
return JSON.stringify(this.config, null, 2)
|
|
}
|
|
},
|
|
methods: {
|
|
applyTemplate(id) {
|
|
const $template = document.getElementById(`form-template-${id}`)
|
|
this.config = JSON.parse($template.innerHTML.trim())
|
|
this.selectedField = null
|
|
},
|
|
updateFromJSON(event) {
|
|
const config = parseConfig(event.target.value)
|
|
if (!config) return
|
|
this.config = config
|
|
this.selectedField = null
|
|
},
|
|
addField(event, path) {
|
|
const fields = this.getFieldsForPath(path)
|
|
const index = fields.length + 1
|
|
const length = fields.push({ type: 'text', name: `newField${index}`, label: `New field ${index}`, fields: [], options: [] })
|
|
this.selectedField = fields[length - 1]
|
|
this.showOffcanvas()
|
|
},
|
|
selectField(event, path, index) {
|
|
const fields = this.getFieldsForPath(path)
|
|
this.selectedField = fields[index]
|
|
this.showOffcanvas()
|
|
},
|
|
removeField(event, path, index) {
|
|
const fields = this.getFieldsForPath(path)
|
|
fields.splice(index, 1)
|
|
this.selectedField = null
|
|
},
|
|
sortFields(event, path) {
|
|
const { newIndex, oldIndex } = event
|
|
const fields = this.getFieldsForPath(path)
|
|
fields.splice(newIndex, 0, fields.splice(oldIndex, 1)[0])
|
|
},
|
|
getFieldsForPath (path) {
|
|
if (!this.config.fields) this.$set(this.config, 'fields', [])
|
|
let fields = this.config.fields
|
|
while (path.length) {
|
|
const name = path.shift()
|
|
const field = fields.find(field => field.name === name)
|
|
if (!field.fields) this.$set(field, 'fields', [])
|
|
fields = field.fields
|
|
}
|
|
return fields
|
|
},
|
|
showOffcanvas() {
|
|
if (window.getComputedStyle(this.$refs.editorOffcanvas).visibility === 'hidden')
|
|
this.editorOffcanvas.show();
|
|
},
|
|
hideOffcanvas() {
|
|
this.editorOffcanvas.hide();
|
|
}
|
|
},
|
|
mounted () {
|
|
if (!this.config.fields || this.config.fields.length === 0) {
|
|
this.addField(null,[])
|
|
}
|
|
this.editorOffcanvas = bootstrap.Offcanvas.getOrCreateInstance(this.$refs.editorOffcanvas);
|
|
}
|
|
})
|
|
})
|