Components
Mentions
The vessel: the candidates themselves as a floating card. The anchor is virtual — a live rectangle off the host's field — so a host keeps its own anatomy (the textarea rides where the host puts it) and the vessel still points at the right place. Shares the detection state with the host through `useMentions`.
#Basic usage
The field is empty.
<script setup lang="ts">
import { Mentions } from "@bysages/vue";
import { ref } from "vue";
const text = ref("");
const teammates = [
{ label: "Lin Hua", value: "lin" },
{ label: "Mei Chen", value: "mei" },
{ label: "Hong Wei", value: "hong" },
{ label: "Yan Wu", value: "yan" },
{ label: "Zhou Lan", value: "zhou" },
];
</script>
<template>
<div class="w-full">
<Mentions
v-model="text"
:items="teammates"
placeholder="Describe the task and @who should read it…"
/>
<p role="status" class="mt-4 text-sm text-tertiary">
{{ text ? `Field holds: ${text}` : "The field is empty." }}
</p>
</div>
</template>
<script setup lang="ts">
import { Mentions } from "@bysages/vue";
import { ref } from "vue";
const text = ref("Review with #");
const reviews = [
{ label: "Design review", value: "design-review" },
{ label: "Editorial review", value: "editorial-review" },
{ label: "Civic review", value: "civic-review" },
];
</script>
<template>
<!-- A different trigger character, for systems where @ already means
something else. -->
<Mentions
v-model="text"
:items="reviews"
trigger="#"
placeholder="Tag a review…"
class="w-full"
/>
</template>
<script setup lang="ts">
import { Field, Mentions } from "@bysages/vue";
import { ref } from "vue";
const text = ref("");
const teammates = [
{ label: "Lin Hua", value: "lin" },
{ label: "Mei Chen", value: "mei" },
{ label: "Hong Wei", value: "hong" },
{ label: "Yan Wu", value: "yan" },
{ label: "Zhou Lan", value: "zhou" },
];
</script>
<template>
<!-- Inside a Field.Root the field picks up the label wiring and the
invalid state on its own. -->
<Field.Root invalid class="w-full">
<Field.Label>Describe the task</Field.Label>
<Mentions
v-model="text"
:items="teammates"
placeholder="Describe the task and @who should read it…"
/>
<Field.ErrorText>Every task needs a body before it files.</Field.ErrorText>
</Field.Root>
</template>
#Props
#MentionsVessel
The vessel: the candidates themselves as a floating card. The anchor
is virtual — a live rectangle off the host's field — so a host keeps
its own anatomy (the textarea rides where the host puts it) and the
vessel still points at the right place. Shares the detection state
with the host through useMentions.
| Prop | Type | Default | Description |
|---|---|---|---|
open | Boolean | false | — |
matches | Array as PropType<MentionEntry[]> | () => [] | — |
active | Number | 0 | — |
anchor | Object as PropType<HTMLTextAreaElement | null> | null | — |
size | String as PropType<"sm" | "md" | "lg"> | "md" | The host field's rung, so the rows keep the field's register. |
#Mentions
@-mentions: a plain textarea that, when the text before the caret ends
with the trigger character followed by a token, offers the matching
candidates in a small anchored vessel; choosing one replaces the token
with trigger + label and hands the whole text back through
update:modelValue. Arrows move, Enter inserts, Escape dismisses.
The field is the shared Field.Textarea — field wiring (label ids,
the invalid state, autoresize) rides on it for free — and the vessel
anchors to the field as a whole (popover machinery), not to the caret
coordinates; caret-precise positioning would need a second
positioning system for no practical gain at typical field sizes.
Composers that keep their own field anatomy (the AI prompt input)
skip this shell and wire useMentions plus MentionsVessel
themselves.
| Prop | Type | Default | Description |
|---|---|---|---|
items | Array as PropType<MentionEntry[]> | () => [] | — |
modelValue | String | undefined | — |
trigger | String | "@" | — |
placeholder | String | undefined | — |
autoresize | Boolean | false | — |
invalid | Boolean | false | — |
size | String as PropType<"sm" | "md" | "lg"> | "md" | One rung of the control-height ladder for the resting field. |