mirror of
https://github.com/nasa/openmct.git
synced 2025-06-26 19:12:02 +00:00
Compare commits
35 Commits
couchdb-ob
...
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