-
Get Started
-
Vue Components
- App
- Accordion
- Action Sheet / Actions
- Badge
- Block / Content Block
- Button
- Cards
- Checkbox
- Chips / Tags
- Contacts List
- Floating Action Button / FAB
- Gauge
- Grid / Layout Grid
- Icon
- Inputs / Form Inputs
- Link
- List View
- List Item
- List Button
- List Index
- Login Screen
- Messagebar
- Messages
- Navbar
- Page
- Panel / Side Panels
- Photo Browser
- Popover
- Popup
- Preloader
- Progressbar
- Radio
- Range Slider
- Searchbar
- Sheet Modal
- Smart Select
- Sortable
- Statusbar
- Stepper
- Subnavbar
- Swiper
- Swipeout
- Tabs
- Toggle
- Toolbar / Tabbar
- View
- Virtual List
Messages Component
Messages Vue component represents Messages component.
Messages Components
There are following components included:
f7-messages
- main Messages containerf7-message
- single message elementf7-messages-title
- single messages title element
Messages Properties
Prop | Type | Default | Description |
---|---|---|---|
<f7-messages> properties | |||
init | boolean | true | Initializes Messages component |
new-messages-first | boolean | false | Enable if you want to use new messages on top, instead of having them on bottom |
scroll-messages | boolean | true | Enable/disable messages autoscrolling when adding new message |
scroll-messages-on-edge | boolean | true | If enabled then messages autoscrolling will happen only when user is on top/bottom of the messages view |
<f7-message> properties | |||
type | string | sent | Message type: sent (default) or received |
text | string | Message text | |
avatar | string | Message user's avatar URL | |
name | string | Message user's name | |
image | string | Message image URL | |
header | string | Message header | |
footer | string | Message footer | |
text-header | string | Message text header | |
text-footer | string | Message text footer | |
first | boolean | false | Defines that the message is first in the conversation |
last | boolean | false | Defines that the message is last in the conversation |
tail | boolean | false | Defines that the message has visual "tail". Usually last message in conversation |
same-name | boolean | false | Defines that this message sender name is the same as on previous message |
same-header | boolean | false | Defines that this message header text is the same as on previous message |
same-footer | boolean | false | Defines that this message footer text is the same as on previous message |
same-avatar | boolean | false | Defines that this message user's avatar URL is the same as on previous message |
Messages Methods
<f7-messages> methods | |
---|---|
.scroll(duration, position); | Scroll messages to top/bottom depending on newMessagesFirst parameter
|
.showTyping(message); | Show typing message indicator
|
.hideTyping() | Hide typing message indicator |
Messages Events
Event | Description |
---|---|
<f7-message> events | |
click | Event will be triggered when user clicks on message bubble |
click:name | Event will be triggered when user clicks on message user's name |
click:text | Event will be triggered when user clicks on message text |
click:avatar | Event will be triggered when user clicks on message user's avatar |
click:header | Event will be triggered when user clicks on message header |
click:footer | Event will be triggered when user clicks on message footer |
click:bubble | Event will be triggered when user clicks on message bubble |
Messages Slots
Single message Vue component (<f7-message>
) has additional slots for custom elements:
default
- element will be inserted as a child of<div class="message-bubble">
element in the endstart
- element will be inserted in the beginning and direct child of main message element<div class="message">
end
- element will be inserted in the end and direct child of main message element<div class="message">
content-start
- element will be inserted in the beginning and direct child of the<div class="message-content">
elementcontent-end
- element will be inserted in the end and direct child of the<div class="message-content">
elementbubble-start
- element will be inserted in the beginning and direct child of the<div class="message-bubble">
elementbubble-end
- element will be inserted in the end and direct child of the<div class="message-bubble">
element. Same asdefault
slot
The following slots can be used inside of single message instead of same props if you need to pass there more complext layout:
header
- element will be inserted in message headerfooter
- element will be inserted in message footertext
- element will be inserted in message textname
- element will be inserted in message nameimage
- element will be inserted in message image (supposed tp be an<img>
element)text-header
- element will be inserted in message text headertext-footer
- element will be inserted in message text footer
<f7-message
type="sent"
text="Hello World"
name="John Doe"
avatar="path/to/image.jpg"
>
<div slot="start">Start</div>
<div slot="end">End</div>
<div slot="content-start">Content Start</div>
<div slot="content-end">Content End</div>
<div slot="bubble-start">Bubble Start</div>
<div slot="bubble-end">Bubble End</div>
</f7-message>
<!-- Renders to: -->
<div class="message message-sent">
<div>Start</div>
<div class="message-avatar" style="background-image: url(path/to/image.jpg);"></div>
<div class="message-content">
<div>Content Start</div>
<div class="message-name">John Doe</div>
<div class="message-bubble">
<div>Bubble Start</div>
<div class="message-text">Hello World</div>
<div>Bubble End</div>
</div>
<div>Content End</div>
</div>
<div>End</div>
</div>
Access To Messages Instance
If you use automatic initalization to init Messages (with init:true
prop) and need to use Messages API you can access its initialized instance by accessing .f7Messages
component's property.
Examples
Here is how the full example of Messages page where it can be used together with Messagebar:
<template>
<f7-page>
<f7-navbar title="Messsages"></f7-navbar>
<f7-messagebar
:placeholder="placeholder"
ref="messagebar"
:attachments-visible="attachmentsVisible"
:sheet-visible="sheetVisible"
>
<f7-link
icon-ios="f7:camera_fill"
icon-md="material:camera_alt"
slot="inner-start"
@click="sheetVisible = !sheetVisible"
></f7-link>
<f7-link
icon-ios="f7:arrow_up_fill"
icon-md="material:send"
slot="inner-end"
@click="sendMessage"
></f7-link>
<f7-messagebar-attachments>
<f7-messagebar-attachment
v-for="(image, index) in attachments"
:key="index"
:image="image"
@attachment:delete="deleteAttachment(image)"
></f7-messagebar-attachment>
</f7-messagebar-attachments>
<f7-messagebar-sheet>
<f7-messagebar-sheet-image
v-for="(image, index) in images"
:key="index"
:image="image"
:checked="attachments.indexOf(image) >= 0"
@change="handleAttachment"
></f7-messagebar-sheet-image>
</f7-messagebar-sheet>
</f7-messagebar>
<f7-messages ref="messages">
<f7-messages-title><b>Sunday, Feb 9,</b> 12:58</f7-messages-title>
<f7-message
v-for="(message, index) in messagesData"
:key="index"
:type="message.type"
:image="message.image"
:name="message.name"
:avatar="message.avatar"
:first="isFirstMessage(message, index)"
:last="isLastMessage(message, index)"
:tail="isTailMessage(message, index)"
>
<span slot="text" v-if="message.text" v-html="message.text"></span>
</f7-message>
<f7-message v-if="typingMessage"
type="received"
:typing="true"
:first="true"
:last="true"
:tail="true"
:header="`${typingMessage.name} is typing`"
:avatar="typingMessage.avatar"
></f7-message>
</f7-messages>
</f7-page>
</template>
<script>
export default {
data() {
return {
attachments: [],
sheetVisible: false,
typingMessage: null,
messagesData: [
{
type: 'sent',
text: 'Hi, Kate',
},
{
type: 'sent',
text: 'How are you?',
},
{
name: 'Kate',
type: 'received',
text: 'Hi, I am good!',
avatar: 'http://lorempixel.com/100/100/people/9',
},
{
name: 'Blue Ninja',
type: 'received',
text: 'Hi there, I am also fine, thanks! And how are you?',
avatar: 'http://lorempixel.com/100/100/people/7',
},
{
type: 'sent',
text: 'Hey, Blue Ninja! Glad to see you ;)',
},
{
type: 'sent',
text: 'Hey, look, cutest kitten ever!',
},
{
type: 'sent',
image: 'http://lorempixel.com/200/260/cats/4/',
},
{
name: 'Kate',
type: 'received',
text: 'Nice!',
avatar: 'http://lorempixel.com/100/100/people/9',
},
{
name: 'Kate',
type: 'received',
text: 'Like it very much!',
avatar: 'http://lorempixel.com/100/100/people/9',
},
{
name: 'Blue Ninja',
type: 'received',
text: 'Awesome!',
avatar: 'http://lorempixel.com/100/100/people/7',
},
],
images: [
'http://lorempixel.com/300/300/cats/1/',
'http://lorempixel.com/200/300/cats/2/',
'http://lorempixel.com/400/300/cats/3/',
'http://lorempixel.com/300/150/cats/4/',
'http://lorempixel.com/150/300/cats/5/',
'http://lorempixel.com/300/300/cats/6/',
'http://lorempixel.com/300/300/cats/7/',
'http://lorempixel.com/200/300/cats/8/',
'http://lorempixel.com/400/300/cats/9/',
'http://lorempixel.com/300/150/cats/10/',
],
people: [
{
name: 'Kate Johnson',
avatar: 'http://lorempixel.com/100/100/people/9',
},
{
name: 'Blue Ninja',
avatar: 'http://lorempixel.com/100/100/people/7',
},
],
answers: [
'Yes!',
'No',
'Hm...',
'I am not sure',
'And what about you?',
'May be ;)',
'Lorem ipsum dolor sit amet, consectetur',
'What?',
'Are you sure?',
'Of course',
'Need to think about it',
'Amazing!!!',
],
responseInProgress: false,
};
},
computed: {
attachmentsVisible() {
const self = this;
return self.attachments.length > 0;
},
placeholder() {
const self = this;
return self.attachments.length > 0 ? 'Add comment or Send' : 'Message';
},
},
mounted() {
const self = this;
self.$f7ready(() => {
self.messagebar = self.$refs.messagebar.f7Messagebar;
self.messages = self.$refs.messages.f7Messages;
});
},
methods: {
isFirstMessage(message, index) {
const self = this;
const previousMessage = self.messagesData[index - 1];
if (message.isTitle) return false;
if (!previousMessage || previousMessage.type !== message.type || previousMessage.name !== message.name) return true;
return false;
},
isLastMessage(message, index) {
const self = this;
const nextMessage = self.messagesData[index + 1];
if (message.isTitle) return false;
if (!nextMessage || nextMessage.type !== message.type || nextMessage.name !== message.name) return true;
return false;
},
isTailMessage(message, index) {
const self = this;
const nextMessage = self.messagesData[index + 1];
if (message.isTitle) return false;
if (!nextMessage || nextMessage.type !== message.type || nextMessage.name !== message.name) return true;
return false;
},
deleteAttachment(image) {
const self = this;
const index = self.attachments.indexOf(image);
self.attachments.splice(index, 1)[0]; // eslint-disable-line
},
handleAttachment(e) {
const self = this;
const index = self.$$(e.target).parents('label.checkbox').index();
const image = self.images[index];
if (e.target.checked) {
// Add to attachments
self.attachments.unshift(image);
} else {
// Remove from attachments
self.attachments.splice(self.attachments.indexOf(image), 1);
}
},
sendMessage() {
const self = this;
const text = self.messagebar.getValue().replace(/\n/g, '<br>').trim();
const messagesToSend = [];
self.attachments.forEach((attachment) => {
messagesToSend.push({
image: attachment,
});
});
if (text.trim().length) {
messagesToSend.push({
text,
});
}
if (messagesToSend.length === 0) {
return;
}
// Reset attachments
self.attachments = [];
// Hide sheet
self.sheetVisible = false;
// Clear area
self.messagebar.clear();
// Focus area
if (text.length) self.messagebar.focus();
// Send message
self.messagesData.push(...messagesToSend);
// Mock response
if (self.responseInProgress) return;
self.responseInProgress = true;
setTimeout(() => {
const answer = self.answers[Math.floor(Math.random() * self.answers.length)];
const person = self.people[Math.floor(Math.random() * self.people.length)];
self.typingMessage = {
name: person.name,
avatar: person.avatar,
};
setTimeout(() => {
self.messagesData.push({
text: answer,
type: 'received',
name: person.name,
avatar: person.avatar,
});
self.typingMessage = null;
self.responseInProgress = false;
}, 4000);
}, 1000);
},
},
};
</script>