訊息元件
訊息 Svelte 元件代表 訊息 元件。
訊息元件
包含下列元件
訊息
- 主要訊息容器訊息
- 單一訊息元素訊息標題
- 單一訊息標題元素
訊息屬性
屬性 | 類型 | 預設值 | 說明 |
---|---|---|---|
<訊息> 屬性 | |||
init | 布林值 | true | 初始化訊息元件 |
newMessagesFirst | 布林值 | false | 啟用此選項,若要使用頂端的最新訊息,而不是底端的訊息 |
scrollMessages | 布林值 | true | 啟用/停用在新增新訊息時自動捲動訊息 |
scrollMessagesOnEdge | 布林值 | true | 如果啟用,則只有當使用者位於訊息檢視的頂端/底端時,才會自動捲動訊息 |
typing | 布林值 | false | 允許顯示/切換輸入訊息指標 |
<訊息> 屬性 | |||
type | 字串 | 已傳送 | 訊息類型:已傳送 (預設值)或 已接收 |
text | 字串 | 訊息文字 | |
avatar | 字串 | 訊息使用者的頭像 URL | |
name | 字串 | 訊息使用者的姓名 | |
image | 字串 | 訊息圖片 URL | |
header | 字串 | 訊息標頭 | |
footer | 字串 | 訊息尾部 | |
textHeader | 字串 | 訊息文字標頭 | |
textFooter | 字串 | 訊息文字尾部 | |
first | 布林值 | false | 定義訊息為對話中的第一則 |
last | 布林值 | false | 定義訊息為對話中的最後一則 |
tail | 布林值 | false | 定義訊息具有視覺上的「尾巴」。通常是對話中的最後一則訊息 |
sameName | 布林值 | false | 定義此訊息傳送者的姓名與前一則訊息相同 |
sameHeader | 布林值 | false | 定義此訊息標頭文字與前一則訊息相同 |
sameFooter | 布林值 | false | 定義此訊息尾部文字與前一則訊息相同 |
sameAvatar | 布林值 | false | 定義此訊息使用者的頭像 URL 與前一則訊息相同 |
訊息事件
事件 | 說明 |
---|---|
<訊息> 事件 | |
click | 當使用者按一下訊息氣泡時,觸發事件 |
clickName | 當使用者按一下訊息使用者姓名時,觸發事件 |
clickText | 當使用者按一下訊息文字時,觸發事件 |
clickAvatar | 當使用者按一下訊息使用者頭像時,觸發事件 |
clickHeader | 當使用者按一下訊息標題時,會觸發事件 |
clickFooter | 當使用者按一下訊息頁尾時,會觸發事件 |
clickBubble | 當使用者按一下訊息氣泡時,觸發事件 |
訊息插槽
單一訊息 Svelte 元件 (<Message>
) 有額外的插槽,可供自訂元素使用
default
- 元素將插入在<div class="message-bubble">
元素的最後一個子元素start
- 元素將插入在開頭,並成為主要訊息元素<div class="message">
的直接子元素end
- 元素將插入在結尾,並成為主要訊息元素<div class="message">
的直接子元素content-start
- 元素將插入在開頭,並成為<div class="message-content">
元素的直接子元素content-end
- 元素將插入在結尾,並成為<div class="message-content">
元素的直接子元素bubble-start
- 元素將插入在開頭,並成為<div class="message-bubble">
元素的直接子元素bubble-end
- 元素將插入在結尾,並成為<div class="message-bubble">
元素的直接子元素。與default
插槽相同
如果需要傳遞較複雜的配置,可以使用下列插槽在單一訊息中,取代相同的 props
header
- 元素將插入在訊息標題中footer
- 元素將插入在訊息頁尾中text
- 元素將插入在訊息文字中name
- 元素將插入在訊息名稱中image
- 元素將插入在訊息圖片中 (假設為<img>
元素)text-header
- 元素將插入在訊息文字標題中text-footer
- 元素將插入在訊息文字頁尾中
<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>
</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>
存取訊息實例
如果你使用自動初始化來初始化訊息 (使用 init={true}
prop),並且需要使用 訊息 API,你可以呼叫元件的 .instance()
方法來存取其初始化實例。例如
<Messages bind:this={component}>...</Messages>
<script>
let component;
// to get instance in some method
component.instance()
</script>
範例
以下是訊息頁面的完整範例,其中可以與 訊息列 一起使用
messages.svelte
<script>
import { onMount } from 'svelte';
import {
f7,
f7ready,
Navbar,
Page,
Messages,
MessagesTitle,
Message,
Messagebar,
Icon,
MessagebarAttachments,
MessagebarAttachment,
MessagebarSheet,
MessagebarSheetImage,
} from 'framework7-svelte';
let messagebarInstance;
let attachments = [];
let sheetVisible = false;
let typingMessage = null;
let messageText = '';
let 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',
},
];
const 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',
];
const 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',
},
];
const 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!!!',
];
let responseInProgress = false;
$: attachmentsVisible = attachments.length > 0;
$: placeholder = attachments.length > 0 ? 'Add comment or Send' : 'Message';
onMount(() => {
f7ready(() => {
messagebarInstance = f7.messagebar.get('.messagebar');
});
});
function isFirstMessage(message, index) {
const previousMessage = messagesData[index - 1];
if (message.isTitle) return false;
if (
!previousMessage ||
previousMessage.type !== message.type ||
previousMessage.name !== message.name
)
return true;
return false;
}
function isLastMessage(message, index) {
const nextMessage = messagesData[index + 1];
if (message.isTitle) return false;
if (!nextMessage || nextMessage.type !== message.type || nextMessage.name !== message.name)
return true;
return false;
}
function isTailMessage(message, index) {
const nextMessage = messagesData[index + 1];
if (message.isTitle) return false;
if (!nextMessage || nextMessage.type !== message.type || nextMessage.name !== message.name)
return true;
return false;
}
function deleteAttachment(image) {
const index = attachments.indexOf(image);
attachments.splice(index, 1);
attachments = attachments;
}
function handleAttachment(e) {
const index = f7.$(e.target).parents('label.checkbox').index();
const image = images[index];
if (e.target.checked) {
// Add to attachments
attachments.unshift(image);
} else {
// Remove from attachments
attachments.splice(attachments.indexOf(image), 1);
}
attachments = attachments;
}
function sendMessage() {
const text = messageText.replace(/\n/g, '<br>').trim();
const messagesToSend = [];
attachments.forEach((attachment) => {
messagesToSend.push({
type: 'sent',
image: attachment,
});
});
if (text.length) {
messagesToSend.push({
text,
type: 'sent',
});
}
if (messagesToSend.length === 0) {
return;
}
// Reset attachments
attachments = [];
// Hide sheet
sheetVisible = false;
// Send message
messagesData = [...messagesData, ...messagesToSend];
// Clear
messageText = '';
messagebarInstance.clear();
// Focus area
if (text.length) messagebarInstance.focus();
// Mock response
if (responseInProgress) return;
responseInProgress = true;
setTimeout(() => {
const answer = answers[Math.floor(Math.random() * answers.length)];
const person = people[Math.floor(Math.random() * people.length)];
typingMessage = {
name: person.name,
avatar: person.avatar,
};
setTimeout(() => {
messagesData = [
...messagesData,
{
text: answer,
type: 'received',
name: person.name,
avatar: person.avatar,
},
];
typingMessage = null;
responseInProgress = false;
}, 4000);
}, 1000);
}
</script>
<!-- svelte-ignore a11y-missing-attribute -->
<Page>
<Navbar title="Messages" />
<Messagebar
{placeholder}
{attachmentsVisible}
{sheetVisible}
value={messageText}
onInput={(e) => (messageText = e.target.value)}
>
<a class="link icon-only" slot="inner-start" on:click={() => (sheetVisible = !sheetVisible)}>
<Icon ios="f7:camera_fill" md="material:camera_alt" />
</a>
<a class="link icon-only" slot="inner-end" on:click={sendMessage}>
<Icon ios="f7:arrow_up_circle_fill" md="material:send" />
</a>
<MessagebarAttachments>
{#each attachments as image, index (index)}
<MessagebarAttachment
key={index}
{image}
onAttachmentDelete={() => deleteAttachment(image)}
/>
{/each}
</MessagebarAttachments>
<MessagebarSheet>
{#each images as image, index (index)}
<MessagebarSheetImage
key={index}
{image}
checked={attachments.indexOf(image) >= 0}
onChange={handleAttachment}
/>
{/each}
</MessagebarSheet>
</Messagebar>
<Messages>
<MessagesTitle><b>Sunday, Feb 9,</b> 12:58</MessagesTitle>
{#each messagesData as message, index (index)}
<Message
type={message.type}
image={message.image}
name={message.name}
avatar={message.avatar}
first={isFirstMessage(message, index)}
last={isLastMessage(message, index)}
tail={isTailMessage(message, index)}
htmlText={message.text}
/>
{/each}
{#if typingMessage}
<Message
type="received"
typing={true}
first={true}
last={true}
tail={true}
header={`${typingMessage.name} is typing`}
avatar={typingMessage.avatar}
/>
{/if}
</Messages>
</Page>