diff --git a/src/ui/layout/mct-tree.scss b/src/ui/layout/mct-tree.scss index b632d79705..592ec592c9 100644 --- a/src/ui/layout/mct-tree.scss +++ b/src/ui/layout/mct-tree.scss @@ -26,6 +26,10 @@ overflow: hidden; transition: all; + .c-tree__item-h { + width: 100%; + } + &__scrollable-children { overflow: auto; } diff --git a/src/ui/layout/mct-tree.vue b/src/ui/layout/mct-tree.vue index 5ad05f5242..3ff04ace5e 100644 --- a/src/ui/layout/mct-tree.vue +++ b/src/ui/layout/mct-tree.vue @@ -1,9 +1,10 @@ @@ -108,15 +133,12 @@ import _ from 'lodash'; import treeItem from './tree-item.vue'; import search from '../components/search.vue'; import objectUtils from 'objectUtils'; +import uuid from 'uuid'; const LOCAL_STORAGE_KEY__TREE_EXPANDED__OLD = 'mct-tree-expanded'; const LOCAL_STORAGE_KEY__EXPANDED_TREE_NODE = 'mct-expanded-tree-node'; -const ROOT_PATH = '/browse/'; +const ROOT_PATH = 'browse'; const ITEM_BUFFER = 5; -const RECHECK_DELAY = 100; -const RESIZE_FIRE_DELAY_MS = 500; -let windowResizeId = undefined; -let windowResizing = false; export default { inject: ['openmct'], @@ -132,59 +154,54 @@ export default { } }, data() { - let isMobile = this.openmct.$injector.get('agentService'); - return { + root: undefined, isLoading: false, + mainTreeHeight: undefined, searchLoading: false, searchValue: '', - allTreeItems: [], + childItems: [], searchResultItems: [], visibleItems: [], ancestors: [], + tempAncestors: [], childrenSlideClass: 'down', - availableContainerHeight: 0, updatingView: false, - itemHeightCalculated: false, - itemHeight: 28, + itemHeight: 27, itemOffset: 0, - scrollable: undefined, activeSearch: false, - shouldEmitHeight: false, - isMobile: isMobile.mobileName, multipleRootChildren: false, - noVisibleItems: false, observedAncestors: {}, - mainTreeTopMargin: undefined, - syncingNavigation: false + mainTreeTopMargin: undefined }; }, computed: { - currentNavigatedPath() { - let ancestorsCopy = [...this.ancestors]; - if (this.multipleRootChildren) { - ancestorsCopy.shift(); // remove root - } - - return ancestorsCopy + currentTreePath() { + let path = [...this.ancestors] .map((ancestor) => ancestor.id) .join('/'); - }, - currentObjectPath() { - let ancestorsCopy = [...this.ancestors]; - return ancestorsCopy - .reverse() - .map((ancestor) => ancestor.object); + // if not multiRootChildren, then need to add in root ('browse') + if (!this.multipleRootChildren) { + path = ROOT_PATH + '/' + path; + // if multiRootChldren, need to replace root id with 'browse' + } else { + path = path.replace('ROOT', ROOT_PATH); + } + + return path; }, focusedItems() { - return this.activeSearch ? this.searchResultItems : this.allTreeItems; + return this.activeSearch ? this.searchResultItems : this.childItems; + }, + focusedAncestors() { + return this.isLoading ? this.tempAncestors : this.ancestors; }, itemLeftOffset() { return this.activeSearch ? '0px' : this.ancestors.length * 10 + 'px'; }, indicatorLeftOffset() { - let offset = ((this.ancestors.length + 1) * 10); + let offset = ((this.focusedAncestors.length + 1) * 10); return { paddingLeft: offset + 'px' @@ -204,62 +221,48 @@ export default { let childrenCount = this.focusedItems.length || 1; return (this.itemHeight * childrenCount) - this.mainTreeTopMargin; // 5px margin + }, + availableContainerHeight() { + return this.mainTreeHeight - this.ancestorsHeight; + }, + showNoItems() { + return this.visibleItems.length === 0 && !this.activeSearch; + }, + showNoSearchResults() { + return this.searchValue && this.searchResultItems.length === 0 && !this.searchLoading; + }, + showChildContainer() { + return !this.isLoading && !this.searchLoading; } }, watch: { syncTreeNavigation() { - this.isLoading = true; - const AND_SAVE_PATH = true; - this.syncingNavigation = true; - let currentLocationPath = this.openmct.router.currentLocation.path; - let hasParent = this.currentlyViewedObjectParentPath() || (this.multipleRootChildren && !this.currentlyViewedObjectParentPath()); - // if there's a current location path, - // if there's a parent of the currently viewed object - // and we're not in the parent of the currently viewed object - let jumpAndScroll = currentLocationPath - && hasParent - && !this.currentPathIsActivePath(); - // if the object being viewed is in the current path - let justScroll = this.currentPathIsActivePath(); + this.searchValue = ''; - if (this.searchValue) { - this.searchValue = ''; + if (!this.openmct.router.path) { + return; } - if (jumpAndScroll) { - this.allTreeItems = []; - this.scrollTo = this.currentlyViewedObjectId(); - this.jumpPath = this.currentlyViewedObjectParentPath(); - - if (this.multipleRootChildren) { - if (!this.jumpPath) { - this.jumpPath = 'ROOT'; - this.ancestors = []; - } else { - this.ancestors = [this.ancestors[0]]; - } - } else { - this.ancestors = []; - } - - this.jumpToPath(AND_SAVE_PATH); - } else if (justScroll) { - this.scrollTo = this.currentlyViewedObjectId(); + if (this.currentPathIsActivePath() && !this.isLoading) { + this.skipScroll = false; this.autoScroll(); - this.isLoading = false; + + return; } + + let routePath = [...this.openmct.router.path]; + routePath.shift(); // remove the child, so it navigates to the parent + routePath.push(this.root); // adding root + this.beginNavigationRequest('jumpTo', [...routePath].reverse()); }, searchValue() { if (this.searchValue !== '' && !this.activeSearch) { - this.searchActivated(); + this.activeSearch = true; + this.$refs.scrollable.scrollTop = 0; + this.skipScroll = true; } else if (this.searchValue === '') { - this.searchDeactivated(); - } - }, - allTreeItems() { - // catches an edge case when new items are added (ex. folder) - if (!this.isLoading) { - this.setContainerHeight(); + this.activeSearch = false; + this.skipScroll = false; } }, ancestors() { @@ -273,58 +276,28 @@ export default { } }, async mounted() { + this.initialize(); - // only reliable way to get final tree top margin - this.readyStateCheck(); + let savedPath = this.getStoredTreePath(); + let rootComposition = await this.loadRoot(); - this.backwardsCompatibilityCheck(); - - let savedPath = this.getSavedNavigatedPath(); - this.searchService = this.openmct.$injector.get('searchService'); - window.addEventListener('resize', this.handleWindowResize); - this.isLoading = true; - let root = await this.openmct.objects.get('ROOT'); - - if (root.identifier !== undefined) { - let rootNode = this.buildTreeItem(root); - let rootCompositionCollection = this.openmct.composition.get(root); - let rootComposition = await rootCompositionCollection.load(); - - // if more than one root item, set multipleRootChildren to true and add root to ancestors - if (rootComposition && rootComposition.length > 1) { - this.ancestors.push(rootNode); - if (!this.itemHeightCalculated) { - await this.calculateItemHeight(); - } - - this.multipleRootChildren = true; - } else if (!savedPath && rootComposition[0] !== undefined) { - // needed if saved path is not set, need to set it to the only root child - savedPath = rootComposition[0].identifier; - } - - if (savedPath) { - let scrollIfApplicable = () => { - if (this.currentPathIsActivePath()) { - this.scrollTo = this.currentlyViewedObjectId(); - } - }; - - this.jumpPath = savedPath; - this.afterJump = scrollIfApplicable; - } - - this.getAllChildren(rootNode); + if (!rootComposition) { + return; } + + if (!savedPath) { + savedPath = ROOT_PATH; + if (!this.multipleRootChildren && rootComposition[0]) { + let id = this.openmct.objects.makeKeyString(rootComposition[0].identifier); + savedPath += '/' + id; + } + } + + this.beginNavigationRequest('jumpTo', savedPath); }, created() { this.getSearchResults = _.debounce(this.getSearchResults, 400); - this.setContainerHeight = _.debounce(this.setContainerHeight, 200); - }, - updated() { - this.$nextTick(() => { - this.setContainerHeight(); - }); + this.handleWindowResize = _.debounce(this.handleWindowResize, 500); }, destroyed() { window.removeEventListener('resize', this.handleWindowResize); @@ -332,18 +305,225 @@ export default { document.removeEventListener('readystatechange', this.setTreeTopMargin); }, methods: { + initialize() { + this.searchService = this.openmct.$injector.get('searchService'); + window.addEventListener('resize', this.handleWindowResize); + this.readyStateCheck(); + this.backwardsCompatibilityCheck(); + }, readyStateCheck() { if (document.readyState !== 'complete') { - document.addEventListener('readystatechange', this.setTreeTopMargin); + document.addEventListener('readystatechange', this.onReadyState); } else { - this.setTreeTopMargin(); + this.onReadyState(); } }, - setTreeTopMargin() { + onReadyState() { if (document.readyState === 'complete') { - this.mainTreeTopMargin = this.getElementStyleValue(this.$refs.mainTree, 'marginTop'); + this.calculateHeights(); } }, + backwardsCompatibilityCheck() { + let oldTreeExpanded = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEY__TREE_EXPANDED__OLD)); + + if (oldTreeExpanded) { + localStorage.removeItem(LOCAL_STORAGE_KEY__TREE_EXPANDED__OLD); + } + + let newTreeExpanded = this.getStoredTreePath(); + + if (newTreeExpanded) { + // see if it's in a deprecated format + if (newTreeExpanded.indexOf('mine') === 0) { + localStorage.setItem(LOCAL_STORAGE_KEY__EXPANDED_TREE_NODE, JSON.stringify('browse/' + newTreeExpanded)); + } + } + }, + async loadRoot() { + this.root = await this.openmct.objects.get('ROOT'); + + if (!this.root.identifier) { + return false; + } + + let rootCompositionCollection = this.openmct.composition.get(this.root); + let rootComposition = await rootCompositionCollection.load(); + + if (rootComposition.length > 1) { + this.multipleRootChildren = true; + } + + return rootComposition; + }, + async beginNavigationRequest(type, request) { + let requestId = uuid(); + + this.isLoading = true; + this.latestNavigationRequest = requestId; + + if (['handleReset', 'handleExpanded'].indexOf(type) !== -1) { + this.skipScroll = true; + } else { + this.skipScroll = false; + } + + let success = await this[type](request, requestId); + + if (success && this.isLatestNavigationRequest(requestId)) { + this.isLoading = false; + this.storeCurrentTreePath(); + } + }, + isLatestNavigationRequest(requestId) { + return this.latestNavigationRequest === requestId; + }, + // will jump to an object path or a string path + async jumpTo(path, requestId) { + if (!this.isLatestNavigationRequest(requestId)) { + return false; + } + + // if path string, build object string + if (typeof path === 'string') { + path = await this.buildObjectPathFromString(path, requestId); + + if (!path) { + return false; + } + } + + // build ancestors tree items from objects + this.tempAncestors = []; + + for (let i = 0; i < path.length; i++) { + let builtAncestor = this.buildTreeItem(path[i], path.slice(0, i)); + this.tempAncestors.push(builtAncestor); + } + + // load children for last ancestor + let childrenItems = await this.getChildrenAsTreeItems(this.tempAncestors[this.tempAncestors.length - 1], path, requestId); + + // if all is good, return true for successful navigation + return this.updateTree(this.tempAncestors, childrenItems, requestId); + }, + async handleReset(node, requestId) { + if (!this.isLatestNavigationRequest(requestId)) { + return false; + } + + this.childrenSlideClass = 'up'; + + this.tempAncestors = [...this.ancestors]; + this.tempAncestors.splice(this.tempAncestors.indexOf(node) + 1); + + let objectPath = this.ancestorsAsObjects(); + objectPath.splice(objectPath.indexOf(node.object) + 1); + + let childrenItems = await this.getChildrenAsTreeItems(node, objectPath, requestId); + + // if all is good, return true for successful navigation + return this.updateTree(this.tempAncestors, childrenItems, requestId); + }, + async handleExpanded(node, requestId) { + if (!this.isLatestNavigationRequest(requestId)) { + return false; + } + + this.childrenSlideClass = 'down'; + + this.tempAncestors = [...this.ancestors]; + this.tempAncestors.push(node); + + let objectPath = this.ancestorsAsObjects().concat(node.object); + + let childrenItems = await this.getChildrenAsTreeItems(node, objectPath, requestId); + + // if all is good, return true for successful navigation + return this.updateTree(this.tempAncestors, childrenItems, requestId); + + }, + ancestorsAsObjects() { + let ancestorsCopy = [...this.ancestors]; + + if (this.multipleRootChildren && ancestorsCopy[0].id === 'ROOT') { + // no root for object paths + ancestorsCopy.shift(); + } + + return ancestorsCopy.map(item => item.object); + }, + async buildObjectPathFromString(path, requestId) { + let pathNodes = path.split('/'); + let objectPath = [this.root]; + + // skip first element, it's root "browse" path, handled above + for (let i = 1; i < pathNodes.length; i++) { + let domainObject = await this.openmct.objects.get(pathNodes[i]); + objectPath.push(domainObject); + + if (!this.isLatestNavigationRequest(requestId)) { + return false; + } + } + + return objectPath; + }, + async getChildrenAsTreeItems(item, parentObjectPath, requestId) { + if (!this.isLatestNavigationRequest(requestId)) { + return false; + } + + if (this.composition) { + this.composition.off('add', this.addChild); + this.composition.off('remove', this.removeChild); + delete this.composition; + } + + this.childItems = []; + let tempComposition = this.openmct.composition.get(item.object); + let children = await tempComposition.load(); + + if (!this.isLatestNavigationRequest(requestId)) { + return false; + } + + this.composition = tempComposition; + + return children.map((child) => { + return this.buildTreeItem(child, parentObjectPath); + }); + }, + async updateTree(ancestors, children, requestId) { + if (!this.isLatestNavigationRequest(requestId)) { + return false; + } + + // show or don't show root + if (!this.multipleRootChildren && ancestors[0].id === 'ROOT') { + ancestors.shift(); + } + + await this.clearVisibleItems(); + + this.ancestors = ancestors; + this.childItems = children; + + // any new items added or removed handled here + this.composition.on('add', this.addChild); + this.composition.on('remove', this.removeChild); + + return true; + }, + // domainObject: from composition add event + addChild(domainObject) { + let item = this.buildTreeItem(domainObject, this.ancestorsAsObjects()); + this.childItems.push(item); + }, + removeChild(identifier) { + let removeId = this.openmct.objects.makeKeyString(identifier); + this.childItems = this.childItems + .filter(c => c.id !== removeId); + }, updateVisibleItems() { if (this.updatingView) { return; @@ -377,21 +557,10 @@ export default { this.itemOffset = start; this.visibleItems = this.focusedItems.slice(start, end); - this.noVisibleItems = false; this.updatingView = false; }); }, - setContainerHeight() { - let mainTree = this.$refs.mainTree; - let mainTreeHeight = mainTree && mainTree.clientHeight ? mainTree.clientHeight : 0; - - if (mainTreeHeight !== 0) { - this.availableContainerHeight = mainTreeHeight - this.ancestorsHeight; - } else { - window.setTimeout(this.setContainerHeight, RECHECK_DELAY); - } - }, calculateFirstVisibleItem() { if (!this.$refs.scrollable) { return; @@ -410,92 +579,59 @@ export default { return Math.ceil(scrollBottom / this.itemHeight); }, - calculateItemHeight() { - this.shouldEmitHeight = true; - - return new Promise((resolve, reject) => { - this.itemHeightResolve = resolve; - }); - }, - async setItemHeight(height) { - - if (this.itemHeightCalculated) { - return; - } - - await this.$nextTick(); - - this.itemHeight = height; - this.itemHeightCalculated = true; - this.shouldEmitHeight = false; - - this.itemHeightResolve(); + scrollItems(event) { + this.updateVisibleItems(); }, handleWindowResize() { - if (!windowResizing) { - windowResizing = true; - window.clearTimeout(windowResizeId); - windowResizeId = window.setTimeout(() => { - this.setContainerHeight(); - windowResizing = false; - }, RESIZE_FIRE_DELAY_MS); - } + this.calculateHeights(); }, - async getAllChildren(node, after) { - let currentNavigationRequest = this.openmct.objects.makeKeyString(node.object.identifier); - this.latestNavigationRequest = currentNavigationRequest; + // domainObject: we're building a tree item for this + // objects: domainObject array up to parent of domainObject, excluding domainObject for item being built + buildTreeItem(domainObject, objects) { + let objectPath = [...objects]; - await this.clearVisibleItems(); - - if (this.composition) { - this.composition.off('add', this.addChild); - this.composition.off('remove', this.removeChild); - delete this.composition; + // remove root (if present) for object path + if (objectPath[0] && objectPath[0].type === 'root') { + objectPath.shift(); } - this.allTreeItems = []; - this.composition = this.openmct.composition.get(node.object); - this.composition.on('add', this.addChild); - this.composition.on('remove', this.removeChild); - - if (currentNavigationRequest === this.latestNavigationRequest) { - if (after && typeof after === 'function') { - after(); - } - - await this.composition.load(); - this.finishLoading(); - } - }, - buildTreeItem(domainObject, path, objectPath) { - let currentPath = path || this.currentNavigatedPath; - let currentObjectPath = objectPath || this.currentObjectPath; - - let navToParent = ROOT_PATH + currentPath; - if (navToParent === ROOT_PATH) { - navToParent = navToParent.slice(0, -1); - } + let navigationPath = this.buildNavigationPath(domainObject, objectPath); + let builtObjectPath = [...objectPath].reverse(); return { id: this.openmct.objects.makeKeyString(domainObject.identifier), object: domainObject, - objectPath: [domainObject].concat(currentObjectPath), - navigateToParent: navToParent + objectPath: [domainObject].concat(builtObjectPath), + navigationPath }; }, + // domainObject: the item we're building the path for (will be used in url and links) + // objects: array of domainObjects representing path to domainobject passed in + buildNavigationPath(domainObject, objects) { + let path = [...objects] + .map(object => this.openmct.objects.makeKeyString(object.identifier)); + + return '/' + [ + ROOT_PATH, + ...path, + this.openmct.objects.makeKeyString(domainObject.identifier) + ].join('/'); + }, observeAncestors() { let observedAncestorIds = Object.keys(this.observedAncestors); // observe any ancestors, not currently being observed - this.ancestors.forEach((ancestor) => { - let ancestorObject = ancestor.object; - let ancestorKeyString = this.openmct.objects.makeKeyString(ancestorObject.identifier); - let index = observedAncestorIds.indexOf(ancestorKeyString); + this.ancestors.forEach((ancestor, index) => { + // skip last ancestor as it's children are currently being watched + if (index !== this.ancestors.length - 1) { + let ancestorKeyString = this.openmct.objects.makeKeyString(ancestor.object.identifier); + let ancestorIndex = observedAncestorIds.indexOf(ancestorKeyString); - if (index !== -1) { // currently observed - observedAncestorIds.splice(index, 1); // remove all active ancestors from id tracking - } else { // not observed, observe it - this.observeAncestor(ancestorKeyString, ancestorObject); + if (ancestorIndex !== -1) { // currently observed + observedAncestorIds.splice(ancestorIndex, 1); // remove all active ancestors from id tracking + } else { // not observed, observe it + this.observeAncestor(ancestorKeyString, ancestor); + } } }); @@ -504,121 +640,63 @@ export default { }, stopObservingAncestors(ids = Object.keys(this.observedAncestors)) { ids.forEach((id) => { - this.observedAncestors[id](); + this.observedAncestors[id].composition.off('add', this.observedAncestors[id].addChild); + this.observedAncestors[id].composition.off('remove', this.observedAncestors[id].removeChild); + this.observedAncestors[id].removeChild = undefined; + this.observedAncestors[id].addChild = undefined; + this.observedAncestors[id].composition = undefined; + + // remove tracking for this id this.observedAncestors[id] = undefined; delete this.observedAncestors[id]; }); }, - observeAncestor(id, object) { - this.observedAncestors[id] = this.openmct.objects.observe(object, 'location', - (location) => { - let ancestorObjects = this.ancestors.map(ancestor => ancestor.object); - // ancestor has been removed from tree, reset to it's parent - if (location === null) { - let index = ancestorObjects.indexOf(object); - let parentIndex = index - 1; - if (this.ancestors[parentIndex]) { - this.handleReset(this.ancestors[parentIndex]); - } - } + async observeAncestor(id, ancestorNode) { + this.observedAncestors[id] = {}; + + this.observedAncestors[id].composition = this.openmct.composition.get(ancestorNode.object); + await this.observedAncestors[id].composition.load(); + this.observedAncestors[id].addChild = this.ancestorAdd(ancestorNode); + this.observedAncestors[id].removeChild = this.ancestorRemove(ancestorNode); + this.observedAncestors[id].composition.on('add', this.observedAncestors[id].addChild); + this.observedAncestors[id].composition.on('remove', this.observedAncestors[id].removeChild); + // } + }, + ancestorAdd(ancestor) { + return (node) => { + // no use case for this as of yet since ancestors do not show siblings + // and the main ancestor being viewed currently has it's composition watched elsewhere + }; + }, + ancestorRemove(ancestorNode) { + return (identifier) => { + // check if this item is showing in the tree currently + let index = this.ancestors.findIndex(treeAncestor => { + let treeAncestorIdentifier = this.openmct.objects.makeKeyString(treeAncestor.object.identifier); + let removedItemIdentifier = this.openmct.objects.makeKeyString(identifier); + + return treeAncestorIdentifier === removedItemIdentifier; }); - }, - buildNavigationPath(objects) { - let objectsCopy = [...objects]; - if (this.multipleRootChildren) { - objectsCopy.shift(); // remove root - } - return objectsCopy - .map((object) => object.id) - .join('/'); - }, - buildObjectPath(objects) { - let objectsCopy = [...objects]; - - return objectsCopy - .reverse() - .map((object) => object.object); - }, - addChild(child) { - let item = this.buildTreeItem(child); - this.allTreeItems.push(item); - }, - removeChild(identifier) { - let removeId = this.openmct.objects.makeKeyString(identifier); - this.allTreeItems = this.allTreeItems - .filter(c => c.id !== removeId); - this.setContainerHeight(); - }, - finishLoading() { - if (this.jumpPath) { - this.jumpToPath(); - } - - this.autoScroll(); - this.isLoading = false; - this.syncingNavigation = false; - this.setContainerHeight(); - }, - async jumpToPath(saveExpandedPath = false) { - // switching back and forth between multiple root children can cause issues, - // this checks for one of those issues - if (this.jumpPath.key) { - this.jumpPath = this.jumpPath.key; - } - - let nodes = this.jumpPath.split('/'); - let tempAncestors = [...this.ancestors]; - let newParent; - - for (let i = 0; i < nodes.length; i++) { - let currentNode = await this.openmct.objects.get(nodes[i]); - let path = this.buildNavigationPath(tempAncestors); - let objectPath = this.buildObjectPath(tempAncestors); - newParent = this.buildTreeItem(currentNode, path, objectPath); - tempAncestors.push(newParent); - - if (!this.itemHeightCalculated) { - this.ancestors.push(newParent); // add for calculations - await this.calculateItemHeight(); - this.ancestors.pop(); // remove after + if (index !== -1) { + let ancestorIndex = this.ancestors.indexOf(ancestorNode); + this.beginNavigationRequest('handleReset', this.ancestors[ancestorIndex]); } - } - - this.jumpPath = ''; - this.getAllChildren(newParent, async () => { - this.ancestors = tempAncestors; // reset ancestors - if (this.afterJump) { - await this.$nextTick(); - this.afterJump(); - delete this.afterJump; - } - - if (saveExpandedPath) { - this.setCurrentNavigatedPath(); - } - }); + }; }, - async autoScroll() { - if (!this.scrollTo) { - return; - } - - if (this.$refs.scrollable) { - let indexOfScroll = this.indexOfItemById(this.scrollTo); + autoScroll() { + if (this.currentPathIsActivePath() && !this.skipScroll && this.$refs.scrollable) { + let indexOfScroll = this.indexOfItemById(this.currentlyViewedObjectId()); let scrollTopAmount = indexOfScroll * this.itemHeight; - - await this.$nextTick(); - - this.$refs.scrollable.scrollTop = scrollTopAmount; - this.scrollTo = undefined; - } else { - window.setTimeout(this.autoScroll, RECHECK_DELAY); + this.$refs.scrollable.scrollTo({ + top: scrollTopAmount, + behavior: 'smooth' + }); } }, indexOfItemById(id) { - for (let i = 0; i < this.allTreeItems.length; i++) { - if (this.allTreeItems[i].id === id) { + for (let i = 0; i < this.childItems.length; i++) { + if (this.childItems[i].id === id) { return i; } } @@ -630,22 +708,22 @@ export default { for (let i = 0; i < results.hits.length; i++) { let result = results.hits[i]; let newStyleObject = objectUtils.toNewFormat(result.object.getModel(), result.object.getId()); - let objectPath = []; - let navigateToParent; + let objectPath = await this.openmct.objects.getOriginalPath(newStyleObject.identifier); - objectPath = await this.openmct.objects.getOriginalPath(newStyleObject.identifier); - objectPath.pop(); // remove root + // removing the item itself, as the path we pass to buildTreeItem is a parent path + objectPath.shift(); - navigateToParent = objectPath.slice(1) - .map((parent) => this.openmct.objects.makeKeyString(parent.identifier)); - navigateToParent = ROOT_PATH + navigateToParent.reverse().join('/'); + // if root, remove, we're not using in object path for tree + let lastObject = objectPath.length ? objectPath[objectPath.length - 1] : false; + if (lastObject && lastObject.type === 'root') { + objectPath.pop(); + } - this.searchResultItems.push({ - id: this.openmct.objects.makeKeyString(newStyleObject.identifier), - object: newStyleObject, - objectPath, - navigateToParent - }); + // we reverse the objectPath in the tree, so have to do it here first, + // since this one is already in the correct direction + let resultObject = this.buildTreeItem(newStyleObject, objectPath.reverse()); + + this.searchResultItems.push(resultObject); } this.searchLoading = false; @@ -660,76 +738,40 @@ export default { this.searchLoading = false; } }, - searchActivated() { - this.activeSearch = true; - this.$refs.scrollable.scrollTop = 0; - }, - async searchDeactivated() { - this.activeSearch = false; - await this.$nextTick(); - this.$refs.scrollable.scrollTop = 0; - }, - handleReset(node) { - this.isLoading = true; - - this.getAllChildren(node, () => { - this.childrenSlideClass = 'up'; - this.ancestors.splice(this.ancestors.indexOf(node) + 1); - this.setCurrentNavigatedPath(); - }); - }, - handleExpanded(node) { - this.isLoading = true; - - let newParent = this.buildTreeItem(node); - - this.getAllChildren(newParent, () => { - this.childrenSlideClass = 'down'; - this.ancestors.push(newParent); - this.setCurrentNavigatedPath(); - }); - }, - getSavedNavigatedPath() { + getStoredTreePath() { return JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEY__EXPANDED_TREE_NODE)); }, - setCurrentNavigatedPath() { + storeCurrentTreePath() { if (!this.searchValue) { - localStorage.setItem(LOCAL_STORAGE_KEY__EXPANDED_TREE_NODE, JSON.stringify(this.currentNavigatedPath)); + localStorage.setItem(LOCAL_STORAGE_KEY__EXPANDED_TREE_NODE, JSON.stringify(this.currentTreePath)); } }, currentPathIsActivePath() { - return this.getSavedNavigatedPath() === this.currentlyViewedObjectParentPath(); + return this.getStoredTreePath() === this.currentlyViewedObjectParentPath(); }, currentlyViewedObjectId() { let currentPath = this.openmct.router.currentLocation.path; if (currentPath) { - currentPath = currentPath.split(ROOT_PATH)[1]; - return currentPath.split('/').pop(); } }, currentlyViewedObjectParentPath() { let currentPath = this.openmct.router.currentLocation.path; + if (currentPath) { - currentPath = currentPath.split(ROOT_PATH)[1]; currentPath = currentPath.split('/'); - currentPath.pop(); + currentPath.shift(); // remove empty array element from initial '/' + currentPath.pop(); // remove current child return currentPath.join('/'); } }, async clearVisibleItems() { - this.noVisibleItems = true; this.visibleItems = []; await this.$nextTick(); // prevents "ghost" image of visibleItems return; }, - scrollItems(event) { - if (!windowResizing) { - this.updateVisibleItems(); - } - }, childrenListStyles() { return { position: 'relative' }; }, @@ -744,12 +786,12 @@ export default { return Number(styleString.slice(0, index)); }, - backwardsCompatibilityCheck() { - let oldTreeExpanded = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEY__TREE_EXPANDED__OLD)); - - if (oldTreeExpanded) { - localStorage.removeItem(LOCAL_STORAGE_KEY__TREE_EXPANDED__OLD); - } + calculateHeights() { + this.mainTreeTopMargin = this.getElementStyleValue(this.$refs.mainTree, 'marginTop'); + this.mainTreeHeight = this.$el.offsetHeight + - this.$refs.search.offsetHeight + - this.mainTreeTopMargin; + this.itemHeight = this.getElementStyleValue(this.$refs.dummyItem, 'height'); } } }; diff --git a/src/ui/layout/tree-item.vue b/src/ui/layout/tree-item.vue index 75a83265c1..a05baee895 100644 --- a/src/ui/layout/tree-item.vue +++ b/src/ui/layout/tree-item.vue @@ -1,5 +1,5 @@