Files
spsays-training/components/social/mention-text.tsx
T
2026-08-07 23:47:22 +05:30

57 lines
1.6 KiB
TypeScript

"use client";
/**
* MentionText — renders user-authored plain text with `@handle` tokens turned
* into profile links.
*
* The single place that decides what a mention looks like. Shared by comment
* bodies and profile bios so the two can't drift apart, and so the
* "@handle" grammar lives next to the regex the composer
* (`MentionTextarea`) uses to detect it.
*
* Input is always PLAIN TEXT, never HTML — we split on the token and emit
* React nodes, so nothing user-supplied is ever interpreted as markup.
* `whitespace-pre-line` on the wrapper preserves the author's line breaks
* (bios are multi-line) without needing <br> injection.
*/
import Link from "next/link";
import { cn } from "@/lib/utils";
/** Capturing group keeps the delimiters, so split() yields text and tokens
* alternately. Handle charset matches MentionTextarea's MENTION_RE. */
const MENTION_TOKEN = /(@[A-Za-z0-9_]+)/g;
/** Split text into plain runs + clickable @handle links. Exported for callers
* that need the nodes without the wrapper element. */
export function renderMentions(text: string) {
return text.split(MENTION_TOKEN).map((part, i) => {
if (part.length > 1 && part[0] === "@") {
return (
<Link
key={i}
href={`/u/${part.slice(1)}`}
className="font-medium text-primary hover:underline"
>
{part}
</Link>
);
}
return part;
});
}
export function MentionText({
text,
className,
}: {
text: string;
className?: string;
}) {
return (
<span className={cn("whitespace-pre-line", className)}>
{renderMentions(text)}
</span>
);
}