<template> <span ref="editable" :contenteditable="editing" @input="update" @keydown="keydown" @focusout="change" @paste="paste" ></span> </template> <script> import { focusEnd } from '@/utils/dom' export default { name: 'Editable', props: { value: { type: String, required: true } }, data() { return { editing: false, oldValue: '', newValue: '' } }, mounted() { this.set(this.value) }, methods: { /** * This method must be called when the is going to be edited. It will enable the * contenteditable state and will focus the element. */ edit() { this.editing = true this.$nextTick(() => { focusEnd(this.$refs.editable) }) }, /** * This method is called when the value has changed and needs to be saved. It will * change the editing state and will emit a change event if the new value has * changed. */ change() { this.editing = false if (this.oldValue === this.newValue) { return } this.$emit('change', { oldValue: this.value, value: this.newValue }) this.oldValue = this.newValue }, /** * Everytime a key is pressed inside the editable this event will be trigger which * will update the new value. */ update(event) { const target = event.target const text = target.textContent this.newValue = text }, /** * When someone pastes something we want to only insert the plain text instead of * the styled content. */ paste(event) { event.preventDefault() const text = (event.originalEvent || event).clipboardData.getData( 'text/plain' ) document.execCommand('insertHTML', false, text) }, /** * If a key is pressed and it is an enter or esc key the change event will be called * to end the editing and save the value. */ keydown(event) { if (event.keyCode === 13 || event.keyCode === 27) { event.preventDefault() this.change() return false } }, /** * */ set(value) { this.oldValue = this.value this.newValue = this.value this.$refs.editable.innerText = this.value } } } </script>