Use composable instead of using event emitters for extended lines overlays

This commit is contained in:
Shefali 2025-03-18 11:32:09 -07:00
parent 2e35212c32
commit 39ce53f561
5 changed files with 273 additions and 84 deletions

View File

@ -34,8 +34,8 @@
class="c-events-tsv__event-line" class="c-events-tsv__event-line"
:class="event.limitClass || ''" :class="event.limitClass || ''"
:style="`left: ${event.left}px`" :style="`left: ${event.left}px`"
@mouseover="showToolTip(event)" @mouseenter="showToolTip(event)"
@mouseleave="dismissToolTip()" @mouseleave="dismissToolTip(event)"
@click.stop="createSelectionForInspector(event)" @click.stop="createSelectionForInspector(event)"
></div> ></div>
</div> </div>
@ -54,6 +54,7 @@ import SwimLane from '@/ui/components/swim-lane/SwimLane.vue';
import tooltipHelpers from '../../../api/tooltips/tooltipMixins'; import tooltipHelpers from '../../../api/tooltips/tooltipMixins';
import { useAlignment } from '../../../ui/composables/alignmentContext.js'; import { useAlignment } from '../../../ui/composables/alignmentContext.js';
import { useExtendedLines } from '../../../ui/composables/extendedLines';
import eventData from '../mixins/eventData.js'; import eventData from '../mixins/eventData.js';
const PADDING = 1; const PADDING = 1;
@ -68,7 +69,21 @@ export default {
const objectPath = inject('objectPath'); const objectPath = inject('objectPath');
const openmct = inject('openmct'); const openmct = inject('openmct');
const { alignment: alignmentData } = useAlignment(domainObject, objectPath, openmct); const { alignment: alignmentData } = useAlignment(domainObject, objectPath, openmct);
return { alignmentData };
const {
extendedLines: extendedLines,
update: updateExtendedLines,
updateLineHover: updateExtendedLineHover,
remove: removeLinesForObject
} = useExtendedLines(domainObject, objectPath, openmct);
return {
alignmentData,
extendedLines,
updateExtendedLineHover,
updateExtendedLines,
removeLinesForObject
};
}, },
data() { data() {
return { return {
@ -107,13 +122,23 @@ export default {
this.setScaleAndPlotEvents(this.timeSystem); this.setScaleAndPlotEvents(this.timeSystem);
}, },
deep: true deep: true
},
extendedLines: {
handler() {
const { enabled = false } = this.extendedLines[this.keyString];
if (this.extendedLinesEnabled !== enabled) {
this.extendedLinesEnabled = enabled === true;
this.updateLines();
}
},
deep: true
} }
}, },
created() { created() {
this.valueMetadata = {}; this.valueMetadata = {};
this.height = 0; this.height = 0;
this.timeSystem = this.openmct.time.getTimeSystem(); this.timeSystem = this.openmct.time.getTimeSystem();
this.extendLines = false; this.extendedLinesEnabled = false;
}, },
mounted() { mounted() {
this.setDimensions(); this.setDimensions();
@ -136,8 +161,6 @@ export default {
this.resize = _.debounce(this.resize, 400); this.resize = _.debounce(this.resize, 400);
this.eventStripResizeObserver = new ResizeObserver(this.resize); this.eventStripResizeObserver = new ResizeObserver(this.resize);
this.eventStripResizeObserver.observe(this.$refs.events); this.eventStripResizeObserver.observe(this.$refs.events);
this.extendedLinesBus.addEventListener('disable-extended-lines', this.disableExtendEventLines);
this.extendedLinesBus.addEventListener('enable-extended-lines', this.enableExtendEventLines);
}, },
beforeUnmount() { beforeUnmount() {
if (this.eventStripResizeObserver) { if (this.eventStripResizeObserver) {
@ -148,15 +171,6 @@ export default {
if (this.unlisten) { if (this.unlisten) {
this.unlisten(); this.unlisten();
} }
if (this.destroyEventContainer) {
this.destroyEventContainer();
}
this.extendedLinesBus.removeEventListener(
'disable-extended-lines',
this.disableExtendEventLines
);
this.extendedLinesBus.removeEventListener('enable-extended-lines', this.enableExtendEventLines);
}, },
methods: { methods: {
setTimeContext() { setTimeContext() {
@ -165,20 +179,6 @@ export default {
this.timeContext.on('timeSystem', this.setScaleAndPlotEvents); this.timeContext.on('timeSystem', this.setScaleAndPlotEvents);
this.timeContext.on('boundsChanged', this.updateViewBounds); this.timeContext.on('boundsChanged', this.updateViewBounds);
}, },
enableExtendEventLines(event) {
const keyStringToEnable = event.detail;
if (this.keyString === keyStringToEnable) {
this.extendLines = true;
this.emitExtendedLines();
}
},
disableExtendEventLines(event) {
const keyStringToDisable = event.detail;
if (this.keyString === keyStringToDisable) {
this.extendLines = false;
this.emitExtendedLines();
}
},
firstNonDomainAttribute(metadata) { firstNonDomainAttribute(metadata) {
return metadata return metadata
.values() .values()
@ -242,12 +242,11 @@ export default {
return { return {
...eventHistoryItem, ...eventHistoryItem,
left: this.xScale(eventHistoryItem.time), left: this.xScale(eventHistoryItem.time),
limitClass limitClass,
hoverEnabled: false
}; };
}); });
if (this.extendLines) { this.updateLines();
this.emitExtendedLines();
}
} }
}, },
setDimensions() { setDimensions() {
@ -354,22 +353,33 @@ export default {
`wrapper-${event.time}`, `wrapper-${event.time}`,
[aClasses.join(' ')] [aClasses.join(' ')]
); );
this.extendedLinesBus.updateHoverExtendEventLine(this.keyString, event.time); this.updateExtendedLineHover({
keyString: this.keyString,
id: event.time,
hoverEnabled: true
});
}, },
dismissToolTip() { dismissToolTip(event) {
this.hideToolTip(); this.hideToolTip();
this.extendedLinesBus.updateHoverExtendEventLine(this.keyString, null); this.updateExtendedLineHover({
keyString: this.keyString,
id: event.time
});
}, },
emitExtendedLines() { updateLines() {
let lines = []; let lines = [];
if (this.extendLines) { if (this.extendedLinesEnabled) {
lines = this.eventItems.map((e) => ({ lines = this.eventItems.map((e) => ({
x: e.left, x: e.left,
limitClass: e.limitClass, limitClass: e.limitClass,
id: e.time id: e.time,
hoverEnabled: false
})); }));
} }
this.extendedLinesBus.updateExtendedLines(this.keyString, lines); this.updateExtendedLines({
lines,
keyString: this.keyString
});
} }
} }
}; };

