MentionMenu
An anchored, grouped autocomplete popup for a caret-driven "@mention" trigger.
A floating, caret-anchored autocomplete popup — built for RichTextEditor's renderMentionPicker (crisp#372, #428), but generic: anchor is a bare {x, y} point, not a trigger element, so anything that already computes a caret position (a plain <input>/<textarea> via useMentionTrigger, a custom combobox) can drive one.
Ships two things together, since neither is useful alone: grouped-section rows (MentionMenuSection / MentionMenuItem, crisp#646 — a consumer still supplies its own "Users"/"Records" data, crisp only owns the group/row chrome and behaviour) and the floating, caret-anchored positioning + keyboard model (crisp#1009) that makes that list usable next to real text instead of centered like Command.
Installation
npm install @crisp-ui-kit/crispProps
MentionMenu
MentionMenuSection
MentionMenuItem
Notes
- Why not
Command.Commandcomposes a Zag dialog machine (modal, centered, focus-trapped) — its props have no anchor/position concept, and Zag's dialog machine has no notion of a virtual anchor point at all.MentionMenuinstead sharesMenu's approach (FloatingPortal+placeFloating), the one already built for a point-anchored popover. - Real DOM focus never moves. Unlike
Menu's roving-focus buttons,MentionMenunever takes focus away from wherever the consumer's own field has it. ↑/↓/Enter/Escape are captured atdocument, ahead of the field's own keydown handling, and a virtualdata-highlightedmarker (mouse hover and keyboard agree on the one attribute) tracks the active row instead. A consumer that owns a real<input>and wants fullaria-activedescendantwiring can pass a stableid— each row's DOM id is${id}-option-${value}. - Grouping crosses section boundaries. ↑/↓ walk every
MentionMenuItemin document order regardless of whichMentionMenuSectionit's in, the same way a flat list would. - Placement. Flips above the anchor when there isn't room below (the same threshold
Menuuses). Horizontal viewport-edge clamping was intentionally left out pending a live measurement against Attio's own@mentionpopover — see the component's own doc comment. - A bare trigger shows nothing. Matching Attio: typing "@" with no query after it should render no picker at all — that's the consumer's own call inside
renderMentionPicker(returnnulluntilqueryis non-empty), not somethingMentionMenuenforces itself.