mirror of
https://gitlab.com/bramw/baserow.git
synced 2024-11-21 23:37:55 +00:00
157 lines
4.0 KiB
Vue
157 lines
4.0 KiB
Vue
<template>
|
|
<span
|
|
ref="editable"
|
|
:contenteditable="editing"
|
|
:placeholder="placeholder"
|
|
:class="{
|
|
'forced-user-select-initial': editing,
|
|
'whitespace-pre-wrap': multiline,
|
|
'editable--multi-line': multiline,
|
|
}"
|
|
@input="update"
|
|
@keydown="keydown"
|
|
@focusout="change"
|
|
@paste="paste"
|
|
>{{ value }}</span
|
|
>
|
|
</template>
|
|
|
|
<script>
|
|
import { focusEnd } from '@baserow/modules/core/utils/dom'
|
|
|
|
export default {
|
|
name: 'Editable',
|
|
props: {
|
|
value: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
placeholder: {
|
|
type: String,
|
|
required: false,
|
|
default: '',
|
|
},
|
|
multiline: {
|
|
type: Boolean,
|
|
required: false,
|
|
default: false,
|
|
},
|
|
},
|
|
data() {
|
|
return {
|
|
editing: false,
|
|
oldValue: '',
|
|
newValue: '',
|
|
oldTextOverflow: '',
|
|
}
|
|
},
|
|
watch: {
|
|
value(value) {
|
|
this.set(value)
|
|
},
|
|
},
|
|
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.$emit('editing', true)
|
|
this.$nextTick(() => {
|
|
focusEnd(this.$refs.editable)
|
|
// In almost all use cases, the parent has overflow hidden and in that case we
|
|
// need to see if the scrollLeft must be changed so that we can see the cursor
|
|
// which has been placed at the end.
|
|
const parent = this.$el.parentElement
|
|
parent.scrollLeft = parent.scrollWidth - parent.clientWidth
|
|
parent.classList.add('forced-text-overflow-initial')
|
|
})
|
|
},
|
|
/**
|
|
* 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
|
|
this.$emit('editing', false)
|
|
this.$nextTick(() => {
|
|
// In almost all use cases, the parent has overflow hidden and it could be that
|
|
// because of the cursor, the scrollLeft value has changed. Here we change it
|
|
// back to what is was before.
|
|
const parent = this.$el.parentElement
|
|
parent.classList.remove('forced-text-overflow-initial')
|
|
if (parent.scrollWidth > parent.clientWidth) {
|
|
parent.scrollLeft = 0
|
|
}
|
|
})
|
|
|
|
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
|
|
let text = target.textContent
|
|
|
|
if (this.multiline && target.innerText !== undefined) {
|
|
// textContent doesn't support new lines
|
|
// so we need innerText in multiline mode
|
|
text = target.innerText
|
|
}
|
|
|
|
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('insertText', 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) {
|
|
// Allow users to create new lines with Shift+Enter
|
|
if (event.key === 'Enter' && event.shiftKey && this.multiline) {
|
|
return false
|
|
}
|
|
|
|
if (event.key === 'Enter' || event.key === 'Escape') {
|
|
event.preventDefault()
|
|
this.change()
|
|
return false
|
|
}
|
|
},
|
|
/**
|
|
*
|
|
*/
|
|
set(value) {
|
|
this.oldValue = this.value
|
|
this.newValue = this.value
|
|
this.$refs.editable.textContent = this.value
|
|
},
|
|
},
|
|
}
|
|
</script>
|