From 7600ba43fda1a5735831ad7c252546fa8a8b86a4 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Wed, 26 Oct 2022 21:06:02 +0800 Subject: [PATCH] chore: slightly enhance Highlight --- src/components/Highlight.tsx | 48 +++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/src/components/Highlight.tsx b/src/components/Highlight.tsx index b43317d..4d73e58 100644 --- a/src/components/Highlight.tsx +++ b/src/components/Highlight.tsx @@ -1,41 +1,45 @@ import * as React from 'react' -export const Highlight = React.memo(function Highlight(props: { text: string; match?: RegExp }) { - const { text, match } = props - const $match = React.useMemo(() => { - if (match instanceof RegExp) { - if (match.flags.includes('g')) return match - return new RegExp(match.source, 'g' + match.flags) - } - return null - }, [match]) +export const Highlight = function Highlight({ text, match }: { text: string; match?: RegExp }) { + const $match = React.useMemo( + () => + match instanceof RegExp + ? match.flags.includes('g') + ? match + : new RegExp(match.source, 'g' + match.flags) + : null, + [match], + ) - const chunks = useChunks(text, $match) + const chunks = React.useMemo(() => getChunks(text, $match), [text, $match]) return <>{chunks.map(([type, text], key) => React.createElement(type, { key }, text))} -}) +} -function useChunks(text: string, $match: RegExp | null) { - const contents: [string, string][] = [] - if ($match === null) return [['span', text]] +type ElementMeta = [tag: string, content: string] +function getChunks(text: string, match: RegExp | null): ElementMeta[] { + const contents: ElementMeta[] = [] - const matchedPieces = Array.from(text.matchAll($match)).map( + if (match === null) { + contents.push(['span', text]) + return contents + } + + const matchedPieces = Array.from(text.matchAll(match)).map( ([text, highlightText = text]) => highlightText, ) - const preservedPieces = text.split($match) + const preservedPieces = text.split(match) const push = (type: string, text: string) => { - if (!text) return const last = contents[contents.length - 1] if (last && last[0] === type) last[1] += text else contents.push([type, text]) } - let i = 0 - while (i < Math.max(matchedPieces.length, preservedPieces.length)) { - push('span', preservedPieces[i]) - push('mark', matchedPieces[i]) - ++i + const max = Math.max(matchedPieces.length, preservedPieces.length) + for (let i = 0; i < max; ++i) { + preservedPieces[i] && push('span', preservedPieces[i]) + matchedPieces[i] && push('mark', matchedPieces[i]) } return contents }