Messagebar Vue Component

Messagebar Vue component represents Framework7's Messagebar component.

Messagebar Components

There are following components included:

Messagebar Properties

PropTypeDefaultDescription
<f7-messagebar> properties
initbooleantrueInitializes Messagebar
namestringTextarea "name" attribute
textarea-idstring
number
Textarea "id" attribute
placeholderstringMessageTextarea placeholder text
valuestring
number
Textarea value
readonlybooleanfalseSets "readonly" textarea attribute
disabledbooleanfalseSets "disabled" textarea attribute
send-linkstringEnables Send link and specifies its text. This property will be ignored in case you use send-link slot
max-heightnumberDefines resizable textarea max height
resizablebooleantrueEnables resizable textarea
sheet-visiblebooleanfalseMakes messagebar sheet visible/active
attachments-visiblebooleanfalseMakes messagebar attachments visible/active
resize-pagebooleantrueWhen enabled, it will resize messages page when messagebar textarea size changed
<f7-messagebar-sheet-image> properties
imagestringSheet image URL
checkedbooleanfalseIndicates whether this sheet image-item is checked or not
<f7-messagebar-attachment> properties
imagestringAttachment image URL
deletablebooleantrueDefines whether the attachment is deletable or not. In case of deletable the additional delete button will be rendered

Messagebar Events

EventArgumentsDescription
<f7-messagebar> events
change(event)Event will be triggered when "change" event occurs on messagebar textarea element
input(event)Event will be triggered when "input" event occurs on messagebar textarea element
focus(event)Event will be triggered when "focus" event occurs on messagebar textarea element
blur(event)Event will be triggered when "blur" event occurs on messagebar textarea element
submit
send
(value, clear)Event will be triggered when user clicks on messagebar "send link"
<f7-messagebar-sheet-image> events
change(event)Event will be triggered on sheet item checkbox change
<f7-messagebar-attachment> events
attachment:click(event)Event will be triggered on attachment click
attachment:delete(event)Event will be triggered on attachment delete button click

Messagebar Slots

Messagebar Vue component has additional slots for custom elements:

<f7-messagebar placeholder="Message" @submit="onSubmit">
  <div slot="before-inner">Before inner</div>
  <div slot="after-inner">After inner</div>
  <div slot="before-area">Before textarea</div>
  <div slot="after-area">After textarea</div>
  <f7-icon ios="f7:arrow_up_fill" md="material:send" slot="send-link"></f7-icon>
  <div>Default slot</div>
</f7-messagebar>

Renders to:

<div class="toolbar messagebar">
  <div>Before inner</div>
  <div class="toolbar-inner">
    <div class="messagebar-area">
      <div>Before textarea</div>
      <textarea placeholder="Message"></textarea>
      <div>After textarea</div>
    </div>
    <a href="#" class="link"><i class="icon f7-icons">arrow_up_fill</i></a>
    <div>Default slot</div>
  </div>
  <div>After inner</div>
</div>

Messagebar v-model

f7-messagebar component supports v-model on value prop:

<template>
  ...
  <f7-messagebar
    v-model:value="messageText"
  />
  ...
</template>
<script>
  export default {
    data() {
      messageText: '',
    },
    ...
  };
</script>

Examples

Here is how the full example of Messages page where it can be used together with Messages:

messages.vue
<template>
  <f7-page>
    <f7-navbar title="Messages"></f7-navbar>

    <f7-messagebar
      ref="messagebar"
      v-model:value="messageText"
      :placeholder="placeholder"
      :attachments-visible="attachmentsVisible"
      :sheet-visible="sheetVisible"
    >
      <template #inner-start>
        <f7-link
          icon-ios="f7:camera_fill"
          icon-md="material:camera_alt"
          @click="sheetVisible = !sheetVisible"
        />
      </template>
      <template #inner-end>
        <f7-link icon-ios="f7:arrow_up_circle_fill" icon-md="material:send" @click="sendMessage" />
      </template>
      <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>
      <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)"
      >
        <template v-if="message.text" #text>
          <!-- eslint-disable-next-line -->
          <span v-html="message.text"></span>
        </template>
      </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>
