diff options
Diffstat (limited to 'app/assets/javascripts/search_settings/components/search_settings.vue')
-rw-r--r-- | app/assets/javascripts/search_settings/components/search_settings.vue | 58 |
1 files changed, 46 insertions, 12 deletions
diff --git a/app/assets/javascripts/search_settings/components/search_settings.vue b/app/assets/javascripts/search_settings/components/search_settings.vue index 116967a62c8..3e23b8a3435 100644 --- a/app/assets/javascripts/search_settings/components/search_settings.vue +++ b/app/assets/javascripts/search_settings/components/search_settings.vue @@ -1,7 +1,13 @@ <script> import { GlSearchBoxByType } from '@gitlab/ui'; -import { uniq } from 'lodash'; -import { EXCLUDED_NODES, HIDE_CLASS, HIGHLIGHT_CLASS, TYPING_DELAY } from '../constants'; +import { uniq, escapeRegExp } from 'lodash'; +import { + EXCLUDED_NODES, + HIDE_CLASS, + HIGHLIGHT_CLASS, + NONE_PADDING_CLASS, + TYPING_DELAY, +} from '../constants'; const origExpansions = new Map(); @@ -37,9 +43,13 @@ const resetSections = ({ sectionSelector }) => { }; const clearHighlights = () => { - document - .querySelectorAll(`.${HIGHLIGHT_CLASS}`) - .forEach((element) => element.classList.remove(HIGHLIGHT_CLASS)); + document.querySelectorAll(`.${HIGHLIGHT_CLASS}`).forEach((element) => { + const { parentNode } = element; + const textNode = document.createTextNode(element.textContent); + parentNode.replaceChild(textNode, element); + + parentNode.normalize(); + }); }; const hideSectionsExcept = (sectionSelector, visibleSections) => { @@ -50,17 +60,41 @@ const hideSectionsExcept = (sectionSelector, visibleSections) => { }); }; -const highlightElements = (elements = []) => { - elements.forEach((element) => element.classList.add(HIGHLIGHT_CLASS)); +const transformMatchElement = (element, searchTerm) => { + const textStr = element.textContent; + const escapedSearchTerm = new RegExp(`(${escapeRegExp(searchTerm)})`, 'gi'); + + const textList = textStr.split(escapedSearchTerm); + const replaceFragment = document.createDocumentFragment(); + textList.forEach((text) => { + let addElement = document.createTextNode(text); + if (escapedSearchTerm.test(text)) { + addElement = document.createElement('mark'); + addElement.className = `${HIGHLIGHT_CLASS} ${NONE_PADDING_CLASS}`; + addElement.textContent = text; + escapedSearchTerm.lastIndex = 0; + } + replaceFragment.appendChild(addElement); + }); + + return replaceFragment; +}; + +const highlightElements = (elements = [], searchTerm) => { + elements.forEach((element) => { + const replaceFragment = transformMatchElement(element, searchTerm); + element.innerHTML = ''; + element.appendChild(replaceFragment); + }); }; -const displayResults = ({ sectionSelector, expandSection }, matches) => { +const displayResults = ({ sectionSelector, expandSection, searchTerm }, matches) => { const elements = matches.map((match) => match.parentElement); const sections = uniq(elements.map((element) => findSettingsSection(sectionSelector, element))); hideSectionsExcept(sectionSelector, sections); sections.forEach(expandSection); - highlightElements(elements); + highlightElements(elements, searchTerm); }; const clearResults = (params) => { @@ -116,21 +150,21 @@ export default { }, methods: { search(value) { + this.searchTerm = value; const displayOptions = { sectionSelector: this.sectionSelector, expandSection: this.expandSection, collapseSection: this.collapseSection, isExpanded: this.isExpandedFn, + searchTerm: this.searchTerm, }; - this.searchTerm = value; - clearResults(displayOptions); if (value.length) { saveExpansionState(document.querySelectorAll(this.sectionSelector), displayOptions); - displayResults(displayOptions, search(this.searchRoot, value)); + displayResults(displayOptions, search(this.searchRoot, this.searchTerm)); } else { restoreExpansionState(displayOptions); } |