mirror of
https://github.com/nasa/openmct.git
synced 2025-04-07 19:34:25 +00:00
Merge 1.8.1 into master (#4562)
* Transaction fix (#4421) (#4461) * When transaction is active, objects.get should search in dirty object first. * Bugfix/create tree node (#4472) * Transaction fix (#4421) * When transaction is active, objects.get should search in dirty object first. * find insert location prior to adding item to tree * no need to resort * add item should only add to direct descendants * remove unused function * copy composition before sorting * remove unused var * remove master pollution * Revert "remove master pollution" * add item to correct location * Changed descending to ascending in sort order method (#4480) * adding RAF to display layout alphanumerics (#4486) * [Tabs] Sizing of offscreen tabs causing issues (#4444) * [LAD Tables] Use RAF for updating template (#4500) * Fixes LAD rows for string telemetry (#4508) * Fixes LAD rows for string telemetry * saving the object if it was missing (#4471) * 4328 - Maintain reference to a focusedImage if the bounds change (#4545) * WIP: adding assertions to catch negative index state * just testing the flow * SUpdate the image history index to previous selected image * Cleaning up spacing and log statements * Converted focusedImageIndex assignment to ternary and general cleanup * imported objects are not persisting (#4477) * imported objects are not persisting #4470 * disabled karma spec reporter suppressErrorSummary * update version number * Delete importFromJsonAction directory since it was empty
This commit is contained in:
parent
7c4258d720
commit
bba29b083f
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "openmct",
|
||||
"version": "1.8.1-SNAPSHOT",
|
||||
"version": "1.8.1",
|
||||
"description": "The Open MCT core platform",
|
||||
"devDependencies": {
|
||||
"@braintree/sanitize-url": "^5.0.2",
|
||||
|
@ -48,6 +48,7 @@ const CONTEXT_MENU_ACTIONS = [
|
||||
'viewHistoricalData',
|
||||
'remove'
|
||||
];
|
||||
const BLANK_VALUE = '---';
|
||||
|
||||
export default {
|
||||
inject: ['openmct', 'currentView'],
|
||||
@ -67,15 +68,43 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
datum: undefined,
|
||||
timestamp: undefined,
|
||||
value: '---',
|
||||
valueClass: '',
|
||||
timestampKey: undefined,
|
||||
unit: ''
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
value() {
|
||||
if (!this.datum) {
|
||||
return BLANK_VALUE;
|
||||
}
|
||||
|
||||
return this.formats[this.valueKey].format(this.datum);
|
||||
},
|
||||
valueClass() {
|
||||
if (!this.datum) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const limit = this.limitEvaluator.evaluate(this.datum, this.valueMetadata);
|
||||
|
||||
return limit ? limit.cssClass : '';
|
||||
|
||||
},
|
||||
formattedTimestamp() {
|
||||
return this.timestamp !== undefined ? this.getFormattedTimestamp(this.timestamp) : '---';
|
||||
if (!this.timestamp) {
|
||||
return BLANK_VALUE;
|
||||
}
|
||||
|
||||
return this.timeSystemFormat.format(this.timestamp);
|
||||
},
|
||||
timeSystemFormat() {
|
||||
if (!this.formats[this.timestampKey]) {
|
||||
console.warn(`No formatter for ${this.timestampKey} time system for ${this.domainObject.name}.`);
|
||||
}
|
||||
|
||||
return this.formats[this.timestampKey];
|
||||
},
|
||||
objectPath() {
|
||||
return [this.domainObject, ...this.pathToTable];
|
||||
@ -96,15 +125,19 @@ export default {
|
||||
|
||||
this.timestampKey = this.openmct.time.timeSystem().key;
|
||||
|
||||
this.valueMetadata = this.metadata ? this
|
||||
.metadata
|
||||
.valuesForHints(['range'])[0] : undefined;
|
||||
this.valueMetadata = undefined;
|
||||
|
||||
if (this.metadata) {
|
||||
this.valueMetadata = this
|
||||
.metadata
|
||||
.valuesForHints(['range'])[0] || this.firstNonDomainAttribute(this.metadata);
|
||||
}
|
||||
|
||||
this.valueKey = this.valueMetadata ? this.valueMetadata.key : undefined;
|
||||
|
||||
this.unsubscribe = this.openmct
|
||||
.telemetry
|
||||
.subscribe(this.domainObject, this.updateValues);
|
||||
.subscribe(this.domainObject, this.setLatestValues);
|
||||
|
||||
this.requestHistory();
|
||||
|
||||
@ -118,29 +151,29 @@ export default {
|
||||
this.openmct.time.off('bounds', this.updateBounds);
|
||||
},
|
||||
methods: {
|
||||
updateValues(datum) {
|
||||
let newTimestamp = this.getParsedTimestamp(datum);
|
||||
let limit;
|
||||
updateView() {
|
||||
if (!this.updatingView) {
|
||||
this.updatingView = true;
|
||||
requestAnimationFrame(() => {
|
||||
let newTimestamp = this.getParsedTimestamp(this.latestDatum);
|
||||
|
||||
if (this.shouldUpdate(newTimestamp)) {
|
||||
this.datum = datum;
|
||||
this.timestamp = newTimestamp;
|
||||
this.value = this.formats[this.valueKey].format(datum);
|
||||
limit = this.limitEvaluator.evaluate(datum, this.valueMetadata);
|
||||
if (limit) {
|
||||
this.valueClass = limit.cssClass;
|
||||
} else {
|
||||
this.valueClass = '';
|
||||
}
|
||||
if (this.shouldUpdate(newTimestamp)) {
|
||||
this.timestamp = newTimestamp;
|
||||
this.datum = this.latestDatum;
|
||||
}
|
||||
|
||||
this.updatingView = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
shouldUpdate(newTimestamp) {
|
||||
let newTimestampInBounds = this.inBounds(newTimestamp);
|
||||
let noExistingTimestamp = this.timestamp === undefined;
|
||||
let newTimestampIsLatest = newTimestamp > this.timestamp;
|
||||
setLatestValues(datum) {
|
||||
this.latestDatum = datum;
|
||||
|
||||
return newTimestampInBounds
|
||||
&& (noExistingTimestamp || newTimestampIsLatest);
|
||||
this.updateView();
|
||||
},
|
||||
shouldUpdate(newTimestamp) {
|
||||
return this.inBounds(newTimestamp)
|
||||
&& (this.timestamp === undefined || newTimestamp > this.timestamp);
|
||||
},
|
||||
requestHistory() {
|
||||
this.openmct
|
||||
@ -151,7 +184,7 @@ export default {
|
||||
size: 1,
|
||||
strategy: 'latest'
|
||||
})
|
||||
.then((array) => this.updateValues(array[array.length - 1]))
|
||||
.then((array) => this.setLatestValues(array[array.length - 1]))
|
||||
.catch((error) => {
|
||||
console.warn('Error fetching data', error);
|
||||
});
|
||||
@ -189,31 +222,21 @@ export default {
|
||||
}
|
||||
},
|
||||
resetValues() {
|
||||
this.value = '---';
|
||||
this.timestamp = undefined;
|
||||
this.valueClass = '';
|
||||
this.datum = undefined;
|
||||
},
|
||||
getParsedTimestamp(timestamp) {
|
||||
if (this.timeSystemFormat()) {
|
||||
return this.formats[this.timestampKey].parse(timestamp);
|
||||
}
|
||||
},
|
||||
getFormattedTimestamp(timestamp) {
|
||||
if (this.timeSystemFormat()) {
|
||||
return this.formats[this.timestampKey].format(timestamp);
|
||||
}
|
||||
},
|
||||
timeSystemFormat() {
|
||||
if (this.formats[this.timestampKey]) {
|
||||
return true;
|
||||
} else {
|
||||
console.warn(`No formatter for ${this.timestampKey} time system for ${this.domainObject.name}.`);
|
||||
|
||||
return false;
|
||||
if (this.timeSystemFormat) {
|
||||
return this.timeSystemFormat.parse(timestamp);
|
||||
}
|
||||
},
|
||||
setUnit() {
|
||||
this.unit = this.valueMetadata.unit || '';
|
||||
},
|
||||
firstNonDomainAttribute(metadata) {
|
||||
return metadata
|
||||
.values()
|
||||
.find(metadatum => metadatum.hints.domain === undefined && metadatum.key !== 'name');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -26,6 +26,7 @@ import {
|
||||
getMockObjects,
|
||||
getMockTelemetry,
|
||||
getLatestTelemetry,
|
||||
spyOnBuiltins,
|
||||
resetApplicationState
|
||||
} from 'utils/testing';
|
||||
|
||||
@ -160,6 +161,11 @@ describe("The LAD Table", () => {
|
||||
anotherTelemetryObjectResolve = resolve;
|
||||
});
|
||||
|
||||
spyOnBuiltins(['requestAnimationFrame']);
|
||||
window.requestAnimationFrame.and.callFake((callBack) => {
|
||||
callBack();
|
||||
});
|
||||
|
||||
openmct.telemetry.request.and.callFake(() => {
|
||||
telemetryRequestResolve(mockTelemetry);
|
||||
|
||||
|
@ -263,7 +263,8 @@ export default {
|
||||
this.openmct.telemetry.request(this.domainObject, options)
|
||||
.then(data => {
|
||||
if (data.length > 0) {
|
||||
this.updateView(data[data.length - 1]);
|
||||
this.latestDatum = data[data.length - 1];
|
||||
this.updateView();
|
||||
}
|
||||
});
|
||||
},
|
||||
@ -275,12 +276,19 @@ export default {
|
||||
|| (datumTimeStamp
|
||||
&& (this.openmct.time.bounds().end >= datumTimeStamp))
|
||||
) {
|
||||
this.updateView(datum);
|
||||
this.latestDatum = datum;
|
||||
this.updateView();
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
updateView(datum) {
|
||||
this.datum = datum;
|
||||
updateView() {
|
||||
if (!this.updatingView) {
|
||||
this.updatingView = true;
|
||||
requestAnimationFrame(() => {
|
||||
this.datum = this.latestDatum;
|
||||
this.updatingView = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
removeSubscription() {
|
||||
if (this.subscription) {
|
||||
@ -290,7 +298,8 @@ export default {
|
||||
},
|
||||
refreshData(bounds, isTick) {
|
||||
if (!isTick) {
|
||||
this.datum = undefined;
|
||||
this.latestDatum = undefined;
|
||||
this.updateView();
|
||||
this.requestHistoricalData(this.domainObject);
|
||||
}
|
||||
},
|
||||
|
@ -414,7 +414,7 @@ export default {
|
||||
if (this.indexForFocusedImage !== undefined) {
|
||||
imageIndex = this.initFocusedImageIndex;
|
||||
} else {
|
||||
imageIndex = newSize - 1;
|
||||
imageIndex = newSize > 0 ? newSize - 1 : undefined;
|
||||
}
|
||||
|
||||
this.setFocusedImage(imageIndex, false);
|
||||
@ -510,6 +510,12 @@ export default {
|
||||
this.timeContext.off("timeContext", this.setTimeContext);
|
||||
}
|
||||
},
|
||||
boundsChange(bounds, isTick) {
|
||||
if (!isTick) {
|
||||
this.previousFocusedImage = this.focusedImage ? JSON.parse(JSON.stringify(this.focusedImage)) : undefined;
|
||||
this.requestHistory();
|
||||
}
|
||||
},
|
||||
expand() {
|
||||
const actionCollection = this.openmct.actions.getActionsCollection(this.objectPath, this.currentView);
|
||||
const visibleActions = actionCollection.getVisibleActions();
|
||||
@ -670,23 +676,47 @@ export default {
|
||||
this.$refs.thumbsWrapper.scrollLeft = scrollWidth;
|
||||
});
|
||||
},
|
||||
matchIndexOfPreviousImage(previous, imageHistory) {
|
||||
// match logic uses a composite of url and time to account
|
||||
// for example imagery not having fully unique urls
|
||||
return imageHistory.findIndex((x) => (
|
||||
x.url === previous.url
|
||||
&& x.time === previous.time
|
||||
));
|
||||
},
|
||||
setFocusedImage(index, thumbnailClick = false) {
|
||||
let focusedIndex = index;
|
||||
if (!(Number.isInteger(index) && index > -1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.previousFocusedImage) {
|
||||
// determine if the previous image exists in the new bounds of imageHistory
|
||||
const matchIndex = this.matchIndexOfPreviousImage(
|
||||
this.previousFocusedImage,
|
||||
this.imageHistory
|
||||
);
|
||||
focusedIndex = matchIndex > -1 ? matchIndex : this.imageHistory.length - 1;
|
||||
|
||||
delete this.previousFocusedImage;
|
||||
}
|
||||
|
||||
if (thumbnailClick) {
|
||||
//We use the props till the user changes what they want to see
|
||||
this.initFocusedImageIndex = undefined;
|
||||
}
|
||||
|
||||
if (this.isPaused && !thumbnailClick && this.initFocusedImageIndex === undefined) {
|
||||
this.nextImageIndex = index;
|
||||
this.nextImageIndex = focusedIndex;
|
||||
//this could happen if bounds changes
|
||||
if (this.focusedImageIndex > this.imageHistory.length - 1) {
|
||||
this.focusedImageIndex = index;
|
||||
this.focusedImageIndex = focusedIndex;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.focusedImageIndex = index;
|
||||
this.focusedImageIndex = focusedIndex;
|
||||
|
||||
if (thumbnailClick && !this.isPaused) {
|
||||
this.paused(true);
|
||||
|
@ -31,8 +31,6 @@
|
||||
flex-direction: column;
|
||||
|
||||
&--hidden {
|
||||
height: 1000px;
|
||||
width: 1000px;
|
||||
position: absolute;
|
||||
left: -9999px;
|
||||
top: -9999px;
|
||||
|
@ -1,6 +1,10 @@
|
||||
<template>
|
||||
<div class="c-tabs-view">
|
||||
<div
|
||||
ref="tabs"
|
||||
class="c-tabs-view"
|
||||
>
|
||||
<div
|
||||
ref="tabsHolder"
|
||||
class="c-tabs-view__tabs-holder c-tabs"
|
||||
:class="{
|
||||
'is-dragging': isDragging && allowEditing,
|
||||
@ -28,8 +32,10 @@
|
||||
}"
|
||||
@click="showTab(tab, index)"
|
||||
>
|
||||
<div class="c-tabs-view__tab__label c-object-label"
|
||||
:class="[tab.status ? `is-status--${tab.status}` : '']"
|
||||
<div
|
||||
ref="tabsLabel"
|
||||
class="c-tabs-view__tab__label c-object-label"
|
||||
:class="[tab.status ? `is-status--${tab.status}` : '']"
|
||||
>
|
||||
<div class="c-object-label__type-icon"
|
||||
:class="tab.type.definition.cssClass"
|
||||
@ -49,11 +55,12 @@
|
||||
<div
|
||||
v-for="tab in tabsList"
|
||||
:key="tab.keyString"
|
||||
:style="getTabStyles(tab)"
|
||||
class="c-tabs-view__object-holder"
|
||||
:class="{'c-tabs-view__object-holder--hidden': !isCurrent(tab)}"
|
||||
>
|
||||
<object-view
|
||||
v-if="isTabLoaded(tab)"
|
||||
v-if="shouldLoadTab(tab)"
|
||||
class="c-tabs-view__object"
|
||||
:default-object="tab.domainObject"
|
||||
:object-path="tab.objectPath"
|
||||
@ -65,6 +72,7 @@
|
||||
<script>
|
||||
import ObjectView from '../../../ui/components/ObjectView.vue';
|
||||
import RemoveAction from '../../remove/RemoveAction.js';
|
||||
import _ from 'lodash';
|
||||
|
||||
const unknownObjectType = {
|
||||
definition: {
|
||||
@ -88,6 +96,8 @@ export default {
|
||||
let keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
||||
|
||||
return {
|
||||
tabWidth: undefined,
|
||||
tabHeight: undefined,
|
||||
internalDomainObject: this.domainObject,
|
||||
currentTab: {},
|
||||
currentTabIndex: undefined,
|
||||
@ -122,6 +132,10 @@ export default {
|
||||
});
|
||||
}
|
||||
|
||||
this.handleWindowResize = _.debounce(this.handleWindowResize, 500);
|
||||
this.tabsViewResizeObserver = new ResizeObserver(this.handleWindowResize);
|
||||
this.tabsViewResizeObserver.observe(this.$refs.tabs);
|
||||
|
||||
this.unsubscribe = this.openmct.objects.observe(this.internalDomainObject, '*', this.updateInternalDomainObject);
|
||||
|
||||
this.openmct.router.on('change:params', this.updateCurrentTab.bind(this));
|
||||
@ -138,6 +152,8 @@ export default {
|
||||
this.composition.off('remove', this.removeItem);
|
||||
this.composition.off('reorder', this.onReorder);
|
||||
|
||||
this.tabsViewResizeObserver.disconnect();
|
||||
|
||||
this.tabsList.forEach(tab => {
|
||||
tab.statusUnsubscribe();
|
||||
});
|
||||
@ -158,12 +174,28 @@ export default {
|
||||
|
||||
this.loadedTabs[tab.keyString] = true;
|
||||
},
|
||||
getTabStyles(tab) {
|
||||
let styles = {};
|
||||
|
||||
if (!this.isCurrent(tab)) {
|
||||
styles = {
|
||||
height: this.tabHeight,
|
||||
width: this.tabWidth
|
||||
};
|
||||
}
|
||||
|
||||
return styles;
|
||||
},
|
||||
setCurrentTabByIndex(index) {
|
||||
if (this.tabsList[index]) {
|
||||
this.showTab(this.tabsList[index]);
|
||||
}
|
||||
},
|
||||
showTab(tab, index) {
|
||||
if (!tab) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (index !== undefined) {
|
||||
this.storeCurrentTabIndexInURL(index);
|
||||
}
|
||||
@ -171,6 +203,13 @@ export default {
|
||||
this.currentTab = tab;
|
||||
this.addTabToLoaded(tab);
|
||||
},
|
||||
shouldLoadTab(tab) {
|
||||
const isLoaded = this.isTabLoaded(tab);
|
||||
const isCurrent = this.isCurrent(tab);
|
||||
const tabElLoaded = this.tabWidth !== undefined && this.tabHeight !== undefined;
|
||||
|
||||
return (isLoaded && isCurrent) || ((isLoaded && !isCurrent) && tabElLoaded);
|
||||
},
|
||||
showRemoveDialog(index) {
|
||||
if (!this.tabsList[index]) {
|
||||
return;
|
||||
@ -325,6 +364,14 @@ export default {
|
||||
|
||||
this.currentTabIndex = tabIndex;
|
||||
this.currentTab = this.tabsList[tabIndex];
|
||||
},
|
||||
handleWindowResize() {
|
||||
if (!this.$refs.tabs || !this.$refs.tabsHolder) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.tabWidth = this.$refs.tabs.offsetWidth + 'px';
|
||||
this.tabHeight = this.$refs.tabsHolder.offsetHeight - this.$refs.tabs.offsetHeight + 'px';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -20,7 +20,11 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
import { createOpenMct, resetApplicationState } from 'utils/testing';
|
||||
import {
|
||||
createOpenMct,
|
||||
resetApplicationState,
|
||||
spyOnBuiltins
|
||||
} from 'utils/testing';
|
||||
import TabsLayout from './plugin';
|
||||
import Vue from "vue";
|
||||
import EventEmitter from "EventEmitter";
|
||||
@ -63,13 +67,13 @@ describe('the plugin', function () {
|
||||
'phase': 5,
|
||||
'randomness': 0
|
||||
},
|
||||
'name': 'Sine Wave Generator',
|
||||
'type': 'generator',
|
||||
'modified': 1592851063871,
|
||||
'location': 'mine',
|
||||
'persisted': 1592851063871
|
||||
};
|
||||
let telemetryItem1 = Object.assign({}, telemetryItemTemplate, {
|
||||
'name': 'Sine Wave Generator 1',
|
||||
'id': '55122607-e65e-44d5-9c9d-9c31a914ca89',
|
||||
'identifier': {
|
||||
'namespace': '',
|
||||
@ -77,6 +81,7 @@ describe('the plugin', function () {
|
||||
}
|
||||
});
|
||||
let telemetryItem2 = Object.assign({}, telemetryItemTemplate, {
|
||||
'name': 'Sine Wave Generator 2',
|
||||
'id': '55122607-e65e-44d5-9c9d-9c31a914ca90',
|
||||
'identifier': {
|
||||
'namespace': '',
|
||||
@ -91,6 +96,9 @@ describe('the plugin', function () {
|
||||
|
||||
element = document.createElement('div');
|
||||
child = document.createElement('div');
|
||||
child.style.display = 'block';
|
||||
child.style.width = '1920px';
|
||||
child.style.height = '1080px';
|
||||
element.appendChild(child);
|
||||
|
||||
openmct.on('start', done);
|
||||
@ -150,8 +158,17 @@ describe('the plugin', function () {
|
||||
let tabsLayoutViewProvider;
|
||||
let mockComposition;
|
||||
let count = 0;
|
||||
let resizeCallback;
|
||||
|
||||
beforeEach(() => {
|
||||
class mockResizeObserver {
|
||||
constructor(cb) {
|
||||
resizeCallback = cb;
|
||||
}
|
||||
observe() { }
|
||||
disconnect() { }
|
||||
}
|
||||
|
||||
mockComposition = new EventEmitter();
|
||||
mockComposition.load = () => {
|
||||
if (count === 0) {
|
||||
@ -165,6 +182,9 @@ describe('the plugin', function () {
|
||||
|
||||
spyOn(openmct.composition, 'get').and.returnValue(mockComposition);
|
||||
|
||||
spyOnBuiltins(['ResizeObserver']);
|
||||
window.ResizeObserver.and.callFake(mockResizeObserver);
|
||||
|
||||
const applicableViews = openmct.objectViews.get(testViewObject, []);
|
||||
tabsLayoutViewProvider = applicableViews.find((viewProvider) => viewProvider.key === 'tabs');
|
||||
let view = tabsLayoutViewProvider.view(testViewObject, []);
|
||||
@ -175,6 +195,7 @@ describe('the plugin', function () {
|
||||
|
||||
afterEach(() => {
|
||||
count = 0;
|
||||
testViewObject.keep_alive = true;
|
||||
});
|
||||
|
||||
it ('renders a tab for each item', () => {
|
||||
@ -185,10 +206,22 @@ describe('the plugin', function () {
|
||||
|
||||
describe('with domainObject.keep_alive set to', () => {
|
||||
|
||||
it ('true, will keep all views loaded, regardless of current tab view', () => {
|
||||
let tabViewEls = element.querySelectorAll('.c-tabs-view__object');
|
||||
it ('true, will keep all views loaded, regardless of current tab view', (done) => {
|
||||
resizeCallback();
|
||||
|
||||
expect(tabViewEls.length).toEqual(2);
|
||||
// the function called by the resize observer is debounced 500ms,
|
||||
// this is to account for that
|
||||
let promise = new Promise((resolve, reject) => {
|
||||
setTimeout(resolve, 501);
|
||||
});
|
||||
|
||||
Promise.all([Vue.nextTick(), promise]).then(() => {
|
||||
let tabViewEls = element.querySelectorAll('.c-tabs-view__object');
|
||||
|
||||
expect(tabViewEls.length).toEqual(2);
|
||||
}).finally(() => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it ('false, will only keep the current tab view loaded', async () => {
|
||||
|
@ -113,7 +113,6 @@ import search from '../components/search.vue';
|
||||
|
||||
const ITEM_BUFFER = 25;
|
||||
const LOCAL_STORAGE_KEY__TREE_EXPANDED = 'mct-tree-expanded';
|
||||
const RETURN_ALL_DESCENDANTS = true;
|
||||
const SORT_MY_ITEMS_ALPH_ASC = true;
|
||||
const TREE_ITEM_INDENT_PX = 18;
|
||||
|
||||
@ -432,7 +431,7 @@ export default {
|
||||
|
||||
return scrollTopAmount >= treeStart && scrollTopAmount < treeEnd;
|
||||
},
|
||||
sortNameDescending(a, b) {
|
||||
sortNameAscending(a, b) {
|
||||
// sorting tree children items
|
||||
if (!(a.name && b.name)) {
|
||||
if (a.object.name.toLowerCase()
|
||||
@ -459,15 +458,16 @@ export default {
|
||||
|
||||
return 0;
|
||||
},
|
||||
|
||||
isSortable(parentObjectPath) {
|
||||
// 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);
|
||||
// determine if any part of the parent's path includes a key value of mine; aka My Items
|
||||
const isNestedInMyItems = Boolean(parentObjectPath.find(path => path.identifier.key === 'mine'));
|
||||
|
||||
if (SORT_MY_ITEMS_ALPH_ASC && isNestedInMyItems) {
|
||||
const sortedComposition = composition.sort(this.sortNameDescending);
|
||||
if (SORT_MY_ITEMS_ALPH_ASC && this.isSortable(parentObjectPath)) {
|
||||
const sortedComposition = composition.slice().sort(this.sortNameAscending);
|
||||
composition = sortedComposition;
|
||||
}
|
||||
|
||||
@ -513,17 +513,35 @@ export default {
|
||||
},
|
||||
compositionAddHandler(navigationPath) {
|
||||
return (domainObject) => {
|
||||
let parentItem = this.getTreeItemByPath(navigationPath);
|
||||
let newItem = this.buildTreeItem(domainObject, parentItem.objectPath, true);
|
||||
let allDescendants = this.getChildrenInTreeFor(parentItem, RETURN_ALL_DESCENDANTS);
|
||||
let afterItem = allDescendants.length ? allDescendants.pop() : parentItem;
|
||||
const parentItem = this.getTreeItemByPath(navigationPath);
|
||||
const newItem = this.buildTreeItem(domainObject, parentItem.objectPath, true);
|
||||
const descendants = this.getChildrenInTreeFor(parentItem, true);
|
||||
const directDescendants = this.getChildrenInTreeFor(parentItem);
|
||||
|
||||
this.addItemToTreeAfter(newItem, afterItem);
|
||||
const isNestedInMyItems = Boolean(parentItem.objectPath && parentItem.objectPath.find(path => path.identifier.key === 'mine'));
|
||||
if (directDescendants.length === 0) {
|
||||
this.addItemToTreeAfter(newItem, parentItem);
|
||||
|
||||
if (SORT_MY_ITEMS_ALPH_ASC && isNestedInMyItems) {
|
||||
this.sortTreeComposition(this.sortNameDescending, navigationPath);
|
||||
return;
|
||||
}
|
||||
|
||||
if (SORT_MY_ITEMS_ALPH_ASC && this.isSortable(parentItem.objectPath)) {
|
||||
const newItemIndex = directDescendants
|
||||
.findIndex(descendant => this.sortNameAscending(descendant, newItem) > 0);
|
||||
const shouldInsertFirst = newItemIndex === 0;
|
||||
const shouldInsertLast = newItemIndex === -1;
|
||||
|
||||
if (shouldInsertFirst) {
|
||||
this.addItemToTreeAfter(newItem, parentItem);
|
||||
} else if (shouldInsertLast) {
|
||||
this.addItemToTreeAfter(newItem, descendants.pop());
|
||||
} else {
|
||||
this.addItemToTreeBefore(newItem, directDescendants[newItemIndex]);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.addItemToTreeAfter(newItem, descendants.pop());
|
||||
};
|
||||
},
|
||||
compositionRemoveHandler(navigationPath) {
|
||||
@ -555,17 +573,18 @@ export default {
|
||||
const removeIndex = this.getTreeItemIndex(item.navigationPath);
|
||||
this.treeItems.splice(removeIndex, 1);
|
||||
},
|
||||
sortTreeComposition(algorithem, parentPath) {
|
||||
const parentIndex = this.getTreeItemIndex(parentPath);
|
||||
const parentItem = this.treeItems[parentIndex];
|
||||
addItemToTreeBefore(addItem, beforeItem) {
|
||||
const addIndex = this.getTreeItemIndex(beforeItem.navigationPath);
|
||||
|
||||
const allDescendants = this.getChildrenInTreeFor(parentItem);
|
||||
const sortedChildren = allDescendants.sort(algorithem);
|
||||
this.treeItems.splice(parentIndex + 1, allDescendants.length, ...sortedChildren);
|
||||
this.addItemToTree(addItem, addIndex);
|
||||
},
|
||||
addItemToTreeAfter(addItem, afterItem) {
|
||||
const addIndex = this.getTreeItemIndex(afterItem.navigationPath);
|
||||
this.treeItems.splice(addIndex + 1, 0, addItem);
|
||||
|
||||
this.addItemToTree(addItem, addIndex + 1);
|
||||
},
|
||||
addItemToTree(addItem, index) {
|
||||
this.treeItems.splice(index, 0, addItem);
|
||||
|
||||
if (this.isTreeItemOpen(addItem)) {
|
||||
this.openTreeItem(addItem);
|
||||
|
Loading…
x
Reference in New Issue
Block a user