mirror of
https://github.com/nasa/openmct.git
synced 2024-12-22 06:27:48 +00:00
Initial implementation of Edit mode (#2181)
* Adding edit mode API and buttons * Select navigated object by default. Enable table configuration in edit mode. * Fixed issue with Table configuration * Removed debugging code * Added basic documentation to Editor API * use selectable, table is selection[0], inspector cleans up Update browse to set selection using openmct.selection.selectable on navation so that selection contexts can be determined correctly. Update table configuration to reference the first item in selection instead of the last item in selection when displaying. Update inspector code to remove display node from document on destroy. * properly remove capturing handler * inspector views respond to editing InspectorViews respond to editing instead of openmct rerendering the inspector on edit.
This commit is contained in:
parent
56b9708ab7
commit
3c324cbea0
12
src/MCT.js
12
src/MCT.js
@ -223,6 +223,8 @@ define([
|
||||
|
||||
this.Dialog = api.Dialog;
|
||||
|
||||
this.editor = new api.EditorAPI.default(this);
|
||||
|
||||
this.legacyRegistry = defaultRegistry;
|
||||
this.install(this.plugins.Plot());
|
||||
this.install(this.plugins.TelemetryTable());
|
||||
@ -310,13 +312,17 @@ define([
|
||||
this.$injector.get('objectService');
|
||||
|
||||
var appLayout = new Vue({
|
||||
mixins: [Layout.default],
|
||||
components: {
|
||||
'Layout': Layout.default
|
||||
},
|
||||
provide: {
|
||||
openmct: this
|
||||
}
|
||||
},
|
||||
template: '<Layout ref="layout"></Layout>'
|
||||
});
|
||||
domElement.appendChild(appLayout.$mount().$el);
|
||||
this.layout = appLayout;
|
||||
|
||||
this.layout = appLayout.$refs.layout;
|
||||
Browse(this);
|
||||
this.router.start();
|
||||
this.emit('start');
|
||||
|
83
src/api/Editor.js
Normal file
83
src/api/Editor.js
Normal file
@ -0,0 +1,83 @@
|
||||
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2018, 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 EventEmitter from 'EventEmitter';
|
||||
|
||||
export default class Editor extends EventEmitter {
|
||||
constructor(openmct) {
|
||||
super();
|
||||
this.editing = false;
|
||||
this.openmct = openmct;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate an editing session. This will start a transaction during
|
||||
* which any persist operations will be deferred until either save()
|
||||
* or finish() are called.
|
||||
*/
|
||||
edit() {
|
||||
this.editing = true;
|
||||
this.getTransactionService().startTransaction();
|
||||
this.emit('isEditing', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns true if the application is in edit mode, false otherwise.
|
||||
*/
|
||||
isEditing() {
|
||||
return this.editing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save any unsaved changes from this editing session. This will
|
||||
* end the current transaction.
|
||||
*/
|
||||
save() {
|
||||
return this.getTransactionService().commit().then((result)=>{
|
||||
this.editing = false;
|
||||
this.emit('isEditing', false);
|
||||
return result
|
||||
}).catch((error)=>{
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* End the currently active transaction and discard unsaved changes.
|
||||
*/
|
||||
cancel() {
|
||||
this.getTransactionService().cancel();
|
||||
this.editing = false;
|
||||
this.emit('isEditing', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
getTransactionService() {
|
||||
if (!this.transactionService) {
|
||||
this.transactionService = this.openmct.$injector.get('transactionService');
|
||||
}
|
||||
return this.transactionService;
|
||||
}
|
||||
}
|
@ -28,7 +28,9 @@ define([
|
||||
'./ui/Dialog',
|
||||
'./ui/GestureAPI',
|
||||
'./telemetry/TelemetryAPI',
|
||||
'./indicators/IndicatorAPI'
|
||||
'./indicators/IndicatorAPI',
|
||||
'./Editor'
|
||||
|
||||
], function (
|
||||
TimeAPI,
|
||||
ObjectAPI,
|
||||
@ -37,7 +39,8 @@ define([
|
||||
Dialog,
|
||||
GestureAPI,
|
||||
TelemetryAPI,
|
||||
IndicatorAPI
|
||||
IndicatorAPI,
|
||||
EditorAPI
|
||||
) {
|
||||
return {
|
||||
TimeAPI: TimeAPI,
|
||||
@ -47,6 +50,7 @@ define([
|
||||
TypeRegistry: TypeRegistry,
|
||||
GestureAPI: GestureAPI,
|
||||
TelemetryAPI: TelemetryAPI,
|
||||
IndicatorAPI: IndicatorAPI
|
||||
IndicatorAPI: IndicatorAPI,
|
||||
EditorAPI: EditorAPI
|
||||
};
|
||||
});
|
||||
|
@ -33,30 +33,6 @@ define([
|
||||
) {
|
||||
|
||||
function TableConfigurationViewProvider(openmct) {
|
||||
let instantiateService;
|
||||
|
||||
function isBeingEdited(object) {
|
||||
let oldStyleObject = getOldStyleObject(object);
|
||||
|
||||
return oldStyleObject.hasCapability('editor') &&
|
||||
oldStyleObject.getCapability('editor').inEditContext();
|
||||
}
|
||||
|
||||
function getOldStyleObject(object) {
|
||||
let oldFormatModel = objectUtils.toOldFormat(object);
|
||||
let oldFormatId = objectUtils.makeKeyString(object.identifier);
|
||||
|
||||
return instantiate(oldFormatModel, oldFormatId);
|
||||
}
|
||||
|
||||
function instantiate(model, id) {
|
||||
if (!instantiateService) {
|
||||
instantiateService = openmct.$injector.get('instantiate');
|
||||
}
|
||||
return instantiateService(model, id);
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
key: 'table-configuration',
|
||||
name: 'Telemetry Table Configuration',
|
||||
@ -65,8 +41,7 @@ define([
|
||||
return false;
|
||||
}
|
||||
let object = selection[0].context.item;
|
||||
return object.type === 'table' &&
|
||||
isBeingEdited(object);
|
||||
return object.type === 'table';
|
||||
},
|
||||
view: function (selection) {
|
||||
let component;
|
||||
@ -86,7 +61,7 @@ define([
|
||||
el: element
|
||||
});
|
||||
},
|
||||
destroy: function (element) {
|
||||
destroy: function () {
|
||||
component.$destroy();
|
||||
component = undefined;
|
||||
}
|
||||
|
@ -23,10 +23,10 @@
|
||||
define([
|
||||
'lodash',
|
||||
'EventEmitter',
|
||||
'./TelemetryTableColumn',
|
||||
'./TelemetryTableColumn'
|
||||
], function (_, EventEmitter, TelemetryTableColumn) {
|
||||
|
||||
class TelemetryTableConfiguration extends EventEmitter{
|
||||
class TelemetryTableConfiguration extends EventEmitter {
|
||||
constructor(domainObject, openmct) {
|
||||
super();
|
||||
|
||||
@ -37,6 +37,8 @@ define([
|
||||
this.addColumnsForObject = this.addColumnsForObject.bind(this);
|
||||
this.removeColumnsForObject = this.removeColumnsForObject.bind(this);
|
||||
this.objectMutated = this.objectMutated.bind(this);
|
||||
//Make copy of configuration, otherwise change detection is impossible if shared instance is being modified.
|
||||
this.oldConfiguration = JSON.parse(JSON.stringify(this.getConfiguration()));
|
||||
|
||||
this.unlistenFromMutation = openmct.objects.observe(domainObject, '*', this.objectMutated);
|
||||
}
|
||||
@ -56,11 +58,11 @@ define([
|
||||
* @param {*} object
|
||||
*/
|
||||
objectMutated(object) {
|
||||
let oldConfiguration = this.domainObject.configuration;
|
||||
|
||||
//Synchronize domain object reference. Duplicate object otherwise change detection becomes impossible.
|
||||
this.domainObject = JSON.parse(JSON.stringify(object));
|
||||
if (!_.eq(object.configuration, oldConfiguration)){
|
||||
this.domainObject = object;
|
||||
if (!_.eq(object.configuration, this.oldConfiguration)) {
|
||||
//Make copy of configuration, otherwise change detection is impossible if shared instance is being modified.
|
||||
this.oldConfiguration = JSON.parse(JSON.stringify(this.getConfiguration()));
|
||||
this.emit('change', object.configuration);
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,12 @@
|
||||
<template>
|
||||
<div class="grid-properties">
|
||||
<!--form class="form" -->
|
||||
<ul class="l-inspector-part">
|
||||
<h2>Table Columns</h2>
|
||||
<li class="grid-row" v-for="(title, key) in headers">
|
||||
<div class="grid-cell label" title="Show or Hide Column"><label :for="key + 'ColumnControl'">{{title}}</label></div>
|
||||
<div class="grid-cell value"><input type="checkbox" :id="key + 'ColumnControl'" :checked="configuration.hiddenColumns[key] !== true" @change="toggleColumn(key)"></div>
|
||||
</li>
|
||||
</ul>
|
||||
<!--/form -->
|
||||
<div class="c-properties" v-if="isEditing">
|
||||
<div class="c-properties__header">Table Columns</div>
|
||||
<ul class="c-properties__section">
|
||||
<li class="c-properties__row" v-for="(title, key) in headers">
|
||||
<div class="c-properties__label" title="Show or Hide Column"><label :for="key + 'ColumnControl'">{{title}}</label></div>
|
||||
<div class="c-properties__value"><input type="checkbox" :id="key + 'ColumnControl'" :checked="configuration.hiddenColumns[key] !== true" @change="toggleColumn(key)"></div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -21,6 +19,7 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
headers: {},
|
||||
isEditing: this.openmct.editor.isEditing(),
|
||||
configuration: this.tableConfiguration.getConfiguration()
|
||||
}
|
||||
},
|
||||
@ -41,11 +40,14 @@ export default {
|
||||
removeObject(objectIdentifier) {
|
||||
this.tableConfiguration.removeColumnsForObject(objectIdentifier, true);
|
||||
this.updateHeaders(this.tableConfiguration.getAllHeaders());
|
||||
},
|
||||
toggleEdit(isEditing) {
|
||||
this.isEditing = isEditing;
|
||||
}
|
||||
|
||||
},
|
||||
mounted() {
|
||||
this.unlisteners = [];
|
||||
this.openmct.editor.on('isEditing', this.toggleEdit);
|
||||
let compositionCollection = this.openmct.composition.get(this.tableConfiguration.domainObject);
|
||||
|
||||
compositionCollection.load()
|
||||
@ -62,6 +64,7 @@ export default {
|
||||
},
|
||||
destroyed() {
|
||||
this.tableConfiguration.destroy();
|
||||
this.openmct.editor.off('isEditing', this.toggleEdit);
|
||||
this.unlisteners.forEach((unlisten) => unlisten());
|
||||
}
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ define(['EventEmitter'], function (EventEmitter) {
|
||||
this.selected[0].element.classList.remove('s-selected');
|
||||
}
|
||||
|
||||
if (this.selected[1]) {
|
||||
if (this.selected[1] && this.selected[1].element) {
|
||||
this.selected[1].element.classList.remove('s-selected-parent');
|
||||
}
|
||||
|
||||
@ -66,7 +66,7 @@ define(['EventEmitter'], function (EventEmitter) {
|
||||
selectable[0].element.classList.add('s-selected');
|
||||
}
|
||||
|
||||
if (selectable[1]) {
|
||||
if (selectable[1] && selectable[1].element) {
|
||||
selectable[1].element.classList.add('s-selected-parent');
|
||||
}
|
||||
|
||||
@ -132,7 +132,7 @@ define(['EventEmitter'], function (EventEmitter) {
|
||||
}
|
||||
|
||||
return function () {
|
||||
element.removeEventListener('click', capture);
|
||||
element.removeEventListener('click', capture, true);
|
||||
element.removeEventListener('click', selectCapture);
|
||||
|
||||
if (unlisten) {
|
||||
|
@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<div class="c-properties"></div>
|
||||
<div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
@ -10,22 +11,26 @@
|
||||
inject: ['openmct'],
|
||||
mounted() {
|
||||
this.openmct.selection.on('change', this.updateSelection);
|
||||
this.updateSelection(this.openmct.selection.get());
|
||||
this.updateSelection();
|
||||
},
|
||||
destroyed() {
|
||||
this.openmct.selection.off('change', this.updateSelection);
|
||||
},
|
||||
methods: {
|
||||
updateSelection(selection) {
|
||||
updateSelection() {
|
||||
let selection = this.openmct.selection.get();
|
||||
if (this.selectedView && this.selectedView.destroy) {
|
||||
this.selectedView.destroy();
|
||||
delete this.viewContainer;
|
||||
this.$el.innerHTML = '';
|
||||
}
|
||||
this.$el.innerHTML = '';
|
||||
this.selectedView = this.openmct.inspectorViews.get(selection);
|
||||
if (!this.selectedView) {
|
||||
return;
|
||||
}
|
||||
this.selectedView.show(this.$el);
|
||||
this.viewContainer = document.createElement('div');
|
||||
this.$el.append(this.viewContainer)
|
||||
this.selectedView.show(this.viewContainer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -41,7 +41,9 @@
|
||||
<div class="l-browse-bar__actions">
|
||||
<div class="l-browse-bar__action c-button icon-eye-open" title="Preview"></div>
|
||||
<div class="l-browse-bar__action c-button icon-notebook" title="New Notebook entry"></div>
|
||||
<div class="l-browse-bar__action c-button c-button--major icon-pencil" title="Edit"></div>
|
||||
<div class="l-browse-bar__action c-button c-button--major icon-pencil" title="Edit" v-if="!isEditing" @click="edit()"></div>
|
||||
<div class="l-browse-bar__action c-button c-button--major icon-save" title="Save and Finish Editing" v-if="isEditing" @click="saveAndFinishEditing()"></div>
|
||||
<div class="l-browse-bar__action c-button icon-x" title="Cancel Editing" v-if="isEditing" @click="cancelEditing()"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -66,13 +68,23 @@
|
||||
this.openmct.router.updateParams({
|
||||
view: this.viewKey
|
||||
});
|
||||
},
|
||||
edit() {
|
||||
this.openmct.editor.edit();
|
||||
},
|
||||
cancelEditing() {
|
||||
this.openmct.editor.cancel();
|
||||
},
|
||||
saveAndFinishEditing() {
|
||||
this.openmct.editor.save();
|
||||
}
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
showViewMenu: false,
|
||||
domainObject: {},
|
||||
viewKey: undefined
|
||||
viewKey: undefined,
|
||||
isEditing: this.openmct.editor.isEditing()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -106,6 +118,10 @@
|
||||
this.showViewMenu = false;
|
||||
}
|
||||
});
|
||||
|
||||
this.openmct.editor.on('isEditing', (isEditing) => {
|
||||
this.isEditing = isEditing;
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="l-shell">
|
||||
<div class="l-shell" :class="{'is-editing': isEditing}">
|
||||
<div class="l-shell__head">
|
||||
<CreateButton class="l-shell__create-button"></CreateButton>
|
||||
<div class="l-shell__controls">
|
||||
@ -242,6 +242,10 @@
|
||||
}
|
||||
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
data() {
|
||||
return {isEditing: false};
|
||||
},
|
||||
components: {
|
||||
Inspector,
|
||||
MctStatus,
|
||||
@ -255,6 +259,11 @@
|
||||
pane,
|
||||
BrowseBar
|
||||
},
|
||||
mounted() {
|
||||
this.openmct.editor.on('isEditing', (isEditing)=>{
|
||||
this.isEditing = isEditing;
|
||||
});
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
fullScreen: false,
|
||||
|
@ -7,12 +7,24 @@ define([
|
||||
return function install(openmct) {
|
||||
let navigateCall = 0;
|
||||
let browseObject;
|
||||
let removeSelectable = undefined;
|
||||
|
||||
|
||||
function viewObject(object, viewProvider) {
|
||||
if (removeSelectable) {
|
||||
removeSelectable();
|
||||
removeSelectable = undefined;
|
||||
}
|
||||
openmct.layout.$refs.browseObject.show(object, viewProvider.key);
|
||||
openmct.layout.$refs.browseBar.domainObject = object;
|
||||
openmct.layout.$refs.browseBar.viewKey = viewProvider.key;
|
||||
removeSelectable = openmct.selection.selectable(
|
||||
openmct.layout.$refs.browseObject.$el,
|
||||
{
|
||||
item: object
|
||||
},
|
||||
true
|
||||
);
|
||||
};
|
||||
|
||||
function navigateToPath(path, currentViewKey) {
|
||||
@ -22,40 +34,42 @@ define([
|
||||
if (!Array.isArray(path)) {
|
||||
path = path.split('/');
|
||||
}
|
||||
let keyString = path[path.length - 1];
|
||||
// TODO: retain complete path in navigation.
|
||||
return openmct.objects.get(keyString)
|
||||
.then((object) => {
|
||||
if (currentNavigation !== navigateCall) {
|
||||
return; // Prevent race.
|
||||
}
|
||||
openmct.layout.$refs.browseBar.domainObject = object;
|
||||
browseObject = object;
|
||||
if (!object) {
|
||||
openmct.layout.$refs.browseObject.clear();
|
||||
return;
|
||||
}
|
||||
let currentProvider = openmct
|
||||
.objectViews
|
||||
.getByProviderKey(currentViewKey)
|
||||
return Promise.all(path.map((keyString)=>{
|
||||
return openmct.objects.get(keyString);
|
||||
})).then((objects)=>{
|
||||
if (currentNavigation !== navigateCall) {
|
||||
return; // Prevent race.
|
||||
}
|
||||
|
||||
if (currentProvider && currentProvider.canView(object)) {
|
||||
viewObject(object, currentProvider);
|
||||
return;
|
||||
}
|
||||
let navigatedObject = objects[objects.length - 1];
|
||||
|
||||
let defaultProvider = openmct.objectViews.get(object)[0];
|
||||
if (defaultProvider) {
|
||||
openmct.router.updateParams({
|
||||
view: defaultProvider.key
|
||||
});
|
||||
} else {
|
||||
openmct.router.updateParams({
|
||||
view: undefined
|
||||
});
|
||||
openmct.layout.$refs.browseObject.clear();
|
||||
}
|
||||
});
|
||||
openmct.layout.$refs.browseBar.domainObject = navigatedObject;
|
||||
browseObject = navigatedObject;
|
||||
if (!navigatedObject) {
|
||||
openmct.layout.$refs.browseObject.clear();
|
||||
return;
|
||||
}
|
||||
let currentProvider = openmct
|
||||
.objectViews
|
||||
.getByProviderKey(currentViewKey)
|
||||
|
||||
if (currentProvider && currentProvider.canView(navigatedObject)) {
|
||||
viewObject(navigatedObject, currentProvider);
|
||||
return;
|
||||
}
|
||||
|
||||
let defaultProvider = openmct.objectViews.get(navigatedObject)[0];
|
||||
if (defaultProvider) {
|
||||
openmct.router.updateParams({
|
||||
view: defaultProvider.key
|
||||
});
|
||||
} else {
|
||||
openmct.router.updateParams({
|
||||
view: undefined
|
||||
});
|
||||
openmct.layout.$refs.browseObject.clear();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
openmct.router.route(/^\/browse\/(.*)$/, (path, results, params) => {
|
||||
|
Loading…
Reference in New Issue
Block a user