Merge branch 'topic-core-refactor' into misc-ui-8

This commit is contained in:
Charles Hacskaylo 2019-04-05 17:30:42 -07:00 committed by GitHub
commit 5ab68c0586
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
49 changed files with 1681 additions and 818 deletions

View File

@ -25,14 +25,20 @@ define([
cssClass: representation.cssClass,
description: representation.description,
canView: function (selection) {
if (!selection[0] || !selection[0].context.item) {
if (selection.length === 0 || selection[0].length === 0) {
return false;
}
let domainObject = selection[0].context.item;
return domainObject.type === typeDefinition.key;
let selectionContext = selection[0][0].context;
if (!selectionContext.item) {
return false;
}
return selectionContext.item.type === typeDefinition.key;
},
view: function (selection) {
let domainObject = selection[0].context.item;
let domainObject = selection[0][0].context.item;
let $rootScope = openmct.$injector.get('$rootScope');
let templateLinker = openmct.$injector.get('templateLinker');
let scope = $rootScope.$new();

View File

@ -79,9 +79,11 @@ export default class Editor extends EventEmitter {
* @private
*/
cancel() {
this.getTransactionService().cancel();
let cancelPromise = this.getTransactionService().cancel();
this.editing = false;
this.emit('isEditing', false);
return cancelPromise;
}
/**

View File

@ -25,7 +25,6 @@ define([
], function (
_
) {
/**
* A CompositionCollection represents the list of domain objects contained
* by another domain object. It provides methods for loading this
@ -63,7 +62,6 @@ define([
this.onProviderRemove = this.onProviderRemove.bind(this);
}
/**
* Listen for changes to this composition. Supports 'add', 'remove', and
* 'load' events.
@ -76,7 +74,11 @@ define([
if (!this.listeners[event]) {
throw new Error('Event not supported by composition: ' + event);
}
if (!this.mutationListener) {
this.mutationListener = this.publicAPI.objects.observe(this.domainObject, '*', (newDomainObject) => {
this.domainObject = newDomainObject;
})
}
if (this.provider.on && this.provider.off) {
if (event === 'add') {
this.provider.on(
@ -132,6 +134,10 @@ define([
this.listeners[event].splice(index, 1);
if (this.listeners[event].length === 0) {
if (this.mutationListener) {
this.mutationListener();
delete this.mutationListener;
}
// Remove provider listener if this is the last callback to
// be removed.
if (this.provider.off && this.provider.on) {

View File

@ -28,11 +28,17 @@ define([], function () {
key: "layout",
description: "A toolbar for objects inside a display layout.",
forSelection: function (selection) {
// Apply the layout toolbar if the selected object
// is inside a layout, or the main layout is selected.
return (selection &&
((selection[1] && selection[1].context.item && selection[1].context.item.type === 'layout') ||
(selection[0].context.item && selection[0].context.item.type === 'layout')));
if (!selection || selection.length === 0) {
return false;
}
let selectionPath = selection[0];
let selectedObject = selectionPath[0];
let selectedParent = selectionPath[1];
// Apply the layout toolbar if the selected object is inside a layout, or the main layout is selected.
return (selectedParent && selectedParent.context.item && selectedParent.context.item.type === 'layout') ||
(selectedObject.context.item && selectedObject.context.item.type === 'layout');
},
toolbar: function (selection) {
const DIALOG_FORM = {
@ -73,190 +79,72 @@ define([], function () {
return openmct.$injector.get('dialogService').getUserInput(form, {});
}
function getPath() {
return `configuration.items[${selection[0].context.index}]`;
function getPath(selectionPath) {
return `configuration.items[${selectionPath[0].context.index}]`;
}
let selectedParent = selection[1] && selection[1].context.item,
selectedObject = selection[0].context.item,
layoutItem = selection[0].context.layoutItem,
toolbar = [];
if (selectedObject && selectedObject.type === 'layout') {
toolbar.push({
control: "menu",
domainObject: selectedObject,
method: function (option) {
let name = option.name.toLowerCase();
let form = DIALOG_FORM[name];
if (form) {
getUserInput(form)
.then(element => selection[0].context.addElement(name, element));
} else {
selection[0].context.addElement(name);
}
},
key: "add",
icon: "icon-plus",
label: "Add",
options: [
{
"name": "Box",
"class": "icon-box-round-corners"
},
{
"name": "Line",
"class": "icon-line-horz"
},
{
"name": "Text",
"class": "icon-font"
},
{
"name": "Image",
"class": "icon-image"
}
]
function getAllTypes(selection) {
return selection.filter(selectionPath => {
let type = selectionPath[0].context.layoutItem.type;
return type === 'text-view' ||
type === 'telemetry-view' ||
type === 'box-view' ||
type === 'image-view' ||
type === 'line-view' ||
type === 'subobject-view';
});
}
if (!layoutItem) {
return toolbar;
}
let separator = {
control: "separator"
};
let remove = {
control: "button",
domainObject: selectedParent,
icon: "icon-trash",
title: "Delete the selected object",
method: function () {
let removeItem = selection[1].context.removeItem;
let prompt = openmct.overlays.dialog({
iconClass: 'alert',
message: `Warning! This action will remove this item from the Display Layout. Do you want to continue?`,
buttons: [
function getAddButton(selection, selectionPath) {
if (selection.length === 1) {
selectionPath = selectionPath || selection[0];
return {
control: "menu",
domainObject: selectionPath[0].context.item,
method: function (option) {
let name = option.name.toLowerCase();
let form = DIALOG_FORM[name];
if (form) {
getUserInput(form)
.then(element => selectionPath[0].context.addElement(name, element));
} else {
selectionPath[0].context.addElement(name);
}
},
key: "add",
icon: "icon-plus",
label: "Add",
options: [
{
label: 'Ok',
emphasis: 'true',
callback: function () {
removeItem(layoutItem, selection[0].context.index);
prompt.dismiss();
}
"name": "Box",
"class": "icon-box-round-corners"
},
{
label: 'Cancel',
callback: function () {
prompt.dismiss();
}
"name": "Line",
"class": "icon-line-horz"
},
{
"name": "Text",
"class": "icon-font"
},
{
"name": "Image",
"class": "icon-image"
}
]
});
};
}
};
let stackOrder = {
control: "menu",
domainObject: selectedParent,
icon: "icon-layers",
title: "Move the selected object above or below other objects",
options: [
{
name: "Move to Top",
value: "top",
class: "icon-arrow-double-up"
},
{
name: "Move Up",
value: "up",
class: "icon-arrow-up"
},
{
name: "Move Down",
value: "down",
class: "icon-arrow-down"
},
{
name: "Move to Bottom",
value: "bottom",
class: "icon-arrow-double-down"
}
],
method: function (option) {
selection[1].context.orderItem(option.value, selection[0].context.index);
}
};
let useGrid = {
control: "toggle-button",
domainObject: selectedParent,
property: function () {
return getPath() + ".useGrid";
},
options: [
{
value: false,
icon: "icon-grid-snap-to",
title: "Grid snapping enabled"
},
{
value: true,
icon: "icon-grid-snap-no",
title: "Grid snapping disabled"
}
]
};
let x = {
control: "input",
type: "number",
domainObject: selectedParent,
property: function () {
return getPath() + ".x";
},
label: "X:",
title: "X position"
},
y = {
control: "input",
type: "number",
domainObject: selectedParent,
property: function () {
return getPath() + ".y";
},
label: "Y:",
title: "Y position",
},
width = {
control: 'input',
type: 'number',
domainObject: selectedParent,
property: function () {
return getPath() + ".width";
},
label: 'W:',
title: 'Resize object width'
},
height = {
control: 'input',
type: 'number',
domainObject: selectedParent,
property: function () {
return getPath() + ".height";
},
label: 'H:',
title: 'Resize object height'
};
}
if (layoutItem.type === 'subobject-view') {
if (toolbar.length > 0) {
toolbar.push(separator);
}
toolbar.push({
function getToggleFrameButton(selectedParent, selection) {
return {
control: "toggle-button",
domainObject: selectedParent,
property: function () {
return getPath() + ".hasFrame";
applicableSelectedItems: selection.filter(selectionPath =>
selectionPath[0].context.layoutItem.type === 'subobject-view'
),
property: function (selectionPath) {
return getPath(selectionPath) + ".hasFrame";
},
options: [
{
@ -270,52 +158,186 @@ define([], function () {
title: "Frame hidden"
}
]
});
toolbar.push(separator);
toolbar.push(stackOrder);
toolbar.push(x);
toolbar.push(y);
toolbar.push(width);
toolbar.push(height);
toolbar.push(useGrid);
toolbar.push(separator);
toolbar.push(remove);
} else {
};
}
function getRemoveButton(selectedParent, selectionPath, selection) {
return {
control: "button",
domainObject: selectedParent,
icon: "icon-trash",
title: "Delete the selected object",
method: function () {
let removeItem = selectionPath[1].context.removeItem;
let prompt = openmct.overlays.dialog({
iconClass: 'alert',
message: `Warning! This action will remove this item from the Display Layout. Do you want to continue?`,
buttons: [
{
label: 'Ok',
emphasis: 'true',
callback: function () {
removeItem(getAllTypes(selection));
prompt.dismiss();
}
},
{
label: 'Cancel',
callback: function () {
prompt.dismiss();
}
}
]
});
}
};
}
function getStackOrder(selectedParent, selectionPath) {
return {
control: "menu",
domainObject: selectedParent,
icon: "icon-layers",
title: "Move the selected object above or below other objects",
options: [
{
name: "Move to Top",
value: "top",
class: "icon-arrow-double-up"
},
{
name: "Move Up",
value: "up",
class: "icon-arrow-up"
},
{
name: "Move Down",
value: "down",
class: "icon-arrow-down"
},
{
name: "Move to Bottom",
value: "bottom",
class: "icon-arrow-double-down"
}
],
method: function (option) {
selectionPath[1].context.orderItem(option.value, getAllTypes(selection));
}
};
}
function getXInput(selectedParent, selection) {
if (selection.length === 1) {
return {
control: "input",
type: "number",
domainObject: selectedParent,
applicableSelectedItems: getAllTypes(selection),
property: function (selectionPath) {
return getPath(selectionPath) + ".x";
},
label: "X:",
title: "X position"
};
}
}
function getYInput(selectedParent, selection) {
if (selection.length === 1) {
return {
control: "input",
type: "number",
domainObject: selectedParent,
applicableSelectedItems: getAllTypes(selection),
property: function (selectionPath) {
return getPath(selectionPath) + ".y";
},
label: "Y:",
title: "Y position",
};
}
}
function getWidthInput(selectedParent, selection) {
if (selection.length === 1) {
return {
control: 'input',
type: 'number',
domainObject: selectedParent,
applicableSelectedItems: getAllTypes(selection),
property: function (selectionPath) {
return getPath(selectionPath) + ".width";
},
label: 'W:',
title: 'Resize object width'
};
}
}
function getHeightInput(selectedParent, selection) {
if (selection.length === 1) {
return {
control: 'input',
type: 'number',
domainObject: selectedParent,
applicableSelectedItems: getAllTypes(selection),
property: function (selectionPath) {
return getPath(selectionPath) + ".height";
},
label: 'H:',
title: 'Resize object height'
};
}
}
function getX2Input(selectedParent, selection) {
if (selection.length === 1) {
return {
control: "input",
type: "number",
domainObject: selectedParent,
applicableSelectedItems: selection.filter(selectionPath => {
return selectionPath[0].context.layoutItem.type === 'line-view';
}),
property: function (selectionPath) {
return getPath(selectionPath) + ".x2";
},
label: "X2:",
title: "X2 position"
};
}
}
function getY2Input(selectedParent, selection) {
if (selection.length === 1) {
return {
control: "input",
type: "number",
domainObject: selectedParent,
applicableSelectedItems: selection.filter(selectionPath => {
return selectionPath[0].context.layoutItem.type === 'line-view';
}),
property: function (selectionPath) {
return getPath(selectionPath) + ".y2";
},
label: "Y2:",
title: "Y2 position",
};
}
}
function getTextSizeMenu(selectedParent, selection) {
const TEXT_SIZE = [8, 9, 10, 11, 12, 13, 14, 15, 16, 20, 24, 30, 36, 48, 72, 96, 128];
let fill = {
control: "color-picker",
domainObject: selectedParent,
property: function () {
return getPath() + ".fill";
},
icon: "icon-paint-bucket",
title: "Set fill color"
},
stroke = {
control: "color-picker",
domainObject: selectedParent,
property: function () {
return getPath() + ".stroke";
},
icon: "icon-line-horz",
title: "Set border color"
},
color = {
control: "color-picker",
domainObject: selectedParent,
property: function () {
return getPath() + ".color";
},
icon: "icon-font",
mandatory: true,
title: "Set text color",
preventNone: true
},
size = {
return {
control: "select-menu",
domainObject: selectedParent,
property: function () {
return getPath() + ".size";
applicableSelectedItems: selection.filter(selectionPath => {
let type = selectionPath[0].context.layoutItem.type;
return type === 'text-view' || type === 'telemetry-view';
}),
property: function (selectionPath) {
return getPath(selectionPath) + ".size";
},
title: "Set text size",
options: TEXT_SIZE.map(size => {
@ -324,13 +346,128 @@ define([], function () {
};
})
};
}
if (layoutItem.type === 'telemetry-view') {
let displayMode = {
function getFillMenu(selectedParent, selection) {
return {
control: "color-picker",
domainObject: selectedParent,
applicableSelectedItems: selection.filter(selectionPath => {
let type = selectionPath[0].context.layoutItem.type;
return type === 'text-view' ||
type === 'telemetry-view' ||
type === 'box-view';
}),
property: function (selectionPath) {
return getPath(selectionPath) + ".fill";
},
icon: "icon-paint-bucket",
title: "Set fill color"
};
}
function getStrokeMenu(selectedParent, selection) {
return {
control: "color-picker",
domainObject: selectedParent,
applicableSelectedItems: selection.filter(selectionPath => {
let type = selectionPath[0].context.layoutItem.type;
return type === 'text-view' ||
type === 'telemetry-view' ||
type === 'box-view' ||
type === 'image-view' ||
type === 'line-view';
}),
property: function (selectionPath) {
return getPath(selectionPath) + ".stroke";
},
icon: "icon-line-horz",
title: "Set border color"
};
}
function getTextColorMenu(selectedParent, selection) {
return {
control: "color-picker",
domainObject: selectedParent,
applicableSelectedItems: selection.filter(selectionPath => {
let type = selectionPath[0].context.layoutItem.type;
return type === 'text-view' || type === 'telemetry-view';
}),
property: function (selectionPath) {
return getPath(selectionPath) + ".color";
},
icon: "icon-font",
mandatory: true,
title: "Set text color",
preventNone: true
};
}
function getURLButton(selectedParent, selection) {
return {
control: "button",
domainObject: selectedParent,
applicableSelectedItems: selection.filter(selectionPath => {
return selectionPath[0].context.layoutItem.type === 'image-view';
}),
property: function (selectionPath) {
return getPath(selectionPath);
},
icon: "icon-image",
title: "Edit image properties",
dialog: DIALOG_FORM['image']
};
}
function getTextButton(selectedParent, selection) {
return {
control: "button",
domainObject: selectedParent,
applicableSelectedItems: selection.filter(selectionPath => {
return selectionPath[0].context.layoutItem.type === 'text-view';
}),
property: function (selectionPath) {
return getPath(selectionPath);
},
icon: "icon-gear",
title: "Edit text properties",
dialog: DIALOG_FORM['text']
};
}
function getTelemetryValueMenu(selectionPath, selection) {
if (selection.length === 1) {
return {
control: "select-menu",
domainObject: selectionPath[1].context.item,
applicableSelectedItems: selection.filter(selectionPath => {
return selectionPath[0].context.layoutItem.type === 'telemetry-view';
}),
property: function (selectionPath) {
return getPath(selectionPath) + ".value";
},
title: "Set value",
options: openmct.telemetry.getMetadata(selectionPath[0].context.item).values().map(value => {
return {
name: value.name,
value: value.key
}
})
};
}
}
function getDisplayModeMenu(selectedParent, selection) {
if (selection.length === 1) {
return {
control: "select-menu",
domainObject: selectedParent,
property: function () {
return getPath() + ".displayMode";
applicableSelectedItems: selection.filter(selectionPath => {
return selectionPath[0].context.layoutItem.type === 'telemetry-view';
}),
property: function (selectionPath) {
return getPath(selectionPath) + ".displayMode";
},
title: "Set display mode",
options: [
@ -347,146 +484,196 @@ define([], function () {
value: "value"
}
]
},
value = {
control: "select-menu",
domainObject: selectedParent,
property: function () {
return getPath() + ".value";
},
title: "Set value",
options: openmct.telemetry.getMetadata(selectedObject).values().map(value => {
return {
name: value.name,
value: value.key
}
})
};
toolbar = [
displayMode,
separator,
value,
separator,
fill,
stroke,
color,
separator,
size,
separator,
stackOrder,
x,
y,
height,
width,
useGrid,
separator,
remove
];
} else if (layoutItem.type === 'text-view') {
let text = {
control: "button",
domainObject: selectedParent,
property: function () {
return getPath();
},
icon: "icon-gear",
title: "Edit text properties",
dialog: DIALOG_FORM['text']
};
toolbar = [
fill,
stroke,
separator,
color,
size,
separator,
stackOrder,
x,
y,
height,
width,
useGrid,
separator,
text,
separator,
remove
];
} else if (layoutItem.type === 'box-view') {
toolbar = [
fill,
stroke,
separator,
stackOrder,
x,
y,
height,
width,
useGrid,
separator,
remove
];
} else if (layoutItem.type === 'image-view') {
let url = {
control: "button",
domainObject: selectedParent,
property: function () {
return getPath();
},
icon: "icon-image",
title: "Edit image properties",
dialog: DIALOG_FORM['image']
};
toolbar = [
stroke,
separator,
stackOrder,
x,
y,
height,
width,
useGrid,
separator,
url,
separator,
remove
];
} else if (layoutItem.type === 'line-view') {
let x2 = {
control: "input",
type: "number",
domainObject: selectedParent,
property: function () {
return getPath() + ".x2";
},
label: "X2:",
title: "X2 position"
},
y2 = {
control: "input",
type: "number",
domainObject: selectedParent,
property: function () {
return getPath() + ".y2";
},
label: "Y2:",
title: "Y2 position",
};
toolbar = [
stroke,
separator,
stackOrder,
x,
y,
x2,
y2,
useGrid,
separator,
remove
];
}
}
return toolbar;
function getSeparator() {
return {
control: "separator"
};
}
function isMainLayoutSelected(selectionPath) {
let selectedObject = selectionPath[0].context.item;
return selectedObject && selectedObject.type === 'layout' &&
!selectionPath[0].context.layoutItem;
}
if (isMainLayoutSelected(selection[0])) {
return [getAddButton(selection)];
}
let toolbar = {
'add-menu': [],
'toggle-frame': [],
'display-mode': [],
'telemetry-value': [],
'style': [],
'text-style': [],
'position': [],
'text': [],
'url': [],
'remove': [],
};
selection.forEach(selectionPath => {
let selectedParent = selectionPath[1].context.item;
let layoutItem = selectionPath[0].context.layoutItem;
if (layoutItem.type === 'subobject-view') {
if (toolbar['add-menu'].length === 0 && selectionPath[0].context.item.type === 'layout') {
toolbar['add-menu'] = [getAddButton(selection, selectionPath)];
}
if (toolbar['toggle-frame'].length === 0) {
toolbar['toggle-frame'] = [getToggleFrameButton(selectedParent, selection)];
}
if (toolbar['position'].length === 0) {
toolbar['position'] = [
getStackOrder(selectedParent, selectionPath),
getXInput(selectedParent, selection),
getYInput(selectedParent, selection),
getHeightInput(selectedParent, selection),
getWidthInput(selectedParent, selection)
];
}
if (toolbar['remove'].length === 0) {
toolbar['remove'] = [getRemoveButton(selectedParent, selectionPath, selection)];
}
} else if (layoutItem.type === 'telemetry-view') {
if (toolbar['display-mode'].length === 0) {
toolbar['display-mode'] = [getDisplayModeMenu(selectedParent, selection)];
}
if (toolbar['telemetry-value'].length === 0) {
toolbar['telemetry-value'] = [getTelemetryValueMenu(selectionPath, selection)];
}
if (toolbar['style'].length < 2) {
toolbar['style'] = [
getFillMenu(selectedParent, selection),
getStrokeMenu(selectedParent, selection)
];
}
if (toolbar['text-style'].length === 0) {
toolbar['text-style'] = [
getTextColorMenu(selectedParent, selection),
getTextSizeMenu(selectedParent, selection)
];
}
if (toolbar['position'].length === 0) {
toolbar['position'] = [
getStackOrder(selectedParent, selectionPath),
getXInput(selectedParent, selection),
getYInput(selectedParent, selection),
getHeightInput(selectedParent, selection),
getWidthInput(selectedParent, selection)
];
}
if (toolbar['remove'].length === 0) {
toolbar['remove'] = [getRemoveButton(selectedParent, selectionPath, selection)];
}
} else if (layoutItem.type === 'text-view') {
if (toolbar['style'].length < 2) {
toolbar['style'] = [
getFillMenu(selectedParent, selection),
getStrokeMenu(selectedParent, selection)
];
}
if (toolbar['text-style'].length === 0) {
toolbar['text-style'] = [
getTextColorMenu(selectedParent, selection),
getTextSizeMenu(selectedParent, selection)
];
}
if (toolbar['position'].length === 0) {
toolbar['position'] = [
getStackOrder(selectedParent, selectionPath),
getXInput(selectedParent, selection),
getYInput(selectedParent, selection),
getHeightInput(selectedParent, selection),
getWidthInput(selectedParent, selection)
];
}
if (toolbar['text'].length === 0) {
toolbar['text'] = [getTextButton(selectedParent, selection)];
}
if (toolbar['remove'].length === 0) {
toolbar['remove'] = [getRemoveButton(selectedParent, selectionPath, selection)];
}
} else if (layoutItem.type === 'box-view') {
if (toolbar['style'].length < 2) {
toolbar['style'] = [
getFillMenu(selectedParent, selection),
getStrokeMenu(selectedParent, selection)
];
}
if (toolbar['position'].length === 0) {
toolbar['position'] = [
getStackOrder(selectedParent, selectionPath),
getXInput(selectedParent, selection),
getYInput(selectedParent, selection),
getHeightInput(selectedParent, selection),
getWidthInput(selectedParent, selection)
];
}
if (toolbar['remove'].length === 0) {
toolbar['remove'] = [getRemoveButton(selectedParent, selectionPath, selection)];
}
} else if (layoutItem.type === 'image-view') {
if (toolbar['style'].length === 0) {
toolbar['style'] = [
getStrokeMenu(selectedParent, selection)
];
}
if (toolbar['position'].length === 0) {
toolbar['position'] = [
getStackOrder(selectedParent, selectionPath),
getXInput(selectedParent, selection),
getYInput(selectedParent, selection),
getHeightInput(selectedParent, selection),
getWidthInput(selectedParent, selection)
];
}
if (toolbar['url'].length === 0) {
toolbar['url'] = [getURLButton(selectedParent, selection)];
}
if (toolbar['remove'].length === 0) {
toolbar['remove'] = [getRemoveButton(selectedParent, selectionPath, selection)];
}
} else if (layoutItem.type === 'line-view') {
if (toolbar['style'].length === 0) {
toolbar['style'] = [
getStrokeMenu(selectedParent, selection)
];
}
if (toolbar['position'].length === 0) {
toolbar['position'] = [
getStackOrder(selectedParent, selectionPath),
getXInput(selectedParent, selection),
getYInput(selectedParent, selection),
getX2Input(selectedParent, selection),
getY2Input(selectedParent, selection)
];
}
if (toolbar['remove'].length === 0) {
toolbar['remove'] = [getRemoveButton(selectedParent, selectionPath, selection)];
}
}
});
let toolbarArray = Object.values(toolbar);
return _.flatten(toolbarArray.reduce((accumulator, group, index) => {
group = group.filter(control => control !== undefined);
if (group.length > 0) {
accumulator.push(group);
if (index < toolbarArray.length - 1) {
accumulator.push(getSeparator());
}
}
return accumulator;
}, []));
}
}
}

View File

@ -95,7 +95,7 @@ define(
* @param {number[]} pixelDelta the offset from the
* original position, in pixels
*/
LayoutDrag.prototype.getAdjustedPosition = function (pixelDelta) {
LayoutDrag.prototype.getAdjustedPositionAndDimensions = function (pixelDelta) {
var gridDelta = toGridDelta(this.gridSize, pixelDelta);
return {
position: max(add(
@ -109,6 +109,16 @@ define(
};
};
LayoutDrag.prototype.getAdjustedPosition = function (pixelDelta) {
var gridDelta = toGridDelta(this.gridSize, pixelDelta);
return {
position: max(add(
this.rawPosition.position,
multiply(gridDelta, this.posFactor)
), [0, 0])
};
};
return LayoutDrag;
}

View File

@ -23,7 +23,8 @@
<template>
<layout-frame :item="item"
:grid-size="gridSize"
@endDrag="(item, updates) => $emit('endDrag', item, updates)">
@move="(gridDelta) => $emit('move', gridDelta)"
@endMove="() => $emit('endMove')">
<div class="c-box-view"
:style="style">
</div>
@ -54,8 +55,7 @@
x: 1,
y: 1,
width: 10,
height: 5,
useGrid: true
height: 5
};
},
inject: ['openmct'],

View File

@ -23,8 +23,11 @@
<template>
<div class="l-layout"
@dragover="handleDragOver"
@click="bypassSelection"
@drop="handleDrop">
@click.capture="bypassSelection"
@drop="handleDrop"
:class="{
'is-multi-selected': selectedLayoutItems.length > 1
}">
<!-- Background grid -->
<div class="l-layout__grid-holder c-grid">
<div class="c-grid__x l-grid l-grid-x"
@ -39,18 +42,38 @@
:is="item.type"
:item="item"
:key="item.id"
:gridSize="item.useGrid ? gridSize : [1, 1]"
:gridSize="gridSize"
:initSelect="initSelectIndex === index"
:index="index"
@endDrag="endDrag"
>
:multiSelect="selectedLayoutItems.length > 1"
@move="move"
@endMove="endMove"
@endLineResize='endLineResize'>
</component>
<edit-marquee v-if='showMarquee'
:gridSize="gridSize"
:selectedLayoutItems="selectedLayoutItems"
@endResize="endResize">
</edit-marquee>
</div>
</template>
<style lang="scss">
@import "~styles/sass-base";
@mixin displayMarquee($c) {
> .c-frame-edit {
// All other frames
//@include test($c, 0.4);
display: block;
}
> .c-frame > .c-frame-edit {
// Line object frame
//@include test($c, 0.4);
display: block;
}
}
.l-layout {
@include abs();
display: flex;
@ -70,7 +93,7 @@
.l-shell__main-container {
&[s-selected],
&[s-selected-parent] {
// Display grid in main layout holder when editing
// Display grid and allow edit marquee to display in main layout holder when editing
> .l-layout {
background: $editUIGridColorBg;
@ -84,7 +107,7 @@
.l-layout__frame {
&[s-selected],
&[s-selected-parent] {
// Display grid in nested layouts when editing
// Display grid and allow edit marquee to display in nested layouts when editing
> * > * > .l-layout {
background: $editUIGridColorBg;
box-shadow: inset $editUIGridColorFg 0 0 2px 1px;
@ -95,10 +118,21 @@
}
}
}
/*********************** EDIT MARQUEE CONTROL */
*[s-selected-parent] {
> .l-layout {
// When main shell layout is the parent
@include displayMarquee(deeppink);
}
> * > * > * {
// When a sub-layout is the parent
@include displayMarquee(blue);
}
}
}
</style>
<script>
import uuid from 'uuid';
@ -108,6 +142,7 @@
import TextView from './TextView.vue'
import LineView from './LineView.vue'
import ImageView from './ImageView.vue'
import EditMarquee from './EditMarquee.vue'
const ITEM_TYPE_VIEW_MAP = {
'subobject-view': SubobjectView,
@ -123,9 +158,11 @@
down: -1,
bottom: Number.NEGATIVE_INFINITY
};
const DRAG_OBJECT_TRANSFER_PREFIX = 'openmct/domain-object/';
let components = ITEM_TYPE_VIEW_MAP;
components['edit-marquee'] = EditMarquee;
function getItemDefinition(itemType, ...options) {
let itemView = ITEM_TYPE_VIEW_MAP[itemType];
@ -141,7 +178,8 @@
let domainObject = JSON.parse(JSON.stringify(this.domainObject));
return {
internalDomainObject: domainObject,
initSelectIndex: undefined
initSelectIndex: undefined,
selection: []
};
},
computed: {
@ -150,82 +188,115 @@
},
layoutItems() {
return this.internalDomainObject.configuration.items;
},
selectedLayoutItems() {
return this.layoutItems.filter(item => {
return this.itemIsInCurrentSelection(item);
});
},
showMarquee() {
let selectionPath = this.selection[0];
let singleSelectedLine = this.selection.length === 1 &&
selectionPath[0].context.layoutItem && selectionPath[0].context.layoutItem.type === 'line-view';
return selectionPath && selectionPath.length > 1 && !singleSelectedLine;
}
},
inject: ['openmct', 'options'],
props: ['domainObject'],
components: ITEM_TYPE_VIEW_MAP,
components: components,
methods: {
addElement(itemType, element) {
this.addItem(itemType + '-view', element);
},
setSelection(selection) {
if (selection.length === 0) {
return;
}
if (this.removeSelectionListener) {
this.removeSelectionListener();
}
let itemIndex = selection[0].context.index;
if (itemIndex !== undefined) {
this.attachSelectionListener(itemIndex);
}
this.selection = selection;
},
attachSelectionListener(index) {
let path = `configuration.items[${index}].useGrid`;
this.removeSelectionListener = this.openmct.objects.observe(this.internalDomainObject, path, function (value) {
let item = this.layoutItems[index];
if (value) {
item.x = Math.round(item.x / this.gridSize[0]);
item.y = Math.round(item.y / this.gridSize[1]);
item.width = Math.round(item.width / this.gridSize[0]);
item.height = Math.round(item.height / this.gridSize[1]);
if (item.x2) {
item.x2 = Math.round(item.x2 / this.gridSize[0]);
}
if (item.y2) {
item.y2 = Math.round(item.y2 / this.gridSize[1]);
}
} else {
item.x = this.gridSize[0] * item.x;
item.y = this.gridSize[1] * item.y;
item.width = this.gridSize[0] * item.width;
item.height = this.gridSize[1] * item.height;
if (item.x2) {
item.x2 = this.gridSize[0] * item.x2;
}
if (item.y2) {
item.y2 = this.gridSize[1] * item.y2;
}
}
item.useGrid = value;
this.mutate(`configuration.items[${index}]`, item);
}.bind(this));
itemIsInCurrentSelection(item) {
return this.selection.some(selectionPath =>
selectionPath[0].context.layoutItem && selectionPath[0].context.layoutItem.id === item.id);
},
bypassSelection($event) {
if (this.dragInProgress) {
if ($event) {
$event.stopImmediatePropagation();
}
this.dragInProgress = false;
return;
}
},
endDrag(item, updates) {
endLineResize(item, updates) {
this.dragInProgress = true;
setTimeout(function () {
this.dragInProgress = false;
}.bind(this), 0);
let index = this.layoutItems.indexOf(item);
Object.assign(item, updates);
this.mutate(`configuration.items[${index}]`, item);
},
endResize(scaleWidth, scaleHeight, marqueeStart, marqueeOffset) {
this.dragInProgress = true;
this.layoutItems.forEach(item => {
if (this.itemIsInCurrentSelection(item)) {
let itemXInMarqueeSpace = item.x - marqueeStart.x;
let itemXInMarqueeSpaceAfterScale = Math.round(itemXInMarqueeSpace * scaleWidth);
item.x = itemXInMarqueeSpaceAfterScale + marqueeOffset.x + marqueeStart.x;
let itemYInMarqueeSpace = item.y - marqueeStart.y;
let itemYInMarqueeSpaceAfterScale = Math.round(itemYInMarqueeSpace * scaleHeight);
item.y = itemYInMarqueeSpaceAfterScale + marqueeOffset.y + marqueeStart.y;
if (item.x2) {
let itemX2InMarqueeSpace = item.x2 - marqueeStart.x;
let itemX2InMarqueeSpaceAfterScale = Math.round(itemX2InMarqueeSpace * scaleWidth);
item.x2 = itemX2InMarqueeSpaceAfterScale + marqueeOffset.x + marqueeStart.x;
} else {
item.width = Math.round(item.width * scaleWidth);
}
if (item.y2) {
let itemY2InMarqueeSpace = item.y2 - marqueeStart.y;
let itemY2InMarqueeSpaceAfterScale = Math.round(itemY2InMarqueeSpace * scaleHeight);
item.y2 = itemY2InMarqueeSpaceAfterScale + marqueeOffset.y + marqueeStart.y;
} else {
item.height = Math.round(item.height * scaleHeight);
}
}
});
this.mutate("configuration.items", this.layoutItems);
},
move(gridDelta) {
this.dragInProgress = true;
if (!this.initialPositions) {
this.initialPositions = {};
_.cloneDeep(this.selectedLayoutItems).forEach(selectedItem => {
if (selectedItem.type === 'line-view') {
this.initialPositions[selectedItem.id] = [selectedItem.x, selectedItem.y, selectedItem.x2, selectedItem.y2];
} else {
this.initialPositions[selectedItem.id] = [selectedItem.x, selectedItem.y];
}
});
}
let layoutItems = this.layoutItems.map(item => {
if (this.initialPositions[item.id]) {
let startingPosition = this.initialPositions[item.id];
let [startingX, startingY, startingX2, startingY2] = startingPosition;
item.x = startingX + gridDelta[0];
item.y = startingY + gridDelta[1];
if (item.x2) {
item.x2 = startingX2 + gridDelta[0];
}
if (item.y2) {
item.y2 = startingY2 + gridDelta[1];
}
}
return item;
});
},
endMove() {
this.mutate('configuration.items', this.layoutItems);
this.initialPositions = undefined;
},
mutate(path, value) {
this.openmct.objects.mutate(this.internalDomainObject, path, value);
},
@ -313,11 +384,15 @@
this.objectViewMap[keyString] = true;
}
},
removeItem(item, index) {
removeItem(selectedItems) {
let indices = [];
this.initSelectIndex = -1;
this.layoutItems.splice(index, 1);
selectedItems.forEach(selectedItem => {
indices.push(selectedItem[0].context.index);
this.untrackItem(selectedItem[0].context.layoutItem);
});
_.pullAt(this.layoutItems, indices);
this.mutate("configuration.items", this.layoutItems);
this.untrackItem(item);
this.$el.click();
},
untrackItem(item) {
@ -383,20 +458,74 @@
this.mutate("configuration.items", layoutItems);
this.$el.click();
},
orderItem(position, index) {
orderItem(position, selectedItems) {
let delta = ORDERS[position];
let newIndex = Math.max(Math.min(index + delta, this.layoutItems.length - 1), 0);
let item = this.layoutItems[index];
let indices = [];
let newIndex = -1;
let items = [];
if (newIndex !== index) {
this.layoutItems.splice(index, 1);
this.layoutItems.splice(newIndex, 0, item);
this.mutate('configuration.items', this.layoutItems);
Object.assign(items, this.layoutItems);
this.selectedLayoutItems.forEach(selectedItem => {
indices.push(this.layoutItems.indexOf(selectedItem));
});
indices.sort((a, b) => a - b);
if (this.removeSelectionListener) {
this.removeSelectionListener();
this.attachSelectionListener(newIndex);
if (position === 'top' || position === 'up') {
indices.reverse();
}
if (position === 'top' || position === 'bottom') {
this.moveToTopOrBottom(position, indices, items, delta);
} else {
this.moveUpOrDown(position, indices, items, delta);
}
this.mutate('configuration.items', this.layoutItems);
},
moveUpOrDown(position, indices, items, delta) {
let previousItemIndex = -1;
let newIndex = -1;
indices.forEach((itemIndex, index) => {
let isAdjacentItemSelected = position === 'up' ?
itemIndex + 1 === previousItemIndex :
itemIndex - 1 === previousItemIndex;
if (index > 0 && isAdjacentItemSelected) {
if (position === 'up') {
newIndex -= 1;
} else {
newIndex += 1;
}
} else {
newIndex = Math.max(Math.min(itemIndex + delta, this.layoutItems.length - 1), 0);
}
previousItemIndex = itemIndex;
this.updateItemOrder(newIndex, itemIndex, items);
});
},
moveToTopOrBottom(position, indices, items, delta) {
let newIndex = -1;
indices.forEach((itemIndex, index) => {
if (index === 0) {
newIndex = Math.max(Math.min(itemIndex + delta, this.layoutItems.length - 1), 0);
} else {
if (position === 'top') {
newIndex -= 1;
} else {
newIndex += 1;
}
}
this.updateItemOrder(newIndex, itemIndex, items);
});
},
updateItemOrder(newIndex, itemIndex, items) {
if (newIndex !== itemIndex) {
this.layoutItems.splice(itemIndex, 1);
this.layoutItems.splice(newIndex, 0, items[itemIndex]);
}
}
},
@ -412,14 +541,10 @@
this.composition.load();
},
destroyed: function () {
this.openmct.off('change', this.setSelection);
this.openmct.selection.off('change', this.setSelection);
this.composition.off('add', this.addChild);
this.composition.off('remove', this.removeChild);
this.unlisten();
if (this.removeSelectionListener) {
this.removeSelectionListener();
}
}
}
</script>

View File

@ -0,0 +1,233 @@
/*****************************************************************************
* 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.
*****************************************************************************/
<template>
<!-- Resize handles -->
<div class="c-frame-edit" :style="style">
<div class="c-frame-edit__handle c-frame-edit__handle--nw"
@mousedown="startResize([1,1], [-1,-1], $event)"></div>
<div class="c-frame-edit__handle c-frame-edit__handle--ne"
@mousedown="startResize([0,1], [1,-1], $event)"></div>
<div class="c-frame-edit__handle c-frame-edit__handle--sw"
@mousedown="startResize([1,0], [-1,1], $event)"></div>
<div class="c-frame-edit__handle c-frame-edit__handle--se"
@mousedown="startResize([0,0], [1,1], $event)"></div>
</div>
</template>
<style lang="scss">
@import "~styles/sass-base";
.c-frame-edit {
// In Layouts, this is the editing rect and handles
display: none; // Set to display: block in DisplayLayout.vue
pointer-events: none;
@include abs();
border: $editMarqueeBorder;
&__handle {
$d: 6px;
$o: floor($d * -0.5);
background: $editFrameColorHandleFg;
box-shadow: $editFrameColorHandleBg 0 0 0 2px;
pointer-events: all;
position: absolute;
width: $d; height: $d;
top: auto; right: auto; bottom: auto; left: auto;
&:before {
// Extended hit area
@include abs(-10px);
content: '';
display: block;
z-index: 0;
}
&:hover {
background: $editUIColor;
}
&--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;
}
}
}
</style>
<script>
import LayoutDrag from './../LayoutDrag'
export default {
inject: ['openmct'],
props: {
selectedLayoutItems: Array,
gridSize: Array
},
data() {
return {
dragPosition: undefined
}
},
computed: {
style() {
let x = Number.POSITIVE_INFINITY;
let y = Number.POSITIVE_INFINITY;
let width = Number.NEGATIVE_INFINITY;
let height = Number.NEGATIVE_INFINITY;
this.selectedLayoutItems.forEach(item => {
if (item.x2) {
let lineWidth = Math.abs(item.x - item.x2);
let lineMinX = Math.min(item.x, item.x2);
x = Math.min(lineMinX, x);
width = Math.max(lineWidth + lineMinX, width);
} else {
x = Math.min(item.x, x);
width = Math.max(item.width + item.x, width);
}
if (item.y2) {
let lineHeight = Math.abs(item.y - item.y2);
let lineMinY = Math.min(item.y, item.y2);
y = Math.min(lineMinY, y);
height = Math.max(lineHeight + lineMinY, height);
} else {
y = Math.min(item.y, y);
height = Math.max(item.height + item.y, height);
}
});
if (this.dragPosition) {
[x, y] = this.dragPosition.position;
[width, height] = this.dragPosition.dimensions;
} else {
width = width - x;
height = height - y;
}
this.marqueePosition = {
x: x,
y: y,
width: width,
height: height
}
return this.getMarqueeStyle(x, y, width, height);
}
},
methods: {
getMarqueeStyle(x, y, width, height) {
return {
left: (this.gridSize[0] * x) + 'px',
top: (this.gridSize[1] * y) + 'px',
width: (this.gridSize[0] * width) + 'px',
height: (this.gridSize[1] * height) + 'px'
};
},
updatePosition(event) {
let currentPosition = [event.pageX, event.pageY];
this.initialPosition = this.initialPosition || currentPosition;
this.delta = currentPosition.map(function (value, index) {
return value - this.initialPosition[index];
}.bind(this));
},
startResize(posFactor, dimFactor, event) {
document.body.addEventListener('mousemove', this.continueResize);
document.body.addEventListener('mouseup', this.endResize);
this.marqueeStartPosition = {
position: [this.marqueePosition.x, this.marqueePosition.y],
dimensions: [this.marqueePosition.width, this.marqueePosition.height]
};
this.updatePosition(event);
this.activeDrag = new LayoutDrag(this.marqueeStartPosition, posFactor, dimFactor, this.gridSize);
event.preventDefault();
},
continueResize(event) {
event.preventDefault();
this.updatePosition(event);
this.dragPosition = this.activeDrag.getAdjustedPositionAndDimensions(this.delta);
},
endResize(event) {
document.body.removeEventListener('mousemove', this.continueResize);
document.body.removeEventListener('mouseup', this.endResize);
this.continueResize(event);
let marqueeStartWidth = this.marqueeStartPosition.dimensions[0];
let marqueeStartHeight = this.marqueeStartPosition.dimensions[1];
let marqueeStartX = this.marqueeStartPosition.position[0];
let marqueeStartY = this.marqueeStartPosition.position[1];
let marqueeEndX = this.dragPosition.position[0];
let marqueeEndY = this.dragPosition.position[1];
let marqueeEndWidth = this.dragPosition.dimensions[0];
let marqueeEndHeight = this.dragPosition.dimensions[1];
let scaleWidth = marqueeEndWidth / marqueeStartWidth;
let scaleHeight = marqueeEndHeight / marqueeStartHeight;
let marqueeStart = {
x: marqueeStartX,
y: marqueeStartY,
height: marqueeStartWidth,
width: marqueeStartHeight
};
let marqueeEnd = {
x: marqueeEndX,
y: marqueeEndY,
width: marqueeEndWidth,
height: marqueeEndHeight
};
let marqueeOffset = {
x: marqueeEnd.x - marqueeStart.x,
y: marqueeEnd.y - marqueeStart.y
};
this.$emit('endResize', scaleWidth, scaleHeight, marqueeStart, marqueeOffset);
this.dragPosition = undefined;
this.initialPosition = undefined;
this.marqueeStartPosition = undefined;
this.delta = undefined;
event.preventDefault();
}
}
}
</script>

View File

@ -23,7 +23,8 @@
<template>
<layout-frame :item="item"
:grid-size="gridSize"
@endDrag="(item, updates) => $emit('endDrag', item, updates)">
@move="(gridDelta) => $emit('move', gridDelta)"
@endMove="() => $emit('endMove')">
<div class="c-image-view"
:style="style">
</div>
@ -56,8 +57,7 @@
y: 1,
width: 10,
height: 5,
url: element.url,
useGrid: true
url: element.url
};
},
inject: ['openmct'],

View File

@ -24,25 +24,14 @@
<div class="l-layout__frame c-frame"
:class="{
'no-frame': !item.hasFrame,
'u-inspectable': inspectable,
'is-resizing': isResizing
'u-inspectable': inspectable
}"
:style="style">
<slot></slot>
<!-- Drag handles -->
<div class="c-frame-edit">
<div class="c-frame-edit__move"
@mousedown="startDrag([1,1], [0,0], $event, 'move')"></div>
<div class="c-frame-edit__handle c-frame-edit__handle--nw"
@mousedown="startDrag([1,1], [-1,-1], $event, 'resize')"></div>
<div class="c-frame-edit__handle c-frame-edit__handle--ne"
@mousedown="startDrag([0,1], [1,-1], $event, 'resize')"></div>
<div class="c-frame-edit__handle c-frame-edit__handle--sw"
@mousedown="startDrag([1,0], [-1,1], $event, 'resize')"></div>
<div class="c-frame-edit__handle c-frame-edit__handle--se"
@mousedown="startDrag([0,0], [1,1], $event, 'resize')"></div>
<div class="c-frame-edit__move"
@mousedown="startMove([1,1], [0,0], $event)">
</div>
</div>
</template>
@ -50,7 +39,7 @@
<style lang="scss">
@import "~styles/sass-base";
/******************************* FRAME */
/******************* FRAME */
.c-frame {
display: flex;
flex-direction: column;
@ -61,114 +50,13 @@
}
}
.c-frame-edit {
// In Layouts, this is the editing rect and handles
// In Fixed Position, this is a wrapper element
@include abs();
.c-frame-edit__move {
display: none;
&__move {
@include abs();
cursor: move;
}
&__handle {
$d: 6px;
$o: floor($d * -0.5);
background: $editFrameColorHandleFg;
box-shadow: $editFrameColorHandleBg 0 0 0 2px;
display: none; // Set to block via s-selected selector
position: absolute;
width: $d; height: $d;
top: auto; right: auto; bottom: auto; left: auto;
&:before {
// Extended hit area
@include abs(-10px);
content: '';
display: block;
z-index: 0;
}
&:hover {
background: $editUIColor;
}
&--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;
}
}
}
.c-so-view.has-complex-content + .c-frame-edit {
// Target frames that hold domain objects that include header elements, as opposed to drawing and alpha objects
// Make the __move element a more affordable drag UI element
.c-frame-edit__move {
@include userSelectNone();
background: $editFrameMovebarColorBg;
box-shadow: rgba(black, 0.2) 0 1px;
bottom: auto;
height: 0; // Height is set on hover on s-selected.c-frame
opacity: 0.8;
max-height: 100%;
overflow: hidden;
text-align: center;
&:before {
// Grippy
$h: 4px;
$tbOffset: ($editFrameMovebarH - $h) / 2;
$lrOffset: 25%;
@include grippy($editFrameMovebarColorFg);
content: '';
display: block;
position: absolute;
top: $tbOffset; right: $lrOffset; bottom: $tbOffset; left: $lrOffset;
}
&:hover {
background: $editFrameHovMovebarColorBg;
&:before { @include grippy($editFrameHovMovebarColorFg); }
}
}
}
.is-editing {
/******************* STYLES FOR C-FRAME WHILE EDITING */
.c-frame {
&.no-frame {
border: $editFrameBorder; // Base border style for a frame element while editing.
}
&-edit {
display: contents;
}
&-edit__move,
.c-so-view {
transition: $transOut;
transition-delay: $moveBarOutDelay;
}
&:not([s-selected]) {
&:hover {
border: $editFrameBorderHov;
@ -180,49 +68,110 @@
border: $editFrameSelectedBorder;
box-shadow: $editFrameSelectedShdw;
> .c-frame-edit {
[class*='__handle'] {
.c-frame-edit__move {
cursor: move;
}
}
}
/******************* DEFAULT STYLES FOR -EDIT__MOVE */
// All object types
.c-frame-edit__move {
@include abs();
display: block;
}
// Has-complex-content objects
.c-so-view.has-complex-content {
transition: $transOut;
transition-delay: $moveBarOutDelay;
> .c-so-view__local-controls {
transition: transform 250ms ease-in-out;
transition-delay: $moveBarOutDelay;
}
+ .c-frame-edit__move {
display: none;
}
}
.l-layout {
/******************* 0 - 1 ITEM SELECTED */
&:not(.is-multi-selected) {
> .l-layout__frame[s-selected] {
> .c-so-view.has-complex-content {
> .c-so-view__local-controls {
transition: transform $transOutTime ease-in-out;
transition-delay: $moveBarOutDelay;
}
+ .c-frame-edit__move {
transition: $transOut;
transition-delay: $moveBarOutDelay;
@include userSelectNone();
background: $editFrameMovebarColorBg;
box-shadow: rgba(black, 0.2) 0 1px;
bottom: auto;
display: block;
height: 0; // Height is set on hover below
opacity: 0.8;
max-height: 100%;
overflow: hidden;
text-align: center;
&:before {
// Grippy
$h: 4px;
$tbOffset: ($editFrameMovebarH - $h) / 2;
$lrOffset: 25%;
@include grippy($editFrameMovebarColorFg);
content: '';
display: block;
position: absolute;
top: $tbOffset;
right: $lrOffset;
bottom: $tbOffset;
left: $lrOffset;
}
}
}
&:hover {
> .c-so-view.has-complex-content {
transition: $transIn;
transition-delay: 0s;
padding-top: $editFrameMovebarH + $interiorMarginSm;
> .c-so-view__local-controls {
transform: translateY($editFrameMovebarH);
transition: transform $transInTime ease-in-out;
transition-delay: 0s;
}
+ .c-frame-edit__move {
transition: $transIn;
transition-delay: 0s;
height: $editFrameMovebarH;
}
}
}
}
}
/******************* > 1 ITEMS SELECTED */
&.is-multi-selected {
.l-layout__frame[s-selected] {
> .c-so-view.has-complex-content + .c-frame-edit__move {
display: block;
}
}
}
}
.l-layout__frame[s-selected]:not(.is-resizing) {
// Show and animate the __move bar for sub-object views with complex content
> .c-so-view.has-complex-content {
> .c-so-view__local-controls {
transition: transform 250ms ease-in-out;
transition-delay: $moveBarOutDelay;
}
}
&:hover > .c-so-view.has-complex-content {
// Move content down so the __move bar doesn't cover it.
padding-top: $editFrameMovebarH;
transition: $transIn;
> .c-so-view__local-controls {
transition: transform 50ms ease-in-out;
transform: translateY($editFrameMovebarH);
}
&.c-so-view--no-frame {
// Move content down with a bit more space
padding-top: $editFrameMovebarH + $interiorMarginSm;
}
// Show the move bar
+ .c-frame-edit .c-frame-edit__move {
height: $editFrameMovebarH;
transition: $transIn;
}
}
}
}
</style>
<script>
import LayoutDrag from './../LayoutDrag'
@ -232,21 +181,9 @@
item: Object,
gridSize: Array
},
data() {
return {
dragPosition: undefined,
isResizing: undefined
}
},
computed: {
style() {
let {x, y, width, height} = this.item;
if (this.dragPosition) {
[x, y] = this.dragPosition.position;
[width, height] = this.dragPosition.dimensions;
}
return {
left: (this.gridSize[0] * x) + 'px',
top: (this.gridSize[1] * y) + 'px',
@ -268,36 +205,40 @@
return value - this.initialPosition[index];
}.bind(this));
},
startDrag(posFactor, dimFactor, event, type) {
document.body.addEventListener('mousemove', this.continueDrag);
document.body.addEventListener('mouseup', this.endDrag);
startMove(posFactor, dimFactor, event) {
document.body.addEventListener('mousemove', this.continueMove);
document.body.addEventListener('mouseup', this.endMove);
this.dragPosition = {
position: [this.item.x, this.item.y],
dimensions: [this.item.width, this.item.height]
position: [this.item.x, this.item.y]
};
this.updatePosition(event);
this.activeDrag = new LayoutDrag(this.dragPosition, posFactor, dimFactor, this.gridSize);
this.isResizing = type === 'resize';
event.preventDefault();
},
continueDrag(event) {
continueMove(event) {
event.preventDefault();
this.updatePosition(event);
this.dragPosition = this.activeDrag.getAdjustedPosition(this.delta);
let newPosition = this.activeDrag.getAdjustedPosition(this.delta);
if (!_.isEqual(newPosition, this.dragPosition)) {
this.dragPosition = newPosition;
this.$emit('move', this.toGridDelta(this.delta));
}
},
endDrag(event) {
document.body.removeEventListener('mousemove', this.continueDrag);
document.body.removeEventListener('mouseup', this.endDrag);
this.continueDrag(event);
let [x, y] = this.dragPosition.position;
let [width, height] = this.dragPosition.dimensions;
this.$emit('endDrag', this.item, {x, y, width, height});
endMove(event) {
document.body.removeEventListener('mousemove', this.continueMove);
document.body.removeEventListener('mouseup', this.endMove);
this.continueMove(event);
this.$emit('endMove');
this.dragPosition = undefined;
this.initialPosition = undefined;
this.delta = undefined;
this.isResizing = undefined;
event.preventDefault();
},
toGridDelta(pixelDelta) {
return pixelDelta.map((v, i) => {
return Math.round(v / this.gridSize[i]);
});
}
}
}

View File

@ -30,9 +30,9 @@
</line>
</svg>
<div class="c-frame-edit">
<div class="c-frame-edit__move"
@mousedown="startDrag($event)"></div>
<div class="c-frame-edit__move"
@mousedown="startDrag($event)"></div>
<div class="c-frame-edit" v-if="showFrameEdit">
<div class="c-frame-edit__handle"
:class="startHandleClass"
@mousedown="startDrag($event, 'start')"></div>
@ -66,8 +66,7 @@
y: 10,
x2: 10,
y2: 5,
stroke: '#717171',
useGrid: true
stroke: '#717171'
};
},
inject: ['openmct'],
@ -76,24 +75,31 @@
gridSize: Array,
initSelect: Boolean,
index: Number,
multiSelect: Boolean
},
data() {
return {
dragPosition: undefined
dragPosition: undefined,
dragging: undefined,
selection: []
};
},
computed: {
showFrameEdit() {
let layoutItem = this.selection.length > 0 && this.selection[0][0].context.layoutItem;
return !this.multiSelect && layoutItem && layoutItem.id === this.item.id;
},
position() {
let {x, y, x2, y2} = this.item;
if (this.dragPosition) {
if (this.dragging && this.dragPosition) {
({x, y, x2, y2} = this.dragPosition);
}
return {x, y, x2, y2};
},
style() {
let {x, y, x2, y2} = this.position;
let width = this.gridSize[0] * Math.abs(x - x2);
let height = this.gridSize[1] * Math.abs(y - y2);
let width = Math.max(this.gridSize[0] * Math.abs(x - x2), 1);
let height = Math.max(this.gridSize[1] * Math.abs(y - y2), 1);
let left = this.gridSize[0] * Math.min(x, x2);
let top = this.gridSize[1] * Math.min(y, y2);
return {
@ -175,13 +181,27 @@
event.preventDefault();
let pxDeltaX = this.startPosition[0] - event.pageX;
let pxDeltaY = this.startPosition[1] - event.pageY;
this.dragPosition = this.calculateDragPosition(pxDeltaX, pxDeltaY);
let newPosition = this.calculateDragPosition(pxDeltaX, pxDeltaY);
if (!this.dragging) {
if (!_.isEqual(newPosition, this.dragPosition)) {
let gridDelta = [event.pageX - this.startPosition[0], event.pageY - this.startPosition[1]];
this.dragPosition = newPosition;
this.$emit('move', this.toGridDelta(gridDelta));
}
} else {
this.dragPosition = newPosition;
}
},
endDrag(event) {
document.body.removeEventListener('mousemove', this.continueDrag);
document.body.removeEventListener('mouseup', this.endDrag);
let {x, y, x2, y2} = this.dragPosition;
this.$emit('endDrag', this.item, {x, y, x2, y2});
if (!this.dragging) {
this.$emit('endMove');
} else {
this.$emit('endLineResize', this.item, {x, y, x2, y2});
}
this.dragPosition = undefined;
this.dragging = undefined;
event.preventDefault();
@ -191,6 +211,7 @@
let gridDeltaY = Math.round(pxDeltaY / this.gridSize[0]); // TODO: should this be gridSize[1]?
let {x, y, x2, y2} = this.item;
let dragPosition = {x, y, x2, y2};
if (this.dragging === 'start') {
dragPosition.x -= gridDeltaX;
dragPosition.y -= gridDeltaY;
@ -205,6 +226,14 @@
dragPosition.y2 -= gridDeltaY;
}
return dragPosition;
},
setSelection(selection) {
this.selection = selection;
},
toGridDelta(pixelDelta) {
return pixelDelta.map((v, i) => {
return Math.round(v / this.gridSize[i]);
});
}
},
watch: {
@ -217,6 +246,7 @@
}
},
mounted() {
this.openmct.selection.on('change', this.setSelection);
this.context = {
layoutItem: this.item,
index: this.index
@ -228,6 +258,7 @@
if (this.removeSelectable) {
this.removeSelectable();
}
this.openmct.selection.off('change', this.setSelection);
}
}
</script>

View File

@ -22,7 +22,8 @@
<template>
<layout-frame :item="item"
:grid-size="gridSize"
@endDrag="(item, updates) => $emit('endDrag', item, updates)">
@move="(gridDelta) => $emit('move', gridDelta)"
@endMove="() => $emit('endMove')">
<object-frame v-if="domainObject"
:domain-object="domainObject"
:object-path="objectPath"
@ -66,8 +67,7 @@
x: position[0],
y: position[1],
identifier: domainObject.identifier,
hasFrame: hasFrameByDefault(domainObject.type),
useGrid: true
hasFrame: hasFrameByDefault(domainObject.type)
};
},
inject: ['openmct'],

View File

@ -23,7 +23,8 @@
<template>
<layout-frame :item="item"
:grid-size="gridSize"
@endDrag="(item, updates) => $emit('endDrag', item, updates)">
@move="(gridDelta) => $emit('move', gridDelta)"
@endMove="() => $emit('endMove')">
<div class="c-telemetry-view"
:style="styleObject"
v-if="domainObject">
@ -96,10 +97,9 @@
displayMode: 'all',
value: metadata.getDefaultDisplayValue(),
stroke: "transparent",
fill: "",
fill: "transparent",
color: "",
size: "13px",
useGrid: true
size: "13px"
};
},
inject: ['openmct'],

View File

@ -23,7 +23,8 @@
<template>
<layout-frame :item="item"
:grid-size="gridSize"
@endDrag="(item, updates) => $emit('endDrag', item, updates)">
@move="(gridDelta) => $emit('move', gridDelta)"
@endMove="() => $emit('endMove')">
<div class="c-text-view"
:style="style">
{{ item.text }}
@ -59,8 +60,7 @@
y: 1,
width: 10,
height: 5,
text: element.text,
useGrid: true
text: element.text
};
},
inject: ['openmct'],

