mirror of
https://github.com/nasa/openmct.git
synced 2025-06-25 10:44:21 +00:00
Compare commits
8 Commits
vue-hack
...
tree-pagin
Author | SHA1 | Date | |
---|---|---|---|
9015028f81 | |||
38d78990da | |||
e0d2c85740 | |||
29ab9bdf02 | |||
0b433ccb50 | |||
e2ecd106f6 | |||
7debe89f8b | |||
a396f50acd |
@ -29,6 +29,7 @@ define([
|
||||
"./res/templates/search.html",
|
||||
"./res/templates/search-menu.html",
|
||||
"raw-loader!./src/services/GenericSearchWorker.js",
|
||||
"raw-loader!./src/services/BareBonesSearchWorker.js",
|
||||
'legacyRegistry'
|
||||
], function (
|
||||
SearchController,
|
||||
@ -39,6 +40,7 @@ define([
|
||||
searchTemplate,
|
||||
searchMenuTemplate,
|
||||
searchWorkerText,
|
||||
BareBonesSearchWorkerText,
|
||||
legacyRegistry
|
||||
) {
|
||||
|
||||
@ -115,6 +117,10 @@ define([
|
||||
}
|
||||
],
|
||||
"workers": [
|
||||
{
|
||||
"key": "bareBonesSearchWorker",
|
||||
"scriptText": BareBonesSearchWorkerText
|
||||
},
|
||||
{
|
||||
"key": "genericSearchWorker",
|
||||
"scriptText": searchWorkerText
|
||||
|
80
platform/search/src/services/BareBonesSearchWorker.js
Normal file
80
platform/search/src/services/BareBonesSearchWorker.js
Normal file
@ -0,0 +1,80 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2018, 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.
|
||||
*****************************************************************************/
|
||||
|
||||
/*global self*/
|
||||
|
||||
/**
|
||||
* Module defining GenericSearchWorker. Created by shale on 07/21/2015.
|
||||
*/
|
||||
(function () {
|
||||
|
||||
// An array of objects composed of domain object IDs and models
|
||||
// {id: domainObject's ID, model: domainObject's model}
|
||||
var indexedItems = [];
|
||||
|
||||
function indexItem(id, model) {
|
||||
indexedItems.push({
|
||||
id: id,
|
||||
name: model.name.toLowerCase()
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets search results from the indexedItems based on provided search
|
||||
* input. Returns matching results from indexedItems
|
||||
*
|
||||
* @param data An object which contains:
|
||||
* * input: The original string which we are searching with
|
||||
* * maxResults: The maximum number of search results desired
|
||||
* * queryId: an id identifying this query, will be returned.
|
||||
*/
|
||||
function search(data) {
|
||||
// This results dictionary will have domain object ID keys which
|
||||
// point to the value the domain object's score.
|
||||
var results,
|
||||
input = data.input.trim().toLowerCase(),
|
||||
message = {
|
||||
request: 'search',
|
||||
results: {},
|
||||
total: 0,
|
||||
queryId: data.queryId
|
||||
};
|
||||
|
||||
results = indexedItems.filter((indexedItem) => {
|
||||
return indexedItem.name.includes(input);
|
||||
});
|
||||
|
||||
message.total = results.length;
|
||||
message.results = results
|
||||
.slice(0, data.maxResults);
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
self.onmessage = function (event) {
|
||||
if (event.data.request === 'index') {
|
||||
indexItem(event.data.id, event.data.model);
|
||||
} else if (event.data.request === 'search') {
|
||||
self.postMessage(search(event.data));
|
||||
}
|
||||
};
|
||||
}());
|
@ -58,6 +58,8 @@ define([
|
||||
|
||||
this.pendingQueries = {};
|
||||
|
||||
this.useBareBones = true;
|
||||
|
||||
this.worker = this.startWorker(workerService);
|
||||
this.indexOnMutation(topic);
|
||||
|
||||
@ -101,8 +103,14 @@ define([
|
||||
* @returns worker the created search worker.
|
||||
*/
|
||||
GenericSearchProvider.prototype.startWorker = function (workerService) {
|
||||
var worker = workerService.run('genericSearchWorker'),
|
||||
provider = this;
|
||||
var provider = this,
|
||||
worker;
|
||||
|
||||
if (this.useBareBones) {
|
||||
worker = workerService.run('bareBonesSearchWorker');
|
||||
} else {
|
||||
worker = workerService.run('genericSearchWorker');
|
||||
}
|
||||
|
||||
worker.addEventListener('message', function (messageEvent) {
|
||||
provider.onWorkerMessage(messageEvent);
|
||||
@ -242,18 +250,34 @@ define([
|
||||
return;
|
||||
}
|
||||
|
||||
var pendingQuery = this.pendingQueries[event.data.queryId],
|
||||
var pendingQuery,
|
||||
modelResults;
|
||||
|
||||
if (this.useBareBones) {
|
||||
pendingQuery = this.pendingQueries[event.data.queryId];
|
||||
modelResults = {
|
||||
total: event.data.total
|
||||
};
|
||||
|
||||
modelResults.hits = event.data.results.map(function (hit) {
|
||||
return {
|
||||
id: hit.item.id,
|
||||
model: hit.item.model,
|
||||
score: hit.matchCount
|
||||
modelResults.hits = event.data.results.map(function (hit) {
|
||||
return {
|
||||
id: hit.id
|
||||
};
|
||||
});
|
||||
} else {
|
||||
pendingQuery = this.pendingQueries[event.data.queryId];
|
||||
modelResults = {
|
||||
total: event.data.total
|
||||
};
|
||||
});
|
||||
|
||||
modelResults.hits = event.data.results.map(function (hit) {
|
||||
return {
|
||||
id: hit.item.id,
|
||||
model: hit.item.model,
|
||||
score: hit.matchCount
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
pendingQuery.resolve(modelResults);
|
||||
delete this.pendingQueries[event.data.queryId];
|
||||
|
@ -144,6 +144,8 @@
|
||||
message.results = results
|
||||
.slice(0, data.maxResults);
|
||||
|
||||
console.log(message);
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
|
@ -18,10 +18,47 @@
|
||||
<span class="c-tree__item__label">Loading...</span>
|
||||
</div>
|
||||
</li>
|
||||
<tree-item v-for="child in children"
|
||||
:key="child.id"
|
||||
:node="child">
|
||||
</tree-item>
|
||||
|
||||
<template v-if="children.length">
|
||||
|
||||
<template v-if="children.length > page_threshold">
|
||||
<li v-show="!showSearchComponent"
|
||||
@click="toggleSearchComponent"
|
||||
class="c-tree__item-h"
|
||||
style="font-size: 0.5em;">
|
||||
<div class="c-tree__item icon-magnify">
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li v-show="showSearchComponent"
|
||||
class="c-tree__item-h"
|
||||
style="font-size: 0.7em">
|
||||
<div class="c-tree__item">
|
||||
<a class="c-tree__item__label c-object-label">
|
||||
<search
|
||||
:value="searchValue"
|
||||
@input="searchChildren"
|
||||
@clear="searchChildren"
|
||||
style="min-width: 80%;">
|
||||
</search>
|
||||
<div style="padding: 2px; margin-left: 10%;"
|
||||
class="icon-x"
|
||||
@click="toggleSearchComponent">
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<div :style="style"
|
||||
@scroll="scrollPage"
|
||||
ref="scrollParent">
|
||||
<tree-item v-for="child in filteredAndPagedChildren"
|
||||
:key="child.id"
|
||||
:node="child">
|
||||
</tree-item>
|
||||
</div>
|
||||
</template>
|
||||
</ul>
|
||||
</li>
|
||||
</template>
|
||||
@ -29,6 +66,9 @@
|
||||
<script>
|
||||
import viewControl from '../components/viewControl.vue';
|
||||
import ObjectLabel from '../components/ObjectLabel.vue';
|
||||
import Search from '../components/search.vue';
|
||||
|
||||
const PAGE_THRESHOLD = 50;
|
||||
|
||||
export default {
|
||||
name: 'tree-item',
|
||||
@ -44,7 +84,13 @@
|
||||
loaded: false,
|
||||
isNavigated: this.navigateToPath === this.openmct.router.currentLocation.path,
|
||||
children: [],
|
||||
expanded: false
|
||||
expanded: false,
|
||||
page: 1,
|
||||
page_threshold: PAGE_THRESHOLD,
|
||||
searchValue: '',
|
||||
filteredChildren: [],
|
||||
scrollTop: 0,
|
||||
showSearchComponent: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -55,6 +101,44 @@
|
||||
}
|
||||
let parentKeyString = this.openmct.objects.makeKeyString(parent.identifier);
|
||||
return parentKeyString !== this.node.object.location;
|
||||
},
|
||||
filteredAndPagedChildren() {
|
||||
if (this.searchValue) {
|
||||
this.filteredChildren = this.children.filter((child) => {
|
||||
let searchLowCase = this.searchValue.toLowerCase(),
|
||||
nameLowerCase = child.object.name.toLowerCase();
|
||||
|
||||
return nameLowerCase.includes(searchLowCase);
|
||||
})
|
||||
} else {
|
||||
this.filteredChildren = this.children;
|
||||
}
|
||||
|
||||
if (this.filteredChildren.length > this.page_threshold) {
|
||||
let maxIndex = this.page * this.page_threshold,
|
||||
minIndex = maxIndex - this.page_threshold;
|
||||
|
||||
return this.filteredChildren.slice(minIndex, maxIndex);
|
||||
} else {
|
||||
return this.filteredChildren;
|
||||
}
|
||||
},
|
||||
lastPage() {
|
||||
return Math.floor(this.filteredChildren.length / this.page_threshold);
|
||||
},
|
||||
style() {
|
||||
let numChildren = this.filteredChildren.length;
|
||||
|
||||
if (!this.$refs.scrollParent || numChildren === 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if ((numChildren * 20) > this.$refs.scrollParent.offsetHeight) {
|
||||
return {
|
||||
"overflow-y": 'scroll',
|
||||
"max-height": (this.page_threshold * 10) + 'px'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@ -97,6 +181,11 @@
|
||||
this.composition.load().then(this.finishLoading);
|
||||
this.isLoading = true;
|
||||
}
|
||||
|
||||
if (!isExpanded) {
|
||||
this.page = 1;
|
||||
this.showSearchComponent = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@ -126,11 +215,53 @@
|
||||
} else if (oldPath === this.navigateToPath) {
|
||||
this.isNavigated = false;
|
||||
}
|
||||
},
|
||||
nextPage() {
|
||||
if (this.page < this.lastPage) {
|
||||
this.page += 1;
|
||||
}
|
||||
},
|
||||
previousPage() {
|
||||
if (this.page >= 1) {
|
||||
this.page -= 1;
|
||||
}
|
||||
},
|
||||
searchChildren(input) {
|
||||
this.searchValue = input;
|
||||
this.page = 1;
|
||||
},
|
||||
scrollPage(event) {
|
||||
let offsetHeight = event.target.offsetHeight,
|
||||
scrollTop = event.target.scrollTop,
|
||||
changePage = true;
|
||||
|
||||
window.clearTimeout(this.scrollLoading);
|
||||
|
||||
if (scrollTop > this.scrollTop && scrollTop > offsetHeight) {
|
||||
this.scrollLoading = window.setTimeout(() => {
|
||||
if (this.page < this.lastPage) {
|
||||
this.nextPage();
|
||||
event.target.scrollTop = 1;
|
||||
}
|
||||
}, 250);
|
||||
} else if (this.scrollTop <= this.scrollTop && scrollTop <= 0) {
|
||||
this.scrollLoading = window.setTimeout(() => {
|
||||
if (this.page > 1) {
|
||||
this.previousPage();
|
||||
event.target.scrollTop = offsetHeight - 1;
|
||||
}
|
||||
}, 250);
|
||||
}
|
||||
this.scrollTop = scrollTop;
|
||||
},
|
||||
toggleSearchComponent() {
|
||||
this.showSearchComponent = !this.showSearchComponent;
|
||||
}
|
||||
},
|
||||
components: {
|
||||
viewControl,
|
||||
ObjectLabel
|
||||
ObjectLabel,
|
||||
Search
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
Reference in New Issue
Block a user