Compare commits
15 Commits
Updates-to
...
imagery-la
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d729616672 | ||
|
|
5f8513552f | ||
|
|
2b66df068c | ||
|
|
5ba2c0f0de | ||
|
|
0d04966519 | ||
|
|
9f44f53473 | ||
|
|
50482660ed | ||
|
|
4520e17a25 | ||
|
|
e5575cebb1 | ||
|
|
b17dc2e978 | ||
|
|
5009944b04 | ||
|
|
147eed6cf5 | ||
|
|
753c871db4 | ||
|
|
6b4a6f9e67 | ||
|
|
fa9f6180c9 |
@@ -59,7 +59,8 @@ export default function () {
|
||||
object.configuration = {
|
||||
imageLocation: '',
|
||||
imageLoadDelayInMilliSeconds: DEFAULT_IMAGE_LOAD_DELAY_IN_MILISECONDS,
|
||||
imageSamples: []
|
||||
imageSamples: [],
|
||||
layers: []
|
||||
};
|
||||
|
||||
object.telemetry = {
|
||||
@@ -90,7 +91,21 @@ export default function () {
|
||||
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'
|
||||
},
|
||||
{
|
||||
source: 'dist/imagery/example-imagery-layer-scale.png',
|
||||
name: 'Scale'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'Image Download Name',
|
||||
|
||||
@@ -25,8 +25,7 @@
|
||||
class="l-layout__frame c-frame"
|
||||
:class="{
|
||||
'no-frame': !item.hasFrame,
|
||||
'u-inspectable': inspectable,
|
||||
'is-in-small-container': size.width < 600 || size.height < 600
|
||||
'u-inspectable': inspectable
|
||||
}"
|
||||
:style="style"
|
||||
>
|
||||
|
||||
@@ -9,10 +9,6 @@
|
||||
> *:first-child {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
&.is-in-small-container {
|
||||
//background: rgba(blue, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.c-frame__move-bar {
|
||||
|
||||
@@ -14,7 +14,7 @@ $elemBg: rgba(black, 0.7);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
z-index: 5;
|
||||
@include userSelectNone;
|
||||
}
|
||||
|
||||
|
||||
74
src/plugins/imagery/components/FilterSettings.vue
Normal file
74
src/plugins/imagery/components/FilterSettings.vue
Normal file
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<div
|
||||
class="c-control-menu c-menu--to-left c-menu--has-close-btn c-image-controls c-image-controls--filters"
|
||||
@click="handleClose"
|
||||
>
|
||||
<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="c-image-controls__reset-btn">
|
||||
<a
|
||||
class="s-icon-button icon-reset t-btn-reset"
|
||||
@click="resetFilters"
|
||||
></a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<button class="c-click-icon icon-x t-btn-close c-switcher-menu__close-button"></button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
data() {
|
||||
return {
|
||||
filters: {
|
||||
brightness: 100,
|
||||
contrast: 100
|
||||
}
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
handleClose(e) {
|
||||
const closeButton = e.target.classList.contains('c-switcher-menu__close-button');
|
||||
if (!closeButton) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
},
|
||||
notifyFiltersChanged() {
|
||||
this.$emit('filterChanged', this.filters);
|
||||
},
|
||||
resetFilters() {
|
||||
this.filters = {
|
||||
brightness: 100,
|
||||
contrast: 100
|
||||
};
|
||||
this.notifyFiltersChanged();
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -21,75 +21,62 @@
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<div class="h-local-controls h-local-controls--overlay-content c-local-controls--show-on-hover c-image-controls__controls">
|
||||
<div class="c-image-controls__control c-image-controls__zoom icon-magnify">
|
||||
<div class="c-button-set c-button-set--strip-h">
|
||||
<button
|
||||
class="c-button t-btn-zoom-out icon-minus"
|
||||
title="Zoom out"
|
||||
@click="zoomOut"
|
||||
></button>
|
||||
<div class="h-local-controls h-local-controls--overlay-content h-local-controls--menus-aligned c-local-controls--show-on-hover">
|
||||
<imagery-view-menu-switcher
|
||||
:icon-class="'icon-brightness'"
|
||||
:title="'Brightness and contrast'"
|
||||
>
|
||||
<filter-settings @filterChanged="updateFilterValues" />
|
||||
</imagery-view-menu-switcher>
|
||||
|
||||
<button
|
||||
class="c-button t-btn-zoom-in icon-plus"
|
||||
title="Zoom in"
|
||||
@click="zoomIn"
|
||||
></button>
|
||||
</div>
|
||||
<imagery-view-menu-switcher
|
||||
v-if="layers.length"
|
||||
:icon-class="'icon-layers'"
|
||||
:title="'Layers'"
|
||||
>
|
||||
<layer-settings
|
||||
:layers="layers"
|
||||
@toggleLayerVisibility="toggleLayerVisibility"
|
||||
/>
|
||||
</imagery-view-menu-switcher>
|
||||
|
||||
<button
|
||||
class="c-button t-btn-zoom-lock"
|
||||
title="Lock current zoom and pan across all images"
|
||||
:class="{'icon-unlocked': !panZoomLocked, 'icon-lock': panZoomLocked}"
|
||||
@click="toggleZoomLock"
|
||||
></button>
|
||||
<zoom-settings
|
||||
class="--hide-if-less-than-220"
|
||||
:pan-zoom-locked="panZoomLocked"
|
||||
:zoom-factor="zoomFactor"
|
||||
@zoomOut="zoomOut"
|
||||
@zoomIn="zoomIn"
|
||||
@toggleZoomLock="toggleZoomLock"
|
||||
@handleResetImage="handleResetImage"
|
||||
/>
|
||||
|
||||
<button
|
||||
class="c-button icon-reset t-btn-zoom-reset"
|
||||
title="Remove zoom and pan"
|
||||
@click="handleResetImage"
|
||||
></button>
|
||||
|
||||
<span class="c-image-controls__zoom-factor">x{{ formattedZoomFactor }}</span>
|
||||
</div>
|
||||
<div class="c-image-controls__control c-image-controls__brightness-contrast">
|
||||
<span
|
||||
class="c-image-controls__sliders"
|
||||
draggable="true"
|
||||
@dragstart.stop.prevent
|
||||
>
|
||||
<div class="c-image-controls__input icon-brightness">
|
||||
<input
|
||||
v-model="filters.contrast"
|
||||
type="range"
|
||||
min="0"
|
||||
max="500"
|
||||
@change="notifyFiltersChanged"
|
||||
>
|
||||
</div>
|
||||
<div class="c-image-controls__input icon-contrast">
|
||||
<input
|
||||
v-model="filters.brightness"
|
||||
type="range"
|
||||
min="0"
|
||||
max="500"
|
||||
@change="notifyFiltersChanged"
|
||||
>
|
||||
</div>
|
||||
</span>
|
||||
<span class="t-reset-btn-holder c-imagery__lc__reset-btn c-image-controls__btn-reset">
|
||||
<button
|
||||
class="c-icon-link icon-reset t-btn-reset"
|
||||
@click="handleResetFilters"
|
||||
></button>
|
||||
</span>
|
||||
</div>
|
||||
<imagery-view-menu-switcher
|
||||
class="--show-if-less-than-220"
|
||||
:icon-class="'icon-magnify'"
|
||||
:title="'Zoom settings'"
|
||||
>
|
||||
<zoom-settings
|
||||
:pan-zoom-locked="panZoomLocked"
|
||||
:class="'c-control-menu c-menu--has-close-btn'"
|
||||
:zoom-factor="zoomFactor"
|
||||
:is-menu="true"
|
||||
@zoomOut="zoomOut"
|
||||
@zoomIn="zoomIn"
|
||||
@toggleZoomLock="toggleZoomLock"
|
||||
@handleResetImage="handleResetImage"
|
||||
/>
|
||||
</imagery-view-menu-switcher>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _ from 'lodash';
|
||||
|
||||
import FilterSettings from "./FilterSettings.vue";
|
||||
import LayerSettings from "./LayerSettings.vue";
|
||||
import ZoomSettings from "./ZoomSettings.vue";
|
||||
import ImageryViewMenuSwitcher from "./ImageryViewMenuSwitcher.vue";
|
||||
|
||||
const DEFAULT_FILTER_VALUES = {
|
||||
brightness: '100',
|
||||
contrast: '100'
|
||||
@@ -101,13 +88,28 @@ const ZOOM_STEP = 1;
|
||||
const ZOOM_WHEEL_SENSITIVITY_REDUCTION = 0.01;
|
||||
|
||||
export default {
|
||||
components: {
|
||||
FilterSettings,
|
||||
LayerSettings,
|
||||
ImageryViewMenuSwitcher,
|
||||
ZoomSettings
|
||||
},
|
||||
inject: ['openmct', 'domainObject'],
|
||||
props: {
|
||||
layers: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
zoomFactor: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
imageUrl: String
|
||||
imageUrl: {
|
||||
type: String,
|
||||
default: () => {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -123,9 +125,6 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
formattedZoomFactor() {
|
||||
return Number.parseFloat(this.zoomFactor).toPrecision(2);
|
||||
},
|
||||
cursorStates() {
|
||||
const isPannable = this.altPressed && this.zoomFactor > 1;
|
||||
const showCursorZoomIn = this.metaPressed && !this.shiftPressed;
|
||||
@@ -267,6 +266,13 @@ export default {
|
||||
|
||||
const newScaleFactor = this.zoomFactor + (this.shiftPressed ? -ZOOM_STEP : ZOOM_STEP);
|
||||
this.zoomImage(newScaleFactor, e.clientX, e.clientY);
|
||||
},
|
||||
toggleLayerVisibility(index) {
|
||||
this.$emit('toggleLayerVisibility', index);
|
||||
},
|
||||
updateFilterValues(filters) {
|
||||
this.filters = filters;
|
||||
this.notifyFiltersChanged();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -28,34 +28,41 @@
|
||||
@keydown="arrowDownHandler"
|
||||
@mouseover="focusElement"
|
||||
>
|
||||
<div class="c-imagery__main-image-wrapper has-local-controls">
|
||||
<div
|
||||
class="c-imagery__main-image-wrapper has-local-controls"
|
||||
:class="{
|
||||
'paused unnsynced': isPaused && !isFixed,
|
||||
'stale': false,
|
||||
'pannable': cursorStates.isPannable,
|
||||
'cursor-zoom-in': cursorStates.showCursorZoomIn,
|
||||
'cursor-zoom-out': cursorStates.showCursorZoomOut
|
||||
}"
|
||||
@mousedown="handlePanZoomClick"
|
||||
>
|
||||
<ImageControls
|
||||
ref="imageControls"
|
||||
:zoom-factor="zoomFactor"
|
||||
:image-url="imageUrl"
|
||||
:layers="layers"
|
||||
@resetImage="resetImage"
|
||||
@panZoomUpdated="handlePanZoomUpdate"
|
||||
@filtersUpdated="setFilters"
|
||||
@cursorsUpdated="setCursorStates"
|
||||
@startPan="startPan"
|
||||
@toggleLayerVisibility="toggleLayerVisibility"
|
||||
/>
|
||||
|
||||
<div
|
||||
v-if="zoomFactor > 1"
|
||||
class="c-imagery__hints"
|
||||
>
|
||||
Alt-drag to pan
|
||||
</div>
|
||||
<div
|
||||
ref="imageBG"
|
||||
class="c-imagery__main-image__bg"
|
||||
:class="{
|
||||
'paused unnsynced': isPaused && !isFixed,
|
||||
'stale': false,
|
||||
'pannable': cursorStates.isPannable,
|
||||
'cursor-zoom-in': cursorStates.showCursorZoomIn,
|
||||
'cursor-zoom-out': cursorStates.showCursorZoomOut
|
||||
}"
|
||||
@click="expand"
|
||||
>
|
||||
<div
|
||||
v-if="zoomFactor > 1"
|
||||
class="c-imagery__hints"
|
||||
>Alt-drag to pan</div>
|
||||
<div
|
||||
ref="focusedImageWrapper"
|
||||
class="image-wrapper"
|
||||
@@ -63,8 +70,18 @@
|
||||
'width': `${sizedImageWidth}px`,
|
||||
'height': `${sizedImageHeight}px`
|
||||
}"
|
||||
@mousedown="handlePanZoomClick"
|
||||
>
|
||||
<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})`,
|
||||
'transform': `scale(${zoomFactor}) translate(${imageTranslateX}px, ${imageTranslateY}px)`,
|
||||
'transition': `${!pan && animateZoom ? 'transform 250ms ease-in' : 'initial'}`,
|
||||
}"
|
||||
>
|
||||
</div>
|
||||
<img
|
||||
ref="focusedImage"
|
||||
class="c-imagery__main-image__image js-imageryView-image "
|
||||
@@ -250,6 +267,9 @@ export default {
|
||||
this.requestCount = 0;
|
||||
|
||||
return {
|
||||
timeFormat: '',
|
||||
layers: [],
|
||||
visibleLayers: [],
|
||||
durationFormatter: undefined,
|
||||
imageHistory: [],
|
||||
timeSystem: timeSystem,
|
||||
@@ -550,8 +570,10 @@ export default {
|
||||
}
|
||||
|
||||
this.listenTo(this.focusedImageWrapper, 'wheel', this.wheelZoom, this);
|
||||
this.loadVisibleLayers();
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.persistVisibleLayers();
|
||||
this.stopFollowingTimeContext();
|
||||
|
||||
if (this.thumbWrapperResizeObserver) {
|
||||
@@ -647,6 +669,37 @@ export default {
|
||||
|
||||
return mostRecent[valueKey];
|
||||
},
|
||||
loadVisibleLayers() {
|
||||
const metaDataValues = this.metadata.valuesForHints(['image'])[0];
|
||||
this.imageFormat = this.openmct.telemetry.getValueFormatter(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 => object.name === layer.name);
|
||||
if (persistedLayer) {
|
||||
layer.visible = persistedLayer.visible === true;
|
||||
}
|
||||
});
|
||||
this.visibleLayers = this.layers.filter(layer => layer.visible);
|
||||
} else {
|
||||
this.visibleLayers = [];
|
||||
this.layers.forEach((layer) => {
|
||||
layer.visible = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
persistVisibleLayers() {
|
||||
if (this.domainObject.configuration) {
|
||||
this.openmct.objects.mutate(this.domainObject, 'configuration.layers', this.layers);
|
||||
}
|
||||
|
||||
this.visibleLayers = [];
|
||||
this.layers = [];
|
||||
},
|
||||
// will subscribe to data for this key if not already done
|
||||
subscribeToDataForKey(key) {
|
||||
if (this.relatedTelemetry[key].isSubscribed) {
|
||||
@@ -703,7 +756,6 @@ export default {
|
||||
focusElement() {
|
||||
this.$el.focus();
|
||||
},
|
||||
|
||||
handleScroll() {
|
||||
const thumbsWrapper = this.$refs.thumbsWrapper;
|
||||
if (!thumbsWrapper || this.resizingWindow) {
|
||||
@@ -984,7 +1036,6 @@ export default {
|
||||
this.resizingWindow = false;
|
||||
});
|
||||
},
|
||||
// debounced method
|
||||
clearWheelZoom() {
|
||||
this.$refs.imageControls.clearWheelZoom();
|
||||
},
|
||||
@@ -1050,6 +1101,11 @@ export default {
|
||||
},
|
||||
setCursorStates(states) {
|
||||
this.cursorStates = states;
|
||||
},
|
||||
toggleLayerVisibility(index) {
|
||||
let isVisible = this.layers[index].visible === true;
|
||||
this.layers[index].visible = !isVisible;
|
||||
this.visibleLayers = this.layers.filter(layer => layer.visible);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
65
src/plugins/imagery/components/ImageryViewMenuSwitcher.vue
Normal file
65
src/plugins/imagery/components/ImageryViewMenuSwitcher.vue
Normal file
@@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<div class="c-switcher-menu">
|
||||
<button
|
||||
:id="id"
|
||||
class="c-button c-button--menu c-switcher-menu__button"
|
||||
:class="iconClass"
|
||||
:title="title"
|
||||
@click="toggleMenu"
|
||||
>
|
||||
<span class="c-button__label"></span>
|
||||
</button>
|
||||
<div
|
||||
v-show="showMenu"
|
||||
class="c-switcher-menu__content"
|
||||
>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import uuid from 'uuid';
|
||||
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
iconClass: {
|
||||
type: String,
|
||||
default() {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default() {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
id: uuid(),
|
||||
showMenu: false
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener('click', this.hideMenu);
|
||||
},
|
||||
destroyed() {
|
||||
document.removeEventListener('click', this.hideMenu);
|
||||
},
|
||||
methods: {
|
||||
toggleMenu() {
|
||||
this.showMenu = !this.showMenu;
|
||||
},
|
||||
hideMenu(e) {
|
||||
if (this.id === e.target.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.showMenu = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
59
src/plugins/imagery/components/LayerSettings.vue
Normal file
59
src/plugins/imagery/components/LayerSettings.vue
Normal file
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<div
|
||||
class="c-control-menu c-menu--to-left c-menu--has-close-btn c-image-controls"
|
||||
@click="handleClose"
|
||||
>
|
||||
<div class="c-checkbox-list 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>
|
||||
</div>
|
||||
|
||||
<button class="c-click-icon icon-x t-btn-close c-switcher-menu__close-button"></button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
layers: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleClose(e) {
|
||||
const closeButton = e.target.classList.contains('c-switcher-menu__close-button');
|
||||
if (!closeButton) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
},
|
||||
toggleLayerVisibility(index) {
|
||||
this.$emit('toggleLayerVisibility', index);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
89
src/plugins/imagery/components/ZoomSettings.vue
Normal file
89
src/plugins/imagery/components/ZoomSettings.vue
Normal file
@@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<div
|
||||
class="c-image-controls__controls-wrapper"
|
||||
@click="handleClose"
|
||||
>
|
||||
<div class="c-image-controls__control c-image-controls__zoom">
|
||||
<div class="c-button-set c-button-set--strip-h">
|
||||
<button
|
||||
class="c-button t-btn-zoom-out icon-minus"
|
||||
title="Zoom out"
|
||||
@click="zoomOut"
|
||||
></button>
|
||||
|
||||
<button
|
||||
class="c-button t-btn-zoom-in icon-plus"
|
||||
title="Zoom in"
|
||||
@click="zoomIn"
|
||||
></button>
|
||||
|
||||
<button
|
||||
class="c-button t-btn-zoom-lock"
|
||||
title="Lock current zoom and pan across all images"
|
||||
:class="{'icon-unlocked': !panZoomLocked, 'icon-lock': panZoomLocked}"
|
||||
@click="toggleZoomLock"
|
||||
></button>
|
||||
|
||||
<button
|
||||
class="c-button icon-reset t-btn-zoom-reset"
|
||||
title="Remove zoom and pan"
|
||||
@click="handleResetImage"
|
||||
></button>
|
||||
</div>
|
||||
<div class="c-image-controls__zoom-factor">x{{ formattedZoomFactor }}</div>
|
||||
</div>
|
||||
<button
|
||||
v-if="isMenu"
|
||||
class="c-click-icon icon-x t-btn-close c-switcher-menu__close-button"
|
||||
></button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
zoomFactor: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
panZoomLocked: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
isMenu: {
|
||||
type: Boolean,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
formattedZoomFactor() {
|
||||
return Number.parseFloat(this.zoomFactor).toPrecision(2);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleClose(e) {
|
||||
const closeButton = e.target.classList.contains('c-switcher-menu__close-button');
|
||||
if (!closeButton) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
},
|
||||
handleResetImage() {
|
||||
this.$emit('handleResetImage');
|
||||
},
|
||||
toggleZoomLock() {
|
||||
this.$emit('toggleZoomLock');
|
||||
},
|
||||
zoomIn() {
|
||||
this.$emit('zoomIn');
|
||||
},
|
||||
zoomOut() {
|
||||
this.$emit('zoomOut');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -16,6 +16,22 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1 1 auto;
|
||||
|
||||
&.unnsynced{
|
||||
@include sUnsynced();
|
||||
}
|
||||
|
||||
&.cursor-zoom-in {
|
||||
cursor: zoom-in;
|
||||
}
|
||||
|
||||
&.cursor-zoom-out {
|
||||
cursor: zoom-out;
|
||||
}
|
||||
|
||||
&.pannable {
|
||||
@include cursorGrab();
|
||||
}
|
||||
}
|
||||
|
||||
.image-wrapper {
|
||||
@@ -33,19 +49,6 @@
|
||||
flex: 1 1 auto;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
&.unnsynced{
|
||||
@include sUnsynced();
|
||||
}
|
||||
&.cursor-zoom-in {
|
||||
cursor: zoom-in;
|
||||
}
|
||||
&.cursor-zoom-out {
|
||||
cursor: zoom-out;
|
||||
}
|
||||
&.pannable {
|
||||
@include cursorGrab();
|
||||
|
||||
}
|
||||
}
|
||||
&__background-image {
|
||||
background-position: center;
|
||||
@@ -65,6 +68,7 @@
|
||||
background: rgba(black, 0.2);
|
||||
border-radius: $smallCr;
|
||||
padding: 2px $interiorMargin;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
right: $m;
|
||||
top: $m;
|
||||
@@ -127,6 +131,11 @@
|
||||
@include flash($animName: flashImageAge, $iter: 2, $dur: 250ms, $valStart: rgba($colorOk, 0.7), $valEnd: rgba($colorOk, 0));
|
||||
}
|
||||
|
||||
&__layer-image {
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&__thumbs-wrapper {
|
||||
display: flex; // Uses row layout
|
||||
|
||||
@@ -165,6 +174,50 @@
|
||||
font-size: 0.8em;
|
||||
margin: $interiorMarginSm;
|
||||
}
|
||||
|
||||
.c-control-menu {
|
||||
// Controls on left of flex column layout, close btn on right
|
||||
@include menuOuter();
|
||||
//background: $colorItemBgHov;
|
||||
border-radius: $controlCr;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
padding: $interiorMargin;
|
||||
width: min-content;
|
||||
|
||||
> * + * {
|
||||
margin-left: $interiorMargin;
|
||||
}
|
||||
}
|
||||
|
||||
.c-switcher-menu {
|
||||
display: contents;
|
||||
|
||||
&__content {
|
||||
// Menu panel
|
||||
top: 28px;
|
||||
position: absolute;
|
||||
|
||||
.c-so-view & {
|
||||
top: 25px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.--width-less-than-220 .--show-if-less-than-220.c-switcher-menu {
|
||||
display: contents !important;
|
||||
}
|
||||
|
||||
.s-image-layer {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
opacity: 0.5;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
/*************************************** THUMBS */
|
||||
@@ -205,70 +258,36 @@
|
||||
/*************************************** IMAGERY LOCAL CONTROLS*/
|
||||
.c-imagery {
|
||||
.h-local-controls--overlay-content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
position: absolute;
|
||||
left: $interiorMargin; top: $interiorMargin;
|
||||
z-index: 2;
|
||||
background: $colorLocalControlOvrBg;
|
||||
border-radius: $basicCr;
|
||||
max-width: 250px;
|
||||
min-width: 170px;
|
||||
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;
|
||||
}
|
||||
}
|
||||
padding: $interiorMargin $interiorMargin;
|
||||
|
||||
.s-status-taking-snapshot & {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__lc {
|
||||
&__reset-btn {
|
||||
// Span that holds bracket graphics and button
|
||||
$bc: $scrollbarTrackColorBg;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
border-right: 1px solid $bc;
|
||||
content:'';
|
||||
display: block;
|
||||
width: 5px;
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
&:before {
|
||||
border-top: 1px solid $bc;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
&:after {
|
||||
border-bottom: 1px solid $bc;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.c-icon-link {
|
||||
color: $colorBtnFg;
|
||||
}
|
||||
[class*='--menus-aligned'] {
|
||||
> * + * {
|
||||
button { margin-left: $interiorMarginSm; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.c-image-controls {
|
||||
&__controls-wrapper {
|
||||
// Wraps __controls and __close-btn
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&__controls {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
|
||||
> * + * {
|
||||
margin-top: $interiorMargin;
|
||||
@@ -290,31 +309,67 @@
|
||||
|
||||
}
|
||||
|
||||
&__input {
|
||||
// A wrapper is needed to add the type icon to left of each control
|
||||
|
||||
input[type='range'] {
|
||||
//width: 100%; // Do we need this?
|
||||
}
|
||||
}
|
||||
|
||||
&__zoom {
|
||||
> * + * { margin-left: $interiorMargin; }
|
||||
> * + * { margin-left: $interiorMargin; } // Is this used?
|
||||
}
|
||||
|
||||
&__sliders {
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
flex-direction: column;
|
||||
&--filters {
|
||||
// Styles specific to the brightness and contrast controls
|
||||
|
||||
> * + * {
|
||||
margin-top: 11px;
|
||||
.c-image-controls {
|
||||
&__sliders {
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
flex-direction: column;
|
||||
min-width: 80px;
|
||||
|
||||
> * + * {
|
||||
margin-top: 11px;
|
||||
}
|
||||
|
||||
input[type="range"] {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&__slider-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&:before { margin-right: $interiorMargin; }
|
||||
}
|
||||
|
||||
&__reset-btn {
|
||||
// Span that holds bracket graphics and button
|
||||
$bc: $scrollbarTrackColorBg;
|
||||
flex: 0 0 auto;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
border-right: 1px solid $bc;
|
||||
content:'';
|
||||
display: block;
|
||||
width: 5px;
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
&:before {
|
||||
border-top: 1px solid $bc;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
&:after {
|
||||
border-bottom: 1px solid $bc;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.c-icon-link {
|
||||
color: $colorBtnFg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__btn-reset {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************** BUTTONS */
|
||||
@@ -359,7 +414,7 @@
|
||||
@include cArrowButtonSizing($dimOuter: 48px);
|
||||
border-radius: $controlCr;
|
||||
|
||||
.is-in-small-container & {
|
||||
.--width-less-than-600 & {
|
||||
@include cArrowButtonSizing($dimOuter: 32px);
|
||||
}
|
||||
}
|
||||
@@ -385,10 +440,6 @@
|
||||
background-color: $colorBodyFg;
|
||||
}
|
||||
|
||||
//[class*='__image-placeholder'] {
|
||||
// display: none;
|
||||
//}
|
||||
|
||||
img {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
BIN
src/plugins/imagery/layers/example-imagery-layer-16x9.png
Normal file
BIN
src/plugins/imagery/layers/example-imagery-layer-16x9.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.4 KiB |
BIN
src/plugins/imagery/layers/example-imagery-layer-safe.png
Normal file
BIN
src/plugins/imagery/layers/example-imagery-layer-safe.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.0 KiB |
BIN
src/plugins/imagery/layers/example-imagery-layer-scale.png
Normal file
BIN
src/plugins/imagery/layers/example-imagery-layer-scale.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
@@ -63,8 +63,9 @@
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.is-in-small-container & {
|
||||
display: none;
|
||||
|
||||
.--width-less-than-600 & {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -384,6 +384,10 @@ select {
|
||||
&__row {
|
||||
> * + * { margin-left: $interiorMargin; }
|
||||
}
|
||||
|
||||
li {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************** TABS */
|
||||
@@ -477,6 +481,9 @@ select {
|
||||
text-shadow: $shdwMenuText;
|
||||
padding: $interiorMarginSm;
|
||||
box-shadow: $shdwMenu;
|
||||
}
|
||||
|
||||
@mixin menuPositioning() {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
@@ -525,6 +532,7 @@ select {
|
||||
|
||||
.c-menu {
|
||||
@include menuOuter();
|
||||
@include menuPositioning();
|
||||
@include menuInner();
|
||||
|
||||
&__section-hint {
|
||||
@@ -548,6 +556,7 @@ select {
|
||||
.c-super-menu {
|
||||
// Two column layout, menu items on left with detail of hover element on right
|
||||
@include menuOuter();
|
||||
@include menuPositioning();
|
||||
display: flex;
|
||||
padding: $interiorMarginLg;
|
||||
flex-direction: row;
|
||||
@@ -993,6 +1002,14 @@ input[type="range"] {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
[class*='--menus-aligned'] {
|
||||
// Contains top level elements that hold dropdown menus
|
||||
// Top level elements use display: contents to allow their menus to compactly align
|
||||
// 03-18-22: used in ImageControls.vue
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
|
||||
.c-local-controls {
|
||||
|
||||
@@ -349,3 +349,22 @@ body.desktop .has-local-controls {
|
||||
pointer-events: none !important;
|
||||
cursor: default !important;
|
||||
}
|
||||
|
||||
/******************************************************** RESPONSIVE CONTAINERS */
|
||||
@mixin responsiveContainerWidths($dimension) {
|
||||
// 3/21/22: `--width-less-than*` classes set in ObjectView.vue
|
||||
.--show-if-less-than-#{$dimension} {
|
||||
// Hide anything that displays within a given width by default.
|
||||
// `display` property must be set within a more specific class
|
||||
// for the particular item to be displayed.
|
||||
display: none !important
|
||||
}
|
||||
|
||||
.--width-less-than-#{$dimension} {
|
||||
.--hide-if-less-than-#{$dimension} { display: none; }
|
||||
}
|
||||
}
|
||||
|
||||
//.--hide-by-default { display: none !important; }
|
||||
@include responsiveContainerWidths('220');
|
||||
@include responsiveContainerWidths('600');
|
||||
|
||||
@@ -118,7 +118,7 @@ mct-plot {
|
||||
}
|
||||
}
|
||||
|
||||
.is-in-small-container & {
|
||||
.--width-less-than-600 & {
|
||||
.c-control-bar {
|
||||
display: none;
|
||||
}
|
||||
@@ -494,7 +494,7 @@ mct-plot {
|
||||
margin-bottom: $interiorMarginSm;
|
||||
}
|
||||
|
||||
.is-in-small-container & {
|
||||
.--width-less-than-600 & {
|
||||
&.is-legend-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ div.c-table {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.is-in-small-container & {
|
||||
.--width-less-than-600 & {
|
||||
&:not(.is-paused) {
|
||||
.c-table-control-bar {
|
||||
display: none;
|
||||
|
||||
@@ -21,9 +21,11 @@
|
||||
*****************************************************************************/
|
||||
<template>
|
||||
<div
|
||||
ref="soView"
|
||||
class="c-so-view js-notebook-snapshot-item-wrapper"
|
||||
:class="[
|
||||
statusClass,
|
||||
widthClass,
|
||||
'c-so-view--' + domainObject.type,
|
||||
{
|
||||
'c-so-view--no-frame': !hasFrame,
|
||||
@@ -111,6 +113,7 @@ const SIMPLE_CONTENT_TYPES = [
|
||||
'hyperlink',
|
||||
'conditionWidget'
|
||||
];
|
||||
const CSS_WIDTH_LESS_STR = '--width-less-than-';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -150,6 +153,7 @@ export default {
|
||||
|
||||
return {
|
||||
cssClass,
|
||||
widthClass: '',
|
||||
complexContent,
|
||||
notebookEnabled: this.openmct.types.get('notebook'),
|
||||
statusBarItems: [],
|
||||
@@ -168,6 +172,11 @@ export default {
|
||||
if (provider) {
|
||||
this.$refs.objectView.show(this.domainObject, provider.key, false, this.objectPath);
|
||||
}
|
||||
|
||||
if (this.$refs.soView) {
|
||||
this.soViewResizeObserver = new ResizeObserver(this.resizeSoView);
|
||||
this.soViewResizeObserver.observe(this.$refs.soView);
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.removeStatusListener();
|
||||
@@ -175,6 +184,10 @@ export default {
|
||||
if (this.actionCollection) {
|
||||
this.unlistenToActionCollection();
|
||||
}
|
||||
|
||||
if (this.soViewResizeObserver) {
|
||||
this.soViewResizeObserver.disconnect();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getSelectionContext() {
|
||||
@@ -207,6 +220,18 @@ export default {
|
||||
},
|
||||
setStatus(status) {
|
||||
this.status = status;
|
||||
},
|
||||
resizeSoView() {
|
||||
let cW = this.$refs.soView.offsetWidth;
|
||||
let wClass = '';
|
||||
|
||||
if (cW < 220) {
|
||||
wClass = CSS_WIDTH_LESS_STR + '220';
|
||||
} else if (cW < 600) {
|
||||
wClass = CSS_WIDTH_LESS_STR + '600';
|
||||
}
|
||||
|
||||
this.widthClass = wClass;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -43,11 +43,11 @@
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.is-in-small-container &,
|
||||
.c-fl-frame & {
|
||||
.--width-less-than-220 &,
|
||||
.--width-less-than-600 & {
|
||||
[class*="__label"] {
|
||||
// button labels
|
||||
display: none;
|
||||
display: none !important;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -141,6 +141,10 @@
|
||||
&.is-status--missing {
|
||||
border: $borderMissing;
|
||||
}
|
||||
|
||||
// Leave for debugging
|
||||
//&.--width-less-than-600 { background: rgba(orange, 0.2) !important; }
|
||||
//&.--width-less-than-220 { background: rgba(red, 0.2) !important; }
|
||||
}
|
||||
|
||||
.l-angular-ov-wrapper {
|
||||
@@ -149,3 +153,5 @@
|
||||
display: block;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -71,6 +71,10 @@ const config = {
|
||||
transform: function (content) {
|
||||
return content.toString().replace(/dist\//g, '');
|
||||
}
|
||||
},
|
||||
{
|
||||
from: 'src/plugins/imagery/layers',
|
||||
to: 'imagery'
|
||||
}
|
||||
]
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user