UNPKG

@eccenca/gui-elements

Version:

GUI elements based on other libraries, usable in React application, written in Typescript.

79 lines (70 loc) 3 kB
import { Content, Parent, Root, Text } from "hast"; import { Transformer } from "unified"; import { Node } from "unist"; import { highlighterUtils } from "../../components/Typography/Highlighter"; /** * Creates a react-markdown reHype plugin that marks text based on a multi-word search query. */ const highlightSearchWordsPluginFactory = (searchQuery: string | undefined) => { const searchStringParts = searchQuery ? highlighterUtils.extractSearchWords(searchQuery) : []; const multiWordRegex = highlighterUtils.createMultiWordRegex(searchStringParts); const createTextNode = (text: string): Text => ({ type: "text", value: text }); // Highlight a text node by returning an array of text and mark elements const highlightTextNode = (textNode: Text): Content[] => { if (searchStringParts.length === 0) { return [textNode]; } const result: Content[] = []; const text = textNode.value; let offset = 0; // loop through matches and add unmatched and matched parts to result array let matchArray = multiWordRegex.exec(text); while (matchArray !== null) { result.push(createTextNode(text.slice(offset, matchArray.index))); result.push({ type: "element", tagName: "mark", properties: {}, children: [createTextNode(matchArray[0])], }); offset = multiWordRegex.lastIndex; matchArray = multiWordRegex.exec(text); } // Add remaining unmatched string result.push(createTextNode(text.slice(offset))); return result; }; // Upper level highlight function const highlightRootNode = (node: Root): Root => highlightParentNode(node); // Highlight function to be called on Parent nodes const highlightParentNode = <T extends Parent>(parentNode: T): T => { const newChildren: Content[] = []; parentNode.children.forEach((child) => { const highlightedChild = highlightTextNodes(child); if (Array.isArray(highlightedChild)) { newChildren.push(...(highlightedChild as Content[])); } else { newChildren.push(highlightedChild as Content); } }); return { ...parentNode, children: newChildren } as T; }; // Highlight function to be called on generic inner nodes const highlightTextNodes = (node: Node): Node | Node[] => { if (node.type === "text") { return highlightTextNode(node as Text); } else { if ((node as Parent).children) { return highlightParentNode(node as Parent); } else { return node; } } }; return function highlightSearchWords(): Transformer<Root, Root> { return (input: Root) => highlightRootNode(input); }; }; export const markdownUtils = { highlightSearchWordsPluginFactory, };