Compare commits
9 Commits
master
...
mct6053-re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
510215e2da | ||
|
|
0b2dc1e8ab | ||
|
|
bb516d668b | ||
|
|
eb150a892c | ||
|
|
b8b6b0f792 | ||
|
|
83723065e5 | ||
|
|
39e9d2a9c4 | ||
|
|
b5a2194c36 | ||
|
|
298e9eb361 |
@@ -740,6 +740,30 @@ export default class ObjectAPI {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse and construct an objectPath from the object's navigation path.
|
||||
*
|
||||
* @param {string} navigationPath
|
||||
* @returns {DomainObject[]} objectPath
|
||||
*/
|
||||
async getRelativeObjectPath(navigationPath) {
|
||||
const identifierRegexp = /mine|[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/gi;
|
||||
const identifiers = navigationPath.split('?')[0].match(identifierRegexp);
|
||||
if (!identifiers) {
|
||||
return [];
|
||||
}
|
||||
|
||||
identifiers.unshift('ROOT');
|
||||
|
||||
const objectPath = (await Promise.all(
|
||||
identifiers.map(
|
||||
identifier => this.get(utils.parseKeyString(identifier))
|
||||
)
|
||||
)).reverse();
|
||||
|
||||
return objectPath;
|
||||
}
|
||||
|
||||
isObjectPathToALink(domainObject, objectPath) {
|
||||
return objectPath !== undefined
|
||||
&& objectPath.length > 1
|
||||
|
||||
@@ -53,8 +53,8 @@
|
||||
type="horizontal"
|
||||
>
|
||||
<pane
|
||||
id="tree-pane"
|
||||
class="l-shell__pane-tree"
|
||||
style="width: 300px;"
|
||||
handle="after"
|
||||
label="Browse"
|
||||
hide-param="hideTree"
|
||||
@@ -75,11 +75,27 @@
|
||||
@click="handleSyncTreeNavigation"
|
||||
>
|
||||
</button>
|
||||
<mct-tree
|
||||
:sync-tree-navigation="triggerSync"
|
||||
:reset-tree-navigation="triggerReset"
|
||||
class="l-shell__tree"
|
||||
/>
|
||||
<multipane
|
||||
type="vertical"
|
||||
>
|
||||
<pane
|
||||
id="tree-pane"
|
||||
>
|
||||
<mct-tree
|
||||
:sync-tree-navigation="triggerSync"
|
||||
:reset-tree-navigation="triggerReset"
|
||||
class="l-shell__tree"
|
||||
/>
|
||||
</pane>
|
||||
<pane
|
||||
handle="before"
|
||||
label="Recently Viewed"
|
||||
>
|
||||
<RecentObjects
|
||||
class="l-shell__tree"
|
||||
/>
|
||||
</pane>
|
||||
</multipane>
|
||||
</pane>
|
||||
<pane class="l-shell__pane-main">
|
||||
<browse-bar
|
||||
@@ -134,6 +150,7 @@ import Toolbar from '../toolbar/Toolbar.vue';
|
||||
import AppLogo from './AppLogo.vue';
|
||||
import Indicators from './status-bar/Indicators.vue';
|
||||
import NotificationBanner from './status-bar/NotificationBanner.vue';
|
||||
import RecentObjects from './RecentObjects.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -148,7 +165,8 @@ export default {
|
||||
Toolbar,
|
||||
AppLogo,
|
||||
Indicators,
|
||||
NotificationBanner
|
||||
NotificationBanner,
|
||||
RecentObjects
|
||||
},
|
||||
inject: ['openmct'],
|
||||
data: function () {
|
||||
|
||||
105
src/ui/layout/RecentObjects.vue
Normal file
105
src/ui/layout/RecentObjects.vue
Normal file
@@ -0,0 +1,105 @@
|
||||
<template>
|
||||
<div
|
||||
class="c-tree-and-search l-shell__tree"
|
||||
>
|
||||
<div
|
||||
class="c-tree-and-search__tree c-tree c-tree__scrollable"
|
||||
>
|
||||
|
||||
<tree-item
|
||||
v-for="(recentItem, index) in treeItems"
|
||||
:key="`${recentItem.navigationPath}-recent-${index}`"
|
||||
:node="recentItem"
|
||||
:is-selector-tree="false"
|
||||
:selected-item="selectedItem"
|
||||
:left-offset="recentItem.leftOffset"
|
||||
:is-new="recentItem.isNew"
|
||||
:item-offset="itemOffset"
|
||||
:item-index="index"
|
||||
:item-height="itemHeight"
|
||||
:open-items="openTreeItems"
|
||||
:loading-items="treeItemLoading"
|
||||
/>
|
||||
<!-- @tree-item-mounted="scrollToCheck($event)"
|
||||
@tree-item-action="treeItemAction(recentItem, $event)"
|
||||
@tree-item-destroyed="removeCompositionListenerFor($event)"
|
||||
@tree-item-selection="recentItemSelection(recentItem)" -->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import treeItem from './tree-item.vue';
|
||||
import treeMixin from '../mixins/tree-mixin.js';
|
||||
const MAX_RECENT_ITEMS = 20;
|
||||
const LOCAL_STORAGE_KEY__RECENT_OBJECTS = 'mct-recent-objects';
|
||||
export default {
|
||||
name: 'RecentObjects',
|
||||
components: {
|
||||
treeItem
|
||||
},
|
||||
mixins: [treeMixin],
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
};
|
||||
},
|
||||
async mounted() {
|
||||
this.openmct.router.on('change:hash', this.onHashChange);
|
||||
|
||||
this.treeResizeObserver = new ResizeObserver(this.handleTreeResize);
|
||||
this.treeResizeObserver.observe(this.$el);
|
||||
await this.calculateHeights();
|
||||
},
|
||||
created() {
|
||||
this.handleTreeResize = _.debounce(this.handleTreeResize, 300);
|
||||
},
|
||||
destroyed() {
|
||||
this.openmct.router.off('change:hash', this.onHashChange);
|
||||
},
|
||||
methods: {
|
||||
async onHashChange(hash) {
|
||||
const objectPath = await this.openmct.objects.getRelativeObjectPath(hash);
|
||||
if (!objectPath.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const navigationPath = `/browse/${this.openmct.objects.getRelativePath(objectPath.slice(0, -1))}`;
|
||||
const foundIndex = this.treeItems.findIndex((item) => {
|
||||
return navigationPath === item.navigationPath;
|
||||
});
|
||||
if (foundIndex > -1) {
|
||||
const removedItem = this.treeItems.splice(foundIndex, 1);
|
||||
this.selectedItem = removedItem[0];
|
||||
} else {
|
||||
this.selectedItem = {
|
||||
id: objectPath[0].identifier,
|
||||
object: objectPath[0],
|
||||
objectPath,
|
||||
navigationPath
|
||||
};
|
||||
}
|
||||
|
||||
this.treeItems.unshift(this.selectedItem);
|
||||
while (this.treeItems.length > MAX_RECENT_ITEMS) {
|
||||
this.treeItems.pop();
|
||||
}
|
||||
},
|
||||
async loadAndBuildTreeItemsFor(domainObject, parentObjectPath, abortSignal) {
|
||||
let collection = this.openmct.composition.get(domainObject);
|
||||
let composition = await collection.load(abortSignal);
|
||||
|
||||
return composition.map((object) => {
|
||||
return this.buildTreeItem(object, parentObjectPath);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -289,7 +289,7 @@
|
||||
}
|
||||
|
||||
&__pane-tree {
|
||||
width: 300px;
|
||||
width: 100%;
|
||||
padding-left: nth($shellPanePad, 2);
|
||||
}
|
||||
|
||||
|
||||
@@ -118,12 +118,13 @@
|
||||
<script>
|
||||
import _ from 'lodash';
|
||||
import treeItem from './tree-item.vue';
|
||||
import treeMixin from '../mixins/tree-mixin.js';
|
||||
import search from '../components/search.vue';
|
||||
|
||||
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 SORT_MY_ITEMS_ALPH_ASC = true;
|
||||
const LOCATOR_ITEM_COUNT_HEIGHT = 10; // how many tree items to make the locator selection box show
|
||||
|
||||
export default {
|
||||
@@ -132,6 +133,7 @@ export default {
|
||||
search,
|
||||
treeItem
|
||||
},
|
||||
mixins: [treeMixin],
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
isSelectorTree: {
|
||||
@@ -214,21 +216,6 @@ export default {
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
syncTreeNavigation() {
|
||||
this.searchValue = '';
|
||||
|
||||
// if there is an abort controller, then a search is in progress and will need to be canceled
|
||||
if (this.abortSearchController) {
|
||||
this.abortSearchController.abort();
|
||||
delete this.abortSearchController;
|
||||
}
|
||||
|
||||
if (!this.openmct.router.path) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.$nextTick(this.showCurrentPathInTree);
|
||||
},
|
||||
resetTreeNavigation() {
|
||||
[...this.openTreeItems].reverse().map(this.closeTreeItemByPath);
|
||||
},
|
||||
@@ -344,58 +331,6 @@ export default {
|
||||
|
||||
return;
|
||||
},
|
||||
closeTreeItemByPath(path) {
|
||||
// if actively loading, abort
|
||||
if (this.isItemLoading(path)) {
|
||||
this.abortItemLoad(path);
|
||||
}
|
||||
|
||||
let pathIndex = this.openTreeItems.indexOf(path);
|
||||
|
||||
if (pathIndex === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.treeItems = this.treeItems.filter((checkItem) => {
|
||||
if (checkItem.navigationPath !== path
|
||||
&& checkItem.navigationPath.includes(path)) {
|
||||
this.destroyObserverByPath(checkItem.navigationPath);
|
||||
this.destroyMutableByPath(checkItem.navigationPath);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
this.openTreeItems.splice(pathIndex, 1);
|
||||
this.removeCompositionListenerFor(path);
|
||||
},
|
||||
closeTreeItem(item) {
|
||||
this.closeTreeItemByPath(item.navigationPath);
|
||||
},
|
||||
// returns an AbortController signal to be passed on to requests
|
||||
startItemLoad(path) {
|
||||
if (this.isItemLoading(path)) {
|
||||
this.abortItemLoad(path);
|
||||
}
|
||||
|
||||
this.$set(this.treeItemLoading, path, new AbortController());
|
||||
|
||||
return this.treeItemLoading[path].signal;
|
||||
},
|
||||
endItemLoad(path) {
|
||||
this.$set(this.treeItemLoading, path, undefined);
|
||||
delete this.treeItemLoading[path];
|
||||
},
|
||||
abortItemLoad(path) {
|
||||
if (this.treeItemLoading[path]) {
|
||||
this.treeItemLoading[path].abort();
|
||||
this.endItemLoad(path);
|
||||
}
|
||||
},
|
||||
isItemLoading(path) {
|
||||
return this.treeItemLoading[path] instanceof AbortController;
|
||||
},
|
||||
showCurrentPathInTree() {
|
||||
const currentPath = this.buildNavigationPath(this.openmct.router.path);
|
||||
|
||||
@@ -547,69 +482,6 @@ export default {
|
||||
// determine if any part of the parent's path includes a key value of mine; aka My Items
|
||||
return Boolean(parentObjectPath.find(path => path.identifier.key === 'mine'));
|
||||
},
|
||||
async loadAndBuildTreeItemsFor(domainObject, parentObjectPath, abortSignal) {
|
||||
let collection = this.openmct.composition.get(domainObject);
|
||||
let composition = await collection.load(abortSignal);
|
||||
|
||||
if (SORT_MY_ITEMS_ALPH_ASC && this.isSortable(parentObjectPath)) {
|
||||
const sortedComposition = composition.slice().sort(this.sortNameAscending);
|
||||
composition = sortedComposition;
|
||||
}
|
||||
|
||||
if (parentObjectPath.length && !this.isSelectorTree) {
|
||||
let navigationPath = this.buildNavigationPath(parentObjectPath);
|
||||
|
||||
if (this.compositionCollections[navigationPath]) {
|
||||
this.removeCompositionListenerFor(navigationPath);
|
||||
}
|
||||
|
||||
this.compositionCollections[navigationPath] = {};
|
||||
this.compositionCollections[navigationPath].collection = collection;
|
||||
this.compositionCollections[navigationPath].addHandler = this.compositionAddHandler(navigationPath);
|
||||
this.compositionCollections[navigationPath].removeHandler = this.compositionRemoveHandler(navigationPath);
|
||||
|
||||
this.compositionCollections[navigationPath].collection.on('add',
|
||||
this.compositionCollections[navigationPath].addHandler);
|
||||
this.compositionCollections[navigationPath].collection.on('remove',
|
||||
this.compositionCollections[navigationPath].removeHandler);
|
||||
}
|
||||
|
||||
return composition.map((object) => {
|
||||
// Only add observers and mutables if this is NOT a selector tree
|
||||
if (!this.isSelectorTree) {
|
||||
if (this.openmct.objects.supportsMutation(object.identifier)) {
|
||||
object = this.openmct.objects.toMutable(object);
|
||||
this.addMutable(object, parentObjectPath);
|
||||
}
|
||||
|
||||
this.addTreeItemObserver(object, parentObjectPath);
|
||||
}
|
||||
|
||||
return this.buildTreeItem(object, parentObjectPath);
|
||||
});
|
||||
},
|
||||
buildTreeItem(domainObject, parentObjectPath, isNew = false) {
|
||||
let objectPath = [domainObject].concat(parentObjectPath);
|
||||
let navigationPath = this.buildNavigationPath(objectPath);
|
||||
|
||||
return {
|
||||
id: this.openmct.objects.makeKeyString(domainObject.identifier),
|
||||
object: domainObject,
|
||||
leftOffset: ((objectPath.length - 1) * TREE_ITEM_INDENT_PX) + 'px',
|
||||
isNew,
|
||||
objectPath,
|
||||
navigationPath
|
||||
};
|
||||
},
|
||||
addMutable(mutableDomainObject, parentObjectPath) {
|
||||
const objectPath = [mutableDomainObject].concat(parentObjectPath);
|
||||
const navigationPath = this.buildNavigationPath(objectPath);
|
||||
|
||||
// If the mutable already exists, destroy it.
|
||||
this.destroyMutableByPath(navigationPath);
|
||||
|
||||
this.mutables[navigationPath] = () => this.openmct.objects.destroyMutable(mutableDomainObject);
|
||||
},
|
||||
addTreeItemObserver(domainObject, parentObjectPath) {
|
||||
const objectPath = [domainObject].concat(parentObjectPath);
|
||||
const navigationPath = this.buildNavigationPath(objectPath);
|
||||
@@ -662,11 +534,6 @@ export default {
|
||||
// Splice in all of the sorted descendants
|
||||
this.treeItems.splice(this.treeItems.indexOf(parentItem) + 1, sortedTreeItems.length, ...sortedTreeItems);
|
||||
},
|
||||
buildNavigationPath(objectPath) {
|
||||
return '/browse/' + [...objectPath].reverse()
|
||||
.map((object) => this.openmct.objects.makeKeyString(object.identifier))
|
||||
.join('/');
|
||||
},
|
||||
compositionAddHandler(navigationPath) {
|
||||
return (domainObject) => {
|
||||
const parentItem = this.getTreeItemByPath(navigationPath);
|
||||
@@ -881,44 +748,6 @@ export default {
|
||||
|
||||
return Math.ceil(scrollBottom / this.itemHeight);
|
||||
},
|
||||
calculateHeights() {
|
||||
const RECHECK = 100;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
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
|
||||
- (paddingOffset * 2);
|
||||
this.itemHeight = this.getElementStyleValue(this.$refs.dummyItem, 'height');
|
||||
|
||||
resolve();
|
||||
} else {
|
||||
setTimeout(checkHeights, RECHECK);
|
||||
}
|
||||
};
|
||||
|
||||
checkHeights();
|
||||
});
|
||||
},
|
||||
getTreeItemByPath(path) {
|
||||
return this.treeItems.find(item => item.navigationPath === path);
|
||||
},
|
||||
@@ -941,22 +770,6 @@ export default {
|
||||
&& childItem.navigationPath.includes(parentPath);
|
||||
});
|
||||
},
|
||||
isTreeItemOpen(item) {
|
||||
return this.isTreeItemPathOpen(item.navigationPath);
|
||||
},
|
||||
isTreeItemPathOpen(path) {
|
||||
return this.openTreeItems.includes(path);
|
||||
},
|
||||
getElementStyleValue(el, style) {
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
|
||||
let styleString = window.getComputedStyle(el)[style];
|
||||
let index = styleString.indexOf('px');
|
||||
|
||||
return Number(styleString.slice(0, index));
|
||||
},
|
||||
getSavedOpenItems() {
|
||||
if (this.isSelectorTree) {
|
||||
return;
|
||||
@@ -972,9 +785,6 @@ export default {
|
||||
|
||||
localStorage.setItem(LOCAL_STORAGE_KEY__TREE_EXPANDED, JSON.stringify(this.openTreeItems));
|
||||
},
|
||||
handleTreeResize() {
|
||||
this.calculateHeights();
|
||||
},
|
||||
/**
|
||||
* Destroy an observer for the given navigationPath.
|
||||
*/
|
||||
|
||||
224
src/ui/mixins/tree-mixin.js
Normal file
224
src/ui/mixins/tree-mixin.js
Normal file
@@ -0,0 +1,224 @@
|
||||
export const SORT_MY_ITEMS_ALPH_ASC = true;
|
||||
export const TREE_ITEM_INDENT_PX = 18;
|
||||
export default {
|
||||
data: function () {
|
||||
return {
|
||||
isLoading: false,
|
||||
treeItems: [],
|
||||
openTreeItems: [],
|
||||
visibleItems: [],
|
||||
updatingView: false,
|
||||
treeItemLoading: {},
|
||||
compositionCollections: {},
|
||||
selectedItem: {},
|
||||
observers: {},
|
||||
itemHeight: 27,
|
||||
itemOffset: 0
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
syncTreeNavigation() {
|
||||
this.searchValue = '';
|
||||
|
||||
// if there is an abort controller, then a search is in progress and will need to be canceled
|
||||
if (this.abortSearchController) {
|
||||
this.abortSearchController.abort();
|
||||
delete this.abortSearchController;
|
||||
}
|
||||
|
||||
if (!this.openmct.router.path) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.$nextTick(this.showCurrentPathInTree);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
abortItemLoad(path) {
|
||||
if (this.treeItemLoading[path]) {
|
||||
this.treeItemLoading[path].abort();
|
||||
this.endItemLoad(path);
|
||||
}
|
||||
},
|
||||
buildNavigationPath(objectPath) {
|
||||
return '/browse/' + [...objectPath].reverse()
|
||||
.map((object) => this.openmct.objects.makeKeyString(object.identifier))
|
||||
.join('/');
|
||||
},
|
||||
buildTreeItem(domainObject, parentObjectPath, isNew = false) {
|
||||
let objectPath = [domainObject].concat(parentObjectPath);
|
||||
let navigationPath = this.buildNavigationPath(objectPath);
|
||||
|
||||
return {
|
||||
id: this.openmct.objects.makeKeyString(domainObject.identifier),
|
||||
object: domainObject,
|
||||
leftOffset: ((objectPath.length - 1) * TREE_ITEM_INDENT_PX) + 'px',
|
||||
isNew,
|
||||
objectPath,
|
||||
navigationPath
|
||||
};
|
||||
},
|
||||
calculateHeights() {
|
||||
const RECHECK = 100;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
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
|
||||
) {
|
||||
|
||||
this.mainTreeTopMargin = treeTopMargin;
|
||||
this.mainTreeHeight = this.$el.offsetHeight
|
||||
- this.$refs.search.offsetHeight
|
||||
- this.mainTreeTopMargin
|
||||
- (paddingOffset * 2);
|
||||
this.itemHeight = this.getElementStyleValue(this.$refs.dummyItem, 'height');
|
||||
|
||||
resolve();
|
||||
} else {
|
||||
setTimeout(checkHeights, RECHECK);
|
||||
}
|
||||
};
|
||||
|
||||
checkHeights();
|
||||
});
|
||||
},
|
||||
closeTreeItem(item) {
|
||||
this.closeTreeItemByPath(item.navigationPath);
|
||||
},
|
||||
closeTreeItemByPath(path) {
|
||||
// if actively loading, abort
|
||||
if (this.isItemLoading(path)) {
|
||||
this.abortItemLoad(path);
|
||||
}
|
||||
|
||||
let pathIndex = this.openTreeItems.indexOf(path);
|
||||
|
||||
if (pathIndex === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.treeItems = this.treeItems.filter((checkItem) => {
|
||||
return checkItem.navigationPath === path
|
||||
|| !checkItem.navigationPath.includes(path);
|
||||
});
|
||||
this.openTreeItems.splice(pathIndex, 1);
|
||||
// this.removeCompositionListenerFor(path);
|
||||
},
|
||||
endItemLoad(path) {
|
||||
this.$set(this.treeItemLoading, path, undefined);
|
||||
delete this.treeItemLoading[path];
|
||||
},
|
||||
getElementStyleValue(el, style) {
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
|
||||
let styleString = window.getComputedStyle(el)[style];
|
||||
let index = styleString.indexOf('px');
|
||||
|
||||
return Number(styleString.slice(0, index));
|
||||
},
|
||||
handleTreeResize() {
|
||||
this.calculateHeights();
|
||||
},
|
||||
isItemLoading(path) {
|
||||
return this.treeItemLoading[path] instanceof AbortController;
|
||||
},
|
||||
isTreeItemOpen(item) {
|
||||
return this.isTreeItemPathOpen(item.navigationPath);
|
||||
},
|
||||
isTreeItemPathOpen(path) {
|
||||
return this.openTreeItems.includes(path);
|
||||
},
|
||||
async loadAndBuildTreeItemsFor(domainObject, parentObjectPath, abortSignal) {
|
||||
let collection = this.openmct.composition.get(domainObject);
|
||||
let composition = await collection.load(abortSignal);
|
||||
|
||||
if (SORT_MY_ITEMS_ALPH_ASC && this.isSortable(parentObjectPath)) {
|
||||
const sortedComposition = composition.slice().sort(this.sortNameAscending);
|
||||
composition = sortedComposition;
|
||||
}
|
||||
|
||||
if (parentObjectPath.length) {
|
||||
let navigationPath = this.buildNavigationPath(parentObjectPath);
|
||||
|
||||
if (this.compositionCollections[navigationPath]) {
|
||||
this.removeCompositionListenerFor(navigationPath);
|
||||
}
|
||||
|
||||
this.compositionCollections[navigationPath] = {};
|
||||
this.compositionCollections[navigationPath].collection = collection;
|
||||
this.compositionCollections[navigationPath].addHandler = this.compositionAddHandler(navigationPath);
|
||||
this.compositionCollections[navigationPath].removeHandler = this.compositionRemoveHandler(navigationPath);
|
||||
|
||||
this.compositionCollections[navigationPath].collection.on('add',
|
||||
this.compositionCollections[navigationPath].addHandler);
|
||||
this.compositionCollections[navigationPath].collection.on('remove',
|
||||
this.compositionCollections[navigationPath].removeHandler);
|
||||
}
|
||||
|
||||
return composition.map((object) => {
|
||||
this.addTreeItemObserver(object, parentObjectPath);
|
||||
|
||||
return this.buildTreeItem(object, parentObjectPath);
|
||||
});
|
||||
},
|
||||
async openTreeItem(parentItem) {
|
||||
let parentPath = parentItem.navigationPath;
|
||||
|
||||
this.startItemLoad(parentPath);
|
||||
// pass in abort signal when functional
|
||||
let childrenItems = await this.loadAndBuildTreeItemsFor(parentItem.object, parentItem.objectPath);
|
||||
let parentIndex = this.treeItems.indexOf(parentItem);
|
||||
|
||||
// if it's not loading, it was aborted
|
||||
if (!this.isItemLoading(parentPath) || parentIndex === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.endItemLoad(parentPath);
|
||||
|
||||
this.treeItems.splice(parentIndex + 1, 0, ...childrenItems);
|
||||
|
||||
if (!this.isTreeItemOpen(parentItem)) {
|
||||
this.openTreeItems.push(parentPath);
|
||||
}
|
||||
|
||||
for (let item of childrenItems) {
|
||||
if (this.isTreeItemOpen(item)) {
|
||||
this.openTreeItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
},
|
||||
// returns an AbortController signal to be passed on to requests
|
||||
startItemLoad(path) {
|
||||
if (this.isItemLoading(path)) {
|
||||
this.abortItemLoad(path);
|
||||
}
|
||||
|
||||
this.$set(this.treeItemLoading, path, new AbortController());
|
||||
|
||||
return this.treeItemLoading[path].signal;
|
||||
},
|
||||
treeItemAction(parentItem, type) {
|
||||
if (type === 'close') {
|
||||
this.closeTreeItem(parentItem);
|
||||
} else {
|
||||
this.openTreeItem(parentItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -227,7 +227,7 @@ class ApplicationRouter extends EventEmitter {
|
||||
|
||||
this.started = true;
|
||||
|
||||
this.locationBar.onChange(p => this.hashChaged(p));
|
||||
this.locationBar.onChange(p => this.hashChanged(p));
|
||||
this.locationBar.start({
|
||||
root: location.pathname
|
||||
});
|
||||
@@ -390,9 +390,9 @@ class ApplicationRouter extends EventEmitter {
|
||||
*
|
||||
* @param {string} hash new hash for url
|
||||
*/
|
||||
hashChaged(hash) {
|
||||
this.emit('change:hash', hash);
|
||||
hashChanged(hash) {
|
||||
this.handleLocationChange(hash);
|
||||
this.emit('change:hash', hash);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user