Compare commits
	
		
			35 Commits
		
	
	
		
			es6-router
			...
			imagery-vi
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | d0bdd1f1b6 | ||
|   | 89e464431b | ||
|   | 6cd0e70b93 | ||
|   | 25598663f0 | ||
|   | 499bb77bc8 | ||
|   | 66719bd182 | ||
|   | 24d19cedbf | ||
|   | 61d5d05045 | ||
|   | bea52b8a4c | ||
|   | 61de8c0bfb | ||
|   | edaee545b9 | ||
|   | d44f05d117 | ||
|   | a82b7abc08 | ||
|   | c9988148f2 | ||
|   | 0909f7c6db | ||
|   | 2ad4af71c6 | ||
|   | a81df8dae8 | ||
|   | d4dc75d5ae | ||
|   | 92bdc56d46 | ||
|   | 11fc87eecb | ||
|   | 5943393b23 | ||
|   | 0085616189 | ||
|   | d4b6cf70b5 | ||
|   | 86984eb3e9 | ||
|   | 4cc4898c3c | ||
|   | f1570a2897 | ||
|   | 300eac03ba | ||
|   | 3f5b937873 | ||
|   | 3407c3bb7a | ||
|   | 1812b77826 | ||
|   | 3cfe3255f3 | ||
|   | a35b15f535 | ||
|   | 153cf6095a | ||
|   | 3d853b1c6e | ||
|   | 0d0ec298ab | 
| @@ -135,6 +135,20 @@ define([ | ||||
|                                 name: 'Image', | ||||
|                                 key: 'url', | ||||
|                                 format: 'image', | ||||
|                                 layers: [ | ||||
|                                     { | ||||
|                                         source: 'dist/imagery/example-imagery-layer-16x9.png', | ||||
|                                         name: '16:9' | ||||
|                                     }, | ||||
|                                     { | ||||
|                                         source: 'dist/imagery/example-imagery-layer-safe.png', | ||||
|                                         name: 'Safe' | ||||
|                                     }, | ||||
|                                     { | ||||
|                                         source: 'dist/imagery/example-imagery-layer-scale.png', | ||||
|                                         name: 'Scale' | ||||
|                                     } | ||||
|                                 ], | ||||
|                                 hints: { | ||||
|                                     image: 1 | ||||
|                                 } | ||||
|   | ||||
| @@ -82,11 +82,6 @@ | ||||
|             margin-left: $interiorMargin; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     .c-button, | ||||
|     .c-click-icon { | ||||
|         filter: $overlayBrightnessAdjust; | ||||
|     } | ||||
| } | ||||
|  | ||||
| body.desktop { | ||||
| @@ -139,4 +134,4 @@ body.desktop { | ||||
|             min-width: 20%; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| } | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								src/images/imagery/example-imagery-layer-16x9.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/images/imagery/example-imagery-layer-16x9.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 8.4 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/images/imagery/example-imagery-layer-safe.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/images/imagery/example-imagery-layer-safe.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 9.0 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/images/imagery/example-imagery-layer-scale.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/images/imagery/example-imagery-layer-scale.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 11 KiB | 
							
								
								
									
										47
									
								
								src/plugins/imagery/components/ImageryLayers.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/plugins/imagery/components/ImageryLayers.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | ||||
| <template> | ||||
| <div class="c-checkbox-menu js-checkbox-menu c-menu--to-left c-menu--has-close-btn"> | ||||
|     <ul | ||||
|         @click="$event.stopPropagation()" | ||||
|     > | ||||
|         <li | ||||
|             v-for="(layer, index) in layers" | ||||
|             :key="index" | ||||
|         > | ||||
|             <input v-if="layer.visible" | ||||
|                    :id="index + 'LayerControl'" | ||||
|                    checked | ||||
|                    type="checkbox" | ||||
|                    @change="toggleLayerVisibility(index)" | ||||
|             > | ||||
|             <input v-else | ||||
|                    :id="index + 'LayerControl'" | ||||
|                    type="checkbox" | ||||
|                    @change="toggleLayerVisibility(index)" | ||||
|             > | ||||
|             <label :for="index + 'LayerControl'">{{ layer.name }}</label> | ||||
|         </li> | ||||
|     </ul> | ||||
|  | ||||
|     <a class="s-icon-button icon-x t-btn-close c-switcher-menu__close-button"></a> | ||||
| </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|  | ||||
| export default { | ||||
|     inject: ['openmct'], | ||||
|     props: { | ||||
|         layers: { | ||||
|             type: Array, | ||||
|             default() { | ||||
|                 return [] | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|     methods: { | ||||
|         toggleLayerVisibility(index) { | ||||
|             this.$emit('toggleLayerVisibility', index); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| </script> | ||||
							
								
								
									
										62
									
								
								src/plugins/imagery/components/ImagerySettings.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								src/plugins/imagery/components/ImagerySettings.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | ||||
| <template> | ||||
| <div class="c-control-menu c-menu--to-left c-menu--has-close-btn c-image-controls"> | ||||
|     <div class="c-image-controls__controls" | ||||
|          @click="$event.stopPropagation()" | ||||
|     > | ||||
|         <span class="c-image-controls__sliders"> | ||||
|             <div class="c-image-controls__slider-wrapper icon-brightness"> | ||||
|                 <input v-model="filters.brightness" | ||||
|                        type="range" | ||||
|                        min="0" | ||||
|                        max="500" | ||||
|                        @change="notifyFiltersChanged" | ||||
|                        @input="notifyFiltersChanged" | ||||
|                 > | ||||
|             </div> | ||||
|             <div class="c-image-controls__slider-wrapper icon-contrast"> | ||||
|                 <input v-model="filters.contrast" | ||||
|                        type="range" | ||||
|                        min="0" | ||||
|                        max="500" | ||||
|                        @change="notifyFiltersChanged" | ||||
|                        @input="notifyFiltersChanged" | ||||
|                 > | ||||
|             </div> | ||||
|         </span> | ||||
|         <span class="holder flex-elem t-reset-btn-holder c-imagery__lc__reset-btn"> | ||||
|             <a class="s-icon-button icon-reset t-btn-reset" | ||||
|                @click="resetFilters" | ||||
|             ></a> | ||||
|         </span> | ||||
|     </div> | ||||
|  | ||||
|     <a class="s-icon-button icon-x t-btn-close c-switcher-menu__close-button"></a> | ||||
| </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|  | ||||
| export default { | ||||
|     inject: ['openmct'], | ||||
|     data() { | ||||
|         return { | ||||
|             filters: { | ||||
|                 brightness: 100, | ||||
|                 contrast: 100 | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|     methods: { | ||||
|         notifyFiltersChanged() { | ||||
|             this.$emit('filterChanged', this.filters); | ||||
|         }, | ||||
|         resetFilters() { | ||||
|             this.filters = { | ||||
|                 brightness: 100, | ||||
|                 contrast: 100 | ||||
|             }; | ||||
|             this.notifyFiltersChanged(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| </script> | ||||
| @@ -1,26 +1,20 @@ | ||||
| <template> | ||||
| <div class="c-imagery"> | ||||
|     <div class="c-imagery__main-image-wrapper has-local-controls"> | ||||
|         <div class="h-local-controls h-local-controls--overlay-content c-local-controls--show-on-hover l-flex-row c-imagery__lc"> | ||||
|             <span class="holder flex-elem grows c-imagery__lc__sliders"> | ||||
|                 <input v-model="filters.brightness" | ||||
|                        class="icon-brightness" | ||||
|                        type="range" | ||||
|                        min="0" | ||||
|                        max="500" | ||||
|                 > | ||||
|                 <input v-model="filters.contrast" | ||||
|                        class="icon-contrast" | ||||
|                        type="range" | ||||
|                        min="0" | ||||
|                        max="500" | ||||
|                 > | ||||
|             </span> | ||||
|             <span class="holder flex-elem t-reset-btn-holder c-imagery__lc__reset-btn"> | ||||
|                 <a class="s-icon-button icon-reset t-btn-reset" | ||||
|                    @click="filters={brightness: 100, contrast: 100}" | ||||
|                 ></a> | ||||
|             </span> | ||||
|         <div class="h-local-controls h-local-controls--horz h-local-controls--overlay-content c-local-controls--show-on-hover c-imagery__lc"> | ||||
|             <imagery-view-menu-switcher :icon-class="'icon-brightness'" | ||||
|                                         :title="'Filters menu'" | ||||
|             > | ||||
|                 <imagery-settings @filterChanged="updateFilterValues" /> | ||||
|             </imagery-view-menu-switcher> | ||||
|             <imagery-view-menu-switcher v-if="layers.length" | ||||
|                                         :icon-class="'icon-layers'" | ||||
|                                         :title="'Layers menu'" | ||||
|             > | ||||
|                 <imagery-layers :layers="layers" | ||||
|                                 @toggleLayerVisibility="toggleLayerVisibility" | ||||
|                 /> | ||||
|             </imagery-view-menu-switcher> | ||||
|         </div> | ||||
|         <div class="main-image s-image-main c-imagery__main-image" | ||||
|              :class="{'paused unnsynced': paused(),'stale':false }" | ||||
| @@ -28,6 +22,12 @@ | ||||
|                       'filter': `brightness(${filters.brightness}%) contrast(${filters.contrast}%)`}" | ||||
|         > | ||||
|         </div> | ||||
|         <div v-for="(layer, index) in visibleLayers" | ||||
|              :key="index" | ||||
|              class="layer-image s-image-layer c-imagery__layer-image" | ||||
|              :style="{'background-image': `url(${layer.source})`}" | ||||
|         > | ||||
|         </div> | ||||
|         <div class="c-imagery__control-bar"> | ||||
|             <div class="c-imagery__timestamp">{{ getTime() }}</div> | ||||
|             <div class="h-local-controls flex-elem"> | ||||
| @@ -60,9 +60,17 @@ | ||||
|  | ||||
| <script> | ||||
| import _ from 'lodash'; | ||||
| import ImagerySettings from "./ImagerySettings.vue"; | ||||
| import ImageryLayers from "./ImageryLayers.vue"; | ||||
| import ImageryViewMenuSwitcher from "./ImageryViewMenuSwitcher.vue"; | ||||
|  | ||||
| export default { | ||||
|     inject: ['openmct', 'domainObject'], | ||||
|     components: { | ||||
|         ImagerySettings, | ||||
|         ImageryLayers, | ||||
|         ImageryViewMenuSwitcher | ||||
|     }, | ||||
|     data() { | ||||
|         return { | ||||
|             autoScroll: true, | ||||
| @@ -79,20 +87,25 @@ export default { | ||||
|             isPaused: false, | ||||
|             metadata: {}, | ||||
|             requestCount: 0, | ||||
|             timeFormat: '' | ||||
|             timeFormat: '', | ||||
|             layers: [], | ||||
|             visibleLayers: [] | ||||
|         } | ||||
|     }, | ||||
|     mounted() { | ||||
|         // set | ||||
|         this.keystring = this.openmct.objects.makeKeyString(this.domainObject.identifier); | ||||
|         this.metadata = this.openmct.telemetry.getMetadata(this.domainObject); | ||||
|         this.imageFormat = this.openmct.telemetry.getValueFormatter(this.metadata.valuesForHints(['image'])[0]); | ||||
|         const metaDataValues = this.metadata.valuesForHints(['image'])[0]; | ||||
|         this.imageFormat = this.openmct.telemetry.getValueFormatter(metaDataValues); | ||||
|         this.loadVisibleLayers(metaDataValues); | ||||
|         // initialize | ||||
|         this.timeKey = this.openmct.time.timeSystem().key; | ||||
|         this.timeFormat = this.openmct.telemetry.getValueFormatter(this.metadata.value(this.timeKey)); | ||||
|         // listen | ||||
|         this.openmct.time.on('bounds', this.boundsChange); | ||||
|         this.openmct.time.on('timeSystem', this.timeSystemChange); | ||||
|         window.addEventListener('beforeunload', this.persistVisibleLayers); | ||||
|         // kickoff | ||||
|         this.subscribe(); | ||||
|         this.requestHistory(); | ||||
| @@ -101,12 +114,14 @@ export default { | ||||
|         this.scrollToRight(); | ||||
|     }, | ||||
|     beforeDestroy() { | ||||
|         this.persistVisibleLayers(); | ||||
|         if (this.unsubscribe) { | ||||
|             this.unsubscribe(); | ||||
|             delete this.unsubscribe; | ||||
|         } | ||||
|         this.openmct.time.off('bounds', this.boundsChange); | ||||
|         this.openmct.time.off('timeSystem', this.timeSystemChange); | ||||
|         window.removeEventListener('beforeunload', this.persistVisibleLayers); | ||||
|     }, | ||||
|     methods: { | ||||
|         datumIsNotValid(datum) { | ||||
| @@ -149,6 +164,27 @@ export default { | ||||
|                     || (scrollHeight - scrollTop) > 2 * clientHeight; | ||||
|             this.autoScroll = !disableScroll; | ||||
|         }, | ||||
|         loadVisibleLayers(metaDataValues) { | ||||
|             let layersMetadata = metaDataValues.layers; | ||||
|             if (layersMetadata) { | ||||
|                 this.layers = layersMetadata; | ||||
|                 if (this.domainObject.configuration) { | ||||
|                     let persistedLayers = this.domainObject.configuration.layers; | ||||
|                     layersMetadata.forEach((layer) => { | ||||
|                         const persistedLayer = persistedLayers.find((object) =>{ return object.name === layer.name; }); | ||||
|                         if (persistedLayer) { | ||||
|                             layer.visible = persistedLayer.visible === true; | ||||
|                         } | ||||
|                     }); | ||||
|                     this.visibleLayers = this.layers.filter((layer) => { return layer.visible === true; }); | ||||
|                 } else { | ||||
|                     this.visibleLayers = []; | ||||
|                     this.layers.forEach((layer) => { | ||||
|                         layer.visible = false; | ||||
|                     }) | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         paused(state) { | ||||
|             if (arguments.length > 0 && state !== this.isPaused) { | ||||
|                 this.unselectAllImages(); | ||||
| @@ -170,6 +206,13 @@ export default { | ||||
|  | ||||
|             return this.isPaused; | ||||
|         }, | ||||
|         persistVisibleLayers() { | ||||
|             if (this.domainObject.configuration) { | ||||
|                 this.openmct.objects.mutate(this.domainObject, 'configuration.layers', this.layers); | ||||
|             } | ||||
|             this.visibleLayers = []; | ||||
|             this.layers = []; | ||||
|         }, | ||||
|         scrollToRight() { | ||||
|             if (this.isPaused || !this.$refs.thumbsWrapper || !this.autoScroll) { | ||||
|                 return; | ||||
| @@ -256,6 +299,14 @@ export default { | ||||
|  | ||||
|             this.time = this.timeFormat.format(datum); | ||||
|             this.imageUrl = this.imageFormat.format(datum); | ||||
|         }, | ||||
|         updateFilterValues(filters) { | ||||
|             this.filters = filters; | ||||
|         }, | ||||
|         toggleLayerVisibility(index) { | ||||
|             let isVisible = this.layers[index].visible === true; | ||||
|             this.layers[index].visible = !isVisible; | ||||
|             this.visibleLayers = this.layers.filter((layer) => { return layer.visible; }); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										58
									
								
								src/plugins/imagery/components/ImageryViewMenuSwitcher.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								src/plugins/imagery/components/ImageryViewMenuSwitcher.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | ||||
| <template> | ||||
| <div class="c-switcher-menu"> | ||||
|     <button | ||||
|         class="c-button c-button--menu c-switcher-menu__button" | ||||
|         :class="iconClass" | ||||
|         :title="title" | ||||
|         @click.stop="toggleMenu" | ||||
|     > | ||||
|         <span class="c-button__label"></span> | ||||
|     </button> | ||||
|     <div | ||||
|         v-show="showMenu" | ||||
|         class="c-switcher-menu__content" | ||||
|     > | ||||
|         <slot></slot> | ||||
|     </div> | ||||
| </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|  | ||||
| export default { | ||||
|     inject: ['openmct'], | ||||
|     props: { | ||||
|         iconClass: { | ||||
|             type: String, | ||||
|             default() { | ||||
|                 return ''; | ||||
|             } | ||||
|         }, | ||||
|         title: { | ||||
|             type: String, | ||||
|             default() { | ||||
|                 return ''; | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|     data() { | ||||
|         return { | ||||
|             showMenu: false | ||||
|         } | ||||
|     }, | ||||
|     mounted() { | ||||
|         document.addEventListener('click', this.hideMenu); | ||||
|     }, | ||||
|     destroyed() { | ||||
|         document.removeEventListener('click', this.hideMenu); | ||||
|     }, | ||||
|     methods: { | ||||
|         toggleMenu() { | ||||
|             this.showMenu = !this.showMenu; | ||||
|         }, | ||||
|         hideMenu() { | ||||
|             this.showMenu = false; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| </script> | ||||
| @@ -14,7 +14,7 @@ | ||||
|         flex: 1 1 auto; | ||||
|     } | ||||
|  | ||||
|     &__main-image { | ||||
|     &__main-image, &__layer-image { | ||||
|         background-position: center; | ||||
|         background-repeat: no-repeat; | ||||
|         background-size: contain; | ||||
| @@ -95,34 +95,31 @@ | ||||
|     border: 1px solid transparent; | ||||
| } | ||||
|  | ||||
| .s-image-layer { | ||||
|     position: absolute; | ||||
|     height: 100%; | ||||
|     width: 100%; | ||||
|     opacity: 0.5; | ||||
| } | ||||
|  | ||||
| /*************************************** IMAGERY LOCAL CONTROLS*/ | ||||
| .c-imagery { | ||||
|     .h-local-controls--overlay-content { | ||||
|         // Outer holder, holds menu buttons | ||||
|         //@include test(); | ||||
|         position: absolute; | ||||
|         right: $interiorMargin; top: $interiorMargin; | ||||
|         z-index: 2; | ||||
|         background: $colorLocalControlOvrBg; | ||||
|         border-radius: $basicCr; | ||||
|         max-width: 200px; | ||||
|         min-width: 100px; | ||||
|         width: 35%; | ||||
|         align-items: center; | ||||
|         padding: $interiorMargin $interiorMarginLg; | ||||
|  | ||||
|         input[type="range"] { | ||||
|             display: block; | ||||
|             width: 100%; | ||||
|             &:not(:first-child) { | ||||
|                 margin-top: $interiorMarginLg; | ||||
|             } | ||||
|  | ||||
|             &:before { | ||||
|                 margin-right: $interiorMarginSm; | ||||
|             } | ||||
|         } | ||||
|         //background: $colorLocalControlOvrBg; | ||||
|         //border-radius: $basicCr; | ||||
|         //max-width: 200px; | ||||
|         //min-width: 100px; | ||||
|         //width: 35%; | ||||
|         //padding: $interiorMargin $interiorMarginLg; | ||||
|     } | ||||
|  | ||||
|     &__lc { | ||||
|         // Local Controls | ||||
|         &__reset-btn { | ||||
|             $bc: $scrollbarTrackColorBg; | ||||
|             &:before, | ||||
| @@ -145,6 +142,47 @@ | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     &__lc { | ||||
|         &__close-btn { | ||||
|             $bc: $scrollbarTrackColorBg; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| .c-image-controls { | ||||
|     // Brightness/contrast | ||||
|  | ||||
|     &__controls { | ||||
|         // Sliders and reset element | ||||
|         display: flex; | ||||
|         align-items: center; | ||||
|         margin-right: $interiorMargin; // Need some extra space due to proximity to close button | ||||
|     } | ||||
|  | ||||
|     &__sliders { | ||||
|         display: flex; | ||||
|         flex-direction: column; | ||||
|  | ||||
|         > * + * { | ||||
|             margin-top: 11px; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     &__slider-wrapper { | ||||
|         // A wrapper is needed to add the type icon to left of each range input | ||||
|         display: flex; | ||||
|         align-items: center; | ||||
|  | ||||
|         &:before { | ||||
|             color: rgba($colorMenuFg, 0.5); | ||||
|             margin-right: $interiorMarginSm; | ||||
|         } | ||||
|  | ||||
|         input[type='range'] { | ||||
|             width: 100px; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /*************************************** BUTTONS */ | ||||
|   | ||||
							
								
								
									
										380
									
								
								src/plugins/imagery/pluginSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										380
									
								
								src/plugins/imagery/pluginSpec.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,380 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2020, 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. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| import {createMouseEvent, createOpenMct} from "testUtils"; | ||||
| import ImageryPlugin from "./plugin"; | ||||
| import Vue from 'vue'; | ||||
|  | ||||
| describe('the plugin', function () { | ||||
|     let element; | ||||
|     let child; | ||||
|     let openmct; | ||||
|  | ||||
|     beforeEach((done) => { | ||||
|         openmct = createOpenMct(); | ||||
|         openmct.install(new ImageryPlugin()); | ||||
|  | ||||
|         element = document.createElement('div'); | ||||
|         child = document.createElement('div'); | ||||
|         element.appendChild(child); | ||||
|  | ||||
|         openmct.on('start', done); | ||||
|         openmct.startHeadless(); | ||||
|     }); | ||||
|  | ||||
|     it('provides an imagery view for telemetry with images', () => { | ||||
|         const testObject = { | ||||
|             id:"test-object", | ||||
|             type: "example.imagery", | ||||
|             telemetry: { | ||||
|                 values: [ | ||||
|                     { | ||||
|                         name: 'Name', | ||||
|                         key: 'name' | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'Time', | ||||
|                         key: 'utc', | ||||
|                         format: 'utc', | ||||
|                         hints: { | ||||
|                             domain: 1 | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'Image', | ||||
|                         key: 'url', | ||||
|                         format: 'image', | ||||
|                         layers: [ | ||||
|                             { | ||||
|                                 source: 'dist/images/bg-splash.jpg', | ||||
|                                 name: 'Big Splash' | ||||
|                             }, | ||||
|                             { | ||||
|                                 source: 'dist/images/logo-nasa.svg', | ||||
|                                 name: 'Nasa Logo' | ||||
|                             } | ||||
|                         ], | ||||
|                         hints: { | ||||
|                             image: 1 | ||||
|                         } | ||||
|                     } | ||||
|                 ] | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         const applicableViews = openmct.objectViews.get(testObject); | ||||
|         let imageryView = applicableViews.find((viewProvider) => viewProvider.key === 'example.imagery'); | ||||
|         expect(imageryView).toBeDefined(); | ||||
|     }); | ||||
|  | ||||
|     describe("The imagery view", () => { | ||||
|         let testTelemetryObject; | ||||
|         let applicableViews; | ||||
|         let imageryViewProvider; | ||||
|         let imageryView; | ||||
|  | ||||
|         beforeEach(() => { | ||||
|             testTelemetryObject = { | ||||
|                 identifier:{ namespace: "", key: "test-object"}, | ||||
|                 type: "test-object", | ||||
|                 name: "Test Object", | ||||
|                 telemetry: { | ||||
|                     values: [{ | ||||
|                         key: "some-key", | ||||
|                         name: "Some attribute", | ||||
|                         hints: { | ||||
|                             domain: 1 | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'Time', | ||||
|                         key: 'utc', | ||||
|                         format: 'utc', | ||||
|                         hints: { | ||||
|                             domain: 1 | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "some-other-key", | ||||
|                         name: "Another attribute", | ||||
|                         format: 'image', | ||||
|                         hints: { | ||||
|                             image: 1 | ||||
|                         }, | ||||
|                         layers: [ | ||||
|                             { | ||||
|                                 source: 'dist/imagery/example-imagery-layer-16x9.png', | ||||
|                                 name: '16:9' | ||||
|                             }, | ||||
|                             { | ||||
|                                 source: 'dist/imagery/example-imagery-layer-safe.png', | ||||
|                                 name: 'Safe' | ||||
|                             } | ||||
|                         ] | ||||
|                     }] | ||||
|                 } | ||||
|             }; | ||||
|             applicableViews = openmct.objectViews.get(testTelemetryObject); | ||||
|             imageryViewProvider = applicableViews.find((viewProvider) => viewProvider.key === 'example.imagery'); | ||||
|             imageryView = imageryViewProvider.view(testTelemetryObject, true, [testTelemetryObject]); | ||||
|             imageryView.show(child, true); | ||||
|             return Vue.nextTick(); | ||||
|         }); | ||||
|  | ||||
|         it("Renders an image filters menu button",() => { | ||||
|             let filtersMenuSwitcher = element.querySelectorAll('button.c-button--menu.icon-brightness'); | ||||
|             expect(filtersMenuSwitcher.length).toBe(1); | ||||
|             expect(filtersMenuSwitcher[0].title).toBe('Filters menu'); | ||||
|         }); | ||||
|  | ||||
|         it("Shows the filters controls",() => { | ||||
|             let filtersMenuSwitcher = element.querySelectorAll('button.c-button--menu.icon-brightness'); | ||||
|             expect(filtersMenuSwitcher.length).toBe(1); | ||||
|             expect(filtersMenuSwitcher[0].title).toBe('Filters menu'); | ||||
|             let event = createMouseEvent('click'); | ||||
|             filtersMenuSwitcher[0].dispatchEvent(event); | ||||
|             return Vue.nextTick().then(() => { | ||||
|                 let filtersMenu = element.querySelectorAll('button.c-button--menu.icon-brightness + .c-switcher-menu__content'); | ||||
|                 expect(filtersMenu.length).toBe(1); | ||||
|                 let filtersSliders = element.querySelectorAll('.c-image-controls__sliders'); | ||||
|                 expect(filtersSliders.length).toBe(1); | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         it("Shows the layers controls",() => { | ||||
|             let layersMenuSwitcher = element.querySelectorAll('button.c-button--menu.icon-layers'); | ||||
|             expect(layersMenuSwitcher.length).toBe(1); | ||||
|             expect(layersMenuSwitcher[0].title).toBe('Layers menu'); | ||||
|             let event = createMouseEvent('click'); | ||||
|             layersMenuSwitcher[0].dispatchEvent(event); | ||||
|             return Vue.nextTick().then(() => { | ||||
|                 let layersMenu = element.querySelectorAll('button.c-button--menu.icon-layers + .c-switcher-menu__content'); | ||||
|                 expect(layersMenu.length).toBe(1); | ||||
|                 let layers = element.querySelectorAll('.js-checkbox-menu li'); | ||||
|                 expect(layers.length).toBe(2); | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|     }); | ||||
|  | ||||
|     describe("The imagery view for creatable objects", () => { | ||||
|         let testCreatableImageryObject; | ||||
|         let applicableViews; | ||||
|         let imageryViewProvider; | ||||
|         let imageryView; | ||||
|  | ||||
|         beforeEach(() => { | ||||
|             testCreatableImageryObject = { | ||||
|                 identifier:{ namespace: "", key: "test-object"}, | ||||
|                 type: "example.imagery", | ||||
|                 name: "Test Object", | ||||
|                 configuration: { | ||||
|                     layers: [ | ||||
|                         { | ||||
|                             source: 'dist/imagery/example-imagery-layer-16x9.png', | ||||
|                             name: '16:9', | ||||
|                             visible: true | ||||
|                         }, | ||||
|                         { | ||||
|                             source: 'dist/imagery/example-imagery-layer-safe.png', | ||||
|                             name: 'Safe' | ||||
|                         } | ||||
|                     ] | ||||
|                 }, | ||||
|                 telemetry: { | ||||
|                     values: [{ | ||||
|                         key: "some-key", | ||||
|                         name: "Some attribute", | ||||
|                         hints: { | ||||
|                             domain: 1 | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'Time', | ||||
|                         key: 'utc', | ||||
|                         format: 'utc', | ||||
|                         hints: { | ||||
|                             domain: 1 | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "some-other-key", | ||||
|                         name: "Another attribute", | ||||
|                         format: 'image', | ||||
|                         hints: { | ||||
|                             image: 1 | ||||
|                         }, | ||||
|                         layers: [ | ||||
|                             { | ||||
|                                 source: 'dist/imagery/example-imagery-layer-16x9.png', | ||||
|                                 name: '16:9' | ||||
|                             }, | ||||
|                             { | ||||
|                                 source: 'dist/imagery/example-imagery-layer-safe.png', | ||||
|                                 name: 'Safe' | ||||
|                             } | ||||
|                         ] | ||||
|                     }] | ||||
|                 } | ||||
|             }; | ||||
|             applicableViews = openmct.objectViews.get(testCreatableImageryObject); | ||||
|             imageryViewProvider = applicableViews.find((viewProvider) => viewProvider.key === 'example.imagery'); | ||||
|             imageryView = imageryViewProvider.view(testCreatableImageryObject, true, [testCreatableImageryObject]); | ||||
|             imageryView.show(child, true); | ||||
|             return Vue.nextTick(); | ||||
|         }); | ||||
|  | ||||
|         it("shows previously visible layers",() => { | ||||
|             let layersMenuSwitcher = element.querySelectorAll('button.c-button--menu.icon-layers'); | ||||
|             expect(layersMenuSwitcher.length).toBe(1); | ||||
|             expect(layersMenuSwitcher[0].title).toBe('Layers menu'); | ||||
|             let event = createMouseEvent('click'); | ||||
|             layersMenuSwitcher[0].dispatchEvent(event); | ||||
|             return Vue.nextTick().then(() => { | ||||
|                 let layersMenu = element.querySelectorAll('button.c-button--menu.icon-layers + .c-switcher-menu__content'); | ||||
|                 expect(layersMenu.length).toBe(1); | ||||
|                 let layers = element.querySelectorAll('.js-checkbox-menu input[checked]'); | ||||
|                 expect(layers.length).toBe(1); | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         it("saves visible layers",() => { | ||||
|             let layersMenuSwitcher = element.querySelectorAll('button.c-button--menu.icon-layers'); | ||||
|             expect(layersMenuSwitcher.length).toBe(1); | ||||
|             expect(layersMenuSwitcher[0].title).toBe('Layers menu'); | ||||
|             let event = createMouseEvent('click'); | ||||
|             layersMenuSwitcher[0].dispatchEvent(event); | ||||
|             return Vue.nextTick().then(() => { | ||||
|                 let layersMenu = element.querySelectorAll('button.c-button--menu.icon-layers + .c-switcher-menu__content'); | ||||
|                 expect(layersMenu.length).toBe(1); | ||||
|                 let checkedlayers = element.querySelectorAll('.js-checkbox-menu input[checked]'); | ||||
|                 expect(checkedlayers.length).toBe(1); | ||||
|                 let uncheckedLayers = element.querySelectorAll('.js-checkbox-menu input:not([checked])'); | ||||
|                 expect(uncheckedLayers.length).toBe(1); | ||||
|                 uncheckedLayers[0].dispatchEvent(event); | ||||
|                 return Vue.nextTick().then(() => { | ||||
|                     imageryView.destroy(); | ||||
|                     return Vue.nextTick().then(() => { | ||||
|                         const visibleLayers = testCreatableImageryObject.configuration.layers.filter((layer) => { | ||||
|                             return layer.visible === true; | ||||
|                         }); | ||||
|                         expect(visibleLayers.length).toBe(2); | ||||
|                     }); | ||||
|  | ||||
|                 }); | ||||
|             }); | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     describe("The imagery view for objects that are not creatable", () => { | ||||
|         let testImageryObject; | ||||
|         let applicableViews; | ||||
|         let imageryViewProvider; | ||||
|         let imageryView; | ||||
|  | ||||
|         beforeEach(() => { | ||||
|             testImageryObject = { | ||||
|                 identifier:{ namespace: "", key: "test-object"}, | ||||
|                 type: "example.imagery", | ||||
|                 name: "Test Object", | ||||
|                 telemetry: { | ||||
|                     values: [{ | ||||
|                         key: "some-key", | ||||
|                         name: "Some attribute", | ||||
|                         hints: { | ||||
|                             domain: 1 | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'Time', | ||||
|                         key: 'utc', | ||||
|                         format: 'utc', | ||||
|                         hints: { | ||||
|                             domain: 1 | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "some-other-key", | ||||
|                         name: "Another attribute", | ||||
|                         format: 'image', | ||||
|                         hints: { | ||||
|                             image: 1 | ||||
|                         }, | ||||
|                         layers: [ | ||||
|                             { | ||||
|                                 source: 'dist/imagery/example-imagery-layer-16x9.png', | ||||
|                                 name: '16:9' | ||||
|                             }, | ||||
|                             { | ||||
|                                 source: 'dist/imagery/example-imagery-layer-safe.png', | ||||
|                                 name: 'Safe' | ||||
|                             } | ||||
|                         ] | ||||
|                     }] | ||||
|                 } | ||||
|             }; | ||||
|             applicableViews = openmct.objectViews.get(testImageryObject); | ||||
|             imageryViewProvider = applicableViews.find((viewProvider) => viewProvider.key === 'example.imagery'); | ||||
|             imageryView = imageryViewProvider.view(testImageryObject, true, [testImageryObject]); | ||||
|             imageryView.show(child, true); | ||||
|             return Vue.nextTick(); | ||||
|         }); | ||||
|  | ||||
|         it("does not show previously visible layers on load",() => { | ||||
|             let layersMenuSwitcher = element.querySelectorAll('button.c-button--menu.icon-layers'); | ||||
|             expect(layersMenuSwitcher.length).toBe(1); | ||||
|             expect(layersMenuSwitcher[0].title).toBe('Layers menu'); | ||||
|             let event = createMouseEvent('click'); | ||||
|             layersMenuSwitcher[0].dispatchEvent(event); | ||||
|             return Vue.nextTick().then(() => { | ||||
|                 let layersMenu = element.querySelectorAll('button.c-button--menu.icon-layers + .c-switcher-menu__content'); | ||||
|                 expect(layersMenu.length).toBe(1); | ||||
|                 let layers = element.querySelectorAll('.js-checkbox-menu input[checked]'); | ||||
|                 expect(layers.length).toBe(0); | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         it("does not save visible layers",() => { | ||||
|             let layersMenuSwitcher = element.querySelectorAll('button.c-button--menu.icon-layers'); | ||||
|             expect(layersMenuSwitcher.length).toBe(1); | ||||
|             expect(layersMenuSwitcher[0].title).toBe('Layers menu'); | ||||
|             let event = createMouseEvent('click'); | ||||
|             layersMenuSwitcher[0].dispatchEvent(event); | ||||
|             return Vue.nextTick().then(() => { | ||||
|                 let layersMenu = element.querySelectorAll('button.c-button--menu.icon-layers + .c-switcher-menu__content'); | ||||
|                 expect(layersMenu.length).toBe(1); | ||||
|                 let checkedLayers = element.querySelectorAll('.js-checkbox-menu input[checked]'); | ||||
|                 expect(checkedLayers.length).toBe(0); | ||||
|                 let uncheckedLayers = element.querySelectorAll('.js-checkbox-menu input:not([checked])'); | ||||
|                 expect(uncheckedLayers.length).toBe(2); | ||||
|                 uncheckedLayers[0].dispatchEvent(event); | ||||
|                 return Vue.nextTick().then(() => { | ||||
|                     imageryView.destroy(); | ||||
|                     return Vue.nextTick().then(() => { | ||||
|                         expect(testImageryObject.configuration).toBeUndefined(); | ||||
|                     }); | ||||
|  | ||||
|                 }); | ||||
|             }); | ||||
|         }); | ||||
|     }); | ||||
| }); | ||||
| @@ -225,9 +225,9 @@ $shdwSelect: rgba(black, 0.5) 0 0.5px 3px; | ||||
| $controlDisabledOpacity: 0.2; | ||||
|  | ||||
| // Menus | ||||
| $colorMenuBg: pullForward($colorBodyBg, 15%); | ||||
| $colorMenuFg: pullForward($colorBodyFg, 30%); | ||||
| $colorMenuIc: pullForward($colorKey, 15%); | ||||
| $colorMenuBg: $colorBodyBg; | ||||
| $colorMenuFg: $colorBodyFg; | ||||
| $colorMenuIc: $colorKey; | ||||
| $colorMenuHovBg: $colorMenuIc; | ||||
| $colorMenuHovFg: pullForward($colorMenuFg, 10%); | ||||
| $colorMenuHovIc: $colorMenuHovFg; | ||||
| @@ -235,6 +235,7 @@ $colorMenuElementHilite: pullForward($colorMenuBg, 10%); | ||||
| $shdwMenu: rgba(black, 0.5) 0 1px 5px; | ||||
| $shdwMenuText: none; | ||||
| $menuItemPad: $interiorMargin, floor($interiorMargin * 1.25); | ||||
| $menuFilter: brightness(1.3); | ||||
|  | ||||
| // Palettes and Swatches | ||||
| $paletteItemBorderOuterColorSelected: black; | ||||
| @@ -283,7 +284,6 @@ $overlayColorFg: $colorMenuFg; | ||||
| $colorOvrBtnBg: pullForward($overlayColorBg, 20%); | ||||
| $colorOvrBtnFg: #fff; | ||||
| $overlayCr: $interiorMarginLg; | ||||
| $overlayBrightnessAdjust: brightness(1.3); // Applied in a filter: property | ||||
|  | ||||
| // Indicator colors | ||||
| $colorIndicatorAvailable: $colorKey; | ||||
| @@ -371,8 +371,8 @@ $colorThumbHoverBg: $colorItemTreeHoverBg; | ||||
| // Scrollbar | ||||
| $scrollbarTrackSize: 7px; | ||||
| $scrollbarTrackShdw: rgba(#000, 0.2) 0 1px 2px; | ||||
| $scrollbarTrackColorBg: rgba(#000, 0.2); | ||||
| $scrollbarThumbColor: pushBack($colorBodyBg, 50%); | ||||
| $scrollbarTrackColorBg: rgba(#000, 0.4); | ||||
| $scrollbarThumbColor: pushBack($colorBodyFg, 30%); | ||||
| $scrollbarThumbColorHov: $colorKey; | ||||
| $scrollbarThumbColorMenu: pullForward($colorMenuBg, 10%); | ||||
| $scrollbarThumbColorMenuHov: pullForward($scrollbarThumbColorMenu, 2%); | ||||
|   | ||||
| @@ -229,9 +229,9 @@ $shdwSelect: rgba(black, 0.5) 0 0.5px 3px; | ||||
| $controlDisabledOpacity: 0.2; | ||||
|  | ||||
| // Menus | ||||
| $colorMenuBg: pullForward($colorBodyBg, 15%); | ||||
| $colorMenuFg: pullForward($colorBodyFg, 30%); | ||||
| $colorMenuIc: pullForward($colorKey, 15%); | ||||
| $colorMenuBg: $colorBodyBg; | ||||
| $colorMenuFg: $colorBodyFg; | ||||
| $colorMenuIc: $colorKey; | ||||
| $colorMenuHovBg: $colorMenuIc; | ||||
| $colorMenuHovFg: pullForward($colorMenuFg, 10%); | ||||
| $colorMenuHovIc: $colorMenuHovFg; | ||||
| @@ -239,6 +239,7 @@ $colorMenuElementHilite: pullForward($colorMenuBg, 10%); | ||||
| $shdwMenu: rgba(black, 0.5) 0 1px 5px; | ||||
| $shdwMenuText: none; | ||||
| $menuItemPad: $interiorMargin, floor($interiorMargin * 1.25); | ||||
| $menuFilter: brightness(1.3); | ||||
|  | ||||
| // Palettes and Swatches | ||||
| $paletteItemBorderOuterColorSelected: black; | ||||
| @@ -287,7 +288,6 @@ $overlayColorFg: $colorMenuFg; | ||||
| $colorOvrBtnBg: pullForward($overlayColorBg, 20%); | ||||
| $colorOvrBtnFg: #fff; | ||||
| $overlayCr: $interiorMarginLg; | ||||
| $overlayBrightnessAdjust: brightness(1.3); // Applied in a filter: property | ||||
|  | ||||
| // Indicator colors | ||||
| $colorIndicatorAvailable: $colorKey; | ||||
|   | ||||
| @@ -225,8 +225,8 @@ $shdwSelect: none; | ||||
| $controlDisabledOpacity: 0.3; | ||||
|  | ||||
| // Menus | ||||
| $colorMenuBg: pushBack($colorBodyBg, 10%); | ||||
| $colorMenuFg: pullForward($colorMenuBg, 70%); | ||||
| $colorMenuBg: $colorBodyBg; | ||||
| $colorMenuFg: $colorBodyFg; | ||||
| $colorMenuIc: $colorKey; | ||||
| $colorMenuHovBg: $colorMenuIc; | ||||
| $colorMenuHovFg: $colorMenuBg; | ||||
| @@ -235,6 +235,7 @@ $colorMenuElementHilite: darken($colorMenuBg, 10%); | ||||
| $shdwMenu: rgba(black, 0.5) 0 1px 5px; | ||||
| $shdwMenuText: none; | ||||
| $menuItemPad: $interiorMargin, floor($interiorMargin * 1.25); | ||||
| $menuFilter: brightness(1); | ||||
|  | ||||
| // Palettes and Swatches | ||||
| $paletteItemBorderOuterColorSelected: black; | ||||
| @@ -283,7 +284,6 @@ $overlayColorFg: $colorMenuFg; | ||||
| $colorOvrBtnBg: pullForward($overlayColorBg, 40%); | ||||
| $colorOvrBtnFg: #fff; | ||||
| $overlayCr: $interiorMarginLg; | ||||
| $overlayBrightnessAdjust: brightness(1); // Applied in a filter: property | ||||
|  | ||||
| // Indicator colors | ||||
| $colorIndicatorAvailable: $colorKey; | ||||
|   | ||||
| @@ -239,6 +239,7 @@ $glyph-icon-spectra-telemetry: '\eb25'; | ||||
| $glyph-icon-command: '\eb26'; | ||||
| $glyph-icon-conditional: '\eb27'; | ||||
| $glyph-icon-condition-widget: '\eb28'; | ||||
| $glyph-icon-image-telemetry: '\eb2a'; | ||||
|  | ||||
| /************************** GLYPHS AS DATA URI */ | ||||
| // Only objects have been converted, for use in Create menu and folder views | ||||
|   | ||||
| @@ -32,12 +32,25 @@ button { | ||||
| } | ||||
|  | ||||
| .c-button { | ||||
|  | ||||
|     &--menu { | ||||
|         &:after { | ||||
|             content: $glyph-icon-arrow-down; | ||||
|             font-family: symbolsfont; | ||||
|             opacity: 0.5; | ||||
|         } | ||||
|  | ||||
|         &_switcher { | ||||
|             margin-right: $interiorMarginSm; | ||||
|  | ||||
|             &__close-btn { | ||||
|                 position: absolute; | ||||
|                 top: $interiorMarginLg; | ||||
|                 right: 0; | ||||
|                 z-index: 3; | ||||
|                 padding-top: $interiorMarginSm; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     &--swatched { | ||||
| @@ -459,6 +472,7 @@ select { | ||||
| @mixin menuOuter() { | ||||
|     border-radius: $basicCr; | ||||
|     background: $colorMenuBg; | ||||
|     filter: $menuFilter; | ||||
|     text-shadow: $shdwMenuText; | ||||
|     padding: $interiorMarginSm; | ||||
|     box-shadow: $shdwMenu; | ||||
| @@ -499,11 +513,55 @@ select { | ||||
|     } | ||||
| } | ||||
|  | ||||
| .c-menu { | ||||
| .c-menu, | ||||
| .c-checkbox-menu, | ||||
| .c-control-menu { | ||||
|     @include menuOuter(); | ||||
|  | ||||
|     &--to-left { | ||||
|         left: auto; | ||||
|         right: 0; | ||||
|     } | ||||
|  | ||||
|     &--has-close-btn { | ||||
|         // Uses flex row layout | ||||
|         display: flex; | ||||
|         align-items: flex-start; | ||||
|  | ||||
|         > * + * { | ||||
|             margin-left: $interiorMargin; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| .c-menu { | ||||
|     @include menuInner(); | ||||
| } | ||||
|  | ||||
| .c-checkbox-menu, | ||||
| .c-control-menu { | ||||
|     padding: $interiorMarginLg; | ||||
| } | ||||
|  | ||||
| .c-checkbox-menu { | ||||
|     //@include menuInner(); | ||||
|     li { | ||||
|         color: $colorMenuFg; | ||||
|         cursor: pointer; | ||||
|         display: flex; | ||||
|         justify-content: start; | ||||
|         white-space: nowrap; | ||||
|  | ||||
|         > * + * { | ||||
|             margin-left: $interiorMarginSm; | ||||
|         } | ||||
|  | ||||
|         + li { | ||||
|              margin-top: $interiorMargin; | ||||
|          } | ||||
|     } | ||||
| } | ||||
|  | ||||
| .c-super-menu { | ||||
|     // Two column layout, menu items on left with detail of hover element on right | ||||
|     @include menuOuter(); | ||||
| @@ -837,20 +895,33 @@ select { | ||||
|  | ||||
|  | ||||
| /******************************************************** SLIDERS AND RANGE */ | ||||
| @mixin sliderKnobRound() { | ||||
|     $h: 12px; | ||||
| @mixin sliderKnobRound($h: 12px) { | ||||
|     @include themedButton(); | ||||
|     cursor: pointer; | ||||
|     width: $h; | ||||
|     height: $h; | ||||
|     border-radius: 50% !important; | ||||
|     transform: translateY(-42%); | ||||
| } | ||||
|  | ||||
| @mixin sliderTrack($bg: $scrollbarTrackColorBg, $knobH: 12px, $trackH: 3px) { | ||||
|     border-radius: 2px; | ||||
|     $breakPointPx: floor(($knobH - $trackH) / 2); | ||||
|     $bp1: $breakPointPx; | ||||
|     $bp2: $breakPointPx + $trackH; | ||||
|     box-sizing: border-box; | ||||
|     // For cross-browser compatibility, the track needs to be the same height as the knob. | ||||
|     height: $knobH; | ||||
|     // Gradient visually adds a horizontal line smaller than the knob | ||||
|     background: linear-gradient(0deg, rgba($bg,0) $bp1, $bg $bp1, $bg $bp2, rgba($bg,0) $bp2); | ||||
| } | ||||
|  | ||||
| input[type="range"] { | ||||
|     // HTML5 range inputs | ||||
|     $knobH: 11px; | ||||
|     $trackH: 3px; | ||||
|     -webkit-appearance: none; /* Hides the slider so that custom slider can be made */ | ||||
|     background: transparent; /* Otherwise white in Chrome */ | ||||
|  | ||||
|     &:focus { | ||||
|         outline: none; /* Removes the blue border. */ | ||||
|     } | ||||
| @@ -858,28 +929,26 @@ input[type="range"] { | ||||
|     // Thumb | ||||
|     &::-webkit-slider-thumb { | ||||
|         -webkit-appearance: none; | ||||
|         @include sliderKnobRound(); | ||||
|         @include sliderKnobRound($knobH); | ||||
|     } | ||||
|     &::-moz-range-thumb { | ||||
|         border: none; | ||||
|         @include sliderKnobRound(); | ||||
|         @include sliderKnobRound($knobH); | ||||
|     } | ||||
|     &::-ms-thumb { | ||||
|         border: none; | ||||
|         @include sliderKnobRound(); | ||||
|         @include sliderKnobRound($knobH); | ||||
|     } | ||||
|  | ||||
|     // Track | ||||
|     &::-webkit-slider-runnable-track { | ||||
|         width: 100%; | ||||
|         height: 3px; | ||||
|         @include sliderTrack(); | ||||
|         @include sliderTrack($knobH: $knobH, $trackH: $trackH); | ||||
|     } | ||||
|  | ||||
|     &::-moz-range-track { | ||||
|         width: 100%; | ||||
|         height: 3px; | ||||
|         @include sliderTrack(); | ||||
|         @include sliderTrack($knobH: $knobH, $trackH: $trackH); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -888,8 +957,11 @@ input[type="range"] { | ||||
|     // Holder for local controls | ||||
|     &--horz { | ||||
|         // Horizontal layout be | ||||
|         display: inline-flex; | ||||
|         align-items: center; | ||||
|         display: flex; | ||||
|  | ||||
|         > * + * { | ||||
|             margin-left: $interiorMarginSm; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     &--overlay-content { | ||||
|   | ||||
| @@ -175,6 +175,7 @@ | ||||
| .icon-command {  @include glyphBefore($glyph-icon-command); } | ||||
| .icon-conditional {  @include glyphBefore($glyph-icon-conditional); } | ||||
| .icon-condition-widget {  @include glyphBefore($glyph-icon-condition-widget); } | ||||
| .icon-image-telemetry {  @include glyphBefore($glyph-icon-image-telemetry); } | ||||
|  | ||||
| /************************** 12 PX CLASSES */ | ||||
| // TODO: sync with 16px redo as of 10/25/18 | ||||
|   | ||||
| @@ -219,12 +219,6 @@ | ||||
|     background-repeat: $repeatDir; | ||||
| } | ||||
|  | ||||
| @mixin sliderTrack($bg: $scrollbarTrackColorBg) { | ||||
|     border-radius: 2px; | ||||
|     box-sizing: border-box; | ||||
|     background-color: $bg; | ||||
| } | ||||
|  | ||||
| @mixin triangle($dir: "left", $size: 5px, $ratio: 1, $color: red) { | ||||
|     width: 0; | ||||
|     height: 0; | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|   "metadata": { | ||||
|     "name": "Open MCT Symbols 16px", | ||||
|     "lastOpened": 0, | ||||
|     "created": 1585252107436 | ||||
|     "created": 1591394188485 | ||||
|   }, | ||||
|   "iconSets": [ | ||||
|     { | ||||
| @@ -1094,6 +1094,14 @@ | ||||
|           "prevSize": 24, | ||||
|           "code": 60200, | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 180, | ||||
|           "id": 155, | ||||
|           "name": "icon-image-telemetry", | ||||
|           "prevSize": 24, | ||||
|           "code": 60202, | ||||
|           "tempChar": "" | ||||
|         } | ||||
|       ], | ||||
|       "id": 0, | ||||
| @@ -2890,6 +2898,23 @@ | ||||
|           "tags": [ | ||||
|             "icon-condition-widget" | ||||
|           ] | ||||
|         }, | ||||
|         { | ||||
|           "id": 155, | ||||
|           "paths": [ | ||||
|             "M512 0c-282.8 0-512 229.2-512 512s229.2 512 512 512 512-229.2 512-512-229.2-512-512-512zM783.6 783.6c-69.581 69.675-165.757 112.776-272 112.776-212.298 0-384.4-172.102-384.4-384.4s172.102-384.4 384.4-384.4c212.298 0 384.4 172.102 384.4 384.4 0 0.008-0 0.017-0 0.025l0-0.001c0.001 0.264 0.001 0.575 0.001 0.887 0 105.769-42.964 201.503-112.391 270.703l-0.010 0.010z", | ||||
|             "M704 384l-128 128-192-192-192 192c0 176.731 143.269 320 320 320s320-143.269 320-320v0z" | ||||
|           ], | ||||
|           "attrs": [ | ||||
|             {}, | ||||
|             {} | ||||
|           ], | ||||
|           "isMulticolor": false, | ||||
|           "isMulticolor2": false, | ||||
|           "grid": 16, | ||||
|           "tags": [ | ||||
|             "icon-image-telemetry" | ||||
|           ] | ||||
|         } | ||||
|       ], | ||||
|       "invisible": false, | ||||
|   | ||||
| @@ -143,4 +143,5 @@ | ||||
| <glyph unicode="" glyph-name="icon-pushbutton" d="M370.2 372.6c9.326-8.53 19.666-16.261 30.729-22.914l0.871-0.486c-11.077 19.209-17.664 42.221-17.8 66.76v0.040c0 39.6 17.8 77.6 50.2 107.4 37 34 87.4 52.6 141.8 52.6 40.2 0 78.2-10.2 110.2-29.2-8.918 15.653-19.693 29.040-32.268 40.482l-0.132 0.118c-37 34-87.4 52.6-141.8 52.6s-104.8-18.6-141.8-52.6c-32.4-29.8-50.2-67.8-50.2-107.4s17.8-77.6 50.2-107.4zM885.4 562.4c-40.6 154.6-192.4 269.6-373.4 269.6s-332.8-115-373.4-269.6c-86-80-138.6-187.8-138.6-306.4 0-247.4 229.2-448 512-448s512 200.6 512 448c0 118.6-52.6 226.4-138.6 306.4zM512 704c141.2 0 256-100.4 256-224s-114.8-224-256-224-256 100.4-256 224 114.8 224 256 224zM512 0c-175.4 0-318.4 127.8-320 285.4 68.8-94.8 186.4-157.4 320-157.4s251.2 62.6 320 157.4c-1.6-157.6-144.6-285.4-320-285.4z" /> | ||||
| <glyph unicode="" glyph-name="icon-conditional" d="M512 832c-282.76 0-512-229.24-512-512s229.24-512 512-512 512 229.24 512 512-229.24 512-512 512zM512 64l-384 256 384 256 384-256z" /> | ||||
| <glyph unicode="" glyph-name="icon-condition-widget" d="M832 832h-640c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h640c105.6 0 192 86.4 192 192v640c0 105.6-86.4 192-192 192zM512 64l-384 256 384 256 384-256z" /> | ||||
| <glyph unicode="" glyph-name="icon-image-telemetry" d="M512 832c-282.8 0-512-229.2-512-512s229.2-512 512-512 512 229.2 512 512-229.2 512-512 512zM783.6 48.4c-69.581-69.675-165.757-112.776-272-112.776-212.298 0-384.4 172.102-384.4 384.4s172.102 384.4 384.4 384.4c212.298 0 384.4-172.102 384.4-384.4 0-0.008 0-0.017 0-0.025v0.001c0.001-0.264 0.001-0.575 0.001-0.887 0-105.769-42.964-201.503-112.391-270.703l-0.010-0.010zM704 448l-128-128-192 192-192-192c0-176.731 143.269-320 320-320s320 143.269 320 320v0z" /> | ||||
| </font></defs></svg> | ||||
| Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 53 KiB | 
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -65,6 +65,10 @@ const webpackConfig = { | ||||
|                 from: 'src/images/favicons', | ||||
|                 to: 'favicons' | ||||
|             }, | ||||
|             { | ||||
|                 from: 'src/images/imagery', | ||||
|                 to: 'imagery' | ||||
|             }, | ||||
|             { | ||||
|                 from: './index.html', | ||||
|                 transform: function (content) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user