Compare commits

...

35 Commits

Author SHA1 Message Date
d0bdd1f1b6 Range sliders fixed for Chrome and Firefox
- New cross-browser approach to range track / knob alignment;
- Markup mods to support "type" icons for each range;
- Moved $sliderTrack mixin from _mixins to _controls, modded for new
track / knob approach;
2020-06-09 17:28:41 -07:00
89e464431b Persist layers on page refresh since vue's destroy method is not called in this case 2020-06-08 10:23:26 -07:00
6cd0e70b93 Merge branch 'master' of https://github.com/nasa/openmct into imagery-view-layers 2020-06-08 10:07:05 -07:00
25598663f0 Styling for Imagery controls and layers menus
- Added new glyph 'icon-image-telemetry';
- Note that this may conflict with changes from branch
`display-layout-improvements-52820` - be sure to merge changes from
both!;
2020-06-05 15:02:35 -07:00
499bb77bc8 Reset layer visibility if object is not creatable 2020-06-05 14:11:06 -07:00
66719bd182 Add more tests for not creatable imagery objects 2020-06-05 13:50:34 -07:00
24d19cedbf Use domainObject configuration to identify if we should save layers 2020-06-05 13:28:30 -07:00
61d5d05045 Merge branch 'imagery-view-layers' of https://github.com/nasa/openmct into imagery-view-layers 2020-06-05 12:04:48 -07:00
bea52b8a4c Removing logging 2020-06-05 12:04:33 -07:00
61de8c0bfb Merge branch 'imagery-view-layers' of https://github.com/nasa/openmct into imagery-view-layers 2020-06-05 12:02:47 -07:00
edaee545b9 Styling for Imagery controls and layers menus WIP
- Fixed menu color constants, now using filter instead of colors;
- Fixed coloring in menus, unit tested in Snow and Espresso themes;
- Normalized and fixed overlay colors;
- Scrollbar colors tweaked for improved legibility;
2020-06-05 12:02:29 -07:00
d44f05d117 Updating logging 2020-06-05 11:59:39 -07:00
a82b7abc08 Adds some logging for debugging 2020-06-05 11:54:11 -07:00
c9988148f2 Fixes failing tests and linting issues 2020-06-05 11:49:35 -07:00
0909f7c6db Styling for Imagery controls and layers menus WIP
- Merge latest changes;
2020-06-04 15:13:24 -07:00
2ad4af71c6 Merge branch 'imagery-view-layers' of https://github.com/nasa/openmct into imagery-view-layers 2020-06-04 15:09:36 -07:00
a81df8dae8 Reset visible layers on destroy 2020-06-04 15:09:20 -07:00
d4dc75d5ae Styling for Imagery controls and layers menus WIP
- Synced spec.js file CSS classname refs with related markup, utilizing
js* class naming strategy;
2020-06-04 15:08:15 -07:00
92bdc56d46 Use object type to determine if the layers should be persisted. 2020-06-04 15:04:19 -07:00
11fc87eecb Styling for Imagery controls and layers menus WIP
- Merge latest and resolve conflicts;
2020-06-04 14:48:23 -07:00
5943393b23 Show only visible layers 2020-06-04 14:20:16 -07:00
0085616189 Fixes persisted layer matching 2020-06-04 14:16:23 -07:00
d4b6cf70b5 Persist layer visibility 2020-06-04 14:02:02 -07:00
86984eb3e9 Persist image layers for creatable domain objects 2020-06-04 10:37:17 -07:00
4cc4898c3c Styling for Imagery controls and layers menus WIP
- Significant mods to markup and CSS;
- Removed legacy CSS from markup;
2020-06-03 22:43:52 -07:00
f1570a2897 Merge branch 'master' of https://github.com/nasa/openmct into imagery-view-layers 2020-06-02 14:36:20 -07:00
300eac03ba Removes fdescribe and updates one test 2020-06-02 14:27:22 -07:00
3f5b937873 Add imagery for layers 2020-06-02 14:15:42 -07:00
3407c3bb7a Adds tests for image layers and filters menus 2020-06-02 14:06:14 -07:00
1812b77826 Adds new files that were missed in previous commit 2020-06-02 10:59:10 -07:00
3cfe3255f3 Merge branch 'imagery-view-layers' of https://github.com/nasa/openmct into imagery-view-layers 2020-06-02 10:58:27 -07:00
a35b15f535 Fixes layers metadata 2020-06-02 10:58:06 -07:00
153cf6095a Merge branch 'master' into imagery-view-layers 2020-06-01 12:29:16 -07:00
3d853b1c6e Merge branch 'master' into imagery-view-layers 2020-05-29 16:09:22 -07:00
0d0ec298ab Adds 3 new components: imagery-layers, imagery-settings and imagery-view-menu-switcher.
ImageViewLayout now shows the new menu switchers to display the filters menu and layers menu
For example imagery, the layers come from telemetry metadata (hardcoded to show images from the dist/images folder)
2020-05-29 16:04:43 -07:00
23 changed files with 824 additions and 81 deletions

View File

@ -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
}

View File

@ -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%;
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View 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>

View 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>

View File

@ -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; });
}
}
}

View 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>

View File

@ -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 */

View 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();
});
});
});
});
});
});

View File

@ -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%);

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -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;

View File

@ -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,

View File

@ -143,4 +143,5 @@
<glyph unicode="&#xeb26;" 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="&#xeb27;" 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="&#xeb28;" 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="&#xeb2a;" 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

View File

@ -65,6 +65,10 @@ const webpackConfig = {
from: 'src/images/favicons',
to: 'favicons'
},
{
from: 'src/images/imagery',
to: 'imagery'
},
{
from: './index.html',
transform: function (content) {