Jesse Mazzella 16e1ac2529
Fixes for e2e tests following the Vue 3 compat upgrade (#6837)
* clock, timeConductor and appActions fixes

* Ensure realtime uses upstream context when available
Eliminate ambiguity when looking for time conductor locator

* Fix log plot e2e tests

* Fix displayLayout e2e tests

* Specify global time conductor to fix issues with duplicate selectors with independent time contexts

* a11y: ARIA for conductor and independent time conductor

* a11y: fix label collisions, specify 'Menu' in label

* Add watch mode

* fix(e2e): update appActions and tests to use a11y locators for ITC

* Don't remove the itc popup from the DOM. Just show/hide it once it's added the first time.

* test(e2e): disable one imagery test due to known bug

* Add fixme to tagging tests, issue described in 6822

* Fix locator for time conductor popups

* Improve how time bounds are set in independent time conductor.
Fix tests for flexible layout and timestrip

* Fix some tests for itc for display layouts

* Fix Inspector tabs remounting on change

* fix autoscale test and snapshot

* Fix telemetry table test

* Fix timestrip test

* e2e: move test info annotations to within test

* 6826: Fixes padStart error due to using it on a number rather than a string

* fix(e2e): update snapshots

* fix(e2e): fix restricted notebook locator

* fix(restrictedNotebook): fix issue causing sections not to update on lock

* fix(restrictedNotebook): fix issue causing snapshots to not be able to be deleted from a locked page

- Using `this.$delete(arr, index)` does not update the `length` property on the underlying target object, so it can lead to bizarre issues where your array is of length 4 but it has 3 objects in it.

* fix: replace all instances of `$delete` with `Array.splice()` or `delete`

* fix(e2e): fix grand search test

* fix(#3117): can remove item from displayLayout via tree context menu while viewing another item

* fix: remove typo 

* Wait for background image to load

* fix(#6832): timelist events can tick down

* fix: ensure that menuitems have the raw objects so emits work

* fix: assign new arrays instead of editing state in-place

* refactor(timelist): use `getClock()` instead of `clock()`

* Revert "refactor(timelist): use `getClock()` instead of `clock()`"

This reverts commit d88855311289bcf9e0d94799cdeee25c8628f65d.

* refactor(timelist): use new timeAPI

* Stop ticking when the independent time context is disabled (#6833)

* Turn off the clock ticket for independent time conductor when it is disabled

* Fix linting issues

---------

Co-authored-by: Khalid Adil <khalidadil29@gmail.com>

* test: update couchdb notebook test

* fix: codeQL warnings

* fix(tree-item): infinite spinner issue

- Using `indexOf()` with an object was failing due to some items in the tree being Proxy-wrapped and others not. So instead, use `findIndex()` with a predicate that compares the navigationPaths of both objects

* [Timer] Remove "refresh" call, it is not needed (#6841)

* removing an unneccessary refresh that waas causing many get requests
* lets just pretend this never happened

* fix(mct-tree): maintain reactivity of all tree items

* Hide change role button in the indicator in cases where there is only… (#6840)

Hide change role button in the indicator in cases where there is only a single role available for the current user

---------

Co-authored-by: Shefali <simplyrender@gmail.com>
Co-authored-by: Khalid Adil <khalidadil29@gmail.com>
Co-authored-by: John Hill <john.c.hill@nasa.gov>
Co-authored-by: David Tsay <david.e.tsay@nasa.gov>
Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
2023-07-28 02:06:41 +00:00

950 lines
30 KiB
Vue

<!--
Open MCT, Copyright (c) 2014-2023, 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="l-layout u-style-receiver js-style-receiver"
:class="{
'is-multi-selected': selectedLayoutItems.length > 1,
'allow-editing': isEditing
}"
@dragover="handleDragOver"
@click.capture="bypassSelection"
@drop="handleDrop"
>
<display-layout-grid
v-if="isEditing"
:grid-size="gridSize"
:show-grid="showGrid"
:grid-dimensions="gridDimensions"
/>
<div
v-if="shouldDisplayLayoutDimensions"
class="l-layout__dimensions"
:style="layoutDimensionsStyle"
>
<div class="l-layout__dimensions-vals">
{{ layoutDimensions[0] }},{{ layoutDimensions[1] }}
</div>
</div>
<component
:is="item.type"
v-for="(item, index) in layoutItems"
:key="item.id"
:ref="`layout-item-${item.id}`"
:item="item"
:grid-size="gridSize"
:init-select="initSelectIndex === index"
:index="index"
:multi-select="selectedLayoutItems.length > 1 || null"
:is-editing="isEditing"
@contextClick="updateViewContext"
@move="move"
@endMove="endMove"
@endLineResize="endLineResize"
@formatChanged="updateTelemetryFormat"
/>
<edit-marquee
v-if="showMarquee"
:grid-size="gridSize"
:selected-layout-items="selectedLayoutItems"
@endResize="endResize"
/>
</div>
</template>
<script>
import { v4 as uuid } from 'uuid';
import SubobjectView from './SubobjectView.vue';
import TelemetryView from './TelemetryView.vue';
import BoxView from './BoxView.vue';
import EllipseView from './EllipseView.vue';
import TextView from './TextView.vue';
import LineView from './LineView.vue';
import ImageView from './ImageView.vue';
import EditMarquee from './EditMarquee.vue';
import DisplayLayoutGrid from './DisplayLayoutGrid.vue';
import _ from 'lodash';
const TELEMETRY_IDENTIFIER_FUNCTIONS = {
table: (domainObject) => {
return Promise.resolve(domainObject.composition);
},
'telemetry.plot.overlay': (domainObject) => {
return Promise.resolve(domainObject.composition);
},
'telemetry.plot.stacked': (domainObject, openmct) => {
let composition = openmct.composition.get(domainObject);
return composition.load().then((objects) => {
let identifiers = [];
objects.forEach((object) => {
if (object.type === 'telemetry.plot.overlay') {
identifiers.push(...object.composition);
} else {
identifiers.push(object.identifier);
}
});
return Promise.resolve(identifiers);
});
}
};
const ITEM_TYPE_VIEW_MAP = {
'subobject-view': SubobjectView,
'telemetry-view': TelemetryView,
'box-view': BoxView,
'ellipse-view': EllipseView,
'line-view': LineView,
'text-view': TextView,
'image-view': ImageView
};
const ORDERS = {
top: Number.POSITIVE_INFINITY,
up: 1,
down: -1,
bottom: Number.NEGATIVE_INFINITY
};
const DRAG_OBJECT_TRANSFER_PREFIX = 'openmct/domain-object/';
const DUPLICATE_OFFSET = 3;
let components = ITEM_TYPE_VIEW_MAP;
components['edit-marquee'] = EditMarquee;
components['display-layout-grid'] = DisplayLayoutGrid;
function getItemDefinition(itemType, ...options) {
let itemView = ITEM_TYPE_VIEW_MAP[itemType];
if (!itemView) {
throw `Invalid itemType: ${itemType}`;
}
return itemView.makeDefinition(...options);
}
export default {
components: components,
inject: ['openmct', 'objectPath', 'options', 'objectUtils', 'currentView'],
props: {
domainObject: {
type: Object,
required: true
},
isEditing: {
type: Boolean,
required: true
}
},
data() {
return {
initSelectIndex: undefined,
selection: [],
showGrid: true,
viewContext: {},
gridDimensions: [0, 0],
layoutItems: this.domainObject.configuration.items || []
};
},
computed: {
gridSize() {
return this.domainObject.configuration.layoutGrid.map(Number);
},
selectedLayoutItems() {
return this.layoutItems.filter((item) => {
return this.itemIsInCurrentSelection(item);
});
},
layoutDimensions() {
return this.domainObject.configuration.layoutDimensions;
},
shouldDisplayLayoutDimensions() {
return this.layoutDimensions && this.layoutDimensions[0] > 0 && this.layoutDimensions[1] > 0;
},
layoutDimensionsStyle() {
const width = `${this.layoutDimensions[0]}px`;
const height = `${this.layoutDimensions[1]}px`;
return {
width,
height
};
},
showMarquee() {
let selectionPath = this.selection[0];
let singleSelectedLine =
this.selection.length === 1 &&
selectionPath[0].context.layoutItem &&
selectionPath[0].context.layoutItem.type === 'line-view';
return this.isEditing && selectionPath && selectionPath.length > 1 && !singleSelectedLine;
}
},
watch: {
isEditing(value) {
if (value) {
this.showGrid = value;
}
},
layoutItems: {
handler(value) {
this.updateGrid();
},
deep: true
}
},
mounted() {
this.openmct.selection.on('change', this.setSelection);
this.initializeItems();
this.composition = this.openmct.composition.get(this.domainObject);
this.composition.on('add', this.addChild);
this.composition.on('remove', this.removeChild);
this.composition.load();
this.gridDimensions = [this.$el.offsetWidth, this.$el.scrollHeight];
this.openmct.objects.observe(this.domainObject, 'configuration.items', (items) => {
this.layoutItems = items;
});
this.watchDisplayResize();
},
unmounted() {
this.openmct.selection.off('change', this.setSelection);
this.composition.off('add', this.addChild);
this.composition.off('remove', this.removeChild);
},
methods: {
updateGrid() {
let wMax = this.$el.clientWidth / this.gridSize[0];
let hMax = this.$el.clientHeight / this.gridSize[1];
this.layoutItems.forEach((item) => {
if (item.x + item.width > wMax) {
wMax = item.x + item.width + 2;
}
if (item.y + item.height > hMax) {
hMax = item.y + item.height + 2;
}
});
this.gridDimensions = [wMax * this.gridSize[0], hMax * this.gridSize[1]];
},
clearSelection() {
this.$el.click();
},
watchDisplayResize() {
const resizeObserver = new ResizeObserver(() => this.updateGrid());
resizeObserver.observe(this.$el);
},
addElement(itemType, element) {
this.addItem(itemType + '-view', element);
},
setSelection(selection) {
this.selection = [...selection];
},
itemIsInCurrentSelection(item) {
return this.selection.some(
(selectionPath) =>
selectionPath[0].context.layoutItem && selectionPath[0].context.layoutItem.id === item.id
);
},
bypassSelection($event) {
if (this.dragInProgress) {
if ($event) {
$event.stopImmediatePropagation();
}
this.dragInProgress = false;
return;
}
},
endLineResize(item, updates) {
this.dragInProgress = true;
let index = this.layoutItems.indexOf(item);
Object.assign(item, updates);
this.mutate(`configuration.items[${index}]`, item);
},
endResize(scaleWidth, scaleHeight, marqueeStart, marqueeOffset) {
this.dragInProgress = true;
this.layoutItems.forEach((item) => {
if (this.itemIsInCurrentSelection(item)) {
let itemXInMarqueeSpace = item.x - marqueeStart.x;
let itemXInMarqueeSpaceAfterScale = Math.round(itemXInMarqueeSpace * scaleWidth);
item.x = itemXInMarqueeSpaceAfterScale + marqueeOffset.x + marqueeStart.x;
let itemYInMarqueeSpace = item.y - marqueeStart.y;
let itemYInMarqueeSpaceAfterScale = Math.round(itemYInMarqueeSpace * scaleHeight);
item.y = itemYInMarqueeSpaceAfterScale + marqueeOffset.y + marqueeStart.y;
if (item.x2) {
let itemX2InMarqueeSpace = item.x2 - marqueeStart.x;
let itemX2InMarqueeSpaceAfterScale = Math.round(itemX2InMarqueeSpace * scaleWidth);
item.x2 = itemX2InMarqueeSpaceAfterScale + marqueeOffset.x + marqueeStart.x;
} else {
item.width = Math.round(item.width * scaleWidth);
}
if (item.y2) {
let itemY2InMarqueeSpace = item.y2 - marqueeStart.y;
let itemY2InMarqueeSpaceAfterScale = Math.round(itemY2InMarqueeSpace * scaleHeight);
item.y2 = itemY2InMarqueeSpaceAfterScale + marqueeOffset.y + marqueeStart.y;
} else {
item.height = Math.round(item.height * scaleHeight);
}
}
});
this.mutate('configuration.items', this.layoutItems);
},
move(gridDelta) {
this.dragInProgress = true;
if (!this.initialPositions) {
this.initialPositions = {};
_.cloneDeep(this.selectedLayoutItems).forEach((selectedItem) => {
if (selectedItem.type === 'line-view') {
this.initialPositions[selectedItem.id] = [
selectedItem.x,
selectedItem.y,
selectedItem.x2,
selectedItem.y2
];
this.startingMinX2 =
this.startingMinX2 !== undefined
? Math.min(this.startingMinX2, selectedItem.x2)
: selectedItem.x2;
this.startingMinY2 =
this.startingMinY2 !== undefined
? Math.min(this.startingMinY2, selectedItem.y2)
: selectedItem.y2;
} else {
this.initialPositions[selectedItem.id] = [selectedItem.x, selectedItem.y];
}
this.startingMinX =
this.startingMinX !== undefined
? Math.min(this.startingMinX, selectedItem.x)
: selectedItem.x;
this.startingMinY =
this.startingMinY !== undefined
? Math.min(this.startingMinY, selectedItem.y)
: selectedItem.y;
});
}
this.layoutItems.forEach((item) => {
if (this.initialPositions[item.id]) {
this.updateItemPosition(item, gridDelta);
}
});
},
updateItemPosition(item, gridDelta) {
let startingPosition = this.initialPositions[item.id];
let [startingX, startingY, startingX2, startingY2] = startingPosition;
if (this.startingMinX + gridDelta[0] >= 0) {
if (item.x2 !== undefined) {
if (this.startingMinX2 + gridDelta[0] >= 0) {
item.x = startingX + gridDelta[0];
}
} else {
item.x = startingX + gridDelta[0];
}
}
if (this.startingMinY + gridDelta[1] >= 0) {
if (item.y2 !== undefined) {
if (this.startingMinY2 + gridDelta[1] >= 0) {
item.y = startingY + gridDelta[1];
}
} else {
item.y = startingY + gridDelta[1];
}
}
if (
item.x2 !== undefined &&
this.startingMinX2 + gridDelta[0] >= 0 &&
this.startingMinX + gridDelta[0] >= 0
) {
item.x2 = startingX2 + gridDelta[0];
}
if (
item.y2 !== undefined &&
this.startingMinY2 + gridDelta[1] >= 0 &&
this.startingMinY + gridDelta[1] >= 0
) {
item.y2 = startingY2 + gridDelta[1];
}
},
endMove() {
this.mutate('configuration.items', this.layoutItems);
this.initialPositions = undefined;
this.startingMinX = undefined;
this.startingMinY = undefined;
this.startingMinX2 = undefined;
this.startingMinY2 = undefined;
},
mutate(path, value) {
this.openmct.objects.mutate(this.domainObject, path, value);
},
handleDrop($event) {
if (!$event.dataTransfer.types.includes('openmct/domain-object-path')) {
return;
}
$event.preventDefault();
let domainObject = JSON.parse($event.dataTransfer.getData('openmct/domain-object-path'))[0];
let elementRect = this.$el.getBoundingClientRect();
let droppedObjectPosition = [
Math.floor(($event.pageX - elementRect.left) / this.gridSize[0]),
Math.floor(($event.pageY - elementRect.top) / this.gridSize[1])
];
if (this.isTelemetry(domainObject)) {
this.addItem('telemetry-view', domainObject, droppedObjectPosition);
} else {
let keyString = this.openmct.objects.makeKeyString(domainObject.identifier);
if (!this.objectViewMap[keyString]) {
this.addItem('subobject-view', domainObject, droppedObjectPosition);
} else {
let prompt = this.openmct.overlays.dialog({
iconClass: 'alert',
message: 'This item is already in layout and will not be added again.',
buttons: [
{
label: 'OK',
callback: function () {
prompt.dismiss();
}
}
]
});
}
}
},
containsObject(identifier) {
if ('composition' in this.domainObject) {
return this.domainObject.composition.some((childId) =>
this.openmct.objects.areIdsEqual(childId, identifier)
);
}
return false;
},
handleDragOver($event) {
if (this.domainObject.locked) {
return;
}
// Get the ID of the dragged object
let draggedKeyString = $event.dataTransfer.types
.filter((type) => type.startsWith(DRAG_OBJECT_TRANSFER_PREFIX))
.map((type) => type.substring(DRAG_OBJECT_TRANSFER_PREFIX.length))[0];
// If the layout already contains the given object, then shortcut the default dragover behavior and
// potentially allow drop. Display layouts allow drag drop of duplicate telemetry objects.
if (this.containsObject(draggedKeyString)) {
$event.preventDefault();
}
},
isTelemetry(domainObject) {
if (
this.openmct.telemetry.isTelemetryObject(domainObject) &&
!this.options.showAsView.includes(domainObject.type)
) {
return true;
} else {
return false;
}
},
addItem(itemType, ...options) {
let item = getItemDefinition(itemType, this.openmct, this.gridSize, ...options);
item.type = itemType;
item.id = uuid();
this.trackItem(item);
this.layoutItems.push(item);
this.openmct.objects.mutate(this.domainObject, 'configuration.items', this.layoutItems);
this.initSelectIndex = this.layoutItems.length - 1;
},
trackItem(item) {
if (!item.identifier) {
return;
}
let keyString = this.openmct.objects.makeKeyString(item.identifier);
if (item.type === 'telemetry-view') {
let count = this.telemetryViewMap[keyString] || 0;
this.telemetryViewMap[keyString] = ++count;
} else if (item.type === 'subobject-view') {
let count = this.objectViewMap[keyString] || 0;
this.objectViewMap[keyString] = ++count;
}
},
removeItem(selectedItems) {
let indices = [];
this.initSelectIndex = -1;
selectedItems.forEach((selectedItem) => {
indices.push(selectedItem[0].context.index);
this.untrackItem(selectedItem[0].context.layoutItem);
});
_.pullAt(this.layoutItems, indices);
this.mutate('configuration.items', this.layoutItems);
this.clearSelection();
},
untrackItem(item) {
if (!item.identifier) {
return;
}
let keyString = this.openmct.objects.makeKeyString(item.identifier);
let telemetryViewCount = this.telemetryViewMap[keyString];
let objectViewCount = this.objectViewMap[keyString];
if (item.type === 'telemetry-view') {
telemetryViewCount = --this.telemetryViewMap[keyString];
if (telemetryViewCount === 0) {
delete this.telemetryViewMap[keyString];
}
} else if (item.type === 'subobject-view') {
objectViewCount = --this.objectViewMap[keyString];
if (objectViewCount === 0) {
delete this.objectViewMap[keyString];
}
}
if (!telemetryViewCount && !objectViewCount) {
this.removeFromComposition(item);
}
},
removeFromComposition(item) {
this.composition.remove(item);
},
initializeItems() {
this.telemetryViewMap = {};
this.objectViewMap = {};
let removedItems = [];
this.layoutItems.forEach((item) => {
if (item.identifier) {
if (this.containsObject(item.identifier)) {
this.trackItem(item);
} else {
removedItems.push(this.openmct.objects.makeKeyString(item.identifier));
}
}
});
this.startTransaction();
removedItems.forEach(this.removeFromConfiguration);
return this.endTransaction();
},
isItemAlreadyTracked(child) {
let found = false;
let keyString = this.openmct.objects.makeKeyString(child.identifier);
this.layoutItems.forEach((item) => {
if (item.identifier) {
let itemKeyString = this.openmct.objects.makeKeyString(item.identifier);
if (itemKeyString === keyString) {
found = true;
return;
}
}
});
if (found) {
return true;
} else if (this.isTelemetry(child)) {
return this.telemetryViewMap[keyString] && this.objectViewMap[keyString];
} else {
return this.objectViewMap[keyString];
}
},
addChild(child) {
if (this.isItemAlreadyTracked(child)) {
return;
}
let type;
if (this.isTelemetry(child)) {
type = 'telemetry-view';
} else {
type = 'subobject-view';
}
this.addItem(type, child);
},
removeChild(identifier) {
let keyString = this.openmct.objects.makeKeyString(identifier);
if (this.objectViewMap[keyString]) {
delete this.objectViewMap[keyString];
this.removeFromConfiguration(keyString);
} else if (this.telemetryViewMap[keyString]) {
delete this.telemetryViewMap[keyString];
this.removeFromConfiguration(keyString);
}
},
removeFromConfiguration(keyString) {
let layoutItems = this.layoutItems.filter((item) => {
if (!item.identifier) {
return true;
} else {
return this.openmct.objects.makeKeyString(item.identifier) !== keyString;
}
});
this.layoutItems = layoutItems;
this.mutate('configuration.items', layoutItems);
this.clearSelection();
},
orderItem(position, selectedItems) {
let delta = ORDERS[position];
let indices = [];
let items = [];
Object.assign(items, this.layoutItems);
this.selectedLayoutItems.forEach((selectedItem) => {
indices.push(this.layoutItems.indexOf(selectedItem));
});
indices.sort((a, b) => a - b);
if (position === 'top' || position === 'up') {
indices.reverse();
}
if (position === 'top' || position === 'bottom') {
this.moveToTopOrBottom(position, indices, items, delta);
} else {
this.moveUpOrDown(position, indices, items, delta);
}
this.mutate('configuration.items', this.layoutItems);
},
moveUpOrDown(position, indices, items, delta) {
let previousItemIndex = -1;
let newIndex = -1;
indices.forEach((itemIndex, index) => {
let isAdjacentItemSelected =
position === 'up'
? itemIndex + 1 === previousItemIndex
: itemIndex - 1 === previousItemIndex;
if (index > 0 && isAdjacentItemSelected) {
if (position === 'up') {
newIndex -= 1;
} else {
newIndex += 1;
}
} else {
newIndex = Math.max(Math.min(itemIndex + delta, this.layoutItems.length - 1), 0);
}
previousItemIndex = itemIndex;
this.updateItemOrder(newIndex, itemIndex, items);
});
},
moveToTopOrBottom(position, indices, items, delta) {
let newIndex = -1;
indices.forEach((itemIndex, index) => {
if (index === 0) {
newIndex = Math.max(Math.min(itemIndex + delta, this.layoutItems.length - 1), 0);
} else {
if (position === 'top') {
newIndex -= 1;
} else {
newIndex += 1;
}
}
this.updateItemOrder(newIndex, itemIndex, items);
});
},
updateItemOrder(newIndex, itemIndex, items) {
if (newIndex !== itemIndex) {
this.layoutItems.splice(itemIndex, 1);
this.layoutItems.splice(newIndex, 0, items[itemIndex]);
}
},
updateTelemetryFormat(item, format) {
let index = this.layoutItems.findIndex((layoutItem) => {
return layoutItem.id === item.id;
});
item.format = format;
this.mutate(`configuration.items[${index}]`, item);
},
createNewDomainObject(domainObject, composition, viewType, nameExtension, model) {
let identifier = {
key: uuid(),
namespace: this.domainObject.identifier.namespace
};
let type = this.openmct.types.get(viewType);
let parentKeyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
let objectName = nameExtension ? `${domainObject.name}-${nameExtension}` : domainObject.name;
let object = {};
if (model) {
object = _.cloneDeep(model);
} else {
object.type = viewType;
type.definition.initialize(object);
object.composition.push(...composition);
}
if (object.modified || object.persisted) {
object.modified = undefined;
object.persisted = undefined;
delete object.modified;
delete object.persisted;
}
object.name = objectName;
object.identifier = identifier;
object.location = parentKeyString;
let savedResolve;
this.openmct.objects.save(object).then(() => {
savedResolve(object);
});
return new Promise((resolve) => {
savedResolve = resolve;
});
},
convertToTelemetryView(identifier, position) {
this.openmct.objects.get(identifier).then((domainObject) => {
this.composition.add(domainObject);
this.addItem('telemetry-view', domainObject, position);
});
},
dispatchMultipleSelection(selectItemsArray) {
let event = new MouseEvent('click', {
bubbles: true,
shiftKey: true,
cancelable: true,
view: window
});
selectItemsArray.forEach((id) => {
let refId = `layout-item-${id}`;
let component = this.$refs[refId] && this.$refs[refId][0];
if (component) {
component.immediatelySelect = event;
component.$el.dispatchEvent(event);
}
});
},
duplicateItem(selectedItems) {
let objectStyles = this.domainObject.configuration.objectStyles || {};
let selectItemsArray = [];
let newDomainObjectsArray = [];
selectedItems.forEach((selectedItem) => {
let layoutItem = selectedItem[0].context.layoutItem;
let domainObject = selectedItem[0].context.item;
let layoutItemStyle = objectStyles[layoutItem.id];
let copy = _.cloneDeep(layoutItem);
copy.id = uuid();
selectItemsArray.push(copy.id);
let offsetKeys = ['x', 'y'];
if (copy.type === 'line-view') {
offsetKeys = offsetKeys.concat(['x2', 'y2']);
}
if (copy.type === 'subobject-view') {
this.createNewDomainObject(
domainObject,
domainObject.composition,
domainObject.type,
'duplicate',
domainObject
).then((newDomainObject) => {
newDomainObjectsArray.push(newDomainObject);
copy.identifier = newDomainObject.identifier;
});
}
offsetKeys.forEach((key) => {
copy[key] += DUPLICATE_OFFSET;
});
if (layoutItemStyle) {
objectStyles[copy.id] = layoutItemStyle;
}
this.trackItem(copy);
this.layoutItems.push(copy);
});
this.$nextTick(() => {
this.openmct.objects.mutate(this.domainObject, 'configuration.items', this.layoutItems);
this.openmct.objects.mutate(this.domainObject, 'configuration.objectStyles', objectStyles);
this.clearSelection();
newDomainObjectsArray.forEach((domainObject) => {
this.composition.add(domainObject);
});
this.dispatchMultipleSelection(selectItemsArray);
});
},
mergeMultipleTelemetryViews(selection, viewType) {
let identifiers = selection.map((selectedItem) => {
return selectedItem[0].context.layoutItem.identifier;
});
let firstDomainObject = selection[0][0].context.item;
let firstLayoutItem = selection[0][0].context.layoutItem;
let position = [firstLayoutItem.x, firstLayoutItem.y];
let mockDomainObject = {
name: 'Merged Telemetry Views',
identifier: firstDomainObject.identifier
};
this.createNewDomainObject(mockDomainObject, identifiers, viewType).then(
(newDomainObject) => {
this.composition.add(newDomainObject);
this.addItem('subobject-view', newDomainObject, position);
this.removeItem(selection);
this.initSelectIndex = this.layoutItems.length - 1;
}
);
},
mergeMultipleOverlayPlots(selection, viewType) {
let overlayPlots = selection.map((selectedItem) => selectedItem[0].context.item);
let overlayPlotIdentifiers = overlayPlots.map((overlayPlot) => overlayPlot.identifier);
let firstOverlayPlot = overlayPlots[0];
let firstLayoutItem = selection[0][0].context.layoutItem;
let position = [firstLayoutItem.x, firstLayoutItem.y];
let mockDomainObject = {
name: 'Merged Overlay Plots',
identifier: firstOverlayPlot.identifier
};
this.createNewDomainObject(mockDomainObject, overlayPlotIdentifiers, viewType).then(
(newDomainObject) => {
let newDomainObjectKeyString = this.openmct.objects.makeKeyString(
newDomainObject.identifier
);
let domainObjectKeyString = this.openmct.objects.makeKeyString(
this.domainObject.identifier
);
this.composition.add(newDomainObject);
this.addItem('subobject-view', newDomainObject, position);
overlayPlots.forEach((overlayPlot) => {
if (overlayPlot.location === domainObjectKeyString) {
this.openmct.objects.mutate(overlayPlot, 'location', newDomainObjectKeyString);
}
});
this.removeItem(selection);
this.initSelectIndex = this.layoutItems.length - 1;
}
);
},
getTelemetryIdentifiers(domainObject) {
let method = TELEMETRY_IDENTIFIER_FUNCTIONS[domainObject.type];
if (method) {
return method(domainObject, this.openmct);
} else {
throw 'No method identified for domainObject type';
}
},
switchViewType(context, viewType, selection) {
let domainObject = context.item;
let layoutItem = context.layoutItem;
let position = [layoutItem.x, layoutItem.y];
let layoutType = 'subobject-view';
if (layoutItem.type === 'telemetry-view') {
this.createNewDomainObject(domainObject, [domainObject.identifier], viewType).then(
(newDomainObject) => {
this.composition.add(newDomainObject);
this.addItem(layoutType, newDomainObject, position);
}
);
} else {
this.getTelemetryIdentifiers(domainObject).then((identifiers) => {
if (viewType === 'telemetry-view') {
identifiers.forEach((identifier, index) => {
let positionX = position[0] + index * DUPLICATE_OFFSET;
let positionY = position[1] + index * DUPLICATE_OFFSET;
this.convertToTelemetryView(identifier, [positionX, positionY]);
});
} else {
this.createNewDomainObject(domainObject, identifiers, viewType).then(
(newDomainObject) => {
this.composition.add(newDomainObject);
this.addItem(layoutType, newDomainObject, position);
}
);
}
});
}
this.removeItem(selection);
this.initSelectIndex = this.layoutItems.length - 1; //restore selection
},
startTransaction() {
if (!this.openmct.objects.isTransactionActive()) {
this.transaction = this.openmct.objects.startTransaction();
}
},
async endTransaction() {
if (!this.transaction) {
return;
}
await this.transaction.commit();
this.openmct.objects.endTransaction();
this.transaction = null;
},
toggleGrid() {
this.showGrid = !this.showGrid;
},
updateViewContext(viewContext) {
this.viewContext.row = viewContext;
},
getViewContext() {
return this.viewContext;
}
}
};
</script>