The vue-quick-chat component allows you to insert a custom live chat on web applications.
Key Features:
- Handle on type event and on message submit
- Chat with multiple participants
- Support for async actions like message uploaded status
- Send images
- Support for profile pictures
How to use it:
1. Import the component and stylesheet into your project.
import { Chat } from 'vue-quick-chat' import '/path/to/vue-quick-chat.css';
2. Add the chat component to your template.
<template> <div id="app"> <div class="content"> <div class="chat-container"> <Chat v-if="visible" :participants="participants" :myself="myself" :messages="messages" :chat-title="chatTitle" :placeholder="placeholder" :colors="colors" :border-style="borderStyle" :hide-close-button="hideCloseButton" :close-button-icon-size="closeButtonIconSize" :submit-icon-size="submitIconSize" :submit-image-icon-size="submitImageIconSize" :load-more-messages="toLoad.length > 0 ? loadMoreMessages : null" :async-mode="asyncMode" :scroll-bottom="scrollBottom" :display-header="true" :send-images="true" :profile-picture-config="profilePictureConfig" @onImageClicked="onImageClicked" @onImageSelected="onImageSelected" @onMessageSubmit="onMessageSubmit" @onType="onType" @onClose="onClose('param value')"/> </div> <div class="external-controller"> <div class="controller-btn-container"> <button class="btn-message" @click="addMessage">Add menssage</button> <button class="btn-participant" @click="addParticipant">Add participant</button> <button class="btn-participant" @click="changeAllProps">Change All Props</button> </div> <div class="message-list"> <ol> <li v-for="(message, index) in messages" :key="index"> {{message.content}} </li> </ol> </div> </div> </div> </div> </template>
3. Register the component and initialize your data as follows:
export default { name: 'app', components: { Chat }, data() { return { visible: true, participants: [ { name: 'Arnaldo', id: 1, profilePicture: 'https://upload.wikimedia.org/wikipedia/en/thumb/a/a1/NafSadh_Profile.jpg/768px-NafSadh_Profile.jpg' }, { name: 'Adam', id: 2, profilePicture: 'https://lh3.googleusercontent.com/-G1d4-a7d_TY/AAAAAAAAAAI/AAAAAAAAAAA/AAKWJJPez_wX5UCJztzEUeCxOd7HBK7-jA.CMID/s83-c/photo.jpg' } ], myself: { name: 'John Doe', id: 3, profilePicture: 'https://lh3.googleusercontent.com/-G1d4-a7d_TY/AAAAAAAAAAI/AAAAAAAAAAA/AAKWJJPez_wX5UCJztzEUeCxOd7HBK7-jA.CMID/s83-c/photo.jpg' }, messages: [ { content: "Really?! I don't care! Haha", participantId: 1, timestamp: {year: 2012, month: 3, day: 5, hour: 20, minute: 10, second: 3, millisecond: 123}, uploaded: true, viewed: true, type: 'text' }, { content: "Really?! I don't care! Haha", participantId: 1, timestamp: {year: 2012, month: 3, day: 5, hour: 20, minute: 10, second: 3, millisecond: 123}, uploaded: true, viewed: true, type: 'text' }, { content: "Really?! I don't care! Haha", participantId: 1, timestamp: {year: 2012, month: 3, day: 5, hour: 20, minute: 10, second: 3, millisecond: 123}, uploaded: true, viewed: true, type: 'text' }, { content: "Hey, Jhon Doe! How are you today", participantId: 1, timestamp: {year: 2012, month: 3, day: 5, hour: 20, minute: 10, second: 3, millisecond: 123}, uploaded: true, viewed: true, type: 'text' }, { content: "Hey, Adam! I'm felling really fine this evening.", participantId: 3, timestamp: {year: 2012, month: 3, day: 5, hour: 20, minute: 10, second: 3, millisecond: 123}, uploaded: true, viewed: true, type: 'text' }, { content: "Really?! I don't care! Haha", participantId: 1, timestamp: {year: 2012, month: 3, day: 5, hour: 20, minute: 10, second: 3, millisecond: 123}, uploaded: true, viewed: true, type: 'text' }, ], chatTitle: 'My chat title', placeholder: 'send your message', colors: { header: { bg: '#d30303', text: '#fff' }, message: { myself: { bg: '#fff', text: '#525252' }, others: { bg: '#fb4141', text: '#fff' }, messagesDisplay: { //bg: 'rgb(247, 243, 243)', //bg: '#f7f3f3' bg: '#f7f3f3' } }, submitIcon: '#b91010', submitImageIcon: '#b91010', }, borderStyle: { topLeft: "10px", topRight: "10px", bottomLeft: "10px", bottomRight: "10px", }, hideCloseButton: false, submitIconSize: 24, submitImageIconSize: 24, closeButtonIconSize: "20px", asyncMode: true, toLoad: [ { content: 'Hey, John Doe! How are you today?', participantId: 2, timestamp: { year: 2016, month: 3, day: 5, hour: 10, minute: 10, second: 3, millisecond: 123 }, uploaded: true, viewed: true }, { content: "Hey, Adam! I'm feeling really fine this evening.", participantId: 3, timestamp: { year: 2016, month: 1, day: 5, hour: 19, minute: 10, second: 3, millisecond:123 }, uploaded: true, viewed: true }, ], scrollBottom: { messageSent: true, messageReceived: false }, profilePictureConfig: { others: true, myself: true, styles: { width: '30px', height: '30px', borderRadius: '50%' } } } }, methods: { // eslint-disable-next-line onType: function (e) { // eslint-disable-next-line console.log('typing'); }, loadMoreMessages(resolve) { setTimeout(() => { resolve(this.toLoad); //Make sure the loaded messages are also added to our local messages copy or they will be lost this.messages.unshift(...this.toLoad); this.toLoad = []; }, 1000); }, onMessageSubmit(message) { /* * example simulating an upload callback. * It's important to notice that even when your message wasn't send * yet to the server you have to add the message into the array */ this.messages.push(message); /* * you can update message state after the server response */ // timeout simulating the request setTimeout(() => { message.uploaded = true message.viewed = true }, 2000) }, onClose(param) { console.log(param) this.visible = false; }, addMessage() { /* this.messages.push( { content: "Update state", // myself: false, participantId: 1, timestamp: {year: 2014, month: 3, day: 5, hour: 20, minute: 10, second: 3, millisecond: 123}, uploaded: true, viewed: true } ) */ this.messages.push( { type: 'image', preview: null, src: 'https://149364066.v2.pressablecdn.com/wp-content/uploads/2017/03/vue.jpg', content: 'image', participantId: 1, timestamp: {year: 2014, month: 3, day: 5, hour: 20, minute: 10, second: 3, millisecond: 123}, uploaded: true, viewed: false } ) }, addParticipant() { let participant = { name: 'Karl', id: 4 }; this.participants.push(participant) }, changeAllProps() { this.myself = { name: 'I Qanah', id: 3 }; this.participants = [ { name: 'Ibrahim', id: 5 }, { name: 'Ana', id: 6 } ]; this.messages = [ { content: "Really?! I don't care! Haha", participantId: 5, timestamp: {year: 2012, month: 3, day: 5, hour: 20, minute: 10, second: 3, millisecond: 123}, uploaded: true, viewed: true }, { content: "Really?! I don't care! Haha", participantId: 6, timestamp: {year: 2012, month: 3, day: 5, hour: 20, minute: 10, second: 3, millisecond: 123}, uploaded: true, viewed: true }, { content: "Really?! I don't care! Haha", participantId: 3, timestamp: {year: 2012, month: 3, day: 5, hour: 20, minute: 10, second: 3, millisecond: 123}, uploaded: true, viewed: true } ]; this.toLoad = [ { content: 'Hey, John Doe! How are you today?', participantId: 6, timestamp: { year: 2016, month: 3, day: 5, hour: 10, minute: 10, second: 3, millisecond: 123 }, uploaded: true, viewed: true }, { content: "Hey, Adam! I'm feeling really fine this evening.", participantId: 3, timestamp: { year: 2016, month: 10, day: 5, hour: 19, minute: 10, second: 3, millisecond:123 }, uploaded: true, viewed: true }, ]; this.chatTitle = 'Change All Participants'; this.placeholder = 'اكتب رسالتك هنا'; }, onImageSelected({file, message}){ let src = 'https://149364066.v2.pressablecdn.com/wp-content/uploads/2017/03/vue.jpg' this.messages.push(message); /** * This timeout simulates a requisition that uploads the image file to the server. * It's up to you implement the request and deal with the response in order to * update the message status and the message URL */ setTimeout((res) => { message.uploaded = true message.src = res.src }, 3000, {src}); }, onImageClicked(message){ /** * This is the callback function that is going to be executed when some image is clicked. * You can add your code here to do whatever you need with the image clicked. A common situation is to display the image clicked in full screen. */ console.log('Image clicked', message.src) } } }
4. All possible props.
participants: { type: Array, required: true }, messages: { type: Array, required: true }, myself: { type: Object, required: true }, chatTitle: { type: String, required: false, default: '' }, placeholder: { type: String, required: false, default: 'type your message here' }, colors: { type: Object, required: true }, borderStyle: { type: Object, required: false, default: () => { return { topLeft: "10px", topRight: "10px", bottomLeft: "10px", bottomRight: "10px", } } }, hideCloseButton: { type: Boolean, required: false, default: false }, submitIconSize: { type: Number, required: false, default: 24 }, submitImageIconSize: { type: Number, required: false, default: 24 }, closeButtonIconSize: { type: String, required: false, default: "15px" }, asyncMode: { type: Boolean, required: false, default: false }, loadMoreMessages: { type: Function, required: false, default: null }, scrollBottom:{ type: Object, required: false, default: () => { return { messageSent: true, messageReceived: false } } }, displayHeader: { type: Boolean, required: false, default: true }, sendImages: { type: Boolean, required: false, default: true }, profilePictureConfig: { type: Object, required: false, default: () => { return { others: true, myself: false, styles: { width: '25px', height: '25px', borderRadius: '50%' } } } }
Preview:
Changelog:
v1.2.8 (02/07/2021)
- Fix setMessages
v1.2.7 (01/16/2021)
- Remove package-lock and Fix null timestamps to current time
v1.2.6 (09/12/2020)
- Add verification regex to avoid empty messages
v1.2.5 (08/01/2020)
- Implement acceptImageTypes prop
Download Details:
Author: MatheusrdSantos
Live Demo: https://github.com/MatheusrdSantos/vue-quick-chat/tree/master/public
Download Link: https://github.com/MatheusrdSantos/vue-quick-chat/archive/master.zip
Official Website: https://github.com/MatheusrdSantos/vue-quick-chat
Install & Download: