Compare commits
	
		
			24 Commits
		
	
	
		
			domainobje
			...
			locator-bu
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 8f45758350 | ||
|   | 6bf4b3aba8 | ||
|   | b659f205f7 | ||
|   | 40d54df567 | ||
|   | b7fa5c7ba8 | ||
|   | 3b0605d17b | ||
|   | d93e7bfd1a | ||
|   | 104cd0ed29 | ||
|   | 7fb3d86d06 | ||
|   | dbb42e9bb6 | ||
|   | d1baa1f98b | ||
|   | 5ab68c0586 | ||
|   | 3cf78f509d | ||
|   | c6053e234a | ||
|   | 964c326535 | ||
|   | baf410a364 | ||
|   | 517a40a32b | ||
|   | 8b275b206b | ||
|   | a40a31aa4c | ||
|   | 6c0c1df010 | ||
|   | c552afff17 | ||
|   | 0837129ad5 | ||
|   | 6f3e2a8fbb | ||
|   | 4189a05758 | 
| @@ -49,7 +49,7 @@ define( | ||||
|                     name: "Properties", | ||||
|                     rows: this.properties.map(function (property, index) { | ||||
|                         // Property definition is same as form row definition | ||||
|                         var row = Object.create(property.getDefinition()); | ||||
|                         var row = JSON.parse(JSON.stringify(property.getDefinition())); | ||||
|                         row.key = index; | ||||
|                         return row; | ||||
|                     }).filter(function (row) { | ||||
|   | ||||
| @@ -66,7 +66,7 @@ define( | ||||
|                 name: "Properties", | ||||
|                 rows: this.properties.map(function (property, index) { | ||||
|                     // Property definition is same as form row definition | ||||
|                     var row = Object.create(property.getDefinition()); | ||||
|                     var row = JSON.parse(JSON.stringify(property.getDefinition())); | ||||
|  | ||||
|                     // Use index as the key into the formValue; | ||||
|                     // this correlates to the indexing provided by | ||||
|   | ||||
| @@ -33,20 +33,25 @@ export default class LegacyContextMenuAction { | ||||
|     } | ||||
|  | ||||
|     invoke(objectPath) { | ||||
|         let context = { | ||||
|             category: 'contextual', | ||||
|             domainObject: this.openmct.legacyObject(objectPath) | ||||
|         } | ||||
|         let legacyAction = new this.LegacyAction(context); | ||||
|         this.openmct.objects.getRoot().then((root) => { | ||||
|             let pathWithRoot = objectPath.slice(); | ||||
|             pathWithRoot.push(root); | ||||
|  | ||||
|         if (!legacyAction.getMetadata) { | ||||
|             let metadata = Object.create(this.LegacyAction.definition); | ||||
|             metadata.context = context; | ||||
|             legacyAction.getMetadata = function () { | ||||
|                 return metadata; | ||||
|             }.bind(legacyAction); | ||||
|         } | ||||
|         legacyAction.perform(); | ||||
|             let context = { | ||||
|                 category: 'contextual', | ||||
|                 domainObject: this.openmct.legacyObject(pathWithRoot) | ||||
|             } | ||||
|             let legacyAction = new this.LegacyAction(context); | ||||
|  | ||||
|             if (!legacyAction.getMetadata) { | ||||
|                 let metadata = Object.create(this.LegacyAction.definition); | ||||
|                 metadata.context = context; | ||||
|                 legacyAction.getMetadata = function () { | ||||
|                     return metadata; | ||||
|                 }.bind(legacyAction); | ||||
|             } | ||||
|             legacyAction.perform(); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     appliesTo(objectPath) { | ||||
|   | ||||
| @@ -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(); | ||||
|   | ||||
| @@ -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; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -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; | ||||
|                 }, [])); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -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; | ||||
|  | ||||
|     } | ||||
|   | ||||
| @@ -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'], | ||||
|   | ||||
| @@ -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> | ||||
|   | ||||
							
								
								
									
										233
									
								
								src/plugins/displayLayout/components/EditMarquee.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										233
									
								
								src/plugins/displayLayout/components/EditMarquee.vue
									
									
									
									
									
										Normal 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> | ||||
| @@ -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'], | ||||
|   | ||||
| @@ -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; | ||||
| @@ -59,124 +48,15 @@ | ||||
|         > *:first-child { | ||||
|             flex: 1 1 auto; | ||||
|         } | ||||
|  | ||||
|         &:not(.no-frame) { | ||||
|             background: $colorBodyBg; | ||||
|             border: $browseFrameBorder; | ||||
|             padding: $interiorMargin; | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     .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 { | ||||
|             $moveBarOutDelay: 500ms; | ||||
|             &.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; | ||||
| @@ -188,37 +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:not(.is-resizing) { | ||||
|             // Show and animate the __move bar for sub-object views with complex content | ||||
|             &: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--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' | ||||
|  | ||||
| @@ -228,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', | ||||
| @@ -264,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]); | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -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> | ||||
|   | ||||
| @@ -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'], | ||||
|   | ||||
| @@ -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'], | ||||
|   | ||||
| @@ -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'], | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -23,17 +23,18 @@ export default { | ||||
|         FilterObject | ||||
|     }, | ||||
|     inject: [ | ||||
|         'openmct', | ||||
|         'providedObject' | ||||
|         'openmct' | ||||
|     ], | ||||
|     data() { | ||||
|         let providedObject = this.openmct.selection.get()[0][0].context.item; | ||||
|         let persistedFilters = {}; | ||||
|  | ||||
|         if (this.providedObject.configuration && this.providedObject.configuration.filters) { | ||||
|             persistedFilters = this.providedObject.configuration.filters; | ||||
|         if (providedObject.configuration && providedObject.configuration.filters) { | ||||
|             persistedFilters = providedObject.configuration.filters; | ||||
|         } | ||||
|  | ||||
|         return { | ||||
|             providedObject, | ||||
|             persistedFilters, | ||||
|             children: {} | ||||
|         } | ||||
| @@ -73,13 +74,14 @@ export default { | ||||
|         this.composition.on('add', this.addChildren); | ||||
|         this.composition.on('remove', this.removeChildren); | ||||
|         this.composition.load(); | ||||
|  | ||||
|         this.unobserve = this.openmct.objects.observe(this.providedObject, 'configuration.filters', this.updatePersistedFilters); | ||||
|         this.unobserveAllMutation = this.openmct.objects.observe(this.providedObject, '*', (mutatedObject) => this.providedObject = mutatedObject); | ||||
|     }, | ||||
|     beforeDestroy() { | ||||
|         this.composition.off('add', this.addChildren); | ||||
|         this.composition.off('remove', this.removeChildren); | ||||
|         this.unobserve(); | ||||
|         this.unobserveAllMutation(); | ||||
|     } | ||||
| } | ||||
| </script> | ||||
|   | ||||
| @@ -33,23 +33,20 @@ 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; | ||||
|  | ||||
|                 return { | ||||
|                     show: function (element) { | ||||
|                         component = new Vue({ | ||||
|                             provide: { | ||||
|                                 openmct, | ||||
|                                 providedObject | ||||
|                                 openmct | ||||
|                             }, | ||||
|                             components: { | ||||
|                                 FiltersView: FiltersView.default | ||||
| @@ -59,8 +56,10 @@ define([ | ||||
|                         }); | ||||
|                     }, | ||||
|                     destroy: function () { | ||||
|                         component.$destroy(); | ||||
|                         component = undefined; | ||||
|                         if (component) { | ||||
|                             component.$destroy(); | ||||
|                             component = undefined; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|   | ||||
| @@ -106,9 +106,6 @@ | ||||
|     .c-fl { | ||||
|         @include abs(); | ||||
|         display: flex; | ||||
|         flex-direction: column; // TEMP: only needed to support temp-toolbar element | ||||
|  | ||||
|         > * + * {  margin-top: $interiorMargin; } | ||||
|  | ||||
|         .temp-toolbar { | ||||
|             flex: 0 0 auto; | ||||
| @@ -292,11 +289,6 @@ | ||||
|             margin-bottom: $interiorMargin; | ||||
|         } | ||||
|  | ||||
|         &__object-view { | ||||
|             flex: 1 1 auto; | ||||
|             overflow: auto; | ||||
|         } | ||||
|  | ||||
|         &__size-indicator { | ||||
|             $size: 35px; | ||||
|  | ||||
| @@ -579,8 +571,7 @@ export default { | ||||
|             }); | ||||
|         }, | ||||
|         setSelectionToParent() { | ||||
|              let currentSelection = this.openmct.selection.selected; | ||||
|              this.openmct.selection.select(currentSelection.slice(1)); | ||||
|             this.$el.click(); | ||||
|         }, | ||||
|         allowContainerDrop(event, index) { | ||||
|             if (!event.dataTransfer.types.includes('containerid')) { | ||||
|   | ||||
| @@ -79,12 +79,14 @@ export default { | ||||
|         }, | ||||
|         setSelection() { | ||||
|             this.$nextTick(function () { | ||||
|                 let childContext = this.$refs.objectFrame.getSelectionContext(); | ||||
|                 childContext.item = this.domainObject; | ||||
|                 childContext.type = 'frame'; | ||||
|                 childContext.frameId = this.frame.id; | ||||
|                 this.unsubscribeSelection = this.openmct.selection.selectable( | ||||
|                     this.$refs.frame, childContext, false); | ||||
|                 if (this.$refs && this.$refs.objectFrame) { | ||||
|                     let childContext = this.$refs.objectFrame.getSelectionContext(); | ||||
|                     childContext.item = this.domainObject; | ||||
|                     childContext.type = 'frame'; | ||||
|                     childContext.frameId = this.frame.id; | ||||
|                     this.unsubscribeSelection = this.openmct.selection.selectable( | ||||
|                         this.$refs.frame, childContext, false); | ||||
|                 } | ||||
|             }); | ||||
|         }, | ||||
|         initDrag(event) { | ||||
|   | ||||
| @@ -27,28 +27,22 @@ 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, | ||||
|                 addContainer, | ||||
|                 toggleFrame, | ||||
|                 separator; | ||||
|  | ||||
|             separator = { | ||||
|                 control: "separator", | ||||
|                 domainObject: selection[0].context.item, | ||||
|                 key: "separator" | ||||
|             }; | ||||
|                 toggleFrame; | ||||
|  | ||||
|             toggleContainer = { | ||||
|                 control: 'toggle-button', | ||||
| @@ -69,6 +63,12 @@ function ToolbarProvider(openmct) { | ||||
|                 ] | ||||
|             }; | ||||
|  | ||||
|             function getSeparator() { | ||||
|                 return { | ||||
|                     control: "separator" | ||||
|                 }; | ||||
|             } | ||||
|  | ||||
|             if (primary.context.type === 'frame') { | ||||
|                 let frameId = primary.context.frameId; | ||||
|                 let layoutObject = tertiary.context.item; | ||||
| @@ -202,9 +202,9 @@ function ToolbarProvider(openmct) { | ||||
|             let toolbar = [ | ||||
|                 toggleContainer, | ||||
|                 addContainer, | ||||
|                 toggleFrame ? separator: undefined, | ||||
|                 toggleFrame ? getSeparator() : undefined, | ||||
|                 toggleFrame, | ||||
|                 deleteFrame || deleteContainer ? separator : undefined, | ||||
|                 deleteFrame || deleteContainer ? getSeparator() : undefined, | ||||
|                 deleteFrame, | ||||
|                 deleteContainer | ||||
|             ]; | ||||
|   | ||||
| @@ -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; | ||||
|                         }); | ||||
|                 } | ||||
|   | ||||
| @@ -85,6 +85,10 @@ export default class RemoveAction { | ||||
|         ); | ||||
|  | ||||
|         this.openmct.objects.mutate(parent, 'composition', composition); | ||||
|  | ||||
|         if (this.inNavigationPath(child) && this.openmct.editor.isEditing()) { | ||||
|             this.openmct.editor.save(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     appliesTo(objectPath) { | ||||
|   | ||||
| @@ -237,6 +237,7 @@ define ([ | ||||
|         }); | ||||
|         delete this.compositionObjs[objectId]; | ||||
|         this.subscriptions[objectId](); //unsubscribe from telemetry source | ||||
|         delete this.subscriptions[objectId]; | ||||
|         this.eventEmitter.emit('remove', identifier); | ||||
|  | ||||
|         if (_.isEmpty(this.compositionObjs)) { | ||||
|   | ||||
| @@ -43,7 +43,7 @@ define([ | ||||
|         return evaluator.requestLatest(options) | ||||
|             .then(function (latestDatum) { | ||||
|                 this.pool.release(evaluator); | ||||
|                 return [latestDatum]; | ||||
|                 return latestDatum ? [latestDatum] : []; | ||||
|             }.bind(this)); | ||||
|     }; | ||||
|  | ||||
|   | ||||
| @@ -52,7 +52,10 @@ define([ | ||||
|             strategy: 'latest', | ||||
|             size: 1 | ||||
|         }).then(function (results) { | ||||
|             if (this.destroyed || this.hasUpdated || this.renderTracker !== renderTracker) { | ||||
|             if (this.destroyed || | ||||
|                 this.hasUpdated || | ||||
|                 this.renderTracker !== renderTracker || | ||||
|                 results.length === 0) { | ||||
|                 return; | ||||
|             } | ||||
|             this.updateState(results[results.length - 1]); | ||||
|   | ||||
| @@ -25,7 +25,7 @@ | ||||
|         <div class="c-tabs-view__object-holder" | ||||
|             v-for="(tab, index) in tabsList" | ||||
|             :key="index" | ||||
|             :class="{'invisible': !isCurrent(tab)}"> | ||||
|             :class="{'c-tabs-view__object-holder--hidden': !isCurrent(tab)}"> | ||||
|             <div v-if="currentTab" | ||||
|                  class="c-tabs-view__object-name l-browse-bar__object-name--w" | ||||
|                  :class="currentTab.type.definition.cssClass"> | ||||
| @@ -68,6 +68,14 @@ | ||||
|             flex: 1 1 auto; | ||||
|             display: flex; | ||||
|             flex-direction: column; | ||||
|  | ||||
|             &--hidden { | ||||
|                 height: 1000px; | ||||
|                 width: 1000px; | ||||
|                 position: absolute; | ||||
|                 left: -9999px; | ||||
|                 top: -9999px; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         &__object-name { | ||||
| @@ -78,7 +86,10 @@ | ||||
|         } | ||||
|  | ||||
|         &__object { | ||||
|             display: flex; | ||||
|             flex-flow: column nowrap; | ||||
|             flex: 1 1 auto; | ||||
|             height: 0; // Chrome 73 oveflow bug fix | ||||
|         } | ||||
|  | ||||
|         &__empty-message { | ||||
|   | ||||
| @@ -31,6 +31,7 @@ define([ | ||||
|  | ||||
|             openmct.types.addType('tabs', { | ||||
|                 name: "Tabs View", | ||||
|                 description: 'Add multiple objects of any type to this view, and quickly navigate between them with tabs', | ||||
|                 creatable: true, | ||||
|                 cssClass: 'icon-tabs-view', | ||||
|                 initialize(domainObject) { | ||||
|   | ||||
| @@ -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; | ||||
|                     } | ||||
|                 } | ||||
|   | ||||
| @@ -137,12 +137,17 @@ define([ | ||||
|             let requestOptions = this.buildOptionsFromConfiguration(telemetryObject); | ||||
|             return this.openmct.telemetry.request(telemetryObject, requestOptions) | ||||
|                 .then(telemetryData => { | ||||
|                     //Check that telemetry object has not been removed since telemetry was requested. | ||||
|                     if (!this.telemetryObjects.includes(telemetryObject)) { | ||||
|                         return; | ||||
|                     } | ||||
|                     let keyString = this.openmct.objects.makeKeyString(telemetryObject.identifier); | ||||
|                     let columnMap = this.getColumnMapForObject(keyString); | ||||
|                     let limitEvaluator = this.openmct.telemetry.limitEvaluator(telemetryObject); | ||||
|  | ||||
|                     let telemetryRows = telemetryData.map(datum => new TelemetryTableRow(datum, columnMap, keyString, limitEvaluator)); | ||||
|                     this.boundedRows.add(telemetryRows); | ||||
|                 }).finally(() => { | ||||
|                     this.decrementOutstandingRequests(); | ||||
|                 }); | ||||
|         } | ||||
| @@ -193,6 +198,10 @@ define([ | ||||
|             let limitEvaluator = this.openmct.telemetry.limitEvaluator(telemetryObject); | ||||
|  | ||||
|             this.subscriptions[keyString] = this.openmct.telemetry.subscribe(telemetryObject, (datum) => { | ||||
|                 //Check that telemetry object has not been removed since telemetry was requested. | ||||
|                 if (!this.telemetryObjects.includes(telemetryObject)) { | ||||
|                     return; | ||||
|                 } | ||||
|                 this.boundedRows.add(new TelemetryTableRow(datum, columnMap, keyString, limitEvaluator)); | ||||
|             }, subscribeOptions); | ||||
|         } | ||||
|   | ||||
| @@ -34,39 +34,13 @@ | ||||
|             isSortable ? 'is-sortable' : '',  | ||||
|             isSortable && sortOptions.key === headerKey ? 'is-sorting' : '',  | ||||
|             isSortable && sortOptions.direction].join(' ')"> | ||||
|             <div v-if="isEditing" class="c-telemetry-table__resize-hotzone c-telemetry-table__resize-hotzone--right" | ||||
|             <div class="c-telemetry-table__resize-hitarea" | ||||
|                 @mousedown="resizeColumnStart" | ||||
|             ></div> | ||||
|             <slot></slot> | ||||
|         </div> | ||||
| </th> | ||||
| </template> | ||||
| <style lang="scss"> | ||||
|     @import "~styles/sass-base"; | ||||
|     @import "~styles/table"; | ||||
|  | ||||
|     $hotzone-size: 6px; | ||||
|  | ||||
|     .c-telemetry-table__headers__content { | ||||
|         width: 100%; | ||||
|     } | ||||
|  | ||||
|     .c-table.c-telemetry-table { | ||||
|         .c-telemetry-table__resize-hotzone { | ||||
|             display: block; | ||||
|             position: absolute; | ||||
|             height: 100%; | ||||
|             padding: 0; | ||||
|             margin: 0; | ||||
|             width: $hotzone-size; | ||||
|             min-width: $hotzone-size; | ||||
|             cursor: col-resize; | ||||
|             border: none; | ||||
|             right: 0px; | ||||
|             margin-right: -$tabularTdPadLR - 1 - $hotzone-size / 2; | ||||
|         } | ||||
|     } | ||||
| </style> | ||||
| <script> | ||||
| import _ from 'lodash'; | ||||
| const MOVE_COLUMN_DT_TYPE = 'movecolumnfromindex'; | ||||
|   | ||||
| @@ -34,7 +34,7 @@ | ||||
|     <div class="c-telemetry-table__headers-w js-table__headers-w" ref="headersTable" :style="{ 'max-width': widthWithScroll}"> | ||||
|         <table class="c-table__headers c-telemetry-table__headers"> | ||||
|             <thead> | ||||
|                 <tr class="c-telemetry-table__headers__name"> | ||||
|                 <tr class="c-telemetry-table__headers__labels"> | ||||
|                     <table-column-header | ||||
|                         v-for="(title, key, headerIndex) in headers" | ||||
|                         :key="key" | ||||
| @@ -49,7 +49,8 @@ | ||||
|                         :columnWidth="columnWidths[key]" | ||||
|                         :sortOptions="sortOptions" | ||||
|                         :isEditing="isEditing" | ||||
|                         >{{title}}</table-column-header> | ||||
|                     ><span class="c-telemetry-table__headers__label">{{title}}</span> | ||||
|                     </table-column-header> | ||||
|                 </tr> | ||||
|                 <tr class="c-telemetry-table__headers__filter"> | ||||
|                     <table-column-header | ||||
| @@ -159,6 +160,32 @@ | ||||
|             thead { | ||||
|                 display: block; | ||||
|             } | ||||
|  | ||||
|             &__labels { | ||||
|                 // Top row, has labels | ||||
|                 .c-telemetry-table__headers__content { | ||||
|                     // Holds __label, sort indicator and resize-hitarea | ||||
|                     display: flex; | ||||
|                     align-items: center; | ||||
|                     justify-content: center; | ||||
|                     width: 100%; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         &__headers__label { | ||||
|             overflow: hidden; | ||||
|             flex: 0 1 auto; | ||||
|         } | ||||
|  | ||||
|         &__resize-hitarea { | ||||
|             // In table-column-header.vue | ||||
|             @include abs(); | ||||
|             display: none; // Set to display: block in .is-editing section below | ||||
|             left: auto; right: -1 * $tabularTdPadLR; | ||||
|             width: $tableResizeColHitareaD; | ||||
|             cursor: col-resize; | ||||
|             transform: translateX(50%); // Move so this element sits over border between columns | ||||
|         } | ||||
|  | ||||
|         /******************************* ELEMENTS */ | ||||
| @@ -221,7 +248,7 @@ | ||||
|  | ||||
|     /******************************* EDITING */ | ||||
|     .is-editing { | ||||
|         .c-telemetry-table__headers__name { | ||||
|         .c-telemetry-table__headers__labels { | ||||
|             th[draggable], | ||||
|             th[draggable] > * { | ||||
|                 cursor: move; | ||||
| @@ -233,6 +260,10 @@ | ||||
|                 > * { background: $b; } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         .c-telemetry-table__resize-hitarea { | ||||
|             display: block; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /******************************* LEGACY */ | ||||
| @@ -557,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); | ||||
|         }, | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|         }; | ||||
|   | ||||
| @@ -79,7 +79,7 @@ $colorKeyHov: #26d8ff; | ||||
| $colorKeyFilter: invert(36%) sepia(76%) saturate(2514%) hue-rotate(170deg) brightness(99%) contrast(101%); | ||||
| $colorKeyFilterHov: invert(63%) sepia(88%) saturate(3029%) hue-rotate(154deg) brightness(101%) contrast(100%); | ||||
| $colorKeySelectedBg: $colorKey; | ||||
| $uiColor: #00b2ff; // Resize bars, splitter bars, etc. | ||||
| $uiColor: #0093ff; // Resize bars, splitter bars, etc. | ||||
| $colorInteriorBorder: rgba($colorBodyFg, 0.2); | ||||
| $colorA: #ccc; | ||||
| $colorAHov: #fff; | ||||
| @@ -143,6 +143,8 @@ $browseSelectedBorder: 1px solid rgba($colorBodyFg, 0.4); | ||||
|  | ||||
| /************************************************** EDITING */ | ||||
| $editUIColor: $uiColor; // Base color | ||||
| $editUIColorBg: $editUIColor; | ||||
| $editUIColorFg: #fff; | ||||
| $editUIColorHov: pullForward(saturate($uiColor, 10%), 20%); // Hover color when $editUIColor is applied as a base color | ||||
| $editUIBaseColor: #344b8d; // Base color, toolbar bg | ||||
| $editUIBaseColorHov: pullForward($editUIBaseColor, 20%); | ||||
| @@ -159,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 | ||||
| @@ -168,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; | ||||
| @@ -224,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; | ||||
| @@ -385,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); | ||||
|  | ||||
|   | ||||
| @@ -147,6 +147,8 @@ $browseSelectedBorder: 1px solid rgba($colorBodyFg, 0.4); | ||||
|  | ||||
| /************************************************** EDITING */ | ||||
| $editUIColor: $uiColor; // Base color | ||||
| $editUIColorBg: $editUIColor; | ||||
| $editUIColorFg: #fff; | ||||
| $editUIColorHov: pullForward(saturate($uiColor, 10%), 20%); // Hover color when $editUIColor is applied as a base color | ||||
| $editUIBaseColor: #344b8d; // Base color, toolbar bg | ||||
| $editUIBaseColorHov: pullForward($editUIBaseColor, 20%); | ||||
| @@ -163,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 | ||||
| @@ -172,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; | ||||
| @@ -228,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; | ||||
| @@ -389,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); | ||||
|  | ||||
|   | ||||
| @@ -143,6 +143,8 @@ $browseSelectedBorder: 1px solid rgba($colorBodyFg, 0.4); | ||||
|  | ||||
| /************************************************** EDITING */ | ||||
| $editUIColor: $uiColor; // Base color | ||||
| $editUIColorBg: $editUIColor; | ||||
| $editUIColorFg: #fff; | ||||
| $editUIColorHov: pullForward(saturate($uiColor, 10%), 20%); // Hover color when $editUIColor is applied as a base color | ||||
| $editUIBaseColor: #cae1ff; // Base color, toolbar bg | ||||
| $editUIBaseColorHov: pushBack($editUIBaseColor, 20%); | ||||
| @@ -159,7 +161,7 @@ $editFrameBorderHov: 1px solid $editFrameColorHov; // Hover on selectable frames | ||||
| $editFrameColorSelected: #333; // 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; | ||||
| $editFrameSelectedShdw: rgba(black, 0.5) 0 1px 5px 2px; | ||||
| $editFrameSelectedBorder: 1px dashed $editFrameColorSelected; // Selected frame element | ||||
| $editFrameMovebarColorBg: $editFrameColor; // Movebar bg color | ||||
| $editFrameMovebarColorFg: pullForward($editFrameMovebarColorBg, 20%);  // Grippy lines, container size text | ||||
| @@ -168,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; | ||||
| @@ -206,7 +209,7 @@ $colorDisclosureCtrlHov: rgba($colorBodyFg, 0.7); | ||||
| $btnStdH: 24px; | ||||
| $colorCursorGuide: rgba(black, 0.6); | ||||
| $shdwCursorGuide: rgba(white, 0.4) 0 0 2px; | ||||
| $colorLocalControlOvrBg: rgba($colorBodyBg, 0.8); | ||||
| $colorLocalControlOvrBg: rgba($colorBodyFg, 0.8); | ||||
|  | ||||
| // Menus | ||||
| $colorMenuBg: pushBack($colorBodyBg, 10%); | ||||
| @@ -224,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; | ||||
| @@ -242,8 +247,8 @@ $colorInputPlaceholder: pushBack($colorBodyFg, 20%); | ||||
| $colorFormText: pushBack($colorBodyFg, 10%); | ||||
| $colorInputIcon: pushBack($colorBodyFg, 25%); | ||||
| $colorFieldHint: pullForward($colorBodyFg, 40%); | ||||
| $shdwInput: inset rgba(black, 0.7) 0 0 1px; | ||||
| $shdwInputHov: inset rgba(black, 0.7) 0 0 2px; | ||||
| $shdwInput: inset rgba(black, 0.5) 0 0 2px; | ||||
| $shdwInputHov: inset rgba(black, 0.8) 0 0 2px; | ||||
| $shdwInputFoc: inset rgba(black, 0.8) 0 0.25px 3px; | ||||
| $formTBPad: $interiorMargin; | ||||
| $formLRPad: $interiorMargin; | ||||
| @@ -385,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); | ||||
|  | ||||
|   | ||||
| @@ -28,6 +28,7 @@ $dirImgs: 'images/'; | ||||
| $controlFadeMs: 100ms; | ||||
| $browseToEditAnimMs: 400ms; | ||||
| $editBorderPulseMs: 500ms; | ||||
| $moveBarOutDelay: 500ms; | ||||
|  | ||||
| /************************** SPATIAL */ | ||||
| $interiorMarginSm: 3px; | ||||
| @@ -84,6 +85,8 @@ $waitSpinnerTreeBorderW: 3px; | ||||
| /*************** Messages */ | ||||
| $messageIconD: 80px; | ||||
| $messageListIconD: 32px; | ||||
| /*************** Tables */ | ||||
| $tableResizeColHitareaD: 6px; | ||||
|  | ||||
| /************************** MOBILE */ | ||||
| $mobileMenuIconD: 24px; // Used | ||||
|   | ||||
| @@ -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(); | ||||
|     } | ||||
| @@ -721,6 +739,7 @@ input[type="range"] { | ||||
|     } | ||||
| } | ||||
|  | ||||
| /******************************************************** LOCAL CONTROLS */ | ||||
| .h-local-controls { | ||||
|     // Holder for local controls | ||||
|     &--horz { | ||||
|   | ||||
| @@ -482,6 +482,8 @@ body.mobile.phone { | ||||
|  | ||||
|     .widget-edit-holder { | ||||
|         display: flex; // Overrides `display: none` during Browse mode | ||||
|         flex: 1 1 auto; | ||||
|  | ||||
|         .flex-accordion-holder { | ||||
|             // Needed because otherwise accordion elements "creep" when contents expand and contract | ||||
|             display: block !important; | ||||
| @@ -497,12 +499,6 @@ body.mobile.phone { | ||||
|                 // Test data is expanded and rules are collapsed | ||||
|                 // Make text data take up all the vertical space | ||||
|                 .flex-accordion-holder { display: flex; } | ||||
|                 .widget-test-data { | ||||
|                     flex-grow: 999999; | ||||
|                 } | ||||
|                 .w-widget-test-data-items { | ||||
|                     max-height: inherit; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         &.expanded-widget-rules { | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -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 */ | ||||
|   | ||||
| @@ -53,6 +53,24 @@ table { | ||||
|     a { color: $colorBtnMajorBg; } | ||||
| } | ||||
|  | ||||
| .is-editing { | ||||
|     td.is-selectable { | ||||
|         &:hover { | ||||
|             background: rgba($editUIColorBg, 0.1); | ||||
|             box-shadow: inset rgba($editUIColorBg, 0.8) 0 0 0 1px; | ||||
|         } | ||||
|  | ||||
|         &[s-selected] { | ||||
|             background: $editUIColorBg !important; | ||||
|             border: 1px solid $editUIColorFg !important; | ||||
|             color: $editUIColorFg !important; | ||||
|             box-shadow: $editFrameSelectedShdw; | ||||
|             z-index: 2; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /******************************************************** C-TABLE */ | ||||
| div.c-table { | ||||
|     // When c-table is used as a wrapper element in more complex table views | ||||
|     height: 100%; | ||||
| @@ -76,7 +94,7 @@ div.c-table { | ||||
|     } | ||||
|  | ||||
|     thead tr, | ||||
|     [class*="__header"] { | ||||
|     &.c-table__headers { | ||||
|         background: $colorTabHeaderBg; | ||||
|  | ||||
|         th { | ||||
|   | ||||
| @@ -26,21 +26,19 @@ | ||||
|             'has-complex-content': complexContent | ||||
|         }"> | ||||
|         <div class="c-so-view__header"> | ||||
|             <div class="c-so-view__header__start"> | ||||
|                 <div class="c-so-view__header__name" | ||||
|                      :class="cssClass"> | ||||
|                     {{ domainObject && domainObject.name }} | ||||
|                 </div> | ||||
|                 <context-menu-drop-down | ||||
|             <div class="c-so-view__header__name" | ||||
|                  :class="cssClass"> | ||||
|                 {{ domainObject && domainObject.name }} | ||||
|             </div> | ||||
|             <context-menu-drop-down | ||||
|                     :object-path="objectPath"> | ||||
|                 </context-menu-drop-down> | ||||
|             </div> | ||||
|             <div class="c-so-view__header__end"> | ||||
|                 <div class="c-button icon-expand local-controls--hidden" | ||||
|                     title="View Large" | ||||
|                     @click="expand"> | ||||
|                 </div> | ||||
|             </div> | ||||
|             </context-menu-drop-down> | ||||
|         </div> | ||||
|         <div class="c-so-view__local-controls c-so-view__view-large h-local-controls c-local-controls--show-on-hover"> | ||||
|             <button class="c-button icon-expand" | ||||
|                  title="View Large" | ||||
|                  @click="expand"> | ||||
|             </button> | ||||
|         </div> | ||||
|         <object-view  | ||||
|             class="c-so-view__object-view" | ||||
| @@ -65,16 +63,6 @@ | ||||
|             align-items: center; | ||||
|             margin-bottom: $interiorMargin; | ||||
|  | ||||
|             &__start, | ||||
|             &__end { | ||||
|                 display: flex; | ||||
|                 flex: 1 1 auto; | ||||
|             } | ||||
|  | ||||
|             &__end { | ||||
|                 justify-content: flex-end; | ||||
|             } | ||||
|  | ||||
|             &__name { | ||||
|                 @include headerFont(1em); | ||||
|                 display: flex; | ||||
| @@ -84,8 +72,20 @@ | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         &--no-frame > .c-so-view__header { | ||||
|             display: none; | ||||
|         &:not(.c-so-view--no-frame) { | ||||
|             background: $colorBodyBg; | ||||
|             border: $browseFrameBorder; | ||||
|             padding: $interiorMargin; | ||||
|         } | ||||
|  | ||||
|         &--no-frame { | ||||
|             > .c-so-view__header { | ||||
|                 display: none; | ||||
|             } | ||||
|  | ||||
|             > .c-so-view__local-controls { | ||||
|                 top: $interiorMarginSm; right: $interiorMarginSm; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         &__name { | ||||
| @@ -101,9 +101,24 @@ | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         &__local-controls { | ||||
|             position: absolute; | ||||
|             top: $interiorMargin; right: $interiorMargin; | ||||
|             z-index: 2; | ||||
|         } | ||||
|  | ||||
|         &__view-large { | ||||
|             display: none; | ||||
|         } | ||||
|  | ||||
|         &.has-complex-content { | ||||
|             > .c-so-view__view-large { display: block; } | ||||
|         } | ||||
|  | ||||
|         /*************************** OBJECT VIEW */ | ||||
|         &__object-view { | ||||
|             flex: 1 1 auto; | ||||
|             height: 0; // Chrome 73 overflow bug fix | ||||
|             overflow: auto; | ||||
|  | ||||
|             .c-object-view { | ||||
| @@ -128,7 +143,9 @@ | ||||
|  | ||||
|     const SIMPLE_CONTENT_TYPES = [ | ||||
|         'clock', | ||||
|         'summary-widget' | ||||
|         'timer', | ||||
|         'summary-widget', | ||||
|         'hyperlink' | ||||
|     ]; | ||||
|  | ||||
|     export default { | ||||
|   | ||||
| @@ -59,7 +59,8 @@ export default { | ||||
|             default() { | ||||
|                 return []; | ||||
|             } | ||||
|         } | ||||
|         }, | ||||
|         navigateToPath: String | ||||
|     }, | ||||
|     data() { | ||||
|         return { | ||||
|   | ||||
| @@ -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(); | ||||
|             } | ||||
|   | ||||
| @@ -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)); | ||||
|                 } | ||||
|   | ||||
| @@ -13,7 +13,7 @@ import _ from 'lodash'; | ||||
|         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); | ||||
| @@ -24,12 +24,11 @@ import _ from 'lodash'; | ||||
|             } | ||||
|         }, | ||||
|         methods: { | ||||
|             updateSelection() { | ||||
|                 let selection = this.openmct.selection.get(); | ||||
|                  | ||||
|                 if (_.isEqual(this.selection[0], selection[0])) { | ||||
|             updateSelection(selection) { | ||||
|                 if (_.isEqual(this.selection, selection)) { | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 this.selection = selection; | ||||
|  | ||||
|                 if (this.selectedViews) { | ||||
| @@ -38,12 +37,14 @@ import _ from 'lodash'; | ||||
|                     }); | ||||
|                     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); | ||||
|                 }); | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -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 && !singleSelectNonObject"> | ||||
|         <li class="c-properties__row"> | ||||
|             <div class="c-properties__label">Title</div> | ||||
|             <div class="c-properties__value">{{ item.name }}</div> | ||||
| @@ -25,6 +25,8 @@ | ||||
|             <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 class="c-properties__row--span-all" v-if="singleSelectNonObject">No properties to display for this item</div> | ||||
| </div> | ||||
| </template> | ||||
|  | ||||
| @@ -35,7 +37,8 @@ export default { | ||||
|     inject: ['openmct'], | ||||
|     data() { | ||||
|         return { | ||||
|             domainObject: {} | ||||
|             domainObject: {}, | ||||
|             multiSelect: false | ||||
|         } | ||||
|     }, | ||||
|     computed: { | ||||
| @@ -79,6 +82,9 @@ export default { | ||||
|                         }, this.item) | ||||
|                     }; | ||||
|                 }); | ||||
|         }, | ||||
|         singleSelectNonObject() { | ||||
|             return !this.item.identifier && !this.multiSelect; | ||||
|         } | ||||
|     }, | ||||
|     mounted() { | ||||
| @@ -90,11 +96,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'; | ||||
|   | ||||
| @@ -119,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, true); | ||||
|                                 }); | ||||
|                                 dialog.dismiss(); | ||||
|                             } | ||||
|                         }, | ||||
| @@ -228,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); | ||||
|         } | ||||
|   | ||||
| @@ -348,7 +348,7 @@ | ||||
|             toggleHasToolbar(selection) { | ||||
|                 let structure = undefined; | ||||
|  | ||||
|                 if (!selection[0]) { | ||||
|                 if (!selection || !selection[0]) { | ||||
|                     structure = []; | ||||
|                 } else { | ||||
|                     structure = this.openmct.toolbars.get(selection); | ||||
|   | ||||
| @@ -211,7 +211,8 @@ | ||||
|                                 return { | ||||
|                                     id: this.openmct.objects.makeKeyString(c.identifier), | ||||
|                                     object: c, | ||||
|                                     objectPath: [c] | ||||
|                                     objectPath: [c], | ||||
|                                     navigateToParent: '/browse' | ||||
|                             }; | ||||
|                         }); | ||||
|                     }); | ||||
|   | ||||
| @@ -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; | ||||
|                 } | ||||
|             } | ||||
|   | ||||
| @@ -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() | ||||
|   | ||||
| @@ -63,7 +63,11 @@ | ||||
|         } | ||||
|  | ||||
|         &__object-view { | ||||
|             background: $colorBodyBg; | ||||
|             border: 1px solid $colorInteriorBorder; | ||||
|             flex: 1 1 auto; | ||||
|             overflow: auto; | ||||
|             padding: $interiorMargin; | ||||
|  | ||||
|             > div:not([class]) { | ||||
|                 // Target an immediate child div without a class and make it display: contents | ||||
|   | ||||
| @@ -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,78 @@ | ||||
|                         delete tracker.newObject; | ||||
|                     } | ||||
|                 }); | ||||
|                 this.toolbarUpdateScheduled = false; | ||||
|             }, | ||||
|             getValue(domainObject, toolbarItem) { | ||||
|                 let value = undefined; | ||||
|                 let applicableSelectedItems = toolbarItem.applicableSelectedItems; | ||||
|  | ||||
|                 if (!applicableSelectedItems && !toolbarItem.property) { | ||||
|                     return value; | ||||
|                 } | ||||
|  | ||||
|                 if (toolbarItem.formKeys) { | ||||
|                     value = this.getFormValue(domainObject, toolbarItem); | ||||
|                 } else { | ||||
|                     let values = []; | ||||
|  | ||||
|                     if (applicableSelectedItems) { | ||||
|                         applicableSelectedItems.forEach(selectionPath => { | ||||
|                             values.push(this.getPropertyValue(domainObject, toolbarItem, selectionPath)); | ||||
|                         }); | ||||
|                     } else { | ||||
|                         values.push(this.getPropertyValue(domainObject, toolbarItem)); | ||||
|                     } | ||||
|  | ||||
|                     // 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; | ||||
|             }, | ||||
|             getPropertyValue(domainObject, toolbarItem, selectionPath, formKey) { | ||||
|                 let property = this.getItemProperty(toolbarItem, selectionPath); | ||||
|  | ||||
|                 if (formKey) { | ||||
|                     property = property + "." + formKey; | ||||
|                 } | ||||
|  | ||||
|                 return _.get(domainObject, property); | ||||
|             }, | ||||
|             getFormValue(domainObject, toolbarItem) { | ||||
|                 let value = {}; | ||||
|                 let values = {}; | ||||
|  | ||||
|                 toolbarItem.formKeys.map(key => { | ||||
|                     value[key] = _.get(domainObject, this.getItemProperty(toolbarItem) + "." + key); | ||||
|                     values[key] = []; | ||||
|  | ||||
|                     if (toolbarItem.applicableSelectedItems) { | ||||
|                         toolbarItem.applicableSelectedItems.forEach(selectionPath => { | ||||
|                             values[key].push(this.getPropertyValue(domainObject, toolbarItem, selectionPath, key)); | ||||
|                         }); | ||||
|                     } else { | ||||
|                         values[key].push(this.getPropertyValue(domainObject, toolbarItem, undefined, 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 +193,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,14 +212,35 @@ | ||||
|                     this.structure.map(s => { | ||||
|                         if (s.formKeys) { | ||||
|                             s.formKeys.forEach(key => { | ||||
|                                 this.openmct.objects.mutate(item.domainObject, this.getItemProperty(item) + "." + key, value[key]); | ||||
|                                 if (item.applicableSelectedItems) { | ||||
|                                     item.applicableSelectedItems.forEach(selectionPath => { | ||||
|                                         this.mutateObject(item, value[key], selectionPath, key); | ||||
|                                     }); | ||||
|                                 } else { | ||||
|                                     this.mutateObject(item, value[key], undefined, key); | ||||
|                                 } | ||||
|                             }); | ||||
|                         } | ||||
|                     }); | ||||
|                 } else { | ||||
|                     this.openmct.objects.mutate(item.domainObject, this.getItemProperty(item), value); | ||||
|                     if (item.applicableSelectedItems) { | ||||
|                         item.applicableSelectedItems.forEach(selectionPath => { | ||||
|                             this.mutateObject(item, value, selectionPath); | ||||
|                         }); | ||||
|                     } else { | ||||
|                         this.mutateObject(item, value); | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             mutateObject(item, value, selectionPath, formKey) { | ||||
|                 let property = this.getItemProperty(item, selectionPath); | ||||
|  | ||||
|                 if (formKey) { | ||||
|                     property = property + "." + formKey; | ||||
|                 } | ||||
|  | ||||
|                 this.openmct.objects.mutate(item.domainObject, property, value); | ||||
|             }, | ||||
|             triggerMethod(item, event) { | ||||
|                 if (item.method) { | ||||
|                     item.method({...event}); | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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: { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user