Mentions
A mention input component that pops a suggestion overlay when typing @ (or # etc.), suited for @member / @task / comment-posting scenarios.
Basic Usage
Basic usage
Typing @ pops the suggestion list: ↑/↓ to select, Enter to insert (the selected item is merged into the text), Esc or clicking outside closes it.
Keyword Filtering
Keyword filtering
Continue typing after @ to filter: by default it fuzzy-matches label OR value (case-insensitive) (@alice hits "Alice"); when nothing matches, the built-in empty state "No matching mentions" shows. For a custom matcher, set the el.filterOption(query, option) function (JS property channel).
Trigger Right After Chinese Text
Trigger without a leading space
No whitespace is required before @: the scan walks back from the caret until a space/newline, so 大家好@张 (Chinese text glued to @) triggers. A keyword segment containing a space or newline does not trigger. While an IME is composing (pinyin candidate not yet confirmed), Enter/↑/↓ will not select suggestions or insert a newline; the panel re-scans after the composition commits.
Multiple Triggers (@ for members / # for tasks)
trigger array (@ / #)
trigger defaults to @; it accepts a single string or a JSON array like ["@","#"]. The oas-search/oas-select details carry the matched prefix field, letting the host route each trigger to its own data source. prefix is a compatibility alias of trigger, usable through both attribute and property channels.
Async Suggestions (oas-search + loading)
Remote search (simulated)
When a prefix is hit or the keyword changes, oas-search fires immediately (detail { query, prefix }, no built-in debounce — debounce in the host). While the host is fetching, set loading and the panel shows "Loading…"; after options is filled in the list appears. Moving the caret within the same keyword does not re-fire; re-triggering or a keyword change does.
Custom Option Rendering (rich labels / avatars)
Rich options (oas-option-render)
After each option row is rendered, oas-option-render fires with detail { index, option, element } — element is the label container the host can rewrite (avatar / two-line rich content), and option carries every extra field from the options JSON. Alternatively, put a <template slot="option"> skeleton inside the component; the [data-option-label] node is bound to the option text automatically.
Disabled Options
Disabled options (visible but not selectable)
Options with disabled: true stay visible but are not selectable: ↑/↓, Home/End keyboard navigation skips them, clicks are ignored, and aria-disabled is synced. When every option is disabled, Enter just closes the panel without inserting anything.
Clearable
Clearable
Panel direction & separator (placement / split)
placement + split
When the text is non-empty and the field is neither disabled nor readonly, a clear button shows in the top-right corner; clicking it clears the text and fires oas-clear (detail carries the previous value) plus an empty oas-change.
Auto-grow (autosize)
Comment box (autosize + max-rows)
With autosize, the height adapts between min-rows (default 1) and max-rows (default 6; an explicit "0" means unlimited) and scrolls past the cap. min-rows="2" is typical for comment boxes to leave a comfortable first-line height.
Whole delete (whole)
Backspace removes a whole mention in one stroke (whole)
With whole on, pressing Backspace while the caret sits right after an inserted mention (e.g. "@Alice"; long names containing spaces like "@Bob (teammate)" are also removed in one stroke) removes the whole "trigger + member name" and fires oas-whole-remove (detail { value, option, prefix }, where option is the full original option object). Caret positions that do not match still delete character by character; the separator between the mention and the following text (a space by default) is not removed along with it — delete it separately as needed. Without whole, Backspace deletes character by character (press several times for long names, or undo to restore).
Single-line mode (type)
Single-line @assign (type=input)
type defaults to textarea (multi-line); set it to input to compress the field into a single-line input (height locked to one row, Enter does not break the line), suited for scenarios like "single-line @assign"; it still combines with autosize / clearable etc.
Size / Variant / Status / Readonly
Size
size supports small / medium / large (follows the size injected by a nearby oas-config-provider).
Variant
Status
status supports success / warning / error: error also sets aria-invalid on the combobox, and it can be driven by oas-form-item / oas-form validation (a host-level aria-invalid is mirrored onto the input automatically).
Readonly / Disabled
readonly is focusable and copyable but not editable and never opens the panel; disabled is fully greyed out.
Panel Slots (header / footer / empty)
Header / footer / empty slots
The header / footer slots render panel chrome (hint bar / summary), and the empty slot replaces the default "No matching mentions" text. The split attribute (default: space) controls the delimiter used both for scanning boundaries and for the space inserted after a mention — switch it when you need a different separator such as a comma.
Deleting a Mention
A mention is written into the text as plain text. With whole on, pressing Backspace with the caret right after a mention removes the whole segment at once; without whole, deletion is character by character (long names containing spaces need several presses — undo also helps). While the suggestion panel is open, Home/End jump to the first/last suggestion and ↑/↓ cycle (skipping disabled items); in type=input single-line mode, Enter with the panel closed is intercepted and does not break the line.
Events
Event feedback
oas-select detail is { value, label, option, prefix } — option is the full original option object (including extra fields), so the host does not need to look up the id afterwards.
Accessible Name (label)
label (accessible name)
label becomes the accessible name (aria-label) of the combobox input: screen readers announce it once set; otherwise it falls back to placeholder → built-in text "Mentions input".
Controlled Value
value (controlled)
The value attribute is the controlled channel; external changes sync to the textarea immediately. While the user is typing, unrelated attribute changes (options/loading) never overwrite the in-progress draft.
required
required (native validation chain)
required drives the native validation chain (form-associated): when unfilled, checkValidity() returns false (valueMissing) and native form submission is blocked; it recovers to :valid once filled.
Programmatic read/write of the current value goes through the public value property (like the native textarea.value): el.value reads the full current text (including @mention marks); el.value = x writes the controlled value attribute and immediately syncs the inner control (controlled assignment takes precedence over unconfirmed drafts) without dispatching events.
API
oas-mentions
Attributes
| Attribute | Description | Type | Default |
|---|---|---|---|
autosize | Auto-grow (pair with min-rows/max-rows) | boolean | — |
clearable | Clear button (clears all content, fires oas-clear) | boolean | — |
disabled | Disabled | boolean | — |
label | Accessible name (built-in text by default) | — | — |
loading | Suggestion panel loading state (during remote search; pair with oas-search) | boolean | — |
max-rows | Maximum rows (with autosize; "0" uncaps) | string | — |
min-rows | Minimum rows (with autosize) | string | 1 |
options | Options, JSON array [{ label, value }] | Option[] | string | [] |
placeholder | Placeholder text | string | — |
placement | Panel direction: auto (default) / top / bottom | string | auto |
readonly | Read-only (focusable and readable, no suggestion panel) | boolean | — |
required | Required marker (drives the native valueMissing validation chain; not passed through to the inner control) | boolean | — |
size | Size preset small / medium (default) / large | string | medium |
split | Separator between a mention and following text (default space; not doubled when already separated) | string | |
status | Validation status: error / warning / success | string | — |
trigger | Trigger: default @, accepts a single string or a JSON array like ["@","#"] for multiple triggers | string | string[] | @ |
type | Variant: textarea (default) / input (single-line lock, Enter does not break line) | string | textarea |
value | Value (controlled, full text) | string | — |
variant | Variant: outlined (default) / filled / borderless | string | outlined |
whole | Whole delete: when cursor is right after a mention, Backspace removes the whole prefix + label and fires oas-whole-remove | boolean | — |
Property (JS property; see each row for attribute reflection)
| Property | Description | Type | Default |
|---|---|---|---|
value | Current value: the full text including @mention tokens | string | — |
Events
| Event | Description |
|---|---|
oas-blur | Fires on blur, detail: { value: this.ta.value } |
oas-change | Text changed after insertion, detail: { value } (full text) |
oas-clear | Fires on clear, detail: { value } (pre-clear value) |
oas-focus | Fires on focus, detail: { value: t.value } |
oas-input | Fires on input (not during IME composition), detail: { value } |
oas-option-render | Fires on option render (rich option channel), detail: { index, option, element } |
oas-search | Fires when the trigger scan finds pending text (remote search hook), detail: { query, prefix } |
oas-select | Suggestion selected, detail: { value, label, option, prefix } (option is the full original option object) |
oas-whole-remove | Fires when a mention is whole-deleted, detail: { value, option, prefix } (option is the full original option object) |
Slots
| Name | Description |
|---|---|
empty | Empty-state content when nothing matches (replaces the default noMatch text) |
footer | Panel footer content |
header | Panel header content |
template[slot="option"] | Custom option template ([data-option-label] binding) |
CSS Variables
| CSS Variable | Default |
|---|---|
--oas-glass-blur | none |
--oas-glass-ring | transparent |