[Inspector] Allow styles (including font and font size) to be saved and reused (#3432)

* working proto for font size

* wip

* Font styling

 - Base classes for font-size and font;
 - WIP!

* working data attribute for fontsize

* Font styling

 - Add `js-style-receiver` to markup, refine style targeting JS for
 better application of styles;
 - Refinements to font and size CSS;
 - WIP!

* Font styling

 - Redo CSS to use `data-*` attributes;
 - New `u-style-receiver` class for use as font-size and font-family CSS
 selector target;
 - New `js-style-receiver` class for use as JS target by ObjectView.vue;
 - New classes added to markup in all Open MCT views;
 - Changed font-size values from 'is-font-size--*' to just the number;
 - Some refinement to individual views to account for font-sizing
 capability;
 - Removed automatic font-size 13px being set by SubobjectView.vue;
 - WIP!

* working mixed styles

* Font styling

 - Added `u-style-receiver` to TelemetryView.vue;
 - Added `icon-font-size` to Font Size dropdown button;
 - TODO: better font-size icon;

* working font-family

* Font styling

 - Art for `icon-font-size` glyph updated;
 - Redefined glyph usage in some Layout toolbar buttons;
 - Updated font-size and font dropdown menus options text;

* Font styling

 - Refined font-size and font dropdown values;
 - Fixed toolbar-select-menu.vue to remove 'px' from non-specific option
  return;

* dont allow font styling on layouts that contain other layouts

* fix lint warning

* add sizing row

* fix bug with column width sizing

* fix bug with header style

* add saved styles inspector view

* WIP

* add vue component for selector

* WIP styles manager to communicate between vue components

* WIP saving and persisting styles

* no duplicate styles prevention

* fix props syntax

* WIP can apply conditional styles

* static styles do not work yet

* display border color in saved styles swatch

* allow deleting styles except default style

* WIP apply static style works but also to layout...

* prevent additional StylesView from being created

* delete style message

* change save order

* move applystyle to selector component

* rename for consistency

* naming refactor

* add style description

* update style properties only if they exist and do not erase properties

* refactor singleton usage

refactor save method

* show save and delete only on hover

* do not show delete icon if not in edit mode

* normalize styles before saving

prevent apply style if conditional and static styles are simultaneously selected

* remove default style

tweak selector display

* allow conditional and static styles to have saved style applied

limit saved styles to 20

* refactor styles manager

remove openmct dependency

use provide/inject

* resolve merge conflicts

* lint fix

* reorganize styles

* add font style editor to styles view

* save and display border correctly in saved styles view

* WIP add font styling controls to inspector styles view

* add font constants

* WIP refactor to provide reactive props

fix locked for edit

* WIP display consolidated font styles for selection in editor

* WIP font styles saved to layout

* WIP persisting font styles from inspector works

* fix styleable check

* move logic up to stylesview because save is two part

* apply font style to thumb

* there can be only one

* show font style for native views

* linting fix

* push stylesManager work to StylesView

* move method to computed

* move constant definition outside of function call

* Styling for saved styles functionality WIP

- Simplified and removed unnecessary markup;
- Standardized style applied to saved style element and toolbar control;
- Removed saved style expand arrow and description, replaced with item
title / tooltip approach;
- Standardized width of `c-style-thumb` element;
- Moved font size and style controls to the designed location;

* Styling for saved styles functionality WIP

- Layout and CSS normalization between style editor control and saved
style preview element;
- Control alignment refined;
- Moved font size and style controls to the designed location;

* Styling for saved styles functionality WIP

- Update font size icon art to normalize size;
- Sanding, tweaking, alignin and layout in style controls area of
Inspector;

* Styling for saved styles functionality WIP

- Hide the font size and style menu buttons unless the user is editing;

* remove font controls from toolbar

* turn styles tab into multipane element

* lint fix

* no font style should not be viewed as non-specific

* delete saved style by index not style

* cleanup

* view and inspector view updates on initial font change

* revert computed back to method

* set initial height

* fix test after removing 2 buttons from toolbar

* fix hidden lint error

* fix lint

Co-authored-by: Deep Tailor <deep.j.tailor@nasa.gov>
Co-authored-by: charlesh88 <charlesh88@gmail.com>
This commit is contained in:
David Tsay 2020-11-02 12:35:43 -08:00 committed by GitHub
parent 2401473012
commit a8228406de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 1307 additions and 133 deletions

View File

@ -19,7 +19,7 @@
this source code distribution or the Licensing information page available this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information. at runtime from the About dialog for additional information.
--> -->
<div class="c-clock l-time-display" ng-controller="ClockController as clock"> <div class="c-clock l-time-display u-style-receiver js-style-receiver" ng-controller="ClockController as clock">
<div class="c-clock__timezone"> <div class="c-clock__timezone">
{{clock.zone()}} {{clock.zone()}}
</div> </div>

View File

@ -19,7 +19,7 @@
this source code distribution or the Licensing information page available this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information. at runtime from the About dialog for additional information.
--> -->
<div class="c-timer is-{{timer.timerState}}" ng-controller="TimerController as timer"> <div class="c-timer u-style-receiver js-style-receiver is-{{timer.timerState}}" ng-controller="TimerController as timer">
<div class="c-timer__controls"> <div class="c-timer__controls">
<button ng-click="timer.clickStopButton()" <button ng-click="timer.clickStopButton()"
ng-hide="timer.timerState == 'stopped'" ng-hide="timer.timerState == 'stopped'"

View File

@ -21,7 +21,7 @@
*****************************************************************************/ *****************************************************************************/
<template> <template>
<div class="c-lad-table-wrapper"> <div class="c-lad-table-wrapper u-style-receiver js-style-receiver">
<table class="c-table c-lad-table"> <table class="c-table c-lad-table">
<thead> <thead>
<tr> <tr>

View File

@ -21,9 +21,10 @@
*****************************************************************************/ *****************************************************************************/
<template> <template>
<div class="c-style"> <div class="c-style has-local-controls c-toolbar">
<span :class="[ <div class="c-style__controls">
{ 'is-style-invisible': styleItem.style.isStyleInvisible }, <div :class="[
{ '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 ]"
@ -34,8 +35,8 @@
> >
ABC ABC
</span> </span>
</span> </div>
<span class="c-toolbar">
<toolbar-color-picker v-if="hasProperty(styleItem.style.border)" <toolbar-color-picker v-if="hasProperty(styleItem.style.border)"
class="c-style__toolbar-button--border-color u-menu-to--center" class="c-style__toolbar-button--border-color u-menu-to--center"
:options="borderColorOption" :options="borderColorOption"
@ -61,7 +62,14 @@
:options="isStyleInvisibleOption" :options="isStyleInvisibleOption"
@change="updateStyleValue" @change="updateStyleValue"
/> />
</span> </div>
<!-- Save Styles -->
<toolbar-button v-if="canSaveStyle"
class="c-style__toolbar-button--save c-local-controls--show-on-hover c-icon-button c-icon-button--major"
:options="saveOptions"
@click="saveItemStyle()"
/>
</div> </div>
</template> </template>
@ -80,12 +88,11 @@ export default {
ToolbarColorPicker, ToolbarColorPicker,
ToolbarToggleButton ToolbarToggleButton
}, },
inject: [ inject: ['openmct'],
'openmct'
],
props: { props: {
isEditing: { isEditing: {
type: Boolean type: Boolean,
required: true
}, },
mixedStyles: { mixedStyles: {
type: Array, type: Array,
@ -93,6 +100,10 @@ export default {
return []; return [];
} }
}, },
nonSpecificFontProperties: {
type: Array,
required: true
},
styleItem: { styleItem: {
type: Object, type: Object,
required: true required: true
@ -182,7 +193,16 @@ export default {
} }
] ]
}; };
},
saveOptions() {
return {
icon: 'icon-save',
title: 'Save style',
isEditing: this.isEditing
};
},
canSaveStyle() {
return this.isEditing && !this.mixedStyles.length && !this.nonSpecificFontProperties.length;
} }
}, },
methods: { methods: {
@ -216,6 +236,9 @@ export default {
} }
this.$emit('persist', this.styleItem, item.property); this.$emit('persist', this.styleItem, item.property);
},
saveItemStyle() {
this.$emit('save-style', this.itemStyle);
} }
} }
}; };

View File

