import { memoizeOne } from "@investtal/toolkit" import { createElement } from "react" import { findAll } from "./highlight-words-core" interface HighlighterProps { activeClassName?: string activeIndex?: number activeStyle?: React.CSSProperties autoEscape?: boolean caseSensitive?: boolean className?: string findChunks?: (options: any) => Array<{ start: number; end: number }> highlightClassName?: string | Record highlightStyle?: React.CSSProperties highlightTag?: React.ComponentType | string sanitize?: (text: string) => string searchWords: Array textToHighlight: string unhighlightTag?: React.ComponentType | string unhighlightClassName?: string unhighlightStyle?: React.CSSProperties [key: string]: any } export default function Highlighter({ activeClassName = "", activeIndex = -1, activeStyle, autoEscape, caseSensitive = false, className, findChunks, highlightClassName = "", highlightStyle = {}, highlightTag = "mark", sanitize, searchWords, textToHighlight, unhighlightTag = "span", unhighlightClassName = "", unhighlightStyle, ...rest }: HighlighterProps) { const chunks = findAll({ autoEscape, caseSensitive, findChunks, sanitize, searchWords, textToHighlight, }) const HighlightTag = highlightTag let highlightIndex = -1 let highlightClassNames = "" let highlightStyles: React.CSSProperties = {} const lowercaseProps = (object: Record): Record => { const mapped: Record = {} for (const key in object) { mapped[key.toLowerCase()] = object[key] } return mapped } const memoizedLowercaseProps = memoizeOne(lowercaseProps) return createElement( "span", { className, ...rest, }, chunks.map((chunk, index) => { const text = textToHighlight.slice(chunk.start, chunk.end) if (chunk.highlight) { highlightIndex++ let highlightClass: string if (typeof highlightClassName === "object") { const classNameMap = !caseSensitive ? memoizedLowercaseProps(highlightClassName) : highlightClassName highlightClass = classNameMap[!caseSensitive ? text.toLowerCase() : text] } else { highlightClass = highlightClassName } const isActive = highlightIndex === +activeIndex highlightClassNames = `${highlightClass} ${isActive ? activeClassName : ""}` highlightStyles = isActive === true && activeStyle != null ? { ...highlightStyle, ...activeStyle } : highlightStyle const props: any = { className: highlightClassNames, key: index, style: highlightStyles, } // Don't attach arbitrary props to DOM elements; this triggers React DEV warnings (https://fb.me/react-unknown-prop) // Only pass through the highlightIndex attribute for custom components. if (typeof HighlightTag !== "string") { props.highlightIndex = highlightIndex } return createElement(HighlightTag, props, text) } return createElement( unhighlightTag, { className: unhighlightClassName, key: index, style: unhighlightStyle, }, text, ) }), ) }