View File

@ -22,12 +22,13 @@
<template> <template>
<div class="c-timeline__overlay-lines"> <div class="c-timeline__overlay-lines">
<div <div
v-for="(lines, key) in extendedLinesPerKey" v-for="(lineData, key) in extendedLines"
:key="key" :key="key"
class="c-timeline__event-line--extended-container" class="c-timeline__event-line--extended-container"
> >
<template v-if="lineData.enabled">
<div <div
v-for="(line, index) in lines" v-for="(line, index) in lineData.lines"
:id="line.id" :id="line.id"
:key="`${index - line.id}`" :key="`${index - line.id}`"
class="c-timeline__event-line--extended" class="c-timeline__event-line--extended"
@ -35,24 +36,25 @@
line.limitClass, line.limitClass,
{ {
'--hilite': '--hilite':
(hoveredLineId && hoveredKeyString === key && line.id === hoveredLineId) || line.hoverEnabled === true ||
(selectedLineId && selectedKeyString === key && line.id === selectedLineId) (selectedLineId && selectedKeyString === key && line.id === selectedLineId)
} }
]" ]"
:style="{ left: `${line.x + leftOffset}px`, height: `${height}px` }" :style="{ left: `${line.x + leftOffset}px`, height: `${height}px` }"
></div> ></div>
</template>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { inject } from 'vue';
import { useExtendedLines } from '../../ui/composables/extendedLines';
export default { export default {
name: 'ExtendedLinesOverlay', name: 'ExtendedLinesOverlay',
props: { props: {
extendedLinesPerKey: {
type: Object,
required: true
},
height: { height: {
type: Number, type: Number,
required: true required: true
@ -61,22 +63,20 @@ export default {
type: Number, type: Number,
default: 0 default: 0
}, },
extendedLineHover: {
type: Object,
required: true
},
extendedLineSelection: { extendedLineSelection: {
type: Object, type: Object,
required: true required: true
} }
}, },
setup() {
const domainObject = inject('domainObject');
const path = inject('path');
const openmct = inject('openmct');
const { extendedLines: extendedLines } = useExtendedLines(domainObject, path, openmct);
return { extendedLines };
},
computed: { computed: {
hoveredLineId() {
return this.extendedLineHover.id || null;
},
hoveredKeyString() {
return this.extendedLineHover.keyString || null;
},
selectedLineId() { selectedLineId() {
return this.extendedLineSelection.id || null; return this.extendedLineSelection.id || null;
}, },

View File

@ -50,9 +50,13 @@
</template> </template>
<script> <script>
import { inject } from 'vue';
import ObjectView from '@/ui/components/ObjectView.vue'; import ObjectView from '@/ui/components/ObjectView.vue';
import SwimLane from '@/ui/components/swim-lane/SwimLane.vue'; import SwimLane from '@/ui/components/swim-lane/SwimLane.vue';
import { useExtendedLines } from '../../ui/composables/extendedLines';
export default { export default {
components: { components: {
ObjectView, ObjectView,
@ -69,6 +73,16 @@ export default {
required: true required: true
} }
}, },
setup() {
const domainObject = inject('domainObject');
const objectPath = inject('path');
const openmct = inject('openmct');
const { disable: disableExetendedLinesForObject, enable: enableExtendedLinesForObject } =
useExtendedLines(domainObject, objectPath, openmct);
return { disableExetendedLinesForObject, enableExtendedLinesForObject };
},
data() { data() {
return { return {
domainObject: null, domainObject: null,
@ -141,11 +155,11 @@ export default {
}, },
enableExtendEventLines() { enableExtendEventLines() {
const keyString = this.openmct.objects.makeKeyString(this.item.domainObject.identifier); const keyString = this.openmct.objects.makeKeyString(this.item.domainObject.identifier);
this.extendedLinesBus.enableExtendEventLines(keyString); this.enableExtendedLinesForObject(keyString);
}, },
disableExtendEventLines() { disableExtendEventLines() {
const keyString = this.openmct.objects.makeKeyString(this.item.domainObject.identifier); const keyString = this.openmct.objects.makeKeyString(this.item.domainObject.identifier);
this.extendedLinesBus.disableExtendEventLines(keyString); this.disableExetendedLinesForObject(keyString);
}, },
setActionCollection(actionCollection) { setActionCollection(actionCollection) {
this.openmct.menus.actionsToMenuItems( this.openmct.menus.actionsToMenuItems(

View File

@ -47,10 +47,8 @@
</div> </div>
<ExtendedLinesOverlay <ExtendedLinesOverlay
:extended-lines-per-key="extendedLinesPerKey"
:height="height" :height="height"
:left-offset="extendedLinesLeftOffset" :left-offset="extendedLinesLeftOffset"
:extended-line-hover="extendedLineHover"
:extended-line-selection="extendedLineSelection" :extended-line-selection="extendedLineSelection"
/> />
</div> </div>
@ -105,8 +103,6 @@ export default {
height: 0, height: 0,
useIndependentTime: this.domainObject.configuration.useIndependentTime === true, useIndependentTime: this.domainObject.configuration.useIndependentTime === true,
timeOptions: this.domainObject.configuration.timeOptions, timeOptions: this.domainObject.configuration.timeOptions,
extendedLinesPerKey: {},
extendedLineHover: {},
extendedLineSelection: {}, extendedLineSelection: {},
extendedLinesLeftOffset: 0 extendedLinesLeftOffset: 0
}; };
@ -128,15 +124,11 @@ export default {
this.handleContentResize.cancel(); this.handleContentResize.cancel();
this.contentResizeObserver.disconnect(); this.contentResizeObserver.disconnect();
this.openmct.selection.off('change', this.checkForLineSelection); this.openmct.selection.off('change', this.checkForLineSelection);
this.extendedLinesBus.removeEventListener('update-extended-lines', this.updateExtendedLines);
this.extendedLinesBus.removeEventListener('update-extended-hover', this.updateExtendedHover);
}, },
mounted() { mounted() {
this.items = []; this.items = [];
this.setTimeContext(); this.setTimeContext();
this.extendedLinesBus.addEventListener('update-extended-lines', this.updateExtendedLines);
this.extendedLinesBus.addEventListener('update-extended-hover', this.updateExtendedHover);
this.openmct.selection.on('change', this.checkForLineSelection); this.openmct.selection.on('change', this.checkForLineSelection);
if (this.composition) { if (this.composition) {
@ -275,10 +267,6 @@ export default {
const { keyString, lines } = event.detail; const { keyString, lines } = event.detail;
this.extendedLinesPerKey[keyString] = lines; this.extendedLinesPerKey[keyString] = lines;
}, },
updateExtendedHover(event) {
const { keyString, id } = event.detail;
this.extendedLineHover = { keyString, id };
},
checkForLineSelection(selection) { checkForLineSelection(selection) {
const selectionContext = selection?.[0]?.[0]?.context; const selectionContext = selection?.[0]?.[0]?.context;
const eventType = selectionContext?.type; const eventType = selectionContext?.type;

View File

@ -0,0 +1,177 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/* eslint-disable func-style */
import { reactive } from 'vue';
/** @type {Map<string, LineData> | null} */
const extendedLineMap = new Map();
/**
* Manages line data given an timeline object.
* This is a Vue composition API utility function.
* @param {Object} targetObject - The target to that's consuming the useExtendedLines composable.
* @param {ObjectPath} objectPath - The path of the target object.
* @param {import('../../../openmct.js').OpenMCT} openmct - The open mct API.
* @returns {Object} An object containing line data and methods to update, enable/disable, and reset extended line data.
*/
export function useExtendedLines(targetObject, objectPath, openmct) {
/**
* Get the key for the extended line given a path.
* @returns {string|undefined} The key of the extended line if found, otherwise undefined.
*/
const getTargetObjectKeyForPath = () => {
const keys = Array.from(extendedLineMap.keys());
return objectPath
.map((domainObject) => openmct.objects.makeKeyString(domainObject.identifier))
.reverse()
.find((keyString) => keys.includes(keyString));
};
// Use the furthest ancestor's lineData if it exists, otherwise, use your own
let targetObjectKey =
getTargetObjectKeyForPath() || openmct.objects.makeKeyString(targetObject.identifier);
if (!extendedLineMap.has(targetObjectKey)) {
extendedLineMap.set(targetObjectKey, reactive({}));
}
/**
* Reset any data for the given target object.
*/
const reset = () => {
const key = getTargetObjectKeyForPath();
if (key && extendedLineMap.has(key)) {
extendedLineMap.delete(key);
}
};
/**
* @typedef {Object} UpdateParams
* @property {Array} lines - The line data to be updated.
* @property {string} keyString - The domain object identifier of the telemetry object.
*/
/**
* Update line data given the keyString and path. The path is used to determine which ancestor should hold the lineData.
* @param {UpdateParams} param0 - The array of lines, keyString.
*/
const update = ({ lines, keyString } = {}) => {
const key = getTargetObjectKeyForPath();
if (key) {
const lineData = extendedLineMap.get(key);
if (lineData[keyString] === undefined) {
lineData[keyString] = {};
}
lineData[keyString].lines = lines;
}
};
/**
* @typedef {Object} UpdateParams
* @property {string} keyString - The domain object identifier of the telemetry object.
* @property {string} id - The identifier of the line
*/
/**
* Update line data given the keyString and path. The path is used to determine which ancestor should hold the lineData.
* @param {UpdateParams} param0 - The keyString and id.
*/
const updateLineHover = ({ keyString, id, hoverEnabled } = {}) => {
const key = getTargetObjectKeyForPath();
if (key) {
const lineData = extendedLineMap.get(key);
if (lineData[keyString] !== undefined) {
const lines = lineData[keyString].lines || [];
let line = lines.find((lineItem) => {
return lineItem.id === id;
});
if (line) {
line.hoverEnabled = hoverEnabled === true;
}
}
}
};
/**
* @typedef {Object} RemoveParams
* @property {string} keyString - The domain object identifier of the telemetry object.
*/
/**
* Unregister a telemetry object from line data.
* @param keyString
*/
const disable = (keyString) => {
const key = getTargetObjectKeyForPath();
if (key) {
const lineData = extendedLineMap.get(key);
if (lineData[keyString] !== undefined) {
lineData[keyString].enabled = false;
}
}
};
/**
* @typedef {Object} AddParams
* @property {string} keyString - The domain object identifier of the telemetry object.
*/
/**
* Register a telemetry object to line data.
* @param keyString
*/
const enable = (keyString) => {
const key = getTargetObjectKeyForPath();
if (key) {
const lineData = extendedLineMap.get(key);
if (lineData[keyString] === undefined) {
lineData[keyString] = {};
}
lineData[keyString].enabled = true;
}
};
return {
extendedLines: extendedLineMap.get(targetObjectKey),
update,
updateLineHover,
enable,
disable,
reset
};
}
/**
* @typedef {import('../../api/objects/ObjectAPI.js').DomainObject[]} ObjectPath
*/
/**
* @typedef {Object} LineData
* @property {Map<string, {Object} boolean, Array.<Line>> keyString - The domain object identifier of the telemetry object (typically an event or command)
*/
/**
* @typedef {Object} Line
* @property {Number} x - the left offset
* @property {string} limitClass - the class to apply to the line overlay
* @property {string} id - the id of the line (typically a timestamp)
* @property {boolean} hoverEnabled - whether the line is currently hovered on */