Items list editor

by Soviut

HTML

<link href="https://cdnjs.cloudflare.com/ajax/libs/quill/1.3.6/quill.core.min.css" rel="stylesheet">
<link href="https://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet">
<script src="https://cdn.quilljs.com/1.3.6/quill.js"></script>

<div id="app">
  <div v-for="section, i in sections">
    <div class="actions">
      <button class="actions__button" @click="addSection('text', i)">Add Text</button>
      <button class="actions__button" @click="addSection('image', i)">Add Image</button>
    </div>

    <div class="section">
      <header class="section__header">
        <button @click="deleteSection(i)">Delete</button>
        <button @click="moveSectionUp(i)" :disabled="i === 0">Move Up</button>
        <button @click="moveSectionDown(i)" :disabled="i === sections.length - 1">Move Down</button>
      </header>
      
      <div class="section__body">
        <template v-if="section.type === 'text'">
          <div ref="editors"></div>
        </template>
        <template v-if="section.type === 'image'">
          <img :src="section.body">
        </template>
      </div>
    </div>
  </div>

  <div class="actions">
    <button class="actions__button" @click="addSection('text')">Add Text</button>
    <button class="actions__button" @click="addSection('image')">Add Image</button>
  </div>
</div>

CSS

body {
  background: #FFF;
  padding: 20px;
  font-family: arial, Helvetica, san-serif;
}

.section {
  margin-top: 10px;

  border: solid 1px #CCC;
}

.section__header {
  padding: 10px;
  
  border-bottom: solid 1px #CCC;
}

.section__body {
  padding: 10px;
}

.section__body > img {
  width: 100%;
}

.actions {
  display: flex;
  margin-top: 10px;
}

.actions__button {
  display: block;
  width: 100%;
  padding: 10px;
  
  border: 0;
  border-radius: 4px;
  background: #336699;
  color: #FFF;
}

Vue

let arrayMove = (arr, from, to) => arr.splice(to, 0, arr.splice(from, 1)[0])

const editorOptions = {
  theme: 'snow',
  modules: {
    toolbar: ['bold', 'italic', 'underline'],
  }
}

new Vue({
  el: "#app",
  data: {
    sections: [],
  },
  methods: {
    addSection(typ, i) {
    	console.log(i)
    
    	let body = ''
      let id = Math.abs(Math.random()).toString().replace('.', '')
    
    	if (typ === 'text') {
	      body = `${id} this is some text.`
      }
      else if (typ === 'image') {
        body = `https://via.placeholder.com/350x150?text=${id}`
      }

			let section = {
        id: id,
        type: typ,
      	body: body
      }

			if (typeof i === 'undefined') {
        this.sections.push(section)
      }
      else {
        this.sections.splice(i, 0, section)
      }

			if (typ === 'text') {
        Vue.nextTick(() => {
          if (typeof i === 'undefined') {
            new Quill(this.$refs.editors[this.$refs.editors.length - 1], editorOptions)
          }
          else {
            new Quill(this.$refs.editors[i + 1], editorOptions)
          }
        })
      }
    },
    
    deleteSection(i) {
    	this.sections.splice(i, 1)
    },
    
    moveSectionUp(i) {
    	if (i > 0) {
      	arrayMove(this.sections, i, i - 1)
      }
    },
    
    moveSectionDown(i) {
    	if (i < this.sections.length - 1) {
      	arrayMove(this.sections, i, i + 1)
      }
    }
  }
})