MentionSender Mention Input Box ๐ฆฅ โ
Introduction โ
MentionSender
is an input box component for chat scenarios.
๐ Warning
Its functionality is basically the same as the Sender
component. The only difference is the properties and methods related to the directive popover. Click here to quickly understand the difference ๐ Directive Difference
Currently, we do not plan to combine the directive features of MentionSender
and Sender
into one, and we distinguish them only by component.
Code Examples โ
Basic Usage โ
This is a MentionSender
input component, the simplest usage example.
<template>
<MentionSender />
</template>
2
3
Placeholder โ
You can set the input placeholder through the placeholder
attribute.
<template>
<MentionSender placeholder="๐ Welcome to Element-Plus-X ~" />
</template>
2
3
Two-way Binding (Unbound, value will not change) โ
You can bind the component's value
property through v-model
.
๐ Warning
- When submitting, content is required for submission to proceed.
- When content is empty, the submit button will be disabled, and using component instance submission will fail.
๐ Info
- Through the
v-model
attribute, you can automatically bind the input value. No need to assign data tov-model
. - Through the
@submit
event, you can trigger the input submission event, which returns avalue
parameter where you can handle the submitted data. - Through the
@cancel
event, you can trigger theloading
button click event. Here you can abort the submission operation.
You can also call through the component ref instance object
senderRef.value.submit()
Trigger submissionsenderRef.value.cancel()
Trigger cancelsenderRef.value.clear()
Reset input value
<script setup lang="ts">
const senderRef = ref();
const timeValue = ref<NodeJS.Timeout | null>(null);
const senderValue = ref('');
const senderLoading = ref(false);
function handleSubmit(value: string) {
ElMessage.info(`Sending`);
senderLoading.value = true;
timeValue.value = setTimeout(() => {
// You can view the print result in the console
console.log('submit-> value๏ผ', value);
console.log('submit-> senderValue', senderValue.value);
senderLoading.value = false;
ElMessage.success(`Sent successfully`);
}, 3500);
}
function handleCancel() {
senderLoading.value = false;
if (timeValue.value)
clearTimeout(timeValue.value);
timeValue.value = null;
ElMessage.info(`Cancel sending`);
}
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 12px">
<div style="display: flex">
<el-button
type="primary"
style="width: fit-content"
@click="senderRef.clear()"
>
Clear using component instance
</el-button>
<el-button
type="primary"
style="width: fit-content"
:disabled="!senderValue"
@click="senderRef.submit()"
>
Submit using component instance
</el-button>
<el-button
type="primary"
style="width: fit-content"
@click="senderRef.cancel()"
>
Cancel using component instance
</el-button>
</div>
<MentionSender
ref="senderRef"
v-model="senderValue"
:loading="senderLoading"
clearable
@submit="handleSubmit"
@cancel="handleCancel"
/>
</div>
</template>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
Submit Button Disabled State โ
You can customize whether to disable the send button through submit-btn-disabled
. When disabled, the component instance's submit
method will fail.
๐ Warning
The built-in send button is bound to v-model
, so when the v-model
bound value is empty, the send button will be in disabled state.
However, there is such a scenario. The user uploaded a file but didn't input content, at this time, the send button is still in disabled state.
So, for decoupling the disable logic, the component provides the submit-btn-disabled
attribute for autonomous control of the send button's disabled state.
When customizing #action-list
, this attribute also takes effect for submit events.
<script setup lang="ts">
const senderRef = ref();
const timeValue = ref<NodeJS.Timeout | null>(null);
const senderValue = ref('');
const senderLoading = ref(false);
const submitBtnDisabled = ref(true);
function handleSubmit(value: string) {
ElMessage.info(`Sending`);
senderLoading.value = true;
timeValue.value = setTimeout(() => {
// You can view the print result in the console
console.log('submit-> value๏ผ', value);
console.log('submit-> senderValue', senderValue.value);
senderLoading.value = false;
ElMessage.success(`Sent successfully`);
}, 3500);
}
function handleCancel() {
senderLoading.value = false;
if (timeValue.value)
clearTimeout(timeValue.value);
timeValue.value = null;
ElMessage.info(`Cancel sending`);
}
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 12px">
<span>This is the built-in disable logic:</span>
<MentionSender
ref="senderRef"
v-model="senderValue"
:loading="senderLoading"
clearable
@submit="handleSubmit"
@cancel="handleCancel"
/>
<span>Custom disable logic:</span>
<MentionSender
ref="senderRef"
v-model="senderValue"
:submit-btn-disabled="submitBtnDisabled"
:loading="senderLoading"
clearable
@submit="handleSubmit"
@cancel="handleCancel"
/>
</div>
</template>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
Custom Max and Min Rows โ
You can set the minimum and maximum display rows for the input through autosize
. autosize
is an object with default value { minRows: 1, maxRows: 6 }
. When exceeding the maximum rows, the input will automatically show a scrollbar.
<script setup lang="ts">
const longerValue = `๐ Welcome to Element-Plus-X ~`.repeat(30);
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 12px">
<MentionSender :auto-size="{ minRows: 2, maxRows: 5 }" />
<MentionSender v-model="longerValue" />
</div>
</template>
2
3
4
5
6
7
8
9
10
Various States of the Input Component โ
You can implement component states through simple properties
๐ Info
- Through the
loading
property, you can control whether the input is loading. - Through the
readOnly
property, you can control whether the input is editable. - Through the
disabled
property, you can control whether the input is disabled. - Through the
clearable
property, you can control whether the input shows a delete button for clearing. - Through the
inputWidth
property, you can control the input width. Default is100%
.
<script setup lang="ts">
const senderReadOnlyValue = ref(`Read-only: ๐ Welcome to Element-Plus-X ~`);
const senderClearableValue = ref(`Clearable: ๐ Welcome to Element-Plus-X ~`);
function handleSubmit(value: string) {
console.log(value);
}
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 12px">
<MentionSender loading placeholder="Loading..." @submit="handleSubmit" />
<MentionSender
v-model="senderReadOnlyValue"
read-only
@submit="handleSubmit"
/>
<MentionSender
value="Disabled: ๐ Welcome to Element-Plus-X ~"
disabled
@submit="handleSubmit"
/>
<MentionSender
v-model="senderClearableValue"
clearable
@submit="handleSubmit"
/>
<MentionSender
style="width: fit-content"
value="Input max width: ๐ Welcome to Element-Plus-X ~"
input-width="150px"
@submit="handleSubmit"
/>
</div>
</template>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
Submission Methods โ
Control line breaks and submit mode with submitType
. Default is 'enter'
, i.e., press Enter to submit, 'shift + Enter'
for a new line.
๐ Info
submitType='enter'
sets Enter to submit,'shift + Enter'
for a new line.submitType='shiftEnter'
sets'shift + Enter'
to submit, Enter for a new line.submitType='cmdOrCtrlEnter'
sets'cmd + Enter'
or'ctrl + Enter'
to submit, Enter for a new line.submitType='altEnter'
sets'alt + Enter'
to submit, Enter for a new line.
<script setup lang="ts">
import type { SenderProps } from 'vue-element-plus-x/types/Sender';
const activeName = ref<SenderProps['submitType']>('enter');
const senderValue = ref('');
const senderLoading = ref(false);
function handleSubmit(value: string) {
ElMessage.info(`Sending...`);
senderLoading.value = true;
setTimeout(() => {
// You can check the console for the output
console.log('submit-> value:', value);
console.log('submit-> senderValue', senderValue.value);
senderLoading.value = false;
ElMessage.success(`Sent successfully`);
}, 2000);
}
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 12px">
<el-radio-group v-model="activeName">
<el-radio-button value="enter">
enter
</el-radio-button>
<el-radio-button value="shiftEnter">
shiftEnter
</el-radio-button>
<el-radio-button value="cmdOrCtrlEnter">
cmdOrCtrlEnter
</el-radio-button>
<el-radio-button value="altEnter">
altEnter
</el-radio-button>
</el-radio-group>
<MentionSender
v-model="senderValue"
:submit-type="activeName"
:loading="senderLoading"
@submit="handleSubmit"
/>
</div>
</template>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
Speech Recognition โ
๐ Warning
Built-in browser speech recognition API. You can use the useRecord
hook for easier integration and control of built-in speech recognition.
Built-in voice recognition
functionality, enable it through the allowSpeech
property. It calls the browser's native voice recognition API, and when used in Google Chrome
, it needs to be in a ๐ชmagic environment
to work properly.
๐ Info
If you don't want to use the built-in voice recognition
functionality, you can listen to the recording state through the @recording-change
event and implement voice recognition functionality yourself.
You can also call through the component ref instance object
senderRef.value.startRecognition()
Trigger start recordingsenderRef.value.stopRecognition()
Trigger stop recording
<script setup lang="ts">
const senderRef = ref();
const senderValue = ref('');
function onRecordingChange(recording: boolean) {
if (recording) {
ElMessage.success('Start recording');
}
else {
ElMessage.success('Stop recording');
}
}
function onsubmit() {
ElMessage.success('Sent successfully');
}
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 12px">
<span>Built-in voice recognition:</span>
<MentionSender v-model="senderValue" allow-speech @submit="onsubmit" />
<span>Custom voice recognition:</span>
<div style="display: flex">
<el-button
type="primary"
style="width: fit-content"
@click="senderRef.startRecognition()"
>
Start recording using component instance
</el-button>
<el-button
type="primary"
style="width: fit-content"
@click="senderRef.stopRecognition()"
>
Stop recording using component instance
</el-button>
</div>
<MentionSender
ref="senderRef"
v-model="senderValue"
allow-speech
@recording-change="onRecordingChange"
/>
</div>
</template>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
Variant - Vertical Style โ
Set the input variant through the variant
property. Default 'default' | Up-down structure 'updown'
This property changes the left-right structure input into an up-down structure input. The top is the input box, and the bottom is the built-in prefix and action list bar
<script setup lang="ts">
import { ElementPlus, Paperclip, Promotion } from '@element-plus/icons-vue';
const senderValue = ref('');
const isSelect = ref(false);
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 20px">
<MentionSender v-model="senderValue" variant="updown" />
<MentionSender v-model="senderValue" variant="updown" clearable />
<MentionSender
v-model="senderValue"
variant="updown"
clearable
allow-speech
/>
<MentionSender
v-model="senderValue"
variant="updown"
:auto-size="{ minRows: 2, maxRows: 5 }"
clearable
allow-speech
placeholder="๐ Here you can customize the prefix and action-list after variant"
>
<template #prefix>
<div
style="display: flex; align-items: center; gap: 8px; flex-wrap: wrap"
>
<el-button round plain color="#626aef">
<el-icon><Paperclip /></el-icon>
</el-button>
<div
:class="{ isSelect }"
style="
display: flex;
align-items: center;
gap: 4px;
padding: 2px 12px;
border: 1px solid silver;
border-radius: 15px;
cursor: pointer;
font-size: 12px;
"
@click="isSelect = !isSelect"
>
<el-icon><ElementPlus /></el-icon>
<span>Deep Thinking</span>
</div>
Left is custom prefix, right is custom action list
</div>
</template>
<template #action-list>
<div style="display: flex; align-items: center; gap: 8px">
<el-button round color="#626aef">
<el-icon><Promotion /></el-icon>
</el-button>
</div>
</template>
</MentionSender>
</div>
</template>
<style scoped lang="scss">
.isSelect {
color: #626aef;
border: 1px solid #626aef !important;
border-radius: 15px;
padding: 3px 12px;
font-weight: 700;
}
</style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
Custom Action List โ
Use the #action-list
slot to customize the input action list content.
๐ Info
When you use the #action-list
slot, the built-in input action buttons will be hidden. You can combine it with component instance methods
to implement richer operations.
<script setup lang="ts">
import {
Delete,
Loading,
Operation,
Position,
Promotion,
Right,
Setting
} from '@element-plus/icons-vue';
const senderRef = ref();
const senderValue = ref('');
const loading = ref(false);
function handleSubmit() {
console.log('submit', senderValue.value);
senderRef.value.submit();
loading.value = true;
}
function handleCancel() {
console.log('cancel');
loading.value = false;
}
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 12px">
<MentionSender>
<!-- Custom action list -->
<template #action-list>
<div class="action-list-self-wrap">
<el-button type="danger" circle>
<el-icon><Delete /></el-icon>
</el-button>
<el-button type="primary" circle style="rotate: -45deg">
<el-icon><Position /></el-icon>
</el-button>
</div>
</template>
</MentionSender>
<MentionSender>
<!-- Custom action list -->
<template #action-list>
<div class="action-list-self-wrap">
<el-button type="primary" plain circle color="#626aef">
<el-icon><Operation /></el-icon>
</el-button>
<el-button type="primary" circle color="#626aef">
<el-icon><Right /></el-icon>
</el-button>
</div>
</template>
</MentionSender>
<MentionSender>
<!-- Custom action list -->
<template #action-list>
<div class="action-list-self-wrap">
<el-button plain circle color="#eebe77">
<el-icon><Setting /></el-icon>
</el-button>
<el-button type="primary" plain circle>
<el-icon><Promotion /></el-icon>
</el-button>
</div>
</template>
</MentionSender>
<MentionSender ref="senderRef" v-model="senderValue" :loading="loading">
<!-- Custom action list -->
<template #action-list>
<div class="action-list-self-wrap">
<el-button
v-if="loading"
type="primary"
plain
circle
@click="handleCancel"
>
<el-icon class="is-loaidng">
<Loading />
</el-icon>
</el-button>
<el-button v-else plain circle @click="handleSubmit">
<el-icon><Position /></el-icon>
</el-button>
</div>
</template>
</MentionSender>
</div>
</template>
<style scoped lang="less">
.action-list-self-wrap {
display: flex;
align-items: center;
& > span {
width: 120px;
font-weight: 600;
color: var(--el-color-primary);
}
}
.is-loaidng {
animation: spin 1s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
Custom Prefix โ
Use the #prefix
slot to customize the input prefix content.
<script setup lang="ts">
import { CircleClose, Link } from '@element-plus/icons-vue';
const senderRef = ref();
const senderValue = ref('');
const showHeaderFlog = ref(false);
onMounted(() => {
showHeaderFlog.value = true;
senderRef.value.openHeader();
});
function openCloseHeader() {
if (!showHeaderFlog.value) {
senderRef.value.openHeader();
}
else {
senderRef.value.closeHeader();
}
showHeaderFlog.value = !showHeaderFlog.value;
}
function closeHeader() {
showHeaderFlog.value = false;
senderRef.value.closeHeader();
}
</script>
<template>
<div
style="
display: flex;
flex-direction: column;
gap: 12px;
height: 230px;
justify-content: flex-end;
"
>
<MentionSender ref="senderRef" v-model="senderValue">
<template #header>
<div class="header-self-wrap">
<div class="header-self-title">
<div class="header-left">
๐ฏ Welcome to Element Plus X
</div>
<div class="header-right">
<el-button @click.stop="closeHeader">
<el-icon><CircleClose /></el-icon>
<span>Close Header</span>
</el-button>
</div>
</div>
<div class="header-self-content">
๐ฆ Custom Header Content
</div>
</div>
</template>
<!-- Custom prefix -->
<template #prefix>
<div class="prefix-self-wrap">
<el-button dark>
<el-icon><Link /></el-icon>
<span>Custom Prefix</span>
</el-button>
<el-button color="#626aef" :dark="true" @click="openCloseHeader">
Open/Close Header
</el-button>
</div>
</template>
</MentionSender>
</div>
</template>
<style scoped lang="less">
.header-self-wrap {
display: flex;
flex-direction: column;
padding: 16px;
height: 200px;
.header-self-title {
width: 100%;
display: flex;
height: 30px;
align-items: center;
justify-content: space-between;
padding-bottom: 8px;
}
.header-self-content {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
color: #626aef;
font-weight: 600;
}
}
.prefix-self-wrap {
display: flex;
}
</style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
Custom Header โ
Use the #header
slot to customize the input header content.
๐ Info
Control header container expand/collapse through component instance
senderRef.value.openHeader()
Open header containersenderRef.value.closeHeader()
Close header container
<script setup lang="ts">
import { CircleClose } from '@element-plus/icons-vue';
const senderRef = ref();
const senderValue = ref('');
const showHeaderFlog = ref(false);
onMounted(() => {
showHeaderFlog.value = true;
senderRef.value.openHeader();
});
function openCloseHeader() {
if (!showHeaderFlog.value) {
senderRef.value.openHeader();
}
else {
senderRef.value.closeHeader();
}
showHeaderFlog.value = !showHeaderFlog.value;
}
function closeHeader() {
showHeaderFlog.value = false;
senderRef.value.closeHeader();
}
</script>
<template>
<div
style="
display: flex;
flex-direction: column;
gap: 12px;
height: 300px;
justify-content: space-between;
"
>
<el-button style="width: fit-content" @click="openCloseHeader">
{{ showHeaderFlog ? 'Close Header' : 'Open Header' }}
</el-button>
<MentionSender ref="senderRef" v-model="senderValue">
<template #header>
<div class="header-self-wrap">
<div class="header-self-title">
<div class="header-left">
๐ฏ Welcome to Element Plus X
</div>
<div class="header-right">
<el-button @click.stop="closeHeader">
<el-icon><CircleClose /></el-icon>
<span>Close Header</span>
</el-button>
</div>
</div>
<div class="header-self-content">
๐ฆ Custom Header Content
</div>
</div>
</template>
</MentionSender>
</div>
</template>
<style scoped lang="less">
.header-self-wrap {
display: flex;
flex-direction: column;
padding: 16px;
height: 200px;
.header-self-title {
width: 100%;
display: flex;
height: 30px;
align-items: center;
justify-content: space-between;
padding-bottom: 8px;
}
.header-self-content {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
color: #626aef;
font-weight: 600;
}
}
</style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
Custom Footer โ
Set input bottom content through the #footer
slot
๐ Info
If you want to set the #footer
slot and don't want the built-in layout of the updown variant, you can add the showUpdown
property to hide the built-in layout of the updown variant
<script setup lang="ts">
import { ElementPlus, Paperclip, Promotion } from '@element-plus/icons-vue';
const senderValue = ref('');
const isSelect = ref(false);
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 20px">
<MentionSender
v-model="senderValue"
:auto-size="{ minRows: 1, maxRows: 5 }"
clearable
allow-speech
placeholder="๐ Welcome to Element-Plus-X"
>
<template #prefix>
<div
style="display: flex; align-items: center; gap: 8px; flex-wrap: wrap"
>
<el-button round plain color="#626aef">
<el-icon><Paperclip /></el-icon>
</el-button>
</div>
</template>
<template #action-list>
<div style="display: flex; align-items: center; gap: 8px">
<el-button round color="#626aef">
<el-icon><Promotion /></el-icon>
</el-button>
</div>
</template>
<!-- Custom footer slot -->
<template #footer>
<div
style="
display: flex;
align-items: center;
justify-content: center;
padding: 12px;
"
>
Default variant custom footer
</div>
</template>
</MentionSender>
<MentionSender
v-model="senderValue"
variant="updown"
:auto-size="{ minRows: 2, maxRows: 5 }"
clearable
allow-speech
placeholder="๐ Here you can customize the prefix and action-list after variant"
>
<template #prefix>
<div
style="display: flex; align-items: center; gap: 8px; flex-wrap: wrap"
>
<el-button round plain color="#626aef">
<el-icon><Paperclip /></el-icon>
</el-button>
<div
:class="{ isSelect }"
style="
display: flex;
align-items: center;
gap: 4px;
padding: 2px 12px;
border: 1px solid silver;
border-radius: 15px;
cursor: pointer;
font-size: 12px;
"
@click="isSelect = !isSelect"
>
<el-icon><ElementPlus /></el-icon>
<span>Deep Thinking</span>
</div>
</div>
</template>
<template #action-list>
<div style="display: flex; align-items: center; gap: 8px">
<el-button round color="#626aef">
<el-icon><Promotion /></el-icon>
</el-button>
</div>
</template>
<!-- Custom footer slot -->
<template #footer>
<div
style="
display: flex;
align-items: center;
justify-content: center;
padding: 12px;
"
>
updown variant custom footer
</div>
</template>
</MentionSender>
<MentionSender
v-model="senderValue"
variant="updown"
:auto-size="{ minRows: 2, maxRows: 5 }"
clearable
allow-speech
placeholder="๐ Hide updown variant built-in layout by setting showUpdown to false"
:show-updown="false"
>
<template #prefix>
<div
style="display: flex; align-items: center; gap: 8px; flex-wrap: wrap"
>
<el-button round plain color="#626aef">
<el-icon><Paperclip /></el-icon>
</el-button>
<div
:class="{ isSelect }"
style="
display: flex;
align-items: center;
gap: 4px;
padding: 2px 12px;
border: 1px solid silver;
border-radius: 15px;
cursor: pointer;
font-size: 12px;
"
@click="isSelect = !isSelect"
>
<el-icon><ElementPlus /></el-icon>
<span>Deep Thinking</span>
</div>
</div>
</template>
<template #action-list>
<div style="display: flex; align-items: center; gap: 8px">
<el-button round color="#626aef">
<el-icon><Promotion /></el-icon>
</el-button>
</div>
</template>
<!-- Custom footer slot -->
<template #footer>
<div
style="
display: flex;
align-items: center;
justify-content: center;
padding: 12px;
text-align: center;
"
>
showUpdown property hides updown variant built-in layout style +
#footer bottom slot combination, completely letting you control the
bottom content
</div>
</template>
</MentionSender>
</div>
</template>
<style scoped lang="scss">
.isSelect {
color: #626aef;
border: 1px solid #626aef !important;
border-radius: 15px;
padding: 3px 12px;
font-weight: 700;
}
</style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
Custom Input Box Style โ
Pass through input styles conveniently through input-style
<script setup lang="ts">
import { ElementPlus, Paperclip, Promotion } from '@element-plus/icons-vue';
const senderValue = ref('This is custom input style');
const isSelect = ref(false);
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 20px">
<MentionSender
v-model="senderValue"
variant="updown"
:input-style="{
backgroundColor: 'rgb(243 244 246)',
color: '#626aef',
fontSize: '24px',
fontWeight: 700
}"
style="background: rgb(243 244 246); border-radius: 8px"
/>
<MentionSender
v-model="senderValue"
variant="updown"
:input-style="{
backgroundColor: 'transparent',
color: '#F0F2F5',
fontSize: '24px',
fontWeight: 700
}"
style="
background-image: linear-gradient(to left, #434343 0%, black 100%);
border-radius: 8px;
"
/>
<MentionSender
v-model="senderValue"
:input-style="{
backgroundColor: 'transparent',
color: '#FF5454',
fontSize: '20px',
fontWeight: 700
}"
style="
background-image: linear-gradient(
to top,
#fdcbf1 0%,
#fdcbf1 1%,
#e6dee9 100%
);
border-radius: 8px;
"
/>
<MentionSender
v-model="senderValue"
variant="updown"
:input-style="{
backgroundColor: 'transparent',
color: '#303133',
fontSize: '16px',
fontWeight: 700
}"
style="
background-image: linear-gradient(
to top,
#d5d4d0 0%,
#d5d4d0 1%,
#eeeeec 31%,
#efeeec 75%,
#e9e9e7 100%
);
border-radius: 8px;
"
>
<template #prefix>
<div
style="display: flex; align-items: center; gap: 8px; flex-wrap: wrap"
>
<el-button round plain color="#626aef">
<el-icon><Paperclip /></el-icon>
</el-button>
<div
:class="{ isSelect }"
style="
display: flex;
align-items: center;
gap: 4px;
padding: 2px 12px;
border: 1px solid black;
border-radius: 15px;
cursor: pointer;
font-size: 12px;
color: black;
"
@click="isSelect = !isSelect"
>
<el-icon><ElementPlus /></el-icon>
<span>Deep Thinking</span>
</div>
</div>
</template>
<template #action-list>
<div style="display: flex; align-items: center; gap: 8px">
<el-button round color="#626aef">
<el-icon><Promotion /></el-icon>
</el-button>
</div>
</template>
</MentionSender>
</div>
</template>
<style scoped lang="scss">
.isSelect {
color: #626aef !important;
border: 1px solid #626aef !important;
border-radius: 15px;
padding: 3px 12px;
font-weight: 700;
}
</style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
Input Focus Control โ
Control focus through ref option.
๐ Info
Control through component instance
senderRef.value.focus('all')
Focus on entire text (default)senderRef.value.focus('start')
Focus on text beginningsenderRef.value.focus('end')
Focus on text endsenderRef.value.blur()
Remove focus
<script setup lang="ts">
const senderRef = ref();
const senderValue = ref('๐ณ Welcome to Element Plus X');
function blur() {
senderRef.value.blur();
}
function focus(type = 'all') {
senderRef.value.focus(type);
}
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 12px">
<div style="display: flex">
<el-button dark type="success" plain @click="focus('start')">
Text Beginning
</el-button>
<el-button dark type="success" plain @click="focus('end')">
Text End
</el-button>
<el-button dark type="success" plain @click="focus('all')">
Entire Text
</el-button>
<el-button dark type="success" plain @click="blur">
Remove Focus
</el-button>
</div>
<MentionSender ref="senderRef" v-model="senderValue" />
</div>
</template>
<style scoped lang="less">
.header-self-wrap {
display: flex;
flex-direction: column;
padding: 16px;
height: 200px;
.header-self-title {
width: 100%;
display: flex;
height: 30px;
align-items: center;
justify-content: space-between;
padding-bottom: 8px;
}
.header-self-content {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
color: #626aef;
font-weight: 600;
}
}
</style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
Mention Directive Usage (Difference from Sender Component) โ
๐ Warning
Below are the properties and methods related to directives that are different from the Sender
component. Please pay attention to the usage differences.
๐ If you need to trigger a mention directive list in the middle of a content, you can use this component.
This warm tip was last updated: 2025-04-16
Custom Trigger Directive Array โ
- Through the
options
property, you can pass an array to define the mention option list. - Through the
triggerStrings
property, the prefix of the trigger field. This is different from theSender
component, where the string length must be exactly 1.
// Type definition for options mention option list
interface MentionOption {
value: string
label?: string
disabled?: boolean
[key: string]: any
}
2
3
4
5
6
7
๐ Info
Setting only the options
property cannot enable the mention functionality. The triggerStrings
property is needed to enable the mention functionality.
<script setup lang="ts">
const senderValue = ref('');
const options = [
{
value: 'value1',
label: 'Option 1'
},
{
value: 'value2',
label: 'Option 2',
disabled: true
},
{
value: 'value3',
label: 'Option 3'
}
];
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 20px">
<MentionSender
v-model="senderValue"
placeholder="Input / to trigger directive popover"
clearable
:options="options"
:trigger-strings="['/']"
/>
</div>
</template>
<style scoped lang="scss"></style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Custom Trigger Directive String โ
Trigger field through the triggerStrings
property. This is different from the Sender
component, where the string length must be exactly 1. The type is Array<string>
.
If you need to trigger through multiple strings, you can combine it with the @search
event to control the popover content display.
๐ Info
Setting only the options
property cannot enable the mention functionality. The triggerStrings
property is needed to enable the mention functionality.
<script setup lang="ts">
import type { MentionOption } from 'vue-element-plus-x/types/MentionSender';
const senderValue = ref('');
const MOCK_DATA: Record<string, string[]> = {
'@': [
'Element-Plus-X',
'HeJiaYue520',
'JsonLee12138',
'lisentowind',
'ZRMYDYCG'
],
'#': ['1.0', '2.0', '3.0', '4.0', '5.0']
};
const options = ref<MentionOption[]>([]);
function handleSearch(_: string, prefix: string) {
console.log('handleSearch', _, prefix);
options.value = (MOCK_DATA[prefix] || []).map(value => ({
value
}));
}
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 20px">
<MentionSender
v-model="senderValue"
placeholder="Input @ and # to trigger directive popover"
clearable
:options="options"
:trigger-strings="['@', '#']"
@search="handleSearch"
/>
</div>
</template>
<style scoped lang="scss"></style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
Custom Trigger Directive Separator โ
Character used to split mentions. The string length must be exactly 1, default is ' '
.
<script setup lang="ts">
import type { MentionOption } from 'vue-element-plus-x/types/MentionSender';
const senderValue1 = ref('');
const options = ref<MentionOption[]>([
{
value: 'HeJiaYue520',
avatar: 'https://avatars.githubusercontent.com/u/76239030'
},
{
value: 'JsonLee12138',
avatar: 'https://avatars.githubusercontent.com/u/160690954'
},
{
value: 'ageerle',
avatar: 'https://avatars.githubusercontent.com/u/32251822'
}
]);
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 20px">
<MentionSender
v-model="senderValue1"
placeholder="Input @ to trigger directive popover"
:options="options"
:trigger-strings="['@']"
trigger-split="ใ"
:whole="true"
>
<template #trigger-label="{ item }">
<div style="display: flex; align-items: center">
<el-avatar :size="24" :src="item.avatar" />
<span style="margin-left: 6px">{{ item.value }}</span>
</div>
</template>
</MentionSender>
</div>
</template>
<style scoped lang="scss"></style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
Trigger Directive Loading โ
Set the loading state of the mention option list through the triggerLoading
property. The triggerLoading
property defaults to false
, meaning no loading state is displayed by default.
<script setup lang="ts">
import type { MentionOption } from 'vue-element-plus-x/types/MentionSender';
const senderValue = ref('');
const MOCK_DATA: Record<string, string[]> = {
'@': [
'Element-Plus-X',
'HeJiaYue520',
'JsonLee12138',
'lisentowind',
'ZRMYDYCG'
],
'#': ['1.0', '2.0', '3.0', '4.0', '5.0']
};
const options = ref<MentionOption[]>([]);
const triggerLoading = ref(false);
function handleSearch(_: string, prefix: string) {
triggerLoading.value = true;
setTimeout(() => {
console.log('handleSearch', _, prefix);
options.value = (MOCK_DATA[prefix] || []).map(value => ({
value
}));
triggerLoading.value = false;
}, 1500);
}
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 20px">
<MentionSender
v-model="senderValue"
placeholder="Input @ and # to trigger directive popover"
clearable
:options="options"
:trigger-strings="['@', '#']"
:trigger-loading="triggerLoading"
@search="handleSearch"
/>
</div>
</template>
<style scoped lang="scss"></style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
Custom Trigger Directive Filter โ
Customize the filter option logic through filter-option
, using a method that returns true
or false
to control the option filtering results. You can also understand it as the search filtering logic.
The type is (pattern: string, option: MentionOption) => boolean
<script setup lang="ts">
import type { MentionOption } from 'vue-element-plus-x/types/MentionSender';
const senderValue1 = ref('');
const options = ref<MentionOption[]>([
{
value: 'HeJiaYue520',
avatar: 'https://avatars.githubusercontent.com/u/76239030'
},
{
value: 'JsonLee12138',
avatar: 'https://avatars.githubusercontent.com/u/160690954'
},
{
value: 'ageerle',
avatar: 'https://avatars.githubusercontent.com/u/32251822'
}
]);
function filterFunc(_: string, option: MentionOption): any {
// Here we print option, each time the directive is triggered, it will iterate through options and trigger filterFunc.
if (option.value === 'ageerle' || option.value === 'JsonLee12138') {
return true;
}
else if (option.value === 'HeJiaYue520') {
return false;
}
}
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 20px">
<MentionSender
v-model="senderValue1"
placeholder="Input @ to trigger directive popover, HeJiaYue520 has been filtered here"
:options="options"
:trigger-strings="['@']"
trigger-split=","
:whole="true"
:filter-option="filterFunc"
>
<template #trigger-label="{ item }">
<div style="display: flex; align-items: center">
<el-avatar :size="24" :src="item.avatar" />
<span style="margin-left: 6px">{{ item.value }}</span>
</div>
</template>
</MentionSender>
</div>
</template>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
Whole Deletion โ
-
Set the
whole
property totrue
, when you press the backspace key, themention
area here will be deleted as a whole. -
Set the
check-is-whole
property to customize the check logic. When you need to do multiple conditions, you can use thecheck-is-whole
property to customize the check logic. -
The check-is-whole property is not an event, the type is (pattern: string, prefix: string) => boolean, returning
true
means the match is successful and will be deleted as a whole, returningfalse
means the match fails and will not be deleted as a whole. Default istrue
<script setup lang="ts">
import type { MentionOption } from 'vue-element-plus-x/types/MentionSender';
const senderValue1 = ref('');
const senderValue2 = ref('');
const MOCK_DATA: Record<string, string[]> = {
'@': [
'Element-Plus-X',
'HeJiaYue520',
'JsonLee12138',
'lisentowind',
'ZRMYDYCG'
],
'#': ['1.0', '2.0', '3.0', '4.0', '5.0']
};
const options = ref<MentionOption[]>([]);
function handleSearch(_: string, prefix: string) {
options.value = (MOCK_DATA[prefix] || []).map(value => ({
value
}));
}
function checkIsWhole(pattern: string, prefix: string) {
console.log('checkIsWhole', pattern, prefix);
return (MOCK_DATA[prefix] || []).includes(pattern);
}
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 20px">
<MentionSender
v-model="senderValue1"
placeholder="Single directive delete as whole: Input @ to trigger directive popover"
clearable
:options="options"
:trigger-strings="['@']"
:whole="true"
@search="handleSearch"
/>
<MentionSender
v-model="senderValue2"
placeholder="Multiple directives delete as whole: Input @ and # to trigger directive popover"
clearable
:options="options"
:trigger-strings="['@', '#']"
whole
:check-is-whole="checkIsWhole"
@search="handleSearch"
/>
</div>
</template>
<style scoped lang="scss"></style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
Trigger Directive Popover Placement โ
Set the popup direction through trigger-popover-placement
. Default is 'top'
, can be set to 'bottom'
. Currently only supports 'top'
and 'bottom'
two types.
<script setup lang="ts">
import type { MentionOption } from 'vue-element-plus-x/types/MentionSender';
const senderValue1 = ref('');
const MOCK_DATA: Record<string, string[]> = {
'@': [
'Element-Plus-X',
'HeJiaYue520',
'JsonLee12138',
'lisentowind',
'ZRMYDYCG'
],
'#': ['1.0', '2.0', '3.0', '4.0', '5.0']
};
const options = ref<MentionOption[]>([]);
function handleSearch(_: string, prefix: string) {
options.value = (MOCK_DATA[prefix] || []).map(value => ({
value
}));
}
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 20px">
<MentionSender
v-model="senderValue1"
placeholder="Input @ and / to trigger directive popover"
clearable
:options="options"
:trigger-strings="['@', '/']"
:whole="true"
trigger-popover-placement="bottom"
@search="handleSearch"
/>
</div>
</template>
<style scoped lang="scss"></style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
Trigger Directive Popover Offset โ
You can set the offset of the popover from the window through the triggerPopoverOffset
property. Default value is 20, representing 20px.
<script setup lang="ts">
import type { MentionOption } from 'vue-element-plus-x/types/MentionSender';
const senderValue1 = ref('');
const MOCK_DATA: Record<string, string[]> = {
'@': [
'Element-Plus-X',
'HeJiaYue520',
'JsonLee12138',
'lisentowind',
'ZRMYDYCG'
],
'#': ['1.0', '2.0', '3.0', '4.0', '5.0']
};
const options = ref<MentionOption[]>([]);
function handleSearch(_: string, prefix: string) {
options.value = (MOCK_DATA[prefix] || []).map(value => ({
value
}));
}
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 20px">
<MentionSender
v-model="senderValue1"
placeholder="Input @ and / to trigger directive popover, current offset 50px"
clearable
:options="options"
:trigger-strings="['@', '/']"
:whole="true"
trigger-popover-placement="bottom"
:trigger-popover-offset="50"
@search="handleSearch"
/>
</div>
</template>
<style scoped lang="scss"></style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
Popover Slots โ
๐ Info
- Supports
#trigger-label
for customizing the label content, i.e., how each data item is displayed - Supports
#trigger-loading
for customizing the loading state, which is shown whentriggerLoading
is true - Supports
#trigger-header
for customizing the content at the top of the dropdown list - Supports
#trigger-footer
for customizing the content at the bottom of the dropdown list
๐ Warning
๐ Friendly Reminder: Since V1.3.1, the component ref can access the popover open state property popoverVisible
and the built-in input instance inputInstance
.
This means:
- You can perform some logic based on whether the popover is open.
- The popover can support richer custom events.
This reminder date: 2025-07-21
<script setup lang="ts">
import type { MentionOption } from 'vue-element-plus-x/types/MentionSender';
import { Loading } from '@element-plus/icons-vue';
import { MentionSender } from 'vue-element-plus-x';
const senderValue1 = ref('');
const options = ref<MentionOption[]>();
const triggerLoading = ref(false);
const senderRef = ref<InstanceType<typeof MentionSender>>();
onMounted(() => {
window.addEventListener('keydown', handleWindowKeydown);
senderRef.value?.inputInstance.addEventListener(
'keydown',
handleInputKeydown
);
});
onUnmounted(() => {
window.removeEventListener('keydown', handleWindowKeydown);
senderRef.value?.inputInstance.removeEventListener(
'keydown',
handleInputKeydown
);
});
function handleSearch() {
triggerLoading.value = true;
setTimeout(() => {
options.value = [
{
value: 'HeJiaYue520',
avatar: 'https://avatars.githubusercontent.com/u/76239030'
},
{
value: 'JsonLee12138',
avatar: 'https://avatars.githubusercontent.com/u/160690954'
},
{
value: 'ageerle',
avatar: 'https://avatars.githubusercontent.com/u/32251822'
}
];
triggerLoading.value = false;
}, 1500);
}
function handleWindowKeydown(e: KeyboardEvent) {
switch (e.key) {
case 'w':
ElMessage.success(`w pressed, input is not affected`);
console.log('w pressed');
break;
case 'a':
ElMessage.success(`a pressed, input is not affected`);
console.log('a pressed');
break;
case 's':
ElMessage.success(`s pressed, input is not affected`);
console.log('s pressed');
break;
case 'd':
ElMessage.success(`d pressed, input is not affected`);
console.log('d pressed');
break;
}
}
// When the popover is visible, prevent some key events in the input to avoid conflicts with global custom keyboard events for the mention popover
function handleInputKeydown(e: KeyboardEvent) {
if (['w', 'a', 's', 'd'].includes(e.key)) {
e.preventDefault();
}
}
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 20px">
<MentionSender
ref="senderRef"
v-model="senderValue1"
placeholder="Type @ to trigger the command popover"
:options="options"
:trigger-strings="['@']"
:whole="true"
:trigger-loading="triggerLoading"
@search="handleSearch"
>
<template #trigger-label="{ item }">
<div style="display: flex; align-items: center">
<el-avatar :size="24" :src="item.avatar" />
<span style="margin-left: 6px">{{ item.value }}</span>
</div>
</template>
<template #trigger-loading>
<div
style="
display: flex;
align-items: center;
justify-content: center;
height: 100%;
padding: 24px;
gap: 12px;
"
>
This is a custom loading state
<el-icon
class="is-loading"
style="color: cornflowerblue; font-size: 20px"
>
<Loading />
</el-icon>
</div>
</template>
<template #trigger-header>
<div
style="
display: flex;
align-items: center;
justify-content: center;
height: 100%;
padding: 8px;
"
>
This is my custom dropdown header content. You can also add custom key
controls for the popover. Try controlling the directions with the
w/a/s/d keys.
</div>
</template>
<template #trigger-footer>
<div
style="
display: flex;
align-items: center;
justify-content: center;
height: 100%;
padding: 8px;
"
>
This is custom dropdown footer content
</div>
</template>
</MentionSender>
</div>
</template>
<style scoped lang="scss"></style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
Search Event โ
Triggered when pressing the trigger field
<script setup lang="ts">
import type { MentionOption } from 'vue-element-plus-x/types/MentionSender';
const senderValue1 = ref('');
const options = ref<MentionOption[]>([
{
value: 'HeJiaYue520',
avatar: 'https://avatars.githubusercontent.com/u/76239030'
},
{
value: 'JsonLee12138',
avatar: 'https://avatars.githubusercontent.com/u/160690954'
},
{
value: 'ageerle',
avatar: 'https://avatars.githubusercontent.com/u/32251822'
}
]);
const filterOptions = ref();
function handleSearch(searchValue: string, prefix: string) {
console.log('Search value', searchValue);
console.log('Popover trigger character prefix', prefix); // Here you can judge multiple directive situations
// After the popover is called, this method will be called every time you type.
filterOptions.value = options.value.filter(option => {
// Here option.value is the content after '@'
// So we need to check if it contains the input content
if (searchValue) {
return option.value.includes(searchValue);
}
else {
// If there's no input content, show all options
return option;
}
});
}
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 20px">
<MentionSender
v-model="senderValue1"
placeholder="Input @ to trigger directive popover, HeJiaYue520 has been filtered here"
:options="filterOptions"
:trigger-strings="['@']"
trigger-split=","
:whole="true"
@search="handleSearch"
>
<template #trigger-label="{ item }">
<div style="display: flex; align-items: center">
<el-avatar :size="24" :src="item.avatar" />
<span style="margin-left: 6px">{{ item.value }}</span>
</div>
</template>
</MentionSender>
</div>
</template>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
Select Event โ
Triggered when user selects an option
<script setup lang="ts">
import type { MentionOption } from 'vue-element-plus-x/types/MentionSender';
const senderValue1 = ref('');
const options = ref<MentionOption[]>([
{
value: 'HeJiaYue520',
avatar: 'https://avatars.githubusercontent.com/u/76239030'
},
{
value: 'JsonLee12138',
avatar: 'https://avatars.githubusercontent.com/u/160690954'
},
{
value: 'ageerle',
avatar: 'https://avatars.githubusercontent.com/u/32251822'
}
]);
const filterOptions = ref();
function handleSearch(searchValue: string, prefix: string) {
console.log('Search value', searchValue);
console.log('Popover trigger character prefix', prefix); // Here you can judge multiple directive situations
// After the popover is called, this method will be called every time you type.
filterOptions.value = options.value.filter(option => {
// Here option.value is the content after '@'
// So we need to check if it contains the input content
if (searchValue) {
return option.value.includes(searchValue);
}
else {
// If there's no input content, show all options
return option;
}
});
}
function handleSelect(option: MentionOption) {
console.log('Selected value', option);
ElMessage.success(`Selected value: ${option.value}`);
}
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 20px">
<MentionSender
v-model="senderValue1"
placeholder="Input @ to trigger directive popover, HeJiaYue520 has been filtered here"
:options="filterOptions"
:trigger-strings="['@']"
trigger-split=","
:whole="true"
@search="handleSearch"
@select="handleSelect"
>
<template #trigger-label="{ item }">
<div style="display: flex; align-items: center">
<el-avatar :size="24" :src="item.avatar" />
<span style="margin-left: 6px">{{ item.value }}</span>
</div>
</template>
</MentionSender>
</div>
</template>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
Props โ
Name | Type | Required | Default | Description |
---|---|---|---|---|
v-model | String | No | '' | Bound value of the input box, use v-model for two-way binding. |
placeholder | String | No | '' | Placeholder text for the input box. |
auto-size | Object | No | { minRows:1, maxRows:6 } | Set the minimum and maximum number of visible rows for the input box. |
read-only | Boolean | No | false | Whether the input box is read-only. |
disabled | Boolean | No | false | Whether the input box is disabled. |
submitBtnDisabled | Boolean | undefined | No | undefined | Disable the built-in send button. (Note the usage scenario) |
loading | Boolean | No | false | Whether to show the loading state. When true , the input box will display a loading animation. |
clearable | Boolean | No | false | Whether the input box can be cleared. Shows the default clear button. |
allowSpeech | Boolean | No | false | Whether to allow voice input. Shows the built-in speech recognition button by default, using the browser's built-in speech recognition API. |
submitType | String | No | 'enter' | Submission method. Supports 'shiftEnter' (submit with Shift + Enter ), 'cmdOrCtrlEnter' (submit with Command + Enter or Ctrl + Enter ), 'altEnter' (submit with Alt + Enter ). |
headerAnimationTimer | Number | No | 300 | Custom header display duration in ms. |
inputWidth | String | No | '100%' | Width of the input box. |
variant | String | No | 'default' | Variant type of the input box. Supports 'default' , 'updown' . |
showUpdown | Boolean | No | true | Whether to show the built-in style when the variant is updown . |
inputStyle | Object | No | {} | Style of the input box. |
triggerStrings | string[] | No | [] | String array for trigger directives. |
triggerPopoverVisible | Boolean | No | false | Whether the popover for the trigger directive is visible. Control with v-model:triggerPopoverVisible . |
triggerPopoverWidth | String | No | 'fit-content' | Width of the popover for the trigger directive. Supports percentage and other CSS units. |
triggerPopoverLeft | String | No | '0px' | Left margin of the popover for the trigger directive. Supports percentage and other CSS units. |
triggerPopoverOffset | Number | No | 8 | Offset of the popover for the trigger directive. Must be a number, unit is px. |
triggerPopoverPlacement | String | No | 'top-start' | Placement of the popover for the trigger directive. Values: 'top' | 'top-start' | 'top-end' | 'bottom' | 'bottom-start' | 'bottom-end' | 'left' | 'left-start' | 'left-end' | 'right' | 'right-start' | 'right-end' |
Events โ
Event Name | Description | Callback Parameter |
---|---|---|
submit | Triggered when the built-in submit button is clicked. | None |
cancel | Triggered when the built-in loading button is clicked. | None |
recordingChange | Triggered when the built-in speech recognition state changes. | None |
select | Triggered when the trigger field is pressed. | option: MentionOption |
search | Triggered when the user selects an option. | searchValue: string, prefix: string |
Ref Instance Methods โ
Name | Type | Description |
---|---|---|
openHeader | Function | Open the custom header of the input box. |
closeHeader | Function | Close the custom header of the input box. |
clear | Function | Clear the content of the input box. |
blur | Function | Remove focus from the input box. |
focus | Function | Focus the input box. By default, focus('all') selects all text, focus('start') focuses at the start, focus('end') focuses at the end. |
submit | Function | Submit the input content. |
cancel | Function | Cancel the loading state. |
startRecognition | Function | Start speech recognition. |
stopRecognition | Function | Stop speech recognition. |
popoverVisible | Boolean | Popover visibility for the trigger directive. |
inputInstance | Object | Input box instance. |
Slots โ
Slot Name | Parameter | Type | Description |
---|---|---|---|
#header | - | Slot | For customizing the header content. |
#prefix | - | Slot | For customizing the prefix content. |
#action-list | - | Slot | For customizing the action list. |
#footer | - | Slot | For customizing the footer content. |
#trigger-label | #trigger-label={ item, index } | Slot | For customizing the popover label. |
#trigger-loading | - | Slot | For customizing the popover loading state. |
#trigger-header | - | Slot | For customizing the popover header. |
#trigger-footer | - | Slot | For customizing the popover footer. |
Features โ
- Focus Control: Supports setting focus to the start, end, or selecting all text, and can also remove focus.
- Custom Content: Provides slots for header, prefix, and action list, allowing users to customize these parts.
- Submission Function: Supports submitting input with
Shift + Enter
, and allows custom actions after submission. - Loading State: Can display a loading state to simulate the submission process.
- Voice Input: Supports voice input for more convenient input.
- Clear Function: The input box can be cleared for easy re-entry.