[Display Layout] support ordering items (#2266)

* Add a toolbar menu for changing the display order of items.

* - Wire up orderItem
- Modify the toolbar API to support function as alternative for property path.
This commit is contained in:
Pegah Sarram
2019-01-23 10:51:29 -08:00
committed by Pete Richards
parent 8ef53d85c4
commit 9e811e722f
4 changed files with 163 additions and 64 deletions

View File

@ -73,10 +73,13 @@ define([], function () {
return openmct.$injector.get('dialogService').getUserInput(form, {}); return openmct.$injector.get('dialogService').getUserInput(form, {});
} }
function getPath() {
return `configuration.items[${selection[0].context.index}]`;
}
let selectedParent = selection[1] && selection[1].context.item, let selectedParent = selection[1] && selection[1].context.item,
selectedObject = selection[0].context.item, selectedObject = selection[0].context.item,
layoutItem = selection[0].context.layoutItem, layoutItem = selection[0].context.layoutItem,
layoutItemIndex = selection[0].context.index,
toolbar = []; toolbar = [];
if (selectedObject && selectedObject.type === 'layout') { if (selectedObject && selectedObject.type === 'layout') {
@ -121,7 +124,6 @@ define([], function () {
return toolbar; return toolbar;
} }
let path = `configuration.items[${layoutItemIndex}]`;
let separator = { let separator = {
control: "separator" control: "separator"
}; };
@ -140,7 +142,7 @@ define([], function () {
label: 'Ok', label: 'Ok',
emphasis: 'true', emphasis: 'true',
callback: function () { callback: function () {
removeItem(layoutItem, layoutItemIndex); removeItem(layoutItem, selection[0].context.index);
prompt.dismiss(); prompt.dismiss();
} }
}, },
@ -154,10 +156,43 @@ define([], function () {
}); });
} }
}; };
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 = { let useGrid = {
control: "toggle-button", control: "toggle-button",
domainObject: selectedParent, domainObject: selectedParent,
property: path + ".useGrid", property: function () {
return getPath() + ".useGrid";
},
options: [ options: [
{ {
value: false, value: false,
@ -177,10 +212,14 @@ define([], function () {
toolbar.push(separator); toolbar.push(separator);
} }
toolbar.push(stackOrder);
toolbar.push(separator);
toolbar.push({ toolbar.push({
control: "toggle-button", control: "toggle-button",
domainObject: selectedParent, domainObject: selectedParent,
property: path + ".hasFrame", property: function () {
return getPath() + ".hasFrame";
},
options: [ options: [
{ {
value: false, value: false,
@ -194,6 +233,7 @@ define([], function () {
} }
] ]
}); });
toolbar.push(separator);
toolbar.push(useGrid); toolbar.push(useGrid);
toolbar.push(separator); toolbar.push(separator);
toolbar.push(remove); toolbar.push(remove);
@ -202,21 +242,27 @@ define([], function () {
let fill = { let fill = {
control: "color-picker", control: "color-picker",
domainObject: selectedParent, domainObject: selectedParent,
property: path + ".fill", property: function () {
return getPath() + ".fill";
},
icon: "icon-paint-bucket", icon: "icon-paint-bucket",
title: "Set fill color" title: "Set fill color"
}, },
stroke = { stroke = {
control: "color-picker", control: "color-picker",
domainObject: selectedParent, domainObject: selectedParent,
property: path + ".stroke", property: function () {
return getPath() + ".stroke";
},
icon: "icon-line-horz", icon: "icon-line-horz",
title: "Set border color" title: "Set border color"
}, },
color = { color = {
control: "color-picker", control: "color-picker",
domainObject: selectedParent, domainObject: selectedParent,
property: path + ".color", property: function () {
return getPath() + ".color";
},
icon: "icon-font", icon: "icon-font",
mandatory: true, mandatory: true,
title: "Set text color", title: "Set text color",
@ -225,7 +271,9 @@ define([], function () {
size = { size = {
control: "select-menu", control: "select-menu",
domainObject: selectedParent, domainObject: selectedParent,
property: path + ".size", property: function () {
return getPath() + ".size";
},
title: "Set text size", title: "Set text size",
options: TEXT_SIZE.map(size => { options: TEXT_SIZE.map(size => {
return { return {
@ -237,7 +285,9 @@ define([], function () {
control: "input", control: "input",
type: "number", type: "number",
domainObject: selectedParent, domainObject: selectedParent,
property: path + ".x", property: function () {
return getPath() + ".x";
},
label: "X:", label: "X:",
title: "X position" title: "X position"
}, },
@ -245,7 +295,9 @@ define([], function () {
control: "input", control: "input",
type: "number", type: "number",
domainObject: selectedParent, domainObject: selectedParent,
property: path + ".y", property: function () {
return getPath() + ".y";
},
label: "Y:", label: "Y:",
title: "Y position", title: "Y position",
}, },
@ -253,7 +305,9 @@ define([], function () {
control: 'input', control: 'input',
type: 'number', type: 'number',
domainObject: selectedParent, domainObject: selectedParent,
property: path + ".width", property: function () {
return getPath() + ".width";
},
label: 'W:', label: 'W:',
title: 'Resize object width' title: 'Resize object width'
}, },
@ -261,7 +315,9 @@ define([], function () {
control: 'input', control: 'input',
type: 'number', type: 'number',
domainObject: selectedParent, domainObject: selectedParent,
property: path + ".height", property: function () {
return getPath() + ".height";
},
label: 'H:', label: 'H:',
title: 'Resize object height' title: 'Resize object height'
}; };
@ -270,7 +326,9 @@ define([], function () {
let displayMode = { let displayMode = {
control: "select-menu", control: "select-menu",
domainObject: selectedParent, domainObject: selectedParent,
property: path + ".displayMode", property: function () {
return getPath() + ".displayMode";
},
title: "Set display mode", title: "Set display mode",
options: [ options: [
{ {
@ -290,7 +348,9 @@ define([], function () {
value = { value = {
control: "select-menu", control: "select-menu",
domainObject: selectedParent, domainObject: selectedParent,
property: path + ".value", property: function () {
return getPath() + ".value";
},
title: "Set value", title: "Set value",
options: openmct.telemetry.getMetadata(selectedObject).values().map(value => { options: openmct.telemetry.getMetadata(selectedObject).values().map(value => {
return { return {
@ -304,6 +364,7 @@ define([], function () {
separator, separator,
value, value,
separator, separator,
stackOrder,
fill, fill,
stroke, stroke,
color, color,
@ -314,7 +375,6 @@ define([], function () {
y, y,
height, height,
width, width,
separator,
useGrid, useGrid,
separator, separator,
remove remove
@ -323,30 +383,34 @@ define([], function () {
let text = { let text = {
control: "button", control: "button",
domainObject: selectedParent, domainObject: selectedParent,
property: path, property: function () {
return getPath();
},
icon: "icon-gear", icon: "icon-gear",
title: "Edit text properties", title: "Edit text properties",
dialog: DIALOG_FORM['text'] dialog: DIALOG_FORM['text']
}; };
toolbar = [ toolbar = [
stackOrder,
fill, fill,
stroke, stroke,
color,
separator, separator,
color,
size, size,
separator, separator,
x, x,
y, y,
height, height,
width, width,
useGrid,
separator, separator,
text, text,
useGrid,
separator, separator,
remove remove
]; ];
} else if (layoutItem.type === 'box-view') { } else if (layoutItem.type === 'box-view') {
toolbar = [ toolbar = [
stackOrder,
fill, fill,
stroke, stroke,
separator, separator,
@ -354,7 +418,6 @@ define([], function () {
y, y,
height, height,
width, width,
separator,
useGrid, useGrid,
separator, separator,
remove remove
@ -363,21 +426,24 @@ define([], function () {
let url = { let url = {
control: "button", control: "button",
domainObject: selectedParent, domainObject: selectedParent,
property: path, property: function () {
return getPath();
},
icon: "icon-image", icon: "icon-image",
title: "Edit image properties", title: "Edit image properties",
dialog: DIALOG_FORM['image'] dialog: DIALOG_FORM['image']
}; };
toolbar = [ toolbar = [
stackOrder,
stroke, stroke,
separator, separator,
x, x,
y, y,
height, height,
width, width,
useGrid,
separator, separator,
url, url,
useGrid,
separator, separator,
remove remove
]; ];
@ -386,7 +452,9 @@ define([], function () {
control: "input", control: "input",
type: "number", type: "number",
domainObject: selectedParent, domainObject: selectedParent,
property: path + ".x2", property: function () {
return getPath() + ".x2";
},
label: "X2:", label: "X2:",
title: "X2 position" title: "X2 position"
}, },
@ -394,18 +462,20 @@ define([], function () {
control: "input", control: "input",
type: "number", type: "number",
domainObject: selectedParent, domainObject: selectedParent,
property: path + ".y2", property: function () {
return getPath() + ".y2";
},
label: "Y2:", label: "Y2:",
title: "Y2 position", title: "Y2 position",
}; };
toolbar = [ toolbar = [
stackOrder,
stroke, stroke,
separator, separator,
x, x,
y, y,
x2, x2,
y2, y2,
separator,
useGrid, useGrid,
separator, separator,
remove remove

View File

@ -117,6 +117,12 @@
'text-view': TextView, 'text-view': TextView,
'image-view': ImageView 'image-view': ImageView
}; };
const ORDERS = {
top: Number.POSITIVE_INFINITY,
up: 1,
down: -1,
bottom: Number.NEGATIVE_INFINITY
};
function getItemDefinition(itemType, ...options) { function getItemDefinition(itemType, ...options) {
let itemView = ITEM_TYPE_VIEW_MAP[itemType]; let itemView = ITEM_TYPE_VIEW_MAP[itemType];
@ -163,42 +169,45 @@
let itemIndex = selection[0].context.index; let itemIndex = selection[0].context.index;
if (itemIndex !== undefined) { if (itemIndex !== undefined) {
let path = `configuration.items[${itemIndex}]`; this.attachSelectionListener(itemIndex);
this.removeSelectionListener = this.openmct.objects.observe(this.internalDomainObject, path + ".useGrid", function (value) {
let item = this.layoutItems[itemIndex];
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[${itemIndex}]`, item);
}.bind(this));
} }
this.updateDrilledIn(); this.updateDrilledIn();
}, },
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));
},
updateDrilledIn(drilledInItem) { updateDrilledIn(drilledInItem) {
let identifier = drilledInItem && this.openmct.objects.makeKeyString(drilledInItem.identifier); let identifier = drilledInItem && this.openmct.objects.makeKeyString(drilledInItem.identifier);
this.drilledIn = identifier; this.drilledIn = identifier;
@ -369,6 +378,22 @@
}); });
this.mutate("configuration.items", layoutItems); this.mutate("configuration.items", layoutItems);
this.$el.click(); this.$el.click();
},
orderItem(position, index) {
let delta = ORDERS[position];
let newIndex = Math.max(Math.min(index + delta, this.layoutItems.length - 1), 0);
let item = this.layoutItems[index];
if (newIndex !== index) {
this.layoutItems.splice(index, 1);
this.layoutItems.splice(newIndex, 0, item);
this.mutate('configuration.items', this.layoutItems);
if (this.removeSelectionListener) {
this.removeSelectionListener();
this.attachSelectionListener(newIndex);
}
}
} }
}, },
mounted() { mounted() {

View File

@ -61,7 +61,8 @@ export default function () {
return { return {
item: domainObject, item: domainObject,
addElement: component && component.$refs.displayLayout.addElement, addElement: component && component.$refs.displayLayout.addElement,
removeItem: component && component.$refs.displayLayout.removeItem removeItem: component && component.$refs.displayLayout.removeItem,
orderItem: component && component.$refs.displayLayout.orderItem
} }
}, },
destroy() { destroy() {

View File

@ -67,7 +67,7 @@
if (formKeys.length > 0) { if (formKeys.length > 0) {
toolbarItem.value = this.getFormValue(domainObject, toolbarItem); toolbarItem.value = this.getFormValue(domainObject, toolbarItem);
} else { } else {
toolbarItem.value = _.get(domainObject, item.property); toolbarItem.value = _.get(domainObject, this.getItemProperty(item));
} }
this.registerListener(domainObject); this.registerListener(domainObject);
@ -116,7 +116,7 @@
if (toolbarItem.formKeys) { if (toolbarItem.formKeys) {
toolbarItem.value = this.getFormValue(newObject, toolbarItem); toolbarItem.value = this.getFormValue(newObject, toolbarItem);
} else { } else {
toolbarItem.value = _.get(newObject, item.property); toolbarItem.value = _.get(newObject, this.getItemProperty(item));
} }
} }
} }
@ -135,10 +135,13 @@
getFormValue(domainObject, toolbarItem) { getFormValue(domainObject, toolbarItem) {
let value = {}; let value = {};
toolbarItem.formKeys.map(key => { toolbarItem.formKeys.map(key => {
value[key] = _.get(domainObject, toolbarItem.property + "." + key); value[key] = _.get(domainObject, this.getItemProperty(toolbarItem) + "." + key);
}); });
return value; return value;
}, },
getItemProperty(item) {
return (typeof item.property === "function") ? item.property() : item.property;
},
removeListeners() { removeListeners() {
if (this.unObserveObjects) { if (this.unObserveObjects) {
this.unObserveObjects.forEach((unObserveObject) => { this.unObserveObjects.forEach((unObserveObject) => {
@ -156,7 +159,7 @@
if (domainObject) { if (domainObject) {
let id = this.openmct.objects.makeKeyString(domainObject.identifier); let id = this.openmct.objects.makeKeyString(domainObject.identifier);
if (changedItemId === id && item.property === s.property) { if (changedItemId === id && this.getItemProperty(item) === this.getItemProperty(s)) {
toolbarItem.value = value; toolbarItem.value = value;
} }
} }
@ -170,12 +173,12 @@
this.structure.map(s => { this.structure.map(s => {
if (s.formKeys) { if (s.formKeys) {
s.formKeys.forEach(key => { s.formKeys.forEach(key => {
this.openmct.objects.mutate(item.domainObject, item.property + "." + key, value[key]); this.openmct.objects.mutate(item.domainObject, this.getItemProperty(item) + "." + key, value[key]);
}); });
} }
}); });
} else { } else {
this.openmct.objects.mutate(item.domainObject, item.property, value); this.openmct.objects.mutate(item.domainObject, this.getItemProperty(item), value);
} }
}, },
triggerMethod(item, event) { triggerMethod(item, event) {