Vue - Nested Draggable with Properties Dialog V5 figma clone concepts

Vue using 'emit' event coupling. For PE Properties dialog and draggable blocks and Block Treeview. You select more than one block and edit properties together. Selection order matters atm. Also uses a single component to wrapper all blocks with a single template with one set of code. Also prototype component Master/clone concept

by Ben Clayton

HTML

<script src="https://unpkg.com/vue"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0-beta/css/materialize.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0-beta/js/materialize.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/Sortable.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Vue.Draggable/2.16.0/vuedraggable.min.js"></script>
The Master Clone concept is only implmented with 'width' atm. just to show how it works.
<div id="preview">
  <div class="collection with-header">
      <draggable :list="blocks"
                  :group="options"
             class="dragArea"
                  :move="beforeMove"
                  @end="onEnd">
         <component  v-for="(block,idx) in blocks" :is="block.componentId" :block="block" :key="idx"></component>
      </draggable>
   </div>
</div>
<hr>
<div id="propdialog">
  
    Properties: ({{blkname}}) {{blocks.length}} selected <br>
  
  <vue-propedit v-for="(option,idx) in options" :option="option" :blocks="blocks" :trigger="trigger" :block="block" :key="idx"></vue-propedit>

</div>
<div id="treedialog">
  
    Blocks: <br>
  
  <vue-treenode v-for="(block,idx) in blocks" :block="block" :key="idx"></vue-treenode>

</div>

CSS

.textbox-edit {
  display: block;
}

.textbox-edit label {
  display: inline-block;
  margin-right: 10px;
  width: 40px;
  text-transform: capitalize;
}
.textbox-edit input {
  width: 30%;
  font-size: 10px;
  height: 20px;
}

.select-edit select {
  display: inline-block;
  font-size: 10px;
  line-height: 12px;
  width: 40px;
  height: 20px;
  padding: 1px;
}

.color-edit {
  font-size: 10px;
}

.color-edit fieldset {
  border-width: 1px;
}

.color-edit label {
  width: 230px;
}

.color-edit fieldset legend {
  font-size: 10px;
}

#propdialog {
  display: inline-block;
  transition: all 1s ease;
  width: 50%;
  border: 1px solid gray;
  padding: 10px;
  margin: 10px;
  vertical-align: top;
}

.propedit {
  margin-bottom: 0px;
  display: inline-block;
}

.propedit .color-edit {
  width: 90%;
}

.color-edit input[type=range] {
  width: 20%;
}

#preview {
  margin-top: 30px;
  position: relative;
}

/* ------------------------------------- */
.block {
  overflow: hidden;
  position:relative;
  overflow: visible;
  margin: 10px;
}

.block.selected::before {
  display:block;
  content:'';
  position: absolute;
  top: 0;
  right: 0;
  width: 100%;
  height: 100%;
  font-size: 24px;
  border: 2px solid blue;
 }
  
  
.vue-box-block {
  padding: 5px;
}

.vue-wrapper-block {
  padding: 5px;
  position: relative;
}

.vue-wrapper-block > div {
  xxxposition: absolute;
}


.vue-circle-block {
  border: 1px solid gray;
  border-radius: 50%;
  padding-top: 19px;
  padding-left: 10px;
}

.vue-box-block.selected::before {
  border-color: blue;
}

.vue-wrapper-block.selected::before {
  border-color: blue;
}

.vue-circle-block.selected::before {
  border-color: red;
}

/* ------------------------- */
#treedialog {
  display: inline-block;
  transition: all 1s ease;
  width: 35%;
  border: 1px solid gray;
  padding: 5px;
  margin: 10px;
  vertical-align: top;
}

.treenode {
  margin-left: 20px;
  cursor: pointer;
  font-size: 10px;
}

.treenode::before {
  content: '';
  display:...

JavaScript

// Code copyright Holograph   2020


var global = {

  options1: {
    name: 'block',
    pull: true,
    put: true
  },
  options2: {
    name: 'block',
    pull: true,
    put: true
  }
}

// Some globals which should be now in a store  (Vuex)
//xblocks = [];
blockptr = [];



// Property Dialog Configs
// Here you can predefine menu controls her once
// Otherwise you would have to have this config repeated on each class where you wanted
// to allow edit of the same functional property

// 'width' does not need to be defined as the property dialog cleverly defaults a lot from
//  the name making config on the fly of the following
//  width: {
//    control: "textbox-edit",
//    propertyName: "width",
//    label: "width"   // CSS capitalises this to 'Width'
//  }
//
// 
var menuitems = {
  importance: {
    control: "select-edit",
    propertyName: "importance",
    label: "Importance",
    options: "h1:h1,h2:h2,h3:h3,h4:h4,h5:h5,h6:h6"
  },
  positioning: {
    control: "select-edit",
    propertyName: "positioning",
    label: "Positioning",
    options: "absolute:Absolute,relative:Relative"
  }
}


// Property Dialog Control components
// vue-propedit - is a base component for all controls
// vue-textbox-edit - textbox edit control
// vue-select-edit - dropdown edit control
// vue-color-edit - color edit control

// ==============================================================================
// Vue components


Vue.component('vue-treedialog', {
  props: ["blocks"],
  template: `
  	<div id="treedialog">
      	Blocks: <br>
    		<vue-treenode v-for="(block,idx) in blocks" :block="block" :key="idx"></vue-treenode>
		</div>  
      `,
});

Vue.component('vue-treenode', {
  props: ["block"],
  data: function() {
    return {
      "blocks": this.block.blocks
    }
  },
  template: `
  	<div class="treenode" :class="{selected: block.selected}"  @click.stop="block.select($event)"> 
      	{{block.name}} {{block.idx}}<br>
        <vue-treenode...