View File

@ -60,6 +60,7 @@ export default function DisplayLayoutPlugin(options) {
getSelectionContext() {
return {
item: domainObject,
supportsMultiSelect: true,
addElement: component && component.$refs.displayLayout.addElement,
removeItem: component && component.$refs.displayLayout.removeItem,
orderItem: component && component.$refs.displayLayout.orderItem

View File

@ -33,16 +33,16 @@ define([
key: 'filters-inspector',
name: 'Filters Inspector View',
canView: function (selection) {
if (selection.length === 0) {
if (selection.length === 0 || selection[0].length === 0) {
return false;
}
let object = selection[0].context.item;
let object = selection[0][0].context.item;
return object && supportedObjectTypesArray.some(type => object.type === type);
},
view: function (selection) {
let component;
let providedObject = selection[0].context.item;
let providedObject = selection[0][0].context.item;
return {
show: function (element) {
@ -59,8 +59,10 @@ define([
});
},
destroy: function () {
component.$destroy();
component = undefined;
if (component) {
component.$destroy();
component = undefined;
}
}
}
},

View File

@ -414,6 +414,7 @@ import Container from '../utils/container';
import Frame from '../utils/frame';
import ResizeHandle from './resizeHandle.vue';
import DropHint from './dropHint.vue';
import RemoveAction from '../../remove/RemoveAction.js';
const MIN_CONTAINER_SIZE = 5;
@ -505,7 +506,7 @@ export default {
remove associated domainObjects from composition
*/
container.frames.forEach(f => {
this.composition.remove({identifier: f.domainObjectIdentifier});
this.removeFromComposition(f.domainObjectIdentifier);
});
this.containers.splice(containerIndex, 1);
@ -520,6 +521,7 @@ export default {
}
sizeToFill(this.containers);
this.setSelectionToParent();
this.persist();
},
moveFrame(toContainerIndex, toFrameIndex, frameId, fromContainerIndex) {
@ -553,20 +555,24 @@ export default {
deleteFrame(frameId) {
let container = this.containers
.filter(c => c.frames.some(f => f.id === frameId))[0];
let containerIndex = this.containers.indexOf(container);
let frame = container
.frames
.filter((f => f.id === frameId))[0];
let frameIndex = container.frames.indexOf(frame);
/*
remove associated domainObject from composition
*/
this.composition.remove({identifier: frame.domainObjectIdentifier});
container.frames.splice(frameIndex, 1);
sizeToFill(container.frames);
this.persist(containerIndex);
this.removeFromComposition(frame.domainObjectIdentifier)
.then(() => {
sizeToFill(container.frames)
this.setSelectionToParent();
});
},
removeFromComposition(identifier) {
return this.openmct.objects.get(identifier).then((childDomainObject) => {
this.RemoveAction.removeFromComposition(this.domainObject, childDomainObject);
});
},
setSelectionToParent() {
let currentSelection = this.openmct.selection.selected;
this.openmct.selection.select(currentSelection.slice(1));
},
allowContainerDrop(event, index) {
if (!event.dataTransfer.types.includes('containerid')) {
@ -657,6 +663,8 @@ export default {
this.composition.on('remove', this.removeChildObject);
this.composition.on('add', this.addFrame);
this.RemoveAction = new RemoveAction(this.openmct);
this.unobserve = this.openmct.objects.observe(this.domainObject, '*', this.updateDomainObject);
},
beforeDestroy() {

View File

@ -79,10 +79,12 @@ export default {
mounted() {
document.addEventListener('dragstart', this.setDragging);
document.addEventListener('dragend', this.unsetDragging);
document.addEventListener('drop', this.unsetDragging);
},
destroyed() {
document.removeEventListener('dragstart', this.setDragging);
document.removeEventListener('dragend', this.unsetDragging);
document.removeEventListener('drop', this.unsetDragging);
}
}
</script>

View File

@ -27,16 +27,17 @@ function ToolbarProvider(openmct) {
key: "flex-layout",
description: "A toolbar for objects inside a Flexible Layout.",
forSelection: function (selection) {
let context = selection[0].context;
let context = selection[0][0].context;
return (context && context.type &&
(context.type === 'flexible-layout' || context.type === 'container' || context.type === 'frame'));
},
toolbar: function (selection) {
let primary = selection[0],
secondary = selection[1],
tertiary = selection[2],
let selectionPath = selection[0],
primary = selectionPath[0],
secondary = selectionPath[1],
tertiary = selectionPath[2],
deleteFrame,
toggleContainer,
deleteContainer,
@ -46,7 +47,7 @@ function ToolbarProvider(openmct) {
separator = {
control: "separator",
domainObject: selection[0].context.item,
domainObject: primary.context.item,
key: "separator"
};
@ -77,11 +78,11 @@ function ToolbarProvider(openmct) {
.containers;
let container = containers
.filter(c => c.frames.some(f => f.id === frameId))[0];
let frame = container
let containerIndex = containers.indexOf(container);
let frame = container && container
.frames
.filter((f => f.id === frameId))[0];
let containerIndex = containers.indexOf(container);
let frameIndex = container.frames.indexOf(frame);
let frameIndex = container && container.frames.indexOf(frame);
deleteFrame = {
control: "button",

View File

@ -14,6 +14,7 @@ export default {
},
mounted() {
this.composition = this.openmct.composition.get(this.domainObject);
this.keystring = this.openmct.objects.makeKeyString(this.domainObject.identifier);
if (!this.composition) {
return;
}
@ -34,7 +35,7 @@ export default {
this.items.push({
model: child,
type: type.definition,
isAlias: this.domainObject.identifier.key !== child.location,
isAlias: this.keystring !== child.location,
objectPath: [child].concat(this.openmct.router.path),
objectKeyString: this.openmct.objects.makeKeyString(child.identifier)
});

View File

@ -71,7 +71,6 @@ define([
height: panel.dimensions[1],
x: panel.position[0],
y: panel.position[1],
useGrid: true,
identifier: domainObject.identifier,
id: uuid(),
type: 'telemetry-view',
@ -88,7 +87,6 @@ define([
height: panel.dimensions[1],
x: panel.position[0],
y: panel.position[1],
useGrid: true,
identifier: domainObject.identifier,
id: uuid(),
type: 'subobject-view',
@ -104,7 +102,7 @@ define([
return migratedObject;
}
function migrateFixedPositionConfiguration(elements, telemetryObjects) {
function migrateFixedPositionConfiguration(elements, telemetryObjects, gridSize) {
const DEFAULT_STROKE = "transparent";
const DEFAULT_SIZE = "13px";
const DEFAULT_COLOR = "";
@ -117,10 +115,16 @@ define([
y: element.y,
width: element.width,
height: element.height,
useGrid: element.useGrid,
id: uuid()
};
if (!element.useGrid) {
item.x = Math.round(item.x / gridSize[0]);
item.y = Math.round(item.y / gridSize[1]);
item.width = Math.round(item.width / gridSize[0]);
item.height = Math.round(item.height / gridSize[1]);
}
if (element.type === "fixed.telemetry") {
item.type = "telemetry-view";
item.stroke = element.stroke || DEFAULT_STROKE;
@ -192,10 +196,11 @@ define([
name: domainObject.name,
type: "layout"
};
let gridSize = domainObject.layoutGrid || DEFAULT_GRID_SIZE;
let layoutType = openmct.types.get('layout');
layoutType.definition.initialize(newLayoutObject);
newLayoutObject.composition = domainObject.composition;
newLayoutObject.configuration.layoutGrid = domainObject.layoutGrid || DEFAULT_GRID_SIZE;
newLayoutObject.configuration.layoutGrid = gridSize;
let elements = domainObject.configuration['fixed-display'].elements;
let telemetryObjects = {};
@ -211,7 +216,7 @@ define([
return Promise.all(promises)
.then(function () {
newLayoutObject.configuration.items =
migrateFixedPositionConfiguration(elements, telemetryObjects);
migrateFixedPositionConfiguration(elements, telemetryObjects, gridSize);
return newLayoutObject;
});
}

View File

@ -70,12 +70,15 @@ define([
this.listenTo(this.$scope, '$destroy', this.destroy, this);
this.listenTo(config.series, 'add', this.addSeries, this);
this.listenTo(config.series, 'remove', this.resetAllSeries, this);
config.series.forEach(this.addSeries, this);
};
PlotOptionsController.prototype.addSeries = function (series, index) {
this.$scope.plotSeries[index] = series;
series.locateOldObject(this.$scope.domainObject);
this.$timeout(function () {
this.$scope.plotSeries[index] = series;
series.locateOldObject(this.$scope.domainObject);
}.bind(this));
};
PlotOptionsController.prototype.resetAllSeries = function (series, index) {

View File

@ -37,15 +37,15 @@ define([
key: 'table-configuration',
name: 'Telemetry Table Configuration',
canView: function (selection) {
if (selection.length === 0) {
if (selection.length === 0 || selection[0].length === 0) {
return false;
}
let object = selection[0].context.item;
let object = selection[0][0].context.item;
return object && object.type === 'table';
},
view: function (selection) {
let component;
let domainObject = selection[0].context.item;
let domainObject = selection[0][0].context.item;
let tableConfiguration = new TelemetryTableConfiguration(domainObject, openmct);
return {
show: function (element) {
@ -62,8 +62,11 @@ define([
});
},
destroy: function () {
component.$destroy();
component = undefined;
if (component) {
component.$destroy();
component = undefined;
}
tableConfiguration = undefined;
}
}

View File

@ -143,6 +143,7 @@ define([
let telemetryRows = telemetryData.map(datum => new TelemetryTableRow(datum, columnMap, keyString, limitEvaluator));
this.boundedRows.add(telemetryRows);
}).finally(() => {
this.decrementOutstandingRequests();
});
}

View File

@ -588,13 +588,18 @@ export default {
let el = this.$el;
let width = el.clientWidth;
let height = el.clientHeight;
let scrollTop = this.scrollable.scrollTop;
this.resizePollHandle = setInterval(() => {
if ((el.clientWidth !== width || el.clientHeight !== height) && this.isAutosizeEnabled) {
this.calculateTableSize();
// On some resize events scrollTop is reset to 0. Possibly due to a transition we're using?
// Need to preserve scroll position in this case.
this.scrollable.scrollTop = scrollTop;
width = el.clientWidth;
height = el.clientHeight;
}
scrollTop = this.scrollable.scrollTop;
}, RESIZE_POLL_INTERVAL);
},

View File

@ -20,7 +20,15 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(['EventEmitter'], function (EventEmitter) {
define(
[
'EventEmitter',
'lodash'
],
function (
EventEmitter,
_
) {
/**
* Manages selection state for Open MCT
@ -47,21 +55,87 @@ define(['EventEmitter'], function (EventEmitter) {
* Selects the selectable object and emits the 'change' event.
*
* @param {object} selectable an object with element and context properties
* @param {Boolean} isMultiSelectEvent flag indication shift key is pressed or not
* @private
*/
Selection.prototype.select = function (selectable) {
Selection.prototype.select = function (selectable, isMultiSelectEvent) {
if (!Array.isArray(selectable)) {
selectable = [selectable];
}
if (this.selected[0] && this.selected[0].element) {
this.selected[0].element.removeAttribute('s-selected');
let multiSelect = isMultiSelectEvent &&
this.parentSupportsMultiSelect(selectable) &&
this.isPeer(selectable) &&
!this.selectionContainsParent(selectable);
if (multiSelect) {
this.handleMultiSelect(selectable);
} else {
this.setSelectionStyles(selectable);
this.selected = [selectable];
}
if (this.selected[1] && this.selected[1].element) {
this.selected[1].element.removeAttribute('s-selected-parent');
this.emit('change', this.selected);
};
/**
* @private
*/
Selection.prototype.handleMultiSelect = function (selectable) {
if (this.elementSelected(selectable)) {
this.remove(selectable);
} else {
this.addSelectionAttributes(selectable);
this.selected.push(selectable);
}
};
/**
* @private
*/
Selection.prototype.elementSelected = function (selectable) {
return this.selected.some(selectionPath => _.isEqual(selectionPath, selectable));
};
/**
* @private
*/
Selection.prototype.remove = function (selectable) {
this.selected = this.selected.filter(selectionPath => !_.isEqual(selectionPath, selectable));
if (this.selected.length === 0) {
this.removeSelectionAttributes(selectable);
selectable[1].element.click(); // Select the parent if there is no selection.
} else {
this.removeSelectionAttributes(selectable, true);
}
};
/**
* @private
*/
Selection.prototype.setSelectionStyles = function (selectable) {
this.selected.map(selectionPath => {
this.removeSelectionAttributes(selectionPath);
});
this.addSelectionAttributes(selectable);
};
Selection.prototype.removeSelectionAttributes = function (selectionPath, keepParentStyle) {
if (selectionPath[0] && selectionPath[0].element) {
selectionPath[0].element.removeAttribute('s-selected');
}
if (selectionPath[1] && selectionPath[1].element && !keepParentStyle) {
selectionPath[1].element.removeAttribute('s-selected-parent');
}
};
/*
* Adds selection attributes to the selected element and its parent.
* @private
*/
Selection.prototype.addSelectionAttributes = function (selectable) {
if (selectable[0] && selectable[0].element) {
selectable[0].element.setAttribute('s-selected', "");
}
@ -69,16 +143,36 @@ define(['EventEmitter'], function (EventEmitter) {
if (selectable[1] && selectable[1].element) {
selectable[1].element.setAttribute('s-selected-parent', "");
}
};
this.selected = selectable;
this.emit('change', this.selected);
/**
* @private
*/
Selection.prototype.parentSupportsMultiSelect = function (selectable) {
return selectable[1] && selectable[1].context.supportsMultiSelect;
};
/**
* @private
*/
Selection.prototype.selectionContainsParent = function (selectable) {
return this.selected.some(selectionPath => _.isEqual(selectionPath[0], selectable[1]));
};
/**
* @private
*/
Selection.prototype.isPeer = function (selectable) {
return this.selected.some(selectionPath => _.isEqual(selectionPath[1], selectable[1]));
};
/**
* @private
*/
Selection.prototype.capture = function (selectable) {
if (!this.capturing) {
let capturingContainsSelectable = this.capturing && this.capturing.includes(selectable);
if (!this.capturing || capturingContainsSelectable) {
this.capturing = [];
}
@ -88,13 +182,14 @@ define(['EventEmitter'], function (EventEmitter) {
/**
* @private
*/
Selection.prototype.selectCapture = function (selectable) {
Selection.prototype.selectCapture = function (selectable, event) {
if (!this.capturing) {
return;
}
this.select(this.capturing.reverse());
let reversedCapturing = this.capturing.reverse();
delete this.capturing;
this.select(reversedCapturing, event.shiftKey);
};
/**
@ -112,7 +207,7 @@ define(['EventEmitter'], function (EventEmitter) {
* @public
*/
Selection.prototype.selectable = function (element, context, select) {
var selectable = {
let selectable = {
context: context,
element: element
};

View File

@ -161,8 +161,8 @@ $editFrameBorderHov: 1px solid $editFrameColorHov; // Hover on selectable frames
$editFrameColorSelected: #ccc; // Border of selected frames
$editFrameColorHandleBg: $colorBodyBg; // Resize handle 'offset' color to make handle standout
$editFrameColorHandleFg: $editFrameColorSelected; // Resize handle main color
$editFrameSelectedShdw: rgba(black, 0.5) 0 1px 10px 1px;
$editFrameSelectedBorder: 1px dashed $editFrameColorSelected; // Selected frame element
$editFrameSelectedShdw: rgba(black, 0.4) 0 1px 5px 1px;
$editFrameSelectedBorder: 1px solid $editFrameColorHov; // Selected frame element
$editFrameMovebarColorBg: $editFrameColor; // Movebar bg color
$editFrameMovebarColorFg: pullForward($editFrameMovebarColorBg, 20%); // Grippy lines, container size text
$editFrameHovMovebarColorBg: pullForward($editFrameMovebarColorBg, 10%); // Hover style
@ -170,6 +170,7 @@ $editFrameHovMovebarColorFg: pullForward($editFrameMovebarColorFg, 10%);
$editFrameSelectedMovebarColorBg: pullForward($editFrameMovebarColorBg, 15%); // Selected style
$editFrameSelectedMovebarColorFg: pullForward($editFrameMovebarColorFg, 15%);
$editFrameMovebarH: 10px; // Height of move bar in layout frame
$editMarqueeBorder: 1px dashed $editFrameColorSelected;
// Icons
$colorIconAlias: #4af6f3;
@ -226,6 +227,8 @@ $menuItemPad: $interiorMargin, floor($interiorMargin * 1.25);
$paletteItemBorderOuterColorSelected: black;
$paletteItemBorderInnerColorSelected: white;
$paletteItemBorderInnerColor: rgba($paletteItemBorderOuterColorSelected, 0.3);
$mixedSettingBg: (transparent rgba($editUIBaseColorHov, 0.7)); // Used in .c-click-icon--mixed
$mixedSettingBgSize: 5px;
// Forms
$colorCheck: $colorKey;
@ -387,8 +390,10 @@ $colorLoadingFg: #776ba2;
$colorLoadingBg: rgba($colorLoadingFg, 0.1);
// Transitions
$transIn: all 50ms ease-in-out;
$transOut: all 250ms ease-in-out;
$transInTime: 50ms;
$transOutTime: 250ms;
$transIn: all $transInTime ease-in-out;
$transOut: all $transOutTime ease-in-out;
$transInBounce: all 200ms cubic-bezier(.47,.01,.25,1.5);
$transInBounceBig: all 300ms cubic-bezier(.2,1.6,.6,3);

View File

@ -165,8 +165,8 @@ $editFrameBorderHov: 1px solid $editFrameColorHov; // Hover on selectable frames
$editFrameColorSelected: #ccc; // Border of selected frames
$editFrameColorHandleBg: $colorBodyBg; // Resize handle 'offset' color to make handle standout
$editFrameColorHandleFg: $editFrameColorSelected; // Resize handle main color
$editFrameSelectedShdw: rgba(black, 0.5) 0 1px 10px 1px;
$editFrameSelectedBorder: 1px dashed $editFrameColorSelected; // Selected frame element
$editFrameSelectedShdw: rgba(black, 0.4) 0 1px 5px 1px;
$editFrameSelectedBorder: 1px solid $editFrameColorHov; // Selected frame element
$editFrameMovebarColorBg: $editFrameColor; // Movebar bg color
$editFrameMovebarColorFg: pullForward($editFrameMovebarColorBg, 20%); // Grippy lines, container size text
$editFrameHovMovebarColorBg: pullForward($editFrameMovebarColorBg, 10%); // Hover style
@ -174,6 +174,7 @@ $editFrameHovMovebarColorFg: pullForward($editFrameMovebarColorFg, 10%);
$editFrameSelectedMovebarColorBg: pullForward($editFrameMovebarColorBg, 15%); // Selected style
$editFrameSelectedMovebarColorFg: pullForward($editFrameMovebarColorFg, 15%);
$editFrameMovebarH: 10px; // Height of move bar in layout frame
$editMarqueeBorder: 1px dashed $editFrameColorSelected;
// Icons
$colorIconAlias: #4af6f3;
@ -230,6 +231,8 @@ $menuItemPad: $interiorMargin, floor($interiorMargin * 1.25);
$paletteItemBorderOuterColorSelected: black;
$paletteItemBorderInnerColorSelected: white;
$paletteItemBorderInnerColor: rgba($paletteItemBorderOuterColorSelected, 0.3);
$mixedSettingBg: (transparent rgba($editUIBaseColorHov, 0.7)); // Used in .c-click-icon--mixed
$mixedSettingBgSize: 5px;
// Forms
$colorCheck: $colorKey;
@ -391,8 +394,10 @@ $colorLoadingFg: #776ba2;
$colorLoadingBg: rgba($colorLoadingFg, 0.1);
// Transitions
$transIn: all 50ms ease-in-out;
$transOut: all 250ms ease-in-out;
$transInTime: 50ms;
$transOutTime: 250ms;
$transIn: all $transInTime ease-in-out;
$transOut: all $transOutTime ease-in-out;
$transInBounce: all 200ms cubic-bezier(.47,.01,.25,1.5);
$transInBounceBig: all 300ms cubic-bezier(.2,1.6,.6,3);

View File

@ -170,6 +170,7 @@ $editFrameHovMovebarColorFg: pullForward($editFrameMovebarColorFg, 10%);
$editFrameSelectedMovebarColorBg: pullForward($editFrameMovebarColorBg, 15%); // Selected style
$editFrameSelectedMovebarColorFg: pullForward($editFrameMovebarColorFg, 15%);
$editFrameMovebarH: 10px; // Height of move bar in layout frame
$editMarqueeBorder: 1px dashed $editFrameColorSelected;
// Icons
$colorIconAlias: #4af6f3;
@ -226,6 +227,8 @@ $menuItemPad: $interiorMargin, floor($interiorMargin * 1.25);
$paletteItemBorderOuterColorSelected: black;
$paletteItemBorderInnerColorSelected: white;
$paletteItemBorderInnerColor: rgba($paletteItemBorderOuterColorSelected, 0.3);
$mixedSettingBg: (transparent rgba($editUIBaseColorHov, 0.9)); // Used in .c-click-icon--mixed
$mixedSettingBgSize: 10px;
// Forms
$colorCheck: $colorKey;
@ -387,8 +390,10 @@ $colorLoadingFg: #776ba2;
$colorLoadingBg: rgba($colorLoadingFg, 0.1);
// Transitions
$transIn: all 50ms ease-in-out;
$transOut: all 250ms ease-in-out;
$transInTime: 50ms;
$transOutTime: 250ms;
$transIn: all $transInTime ease-in-out;
$transOut: all $transOutTime ease-in-out;
$transInBounce: all 200ms cubic-bezier(.47,.01,.25,1.5);
$transInBounceBig: all 300ms cubic-bezier(.2,1.6,.6,3);

View File

@ -85,6 +85,15 @@ button {
margin-left: $interiorMargin;
}
$c1: nth($mixedSettingBg, 1);
$c2: nth($mixedSettingBg, 2);
$mixedBgD: $mixedSettingBgSize $mixedSettingBgSize;
&--mixed {
// E.g. click-icons in toolbar that apply to multiple selected items with different settings
@include bgStripes2Color($c1, $c2, $bgSize: $mixedBgD);
}
&--swatched {
// Color control, show swatch element
display: flex;
@ -93,9 +102,9 @@ button {
justify-content: center;
> [class*='swatch'] {
box-shadow: inset rgba(black, 0.2) 0 0 1px;
box-shadow: inset rgba($editUIBaseColorFg, 0.2) 0 0 0 1px;
flex: 0 0 auto;
height: 4px;
height: 5px;
width: 100%;
margin-top: 1px;
}
@ -105,6 +114,13 @@ button {
flex: 1 1 auto;
font-size: 1.1em;
}
&--mixed {
// Styling for swatched buttons when settings are mixed
> [class*='swatch'] {
@include bgStripes2Color($c1, $c2, $bgSize: $mixedBgD);
}
}
}
}
@ -148,8 +164,6 @@ button {
display: block;
font-family: symbolsfont;
font-size: 1rem * $s;
transform-origin: center;
transition: $transOut;
}
}
@ -571,6 +585,10 @@ select {
margin-left: 2px;
}
&__button {
}
&__separator {
@include cToolbarSeparator();
}

View File

@ -108,7 +108,7 @@
}
@mixin bgStripes($c: yellow, $a: 0.1, $bgsize: 5px, $angle: 90deg) {
background-image: linear-gradient($angle,
background: linear-gradient($angle,
rgba($c, $a) 25%, transparent 25%,
transparent 50%, rgba($c, $a) 50%,
rgba($c, $a) 75%, transparent 75%,
@ -117,6 +117,16 @@
background-size: $bgsize $bgsize;
}
@mixin bgStripes2Color($c1, $c2, $bgSize, $angle: -45deg) {
background: linear-gradient(-45deg,
$c1 0%, $c1 25%,
$c2 25%, $c2 50%,
$c1 50%, $c1 75%,
$c2 75%, $c2 100%
) repeat;
background-size: $bgSize;
}
@mixin disabled() {
opacity: $controlDisabledOpacity;
pointer-events: none !important;
@ -564,7 +574,6 @@
@mixin test($c: deeppink, $a: 0.3) {
background: rgba($c, $a) !important;
background-color: rgba($c, $a) !important;
box-shadow: deeppink 0 0 10px 1px !important;
}

View File

@ -20,19 +20,38 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*************************************************** MIXINS */
@mixin statusStyle($bg, $fg, $ic) {
background: $bg !important;
background-color: $bg !important;
color: $fg !important;
@mixin statusStyle($bg, $fg, $imp: false) {
$impStr: null;
@if $imp {
$impStr: !important;
}
background: $bg $impStr;
background-color: $bg $impStr;
color: $fg $impStr;
}
@mixin statusIcon($ic, $glyph: null, $imp: false) {
$impStr: null;
@if $imp {
$impStr: !important;
}
&:before {
color: $ic;
display: inline-block;
font-family: symbolsfont;
font-size: 0.7em;
font-size: 0.8em;
margin-right: $interiorMargin;
@if $glyph != null {
content: $glyph $impStr;
}
}
}
@mixin statusStyleCombined($bg, $fg, $ic) {
@include statusStyle($bg, $fg, $imp: true);
@include statusIcon($ic);
}
@mixin elementStatusColors($c) {
// Sets bg and icon colors for elements
background: rgba($c, 0.5) !important;
@ -47,16 +66,45 @@
}
}
.is-limit--yellow {
@include statusStyle($colorLimitYellowBg, $colorLimitYellowFg, $colorLimitYellowIc);
&.is-limit--upr:before { content: $glyph-icon-arrow-up; }
&.is-limit--lwr:before { content: $glyph-icon-arrow-down; }
@mixin andUprLwr {
&.is-limit--upr:before { content: $glyph-icon-arrow-up !important; }
&.is-limit--lwr:before { content: $glyph-icon-arrow-down !important; }
}
.is-limit--red {
@include statusStyle($colorLimitRedBg, $colorLimitRedFg, $colorLimitRedIc);
&.is-limit--upr:before { content: $glyph-icon-arrow-double-up; }
&.is-limit--lwr:before { content: $glyph-icon-arrow-double-down; }
/*************************************************** STYLES */
*:not(tr) {
&.is-limit--yellow {
@include statusStyle($colorLimitYellowBg, $colorLimitYellowFg, true);
@include statusIcon($colorLimitYellowIc, $glyph-icon-alert-rect);
@include andUprLwr();
}
&.is-limit--red {
@include statusStyle($colorLimitRedBg, $colorLimitRedFg, true);
@include statusIcon($colorLimitRedIc, $glyph-icon-alert-triangle);
@include andUprLwr();
}
}
tr {
&.is-limit--yellow {
@include statusStyle($colorLimitYellowBg, $colorLimitYellowFg);
td:first-child {
@include statusIcon($colorLimitYellowIc, $glyph-icon-alert-rect);
}
td { color: $colorLimitYellowFg; }
}
&.is-limit--red {
@include statusStyle($colorLimitRedBg, $colorLimitRedFg);
td:first-child {
@include statusIcon($colorLimitRedIc, $glyph-icon-alert-triangle);
}
td { color: $colorLimitRedFg; }
}
&.is-limit--upr { td:first-child:before { content: $glyph-icon-arrow-up !important; } }
&.is-limit--lwr { td:first-child:before { content: $glyph-icon-arrow-down !important; } }
}
/*************************************************** STATUS */

View File

@ -103,7 +103,7 @@
&__local-controls {
position: absolute;
top: 0; right: 0;
top: $interiorMargin; right: $interiorMargin;
z-index: 2;
}
@ -112,7 +112,7 @@
}
&.has-complex-content {
.c-so-view__view-large { display: block; }
> .c-so-view__view-large { display: block; }
}
/*************************** OBJECT VIEW */

View File

@ -59,7 +59,8 @@ export default {
default() {
return [];
}
}
},
navigateToPath: String
},
data() {
return {

View File

@ -102,7 +102,8 @@ export default {
this.elements = [];
this.elementsCache = {};
this.listeners = [];
this.parentObject = selection[0].context.item;
this.parentObject = selection && selection[0] && selection[0][0].context.item;
if (this.mutationUnobserver) {
this.mutationUnobserver();
}

View File

@ -199,8 +199,8 @@
},
methods: {
refreshComposition(selection) {
if (selection[0]) {
let parentObject = selection[0].context.item;
if (selection.length > 0 && selection[0].length > 0) {
let parentObject = selection[0][0].context.item;
this.hasComposition = !!(parentObject && this.openmct.composition.get(parentObject));
}

View File

@ -7,30 +7,44 @@
</style>
<script>
import _ from 'lodash';
export default {
inject: ['openmct'],
mounted() {
this.openmct.selection.on('change', this.updateSelection);
this.updateSelection();
this.updateSelection(this.openmct.selection.get());
},
destroyed() {
this.openmct.selection.off('change', this.updateSelection);
},
data() {
return {
selection: []
}
},
methods: {
updateSelection() {
let selection = this.openmct.selection.get();
updateSelection(selection) {
if (_.isEqual(this.selection, selection)) {
return;
}
this.selection = selection;
if (this.selectedViews) {
this.selectedViews.forEach(selectedView => {
selectedView.destroy();
});
this.$el.innerHTML = '';
}
this.viewContainers = [];
if (selection.length > 1) {
return;
}
this.selectedViews = this.openmct.inspectorViews.get(selection);
this.selectedViews.forEach(selectedView => {
let viewContainer = document.createElement('div');
this.viewContainers.push(viewContainer);
this.$el.append(viewContainer)
selectedView.show(viewContainer);
});

View File

@ -1,7 +1,7 @@
<template>
<div class="c-properties c-properties--location">
<div class="c-properties__header" title="The location of this linked object.">Original Location</div>
<ul class="c-properties__section">
<ul class="c-properties__section" v-if="!multiSelect">
<li class="c-properties__row" v-if="originalPath.length">
<ul class="c-properties__value c-location">
<li v-for="pathObject in orderedOriginalPath"
@ -15,6 +15,7 @@
</ul>
</li>
</ul>
<div class="c-properties__row--span-all" v-if="multiSelect">No location to display for multiple items</div>
</div>
</template>
@ -72,6 +73,7 @@ export default {
data() {
return {
domainObject: {},
multiSelect: false,
originalPath: [],
keyString: ''
}
@ -106,16 +108,27 @@ export default {
this.keyString = '';
},
updateSelection(selection) {
if (!selection.length) {
if (!selection.length || !selection[0].length) {
this.clearData();
return;
} else if (!selection[0].context.item && selection[1] && selection[1].context.item) {
this.setOriginalPath([selection[1].context.item], true);
}
if (selection.length > 1) {
this.multiSelect = true;
return;
} else {
this.multiSelect = false;
}
this.domainObject = selection[0][0].context.item;
let parentObject = selection[0][1];
if (!this.domainObject && parentObject && parentObject.context.item) {
this.setOriginalPath([parentObject.context.item], true);
this.keyString = '';
return;
}
this.domainObject = selection[0].context.item;
let keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
if (keyString && this.keyString !== keyString) {

View File

@ -1,7 +1,7 @@
<template>
<div class="c-properties c-properties--properties">
<div class="c-properties__header">Properties</div>
<ul class="c-properties__section">
<ul class="c-properties__section" v-if="!multiSelect">
<li class="c-properties__row">
<div class="c-properties__label">Title</div>
<div class="c-properties__value">{{ item.name }}</div>
@ -25,6 +25,7 @@
<div class="c-properties__value">{{ prop.value }}</div>
</li>
</ul>
<div class="c-properties__row--span-all" v-if="multiSelect">No properties to display for multiple items</div>
</div>
</template>
@ -35,7 +36,8 @@ export default {
inject: ['openmct'],
data() {
return {
domainObject: {}
domainObject: {},
multiSelect: false
}
},
computed: {
@ -90,11 +92,19 @@ export default {
},
methods: {
updateSelection(selection) {
if (selection.length === 0) {
if (selection.length === 0 || selection[0].length === 0) {
this.domainObject = {};
return;
}
this.domainObject = selection[0].context.item;
if (selection.length > 1) {
this.multiSelect = true;
this.domainObject = {};
return;
} else {
this.multiSelect = false;
this.domainObject = selection[0][0].context.item;
}
},
formatTime(unixTime) {
return Moment.utc(unixTime).format('YYYY-MM-DD[\n]HH:mm:ss') + ' UTC';

View File

@ -43,7 +43,8 @@
</div>
<!-- Action buttons -->
<div class="l-browse-bar__actions">
<button class="l-browse-bar__actions__notebook-entry c-button icon-notebook"
<button v-if="notebookEnabled"
class="l-browse-bar__actions__notebook-entry c-button icon-notebook"
title="New Notebook entry"
@click="snapshot()">
</button>
@ -118,7 +119,10 @@ const PLACEHOLDER_OBJECT = {};
label: 'Ok',
emphasis: true,
callback: () => {
this.openmct.editor.cancel();
this.openmct.editor.cancel().then(() => {
//refresh object view
this.openmct.layout.$refs.browseObject.show(this.domainObject, this.viewKey, false);
});
dialog.dismiss();
}
},
@ -167,7 +171,8 @@ const PLACEHOLDER_OBJECT = {};
showSaveMenu: false,
domainObject: PLACEHOLDER_OBJECT,
viewKey: undefined,
isEditing: this.openmct.editor.isEditing()
isEditing: this.openmct.editor.isEditing(),
notebookEnabled: false
}
},
computed: {
@ -213,7 +218,11 @@ const PLACEHOLDER_OBJECT = {};
}
},
mounted: function () {
this.notebookSnapshot = new NotebookSnapshot(this.openmct);
if (this.openmct.types.get('notebook')) {
this.notebookSnapshot = new NotebookSnapshot(this.openmct);
this.notebookEnabled = true;
}
document.addEventListener('click', this.closeViewAndSaveMenu);
window.addEventListener('beforeunload', this.promptUserbeforeNavigatingAway);
@ -222,7 +231,20 @@ const PLACEHOLDER_OBJECT = {};
this.isEditing = isEditing;
});
},
watch: {
domainObject() {
if (this.mutationObserver) {
this.mutationObserver();
}
this.mutationObserver = this.openmct.objects.observe(this.domainObject, '*', (domainObject) => {
this.domainObject = domainObject;
});
}
},
beforeDestroy: function () {
if (this.mutationObserver) {
this.mutationObserver();
}
document.removeEventListener('click', this.closeViewAndSaveMenu);
window.removeEventListener('click', this.promptUserbeforeNavigatingAway);
}

View File

@ -348,7 +348,7 @@
toggleHasToolbar(selection) {
let structure = undefined;
if (!selection[0]) {
if (!selection || !selection[0]) {
structure = [];
} else {
structure = this.openmct.toolbars.get(selection);

View File

@ -211,7 +211,8 @@
return {
id: this.openmct.objects.makeKeyString(c.identifier),
object: c,
objectPath: [c]
objectPath: [c],
navigateToParent: '/browse'
};
});
});

View File

@ -7,7 +7,8 @@
v-model="expanded">
</view-control>
<object-label :domainObject="node.object"
:objectPath="node.objectPath">
:objectPath="node.objectPath"
:navigateToPath="navigateToPath">
</object-label>
</div>
<ul v-if="expanded" class="c-tree">
@ -36,13 +37,12 @@
node: Object
},
data() {
this.pathToObject = this.buildPathString(this.node.objectPath);
this.navigateToPath = this.buildPathString(this.node.navigateToParent)
return {
hasChildren: false,
isLoading: false,
loaded: false,
isNavigated: this.pathToObject === this.openmct.router.currentLocation.path,
isNavigated: this.navigateToPath === this.openmct.router.currentLocation.path,
children: [],
expanded: false
}
@ -103,7 +103,8 @@
this.children.push({
id: this.openmct.objects.makeKeyString(child.identifier),
object: child,
objectPath: [child].concat(this.node.objectPath)
objectPath: [child].concat(this.node.objectPath),
navigateToParent: this.navigateToPath
});
},
removeChild(identifier) {
@ -115,15 +116,13 @@
this.isLoading = false;
this.loaded = true;
},
buildPathString(path) {
return '/browse/' + path.map(o => o && this.openmct.objects.makeKeyString(o.identifier))
.reverse()
.join('/');
buildPathString(parentPath) {
return [parentPath, this.openmct.objects.makeKeyString(this.node.object.identifier)].join('/');
},
highlightIfNavigated(newPath, oldPath){
if (newPath === this.pathToObject) {
if (newPath === this.navigateToPath) {
this.isNavigated = true;
} else if (oldPath === this.pathToObject) {
} else if (oldPath === this.navigateToPath) {
this.isNavigated = false;
}
}

View File

@ -13,6 +13,9 @@ export default {
if (!this.objectPath.length) {
return;
}
if (this.navigateToPath) {
return '#' + this.navigateToPath;
}
return '#/browse/' + this.objectPath
.map(o => o && this.openmct.objects.makeKeyString(o.identifier))
.reverse()

View File

@ -42,14 +42,13 @@
this.removeListeners();
this.domainObjectsById = {};
if (!selection[0]) {
if (selection.length === 0 || !selection[0][0]) {
this.structure = [];
return;
}
let structure = this.openmct.toolbars.get(selection) || [];
this.structure = structure.map(item => {
let toolbarItem = {...item};
this.structure = structure.map(toolbarItem => {
let domainObject = toolbarItem.domainObject;
let formKeys = [];
toolbarItem.control = "toolbar-" + toolbarItem.control;
@ -64,12 +63,7 @@
}
if (domainObject) {
if (formKeys.length > 0) {
toolbarItem.value = this.getFormValue(domainObject, toolbarItem);
} else {
toolbarItem.value = _.get(domainObject, this.getItemProperty(item));
}
toolbarItem.value = this.getValue(domainObject, toolbarItem);
this.registerListener(domainObject);
}
@ -89,21 +83,12 @@
observeObject(domainObject, id) {
let unobserveObject = this.openmct.objects.observe(domainObject, '*', function(newObject) {
this.domainObjectsById[id].newObject = JSON.parse(JSON.stringify(newObject));
this.scheduleToolbarUpdate();
this.updateToolbarAfterMutation();
}.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};
this.structure = this.structure.map(toolbarItem => {
let domainObject = toolbarItem.domainObject;
if (domainObject) {
@ -112,12 +97,7 @@
if (newObject) {
toolbarItem.domainObject = newObject;
if (toolbarItem.formKeys) {
toolbarItem.value = this.getFormValue(newObject, toolbarItem);
} else {
toolbarItem.value = _.get(newObject, this.getItemProperty(item));
}
toolbarItem.value = this.getValue(newObject, toolbarItem);
}
}
@ -130,17 +110,58 @@
delete tracker.newObject;
}
});
this.toolbarUpdateScheduled = false;
},
getValue(domainObject, toolbarItem) {
let value = undefined;
let applicableSelectedItems = toolbarItem.applicableSelectedItems;
if (!applicableSelectedItems) {
return value;
}
if (toolbarItem.formKeys) {
value = this.getFormValue(domainObject, toolbarItem);
} else {
let values = [];
applicableSelectedItems.forEach(selectionPath => {
values.push(_.get(domainObject, this.getItemProperty(toolbarItem, selectionPath)));
});
// If all values are the same, use it, otherwise mark the item as non-specific.
if (values.every(value => value === values[0])) {
value = values[0];
toolbarItem.nonSpecific = false;
} else {
toolbarItem.nonSpecific = true;
}
}
return value;
},
getFormValue(domainObject, toolbarItem) {
let value = {};
let values = {};
toolbarItem.formKeys.map(key => {
value[key] = _.get(domainObject, this.getItemProperty(toolbarItem) + "." + key);
values[key] = [];
toolbarItem.applicableSelectedItems.forEach(selectionPath => {
values[key].push(_.get(domainObject, this.getItemProperty(toolbarItem, selectionPath) + "." + key));
});
});
for (const key in values) {
if (values[key].every(value => value === values[key][0])) {
value[key] = values[key][0];
toolbarItem.nonSpecific = false;
} else {
toolbarItem.nonSpecific = true;
return {};
}
}
return value;
},
getItemProperty(item) {
return (typeof item.property === "function") ? item.property() : item.property;
getItemProperty(item, selectionPath) {
return (typeof item.property === "function") ? item.property(selectionPath) : item.property;
},
removeListeners() {
if (this.unObserveObjects) {
@ -152,14 +173,12 @@
},
updateObjectValue(value, item) {
let changedItemId = this.openmct.objects.makeKeyString(item.domainObject.identifier);
this.structure = this.structure.map((s) => {
let toolbarItem = {...s};
let domainObject = toolbarItem.domainObject;
if (domainObject) {
let id = this.openmct.objects.makeKeyString(domainObject.identifier);
this.structure = this.structure.map(toolbarItem => {
if (toolbarItem.domainObject) {
let id = this.openmct.objects.makeKeyString(toolbarItem.domainObject.identifier);
if (changedItemId === id && this.getItemProperty(item) === this.getItemProperty(s)) {
if (changedItemId === id && _.isEqual(toolbarItem, item)) {
toolbarItem.value = value;
}
}
@ -173,12 +192,17 @@
this.structure.map(s => {
if (s.formKeys) {
s.formKeys.forEach(key => {
this.openmct.objects.mutate(item.domainObject, this.getItemProperty(item) + "." + key, value[key]);
item.applicableSelectedItems.forEach(selectionPath => {
this.openmct.objects.mutate(item.domainObject,
this.getItemProperty(item, selectionPath) + "." + key, value[key]);
});
});
}
});
} else {
this.openmct.objects.mutate(item.domainObject, this.getItemProperty(item), value);
item.applicableSelectedItems.forEach(selectionPath => {
this.openmct.objects.mutate(item.domainObject, this.getItemProperty(item, selectionPath), value);
});
}
},
triggerMethod(item, event) {

View File

@ -4,14 +4,14 @@
:title="options.title"
:class="{
[options.icon]: true,
'c-icon-button--caution': options.modifier === 'caution'
'c-icon-button--caution': options.modifier === 'caution',
'c-icon-button--mixed': nonSpecific
}"
@click="onClick">
<div class="c-icon-button__label"
v-if="options.label">
{{ options.label }}
</div>
</div>
</div>
</template>
@ -22,6 +22,11 @@ export default {
props: {
options: Object
},
computed: {
nonSpecific() {
return this.options.nonSpecific === true;
}
},
methods: {
onClick(event) {
if (this.options.dialog) {

View File

@ -1,7 +1,7 @@
<template>
<div class="c-ctrl-wrapper">
<div class="c-icon-button c-icon-button--swatched"
:class="options.icon"
:class="[options.icon, {'c-icon-button--mixed': nonSpecific}]"
:title="options.title"
@click="toggle">
<div class="c-swatch" :style="{
@ -36,6 +36,11 @@ export default {
props: {
options: Object
},
computed: {
nonSpecific() {
return this.options.nonSpecific === true;
}
},
methods: {
select(color) {
if (color.value !== this.options.value) {

View File

@ -1,7 +1,7 @@
<template>
<div class="c-ctrl-wrapper">
<div class="c-icon-button c-icon-button--menu"
:class="options.icon"
:class="[options.icon, {'c-click-icon--mixed': nonSpecific}]"
:title="options.title"
@click="toggle">
<div class="c-button__label">{{ selectedName }}</div>
@ -47,7 +47,11 @@ export default {
if (selectedOption) {
return selectedOption.name || selectedOption.value
}
return '';
// If no selected option, then options are non-specific
return '??px';
},
nonSpecific() {
return this.options.nonSpecific === true;
}
}
}

View File

@ -2,7 +2,7 @@
<div class="c-ctrl-wrapper">
<div class="c-icon-button"
:title="nextValue.title"
:class="nextValue.icon"
:class="[nextValue.icon, {'c-click-icon--mixed': nonSpecific}]"
@click="cycle">
</div>
</div>
@ -23,6 +23,9 @@ export default {
nextIndex = 0;
}
return this.options.options[nextIndex];
},
nonSpecific() {
return this.options.nonSpecific === true;
}
},
methods: {