feat: profile bio
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
"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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user