So I am struggling with a mini page builder I am working on. I have the following code I am working on:
JavaScript/Vue Code:
// Import Vue Data
import Vue from 'vue';
import $ from 'jquery';
import draggable from 'vuedraggable'
// Import page builder registry
import Registry from '../pagebuilder/registry.json';
// Import page builder components
import ComTextBlock from '../pagebuilder/com-text-block.vue';
// Setup new Vuex store
window.vm.content_pages_create = new Vue({
el: '#content-pages-create',
name: 'PageBuilder - Create',
store: window.vm.store,
components: {
draggable,
ComTextBlock,
},
data: {
// Define pagebuilder registry
registry: Registry,
// Define the page content
page: {
elements: 0,
rows: [],
},
},
methods: {
// Add new page row
addPageRow: function(index) {
var under_index = (typeof index === 'undefined' ? false : parseInt(index));
var element_id = this.page.elements++;
this.page.rows.push({
id: element_id,
text: 'Hello #' + element_id,
columns: [
{id: this.page.elements++, width: 6, components: []},
{id: this.page.elements++, width: 6, components: [
{
id: this.page.elements++,
data: {},
name: 'Text Block',
},
{
id: this.page.elements++,
data: {},
name: 'Text Block',
},
]},
],
});
},
// Remove page row
removeRow: function(index) {
if (window.confirm('Are you sure you want to remove this row and it\'s contents?')) {
this.page.rows.splice(index, 1);
}
},
// Clone component
cloneComponent: function(row_index, col_index, com_index) {
var component = JSON.parse(JSON.stringify(this.page.rows[row_index].columns[col_index].components[com_index]));
this.page.rows[row_index].columns[col_index].components.push(component);
},
// Remove component
removeComponent: function(row_index, col_index, com_index) {
if (window.confirm('Are you sure you want to remove this component?')) {
this.page.rows[row_index].columns[col_index].components.splice(com_index, 1);
}
},
// Resolve a component name
resolveComponentName: function(component) {
var cname = component.name.split(' ');
for (var i = 0; i < cname.length; i++) {
cname[i] = cname[i].toLowerCase().charAt(0).toUpperCase() + cname[i].slice(1);
}
return 'Com' + cname.join('');
},
},
mounted: function() {
// Hide sidebar by default
this.$store.dispatch('toggleSidebar', false);
$('#content').addClass('sidebar-hidden');
},
});
HTML / Twig Code:
{% block body %}
<div class="content-container" id="content-pages-create">
<br /><br /><br />
<button @click="addPageRow()">Add Row</button>
<br /><br /><br />
<!-- Page Holder -->
<div class="page-holder" v-cloak>
<draggable v-model="page.rows" handle=".control-drag">
<transition-group>
<!-- Row Item -->
<div class="row-item" v-for="(row, row_index) in page.rows" :key="row.id">
<!-- Row Controls -->
<div class="row-controls">
<div class="control-item control-drag"><i class="fas fa-arrows-alt"></i></div>
<div class="control-item control-pause"><i class="fas fa-pause"></i></div>
<div class="control-item control-clone"><i class="fas fa-clone"></i></div>
<div class="control-item control-edit"><i class="fas fa-edit"></i></div>
<div class="control-item control-trash" @click="removeRow(row_index)"><i class="fas fa-trash"></i></div>
</div>
<!-- Row Inner -->
<div class="row-inner">
<!-- Column Item -->
<div class="column-item" :class="['column-width-' + column.width]" v-for="(column, col_index) in row.columns">
<!-- Column Inner -->
<div class="column-inner">
<!-- Component Item (Draggable) -->
<draggable v-model="column.components" :group="'column.components'" handle=".control-dragcom">
<!-- Component Placeholder -->
<div class="component-placeholder" v-if="column.components.length === 0">
<p class="text">+</p>
</div>
<!-- Component Item -->
<div class="component-item" v-for="(component, com_index) in column.components" :key="component.id">
<!-- Component Controls -->
<div class="component-controls">
<div class="control-item control-dragcom"><i class="fas fa-arrows-alt"></i></div>
<div class="control-item control-clone" @click="cloneComponent(row_index, col_index, com_index)"><i class="fas fa-clone"></i></div>
<div class="control-item control-edit"><i class="fas fa-edit"></i></div>
<div class="control-item control-trash" @click="removeComponent(row_index, col_index, com_index)"><i class="fas fa-trash"></i></div>
</div>
<!-- Component Inner -->
<div class="component-inner">
<component :is="resolveComponentName(component)"></component>
</div>
</div>
</draggable>
</div>
</div>
</div>
</div>
</transition-group>
</draggable>
</div>
</div>
{% endblock %}
The rows move as expected, so I can fully move the rows around as expected and sort them and it works and it updates the array correctly. But when I try to move the components around, it seems to move as expected, so you can move it around, but when you let go of the drag, it does not stay there, and reverts back to what it was.
I did some looking around but could not find any ways of fixing it.
I am really stuck on what to do, so any suggestions or help would be amazing, thanks in advance.
This is due to the placeholder, the content of the default slot should match exactly the list. You can try something like:
<!-- Component Placeholder -->
<div class="component-placeholder" v-if="column.components.length === 0">
<p class="text">+</p>
</div>
<draggable v-else v-model="column.components" :group="'column.components'" handle=".control-dragcom">
<!-- Component Item -->
<div class="component-item" v-for="(component, com_index) in column.components" :key="component.id">
<!-- Component Controls -->
<div class="component-controls">
<div class="control-item control-dragcom"><i class="fas fa-arrows-alt"></i></div>
<div class="control-item control-clone" @click="cloneComponent(row_index, col_index, com_index)"><i class="fas fa-clone"></i></div>
<div class="control-item control-edit"><i class="fas fa-edit"></i></div>
<div class="control-item control-trash" @click="removeComponent(row_index, col_index, com_index)"><i class="fas fa-trash"></i></div>
</div>
<!-- Component Inner -->
<div class="component-inner">
<component :is="resolveComponentName(component)"></component>
</div>
</div>
</draggable>