Toolbar in Fixed Position (#2194)

* Initial attempt at getting the toolbar for the fixed position working with the Vue toolbar controls.

* Set title for controls

* Significant mods to support legacy Fixed Position

- Moved selection and editing styles into _global.scss;
- Changed class naming in legacy fixed.html to map to newer CSS styles;

* Pass in the color value

* Do not show the toolbar container if structure is empty.

* Use a plugin for fixed position to get access to openmct. Show the toolbar only if the object is being edited.

* Ensure fixedController is on the selection context when editing

* Add listener for a domain object with the same id only once.
Update the toolbar value after the object mutation.
Remove editor isEditing listener on destroyed.

* Remove space between the size and px.
If newObject exists, update the toolbar value.

* Remove --nwse class name which seems to be a typo

* use modifier convention
This commit is contained in:
Pegah Sarram 2018-10-19 11:42:30 -07:00 committed by Pete Richards
parent 67883519ee
commit 4374a6fa28
20 changed files with 760 additions and 649 deletions

View File

@ -49,6 +49,7 @@
openmct.install(openmct.plugins.ExampleImagery());
openmct.install(openmct.plugins.UTCTimeSystem());
openmct.install(openmct.plugins.ImportExport());
openmct.install(openmct.plugins.FixedView());
openmct.install(openmct.plugins.AutoflowView({
type: "telemetry.panel"
}));

View File

@ -1,449 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define([
"./src/FixedController",
"./templates/fixed.html",
"./templates/frame.html",
"./templates/elements/telemetry.html",
"./templates/elements/box.html",
"./templates/elements/line.html",
"./templates/elements/text.html",
"./templates/elements/image.html",
"legacyRegistry"
], function (
FixedController,
fixedTemplate,
frameTemplate,
telemetryTemplate,
boxTemplate,
lineTemplate,
textTemplate,
imageTemplate,
legacyRegistry
) {
legacyRegistry.register("platform/features/fixed", {
"name": "Fixed position components.",
"description": "Plug in adding Fixed Position object type.",
"extensions": {
"views": [
{
"key": "fixed-display",
"name": "Fixed Position Display",
"cssClass": "icon-box-with-dashed-lines",
"type": "telemetry.fixed",
"template": fixedTemplate,
"uses": ["composition"],
"editable": true
}
],
"templates": [
{
"key": "fixed.telemetry",
"template": telemetryTemplate
},
{
"key": "fixed.box",
"template": boxTemplate
},
{
"key": "fixed.line",
"template": lineTemplate
},
{
"key": "fixed.text",
"template": textTemplate
},
{
"key": "fixed.image",
"template": imageTemplate
}
],
"controllers": [
{
"key": "FixedController",
"implementation": FixedController,
"depends": [
"$scope",
"$q",
"dialogService",
"openmct",
"$element"
]
}
],
"toolbars": [
{
name: "Fixed Position Toolbar",
key: "fixed.position",
description: "Toolbar for the selected element inside a fixed position display.",
forSelection: function (selection) {
if (!selection) {
return;
}
return (
selection[0] && selection[0].context.elementProxy &&
selection[1] && selection[1].context.item.type === 'telemetry.fixed' ||
selection[0] && selection[0].context.item.type === 'telemetry.fixed'
);
},
toolbar: function (selection) {
var imageProperties = ["add", "remove", "order", "stroke", "useGrid", "x", "y", "height", "width", "url"];
var boxProperties = ["add", "remove", "order", "stroke", "useGrid", "x", "y", "height", "width", "fill"];
var textProperties = ["add", "remove", "order", "stroke", "useGrid", "x", "y", "height", "width", "fill", "color", "size", "text"];
var lineProperties = ["add", "remove", "order", "stroke", "useGrid", "x", "y", "x2", "y2"];
var telemetryProperties = ["add", "remove", "order", "stroke", "useGrid", "x", "y", "height", "width", "fill", "color", "size", "titled"];
var fixedPageProperties = ["add"];
var properties = [],
fixedItem = selection[0] && selection[0].context.item,
elementProxy = selection[0] && selection[0].context.elementProxy,
domainObject = selection[1] && selection[1].context.item,
path;
if (elementProxy) {
var type = elementProxy.element.type;
path = "configuration['fixed-display'].elements[" + elementProxy.index + "]";
properties =
type === 'fixed.image' ? imageProperties :
type === 'fixed.text' ? textProperties :
type === 'fixed.box' ? boxProperties :
type === 'fixed.line' ? lineProperties :
type === 'fixed.telemetry' ? telemetryProperties : [];
} else if (fixedItem) {
properties = domainObject && domainObject.type === 'layout' ? [] : fixedPageProperties;
}
return [
{
control: "menu-button",
domainObject: domainObject || selection[0].context.item,
method: function (value) {
selection[0].context.fixedController.add(value);
},
key: "add",
cssClass: "icon-plus",
text: "Add",
options: [
{
"name": "Box",
"cssClass": "icon-box",
"key": "fixed.box"
},
{
"name": "Line",
"cssClass": "icon-line-horz",
"key": "fixed.line"
},
{
"name": "Text",
"cssClass": "icon-T",
"key": "fixed.text"
},
{
"name": "Image",
"cssClass": "icon-image",
"key": "fixed.image"
}
]
},
{
control: "menu-button",
domainObject: domainObject,
method: function (value) {
selection[0].context.fixedController.order(
selection[0].context.elementProxy,
value
);
},
key: "order",
cssClass: "icon-layers",
title: "Layering",
description: "Move the selected object above or below other objects",
options: [
{
"name": "Move to Top",
"cssClass": "icon-arrow-double-up",
"key": "top"
},
{
"name": "Move Up",
"cssClass": "icon-arrow-up",
"key": "up"
},
{
"name": "Move Down",
"cssClass": "icon-arrow-down",
"key": "down"
},
{
"name": "Move to Bottom",
"cssClass": "icon-arrow-double-down",
"key": "bottom"
}
]
},
{
control: "color",
domainObject: domainObject,
property: path + ".fill",
cssClass: "icon-paint-bucket",
title: "Fill color",
description: "Set fill color",
key: 'fill'
},
{
control: "color",
domainObject: domainObject,
property: path + ".stroke",
cssClass: "icon-line-horz",
title: "Border color",
description: "Set border color",
key: 'stroke'
},
{
control: "dialog-button",
domainObject: domainObject,
property: path + ".url",
cssClass: "icon-image",
title: "Image Properties",
description: "Edit image properties",
key: 'url',
dialog: {
control: "textfield",
name: "Image URL",
cssClass: "l-input-lg",
required: true
}
},
{
control: "color",
domainObject: domainObject,
property: path + ".color",
cssClass: "icon-T",
title: "Text color",
mandatory: true,
description: "Set text color",
key: 'color'
},
{
control: "select",
domainObject: domainObject,
property: path + ".size",
title: "Text size",
description: "Set text size",
"options": [9, 10, 11, 12, 13, 14, 15, 16, 20, 24, 30, 36, 48, 72, 96].map(function (size) {
return { "name": size + " px", "value": size + "px" };
}),
key: 'size'
},
{
control: "numberfield",
domainObject: domainObject,
property: path + ".x",
text: "X",
name: "X",
key: "x",
cssClass: "l-input-sm",
min: "0"
},
{
control: "numberfield",
domainObject: domainObject,
property: path + ".y",
text: "Y",
name: "Y",
key: "y",
cssClass: "l-input-sm",
min: "0"
},
{
control: "numberfield",
domainObject: domainObject,
property: path + ".x",
text: "X1",
name: "X1",
key: "x1",
cssClass: "l-input-sm",
min: "0"
},
{
control: "numberfield",
domainObject: domainObject,
property: path + ".y",
text: "Y1",
name: "Y1",
key: "y1",
cssClass: "l-input-sm",
min: "0"
},
{
control: "numberfield",
domainObject: domainObject,
property: path + ".x2",
text: "X2",
name: "X2",
key: "x2",
cssClass: "l-input-sm",
min: "0"
},
{
control: "numberfield",
domainObject: domainObject,
property: path + ".y2",
text: "Y2",
name: "Y2",
key: "y2",
cssClass: "l-input-sm",
min: "0"
},
{
control: "numberfield",
domainObject: domainObject,
property: path + ".height",
text: "H",
name: "H",
key: "height",
cssClass: "l-input-sm",
description: "Resize object height",
min: "1"
},
{
control: "numberfield",
domainObject: domainObject,
property: path + ".width",
text: "W",
name: "W",
key: "width",
cssClass: "l-input-sm",
description: "Resize object width",
min: "1"
},
{
control: "checkbox",
domainObject: domainObject,
property: path + ".useGrid",
name: "Snap to Grid",
key: "useGrid"
},
{
control: "dialog-button",
domainObject: domainObject,
property: path + ".text",
cssClass: "icon-gear",
title: "Text Properties",
description: "Edit text properties",
key: "text",
dialog: {
control: "textfield",
name: "Text",
required: true
}
},
{
control: "checkbox",
domainObject: domainObject,
property: path + ".titled",
name: "Show Title",
key: "titled"
},
{
control: "button",
domainObject: domainObject,
method: function () {
selection[0].context.fixedController.remove(
selection[0].context.elementProxy
);
},
key: "remove",
cssClass: "icon-trash"
}
].filter(function (item) {
var filtered;
properties.forEach(function (property) {
if (item.property && item.key === property ||
item.method && item.key === property) {
filtered = item;
}
});
return filtered;
});
}
}
],
"types": [
{
"key": "telemetry.fixed",
"name": "Fixed Position Display",
"cssClass": "icon-box-with-dashed-lines",
"description": "Collect and display telemetry elements in " +
"alphanumeric format in a simple canvas workspace. " +
"Elements can be positioned and sized. " +
"Lines, boxes and images can be added as well.",
"priority": 899,
"delegates": [
"telemetry"
],
"features": "creation",
"contains": [
{
"has": "telemetry"
}
],
"model": {
"layoutGrid": [64, 16],
"composition": []
},
"properties": [
{
"name": "Layout Grid",
"control": "composite",
"items": [
{
"name": "Horizontal grid (px)",
"control": "textfield",
"cssClass": "l-input-sm l-numeric"
},
{
"name": "Vertical grid (px)",
"control": "textfield",
"cssClass": "l-input-sm l-numeric"
}
],
"pattern": "^(\\d*[1-9]\\d*)?$",
"property": "layoutGrid",
"conversion": "number[]"
}
],
"views": [
"fixed-display"
]
}
]
}
});
});

View File

@ -0,0 +1,475 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define([
"./src/FixedController",
"./templates/fixed.html",
"./templates/frame.html",
"./templates/elements/telemetry.html",
"./templates/elements/box.html",
"./templates/elements/line.html",
"./templates/elements/text.html",
"./templates/elements/image.html",
"legacyRegistry"
], function (
FixedController,
fixedTemplate,
frameTemplate,
telemetryTemplate,
boxTemplate,
lineTemplate,
textTemplate,
imageTemplate,
legacyRegistry
) {
return function() {
return function (openmct) {
openmct.legacyRegistry.register("platform/features/fixed", {
"name": "Fixed position components.",
"description": "Plug in adding Fixed Position object type.",
"extensions": {
"views": [
{
"key": "fixed-display",
"name": "Fixed Position Display",
"cssClass": "icon-box-with-dashed-lines",
"type": "telemetry.fixed",
"template": fixedTemplate,
"uses": ["composition"],
"editable": true
}
],
"templates": [
{
"key": "fixed.telemetry",
"template": telemetryTemplate
},
{
"key": "fixed.box",
"template": boxTemplate
},
{
"key": "fixed.line",
"template": lineTemplate
},
{
"key": "fixed.text",
"template": textTemplate
},
{
"key": "fixed.image",
"template": imageTemplate
}
],
"controllers": [
{
"key": "FixedController",
"implementation": FixedController,
"depends": [
"$scope",
"$q",
"dialogService",
"openmct",
"$element"
]
}
],
"toolbars": [
{
name: "Fixed Position Toolbar",
key: "fixed.position",
description: "Toolbar for the selected element inside a fixed position display.",
forSelection: function (selection) {
if (!selection) {
return;
}
return (openmct.editor.isEditing() &&
(selection[0] && selection[0].context.elementProxy &&
selection[1] && selection[1].context.item.type === 'telemetry.fixed' ||
selection[0] && selection[0].context.item.type === 'telemetry.fixed'));
},
toolbar: function (selection) {
var imageProperties = ["add", "remove", "order", "stroke", "useGrid", "x", "y", "height", "width", "url"];
var boxProperties = ["add", "remove", "order", "stroke", "useGrid", "x", "y", "height", "width", "fill"];
var textProperties = ["add", "remove", "order", "stroke", "useGrid", "x", "y", "height", "width", "fill", "color", "size", "text"];
var lineProperties = ["add", "remove", "order", "stroke", "useGrid", "x", "y", "x2", "y2"];
var telemetryProperties = ["add", "remove", "order", "stroke", "useGrid", "x", "y", "height", "width", "fill", "color", "size", "titled"];
var fixedPageProperties = ["add"];
var properties = [],
fixedItem = selection[0] && selection[0].context.item,
elementProxy = selection[0] && selection[0].context.elementProxy,
domainObject = selection[1] && selection[1].context.item,
path;
if (elementProxy) {
var type = elementProxy.element.type;
path = "configuration['fixed-display'].elements[" + elementProxy.index + "]";
properties =
type === 'fixed.image' ? imageProperties :
type === 'fixed.text' ? textProperties :
type === 'fixed.box' ? boxProperties :
type === 'fixed.line' ? lineProperties :
type === 'fixed.telemetry' ? telemetryProperties : [];
} else if (fixedItem) {
properties = domainObject && domainObject.type === 'layout' ? [] : fixedPageProperties;
}
return [
{
control: "menu",
domainObject: domainObject || selection[0].context.item,
method: function (option) {
selection[0].context.fixedController.add(option.key);
},
key: "add",
icon: "icon-plus",
label: "Add",
options: [
{
"name": "Box",
"class": "icon-box",
"key": "fixed.box"
},
{
"name": "Line",
"class": "icon-line-horz",
"key": "fixed.line"
},
{
"name": "Text",
"class": "icon-T",
"key": "fixed.text"
},
{
"name": "Image",
"class": "icon-image",
"key": "fixed.image"
}
]
},
{
control: "menu",
domainObject: domainObject,
method: function (option) {
console.log('option', option)
selection[0].context.fixedController.order(
selection[0].context.elementProxy,
option.key
);
},
key: "order",
icon: "icon-layers",
title: "Move the selected object above or below other objects",
options: [
{
"name": "Move to Top",
"class": "icon-arrow-double-up",
"key": "top"
},
{
"name": "Move Up",
"class": "icon-arrow-up",
"key": "up"
},
{
"name": "Move Down",
"class": "icon-arrow-down",
"key": "down"
},
{
"name": "Move to Bottom",
"class": "icon-arrow-double-down",
"key": "bottom"
}
]
},
{
control: "color-picker",
domainObject: domainObject,
property: path + ".fill",
icon: "icon-paint-bucket",
title: "Set fill color",
key: 'fill'
},
{
control: "color-picker",
domainObject: domainObject,
property: path + ".stroke",
icon: "icon-line-horz",
title: "Set border color",
key: 'stroke'
},
{
control: "button",
domainObject: domainObject,
property: path + ".url",
icon: "icon-image",
title: "Edit image properties",
key: 'url',
dialog: {
control: "input",
type: "text",
name: "Image URL",
class: "l-input-lg",
required: true
}
},
{
control: "color-picker",
domainObject: domainObject,
property: path + ".color",
icon: "icon-T",
mandatory: true,
title: "Set text color",
key: 'color'
},
{
control: "select-menu",
domainObject: domainObject,
property: path + ".size",
title: "Set text size",
key: 'size',
options: [9, 10, 11, 12, 13, 14, 15, 16, 20, 24, 30, 36, 48, 72, 96].map(function (size) {
return { "value": size + "px"};
})
},
{
control: "input",
type: "number",
domainObject: domainObject,
property: path + ".x",
label: "X",
title: "X position",
key: "x",
class: "l-input-sm",
min: "0"
},
{
control: "input",
type: "number",
domainObject: domainObject,
property: path + ".y",
label: "Y",
title: "Y position",
key: "y",
class: "l-input-sm",
min: "0"
},
{
control: "input",
type: "number",
domainObject: domainObject,
property: path + ".x",
label: "X1",
title: "X1 position",
key: "x1",
class: "l-input-sm",
min: "0"
},
{
control: "input",
type: "number",
domainObject: domainObject,
property: path + ".y",
label: "Y1",
title: "Y1 position",
key: "y1",
class: "l-input-sm",
min: "0"
},
{
control: "input",
type: "number",
domainObject: domainObject,
property: path + ".x2",
label: "X2",
title: "X2 position",
key: "x2",
class: "l-input-sm",
min: "0"
},
{
control: "input",
type: "number",
domainObject: domainObject,
property: path + ".y2",
label: "Y2",
title: "Y2 position",
key: "y2",
class: "l-input-sm",
min: "0"
},
{
control: "input",
type: "number",
domainObject: domainObject,
property: path + ".height",
label: "H",
title: "Resize object height",
key: "height",
class: "l-input-sm",
min: "1"
},
{
control: "input",
type: "number",
domainObject: domainObject,
property: path + ".width",
label: "W",
title: "Resize object width",
key: "width",
class: "l-input-sm",
min: "1"
},
{
control: "toggle-button",
domainObject: domainObject,
property: path + ".useGrid",
key: "useGrid",
options: [
{
value: true,
icon: 'icon-grid-snap-to',
title: 'Snap to grid'
},
{
value: false,
icon: 'icon-grid-snap-no',
title: "Do not snap to grid"
}
]
},
{
control: "button",
domainObject: domainObject,
property: path + ".text",
icon: "icon-gear",
title: "Edit text properties",
key: "text",
dialog: {
control: "input",
type: "text",
name: "Text",
required: true
}
},
{
control: "toggle-button",
domainObject: domainObject,
property: path + ".titled",
key: "titled",
options: [
{
value: true,
icon: 'icon-two-parts-both',
title: 'Show label'
},
{
value: false,
icon: 'icon-two-parts-one-only',
title: "Hide label"
}
]
},
{
control: "button",
domainObject: domainObject,
method: function () {
selection[0].context.fixedController.remove(
selection[0].context.elementProxy
);
},
key: "remove",
icon: "icon-trash"
}
].filter(function (item) {
var filtered;
properties.forEach(function (property) {
if (item.property && item.key === property ||
item.method && item.key === property) {
filtered = item;
}
});
return filtered;
});
}
}
],
"types": [
{
"key": "telemetry.fixed",
"name": "Fixed Position Display",
"cssClass": "icon-box-with-dashed-lines",
"description": "Collect and display telemetry elements in " +
"alphanumeric format in a simple canvas workspace. " +
"Elements can be positioned and sized. " +
"Lines, boxes and images can be added as well.",
"priority": 899,
"delegates": [
"telemetry"
],
"features": "creation",
"contains": [
{
"has": "telemetry"
}
],
"model": {
"layoutGrid": [64, 16],
"composition": []
},
"properties": [
{
"name": "Layout Grid",
"control": "composite",
"items": [
{
"name": "Horizontal grid (px)",
"control": "textfield",
"cssClass": "l-input-sm l-numeric"
},
{
"name": "Vertical grid (px)",
"control": "textfield",
"cssClass": "l-input-sm l-numeric"
}
],
"pattern": "^(\\d*[1-9]\\d*)?$",
"property": "layoutGrid",
"conversion": "number[]"
}
],
"views": [
"fixed-display"
]
}
]
}
});
openmct.legacyRegistry.enable("platform/features/fixed");
}
}
});

View File

@ -225,6 +225,8 @@ define(
this.openmct.time.on("bounds", updateDisplayBounds);
this.openmct.selection.on('change', this.setSelection.bind(this));
this.openmct.editor.on('isEditing', this.handleEditing.bind(this));
this.$element.on('click', this.bypassSelection.bind(this));
this.unlisten = this.openmct.objects.observe(this.newDomainObject, '*', function (obj) {
this.newDomainObject = JSON.parse(JSON.stringify(obj));
@ -421,6 +423,7 @@ define(
this.openmct.selection.off("change", this.setSelection);
this.composition.off('add', this.onCompositionAdd, this);
this.composition.off('remove', this.onCompositionRemove, this);
this.openmct.editor.off('isEditing', this.handleEditing, this);
};
/**
@ -706,6 +709,12 @@ define(
this.openmct.objects.mutate(this.newDomainObject, path, value);
};
FixedController.prototype.handleEditing = function (isEditing) {
// Listen for edit mode changes and update selection if necessary.
// Mainly to ensure fixedController is on the selection context when editing.
this.setSelection(this.openmct.selection.get());
}
return FixedController;
}
);

View File

@ -42,7 +42,8 @@ define(
},
"fixed.text": {
fill: "transparent",
stroke: "transparent"
stroke: "transparent",
size: "13px"
}
},
DIALOGS = {

View File

@ -67,10 +67,6 @@ define(
*/
proxy.size = new AccessorMutator(element, 'size');
if (proxy.size() === undefined) {
proxy.size("13px");
}
return proxy;
}

View File

@ -23,18 +23,18 @@
ng-controller="FixedController as controller">
<!-- Background grid -->
<div class="l-grid-holder" ng-click="controller.bypassSelection($event)">
<div class="l-grid l-grid-x"
<div class="l-fixed-position__grid-holder l-grid-holder c-grid" ng-click="controller.bypassSelection($event)">
<div class="c-grid__x l-grid l-grid-x"
ng-if="!controller.getGridSize()[0] < 3"
ng-style="{ 'background-size': controller.getGridSize() [0] + 'px 100%' }"></div>
<div class="l-grid l-grid-y"
<div class="c-grid__y l-grid l-grid-y"
ng-if="!controller.getGridSize()[1] < 3"
ng-style="{ 'background-size': '100% ' + controller.getGridSize() [1] + 'px' }"></div>
</div>
<!-- Fixed position elements -->
<div ng-repeat="element in controller.getElements()"
class="l-fixed-position-item s-selectable s-moveable s-hover-border"
class="l-fixed-position-item s-selectable s-moveable is-selectable is-moveable"
ng-style="element.style"
mct-selectable="controller.getContext(element)"
mct-init-select="controller.shouldSelect(element)">
@ -44,15 +44,15 @@
</mct-include>
</div>
<!-- Selection highlight, handles -->
<span class="s-selected s-moveable" ng-if="controller.isElementSelected()">
<div class="l-fixed-position-item t-edit-handle-holder"
<span class="c-frame-edit" ng-if="controller.isElementSelected()">
<div class="c-frame-edit__move"
mct-drag-down="controller.moveHandle().startDrag()"
mct-drag="controller.moveHandle().continueDrag(delta)"
mct-drag-up="controller.endDrag()"
ng-style="controller.getSelectedElementStyle()">
</div>
<div ng-repeat="handle in controller.handles()"
class="l-fixed-position-item-handle edit-corner"
class="c-frame-edit__handle c-frame-edit__handle--nwse"
ng-style="handle.style()"
mct-drag-down="handle.startDrag()"
mct-drag="handle.continueDrag(delta)"

View File

@ -54,7 +54,6 @@ define([
'../platform/execution/bundle',
'../platform/exporters/bundle',
'../platform/features/clock/bundle',
'../platform/features/fixed/bundle',
'../platform/features/imagery/bundle',
'../platform/features/my-items/bundle',
'../platform/features/pages/bundle',
@ -96,7 +95,6 @@ define([
'platform/exporters',
'platform/telemetry',
'platform/features/clock',
'platform/features/fixed',
'platform/features/imagery',
'platform/features/pages',
'platform/features/hyperlink',

View File

@ -53,14 +53,8 @@
<style lang="scss">
@import "~styles/sass-base";
.l-layout,
.c-grid,
.c-grid__x,
.c-grid__y {
@include abs();
}
.l-layout {
@include abs();
display: flex;
flex-direction: column;
@ -78,34 +72,15 @@
}
}
.c-grid {
pointer-events: none;
&__x { @include bgTicks($colorGridLines, 'x'); }
&__y { @include bgTicks($colorGridLines, 'y'); }
}
.is-editing {
.l-shell__main-container > .l-layout {
// Target the top-most layout container and color its background
background: rgba($editColor, 0.1);
}
[s-selected],
[s-selected-parent] {
.l-layout {
// Show the layout grid for the top-most child of the current selection,
// and hide the grid for deeper nested levels.
[class*="__grid-holder"] {
display: block;
}
.l-layout [class*="__grid-holder"] {
display: none;
}
.l-shell__main-container {
> .l-layout {
[s-selected] {
border: $browseBorderSelected;
}
}
}
// Styles moved to _global.scss;
</style>
@ -237,22 +212,17 @@
return;
}
let domainObject = selection[0].context.item;
if (domainObject && domainObject === this.selectedObject) {
return;
}
this.selectedObject = domainObject;
this.removeListeners();
let domainObject = selection[0].context.item;
if (selection[1]) {
this.attachSelectionListeners();
if (selection[1] && domainObject) {
this.attachSelectionListeners(domainObject.identifier);
}
this.updateDrilledInState();
},
attachSelectionListeners() {
let id = this.openmct.objects.makeKeyString(this.selectedObject.identifier);
attachSelectionListeners(identifier) {
let id = this.openmct.objects.makeKeyString(identifier);
let path = "configuration.layout.panels[" + id + "]";
this.listeners.push(this.openmct.objects.observe(this.newDomainObject, path + ".hasFrame", function (newValue) {
this.frameItems.forEach(function (item) {

View File

@ -132,110 +132,8 @@
padding: $interiorMargin;
}
/*************************** SELECTION */
&.is-selectable {
&:hover {
box-shadow: $browseShdwSelectableHov;
}
}
&[s-selected], // LEGACY
&.is-selected {
border: $browseBorderSelected;
}
// Styles moved to _global.scss;
}
/*************************** EDITING */
.is-editing {
.c-frame {
&:not(.is-drilled-in).is-selectable {
border: $editBorderSelectable;
&:hover {
border: $editBorderSelectableHov;
}
&[s-selected],
&.is-selected {
border: $editBorderSelected;
> .c-frame-edit {
display: block; // Show the editing rect and handles
}
}
}
&.is-drilled-in {
border: $editBorderDrilledIn;
}
.u-links {
// Applied in markup to objects that provide links. Disable while editing.
pointer-events: none;
}
}
}
.c-frame-edit {
// The editing rect and handles
$z: 10;
@include abs();
box-shadow: rgba($editColor, 0.5) 0 0 10px;
display: none;
&__move {
@include abs();
cursor: move;
z-index: $z;
}
&__handle {
$d: 8px;
$o: floor($d * -0.5);
background: rgba($editColor, 0.3);
border: 1px solid $editColor;
position: absolute;
width: $d; height: $d;
top: auto; right: auto; bottom: auto; left: auto;
z-index: $z + 1;
&:before {
// Extended hit area
$m: -5px;
content: '';
display: block;
position: absolute;
top: $m; right: $m; bottom: $m; left: $m;
z-index: -1;
}
&:hover {
background: $editColor;
}
&.--nw {
cursor: nw-resize;
left: $o; top: $o;
}
&.--ne {
cursor: ne-resize;
right: $o; top: $o;
}
&.--se {
cursor: se-resize;
right: $o; bottom: $o;
}
&.--sw {
cursor: sw-resize;
left: $o; bottom: $o;
}
}
}
</style>

View File

@ -72,6 +72,7 @@ export default function () {
// and in edit mode.
return (selection &&
selection[1] &&
selection[1].context.item &&
selection[1].context.item.type === 'layout' &&
openmct.editor.isEditing());
},

View File

@ -36,7 +36,8 @@ define([
'./staticRootPlugin/plugin',
'./notebook/plugin',
'./displayLayout/plugin',
'./folderView/plugin'
'./folderView/plugin',
'../../platform/features/fixed/plugin'
], function (
_,
UTCTimeSystem,
@ -53,7 +54,8 @@ define([
StaticRootPlugin,
Notebook,
DisplayLayoutPlugin,
FolderView
FolderView,
FixedView
) {
var bundleMap = {
LocalStorage: 'platform/persistence/local',
@ -165,6 +167,7 @@ define([
plugins.Notebook = Notebook;
plugins.DisplayLayout = DisplayLayoutPlugin.default;
plugins.FolderView = FolderView;
plugins.FixedView = FixedView;
return plugins;
});

View File

@ -41,7 +41,7 @@ define([
return false;
}
let object = selection[0].context.item;
return object.type === 'table';
return object && object.type === 'table';
},
view: function (selection) {
let component;

View File

@ -229,6 +229,157 @@ body.desktop .has-local-controls {
//}
//}
/******************************************************** SELECTION AND EDITING */
// Provides supporting styles for Display Layouts and augmented legacy Fixed Position view
.c-grid,
.c-grid__x,
.c-grid__y {
@include abs();
}
.c-grid {
pointer-events: none;
&__x { @include bgTicks($colorGridLines, 'x'); }
&__y { @include bgTicks($colorGridLines, 'y'); }
}
/*************************** SELECTION */
.is-selectable {
&:hover {
box-shadow: $browseShdwSelectableHov;
}
}
/**************************** EDITING */
.is-editing {
*:not(.is-drilled-in).is-selectable {
border: $editBorderSelectable;
&:hover {
border: $editBorderSelectableHov;
}
&[s-selected],
&.is-selected {
border: $editBorderSelected;
> .c-frame-edit {
display: block; // Show the editing rect and handles
}
}
}
*.is-drilled-in {
border: $editBorderDrilledIn;
}
.u-links {
// Applied in markup to objects that provide links. Disable while editing.
pointer-events: none;
}
.c-frame-edit {
// In Layouts, this is the editing rect and handles
// In Fixed Position, this is a wrapper element
$z: 10;
@include abs();
display: none;
&__move {
@include abs();
box-shadow: rgba($editColor, 0.5) 0 0 10px;
cursor: move;
z-index: $z;
}
&__handle {
$d: 8px;
$o: floor($d * -0.5);
background: rgba($editColor, 0.3);
border: 1px solid $editColor;
position: absolute;
width: $d; height: $d;
top: auto; right: auto; bottom: auto; left: auto;
z-index: $z + 1;
&:before {
// Extended hit area
$m: -5px;
content: '';
display: block;
position: absolute;
top: $m; right: $m; bottom: $m; left: $m;
z-index: -1;
}
&:hover {
background: $editColor;
}
&--nwse {
cursor: nwse-resize;
}
&--nw {
cursor: nw-resize;
left: $o; top: $o;
}
&--ne {
cursor: ne-resize;
right: $o; top: $o;
}
&--se {
cursor: se-resize;
right: $o; bottom: $o;
}
&--sw {
cursor: sw-resize;
left: $o; bottom: $o;
}
}
}
.l-shell__main-container > .l-layout,
.l-shell__main-container > .c-object-view .l-fixed-position {
// Target the top-most layout container and color its background
background: rgba($editColor, 0.1);
}
// Layouts
[s-selected],
[s-selected-parent] {
.l-layout {
// Show the layout grid for the top-most child of the current selection,
// and hide the grid for deeper nested levels.
[class*="__grid-holder"] {
display: block;
}
.l-layout [class*="__grid-holder"] {
display: none;
}
}
}
// Fixed position
.l-fixed-position {
&__grid-holder {
display: block;
}
.c-frame-edit {
display: block;
}
}
}
/************************** LEGACY */
mct-container {

View File

@ -4,19 +4,19 @@
<ul class="c-properties__section">
<li class="c-properties__row">
<div class="c-properties__label">Title</div>
<div class="c-properties__value">{{ domainObject.name }}</div>
<div class="c-properties__value">{{ item.name }}</div>
</li>
<li class="c-properties__row">
<div class="c-properties__label">Type</div>
<div class="c-properties__value">{{ typeName }}</div>
</li>
<li class="c-properties__row" v-if="domainObject.created">
<li class="c-properties__row" v-if="item.created">
<div class="c-properties__label">Created</div>
<div class="c-properties__value">{{ domainObject.created }}</div>
<div class="c-properties__value">{{ item.created }}</div>
</li>
<li class="c-properties__row" v-if="domainObject.modified">
<li class="c-properties__row" v-if="item.modified">
<div class="c-properties__label">Modified</div>
<div class="c-properties__value">{{ domainObject.modified }}</div>
<div class="c-properties__value">{{ item.modified }}</div>
</li>
<li class="c-properties__row"
v-for="prop in typeProperties"
@ -37,12 +37,15 @@ export default {
}
},
computed: {
item() {
return this.domainObject || {};
},
type() {
return this.openmct.types.get(this.domainObject.type);
return this.openmct.types.get(this.item.type);
},
typeName() {
if (!this.type) {
return `Unknown: ${this.domainObject.type}`;
return `Unknown: ${this.item.type}`;
}
return this.type.definition.name;
},
@ -71,7 +74,7 @@ export default {
name: field.name,
value: field.path.reduce((object, field) => {
return object[field];
}, this.domainObject)
}, this.item)
};
});
}

View File

@ -1,8 +1,9 @@
<template>
<div class="c-toolbar">
<div class="c-toolbar" v-if="structure.length !== 0">
<component v-for="item in structure"
:is="item.control"
:options="item"
@click="triggerMethod(item, $event)"
@change="updateObjectValue"></component>
</div>
</template>
@ -36,18 +37,14 @@
},
methods: {
handleSelection(selection) {
this.removeListeners();
this.domainObjectsById = {};
if (!selection[0]) {
this.structure = [];
this.selectedObject = undefined;
this.removeListeners();
return;
}
let domainObject = selection[0].context.item;
this.selectedObject = domainObject;
this.removeListeners();
let structure = this.openmct.toolbars.get(selection) || [];
this.structure = structure.map(function (item) {
let toolbarItem = {...item};
@ -58,19 +55,56 @@
}.bind(this));
},
registerListener(domainObject) {
let id = this.openmct.objects.makeKeyString(domainObject.identifier);
if (!this.domainObjectsById[id]) {
this.domainObjectsById[id] = {
domainObject: domainObject
}
this.observeObject(domainObject, id);
}
},
observeObject(domainObject, id) {
let unobserveObject = this.openmct.objects.observe(domainObject, '*', function(newObject) {
let newObjectId = this.openmct.objects.makeKeyString(newObject.identifier);
this.structure = this.structure.map((item) => {
let toolbarItem = {...item};
let domainObjectId = this.openmct.objects.makeKeyString(toolbarItem.domainObject.identifier);
if (domainObjectId === newObjectId) {
toolbarItem.domainObject = JSON.parse(JSON.stringify(newObject));
}
return toolbarItem;
});
this.domainObjectsById[id].newObject = JSON.parse(JSON.stringify(newObject));
this.scheduleToolbarUpdate();
}.bind(this));
this.unObserveObjects.push(unobserveObject);
},
scheduleToolbarUpdate() {
if (this.toolbarUpdateScheduled) {
return;
}
this.toolbarUpdateScheduled = true;
setTimeout(this.updateToolbarAfterMutation.bind(this));
},
updateToolbarAfterMutation() {
this.structure = this.structure.map((item) => {
let toolbarItem = {...item};
let id = this.openmct.objects.makeKeyString(toolbarItem.domainObject.identifier);
let newObject = this.domainObjectsById[id].newObject;
if (newObject) {
toolbarItem.domainObject = newObject;
let newValue = _.get(newObject, item.property);
if (toolbarItem.value !== newValue) {
toolbarItem.value = newValue;
}
}
return toolbarItem;
});
Object.values(this.domainObjectsById).forEach(function (tracker) {
if (tracker.newObject) {
tracker.domainObject = tracker.newObject;
delete tracker.newObject;
}
});
this.toolbarUpdateScheduled = false;
},
removeListeners() {
if (this.unObserveObjects) {
this.unObserveObjects.forEach((unObserveObject) => {
@ -80,15 +114,26 @@
this.unObserveObjects = [];
},
updateObjectValue(value, item) {
let changedId = this.openmct.objects.makeKeyString(item.domainObject.identifier);
let changedItemId = this.openmct.objects.makeKeyString(item.domainObject.identifier);
this.structure = this.structure.map((s) => {
let toolbarItem = {...s};
if (changedId === this.openmct.objects.makeKeyString(toolbarItem.domainObject.identifier)) {
let id = this.openmct.objects.makeKeyString(toolbarItem.domainObject.identifier);
if (changedItemId === id && item.property === s.property) {
toolbarItem.value = value;
}
return toolbarItem;
});
this.openmct.objects.mutate(item.domainObject, item.property, value);
},
triggerMethod(item, event) {
if (item.method) {
item.method({...event});
}
},
handleEditing(isEditing) {
this.handleSelection(this.openmct.selection.get());
}
},
mounted() {
@ -97,12 +142,11 @@
// Toolbars may change when edit mode is enabled/disabled, so listen
// for edit mode changes and update toolbars if necessary.
this.openmct.editor.on('isEditing', (isEditing) => {
this.handleSelection(this.openmct.selection.get());
});
this.openmct.editor.on('isEditing', this.handleEditing);
},
detroyed() {
this.openmct.selection.off('change', this.handleSelection);
this.openmct.editor.off('isEditing', this.handleEditing);
this.removeListeners();
}
}

View File

@ -1,6 +1,7 @@
<template>
<div class="c-ctrl-wrapper">
<div class="c-click-icon"
:title="options.title"
:class="{
[options.icon]: true,
'c-click-icon--caution': options.modifier === 'caution'
@ -22,6 +23,9 @@ export default {
},
methods: {
onClick(event) {
if (this.options.dialog) {
// TODO: display a dialog
}
this.$emit('click', this.options);
}
}

View File

@ -2,6 +2,7 @@
<div class="c-ctrl-wrapper">
<div class="c-click-icon c-click-icon--swatched"
:class="options.icon"
:title="options.title"
@click="toggle">
<div class="c-swatch" :style="{
background: options.value
@ -38,7 +39,7 @@ export default {
methods: {
select(color) {
if (color.value !== this.options.value) {
this.$emit('change', color, this.options);
this.$emit('change', color.value, this.options);
}
}
},

View File

@ -2,6 +2,7 @@
<div class="c-ctrl-wrapper">
<div class="c-click-icon c-click-icon--menu"
:class="options.icon"
:title="options.title"
@click="toggle">
<div class="c-click-icon__label"
v-if="options.label">
@ -11,8 +12,8 @@
<div class="c-menu" v-if="open">
<ul>
<li v-for="option in options.options"
:class="option.class"
:title="option.title">
@click="onClick(option)"
:class="option.class">
{{ option.name }}
</li>
</ul>
@ -33,6 +34,11 @@ export default {
value.options.every((o) => o.name);
}
}
},
methods: {
onClick(option) {
this.$emit('click', option)
}
}
}
</script>

View File

@ -2,6 +2,7 @@
<div class="c-ctrl-wrapper">
<div class="c-click-icon c-click-icon--menu"
:class="options.icon"
:title="options.title"
@click="toggle">
<div class="c-button__label">{{ selectedName }}</div>
</div>
@ -10,7 +11,7 @@
<li v-for="option in options.options"
:key="option.value"
@click="select(option)">
{{ option.name }}
{{ option.name || option.value }}
</li>
</ul>
</div>
@ -28,7 +29,7 @@ export default {
validator(value) {
// must pass valid options array.
return Array.isArray(value.options) &&
value.options.every((o) => o.value && o.name);
value.options.every((o) => o.value);
}
}
},
@ -40,13 +41,11 @@ export default {
this.$emit('change', option.value, this.options);
}
},
data() {
},
computed: {
selectedName() {
let selectedOption = this.options.options.filter((o) => o.value === this.options.value)[0];
if (selectedOption) {
return selectedOption.name
return selectedOption.name || selectedOption.value
}
return '';
}