PXD

Mention

Mention people or entities inside rich text with @, backed by a searchable suggestion list.

Default

Hello @Alice​
<script setup>
import { ref } from 'vue'

const value = ref('Hello <at key="1">Alice</at>')

const options = [
  { label: 'Alice', value: '1' },
  { label: 'Bob', value: '2' },
  { label: 'Charlie', value: '3' },
]
</script>

<template>
  <PMention v-model="value" class="max-w-md" :options="options" placeholder="Type @ to mention" search-placeholder="Search..." />
</template>

Async filter


<script setup>
import { ref } from 'vue'

const value = ref('')

const all = [
  { label: 'Alice', value: '1', keywords: ['alice'] },
  { label: 'Bob', value: '2', keywords: ['bob'] },
  { label: 'Charlie', value: '3', keywords: ['charlie'] },
  { label: 'Diana', value: '4', keywords: ['diana'] },
]

async function filterMethod(query) {
  await new Promise((resolve) => setTimeout(resolve, 200))
  const needle = query.toLowerCase()
  return all.filter((item) => item.label.toLowerCase().includes(needle))
}
</script>

<template>
  <PMention
    v-model="value"
    class="max-w-md"
    :filter-method="filterMethod"
    placeholder="Type @ then search in the popover"
    search-placeholder="Filter people"
  />
</template>

Mention click

Ping @Alice​
<script setup>
import { ref } from 'vue'

const value = ref('Ping <at key="1">Alice</at>')
const last = ref('')

const options = [
  { label: 'Alice', value: '1' },
  { label: 'Bob', value: '2' },
]
</script>

<template>
  <PStack direction="vertical" class="max-w-md">
    <PMention
      v-model="value"
      :options="options"
      placeholder="Type @ to mention"
      search-placeholder="Search..."
      @mention-click="(payload) => (last = `${payload.label}#${payload.key}`)"
    />
    <PText v-if="last" class="text-sm text-foreground-secondary">Clicked: {{ last }}</PText>
  </PStack>
</template>

Props

NameTypeDefaultDescription
model-valuestring''Mention HTML: text + <at key="...">label</at>
optionsListOptions() => []Default options when the popover opens with an empty query
size'sm' | 'md' | 'lg'-Size of the editor, falls back to the config provider size
virtualbooleanfalseEnable virtualized rendering for large option sets
filter-method(query: string) => ListOptions | Promise<ListOptions>-Async/sync search; errors fall back to an empty list
placeholderstring''Editor placeholder
search-placeholderstring''Suggestion search input placeholder
disabledbooleanfalseMake the editor read-only and stop the suggestion popover from opening
close-on-press-escapebooleantrueClose the suggestion popover when pressing Escape

Events

NameTypeDescription
change(value: string) => voidEmitted when the editor HTML changes.
mention-click(payload: MentionClickPayload) => voidEmitted when a mention chip is clicked.
update:modelValue(value: string) => voidEmitted when the editor HTML changes.

Slots

NameDescription
itemCustom list item: { item, index, group, groupIndex }
groupCustom group label: { group, index }
emptyCustom empty search result content

Notes

  • Type @ in the editor to open suggestions. The @ stays until you pick an item (so typing a literal @ remains possible if you dismiss the popover).
  • Mentions are read-only chips (contenteditable="false"). Backspace removes a whole chip.
  • Paste keeps only legal <at key> nodes; everything else becomes plain text.
  • Undo/redo uses the browser’s native contenteditable history (Ctrl/Cmd+Z, etc.).

Source

Source