diff --git a/ui/src/album/AlbumDetails.js b/ui/src/album/AlbumDetails.js index 165522117..c22805879 100644 --- a/ui/src/album/AlbumDetails.js +++ b/ui/src/album/AlbumDetails.js @@ -33,6 +33,7 @@ import { import config from '../config' import { intersperse } from '../utils' import AlbumExternalLinks from './AlbumExternalLinks' +import AnchorMe from '../common/AnchorMe' const useStyles = makeStyles( (theme) => ({ @@ -116,7 +117,7 @@ const AlbumComment = ({ record }) => { const formatted = useMemo(() => { return lines.map((line, idx) => ( - +
)) diff --git a/ui/src/common/AnchorMe.js b/ui/src/common/AnchorMe.js new file mode 100644 index 000000000..dadd06ef3 --- /dev/null +++ b/ui/src/common/AnchorMe.js @@ -0,0 +1,73 @@ +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 AnchorMe = ({ 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( + + {href} + + ) + + lastIndex = match.index + href.length + }) + + // Push remaining text + if (text.length > lastIndex) { + elements.push( + + ) + } + + return elements.length === 1 ? elements[0] : elements + }, [linkify, text, rest, classes.link]) + + const parsedText = useMemo(() => parse(), [parse]) + + return <>{parsedText} +} + +AnchorMe.propTypes = { + text: PropTypes.string, +} + +export default React.memo(AnchorMe)