Compare commits
	
		
			16 Commits
		
	
	
		
			notebook-c
			...
			notebook-p
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | e0ce4832f7 | ||
|   | cc947de500 | ||
|   | 46701d8dd5 | ||
|   | adc2326429 | ||
|   | 97eded21da | ||
|   | 670795201f | ||
|   | cdcd4bb27e | ||
|   | 5be3d7c091 | ||
|   | 7fd83ea817 | ||
|   | dfb57327ed | ||
|   | 9819eba586 | ||
|   | ba84028fad | ||
|   | 000a9d36ef | ||
|   | c43518fb55 | ||
|   | f850c3c649 | ||
|   | 1f6476ec09 | 
| @@ -30,7 +30,6 @@ define([ | ||||
|     "./src/controllers/CompositeController", | ||||
|     "./src/controllers/ColorController", | ||||
|     "./src/controllers/DialogButtonController", | ||||
|     "./src/controllers/SnapshotPreviewController", | ||||
|     "./res/templates/controls/autocomplete.html", | ||||
|     "./res/templates/controls/checkbox.html", | ||||
|     "./res/templates/controls/datetime.html", | ||||
| @@ -44,8 +43,7 @@ define([ | ||||
|     "./res/templates/controls/menu-button.html", | ||||
|     "./res/templates/controls/dialog.html", | ||||
|     "./res/templates/controls/radio.html", | ||||
|     "./res/templates/controls/file-input.html", | ||||
|     "./res/templates/controls/snap-view.html" | ||||
|     "./res/templates/controls/file-input.html" | ||||
| ], function ( | ||||
|     MCTForm, | ||||
|     MCTControl, | ||||
| @@ -56,7 +54,6 @@ define([ | ||||
|     CompositeController, | ||||
|     ColorController, | ||||
|     DialogButtonController, | ||||
|     SnapshotPreviewController, | ||||
|     autocompleteTemplate, | ||||
|     checkboxTemplate, | ||||
|     datetimeTemplate, | ||||
| @@ -70,8 +67,7 @@ define([ | ||||
|     menuButtonTemplate, | ||||
|     dialogTemplate, | ||||
|     radioTemplate, | ||||
|     fileInputTemplate, | ||||
|     snapViewTemplate | ||||
|     fileInputTemplate | ||||
| ) { | ||||
|  | ||||
|     return { | ||||
| @@ -157,10 +153,6 @@ define([ | ||||
|                     { | ||||
|                         "key": "file-input", | ||||
|                         "template": fileInputTemplate | ||||
|                     }, | ||||
|                     { | ||||
|                         "key": "snap-view", | ||||
|                         "template": snapViewTemplate | ||||
|                     } | ||||
|                 ], | ||||
|                 "controllers": [ | ||||
| @@ -194,14 +186,6 @@ define([ | ||||
|                             "$scope", | ||||
|                             "dialogService" | ||||
|                         ] | ||||
|                     }, | ||||
|                     { | ||||
|                         "key": "SnapshotPreviewController", | ||||
|                         "implementation": SnapshotPreviewController, | ||||
|                         "depends": [ | ||||
|                             "$scope", | ||||
|                             "openmct" | ||||
|                         ] | ||||
|                     } | ||||
|                 ], | ||||
|                 "components": [ | ||||
|   | ||||
| @@ -1,36 +0,0 @@ | ||||
| <!-- | ||||
|  Open MCT, Copyright (c) 2014-2018, United States Government | ||||
|  as represented by the Administrator of the National Aeronautics and Space | ||||
|  Administration. All rights reserved. | ||||
|  | ||||
|  Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  "License"); you may not use this file except in compliance with the License. | ||||
|  You may obtain a copy of the License at | ||||
|  http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  | ||||
|  Unless required by applicable law or agreed to in writing, software | ||||
|  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  License for the specific language governing permissions and limitations | ||||
|  under the License. | ||||
|  | ||||
|  Open MCT includes source code licensed under additional open source | ||||
|  licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  this source code distribution or the Licensing information page available | ||||
|  at runtime from the About dialog for additional information. | ||||
| --> | ||||
| <span ng-controller="SnapshotPreviewController"  | ||||
|     class='form-control shell'> | ||||
|     <span class='field control {{structure.cssClass}}'> | ||||
|         <image  | ||||
|                class="c-ne__embed__snap-thumb" | ||||
|                src="{{imageUrl || structure.src}}" | ||||
|                ng-click="previewImage(imageUrl || structure.src)" | ||||
|                name="mctControl"> | ||||
|         </image> | ||||
|         <br> | ||||
|         <a title="Annotate" class="s-button icon-pencil" ng-click="annotateImage(ngModel, field, imageUrl || structure.src)"> | ||||
|             <span class="title-label">Annotate</span> | ||||
|         </a> | ||||
|     </span> | ||||
| </span> | ||||
| @@ -1,131 +0,0 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2018, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define( | ||||
|     [ | ||||
|         'painterro' | ||||
|     ], | ||||
|     function (Painterro) { | ||||
|  | ||||
|         function SnapshotPreviewController($scope, openmct) { | ||||
|  | ||||
|             $scope.previewImage = function (imageUrl) { | ||||
|                 let imageDiv = document.createElement('div'); | ||||
|                 imageDiv.classList = 'image-main s-image-main'; | ||||
|                 imageDiv.style.backgroundImage = `url(${imageUrl})`; | ||||
|  | ||||
|                 let previewImageOverlay = openmct.overlays.overlay( | ||||
|                     { | ||||
|                         element: imageDiv, | ||||
|                         size: 'large', | ||||
|                         buttons: [ | ||||
|                             { | ||||
|                                 label: 'Done', | ||||
|                                 callback: function () { | ||||
|                                     previewImageOverlay.dismiss(); | ||||
|                                 } | ||||
|                             } | ||||
|                         ] | ||||
|                     } | ||||
|                 ); | ||||
|             }; | ||||
|  | ||||
|             $scope.annotateImage = function (ngModel, field, imageUrl) { | ||||
|                 $scope.imageUrl = imageUrl; | ||||
|  | ||||
|                 let div = document.createElement('div'), | ||||
|                     painterroInstance = {}, | ||||
|                     save = false; | ||||
|  | ||||
|                 div.id = 'snap-annotation'; | ||||
|  | ||||
|                 let annotateImageOverlay = openmct.overlays.overlay( | ||||
|                     { | ||||
|                         element: div, | ||||
|                         size: 'large', | ||||
|                         buttons: [ | ||||
|                             { | ||||
|                                 label: 'Cancel', | ||||
|                                 callback: function () { | ||||
|                                     save = false; | ||||
|                                     painterroInstance.save(); | ||||
|                                     annotateImageOverlay.dismiss(); | ||||
|                                 } | ||||
|                             }, | ||||
|                             { | ||||
|                                 label: 'Save', | ||||
|                                 callback: function () { | ||||
|                                     save = true; | ||||
|                                     painterroInstance.save(); | ||||
|                                     annotateImageOverlay.dismiss(); | ||||
|                                 } | ||||
|                             } | ||||
|                         ] | ||||
|                     } | ||||
|                 ); | ||||
|  | ||||
|                 painterroInstance = Painterro({ | ||||
|                     id: 'snap-annotation', | ||||
|                     activeColor: '#ff0000', | ||||
|                     activeColorAlpha: 1.0, | ||||
|                     activeFillColor: '#fff', | ||||
|                     activeFillColorAlpha: 0.0, | ||||
|                     backgroundFillColor: '#000', | ||||
|                     backgroundFillColorAlpha: 0.0, | ||||
|                     defaultFontSize: 16, | ||||
|                     defaultLineWidth: 2, | ||||
|                     defaultTool: 'ellipse', | ||||
|                     hiddenTools: ['save', 'open', 'close', 'eraser', 'pixelize', 'rotate', 'settings', 'resize'], | ||||
|                     translation: { | ||||
|                         name: 'en', | ||||
|                         strings: { | ||||
|                             lineColor: 'Line', | ||||
|                             fillColor: 'Fill', | ||||
|                             lineWidth: 'Size', | ||||
|                             textColor: 'Color', | ||||
|                             fontSize: 'Size', | ||||
|                             fontStyle: 'Style' | ||||
|                         } | ||||
|                     }, | ||||
|                     saveHandler: function (image, done) { | ||||
|                         if (save) { | ||||
|                             let url = image.asBlob(), | ||||
|                                 reader = new window.FileReader(); | ||||
|  | ||||
|                             reader.readAsDataURL(url); | ||||
|                             reader.onloadend = function () { | ||||
|                                 $scope.imageUrl = reader.result; | ||||
|                                 ngModel[field] = reader.result; | ||||
|                             }; | ||||
|                         } else { | ||||
|                             ngModel.field = imageUrl; | ||||
|                             console.warn('You cancelled the annotation!!!'); | ||||
|                         } | ||||
|                         done(true); | ||||
|                     } | ||||
|                 }).show(imageUrl); | ||||
|             }; | ||||
|         } | ||||
|  | ||||
|         return SnapshotPreviewController; | ||||
|     } | ||||
| ); | ||||
							
								
								
									
										21
									
								
								src/plugins/notebook/components/menu-items.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/plugins/notebook/components/menu-items.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| <template> | ||||
| <div class="c-menu"> | ||||
|     <ul> | ||||
|         <li | ||||
|             v-for="(item, index) in popupMenuItems" | ||||
|             :key="index" | ||||
|             :class="item.cssClass" | ||||
|             :title="item.name" | ||||
|             @click="item.callback" | ||||
|         > | ||||
|             {{ item.name }} | ||||
|         </li> | ||||
|     </ul> | ||||
| </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| export default { | ||||
|     inject: ['popupMenuItems'] | ||||
| } | ||||
| </script> | ||||
| @@ -12,29 +12,12 @@ | ||||
|                :class="embed.cssClass" | ||||
|                @click="changeLocation" | ||||
|             >{{ embed.name }}</a> | ||||
|             <a class="c-ne__embed__context-available icon-arrow-down" | ||||
|                @click="toggleActionMenu" | ||||
|             ></a> | ||||
|         </div> | ||||
|         <div class="hide-menu hidden"> | ||||
|             <div class="menu-element context-menu-wrapper mobile-disable-select"> | ||||
|                 <div class="c-menu"> | ||||
|                     <ul> | ||||
|                         <li v-for="action in actions" | ||||
|                             :key="action.name" | ||||
|                             :class="action.cssClass" | ||||
|                             @click="action.perform(embed)" | ||||
|                         > | ||||
|                             {{ action.name }} | ||||
|                         </li> | ||||
|                     </ul> | ||||
|                 </div> | ||||
|             </div> | ||||
|             <PopupMenu :popup-menu-items="popupMenuItems" /> | ||||
|         </div> | ||||
|         <div v-if="embed.snapshot" | ||||
|              class="c-ne__embed__time" | ||||
|         > | ||||
|             {{ formatTime(embed.createdOn, 'YYYY-MM-DD HH:mm:ss') }} | ||||
|             {{ createdOn }} | ||||
|         </div> | ||||
|     </div> | ||||
| </div> | ||||
| @@ -42,15 +25,17 @@ | ||||
|  | ||||
| <script> | ||||
| import Moment from 'moment'; | ||||
| import PopupMenu from './popup-menu.vue'; | ||||
| import PreviewAction from '../../../ui/preview/PreviewAction'; | ||||
| import Painterro from 'painterro'; | ||||
| import PainterroInstance from '../utils/painterroInstance'; | ||||
| import RemoveDialog from '../utils/removeDialog'; | ||||
| import SnapshotTemplate from './snapshot-template.html'; | ||||
| import { togglePopupMenu } from '../utils/popup-menu'; | ||||
| import Vue from 'vue'; | ||||
|  | ||||
| export default { | ||||
|     inject: ['openmct'], | ||||
|     components: { | ||||
|         PopupMenu | ||||
|     }, | ||||
|     props: { | ||||
|         embed: { | ||||
| @@ -62,107 +47,75 @@ export default { | ||||
|         removeActionString: { | ||||
|             type: String, | ||||
|             default() { | ||||
|                 return 'Remove Embed'; | ||||
|                 return 'Remove This Embed'; | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|     data() { | ||||
|         return { | ||||
|             actions: [this.removeEmbedAction()], | ||||
|             agentService: this.openmct.$injector.get('agentService'), | ||||
|             popupService: this.openmct.$injector.get('popupService') | ||||
|             popupMenuItems: [] | ||||
|         } | ||||
|     }, | ||||
|     watch: { | ||||
|     computed: { | ||||
|         createdOn() { | ||||
|             return this.formatTime(this.embed.createdOn, 'YYYY-MM-DD HH:mm:ss'); | ||||
|         } | ||||
|     }, | ||||
|     beforeMount() { | ||||
|         this.populateActionMenu(); | ||||
|     mounted() { | ||||
|         this.addPopupMenuItems(); | ||||
|     }, | ||||
|     methods: { | ||||
|         annotateSnapshot() { | ||||
|             const self = this; | ||||
|         addPopupMenuItems() { | ||||
|             const removeEmbed = { | ||||
|                 cssClass: 'icon-trash', | ||||
|                 name: this.removeActionString, | ||||
|                 callback: this.getRemoveDialog.bind(this) | ||||
|             } | ||||
|             const preview = { | ||||
|                 cssClass: 'icon-eye-open', | ||||
|                 name: 'Preview', | ||||
|                 callback: this.previewEmbed.bind(this) | ||||
|             } | ||||
|  | ||||
|             let save = false; | ||||
|             let painterroInstance = {}; | ||||
|             this.popupMenuItems = [removeEmbed, preview]; | ||||
|         }, | ||||
|         annotateSnapshot() { | ||||
|             let painterroInstance = null; | ||||
|             const annotateVue = new Vue({ | ||||
|                 template: '<div id="snap-annotation"></div>' | ||||
|             }); | ||||
|  | ||||
|             let annotateOverlay = self.openmct.overlays.overlay({ | ||||
|             const annotateOverlay = this.openmct.overlays.overlay({ | ||||
|                 element: annotateVue.$mount().$el, | ||||
|                 size: 'large', | ||||
|                 dismissable: false, | ||||
|                 buttons: [ | ||||
|                     { | ||||
|                         label: 'Cancel', | ||||
|                         callback: function () { | ||||
|                             save = false; | ||||
|                             painterroInstance.save(); | ||||
|                         emphasis: true, | ||||
|                         callback: () => { | ||||
|                             painterroInstance.dismiss(); | ||||
|                             annotateOverlay.dismiss(); | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         label: 'Save', | ||||
|                         callback: function () { | ||||
|  | ||||
|                             save = true; | ||||
|                         callback: () => { | ||||
|                             painterroInstance.save(); | ||||
|                             annotateOverlay.dismiss(); | ||||
|                             this.snapshotOverlay.dismiss(); | ||||
|                             this.openSnapshot(); | ||||
|                         } | ||||
|                     } | ||||
|                 ], | ||||
|                 onDestroy: function () { | ||||
|                 onDestroy: () => { | ||||
|                     annotateVue.$destroy(true); | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|             painterroInstance = Painterro({ | ||||
|                 id: 'snap-annotation', | ||||
|                 activeColor: '#ff0000', | ||||
|                 activeColorAlpha: 1.0, | ||||
|                 activeFillColor: '#fff', | ||||
|                 activeFillColorAlpha: 0.0, | ||||
|                 backgroundFillColor: '#000', | ||||
|                 backgroundFillColorAlpha: 0.0, | ||||
|                 defaultFontSize: 16, | ||||
|                 defaultLineWidth: 2, | ||||
|                 defaultTool: 'ellipse', | ||||
|                 hiddenTools: ['save', 'open', 'close', 'eraser', 'pixelize', 'rotate', 'settings', 'resize'], | ||||
|                 translation: { | ||||
|                     name: 'en', | ||||
|                     strings: { | ||||
|                         lineColor: 'Line', | ||||
|                         fillColor: 'Fill', | ||||
|                         lineWidth: 'Size', | ||||
|                         textColor: 'Color', | ||||
|                         fontSize: 'Size', | ||||
|                         fontStyle: 'Style' | ||||
|                     } | ||||
|                 }, | ||||
|                 saveHandler: function (image, done) { | ||||
|                     if (save) { | ||||
|                         const url = image.asBlob(); | ||||
|                         const reader = new window.FileReader(); | ||||
|                         reader.readAsDataURL(url); | ||||
|                         reader.onloadend = function () { | ||||
|                             const snapshot = reader.result; | ||||
|                             const snapshotObject = { | ||||
|                                 src: snapshot, | ||||
|                                 type: url.type, | ||||
|                                 size: url.size, | ||||
|                                 modified: Date.now() | ||||
|                             }; | ||||
|  | ||||
|                             self.embed.snapshot = snapshotObject; | ||||
|                             self.updateEmbed(self.embed); | ||||
|                         }; | ||||
|                     } else { | ||||
|                         console.log('You cancelled the annotation!!!'); | ||||
|                     } | ||||
|  | ||||
|                     done(true); | ||||
|                 } | ||||
|             }).show(this.embed.snapshot.src); | ||||
|             painterroInstance = new PainterroInstance(); | ||||
|             painterroInstance.callback = this.updateSnapshot; | ||||
|             painterroInstance.show(this.embed.snapshot.src); | ||||
|         }, | ||||
|         changeLocation() { | ||||
|             this.openmct.time.stopClock(); | ||||
| @@ -172,9 +125,6 @@ export default { | ||||
|             }); | ||||
|  | ||||
|             const link = this.embed.historicLink; | ||||
|             if (!link) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             window.location.href = link; | ||||
|             const message = 'Time bounds changed to fixed timespan mode'; | ||||
| @@ -183,23 +133,30 @@ export default { | ||||
|         formatTime(unixTime, timeFormat) { | ||||
|             return Moment.utc(unixTime).format(timeFormat); | ||||
|         }, | ||||
|         getRemoveDialog() { | ||||
|             const options = { | ||||
|                 name: this.removeActionString, | ||||
|                 callback: this.removeEmbed.bind(this) | ||||
|             } | ||||
|             const removeDialog = new RemoveDialog(this.openmct, options); | ||||
|             removeDialog.show(); | ||||
|         }, | ||||
|         openSnapshot() { | ||||
|             const self = this; | ||||
|             const snapshot = new Vue({ | ||||
|                 data: () => { | ||||
|                     return { | ||||
|                         embed: self.embed | ||||
|                         createdOn: this.createdOn, | ||||
|                         embed: this.embed | ||||
|                     }; | ||||
|                 }, | ||||
|                 methods: { | ||||
|                     formatTime: self.formatTime, | ||||
|                     annotateSnapshot: self.annotateSnapshot | ||||
|                     annotateSnapshot: this.annotateSnapshot.bind(this) | ||||
|                 }, | ||||
|                 template: SnapshotTemplate | ||||
|             }); | ||||
|             }).$mount(); | ||||
|  | ||||
|             const snapshotOverlay = this.openmct.overlays.overlay({ | ||||
|                 element: snapshot.$mount().$el, | ||||
|             this.snapshotOverlay = this.openmct.overlays.overlay({ | ||||
|                 element: snapshot.$el, | ||||
|                 onDestroy: () => { snapshot.$destroy(true) }, | ||||
|                 size: 'large', | ||||
|                 dismissable: true, | ||||
| @@ -208,62 +165,30 @@ export default { | ||||
|                         label: 'Done', | ||||
|                         emphasis: true, | ||||
|                         callback: () => { | ||||
|                             snapshotOverlay.dismiss(); | ||||
|                             this.snapshotOverlay.dismiss(); | ||||
|                         } | ||||
|                     } | ||||
|                 ] | ||||
|             }); | ||||
|         }, | ||||
|         populateActionMenu() { | ||||
|         previewEmbed() { | ||||
|             const self = this; | ||||
|             const actions = [new PreviewAction(self.openmct)]; | ||||
|             const previewAction = new PreviewAction(self.openmct); | ||||
|             previewAction.invoke(JSON.parse(self.embed.objectPath)); | ||||
|         }, | ||||
|         removeEmbed(success) { | ||||
|             if (!success) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             actions.forEach((action) => { | ||||
|                 self.actions.push({ | ||||
|                     cssClass: action.cssClass, | ||||
|                     name: action.name, | ||||
|                     perform: () => { | ||||
|                         action.invoke(JSON.parse(self.embed.objectPath)); | ||||
|                     } | ||||
|                 }); | ||||
|             }); | ||||
|         }, | ||||
|         removeEmbed(id) { | ||||
|             this.$emit('removeEmbed', id); | ||||
|         }, | ||||
|         removeEmbedAction() { | ||||
|             const self = this; | ||||
|  | ||||
|             return { | ||||
|                 name: self.removeActionString, | ||||
|                 cssClass: 'icon-trash', | ||||
|                 perform: function (embed) { | ||||
|                     const dialog = self.openmct.overlays.dialog({ | ||||
|                         iconClass: "error", | ||||
|                         message: `This action will permanently ${self.removeActionString.toLowerCase()}. Do you wish to continue?`, | ||||
|                         buttons: [{ | ||||
|                             label: "No", | ||||
|                             callback: function () { | ||||
|                                 dialog.dismiss(); | ||||
|                             } | ||||
|                         }, | ||||
|                         { | ||||
|                             label: "Yes", | ||||
|                             emphasis: true, | ||||
|                             callback: function () { | ||||
|                                 dialog.dismiss(); | ||||
|                                 self.removeEmbed(embed.id); | ||||
|                             } | ||||
|                         }] | ||||
|                     }); | ||||
|                 } | ||||
|             }; | ||||
|         }, | ||||
|         toggleActionMenu(event) { | ||||
|             togglePopupMenu(event, this.openmct); | ||||
|             this.$emit('removeEmbed', this.embed.id); | ||||
|         }, | ||||
|         updateEmbed(embed) { | ||||
|             this.$emit('updateEmbed', embed); | ||||
|         }, | ||||
|         updateSnapshot(snapshotObject) { | ||||
|             this.embed.snapshot = snapshotObject; | ||||
|             this.updateEmbed(this.embed); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,13 +1,13 @@ | ||||
| <template> | ||||
| <div class="c-notebook__entry c-ne has-local-controls" | ||||
|      @dragover="dragover" | ||||
|      @drop.capture="dropCapture" | ||||
|      @drop.prevent="dropOnEntry(entry.id, $event)" | ||||
|      @dragover="changeCursor" | ||||
|      @drop.capture="cancelEditMode" | ||||
|      @drop.prevent="dropOnEntry" | ||||
| > | ||||
|     <div class="c-ne__time-and-content"> | ||||
|         <div class="c-ne__time"> | ||||
|             <span>{{ formatTime(entry.createdOn, 'YYYY-MM-DD') }}</span> | ||||
|             <span>{{ formatTime(entry.createdOn, 'HH:mm:ss') }}</span> | ||||
|             <span>{{ createdOnDate }}</span> | ||||
|             <span>{{ createdOnTime }}</span> | ||||
|         </div> | ||||
|         <div class="c-ne__content"> | ||||
|             <div :id="entry.id" | ||||
| @@ -15,8 +15,8 @@ | ||||
|                  :class="{'c-input-inline' : !readOnly }" | ||||
|                  :contenteditable="!readOnly" | ||||
|                  :style="!entry.text.length ? defaultEntryStyle : ''" | ||||
|                  @blur="textBlur($event, entry.id)" | ||||
|                  @focus="textFocus($event, entry.id)" | ||||
|                  @blur="updateEntryValue($event, entry.id)" | ||||
|                  @focus="updateCurrentEntryValue($event, entry.id)" | ||||
|             >{{ entry.text.length ? entry.text : defaultText }}</div> | ||||
|             <div class="c-snapshots c-ne__embeds"> | ||||
|                 <NotebookEmbed v-for="embed in entry.embeds" | ||||
| @@ -114,29 +114,32 @@ export default { | ||||
|             defaultText: 'add description' | ||||
|         } | ||||
|     }, | ||||
|     watch: { | ||||
|         entry() { | ||||
|     computed : { | ||||
|         createdOnDate() { | ||||
|             return this.formatTime(this.entry.createdOn, 'YYYY-MM-DD'); | ||||
|         }, | ||||
|         readOnly(readOnly) { | ||||
|         }, | ||||
|         selectedSection(selectedSection) { | ||||
|         }, | ||||
|         selectedPage(selectedSection) { | ||||
|         createdOnTime() { | ||||
|             return this.formatTime(this.entry.createdOn, 'HH:mm:ss'); | ||||
|         } | ||||
|     }, | ||||
|     mounted() { | ||||
|         this.updateEntries = this.updateEntries.bind(this); | ||||
|     }, | ||||
|     beforeDestory() { | ||||
|         this.dropOnEntry = this.dropOnEntry.bind(this); | ||||
|     }, | ||||
|     methods: { | ||||
|         cancelEditMode(event) { | ||||
|             const isEditing = this.openmct.editor.isEditing(); | ||||
|             if (isEditing) { | ||||
|                 this.openmct.editor.cancel(); | ||||
|             } | ||||
|         }, | ||||
|         changeCursor() { | ||||
|             event.preventDefault(); | ||||
|             event.dataTransfer.dropEffect = "copy"; | ||||
|         }, | ||||
|         deleteEntry() { | ||||
|             const self = this; | ||||
|             if (!self.domainObject || !self.selectedSection || !self.selectedPage || !self.entry.id) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             const entryPosById = this.entryPosById(this.entry.id); | ||||
|             const entryPosById = self.entryPosById(self.entry.id); | ||||
|             if (entryPosById === -1) { | ||||
|                 return; | ||||
|             } | ||||
| @@ -151,7 +154,7 @@ export default { | ||||
|                         callback: () => { | ||||
|                             const entries = getNotebookEntries(self.domainObject, self.selectedSection, self.selectedPage); | ||||
|                             entries.splice(entryPosById, 1); | ||||
|                             this.updateEntries(entries); | ||||
|                             self.updateEntries(entries); | ||||
|                             dialog.dismiss(); | ||||
|                         } | ||||
|                     }, | ||||
| @@ -164,24 +167,10 @@ export default { | ||||
|                 ] | ||||
|             }); | ||||
|         }, | ||||
|         dragover() { | ||||
|             event.preventDefault(); | ||||
|             event.dataTransfer.dropEffect = "copy"; | ||||
|         }, | ||||
|         dropCapture(event) { | ||||
|             const isEditing = this.openmct.editor.isEditing(); | ||||
|             if (isEditing) { | ||||
|                 this.openmct.editor.cancel(); | ||||
|             } | ||||
|         }, | ||||
|         dropOnEntry(entryId, $event) { | ||||
|         dropOnEntry($event) { | ||||
|             event.stopImmediatePropagation(); | ||||
|  | ||||
|             if (!this.domainObject || !this.selectedSection || !this.selectedPage) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             const snapshotId = $event.dataTransfer.getData('snapshot/id'); | ||||
|             const snapshotId = $event.dataTransfer.getData('openmct/snapshot/id'); | ||||
|             if (snapshotId.length) { | ||||
|                 this.moveSnapshot(snapshotId); | ||||
|  | ||||
| @@ -190,7 +179,7 @@ export default { | ||||
|  | ||||
|             const data = $event.dataTransfer.getData('openmct/domain-object-path'); | ||||
|             const objectPath = JSON.parse(data); | ||||
|             const entryPos = this.entryPosById(entryId); | ||||
|             const entryPos = this.entryPosById(this.entry.id); | ||||
|             const bounds = this.openmct.time.bounds(); | ||||
|             const snapshotMeta = { | ||||
|                 bounds, | ||||
| @@ -253,7 +242,44 @@ export default { | ||||
|             selection.removeAllRanges(); | ||||
|             selection.addRange(range); | ||||
|         }, | ||||
|         textBlur($event, entryId) { | ||||
|         updateCurrentEntryValue($event) { | ||||
|             if (this.readOnly) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             const target = $event.target | ||||
|             this.currentEntryValue = target ? target.innerText : ''; | ||||
|  | ||||
|             if (!this.entry.text.length) { | ||||
|                 this.selectTextInsideElement(target); | ||||
|             } | ||||
|         }, | ||||
|         updateEmbed(newEmbed) { | ||||
|             this.entry.embeds.some(e => { | ||||
|                 const found = (e.id === newEmbed.id); | ||||
|                 if (found) { | ||||
|                     e = newEmbed; | ||||
|                 } | ||||
|  | ||||
|                 return found; | ||||
|             }); | ||||
|  | ||||
|             this.updateEntry(this.entry); | ||||
|         }, | ||||
|         updateEntry(newEntry) { | ||||
|             const entries = getNotebookEntries(this.domainObject, this.selectedSection, this.selectedPage); | ||||
|             entries.some(entry => { | ||||
|                 const found = (entry.id === newEntry.id); | ||||
|                 if (found) { | ||||
|                     entry = newEntry; | ||||
|                 } | ||||
|  | ||||
|                 return found; | ||||
|             }); | ||||
|  | ||||
|             this.updateEntries(entries); | ||||
|         }, | ||||
|         updateEntryValue($event, entryId) { | ||||
|             if (!this.domainObject || !this.selectedSection || !this.selectedPage) { | ||||
|                 return; | ||||
|             } | ||||
| @@ -272,42 +298,6 @@ export default { | ||||
|                 this.updateEntries(entries); | ||||
|             } | ||||
|         }, | ||||
|         textFocus($event) { | ||||
|             if (this.readOnly || !this.domainObject || !this.selectedSection || !this.selectedPage) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             const target = $event.target | ||||
|             this.currentEntryValue = target ? target.innerText : ''; | ||||
|  | ||||
|             if (!this.entry.text.length) { | ||||
|                 this.selectTextInsideElement(target); | ||||
|             } | ||||
|         }, | ||||
|         updateEmbed(newEmbed) { | ||||
|             let embed = this.entry.embeds.find(e => e.id === newEmbed.id); | ||||
|  | ||||
|             if (!embed) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             embed = newEmbed; | ||||
|             this.updateEntry(this.entry); | ||||
|         }, | ||||
|         updateEntry(newEntry) { | ||||
|             if (!this.domainObject || !this.selectedSection || !this.selectedPage) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             const entries = getNotebookEntries(this.domainObject, this.selectedSection, this.selectedPage); | ||||
|             entries.forEach(entry => { | ||||
|                 if (entry.id === newEntry.id) { | ||||
|                     entry = newEntry; | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|             this.updateEntries(entries); | ||||
|         }, | ||||
|         updateEntries(entries) { | ||||
|             this.$emit('updateEntries', entries); | ||||
|         } | ||||
|   | ||||
| @@ -64,9 +64,7 @@ export default { | ||||
|             const defaultNotebook = getDefaultNotebook(); | ||||
|  | ||||
|             if (defaultNotebook) { | ||||
|                 const domainObject = await this.openmct.objects.get(defaultNotebook.notebookMeta.identifier) | ||||
|                     .then(d => d); | ||||
|  | ||||
|                 const domainObject = await this.openmct.objects.get(defaultNotebook.notebookMeta.identifier); | ||||
|                 if (!domainObject.location) { | ||||
|                     clearDefaultNotebook(); | ||||
|                 } else { | ||||
|   | ||||
| @@ -10,24 +10,9 @@ | ||||
|                     > {{ snapshots.length }} of {{ getNotebookSnapshotMaxCount() }} | ||||
|                     </span> | ||||
|                 </div> | ||||
|                 <a class="l-browse-bar__context-actions c-disclosure-button" | ||||
|                    @click="toggleActionMenu" | ||||
|                 ></a> | ||||
|                 <div class="hide-menu hidden"> | ||||
|                     <div class="menu-element context-menu-wrapper mobile-disable-select"> | ||||
|                         <div class="c-menu"> | ||||
|                             <ul> | ||||
|                                 <li v-for="action in actions" | ||||
|                                     :key="action.name" | ||||
|                                     :class="action.cssClass" | ||||
|                                     @click="action.perform()" | ||||
|                                 > | ||||
|                                     {{ action.name }} | ||||
|                                 </li> | ||||
|                             </ul> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </div> | ||||
|                 <PopupMenu v-if="snapshots.length > 0" | ||||
|                            :popup-menu-items="popupMenuItems" | ||||
|                 /> | ||||
|             </div> | ||||
|  | ||||
|         </div> | ||||
| @@ -46,7 +31,7 @@ | ||||
|             <NotebookEmbed ref="notebookEmbed" | ||||
|                            :key="snapshot.id" | ||||
|                            :embed="snapshot" | ||||
|                            :remove-action-string="'Delete Snapshot'" | ||||
|                            :remove-action-string="'Delete This Snapshot'" | ||||
|                            @updateEmbed="updateSnapshot" | ||||
|                            @removeEmbed="removeSnapshot" | ||||
|             /> | ||||
| @@ -62,14 +47,16 @@ | ||||
|  | ||||
| <script> | ||||
| import NotebookEmbed from './notebook-embed.vue'; | ||||
| import PopupMenu from './popup-menu.vue'; | ||||
| import RemoveDialog from '../utils/removeDialog'; | ||||
| import { NOTEBOOK_SNAPSHOT_MAX_COUNT } from '../snapshot-container'; | ||||
| import { EVENT_SNAPSHOTS_UPDATED } from '../notebook-constants'; | ||||
| import { togglePopupMenu } from '../utils/popup-menu'; | ||||
|  | ||||
| export default { | ||||
|     inject: ['openmct', 'snapshotContainer'], | ||||
|     components: { | ||||
|         NotebookEmbed | ||||
|         NotebookEmbed, | ||||
|         PopupMenu | ||||
|     }, | ||||
|     props: { | ||||
|         toggleSnapshot: { | ||||
| @@ -81,54 +68,45 @@ export default { | ||||
|     }, | ||||
|     data() { | ||||
|         return { | ||||
|             actions: [this.removeAllSnapshotAction()], | ||||
|             popupMenuItems: [], | ||||
|             removeActionString: 'Delete All Snapshots', | ||||
|             snapshots: [] | ||||
|         } | ||||
|     }, | ||||
|     mounted() { | ||||
|         this.addPopupMenuItems(); | ||||
|         this.snapshotContainer.on(EVENT_SNAPSHOTS_UPDATED, this.snapshotsUpdated); | ||||
|         this.snapshots = this.snapshotContainer.getSnapshots(); | ||||
|     }, | ||||
|     beforeDestory() { | ||||
|     }, | ||||
|     methods: { | ||||
|         addPopupMenuItems() { | ||||
|             const removeSnapshot = { | ||||
|                 cssClass: 'icon-trash', | ||||
|                 name: this.removeActionString, | ||||
|                 callback: this.getRemoveDialog.bind(this) | ||||
|             } | ||||
|  | ||||
|             this.popupMenuItems = [removeSnapshot]; | ||||
|         }, | ||||
|         close() { | ||||
|             this.toggleSnapshot(); | ||||
|         }, | ||||
|         getNotebookSnapshotMaxCount() { | ||||
|             return NOTEBOOK_SNAPSHOT_MAX_COUNT; | ||||
|         }, | ||||
|         removeAllSnapshotAction() { | ||||
|             const self = this; | ||||
|  | ||||
|             return { | ||||
|                 name: 'Delete All Snapshots', | ||||
|                 cssClass: 'icon-trash', | ||||
|                 perform: function (embed) { | ||||
|                     const dialog = self.openmct.overlays.dialog({ | ||||
|                         iconClass: "error", | ||||
|                         message: 'This action will delete all notebook snapshots. Do you want to continue?', | ||||
|                         buttons: [ | ||||
|                             { | ||||
|                                 label: "No", | ||||
|                                 callback: () => { | ||||
|                                     dialog.dismiss(); | ||||
|                                 } | ||||
|                             }, | ||||
|                             { | ||||
|                                 label: "Yes", | ||||
|                                 emphasis: true, | ||||
|                                 callback: () => { | ||||
|                                     self.removeAllSnapshots(); | ||||
|                                     dialog.dismiss(); | ||||
|                                 } | ||||
|                             } | ||||
|                         ] | ||||
|                     }); | ||||
|                 } | ||||
|             }; | ||||
|         getRemoveDialog() { | ||||
|             const options = { | ||||
|                 name: this.removeActionString, | ||||
|                 callback: this.removeAllSnapshots.bind(this) | ||||
|             } | ||||
|             const removeDialog = new RemoveDialog(this.openmct, options); | ||||
|             removeDialog.show(); | ||||
|         }, | ||||
|         removeAllSnapshots() { | ||||
|         removeAllSnapshots(success) { | ||||
|             if (!success) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             this.snapshotContainer.removeAllSnapshots(); | ||||
|         }, | ||||
|         removeSnapshot(id) { | ||||
| @@ -139,10 +117,7 @@ export default { | ||||
|         }, | ||||
|         startEmbedDrag(snapshot, event) { | ||||
|             event.dataTransfer.setData('text/plain', snapshot.id); | ||||
|             event.dataTransfer.setData('snapshot/id', snapshot.id); | ||||
|         }, | ||||
|         toggleActionMenu(event) { | ||||
|             togglePopupMenu(event, this.openmct); | ||||
|             event.dataTransfer.setData('openmct/snapshot/id', snapshot.id); | ||||
|         }, | ||||
|         updateSnapshot(snapshot) { | ||||
|             this.snapshotContainer.updateSnapshot(snapshot); | ||||
|   | ||||
| @@ -9,8 +9,10 @@ | ||||
|     </div> | ||||
|     <SearchResults v-if="search.length" | ||||
|                    ref="searchResults" | ||||
|                    :results="getSearchResults()" | ||||
|                    :domain-object="internalDomainObject" | ||||
|                    :results="searchedEntries" | ||||
|                    @changeSectionPage="changeSelectedSection" | ||||
|                    @updateEntries="updateEntries" | ||||
|     /> | ||||
|  | ||||
|     <div v-if="!search.length" | ||||
| @@ -139,6 +141,9 @@ export default { | ||||
|         pages() { | ||||
|             return this.getPages() || []; | ||||
|         }, | ||||
|         searchedEntries() { | ||||
|             return this.getSearchResults(); | ||||
|         }, | ||||
|         sections() { | ||||
|             return this.internalDomainObject.configuration.sections || []; | ||||
|         }, | ||||
| @@ -158,8 +163,6 @@ export default { | ||||
|             return this.sections.find(section => section.isSelected); | ||||
|         } | ||||
|     }, | ||||
|     watch: { | ||||
|     }, | ||||
|     beforeMount() { | ||||
|         this.throttledSearchItem = throttle(this.searchItem, 500); | ||||
|     }, | ||||
| @@ -244,7 +247,7 @@ export default { | ||||
|             event.preventDefault(); | ||||
|             event.stopImmediatePropagation(); | ||||
|  | ||||
|             const snapshotId = event.dataTransfer.getData('snapshot/id'); | ||||
|             const snapshotId = event.dataTransfer.getData('openmct/snapshot/id'); | ||||
|             if (snapshotId.length) { | ||||
|                 const snapshot = this.snapshotContainer.getSnapshot(snapshotId); | ||||
|                 this.newEntry(snapshot); | ||||
|   | ||||
| @@ -70,12 +70,6 @@ export default { | ||||
|         return { | ||||
|         } | ||||
|     }, | ||||
|     watch: { | ||||
|     }, | ||||
|     mounted() { | ||||
|     }, | ||||
|     destroyed() { | ||||
|     }, | ||||
|     methods: { | ||||
|         deletePage(id) { | ||||
|             const selectedSection = this.sections.find(s => s.isSelected); | ||||
|   | ||||
| @@ -9,32 +9,19 @@ | ||||
|           @keydown.enter="updateName" | ||||
|           @blur="updateName" | ||||
|     >{{ page.name.length ? page.name : `Unnamed ${pageTitle}` }}</span> | ||||
|     <a class="c-list__item__menu-indicator icon-arrow-down" | ||||
|        @click="toggleActionMenu" | ||||
|     ></a> | ||||
|     <div class="hide-menu hidden"> | ||||
|         <div class="menu-element context-menu-wrapper mobile-disable-select"> | ||||
|             <div class="c-menu"> | ||||
|                 <ul> | ||||
|                     <li v-for="action in actions" | ||||
|                         :key="action.name" | ||||
|                         :class="action.cssClass" | ||||
|                         @click="action.perform(page.id)" | ||||
|                     > | ||||
|                         {{ action.name }} | ||||
|                     </li> | ||||
|                 </ul> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
|     <PopupMenu :popup-menu-items="popupMenuItems" /> | ||||
| </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import { togglePopupMenu } from '../utils/popup-menu'; | ||||
| import PopupMenu from './popup-menu.vue'; | ||||
| import RemoveDialog from '../utils/removeDialog'; | ||||
|  | ||||
| export default { | ||||
|     inject: ['openmct'], | ||||
|     components: { | ||||
|         PopupMenu | ||||
|     }, | ||||
|     props: { | ||||
|         defaultPageId: { | ||||
|             type: String, | ||||
| @@ -55,7 +42,8 @@ export default { | ||||
|     }, | ||||
|     data() { | ||||
|         return { | ||||
|             actions: [this.deletePage()] | ||||
|             popupMenuItems: [], | ||||
|             removeActionString: `Delete ${this.pageTitle}` | ||||
|         } | ||||
|     }, | ||||
|     watch: { | ||||
| @@ -64,40 +52,35 @@ export default { | ||||
|         } | ||||
|     }, | ||||
|     mounted() { | ||||
|         this.addPopupMenuItems(); | ||||
|         this.toggleContentEditable(); | ||||
|     }, | ||||
|     destroyed() { | ||||
|     }, | ||||
|     methods: { | ||||
|         deletePage() { | ||||
|             const self = this; | ||||
|  | ||||
|             return { | ||||
|                 name: `Delete ${this.pageTitle}`, | ||||
|         addPopupMenuItems() { | ||||
|             const removePage = { | ||||
|                 cssClass: 'icon-trash', | ||||
|                 perform: function (id) { | ||||
|                     const dialog = self.openmct.overlays.dialog({ | ||||
|                         iconClass: "error", | ||||
|                         message: 'This action will delete this page and all of its entries. Do you want to continue?', | ||||
|                         buttons: [ | ||||
|                             { | ||||
|                                 label: "No", | ||||
|                                 callback: () => { | ||||
|                                     dialog.dismiss(); | ||||
|                                 } | ||||
|                             }, | ||||
|                             { | ||||
|                                 label: "Yes", | ||||
|                                 emphasis: true, | ||||
|                                 callback: () => { | ||||
|                                     self.$emit('deletePage', id); | ||||
|                                     dialog.dismiss(); | ||||
|                                 } | ||||
|                             } | ||||
|                         ] | ||||
|                     }); | ||||
|                 } | ||||
|             }; | ||||
|                 name: this.removeActionString, | ||||
|                 callback: this.getRemoveDialog.bind(this) | ||||
|             } | ||||
|  | ||||
|             this.popupMenuItems = [removePage]; | ||||
|         }, | ||||
|         deletePage(success) { | ||||
|             if (!success) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             this.$emit('deletePage', this.page.id); | ||||
|         }, | ||||
|         getRemoveDialog() { | ||||
|             const message = 'This action will delete this page and all of its entries. Do you want to continue?'; | ||||
|             const options = { | ||||
|                 name: this.removeActionString, | ||||
|                 callback: this.deletePage.bind(this), | ||||
|                 message | ||||
|             } | ||||
|             const removeDialog = new RemoveDialog(this.openmct, options); | ||||
|             removeDialog.show(); | ||||
|         }, | ||||
|         selectPage(event) { | ||||
|             const target = event.target; | ||||
| @@ -117,10 +100,6 @@ export default { | ||||
|  | ||||
|             this.$emit('selectPage', id); | ||||
|         }, | ||||
|         toggleActionMenu(event) { | ||||
|             event.preventDefault(); | ||||
|             togglePopupMenu(event, this.openmct); | ||||
|         }, | ||||
|         toggleContentEditable(page = this.page) { | ||||
|             const pageTitle = this.$el.querySelector('span'); | ||||
|             pageTitle.contentEditable = page.isSelected; | ||||
|   | ||||
							
								
								
									
										96
									
								
								src/plugins/notebook/components/popup-menu.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								src/plugins/notebook/components/popup-menu.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,96 @@ | ||||
| <template> | ||||
| <div class="l-browse-bar__view-switcher c-ctrl-wrapper c-ctrl-wrapper--menus-left"> | ||||
|     <button | ||||
|         class="l-browse-bar__context-actions c-disclosure-button" | ||||
|         title="popup menu" | ||||
|         @click="showMenuItems" | ||||
|     > | ||||
|         <span class="c-button__label"></span> | ||||
|     </button> | ||||
| </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import MenuItems from './menu-items.vue'; | ||||
| import Vue from 'vue'; | ||||
|  | ||||
| export default { | ||||
|     inject: ['openmct'], | ||||
|     props: { | ||||
|         domainObject: { | ||||
|             type: Object, | ||||
|             default() { | ||||
|                 return {}; | ||||
|             } | ||||
|         }, | ||||
|         popupMenuItems: { | ||||
|             type: Array, | ||||
|             default() { | ||||
|                 return []; | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|     data() { | ||||
|         return { | ||||
|             menuItems: null | ||||
|         } | ||||
|     }, | ||||
|     mounted() { | ||||
|     }, | ||||
|     methods: { | ||||
|         calculateMenuPosition(event, element) { | ||||
|             let eventPosX = event.clientX; | ||||
|             let eventPosY = event.clientY; | ||||
|  | ||||
|             let menuDimensions = element.getBoundingClientRect(); | ||||
|             let overflowX = (eventPosX + menuDimensions.width) - document.body.clientWidth; | ||||
|             let overflowY = (eventPosY + menuDimensions.height) - document.body.clientHeight; | ||||
|  | ||||
|             if (overflowX > 0) { | ||||
|                 eventPosX = eventPosX - overflowX; | ||||
|             } | ||||
|  | ||||
|             if (overflowY > 0) { | ||||
|                 eventPosY = eventPosY - overflowY; | ||||
|             } | ||||
|  | ||||
|             return { | ||||
|                 x: eventPosX, | ||||
|                 y: eventPosY | ||||
|             } | ||||
|         }, | ||||
|         hideMenuItems() { | ||||
|             document.body.removeChild(this.menuItems.$el); | ||||
|             this.menuItems.$destroy(); | ||||
|             this.menuItems = null; | ||||
|             document.removeEventListener('click', this.hideMenuItems); | ||||
|  | ||||
|             return; | ||||
|         }, | ||||
|         showMenuItems($event) { | ||||
|             const menuItems = new Vue({ | ||||
|                 components: { | ||||
|                     MenuItems | ||||
|                 }, | ||||
|                 provide: { | ||||
|                     popupMenuItems: this.popupMenuItems | ||||
|                 }, | ||||
|                 template: '<MenuItems />' | ||||
|             }); | ||||
|             this.menuItems = menuItems; | ||||
|  | ||||
|             menuItems.$mount(); | ||||
|             const element = this.menuItems.$el; | ||||
|             document.body.appendChild(element); | ||||
|  | ||||
|             const position = this.calculateMenuPosition($event, element); | ||||
|             element.style.left = `${position.x}px`; | ||||
|             element.style.top = `${position.y}px`; | ||||
|  | ||||
|             setTimeout(() => { | ||||
|                 document.addEventListener('click', this.hideMenuItems); | ||||
|             }, 0); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| </script> | ||||
| @@ -4,12 +4,14 @@ | ||||
|     <div class="c-notebook__entries"> | ||||
|         <NotebookEntry v-for="(result, index) in results" | ||||
|                        :key="index" | ||||
|                        :domain-object="domainObject" | ||||
|                        :result="result" | ||||
|                        :entry="result.entry" | ||||
|                        :read-only="true" | ||||
|                        :selected-page="null" | ||||
|                        :selected-section="null" | ||||
|                        :selected-page="result.page" | ||||
|                        :selected-section="result.section" | ||||
|                        @changeSectionPage="changeSectionPage" | ||||
|                        @updateEntries="updateEntries" | ||||
|         /> | ||||
|     </div> | ||||
| </div> | ||||
| @@ -19,11 +21,17 @@ | ||||
| import NotebookEntry from './notebook-entry.vue'; | ||||
|  | ||||
| export default { | ||||
|     inject: ['openmct', 'domainObject'], | ||||
|     inject: ['openmct', 'snapshotContainer'], | ||||
|     components: { | ||||
|         NotebookEntry | ||||
|     }, | ||||
|     props:{ | ||||
|         domainObject: { | ||||
|             type: Object, | ||||
|             default() { | ||||
|                 return {}; | ||||
|             } | ||||
|         }, | ||||
|         results: { | ||||
|             type: Array, | ||||
|             default() { | ||||
| @@ -31,19 +39,12 @@ export default { | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|     data() { | ||||
|         return {} | ||||
|     }, | ||||
|     watch: { | ||||
|         results(newResults) {} | ||||
|     }, | ||||
|     destroyed() { | ||||
|     }, | ||||
|     mounted() { | ||||
|     }, | ||||
|     methods: { | ||||
|         changeSectionPage(data) { | ||||
|             this.$emit('changeSectionPage', data); | ||||
|         }, | ||||
|         updateEntries(entries) { | ||||
|             this.$emit('updateEntries', entries); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -57,12 +57,6 @@ export default { | ||||
|         return { | ||||
|         } | ||||
|     }, | ||||
|     watch: { | ||||
|     }, | ||||
|     mounted() { | ||||
|     }, | ||||
|     destroyed() { | ||||
|     }, | ||||
|     methods: { | ||||
|         deleteSection(id) { | ||||
|             const section = this.sections.find(s => s.id === id); | ||||
|   | ||||
| @@ -9,24 +9,7 @@ | ||||
|           @keydown.enter="updateName" | ||||
|           @blur="updateName" | ||||
|     >{{ section.name.length ? section.name : `Unnamed ${sectionTitle}` }}</span> | ||||
|     <a class="c-list__item__menu-indicator icon-arrow-down" | ||||
|        @click="toggleActionMenu" | ||||
|     ></a> | ||||
|     <div class="hide-menu hidden"> | ||||
|         <div class="menu-element context-menu-wrapper mobile-disable-select"> | ||||
|             <div class="c-menu"> | ||||
|                 <ul> | ||||
|                     <li v-for="action in actions" | ||||
|                         :key="action.name" | ||||
|                         :class="action.cssClass" | ||||
|                         @click="action.perform(section.id)" | ||||
|                     > | ||||
|                         {{ action.name }} | ||||
|                     </li> | ||||
|                 </ul> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
|     <PopupMenu :popup-menu-items="popupMenuItems" /> | ||||
| </div> | ||||
| </template> | ||||
|  | ||||
| @@ -34,10 +17,14 @@ | ||||
| </style> | ||||
|  | ||||
| <script> | ||||
| import { togglePopupMenu } from '../utils/popup-menu'; | ||||
| import PopupMenu from './popup-menu.vue'; | ||||
| import RemoveDialog from '../utils/removeDialog'; | ||||
|  | ||||
| export default { | ||||
|     inject: ['openmct'], | ||||
|     components: { | ||||
|         PopupMenu | ||||
|     }, | ||||
|     props: { | ||||
|         defaultSectionId: { | ||||
|             type: String, | ||||
| @@ -58,7 +45,8 @@ export default { | ||||
|     }, | ||||
|     data() { | ||||
|         return { | ||||
|             actions: [this.deleteSectionAction()] | ||||
|             popupMenuItems: [], | ||||
|             removeActionString: `Delete ${this.sectionTitle}` | ||||
|         } | ||||
|     }, | ||||
|     watch: { | ||||
| @@ -67,40 +55,36 @@ export default { | ||||
|         } | ||||
|     }, | ||||
|     mounted() { | ||||
|         this.addPopupMenuItems(); | ||||
|         this.toggleContentEditable(); | ||||
|     }, | ||||
|     destroyed() { | ||||
|     }, | ||||
|     methods: { | ||||
|         deleteSectionAction() { | ||||
|             const self = this; | ||||
|  | ||||
|             return { | ||||
|                 name: `Delete ${this.sectionTitle}`, | ||||
|         addPopupMenuItems() { | ||||
|             const removeSection = { | ||||
|                 cssClass: 'icon-trash', | ||||
|                 perform: function (id) { | ||||
|                     const dialog = self.openmct.overlays.dialog({ | ||||
|                         iconClass: "error", | ||||
|                         message: 'This action will delete this section and all of its pages and entries. Do you want to continue?', | ||||
|                         buttons: [ | ||||
|                             { | ||||
|                                 label: "No", | ||||
|                                 callback: () => { | ||||
|                                     dialog.dismiss(); | ||||
|                                 } | ||||
|                             }, | ||||
|                             { | ||||
|                                 label: "Yes", | ||||
|                                 emphasis: true, | ||||
|                                 callback: () => { | ||||
|                                     self.$emit('deleteSection', id); | ||||
|                                     dialog.dismiss(); | ||||
|                                 } | ||||
|                             } | ||||
|                         ] | ||||
|                     }); | ||||
|                 } | ||||
|             }; | ||||
|                 name: this.removeActionString, | ||||
|                 callback: this.getRemoveDialog.bind(this) | ||||
|             } | ||||
|  | ||||
|             this.popupMenuItems = [removeSection]; | ||||
|         }, | ||||
|         deleteSection(success) { | ||||
|             if (!success) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             this.$emit('deleteSection', this.section.id); | ||||
|         }, | ||||
|         getRemoveDialog() { | ||||
|             const message = 'This action will delete this section and all of its pages and entries. Do you want to continue?'; | ||||
|             const options = { | ||||
|                 name: this.removeActionString, | ||||
|                 callback: this.deleteSection.bind(this), | ||||
|                 message | ||||
|             } | ||||
|  | ||||
|             const removeDialog = new RemoveDialog(this.openmct, options); | ||||
|             removeDialog.show(); | ||||
|         }, | ||||
|         selectSection(event) { | ||||
|             const target = event.target; | ||||
| @@ -121,9 +105,6 @@ export default { | ||||
|  | ||||
|             this.$emit('selectSection', id); | ||||
|         }, | ||||
|         toggleActionMenu(event) { | ||||
|             togglePopupMenu(event, this.openmct); | ||||
|         }, | ||||
|         toggleContentEditable(section = this.section) { | ||||
|             const sectionTitle = this.$el.querySelector('span'); | ||||
|             sectionTitle.contentEditable = section.isSelected; | ||||
|   | ||||
| @@ -139,8 +139,6 @@ export default { | ||||
|             this.addSection(); | ||||
|         } | ||||
|     }, | ||||
|     destroyed() { | ||||
|     }, | ||||
|     methods: { | ||||
|         addPage() { | ||||
|             const pageTitle = this.pageTitle; | ||||
|   | ||||
| @@ -13,7 +13,7 @@ | ||||
|  | ||||
|         <div class="l-browse-bar__end"> | ||||
|             <div class="l-browse-bar__snapshot-datetime"> | ||||
|                 SNAPSHOT {{formatTime(embed.createdOn, 'YYYY-MM-DD HH:mm:ss')}} | ||||
|                 SNAPSHOT {{ createdOn }} | ||||
|             </div> | ||||
|             <a class="l-browse-bar__annotate-button c-button icon-pencil" title="Annotate" @click="annotateSnapshot"> | ||||
|                 <span class="title-label">Annotate</span> | ||||
|   | ||||
							
								
								
									
										75
									
								
								src/plugins/notebook/utils/painterroInstance.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								src/plugins/notebook/utils/painterroInstance.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,75 @@ | ||||
| import Painterro from 'painterro'; | ||||
|  | ||||
| const defaultConfig = { | ||||
|     id: 'snap-annotation', | ||||
|     activeColor: '#ff0000', | ||||
|     activeColorAlpha: 1.0, | ||||
|     activeFillColor: '#fff', | ||||
|     activeFillColorAlpha: 0.0, | ||||
|     backgroundFillColor: '#000', | ||||
|     backgroundFillColorAlpha: 0.0, | ||||
|     defaultFontSize: 16, | ||||
|     defaultLineWidth: 2, | ||||
|     defaultTool: 'ellipse', | ||||
|     hiddenTools: ['save', 'open', 'close', 'eraser', 'pixelize', 'rotate', 'settings', 'resize'], | ||||
|     translation: { | ||||
|         name: 'en', | ||||
|         strings: { | ||||
|             lineColor: 'Line', | ||||
|             fillColor: 'Fill', | ||||
|             lineWidth: 'Size', | ||||
|             textColor: 'Color', | ||||
|             fontSize: 'Size', | ||||
|             fontStyle: 'Style' | ||||
|         } | ||||
|     } | ||||
| }; | ||||
|  | ||||
| export default class PainterroInstance { | ||||
|     constructor() { | ||||
|         this.callback = null; | ||||
|         this.config = Object.assign({}, defaultConfig); | ||||
|         this.config.id = this.config.id; | ||||
|         this.config.saveHandler = this.saveHandler.bind(this); | ||||
|         this.isSave = false; | ||||
|  | ||||
|         this.painterro = Painterro(this.config); | ||||
|         this.painterroInstance = null; | ||||
|     } | ||||
|  | ||||
|     dismiss() { | ||||
|         this.isSave = false; | ||||
|         this.painterroInstance.save(); | ||||
|     } | ||||
|  | ||||
|     save() { | ||||
|         this.isSave = true; | ||||
|         this.painterroInstance.save(); | ||||
|     } | ||||
|  | ||||
|     saveHandler(image, done) { | ||||
|         if (this.isSave) { | ||||
|             const self = this; | ||||
|             const url = image.asBlob(); | ||||
|             const reader = new window.FileReader(); | ||||
|             reader.readAsDataURL(url); | ||||
|             reader.onloadend = () => { | ||||
|                 const snapshot = reader.result; | ||||
|                 const snapshotObject = { | ||||
|                     src: snapshot, | ||||
|                     type: url.type, | ||||
|                     size: url.size, | ||||
|                     modified: Date.now() | ||||
|                 }; | ||||
|  | ||||
|                 self.callback(snapshotObject); | ||||
|             }; | ||||
|         } | ||||
|  | ||||
|         done(true); | ||||
|     } | ||||
|  | ||||
|     show(src) { | ||||
|         this.painterroInstance = this.painterro.show(src); | ||||
|     } | ||||
| } | ||||
| @@ -1,45 +0,0 @@ | ||||
| import $ from 'zepto'; | ||||
|  | ||||
| export const togglePopupMenu = (event, openmct) => { | ||||
|     event.preventDefault(); | ||||
|  | ||||
|     const body = $(document.body); | ||||
|     const container = $(event.target.parentElement.parentElement); | ||||
|     const classList = document.querySelector('body').classList; | ||||
|     const isPhone = Array.from(classList).includes('phone'); | ||||
|     const isTablet = Array.from(classList).includes('tablet'); | ||||
|  | ||||
|     const initiatingEvent = isPhone || isTablet | ||||
|         ? 'touchstart' | ||||
|         : 'mousedown'; | ||||
|     const menu = container.find('.menu-element'); | ||||
|     let dismissExistingMenu; | ||||
|  | ||||
|     function dismiss() { | ||||
|         container.find('.hide-menu').append(menu); | ||||
|         body.off(initiatingEvent, menuClickHandler); | ||||
|         dismissExistingMenu = undefined; | ||||
|     } | ||||
|  | ||||
|     function menuClickHandler(e) { | ||||
|         window.setTimeout(() => { | ||||
|             dismiss(); | ||||
|         }, 100); | ||||
|     } | ||||
|  | ||||
|     // Dismiss any menu which was already showing | ||||
|     if (dismissExistingMenu) { | ||||
|         dismissExistingMenu(); | ||||
|     } | ||||
|  | ||||
|     // ...and record the presence of this menu. | ||||
|     dismissExistingMenu = dismiss; | ||||
|  | ||||
|     const popupService = openmct.$injector.get('popupService'); | ||||
|     popupService.display(menu, [event.pageX,event.pageY], { | ||||
|         marginX: 0, | ||||
|         marginY: -50 | ||||
|     }); | ||||
|  | ||||
|     body.on(initiatingEvent, menuClickHandler); | ||||
| } | ||||
							
								
								
									
										36
									
								
								src/plugins/notebook/utils/removeDialog.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/plugins/notebook/utils/removeDialog.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| export default class RemoveDialog { | ||||
|     constructor(openmct, options) { | ||||
|         this.name = options.name; | ||||
|         this.openmct = openmct; | ||||
|  | ||||
|         this.callback = options.callback; | ||||
|         this.cssClass = options.cssClass || 'icon-trash'; | ||||
|         this.description = options.description || 'Remove action dialog'; | ||||
|         this.iconClass = "error"; | ||||
|         this.key = 'remove'; | ||||
|         this.message = options.message || `This action will permanently ${this.name.toLowerCase()}. Do you wish to continue?`; | ||||
|     } | ||||
|  | ||||
|     show() { | ||||
|         const dialog = this.openmct.overlays.dialog({ | ||||
|             iconClass: this.iconClass, | ||||
|             message: this.message, | ||||
|             buttons: [ | ||||
|                 { | ||||
|                     label: "Ok", | ||||
|                     callback: () => { | ||||
|                         this.callback(true); | ||||
|                         dialog.dismiss(); | ||||
|                     } | ||||
|                 }, | ||||
|                 { | ||||
|                     label: "Cancel", | ||||
|                     callback: () => { | ||||
|                         this.callback(false); | ||||
|                         dialog.dismiss(); | ||||
|                     } | ||||
|                 } | ||||
|             ] | ||||
|         }); | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user