builder block: allow to use the code editor on each item
This commit is contained in:
parent
bc148f0b6b
commit
8e947b0b77
|
@ -862,9 +862,16 @@ label.required::after {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
dialog {
|
.builder-code-editor {
|
||||||
border: 0;
|
border: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
background: none;
|
background: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 15px;
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
font-family: Monospace;
|
||||||
|
min-height: 50vh;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,64 +45,23 @@
|
||||||
position="bottom"
|
position="bottom"
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<button
|
<BuilderBlockCodeEditor
|
||||||
type="button"
|
ref="dialog"
|
||||||
class="btn btn-sm"
|
:value="value"
|
||||||
v-on:click="openCodeEditor"
|
:widgets="widgets"
|
||||||
>
|
@update="codeUpdate"
|
||||||
<i class="fas fa-code"></i>
|
/>
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<textarea :name="name" class="d-none">{{ toJson(value) }}</textarea>
|
<textarea :name="name" class="d-none">{{ toJson(value) }}</textarea>
|
||||||
<dialog ref="dialog" class="modal-dialog modal-dialog-large">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h5 class="modal-title">Code editor</h5>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="close"
|
|
||||||
v-on:click="closeCodeEditor"
|
|
||||||
>
|
|
||||||
<span aria-hidden="true">×</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<div class="form-group">
|
|
||||||
<textarea
|
|
||||||
class="form-control"
|
|
||||||
rows="10"
|
|
||||||
ref="codeEditor"
|
|
||||||
v-model="nextValue"
|
|
||||||
>
|
|
||||||
</textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="btn btn-secondary"
|
|
||||||
v-on:click="closeCodeEditor"
|
|
||||||
>
|
|
||||||
Close
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="btn btn-primary"
|
|
||||||
v-on:click="checkAndSaveNextValue"
|
|
||||||
>
|
|
||||||
Save
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</dialog>
|
|
||||||
</Draggable>
|
</Draggable>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import BuilderBlockItem from './BuilderBlockItem'
|
import BuilderBlockItem from './BuilderBlockItem'
|
||||||
|
import BuilderBlockCodeEditor from './BuilderBlockCodeEditor'
|
||||||
import BuilderBlockCreate from './BuilderBlockCreate'
|
import BuilderBlockCreate from './BuilderBlockCreate'
|
||||||
import Routing from '../../../../../../../../../friendsofsymfony/jsrouting-bundle/Resources/public/js/router.min.js'
|
import Routing from '../../../../../../../../../friendsofsymfony/jsrouting-bundle/Resources/public/js/router.min.js'
|
||||||
import Draggable from 'vuedraggable'
|
import Draggable from 'vuedraggable'
|
||||||
|
@ -113,6 +72,12 @@ Routing.setRoutingData(routes)
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'BuilderBlock',
|
name: 'BuilderBlock',
|
||||||
|
components: {
|
||||||
|
BuilderBlockItem,
|
||||||
|
BuilderBlockCreate,
|
||||||
|
Draggable,
|
||||||
|
BuilderBlockCodeEditor,
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
id: {
|
id: {
|
||||||
type: String,
|
type: String,
|
||||||
|
@ -144,71 +109,8 @@ export default {
|
||||||
triggerBuilderBlockEvent() {
|
triggerBuilderBlockEvent() {
|
||||||
document.querySelector('body').dispatchEvent(new Event('builder_block.update'))
|
document.querySelector('body').dispatchEvent(new Event('builder_block.update'))
|
||||||
},
|
},
|
||||||
openCodeEditor() {
|
codeUpdate(nextValue) {
|
||||||
this.nextValue = this.toJson(this.value)
|
this.value = nextValue
|
||||||
this.$refs.dialog.showModal()
|
|
||||||
},
|
|
||||||
closeCodeEditor() {
|
|
||||||
this.$refs.codeEditor.classList.toggle('is-invalid', false)
|
|
||||||
this.$refs.dialog.close()
|
|
||||||
},
|
|
||||||
isNextValueItemValueValid(item) {
|
|
||||||
const hasId = typeof item.id === 'string'
|
|
||||||
const hasWidget = typeof item.widget === 'string' && this.widgets[item.widget]
|
|
||||||
const hasSettings = typeof item.settings === 'object'
|
|
||||||
const hasChildren = Array.isArray(item.children)
|
|
||||||
|
|
||||||
if (!hasId || !hasWidget || !hasSettings || !hasChildren) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let child of item.children) {
|
|
||||||
if (!this.isNextValueItemValueValid(child)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
updateNextValueRecursiveIds(data) {
|
|
||||||
if (Array.isArray(data)) {
|
|
||||||
data.forEach((value, key) => {
|
|
||||||
data[key] = this.updateNextValueRecursiveIds(value)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
data.id = this.makeId()
|
|
||||||
data.children = this.updateNextValueRecursiveIds(data.children)
|
|
||||||
}
|
|
||||||
|
|
||||||
return data
|
|
||||||
},
|
|
||||||
checkAndSaveNextValue() {
|
|
||||||
this.$refs.codeEditor.classList.toggle('is-invalid', false)
|
|
||||||
let hasError = false
|
|
||||||
|
|
||||||
try {
|
|
||||||
let data = JSON.parse(this.nextValue)
|
|
||||||
|
|
||||||
if (!Array.isArray(data)) {
|
|
||||||
hasError = true
|
|
||||||
} else {
|
|
||||||
for (let item of data) {
|
|
||||||
if (!this.isNextValueItemValueValid(item)) {
|
|
||||||
hasError = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasError) {
|
|
||||||
this.value = this.updateNextValueRecursiveIds(data)
|
|
||||||
++this.blockKey
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (e) {
|
|
||||||
hasError = true
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.$refs.codeEditor.classList.toggle('is-invalid', hasError)
|
|
||||||
},
|
},
|
||||||
removeBlock(key) {
|
removeBlock(key) {
|
||||||
let newValue = []
|
let newValue = []
|
||||||
|
@ -259,22 +161,6 @@ export default {
|
||||||
|
|
||||||
return data
|
return data
|
||||||
},
|
},
|
||||||
makeId() {
|
|
||||||
let result = ''
|
|
||||||
const characters = 'abcdefghijklmnopqrstuvwxyz0123456789'
|
|
||||||
const charactersLength = characters.length
|
|
||||||
|
|
||||||
for (let i = 0; i < 7; i++) {
|
|
||||||
result += characters.charAt(Math.floor(Math.random() * charactersLength))
|
|
||||||
}
|
|
||||||
|
|
||||||
return `block-${result}`
|
|
||||||
},
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
BuilderBlockItem,
|
|
||||||
BuilderBlockCreate,
|
|
||||||
Draggable,
|
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
const that = this
|
const that = this
|
||||||
|
|
|
@ -0,0 +1,163 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-sm"
|
||||||
|
v-on:click="open"
|
||||||
|
>
|
||||||
|
<i class="fas fa-code"></i>
|
||||||
|
</button>
|
||||||
|
<dialog ref="dialog" class="modal-dialog modal-dialog-large builder-code-editor">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Code editor</h5>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="close"
|
||||||
|
v-on:click="close"
|
||||||
|
>
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="form-group">
|
||||||
|
<textarea
|
||||||
|
class="form-control"
|
||||||
|
rows="10"
|
||||||
|
ref="codeEditor"
|
||||||
|
v-model="nextValue"
|
||||||
|
>
|
||||||
|
</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-secondary"
|
||||||
|
v-on:click="close"
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-primary"
|
||||||
|
v-on:click="checkAndSaveNextValue"
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'BuilderBlockCodeEditor',
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
widgets: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
nextValue: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toJson(value) {
|
||||||
|
return JSON.stringify(value, null, 2)
|
||||||
|
},
|
||||||
|
open() {
|
||||||
|
this.nextValue = this.toJson(this.value)
|
||||||
|
this.$refs.dialog.showModal()
|
||||||
|
},
|
||||||
|
close() {
|
||||||
|
this.$refs.codeEditor.classList.toggle('is-invalid', false)
|
||||||
|
this.$refs.dialog.close()
|
||||||
|
},
|
||||||
|
isNextValueItemValueValid(item) {
|
||||||
|
const hasId = typeof item.id === 'string'
|
||||||
|
const hasWidget = typeof item.widget === 'string' && this.widgets[item.widget]
|
||||||
|
const hasSettings = typeof item.settings === 'object'
|
||||||
|
const hasChildren = Array.isArray(item.children)
|
||||||
|
|
||||||
|
if (!hasId || !hasWidget || !hasSettings || !hasChildren) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let child of item.children) {
|
||||||
|
if (!this.isNextValueItemValueValid(child)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
updateNextValueRecursiveIds(data) {
|
||||||
|
if (Array.isArray(data)) {
|
||||||
|
data.forEach((value, key) => {
|
||||||
|
data[key] = this.updateNextValueRecursiveIds(value)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
data.id = this.makeId()
|
||||||
|
data.children = this.updateNextValueRecursiveIds(data.children)
|
||||||
|
}
|
||||||
|
|
||||||
|
return data
|
||||||
|
},
|
||||||
|
checkAndSaveNextValue() {
|
||||||
|
this.$refs.codeEditor.classList.toggle('is-invalid', false)
|
||||||
|
let hasError = false
|
||||||
|
|
||||||
|
try {
|
||||||
|
let data = JSON.parse(this.nextValue)
|
||||||
|
|
||||||
|
if (!Array.isArray(data)) {
|
||||||
|
hasError = true
|
||||||
|
} else {
|
||||||
|
for (let item of data) {
|
||||||
|
if (!this.isNextValueItemValueValid(item)) {
|
||||||
|
hasError = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasError) {
|
||||||
|
this.$emit('update', this.updateNextValueRecursiveIds(data))
|
||||||
|
this.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
hasError = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.$refs.codeEditor.classList.toggle('is-invalid', hasError)
|
||||||
|
},
|
||||||
|
makeId() {
|
||||||
|
let result = ''
|
||||||
|
const characters = 'abcdefghijklmnopqrstuvwxyz0123456789'
|
||||||
|
const charactersLength = characters.length
|
||||||
|
|
||||||
|
for (let i = 0; i < 7; i++) {
|
||||||
|
result += characters.charAt(Math.floor(Math.random() * charactersLength))
|
||||||
|
}
|
||||||
|
|
||||||
|
return `block-${result}`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
},
|
||||||
|
updated() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -110,24 +110,39 @@
|
||||||
</Draggable>
|
</Draggable>
|
||||||
|
|
||||||
<div v-if="widget.isContainer" class="container">
|
<div v-if="widget.isContainer" class="container">
|
||||||
<BuilderBlockCreate
|
<div class="d-flex justify-content-between">
|
||||||
:container="item.children"
|
<BuilderBlockCreate
|
||||||
:widgets="widgets"
|
:container="item.children"
|
||||||
:openedBlocks="openedBlocks"
|
:widgets="widgets"
|
||||||
:allowedWidgets="widget.widgets"
|
:openedBlocks="openedBlocks"
|
||||||
position="bottom"
|
:allowedWidgets="widget.widgets"
|
||||||
/>
|
position="bottom"
|
||||||
|
/>
|
||||||
|
<BuilderBlockCodeEditor
|
||||||
|
ref="dialog"
|
||||||
|
:value="item.children"
|
||||||
|
:widgets="widgets"
|
||||||
|
@update="codeUpdate"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import BuilderBlockCreate from './BuilderBlockCreate'
|
import BuilderBlockCreate from './BuilderBlockCreate'
|
||||||
|
import BuilderBlockCodeEditor from './BuilderBlockCodeEditor'
|
||||||
import BuilderBlockSetting from './BuilderBlockSetting'
|
import BuilderBlockSetting from './BuilderBlockSetting'
|
||||||
import Draggable from 'vuedraggable'
|
import Draggable from 'vuedraggable'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'BuilderBlockItem',
|
name: 'BuilderBlockItem',
|
||||||
|
components: {
|
||||||
|
BuilderBlockCreate,
|
||||||
|
BuilderBlockSetting,
|
||||||
|
BuilderBlockCodeEditor,
|
||||||
|
Draggable,
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
widgets: {
|
widgets: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -164,6 +179,9 @@ export default {
|
||||||
removeMe() {
|
removeMe() {
|
||||||
this.$emit('remove-item')
|
this.$emit('remove-item')
|
||||||
},
|
},
|
||||||
|
codeUpdate(nextValue) {
|
||||||
|
this.item.children = nextValue
|
||||||
|
},
|
||||||
removeBlock(key) {
|
removeBlock(key) {
|
||||||
let children = []
|
let children = []
|
||||||
|
|
||||||
|
@ -184,11 +202,6 @@ export default {
|
||||||
++this.blockKey
|
++this.blockKey
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
components: {
|
|
||||||
BuilderBlockCreate,
|
|
||||||
BuilderBlockSetting,
|
|
||||||
Draggable,
|
|
||||||
},
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.widget = this.widgets[this.item.widget]
|
this.widget = this.widgets[this.item.widget]
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue