github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/ui/src/util/highlightedText.tsx (about) 1 // Copyright 2018 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 import React from "react"; 12 13 export default function getHighlightedText(text: string, highlight: string, isOriginalText?: boolean) { 14 if (!highlight || highlight.length === 0) { 15 return text; 16 } 17 highlight = highlight.replace(/[°§%()\[\]{}\\?´`'#|;:+-]+/g, "highlightNotDefined"); 18 const search = highlight.split(" ").map(val => { 19 if (val.length > 0) { 20 return val.toLowerCase(); 21 } 22 return "highlightNotDefined"; 23 }).join("|"); 24 const parts = isOriginalText ? text.split(new RegExp(`(${search})`, "gi")) : rebaseText(text, highlight).split(new RegExp(`(${search})`, "gi")); 25 return parts.map((part, i) => { 26 if (search.includes(part.toLowerCase())) { 27 return ( 28 <span key={i} className="_text-bold"> 29 {`${part}`} 30 </span> 31 ); 32 } else { 33 return `${part}`; 34 } 35 }); 36 } 37 38 function rebaseText(text: string, highlight: string) { 39 const search = highlight.split(" "); 40 const maxLength = 425; 41 const defaultCropLength = 150; 42 const defaultBeforeAfterCrop = 20; 43 const isTextIncludesInTheRange425 = isStringIncludesArrayElement(search, text.slice(0, maxLength)); 44 if (!isTextIncludesInTheRange425) { 45 let newText = text.slice(0, defaultCropLength) + "..."; 46 let currentPosition = defaultCropLength; 47 search.forEach(value => { 48 const wordPosition = getWordAt(value, text); 49 const isPositionMoreCurrent = wordPosition - defaultBeforeAfterCrop > currentPosition; 50 const isPositionMoreCurrentCrop = isPositionMoreCurrent ? wordPosition - defaultBeforeAfterCrop : wordPosition; 51 currentPosition = currentPosition + value.length + (isPositionMoreCurrent ? (defaultBeforeAfterCrop * 2) : defaultBeforeAfterCrop); 52 newText = `${newText} ${text.slice(isPositionMoreCurrentCrop, wordPosition + (defaultBeforeAfterCrop + value.length))}...`; 53 }); 54 return newText.length < maxLength ? `${newText} ${text.slice(currentPosition, maxLength)}` : newText.slice(0, maxLength); 55 } 56 return text.length > maxLength ? `${text.slice(0, maxLength)}...` : text; 57 } 58 59 export function isStringIncludesArrayElement(arr: string[], text: string) { 60 let includes = false; 61 arr.forEach(val => { 62 if (text.toLowerCase().includes(val.toLowerCase())) { 63 includes = true; 64 } 65 }); 66 return includes; 67 } 68 69 export function getWordAt(word: string, text: string) { 70 const regex = new RegExp("\\b" + word.toLowerCase() + "\\b"); 71 return text.toLowerCase().search(regex); 72 }