
Messages Component
Messages Svelte component represents Messages component.
Messages Components
There are following components included:
Messages- main Messages containerMessage- single message elementMessagesTitle- single messages title element
Messages Properties
| Prop | Type | Default | Description |
|---|---|---|---|
| <Messages> properties | |||
| --- | --- | --- | --- |
| init | boolean | true | Initializes Messages component |
| newMessagesFirst | boolean | false | Enable if you want to use new messages on top, instead of having them on bottom |
| scrollMessages | boolean | true | Enable/disable messages autoscrolling when adding new message |
| scrollMessagesOnEdge | boolean | true | If enabled then messages autoscrolling will happen only when user is on top/bottom of the messages view |
| <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 | |
| textHeader | string | Message text header | |
| textFooter | 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 |
| sameName | boolean | false | Defines that this message sender name is the same as on previous message |
| sameHeader | boolean | false | Defines that this message header text is the same as on previous message |
| sameFooter | boolean | false | Defines that this message footer text is the same as on previous message |
| sameAvatar | boolean | false | Defines that this message user’s avatar URL is the same as on previous message |
Messages Methods
| <Messages> methods | |
|---|---|
| .scroll(duration, position); | Scroll messages to top/bottom depending on newMessagesFirst parameter - duration - number scroll duration in ms - position - number scroll position in px |
| .showTyping(message); | Show typing message indicator - message - object - parameters of message to add |
| .hideTyping() | Hide typing message indicator |
Messages Events
| Event | Description |
|---|---|
| <Message> events | |
| --- | --- |
| click | Event will be triggered when user clicks on message bubble |
| clickName | Event will be triggered when user clicks on message user’s name |
| clickText | Event will be triggered when user clicks on message text |
| clickAvatar | Event will be triggered when user clicks on message user’s avatar |
| clickHeader | Event will be triggered when user clicks on message header |
| clickFooter | Event will be triggered when user clicks on message footer |
| clickBubble | Event will be triggered when user clicks on message bubble |
Messages Slots
Single message Svelte component (<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 asdefaultslot
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
1. <Message
2. type="sent"
3. text="Hello World"
4. name="John Doe"
5. avatar="path/to/image.jpg"
6. >
7. <div slot="start">Start</div>
8. <div slot="end">End</div>
9. <div slot="content-start">Content Start</div>
10. <div slot="content-end">Content End</div>
11. <div slot="bubble-start">Bubble Start</div>
12. <div slot="bubble-end">Bubble End</div>
13. </Message>
15. <!-- Renders to: -->
17. <div class="message message-sent">
18. <div>Start</div>
19. <div class="message-avatar" style="background-image: url(path/to/image.jpg);"></div>
20. <div class="message-content">
21. <div>Content Start</div>
22. <div class="message-name">John Doe</div>
23. <div class="message-bubble">
24. <div>Bubble Start</div>
25. <div class="message-text">Hello World</div>
26. <div>Bubble End</div>
27. </div>
28. <div>Content End</div>
29. </div>
30. <div>End</div>
31. </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 calling .instance() component’s method. For example:
1. <Messages bind:this={component}>...</Messages>
3. <script>
4. let component;
6. // to get instance in some method
7. component.instance()
8. </script>
Examples
Here is how the full example of Messages page where it can be used together with Messagebar:
1. <Page>
2. <Navbar title="Messages"></Navbar>
4. <Messagebar
5. placeholder={placeholder}
6. bind:this={messagebarComponent}
7. attachmentsVisible={attachmentsVisible}
8. sheetVisible={sheetVisible}
9. value={messageText}
10. onInput={(e) => messageText = e.target.value}
11. >
12. <a class="link icon-only" slot="inner-start" on:click={() => sheetVisible = !sheetVisible}>
13. <Icon
14. ios="f7:camera_fill"
15. aurora="f7:camera_fill"
16. md="material:camera_alt"
17. />
18. </a>
19. <a class="link icon-only" slot="inner-end" on:click={sendMessage}>
20. <Icon
21. ios="f7:arrow_up_circle_fill"
22. aurora="f7:arrow_up_circle_fill"
23. md="material:send"
24. />
25. </a>
26. <MessagebarAttachments>
27. {#each attachments as image, index (index)}
28. <MessagebarAttachment
29. key={index}
30. image={image}
31. onAttachmentDelete={() => deleteAttachment(image)}
32. ></MessagebarAttachment>
33. {/each}
34. </MessagebarAttachments>
35. <MessagebarSheet>
36. {#each images as image, index (index)}
37. <MessagebarSheetImage
38. key={index}
39. image={image}
40. checked={attachments.indexOf(image) >= 0}
41. onChange={handleAttachment}
42. ></MessagebarSheetImage>
43. {/each}
44. </MessagebarSheet>
45. </Messagebar>
47. <Messages>
48. <MessagesTitle><b>Sunday, Feb 9,</b> 12:58</MessagesTitle>
49. {#each messagesData as message, index (index)}
50. <Message
51. type={message.type}
52. image={message.image}
53. name={message.name}
54. avatar={message.avatar}
55. first={isFirstMessage(message, index)}
56. last={isLastMessage(message, index)}
57. tail={isTailMessage(message, index)}
58. htmlText={message.text}
59. />
60. {/each}
61. {#if typingMessage}
62. <Message
63. type="received"
64. typing={true}
65. first={true}
66. last={true}
67. tail={true}
68. header={`${typingMessage.name} is typing`}
69. avatar={typingMessage.avatar}
70. ></Message>
71. {/if}
72. </Messages>
73. </Page>
76. <script>
77. import { onMount, onDestroy } from 'svelte';
78. import { f7, f7ready, Navbar, Page, Messages, MessagesTitle, Message, Messagebar, Icon, MessagebarAttachments, MessagebarAttachment, MessagebarSheet, MessagebarSheetImage } from 'framework7-svelte';
80. let messagebarComponent;
81. let messagebarInstance;
83. let attachments = [];
84. let sheetVisible = false;
85. let typingMessage = null;
86. let messageText = '';
87. let messagesData = [
88. {
89. type: 'sent',
90. text: 'Hi, Kate',
91. },
92. {
93. type: 'sent',
94. text: 'How are you?',
95. },
96. {
97. name: 'Kate',
98. type: 'received',
99. text: 'Hi, I am good!',
100. avatar: 'https://cdn.framework7.io/placeholder/people-100x100-9.jpg',
101. },
102. {
103. name: 'Blue Ninja',
104. type: 'received',
105. text: 'Hi there, I am also fine, thanks! And how are you?',
106. avatar: 'https://cdn.framework7.io/placeholder/people-100x100-7.jpg',
107. },
108. {
109. type: 'sent',
110. text: 'Hey, Blue Ninja! Glad to see you ;)',
111. },
112. {
113. type: 'sent',
114. text: 'Hey, look, cutest kitten ever!',
115. },
116. {
117. type: 'sent',
118. image: 'https://cdn.framework7.io/placeholder/cats-200x260-4.jpg',
120. },
121. {
122. name: 'Kate',
123. type: 'received',
124. text: 'Nice!',
125. avatar: 'https://cdn.framework7.io/placeholder/people-100x100-9.jpg',
126. },
127. {
128. name: 'Kate',
129. type: 'received',
130. text: 'Like it very much!',
131. avatar: 'https://cdn.framework7.io/placeholder/people-100x100-9.jpg',
132. },
133. {
134. name: 'Blue Ninja',
135. type: 'received',
136. text: 'Awesome!',
137. avatar: 'https://cdn.framework7.io/placeholder/people-100x100-7.jpg',
138. },
139. ];
140. let images = [
141. 'https://cdn.framework7.io/placeholder/cats-300x300-1.jpg',
142. 'https://cdn.framework7.io/placeholder/cats-200x300-2.jpg',
143. 'https://cdn.framework7.io/placeholder/cats-400x300-3.jpg',
144. 'https://cdn.framework7.io/placeholder/cats-300x150-4.jpg',
145. 'https://cdn.framework7.io/placeholder/cats-150x300-5.jpg',
146. 'https://cdn.framework7.io/placeholder/cats-300x300-6.jpg',
147. 'https://cdn.framework7.io/placeholder/cats-300x300-7.jpg',
148. 'https://cdn.framework7.io/placeholder/cats-200x300-8.jpg',
149. 'https://cdn.framework7.io/placeholder/cats-400x300-9.jpg',
150. 'https://cdn.framework7.io/placeholder/cats-300x150-10.jpg',
151. ];
152. let people = [
153. {
154. name: 'Kate Johnson',
155. avatar: 'https://cdn.framework7.io/placeholder/people-100x100-9.jpg',
156. },
157. {
158. name: 'Blue Ninja',
159. avatar: 'https://cdn.framework7.io/placeholder/people-100x100-7.jpg',
160. },
161. ];
162. let answers = [
163. 'Yes!',
164. 'No',
165. 'Hm...',
166. 'I am not sure',
167. 'And what about you?',
168. 'May be ;)',
169. 'Lorem ipsum dolor sit amet, consectetur',
170. 'What?',
171. 'Are you sure?',
172. 'Of course',
173. 'Need to think about it',
174. 'Amazing!!!',
175. ];
176. let responseInProgress = false;
178. $: attachmentsVisible = attachments.length > 0;
179. $: placeholder = attachments.length > 0 ? 'Add comment or Send' : 'Message';
181. onMount(() => {
182. f7ready(() => {
183. messagebarInstance = messagebarComponent.instance();
184. });
185. });
187. function isFirstMessage(message, index) {
188. const previousMessage = messagesData[index - 1];
189. if (message.isTitle) return false;
190. if (!previousMessage || previousMessage.type !== message.type || previousMessage.name !== message.name) return true;
191. return false;
192. }
193. function isLastMessage(message, index) {
194. const nextMessage = messagesData[index + 1];
195. if (message.isTitle) return false;
196. if (!nextMessage || nextMessage.type !== message.type || nextMessage.name !== message.name) return true;
197. return false;
198. }
199. function isTailMessage(message, index) {
200. const nextMessage = messagesData[index + 1];
201. if (message.isTitle) return false;
202. if (!nextMessage || nextMessage.type !== message.type || nextMessage.name !== message.name) return true;
203. return false;
204. }
205. function deleteAttachment(image) {
206. const index = attachments.indexOf(image);
207. attachments.splice(index, 1);
208. attachments = attachments;
209. }
210. function handleAttachment(e) {
211. const index = f7.$(e.target).parents('label.checkbox').index();
212. const image = images[index];
213. if (e.target.checked) {
214. // Add to attachments
215. attachments.unshift(image);
216. } else {
217. // Remove from attachments
218. attachments.splice(attachments.indexOf(image), 1);
219. }
220. attachments = attachments;
221. }
222. function sendMessage() {
223. const text = messageText.replace(/\n/g, '<br>').trim();
224. const messagesToSend = [];
225. attachments.forEach((attachment) => {
226. messagesToSend.push({
227. type: 'sent',
228. image: attachment,
229. });
230. });
231. if (text.length) {
232. messagesToSend.push({
233. text,
234. type: 'sent',
235. });
236. }
237. if (messagesToSend.length === 0) {
238. return;
239. }
241. // Reset attachments
242. attachments = [];
243. // Hide sheet
244. sheetVisible = false;
245. // Send message
246. messagesData = [...messagesData, ...messagesToSend];
247. // Clear
248. messageText = '';
249. messagebarInstance.clear();
251. // Focus area
252. if (text.length) messagebarInstance.focus();
254. // Mock response
255. if (responseInProgress) return;
256. responseInProgress = true;
258. setTimeout(() => {
259. const answer = answers[Math.floor(Math.random() * answers.length)];
260. const person = people[Math.floor(Math.random() * people.length)];
261. typingMessage = {
262. name: person.name,
263. avatar: person.avatar,
264. };
265. setTimeout(() => {
266. messagesData = [...messagesData, {
267. text: answer,
268. type: 'received',
269. name: person.name,
270. avatar: person.avatar,
271. }];
272. typingMessage = null;
273. responseInProgress = false;
274. }, 4000);
275. }, 1000);
276. }
277. </script>
