mirror of
https://github.com/nasa/openmct.git
synced 2025-02-01 00:45:41 +00:00
[Notebook] Improve Search and [TextHighlight] Ability to highlight text (#3760)
This commit is contained in:
parent
f9bd31deee
commit
ac0e1d3161
@ -1,16 +1,38 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2021, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="c-notebook">
|
<div class="c-notebook">
|
||||||
<div class="c-notebook__head">
|
<div class="c-notebook__head">
|
||||||
<Search class="c-notebook__search"
|
<Search class="c-notebook__search"
|
||||||
:value="search"
|
:value="search"
|
||||||
@input="throttledSearchItem"
|
@input="search = $event"
|
||||||
@clear="throttledSearchItem"
|
@clear="resetSearch()"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<SearchResults v-if="search.length"
|
<SearchResults v-if="search.length"
|
||||||
ref="searchResults"
|
ref="searchResults"
|
||||||
:domain-object="internalDomainObject"
|
:domain-object="internalDomainObject"
|
||||||
:results="searchedEntries"
|
:results="searchResults"
|
||||||
@changeSectionPage="changeSelectedSection"
|
@changeSectionPage="changeSelectedSection"
|
||||||
@updateEntries="updateEntries"
|
@updateEntries="updateEntries"
|
||||||
/>
|
/>
|
||||||
@ -115,9 +137,13 @@ import { clearDefaultNotebook, getDefaultNotebook, setDefaultNotebook, setDefaul
|
|||||||
import { addNotebookEntry, createNewEmbed, getEntryPosById, getNotebookEntries, mutateObject } from '../utils/notebook-entries';
|
import { addNotebookEntry, createNewEmbed, getEntryPosById, getNotebookEntries, mutateObject } from '../utils/notebook-entries';
|
||||||
import objectUtils from 'objectUtils';
|
import objectUtils from 'objectUtils';
|
||||||
|
|
||||||
import { throttle } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
import objectLink from '../../../ui/mixins/object-link';
|
import objectLink from '../../../ui/mixins/object-link';
|
||||||
|
|
||||||
|
function objectCopy(obj) {
|
||||||
|
return JSON.parse(JSON.stringify(obj));
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
NotebookEntry,
|
NotebookEntry,
|
||||||
@ -134,6 +160,7 @@ export default {
|
|||||||
focusEntryId: null,
|
focusEntryId: null,
|
||||||
internalDomainObject: this.domainObject,
|
internalDomainObject: this.domainObject,
|
||||||
search: '',
|
search: '',
|
||||||
|
searchResults: [],
|
||||||
showTime: 0,
|
showTime: 0,
|
||||||
showNav: false,
|
showNav: false,
|
||||||
sidebarCoversEntries: false
|
sidebarCoversEntries: false
|
||||||
@ -156,9 +183,6 @@ export default {
|
|||||||
pages() {
|
pages() {
|
||||||
return this.getPages() || [];
|
return this.getPages() || [];
|
||||||
},
|
},
|
||||||
searchedEntries() {
|
|
||||||
return this.getSearchResults();
|
|
||||||
},
|
|
||||||
sections() {
|
sections() {
|
||||||
return this.internalDomainObject.configuration.sections || [];
|
return this.internalDomainObject.configuration.sections || [];
|
||||||
},
|
},
|
||||||
@ -178,8 +202,13 @@ export default {
|
|||||||
return this.sections.find(section => section.isSelected);
|
return this.sections.find(section => section.isSelected);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
search() {
|
||||||
|
this.getSearchResults();
|
||||||
|
}
|
||||||
|
},
|
||||||
beforeMount() {
|
beforeMount() {
|
||||||
this.throttledSearchItem = throttle(this.searchItem, 500);
|
this.getSearchResults = debounce(this.getSearchResults, 500);
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.unlisten = this.openmct.objects.observe(this.internalDomainObject, '*', this.updateInternalDomainObject);
|
this.unlisten = this.openmct.objects.observe(this.internalDomainObject, '*', this.updateInternalDomainObject);
|
||||||
@ -228,7 +257,7 @@ export default {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.sectionsChanged({ sections });
|
this.sectionsChanged({ sections });
|
||||||
this.throttledSearchItem('');
|
this.resetSearch();
|
||||||
},
|
},
|
||||||
createNotebookStorageObject() {
|
createNotebookStorageObject() {
|
||||||
const notebookMeta = {
|
const notebookMeta = {
|
||||||
@ -377,25 +406,79 @@ export default {
|
|||||||
const output = [];
|
const output = [];
|
||||||
const entries = this.internalDomainObject.configuration.entries;
|
const entries = this.internalDomainObject.configuration.entries;
|
||||||
const sectionKeys = Object.keys(entries);
|
const sectionKeys = Object.keys(entries);
|
||||||
|
const searchTextLower = this.search.toLowerCase();
|
||||||
|
const originalSearchText = this.search;
|
||||||
|
let sectionTrackPageHit;
|
||||||
|
let pageTrackEntryHit;
|
||||||
|
let sectionTrackEntryHit;
|
||||||
|
|
||||||
sectionKeys.forEach(sectionKey => {
|
sectionKeys.forEach(sectionKey => {
|
||||||
const pages = entries[sectionKey];
|
const pages = entries[sectionKey];
|
||||||
const pageKeys = Object.keys(pages);
|
const pageKeys = Object.keys(pages);
|
||||||
|
const section = this.getSection(sectionKey);
|
||||||
|
let resultMetadata = {
|
||||||
|
originalSearchText,
|
||||||
|
sectionHit: section.name && section.name.toLowerCase().includes(searchTextLower)
|
||||||
|
};
|
||||||
|
sectionTrackPageHit = false;
|
||||||
|
sectionTrackEntryHit = false;
|
||||||
|
|
||||||
pageKeys.forEach(pageKey => {
|
pageKeys.forEach(pageKey => {
|
||||||
const pageEntries = entries[sectionKey][pageKey];
|
const pageEntries = entries[sectionKey][pageKey];
|
||||||
|
const page = this.getPage(section, pageKey);
|
||||||
|
resultMetadata.pageHit = page.name && page.name.toLowerCase().includes(searchTextLower);
|
||||||
|
pageTrackEntryHit = false;
|
||||||
|
|
||||||
|
if (resultMetadata.pageHit) {
|
||||||
|
sectionTrackPageHit = true;
|
||||||
|
}
|
||||||
|
|
||||||
pageEntries.forEach(entry => {
|
pageEntries.forEach(entry => {
|
||||||
if (entry.text && entry.text.toLowerCase().includes(this.search.toLowerCase())) {
|
const entryHit = entry.text && entry.text.toLowerCase().includes(searchTextLower);
|
||||||
const section = this.getSection(sectionKey);
|
|
||||||
output.push({
|
// any entry hit goes in, it's the most unique of the hits
|
||||||
|
if (entryHit) {
|
||||||
|
resultMetadata.entryHit = entryHit;
|
||||||
|
pageTrackEntryHit = true;
|
||||||
|
sectionTrackEntryHit = true;
|
||||||
|
|
||||||
|
output.push(objectCopy({
|
||||||
|
metadata: resultMetadata,
|
||||||
section,
|
section,
|
||||||
page: this.getPage(section, pageKey),
|
page,
|
||||||
entry
|
entry
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// all entries checked, now in pages,
|
||||||
|
// if page hit, but not in results, need to add
|
||||||
|
if (resultMetadata.pageHit && !pageTrackEntryHit) {
|
||||||
|
resultMetadata.entryHit = false;
|
||||||
|
|
||||||
|
output.push(objectCopy({
|
||||||
|
metadata: resultMetadata,
|
||||||
|
section,
|
||||||
|
page
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
// all pages checked, now in sections,
|
||||||
|
// if section hit, but not in results, need to add and default page
|
||||||
|
if (resultMetadata.sectionHit && !sectionTrackPageHit && !sectionTrackEntryHit) {
|
||||||
|
resultMetadata.entryHit = false;
|
||||||
|
resultMetadata.pageHit = false;
|
||||||
|
|
||||||
|
output.push(objectCopy({
|
||||||
|
metadata: resultMetadata,
|
||||||
|
section,
|
||||||
|
page: this.getPage(section, pageKeys[0])
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return output;
|
this.searchResults = output;
|
||||||
},
|
},
|
||||||
getPages() {
|
getPages() {
|
||||||
const selectedSection = this.getSelectedSection();
|
const selectedSection = this.getSelectedSection();
|
||||||
@ -456,12 +539,11 @@ export default {
|
|||||||
this.sectionsChanged({ sections });
|
this.sectionsChanged({ sections });
|
||||||
},
|
},
|
||||||
newEntry(embed = null) {
|
newEntry(embed = null) {
|
||||||
this.search = '';
|
this.resetSearch();
|
||||||
const notebookStorage = this.createNotebookStorageObject();
|
const notebookStorage = this.createNotebookStorageObject();
|
||||||
this.updateDefaultNotebook(notebookStorage);
|
this.updateDefaultNotebook(notebookStorage);
|
||||||
const id = addNotebookEntry(this.openmct, this.internalDomainObject, notebookStorage, embed);
|
const id = addNotebookEntry(this.openmct, this.internalDomainObject, notebookStorage, embed);
|
||||||
this.focusEntryId = id;
|
this.focusEntryId = id;
|
||||||
this.search = '';
|
|
||||||
},
|
},
|
||||||
orientationChange() {
|
orientationChange() {
|
||||||
this.formatSidebar();
|
this.formatSidebar();
|
||||||
@ -491,8 +573,9 @@ export default {
|
|||||||
|
|
||||||
this.openmct.status.delete(domainObject.identifier);
|
this.openmct.status.delete(domainObject.identifier);
|
||||||
},
|
},
|
||||||
searchItem(input) {
|
resetSearch() {
|
||||||
this.search = input;
|
this.search = '';
|
||||||
|
this.searchResults = [];
|
||||||
},
|
},
|
||||||
toggleNav() {
|
toggleNav() {
|
||||||
this.showNav = !this.showNav;
|
this.showNav = !this.showNav;
|
||||||
|
@ -1,3 +1,25 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2021, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="c-notebook__entry c-ne has-local-controls"
|
<div class="c-notebook__entry c-ne has-local-controls"
|
||||||
@dragover="changeCursor"
|
@dragover="changeCursor"
|
||||||
@ -10,17 +32,32 @@
|
|||||||
<span>{{ createdOnTime }}</span>
|
<span>{{ createdOnTime }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="c-ne__content">
|
<div class="c-ne__content">
|
||||||
<div :id="entry.id"
|
<template v-if="readOnly && result">
|
||||||
class="c-ne__text"
|
<div
|
||||||
tabindex="0"
|
:id="entry.id"
|
||||||
:class="{ 'c-ne__input' : !readOnly }"
|
class="c-ne__text highlight"
|
||||||
:contenteditable="!readOnly"
|
tabindex="0"
|
||||||
@blur="updateEntryValue($event)"
|
>
|
||||||
@keydown.enter.exact.prevent
|
<TextHighlight
|
||||||
@keyup.enter.exact.prevent="forceBlur($event)"
|
:text="entryText"
|
||||||
v-text="entry.text"
|
:highlight="highlightText"
|
||||||
>
|
:highlight-class="'search-highlight'"
|
||||||
</div>
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<div
|
||||||
|
:id="entry.id"
|
||||||
|
class="c-ne__text c-ne__input"
|
||||||
|
tabindex="0"
|
||||||
|
contenteditable
|
||||||
|
@blur="updateEntryValue($event)"
|
||||||
|
@keydown.enter.exact.prevent
|
||||||
|
@keyup.enter.exact.prevent="forceBlur($event)"
|
||||||
|
v-text="entry.text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
<div class="c-snapshots c-ne__embeds">
|
<div class="c-snapshots c-ne__embeds">
|
||||||
<NotebookEmbed v-for="embed in entry.embeds"
|
<NotebookEmbed v-for="embed in entry.embeds"
|
||||||
:key="embed.id"
|
:key="embed.id"
|
||||||
@ -45,14 +82,18 @@
|
|||||||
<div v-if="readOnly"
|
<div v-if="readOnly"
|
||||||
class="c-ne__section-and-page"
|
class="c-ne__section-and-page"
|
||||||
>
|
>
|
||||||
<a class="c-click-link"
|
<a
|
||||||
@click="navigateToSection()"
|
class="c-click-link"
|
||||||
|
:class="{ 'search-highlight': result.metadata.sectionHit }"
|
||||||
|
@click="navigateToSection()"
|
||||||
>
|
>
|
||||||
{{ result.section.name }}
|
{{ result.section.name }}
|
||||||
</a>
|
</a>
|
||||||
<span class="icon-arrow-right"></span>
|
<span class="icon-arrow-right"></span>
|
||||||
<a class="c-click-link"
|
<a
|
||||||
@click="navigateToPage()"
|
class="c-click-link"
|
||||||
|
:class="{ 'search-highlight': result.metadata.pageHit }"
|
||||||
|
@click="navigateToPage()"
|
||||||
>
|
>
|
||||||
{{ result.page.name }}
|
{{ result.page.name }}
|
||||||
</a>
|
</a>
|
||||||
@ -64,10 +105,12 @@
|
|||||||
import NotebookEmbed from './NotebookEmbed.vue';
|
import NotebookEmbed from './NotebookEmbed.vue';
|
||||||
import { createNewEmbed } from '../utils/notebook-entries';
|
import { createNewEmbed } from '../utils/notebook-entries';
|
||||||
import Moment from 'moment';
|
import Moment from 'moment';
|
||||||
|
import TextHighlight from '../../../utils/textHighlight/TextHighlight.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
NotebookEmbed
|
NotebookEmbed,
|
||||||
|
TextHighlight
|
||||||
},
|
},
|
||||||
inject: ['openmct', 'snapshotContainer'],
|
inject: ['openmct', 'snapshotContainer'],
|
||||||
props: {
|
props: {
|
||||||
@ -114,6 +157,24 @@ export default {
|
|||||||
},
|
},
|
||||||
createdOnTime() {
|
createdOnTime() {
|
||||||
return this.formatTime(this.entry.createdOn, 'HH:mm:ss');
|
return this.formatTime(this.entry.createdOn, 'HH:mm:ss');
|
||||||
|
},
|
||||||
|
entryText() {
|
||||||
|
let text = this.entry.text;
|
||||||
|
|
||||||
|
if (!this.result.metadata.entryHit) {
|
||||||
|
text = `[ no result for '${this.result.metadata.originalSearchText}' in entry ]`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return text;
|
||||||
|
},
|
||||||
|
highlightText() {
|
||||||
|
let text = '';
|
||||||
|
|
||||||
|
if (this.result.metadata.entryHit) {
|
||||||
|
text = this.result.metadata.originalSearchText;
|
||||||
|
}
|
||||||
|
|
||||||
|
return text;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
@ -1,3 +1,25 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2021, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="c-notebook__search-results">
|
<div class="c-notebook__search-results">
|
||||||
<div class="c-notebook__search-results__header">Search Results</div>
|
<div class="c-notebook__search-results__header">Search Results</div>
|
||||||
|
@ -21,6 +21,12 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
/*********************************************** NOTEBOOK */
|
/*********************************************** NOTEBOOK */
|
||||||
|
@mixin searchHighlight {
|
||||||
|
background: rgba($colorBtnSelectedBg, 0.4);
|
||||||
|
color: $colorBtnSelectedFg;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
.c-notebook {
|
.c-notebook {
|
||||||
$headerFontSize: 1.3em;
|
$headerFontSize: 1.3em;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -274,7 +280,14 @@
|
|||||||
|
|
||||||
&__text {
|
&__text {
|
||||||
min-height: 22px; // Needed in Firefox when field is blank
|
min-height: 22px; // Needed in Firefox when field is blank
|
||||||
white-space: pre-wrap;
|
|
||||||
|
&:not(.highlight) {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-highlight {
|
||||||
|
@include searchHighlight();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__input {
|
&__input {
|
||||||
@ -305,6 +318,10 @@
|
|||||||
align-self: flex-start;
|
align-self: flex-start;
|
||||||
padding: $interiorMargin;
|
padding: $interiorMargin;
|
||||||
|
|
||||||
|
.search-highlight {
|
||||||
|
@include searchHighlight();
|
||||||
|
}
|
||||||
|
|
||||||
> * + * {
|
> * + * {
|
||||||
margin-left: $interiorMargin;
|
margin-left: $interiorMargin;
|
||||||
}
|
}
|
||||||
|
123
src/utils/textHighlight/TextHighlight.vue
Normal file
123
src/utils/textHighlight/TextHighlight.vue
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2021, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
<template>
|
||||||
|
|
||||||
|
<span>
|
||||||
|
<span
|
||||||
|
v-for="segment in segments"
|
||||||
|
:key="segment.id"
|
||||||
|
:style="getStyles(segment)"
|
||||||
|
:class="{ [highlightClass] : segment.type === 'highlight' }"
|
||||||
|
>
|
||||||
|
{{ segment.text }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import uuid from 'uuid';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
text: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
highlight: {
|
||||||
|
type: String,
|
||||||
|
default() {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
highlightClass: {
|
||||||
|
type: String,
|
||||||
|
default() {
|
||||||
|
return 'highlight';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
segments: []
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
highlight() {
|
||||||
|
this.highlightText();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.highlightText();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
addHighlightSegment(segment) {
|
||||||
|
this.segments.push({
|
||||||
|
id: uuid(),
|
||||||
|
text: segment,
|
||||||
|
type: 'highlight',
|
||||||
|
spaceBefore: segment.startsWith(' '),
|
||||||
|
spaceAfter: segment.endsWith(' ')
|
||||||
|
});
|
||||||
|
},
|
||||||
|
addTextSegment(segment) {
|
||||||
|
this.segments.push({
|
||||||
|
id: uuid(),
|
||||||
|
text: segment,
|
||||||
|
type: 'text',
|
||||||
|
spaceBefore: segment.startsWith(' '),
|
||||||
|
spaceAfter: segment.endsWith(' ')
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getStyles(segment) {
|
||||||
|
let styles = {
|
||||||
|
display: 'inline-block'
|
||||||
|
};
|
||||||
|
|
||||||
|
if (segment.spaceBefore) {
|
||||||
|
styles.paddingLeft = '.33em';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (segment.spaceAfter) {
|
||||||
|
styles.paddingRight = '.33em';
|
||||||
|
}
|
||||||
|
|
||||||
|
return styles;
|
||||||
|
},
|
||||||
|
highlightText() {
|
||||||
|
this.segments = [];
|
||||||
|
let regex = new RegExp('(' + this.highlight + ')', 'gi');
|
||||||
|
let textSegments = this.text.split(regex);
|
||||||
|
|
||||||
|
for (let i = 0; i < textSegments.length; i++) {
|
||||||
|
if (textSegments[i].toLowerCase() === this.highlight.toLowerCase()) {
|
||||||
|
this.addHighlightSegment(textSegments[i]);
|
||||||
|
} else {
|
||||||
|
this.addTextSegment(textSegments[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
Loading…
x
Reference in New Issue
Block a user