(READ ONLY) Margin is an open annotation layer for the internet. Powered by the AT Protocol.
margin.at
extension
web
atproto
comments
1import React from "react";
2import { Link } from "react-router-dom";
3
4interface RichTextProps {
5 text: string;
6 className?: string;
7}
8
9const MENTION_REGEX =
10 /(^|[\s(])@([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)/g;
11
12export default function RichText({ text, className }: RichTextProps) {
13 const parts: React.ReactNode[] = [];
14 let lastIndex = 0;
15
16 for (const match of text.matchAll(MENTION_REGEX)) {
17 const fullMatch = match[0];
18 const prefix = match[1];
19 const handle = match[2];
20 const startIndex = match.index!;
21
22 if (startIndex > lastIndex) {
23 parts.push(text.slice(lastIndex, startIndex));
24 }
25
26 if (prefix) {
27 parts.push(prefix);
28 }
29
30 parts.push(
31 <Link
32 key={startIndex}
33 to={`/profile/${handle}`}
34 className="text-primary-600 dark:text-primary-400 hover:underline"
35 onClick={(e) => e.stopPropagation()}
36 >
37 @{handle}
38 </Link>,
39 );
40
41 lastIndex = startIndex + fullMatch.length;
42 }
43
44 if (lastIndex < text.length) {
45 parts.push(text.slice(lastIndex));
46 }
47
48 if (parts.length === 0) {
49 return <span className={className}>{text}</span>;
50 }
51
52 return <span className={className}>{parts}</span>;
53}