mirror of
https://github.com/nasa/openmct.git
synced 2025-02-21 09:52:04 +00:00
[MCT Tree] Enhance to be used as selection tree as well (#4734)
* removed selector tree, using mct-tree for selctor now, updated style view to use new forms api, update mct-tree to be a selector if need be * added some extra calculations for height when the tree is being used as a selector in forms Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
This commit is contained in:
parent
91e909bb4a
commit
45373c56f7
@ -21,19 +21,19 @@
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<SelectorDialogTree :ignore-type-check="true"
|
||||
:css-class="`form-locator c-form-control--locator`"
|
||||
:parent="model.parent"
|
||||
@treeItemSelected="handleItemSelection"
|
||||
<mct-tree
|
||||
:is-selector-tree="true"
|
||||
:initial-selection="model.parent"
|
||||
@tree-item-selection="handleItemSelection"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SelectorDialogTree from '@/ui/components/SelectorDialogTree.vue';
|
||||
import MctTree from '@/ui/layout/mct-tree.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
SelectorDialogTree
|
||||
MctTree
|
||||
},
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
@ -43,10 +43,10 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleItemSelection({ parentObjectPath }) {
|
||||
handleItemSelection(item) {
|
||||
const data = {
|
||||
model: this.model,
|
||||
value: parentObjectPath
|
||||
value: item.objectPath
|
||||
};
|
||||
|
||||
this.$emit('onChange', data);
|
||||
|
@ -148,10 +148,8 @@ import FontStyleEditor from '@/ui/inspector/styles/FontStyleEditor.vue';
|
||||
import StyleEditor from "./StyleEditor.vue";
|
||||
import PreviewAction from "@/ui/preview/PreviewAction.js";
|
||||
import { getApplicableStylesForItem, getConsolidatedStyleValues, getConditionSetIdentifierForItem } from "@/plugins/condition/utils/styleUtils";
|
||||
import SelectorDialogTree from '@/ui/components/SelectorDialogTree.vue';
|
||||
import ConditionError from "@/plugins/condition/components/ConditionError.vue";
|
||||
import ConditionDescription from "@/plugins/condition/components/ConditionDescription.vue";
|
||||
import Vue from 'vue';
|
||||
|
||||
const NON_SPECIFIC = '??';
|
||||
const NON_STYLEABLE_CONTAINER_TYPES = [
|
||||
@ -551,53 +549,28 @@ export default {
|
||||
return this.conditions ? this.conditions[id] : {};
|
||||
},
|
||||
addConditionSet() {
|
||||
let conditionSetDomainObject;
|
||||
let self = this;
|
||||
function handleItemSelection({ item }) {
|
||||
if (item) {
|
||||
conditionSetDomainObject = item;
|
||||
}
|
||||
}
|
||||
const conditionWidgetParent = this.openmct.router.path[1];
|
||||
const formStructure = {
|
||||
title: 'Select Condition Set',
|
||||
sections: [{
|
||||
name: 'Location',
|
||||
cssClass: 'grows',
|
||||
rows: [{
|
||||
key: 'location',
|
||||
name: 'Condition Set',
|
||||
cssClass: 'grows',
|
||||
control: 'locator',
|
||||
required: true,
|
||||
parent: conditionWidgetParent,
|
||||
validate: data => data.value[0].type === 'conditionSet'
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
function dismissDialog(overlay, initialize) {
|
||||
overlay.dismiss();
|
||||
|
||||
if (initialize && conditionSetDomainObject) {
|
||||
self.conditionSetDomainObject = conditionSetDomainObject;
|
||||
self.conditionalStyles = [];
|
||||
self.initializeConditionalStyles();
|
||||
}
|
||||
}
|
||||
|
||||
let vm = new Vue({
|
||||
components: { SelectorDialogTree },
|
||||
provide: {
|
||||
openmct: this.openmct
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
handleItemSelection,
|
||||
title: 'Select Condition Set'
|
||||
};
|
||||
},
|
||||
template: '<SelectorDialogTree :title="title" @treeItemSelected="handleItemSelection"></SelectorDialogTree>'
|
||||
}).$mount();
|
||||
|
||||
let overlay = this.openmct.overlays.overlay({
|
||||
element: vm.$el,
|
||||
size: 'small',
|
||||
buttons: [
|
||||
{
|
||||
label: 'OK',
|
||||
emphasis: 'true',
|
||||
callback: () => dismissDialog(overlay, true)
|
||||
},
|
||||
{
|
||||
label: 'Cancel',
|
||||
callback: () => dismissDialog(overlay, false)
|
||||
}
|
||||
],
|
||||
onDestroy: () => vm.$destroy()
|
||||
this.openmct.forms.showForm(formStructure).then(data => {
|
||||
this.conditionSetDomainObject = data.location[0];
|
||||
this.conditionalStyles = [];
|
||||
this.initializeConditionalStyles();
|
||||
});
|
||||
},
|
||||
removeConditionSet() {
|
||||
|
@ -1,240 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2022, 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>
|
||||
<div class="u-contents">
|
||||
<div v-if="title.length"
|
||||
class="c-overlay__top-bar"
|
||||
>
|
||||
<div class="c-overlay__dialog-title">{{ title }}</div>
|
||||
</div>
|
||||
<div class="c-selector c-tree-and-search"
|
||||
:class="cssClass"
|
||||
>
|
||||
<div class="c-tree-and-search__search">
|
||||
<Search ref="shell-search"
|
||||
class="c-search"
|
||||
:value="searchValue"
|
||||
@input="searchTree"
|
||||
@clear="searchTree"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-if="isLoading"
|
||||
class="c-tree-and-search__loading loading"
|
||||
></div>
|
||||
|
||||
<div v-if="shouldDisplayNoResultsText"
|
||||
class="c-tree-and-search__no-results"
|
||||
>
|
||||
No results found
|
||||
</div>
|
||||
|
||||
<ul v-if="!isLoading"
|
||||
v-show="!searchValue"
|
||||
class="c-tree-and-search__tree c-tree"
|
||||
>
|
||||
<SelectorDialogTreeItem
|
||||
v-for="treeItem in allTreeItems"
|
||||
:key="treeItem.id"
|
||||
:node="treeItem"
|
||||
:selected-item="selectedItem"
|
||||
:handle-item-selected="handleItemSelection"
|
||||
:navigate-to-parent="navigateToParent"
|
||||
/>
|
||||
</ul>
|
||||
|
||||
<ul v-if="searchValue && !isLoading"
|
||||
class="c-tree-and-search__tree c-tree"
|
||||
>
|
||||
<SelectorDialogTreeItem
|
||||
v-for="treeItem in filteredTreeItems"
|
||||
:key="treeItem.id"
|
||||
:node="treeItem"
|
||||
:selected-item="selectedItem"
|
||||
:handle-item-selected="handleItemSelection"
|
||||
/>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import debounce from 'lodash/debounce';
|
||||
import Search from '@/ui/components/search.vue';
|
||||
import SelectorDialogTreeItem from './SelectorDialogTreeItem.vue';
|
||||
|
||||
export default {
|
||||
name: 'SelectorDialogTree',
|
||||
components: {
|
||||
Search,
|
||||
SelectorDialogTreeItem
|
||||
},
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
cssClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default() {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: false,
|
||||
default() {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
ignoreTypeCheck: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default() {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
parent: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default() {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
allTreeItems: [],
|
||||
expanded: false,
|
||||
filteredTreeItems: [],
|
||||
isLoading: false,
|
||||
navigateToParent: undefined,
|
||||
searchValue: '',
|
||||
selectedItem: undefined
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
shouldDisplayNoResultsText() {
|
||||
if (this.isLoading) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.allTreeItems.length === 0
|
||||
|| (this.searchValue && this.filteredTreeItems.length === 0);
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getDebouncedFilteredChildren = debounce(this.getFilteredChildren, 400);
|
||||
},
|
||||
mounted() {
|
||||
if (this.parent) {
|
||||
(async () => {
|
||||
const objectPath = await this.openmct.objects.getOriginalPath(this.parent.identifier);
|
||||
this.navigateToParent = '/browse/'
|
||||
+ objectPath
|
||||
.map(parent => this.openmct.objects.makeKeyString(parent.identifier))
|
||||
.reverse()
|
||||
.join('/');
|
||||
|
||||
this.getAllChildren(this.navigateToParent);
|
||||
})();
|
||||
} else {
|
||||
this.getAllChildren();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async aggregateFilteredChildren(results) {
|
||||
for (const object of results) {
|
||||
const objectPath = await this.openmct.objects.getOriginalPath(object.identifier);
|
||||
|
||||
const navigateToParent = '/browse/'
|
||||
+ objectPath.slice(1)
|
||||
.map(parent => this.openmct.objects.makeKeyString(parent.identifier))
|
||||
.join('/');
|
||||
|
||||
const filteredChild = {
|
||||
id: this.openmct.objects.makeKeyString(object.identifier),
|
||||
object,
|
||||
objectPath,
|
||||
navigateToParent
|
||||
};
|
||||
|
||||
this.filteredTreeItems.push(filteredChild);
|
||||
}
|
||||
},
|
||||
getAllChildren(navigateToParent) {
|
||||
this.isLoading = true;
|
||||
this.openmct.objects.get('ROOT')
|
||||
.then(root => {
|
||||
return this.openmct.composition.get(root).load();
|
||||
})
|
||||
.then(children => {
|
||||
this.isLoading = false;
|
||||
this.allTreeItems = children.map(c => {
|
||||
return {
|
||||
id: this.openmct.objects.makeKeyString(c.identifier),
|
||||
object: c,
|
||||
objectPath: [c],
|
||||
navigateToParent: navigateToParent || '/browse'
|
||||
};
|
||||
});
|
||||
});
|
||||
},
|
||||
getFilteredChildren() {
|
||||
// clear any previous search results
|
||||
this.filteredTreeItems = [];
|
||||
|
||||
const promises = this.openmct.objects.search(this.searchValue)
|
||||
.map(promise => promise
|
||||
.then(results => this.aggregateFilteredChildren(results)));
|
||||
|
||||
Promise.all(promises).then(() => {
|
||||
this.isLoading = false;
|
||||
});
|
||||
},
|
||||
handleItemSelection(item, node) {
|
||||
if (item && (this.ignoreTypeCheck || item.type === 'conditionSet')) {
|
||||
const parentId = (node.objectPath && node.objectPath.length > 1) ? node.objectPath[1].identifier : undefined;
|
||||
this.selectedItem = {
|
||||
itemId: item.identifier,
|
||||
parentId
|
||||
};
|
||||
|
||||
this.$emit('treeItemSelected',
|
||||
{
|
||||
item,
|
||||
parentObjectPath: node.objectPath
|
||||
});
|
||||
}
|
||||
},
|
||||
searchTree(value) {
|
||||
this.searchValue = value;
|
||||
this.isLoading = true;
|
||||
|
||||
if (this.searchValue !== '') {
|
||||
this.getDebouncedFilteredChildren();
|
||||
} else {
|
||||
this.isLoading = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
@ -1,206 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2022, 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>
|
||||
<li class="c-tree__item-h">
|
||||
<div
|
||||
class="c-tree__item"
|
||||
:class="{ 'is-alias': isAlias, 'is-navigated-object': navigated }"
|
||||
@click="handleItemSelected(node.object, node)"
|
||||
>
|
||||
<view-control
|
||||
v-model="expanded"
|
||||
class="c-tree__item__view-control"
|
||||
:enabled="hasChildren"
|
||||
/>
|
||||
<div class="c-tree__item__label c-object-label">
|
||||
<div
|
||||
class="c-tree__item__type-icon c-object-label__type-icon"
|
||||
:class="typeClass"
|
||||
></div>
|
||||
<div class="c-tree__item__name c-object-label__name">{{ node.object.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<ul
|
||||
v-if="expanded && !isLoading"
|
||||
class="c-tree"
|
||||
>
|
||||
<li
|
||||
v-if="isLoading && !loaded"
|
||||
class="c-tree__item-h"
|
||||
>
|
||||
<div class="c-tree__item loading">
|
||||
<span class="c-tree__item__label">Loading...</span>
|
||||
</div>
|
||||
</li>
|
||||
<SelectorDialogTreeItem
|
||||
v-for="child in children"
|
||||
:key="child.id"
|
||||
:node="child"
|
||||
:selected-item="selectedItem"
|
||||
:handle-item-selected="handleItemSelected"
|
||||
:navigate-to-parent="navigateToParent"
|
||||
/>
|
||||
</ul>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import viewControl from '@/ui/components/viewControl.vue';
|
||||
|
||||
export default {
|
||||
name: 'SelectorDialogTreeItem',
|
||||
components: {
|
||||
viewControl
|
||||
},
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
node: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
selectedItem: {
|
||||
type: Object,
|
||||
default() {
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
handleItemSelected: {
|
||||
type: Function,
|
||||
default() {
|
||||
return (item) => {};
|
||||
}
|
||||
},
|
||||
navigateToParent: {
|
||||
type: String,
|
||||
default() {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
hasChildren: false,
|
||||
isLoading: false,
|
||||
loaded: false,
|
||||
children: [],
|
||||
expanded: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
navigated() {
|
||||
const itemId = this.selectedItem && this.selectedItem.itemId;
|
||||
const isSelectedObject = itemId && this.openmct.objects.areIdsEqual(this.node.object.identifier, itemId);
|
||||
if (isSelectedObject && this.node.objectPath && this.node.objectPath.length > 1) {
|
||||
const isParent = this.openmct.objects.areIdsEqual(this.node.objectPath[1].identifier, this.selectedItem.parentId);
|
||||
|
||||
return isSelectedObject && isParent;
|
||||
}
|
||||
|
||||
return isSelectedObject;
|
||||
},
|
||||
isAlias() {
|
||||
let parent = this.node.objectPath[1];
|
||||
if (!parent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let parentKeyString = this.openmct.objects.makeKeyString(parent.identifier);
|
||||
|
||||
return parentKeyString !== this.node.object.location;
|
||||
},
|
||||
typeClass() {
|
||||
let type = this.openmct.types.get(this.node.object.type);
|
||||
if (!type) {
|
||||
return 'icon-object-unknown';
|
||||
}
|
||||
|
||||
return type.definition.cssClass;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
expanded() {
|
||||
if (!this.hasChildren) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.loaded && !this.isLoading) {
|
||||
this.composition = this.openmct.composition.get(this.domainObject);
|
||||
this.composition.on('add', this.addChild);
|
||||
this.composition.on('remove', this.removeChild);
|
||||
this.composition.load().then(this.finishLoading);
|
||||
this.isLoading = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.domainObject = this.node.object;
|
||||
|
||||
if (this.navigateToParent && this.navigateToParent.includes(this.openmct.objects.makeKeyString(this.domainObject.identifier))) {
|
||||
this.expanded = true;
|
||||
}
|
||||
|
||||
if (this.navigateToParent && this.navigateToParent.endsWith(this.openmct.objects.makeKeyString(this.domainObject.identifier))) {
|
||||
this.handleItemSelected(this.node.object, this.node);
|
||||
}
|
||||
|
||||
let removeListener = this.openmct.objects.observe(this.domainObject, '*', (newObject) => {
|
||||
this.domainObject = newObject;
|
||||
});
|
||||
|
||||
this.$once('hook:destroyed', removeListener);
|
||||
if (this.openmct.composition.get(this.node.object)) {
|
||||
this.hasChildren = true;
|
||||
}
|
||||
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.expanded = false;
|
||||
},
|
||||
destroyed() {
|
||||
if (this.composition) {
|
||||
this.composition.off('add', this.addChild);
|
||||
this.composition.off('remove', this.removeChild);
|
||||
delete this.composition;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addChild(child) {
|
||||
this.children.push({
|
||||
id: this.openmct.objects.makeKeyString(child.identifier),
|
||||
object: child,
|
||||
objectPath: [child].concat(this.node.objectPath),
|
||||
navigateToParent: this.navigateToPath
|
||||
});
|
||||
},
|
||||
removeChild(identifier) {
|
||||
let removeId = this.openmct.objects.makeKeyString(identifier);
|
||||
this.children = this.children
|
||||
.filter(c => c.id !== removeId);
|
||||
},
|
||||
finishLoading() {
|
||||
this.isLoading = false;
|
||||
this.loaded = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
@ -280,38 +280,5 @@
|
||||
border: 1px solid $colorFormLines;
|
||||
border-radius: $controlCr;
|
||||
padding: $interiorMargin;
|
||||
|
||||
> .c-tree {
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TRANSITIONS
|
||||
.children-enter-active {
|
||||
&.down {
|
||||
animation: animSlideLeft 500ms;
|
||||
}
|
||||
|
||||
&.up {
|
||||
animation: animSlideRight 500ms;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes animSlideLeft {
|
||||
0% {opacity: 0; transform: translateX(100%);}
|
||||
10% {opacity: 1;}
|
||||
100% {transform: translateX(0);}
|
||||
}
|
||||
|
||||
@keyframes animSlideRight {
|
||||
0% {opacity: 0; transform: translateX(-100%);}
|
||||
10% {opacity: 1;}
|
||||
100% {transform: translateX(0);}
|
||||
}
|
||||
|
||||
@keyframes animTemporaryHighlight {
|
||||
from { background: transparent; }
|
||||
30% { background: $colorItemTreeNewNode; }
|
||||
100% { background: transparent; }
|
||||
}
|
||||
|
@ -1,6 +1,12 @@
|
||||
<template>
|
||||
<div class="c-tree-and-search">
|
||||
|
||||
<div
|
||||
ref="treeContainer"
|
||||
class="c-tree-and-search"
|
||||
:class="{
|
||||
'c-selector': isSelectorTree
|
||||
}"
|
||||
:style="treeHeight"
|
||||
>
|
||||
<div
|
||||
ref="search"
|
||||
class="c-tree-and-search__search"
|
||||
@ -72,6 +78,8 @@
|
||||
v-for="(treeItem, index) in visibleItems"
|
||||
:key="treeItem.navigationPath"
|
||||
:node="treeItem"
|
||||
:is-selector-tree="isSelectorTree"
|
||||
:selected-item="selectedItem"
|
||||
:active-search="activeSearch"
|
||||
:left-offset="!activeSearch ? treeItem.leftOffset : '0px'"
|
||||
:is-new="treeItem.isNew"
|
||||
@ -82,7 +90,8 @@
|
||||
:loading-items="treeItemLoading"
|
||||
@tree-item-mounted="scrollToCheck($event)"
|
||||
@tree-item-destroyed="removeCompositionListenerFor($event)"
|
||||
@navigation-click="treeItemAction(treeItem, $event)"
|
||||
@tree-item-action="treeItemAction(treeItem, $event)"
|
||||
@tree-item-selection="treeItemSelection(treeItem)"
|
||||
/>
|
||||
<!-- main loading -->
|
||||
<div
|
||||
@ -115,6 +124,7 @@ const ITEM_BUFFER = 25;
|
||||
const LOCAL_STORAGE_KEY__TREE_EXPANDED = 'mct-tree-expanded';
|
||||
const SORT_MY_ITEMS_ALPH_ASC = true;
|
||||
const TREE_ITEM_INDENT_PX = 18;
|
||||
const LOCATOR_ITEM_COUNT_HEIGHT = 10; // how many tree items to make the locator selection box show
|
||||
|
||||
export default {
|
||||
name: 'MctTree',
|
||||
@ -124,13 +134,27 @@ export default {
|
||||
},
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
isSelectorTree: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default() {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
initialSelection: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default() {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
syncTreeNavigation: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
required: false
|
||||
},
|
||||
resetTreeNavigation: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
required: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@ -149,7 +173,8 @@ export default {
|
||||
itemHeight: 27,
|
||||
itemOffset: 0,
|
||||
activeSearch: false,
|
||||
mainTreeTopMargin: undefined
|
||||
mainTreeTopMargin: undefined,
|
||||
selectedItem: {}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@ -179,6 +204,13 @@ export default {
|
||||
},
|
||||
showNoSearchResults() {
|
||||
return this.searchValue && this.searchResultItems.length === 0 && !this.searchLoading;
|
||||
},
|
||||
treeHeight() {
|
||||
if (!this.isSelectorTree) {
|
||||
return {};
|
||||
} else {
|
||||
return { height: this.itemHeight * LOCATOR_ITEM_COUNT_HEIGHT + 'px' };
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@ -223,7 +255,14 @@ export default {
|
||||
await this.loadRoot();
|
||||
this.isLoading = false;
|
||||
|
||||
await this.syncTreeOpenItems();
|
||||
if (!this.isSelectorTree) {
|
||||
await this.syncTreeOpenItems();
|
||||
} else {
|
||||
const objectPath = await this.openmct.objects.getOriginalPath(this.initialSelection.identifier);
|
||||
const navigationPath = this.buildNavigationPath(objectPath);
|
||||
|
||||
this.openAndScrollTo(navigationPath);
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getSearchResults = _.debounce(this.getSearchResults, 400);
|
||||
@ -265,6 +304,10 @@ export default {
|
||||
this.openTreeItem(parentItem);
|
||||
}
|
||||
},
|
||||
treeItemSelection(item) {
|
||||
this.selectedItem = item;
|
||||
this.$emit('tree-item-selection', item);
|
||||
},
|
||||
async openTreeItem(parentItem) {
|
||||
let parentPath = parentItem.navigationPath;
|
||||
|
||||
@ -358,6 +401,10 @@ export default {
|
||||
}
|
||||
},
|
||||
openAndScrollTo(navigationPath) {
|
||||
if (navigationPath.includes('/ROOT')) {
|
||||
navigationPath = navigationPath.split('/ROOT').join('');
|
||||
}
|
||||
|
||||
let idArray = navigationPath.split('/');
|
||||
let fullPathArray = [];
|
||||
let pathsToOpen;
|
||||
@ -367,7 +414,6 @@ export default {
|
||||
// skip root
|
||||
idArray.splice(0, 2);
|
||||
idArray[0] = 'browse/' + idArray[0];
|
||||
|
||||
idArray.reduce((parentPath, childPath) => {
|
||||
let fullPath = [parentPath, childPath].join('/');
|
||||
|
||||
@ -383,7 +429,11 @@ export default {
|
||||
|
||||
return this.openTreeItem(this.getTreeItemByPath(childPath));
|
||||
|
||||
}, Promise.resolve());
|
||||
}, Promise.resolve()).then(() => {
|
||||
if (this.isSelectorTree) {
|
||||
this.treeItemSelection(this.getTreeItemByPath(navigationPath));
|
||||
}
|
||||
});
|
||||
},
|
||||
scrollToCheck(navigationPath) {
|
||||
if (this.scrollToPath && this.scrollToPath === navigationPath) {
|
||||
@ -721,18 +771,26 @@ export default {
|
||||
|
||||
let checkHeights = () => {
|
||||
let treeTopMargin = this.getElementStyleValue(this.$refs.mainTree, 'marginTop');
|
||||
let paddingOffset = 0;
|
||||
|
||||
if (
|
||||
this.$el
|
||||
&& this.$refs.search
|
||||
&& this.$refs.mainTree
|
||||
&& this.$refs.treeContainer
|
||||
&& this.$refs.dummyItem
|
||||
&& this.$el.offsetHeight !== 0
|
||||
&& treeTopMargin > 0
|
||||
) {
|
||||
if (this.isSelectorTree) {
|
||||
paddingOffset = this.getElementStyleValue(this.$refs.treeContainer, 'padding');
|
||||
}
|
||||
|
||||
this.mainTreeTopMargin = treeTopMargin;
|
||||
this.mainTreeHeight = this.$el.offsetHeight
|
||||
- this.$refs.search.offsetHeight
|
||||
- this.mainTreeTopMargin;
|
||||
- this.mainTreeTopMargin
|
||||
- (paddingOffset * 2);
|
||||
this.itemHeight = this.getElementStyleValue(this.$refs.dummyItem, 'height');
|
||||
|
||||
resolve();
|
||||
@ -783,10 +841,18 @@ export default {
|
||||
return Number(styleString.slice(0, index));
|
||||
},
|
||||
getSavedOpenItems() {
|
||||
if (this.isSelectorTree) {
|
||||
return;
|
||||
}
|
||||
|
||||
let openItems = localStorage.getItem(LOCAL_STORAGE_KEY__TREE_EXPANDED);
|
||||
this.openTreeItems = openItems ? JSON.parse(openItems) : [];
|
||||
},
|
||||
setSavedOpenItems() {
|
||||
if (this.isSelectorTree) {
|
||||
return;
|
||||
}
|
||||
|
||||
localStorage.setItem(LOCAL_STORAGE_KEY__TREE_EXPANDED, JSON.stringify(this.openTreeItems));
|
||||
},
|
||||
handleTreeResize() {
|
||||
|
@ -7,19 +7,19 @@
|
||||
class="c-tree__item"
|
||||
:class="{
|
||||
'is-alias': isAlias,
|
||||
'is-navigated-object': navigated,
|
||||
'is-navigated-object': shouldHightlight,
|
||||
'is-context-clicked': contextClickActive,
|
||||
'is-new': isNewItem
|
||||
}"
|
||||
@click.capture="handleClick"
|
||||
@click.capture="itemClick"
|
||||
@contextmenu.capture="handleContextMenu"
|
||||
>
|
||||
<view-control
|
||||
ref="navigate"
|
||||
ref="action"
|
||||
class="c-tree__item__view-control"
|
||||
:value="isOpen || isLoading"
|
||||
:enabled="!activeSearch && hasComposition"
|
||||
@input="navigationClick()"
|
||||
@input="itemAction()"
|
||||
/>
|
||||
<object-label
|
||||
ref="objectLabel"
|
||||
@ -52,6 +52,14 @@ export default {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
isSelectorTree: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
selectedItem: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
activeSearch: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
@ -109,6 +117,9 @@ export default {
|
||||
|
||||
return parentKeyString !== this.node.object.location;
|
||||
},
|
||||
isSelectedItem() {
|
||||
return this.selectedItem.objectPath === this.node.objectPath;
|
||||
},
|
||||
isNewItem() {
|
||||
return this.isNew;
|
||||
},
|
||||
@ -118,6 +129,13 @@ export default {
|
||||
isOpen() {
|
||||
return this.openItems.includes(this.navigationPath);
|
||||
},
|
||||
shouldHightlight() {
|
||||
if (this.isSelectorTree) {
|
||||
return this.isSelectedItem;
|
||||
} else {
|
||||
return this.navigated;
|
||||
}
|
||||
},
|
||||
treeItemStyles() {
|
||||
let itemTop = (this.itemOffset + this.itemIndex) * this.itemHeight + 'px';
|
||||
|
||||
@ -144,20 +162,30 @@ export default {
|
||||
this.$emit('tree-item-destoyed', this.navigationPath);
|
||||
},
|
||||
methods: {
|
||||
navigationClick() {
|
||||
this.$emit('navigation-click', this.isOpen || this.isLoading ? 'close' : 'open');
|
||||
itemAction() {
|
||||
this.$emit('tree-item-action', this.isOpen || this.isLoading ? 'close' : 'open');
|
||||
},
|
||||
handleClick(event) {
|
||||
itemClick(event) {
|
||||
// skip for navigation, let viewControl handle click
|
||||
if (this.$refs.navigate.$el === event.target) {
|
||||
if (this.$refs.action.$el === event.target) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.stopPropagation();
|
||||
this.$refs.objectLabel.navigateOrPreview(event);
|
||||
|
||||
if (!this.isSelectorTree) {
|
||||
this.$refs.objectLabel.navigateOrPreview(event);
|
||||
} else {
|
||||
this.$emit('tree-item-selection', this.node);
|
||||
}
|
||||
},
|
||||
handleContextMenu(event) {
|
||||
event.stopPropagation();
|
||||
|
||||
if (this.isSelectorTree) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.$refs.objectLabel.showContextMenu(event);
|
||||
},
|
||||
isNavigated() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user