navidrome/ui/src/common/Linkify.jsx
Deluan Quintão fcdd30ba8f
build(ui): migrate from CRA/Jest to Vite/Vitest (#3311)
* feat: create vite project

* feat: it's alive!

* feat: `make dev` working!

* feat: replace custom serviceWorker with vite plugin

* test: replace Jest with Vitest

* fix: run prettier

* fix: skip eslint for now.

* chore: remove ui.old folder

* refactor: replace lodash.pick with simple destructuring

* fix: eslint errors (wip)

* fix: eslint errors (wip)

* fix: display-name eslint errors (wip)

* fix: no-console eslint errors (wip)

* fix: react-refresh/only-export-components eslint errors (wip)

* fix: react-refresh/only-export-components eslint errors (wip)

* fix: react-refresh/only-export-components eslint errors (wip)

* fix: react-refresh/only-export-components eslint errors (wip)

* fix: build

* fix: pwa manifest

* refactor: pwa manifest

* refactor: simplify PORT configuration

* refactor: rename simple JS files

* test: cover playlistUtils

* fix: react-image-lightbox

* feat(ui): add sourcemaps to help debug issues
2024-09-28 11:54:36 -04:00

77 lines
1.8 KiB
JavaScript

import React, { useCallback, useMemo } from 'react'
import { Link } from '@material-ui/core'
import { makeStyles } from '@material-ui/core/styles'
import PropTypes from 'prop-types'
const useStyles = makeStyles(
(theme) => ({
link: {
textDecoration: 'none',
color: theme.palette.primary.main,
},
}),
{ name: 'RaLink' },
)
const Linkify = ({ text, ...rest }) => {
const classes = useStyles()
const linkify = useCallback((text) => {
const urlRegex =
/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#/%?=~_|!:,.;]*[-A-Z0-9+&@#/%=~_|])/gi
return [...text.matchAll(urlRegex)]
}, [])
const parse = useCallback(() => {
const matches = linkify(text)
if (matches.length === 0) return text
const elements = []
let lastIndex = 0
matches.forEach((match, index) => {
// Push text located before matched string
if (match.index > lastIndex) {
elements.push(text.substring(lastIndex, match.index))
}
const href = match[0]
// Push Link component
elements.push(
<Link
{...rest}
target="_blank"
className={classes.link}
rel="noopener noreferrer"
key={index}
href={href}
>
{href}
</Link>,
)
lastIndex = match.index + href.length
})
// Push remaining text
if (text.length > lastIndex) {
elements.push(
<span
key={'last-span-key'}
dangerouslySetInnerHTML={{ __html: text.substring(lastIndex) }}
/>,
)
}
return elements.length === 1 ? elements[0] : elements
}, [linkify, text, rest, classes.link])
const parsedText = useMemo(() => parse(), [parse])
return <>{parsedText}</>
}
Linkify.propTypes = {
text: PropTypes.string,
}
export default React.memo(Linkify)