Mention List
The panel that opens when you type @ or / in a message. The text field keeps focus and moves through the options with the arrow keys. Options can sit in groups, and status rows say when a source is loading, empty, or failed.
Files with @
Type @ in the message, or press Add file. With Google Drive connected, results sit in a Files group and a Google Drive group, and each source has its own status row. Without Drive the files are one flat list. Search, files, and Drive in this example are sample data.
Usage
import {
MentionGroup,
MentionList,
MentionOption,
MentionStatus,
} from "@tulipfarm/sprout/mention-list";
<Textarea
aria-autocomplete="list"
aria-controls={open ? listId : undefined}
aria-activedescendant={open && options.length ? optionId(active) : undefined}
onKeyDown={moveWithArrows}
/>
{open && (
<MentionList
id={listId}
label="Files you can add"
status={loading ? <MentionStatus tone="loading">Searching your files…</MentionStatus> : undefined}
announcement={`${options.length} files you can add. Use Up and Down to choose, Enter to add, Escape to close.`}
>
<MentionGroup label="Files">
{files.map((file, index) => (
<MentionOption
key={file.id}
id={optionId(index)}
active={index === active}
onSelect={() => add(file)}
media={<FileTypeIcon name={file.name} mediaType={file.mediaType} />}
title={file.name}
description="48 KB"
/>
))}
</MentionGroup>
</MentionList>
)}MentionList takes the listbox id and a label. status holds one or more MentionStatus rows under the options. announcement is read aloud when it changes, because a textarea keeps its textbox role and cannot say that a list opened.
MentionOption needs an id for aria-activedescendant, active, onSelect, and a title. media (a 16px icon, file icon, brand glyph, or small avatar) and description are optional. Long titles end in an ellipsis and show in full on hover. The active option scrolls into view.
MentionStatus has three tones: loading adds a spinner,empty is muted text, and error is an alert in the danger color. Pass action for a small outline button such as Retry.
Skills with /
One flat list with a book icon and the skill version. Typing /c at the start also offers the /compact command. Picking a skill stages it above the message.
Rich editor panel
The rich text editor shares one panel for files and skills. An error wins over loading, and loading wins over the empty text. An option can have no icon or no description.
4 options. Use Up and Down to choose, Enter to select, Escape to close.
Nothing selected yet.
States
Each source reports its own status, so rows can stack: results with a source still loading, or two failed sources where only one offers a retry.
2 files you can add. Use Up and Down to choose, Enter to add, Escape to close.
No retries yet.
People
Grouped people and agents with avatars. Click a row to select it.
Alex Meadow selected
Accessibility
Focus stays in the text field. ↓ and ↑ move the active option and wrap across groups, Enter or Tab picks it, and Escape closes the panel. When a search failed, Enter retries. Options and the retry button ignore mouse focus, so the caret never leaves the message. Touch rows are 44px tall; mouse rows are 36px. The panel rises in over 120ms and row highlights fade over 120ms; reduced motion turns both off. In forced colors the active option gets an outline.