Compare commits
	
		
			4 Commits
		
	
	
		
			topic-core
			...
			open-issue
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					d71613346d | ||
| 
						 | 
					7e50010463 | ||
| 
						 | 
					974be0ae2c | ||
| 
						 | 
					3dc6dac12d | 
@@ -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']));
 | 
			
		||||
 
 | 
			
		||||
@@ -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>
 | 
			
		||||
 
 | 
			
		||||
@@ -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>
 | 
			
		||||
 
 | 
			
		||||
@@ -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