diff --git a/.gitignore b/.gitignore index 1c751e9d..00a257af 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ yarn-error.log* # Misc .ghnpmpkgtoken +package-lock.json diff --git a/README.md b/README.md index 65473b7a..fb057736 100644 --- a/README.md +++ b/README.md @@ -178,6 +178,7 @@ For more detailed examples see the demo folder. | *close | Function | The function passed to the component that mutates the above mentioned bool toggle for closing the chat | | messageList | [message] | An array of message objects to be rendered as a conversation. | | showEmoji | Boolean | A bool indicating whether or not to show the emoji button +| sendEmojisDirectly | Boolean | A bool indicating whether or not to send emojis directly as a message as opposed to inserting them into the user input | showFile | Boolean | A bool indicating whether or not to show the file chooser button | showDeletion | Boolean | A bool indicating whether or not to show the edit button for a message | showConfirmationDeletion | Boolean | A bool indicating whether or not to show the confirmation text when we remove a message diff --git a/src/ChatWindow.vue b/src/ChatWindow.vue index 21e992dc..5702cfc6 100644 --- a/src/ChatWindow.vue +++ b/src/ChatWindow.vue @@ -49,6 +49,7 @@ false + }, + sendEmojisDirectly: { + type: Boolean, + default: () => true }, showEdition: { type: Boolean, diff --git a/src/UserInput.vue b/src/UserInput.vue index c73b3989..6558563e 100644 --- a/src/UserInput.vue +++ b/src/UserInput.vue @@ -121,6 +121,10 @@ export default { type: Boolean, default: () => false }, + sendEmojisDirectly: { + type: Boolean, + default: () => true + }, suggestions: { type: Array, default: () => [] @@ -145,7 +149,8 @@ export default { data() { return { file: null, - inputActive: false + inputActive: false, + previousSelectionRange: null } }, computed: { @@ -172,6 +177,24 @@ export default { this.focusUserInput() } }) + + document.addEventListener('selectionchange', () => { + var selection = document.getSelection() + if ( + !selection || + !selection.anchorNode || + (selection.anchorNode != this.$refs.userInput && + selection.anchorNode.parentNode != this.$refs.userInput) + ) { + return + } + + if (selection.rangeCount) { + this.previousSelectionRange = selection.getRangeAt(0).cloneRange() + } else { + this.previousSelectionRange = null + } + }) }, methods: { cancelFile() { @@ -268,6 +291,13 @@ export default { } }, _handleEmojiPicked(emoji) { + if (this.sendEmojisDirectly) { + this._submitEmoji(emoji) + } else { + this._insertEmoji(emoji) + } + }, + _submitEmoji(emoji) { this._checkSubmitSuccess( this.onSubmit({ author: 'me', @@ -276,6 +306,29 @@ export default { }) ) }, + _insertEmoji(emoji) { + var range = this.previousSelectionRange + if (!range) { + if (!this.$refs.userInput.firstChild) { + this.$refs.userInput.append(document.createTextNode('')) + } + range = document.createRange() + range.setStart(this.$refs.userInput.firstChild, this.$refs.userInput.textContent.length) + range.collapse(true) + } + + var selection = window.getSelection() + selection.removeAllRanges() + selection.addRange(range) + + var textNode = document.createTextNode(emoji) + + range.deleteContents() + range.insertNode(textNode) + range.collapse(false) + + this.$refs.userInput.focus() + }, _handleFileSubmit(file) { this.file = file },