manifold/web/components/linkify.tsx

49 lines
1.3 KiB
TypeScript
Raw Normal View History

2021-12-19 09:08:12 +00:00
import { Fragment } from 'react'
import { SiteLink } from './site-link'
2021-12-19 09:08:12 +00:00
// Return a JSX span, linkifying @username, #hashtags, and https://...
2022-02-16 18:32:44 +00:00
// TODO: Use a markdown parser instead of rolling our own here.
2022-01-04 22:09:03 +00:00
export function Linkify(props: { text: string; gray?: boolean }) {
const { text, gray } = props
2022-02-16 18:32:44 +00:00
const regex =
2022-04-11 00:47:26 +00:00
/(?:^|\s)(?:[@#][a-z0-9_]+|https?:\/\/[-A-Za-z0-9+&@#\/%?=~_()|!:,.;]*[-A-Za-z0-9+&@#\/%=~_|])/gi
2021-12-19 09:08:12 +00:00
const matches = text.match(regex) || []
const links = matches.map((match) => {
// Matches are in the form: " @username" or "https://example.com"
const whitespace = match.match(/^\s/)
const symbol = match.trim().substring(0, 1)
const tag = match.trim().substring(1)
const href =
{
'@': `/${tag}`,
'#': `/tag/${tag}`,
}[symbol] ?? match.trim()
2021-12-19 09:08:12 +00:00
return (
<>
{whitespace}
2022-01-04 22:09:03 +00:00
<SiteLink
className={gray ? 'text-gray-500' : 'text-indigo-700'}
href={href}
>
{symbol}
{tag}
</SiteLink>
2021-12-19 09:08:12 +00:00
</>
)
})
return (
<span
className="break-words"
style={{ /* For iOS safari */ wordBreak: 'break-word' }}
>
2021-12-19 09:08:12 +00:00
{text.split(regex).map((part, i) => (
<Fragment key={i}>
{part}
{links[i]}
</Fragment>
))}
</span>
)
}