import {
  f7Navbar,
  f7Page,
  f7Messages,
  f7MessagesTitle,
  f7Message,
  f7Messagebar,
  f7Link,
  f7MessagebarAttachments,
  f7MessagebarAttachment,
  f7MessagebarSheet,
  f7MessagebarSheetImage,
  f7ready,
  f7,
} from 'framework7-vue';
import $ from 'dom7';

export default {
  components: {
    f7Navbar,
    f7Page,
    f7Messages,
    f7MessagesTitle,
    f7Message,
    f7Messagebar,
    f7MessagebarAttachments,
    f7MessagebarAttachment,
    f7MessagebarSheet,
    f7MessagebarSheetImage,
    f7Link,
  },
  data() {
    return {
      attachments: [],
      sheetVisible: false,
      typingMessage: null,
      messageText: '',
      messagesData: [
        {
          type: 'sent',
          text: 'Hi, Kate',
        },
        {
          type: 'sent',
          text: 'How are you?',
        },
        {
          name: 'Kate',
          type: 'received',
          text: 'Hi, I am good!',
          avatar: 'https://cdn.framework7.io/placeholder/people-100x100-9.jpg',
        },
        {
          name: 'Blue Ninja',
          type: 'received',
          text: 'Hi there, I am also fine, thanks! And how are you?',
          avatar: 'https://cdn.framework7.io/placeholder/people-100x100-7.jpg',
        },
        {
          type: 'sent',
          text: 'Hey, Blue Ninja! Glad to see you ;)',
        },
        {
          type: 'sent',
          text: 'Hey, look, cutest kitten ever!',
        },
        {
          type: 'sent',
          image: 'https://cdn.framework7.io/placeholder/cats-200x260-4.jpg',
        },
        {
          name: 'Kate',
          type: 'received',
          text: 'Nice!',
          avatar: 'https://cdn.framework7.io/placeholder/people-100x100-9.jpg',
        },
        {
          name: 'Kate',
          type: 'received',
          text: 'Like it very much!',
          avatar: 'https://cdn.framework7.io/placeholder/people-100x100-9.jpg',
        },
        {
          name: 'Blue Ninja',
          type: 'received',
          text: 'Awesome!',
          avatar: 'https://cdn.framework7.io/placeholder/people-100x100-7.jpg',
        },
      ],
      images: [
        'https://cdn.framework7.io/placeholder/cats-300x300-1.jpg',
        'https://cdn.framework7.io/placeholder/cats-200x300-2.jpg',
        'https://cdn.framework7.io/placeholder/cats-400x300-3.jpg',
        'https://cdn.framework7.io/placeholder/cats-300x150-4.jpg',
        'https://cdn.framework7.io/placeholder/cats-150x300-5.jpg',
        'https://cdn.framework7.io/placeholder/cats-300x300-6.jpg',
        'https://cdn.framework7.io/placeholder/cats-300x300-7.jpg',
        'https://cdn.framework7.io/placeholder/cats-200x300-8.jpg',
        'https://cdn.framework7.io/placeholder/cats-400x300-9.jpg',
        'https://cdn.framework7.io/placeholder/cats-300x150-10.jpg',
      ],
      people: [
        {
          name: 'Kate Johnson',
          avatar: 'https://cdn.framework7.io/placeholder/people-100x100-9.jpg',
        },
        {
          name: 'Blue Ninja',
          avatar: 'https://cdn.framework7.io/placeholder/people-100x100-7.jpg',
        },
      ],
      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;
    f7ready(() => {
      self.messagebar = f7.messagebar.get(self.$refs.messagebar.$el);
    });
  },
  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 = $(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.messageText.replace(/\n/g, '<br>').trim();
      const messagesToSend = [];
      self.attachments.forEach((attachment) => {
        messagesToSend.push({
          image: attachment,
        });
      });
      if (text.length) {
        messagesToSend.push({
          text,
        });
      }
      if (messagesToSend.length === 0) {
        return;
      }

      // Reset attachments
      self.attachments = [];
      // Hide sheet
      self.sheetVisible = false;
      // Clear area
      self.messageText = '';
      // 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>