Compare commits
6 Commits
table-perf
...
open-issue
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d71613346d | ||
|
|
7e50010463 | ||
|
|
974be0ae2c | ||
|
|
3dc6dac12d | ||
|
|
aafe524454 | ||
|
|
e84ade1752 |
@@ -79,6 +79,7 @@
|
||||
}));
|
||||
openmct.install(openmct.plugins.SummaryWidget());
|
||||
openmct.install(openmct.plugins.Notebook());
|
||||
openmct.install(openmct.plugins.LADTable());
|
||||
openmct.install(openmct.plugins.Filters(['table', 'telemetry.plot.overlay']));
|
||||
openmct.install(openmct.plugins.ObjectMigration());
|
||||
openmct.install(openmct.plugins.ClearData(['table', 'telemetry.plot.overlay', 'telemetry.plot.stacked']));
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "openmct",
|
||||
"version": "0.14.0-SNAPSHOT",
|
||||
"version": "1.0.0-beta",
|
||||
"description": "The Open MCT core platform",
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -97,7 +97,7 @@ define(
|
||||
this.$timeout(() => {
|
||||
// Create the overlay element and add it to the document's body
|
||||
element = this.$compile(TEMPLATE)(scope);
|
||||
|
||||
|
||||
// Append so that most recent dialog is last in DOM. This means the most recent dialog will be on top when
|
||||
// multiple overlays with the same z-index are active.
|
||||
this.findBody().append(element);
|
||||
|
||||
@@ -259,7 +259,6 @@ define([
|
||||
this.install(this.plugins.FolderView());
|
||||
this.install(this.plugins.Tabs());
|
||||
this.install(this.plugins.FlexibleLayout());
|
||||
this.install(this.plugins.LADTable());
|
||||
this.install(this.plugins.GoToOriginalAction());
|
||||
|
||||
if (typeof BUILD_CONSTANTS !== 'undefined') {
|
||||
|
||||
@@ -129,7 +129,7 @@ define([
|
||||
return priority;
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
return LegacyViewProvider;
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ define([
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
return TypeInspectorViewProvider;
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ let brandingOptions = {};
|
||||
*/
|
||||
|
||||
/**
|
||||
* Set branding options for the application. These will override certain visual elements
|
||||
* Set branding options for the application. These will override certain visual elements
|
||||
* of the application and allow for customization of the application.
|
||||
* @param {BrandingOptions} options
|
||||
*/
|
||||
|
||||
@@ -42,7 +42,7 @@ import EventEmitter from 'EventEmitter';
|
||||
*
|
||||
* @typedef {object} NotificationModel
|
||||
* @property {string} message The message to be displayed by the notification
|
||||
* @property {number | 'unknown'} [progress] The progres of some ongoing task. Should be a number between 0 and 100, or
|
||||
* @property {number | 'unknown'} [progress] The progres of some ongoing task. Should be a number between 0 and 100, or
|
||||
* with the string literal 'unknown'.
|
||||
* @property {string} [progressText] A message conveying progress of some ongoing task.
|
||||
|
||||
|
||||
@@ -329,7 +329,7 @@ define([
|
||||
hints: {
|
||||
priority: 2
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
{
|
||||
key: "timestamp",
|
||||
@@ -365,7 +365,7 @@ define([
|
||||
{
|
||||
key: "name",
|
||||
name: "Name"
|
||||
|
||||
|
||||
},
|
||||
{
|
||||
key: "timestamp",
|
||||
@@ -392,7 +392,7 @@ define([
|
||||
{
|
||||
key: "name",
|
||||
name: "Name"
|
||||
|
||||
|
||||
},
|
||||
{
|
||||
key: "timestamp-utc",
|
||||
@@ -434,7 +434,7 @@ define([
|
||||
{
|
||||
key: "name",
|
||||
name: "Name"
|
||||
|
||||
|
||||
},
|
||||
{
|
||||
key: "timestamp-utc",
|
||||
@@ -486,7 +486,7 @@ define([
|
||||
hints: {
|
||||
priority: 1
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
{
|
||||
key: "timestamp-utc",
|
||||
|
||||
@@ -32,6 +32,6 @@ class CSVExporter {
|
||||
let blob = new Blob([csvText], { type: "text/csv" });
|
||||
saveAs(blob, filename);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default CSVExporter;
|
||||
|
||||
@@ -29,7 +29,7 @@ define([
|
||||
function isTelemetryObject(selectionPath) {
|
||||
let selectedObject = selectionPath[0].context.item;
|
||||
let parentObject = selectionPath[1].context.item;
|
||||
return parentObject &&
|
||||
return parentObject &&
|
||||
parentObject.type === 'layout' &&
|
||||
selectedObject &&
|
||||
openmct.telemetry.isTelemetryObject(selectedObject) &&
|
||||
@@ -75,4 +75,4 @@ define([
|
||||
}
|
||||
|
||||
return AlphanumericFormatViewProvider;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -140,7 +140,7 @@ define([], function () {
|
||||
return {
|
||||
control: "toggle-button",
|
||||
domainObject: selectedParent,
|
||||
applicableSelectedItems: selection.filter(selectionPath =>
|
||||
applicableSelectedItems: selection.filter(selectionPath =>
|
||||
selectionPath[0].context.layoutItem.type === 'subobject-view'
|
||||
),
|
||||
property: function (selectionPath) {
|
||||
@@ -254,7 +254,7 @@ define([], function () {
|
||||
return getPath(selectionPath) + ".y";
|
||||
},
|
||||
label: "Y:",
|
||||
title: "Y position",
|
||||
title: "Y position"
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -322,7 +322,7 @@ define([], function () {
|
||||
return getPath(selectionPath) + ".y2";
|
||||
},
|
||||
label: "Y2:",
|
||||
title: "Y2 position",
|
||||
title: "Y2 position"
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -416,24 +416,24 @@ define([], function () {
|
||||
},
|
||||
icon: "icon-image",
|
||||
title: "Edit image properties",
|
||||
dialog: DIALOG_FORM['image']
|
||||
dialog: DIALOG_FORM.image
|
||||
};
|
||||
}
|
||||
|
||||
function getTextButton(selectedParent, selection) {
|
||||
return {
|
||||
control: "button",
|
||||
domainObject: selectedParent,
|
||||
applicableSelectedItems: selection.filter(selectionPath => {
|
||||
return selectionPath[0].context.layoutItem.type === 'text-view';
|
||||
}),
|
||||
property: function (selectionPath) {
|
||||
return getPath(selectionPath);
|
||||
},
|
||||
icon: "icon-gear",
|
||||
title: "Edit text properties",
|
||||
dialog: DIALOG_FORM['text']
|
||||
};
|
||||
return {
|
||||
control: "button",
|
||||
domainObject: selectedParent,
|
||||
applicableSelectedItems: selection.filter(selectionPath => {
|
||||
return selectionPath[0].context.layoutItem.type === 'text-view';
|
||||
}),
|
||||
property: function (selectionPath) {
|
||||
return getPath(selectionPath);
|
||||
},
|
||||
icon: "icon-gear",
|
||||
title: "Edit text properties",
|
||||
dialog: DIALOG_FORM.text
|
||||
};
|
||||
}
|
||||
|
||||
function getTelemetryValueMenu(selectionPath, selection) {
|
||||
@@ -514,7 +514,7 @@ define([], function () {
|
||||
'position': [],
|
||||
'text': [],
|
||||
'url': [],
|
||||
'remove': [],
|
||||
'remove': []
|
||||
};
|
||||
|
||||
selection.forEach(selectionPath => {
|
||||
@@ -528,8 +528,8 @@ define([], function () {
|
||||
if (toolbar['toggle-frame'].length === 0) {
|
||||
toolbar['toggle-frame'] = [getToggleFrameButton(selectedParent, selection)];
|
||||
}
|
||||
if (toolbar['position'].length === 0) {
|
||||
toolbar['position'] = [
|
||||
if (toolbar.position.length === 0) {
|
||||
toolbar.position = [
|
||||
getStackOrder(selectedParent, selectionPath),
|
||||
getXInput(selectedParent, selection),
|
||||
getYInput(selectedParent, selection),
|
||||
@@ -537,8 +537,8 @@ define([], function () {
|
||||
getWidthInput(selectedParent, selection)
|
||||
];
|
||||
}
|
||||
if (toolbar['remove'].length === 0) {
|
||||
toolbar['remove'] = [getRemoveButton(selectedParent, selectionPath, selection)];
|
||||
if (toolbar.remove.length === 0) {
|
||||
toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selection)];
|
||||
}
|
||||
} else if (layoutItem.type === 'telemetry-view') {
|
||||
if (toolbar['display-mode'].length === 0) {
|
||||
@@ -547,8 +547,8 @@ define([], function () {
|
||||
if (toolbar['telemetry-value'].length === 0) {
|
||||
toolbar['telemetry-value'] = [getTelemetryValueMenu(selectionPath, selection)];
|
||||
}
|
||||
if (toolbar['style'].length < 2) {
|
||||
toolbar['style'] = [
|
||||
if (toolbar.style.length < 2) {
|
||||
toolbar.style = [
|
||||
getFillMenu(selectedParent, selection),
|
||||
getStrokeMenu(selectedParent, selection)
|
||||
];
|
||||
@@ -559,8 +559,8 @@ define([], function () {
|
||||
getTextSizeMenu(selectedParent, selection)
|
||||
];
|
||||
}
|
||||
if (toolbar['position'].length === 0) {
|
||||
toolbar['position'] = [
|
||||
if (toolbar.position.length === 0) {
|
||||
toolbar.position = [
|
||||
getStackOrder(selectedParent, selectionPath),
|
||||
getXInput(selectedParent, selection),
|
||||
getYInput(selectedParent, selection),
|
||||
@@ -568,12 +568,12 @@ define([], function () {
|
||||
getWidthInput(selectedParent, selection)
|
||||
];
|
||||
}
|
||||
if (toolbar['remove'].length === 0) {
|
||||
toolbar['remove'] = [getRemoveButton(selectedParent, selectionPath, selection)];
|
||||
if (toolbar.remove.length === 0) {
|
||||
toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selection)];
|
||||
}
|
||||
} else if (layoutItem.type === 'text-view') {
|
||||
if (toolbar['style'].length < 2) {
|
||||
toolbar['style'] = [
|
||||
if (toolbar.style.length < 2) {
|
||||
toolbar.style = [
|
||||
getFillMenu(selectedParent, selection),
|
||||
getStrokeMenu(selectedParent, selection)
|
||||
];
|
||||
@@ -584,8 +584,8 @@ define([], function () {
|
||||
getTextSizeMenu(selectedParent, selection)
|
||||
];
|
||||
}
|
||||
if (toolbar['position'].length === 0) {
|
||||
toolbar['position'] = [
|
||||
if (toolbar.position.length === 0) {
|
||||
toolbar.position = [
|
||||
getStackOrder(selectedParent, selectionPath),
|
||||
getXInput(selectedParent, selection),
|
||||
getYInput(selectedParent, selection),
|
||||
@@ -593,21 +593,21 @@ define([], function () {
|
||||
getWidthInput(selectedParent, selection)
|
||||
];
|
||||
}
|
||||
if (toolbar['text'].length === 0) {
|
||||
toolbar['text'] = [getTextButton(selectedParent, selection)];
|
||||
if (toolbar.text.length === 0) {
|
||||
toolbar.text = [getTextButton(selectedParent, selection)];
|
||||
}
|
||||
if (toolbar['remove'].length === 0) {
|
||||
toolbar['remove'] = [getRemoveButton(selectedParent, selectionPath, selection)];
|
||||
if (toolbar.remove.length === 0) {
|
||||
toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selection)];
|
||||
}
|
||||
} else if (layoutItem.type === 'box-view') {
|
||||
if (toolbar['style'].length < 2) {
|
||||
toolbar['style'] = [
|
||||
if (toolbar.style.length < 2) {
|
||||
toolbar.style = [
|
||||
getFillMenu(selectedParent, selection),
|
||||
getStrokeMenu(selectedParent, selection)
|
||||
];
|
||||
}
|
||||
if (toolbar['position'].length === 0) {
|
||||
toolbar['position'] = [
|
||||
if (toolbar.position.length === 0) {
|
||||
toolbar.position = [
|
||||
getStackOrder(selectedParent, selectionPath),
|
||||
getXInput(selectedParent, selection),
|
||||
getYInput(selectedParent, selection),
|
||||
@@ -615,17 +615,17 @@ define([], function () {
|
||||
getWidthInput(selectedParent, selection)
|
||||
];
|
||||
}
|
||||
if (toolbar['remove'].length === 0) {
|
||||
toolbar['remove'] = [getRemoveButton(selectedParent, selectionPath, selection)];
|
||||
if (toolbar.remove.length === 0) {
|
||||
toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selection)];
|
||||
}
|
||||
} else if (layoutItem.type === 'image-view') {
|
||||
if (toolbar['style'].length === 0) {
|
||||
toolbar['style'] = [
|
||||
if (toolbar.style.length === 0) {
|
||||
toolbar.style = [
|
||||
getStrokeMenu(selectedParent, selection)
|
||||
];
|
||||
}
|
||||
if (toolbar['position'].length === 0) {
|
||||
toolbar['position'] = [
|
||||
if (toolbar.position.length === 0) {
|
||||
toolbar.position = [
|
||||
getStackOrder(selectedParent, selectionPath),
|
||||
getXInput(selectedParent, selection),
|
||||
getYInput(selectedParent, selection),
|
||||
@@ -633,20 +633,20 @@ define([], function () {
|
||||
getWidthInput(selectedParent, selection)
|
||||
];
|
||||
}
|
||||
if (toolbar['url'].length === 0) {
|
||||
toolbar['url'] = [getURLButton(selectedParent, selection)];
|
||||
if (toolbar.url.length === 0) {
|
||||
toolbar.url = [getURLButton(selectedParent, selection)];
|
||||
}
|
||||
if (toolbar['remove'].length === 0) {
|
||||
toolbar['remove'] = [getRemoveButton(selectedParent, selectionPath, selection)];
|
||||
if (toolbar.remove.length === 0) {
|
||||
toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selection)];
|
||||
}
|
||||
} else if (layoutItem.type === 'line-view') {
|
||||
if (toolbar['style'].length === 0) {
|
||||
toolbar['style'] = [
|
||||
if (toolbar.style.length === 0) {
|
||||
toolbar.style = [
|
||||
getStrokeMenu(selectedParent, selection)
|
||||
];
|
||||
}
|
||||
if (toolbar['position'].length === 0) {
|
||||
toolbar['position'] = [
|
||||
if (toolbar.position.length === 0) {
|
||||
toolbar.position = [
|
||||
getStackOrder(selectedParent, selectionPath),
|
||||
getXInput(selectedParent, selection),
|
||||
getYInput(selectedParent, selection),
|
||||
@@ -654,8 +654,8 @@ define([], function () {
|
||||
getY2Input(selectedParent, selection)
|
||||
];
|
||||
}
|
||||
if (toolbar['remove'].length === 0) {
|
||||
toolbar['remove'] = [getRemoveButton(selectedParent, selectionPath, selection)];
|
||||
if (toolbar.remove.length === 0) {
|
||||
toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selection)];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -31,7 +31,7 @@ define(function () {
|
||||
domainObject.composition = [];
|
||||
domainObject.configuration = {
|
||||
items: [],
|
||||
layoutGrid: [10, 10],
|
||||
layoutGrid: [10, 10]
|
||||
};
|
||||
},
|
||||
form: [
|
||||
|
||||
@@ -53,7 +53,7 @@ export default function DisplayLayoutPlugin(options) {
|
||||
objectPath
|
||||
},
|
||||
el: container,
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
domainObject: domainObject
|
||||
};
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
<template>
|
||||
<div class="u-contents c-filter-settings">
|
||||
<li class="grid-row c-filter-settings__setting"
|
||||
<div class="c-properties__section c-filter-settings">
|
||||
<li class="c-properties__row c-filter-settings__setting"
|
||||
v-for="(filter, index) in filterField.filters"
|
||||
:key="index">
|
||||
<div class="grid-cell label">
|
||||
<div class="c-properties__label label"
|
||||
:disabled="useGlobal">
|
||||
{{ filterField.name }} =
|
||||
</div>
|
||||
<div class="grid-cell value">
|
||||
<div class="c-properties__value value">
|
||||
<!-- EDITING -->
|
||||
<!-- String input, editing -->
|
||||
<template v-if="!filter.possibleValues && isEditing">
|
||||
<input class="c-input--flex"
|
||||
type="text"
|
||||
placeholder="Enter Value"
|
||||
:id="`${filter}filterControl`"
|
||||
:disabled="useGlobal"
|
||||
:value="persistedValue(filter)"
|
||||
@change="updateFilterValue($event, filter)">
|
||||
</template>
|
||||
@@ -21,15 +22,16 @@
|
||||
<!-- Checkbox list, editing -->
|
||||
<template v-if="filter.possibleValues && isEditing">
|
||||
<div class="c-checkbox-list__row"
|
||||
v-for="value in filter.possibleValues"
|
||||
:key="value">
|
||||
v-for="option in filter.possibleValues"
|
||||
:key="option.value">
|
||||
<input class="c-checkbox-list__input"
|
||||
type="checkbox"
|
||||
:id="`${value}filterControl`"
|
||||
@change="onUserSelect($event, filter.comparator, value)"
|
||||
:checked="isChecked(filter.comparator, value)">
|
||||
:id="`${option.value}filterControl`"
|
||||
:disabled="useGlobal"
|
||||
@change="updateFilterValue($event, filter.comparator, option.value)"
|
||||
:checked="isChecked(filter.comparator, option.value)">
|
||||
<span class="c-checkbox-list__value">
|
||||
{{ value }}
|
||||
{{ option.label }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
@@ -42,9 +44,8 @@
|
||||
|
||||
<!-- Checkbox list, NOT editing -->
|
||||
<template v-if="filter.possibleValues && !isEditing">
|
||||
<span
|
||||
v-if="persistedFilters[filter.comparator]">
|
||||
{{persistedFilters[filter.comparator].join(', ')}}
|
||||
<span v-if="persistedFilters[filter.comparator]">
|
||||
{{ getFilterLabels(filter) }}
|
||||
</span>
|
||||
</template>
|
||||
</div>
|
||||
@@ -52,26 +53,14 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import "~styles/sass-base";
|
||||
|
||||
.c-filter-settings {
|
||||
&__setting {
|
||||
.grid-cell.label {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<script>
|
||||
export default {
|
||||
inject: [
|
||||
'openmct'
|
||||
],
|
||||
props: {
|
||||
filterField: Object,
|
||||
filterField: Object,
|
||||
useGlobal: Boolean,
|
||||
persistedFilters: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
@@ -81,7 +70,6 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
expanded: false,
|
||||
isEditing: this.openmct.editor.isEditing()
|
||||
}
|
||||
},
|
||||
@@ -89,9 +77,6 @@ export default {
|
||||
toggleIsEditing(isEditing) {
|
||||
this.isEditing = isEditing;
|
||||
},
|
||||
onUserSelect(event, comparator, value){
|
||||
this.$emit('onUserSelect', this.filterField.key, comparator, value, event.target.checked);
|
||||
},
|
||||
isChecked(comparator, value) {
|
||||
if (this.persistedFilters[comparator] && this.persistedFilters[comparator].includes(value)) {
|
||||
return true;
|
||||
@@ -102,8 +87,25 @@ export default {
|
||||
persistedValue(comparator) {
|
||||
return this.persistedFilters && this.persistedFilters[comparator];
|
||||
},
|
||||
updateFilterValue(event, comparator) {
|
||||
this.$emit('onTextEnter', this.filterField.key, comparator, event.target.value);
|
||||
updateFilterValue(event, comparator, value) {
|
||||
if (value !== undefined) {
|
||||
this.$emit('filterSelected', this.filterField.key, comparator, value, event.target.checked);
|
||||
} else {
|
||||
this.$emit('filterTextValueChanged', this.filterField.key, comparator, event.target.value);
|
||||
}
|
||||
},
|
||||
getFilterLabels(filter) {
|
||||
return this.persistedFilters[filter.comparator].reduce((accum, filterValue) => {
|
||||
accum.push(filter.possibleValues.reduce((label, possibleValue) => {
|
||||
if (filterValue === possibleValue.value) {
|
||||
label = possibleValue.label;
|
||||
}
|
||||
|
||||
return label;
|
||||
}, ''));
|
||||
|
||||
return accum;
|
||||
}, []).join(', ');
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
<template>
|
||||
<li>
|
||||
<li class="c-tree__item-h">
|
||||
<div class="c-tree__item menus-to-left"
|
||||
@click="toggleExpanded">
|
||||
<div class="c-filter-tree-item__filter-indicator"
|
||||
:class="{'icon-filter': hasActiveFilters }"></div>
|
||||
<span class="c-disclosure-triangle is-enabled flex-elem"
|
||||
:class="{'c-disclosure-triangle--expanded': expanded}"></span>
|
||||
<div class="c-tree__item__label">
|
||||
<div class="c-tree__item__label c-object-label">
|
||||
<div class="c-object-label">
|
||||
<div class="c-object-label__type-icon"
|
||||
:class="objectCssClass">
|
||||
@@ -13,30 +15,47 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="grid-properties" v-if="expanded">
|
||||
<filter-field
|
||||
v-for="field in filterObject.valuesWithFilters"
|
||||
:key="field.key"
|
||||
:filterField="field"
|
||||
:persistedFilters="persistedFilters[field.key]"
|
||||
@onUserSelect="collectUserSelects"
|
||||
@onTextEnter="updateTextFilter">
|
||||
</filter-field>
|
||||
</ul>
|
||||
|
||||
<div v-if="expanded">
|
||||
<ul class="c-properties">
|
||||
<div class="c-properties__label span-all"
|
||||
v-if="!isEditing && persistedFilters.useGlobal">
|
||||
Uses global filter
|
||||
</div>
|
||||
|
||||
<div class="c-properties__label span-all"
|
||||
v-if="isEditing">
|
||||
<toggle-switch
|
||||
:id="keyString"
|
||||
@change="useGlobalFilter"
|
||||
:checked="persistedFilters.useGlobal">
|
||||
</toggle-switch>
|
||||
Use global filter
|
||||
</div>
|
||||
<filter-field
|
||||
v-if="(!persistedFilters.useGlobal && !isEditing) || isEditing"
|
||||
v-for="metadatum in filterObject.metadataWithFilters"
|
||||
:key="metadatum.key"
|
||||
:filterField="metadatum"
|
||||
:useGlobal="persistedFilters.useGlobal"
|
||||
:persistedFilters="updatedFilters[metadatum.key]"
|
||||
@filterSelected="updateFiltersWithSelectedValue"
|
||||
@filterTextValueChanged="updateFiltersWithTextValue">
|
||||
</filter-field>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import FilterField from './FilterField.vue';
|
||||
import ToggleSwitch from '../../../ui/components/ToggleSwitch.vue';
|
||||
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
components: {
|
||||
FilterField
|
||||
FilterField,
|
||||
ToggleSwitch
|
||||
},
|
||||
props: {
|
||||
filterObject: Object,
|
||||
@@ -51,58 +70,74 @@ export default {
|
||||
return {
|
||||
expanded: false,
|
||||
objectCssClass: undefined,
|
||||
updatedFilters: this.persistedFilters
|
||||
updatedFilters: JSON.parse(JSON.stringify(this.persistedFilters)),
|
||||
isEditing: this.openmct.editor.isEditing()
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
persistedFilters: {
|
||||
handler: function checkFilters(newpersistedFilters) {
|
||||
this.updatedFilters = JSON.parse(JSON.stringify(newpersistedFilters));
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
hasActiveFilters() {
|
||||
// Should be true when the user has entered any filter values.
|
||||
return Object.values(this.persistedFilters).some(comparator => {
|
||||
return (typeof(comparator) === 'object' && !_.isEmpty(comparator));
|
||||
});
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleExpanded() {
|
||||
this.expanded = !this.expanded;
|
||||
},
|
||||
collectUserSelects(key, comparator, valueName, value) {
|
||||
updateFiltersWithSelectedValue(key, comparator, valueName, value) {
|
||||
let filterValue = this.updatedFilters[key];
|
||||
|
||||
if (filterValue && filterValue[comparator]) {
|
||||
if (value === false) {
|
||||
let filteredValueName = filterValue[comparator].filter(v => v !== valueName);
|
||||
|
||||
if (filteredValueName.length === 0) {
|
||||
delete this.updatedFilters[key];
|
||||
} else {
|
||||
filterValue[comparator] = filteredValueName;
|
||||
}
|
||||
} else {
|
||||
if (filterValue[comparator]) {
|
||||
if (value === true) {
|
||||
filterValue[comparator].push(valueName);
|
||||
} else {
|
||||
if (filterValue[comparator].length === 1) {
|
||||
this.$set(this.updatedFilters, key, {});
|
||||
} else {
|
||||
filterValue[comparator] = filterValue[comparator].filter(v => v !== valueName);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!this.updatedFilters[key]) {
|
||||
this.$set(this.updatedFilters, key, {});
|
||||
}
|
||||
this.$set(this.updatedFilters[key], comparator, [value ? valueName : undefined]);
|
||||
this.$set(this.updatedFilters[key], comparator, [valueName]);
|
||||
}
|
||||
|
||||
this.$emit('updateFilters', this.keyString, this.updatedFilters);
|
||||
},
|
||||
updateTextFilter(key, comparator, value) {
|
||||
updateFiltersWithTextValue(key, comparator, value) {
|
||||
if (value.trim() === '') {
|
||||
if (this.updatedFilters[key]) {
|
||||
delete this.updatedFilters[key];
|
||||
this.$emit('updateFilters', this.keyString, this.updatedFilters);
|
||||
}
|
||||
return;
|
||||
this.$set(this.updatedFilters, key, {});
|
||||
} else {
|
||||
this.$set(this.updatedFilters[key], comparator, value);
|
||||
}
|
||||
|
||||
if (!this.updatedFilters[key]) {
|
||||
this.$set(this.updatedFilters, key, {});
|
||||
this.$set(this.updatedFilters[key], comparator, '');
|
||||
}
|
||||
this.updatedFilters[key][comparator] = value;
|
||||
this.$emit('updateFilters', this.keyString, this.updatedFilters);
|
||||
}
|
||||
},
|
||||
useGlobalFilter(checked) {
|
||||
this.updatedFilters.useGlobal = checked;
|
||||
this.$emit('updateFilters', this.keyString, this.updatedFilters, checked);
|
||||
},
|
||||
toggleIsEditing(isEditing) {
|
||||
this.isEditing = isEditing;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
let type = this.openmct.types.get(this.filterObject.domainObject.type) || {};
|
||||
this.keyString = this.openmct.objects.makeKeyString(this.filterObject.domainObject.identifier);
|
||||
this.objectCssClass = type.definition.cssClass;
|
||||
this.openmct.editor.on('isEditing', this.toggleIsEditing);
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.openmct.editor.off('isEditing', this.toggleIsEditing);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
<template>
|
||||
<ul class="tree c-tree c-properties__section" v-if="Object.keys(children).length">
|
||||
<h2 class="c-properties__header">Filters</h2>
|
||||
<ul class="c-tree c-filter-tree" v-if="Object.keys(children).length">
|
||||
<h2>Data Filters</h2>
|
||||
<div class="c-filter-indication"
|
||||
v-if="hasActiveFilters">{{ label }}
|
||||
</div>
|
||||
<global-filters
|
||||
:globalFilters="globalFilters"
|
||||
:globalMetadata="globalMetadata"
|
||||
@persistGlobalFilters="persistGlobalFilters">
|
||||
</global-filters>
|
||||
<filter-object
|
||||
v-for="(child, key) in children"
|
||||
:key="key"
|
||||
@@ -12,80 +20,230 @@
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import "~styles/sass-base";
|
||||
|
||||
.c-inspector {
|
||||
.c-filter-indication {
|
||||
border-radius: $smallCr;
|
||||
font-size: inherit;
|
||||
padding: $interiorMarginSm $interiorMargin;
|
||||
text-transform: inherit;
|
||||
}
|
||||
.c-filter-tree {
|
||||
// Filters UI uses a tree-based structure
|
||||
.c-properties {
|
||||
// Add extra margin to account for filter-indicator
|
||||
margin-left: 38px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import FilterObject from './FilterObject.vue';
|
||||
import FilterObject from './FilterObject.vue';
|
||||
import GlobalFilters from './GlobalFilters.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
FilterObject
|
||||
},
|
||||
inject: [
|
||||
'openmct'
|
||||
],
|
||||
data() {
|
||||
let providedObject = this.openmct.selection.get()[0][0].context.item;
|
||||
let persistedFilters = {};
|
||||
const FILTER_VIEW_TITLE = 'Filters applied';
|
||||
const FILTER_VIEW_TITLE_MIXED = 'Mixed filters applied';
|
||||
const USE_GLOBAL = 'useGlobal';
|
||||
|
||||
if (providedObject.configuration && providedObject.configuration.filters) {
|
||||
persistedFilters = providedObject.configuration.filters;
|
||||
}
|
||||
export default {
|
||||
components: {
|
||||
FilterObject,
|
||||
GlobalFilters
|
||||
},
|
||||
inject: [
|
||||
'openmct'
|
||||
],
|
||||
data() {
|
||||
let providedObject = this.openmct.selection.get()[0][0].context.item;
|
||||
let configuration = providedObject.configuration;
|
||||
|
||||
return {
|
||||
providedObject,
|
||||
persistedFilters,
|
||||
children: {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addChildren(child) {
|
||||
let keyString = this.openmct.objects.makeKeyString(child.identifier),
|
||||
metadata = this.openmct.telemetry.getMetadata(child),
|
||||
valuesWithFilters = metadata.valueMetadatas.filter((value) => value.filters),
|
||||
childObject = {
|
||||
name: child.name,
|
||||
domainObject: child,
|
||||
valuesWithFilters
|
||||
};
|
||||
|
||||
if (childObject.valuesWithFilters.length) {
|
||||
this.$set(this.children, keyString, childObject);
|
||||
} else {
|
||||
return;
|
||||
return {
|
||||
persistedFilters: (configuration && configuration.filters) || {},
|
||||
globalFilters: (configuration && configuration.globalFilters) || {},
|
||||
globalMetadata: {},
|
||||
providedObject,
|
||||
children: {}
|
||||
}
|
||||
},
|
||||
removeChildren(identifier) {
|
||||
let keyString = this.openmct.objects.makeKeyString(identifier);
|
||||
this.$delete(this.children, keyString);
|
||||
delete this.persistedFilters[keyString];
|
||||
this.mutateConfigurationFilters();
|
||||
computed: {
|
||||
hasActiveFilters() {
|
||||
// Should be true when the user has entered any filter values.
|
||||
return Object.values(this.persistedFilters).some(filters => {
|
||||
return Object.values(filters).some(comparator => {
|
||||
return (typeof(comparator) === 'object' && !_.isEmpty(comparator));
|
||||
});
|
||||
});
|
||||
},
|
||||
hasMixedFilters() {
|
||||
// Should be true when filter values are mixed.
|
||||
let filtersToCompare = _.omit(this.persistedFilters[Object.keys(this.persistedFilters)[0]], [USE_GLOBAL]);
|
||||
return Object.values(this.persistedFilters).some(filters => {
|
||||
return !_.isEqual(filtersToCompare, _.omit(filters, [USE_GLOBAL]));
|
||||
});
|
||||
},
|
||||
label() {
|
||||
if (this.hasActiveFilters) {
|
||||
if (this.hasMixedFilters) {
|
||||
return FILTER_VIEW_TITLE_MIXED;
|
||||
} else {
|
||||
return FILTER_VIEW_TITLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
persistFilters(keyString, userSelects) {
|
||||
this.persistedFilters[keyString] = userSelects;
|
||||
this.mutateConfigurationFilters();
|
||||
methods: {
|
||||
addChildren(domainObject) {
|
||||
let keyString = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||
let metadata = this.openmct.telemetry.getMetadata(domainObject);
|
||||
let metadataWithFilters = metadata.valueMetadatas.filter(value => value.filters);
|
||||
let hasFiltersWithKeyString = this.persistedFilters[keyString] !== undefined;
|
||||
let mutateFilters = false;
|
||||
let childObject = {
|
||||
name: domainObject.name,
|
||||
domainObject: domainObject,
|
||||
metadataWithFilters
|
||||
};
|
||||
|
||||
if (metadataWithFilters.length) {
|
||||
this.$set(this.children, keyString, childObject);
|
||||
|
||||
metadataWithFilters.forEach(metadatum => {
|
||||
if (!this.globalFilters[metadatum.key]) {
|
||||
this.$set(this.globalFilters, metadatum.key, {});
|
||||
}
|
||||
|
||||
if (!this.globalMetadata[metadatum.key]) {
|
||||
this.$set(this.globalMetadata, metadatum.key, metadatum);
|
||||
}
|
||||
|
||||
if (!hasFiltersWithKeyString) {
|
||||
if (!this.persistedFilters[keyString]) {
|
||||
this.$set(this.persistedFilters, keyString, {});
|
||||
this.$set(this.persistedFilters[keyString], 'useGlobal', true);
|
||||
mutateFilters = true;
|
||||
}
|
||||
|
||||
this.$set(this.persistedFilters[keyString], metadatum.key, this.globalFilters[metadatum.key]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (mutateFilters) {
|
||||
this.mutateConfigurationFilters();
|
||||
}
|
||||
},
|
||||
removeChildren(identifier) {
|
||||
let keyString = this.openmct.objects.makeKeyString(identifier);
|
||||
let globalFiltersToRemove = this.getGlobalFiltersToRemove(keyString);
|
||||
|
||||
if (globalFiltersToRemove.length > 0) {
|
||||
globalFiltersToRemove.forEach(key => {
|
||||
this.$delete(this.globalFilters, key);
|
||||
this.$delete(this.globalMetadata, key);
|
||||
});
|
||||
this.mutateConfigurationGlobalFilters();
|
||||
}
|
||||
|
||||
this.$delete(this.children, keyString);
|
||||
this.$delete(this.persistedFilters, keyString);
|
||||
this.mutateConfigurationFilters();
|
||||
},
|
||||
getGlobalFiltersToRemove(keyString) {
|
||||
let filtersToRemove = new Set();
|
||||
|
||||
this.children[keyString].metadataWithFilters.forEach(metadatum => {
|
||||
let keepFilter = false
|
||||
Object.keys(this.children).forEach(childKeyString => {
|
||||
if (childKeyString !== keyString) {
|
||||
let filterMatched = this.children[childKeyString].metadataWithFilters.some(childMetadatum => childMetadatum.key === metadatum.key);
|
||||
|
||||
if (filterMatched) {
|
||||
keepFilter = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!keepFilter) {
|
||||
filtersToRemove.add(metadatum.key);
|
||||
}
|
||||
});
|
||||
|
||||
return Array.from(filtersToRemove);
|
||||
},
|
||||
persistFilters(keyString, updatedFilters, useGlobalValues) {
|
||||
this.persistedFilters[keyString] = updatedFilters;
|
||||
|
||||
if (useGlobalValues) {
|
||||
Object.keys(this.persistedFilters[keyString]).forEach(key => {
|
||||
if (typeof(this.persistedFilters[keyString][key]) === 'object') {
|
||||
this.persistedFilters[keyString][key] = this.globalFilters[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.mutateConfigurationFilters();
|
||||
},
|
||||
updatePersistedFilters(filters) {
|
||||
this.persistedFilters = filters;
|
||||
},
|
||||
persistGlobalFilters(key, filters) {
|
||||
this.globalFilters[key] = filters[key];
|
||||
this.mutateConfigurationGlobalFilters();
|
||||
let mutateFilters = false;
|
||||
|
||||
Object.keys(this.children).forEach(keyString => {
|
||||
if (this.persistedFilters[keyString].useGlobal !== false && this.containsField(keyString, key)) {
|
||||
if (!this.persistedFilters[keyString][key]) {
|
||||
this.$set(this.persistedFilters[keyString], key, {});
|
||||
}
|
||||
|
||||
this.$set(this.persistedFilters[keyString], key, filters[key]);
|
||||
mutateFilters = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (mutateFilters) {
|
||||
this.mutateConfigurationFilters();
|
||||
}
|
||||
},
|
||||
updateGlobalFilters(filters) {
|
||||
this.globalFilters = filters;
|
||||
},
|
||||
containsField(keyString, field) {
|
||||
let hasField = false;
|
||||
this.children[keyString].metadataWithFilters.forEach(metadatum => {
|
||||
if (metadatum.key === field) {
|
||||
hasField = true;
|
||||
return;
|
||||
}
|
||||
});
|
||||
return hasField;
|
||||
},
|
||||
mutateConfigurationFilters() {
|
||||
this.openmct.objects.mutate(this.providedObject, 'configuration.filters', this.persistedFilters);
|
||||
},
|
||||
mutateConfigurationGlobalFilters() {
|
||||
this.openmct.objects.mutate(this.providedObject, 'configuration.globalFilters', this.globalFilters);
|
||||
}
|
||||
},
|
||||
updatePersistedFilters(filters) {
|
||||
this.persistedFilters = filters;
|
||||
mounted(){
|
||||
this.composition = this.openmct.composition.get(this.providedObject);
|
||||
this.composition.on('add', this.addChildren);
|
||||
this.composition.on('remove', this.removeChildren);
|
||||
this.composition.load();
|
||||
this.unobserve = this.openmct.objects.observe(this.providedObject, 'configuration.filters', this.updatePersistedFilters);
|
||||
this.unobserveGlobalFilters = this.openmct.objects.observe(this.providedObject, 'configuration.globalFilters', this.updateGlobalFilters);
|
||||
this.unobserveAllMutation = this.openmct.objects.observe(this.providedObject, '*', (mutatedObject) => this.providedObject = mutatedObject);
|
||||
},
|
||||
mutateConfigurationFilters() {
|
||||
this.openmct.objects.mutate(this.providedObject, 'configuration.filters', this.persistedFilters);
|
||||
beforeDestroy() {
|
||||
this.composition.off('add', this.addChildren);
|
||||
this.composition.off('remove', this.removeChildren);
|
||||
this.unobserve();
|
||||
this.unobserveGlobalFilters();
|
||||
this.unobserveAllMutation();
|
||||
}
|
||||
},
|
||||
mounted(){
|
||||
this.composition = this.openmct.composition.get(this.providedObject);
|
||||
this.composition.on('add', this.addChildren);
|
||||
this.composition.on('remove', this.removeChildren);
|
||||
this.composition.load();
|
||||
this.unobserve = this.openmct.objects.observe(this.providedObject, 'configuration.filters', this.updatePersistedFilters);
|
||||
this.unobserveAllMutation = this.openmct.objects.observe(this.providedObject, '*', (mutatedObject) => this.providedObject = mutatedObject);
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.composition.off('add', this.addChildren);
|
||||
this.composition.off('remove', this.removeChildren);
|
||||
this.unobserve();
|
||||
this.unobserveAllMutation();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
134
src/plugins/filters/components/GlobalFilters.vue
Normal file
134
src/plugins/filters/components/GlobalFilters.vue
Normal file
@@ -0,0 +1,134 @@
|
||||
<template>
|
||||
<li class="c-tree__item-h">
|
||||
<div class="c-tree__item menus-to-left"
|
||||
@click="toggleExpanded">
|
||||
<div class="c-filter-tree-item__filter-indicator"
|
||||
:class="{'icon-filter': hasActiveGlobalFilters }"></div>
|
||||
<span class="c-disclosure-triangle is-enabled flex-elem"
|
||||
:class="{'c-disclosure-triangle--expanded': expanded}"></span>
|
||||
<div class="c-tree__item__label c-object-label">
|
||||
<div class="c-object-label">
|
||||
<div class="c-object-label__type-icon icon-gear"></div>
|
||||
<div class="c-object-label__name flex-elem grows">Global Filtering</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="c-properties" v-if="expanded">
|
||||
<filter-field
|
||||
v-for="metadatum in globalMetadata"
|
||||
:key="metadatum.key"
|
||||
:filterField="metadatum"
|
||||
:persistedFilters="updatedFilters[metadatum.key]"
|
||||
@filterSelected="updateFiltersWithSelectedValue"
|
||||
@filterTextValueChanged="updateFiltersWithTextValue">
|
||||
</filter-field>
|
||||
</ul>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import "~styles/sass-base";
|
||||
.c-filter-indication {
|
||||
// Appears as a block element beneath tables
|
||||
@include userSelectNone();
|
||||
background: $colorFilterBg;
|
||||
color: $colorFilterFg;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 0.9em;
|
||||
margin-top: $interiorMarginSm;
|
||||
padding: 2px;
|
||||
text-transform: uppercase;
|
||||
|
||||
&:before {
|
||||
font-family: symbolsfont-12px;
|
||||
content: $glyph-icon-filter;
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
margin-right: $interiorMarginSm;
|
||||
}
|
||||
}
|
||||
|
||||
.c-filter-tree-item {
|
||||
&__filter-indicator {
|
||||
color: $colorFilter;
|
||||
width: 1.2em; // Set width explicitly for layout reasons: will either have class icon-filter, or none.
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import FilterField from './FilterField.vue';
|
||||
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
components: {
|
||||
FilterField
|
||||
},
|
||||
props: {
|
||||
globalMetadata: Object,
|
||||
globalFilters: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
expanded: false,
|
||||
updatedFilters: JSON.parse(JSON.stringify(this.globalFilters))
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
hasActiveGlobalFilters() {
|
||||
return Object.values(this.globalFilters).some(field => {
|
||||
return Object.values(field).some(comparator => {
|
||||
return (comparator && (comparator !== '' || comparator.length > 0));
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
globalFilters: {
|
||||
handler: function checkFilters(newGlobalFilters) {
|
||||
this.updatedFilters = JSON.parse(JSON.stringify(newGlobalFilters));
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleExpanded() {
|
||||
this.expanded = !this.expanded;
|
||||
},
|
||||
updateFiltersWithSelectedValue(key, comparator, valueName, value) {
|
||||
let filterValue = this.updatedFilters[key];
|
||||
|
||||
if (filterValue[comparator]) {
|
||||
if (value === true) {
|
||||
filterValue[comparator].push(valueName);
|
||||
} else {
|
||||
if (filterValue[comparator].length === 1) {
|
||||
this.$set(this.updatedFilters, key, {});
|
||||
} else {
|
||||
filterValue[comparator] = filterValue[comparator].filter(v => v !== valueName);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.$set(this.updatedFilters[key], comparator, [valueName]);
|
||||
}
|
||||
|
||||
this.$emit('persistGlobalFilters', key, this.updatedFilters);
|
||||
},
|
||||
updateFiltersWithTextValue(key, comparator, value) {
|
||||
if (value.trim() === '') {
|
||||
this.$set(this.updatedFilters, key, {});
|
||||
} else {
|
||||
this.$set(this.updatedFilters[key], comparator, value);
|
||||
}
|
||||
|
||||
this.$emit('persistGlobalFilters', key, this.updatedFilters);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -21,7 +21,7 @@
|
||||
*****************************************************************************/
|
||||
|
||||
define([
|
||||
'./filtersInspectorViewProvider'
|
||||
'./FiltersInspectorViewProvider'
|
||||
], function (
|
||||
FiltersInspectorViewProvider
|
||||
) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import uuid from 'uuid';
|
||||
|
||||
class Container {
|
||||
constructor (size) {
|
||||
constructor(size) {
|
||||
this.id = uuid();
|
||||
this.frames = [];
|
||||
this.size = size;
|
||||
|
||||
@@ -27,7 +27,7 @@ function (
|
||||
Moment
|
||||
) {
|
||||
|
||||
function EntryController (openmct, domainObject) {
|
||||
function EntryController(openmct, domainObject) {
|
||||
this.openmct = openmct;
|
||||
this.domainObject = domainObject;
|
||||
|
||||
@@ -111,7 +111,7 @@ function (
|
||||
domainObject = objectPath[0],
|
||||
domainObjectKey = domainObject.identifier.key,
|
||||
domainObjectType = this.openmct.types.get(domainObject.type),
|
||||
cssClass = domainObjectType && domainObjectType.definition ?
|
||||
cssClass = domainObjectType && domainObjectType.definition ?
|
||||
domainObjectType.definition.cssClass : 'icon-object-unknown',
|
||||
entryPos = this.entryPosById(entryid),
|
||||
currentEntryEmbeds = this.domainObject.entries[entryPos].embeds,
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
*****************************************************************************/
|
||||
define(function () {
|
||||
class TelemetryTableColumn {
|
||||
constructor (openmct, metadatum, options = {selectable: false}) {
|
||||
constructor(openmct, metadatum, options = {selectable: false}) {
|
||||
this.metadatum = metadatum;
|
||||
this.formatter = openmct.telemetry.getValueFormatter(metadatum);
|
||||
this.titleValue = this.metadatum.name;
|
||||
|
||||
@@ -58,7 +58,7 @@ define([
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {*} object
|
||||
* @param {*} object
|
||||
*/
|
||||
objectMutated(object) {
|
||||
//Synchronize domain object reference. Duplicate object otherwise change detection becomes impossible.
|
||||
|
||||
@@ -79,7 +79,7 @@ define([], function () {
|
||||
* Maps all sources to keys.
|
||||
* @private
|
||||
* @param {*} telemetryDatum
|
||||
* @param {*} metadataValues
|
||||
* @param {*} metadataValues
|
||||
*/
|
||||
function createNormalizedDatum(datum, columns) {
|
||||
return Object.values(columns).reduce((normalizedDatum, column) => {
|
||||
|
||||
@@ -60,7 +60,7 @@ define([
|
||||
}
|
||||
},
|
||||
components: {
|
||||
TableComponent: TableComponent.default,
|
||||
TableComponent: TableComponent.default
|
||||
},
|
||||
provide: {
|
||||
openmct,
|
||||
|
||||
@@ -37,7 +37,7 @@ define(
|
||||
//Synchronize with master collection
|
||||
this.masterCollection.on('add', this.add);
|
||||
this.masterCollection.on('remove', this.remove);
|
||||
|
||||
|
||||
//Default to master collection's sort options
|
||||
this.sortOptions = masterCollection.sortBy();
|
||||
}
|
||||
@@ -70,9 +70,9 @@ define(
|
||||
* @private
|
||||
*/
|
||||
isSubsetOfCurrentFilter(columnKey, filter) {
|
||||
return this.columnFilters[columnKey] &&
|
||||
return this.columnFilters[columnKey] &&
|
||||
filter.startsWith(this.columnFilters[columnKey]) &&
|
||||
// startsWith check will otherwise fail when filter cleared
|
||||
// startsWith check will otherwise fail when filter cleared
|
||||
// because anyString.startsWith('') === true
|
||||
filter !== '';
|
||||
}
|
||||
@@ -91,8 +91,8 @@ define(
|
||||
return false;
|
||||
} else {
|
||||
let formattedValue = row.getFormattedValue(key).toLowerCase();
|
||||
doesMatchFilters = doesMatchFilters &&
|
||||
formattedValue.indexOf(this.columnFilters[key]) !== -1;
|
||||
doesMatchFilters = doesMatchFilters &&
|
||||
formattedValue.indexOf(this.columnFilters[key]) !== -1;
|
||||
}
|
||||
}
|
||||
return doesMatchFilters;
|
||||
@@ -109,4 +109,4 @@ define(
|
||||
}
|
||||
|
||||
return FilteredTableRowCollection;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -37,7 +37,7 @@ define(
|
||||
* @constructor
|
||||
*/
|
||||
class SortedTableRowCollection extends EventEmitter {
|
||||
constructor () {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.dupeCheck = false;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div v-if="filterNames.length > 0"
|
||||
:title=title
|
||||
class="c-filter-indication"
|
||||
:class="{ 'c-filter-indication--mixed': mixed }">
|
||||
:class="{ 'c-filter-indication--mixed': hasMixedFilters }">
|
||||
<span class="c-filter-indication__mixed">{{ label }}</span>
|
||||
<span v-for="(name, index) in filterNames"
|
||||
class="c-filter-indication__label">
|
||||
@@ -33,7 +33,6 @@
|
||||
}
|
||||
|
||||
&__mixed {
|
||||
font-weight: bold;
|
||||
margin-right: $interiorMarginSm;
|
||||
}
|
||||
|
||||
@@ -58,119 +57,109 @@
|
||||
const FILTER_INDICATOR_LABEL_MIXED = 'Mixed Filters:';
|
||||
const FILTER_INDICATOR_TITLE = 'Data filters are being applied to this view.';
|
||||
const FILTER_INDICATOR_TITLE_MIXED = 'A mix of data filter values are being applied to this view.';
|
||||
const USE_GLOBAL = 'useGlobal';
|
||||
|
||||
export default {
|
||||
inject: ['openmct', 'table'],
|
||||
data() {
|
||||
return {
|
||||
filterNames: [],
|
||||
filteredTelemetry: {},
|
||||
mixed: false,
|
||||
label: '',
|
||||
title: ''
|
||||
filteredTelemetry: {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isTelemetryObject(domainObject) {
|
||||
return domainObject.hasOwnProperty('telemetry');
|
||||
computed: {
|
||||
hasMixedFilters() {
|
||||
let filtersToCompare = _.omit(this.filteredTelemetry[Object.keys(this.filteredTelemetry)[0]], [USE_GLOBAL]);
|
||||
return Object.values(this.filteredTelemetry).some(filters => {
|
||||
return !_.isEqual(filtersToCompare, _.omit(filters, [USE_GLOBAL]));
|
||||
});
|
||||
},
|
||||
label() {
|
||||
if (this.hasMixedFilters) {
|
||||
return FILTER_INDICATOR_LABEL_MIXED;
|
||||
} else {
|
||||
return FILTER_INDICATOR_LABEL;
|
||||
}
|
||||
},
|
||||
title() {
|
||||
if (this.hasMixedFilters) {
|
||||
return FILTER_INDICATOR_TITLE_MIXED;
|
||||
} else {
|
||||
return FILTER_INDICATOR_TITLE;
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setFilterNames() {
|
||||
let names = [];
|
||||
let composition = this.openmct.composition.get(this.table.configuration.domainObject);
|
||||
|
||||
this.composition && this.composition.load().then((domainObjects) => {
|
||||
composition && composition.load().then((domainObjects) => {
|
||||
domainObjects.forEach(telemetryObject => {
|
||||
let keyString= this.openmct.objects.makeKeyString(telemetryObject.identifier);
|
||||
let metadataValues = this.openmct.telemetry.getMetadata(telemetryObject).values();
|
||||
let filters = this.filteredTelemetry[keyString];
|
||||
this.telemetryKeyStrings.add(keyString);
|
||||
|
||||
if (filters !== undefined) {
|
||||
let metadataValues = this.openmct.telemetry.getMetadata(telemetryObject).values();
|
||||
Object.keys(filters).forEach(key => {
|
||||
metadataValues.forEach(metadaum => {
|
||||
|
||||
if (key === metadaum.key) {
|
||||
names.push(metadaum.name);
|
||||
}
|
||||
});
|
||||
});
|
||||
names.push(this.getFilterNamesFromMetadata(filters, metadataValues));
|
||||
}
|
||||
});
|
||||
this.filterNames = Array.from(new Set(names));
|
||||
|
||||
names = _.flatten(names);
|
||||
this.filterNames = names.length === 0 ? names : Array.from(new Set(names));
|
||||
});
|
||||
},
|
||||
getFilterNamesFromMetadata(filters, metadataValues) {
|
||||
let filterNames = [];
|
||||
filters = _.omit(filters, [USE_GLOBAL]);
|
||||
|
||||
Object.keys(filters).forEach(key => {
|
||||
if (!_.isEmpty(filters[key])) {
|
||||
metadataValues.forEach(metadatum => {
|
||||
if (key === metadatum.key) {
|
||||
if (typeof metadatum.filters[0] === "object") {
|
||||
filterNames.push(this.getFilterLabels(filters[key], metadatum));
|
||||
} else {
|
||||
filterNames.push(metadatum.name);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return _.flatten(filterNames);
|
||||
},
|
||||
getFilterLabels(filterObject, metadatum, ) {
|
||||
let filterLabels = [];
|
||||
Object.values(filterObject).forEach(comparator => {
|
||||
comparator.forEach(filterValue => {
|
||||
metadatum.filters[0].possibleValues.forEach(option => {
|
||||
if (option.value === filterValue) {
|
||||
filterLabels.push(option.label);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return filterLabels;
|
||||
},
|
||||
handleConfigurationChanges(configuration) {
|
||||
if (!_.eq(this.filteredTelemetry, configuration.filters)) {
|
||||
this.updateFilters(configuration.filters || {});
|
||||
}
|
||||
},
|
||||
checkFiltersForMixedValues() {
|
||||
let valueToCompare = this.filteredTelemetry[Object.keys(this.filteredTelemetry)[0]];
|
||||
let mixed = false;
|
||||
|
||||
Object.values(this.filteredTelemetry).forEach(value => {
|
||||
if (!_.isEqual(valueToCompare, value)) {
|
||||
mixed = true;
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
// If the filtered telemetry is not mixed at this point, check the number of available objects
|
||||
// with the number of filtered telemetry. If they are not equal, the filters must be mixed.
|
||||
if (mixed === false && _.size(this.filteredTelemetry) !== this.telemetryKeyStrings.size) {
|
||||
mixed = true;
|
||||
}
|
||||
|
||||
this.mixed = mixed;
|
||||
},
|
||||
setLabels() {
|
||||
if (this.mixed) {
|
||||
this.label = FILTER_INDICATOR_LABEL_MIXED;
|
||||
this.title = FILTER_INDICATOR_TITLE_MIXED;
|
||||
} else {
|
||||
this.label = FILTER_INDICATOR_LABEL;
|
||||
this.title = FILTER_INDICATOR_TITLE;
|
||||
}
|
||||
},
|
||||
updateFilters(filters) {
|
||||
this.filteredTelemetry = JSON.parse(JSON.stringify(filters));
|
||||
this.setFilterNames();
|
||||
this.updateIndicatorLabel();
|
||||
},
|
||||
addChildren(child) {
|
||||
let keyString = this.openmct.objects.makeKeyString(child.identifier);
|
||||
this.telemetryKeyStrings.add(keyString);
|
||||
this.updateIndicatorLabel();
|
||||
},
|
||||
removeChildren(identifier) {
|
||||
let keyString = this.openmct.objects.makeKeyString(identifier);
|
||||
this.telemetryKeyStrings.delete(keyString);
|
||||
this.updateIndicatorLabel();
|
||||
},
|
||||
updateIndicatorLabel() {
|
||||
this.checkFiltersForMixedValues();
|
||||
this.setLabels();
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
let filters = this.table.configuration.getConfiguration().filters || {};
|
||||
this.telemetryKeyStrings = new Set();
|
||||
this.composition = this.openmct.composition.get(this.table.configuration.domainObject);
|
||||
|
||||
if (this.composition) {
|
||||
this.composition.on('add', this.addChildren);
|
||||
this.composition.on('remove', this.removeChildren);
|
||||
}
|
||||
|
||||
this.table.configuration.on('change', this.handleConfigurationChanges);
|
||||
this.updateFilters(filters);
|
||||
},
|
||||
destroyed() {
|
||||
this.table.configuration.off('change', this.handleConfigurationChanges);
|
||||
|
||||
if (this.composition) {
|
||||
this.composition.off('add', this.addChildren);
|
||||
this.composition.off('remove', this.removeChildren);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
<template>
|
||||
<td @click="selectCell($event.currentTarget, columnKey)">{{formattedValue}}</td>
|
||||
<td @click="selectCell($event.currentTarget, columnKey)" :title="formattedValue">{{formattedValue}}</td>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
<div class="c-properties__header">Table Column Size</div>
|
||||
<ul class="c-properties__section">
|
||||
<li class="c-properties__row">
|
||||
<div class="c-properties__label" title="Show or Hide Column"><label for="AutoSizeControl">Auto-size</label></div>
|
||||
<div class="c-properties__label" title="Auto-size table"><label for="AutoSizeControl">Auto-size</label></div>
|
||||
<div class="c-properties__value"><input type="checkbox" id="AutoSizeControl" :checked="configuration.autosize !== false" @change="toggleAutosize()"></div>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="c-properties__header">Table Column Visibility</div>
|
||||
<ul class="c-properties__section">
|
||||
<li class="c-properties__row" v-for="(title, key) in headers">
|
||||
<div class="c-properties__label" title="Show or Hide Column"><label :for="key + 'ColumnControl'">{{title}}</label></div>
|
||||
<div class="c-properties__label" title="Show or hide column"><label :for="key + 'ColumnControl'">{{title}}</label></div>
|
||||
<div class="c-properties__value"><input type="checkbox" :id="key + 'ColumnControl'" :checked="configuration.hiddenColumns[key] !== true" @change="toggleColumn(key)"></div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
:is="componentList[key]"
|
||||
:columnKey="key"
|
||||
:style="columnWidths[key] === undefined ? {} : { width: columnWidths[key] + 'px', 'max-width': columnWidths[key] + 'px'}"
|
||||
:title="formattedRow[key]"
|
||||
:class="[cellLimitClasses[key], selectableColumns[key] ? 'is-selectable' : '']"
|
||||
:objectPath="objectPath"
|
||||
:row="row">
|
||||
@@ -60,7 +59,6 @@ export default {
|
||||
data: function () {
|
||||
return {
|
||||
rowTop: (this.rowOffset + this.rowIndex) * this.rowHeight + 'px',
|
||||
formattedRow: this.row.getFormattedDatum(this.headers),
|
||||
rowClass: this.row.getRowClass(),
|
||||
cellLimitClasses: this.row.getCellLimitClasses(),
|
||||
componentList: Object.keys(this.headers).reduce((components, header) => {
|
||||
@@ -116,7 +114,6 @@ export default {
|
||||
this.rowTop = (rowOffset + this.rowIndex) * this.rowHeight + 'px';
|
||||
},
|
||||
formatRow: function (row) {
|
||||
this.formattedRow = row.getFormattedDatum(this.headers);
|
||||
this.rowClass = row.getRowClass();
|
||||
this.cellLimitClasses = row.getCellLimitClasses();
|
||||
},
|
||||
|
||||
@@ -28,213 +28,213 @@ define(
|
||||
function (
|
||||
EventEmitter,
|
||||
_
|
||||
) {
|
||||
) {
|
||||
|
||||
/**
|
||||
* Manages selection state for Open MCT
|
||||
* @private
|
||||
*/
|
||||
function Selection(openmct) {
|
||||
EventEmitter.call(this);
|
||||
/**
|
||||
* Manages selection state for Open MCT
|
||||
* @private
|
||||
*/
|
||||
function Selection(openmct) {
|
||||
EventEmitter.call(this);
|
||||
|
||||
this.openmct = openmct;
|
||||
this.selected = [];
|
||||
}
|
||||
|
||||
Selection.prototype = Object.create(EventEmitter.prototype);
|
||||
|
||||
/**
|
||||
* Gets the selected object.
|
||||
* @public
|
||||
*/
|
||||
Selection.prototype.get = function () {
|
||||
return this.selected;
|
||||
};
|
||||
|
||||
/**
|
||||
* Selects the selectable object and emits the 'change' event.
|
||||
*
|
||||
* @param {object} selectable an object with element and context properties
|
||||
* @param {Boolean} isMultiSelectEvent flag indication shift key is pressed or not
|
||||
* @private
|
||||
*/
|
||||
Selection.prototype.select = function (selectable, isMultiSelectEvent) {
|
||||
if (!Array.isArray(selectable)) {
|
||||
selectable = [selectable];
|
||||
this.openmct = openmct;
|
||||
this.selected = [];
|
||||
}
|
||||
|
||||
let multiSelect = isMultiSelectEvent &&
|
||||
this.parentSupportsMultiSelect(selectable) &&
|
||||
this.isPeer(selectable) &&
|
||||
!this.selectionContainsParent(selectable);
|
||||
Selection.prototype = Object.create(EventEmitter.prototype);
|
||||
|
||||
if (multiSelect) {
|
||||
this.handleMultiSelect(selectable);
|
||||
} else {
|
||||
this.setSelectionStyles(selectable);
|
||||
this.selected = [selectable];
|
||||
}
|
||||
|
||||
this.emit('change', this.selected);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
Selection.prototype.handleMultiSelect = function (selectable) {
|
||||
if (this.elementSelected(selectable)) {
|
||||
this.remove(selectable);
|
||||
} else {
|
||||
this.addSelectionAttributes(selectable);
|
||||
this.selected.push(selectable);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
Selection.prototype.elementSelected = function (selectable) {
|
||||
return this.selected.some(selectionPath => _.isEqual(selectionPath, selectable));
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
Selection.prototype.remove = function (selectable) {
|
||||
this.selected = this.selected.filter(selectionPath => !_.isEqual(selectionPath, selectable));
|
||||
|
||||
if (this.selected.length === 0) {
|
||||
this.removeSelectionAttributes(selectable);
|
||||
selectable[1].element.click(); // Select the parent if there is no selection.
|
||||
} else {
|
||||
this.removeSelectionAttributes(selectable, true);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
Selection.prototype.setSelectionStyles = function (selectable) {
|
||||
this.selected.map(selectionPath => {
|
||||
this.removeSelectionAttributes(selectionPath);
|
||||
});
|
||||
this.addSelectionAttributes(selectable);
|
||||
};
|
||||
|
||||
Selection.prototype.removeSelectionAttributes = function (selectionPath, keepParentStyle) {
|
||||
if (selectionPath[0] && selectionPath[0].element) {
|
||||
selectionPath[0].element.removeAttribute('s-selected');
|
||||
}
|
||||
|
||||
if (selectionPath[1] && selectionPath[1].element && !keepParentStyle) {
|
||||
selectionPath[1].element.removeAttribute('s-selected-parent');
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Adds selection attributes to the selected element and its parent.
|
||||
* @private
|
||||
*/
|
||||
Selection.prototype.addSelectionAttributes = function (selectable) {
|
||||
if (selectable[0] && selectable[0].element) {
|
||||
selectable[0].element.setAttribute('s-selected', "");
|
||||
}
|
||||
|
||||
if (selectable[1] && selectable[1].element) {
|
||||
selectable[1].element.setAttribute('s-selected-parent', "");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
Selection.prototype.parentSupportsMultiSelect = function (selectable) {
|
||||
return selectable[1] && selectable[1].context.supportsMultiSelect;
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
Selection.prototype.selectionContainsParent = function (selectable) {
|
||||
return this.selected.some(selectionPath => _.isEqual(selectionPath[0], selectable[1]));
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
Selection.prototype.isPeer = function (selectable) {
|
||||
return this.selected.some(selectionPath => _.isEqual(selectionPath[1], selectable[1]));
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
Selection.prototype.capture = function (selectable) {
|
||||
let capturingContainsSelectable = this.capturing && this.capturing.includes(selectable);
|
||||
|
||||
if (!this.capturing || capturingContainsSelectable) {
|
||||
this.capturing = [];
|
||||
}
|
||||
|
||||
this.capturing.push(selectable);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
Selection.prototype.selectCapture = function (selectable, event) {
|
||||
if (!this.capturing) {
|
||||
return;
|
||||
}
|
||||
|
||||
let reversedCapturing = this.capturing.reverse();
|
||||
delete this.capturing;
|
||||
this.select(reversedCapturing, event.shiftKey);
|
||||
};
|
||||
|
||||
/**
|
||||
* Attaches the click handlers to the element.
|
||||
*
|
||||
* @param element an html element
|
||||
* @param context object which defines item or other arbitrary properties.
|
||||
* e.g. {
|
||||
* item: domainObject,
|
||||
* elementProxy: element,
|
||||
* controller: fixedController
|
||||
* }
|
||||
* @param select a flag to select the element if true
|
||||
* @returns a function that removes the click handlers from the element
|
||||
* @public
|
||||
*/
|
||||
Selection.prototype.selectable = function (element, context, select) {
|
||||
let selectable = {
|
||||
context: context,
|
||||
element: element
|
||||
/**
|
||||
* Gets the selected object.
|
||||
* @public
|
||||
*/
|
||||
Selection.prototype.get = function () {
|
||||
return this.selected;
|
||||
};
|
||||
var capture = this.capture.bind(this, selectable);
|
||||
var selectCapture = this.selectCapture.bind(this, selectable);
|
||||
element.addEventListener('click', capture, true);
|
||||
element.addEventListener('click', selectCapture);
|
||||
|
||||
if (context.item) {
|
||||
var unlisten = this.openmct.objects.observe(context.item, "*", function (newItem) {
|
||||
context.item = newItem;
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Selects the selectable object and emits the 'change' event.
|
||||
*
|
||||
* @param {object} selectable an object with element and context properties
|
||||
* @param {Boolean} isMultiSelectEvent flag indication shift key is pressed or not
|
||||
* @private
|
||||
*/
|
||||
Selection.prototype.select = function (selectable, isMultiSelectEvent) {
|
||||
if (!Array.isArray(selectable)) {
|
||||
selectable = [selectable];
|
||||
}
|
||||
|
||||
if (select) {
|
||||
element.click();
|
||||
}
|
||||
let multiSelect = isMultiSelectEvent &&
|
||||
this.parentSupportsMultiSelect(selectable) &&
|
||||
this.isPeer(selectable) &&
|
||||
!this.selectionContainsParent(selectable);
|
||||
|
||||
return function () {
|
||||
element.removeEventListener('click', capture, true);
|
||||
element.removeEventListener('click', selectCapture);
|
||||
if (multiSelect) {
|
||||
this.handleMultiSelect(selectable);
|
||||
} else {
|
||||
this.setSelectionStyles(selectable);
|
||||
this.selected = [selectable];
|
||||
}
|
||||
|
||||
if (unlisten) {
|
||||
unlisten();
|
||||
this.emit('change', this.selected);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
Selection.prototype.handleMultiSelect = function (selectable) {
|
||||
if (this.elementSelected(selectable)) {
|
||||
this.remove(selectable);
|
||||
} else {
|
||||
this.addSelectionAttributes(selectable);
|
||||
this.selected.push(selectable);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
return Selection;
|
||||
});
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
Selection.prototype.elementSelected = function (selectable) {
|
||||
return this.selected.some(selectionPath => _.isEqual(selectionPath, selectable));
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
Selection.prototype.remove = function (selectable) {
|
||||
this.selected = this.selected.filter(selectionPath => !_.isEqual(selectionPath, selectable));
|
||||
|
||||
if (this.selected.length === 0) {
|
||||
this.removeSelectionAttributes(selectable);
|
||||
selectable[1].element.click(); // Select the parent if there is no selection.
|
||||
} else {
|
||||
this.removeSelectionAttributes(selectable, true);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
Selection.prototype.setSelectionStyles = function (selectable) {
|
||||
this.selected.map(selectionPath => {
|
||||
this.removeSelectionAttributes(selectionPath);
|
||||
});
|
||||
this.addSelectionAttributes(selectable);
|
||||
};
|
||||
|
||||
Selection.prototype.removeSelectionAttributes = function (selectionPath, keepParentStyle) {
|
||||
if (selectionPath[0] && selectionPath[0].element) {
|
||||
selectionPath[0].element.removeAttribute('s-selected');
|
||||
}
|
||||
|
||||
if (selectionPath[1] && selectionPath[1].element && !keepParentStyle) {
|
||||
selectionPath[1].element.removeAttribute('s-selected-parent');
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Adds selection attributes to the selected element and its parent.
|
||||
* @private
|
||||
*/
|
||||
Selection.prototype.addSelectionAttributes = function (selectable) {
|
||||
if (selectable[0] && selectable[0].element) {
|
||||
selectable[0].element.setAttribute('s-selected', "");
|
||||
}
|
||||
|
||||
if (selectable[1] && selectable[1].element) {
|
||||
selectable[1].element.setAttribute('s-selected-parent', "");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
Selection.prototype.parentSupportsMultiSelect = function (selectable) {
|
||||
return selectable[1] && selectable[1].context.supportsMultiSelect;
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
Selection.prototype.selectionContainsParent = function (selectable) {
|
||||
return this.selected.some(selectionPath => _.isEqual(selectionPath[0], selectable[1]));
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
Selection.prototype.isPeer = function (selectable) {
|
||||
return this.selected.some(selectionPath => _.isEqual(selectionPath[1], selectable[1]));
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
Selection.prototype.capture = function (selectable) {
|
||||
let capturingContainsSelectable = this.capturing && this.capturing.includes(selectable);
|
||||
|
||||
if (!this.capturing || capturingContainsSelectable) {
|
||||
this.capturing = [];
|
||||
}
|
||||
|
||||
this.capturing.push(selectable);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
Selection.prototype.selectCapture = function (selectable, event) {
|
||||
if (!this.capturing) {
|
||||
return;
|
||||
}
|
||||
|
||||
let reversedCapturing = this.capturing.reverse();
|
||||
delete this.capturing;
|
||||
this.select(reversedCapturing, event.shiftKey);
|
||||
};
|
||||
|
||||
/**
|
||||
* Attaches the click handlers to the element.
|
||||
*
|
||||
* @param element an html element
|
||||
* @param context object which defines item or other arbitrary properties.
|
||||
* e.g. {
|
||||
* item: domainObject,
|
||||
* elementProxy: element,
|
||||
* controller: fixedController
|
||||
* }
|
||||
* @param select a flag to select the element if true
|
||||
* @returns a function that removes the click handlers from the element
|
||||
* @public
|
||||
*/
|
||||
Selection.prototype.selectable = function (element, context, select) {
|
||||
let selectable = {
|
||||
context: context,
|
||||
element: element
|
||||
};
|
||||
var capture = this.capture.bind(this, selectable);
|
||||
var selectCapture = this.selectCapture.bind(this, selectable);
|
||||
element.addEventListener('click', capture, true);
|
||||
element.addEventListener('click', selectCapture);
|
||||
|
||||
if (context.item) {
|
||||
var unlisten = this.openmct.objects.observe(context.item, "*", function (newItem) {
|
||||
context.item = newItem;
|
||||
});
|
||||
}
|
||||
|
||||
if (select) {
|
||||
element.click();
|
||||
}
|
||||
|
||||
return function () {
|
||||
element.removeEventListener('click', capture, true);
|
||||
element.removeEventListener('click', selectCapture);
|
||||
|
||||
if (unlisten) {
|
||||
unlisten();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
return Selection;
|
||||
});
|
||||
|
||||
@@ -116,6 +116,7 @@ $colorOk: #33cc33;
|
||||
$colorOkFg: #fff;
|
||||
$colorFilterBg: #44449c;
|
||||
$colorFilterFg: #8984e9;
|
||||
$colorFilter: $colorFilterFg; // Standalone against $colorBodyBg
|
||||
|
||||
// States
|
||||
$colorPausedBg: #ff9900;
|
||||
|
||||
@@ -120,6 +120,7 @@ $colorOk: #33cc33;
|
||||
$colorOkFg: #fff;
|
||||
$colorFilterBg: #44449c;
|
||||
$colorFilterFg: #8984e9;
|
||||
$colorFilter: $colorFilterFg; // Standalone against $colorBodyBg
|
||||
|
||||
// States
|
||||
$colorPausedBg: #ff9900;
|
||||
|
||||
@@ -116,6 +116,7 @@ $colorOk: #33cc33;
|
||||
$colorOkFg: #fff;
|
||||
$colorFilterBg: #a29fe2;
|
||||
$colorFilterFg: #fff;
|
||||
$colorFilter: $colorFilterBg; // Standalone against $colorBodyBg
|
||||
|
||||
// States
|
||||
$colorPausedBg: #ff9900;
|
||||
|
||||
@@ -93,7 +93,7 @@ $mobileMenuIconD: 24px; // Used
|
||||
$mobileTreeItemH: 35px; // Used
|
||||
|
||||
/************************** VISUAL */
|
||||
$controlDisabledOpacity: 0.3;
|
||||
$controlDisabledOpacity: 0.5;
|
||||
|
||||
/************************** UI ELEMENTS */
|
||||
/*************** Progress Bar */
|
||||
|
||||
@@ -202,6 +202,11 @@ body.desktop .has-local-controls {
|
||||
}
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
opacity: 0.5;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/******************************************************** STATES */
|
||||
@mixin spinner($b: 5px, $c: $colorKey) {
|
||||
animation-name: rotation-centered;
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
align-items: center;
|
||||
flex: 1 1 auto;
|
||||
overflow: hidden;
|
||||
padding: $interiorMarginSm;
|
||||
padding: $interiorMarginSm 1px;
|
||||
white-space: nowrap;
|
||||
|
||||
&__name {
|
||||
|
||||
78
src/ui/components/ToggleSwitch.vue
Normal file
78
src/ui/components/ToggleSwitch.vue
Normal file
@@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<label class="c-toggle-switch">
|
||||
<input type="checkbox"
|
||||
:id="id"
|
||||
:checked="checked"
|
||||
@change="onUserSelect($event)"/>
|
||||
<span class="c-toggle-switch__slider"></span>
|
||||
</label>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import "~styles/sass-base";
|
||||
|
||||
.c-toggle-switch {
|
||||
$d: 12px;
|
||||
$m: 2px;
|
||||
$br: $d/1.5;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
display: inline;
|
||||
vertical-align: middle;
|
||||
|
||||
&__slider {
|
||||
background: $colorBtnBg; // TODO: make discrete theme constants for these colors
|
||||
border-radius: $br;
|
||||
//box-shadow: inset rgba($colorBtnFg, 0.4) 0 0 0 1px;
|
||||
display: inline-block;
|
||||
height: $d + ($m*2);
|
||||
position: relative;
|
||||
transform: translateY(2px); // TODO: get this to work without this kind of hack!
|
||||
width: $d*2 + $m*2;
|
||||
|
||||
&:before {
|
||||
// Knob
|
||||
background: $colorBtnFg; // TODO: make discrete theme constants for these colors
|
||||
border-radius: floor($br * 0.8);
|
||||
box-shadow: rgba(black, 0.4) 0 0 2px;
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
height: $d; width: $d;
|
||||
top: $m; left: $m; right: auto;
|
||||
transition: transform 100ms ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
|
||||
&:checked {
|
||||
+ .c-toggle-switch__slider {
|
||||
background: $colorKey; // TODO: make discrete theme constants for these colors
|
||||
&:before {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
id: String,
|
||||
checked: Boolean
|
||||
},
|
||||
methods: {
|
||||
onUserSelect(event) {
|
||||
this.$emit('change', event.target.checked);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -106,8 +106,10 @@
|
||||
display: contents;
|
||||
}
|
||||
|
||||
&__row + &__row {
|
||||
> [class*="__"] {
|
||||
&__row + &__row,
|
||||
&__section + &__section {
|
||||
[class*="__label"],
|
||||
[class*="__value"] {
|
||||
// Row borders, effected via border-top on child elements of the row
|
||||
border-top: 1px solid $colorInspectorSectionHeaderBg;
|
||||
}
|
||||
|
||||
@@ -77,9 +77,10 @@ const PLACEHOLDER_OBJECT = {};
|
||||
this.showSaveMenu = false;
|
||||
},
|
||||
updateName(event) {
|
||||
// TODO: handle isssues with contenteditable text escaping.
|
||||
if (event.target.innerText !== this.domainObject.name) {
|
||||
if (event.target.innerText !== this.domainObject.name && event.target.innerText.match(/\S/)) {
|
||||
this.openmct.objects.mutate(this.domainObject, 'name', event.target.innerText);
|
||||
} else {
|
||||
event.target.innerText = this.domainObject.name;
|
||||
}
|
||||
},
|
||||
updateNameOnEnterKeyPress (event) {
|
||||
|
||||
@@ -81,6 +81,10 @@
|
||||
padding: $interiorMargin - $aPad;
|
||||
transition: background 150ms ease;
|
||||
|
||||
> * + * {
|
||||
margin-left: $interiorMarginSm;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: $colorItemTreeHoverBg;
|
||||
.c-tree__item__type-icon:before {
|
||||
@@ -116,10 +120,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
&__view-control {
|
||||
margin-right: $interiorMarginSm;
|
||||
}
|
||||
|
||||
// Object labels in trees
|
||||
&__label {
|
||||
// <a> tag that holds type icon and name.
|
||||
|
||||
@@ -25,5 +25,5 @@ export default {
|
||||
},
|
||||
destroyed() {
|
||||
document.removeEventListener('click', this.toggle);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ define(['EventEmitter'], function (EventEmitter) {
|
||||
* OR
|
||||
* * Return a {@link openmct.View} from the `view` function defining a read-only view.
|
||||
* AND
|
||||
* * Define an {@link openmct.ViewProvider#Edit} function on the view provider that returns an
|
||||
* * Define an {@link openmct.ViewProvider#Edit} function on the view provider that returns an
|
||||
* editing-specific view.
|
||||
*
|
||||
* @method canEdit
|
||||
|
||||
Reference in New Issue
Block a user