Compare commits

...

7 Commits

Author SHA1 Message Date
Joshi
5283d06e05 Save window location 2020-05-18 10:43:18 -07:00
Joshi
888695f636 Saves indicator buttons 2020-05-14 14:37:30 -07:00
Joshi
9f0af90663 WIP - Open 2 windows, close them and open the saved 2 windows 2020-05-14 11:53:53 -07:00
Joshi
e93d36ff19 WIP windowLayouts 2020-05-12 10:43:20 -07:00
Shefali Joshi
4a87a5d847 Show object styles in preview modal (#3018)
* Adds conditional styles to Preview window
2020-05-11 14:25:39 -07:00
Deep Tailor
421c09ec2c Allow users to lazy load Tabs (#2958)
* lazy load tabs

* remove listener on destroy

* fix lint error

* Store current tab position on domainObject

* remove lodash dependency and use keystring
2020-05-08 10:36:13 -07:00
Nikhil
0679b246b8 [Notebook]: Remove default section/page from localstorage on notebook delete (#2900)
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-05-07 16:13:32 -07:00
10 changed files with 390 additions and 31 deletions

View File

@@ -82,6 +82,7 @@
openmct.install(openmct.plugins.Filters(['table', 'telemetry.plot.overlay']));
openmct.install(openmct.plugins.ObjectMigration());
openmct.install(openmct.plugins.ClearData(['table', 'telemetry.plot.overlay', 'telemetry.plot.stacked']));
openmct.install(openmct.plugins.WindowLayout());
openmct.start();
</script>
</html>

View File

@@ -29,7 +29,7 @@
<script>
import Snapshot from '../snapshot';
import { clearDefaultNotebook, getDefaultNotebook } from '../utils/notebook-storage';
import { getDefaultNotebook } from '../utils/notebook-storage';
import { NOTEBOOK_DEFAULT, NOTEBOOK_SNAPSHOT } from '../notebook-constants';
export default {
@@ -72,28 +72,22 @@ export default {
methods: {
async setNotebookTypes() {
const notebookTypes = [];
let defaultPath = '';
const defaultNotebook = getDefaultNotebook();
if (defaultNotebook) {
const domainObject = await this.openmct.objects.get(defaultNotebook.notebookMeta.identifier)
.then(d => d);
const domainObject = defaultNotebook.domainObject;
if (!domainObject.location) {
clearDefaultNotebook();
} else {
defaultPath = `${domainObject.name} - ${defaultNotebook.section.name} - ${defaultNotebook.page.name}`;
if (domainObject.location) {
const defaultPath = `${domainObject.name} - ${defaultNotebook.section.name} - ${defaultNotebook.page.name}`;
notebookTypes.push({
cssClass: 'icon-notebook',
name: `Save to Notebook ${defaultPath}`,
type: NOTEBOOK_DEFAULT
});
}
}
if (defaultPath.length !== 0) {
notebookTypes.push({
cssClass: 'icon-notebook',
name: `Save to Notebook ${defaultPath}`,
type: NOTEBOOK_DEFAULT
});
}
notebookTypes.push({
cssClass: 'icon-notebook',
name: 'Save to Notebook Snapshots',

View File

@@ -239,6 +239,7 @@ export default {
const section = this.getSelectedSection();
return {
domainObject: this.internalDomainObject,
notebookMeta,
section,
page
@@ -440,7 +441,7 @@ export default {
async updateDefaultNotebook(notebookStorage) {
const defaultNotebookObject = await this.getDefaultNotebookObject();
this.removeDefaultClass(defaultNotebookObject);
setDefaultNotebook(notebookStorage);
setDefaultNotebook(this.openmct, notebookStorage);
this.addDefaultClass();
this.defaultSectionId = notebookStorage.section.id;
this.defaultPageId = notebookStorage.page.id;

View File

@@ -1,6 +1,46 @@
const NOTEBOOK_LOCAL_STORAGE = 'notebook-storage';
let currentNotebookObject = null;
let unlisten = null;
function defaultNotebookObjectChanged(newDomainObject) {
if (newDomainObject.location !== null) {
currentNotebookObject = newDomainObject;
const notebookStorage = getDefaultNotebook();
notebookStorage.domainObject = newDomainObject;
saveDefaultNotebook(notebookStorage);
return;
}
if (unlisten) {
unlisten();
unlisten = null;
}
clearDefaultNotebook();
}
function observeDefaultNotebookObject(openmct, notebookStorage) {
const domainObject = notebookStorage.domainObject;
if (currentNotebookObject
&& currentNotebookObject.identifier.key === domainObject.identifier.key) {
return;
}
if (unlisten) {
unlisten();
unlisten = null;
}
unlisten = openmct.objects.observe(notebookStorage.domainObject, '*', defaultNotebookObjectChanged);
}
function saveDefaultNotebook(notebookStorage) {
window.localStorage.setItem(NOTEBOOK_LOCAL_STORAGE, JSON.stringify(notebookStorage));
}
export function clearDefaultNotebook() {
currentNotebookObject = null;
window.localStorage.setItem(NOTEBOOK_LOCAL_STORAGE, null);
}
@@ -10,20 +50,21 @@ export function getDefaultNotebook() {
return JSON.parse(notebookStorage);
}
export function setDefaultNotebook(notebookStorage) {
window.localStorage.setItem(NOTEBOOK_LOCAL_STORAGE, JSON.stringify(notebookStorage));
export function setDefaultNotebook(openmct, notebookStorage) {
observeDefaultNotebookObject(openmct, notebookStorage);
saveDefaultNotebook(notebookStorage);
}
export function setDefaultNotebookSection(section) {
const notebookStorage = getDefaultNotebook();
notebookStorage.section = section;
window.localStorage.setItem(NOTEBOOK_LOCAL_STORAGE, JSON.stringify(notebookStorage));
saveDefaultNotebook(notebookStorage);
}
export function setDefaultNotebookPage(page) {
const notebookStorage = getDefaultNotebook();
notebookStorage.page = page;
window.localStorage.setItem(NOTEBOOK_LOCAL_STORAGE, JSON.stringify(notebookStorage));
saveDefaultNotebook(notebookStorage);
}

View File

@@ -51,7 +51,8 @@ define([
'./conditionWidget/plugin',
'./themes/espresso',
'./themes/maelstrom',
'./themes/snow'
'./themes/snow',
'./windowLayout/plugin'
], function (
_,
UTCTimeSystem,
@@ -83,7 +84,8 @@ define([
ConditionWidgetPlugin,
Espresso,
Maelstrom,
Snow
Snow,
WindowLayout
) {
var bundleMap = {
LocalStorage: 'platform/persistence/local',
@@ -192,6 +194,7 @@ define([
plugins.Snow = Snow.default;
plugins.Condition = ConditionPlugin.default;
plugins.ConditionWidget = ConditionWidgetPlugin.default;
plugins.WindowLayout = WindowLayout.default;
return plugins;
});

View File

@@ -27,7 +27,7 @@
{'is-current': isCurrent(tab)},
tab.type.definition.cssClass
]"
@click="showTab(tab)"
@click="showTab(tab, index)"
>
<span class="c-button__label">{{ tab.domainObject.name }}</span>
</button>
@@ -48,6 +48,7 @@
</div>
</div>
<object-view
v-if="internalDomainObject.keep_alive ? currentTab : isCurrent(tab)"
class="c-tabs-view__object"
:object="tab.domainObject"
/>
@@ -57,7 +58,6 @@
<script>
import ObjectView from '../../../ui/components/ObjectView.vue';
import _ from 'lodash';
var unknownObjectType = {
definition: {
@@ -73,6 +73,7 @@ export default {
},
data: function () {
return {
internalDomainObject: this.domainObject,
currentTab: {},
tabsList: [],
setCurrentTab: true,
@@ -85,9 +86,17 @@ export default {
this.composition.on('add', this.addItem);
this.composition.on('remove', this.removeItem);
this.composition.on('reorder', this.onReorder);
this.composition.load();
this.composition.load().then(() => {
let currentTabIndex = this.domainObject.currentTabIndex;
if (currentTabIndex !== undefined && this.tabsList.length > currentTabIndex) {
this.currentTab = this.tabsList[currentTabIndex];
}
});
}
this.unsubscribe = this.openmct.objects.observe(this.internalDomainObject, '*', this.updateInternalDomainObject);
document.addEventListener('dragstart', this.dragstart);
document.addEventListener('dragend', this.dragend);
},
@@ -96,18 +105,25 @@ export default {
this.composition.off('remove', this.removeItem);
this.composition.off('reorder', this.onReorder);
this.unsubscribe();
document.removeEventListener('dragstart', this.dragstart);
document.removeEventListener('dragend', this.dragend);
},
methods:{
showTab(tab) {
showTab(tab, index) {
if (index !== undefined) {
this.storeCurrentTabIndex(index);
}
this.currentTab = tab;
},
addItem(domainObject) {
let type = this.openmct.types.get(domainObject.type) || unknownObjectType,
tabItem = {
domainObject,
type: type
type: type,
key: this.openmct.objects.makeKeyString(domainObject.identifier)
};
this.tabsList.push(tabItem);
@@ -126,7 +142,7 @@ export default {
this.tabsList.splice(pos, 1);
if (this.isCurrent(tabToBeRemoved)) {
this.showTab(this.tabsList[this.tabsList.length - 1]);
this.showTab(this.tabsList[this.tabsList.length - 1], this.tabsList.length - 1);
}
},
onReorder(reorderPlan) {
@@ -138,6 +154,7 @@ export default {
},
onDrop(e) {
this.setCurrentTab = true;
this.storeCurrentTabIndex(this.tabsList.length);
},
dragstart(e) {
if (e.dataTransfer.types.includes('openmct/domain-object-path')) {
@@ -155,7 +172,13 @@ export default {
this.allowDrop = false;
},
isCurrent(tab) {
return _.isEqual(this.currentTab, tab)
return this.currentTab.key === tab.key;
},
updateInternalDomainObject(domainObject) {
this.internalDomainObject = domainObject;
},
storeCurrentTabIndex(index) {
this.openmct.objects.mutate(this.internalDomainObject, 'currentTabIndex', index);
}
}
}

View File

@@ -36,7 +36,27 @@ define([
cssClass: 'icon-tabs-view',
initialize(domainObject) {
domainObject.composition = [];
}
domainObject.keep_alive = true;
},
form: [
{
"key": "keep_alive",
"name": "Keep Tabs Alive",
"control": "select",
"options": [
{
'name': 'True',
'value': true
},
{
'name': 'False',
'value': false
}
],
"required": true,
"cssClass": "l-input"
}
]
});
};
};

View File

@@ -0,0 +1,183 @@
<template>
<div v-if="!isOpener"
class="c-indicator c-indicator--clickable icon-save s-status-caution"
>
<span class="label c-indicator__label">
<button @click="open2Windows">Open 2 Windows</button>
<button @click="closeAllWindows">Close Windows</button>
<button @click="openSavedWindows">Open Saved Windows</button>
</span>
</div>
</template>
<script>
export default {
inject: ['openmct'],
computed: {
isOpener() {
return window.opener;
}
},
mounted() {
this.openWindows = {};
this.windowFeatures = "menubar=yes,location=yes,tabs=yes, resizable=yes,scrollbars=yes, innerHeight=480,innerWidth=640,screenY=100";
window.addEventListener("message", this.receiveMessage, false);
if (window.opener) {
window.opener.postMessage({
status: 'READY',
name: window.name
},'*');
window.addEventListener('beforeunload', () => {
window.opener.postMessage({
status: 'CLOSED',
name: window.name
},'*');
});
window.addEventListener('blur', () => {
window.opener.postMessage({
query: 'QUERY__SIZE',
info: {
innerHeight: window.innerHeight,
innerWidth: window.innerWidth,
screenLeft: window.screenLeft,
screenTop: window.screenTop,
screenWidth: screen.width,
screenHeight: screen.height,
screenAvailableWidth: screen.availWidth,
screenAvailHeight: screen.availHeight,
devicePixelRatio: window.devicePixelRatio,
url: window.location.href
},
name: window.name
},'*');
});
}
},
methods: {
persistWindowInformation() {
window.localStorage.setItem('openmct-windowlayout-items',
JSON.stringify(this.getOpenWindowSpecifications()));
},
getScreenX() {
return !Object.keys(this.openWindows).length ? 100: (screen.width - (Object.keys(this.openWindows).length*640));
},
open2Windows() {
this.openWindow(`${this.windowFeatures},screenX=${this.getScreenX()}`);
this.openWindow(`${this.windowFeatures},screenX=${this.getScreenX()}`);
},
openWindow(windowFeatures) {
let newWindowName = `Open MCT Window ${Object.keys(this.openWindows).length+1}`;
this.openWindows[newWindowName] = { windowReference: window.open(window.location.href, newWindowName, windowFeatures)};
},
moveWindow() {
const key = Object.keys(this.openWindows)[0];
this.openWindows[key].postMessage({
command: 'moveTo',
params: [window.screenLeft + 40, window.screenTop + 40]
}, 'http://localhost:8080');
},
openSavedWindows() {
const persistedWindowObjs = window.localStorage.getItem('openmct-windowlayout-items');
if (persistedWindowObjs) {
const windowObjs = JSON.parse(persistedWindowObjs);
windowObjs.forEach(windowObj => {
let newWindowName = windowObj.name;
const features = `menubar=yes,location=yes,resizable=yes,scrollbars=yes,innerHeight=${windowObj.info.innerHeight || 480},innerWidth=${windowObj.info.innerWidth || 640},screenX=${windowObj.info.screenLeft || this.getScreenX()},screenY=${windowObj.info.screenTop || 100}`;
const windowReference = window.open((windowObj.info.url || window.location.href), newWindowName, features);
this.openWindows[newWindowName] = {
windowReference,
name: newWindowName,
info: windowObj.info
}
});
}
},
closeAllWindows() {
this.persistWindowInformation();
Object.keys(this.openWindows).forEach((windowName => {
const windowObj = this.openWindows[windowName];
windowObj.closedByOpener = true;
windowObj.windowReference.close();
}));
},
receiveMessage(event) {
const { data, origin, source} = event;
switch(origin) {
case 'http://localhost:8080':
if (data) {
if (data.status === 'READY') {
let newWindowReference = this.openWindows[data.name].windowReference;
newWindowReference.postMessage({
command: 'QUERY__SIZE'
}, 'http://localhost:8080');
} else if (data.status === 'CLOSED') {
const closedByOpener = this.openWindows[data.name].closedByOpener;
this.openWindows[data.name] = undefined;
delete this.openWindows[data.name];
if (!closedByOpener) {
this.persistWindowInformation();
}
} else if (data.command) {
switch(data.command) {
case 'QUERY__SIZE':
source.postMessage({
query: data.command,
info: {
innerHeight: window.innerHeight,
innerWidth: window.innerWidth,
screenLeft: window.screenLeft,
screenTop: window.screenTop,
screenWidth: screen.width,
screenHeight: screen.height,
screenAvailableWidth: screen.availWidth,
screenAvailHeight: screen.availHeight,
devicePixelRatio: window.devicePixelRatio,
url: window.location.href
},
name: window.name
}, origin);
break;
default:
window[data.command](...data.params);
this.$nextTick(() => {
source.postMessage({
query: data.command,
name: window.name
}, origin);
});
break;
}
} else if (data.query) {
if (data.info) {
if (!this.openWindows[data.name]) {
return;
}
this.openWindows[data.name].info = data.info;
this.persistWindowInformation();
} else {
if (!this.openWindows[data.name]) {
return;
}
let newWindowReference = this.openWindows[data.name].windowReference;
newWindowReference.postMessage({
command: 'QUERY__SIZE'
}, 'http://localhost:8080');
}
}
}
break;
default:
break;
}
},
getOpenWindowSpecifications() {
return Object.keys(this.openWindows).map(key => {
return {
name: key,
info: this.openWindows[key].info
}
});
}
}
}
</script>

View File

@@ -0,0 +1,43 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2019, 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 Vue from 'vue';
import WindowLayout from "./components/WindowLayout.vue";
export default function plugin() {
return function install(openmct) {
let component = new Vue ({
provide: {
openmct
},
components: {
WindowLayout: WindowLayout
},
template: '<WindowLayout></WindowLayout>'
}),
indicator = {
element: component.$mount().$el
};
openmct.indicators.add(indicator);
};
}

View File

@@ -35,6 +35,8 @@
<script>
import PreviewHeader from './preview-header.vue';
import {STYLE_CONSTANTS} from "@/plugins/condition/utils/constants";
import StyleRuleManager from "@/plugins/condition/StyleRuleManager";
export default {
components: {
@@ -69,6 +71,14 @@ export default {
},
destroyed() {
this.view.destroy();
if (this.stopListeningStyles) {
this.stopListeningStyles();
}
if (this.styleRuleManager) {
this.styleRuleManager.destroy();
delete this.styleRuleManager;
}
},
methods: {
clear() {
@@ -90,6 +100,46 @@ export default {
this.view = this.currentView.view(this.domainObject, this.objectPath);
this.view.show(this.viewContainer, false);
this.initObjectStyles();
},
initObjectStyles() {
if (!this.styleRuleManager) {
this.styleRuleManager = new StyleRuleManager((this.domainObject.configuration && this.domainObject.configuration.objectStyles), this.openmct, this.updateStyle.bind(this));
} else {
this.styleRuleManager.updateObjectStyleConfig(this.domainObject.configuration && this.domainObject.configuration.objectStyles);
}
if (this.stopListeningStyles) {
this.stopListeningStyles();
}
this.stopListeningStyles = this.openmct.objects.observe(this.domainObject, 'configuration.objectStyles', (newObjectStyle) => {
//Updating styles in the inspector view will trigger this so that the changes are reflected immediately
this.styleRuleManager.updateObjectStyleConfig(newObjectStyle);
});
},
updateStyle(styleObj) {
if (!styleObj) {
return;
}
let keys = Object.keys(styleObj);
keys.forEach(key => {
let firstChild = this.$refs.objectView.querySelector(':first-child');
if (firstChild) {
if ((typeof styleObj[key] === 'string') && (styleObj[key].indexOf('__no_value') > -1)) {
if (firstChild.style[key]) {
firstChild.style[key] = '';
}
} else {
if (!styleObj.isStyleInvisible && firstChild.classList.contains(STYLE_CONSTANTS.isStyleInvisible)) {
firstChild.classList.remove(STYLE_CONSTANTS.isStyleInvisible);
} else if (styleObj.isStyleInvisible && !firstChild.classList.contains(styleObj.isStyleInvisible)) {
firstChild.classList.add(styleObj.isStyleInvisible);
}
firstChild.style[key] = styleObj[key];
}
}
});
}
}
}