chore: slightly enhance Highlight

This commit is contained in:
EnixCoda 2022-10-26 21:06:02 +08:00
parent 36d6421f6e
commit 7600ba43fd

View file

@ -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
}