Compare commits

...

5 Commits

18 changed files with 130 additions and 120 deletions

View File

@ -39,6 +39,7 @@ export default class Editor extends EventEmitter {
* Initiate an editing session. This will start a transaction during * Initiate an editing session. This will start a transaction during
* which any persist operations will be deferred until either save() * which any persist operations will be deferred until either save()
* or finish() are called. * or finish() are called.
* @private
*/ */
edit() { edit() {
if (this.editing === true) { if (this.editing === true) {
@ -59,6 +60,8 @@ export default class Editor extends EventEmitter {
/** /**
* Save any unsaved changes from this editing session. This will * Save any unsaved changes from this editing session. This will
* end the current transaction. * end the current transaction.
*
* @private
*/ */
save() { save() {
return this.getTransactionService().commit().then((result)=>{ return this.getTransactionService().commit().then((result)=>{
@ -72,6 +75,8 @@ export default class Editor extends EventEmitter {
/** /**
* End the currently active transaction and discard unsaved changes. * End the currently active transaction and discard unsaved changes.
*
* @private
*/ */
cancel() { cancel() {
this.getTransactionService().cancel(); this.getTransactionService().cancel();

View File

@ -47,7 +47,8 @@
:key="frame.id" :key="frame.id"
:frame="frame" :frame="frame"
:index="i" :index="i"
:containerIndex="index"> :containerIndex="index"
:isEditing="isEditing">
</frame-component> </frame-component>
<drop-hint <drop-hint
@ -65,7 +66,8 @@
:orientation="rowsLayout ? 'horizontal' : 'vertical'" :orientation="rowsLayout ? 'horizontal' : 'vertical'"
@init-move="startFrameResizing" @init-move="startFrameResizing"
@move="frameResizing" @move="frameResizing"
@end-move="endFrameResizing"> @end-move="endFrameResizing"
:isEditing="isEditing">
</resize-handle> </resize-handle>
</template> </template>
</div> </div>
@ -77,14 +79,12 @@ import FrameComponent from './frame.vue';
import Frame from '../utils/frame'; import Frame from '../utils/frame';
import ResizeHandle from './resizeHandle.vue'; import ResizeHandle from './resizeHandle.vue';
import DropHint from './dropHint.vue'; import DropHint from './dropHint.vue';
import isEditingMixin from '../mixins/isEditing';
const MIN_FRAME_SIZE = 5; const MIN_FRAME_SIZE = 5;
export default { export default {
inject:['openmct'], inject:['openmct'],
props: ['container', 'index', 'rowsLayout'], props: ['container', 'index', 'rowsLayout', 'isEditing'],
mixins: [isEditingMixin],
components: { components: {
FrameComponent, FrameComponent,
ResizeHandle, ResizeHandle,

View File

@ -49,6 +49,7 @@
:index="index" :index="index"
:container="container" :container="container"
:rowsLayout="rowsLayout" :rowsLayout="rowsLayout"
:isEditing="isEditing"
@move-frame="moveFrame" @move-frame="moveFrame"
@create-frame="createFrame" @create-frame="createFrame"
@persist="persist"> @persist="persist">
@ -59,6 +60,7 @@
:key="index" :key="index"
:index="index" :index="index"
:orientation="rowsLayout ? 'vertical' : 'horizontal'" :orientation="rowsLayout ? 'vertical' : 'horizontal'"
:isEditing="isEditing"
@init-move="startContainerResizing" @init-move="startContainerResizing"
@move="containerResizing" @move="containerResizing"
@end-move="endContainerResizing"> @end-move="endContainerResizing">
@ -398,7 +400,6 @@ import Container from '../utils/container';
import Frame from '../utils/frame'; import Frame from '../utils/frame';
import ResizeHandle from './resizeHandle.vue'; import ResizeHandle from './resizeHandle.vue';
import DropHint from './dropHint.vue'; import DropHint from './dropHint.vue';
import isEditingMixin from '../mixins/isEditing';
const MIN_CONTAINER_SIZE = 5; const MIN_CONTAINER_SIZE = 5;
@ -443,7 +444,6 @@ function sizeToFill(items) {
export default { export default {
inject: ['openmct', 'layoutObject'], inject: ['openmct', 'layoutObject'],
mixins: [isEditingMixin],
components: { components: {
ContainerComponent, ContainerComponent,
ResizeHandle, ResizeHandle,
@ -454,6 +454,9 @@ export default {
domainObject: this.layoutObject domainObject: this.layoutObject
} }
}, },
props: {
isEditing: Boolean
},
computed: { computed: {
layoutDirectionStr() { layoutDirectionStr() {
if (this.rowsLayout) { if (this.rowsLayout) {

View File

@ -50,12 +50,10 @@
<script> <script>
import ResizeHandle from './resizeHandle.vue'; import ResizeHandle from './resizeHandle.vue';
import ObjectFrame from '../../../ui/components/ObjectFrame.vue'; import ObjectFrame from '../../../ui/components/ObjectFrame.vue';
import isEditingMixin from '../mixins/isEditing';
export default { export default {
inject: ['openmct'], inject: ['openmct'],
props: ['frame', 'index', 'containerIndex'], props: ['frame', 'index', 'containerIndex', 'isEditing'],
mixins: [isEditingMixin],
data() { data() {
return { return {
domainObject: undefined, domainObject: undefined,

View File

@ -29,11 +29,8 @@
</template> </template>
<script> <script>
import isEditingMixin from '../mixins/isEditing';
export default { export default {
props: ['orientation', 'index'], props: ['orientation', 'index', 'isEditing'],
mixins: [isEditingMixin],
data() { data() {
return { return {
initialPos: 0, initialPos: 0,

View File

@ -42,8 +42,13 @@ define([
let component; let component;
return { return {
show: function (element) { show: function (element, isEditing) {
component = new Vue({ component = new Vue({
data() {
return {
isEditing: isEditing
}
},
components: { components: {
FlexibleLayoutComponent: FlexibleLayoutComponent.default FlexibleLayoutComponent: FlexibleLayoutComponent.default
}, },
@ -52,9 +57,12 @@ define([
layoutObject: domainObject layoutObject: domainObject
}, },
el: element, el: element,
template: '<flexible-layout-component ref="flexibleLayout"></flexible-layout-component>' template: '<flexible-layout-component :isEditing="isEditing"></flexible-layout-component>'
}); });
}, },
onEditModeChange(isEditing) {
component.isEditing = isEditing;
},
getSelectionContext: function () { getSelectionContext: function () {
return { return {
item: domainObject, item: domainObject,

View File

@ -1,19 +0,0 @@
export default {
inject: ['openmct'],
data() {
return {
isEditing: this.openmct.editor.isEditing()
};
},
mounted() {
this.openmct.editor.on('isEditing', this.toggleEditing);
},
destroyed() {
this.openmct.editor.off('isEditing', this.toggleEditing);
},
methods: {
toggleEditing(value) {
this.isEditing = value;
}
}
};

View File

@ -5,7 +5,7 @@
<span class="grippy-holder"> <span class="grippy-holder">
<span class="t-grippy grippy local-control local-controls-hidden"></span> <span class="t-grippy grippy local-control local-controls-hidden"></span>
</span> </span>
<span class="view-control expanded"></span> <span class="js-disclosure-control c-disclosure-triangle is-enabled"></span>
<span class="t-widget-thumb widget-thumb"> <span class="t-widget-thumb widget-thumb">
<span class="widget-label">DEF</span> <span class="widget-label">DEF</span>
</span> </span>

View File

@ -54,7 +54,7 @@ define([
this.title = $('.rule-title', this.domElement); this.title = $('.rule-title', this.domElement);
this.description = $('.rule-description', this.domElement); this.description = $('.rule-description', this.domElement);
this.trigger = $('.t-trigger', this.domElement); this.trigger = $('.t-trigger', this.domElement);
this.toggleConfigButton = $('.view-control', this.domElement); this.toggleConfigButton = $('.js-disclosure-control', this.domElement);
this.configArea = $('.widget-rule-content', this.domElement); this.configArea = $('.widget-rule-content', this.domElement);
this.grippy = $('.t-grippy', this.domElement); this.grippy = $('.t-grippy', this.domElement);
this.conditionArea = $('.t-widget-rule-config', this.domElement); this.conditionArea = $('.t-widget-rule-config', this.domElement);
@ -168,7 +168,7 @@ define([
*/ */
function toggleConfig() { function toggleConfig() {
self.configArea.toggleClass('expanded'); self.configArea.toggleClass('expanded');
self.toggleConfigButton.toggleClass('expanded'); self.toggleConfigButton.toggleClass('c-disclosure-triangle--expanded');
self.config.expanded = !self.config.expanded; self.config.expanded = !self.config.expanded;
} }

View File

@ -56,9 +56,7 @@ define([
this.toggleRulesControl = $('.t-view-control-rules', this.domElement); this.toggleRulesControl = $('.t-view-control-rules', this.domElement);
this.toggleTestDataControl = $('.t-view-control-test-data', this.domElement); this.toggleTestDataControl = $('.t-view-control-test-data', this.domElement);
this.widgetButton = this.domElement.children('#widget'); this.widgetButton = this.domElement.children('#widget');
this.editing = false;
this.container = ''; this.container = '';
this.editListenerUnsubscribe = $.noop;
this.outerWrapper = $('.widget-edit-holder', this.domElement); this.outerWrapper = $('.widget-edit-holder', this.domElement);
this.ruleArea = $('#ruleArea', this.domElement); this.ruleArea = $('#ruleArea', this.domElement);
@ -74,7 +72,6 @@ define([
this.show = this.show.bind(this); this.show = this.show.bind(this);
this.destroy = this.destroy.bind(this); this.destroy = this.destroy.bind(this);
this.addRule = this.addRule.bind(this); this.addRule = this.addRule.bind(this);
this.onEdit = this.onEdit.bind(this);
this.addHyperlink(domainObject.url, domainObject.openNewTab); this.addHyperlink(domainObject.url, domainObject.openNewTab);
this.watchForChanges(openmct, domainObject); this.watchForChanges(openmct, domainObject);
@ -103,19 +100,6 @@ define([
self.toggleRulesControl.toggleClass('expanded'); self.toggleRulesControl.toggleClass('expanded');
} }
this.listenTo(this.toggleRulesControl, 'click', toggleRules); this.listenTo(this.toggleRulesControl, 'click', toggleRules);
openmct.$injector.get('objectService')
.getObjects([id])
.then(function (objs) {
oldDomainObject = objs[id];
statusCapability = oldDomainObject.getCapability('status');
self.editListenerUnsubscribe = statusCapability.listen(self.onEdit);
if (statusCapability.get('editing')) {
self.onEdit(['editing']);
} else {
self.onEdit([]);
}
});
} }
/** /**
@ -172,7 +156,10 @@ define([
}); });
this.refreshRules(); this.refreshRules();
this.updateWidget(); this.updateWidget();
this.updateView();
this.ruleArea.show();
this.testDataArea.show();
this.addRuleButton.show();
this.listenTo(this.addRuleButton, 'click', this.addRule); this.listenTo(this.addRuleButton, 'click', this.addRule);
this.conditionManager.on('receiveTelemetry', this.executeRules, this); this.conditionManager.on('receiveTelemetry', this.executeRules, this);
@ -184,7 +171,6 @@ define([
* and clean up event handlers * and clean up event handlers
*/ */
SummaryWidget.prototype.destroy = function (container) { SummaryWidget.prototype.destroy = function (container) {
this.editListenerUnsubscribe();
this.conditionManager.destroy(); this.conditionManager.destroy();
this.testDataManager.destroy(); this.testDataManager.destroy();
this.widgetDnD.destroy(); this.widgetDnD.destroy();
@ -196,37 +182,6 @@ define([
this.stopListening(); this.stopListening();
}; };
/**
* A callback function for the Open MCT status capability listener. If the
* view representing the domain object is in edit mode, update the internal
* state and widget view accordingly.
* @param {string[]} status an array containing the domain object's current status
*/
SummaryWidget.prototype.onEdit = function (status) {
if (status && status.includes('editing')) {
this.editing = true;
} else {
this.editing = false;
}
this.updateView();
};
/**
* If this view is currently in edit mode, show all rule configuration interfaces.
* Otherwise, hide them.
*/
SummaryWidget.prototype.updateView = function () {
if (this.editing) {
this.ruleArea.show();
this.testDataArea.show();
this.addRuleButton.show();
} else {
this.ruleArea.hide();
this.testDataArea.hide();
this.addRuleButton.hide();
}
};
/** /**
* Update the view from the current rule configuration and order * Update the view from the current rule configuration and order
*/ */

View File

@ -24,16 +24,10 @@ define([
return domainObject.type === 'summary-widget'; return domainObject.type === 'summary-widget';
}, },
view: function (domainObject) { view: function (domainObject) {
var statusService = openmct.$injector.get('statusService'); return new SummaryWidgetView(domainObject, openmct);
var objectId = objectUtils.makeKeyString(domainObject.identifier); },
var statuses = statusService.listStatuses(objectId); edit: function (domainObject) {
var isEditing = statuses.indexOf('editing') !== -1; return new SummaryWidgetEditView(domainObject, openmct);
if (isEditing) {
return new SummaryWidgetEditView(domainObject, openmct);
} else {
return new SummaryWidgetView(domainObject, openmct);
}
}, },
priority: function (domainObject) { priority: function (domainObject) {
if (domainObject.type === 'summary-widget') { if (domainObject.type === 'summary-widget') {

View File

@ -47,8 +47,13 @@ define([
let table = new TelemetryTable(domainObject, openmct); let table = new TelemetryTable(domainObject, openmct);
let component; let component;
return { return {
show: function (element) { show: function (element, isEditing) {
component = new Vue({ component = new Vue({
data() {
return {
isEditing: false
}
},
components: { components: {
TableComponent: TableComponent.default, TableComponent: TableComponent.default,
}, },
@ -58,9 +63,12 @@ define([
table table
}, },
el: element, el: element,
template: '<table-component></table-component>' template: '<table-component :isEditing="isEditing"></table-component>'
}); });
}, },
onEditModeChange(isEditing) {
component.isEditing = isEditing;
},
destroy: function (element) { destroy: function (element) {
component.$destroy(); component.$destroy();
component = undefined; component = undefined;

View File

@ -73,18 +73,14 @@ const MOVE_COLUMN_DT_TYPE = 'movecolumnfromindex';
export default { export default {
inject: ['openmct'], inject: ['openmct'],
data() {
return {
isEditing: this.openmct.editor.isEditing()
}
},
props: { props: {
headerKey: String, headerKey: String,
headerIndex: Number, headerIndex: Number,
isHeaderTitle: Boolean, isHeaderTitle: Boolean,
sortOptions: Object, sortOptions: Object,
columnWidth: Number, columnWidth: Number,
hotzone: Boolean hotzone: Boolean,
isEditing: Boolean
}, },
computed: { computed: {
isSortable() { isSortable() {
@ -167,16 +163,7 @@ export default {
}, },
sort() { sort() {
this.$emit("sort"); this.$emit("sort");
},
toggleEditMode(isEditing) {
this.isEditing = isEditing;
} }
},
created() {
this.openmct.editor.on('isEditing', this.toggleEditMode);
},
destroyed() {
this.openmct.editor.off('isEditing', this.toggleEditMode);
} }
} }
</script> </script>

View File

@ -48,6 +48,7 @@
@resizeColumnEnd="updateConfiguredColumnWidths" @resizeColumnEnd="updateConfiguredColumnWidths"
:columnWidth="columnWidths[key]" :columnWidth="columnWidths[key]"
:sortOptions="sortOptions" :sortOptions="sortOptions"
:isEditing="isEditing"
>{{title}}</table-column-header> >{{title}}</table-column-header>
</tr> </tr>
<tr class="c-telemetry-table__headers__filter"> <tr class="c-telemetry-table__headers__filter">
@ -61,7 +62,9 @@
@dropTargetActive="dropTargetActive" @dropTargetActive="dropTargetActive"
@reorderColumn="reorderColumn" @reorderColumn="reorderColumn"
@resizeColumnEnd="updateConfiguredColumnWidths" @resizeColumnEnd="updateConfiguredColumnWidths"
:columnWidth="columnWidths[key]"> :columnWidth="columnWidths[key]"
:isEditing="isEditing"
>
<search class="c-table__search" <search class="c-table__search"
v-model="filters[key]" v-model="filters[key]"
v-on:input="filterChanged(key)" v-on:input="filterChanged(key)"
@ -260,6 +263,12 @@ export default {
search search
}, },
inject: ['table', 'openmct', 'csvExporter'], inject: ['table', 'openmct', 'csvExporter'],
props: {
isEditing: {
type: Boolean,
default: false
}
},
data() { data() {
let configuration = this.table.configuration.getConfiguration(); let configuration = this.table.configuration.getConfiguration();

View File

@ -123,7 +123,7 @@
} }
/********************************************************** EDITING A WIDGET */ /********************************************************** EDITING A WIDGET */
.s-status-editing > mct-view > .w-summary-widget { .is-editing .w-summary-widget {
// Classes for editor layout while editing a widget // Classes for editor layout while editing a widget
// This selector is ugly and brittle, but needed to prevent interface from showing when widget is in a layout // This selector is ugly and brittle, but needed to prevent interface from showing when widget is in a layout
// being edited. // being edited.

View File

@ -12,6 +12,7 @@ export default {
}, },
destroyed() { destroyed() {
this.clear(); this.clear();
this.openmct.editor.off('isEditing', this.setEditMode);
}, },
watch: { watch: {
view(newView, oldView) { view(newView, oldView) {
@ -37,10 +38,22 @@ export default {
if (this.currentView) { if (this.currentView) {
this.currentView.destroy(); this.currentView.destroy();
this.$el.innerHTML = ''; this.$el.innerHTML = '';
if (this.releaseEditModeHandler) {
this.releaseEditModeHandler();
delete this.releaseEditModeHandler;
}
} }
delete this.viewContainer; delete this.viewContainer;
delete this.currentView; delete this.currentView;
}, },
invokeEditModeHandler(editMode) {
this.currentView.onEditModeChange(editMode);
},
toggleEditView(editMode) {
this.clear();
this.updateView(true);
},
updateView(immediatelySelect) { updateView(immediatelySelect) {
this.clear(); this.clear();
if (!this.currentObject) { if (!this.currentObject) {
@ -50,14 +63,32 @@ export default {
this.viewContainer.classList.add('c-object-view','u-contents'); this.viewContainer.classList.add('c-object-view','u-contents');
this.$el.append(this.viewContainer); this.$el.append(this.viewContainer);
let provider = this.openmct.objectViews.getByProviderKey(this.viewKey); let provider = this.openmct.objectViews.getByProviderKey(this.viewKey);
if (!provider) { if (!provider) {
provider = this.openmct.objectViews.get(this.currentObject)[0]; provider = this.openmct.objectViews.get(this.currentObject)[0];
if (!provider) { if (!provider) {
return; return;
} }
} }
this.currentView = provider.view(this.currentObject);
this.currentView.show(this.viewContainer);
if (provider.edit) {
if (this.openmct.editor.isEditing()) {
this.currentView = provider.edit(this.currentObject);
} else {
this.currentView = provider.view(this.currentObject, false);
}
this.openmct.editor.on('isEditing', this.toggleEditView);
this.releaseEditModeHandler = () => this.openmct.editor.off('isEditing', this.toggleEditView);
} else {
this.currentView = provider.view(this.currentObject, this.openmct.editor.isEditing());
if (this.currentView.onEditModeChange) {
this.openmct.editor.on('isEditing', this.invokeEditModeHandler);
this.releaseEditModeHandler = () => this.openmct.editor.off('isEditing', this.invokeEditModeHandler);
}
}
this.currentView.show(this.viewContainer, this.openmct.editor.isEditing());
if (immediatelySelect) { if (immediatelySelect) {
this.removeSelectable = openmct.selection.selectable( this.removeSelectable = openmct.selection.selectable(

View File

@ -84,7 +84,7 @@
mounted() { mounted() {
let viewProvider = this.openmct.objectViews.get(this.domainObject)[0]; let viewProvider = this.openmct.objectViews.get(this.domainObject)[0];
this.view = viewProvider.view(this.domainObject); this.view = viewProvider.view(this.domainObject);
this.view.show(this.$refs.objectView); this.view.show(this.$refs.objectView, false);
}, },
destroy() { destroy() {
this.view.destroy(); this.view.destroy();

View File

@ -120,6 +120,19 @@ define([], function () {
* @memberof module:openmct.View# * @memberof module:openmct.View#
*/ */
/**
* Indicates whether or not the application is in edit mode. This supports
* views that have distinct visual and behavioral elements when the
* navigated object is being edited.
*
* For cases where a completely separate view is desired for editing purposes,
* see {@link openmct.ViewProvider#edit}
*
* @param {boolean} isEditing
* @method show
* @memberof module:openmct.View#
*/
/** /**
* Release any resources associated with this view. * Release any resources associated with this view.
* *
@ -172,7 +185,15 @@ define([], function () {
/** /**
* An optional function that defines whether or not this view can be used to edit a given object. * An optional function that defines whether or not this view can be used to edit a given object.
* If not provided, will default to `false` and the view will not support editing. * If not provided, will default to `false` and the view will not support editing. To support editing,
* return true from this function and then -
* * Return a {@link openmct.View} from the `view` function, using the `onEditModeChange` callback to
* add and remove editing elements from the view
* OR
* * Return a {@link openmct.View} from the `view` function defining a read-only view.
* AND
* * Define an {@link openmct.ViewProvider#Edit} function on the view provider that returns an
* editing-specific view.
* *
* @method canEdit * @method canEdit
* @memberof module:openmct.ViewProvider# * @memberof module:openmct.ViewProvider#
@ -211,6 +232,19 @@ define([], function () {
* @returns {module:openmct.View} a view of this domain object * @returns {module:openmct.View} a view of this domain object
*/ */
/**
* Provide an edit-mode specific view of this object.
*
* If optionally specified, this function will be called when the application
* enters edit mode. This will cause the active non-edit mode view and its
* dom element to be destroyed.
*
* @method edit
* @memberof module:openmct.ViewProvider#
* @param {*} object the object to be edit
* @returns {module:openmct.View} an editable view of this domain object
*/
return ViewRegistry; return ViewRegistry;
}); });