UNPKG

react-native-hyperlinks

Version:

A simple and customizable library to display hyperlinks in React Native

126 lines 4.04 kB
import React from "react"; import { Linking, Text } from "react-native"; import linkifyIt from "linkify-it"; export default function Hyperlinks({ text, hyperlinkStyle, autoDetectMentions = true, autoDetectHastags = true, customHyperlinks = [], linkify = linkifyIt(), onLinkPress, onMentionPress, onHashtagPress, onCustomHyperlinkPress, style, ...textProps }) { const matches = linkify.match(text) ?? []; const detectedMentions = autoDetectMentions ? getMentions(text) : []; const hashtags = autoDetectHastags ? getHashtags(text) : []; if (matches.length === 0 && detectedMentions.length === 0 && hashtags.length === 0 && customHyperlinks.length === 0) { return (<Text style={style} {...textProps}> {text} </Text>); } const allMatches = []; function handleOnLinkPress(url) { try { if (onLinkPress) { onLinkPress(url); } else { Linking.openURL(url); } } catch { } } matches.forEach((match) => { allMatches.push({ start: match.index, end: match.lastIndex, onPress: () => match.raw.startsWith("@") ? onMentionPress?.(match.raw.replace("@", "")) : handleOnLinkPress(match.url), }); }); detectedMentions.forEach((mention) => { allMatches.push({ start: mention.start, end: mention.end, onPress: () => { onMentionPress?.(mention.username); }, }); }); hashtags.forEach((hashtag) => { allMatches.push({ start: hashtag.start, end: hashtag.end, onPress: () => { onHashtagPress?.(hashtag.tag); }, }); }); customHyperlinks.forEach((hyperlink) => { allMatches.push({ start: hyperlink.start, end: hyperlink.end, onPress: () => onCustomHyperlinkPress?.(hyperlink), }); }); const orderedMatches = allMatches.sort((a, b) => { return a.start - b.start; }); const chunks = []; orderedMatches.forEach((match, index) => { const isFirst = index === 0; if (isFirst) { if (match.start !== 0) { chunks.push({ label: text.substring(0, match.start), }); } } chunks.push({ label: text.substring(match.start, match.end), onPress: match.onPress, }); const hasNext = orderedMatches.length !== index + 1; if (hasNext) { chunks.push({ label: text.substring(match.end, orderedMatches[index + 1].start), }); } else { const hasMoreText = text.length > match.end; if (hasMoreText) { chunks.push({ label: text.substring(match.end, text.length), }); } } }); return (<Text style={style} {...textProps}> {chunks.map((chunk, index) => (<Text onPress={chunk.onPress} style={[style, chunk.onPress ? hyperlinkStyle : {}]} key={index}> {chunk.label} </Text>))} </Text>); } function getMentions(text) { const mentionedUsers = []; const regex = /\B@[a-zA-Z0-9_]+/gi; let match; while ((match = regex.exec(text)) != null) { mentionedUsers.push({ username: match[0].replace("@", ""), start: match.index, end: match[0].length + match.index, }); } return mentionedUsers; } function getHashtags(text) { const hashtags = []; const regex = /\B#[a-zA-Z0-9_]+/gi; let match; while ((match = regex.exec(text)) != null) { hashtags.push({ tag: match[0].replace("#", ""), start: match.index, end: match[0].length + match.index, }); } return hashtags; } //# sourceMappingURL=Hyperlinks.js.map