mirror of
https://github.com/nasa/openmct.git
synced 2025-06-25 10:44:21 +00:00
Compare commits
26 Commits
Updates-to
...
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>
|
||||
<div class="c-style">
|
||||
<div class="c-style has-local-controls">
|
||||
<span :class="[
|
||||
{ 'is-style-invisible': styleItem.style.isStyleInvisible },
|
||||
{ 'is-style-invisible': styleItem.style && styleItem.style.isStyleInvisible },
|
||||
{ 'c-style-thumb--mixed': mixedStyles.indexOf('backgroundColor') > -1 }
|
||||
]"
|
||||
:style="[styleItem.style.imageUrl ? { backgroundImage:'url(' + styleItem.style.imageUrl + ')'} : itemStyle ]"
|
||||
@ -61,6 +61,13 @@
|
||||
:options="isStyleInvisibleOption"
|
||||
@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>
|
||||
</div>
|
||||
</template>
|
||||
@ -81,11 +88,12 @@ export default {
|
||||
ToolbarToggleButton
|
||||
},
|
||||
inject: [
|
||||
'openmct'
|
||||
'openmct', 'stylesManager'
|
||||
],
|
||||
props: {
|
||||
isEditing: {
|
||||
type: Boolean
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
mixedStyles: {
|
||||
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: {
|
||||
@ -216,6 +233,12 @@ export default {
|
||||
}
|
||||
|
||||
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: [
|
||||
'openmct',
|
||||
'selection'
|
||||
'selection',
|
||||
'stylesManager'
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
@ -149,6 +150,8 @@ export default {
|
||||
},
|
||||
destroyed() {
|
||||
this.removeListeners();
|
||||
this.openmct.editor.off('isEditing', this.setEditState);
|
||||
this.stylesManager.off('styleSelected', this.updateSelectionStyle);
|
||||
},
|
||||
mounted() {
|
||||
this.items = [];
|
||||
@ -167,6 +170,7 @@ export default {
|
||||
}
|
||||
|
||||
this.openmct.editor.on('isEditing', this.setEditState);
|
||||
this.stylesManager.on('styleSelected', this.updateSelectionStyle);
|
||||
},
|
||||
methods: {
|
||||
getObjectStyles() {
|
||||
@ -637,6 +641,30 @@ export default {
|
||||
},
|
||||
persist(domainObject, 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 {
|
||||
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 {
|
||||
&:before {
|
||||
// Icon
|
||||
@ -120,7 +124,8 @@ button {
|
||||
}
|
||||
}
|
||||
|
||||
.c-icon-button {
|
||||
.c-icon-button,
|
||||
.c-icon-button--disabled {
|
||||
&__label {
|
||||
margin-left: $interiorMargin;
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
</div>
|
||||
<div class="c-inspector__content">
|
||||
<multipane v-if="currentTabbedView.key === '__properties'"
|
||||
<multipane v-show="currentTabbedView.key === '__properties'"
|
||||
type="vertical"
|
||||
>
|
||||
<pane class="c-inspector__properties">
|
||||
@ -32,9 +32,13 @@
|
||||
<elements />
|
||||
</pane>
|
||||
</multipane>
|
||||
<template v-else>
|
||||
<styles-inspector-view />
|
||||
</template>
|
||||
<StylesInspectorView
|
||||
v-show="currentTabbedView.key === '__styles'"
|
||||
/>
|
||||
<SavedStylesInspectorView
|
||||
v-show="currentTabbedView.key === '__styles'"
|
||||
:is-editing="isEditing"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -48,12 +52,18 @@ import Properties from './Properties.vue';
|
||||
import ObjectName from './ObjectName.vue';
|
||||
import InspectorViews from './InspectorViews.vue';
|
||||
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 {
|
||||
provide: {
|
||||
stylesManager: stylesManager
|
||||
},
|
||||
inject: ['openmct'],
|
||||
components: {
|
||||
StylesInspectorView,
|
||||
SavedStylesInspectorView,
|
||||
multipane,
|
||||
pane,
|
||||
Elements,
|
||||
@ -63,7 +73,10 @@ export default {
|
||||
InspectorViews
|
||||
},
|
||||
props: {
|
||||
'isEditing': Boolean
|
||||
isEditing: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
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';
|
||||
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
inject: ['openmct', 'stylesManager'],
|
||||
data() {
|
||||
return {
|
||||
selection: []
|
||||
@ -56,7 +56,8 @@ export default {
|
||||
this.component = new Vue({
|
||||
provide: {
|
||||
openmct: this.openmct,
|
||||
selection: selection
|
||||
selection: selection,
|
||||
stylesManager: this.stylesManager
|
||||
},
|
||||
el: viewContainer,
|
||||
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 */
|
||||
.c-inspector {
|
||||
// FilterField.vue
|
||||
|
Reference in New Issue
Block a user