import { ReactNode, forwardRef } from 'react' import { useTranslation } from 'tldraw' import { Avatar } from './avatar' import { CommentAuthor } from './comment-author' /** A person the composer's \@-mention picker can offer: a {@link CommentAuthor} plus its id and * picker-only display fields. @public */ export interface MentionMember extends CommentAuthor { id: string /** A secondary line under the name — e.g. an email or handle. Omit for a single-line row. */ secondary?: string /** Marks the current user; shown as a "(You)" suffix after the name. */ you?: boolean /** Hosts may carry extra fields; a custom `renderMember` can read them. */ [key: string]: unknown } /** @public */ export interface MentionListProps { /** Members to choose from — already resolved for the current query by the host. */ members: MentionMember[] /** Index of the highlighted row, driven by the composer's keyboard navigation. */ activeIndex?: number /** Called when a member is chosen (click, or Enter on the active row). */ onSelect?(member: MentionMember): void /** Shown when no member matches the query. Defaults to a translated "No matches". */ emptyLabel?: string /** Override a row's content (inside the selectable button). Defaults to avatar + name (+ secondary). */ renderMember?(member: MentionMember): ReactNode } /** The default row content: avatar, name (with a "(You)" marker), and an optional secondary line. */ function DefaultMemberRow({ member }: { member: MentionMember }) { const msg = useTranslation() return ( <> {member.name} {member.you && ( {`(${msg('comments.mention-you')})`} )} {member.secondary && ( {member.secondary} )} ) } /** * The \@-mention member picker: a popover list of people to mention, shown as the user types after * `@`. Presentational — the composer's suggestion plugin owns resolution, keyboard navigation, and * placement, and drives this with the host-resolved `members` and the highlighted `activeIndex`. * @public @react */ export const MentionList = forwardRef(function MentionList( { members, activeIndex = 0, onSelect, emptyLabel, renderMember }, ref ) { const msg = useTranslation() if (members.length === 0) { return (
{emptyLabel ?? msg('comments.mention-no-matches')}
) } return (
{members.map((member, i) => { const active = i === activeIndex return ( ) })}
) })