@ -31,6 +31,11 @@
<div class="c-inspect-styles__header"> <div class="c-inspect-styles__header">
Object Style Object Style
</div> </div>
<FontStyleEditor
v-if="canStyleFont"
:font-style="consolidatedFontStyle"
@set-font-property="setFontProperty"
/>
<div class="c-inspect-styles__content"> <div class="c-inspect-styles__content">
<div v-if="staticStyle" <div v-if="staticStyle"
class="c-inspect-styles__style" class="c-inspect-styles__style"
@ -39,7 +44,9 @@
:style-item="staticStyle" :style-item="staticStyle"
:is-editing="allowEditing" :is-editing="allowEditing"
:mixed-styles="mixedStyles" :mixed-styles="mixedStyles"
:non-specific-font-properties="nonSpecificFontProperties"
@persist="updateStaticStyle" @persist="updateStaticStyle"
@save-style="saveStyle"
/> />
</div> </div>
<button <button
@ -58,10 +65,11 @@
</div> </div>
<div class="c-inspect-styles__content c-inspect-styles__condition-set"> <div class="c-inspect-styles__content c-inspect-styles__condition-set">
<a v-if="conditionSetDomainObject" <a v-if="conditionSetDomainObject"
class="c-object-label icon-conditional" class="c-object-label"
:href="navigateToPath" :href="navigateToPath"
@click="navigateOrPreview" @click="navigateOrPreview"
> >
<span class="c-object-label__type-icon icon-conditional"></span>
<span class="c-object-label__name">{{ conditionSetDomainObject.name }}</span> <span class="c-object-label__name">{{ conditionSetDomainObject.name }}</span>
</a> </a>
<template v-if="allowEditing"> <template v-if="allowEditing">
@ -80,6 +88,12 @@
</template> </template>
</div> </div>
<FontStyleEditor
v-if="canStyleFont"
:font-style="consolidatedFontStyle"
@set-font-property="setFontProperty"
/>
<div v-if="conditionsLoaded" <div v-if="conditionsLoaded"
class="c-inspect-styles__conditions" class="c-inspect-styles__conditions"
> >
@ -97,8 +111,10 @@
/> />
<style-editor class="c-inspect-styles__editor" <style-editor class="c-inspect-styles__editor"
:style-item="conditionStyle" :style-item="conditionStyle"
:non-specific-font-properties="nonSpecificFontProperties"
:is-editing="allowEditing" :is-editing="allowEditing"
@persist="updateConditionalStyle" @persist="updateConditionalStyle"
@save-style="saveStyle"
/> />
</div> </div>
</div> </div>
@ -108,6 +124,7 @@
<script> <script>
import FontStyleEditor from '@/ui/inspector/styles/FontStyleEditor.vue';
import StyleEditor from "./StyleEditor.vue"; import StyleEditor from "./StyleEditor.vue";
import PreviewAction from "@/ui/preview/PreviewAction.js"; import PreviewAction from "@/ui/preview/PreviewAction.js";
import { getApplicableStylesForItem, getConsolidatedStyleValues, getConditionSetIdentifierForItem } from "@/plugins/condition/utils/styleUtils"; import { getApplicableStylesForItem, getConsolidatedStyleValues, getConditionSetIdentifierForItem } from "@/plugins/condition/utils/styleUtils";
@ -116,16 +133,30 @@ import ConditionError from "@/plugins/condition/components/ConditionError.vue";
import ConditionDescription from "@/plugins/condition/components/ConditionDescription.vue"; import ConditionDescription from "@/plugins/condition/components/ConditionDescription.vue";
import Vue from 'vue'; import Vue from 'vue';
const NON_SPECIFIC = '??';
const NON_STYLEABLE_CONTAINER_TYPES = [
'layout',
'flexible-layout',
'tabs'
];
const NON_STYLEABLE_LAYOUT_ITEM_TYPES = [
'line-view',
'box-view',
'image-view'
];
export default { export default {
name: 'StylesView', name: 'StylesView',
components: { components: {
FontStyleEditor,
StyleEditor, StyleEditor,
ConditionError, ConditionError,
ConditionDescription ConditionDescription
}, },
inject: [ inject: [
'openmct', 'openmct',
'selection' 'selection',
'stylesManager'
], ],
data() { data() {
return { return {
@ -139,19 +170,80 @@ export default {
conditionsLoaded: false, conditionsLoaded: false,
navigateToPath: '', navigateToPath: '',
selectedConditionId: '', selectedConditionId: '',
locked: false items: [],
domainObject: undefined,
consolidatedFontStyle: {}
}; };
}, },
computed: { computed: {
locked() {
return this.selection.some(selectionPath => {
const self = selectionPath[0].context.item;
const parent = selectionPath.length > 1 ? selectionPath[1].context.item : undefined;
return (self && self.locked) || (parent && parent.locked);
});
},
allowEditing() { allowEditing() {
return this.isEditing && !this.locked; return this.isEditing && !this.locked;
},
styleableFontItems() {
return this.selection.filter(selectionPath => {
const item = selectionPath[0].context.item;
const itemType = item && item.type;
const layoutItem = selectionPath[0].context.layoutItem;
const layoutItemType = layoutItem && layoutItem.type;
if (itemType && NON_STYLEABLE_CONTAINER_TYPES.includes(itemType)) {
return false;
}
if (layoutItemType && NON_STYLEABLE_LAYOUT_ITEM_TYPES.includes(layoutItemType)) {
return false;
}
return true;
});
},
computedconsolidatedFontStyle() {
let consolidatedFontStyle;
const styles = [];
this.styleableFontItems.forEach(styleable => {
const fontStyle = this.getFontStyle(styleable[0]);
styles.push(fontStyle);
});
if (styles.length) {
const hasConsolidatedFontSize = styles.length && styles.every((fontStyle, i, arr) => fontStyle.fontSize === arr[0].fontSize);
const hasConsolidatedFont = styles.length && styles.every((fontStyle, i, arr) => fontStyle.font === arr[0].font);
consolidatedFontStyle = {
fontSize: hasConsolidatedFontSize ? styles[0].fontSize : NON_SPECIFIC,
font: hasConsolidatedFont ? styles[0].font : NON_SPECIFIC
};
}
return consolidatedFontStyle;
},
nonSpecificFontProperties() {
if (!this.consolidatedFontStyle) {
return [];
}
return Object.keys(this.consolidatedFontStyle).filter(property => this.consolidatedFontStyle[property] === NON_SPECIFIC);
},
canStyleFont() {
return this.styleableFontItems.length && this.allowEditing;
} }
}, },
destroyed() { destroyed() {
this.removeListeners(); this.removeListeners();
this.openmct.editor.off('isEditing', this.setEditState);
this.stylesManager.off('styleSelected', this.applyStyleToSelection);
}, },
mounted() { mounted() {
this.items = [];
this.previewAction = new PreviewAction(this.openmct); this.previewAction = new PreviewAction(this.openmct);
this.isMultipleSelection = this.selection.length > 1; this.isMultipleSelection = this.selection.length > 1;
this.getObjectsAndItemsFromSelection(); this.getObjectsAndItemsFromSelection();
@ -166,7 +258,10 @@ export default {
this.initializeStaticStyle(); this.initializeStaticStyle();
} }
this.setConsolidatedFontStyle();
this.openmct.editor.on('isEditing', this.setEditState); this.openmct.editor.on('isEditing', this.setEditState);
this.stylesManager.on('styleSelected', this.applyStyleToSelection);
}, },
methods: { methods: {
getObjectStyles() { getObjectStyles() {
@ -178,10 +273,10 @@ export default {
} }
} else if (this.items.length) { } else if (this.items.length) {
const itemId = this.items[0].id; const itemId = this.items[0].id;
if (this.domainObject.configuration && this.domainObject.configuration.objectStyles && this.domainObject.configuration.objectStyles[itemId]) { if (this.domainObject && this.domainObject.configuration && this.domainObject.configuration.objectStyles && this.domainObject.configuration.objectStyles[itemId]) {
objectStyles = this.domainObject.configuration.objectStyles[itemId]; objectStyles = this.domainObject.configuration.objectStyles[itemId];
} }
} else if (this.domainObject.configuration && this.domainObject.configuration.objectStyles) { } else if (this.domainObject && this.domainObject.configuration && this.domainObject.configuration.objectStyles) {
objectStyles = this.domainObject.configuration.objectStyles; objectStyles = this.domainObject.configuration.objectStyles;
} }
@ -247,13 +342,8 @@ export default {
this.selection.forEach((selectionItem) => { this.selection.forEach((selectionItem) => {
const item = selectionItem[0].context.item; const item = selectionItem[0].context.item;
const layoutItem = selectionItem[0].context.layoutItem; const layoutItem = selectionItem[0].context.layoutItem;
const layoutDomainObject = selectionItem[0].context.item;
const isChildItem = selectionItem.length > 1; const isChildItem = selectionItem.length > 1;
if (layoutDomainObject && layoutDomainObject.locked) {
this.locked = true;
}
if (!isChildItem) { if (!isChildItem) {
domainObject = item; domainObject = item;
itemStyle = getApplicableStylesForItem(item); itemStyle = getApplicableStylesForItem(item);
@ -287,7 +377,7 @@ export default {
const {styles, mixedStyles} = getConsolidatedStyleValues(itemInitialStyles); const {styles, mixedStyles} = getConsolidatedStyleValues(itemInitialStyles);
this.initialStyles = styles; this.initialStyles = styles;
this.mixedStyles = mixedStyles; this.mixedStyles = mixedStyles;
// main layout
this.domainObject = domainObject; this.domainObject = domainObject;
this.removeListeners(); this.removeListeners();
if (this.domainObject) { if (this.domainObject) {
@ -310,6 +400,7 @@ export default {
isKeyItemId(key) { isKeyItemId(key) {
return (key !== 'styles') return (key !== 'styles')
&& (key !== 'staticStyle') && (key !== 'staticStyle')
&& (key !== 'fontStyle')
&& (key !== 'defaultConditionId') && (key !== 'defaultConditionId')
&& (key !== 'selectedConditionId') && (key !== 'selectedConditionId')
&& (key !== 'conditionSetIdentifier'); && (key !== 'conditionSetIdentifier');
@ -649,6 +740,124 @@ export default {
}, },
persist(domainObject, style) { persist(domainObject, style) {
this.openmct.objects.mutate(domainObject, 'configuration.objectStyles', style); this.openmct.objects.mutate(domainObject, 'configuration.objectStyles', style);
},
applyStyleToSelection(style) {
if (!this.allowEditing) {
return;
}
this.updateSelectionFontStyle(style);
this.updateSelectionStyle(style);
},
updateSelectionFontStyle(style) {
const fontSizeProperty = {
fontSize: style.fontSize
};
const fontProperty = {
font: style.font
};
this.setFontProperty(fontSizeProperty);
this.setFontProperty(fontProperty);
},
updateSelectionStyle(style) {
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);
}
});
}
},
saveStyle(style) {
const styleToSave = {
...style,
...this.consolidatedFontStyle
};
this.stylesManager.save(styleToSave);
},
setConsolidatedFontStyle() {
const styles = [];
this.styleableFontItems.forEach(styleable => {
const fontStyle = this.getFontStyle(styleable[0]);
styles.push(fontStyle);
});
if (styles.length) {
const hasConsolidatedFontSize = styles.length && styles.every((fontStyle, i, arr) => fontStyle.fontSize === arr[0].fontSize);
const hasConsolidatedFont = styles.length && styles.every((fontStyle, i, arr) => fontStyle.font === arr[0].font);
const fontSize = hasConsolidatedFontSize ? styles[0].fontSize : NON_SPECIFIC;
const font = hasConsolidatedFont ? styles[0].font : NON_SPECIFIC;
this.$set(this.consolidatedFontStyle, 'fontSize', fontSize);
this.$set(this.consolidatedFontStyle, 'font', font);
}
},
getFontStyle(selectionPath) {
const item = selectionPath.context.item;
const layoutItem = selectionPath.context.layoutItem;
let fontStyle = item && item.configuration && item.configuration.fontStyle;
// support for legacy where font styling in layouts only
if (!fontStyle) {
fontStyle = {
fontSize: layoutItem && layoutItem.fontSize || 'default',
font: layoutItem && layoutItem.font || 'default'
};
}
return fontStyle;
},
setFontProperty(fontStyleObject) {
let layoutDomainObject;
const [property, value] = Object.entries(fontStyleObject)[0];
this.styleableFontItems.forEach(styleable => {
if (!this.isLayoutObject(styleable)) {
const fontStyle = this.getFontStyle(styleable[0]);
fontStyle[property] = value;
this.openmct.objects.mutate(styleable[0].context.item, 'configuration.fontStyle', fontStyle);
} else {
// all layoutItems in this context will share same parent layout
if (!layoutDomainObject) {
layoutDomainObject = styleable[1].context.item;
}
// save layout item font style to parent layout configuration
const layoutItemIndex = styleable[0].context.index;
const layoutItemConfiguration = layoutDomainObject.configuration.items[layoutItemIndex];
layoutItemConfiguration[property] = value;
}
});
if (layoutDomainObject) {
this.openmct.objects.mutate(layoutDomainObject, 'configuration.items', layoutDomainObject.configuration.items);
}
// sync vue component on font update
this.$set(this.consolidatedFontStyle, property, value);
},
isLayoutObject(selectionPath) {
const layoutItemType = selectionPath[0].context.layoutItem && selectionPath[0].context.layoutItem.type;
return layoutItemType && layoutItemType !== 'subobject-view';
} }
} }
}; };

View File

@ -40,9 +40,11 @@
} }
&__condition-set { &__condition-set {
align-items: baseline;
border-bottom: 1px solid $colorInteriorBorder;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; padding-bottom: $interiorMargin;
.c-object-label { .c-object-label {
flex: 1 1 auto; flex: 1 1 auto;
@ -53,7 +55,10 @@
} }
} }
&__style, &__style {
padding-bottom: $interiorMargin;
}
&__condition { &__condition {
padding: $interiorMargin; padding: $interiorMargin;
} }

View File

@ -22,7 +22,7 @@
<template> <template>
<component :is="urlDefined ? 'a' : 'span'" <component :is="urlDefined ? 'a' : 'span'"
class="c-condition-widget" class="c-condition-widget u-style-receiver js-style-receiver"
:href="urlDefined ? internalDomainObject.url : null" :href="urlDefined ? internalDomainObject.url : null"
> >
<div class="c-condition-widget__label"> <div class="c-condition-widget__label">

View File

@ -73,7 +73,6 @@ define(['lodash'], function (_) {
] ]
} }
}; };
const VIEW_TYPES = { const VIEW_TYPES = {
'telemetry-view': { 'telemetry-view': {
value: 'telemetry-view', value: 'telemetry-view',
@ -96,7 +95,6 @@ define(['lodash'], function (_) {
class: 'icon-tabular-realtime' class: 'icon-tabular-realtime'
} }
}; };
const APPLICABLE_VIEWS = { const APPLICABLE_VIEWS = {
'telemetry-view': [ 'telemetry-view': [
VIEW_TYPES['telemetry.plot.overlay'], VIEW_TYPES['telemetry.plot.overlay'],
@ -390,29 +388,6 @@ define(['lodash'], function (_) {
} }
} }
function getTextSizeMenu(selectedParent, selection) {
const TEXT_SIZE = [8, 9, 10, 11, 12, 13, 14, 15, 16, 20, 24, 30, 36, 48, 72, 96, 128];
return {
control: "select-menu",
domainObject: selectedParent,
applicableSelectedItems: selection.filter(selectionPath => {
let type = selectionPath[0].context.layoutItem.type;
return type === 'text-view' || type === 'telemetry-view';
}),
property: function (selectionPath) {
return getPath(selectionPath) + ".size";
},
title: "Set text size",
options: TEXT_SIZE.map(size => {
return {
value: size + "px"
};
})
};
}
function getTextButton(selectedParent, selection) { function getTextButton(selectedParent, selection) {
return { return {
control: "button", control: "button",
@ -423,7 +398,7 @@ define(['lodash'], function (_) {
property: function (selectionPath) { property: function (selectionPath) {
return getPath(selectionPath); return getPath(selectionPath);
}, },
icon: "icon-font", icon: "icon-pencil",
title: "Edit text properties", title: "Edit text properties",
dialog: DIALOG_FORM.text dialog: DIALOG_FORM.text
}; };
@ -678,7 +653,6 @@ define(['lodash'], function (_) {
'display-mode': [], 'display-mode': [],
'telemetry-value': [], 'telemetry-value': [],
'style': [], 'style': [],
'text-style': [],
'position': [], 'position': [],
'duplicate': [], 'duplicate': [],
'unit-toggle': [], 'unit-toggle': [],
@ -729,12 +703,6 @@ define(['lodash'], function (_) {
toolbar['telemetry-value'] = [getTelemetryValueMenu(selectionPath, selectedObjects)]; toolbar['telemetry-value'] = [getTelemetryValueMenu(selectionPath, selectedObjects)];
} }
if (toolbar['text-style'].length === 0) {
toolbar['text-style'] = [
getTextSizeMenu(selectedParent, selectedObjects)
];
}
if (toolbar.position.length === 0) { if (toolbar.position.length === 0) {
toolbar.position = [ toolbar.position = [
getStackOrder(selectedParent, selectionPath), getStackOrder(selectedParent, selectionPath),
@ -760,12 +728,6 @@ define(['lodash'], function (_) {
} }
} }
} else if (layoutItem.type === 'text-view') { } else if (layoutItem.type === 'text-view') {
if (toolbar['text-style'].length === 0) {
toolbar['text-style'] = [
getTextSizeMenu(selectedParent, selectedObjects)
];
}
if (toolbar.position.length === 0) { if (toolbar.position.length === 0) {
toolbar.position = [ toolbar.position = [
getStackOrder(selectedParent, selectionPath), getStackOrder(selectedParent, selectionPath),

View File

@ -29,7 +29,7 @@
@endMove="() => $emit('endMove')" @endMove="() => $emit('endMove')"
> >
<div <div
class="c-box-view" class="c-box-view u-style-receiver js-style-receiver"
:class="[styleClass]" :class="[styleClass]"
:style="style" :style="style"
></div> ></div>

View File

@ -22,7 +22,7 @@
<template> <template>
<div <div
class="l-layout" class="l-layout u-style-receiver js-style-receiver"
:class="{ :class="{
'is-multi-selected': selectedLayoutItems.length > 1, 'is-multi-selected': selectedLayoutItems.length > 1,
'allow-editing': isEditing 'allow-editing': isEditing

View File

@ -81,6 +81,7 @@ export default {
style() { style() {
let backgroundImage = 'url(' + this.item.url + ')'; let backgroundImage = 'url(' + this.item.url + ')';
let border = '1px solid ' + this.item.stroke; let border = '1px solid ' + this.item.stroke;
if (this.itemStyle) { if (this.itemStyle) {
if (this.itemStyle.imageUrl !== undefined) { if (this.itemStyle.imageUrl !== undefined) {
backgroundImage = 'url(' + this.itemStyle.imageUrl + ')'; backgroundImage = 'url(' + this.itemStyle.imageUrl + ')';

View File

@ -35,6 +35,8 @@
:object-path="currentObjectPath" :object-path="currentObjectPath"
:has-frame="item.hasFrame" :has-frame="item.hasFrame"
:show-edit-view="false" :show-edit-view="false"
:layout-font-size="item.fontSize"
:layout-font="item.font"
/> />
</layout-frame> </layout-frame>
</template> </template>
@ -73,6 +75,8 @@ export default {
y: position[1], y: position[1],
identifier: domainObject.identifier, identifier: domainObject.identifier,
hasFrame: hasFrameByDefault(domainObject.type), hasFrame: hasFrameByDefault(domainObject.type),
fontSize: 'default',
font: 'default',
viewKey viewKey
}; };
}, },

View File

@ -30,12 +30,14 @@
> >
<div <div
v-if="domainObject" v-if="domainObject"
class="c-telemetry-view" class="u-style-receiver c-telemetry-view"
:class="{ :class="{
styleClass, styleClass,
'is-missing': domainObject.status === 'missing' 'is-missing': domainObject.status === 'missing'
}" }"
:style="styleObject" :style="styleObject"
:data-font-size="item.fontSize"
:data-font="item.font"
@contextmenu.prevent="showContextMenu" @contextmenu.prevent="showContextMenu"
> >
<div class="is-missing__indicator" <div class="is-missing__indicator"
@ -95,7 +97,8 @@ export default {
stroke: "", stroke: "",
fill: "", fill: "",
color: "", color: "",
size: "13px" fontSize: 'default',
font: 'default'
}; };
}, },
inject: ['openmct', 'objectPath'], inject: ['openmct', 'objectPath'],
@ -150,10 +153,15 @@ export default {
return unit; return unit;
}, },
styleObject() { styleObject() {
return Object.assign({}, { let size;
fontSize: this.item.size //for legacy size support
}, this.itemStyle); if (!this.item.fontSize) {
size = this.item.size;
}
return Object.assign({}, {
size
}, this.itemStyle);
}, },
fieldName() { fieldName() {
return this.valueMetadata && this.valueMetadata.name; return this.valueMetadata && this.valueMetadata.name;

View File

@ -29,7 +29,9 @@
@endMove="() => $emit('endMove')" @endMove="() => $emit('endMove')"
> >
<div <div
class="c-text-view" class="c-text-view u-style-receiver js-style-receiver"
:data-font-size="item.fontSize"
:data-font="item.font"
:class="[styleClass]" :class="[styleClass]"
:style="style" :style="style"
> >
@ -47,13 +49,14 @@ export default {
return { return {
fill: '', fill: '',
stroke: '', stroke: '',
size: '13px',
color: '', color: '',
x: 1, x: 1,
y: 1, y: 1,
width: 10, width: 10,
height: 5, height: 5,
text: element.text text: element.text,
fontSize: 'default',
font: 'default'
}; };
}, },
inject: ['openmct'], inject: ['openmct'],
@ -84,8 +87,14 @@ export default {
}, },
computed: { computed: {
style() { style() {
let size;
//legacy size support
if (!this.item.fontSize) {
size = this.item.size;
}
return Object.assign({ return Object.assign({
fontSize: this.item.size size
}, this.itemStyle); }, this.itemStyle);
} }
}, },

View File

@ -27,6 +27,7 @@ export default {
inject: ['openmct'], inject: ['openmct'],
data() { data() {
return { return {
objectStyle: undefined,
itemStyle: undefined, itemStyle: undefined,
styleClass: '' styleClass: ''
}; };

View File

@ -341,7 +341,7 @@ describe('the plugin', function () {
it('provides controls including separators', () => { it('provides controls including separators', () => {
const displayLayoutToolbar = openmct.toolbars.get(selection); const displayLayoutToolbar = openmct.toolbars.get(selection);
expect(displayLayoutToolbar.length).toBe(11); expect(displayLayoutToolbar.length).toBe(9);
}); });
}); });
}); });

View File

@ -10,7 +10,8 @@
<div class="h-local-controls h-local-controls--overlay-content c-local-controls--show-on-hover c-image-controls__controls"> <div class="h-local-controls h-local-controls--overlay-content c-local-controls--show-on-hover c-image-controls__controls">
<span class="c-image-controls__sliders" <span class="c-image-controls__sliders"
draggable="true" draggable="true"
@dragstart="startDrag"> @dragstart="startDrag"
>
<div class="c-image-controls__slider-wrapper icon-brightness"> <div class="c-image-controls__slider-wrapper icon-brightness">
<input v-model="filters.brightness" <input v-model="filters.brightness"
type="range" type="range"
@ -59,7 +60,7 @@
<div class="c-imagery__control-bar"> <div class="c-imagery__control-bar">
<div class="c-imagery__time"> <div class="c-imagery__time">
<div class="c-imagery__timestamp">{{ time }}</div> <div class="c-imagery__timestamp u-style-receiver js-style-receiver">{{ time }}</div>
<div <div
v-if="canTrackDuration" v-if="canTrackDuration"
:class="{'c-imagery--new': isImageNew && !refreshCSS}" :class="{'c-imagery--new': isImageNew && !refreshCSS}"

View File

@ -46,7 +46,7 @@
</button> </button>
</div> </div>
<div class="l-view-section"> <div class="l-view-section u-style-receiver js-style-receiver">
<div class="c-loading--overlay loading" <div class="c-loading--overlay loading"
ng-show="!!pending"></div> ng-show="!!pending"></div>
<mct-plot config="controller.config" <mct-plot config="controller.config"

View File

@ -45,7 +45,7 @@
title="Toggle grid lines"> title="Toggle grid lines">
</button> </button>
</div> </div>
<div class="l-view-section"> <div class="l-view-section u-style-receiver js-style-receiver">
<div class="c-loading--overlay loading" <div class="c-loading--overlay loading"
ng-show="!!currentRequest.pending"></div> ng-show="!!currentRequest.pending"></div>
<div class="gl-plot child-frame u-inspectable" <div class="gl-plot child-frame u-inspectable"

View File

@ -0,0 +1,54 @@
<template>
<tr class="c-telemetry-table__sizing-tr"><td>SIZING ROW</td></tr>
</template>
<script>
export default {
props: {
isEditing: {
type: Boolean,
default: false
}
},
watch: {
isEditing: function (isEditing) {
if (isEditing) {
this.pollForRowHeight();
} else {
this.clearPoll();
}
}
},
mounted() {
this.$nextTick().then(() => {
this.height = this.$el.offsetHeight;
this.$emit('change-height', this.height);
});
if (this.isEditing) {
this.pollForRowHeight();
}
},
destroyed() {
this.clearPoll();
},
methods: {
pollForRowHeight() {
this.clearPoll();
this.pollID = window.setInterval(this.heightPoll, 300);
},
clearPoll() {
if (this.pollID) {
window.clearInterval(this.pollID);
this.pollID = undefined;
}
},
heightPoll() {
let height = this.$el.offsetHeight;
if (height !== this.height) {
this.$emit('change-height', height);
this.height = height;
}
}
}
};
</script>

View File

@ -9,6 +9,9 @@
.c-telemetry-table { .c-telemetry-table {
// Table that displays telemetry in a scrolling body area // Table that displays telemetry in a scrolling body area
@include fontAndSize();
display: flex; display: flex;
flex-flow: column nowrap; flex-flow: column nowrap;
justify-content: flex-start; justify-content: flex-start;
@ -108,7 +111,7 @@
display: flex; // flex-flow defaults to row nowrap (which is what we want) so no need to define display: flex; // flex-flow defaults to row nowrap (which is what we want) so no need to define
align-items: stretch; align-items: stretch;
position: absolute; position: absolute;
height: 18px; // Needed when a row has empty values in its cells min-height: 18px; // Needed when a row has empty values in its cells
.is-editing .l-layout__frame & { .is-editing .l-layout__frame & {
pointer-events: none; pointer-events: none;
@ -151,6 +154,12 @@
} }
} }
&__sizing-tr {
// A row element used to determine sizing of rows based on font size
visibility: hidden;
pointer-events: none;
}
&__footer { &__footer {
$pt: 2px; $pt: 2px;
border-top: 1px solid $colorInteriorBorder; border-top: 1px solid $colorInteriorBorder;

View File

@ -125,7 +125,7 @@
<!-- alternate controlbar end --> <!-- alternate controlbar end -->
<div <div
class="c-table c-telemetry-table c-table--filterable c-table--sortable has-control-bar" class="c-table c-telemetry-table c-table--filterable c-table--sortable has-control-bar u-style-receiver js-style-receiver"
:class="{ :class="{
'loading': loading, 'loading': loading,
'is-paused' : paused 'is-paused' : paused
@ -234,6 +234,10 @@
class="c-telemetry-table__sizing js-telemetry-table__sizing" class="c-telemetry-table__sizing js-telemetry-table__sizing"
:style="sizingTableWidth" :style="sizingTableWidth"
> >
<sizing-row
:is-editing="isEditing"
@change-height="setRowHeight"
/>
<tr> <tr>
<template v-for="(title, key) in headers"> <template v-for="(title, key) in headers">
<th <th
@ -270,6 +274,7 @@ import TableFooterIndicator from './table-footer-indicator.vue';
import CSVExporter from '../../../exporters/CSVExporter.js'; import CSVExporter from '../../../exporters/CSVExporter.js';
import _ from 'lodash'; import _ from 'lodash';
import ToggleSwitch from '../../../ui/components/ToggleSwitch.vue'; import ToggleSwitch from '../../../ui/components/ToggleSwitch.vue';
import SizingRow from './sizing-row.vue';
const VISIBLE_ROW_COUNT = 100; const VISIBLE_ROW_COUNT = 100;
const ROW_HEIGHT = 17; const ROW_HEIGHT = 17;
@ -282,7 +287,8 @@ export default {
TableColumnHeader, TableColumnHeader,
search, search,
TableFooterIndicator, TableFooterIndicator,
ToggleSwitch ToggleSwitch,
SizingRow
}, },
inject: ['table', 'openmct', 'objectPath'], inject: ['table', 'openmct', 'objectPath'],
props: { props: {
@ -506,7 +512,7 @@ export default {
let columnWidths = {}; let columnWidths = {};
let totalWidth = 0; let totalWidth = 0;
let headerKeys = Object.keys(this.headers); let headerKeys = Object.keys(this.headers);
let sizingTableRow = this.sizingTable.children[0]; let sizingTableRow = this.sizingTable.children[1];
let sizingCells = sizingTableRow.children; let sizingCells = sizingTableRow.children;
headerKeys.forEach((headerKey, headerIndex, array) => { headerKeys.forEach((headerKey, headerIndex, array) => {
@ -901,6 +907,12 @@ export default {
this.isAutosizeEnabled = true; this.isAutosizeEnabled = true;
this.$nextTick().then(this.calculateColumnWidths); this.$nextTick().then(this.calculateColumnWidths);
},
setRowHeight(height) {
this.rowHeight = height;
this.setHeight();
this.calculateTableSize();
this.clearRowsAndRerender();
} }
} }
}; };

View File

@ -103,6 +103,8 @@ $colorProgressBarHolder: rgba(black, 0.1);
$colorProgressBar: #0085ad; $colorProgressBar: #0085ad;
$progressAnimW: 500px; $progressAnimW: 500px;
$progressBarMinH: 6px; $progressBarMinH: 6px;
/************************** FONT STYLING */
$listFontSizes: 8,9,10,11,12,13,14,16,18,20,24,28,32,36,42,48,72,96,128;
/************************** GLYPH CHAR UNICODES */ /************************** GLYPH CHAR UNICODES */
$glyph-icon-alert-rect: '\e900'; $glyph-icon-alert-rect: '\e900';

View File

@ -113,6 +113,10 @@ button {
} }
} }
.c-icon-button--disabled {
@include cClickIconButtonLayout();
}
.c-icon-link { .c-icon-link {
&:before { &:before {
// Icon // Icon
@ -121,8 +125,8 @@ button {
} }
.c-icon-button { .c-icon-button {
&__label { [class*='label'] {
margin-left: $interiorMargin; opacity: 0.6;
} }
&--mixed { &--mixed {
@ -639,6 +643,7 @@ select {
} }
&__item-none { &__item-none {
@include userSelectNone();
flex: 0 0 auto; flex: 0 0 auto;
display: flex; display: flex;
align-items: center; align-items: center;
@ -760,6 +765,12 @@ select {
color: $editUIBaseColorFg !important; color: $editUIBaseColorFg !important;
} }
&--menu {
$p: 4px;
padding-top: $p;
padding-bottom: $p;
}
&--swatched { &--swatched {
padding-bottom: floor($pTB / 2); padding-bottom: floor($pTB / 2);
width: 2em; // Standardize the width width: 2em; // Standardize the width
@ -841,17 +852,52 @@ select {
display: flex; display: flex;
flex: 1 1 auto; flex: 1 1 auto;
align-items: center; align-items: center;
justify-content: space-between;
&__controls {
// Holds thumb, icon buttons
display: flex;
flex: 1 0 auto;
> * + * { margin-left: $interiorMargin; } > * + * { margin-left: $interiorMargin; }
} }
&__button-save,
&__button-delete {
// Holds save and delete buttons accordingly
flex: 0 0 auto;
}
&--saved {
border-radius: $controlCr;
padding: $interiorMargin !important;
cursor: pointer;
@include hover {
background: rgba($editUIBaseColorHov, 0.3);
}
.c-style__controls {
[class*='button'] {
pointer-events: none;
&:before {
opacity: $controlDisabledOpacity;
}
}
}
}
}
.c-style-thumb { .c-style-thumb {
background-size: cover; background-size: cover;
border: 1px solid transparent; border: 1px solid transparent;
border-radius: $basicCr; border-radius: $basicCr;
box-shadow: rgba($colorBodyFg, 0.4) 0 0 3px; box-shadow: rgba($colorBodyFg, 0.4) 0 0 3px;
flex: 0 0 auto; flex: 0 0 auto;
padding: $interiorMargin $interiorMarginLg; padding: $interiorMargin;
text-align: center;
width: 50px;
&--mixed { &--mixed {
@include mixedBg(); @include mixedBg();

View File

@ -96,6 +96,37 @@ body.desktop {
} }
} }
/******************************************************** FONTS */
@mixin fontAndSize() {
@each $size in $listFontSizes {
&[data-font-size="#{$size}"] {
font-size: #{$size}px;
// Set row heights in telemetry tables
tr {
min-height: #{$size + ($tabularTdPadTB * 2)};
}
}
}
&[data-font*="bold"] {
font-weight: bold;
}
&[data-font*="narrow"] {
font-family: 'Arial Narrow', sans-serif;
}
&[data-font*="monospace"] {
font-family: 'Andale Mono', sans-serif;
}
}
.u-style-receiver {
@include fontAndSize();
}
/******************************************************** HTML ENTITIES */ /******************************************************** HTML ENTITIES */
a { a {
color: $colorA; color: $colorA;
@ -227,7 +258,7 @@ body.desktop .has-local-controls {
} }
/******************************************************** STATES */ /******************************************************** STATES */
@mixin spinner($b: 5px, $c: $colorKey) { @mixin spinner($b: 5, $c: $colorKey) {
animation-name: rotation-centered; animation-name: rotation-centered;
animation-duration: 0.5s; animation-duration: 0.5s;
animation-iteration-count: infinite; animation-iteration-count: infinite;
@ -277,7 +308,7 @@ body.desktop .has-local-controls {
} }
&.c-tree__item { &.c-tree__item {
$d: $waitSpinnerTreeD; $d: $waitSpinnerTreeD;
$spinnerL: 19px + $d/2; $spinnerL: 19 + $d/2;
display: flex; display: flex;
align-items: center; align-items: center;

View File

@ -52,6 +52,7 @@
$ctrlW: 22px; $ctrlW: 22px;
&__controls { &__controls {
font-size: 1rem !important;
margin-right: 0; margin-right: 0;
min-width: 0; min-width: 0;
overflow: hidden; overflow: hidden;
@ -62,7 +63,7 @@
} }
&__direction { &__direction {
font-size: 0.9em; font-size: 0.9rem !important;
margin-right: $interiorMargin; margin-right: $interiorMargin;
} }

View File

@ -534,7 +534,7 @@
} }
&[class*="--major"] { &[class*="--major"] {
color: $colorKey; color: $colorBtnMajorBg !important;
} }
} }

View File

@ -2,7 +2,7 @@
"metadata": { "metadata": {
"name": "Open MCT Symbols 16px", "name": "Open MCT Symbols 16px",
"lastOpened": 0, "lastOpened": 0,
"created": 1597943624771 "created": 1602779919972
}, },
"iconSets": [ "iconSets": [
{ {
@ -752,7 +752,7 @@
"tempChar": "" "tempChar": ""
}, },
{ {
"order": 114, "order": 194,
"id": 4, "id": 4,
"name": "icon-font-size", "name": "icon-font-size",
"prevSize": 24, "prevSize": 24,
@ -2718,16 +2718,25 @@
{ {
"id": 4, "id": 4,
"paths": [ "paths": [
"M842.841 380.048h-120.956l-52.382 139.676 52.918 141.12 59.942-159.84 62.361 166.314h-119.884l34.019 90.717h119.884l39.695 105.836h105.836l-181.434-483.823z", "M1226.4 320h-176l-76.22 203.24 77 205.34 87.22-232.58 90.74 242h-174.44l49.5 132h174.44l57.76 154h154l-264-704z",
"M263.903 160.129l-263.903 703.742h153.944l57.729-153.944h280.397l57.729 153.944h153.944l-263.903-703.742zM261.154 577.976l90.717-241.911 90.717 241.911z" "M384 0l-384 1024h224l84-224h408l84 224h224l-384-1024zM380 608l132-352 132 352z"
],
"attrs": [
{},
{}
], ],
"attrs": [],
"grid": 16, "grid": 16,
"tags": [ "tags": [
"icon-font-size-alt1" "icon-font-size-alt1"
], ],
"width": 1504,
"isMulticolor": false,
"isMulticolor2": false,
"colorPermutations": { "colorPermutations": {
"12552552551": [] "12552552551": [
{},
{}
]
} }
}, },
{ {

View File

@ -100,7 +100,7 @@
<glyph unicode="&#xea2c;" glyph-name="icon-frame-hide" d="M128 642h420l104 128h-652v-802.4l128 157.4zM896 2h-420l-104-128h652v802.4l-128-157.4zM832 834l-832-1024h192l832 1024zM392 450l104 128h-304v-128z" /> <glyph unicode="&#xea2c;" glyph-name="icon-frame-hide" d="M128 642h420l104 128h-652v-802.4l128 157.4zM896 2h-420l-104-128h652v802.4l-128-157.4zM832 834l-832-1024h192l832 1024zM392 450l104 128h-304v-128z" />
<glyph unicode="&#xea2d;" glyph-name="icon-import" d="M832 639.6v-639.4c0-0.2-0.2-0.2-0.4-0.4h-319.6v-192h320c105.6 0 192 86.4 192 192v640.2c0 105.6-86.4 192-192 192h-320v-192h319.6c0.2 0 0.4-0.2 0.4-0.4zM192 128v-192l384 384-384 384v-192h-192v-384z" /> <glyph unicode="&#xea2d;" glyph-name="icon-import" d="M832 639.6v-639.4c0-0.2-0.2-0.2-0.4-0.4h-319.6v-192h320c105.6 0 192 86.4 192 192v640.2c0 105.6-86.4 192-192 192h-320v-192h319.6c0.2 0 0.4-0.2 0.4-0.4zM192 128v-192l384 384-384 384v-192h-192v-384z" />
<glyph unicode="&#xea2e;" glyph-name="icon-export" d="M192 0.34v639.32l0.34 0.34h319.66v192h-320c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h320v192h-319.66zM1024 320l-384 384v-192h-192v-384h192v-192l384 384z" /> <glyph unicode="&#xea2e;" glyph-name="icon-export" d="M192 0.34v639.32l0.34 0.34h319.66v192h-320c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h320v192h-319.66zM1024 320l-384 384v-192h-192v-384h192v-192l384 384z" />
<glyph unicode="&#xea2f;" glyph-name="icon-font-size" d="M842.841 451.952h-120.956l-52.382-139.676 52.918-141.12 59.942 159.84 62.361-166.314h-119.884l34.019-90.717h119.884l39.695-105.836h105.836l-181.434 483.823zM263.903 671.871l-263.903-703.742h153.944l57.729 153.944h280.397l57.729-153.944h153.944l-263.903 703.742zM261.154 254.024l90.717 241.911 90.717-241.911z" /> <glyph unicode="&#xea2f;" glyph-name="icon-font-size" horiz-adv-x="1504" d="M1226.4 512h-176l-76.22-203.24 77-205.34 87.22 232.58 90.74-242h-174.44l49.5-132h174.44l57.76-154h154l-264 704zM384 832l-384-1024h224l84 224h408l84-224h224l-384 1024zM380 224l132 352 132-352z" />
<glyph unicode="&#xea30;" glyph-name="icon-clear-data" d="M632 520l-120-120-120 120-80-80 120-120-120-120 80-80 120 120 120-120 80 80-120 120 120 120-80 80zM512 832c-282.76 0-512-86-512-192v-640c0-106 229.24-192 512-192s512 86 512 192v640c0 106-229.24 192-512 192zM512 0c-176.731 0-320 143.269-320 320s143.269 320 320 320c176.731 0 320-143.269 320-320v0c0-176.731-143.269-320-320-320v0z" /> <glyph unicode="&#xea30;" glyph-name="icon-clear-data" d="M632 520l-120-120-120 120-80-80 120-120-120-120 80-80 120 120 120-120 80 80-120 120 120 120-80 80zM512 832c-282.76 0-512-86-512-192v-640c0-106 229.24-192 512-192s512 86 512 192v640c0 106-229.24 192-512 192zM512 0c-176.731 0-320 143.269-320 320s143.269 320 320 320c176.731 0 320-143.269 320-320v0c0-176.731-143.269-320-320-320v0z" />
<glyph unicode="&#xea31;" glyph-name="icon-history" d="M576 768c-247.4 0-448-200.6-448-448h-128l192-192 192 192h-128c0 85.4 33.2 165.8 93.8 226.2 60.4 60.6 140.8 93.8 226.2 93.8s165.8-33.2 226.2-93.8c60.6-60.4 93.8-140.8 93.8-226.2s-33.2-165.8-93.8-226.2c-60.4-60.6-140.8-93.8-226.2-93.8s-165.8 33.2-226.2 93.8l-90.6-90.6c81-81 193-131.2 316.8-131.2 247.4 0 448 200.6 448 448s-200.6 448-448 448zM576 560c-26.6 0-48-21.4-48-48v-211.8l142-142c9.4-9.4 21.6-14 34-14s24.6 4.6 34 14c18.8 18.8 18.8 49.2 0 67.8l-114 114v172c0 26.6-21.4 48-48 48z" /> <glyph unicode="&#xea31;" glyph-name="icon-history" d="M576 768c-247.4 0-448-200.6-448-448h-128l192-192 192 192h-128c0 85.4 33.2 165.8 93.8 226.2 60.4 60.6 140.8 93.8 226.2 93.8s165.8-33.2 226.2-93.8c60.6-60.4 93.8-140.8 93.8-226.2s-33.2-165.8-93.8-226.2c-60.4-60.6-140.8-93.8-226.2-93.8s-165.8 33.2-226.2 93.8l-90.6-90.6c81-81 193-131.2 316.8-131.2 247.4 0 448 200.6 448 448s-200.6 448-448 448zM576 560c-26.6 0-48-21.4-48-48v-211.8l142-142c9.4-9.4 21.6-14 34-14s24.6 4.6 34 14c18.8 18.8 18.8 49.2 0 67.8l-114 114v172c0 26.6-21.4 48-48 48z" />
<glyph unicode="&#xea32;" glyph-name="icon-arrow-up-to-parent" horiz-adv-x="1056" d="M643.427 6.739c-81.955 0.697-148.179 67.065-148.642 149.010v395.872l296.871-247.393v197.914l-395.828 329.857-395.828-328.62v-197.502l296.871 246.156v-396.241c0-190.905 155.239-346.556 346.144-346.968l412.321-0.825 0.412 197.914z" /> <glyph unicode="&#xea32;" glyph-name="icon-arrow-up-to-parent" horiz-adv-x="1056" d="M643.427 6.739c-81.955 0.697-148.179 67.065-148.642 149.010v395.872l296.871-247.393v197.914l-395.828 329.857-395.828-328.62v-197.502l296.871 246.156v-396.241c0-190.905 155.239-346.556 346.144-346.968l412.321-0.825 0.412 197.914z" />

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 58 KiB

View File

@ -66,6 +66,8 @@
:object="domainObject" :object="domainObject"
:show-edit-view="showEditView" :show-edit-view="showEditView"
:object-path="objectPath" :object-path="objectPath"
:layout-font-size="layoutFontSize"
:layout-font="layoutFont"
/> />
</div> </div>
</template> </template>
@ -103,6 +105,14 @@ export default {
showEditView: { showEditView: {
type: Boolean, type: Boolean,
default: true default: true
},
layoutFontSize: {
type: String,
default: ''
},
layoutFont: {
type: String,
default: ''
} }
}, },
data() { data() {

View File

@ -20,6 +20,30 @@ export default {
default: () => { default: () => {
return []; return [];
} }
},
layoutFontSize: {
type: String,
default: ''
},
layoutFont: {
type: String,
default: ''
}
},
data() {
return {
currentObject: this.object
};
},
computed: {
objectFontStyle() {
return this.currentObject && this.currentObject.configuration && this.currentObject.configuration.fontStyle;
},
fontSize() {
return this.objectFontStyle ? this.objectFontStyle.fontSize : this.layoutFontSize;
},
font() {
return this.objectFontStyle ? this.objectFontStyle.font : this.layoutFont;
} }
}, },
watch: { watch: {
@ -42,6 +66,10 @@ export default {
this.stopListeningStyles(); this.stopListeningStyles();
} }
if (this.stopListeningFontStyles) {
this.stopListeningFontStyles();
}
if (this.styleRuleManager) { if (this.styleRuleManager) {
this.styleRuleManager.destroy(); this.styleRuleManager.destroy();
delete this.styleRuleManager; delete this.styleRuleManager;
@ -51,7 +79,6 @@ export default {
this.debounceUpdateView = _.debounce(this.updateView, 10); this.debounceUpdateView = _.debounce(this.updateView, 10);
}, },
mounted() { mounted() {
this.currentObject = this.object;
this.updateView(); this.updateView();
this.$el.addEventListener('dragover', this.onDragOver, { this.$el.addEventListener('dragover', this.onDragOver, {
capture: true capture: true
@ -64,7 +91,6 @@ export default {
//This is to apply styles to subobjects in a layout //This is to apply styles to subobjects in a layout
this.initObjectStyles(); this.initObjectStyles();
} }
}, },
methods: { methods: {
clear() { clear() {
@ -92,6 +118,15 @@ export default {
this.openmct.objectViews.off('clearData', this.clearData); this.openmct.objectViews.off('clearData', this.clearData);
}, },
getStyleReceiver() {
let styleReceiver = this.$el.querySelector('.js-style-receiver');
if (!styleReceiver) {
styleReceiver = this.$el.querySelector(':first-child');
}
return styleReceiver;
},
invokeEditModeHandler(editMode) { invokeEditModeHandler(editMode) {
let edit; let edit;
@ -113,21 +148,21 @@ export default {
} }
let keys = Object.keys(styleObj); let keys = Object.keys(styleObj);
let elemToStyle = this.getStyleReceiver();
keys.forEach(key => { keys.forEach(key => {
let firstChild = this.$el.querySelector(':first-child'); if (elemToStyle) {
if (firstChild) {
if ((typeof styleObj[key] === 'string') && (styleObj[key].indexOf('__no_value') > -1)) { if ((typeof styleObj[key] === 'string') && (styleObj[key].indexOf('__no_value') > -1)) {
if (firstChild.style[key]) { if (elemToStyle.style[key]) {
firstChild.style[key] = ''; elemToStyle.style[key] = '';
} }
} else { } else {
if (!styleObj.isStyleInvisible && firstChild.classList.contains(STYLE_CONSTANTS.isStyleInvisible)) { if (!styleObj.isStyleInvisible && elemToStyle.classList.contains(STYLE_CONSTANTS.isStyleInvisible)) {
firstChild.classList.remove(STYLE_CONSTANTS.isStyleInvisible); elemToStyle.classList.remove(STYLE_CONSTANTS.isStyleInvisible);
} else if (styleObj.isStyleInvisible && !firstChild.classList.contains(styleObj.isStyleInvisible)) { } else if (styleObj.isStyleInvisible && !elemToStyle.classList.contains(styleObj.isStyleInvisible)) {
firstChild.classList.add(styleObj.isStyleInvisible); elemToStyle.classList.add(styleObj.isStyleInvisible);
} }
firstChild.style[key] = styleObj[key]; elemToStyle.style[key] = styleObj[key];
} }
} }
}); });
@ -228,6 +263,14 @@ export default {
//Updating styles in the inspector view will trigger this so that the changes are reflected immediately //Updating styles in the inspector view will trigger this so that the changes are reflected immediately
this.styleRuleManager.updateObjectStyleConfig(newObjectStyle); this.styleRuleManager.updateObjectStyleConfig(newObjectStyle);
}); });
this.setFontSize(this.fontSize);
this.setFont(this.font);
this.stopListeningFontStyles = this.openmct.objects.observe(this.currentObject, 'configuration.fontStyle', (newFontStyle) => {
this.setFontSize(newFontStyle.fontSize);
this.setFont(newFontStyle.font);
});
}, },
loadComposition() { loadComposition() {
return this.composition.load(); return this.composition.load();
@ -311,6 +354,14 @@ export default {
let parentObject = objectPath[1]; let parentObject = objectPath[1];
return [browseObject, parentObject, this.currentObject].every(object => object && !object.locked); return [browseObject, parentObject, this.currentObject].every(object => object && !object.locked);
},
setFontSize(newSize) {
let elemToStyle = this.getStyleReceiver();
elemToStyle.dataset.fontSize = newSize;
},
setFont(newFont) {
let elemToStyle = this.getStyleReceiver();
elemToStyle.dataset.font = newFont;
} }
} }
}; };

View File

@ -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,22 @@
<elements /> <elements />
</pane> </pane>
</multipane> </multipane>
<template v-else> <multipane
<styles-inspector-view /> v-show="currentTabbedView.key === '__styles'"
</template> type="vertical"
>
<pane class="c-inspector__styles">
<StylesInspectorView />
</pane>
<pane
v-if="isEditing"
class="c-inspector__saved-styles"
handle="before"
label="Saved Styles"
>
<SavedStylesInspectorView :is-editing="isEditing" />
</pane>
</multipane>
</div> </div>
</div> </div>
</template> </template>
@ -48,12 +61,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/styles/StylesManager';
import StylesInspectorView from '@/ui/inspector/styles/StylesInspectorView.vue';
import SavedStylesInspectorView from '@/ui/inspector/styles/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 +82,10 @@ export default {
InspectorViews InspectorViews
}, },
props: { props: {
'isEditing': Boolean isEditing: {
type: Boolean,
required: true
}
}, },
data() { data() {
return { return {

View File

@ -57,6 +57,10 @@
} }
} }
&__saved-styles {
height: 300px;
}
.c-color-swatch { .c-color-swatch {
$d: 12px; $d: 12px;
display: block; display: block;
@ -162,6 +166,11 @@
} }
} }
/********************************************* INSPECTOR PROPERTIES TAB */
.c-saved-style {
cursor: default;
}
/********************************************* LEGACY SUPPORT */ /********************************************* LEGACY SUPPORT */
.c-inspector { .c-inspector {
// FilterField.vue // FilterField.vue

View File

@ -0,0 +1,63 @@
<template>
<div class="c-toolbar">
<toolbar-select-menu
:options="fontSizeMenuOptions"
@change="setFontSize"
/>
<div class="c-toolbar__separator"></div>
<toolbar-select-menu
:options="fontMenuOptions"
@change="setFont"
/>
</div>
</template>
<script>
import ToolbarSelectMenu from '@/ui/toolbar/components/toolbar-select-menu.vue';
import {
FONT_SIZES,
FONTS
} from '@/ui/inspector/styles/constants';
export default {
inject: ['openmct'],
components: {
ToolbarSelectMenu
},
props: {
fontStyle: {
type: Object,
required: true
}
},
computed: {
fontMenuOptions() {
return {
control: 'select-menu',
icon: "icon-font",
title: "Set font style",
value: this.fontStyle.font,
options: FONTS
};
},
fontSizeMenuOptions() {
return {
control: 'select-menu',
icon: "icon-font-size",
title: "Set font size",
value: this.fontStyle.fontSize,
options: FONT_SIZES
};
}
},
methods: {
setFont(font) {
this.$emit('set-font-property', { font: font });
},
setFontSize(fontSize) {
this.$emit('set-font-property', { fontSize: fontSize });
}
}
};
</script>

View File

@ -0,0 +1,195 @@
/*****************************************************************************
* 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>
<div class="c-style c-style--saved has-local-controls c-toolbar">
<div class="c-style__controls"
:title="description"
@click="selectStyle()"
>
<div
class="c-style-thumb"
:style="thumbStyle"
>
<span
class="c-style-thumb__text u-style-receiver js-style-receiver"
:class="{ 'hide-nice': !hasProperty(savedStyle.color) }"
:data-font="savedStyle.font"
>
{{ thumbLabel }}
</span>
</div>
<div
class="c-icon-button c-icon-button--disabled c-icon-button--swatched icon-line-horz"
title="Border color"
>
<div
class="c-swatch"
:style="{
background: borderColor
}"
></div>
</div>
<div
class="c-icon-button 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
class="c-icon-button c-icon-button--disabled c-icon-button--swatched icon-font"
title="Text color"
>
<div
class="c-swatch"
:style="{ background: savedStyle.color }"
></div>
</div>
</div>
<div
v-if="canDeleteStyle"
class="c-style__button-delete c-local-controls--show-on-hover"
>
<div
class="c-icon-button icon-trash"
title="Delete this saved style"
@click.stop="deleteStyle()"
>
</div>
</div>
</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: {
borderColor() {
return this.savedStyle.border.substring(this.savedStyle.border.indexOf('#'));
},
thumbStyle() {
return {
border: this.savedStyle.border,
backgroundColor: this.savedStyle.backgroundColor,
color: this.savedStyle.color
};
},
thumbLabel() {
return this.savedStyle.fontSize !== 'default' ? `${this.savedStyle.fontSize}px` : 'ABC';
},
description() {
const fill = `Fill: ${this.savedStyle.backgroundColor || 'none'}`;
const border = `Border: ${this.savedStyle.border || 'none'}`;
const color = `Text Color: ${this.savedStyle.color || 'default'}`;
const fontSize = this.savedStyle.fontSize ? `Font Size: ${this.savedStyle.fontSize}` : '';
const font = this.savedStyle.font ? `Font Style: ${this.savedStyle.font}` : '';
// Note: lack of indention in the return string is deliberate, it affects how the text is rendered
return `Click to apply this style:
${fill}
${border}
${color}
${fontSize}
${font}`;
},
canDeleteStyle() {
return this.isEditing;
}
},
methods: {
selectStyle() {
if (this.isEditing) {
this.stylesManager.select(this.savedStyle);
}
},
deleteStyle() {
this.showDeleteStyleDialog()
.then(() => {
this.$emit('delete-style');
})
.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>

View 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 '@/ui/inspector/styles/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>

View File

@ -0,0 +1,129 @@
/*****************************************************************************
* 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__content">
<div class="c-inspect-styles__saved-styles">
<saved-style-selector
v-for="(savedStyle, index) in savedStyles"
:key="index"
class="c-inspect-styles__saved-style"
:is-editing="isEditing"
:saved-style="savedStyle"
@delete-style="deleteStyle(index)"
/>
</div>
</div>
</div>
</template>
<script>
import SavedStyleSelector from '@/ui/inspector/styles/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();
}
}
]
});
},
deleteStyle(index) {
this.stylesManager.delete(index);
}
}
};
</script>

View File

@ -25,11 +25,11 @@
</template> </template>
<script> <script>
import StylesView from '../../plugins/condition/components/inspector/StylesView.vue'; import StylesView from '@/plugins/condition/components/inspector/StylesView.vue';
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: {

View File

@ -0,0 +1,126 @@
import EventEmitter from 'EventEmitter';
const LOCAL_STORAGE_KEY = 'mct-saved-styles';
const LIMIT = 20;
/**
* @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);
if (this.persist(styles)) {
this.emit('stylesUpdated', styles);
}
}
}
delete(index) {
const styles = this.load();
styles.splice(index, 1);
if (this.persist(styles)) {
this.emit('stylesUpdated', styles);
}
}
select(style) {
this.emit('styleSelected', style);
}
/**
* @private
*/
normalizeStyle(style) {
const normalizedStyle = this.getBaseStyleObject();
Object.keys(normalizedStyle).forEach(property => {
const value = style[property];
if (value !== undefined) {
normalizedStyle[property] = value;
}
});
return normalizedStyle;
}
/**
* @private
*/
getBaseStyleObject() {
return {
backgroundColor: '',
border: '',
color: '',
fontSize: 'default',
font: 'default'
};
}
/**
* @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 (e) {
this.emit('persistError');
}
return false;
}
/**
* @private
*/
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;
}
}
const stylesManager = new StylesManager();
// breaks on adding listener later
// Object.freeze(stylesManager);
export default stylesManager;

View File

@ -0,0 +1,109 @@
export const FONT_SIZES = [
{
name: 'Default Size',
value: 'default'
},
{
name: '8px',
value: '8'
},
{
name: '9px',
value: '9'
},
{
name: '10px',
value: '10'
},
{
name: '11px',
value: '11'
},
{
name: '12px',
value: '12'
},
{
name: '13px',
value: '13'
},
{
name: '14px',
value: '14'
},
{
name: '16px',
value: '16'
},
{
name: '18px',
value: '18'
},
{
name: '20px',
value: '20'
},
{
name: '24px',
value: '24'
},
{
name: '28px',
value: '28'
},
{
name: '32px',
value: '32'
},
{
name: '36px',
value: '36'
},
{
name: '42px',
value: '42'
},
{
name: '48px',
value: '48'
},
{
name: '72px',
value: '72'
},
{
name: '96px',
value: '96'
},
{
name: '128px',
value: '128'
}
];
export const FONTS = [
{
name: 'Default',
value: 'default'
},
{
name: 'Bold',
value: 'default-bold'
},
{
name: 'Narrow',
value: 'narrow'
},
{
name: 'Narrow Bold',
value: 'narrow-bold'
},
{
name: 'Monospace',
value: 'monospace'
},
{
name: 'Monospace Bold',
value: 'monospace-bold'
}
];

View File

@ -51,7 +51,7 @@ export default {
} }
// If no selected option, then options are non-specific // If no selected option, then options are non-specific
return '??px'; return '??';
}, },
nonSpecific() { nonSpecific() {
return this.options.nonSpecific === true; return this.options.nonSpecific === true;