[Imagery] Click on image to get a large view ()

* [Imagery] Click on image to get a large view 
* Created new viewLargeAction.
* Changes in view registry to add parent element property inside view object.
* Separate class for views and added missing changes for LadTableSet.
* Renamed callBack to onItemClicked.

Co-authored-by: Andrew Henry <akhenry@gmail.com>
This commit is contained in:
Nikhil 2021-07-29 09:19:07 -07:00 committed by GitHub
parent ca66898e51
commit 071a13b219
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
56 changed files with 930 additions and 659 deletions

@ -1,4 +1,4 @@
import Vue from 'Vue';
import Vue from 'vue';
import HelloWorld from './HelloWorld.vue';
function SimpleVuePlugin() {

@ -262,7 +262,7 @@ define([
// Plugins that are installed by default
this.install(this.plugins.Plot());
this.install(this.plugins.TelemetryTable());
this.install(this.plugins.TelemetryTable.default());
this.install(PreviewPlugin.default());
this.install(LegacyIndicatorsPlugin());
this.install(LicensesPlugin.default());
@ -283,6 +283,7 @@ define([
this.install(this.plugins.NotificationIndicator());
this.install(this.plugins.NewFolderAction());
this.install(this.plugins.ViewDatumAction());
this.install(this.plugins.ViewLargeAction());
this.install(this.plugins.ObjectInterceptors());
this.install(this.plugins.NonEditableFolder());
}

@ -46,8 +46,6 @@ class ActionCollection extends EventEmitter {
this._observeObjectPath();
this.openmct.editor.on('isEditing', this._updateActions);
}
this._initializeActions();
}
disable(actionKeys) {
@ -156,19 +154,10 @@ class ActionCollection extends EventEmitter {
});
}
_initializeActions() {
Object.keys(this.applicableActions).forEach(key => {
this.applicableActions[key].callBack = () => {
return this.applicableActions[key].invoke(this.objectPath, this.view);
};
});
}
_updateActions() {
let newApplicableActions = this.openmct.actions._applicableActions(this.objectPath, this.view);
this.applicableActions = this._mergeOldAndNewActions(this.applicableActions, newApplicableActions);
this._initializeActions();
this._update();
}

@ -34,7 +34,7 @@ class ActionsAPI extends EventEmitter {
this._groupOrder = ['windowing', 'undefined', 'view', 'action', 'json'];
this.register = this.register.bind(this);
this.get = this.get.bind(this);
this.getActionsCollection = this.getActionsCollection.bind(this);
this._applicableActions = this._applicableActions.bind(this);
this._updateCachedActionCollections = this._updateCachedActionCollections.bind(this);
}
@ -43,12 +43,14 @@ class ActionsAPI extends EventEmitter {
this._allActions[actionDefinition.key] = actionDefinition;
}
get(objectPath, view) {
if (view) {
getAction(key) {
return this._allActions[key];
}
getActionsCollection(objectPath, view) {
if (view) {
return this._getCachedActionCollection(objectPath, view) || this._newActionCollection(objectPath, view, true);
} else {
return this._newActionCollection(objectPath, view, true);
}
}
@ -57,15 +59,6 @@ class ActionsAPI extends EventEmitter {
this._groupOrder = groupArray;
}
_get(objectPath, view) {
let actionCollection = this._newActionCollection(objectPath, view);
this._actionCollections.set(view, actionCollection);
actionCollection.on('destroy', this._updateCachedActionCollections);
return actionCollection;
}
_getCachedActionCollection(objectPath, view) {
let cachedActionCollection = this._actionCollections.get(view);
@ -75,7 +68,17 @@ class ActionsAPI extends EventEmitter {
_newActionCollection(objectPath, view, skipEnvironmentObservers) {
let applicableActions = this._applicableActions(objectPath, view);
return new ActionCollection(applicableActions, objectPath, view, this._openmct, skipEnvironmentObservers);
const actionCollection = new ActionCollection(applicableActions, objectPath, view, this._openmct, skipEnvironmentObservers);
if (view) {
this._cacheActionCollection(view, actionCollection);
}
return actionCollection;
}
_cacheActionCollection(view, actionCollection) {
this._actionCollections.set(view, actionCollection);
actionCollection.on('destroy', this._updateCachedActionCollections);
}
_updateCachedActionCollections(key) {

@ -106,7 +106,7 @@ describe('The Actions API', () => {
it("adds action to ActionsAPI", () => {
actionsAPI.register(mockAction);
let actionCollection = actionsAPI.get(mockObjectPath, mockViewContext1);
let actionCollection = actionsAPI.getActionsCollection(mockObjectPath, mockViewContext1);
let action = actionCollection.getActionsObject()[mockAction.key];
expect(action.key).toEqual(mockAction.key);
@ -121,21 +121,21 @@ describe('The Actions API', () => {
});
it("returns an ActionCollection when invoked with an objectPath only", () => {
let actionCollection = actionsAPI.get(mockObjectPath);
let actionCollection = actionsAPI.getActionsCollection(mockObjectPath);
let instanceOfActionCollection = actionCollection instanceof ActionCollection;
expect(instanceOfActionCollection).toBeTrue();
});
it("returns an ActionCollection when invoked with an objectPath and view", () => {
let actionCollection = actionsAPI.get(mockObjectPath, mockViewContext1);
let actionCollection = actionsAPI.getActionsCollection(mockObjectPath, mockViewContext1);
let instanceOfActionCollection = actionCollection instanceof ActionCollection;
expect(instanceOfActionCollection).toBeTrue();
});
it("returns relevant actions when invoked with objectPath only", () => {
let actionCollection = actionsAPI.get(mockObjectPath);
let actionCollection = actionsAPI.getActionsCollection(mockObjectPath);
let action = actionCollection.getActionsObject()[mockObjectPathAction.key];
expect(action.key).toEqual(mockObjectPathAction.key);
@ -143,7 +143,7 @@ describe('The Actions API', () => {
});
it("returns relevant actions when invoked with objectPath and view", () => {
let actionCollection = actionsAPI.get(mockObjectPath, mockViewContext1);
let actionCollection = actionsAPI.getActionsCollection(mockObjectPath, mockViewContext1);
let action = actionCollection.getActionsObject()[mockAction.key];
expect(action.key).toEqual(mockAction.key);

@ -37,7 +37,7 @@ import Menu, { MENU_PLACEMENT } from './menu.js';
* @property {Boolean} isDisabled adds disable class if true
* @property {String} name Menu item text
* @property {String} description Menu item description
* @property {Function} callBack callback function: invoked when item is clicked
* @property {Function} onItemClicked callback function: invoked when item is clicked
*/
/**
@ -66,12 +66,27 @@ class MenuAPI {
* @param {Array.<Action>|Array.<Array.<Action>>} actions collection of actions{@link Action} or collection of groups of actions {@link Action}
* @param {MenuOptions} [menuOptions] [Optional] The {@link MenuOptions} options for Menu
*/
showMenu(x, y, actions, menuOptions) {
this._createMenuComponent(x, y, actions, menuOptions);
showMenu(x, y, items, menuOptions) {
this._createMenuComponent(x, y, items, menuOptions);
this.menuComponent.showMenu();
}
actionsToMenuItems(actions, objectPath, view) {
return actions.map(action => {
const isActionGroup = Array.isArray(action);
if (isActionGroup) {
action = this.actionsToMenuItems(action, objectPath, view);
} else {
action.onItemClicked = () => {
action.invoke(objectPath, view);
};
}
return action;
});
}
/**
* Show popup menu with description of item on hover
* @param {number} x x-coordinates for popup

@ -57,7 +57,7 @@ describe ('The Menu API', () => {
name: 'Test Action 1',
cssClass: 'icon-clock',
description: 'This is a test action',
callBack: () => {
onItemClicked: () => {
result = 'Test Action 1 Invoked';
}
},
@ -66,7 +66,7 @@ describe ('The Menu API', () => {
name: 'Test Action 2',
cssClass: 'icon-clock',
description: 'This is a test action',
callBack: () => {
onItemClicked: () => {
result = 'Test Action 2 Invoked';
}
}

@ -11,7 +11,7 @@
:key="action.name"
:class="[action.cssClass, action.isDisabled ? 'disabled' : '']"
:title="action.description"
@click="action.callBack"
@click="action.onItemClicked"
>
{{ action.name }}
</li>
@ -36,7 +36,7 @@
:key="action.name"
:class="action.cssClass"
:title="action.description"
@click="action.callBack"
@click="action.onItemClicked"
>
{{ action.name }}
</li>

@ -13,7 +13,7 @@
:key="action.name"
:class="[action.cssClass, action.isDisabled ? 'disabled' : '']"
:title="action.description"
@click="action.callBack"
@click="action.onItemClicked"
@mouseover="toggleItemDescription(action)"
@mouseleave="toggleItemDescription()"
>
@ -42,7 +42,7 @@
:key="action.name"
:class="action.cssClass"
:title="action.description"
@click="action.callBack"
@click="action.onItemClicked"
@mouseover="toggleItemDescription(action)"
@mouseleave="toggleItemDescription()"
>

@ -71,12 +71,12 @@ class Menu extends EventEmitter {
showMenu() {
this.component = new Vue({
provide: {
options: this.options
},
components: {
MenuComponent
},
provide: {
options: this.options
},
template: '<menu-component />'
});
@ -85,12 +85,12 @@ class Menu extends EventEmitter {
showSuperMenu() {
this.component = new Vue({
provide: {
options: this.options
},
components: {
SuperMenuComponent
},
provide: {
options: this.options
},
template: '<super-menu-component />'
});

@ -19,8 +19,8 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import LadTableSet from './components/LadTableSet.vue';
import Vue from 'vue';
import LadTableSetView from './LadTableSetView';
export default function LADTableSetViewProvider(openmct) {
return {
@ -34,32 +34,7 @@ export default function LADTableSetViewProvider(openmct) {
return domainObject.type === 'LadTableSet';
},
view: function (domainObject, objectPath) {
let component;
return {
show: function (element) {
component = new Vue({
el: element,
components: {
LadTableSet: LadTableSet
},
provide: {
openmct,
objectPath
},
data() {
return {
domainObject
};
},
template: '<lad-table-set :domain-object="domainObject"></lad-table-set>'
});
},
destroy: function (element) {
component.$destroy();
component = undefined;
}
};
return new LadTableSetView(openmct, domainObject, objectPath);
},
priority: function () {
return 1;

@ -0,0 +1,45 @@
import LadTable from './components/LADTable.vue';
import Vue from 'vue';
export default class LADTableView {
constructor(openmct, domainObject, objectPath) {
this.openmct = openmct;
this.domainObject = domainObject;
this.objectPath = objectPath;
this.component = undefined;
}
show(element) {
this.component = new Vue({
el: element,
components: {
LadTable
},
provide: {
openmct: this.openmct,
currentView: this
},
data: () => {
return {
domainObject: this.domainObject,
objectPath: this.objectPath
};
},
template: '<lad-table ref="ladTable" :domain-object="domainObject" :object-path="objectPath"></lad-table>'
});
}
getViewContext() {
if (!this.component) {
return {};
}
return this.component.$refs.ladTable.getViewContext();
}
destroy(element) {
this.component.$destroy();
this.component = undefined;
}
}

@ -19,50 +19,30 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import LadTable from './components/LADTable.vue';
import Vue from 'vue';
export default function LADTableViewProvider(openmct) {
return {
key: 'LadTable',
name: 'LAD Table',
cssClass: 'icon-tabular-lad',
canView: function (domainObject) {
return domainObject.type === 'LadTable';
},
canEdit: function (domainObject) {
return domainObject.type === 'LadTable';
},
view: function (domainObject, objectPath) {
let component;
import LADTableView from './LADTableView';
return {
show: function (element) {
component = new Vue({
el: element,
components: {
LadTableComponent: LadTable
},
provide: {
openmct
},
data: () => {
return {
domainObject,
objectPath
};
},
template: '<lad-table-component :domain-object="domainObject" :object-path="objectPath"></lad-table-component>'
});
},
destroy: function (element) {
component.$destroy();
component = undefined;
}
};
},
priority: function () {
return 1;
}
};
export default class LADTableViewProvider {
constructor(openmct) {
this.openmct = openmct;
this.name = 'LAD Table';
this.key = 'LadTable';
this.cssClass = 'icon-tabular-lad';
}
canView(domainObject) {
return domainObject.type === 'LadTable';
}
canEdit(domainObject) {
return domainObject.type === 'LadTable';
}
view(domainObject, objectPath) {
return new LADTableView(this.openmct, domainObject, objectPath);
}
priority(domainObject) {
return 1;
}
}

@ -0,0 +1,45 @@
import LadTableSet from './components/LadTableSet.vue';
import Vue from 'vue';
export default class LadTableSetView {
constructor(openmct, domainObject, objectPath) {
this.openmct = openmct;
this.domainObject = domainObject;
this.objectPath = objectPath;
this.component = undefined;
}
show(element) {
this.component = new Vue({
el: element,
components: {
LadTableSet
},
provide: {
openmct: this.openmct,
objectPath: this.objectPath,
currentView: this
},
data: () => {
return {
domainObject: this.domainObject
};
},
template: '<lad-table-set ref="ladTableSet" :domain-object="domainObject"></lad-table-set>'
});
}
getViewContext() {
if (!this.component) {
return {};
}
return this.component.$refs.ladTableSet.getViewContext();
}
destroy(element) {
this.component.$destroy();
this.component = undefined;
}
}

@ -50,7 +50,7 @@ const CONTEXT_MENU_ACTIONS = [
];
export default {
inject: ['openmct'],
inject: ['openmct', 'currentView'],
props: {
domainObject: {
type: Object,
@ -167,25 +167,23 @@ export default {
this.resetValues();
this.timestampKey = timeSystem.key;
},
getView() {
return {
getViewContext: () => {
return {
viewHistoricalData: true,
viewDatumAction: true,
getDatum: () => {
return this.datum;
}
};
updateViewContext() {
this.$emit('rowContextClick', {
viewHistoricalData: true,
viewDatumAction: true,
getDatum: () => {
return this.datum;
}
};
});
},
showContextMenu(event) {
let actionCollection = this.openmct.actions.get(this.objectPath, this.getView());
let allActions = actionCollection.getActionsObject();
let applicableActions = CONTEXT_MENU_ACTIONS.map(key => allActions[key]);
this.updateViewContext();
this.openmct.menus.showMenu(event.x, event.y, applicableActions);
const actions = CONTEXT_MENU_ACTIONS.map(key => this.openmct.actions.getAction(key));
const menuItems = this.openmct.menus.actionsToMenuItems(actions, this.objectPath, this.currentView);
if (menuItems.length) {
this.openmct.menus.showMenu(event.x, event.y, menuItems);
}
},
resetValues() {
this.value = '---';

@ -38,6 +38,7 @@
:domain-object="ladRow.domainObject"
:path-to-table="objectPath"
:has-units="hasUnits"
@rowContextClick="updateViewContext"
/>
</tbody>
</table>
@ -51,7 +52,7 @@ export default {
components: {
LadRow
},
inject: ['openmct'],
inject: ['openmct', 'currentView'],
props: {
domainObject: {
type: Object,
@ -64,7 +65,8 @@ export default {
},
data() {
return {
items: []
items: [],
viewContext: {}
};
},
computed: {
@ -114,6 +116,12 @@ export default {
let metadataWithUnits = valueMetadatas.filter(metadatum => metadatum.unit);
return metadataWithUnits.length > 0;
},
updateViewContext(rowContext) {
this.viewContext.row = rowContext;
},
getViewContext() {
return this.viewContext;
}
}
};

@ -48,6 +48,7 @@
:domain-object="ladRow.domainObject"
:path-to-table="ladTable.objectPath"
:has-units="hasUnits"
@rowContextClick="updateViewContext"
/>
</template>
</tbody>
@ -61,7 +62,7 @@ export default {
components: {
LadRow
},
inject: ['openmct', 'objectPath'],
inject: ['openmct', 'objectPath', 'currentView'],
props: {
domainObject: {
type: Object,
@ -72,7 +73,8 @@ export default {
return {
ladTableObjects: [],
ladTelemetryObjects: {},
compositions: []
compositions: [],
viewContext: {}
};
},
computed: {
@ -166,6 +168,12 @@ export default {
this.$set(this.ladTelemetryObjects, ladTable.key, telemetryObjects);
};
},
updateViewContext(rowContext) {
this.viewContext.row = rowContext;
},
getViewContext() {
return this.viewContext;
}
}
};

@ -215,7 +215,8 @@ export default {
},
isEditing: {
type: Boolean,
required: true
required: true,
default: false
},
telemetry: {
type: Array,

@ -20,71 +20,78 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([
'./components/AlphanumericFormatView.vue',
'vue'
], function (AlphanumericFormatView, Vue) {
import AlphanumericFormat from './components/AlphanumericFormat.vue';
function AlphanumericFormatViewProvider(openmct, options) {
function isTelemetryObject(selectionPath) {
let selectedObject = selectionPath[0].context.item;
let parentObject = selectionPath[1].context.item;
let selectedLayoutItem = selectionPath[0].context.layoutItem;
import Vue from 'vue';
return parentObject
&& parentObject.type === 'layout'
&& selectedObject
&& selectedLayoutItem
&& selectedLayoutItem.type === 'telemetry-view'
&& openmct.telemetry.isTelemetryObject(selectedObject)
&& !options.showAsView.includes(selectedObject.type);
}
return {
key: 'alphanumeric-format',
name: 'Alphanumeric Format',
canView: function (selection) {
if (selection.length === 0 || selection[0].length === 1) {
return false;
}
return selection.every(isTelemetryObject);
},
view: function (domainObject, objectPath) {
let component;
return {
show: function (element) {
component = new Vue({
el: element,
components: {
AlphanumericFormatView: AlphanumericFormatView.default
},
provide: {
openmct,
objectPath
},
template: '<alphanumeric-format-view ref="alphanumericFormatView"></alphanumeric-format-view>'
});
},
getViewContext() {
if (component) {
return component.$refs.alphanumericFormatView.getViewContext();
} else {
return {};
}
},
destroy: function () {
component.$destroy();
component = undefined;
}
};
},
priority: function () {
return 1;
}
};
class AlphanumericFormatView {
constructor(openmct, domainObject, objectPath) {
this.openmct = openmct;
this.domainObject = domainObject;
this.objectPath = objectPath;
this.component = undefined;
}
return AlphanumericFormatViewProvider;
});
show(element) {
this.component = new Vue({
el: element,
name: 'AlphanumericFormat',
components: {
AlphanumericFormat
},
provide: {
openmct: this.openmct,
objectPath: this.objectPath,
currentView: this
},
template: '<alphanumeric-format ref="alphanumericFormat"></alphanumeric-format>'
});
}
getViewContext() {
if (this.component) {
return {};
}
return this.component.$refs.alphanumericFormat.getViewContext();
}
destroy() {
this.component.$destroy();
this.component = undefined;
}
}
export default function AlphanumericFormatViewProvider(openmct, options) {
function isTelemetryObject(selectionPath) {
let selectedObject = selectionPath[0].context.item;
let parentObject = selectionPath[1].context.item;
let selectedLayoutItem = selectionPath[0].context.layoutItem;
return parentObject
&& parentObject.type === 'layout'
&& selectedObject
&& selectedLayoutItem
&& selectedLayoutItem.type === 'telemetry-view'
&& openmct.telemetry.isTelemetryObject(selectedObject)
&& !options.showAsView.includes(selectedObject.type);
}
return {
key: 'alphanumeric-format',
name: 'Alphanumeric Format',
canView: function (selection) {
if (selection.length === 0 || selection[0].length === 1) {
return false;
}
return selection.every(isTelemetryObject);
},
view: function (domainObject, objectPath) {
return new AlphanumericFormatView(openmct, domainObject, objectPath);
},
priority: function () {
return 1;
}
};
}

@ -14,7 +14,7 @@ export default class CopyToClipboardAction {
invoke(objectPath, view = {}) {
const viewContext = view.getViewContext && view.getViewContext();
const formattedValue = viewContext.formattedValueForCopy();
const formattedValue = viewContext.row.formattedValueForCopy();
clipboard.updateClipboard(formattedValue)
.then(() => {
@ -26,9 +26,13 @@ export default class CopyToClipboardAction {
}
appliesTo(objectPath, view = {}) {
let viewContext = view.getViewContext && view.getViewContext();
const viewContext = view.getViewContext && view.getViewContext();
const row = viewContext && viewContext.row;
if (!row) {
return false;
}
return viewContext && viewContext.formattedValueForCopy
&& typeof viewContext.formattedValueForCopy === 'function';
return row.formattedValueForCopy
&& typeof row.formattedValueForCopy === 'function';
}
}

@ -52,7 +52,8 @@
<script>
export default {
inject: ['openmct'],
name: 'AlphanumericFormat',
inject: ['openmct', 'objectPath'],
data() {
return {
isEditing: this.openmct.editor.isEditing(),

@ -56,6 +56,7 @@
:index="index"
:multi-select="selectedLayoutItems.length > 1"
:is-editing="isEditing"
@contextClick="updateViewContext"
@move="move"
@endMove="endMove"
@endLineResize="endLineResize"
@ -140,7 +141,7 @@ function getItemDefinition(itemType, ...options) {
export default {
components: components,
inject: ['openmct', 'options', 'objectPath'],
inject: ['openmct', 'objectPath', 'options', 'objectUtils', 'currentView'],
props: {
domainObject: {
type: Object,
@ -155,7 +156,8 @@ export default {
return {
initSelectIndex: undefined,
selection: [],
showGrid: true
showGrid: true,
viewContext: {}
};
},
computed: {
@ -819,6 +821,12 @@ export default {
},
toggleGrid() {
this.showGrid = !this.showGrid;
},
updateViewContext(viewContext) {
this.viewContext.row = viewContext;
},
getViewContext() {
return this.viewContext;
}
}
};

@ -102,7 +102,7 @@ export default {
LayoutFrame
},
mixins: [conditionalStylesMixin],
inject: ['openmct', 'objectPath'],
inject: ['openmct', 'objectPath', 'currentView'],
props: {
item: {
type: Object,
@ -294,16 +294,6 @@ export default {
this.requestHistoricalData(this.domainObject);
}
},
getView() {
return {
getViewContext: () => {
return {
viewHistoricalData: true,
formattedValueForCopy: this.formattedValueForCopy
};
}
};
},
setObject(domainObject) {
this.domainObject = domainObject;
this.mutablePromise = undefined;
@ -338,30 +328,38 @@ export default {
this.$emit('formatChanged', this.item, format);
},
updateViewContext() {
this.$emit('contextClick', {
viewHistoricalData: true,
formattedValueForCopy: this.formattedValueForCopy
});
},
async getContextMenuActions() {
const defaultNotebook = getDefaultNotebook();
const domainObject = defaultNotebook && await this.openmct.objects.get(defaultNotebook.notebookMeta.identifier);
const actionCollection = this.openmct.actions.get(this.currentObjectPath, this.getView());
const actionsObject = actionCollection.getActionsObject();
let copyToNotebookAction = actionsObject.copyToNotebook;
let defaultNotebookName;
if (defaultNotebook) {
const defaultPath = domainObject && `${domainObject.name} - ${defaultNotebook.section.name} - ${defaultNotebook.page.name}`;
copyToNotebookAction.name = `Copy to Notebook ${defaultPath}`;
} else {
actionsObject.copyToNotebook = undefined;
delete actionsObject.copyToNotebook;
defaultNotebookName = `Copy to Notebook ${defaultPath}`;
}
return CONTEXT_MENU_ACTIONS.map(actionKey => {
return actionsObject[actionKey];
}).filter(action => action !== undefined);
return CONTEXT_MENU_ACTIONS
.map(actionKey => {
const action = this.openmct.actions.getAction(actionKey);
if (action.key === 'copyToNotebook') {
action.name = defaultNotebookName;
}
return action;
})
.filter(action => action.name !== undefined);
},
async showContextMenu(event) {
this.updateViewContext();
const contextMenuActions = await this.getContextMenuActions();
this.openmct.menus.showMenu(event.x, event.y, contextMenuActions);
const menuItems = this.openmct.menus.actionsToMenuItems(contextMenuActions, this.currentObjectPath, this.currentView);
this.openmct.menus.showMenu(event.x, event.y, menuItems);
},
setStatus(status) {
this.status = status;

@ -20,13 +20,81 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
import Layout from './components/DisplayLayout.vue';
import Vue from 'vue';
import objectUtils from 'objectUtils';
import DisplayLayoutType from './DisplayLayoutType.js';
import DisplayLayoutToolbar from './DisplayLayoutToolbar.js';
import AlphaNumericFormatViewProvider from './AlphanumericFormatViewProvider.js';
import CopyToClipboardAction from './actions/CopyToClipboardAction';
import DisplayLayout from './components/DisplayLayout.vue';
import DisplayLayoutToolbar from './DisplayLayoutToolbar.js';
import DisplayLayoutType from './DisplayLayoutType.js';
import objectUtils from 'objectUtils';
import Vue from 'vue';
class DisplayLayoutView {
constructor(openmct, domainObject, objectPath, options) {
this.openmct = openmct;
this.domainObject = domainObject;
this.objectPath = objectPath;
this.options = options;
this.component = undefined;
}
show(container, isEditing) {
this.component = new Vue({
el: container,
components: {
DisplayLayout
},
provide: {
openmct: this.openmct,
objectPath: this.objectPath,
options: this.options,
objectUtils,
currentView: this
},
data: () => {
return {
domainObject: this.domainObject,
isEditing
};
},
template: '<display-layout ref="displayLayout" :domain-object="domainObject" :is-editing="isEditing"></display-layout>'
});
}
getViewContext() {
if (!this.component) {
return {};
}
return this.component.$refs.displayLayout.getViewContext();
}
getSelectionContext() {
return {
item: this.domainObject,
supportsMultiSelect: true,
addElement: this.component && this.component.$refs.displayLayout.addElement,
removeItem: this.component && this.component.$refs.displayLayout.removeItem,
orderItem: this.component && this.component.$refs.displayLayout.orderItem,
duplicateItem: this.component && this.component.$refs.displayLayout.duplicateItem,
switchViewType: this.component && this.component.$refs.displayLayout.switchViewType,
mergeMultipleTelemetryViews: this.component && this.component.$refs.displayLayout.mergeMultipleTelemetryViews,
mergeMultipleOverlayPlots: this.component && this.component.$refs.displayLayout.mergeMultipleOverlayPlots,
toggleGrid: this.component && this.component.$refs.displayLayout.toggleGrid
};
}
onEditModeChange(isEditing) {
this.component.isEditing = isEditing;
}
destroy() {
this.component.$destroy();
this.component = undefined;
}
}
export default function DisplayLayoutPlugin(options) {
return function (openmct) {
@ -41,51 +109,7 @@ export default function DisplayLayoutPlugin(options) {
return domainObject.type === 'layout';
},
view: function (domainObject, objectPath) {
let component;
return {
show(container) {
component = new Vue({
el: container,
components: {
Layout
},
provide: {
openmct,
objectUtils,
options,
objectPath
},
data() {
return {
domainObject: domainObject,
isEditing: openmct.editor.isEditing()
};
},
template: '<layout ref="displayLayout" :domain-object="domainObject" :is-editing="isEditing"></layout>'
});
},
getSelectionContext() {
return {
item: domainObject,
supportsMultiSelect: true,
addElement: component && component.$refs.displayLayout.addElement,
removeItem: component && component.$refs.displayLayout.removeItem,
orderItem: component && component.$refs.displayLayout.orderItem,
duplicateItem: component && component.$refs.displayLayout.duplicateItem,
switchViewType: component && component.$refs.displayLayout.switchViewType,
mergeMultipleTelemetryViews: component && component.$refs.displayLayout.mergeMultipleTelemetryViews,
mergeMultipleOverlayPlots: component && component.$refs.displayLayout.mergeMultipleOverlayPlots,
toggleGrid: component && component.$refs.displayLayout.toggleGrid
};
},
onEditModeChange: function (isEditing) {
component.isEditing = isEditing;
},
destroy() {
component.$destroy();
}
};
return new DisplayLayoutView(openmct, domainObject, objectPath, options);
},
priority() {
return 100;

@ -0,0 +1,37 @@
import ImageryViewLayout from './components/ImageryViewLayout.vue';
import Vue from 'vue';
export default class ImageryView {
constructor(openmct, domainObject, objectPath) {
this.openmct = openmct;
this.domainObject = domainObject;
this.objectPath = objectPath;
this.component = undefined;
}
show(element) {
this.component = new Vue({
el: element,
components: {
ImageryViewLayout
},
provide: {
openmct: this.openmct,
domainObject: this.domainObject,
objectPath: this.objectPath,
currentView: this
},
template: '<imagery-view-layout ref="ImageryLayout"></imagery-view-layout>'
});
}
destroy() {
this.component.$destroy();
this.component = undefined;
}
_getInstance() {
return this.component;
}
}

@ -19,9 +19,7 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import ImageryViewLayout from './components/ImageryViewLayout.vue';
import Vue from 'vue';
import ImageryView from './ImageryView';
export default function ImageryViewProvider(openmct) {
const type = 'example.imagery';
@ -42,31 +40,8 @@ export default function ImageryViewProvider(openmct) {
canView: function (domainObject) {
return hasImageTelemetry(domainObject);
},
view: function (domainObject) {
let component;
return {
show: function (element) {
component = new Vue({
el: element,
components: {
ImageryViewLayout
},
provide: {
openmct,
domainObject
},
template: '<imagery-view-layout ref="ImageryLayout"></imagery-view-layout>'
});
},
destroy: function () {
component.$destroy();
component = undefined;
},
_getInstance: function () {
return component;
}
};
view: function (domainObject, objectPath) {
return new ImageryView(openmct, domainObject, objectPath);
}
};
}

@ -58,6 +58,7 @@
<div ref="imageBG"
class="c-imagery__main-image__bg"
:class="{'paused unnsynced': isPaused,'stale':false }"
@click="expand"
>
<div class="image-wrapper"
:style="{
@ -170,8 +171,9 @@
<script>
import _ from 'lodash';
import moment from 'moment';
import Compass from './Compass/Compass.vue';
import RelatedTelemetry from './RelatedTelemetry/RelatedTelemetry';
import Compass from './Compass/Compass.vue';
const DEFAULT_DURATION_FORMATTER = 'duration';
const REFRESH_CSS_MS = 500;
@ -195,7 +197,7 @@ export default {
components: {
Compass
},
inject: ['openmct', 'domainObject'],
inject: ['openmct', 'domainObject', 'objectPath', 'currentView'],
data() {
let timeSystem = this.openmct.time.timeSystem();
@ -468,6 +470,16 @@ export default {
}
},
methods: {
expand() {
const actionCollection = this.openmct.actions.getActionsCollection(this.objectPath, this.currentView);
const visibleActions = actionCollection.getVisibleActions();
const viewLargeAction = visibleActions
&& visibleActions.find(action => action.key === 'large.view');
if (viewLargeAction && viewLargeAction.appliesTo(this.objectPath, this.currentView)) {
viewLargeAction.onItemClicked();
}
},
async initializeRelatedTelemetry() {
this.relatedTelemetry = new RelatedTelemetry(
this.openmct,

@ -25,16 +25,20 @@ export default class CopyToNotebookAction {
});
}
invoke(objectPath, view = {}) {
let viewContext = view.getViewContext && view.getViewContext();
invoke(objectPath, view) {
const formattedValueForCopy = view.getViewContext().row.formattedValueForCopy;
this.copyToNotebook(viewContext.formattedValueForCopy());
this.copyToNotebook(formattedValueForCopy());
}
appliesTo(objectPath, view = {}) {
let viewContext = view.getViewContext && view.getViewContext();
const viewContext = view.getViewContext && view.getViewContext();
const row = viewContext && viewContext.row;
if (!row) {
return;
}
return viewContext && viewContext.formattedValueForCopy
&& typeof viewContext.formattedValueForCopy === 'function';
return row.formattedValueForCopy
&& typeof row.formattedValueForCopy === 'function';
}
}

@ -80,7 +80,7 @@ export default {
notebookTypes.push({
cssClass: 'icon-notebook',
name: `Save to Notebook ${defaultPath}`,
callBack: () => {
onItemClicked: () => {
return this.snapshot(NOTEBOOK_DEFAULT);
}
});
@ -89,7 +89,7 @@ export default {
notebookTypes.push({
cssClass: 'icon-camera',
name: 'Save to Notebook Snapshots',
callBack: () => {
onItemClicked: () => {
return this.snapshot(NOTEBOOK_SNAPSHOT);
}
});

@ -63,6 +63,7 @@ define([
'./defaultRootName/plugin',
'./plan/plugin',
'./viewDatumAction/plugin',
'./viewLargeAction/plugin',
'./interceptors/plugin',
'./performanceIndicator/plugin',
'./CouchDBSearchFolder/plugin',
@ -110,6 +111,7 @@ define([
DefaultRootName,
PlanLayout,
ViewDatumAction,
ViewLargeAction,
ObjectInterceptors,
PerformanceIndicator,
CouchDBSearchFolder,
@ -211,6 +213,7 @@ define([
plugins.DefaultRootName = DefaultRootName.default;
plugins.PlanLayout = PlanLayout.default;
plugins.ViewDatumAction = ViewDatumAction.default;
plugins.ViewLargeAction = ViewLargeAction.default;
plugins.ObjectInterceptors = ObjectInterceptors.default;
plugins.PerformanceIndicator = PerformanceIndicator.default;
plugins.CouchDBSearchFolder = CouchDBSearchFolder.default;

@ -0,0 +1,67 @@
import TableComponent from './components/table.vue';
import TelemetryTable from './TelemetryTable';
import Vue from 'vue';
export default class TelemetryTableView {
constructor(openmct, domainObject, objectPath) {
this.openmct = openmct;
this.domainObject = domainObject;
this.objectPath = objectPath;
this.component = undefined;
this.table = new TelemetryTable(domainObject, openmct);
}
getViewContext() {
if (!this.component) {
return {};
}
return this.component.$refs.tableComponent.getViewContext();
}
onEditModeChange(editMode) {
this.component.isEditing = editMode;
}
onClearData() {
this.table.clearData();
}
getTable() {
return this.table;
}
destroy(element) {
this.component.$destroy();
this.component = undefined;
}
show(element, editMode) {
this.component = new Vue({
el: element,
components: {
TableComponent
},
provide: {
openmct: this.openmct,
objectPath: this.objectPath,
table: this.table,
currentView: this
},
data() {
return {
isEditing: editMode,
marking: {
disableMultiSelect: false,
enable: true,
rowName: '',
rowNamePlural: '',
useAlternateControlBar: false
}
};
},
template: '<table-component ref="tableComponent" :is-editing="isEditing" :marking="marking"></table-component>'
});
}
}

@ -20,99 +20,35 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([
'./components/table.vue',
'./TelemetryTable',
'vue'
], function (
TableComponent,
TelemetryTable,
Vue
) {
function TelemetryTableViewProvider(openmct) {
function hasTelemetry(domainObject) {
if (!Object.prototype.hasOwnProperty.call(domainObject, 'telemetry')) {
return false;
}
import TelemetryTableView from './TelemetryTableView';
let metadata = openmct.telemetry.getMetadata(domainObject);
return metadata.values().length > 0;
export default function TelemetryTableViewProvider(openmct) {
function hasTelemetry(domainObject) {
if (!Object.prototype.hasOwnProperty.call(domainObject, 'telemetry')) {
return false;
}
return {
key: 'table',
name: 'Telemetry Table',
cssClass: 'icon-tabular-realtime',
canView(domainObject) {
return domainObject.type === 'table'
|| hasTelemetry(domainObject);
},
canEdit(domainObject) {
return domainObject.type === 'table';
},
view(domainObject, objectPath) {
let table = new TelemetryTable(domainObject, openmct);
let component;
let markingProp = {
enable: true,
useAlternateControlBar: false,
rowName: '',
rowNamePlural: ''
};
const view = {
show: function (element, editMode) {
component = new Vue({
el: element,
components: {
TableComponent: TableComponent.default
},
provide: {
openmct,
table,
objectPath
},
data() {
return {
isEditing: editMode,
markingProp,
view
};
},
template: '<table-component ref="tableComponent" :isEditing="isEditing" :marking="markingProp" :view="view"/>'
});
},
onEditModeChange(editMode) {
component.isEditing = editMode;
},
onClearData() {
table.clearData();
},
getViewContext() {
if (component) {
return component.$refs.tableComponent.getViewContext();
} else {
return {
type: 'telemetry-table'
};
}
},
destroy: function (element) {
component.$destroy();
component = undefined;
},
_getTable: function () {
return table;
}
};
let metadata = openmct.telemetry.getMetadata(domainObject);
return view;
},
priority() {
return 1;
}
};
return metadata.values().length > 0;
}
return TelemetryTableViewProvider;
});
return {
key: 'table',
name: 'Telemetry Table',
cssClass: 'icon-tabular-realtime',
canView(domainObject) {
return domainObject.type === 'table'
|| hasTelemetry(domainObject);
},
canEdit(domainObject) {
return domainObject.type === 'table';
},
view(domainObject, objectPath) {
return new TelemetryTableView(openmct, domainObject, objectPath);
},
priority() {
return 1;
}
};
}

@ -20,83 +20,89 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
let exportCSV = {
const exportCSV = {
name: 'Export Table Data',
key: 'export-csv-all',
description: "Export this view's data",
cssClass: 'icon-download labeled',
invoke: (objectPath, viewProvider) => {
viewProvider.getViewContext().exportAllDataAsCSV();
invoke: (objectPath, view) => {
view.getViewContext().exportAllDataAsCSV();
},
group: 'view'
};
let exportMarkedDataAsCSV = {
const exportMarkedDataAsCSV = {
name: 'Export Marked Rows',
key: 'export-csv-marked',
description: "Export marked rows as CSV",
cssClass: 'icon-download labeled',
invoke: (objectPath, viewProvider) => {
viewProvider.getViewContext().exportMarkedDataAsCSV();
invoke: (objectPath, view) => {
view.getViewContext().exportMarkedDataAsCSV();
},
group: 'view'
};
let unmarkAllRows = {
const unmarkAllRows = {
name: 'Unmark All Rows',
key: 'unmark-all-rows',
description: 'Unmark all rows',
cssClass: 'icon-x labeled',
invoke: (objectPath, viewProvider) => {
viewProvider.getViewContext().unmarkAllRows();
},
showInStatusBar: true,
group: 'view'
};
let pause = {
name: 'Pause',
key: 'pause-data',
description: 'Pause real-time data flow',
cssClass: 'icon-pause',
invoke: (objectPath, viewProvider) => {
viewProvider.getViewContext().togglePauseByButton();
},
showInStatusBar: true,
group: 'view'
};
let play = {
name: 'Play',
key: 'play-data',
description: 'Continue real-time data flow',
cssClass: 'c-button pause-play is-paused',
invoke: (objectPath, viewProvider) => {
viewProvider.getViewContext().togglePauseByButton();
},
showInStatusBar: true,
group: 'view'
};
let expandColumns = {
name: 'Expand Columns',
key: 'expand-columns',
description: "Increase column widths to fit currently available data.",
cssClass: 'icon-arrows-right-left labeled',
invoke: (objectPath, viewProvider) => {
viewProvider.getViewContext().expandColumns();
},
showInStatusBar: true,
group: 'view'
};
let autosizeColumns = {
name: 'Autosize Columns',
key: 'autosize-columns',
description: "Automatically size columns to fit the table into the available space.",
cssClass: 'icon-expand labeled',
invoke: (objectPath, viewProvider) => {
viewProvider.getViewContext().autosizeColumns();
invoke: (objectPath, view) => {
view.getViewContext().unmarkAllRows();
},
showInStatusBar: true,
group: 'view'
};
let viewActions = [
const pause = {
name: 'Pause',
key: 'pause-data',
description: 'Pause real-time data flow',
cssClass: 'icon-pause',
invoke: (objectPath, view) => {
view.getViewContext().togglePauseByButton();
},
showInStatusBar: true,
group: 'view'
};
const play = {
name: 'Play',
key: 'play-data',
description: 'Continue real-time data flow',
cssClass: 'c-button pause-play is-paused',
invoke: (objectPath, view) => {
view.getViewContext().togglePauseByButton();
},
showInStatusBar: true,
group: 'view'
};
const expandColumns = {
name: 'Expand Columns',
key: 'expand-columns',
description: "Increase column widths to fit currently available data.",
cssClass: 'icon-arrows-right-left labeled',
invoke: (objectPath, view) => {
view.getViewContext().expandColumns();
},
showInStatusBar: true,
group: 'view'
};
const autosizeColumns = {
name: 'Autosize Columns',
key: 'autosize-columns',
description: "Automatically size columns to fit the table into the available space.",
cssClass: 'icon-expand labeled',
invoke: (objectPath, view) => {
view.getViewContext().autosizeColumns();
},
showInStatusBar: true,
group: 'view'
};
const viewActions = [
exportCSV,
exportMarkedDataAsCSV,
unmarkAllRows,
@ -107,16 +113,13 @@ let viewActions = [
];
viewActions.forEach(action => {
action.appliesTo = (objectPath, viewProvider = {}) => {
let viewContext = viewProvider.getViewContext && viewProvider.getViewContext();
if (viewContext) {
let type = viewContext.type;
return type === 'telemetry-table';
action.appliesTo = (objectPath, view = {}) => {
const viewContext = view.getViewContext && view.getViewContext();
if (!viewContext) {
return false;
}
return false;
return viewContext.type === 'telemetry-table';
};
});

@ -49,7 +49,7 @@ export default {
components: {
TableCell
},
inject: ['openmct'],
inject: ['openmct', 'currentView'],
props: {
headers: {
type: Object,
@ -97,16 +97,7 @@ export default {
selectable[columnKeys] = this.row.columns[columnKeys].selectable;
return selectable;
}, {}),
actionsViewContext: {
getViewContext: () => {
return {
viewHistoricalData: true,
viewDatumAction: true,
getDatum: this.getDatum
};
}
}
}, {})
};
},
computed: {
@ -187,19 +178,20 @@ export default {
showContextMenu: function (event) {
event.preventDefault();
this.updateViewContext();
this.markRow(event);
this.row.getContextualDomainObject(this.openmct, this.row.objectKeyString).then(domainObject => {
let contextualObjectPath = this.objectPath.slice();
contextualObjectPath.unshift(domainObject);
let actionsCollection = this.openmct.actions.get(contextualObjectPath, this.actionsViewContext);
let allActions = actionsCollection.getActionsObject();
let applicableActions = this.row.getContextMenuActions().map(key => allActions[key]);
if (applicableActions.length) {
this.openmct.menus.showMenu(event.x, event.y, applicableActions);
}
const actions = this.row.getContextMenuActions().map(key => this.openmct.actions.getAction(key));
const menuItems = this.openmct.menus.actionsToMenuItems(actions, this.objectPath, this.currentView);
if (menuItems.length) {
this.openmct.menus.showMenu(event.x, event.y, menuItems);
}
},
updateViewContext() {
this.$emit('rowContextClick', {
viewHistoricalData: true,
viewDatumAction: true,
getDatum: this.getDatum
});
}
}

@ -233,6 +233,7 @@
@mark="markRow"
@unmark="unmarkRow"
@markMultipleConcurrent="markMultipleConcurrentRows"
@rowContextClick="updateViewContext"
/>
</tbody>
</table>
@ -263,6 +264,7 @@
:column-widths="configuredColumnWidths"
:row="sizingRowData"
:object-path="objectPath"
@rowContextClick="updateViewContext"
/>
</table>
<table-footer-indicator
@ -298,12 +300,25 @@ export default {
ToggleSwitch,
SizingRow
},
inject: ['table', 'openmct', 'objectPath'],
inject: ['openmct', 'objectPath', 'table', 'currentView'],
props: {
isEditing: {
type: Boolean,
default: false
},
marking: {
type: Object,
required: true,
default() {
return {
enable: false,
disableMultiSelect: false,
useAlternateControlBar: false,
rowName: '',
rowNamePlural: ''
};
}
},
allowExport: {
type: Boolean,
default: true
@ -316,28 +331,9 @@ export default {
type: Boolean,
default: true
},
marking: {
type: Object,
default() {
return {
enable: false,
disableMultiSelect: false,
useAlternateControlBar: false,
rowName: '',
rowNamePlural: ""
};
}
},
enableLegacyToolbar: {
type: Boolean,
default: false
},
view: {
type: Object,
required: false,
default() {
return {};
}
}
},
data() {
@ -373,7 +369,8 @@ export default {
isShowingMarkedRowsOnly: false,
enableRegexSearch: {},
hideHeaders: configuration.hideHeaders,
totalNumberOfRows: 0
totalNumberOfRows: 0,
rowContext: {}
};
},
computed: {
@ -461,8 +458,10 @@ export default {
this.scroll = _.throttle(this.scroll, 100);
if (!this.marking.useAlternateControlBar && !this.enableLegacyToolbar) {
this.viewActionsCollection = this.openmct.actions.get(this.objectPath, this.view);
this.initializeViewActions();
this.$nextTick(() => {
this.viewActionsCollection = this.openmct.actions.getActionsCollection(this.objectPath, this.currentView);
this.initializeViewActions();
});
}
this.table.on('object-added', this.addObject);
@ -996,7 +995,8 @@ export default {
unmarkAllRows: this.unmarkAllRows,
togglePauseByButton: this.togglePauseByButton,
expandColumns: this.recalculateColumnWidths,
autosizeColumns: this.autosizeColumns
autosizeColumns: this.autosizeColumns,
row: this.rowContext
};
},
initializeViewActions() {
@ -1027,6 +1027,9 @@ export default {
this.setHeight();
this.calculateTableSize();
this.clearRowsAndRerender();
},
updateViewContext(rowContext) {
this.rowContext = rowContext;
}
}
};

@ -19,34 +19,26 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import TelemetryTableViewProvider from './TelemetryTableViewProvider';
import TableConfigurationViewProvider from './TableConfigurationViewProvider';
import TelemetryTableType from './TelemetryTableType';
import TelemetryTableViewActions from './ViewActions';
define([
'./TelemetryTableViewProvider',
'./TableConfigurationViewProvider',
'./TelemetryTableType',
'./ViewActions'
], function (
TelemetryTableViewProvider,
TableConfigurationViewProvider,
TelemetryTableType,
TelemetryTableViewActions
) {
return function plugin() {
return function install(openmct) {
openmct.objectViews.addProvider(new TelemetryTableViewProvider(openmct));
openmct.inspectorViews.addProvider(new TableConfigurationViewProvider(openmct));
openmct.types.addType('table', TelemetryTableType);
openmct.composition.addPolicy((parent, child) => {
if (parent.type === 'table') {
return Object.prototype.hasOwnProperty.call(child, 'telemetry');
} else {
return true;
}
});
export default function plugin() {
return function install(openmct) {
openmct.objectViews.addProvider(new TelemetryTableViewProvider(openmct));
openmct.inspectorViews.addProvider(new TableConfigurationViewProvider(openmct));
openmct.types.addType('table', TelemetryTableType);
openmct.composition.addPolicy((parent, child) => {
if (parent.type === 'table') {
return Object.prototype.hasOwnProperty.call(child, 'telemetry');
} else {
return true;
}
});
TelemetryTableViewActions.default.forEach(action => {
openmct.actions.register(action);
});
};
TelemetryTableViewActions.forEach(action => {
openmct.actions.register(action);
});
};
});
}

@ -203,7 +203,7 @@ describe("the plugin", () => {
tableView = tableViewProvider.view(testTelemetryObject, [testTelemetryObject]);
tableView.show(child, true);
tableInstance = tableView._getTable();
tableInstance = tableView.getTable();
return telemetryPromise.then(() => Vue.nextTick());
});

@ -151,7 +151,7 @@ export default {
cssClass: 'icon-history',
name,
description,
callBack: () => this.selectTimespan(timespan)
onItemClicked: () => this.selectTimespan(timespan)
};
});
@ -160,7 +160,7 @@ export default {
description: 'Past timeframes, ordered by latest first',
isDisabled: true,
name: 'Past timeframes, ordered by latest first',
callBack: () => {}
onItemClicked: () => {}
});
return history;
@ -171,7 +171,7 @@ export default {
cssClass: 'icon-clock',
name: preset.label,
description: preset.label,
callBack: () => this.selectPresetBounds(preset.bounds)
onItemClicked: () => this.selectPresetBounds(preset.bounds)
};
});
},

@ -104,7 +104,7 @@ export default {
name: 'Fixed Timespan',
description: 'Query and explore data that falls between two fixed datetimes.',
cssClass: 'icon-tabular',
callBack: () => this.setOption(key)
onItemClicked: () => this.setOption(key)
};
} else {
const key = clock.key;
@ -115,7 +115,7 @@ export default {
description: "Monitor streaming data in real-time. The Time "
+ "Conductor and displays will automatically advance themselves based on this clock. " + clock.description,
cssClass: clock.cssClass || 'icon-clock',
callBack: () => this.setOption(key)
onItemClicked: () => this.setOption(key)
};
}
},

@ -70,7 +70,7 @@ export default {
.filter(menuOption => menuOption.clock === (clock && clock.key))
.map(menuOption => {
const timeSystem = JSON.parse(JSON.stringify(this.openmct.time.timeSystems.get(menuOption.timeSystem)));
timeSystem.callBack = () => this.setTimeSystemFromView(timeSystem);
timeSystem.onItemClicked = () => this.setTimeSystemFromView(timeSystem);
return timeSystem;
});

@ -34,15 +34,16 @@ export default class ViewDatumAction {
}
invoke(objectPath, view) {
let viewContext = view.getViewContext && view.getViewContext();
let attributes = viewContext.getDatum && viewContext.getDatum();
const row = viewContext.row;
let attributes = row.getDatum && row.getDatum();
let component = new Vue ({
components: {
MetadataListView
},
provide: {
name: this.name,
attributes
},
components: {
MetadataListView
},
template: '<MetadataListView />'
});
@ -57,9 +58,13 @@ export default class ViewDatumAction {
}
appliesTo(objectPath, view = {}) {
let viewContext = (view.getViewContext && view.getViewContext()) || {};
let datum = viewContext.getDatum;
let enabled = viewContext.viewDatumAction;
const row = viewContext.row;
if (!row) {
return false;
}
let datum = row.getDatum;
let enabled = row.viewDatumAction;
if (enabled && datum) {
return true;
}

@ -57,9 +57,11 @@ describe("the plugin", () => {
mockView = {
getViewContext: () => {
return {
viewDatumAction: true,
getDatum: () => {
return mockDatum;
row: {
viewDatumAction: true,
getDatum: () => {
return mockDatum;
}
}
};
}

@ -0,0 +1,29 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
import ViewLargeAction from './viewLargeAction.js';
export default function plugin() {
return function install(openmct) {
openmct.actions.register(new ViewLargeAction(openmct));
};
}

@ -0,0 +1,115 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
import PreviewHeader from '@/ui/preview/preview-header.vue';
import Vue from 'vue';
export default class ViewLargeAction {
constructor(openmct) {
this.openmct = openmct;
this.cssClass = 'icon-items-expand';
this.description = 'View Large';
this.group = 'windowing';
this.key = 'large.view';
this.name = 'Large View';
this.priority = 1;
this.showInStatusBar = true;
}
invoke(objectPath, view) {
const parentElement = view.parentElement;
let childElement = parentElement && parentElement.firstChild;
if (!childElement) {
const message = "ViewLargeAction: missing element";
this.openmct.notifications.error(message);
throw new Error(message);
}
this._expand(objectPath, childElement, view);
}
appliesTo(objectPath, view = {}) {
const parentElement = view.parentElement;
const element = parentElement && parentElement.firstChild;
const viewLargeAction = element && !element.classList.contains('js-main-container')
&& !this._isNavigatedObject(objectPath);
return viewLargeAction;
}
_expand(objectPath, childElement, view) {
const parentElement = childElement.parentElement;
this.overlay = this.openmct.overlays.overlay({
element: this._getOverlayElement(objectPath, childElement, view),
size: 'large',
onDestroy() {
parentElement.append(childElement);
}
});
}
_getOverlayElement(objectPath, childElement, view) {
const fragment = new DocumentFragment();
const header = this._getPreviewHeader(objectPath, view);
fragment.append(header);
const wrapper = document.createElement('div');
wrapper.classList.add('l-preview-window__object-view');
wrapper.append(childElement);
fragment.append(wrapper);
return fragment;
}
_getPreviewHeader(objectPath, view) {
const domainObject = objectPath[0];
const actionCollection = this.openmct.actions.getActionsCollection(objectPath, view);
const preview = new Vue({
components: {
PreviewHeader
},
provide: {
openmct: this.openmct,
objectPath: this.objectPath
},
data() {
return {
domainObject,
actionCollection
};
},
template: '<PreviewHeader :actionCollection="actionCollection" :domainObject="domainObject" :hideViewSwitcher="true" :showNotebookMenuSwitcher="true"></PreviewHeader>'
});
return preview.$mount().$el;
}
_isNavigatedObject(objectPath) {
let targetObject = objectPath[0];
let navigatedObject = this.openmct.router.path[0];
return this.openmct.objects.areIdsEqual(targetObject.identifier, navigatedObject.identifier);
}
}

@ -56,26 +56,19 @@
'has-complex-content': complexContent
}"
>
<div class="c-so-view__frame-controls__btns">
<div v-if="statusBarItems.length > 0"
class="c-so-view__frame-controls__btns"
>
<button
v-for="(item, index) in statusBarItems"
:key="index"
class="c-icon-button"
:class="item.cssClass"
:title="item.name"
@click="item.callBack"
@click="item.onItemClicked"
>
<span class="c-icon-button__label">{{ item.name }}</span>
</button>
<button
class="c-icon-button icon-items-expand"
title="View Large"
@click="expand"
>
<span class="c-icon-button__label">View Large</span>
</button>
</div>
<button
class="c-icon-button icon-3-dots c-so-view__frame-controls__more"
@ -87,7 +80,7 @@
<object-view
ref="objectView"
class="c-so-view__object-view"
class="c-so-view__object-view js-object-view"
:show-edit-view="showEditView"
:object-path="objectPath"
:layout-font-size="layoutFontSize"
@ -99,8 +92,6 @@
<script>
import ObjectView from './ObjectView.vue';
import PreviewHeader from '@/ui/preview/preview-header.vue';
import Vue from 'vue';
const SIMPLE_CONTENT_TYPES = [
'clock',
@ -160,7 +151,8 @@ export default {
mounted() {
this.status = this.openmct.status.get(this.domainObject.identifier);
this.removeStatusListener = this.openmct.status.observe(this.domainObject.identifier, this.setStatus);
this.$refs.objectView.show(this.domainObject, undefined, false, this.objectPath);
const provider = this.openmct.objectViews.get(this.domainObject, this.objectPath)[0];
this.$refs.objectView.show(this.domainObject, provider.key, false, this.objectPath);
},
beforeDestroy() {
this.removeStatusListener();
@ -170,52 +162,6 @@ export default {
}
},
methods: {
expand() {
let objectView = this.$refs.objectView;
let parentElement = objectView.$el;
let childElement = parentElement.children[0];
this.openmct.overlays.overlay({
element: this.getOverlayElement(childElement),
size: 'large',
onDestroy() {
parentElement.append(childElement);
}
});
},
getOverlayElement(childElement) {
const fragment = new DocumentFragment();
const header = this.getPreviewHeader();
const wrapper = document.createElement('div');
wrapper.classList.add('l-preview-window__object-view');
wrapper.append(childElement);
fragment.append(header);
fragment.append(wrapper);
return fragment;
},
getPreviewHeader() {
const domainObject = this.objectPath[0];
const actionCollection = this.actionCollection;
const preview = new Vue({
components: {
PreviewHeader
},
provide: {
openmct: this.openmct,
objectPath: this.objectPath
},
data() {
return {
domainObject,
actionCollection
};
},
template: '<PreviewHeader :actionCollection="actionCollection" :domainObject="domainObject" :hideViewSwitcher="true" :showNotebookMenuSwitcher="true"></PreviewHeader>'
});
return preview.$mount().$el;
},
getSelectionContext() {
return this.$refs.objectView.getSelectionContext();
},
@ -233,12 +179,14 @@ export default {
delete this.actionCollection;
},
updateActionItems(actionItems) {
this.statusBarItems = this.actionCollection.getStatusBarActions();
const statusBarItems = this.actionCollection.getStatusBarActions();
this.statusBarItems = this.openmct.menus.actionsToMenuItems(statusBarItems, this.actionCollection.objectPath, this.actionCollection.view);
this.menuActionItems = this.actionCollection.getVisibleActions();
},
showMenuItems(event) {
let sortedActions = this.openmct.actions._groupAndSortActions(this.menuActionItems);
this.openmct.menus.showMenu(event.x, event.y, sortedActions);
const sortedActions = this.openmct.actions._groupAndSortActions(this.menuActionItems);
const menuItems = this.openmct.menus.actionsToMenuItems(sortedActions, this.actionCollection.objectPath, this.actionCollection.view);
this.openmct.menus.showMenu(event.x, event.y, menuItems);
},
setStatus(status) {
this.status = status;

@ -209,7 +209,6 @@ export default {
}
}
this.getActionCollection();
this.currentView.show(this.viewContainer, this.openmct.editor.isEditing());
if (immediatelySelect) {
@ -218,13 +217,17 @@ export default {
}
this.openmct.objectViews.on('clearData', this.clearData);
this.$nextTick(() => {
this.getActionCollection();
});
},
getActionCollection() {
if (this.actionCollection) {
this.actionCollection.destroy();
}
this.actionCollection = this.openmct.actions._get(this.currentObjectPath || this.objectPath, this.currentView);
this.actionCollection = this.openmct.actions.getActionsCollection(this.currentObjectPath || this.objectPath, this.currentView);
this.$emit('change-action-collection', this.actionCollection);
},
show(object, viewKey, immediatelySelect, currentObjectPath) {
@ -318,7 +321,6 @@ export default {
return viewKey;
},
getViewProvider() {
let provider = this.openmct.objectViews.getByProviderKey(this.getViewKey());
if (!provider) {

@ -63,7 +63,7 @@ export default {
cssClass: font.cssClass || '',
name: font.name,
description: font.name,
callBack: () => this.setFont(font.value)
onItemClicked: () => this.setFont(font.value)
};
});
},
@ -73,7 +73,7 @@ export default {
cssClass: fontSize.cssClass || '',
name: fontSize.name,
description: fontSize.name,
callBack: () => this.setFontSize(fontSize.value)
onItemClicked: () => this.setFontSize(fontSize.value)
};
});
}

@ -32,7 +32,7 @@
</div>
<div class="l-browse-bar__end">
<view-switcher
<ViewSwitcher
v-if="!isEditing"
:current-view="currentView"
:views="views"
@ -49,7 +49,7 @@
:key="index"
class="c-button"
:class="item.cssClass"
@click="item.callBack"
@click="item.onItemClicked"
>
</button>
@ -172,9 +172,7 @@ export default {
key: p.key,
cssClass: p.cssClass,
name: p.name,
callBack: () => {
return this.setView({key: p.key});
}
onItemClicked: () => this.setView({key: p.key})
};
});
},
@ -339,12 +337,14 @@ export default {
this.openmct.router.navigate(this.parentUrl);
},
updateActionItems(actionItems) {
this.statusBarItems = this.actionCollection.getStatusBarActions();
const statusBarItems = this.actionCollection.getStatusBarActions();
this.statusBarItems = this.openmct.menus.actionsToMenuItems(statusBarItems, this.actionCollection.objectPath, this.actionCollection.view);
this.menuActionItems = this.actionCollection.getVisibleActions();
},
showMenuItems(event) {
let sortedActions = this.openmct.actions._groupAndSortActions(this.menuActionItems);
this.openmct.menus.showMenu(event.x, event.y, sortedActions);
const sortedActions = this.openmct.actions._groupAndSortActions(this.menuActionItems);
const menuItems = this.openmct.menus.actionsToMenuItems(sortedActions, this.actionCollection.objectPath, this.actionCollection.view);
this.openmct.menus.showMenu(event.x, event.y, menuItems);
},
unlistenToActionCollection() {
this.actionCollection.off('update', this.updateActionItems);

@ -28,7 +28,7 @@ export default {
cssClass: menuItem.cssClass,
name: menuItem.name,
description: menuItem.description,
callBack: () => this.create(key)
onItemClicked: () => this.create(key)
};
items.push(menuItemTemplate);

@ -90,7 +90,7 @@
/>
<object-view
ref="browseObject"
class="l-shell__main-container"
class="l-shell__main-container js-main-container"
data-selectable
:show-edit-view="true"
@change-action-collection="setActionCollection"

@ -36,7 +36,7 @@ export default {
event.preventDefault();
event.stopPropagation();
let actionsCollection = this.openmct.actions.get(this.objectPath);
let actionsCollection = this.openmct.actions.getActionsCollection(this.objectPath);
let actions = actionsCollection.getVisibleActions();
let sortedActions = this.openmct.actions._groupAndSortActions(actions);
@ -44,7 +44,8 @@ export default {
onDestroy: this.onContextMenuDestroyed
};
this.openmct.menus.showMenu(event.clientX, event.clientY, sortedActions, menuOptions);
const menuItems = this.openmct.menus.actionsToMenuItems(sortedActions, actionsCollection.objectPath, actionsCollection.view);
this.openmct.menus.showMenu(event.clientX, event.clientY, menuItems, menuOptions);
this.contextClickActive = true;
this.$emit('context-click-active', true);
},

@ -20,7 +20,7 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
<template>
<div class="l-preview-window">
<div class="l-preview-window js-preview-window">
<PreviewHeader
:current-view="currentView"
:action-collection="actionCollection"
@ -116,7 +116,7 @@ export default {
this.actionCollection.destroy();
}
this.actionCollection = this.openmct.actions._get(this.objectPath, this.view);
this.actionCollection = this.openmct.actions.getActionsCollection(this.objectPath, this.view);
},
initObjectStyles() {
if (!this.styleRuleManager) {

@ -75,8 +75,13 @@ export default class PreviewAction {
PreviewAction.isVisible = true;
}
appliesTo(objectPath) {
return !PreviewAction.isVisible && !this._isNavigatedObject(objectPath);
appliesTo(objectPath, view = {}) {
const parentElement = view.parentElement;
const isObjectView = parentElement && parentElement.classList.contains('js-object-view');
return !PreviewAction.isVisible
&& !this._isNavigatedObject(objectPath)
&& !isObjectView;
}
_isNavigatedObject(objectPath) {
@ -85,6 +90,7 @@ export default class PreviewAction {
return this._openmct.objects.areIdsEqual(targetObject.identifier, navigatedObject.identifier);
}
_preventPreview(objectPath) {
const noPreviewTypes = ['folder'];

@ -36,10 +36,9 @@ export default class ViewHistoricalDataAction extends PreviewAction {
appliesTo(objectPath, view = {}) {
let viewContext = view.getViewContext && view.getViewContext();
if (objectPath.length && viewContext && viewContext.viewHistoricalData) {
return true;
} else {
return false;
}
return objectPath.length
&& viewContext
&& viewContext.row
&& viewContext.row.viewHistoricalData;
}
}

@ -24,7 +24,7 @@
:key="index"
class="c-button"
:class="item.cssClass"
@click="item.callBack"
@click="item.onItemClicked"
>
</button>
<button
@ -42,7 +42,8 @@ import ViewSwitcher from '../../ui/layout/ViewSwitcher.vue';
const HIDDEN_ACTIONS = [
'remove',
'move',
'preview'
'preview',
'large.view'
];
export default {
@ -114,6 +115,19 @@ export default {
}
},
methods: {
filterHiddenItems(menuItems) {
const items = [];
menuItems.forEach(menuItem => {
const isGroup = Array.isArray(menuItem);
if (isGroup) {
items.push(this.filterHiddenItems(menuItem));
} else if (!HIDDEN_ACTIONS.includes(menuItem.key)) {
items.push(menuItem);
}
});
return items;
},
setView(view) {
this.$emit('setView', view);
},
@ -122,12 +136,16 @@ export default {
delete this.actionCollection;
},
updateActionItems() {
this.statusBarItems = this.actionCollection.getStatusBarActions();
const statusBarItems = this.actionCollection.getStatusBarActions();
const menuItems = this.openmct.menus.actionsToMenuItems(statusBarItems, this.actionCollection.objectPath, this.actionCollection.view);
this.statusBarItems = this.filterHiddenItems(menuItems);
this.menuActionItems = this.actionCollection.getVisibleActions();
},
showMenuItems(event) {
let sortedActions = this.openmct.actions._groupAndSortActions(this.menuActionItems);
this.openmct.menus.showMenu(event.x, event.y, sortedActions);
const menuItems = this.openmct.menus.actionsToMenuItems(sortedActions, this.actionCollection.objectPath, this.actionCollection.view);
const visibleMenuItems = this.filterHiddenItems(menuItems);
this.openmct.menus.showMenu(event.x, event.y, visibleMenuItems);
}
}
};

@ -86,6 +86,18 @@ define(['EventEmitter'], function (EventEmitter) {
console.warn("Provider already defined for key '%s'. Provider keys must be unique.", key);
}
const wrappedView = provider.view.bind(provider);
provider.view = (domainObject, objectPath) => {
const viewObject = wrappedView(domainObject, objectPath);
const wrappedShow = viewObject.show.bind(viewObject);
viewObject.show = (element, isEditing) => {
viewObject.parentElement = element.parentElement;
wrappedShow(element, isEditing);
};
return viewObject;
};
this.providers[key] = provider;
};