mirror of
https://github.com/nasa/openmct.git
synced 2025-06-27 19:38:53 +00:00
Compare commits
26 Commits
experiment
...
save-style
Author | SHA1 | Date | |
---|---|---|---|
d4269e2546 | |||
c3cb47c670 | |||
42d519cf4a | |||
7c98d809c0 | |||
306b9db8ed | |||
e05580b973 | |||
d44ad83445 | |||
1397c45a96 | |||
15c0a1cf36 | |||
106ef8994a | |||
72e5618706 | |||
5e9c51a22c | |||
1270976a0e | |||
ad9ac4ca1e | |||
0c2c21a015 | |||
92b8b811d5 | |||
f5a0d6620b | |||
dbf3f3c6d8 | |||
cfa7717204 | |||
5127a40bd5 | |||
3eb2a64969 | |||
8f38a0da9a | |||
21f337c308 | |||
bad0b380b9 | |||
b83a38a00b | |||
dd1f57b2bf |
@ -21,9 +21,9 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="c-style">
|
<div class="c-style has-local-controls">
|
||||||
<span :class="[
|
<span :class="[
|
||||||
{ 'is-style-invisible': styleItem.style.isStyleInvisible },
|
{ 'is-style-invisible': styleItem.style && styleItem.style.isStyleInvisible },
|
||||||
{ 'c-style-thumb--mixed': mixedStyles.indexOf('backgroundColor') > -1 }
|
{ 'c-style-thumb--mixed': mixedStyles.indexOf('backgroundColor') > -1 }
|
||||||
]"
|
]"
|
||||||
:style="[styleItem.style.imageUrl ? { backgroundImage:'url(' + styleItem.style.imageUrl + ')'} : itemStyle ]"
|
:style="[styleItem.style.imageUrl ? { backgroundImage:'url(' + styleItem.style.imageUrl + ')'} : itemStyle ]"
|
||||||
@ -61,6 +61,13 @@
|
|||||||
:options="isStyleInvisibleOption"
|
:options="isStyleInvisibleOption"
|
||||||
@change="updateStyleValue"
|
@change="updateStyleValue"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<!-- Save Styles -->
|
||||||
|
<toolbar-button v-if="canSaveStyle()"
|
||||||
|
class="c-style__toolbar-button--save c-local-controls--show-on-hover"
|
||||||
|
:options="saveOptions"
|
||||||
|
@click="saveItemStyle()"
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -81,11 +88,12 @@ export default {
|
|||||||
ToolbarToggleButton
|
ToolbarToggleButton
|
||||||
},
|
},
|
||||||
inject: [
|
inject: [
|
||||||
'openmct'
|
'openmct', 'stylesManager'
|
||||||
],
|
],
|
||||||
props: {
|
props: {
|
||||||
isEditing: {
|
isEditing: {
|
||||||
type: Boolean
|
type: Boolean,
|
||||||
|
required: true
|
||||||
},
|
},
|
||||||
mixedStyles: {
|
mixedStyles: {
|
||||||
type: Array,
|
type: Array,
|
||||||
@ -182,7 +190,16 @@ export default {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
},
|
||||||
|
saveOptions() {
|
||||||
|
return {
|
||||||
|
icon: 'icon-save',
|
||||||
|
title: 'Save style',
|
||||||
|
// value: this.normalizeValueForSwatch(value),
|
||||||
|
// property: 'color',
|
||||||
|
isEditing: this.isEditing
|
||||||
|
// nonSpecific: this.mixedStyles.indexOf('color') > -1
|
||||||
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@ -216,6 +233,12 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.$emit('persist', this.styleItem, item.property);
|
this.$emit('persist', this.styleItem, item.property);
|
||||||
|
},
|
||||||
|
canSaveStyle() {
|
||||||
|
return this.isEditing && !this.mixedStyles.length;
|
||||||
|
},
|
||||||
|
saveItemStyle() {
|
||||||
|
this.stylesManager.save(this.itemStyle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -125,7 +125,8 @@ export default {
|
|||||||
},
|
},
|
||||||
inject: [
|
inject: [
|
||||||
'openmct',
|
'openmct',
|
||||||
'selection'
|
'selection',
|
||||||
|
'stylesManager'
|
||||||
],
|
],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@ -149,6 +150,8 @@ export default {
|
|||||||
},
|
},
|
||||||
destroyed() {
|
destroyed() {
|
||||||
this.removeListeners();
|
this.removeListeners();
|
||||||
|
this.openmct.editor.off('isEditing', this.setEditState);
|
||||||
|
this.stylesManager.off('styleSelected', this.updateSelectionStyle);
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.items = [];
|
this.items = [];
|
||||||
@ -167,6 +170,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.openmct.editor.on('isEditing', this.setEditState);
|
this.openmct.editor.on('isEditing', this.setEditState);
|
||||||
|
this.stylesManager.on('styleSelected', this.updateSelectionStyle);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getObjectStyles() {
|
getObjectStyles() {
|
||||||
@ -637,6 +641,30 @@ export default {
|
|||||||
},
|
},
|
||||||
persist(domainObject, style) {
|
persist(domainObject, style) {
|
||||||
this.openmct.objects.mutate(domainObject, 'configuration.objectStyles', style);
|
this.openmct.objects.mutate(domainObject, 'configuration.objectStyles', style);
|
||||||
|
},
|
||||||
|
updateSelectionStyle(style) {
|
||||||
|
if (!this.allowEditing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const foundStyle = this.findStyleByConditionId(this.selectedConditionId);
|
||||||
|
|
||||||
|
if (foundStyle && !this.isStaticAndConditionalStyles) {
|
||||||
|
Object.entries(style).forEach(([property, value]) => {
|
||||||
|
if (foundStyle.style[property] !== undefined && foundStyle.style[property] !== value) {
|
||||||
|
foundStyle.style[property] = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.getAndPersistStyles();
|
||||||
|
} else {
|
||||||
|
this.removeConditionSet();
|
||||||
|
Object.entries(style).forEach(([property, value]) => {
|
||||||
|
if (this.staticStyle.style[property] !== undefined && this.staticStyle.style[property] !== value) {
|
||||||
|
this.staticStyle.style[property] = value;
|
||||||
|
this.getAndPersistStyles(property);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -87,6 +87,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__selector {
|
||||||
|
&:hover {
|
||||||
|
$c: $colorBodyFg;
|
||||||
|
border-color: rgba($c, 0.2);
|
||||||
|
background: rgba($c, 0.2);
|
||||||
|
transition: $transIn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.c-style {
|
.c-style {
|
||||||
padding: 2px; // Allow a bit of room for thumb box-shadow
|
padding: 2px; // Allow a bit of room for thumb box-shadow
|
||||||
|
|
||||||
|
@ -113,6 +113,10 @@ button {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.c-icon-button--disabled {
|
||||||
|
@include cClickIconButtonLayout();
|
||||||
|
}
|
||||||
|
|
||||||
.c-icon-link {
|
.c-icon-link {
|
||||||
&:before {
|
&:before {
|
||||||
// Icon
|
// Icon
|
||||||
@ -120,7 +124,8 @@ button {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.c-icon-button {
|
.c-icon-button,
|
||||||
|
.c-icon-button--disabled {
|
||||||
&__label {
|
&__label {
|
||||||
margin-left: $interiorMargin;
|
margin-left: $interiorMargin;
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="c-inspector__content">
|
<div class="c-inspector__content">
|
||||||
<multipane v-if="currentTabbedView.key === '__properties'"
|
<multipane v-show="currentTabbedView.key === '__properties'"
|
||||||
type="vertical"
|
type="vertical"
|
||||||
>
|
>
|
||||||
<pane class="c-inspector__properties">
|
<pane class="c-inspector__properties">
|
||||||
@ -32,9 +32,13 @@
|
|||||||
<elements />
|
<elements />
|
||||||
</pane>
|
</pane>
|
||||||
</multipane>
|
</multipane>
|
||||||
<template v-else>
|
<StylesInspectorView
|
||||||
<styles-inspector-view />
|
v-show="currentTabbedView.key === '__styles'"
|
||||||
</template>
|
/>
|
||||||
|
<SavedStylesInspectorView
|
||||||
|
v-show="currentTabbedView.key === '__styles'"
|
||||||
|
:is-editing="isEditing"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -48,12 +52,18 @@ import Properties from './Properties.vue';
|
|||||||
import ObjectName from './ObjectName.vue';
|
import ObjectName from './ObjectName.vue';
|
||||||
import InspectorViews from './InspectorViews.vue';
|
import InspectorViews from './InspectorViews.vue';
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import StylesInspectorView from "./StylesInspectorView.vue";
|
import stylesManager from '@/ui/inspector/StylesManager';
|
||||||
|
import StylesInspectorView from './StylesInspectorView.vue';
|
||||||
|
import SavedStylesInspectorView from './SavedStylesInspectorView.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
provide: {
|
||||||
|
stylesManager: stylesManager
|
||||||
|
},
|
||||||
inject: ['openmct'],
|
inject: ['openmct'],
|
||||||
components: {
|
components: {
|
||||||
StylesInspectorView,
|
StylesInspectorView,
|
||||||
|
SavedStylesInspectorView,
|
||||||
multipane,
|
multipane,
|
||||||
pane,
|
pane,
|
||||||
Elements,
|
Elements,
|
||||||
@ -63,7 +73,10 @@ export default {
|
|||||||
InspectorViews
|
InspectorViews
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
'isEditing': Boolean
|
isEditing: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
208
src/ui/inspector/SavedStyleSelector.vue
Normal file
208
src/ui/inspector/SavedStyleSelector.vue
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="c-saved-style"
|
||||||
|
@click="selectStyle()"
|
||||||
|
>
|
||||||
|
<div class="c-style has-local-controls">
|
||||||
|
<span
|
||||||
|
class="c-disclosure-triangle is-enabled"
|
||||||
|
:class="{ 'c-disclosure-triangle--expanded': expanded }"
|
||||||
|
@click.stop="toggleExpanded()"
|
||||||
|
></span>
|
||||||
|
<span
|
||||||
|
class="c-style-thumb"
|
||||||
|
:style="thumbStyle"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="c-style-thumb__text"
|
||||||
|
:class="{ 'hide-nice': !hasProperty(savedStyle.color) }"
|
||||||
|
>
|
||||||
|
ABC
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span class="c-toolbar">
|
||||||
|
<div class="c-ctrl-wrapper">
|
||||||
|
<div
|
||||||
|
class="c-icon-button--disabled c-icon-button--swatched icon-line-horz"
|
||||||
|
title="Border color"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="c-swatch"
|
||||||
|
:style="{
|
||||||
|
background: savedStyle.border
|
||||||
|
}"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="c-ctrl-wrapper">
|
||||||
|
<div
|
||||||
|
class="c-icon-button--disabled c-icon-button--swatched icon-paint-bucket"
|
||||||
|
title="Background color"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="c-swatch"
|
||||||
|
:style="{
|
||||||
|
background: savedStyle.backgroundColor
|
||||||
|
}"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="c-ctrl-wrapper">
|
||||||
|
<div
|
||||||
|
class="c-icon-button--disabled c-icon-button--swatched icon-font"
|
||||||
|
title="Text color"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="c-swatch"
|
||||||
|
:style="{
|
||||||
|
background: savedStyle.color
|
||||||
|
}"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- delete saved style -->
|
||||||
|
<div
|
||||||
|
v-if="canDeleteStyle"
|
||||||
|
class="c-ctrl-wrapper c-local-controls--show-on-hover"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="c-icon-button icon-trash"
|
||||||
|
title="Delete Style"
|
||||||
|
@click.stop="deleteStyle()"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="expanded">
|
||||||
|
{{ description }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'SavedStyleSelector',
|
||||||
|
inject: [
|
||||||
|
'openmct',
|
||||||
|
'stylesManager'
|
||||||
|
],
|
||||||
|
props: {
|
||||||
|
isEditing: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
savedStyle: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
expanded: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
thumbStyle() {
|
||||||
|
return {
|
||||||
|
border: `1px solid ${this.savedStyle.border}`,
|
||||||
|
backgroundColor: this.savedStyle.backgroundColor,
|
||||||
|
color: this.savedStyle.color
|
||||||
|
};
|
||||||
|
},
|
||||||
|
description() {
|
||||||
|
const fill = `Fill: ${this.savedStyle.backgroundColor || 'None'};`;
|
||||||
|
const border = `Border: ${this.savedStyle.border || 'None'};`;
|
||||||
|
const color = `Text Color: ${this.savedStyle.color || 'None'};`;
|
||||||
|
const fontSize = this.savedStyle.fontSize ? `Font Size: ${this.savedStyle.fontSize};` : '';
|
||||||
|
const font = this.savedStyle.font ? `Font Family: ${this.savedStyle.font};` : '';
|
||||||
|
|
||||||
|
return `
|
||||||
|
${fill}
|
||||||
|
${border}
|
||||||
|
${color}
|
||||||
|
${fontSize}
|
||||||
|
${font}
|
||||||
|
`;
|
||||||
|
},
|
||||||
|
canDeleteStyle() {
|
||||||
|
return this.isEditing;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
selectStyle() {
|
||||||
|
if (this.isEditing) {
|
||||||
|
this.stylesManager.select(this.savedStyle);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deleteStyle(style) {
|
||||||
|
this.showDeleteStyleDialog(style)
|
||||||
|
.then(() => {
|
||||||
|
this.stylesManager.delete(this.savedStyle);
|
||||||
|
})
|
||||||
|
.catch(() => {});
|
||||||
|
},
|
||||||
|
showDeleteStyleDialog(style) {
|
||||||
|
const message = `
|
||||||
|
This will delete this saved style.
|
||||||
|
This action will not effect styling that has already been applied.
|
||||||
|
Do you want to continue?
|
||||||
|
`;
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let dialog = this.openmct.overlays.dialog({
|
||||||
|
title: 'Delete Saved Style',
|
||||||
|
iconClass: 'alert',
|
||||||
|
message: message,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
label: 'OK',
|
||||||
|
callback: () => {
|
||||||
|
dialog.dismiss();
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Cancel',
|
||||||
|
callback: () => {
|
||||||
|
dialog.dismiss();
|
||||||
|
reject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
hasProperty(property) {
|
||||||
|
return property !== undefined;
|
||||||
|
},
|
||||||
|
toggleExpanded() {
|
||||||
|
this.expanded = !this.expanded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
72
src/ui/inspector/SavedStylesInspectorView.vue
Normal file
72
src/ui/inspector/SavedStylesInspectorView.vue
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="u-contents"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import SavedStylesView from './SavedStylesView.vue';
|
||||||
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
inject: ['openmct', 'stylesManager'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
selection: []
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.openmct.selection.on('change', this.updateSelection);
|
||||||
|
this.updateSelection(this.openmct.selection.get());
|
||||||
|
},
|
||||||
|
destroyed() {
|
||||||
|
this.openmct.selection.off('change', this.updateSelection);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
updateSelection(selection) {
|
||||||
|
if (selection.length > 0 && selection[0].length > 0) {
|
||||||
|
if (this.component) {
|
||||||
|
this.component.$destroy();
|
||||||
|
this.component = undefined;
|
||||||
|
this.$el.innerHTML = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
let viewContainer = document.createElement('div');
|
||||||
|
this.$el.append(viewContainer);
|
||||||
|
this.component = new Vue({
|
||||||
|
el: viewContainer,
|
||||||
|
components: {
|
||||||
|
SavedStylesView
|
||||||
|
},
|
||||||
|
provide: {
|
||||||
|
openmct: this.openmct,
|
||||||
|
selection: selection,
|
||||||
|
stylesManager: this.stylesManager
|
||||||
|
},
|
||||||
|
template: '<saved-styles-view />'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
132
src/ui/inspector/SavedStylesView.vue
Normal file
132
src/ui/inspector/SavedStylesView.vue
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="c-inspector__saved-styles c-inspect-styles">
|
||||||
|
<div class="c-inspect-styles__header">
|
||||||
|
Saved Styles
|
||||||
|
</div>
|
||||||
|
<div class="c-inspect-styles__content">
|
||||||
|
<div class="c-inspect-styles__saved-style">
|
||||||
|
<div
|
||||||
|
v-for="(savedStyle, index) in savedStyles"
|
||||||
|
:key="index"
|
||||||
|
class="c-inspect-styles__saved-style"
|
||||||
|
>
|
||||||
|
<saved-style-selector
|
||||||
|
class="c-inspect-styles__selector"
|
||||||
|
:is-editing="isEditing"
|
||||||
|
:saved-style="savedStyle"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import SavedStyleSelector from './SavedStyleSelector.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'SavedStylesView',
|
||||||
|
components: {
|
||||||
|
SavedStyleSelector
|
||||||
|
},
|
||||||
|
inject: [
|
||||||
|
'openmct',
|
||||||
|
'selection',
|
||||||
|
'stylesManager'
|
||||||
|
],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isEditing: this.openmct.editor.isEditing(),
|
||||||
|
savedStyles: undefined
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.openmct.editor.on('isEditing', this.setIsEditing);
|
||||||
|
this.stylesManager.on('stylesUpdated', this.setStyles);
|
||||||
|
this.stylesManager.on('limitReached', this.showLimitReachedDialog);
|
||||||
|
this.stylesManager.on('persistError', this.showPersistErrorDialog);
|
||||||
|
|
||||||
|
this.loadStyles();
|
||||||
|
},
|
||||||
|
destroyed() {
|
||||||
|
this.openmct.editor.off('isEditing', this.setIsEditing);
|
||||||
|
this.stylesManager.off('stylesUpdated', this.setStyles);
|
||||||
|
this.stylesManager.off('limitReached', this.showLimitReachedDialog);
|
||||||
|
this.stylesManager.off('persistError', this.showPersistErrorDialog);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
setIsEditing(isEditing) {
|
||||||
|
this.isEditing = isEditing;
|
||||||
|
},
|
||||||
|
loadStyles() {
|
||||||
|
const styles = this.stylesManager.load();
|
||||||
|
|
||||||
|
this.setStyles(styles);
|
||||||
|
},
|
||||||
|
setStyles(styles) {
|
||||||
|
this.savedStyles = styles;
|
||||||
|
},
|
||||||
|
showLimitReachedDialog(limit) {
|
||||||
|
const message = `
|
||||||
|
You have reached the limit on the number of saved styles.
|
||||||
|
Please delete one or more saved styles and try again.
|
||||||
|
`;
|
||||||
|
|
||||||
|
let dialog = this.openmct.overlays.dialog({
|
||||||
|
title: 'Saved Styles Limit',
|
||||||
|
iconClass: 'alert',
|
||||||
|
message: message,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
label: 'OK',
|
||||||
|
callback: () => {
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
},
|
||||||
|
showPersistErrorDialog() {
|
||||||
|
const message = `
|
||||||
|
Problem encountered saving styles.
|
||||||
|
Try again or delete one or more styles before trying again.
|
||||||
|
`;
|
||||||
|
let dialog = this.openmct.overlays.dialog({
|
||||||
|
title: 'Error Saving Style',
|
||||||
|
iconClass: 'error',
|
||||||
|
message: message,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
label: 'OK',
|
||||||
|
callback: () => {
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
@ -29,7 +29,7 @@ import StylesView from '../../plugins/condition/components/inspector/StylesView.
|
|||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inject: ['openmct'],
|
inject: ['openmct', 'stylesManager'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
selection: []
|
selection: []
|
||||||
@ -56,7 +56,8 @@ export default {
|
|||||||
this.component = new Vue({
|
this.component = new Vue({
|
||||||
provide: {
|
provide: {
|
||||||
openmct: this.openmct,
|
openmct: this.openmct,
|
||||||
selection: selection
|
selection: selection,
|
||||||
|
stylesManager: this.stylesManager
|
||||||
},
|
},
|
||||||
el: viewContainer,
|
el: viewContainer,
|
||||||
components: {
|
components: {
|
||||||
|
111
src/ui/inspector/StylesManager.js
Normal file
111
src/ui/inspector/StylesManager.js
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
import EventEmitter from 'EventEmitter';
|
||||||
|
|
||||||
|
const LOCAL_STORAGE_KEY = 'mct-saved-styles';
|
||||||
|
const LIMIT = 20;
|
||||||
|
const STYLE_PROPERTIES = [
|
||||||
|
'backgroundColor', 'border', 'color'
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} Style
|
||||||
|
* @property {*} property
|
||||||
|
*/
|
||||||
|
class StylesManager extends EventEmitter {
|
||||||
|
load() {
|
||||||
|
let styles = window.localStorage.getItem(LOCAL_STORAGE_KEY);
|
||||||
|
styles = styles ? JSON.parse(styles) : [];
|
||||||
|
|
||||||
|
return styles;
|
||||||
|
}
|
||||||
|
|
||||||
|
save(style) {
|
||||||
|
const normalizedStyle = this.normalizeStyle(style);
|
||||||
|
const styles = this.load();
|
||||||
|
|
||||||
|
if (!this.isSaveLimitReached(styles)) {
|
||||||
|
styles.unshift(normalizedStyle);
|
||||||
|
const persistSucceeded = this.persist(styles);
|
||||||
|
|
||||||
|
if (persistSucceeded) {
|
||||||
|
this.emit('stylesUpdated', styles);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
normalizeStyle(style) {
|
||||||
|
const normalizedStyle = style;
|
||||||
|
|
||||||
|
// strip border styling down to border color only
|
||||||
|
style.border = style.border.substring(style.border.lastIndexOf('#'));
|
||||||
|
|
||||||
|
return normalizedStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
isSaveLimitReached(styles) {
|
||||||
|
if (styles.length >= LIMIT) {
|
||||||
|
this.emit('limitReached');
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
isExistingStyle(style, styles) {
|
||||||
|
return styles.some(existingStyle => this.isEqual(style, existingStyle));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
persist(styles) {
|
||||||
|
try {
|
||||||
|
window.localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(styles));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
this.emit('persistError');
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
isEqual(style1, style2) {
|
||||||
|
const keys = Object.keys(Object.assign({}, style1, style2));
|
||||||
|
const different = keys.some(key => (!style1[key] && style2[key])
|
||||||
|
|| (style1[key] && !style2[key])
|
||||||
|
|| (style1[key] !== style2[key])
|
||||||
|
);
|
||||||
|
|
||||||
|
return !different;
|
||||||
|
}
|
||||||
|
|
||||||
|
select(style) {
|
||||||
|
this.emit('styleSelected', style);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(style) {
|
||||||
|
const styles = this.load();
|
||||||
|
const remainingStyles = styles.filter(keep => !this.isEqual(keep, style));
|
||||||
|
|
||||||
|
const persistSuccess = this.persist(remainingStyles);
|
||||||
|
if (persistSuccess) {
|
||||||
|
this.emit('stylesUpdated', remainingStyles);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const stylesManager = new StylesManager();
|
||||||
|
// breaks on adding listener later
|
||||||
|
// Object.freeze(stylesManager);
|
||||||
|
|
||||||
|
export default stylesManager;
|
@ -162,6 +162,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/********************************************* INSPECTOR PROPERTIES TAB */
|
||||||
|
.c-saved-style {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
/********************************************* LEGACY SUPPORT */
|
/********************************************* LEGACY SUPPORT */
|
||||||
.c-inspector {
|
.c-inspector {
|
||||||
// FilterField.vue
|
// FilterField.vue
|
||||||
|
Reference in New Issue
Block a user